把AspDotNetCoreMvc程序运行在Docker上-part5:使用docker-compose
在上一part《把AspDotNetCoreMvc程序运行在Docker上-part4:实现负载均衡》中,我们通过几个比较复杂的步骤在docker平台上实现了对网站程序的负载均衡,配置步骤比较多。如果实际的站点较少,整个架构比较简单的情况下,这么做没有太大问题,如果应用较多的时候,会容易出错。那么这时候我们可能会想到自己写一些脚本来实现自动化,当然这是可行的。然而docker已为我们着想好,给我们提供了docker-compose功能,利用它我们可以实现对复杂应用的管理,包括容器、网络、volume等。
准备工作
先删除上一part我们创建的容器、网络和volume(不用觉得可惜,后面用上docker-compose可以轻易实现)
docker rm -f $(docker ps -aq)
docker network rm $(docker network ls -q)
docker volume rm $(docker volume ls -q)
安装docker-compose
在linux平台上需要手动执行如下命令安装
sudo curl -L https://github.com/docker/compose/releases/download/1.17.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
(最新版本可以从这里https://github.com/docker/compose/releases获知)
安装之后,运行docker-compose –version检查是否成功
准备MVC站点程序
可以从这里下载
https://github.com/shenba2014/AspDotNetCoreMvcDocker/tree/docker-compose
然后执行
dotnet restore
bower install
一会用到这个站点程序创建一个镜像以及对应的容器
创建docker-compose配置文件
新建docker-compose.yml文件(在docker所在宿主服务器直接创建也可以)
version: "3"
volumes:
productdata:
networks:
frontend:
backend:
services:
mysql:
image: "mysql:8.0.0"
volumes:
- productdata:/var/lib/mysql
networks:
- backend
environment:
- MYSQL_ROOT_PASSWORD=password
- bind-address=0.0.0.0
这个文件足够的自描述,把上一节我们用到的内容都作出了定义,还是简单解释一下
version:版本号,目前最新的是3.0
volumes:之前的《把AspDotNetCoreMvc程序运行在Docker上-part3:使用独立的存储容器》里面有介绍过,用于在docker容器外存储数据
networks:在上一part说过,定义了两个网络:frontend和backend
services:定义将要用到的容器,这里只定义了mysql容器,这些参数跟使用docker create命令创建容器的参数一致。具体可参考上一part《把AspDotNetCoreMvc程序运行在Docker上-part4:实现负载均衡》。
docker-compose执行构建
执行如下构建命令
docker-compose –f docker-compose.yml build
输出结果
WARNING: Some networks were defined but are not used by any service: frontend, backend
mysql uses an image, skipping
这里的警告信息意思是我们定义的两个网络还没被任何服务组件使用。
运行了这个命令只是定义,并没有真正的创建volume、network或者容器。
接下来运行docker-compose.yml定义的应用,才会真正的创建内容
docker-compose -f docker-compose.yml up
可以理解为启动刚才我们定义的组件,然后会有一堆的输出内容
WARNING: Some networks were defined but are not used by any service: frontend
Creating network "dockertemp_backend" with the default driver
Creating dockertemp_mysql_1 ...
Creating dockertemp_mysql_1 ... done
Attaching to dockertemp_mysql_1
很明显docker-compose在依次创建network、容器实例。
名字看起来比较奇怪,都带了dockertemp_前缀。因为docker-compose.yml所在的目录命令是dockertemp,所以最终提供的名称有这个前缀,避免了命名冲突。
而为什么是mysql_1,带有_1后缀,后续会用到,这里是为了用于横向扩展做集群的时候用到。
进一步通过如下命令验证volume、network和容器是创建成功的
docker volume ls
docker netowrk ls
docker ps –a
使用docker-compose –f docker-compose.yml down -v可删除network、容器和volume,全自动完成(保存在volume的数据也会删除,如果要保留volume把-v去掉即可)。
现在docker-compose文件里只定义了一个service,接下来继续添加MVC站点容器以及负载均衡容器。
添加MVC站点容器以及负载均衡容器
首先从以下路径下载代码
https://github.com/shenba2014/AspDotNetCoreMvcDocker/tree/docker-compose
这个分支跟之前的不一样,增加了对支持docker-compose的一些改动。
代码拉下来之后执行如下命令
dotnet restore
dotnet publish --framework netcoreapp2.0 --configuration Release --output dist
cd dist | npm install
dist文件夹里的内容就是我们后续要用到的MVC站点内容。
从源代码查看完整的docker和docker-compose.yml文件
先看看docker文件的更新内容
FROM microsoft/aspnetcore:2.0.0
COPY dist /app
COPY dist/node_modules/wait-for-it.sh/bin/wait-for-it /app/wait-for-it.sh
RUN chmod +x /app/wait-for-it.sh
WORKDIR /app
EXPOSE 80/tcp
ENV WAITHOST=mysql WAITPORT=3306
ENTRYPOINT ./wait-for-it.sh $WAITHOST:$WAITPORT --timeout=0 && exec dotnet AspDotNetCoreMvcDocker.dll
红色部分是新增的内容,这里拷贝了一个wait-for-it的脚本到了容器内部,并且在ENTRYPOINT里设置了等待mysql启动之后再启动MVC容器。
简单解释一下,因为使用了docker-compose启动容器之后,我们不能确保mysql容器服务是否已正常启动,如果mysql没有启动,那么站点也没法使用。所以容器必须在mysql容器启动之后执行,这里就引入了一个npm的package:wait-for-it,这个只能在Linux平台下使用。
接下打开完整的docker-compose.yml文件,这里只列出新增的service(完整文件内容不列出,可查看代码)
dbinit:
build:
context: .
dockerfile: Dockerfile
networks:
- backend
environment:
- INITDB=true
- DBHOST=mysql
- DBPASSWORD=password
depends_on:
- mysql
mvc:
build:
context: .
dockerfile: Dockerfile
networks:
- backend
- frontend
environment:
- DBHOST=mysql
- DBPASSWORD=password
depends_on:
- mysql
loadbalancer:
image: dockercloud/haproxy
ports:
- 3000:80
links:
- mvc
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
- frontend
这里才原来的基础上增加了dbinit,mvc和loadbalancer三个service,分别描述如下
dbinit:主要是用于初始化数据库,这里是把它当做一个容器来执行,实际在完成了数据库初始化自后就会退出。
mvc:这里就是MVC站点的容器,跟我们上一part定义MVC的参数类似。主要是增加了depends_on参数,指明mvc容器依赖于mysql容器。
loadbalancer:顾名思义,就是负载均衡服务器容器,通过links参数指向了其代理的站点是MVC容器,并且通过volumes参数配置了/var/run/docker.sock文件映射为主机的文件,具体作用不描述,主要是为了在扩容的时候通知到负载均衡服务器。
启动站点
把上一步的dist文件夹docker、docker-compose.yml文件拷贝到docker服务器(如果开发机就是docker服务器可以忽略),然后在这些文件的所在目录执行如下命令
docker-compose -f docker-compose.yml build
这个命令之前提到过,类似于编译,检查是否有错误,实际上不会创建容器
接着运行
docker-compose -f docker-compose.yml up dbinit
开启dbinit容器,之前我们提到,这个dbinit也是个MVC容器,但是只是负责数据库初始化,完成之后会自动关闭,这是这个命令的output
Creating network "dockertemp_frontend" with the default driver
Creating network "dockertemp_backend" with the default driver
Creating volume "dockertemp_productdata" with default driver
Creating dockertemp_mysql_1 ...
Creating dockertemp_mysql_1 ... done
Creating dockertemp_dbinit_1 ...
Creating dockertemp_dbinit_1 ... done
Attaching to dockertemp_dbinit_1
dbinit_1 | wait-for-it.sh: waiting for mysql:3306 without a timeout
dbinit_1 | wait-for-it.sh: mysql:3306 is available after 16 seconds
dbinit_1 | warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
dbinit_1 | No XML encryptor configured. Key {1b1e827d-d1d0-4d0c-a92d-8632ee0791ef} may be persisted to storage in unencrypted form.
dbinit_1 | Preparing Database...
dbinit_1 | Applying Migrations...
dbinit_1 | Creating Seed Data...
dbinit_1 | Database Preparation Complete
dockertemp_dbinit_1 exited with code 0
从output可以看到有一个等待mysql启动的步骤,等待了16秒。然后是执行了数据库初始化,创建表以及插入一些种子数据。最后一个行输出表明该容器自动退出。
好了数据库准备好之后,就可以启动MVC和负载均衡容器,还是通过docker-compose命令
docker-compose -f docker-compose.yml up mvc loadbalancer
执行完成之后,就可以访问我们的MVC站点了,这里我们只启用了一个MVC站点,试试从浏览器打开http://192.168.115.136:3000/,一切正常的话就能看到网站和数据了
做了这么多步骤,就是为了实现负载均衡,接下来一行命令让他立即扩容到4个MVC站点
docker-compose -f docker-compose.yml scale mvc=4
秒级扩容就是这简单,不信的话运行docker ps看看下面是不是有4个MVC站点容器,还不相信的话手动多刷几次http://192.168.115.136:3000/,会发现from server的值是会发生变化的。
好了,如果这时候要实现优雅降级怎么办,没问题,还是一行命令
docker-compose -f docker-compose.yml scale mvc=1
系统自动移除了三个MVC站点容器,当然也是秒级的。
最后关闭所有服务的命令是
docker-compose -f docker-compose.yml stop
写了这么多就是为了描述docker-compose的使用方法,对比上一part,实际上就是把之前的工作都放到一个批处理文件里执行,将所有服务当做一个整体来管理,同时也提供了遍历站点扩容和降级的方式。看起来已经很方便了,当然还没完,后续还有大招。