Loading

docker部署ASP.NET Core、Nginx、MySQL

2019/10/24, docker 19.03.4, ASP.NET Core 3.0, CentOS 7.6

摘要:ASP.NET Core 3.0 网站项目容器化部署,使用docker-compose编排Nginx容器、MySQL容器、web容器
案例代码

依赖结构介绍

整个网站项目取名samplems,一共需要三个容器(按依赖顺序):
1.MySQL容器,取名为samplems.mysql,来自mysql(官方)镜像
2.web容器,取名为samplems.web,来自samplemsweb(自己build出来的)镜像
3.nginx容器,取名为samplems.nginx,来自nginx(官方)镜像
使用docker-compose编排三个容器,建立桥接网络(取名samplems-net)联系三者

目录结构

在CentOS7.6中/home/yzh(yzh是我登录账户的个人文件夹)下新建deploy部署文件夹,用于存放部署所需文件,deploy里文件结构如下(后文会逐步建立以下内容):

│  docker-compose.yml       //compose编排文件
│ 
├─web                       //web 目录 
│  │  Dockerfile            //web docker配置文件 
│  │                      
│  └─publish                //web 发布文件(发布生成的内容) 
│      ...                     
│      appsettings.json     //发布文件中包含appsettings.json,其中有数据库连接配置 
│      ... 
│   
└─nginx                     //nginx目录
    │                                                         
    └─confs                 //nginx 配置文件目录
        nginx.conf          //nginx全局配置
        default.conf       //web项目对应的nginx配置文件

在vs中方案文件夹结构如下:

这样组织后,WebApi发布到deploy/web/publish中,整个deploy文件夹打包发送到服务器进行部署。

把部署分为两步:
1.构建所需镜像
2.使用docker-compose编排容器

构建所需镜像

MySQL镜像

本次项目用到的MySQL镜像直接使用官方镜像,不需要进一步包装修改,直接拉取(下载)官方镜像:

docker pull mysql

MySQL配置数据库密码、数据卷挂载都稍后在docker-compose.yml中配置。

web镜像

web项目准备

Visual Studio中,web项目的MySQL数据库连接字符串(appsettings.json中)改为:

server=samplems.mysql;database=samplems;user=root;password=mysql@samplems

注意点:

  • server=samplems.mysql,这个地址就是我们的MySQL容器名;
  • 此处的密码和后文中docker-compose.yml指定的MySQL密码要一致;
  • 连接字符串没有显式指定连接端口,所以是默认的3306端口

web项目发布时,目标运行时选择linux-x64可移植

将发布内容拷贝到目标系统上,把它放进/home/yzh/deploy/web/publish文件夹内,如下图:

我这里web项目名称为WebApi,所以其中的WebApi.dll就是后文Dockerfile中dotnet指令的对象。

Dockerfile编写

/home/yzh/deploy/web文件夹下新建Dockerfile文件:

touch Dockerfile

内容如下:

FROM mcr.microsoft.com/dotnet/core/aspnet:3.0
WORKDIR /app
COPY ./publish/ /app 
EXPOSE 80
ENTRYPOINT ["dotnet","WebApi.dll"]

上面五句话的意思分别是:

  • 基于mcr.microsoft.com/dotnet/core/aspnet:3.0镜像(因为是netcore3.0的web项目),会自动下载该镜像
  • 工作目录为容器内的/app
  • 将/web/publish文件夹里所有内容拷贝至/app
  • 容器开放80端口
  • 使用dotnet运行asp.net core web项目,这里是"WebApi.dll",因为WebApi.dll就在工作目录中,所以不需要任何路径

构建web镜像

在deploy/web文件夹下使用以下指令构建镜像:(注意samplemsweb与.之间有空格,samplemsweb就是我们命名的镜像名称)

docker build -t samplemsweb .

如下图所示:

Nginx镜像

本次项目用到的Nginx镜像直接使用官方镜像,不需要进一步包装修改,直接拉取(下载)官方镜像:

docker pull nginx

Nginx配置

/home/yzh/deploy/nginx/confs/下新建nginx.conf文件,此文件是Nginx的配置文件,内容如下:

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;


    #允许客户端请求的最大字节
    client_max_body_size 256m;
    #缓冲区最大字节
    client_body_buffer_size 256k;

    #代理服务器链接后端服务器的超时时间
    proxy_connect_timeout 30;
    #代理服务器等待后端服务器响应的超时时间
    proxy_read_timeout 60;
    #后端服务器返回数据给代理服务器的最大传输时间
    proxy_send_timeout 30;
    #代理服务器缓冲区大小,客户端的头信息会保存在这里
    proxy_buffer_size 64k;
    #代理服务器有几个缓冲区,最大是多大
    proxy_buffers 4 64k;
    #代理服务器繁忙可以申请更大的缓冲区,Nginx官方推荐为*2即可
    proxy_busy_buffers_size 128k;
    #代理服务器临时文件大小
    proxy_temp_file_write_size 256k;

	#websocket 启用
    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

