回顾前文

前文演示了在单一容器中部署 Nginx和ASP.NET Core WebApp, 正在前文评论区某大牛指出的,容器化部署 nginx+ASP.NET Core 有更符合实战的部署选择:多容器独立部署。

这次记录我在工作中利用 docker-compose部署企业级web应用。

本文会讲述企业级示例项目中用到的 docker volume、docker network、redis、sqlite、docker HealthCheck 等相关知识, 略去CentOS平台基本操作、Linux 下安装Docker ,docker compose工具, Linux安装Redis等前置知识点。

头脑风暴

  图片总是比文字更能表达思想,下面图示帮助同学们在头脑中快速临摹出本次企业级项目的业务、部署流程, 方便同学们对照实战。

Web App业务上依赖第三方服务、容器外Redis服务、Sqlite数据库,可以想见我们会利用到 docker Volume机制和部分容器网络知识,

此处我们会以独立容器分别部署ASP.NETCore WebApp、Nginx容器,docker-compose 容器编排工具登场。

操作步骤

1. 准备应用程序部署文件

利用dotnet publish CLI命令或者 WebDeploy工具生成部署文件,这里因为还没有实现CI自动构建镜像,需要手动将部署文件拷贝到如下图示publish文件夹,现场生成镜像。

EqidManager
├── app
│ ├── Dockerfile
│ └── publish
├── applogs
├── docker-compose.yml
├── EqidManager.db
└── nginx
├── Dockerfile
└── nginx.conf

2. 应用docker-compose 工具

         这次将涉及两个独立的Docker容器,Docker Compose工具将两者连接在一起。

Docker 的优势非常明显,尤其是对于开发者来说,它提供了一种全新的软件发布机制:使用 docker镜像作为软件产品的载体,使用 docker容器提供独立的软件运行上下文环境,使用 docker hub 等提供镜像的集中管理,这其中最重要的是使用 Dockerfile 定义容器的内部行为和关键属性来支持软件运行。

但实际的生产环境往往需要定义数量庞大的 docker 容器,并且容器之间具有错综复杂的联系,手动的记录和配置这些复杂的容器关系,不仅效率低下而且容易出错。所以迫切需要一种类似于【Dockerfile定义docker容器】那样能够【定义容器集群编排和部署】的工具。于是Docker Compose 出现了(其实应该说 Fig 出现了,docker 收购了 Fig 并改名为 compose)。

         针对以上应用程序,在根目录下创建docker-compose.yml 文件:
version: "3.4"

services:
app:
build:
context: ./app
dockerfile: Dockerfile
expose:
- ""
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:
start_period: 6s
logging:
options:
max-size: "200k"
max-file: "10"
proxy:
build:
context: ./nginx
dockerfile: Dockerfile
ports:
- "80:80"
environment:
TZ: Asia/Shanghai
links:
    - app
logging:
options:
max-size: "200k"
max-file: "10"

这个配置定义了两个服务: app、nginx

  • 对于每个服务,【build】 告诉docker-compose怎样为每个服务构建镜像

  • 【expose】和【ports】控制服务与 network bridge、宿主机交互的方式

  • 【links】表明链接另外的容器,意味着nginx启动的时会去启动app服务

  • 在本应用程序中有业务数据需要被持久化, 同时使用了Sqlite数据库,所以使用 【Volumes】来映射宿主机路径到app 容器内路径, 注意容器挂在的源目录必须使用绝对路径。

  • 本应用程序中因为涉及按小时生成业务日志文件,与本地时间有很大关联性,这里特意强调容器内外最好使用同一时区, 容器内默认时区可能与宿主机本地不符,使用【TZ】环境变量配置容器内时区

  • 应用程序在http://localhost/healthcheck 配置了健康检查能力, 这里使用Docker内置的【HealthCheck】指令轮询 app内的健康检查端口, 以判断容器是否持续以预期的方式运作, 更多信息,请参考...

  • 其中的【extra_hosts】在容器内添加主机名映射, 类比与 在我们的电脑上hosts文件中增加一行主机名映射关系, 这个稍后会细说

  • 2019-06-16日更新:  添加Logging配置节,配置web程序和nginx日志大小(10个日志文件,每个最大200k)

