docker-compose 是个好东西,越用越香
docker-compose 是个好东西,越用越香
回顾前文
前文演示了在单一容器中部署 Nginx和ASP.NET Core WebApp, 正在前文留言中某位大牛指出的,容器化部署 nginx+ASP.NETcore 有更生产化的部署选择,
这次我们会演示利用 docker-compose 创建两个独立的容器 部署企业级应用。
本文会讲述企业级示例项目中用到的 docker volume、docker network、redis、sqlite、docker HealthCheck 等相关知识, 略去CentOS平台基本操作、Linux 下安装Docker, Linux安装Redis等前置知识点。
头脑风暴
WebApp业务上依赖第三方服务、容器外Redis服务、Sqlite数据库,可以想见我们会利用到 docker Volume机制和部分容器网络知识,
此处我们会以独立容器分别部署ASP.NETCore WebApp、Nginx容器,docker-compose 容器编排工具登场。
操作步骤
1. 准备应用程序部署文件
利用dotnet publish CLI命令或者 WebDeploy工具生成部署文件,将部署文件拷贝到如下图示publish文件夹
. ├── app │ ├── Dockerfile │ └── publish ├── applogs ├── docker-compose.yml ├── EqidManager.db └── nginx ├── Dockerfile └── nginx.conf
2. 应用docker-compose 工具
Docker Compose用于运行多容器Docker应用程序,通过docker-compose.yml文件,您可以配置多个应用程序容器,然后,您可以构建并运行容器集合(类似于单个容器的方式)。
version: "3.4" services: app: build: context: ./app dockerfile: Dockerfile expose: - "80" extra_hosts: - "dockerhost:172.18.0.1" environment: TZ: Asia/Shanghai volumes: - type: bind source: /mnt/eqidmanager/eqidlogs target: /app/eqidlogs - type: bind source: /home/huangjun/eqidmanager/applogs target: /app/logs - type: bind source: /home/huangjun/eqidmanager/EqidManager.db target: /app/EqidManager.db healthcheck: test: ['CMD','curl','-f','http://localhost/healthcheck'] interval: 1m30s timeout: 10s retries: 3 start_period: 6s proxy: build: context: ./nginx dockerfile: Dockerfile ports: - "80:80" environment: TZ: Asia/Shanghai links: - app
这个配置定义了两个服务: app、nginx
-
对于每个服务,【build】 告诉Docker Compose怎么去为每个服务构建镜像
-
【expose】和【ports】控制服务与 network bridge、宿主机交互的方式
-
【links】表明链接另外的容器,意味着 代理服务启动的时候捡回去启动app服务
-
在本应用程序中有业务数据需要被持久化, 同时使用了本地化的Sqlite数据库,所以使用 【Volumes】来映射宿主机路径到 app 服务
-
本应用程序中 因为涉及 按小时生成业务日志文件,文件命名根据时间戳,这里特意强调 容器内外使用同一时区, 容器内默认时区可能与宿主机本地不符,使用【TZ】配置容器内时区
-
应用程序在http://localhost/healthcheck 配置了健康检查能力, 这里使用Docker内置的【HealthCheck】指令轮询 app内的健康检查端口, 以判断容器是否持续以预期的方式运作, 更多信息,请参考…
-
其中的【extra_hosts】在容器内添加主机名映射, 类比与 在我们的电脑上hosts文件中增加一行主机名映射关系, 这个稍后会细说
3. 创建独立镜像
① 在app目录创建Dockerfile文件
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 WORKDIR /app COPY publish . EXPOSE 80 ENTRYPOINT ["dotnet","EqidManager.dll"]
View Code
上面的Dockerfile 显示将publish 文件件下的部署文件拷贝进docker镜像, 配置容器在80端口监听请求
② 在nginx文件夹下创建Dockerfile 文件,将会使用基础nginx镜像和自定义的nginx.conf文件
FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
View Code
nginx.conf 文件与前文类似:
worker_processes 4; events { worker_connections 1024; } http { sendfile on; upstream app_servers { server app:80; } server { listen 80; location / { proxy_pass http://app_servers; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; } } }
该nginx.conf与前文的区别是第9 行,前文因为app和nginx在一个容器,所以【upstream app_servers】配置为 server localhost:5000, 这里我们更改为server app:80, 其中app是在docker-compose.yml 文件中指定的服务名称。
4. 构建容器集合 –> 运行集合
在CentOS上安装了docker-compose工具之后, docker-compose –help 会看到可以利用的工具指令:
// build 命令会构建/重建每一个服务, 然后使用项目名称和服务名称标记每个镜像、容器
docker–compose build
// up 命令创建并运行容器
docker–compose up
如下图示: docker-compose 默认会利用项目名称EqidManager , 应用程序服务名称app 构建 ImageName=“EqidManager_app”镜像和对应名称容器。
本例中,访问localhost:80可验证是否成功部署。
————————————————————————–华丽丽的分割线 ————————————————————————
Network
最后我们来探究容器集合的网络连接, 这也是容器比较复杂的功能部分。
当执行docker-compose时,会创建一个共享网桥设备,集合内所有服务都可通过该网桥交流。
-
创建名称是 {project}_default 的网桥
-
使用【app】配置创建容器,同时使用服务名称app 加入 {project}_default 网络
-
使用【nginx】配置创建容器,同时使用服务名称nginx 加入 {project}_default 网络
现在来说下docker-compose.yml文件中【extra_hosts】的用法:
当前程序架构中使用的Redis 服务是宿主机的常驻服务,在app 容器内不能再使用localhost:6379引用redis服务, 因为容器内localhost 指向的是容器自身。
【extra_hosts】指令用于主机名映射, 可通过docker inspect [network_id] 查看宿主机在网桥上的映射IP:
[root@search-referer1 nginx]# docker inspect 0b576abb7ead [ { "Name": "eqidmanager_default", "Id": "0b576abb7ead9041a4aa0fe786c3e448f0ca93abe2559560e75f491bea326754", "Created": "2019-04-30T00:53:31.047534813+08:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "172.18.0.0/16", "Gateway": "172.18.0.1" } ] }, "Internal": false, "Attachable": true, "Ingress": false, "Containers": { "f0b08e0e54e7e9293211bba0eb92f55749c3de4b31dc8011c3f803c02a69000a": { "Name": "eqidmanager_proxy_1", "EndpointID": "1311f2b21b2a0ecec205e6a8902d298eece8a782f7f7ab785ded6561b8ff7c5e", "MacAddress": "02:42:ac:12:00:03", "IPv4Address": "172.18.0.3/16", "IPv6Address": "" }, "f57f5f1f69351245932885c2de271d387df0055b1c51a17242c8bc1e941ed32b": { "Name": "eqidmanager_app_1", "EndpointID": "b89d144948ee58870e07b9d1cfc5fd42f1bfe0a4b15e4fc2905d13136acb0a7e", "MacAddress": "02:42:ac:12:00:02", "IPv4Address": "172.18.0.2/16", "IPv6Address": "" } }, "Options": {}, "Labels": { "com.docker.compose.network": "default", "com.docker.compose.project": "eqidmanager", "com.docker.compose.version": "1.24.0" } } ]
View Code
本实例中宿主机在这个eqidmanager_default 网桥上的网关是 172.18.0.1,所以在docker-compose.yml 文件中配置了上述【extra_hosts】,在对应的app容器内我们cat /etc/hosts 会发现新增的映射记录:
相应的我们redis连接字符串是 :“redis”: “dockerhost:6379,password=*****@1,connectTimeout=10000,writeBuffer=40960”
That‘s all, 编写一个企业级docker-compose.yml 文件需要对项目业务流程和部署流程有全盘了解,同时必须要具备完备的 计算机操作原理和网络原理知识;
当然,当你编写完一个企业级docker-compose.yml文件并成功运行,这也印证了你已经全盘熟悉项目架构同时也帮你温习了丢失已久的 计算机操作原理和网络原理,
心中窃喜, docker-compose是个好东西,越用越香,
希望本文对初涉容器平台的同学能有一个抛砖引玉的效果。