简单解读:

  • client_max_body_size 256m;是限制web上传文件的大小限制
  • proxy开头的配置是为了解决an upstream response is buffered to a temporary file报错(请求文件大于缓冲区大小,导致写入成临时文件影响性能)
  • include /etc/nginx/conf.d/*.conf;是包含/etc/nginx/conf.d/下所有的配置文件,默认会有一个default.conf,下文会新建一个default.conf覆盖默认的
  • 定义了nginx错误日志和访问日志的路径

/home/yzh/deploy/nginx/confs/下新建default.conf文件,内容如下:

#设定负载均衡后台服务器列表
upstream composeserver {
	#指定支持的调度算法
	ip_hash;
    server samplems.web:80;
}

#虚拟主机的配置
server {
    listen 80;
    location / {
        proxy_pass http://composeserver;
		
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	# 以下三句话是websocket启用
	proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

注意点:

  • default.conf是nginx
  • 其中samplems.web就是我们web容器的名称,后面的80即容器端口
  • listen 80 监听80端口,也就是后文为什么要开放nginx容器的80端口

至此所有的镜像已准备好

编写docker-compose.yml

在deploy文件夹下新建docker-compose.yml文件,内容如下:

version: '3.4'

services:
  samplems.mysql:
    image: mysql
    container_name: samplems.mysql
    ports:
      - "3306:3306"
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=mysql@samplems
      - TZ=Asia/Shanghai
    volumes:
      - /app/data/mysql:/var/lib/mysql
    networks:
      - samplems-net

  samplems.web:
    image: samplemsweb
    container_name: samplems.web
    restart: always
    depends_on:
      - samplems.mysql
    environment:
      - TZ=Asia/Shanghai
    networks:
      - samplems-net


  samplems.nginx:
    image: nginx
    container_name: samplems.nginx
    restart: always
    ports:
      - "80:80"
      - "443:443"
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - /app/data/nginx/default.conf:/etc/nginx/conf.d/default.conf
      - /app/data/nginx/nginx.conf:/etc/nginx/nginx.conf
      - /app/logs/nginxlogs:/var/log/nginx
    depends_on:
      - samplems.web
    networks:
      - samplems-net

networks:
  samplems-net:
    driver: bridge

如上代码所示,整个yml文件结构大致分为version、services、networks三块,其中services内又有samplems.mysql、samplems.web、samplems.nginx三块

samplems.mysql:

  • image 表示引用的镜像,这里是官方的mysql
  • container_name:容器名称(其他两个容器这个字段的定义类似)
  • ports 映射的端口号,宿主机的3306映射到容器的3306端口(其他两个容器这个字段的定义类似)
  • restart: always 指定容器退出后的重启策略为始终重启(其他两个容器这个字段的定义相同)
  • environment 中定义了MySQL root账号的密码为mysql@samplems,这里需要和web项目中密码保持一致
  • environment 中定义了容器时区为亚洲上海,防止容器时区和宿主机时区不一致导致获取到的时间有偏差(其他两个容器这个字段的定义相同)
  • volumes 挂载数据卷,将容器中所有要写入/var/lib/mysql中的数据库文件,写入到宿主机/app/data/mysql中(这样即使mysql容器被删除,数据依然存在)
  • networks 使用自定义的samplems-net桥接网络(其他两个容器这个字段的定义相同)

samplems.web:

  • image 这里引用的是我们自己build的web镜像
  • depends_on 表明容器要先启动samplems.mysql,再启动samplems.web

samplems.nginx:

  • ports 80端口是默认的http端口,443是默认的https端口
  • volumes 挂载数据卷,将default.conf和nginx.conf两个配置文件映射到容器中,这样如果配置文件变动不需要重新构建nginx镜像(可以看到两个文件都在/app/data/nginx/下,所有容器编排启动前,需要将配置文件拷贝到/app/data/nginx/下)
  • volumes 挂载数据卷,将容器中所有要写入/var/log/nginx中的文件,写入到宿主机/app/logs/nginxlogs中,这里主要是nginx的日志文件
  • depends_on 表明容器要先启动samplems.web,再启动samplems.nginx,如此一来,启动的先后顺序为 mysql->web->nginx

启动容器

启动容器前,需要将nginx的配置文件拷贝至/app/data/nginx/下

#创建目录
sudo mkdir -p /app/data
#复制文件{/home/yzh}请对应修改
sudo cp -rf /home/yzh/deploy/nginx/confs /app/data/nginx

至此所有的准备工作都已完成,在deploy文件夹下执行以下命令启动容器:

docker-compose up -d

如下图,三个绿色的done表示三个容器启动成功:

使用docker ps查看容器的运行状态,是正常运行:

开放宿主机防火墙80端口:

sudo firewall-cmd --zone=public --add-port=80/tcp --permanent
sudo systemctl restart firewalld

浏览器访问CentOS的IP地址,即可查看网页:

注意点:

1.Linux下使用到的脚本、配置文件,在visual studio中建议使用高级保存选项:utf-8编码、Unix换行格式进行保存(如果这样还报错,可以直接在Linux下新建文件,再把内容复制进去)

2.网站Program.cs中,建议把CreateHostBuilder(args).Build().Run()包裹在try catch中,这样网站出错后容器会自动重启

整个访问流程

流程:
1.浏览器访问CentOS的IP地址(默认80端口)时,访问到宿主机的80端口
2.由于docker-compose.yml中定义了nginx的端口80:80,所有宿主机的80端口映射到nginx容器的80端口
3.由于nginx容器的配置文件default.conf定义了监听80端口,所以能响应到浏览器的请求
4.由于nginx容器的配置文件default.conf定义了反向代理,将请求转发给了samplems.web容器(因为共用了samplems-net网络,所以能找到对方),端口也是80
5.samplems.web在Dockerfile中暴露了80端口,所以响应nginx的转发,web服务做出对应response返回数据内容
6.因为共用了samplems-net网络,所以web服务在连接数据库中,能找到samplems.mysql,应用了数据库操作

posted @ 2019-10-24 21:39  kasnti  阅读(1880)  评论(2编辑  收藏  举报