3. 创建独立镜像

① 在app目录创建Dockerfile文件

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

上面的Dockerfile 显示将publish 文件件下的部署文件拷贝进docker镜像, 配置容器在80端口监听请求

② 在nginx文件夹下创建Dockerfile 文件,将会使用基础nginx镜像和自定义的nginx.conf文件

 FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf

nginx.conf 文件与前文类似:

worker_processes ;

events { worker_connections ; }

http {
sendfile on; upstream app_servers {
server app:;
} server {
listen ; 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可验证是否成功部署。

网桥模式

最后我们来探究容器集合的网络连接, 这也是容器比较复杂的部分。

docker引擎刚建立的时候,会新建一个docker0网桥(driver= bridge), 新加入的容器默认都会接入这个网桥。

 当执行docker-compose  up时,会创建新的网桥设备,集合内所有容器都通过该网桥交流:

① 创建名为 {project}_default 的网桥

②  以服务名app加入 {project}_default 网络; 以服务名nginx加入 {project}_default 网络

     每一个容器现在可使用 “app” /  “nginx”  服务名作为主机名相互访问

为啥可以通过 服务名访问 容器?

是因为利用了 Docker引擎内置的DNS, 查询服务名----》 查询DNS(每个服务名: 对应容器IP)

所以在nginx.conf 文件中我们给 【upstream app_servers】配置 app:80 能正确转发请求:

 

  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"
}
}
]

本实例中docker-compose 新建的eqidmanager_default网桥网关是 172.18.0.1,在docker-compose.yml 文件中配置了上述【extra_hosts】,在对应的app容器内我们cat  /etc/hosts 会发现新增的映射记录:

相应的连接字符串是 :

"connectionstrings": {
    ”sqlite": "Data Source=EqidManager.db",
     "redis": "dockerhost:6379,password=****@1,connectTimeout=10000,writeBuffer=40960"
},

tip: 这里假定每次执行docker-compose up/down命令,网关/子网都不变, 实际上很有可能变动, 最好使用自定义网桥。

由网桥而来的流量不经过宿主机的本地回环,因此需要将宿主机上的应用(MySQL,Redis等)配置为监听0.0.0.0。

 That‘s all, 编写一个企业级docker-compose.yml 文件需要对项目业务流程和部署流程有全盘了解,同时必须要具备完备的计算机操作原理和网络原理知识;

当然,当你编写完一个企业级docker-compose.yml文件并成功运行,这也印证了你已经全盘熟悉项目架构同时也重温了计算机操作原理和网络原理,

心中窃喜, docker-compose是个好东西,越用越香,

希望本文对初涉容器平台的同学能有一个抛砖引玉的效果。

https://www.cnblogs.com/JulianHuang/p/11636825.html

作者:JulianHuang

码甲拙见,如有问题请下方留言大胆斧正;码字+Visio制图,均为原创,看官请不吝好评+关注,  ~。。~

本文欢迎转载,请转载页面明显位置注明原作者及原文链接

 

docker-compose是个好东西,越用越香的更多相关文章

  1. Docker compose学习笔记

    一.compose compose 作用 你的应用可能需要很多个服务,比如web服务,数据库服务,缓存服务等等.我们可以把这些服务放到单独的容器里面,如果手工去配置这些服务会有些麻烦,docker c ...

  2. Docker & ASP.NET Core (5):Docker Compose

    第一篇:把代码连接到容器 第二篇:定制Docker镜像 第三篇:发布镜像 第四篇:容器间的连接 Docker Compose简介 Compose是一个用来定义和运行多容器Docker应用的工具.使用C ...

  3. Docker,Docker Compose,Docker Swarm,Kubernetes之间的区别

    Dcoker Docker 这个东西所扮演的角色,容易理解,它是一个容器引擎,也就是说实际上我们的容器最终是由Docker创建,运行在Docker中,其他相关的容器技术都是以Docker为基础,它是我 ...

  4. 四、docker compose

    docker compose可以方便我们快捷高效地管理容器的启动.停止以及重启等操作,和批量管理容器,它类似于linux下的shell脚本,基于yaml语法,在该文件里我们可以描述应用的架构,比如用什 ...

  5. Docker Compose(八)

    Docker Compose 是Docker官方编排(Orchstration)项目之一,负责快速在集群中部署分布式应用.   Dockerfile可以让用户管理一个单独的应用容器:而Compose则 ...

  6. 附003.Docker Compose命令详解

    一 Docker Compose命令格式 Usage: docker-compose [-f <arg>...] [options] [COMMAND] [ARGS...] docker- ...

  7. Docker学习笔记之编写 Docker Compose 项目

    0x00 概述 通过阅读之前的小节,相信大家对 Docker 在开发中的应用已经有了一定的了解.作为一款实用的软件,我们必须回归到实践中来,这样才能更好地理解 Docker 的实用逻辑和背后的原理.在 ...

  8. Docker学习笔记之使用 Docker Compose 管理容器

    0x00 概述 通过之前的介绍,我们已经基本掌握了构建.运行容器的方法,但这还远远不够,由于 Docker 采用轻量级容器的设计,每个容器一般只运行一个软件,而目前绝大多数应用系统都绝不是一个软件所能 ...

  9. Docker三剑客之Docker Compose

    一.什么是Docker Compose Compose 项目是Docker官方的开源项目,负责实现Docker容器集群的快速编排,开源代码在https://github.com/docker/comp ...

随机推荐

  1. mysql系列之6.mysql主从同步

    普通文件的数据同步 nfs: 网络文件共享 samba: 共享数据 定时任务或守护进程结合 rsync.scp inotify(sersync)+rsync 触发式实时数据同步 ftp数据同步 ssh ...

  2. 基于 HTTP 协议的几种实时数据获取技术(转)

    HTTP协议 HTTP协议大家都很熟悉了,开始本文之前,首先简单回顾一下HTTP协议. HTTP协议是建立在TCP协议上的应用层协议,协议的本质是请求----应答: 即对于HTTP协议来说,服务端给一 ...

  3. 【题解】Fence(单调队列)

    [题解]Fence(单调队列) POJ - 1821 题目大意 有\(k\)个粉刷匠,每个粉刷匠一定要粉刷某个位置\(S_i\),一个粉刷匠可以粉刷至多\(l_i\)个位置(必须连续\(l_i\)互不 ...

  4. 我的Java开发学习之旅------>求N内所有的素数

    一.素数的概念 质数(prime number)又称素数,有无限个.一个大于1的自然数,除了1和它本身外,不能被其他自然数(质数)整除,换句话说就是该数除了1和它本身以外不再有其他的因数:否则称为合数 ...

  5. datetime-local设置初始值

    //全局变量 var format = ""; //构造符合datetime-local格式的当前日期 function getFormat(){ format = "& ...

  6. 【shell】判断一个变量是否为空

    #!/bin/bash argv=" if [ -z "$argv" ] then echo "argv is empty" else echo &q ...

  7. http://blog.csdn.net/renfufei/article/details/37725057/

    版权声明:本文为博主原创文章,未经博主允许不得转载. 原创:http://blog.csdn.net/renfufei/article/details/37725057/ 说明: 首先,你需要注册一个 ...

  8. 从mediaserver入手快速理解binder机制(最简单理解binder)【转】

    本文转载自;https://blog.csdn.net/u010164190/article/details/53015194 Android的binder机制提供一种进程间通信的方法,使一个进程可以 ...

  9. 加州小学grade1,学习计划

    Visual vocabulary Grammar Spelling Maths Chapter 1 Patterns and Number SenseChapter 2Understanding A ...

  10. Bootstrap简单介绍

    一.一个小知识点 1.截取长屏的操作 2.设置默认格式 3.md,sm, xs 4.空格和没有空格的选择器 二.响应式介绍 - 响应式布局是什么? 同一个网页在不同的终端上呈现不同的布局等 - 响应式 ...