Docker随着不断地发展与完善,其API接口变得越来越多,尤其在容器参数的配置方面,功能的完善势必造成参数列表的增长。若在Docker的范畴内管理容器,则唯一的途径是使用Docker client。而Docker client最原生的使用方式是:利用docker二进制文件发送命令行命令来完成容器的管理,这显然不是长久之计,很长一段时间内,全球的Docker爱好者都在探索以及寻找方便容器部署的途径。
Docker诞生于2013年3月,同年12月,基于Docker容器的部署工具Fig隆重登场。在Docker生态圈中,经过了两年多的洗礼,Fig项目得到飞速发展的同时,背后的东家也发生了很大的变化。作为Docker届容器自动化部署工具的翘楚,Fig原本是英国伦敦一家创业型公司的产品。随着产品的发展,Fig的巨大潜力受到工业界的普遍认可,在不到一年的时间内就受到Docker公司的密切关注。很快就在2014年7月双发爆出新闻:Docker收购Fig,收购完成之后,Fig改名为Compose,命令改为docker-compose。
Docker Compose的前身是Fig,它是一个定义及运行多个Docker容器的工具。使用Docker Compose你只需要在一个配置文件中定义多个Docker容器,然后使用一条命令将多个容器启动,Docker Compose会通过解析容器件的依赖关系(link, 网络容器 –net-from或数据容器 –volume-from)按先后顺序启动所定义的容器。
二、Compose介绍
探听Fig与Compose的前世今生之后,让我们回到Compose本身,认识一样新事物,从新事物的作用入手,往往不会出太大差错。而Compose最大的作用就是帮助用户缓解甚至解决容器部署的复杂性。最原始的情况下,通过Docker Client发送容器管理请求,尤其是docker run命令,一旦参数数量剧增,通过命令行终端来配置容器较为耗时,同时容错性较差。Compose则将所有容器参数通过精简的配置文件来存储,用户最终通过剪短有效的docker-compose命令管理该配置文件,完成Docker容器的部署。
编辑配置文件与编辑命令行命令的难易程度高下立判,同时配置文件数据的结构化程度越高,可读性也会越强。传统情况下,如docker run等命令的参数数量很多时,由于flag参数的书写格式各异,很容易造成用户费解的情况;而配置文件中一行内容就是一类具体的参数值,可读性大大增强。
在生产环境中,docker client还有一方面经常被Docker爱好者所诟病,那就是难以进行多容器的管理,每次管理的容器对象最多只能有1个。容器虽然运行时相对非常独立,但是很多情况下,容器之间会存在逻辑关系,如容器A使用容器B的data volume,如容器C需要对容器D执行link操作等。对于有逻辑关联的容器,如果能将其作为一个整体,被工具统一化管理,那将大大减少用户的认为参与,提高部署效率。
Compose软件的开发绝大部分是通过Python语言完成。由于Docker社区大部分项目是Go编写的,Compose使用python不利于项目间代码共享。 所幸的是,Compose社区目前已经开始着手此事,并以lib方式提供。
三、Compose架构
Docker生态圈中,Compose扮演的是部署工具的角色。用户使用Compose时,首先需要将部署意图通过配置文件的形式交给Compose。这样的需求包括:容器的服务名、容器镜像的build路径、容器运行环境的配置等。以下是一个较为简单的Compose配置文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
version
:
'2'
services
:
web
:
build
:
.
ports
:
-
"5000:5000"
volumes
:
-
/
data
/
log
:
/
var
/
log
links
:
-
redis
redis
:
image
:
redis
container_name
:
redis
-
01
hostname
:
live
-
app
-
redis
-
01
restart
:
always
mem_limit
:
1024M
#volumes:
#networks:
此配置文件定义了两个服务,名称分别为web以及redis。服务web的镜像可以通过docker build来构建,Dockerfile所处目录为该配置文件当前目录;服务web需要对redis服务进行links操作;最终服务web将宿主机上的5000端口映射到内部5000端口,并且挂载卷。服务redis通过镜像来创建。
配置文件是Compose体系中不可或缺,Fig时代支持的配置文件名为fig.yml以及fig.yaml;为了兼容遗留的Fig化配置,目前Compose支持的配置文件类型非常丰富,主要有以下5种:fig.yaml、docker-compose.yml、docker-compose.yaml以及用户指定的配置文件路径。可通过环境变量COMPOSE_FILE或-f参数自定义配置文件。
配置文件的存在未Compose提供了容器服务的配置信息,在此基础上,Compose通过不同的命令类型,将用户的docker-compose命令请求分发到不同的处理方法进行相应的处理。用户docker-compose的命令类型有很多,如命令请求docker-compose up….的类型为up请求,Compose将up请求分发至隶属于up的处理方法来处理;命令请求docker-compose run…..的类型为run,Compose将run请求分发至隶属于run的处理方法来处理。对于不同的docker-compose请求,Compose将调用不同的处理方法来处理。由于最终处理必须落实到Docker Daemon对容器的部署与管理上,故Compose最终必须与Docker Daemon建立连接,并在该连接之上完成Docker的API请求。事实上,Compose借助docker-py来完成此任务。docker-py是一个使用Python开发并调用docker daemon API的docker client包。需要说明的是:毕竟docker-py作为docker官方的一个Python软件包,和docker并不隶属于同一个项目,因此docker-py在很多方面的发展均会滞后于Docker。
清楚了Compose的配置文件,处理方法以及docker-py概念之后,接下来可以看一下Compose的架构,如下图:
Compose将所管理的容器分为三层,工程(project),服务(service)以及容器(contaienr)。这三个概念均为Compose抽象的数据类型,其中project会包含service以及container。首先介绍这三者的意义。
project代表用户需要完成的一个项目,何为项目?Compose的一个配置文件可以解析为一个项目,即Compose通过分析指定配置文件,得出配置文件所需完成的所有容器管理与部署操作。例如:用户在当前目录下执行docker-compose up -d,配置文件为当前目录下的配置文件docker-compose.yml,命令请求类型为up,-d为命令参数,对于配置文件中的内容,compose会将其解析为一个project。一个project拥有特定的名称,并且包含多个或一个service,同时还带有一个Docker Client。
service,代表配置文件中的每一项服务,何为服务?即以容器为粒度,用户需要Compose所完成的任务,比如前面的配置文件中包含了两个service,第一个为web,第二个为redis。一个service包含的内容,无非是用户对服务的定义。定义一个服务,可以为服务容器指定镜像,设定构建的Dockerfile,可以为其指定link的其他容器,还可以为其指定端口的映射等。
从配置文件到service,实现了用户语义到Compose语义的转换。虽然一个service尽可能详细地描述了一个容器的具体信息,但是Compose并一定必须在service之上管理容器,如果用户使用docker-compose pull redis命令,则仅仅完成redis服务中指定镜像的下载。除此之外,Compose的service还可以映射到多个容器,如果用户使用docker-compose scale web=3命令,则可以将web服务横向扩展到3个容器。
读到这里,再来说一说容器怎么解决service之间关系的依赖的。如果Compose一味按照配置文件中的书写顺序来完成service的指定任务,显然会出现一些不可避免的问题,假设多个service所描述的容器之间存在依赖关系,一旦配置文件中的顺序与实际的正常启动顺序不一致,必将导致容器启动失败。一般而言,容器依赖关系会存在以下三种情况:
存在links参数,容器的启动需要链接到另一个容器,就是一个容器需要通过本地访问另一个容器的服务。
存在volumes_from参数,容器的启动需要挂载另一个容器的data volume。
存在net参数,容器的启动过程中网络模式采用other container模式,使用另一个容器的网络栈。
为了解决这些问题,对于用户的某些请求,如docker-compose up等,Compose在解析出所有service之后,需要根据各个service的定义情况,梳理出所有的依赖关系,并最终以一个没有冲突的顺序启动所有的service容器。当然,这种情况无法应对环式依赖。
说说links参数?
如上dockerfile,web服务links了redis服务,什么意思呢?
容器之间的链接实际做了什么?一个链接允许一个源容器提供信息访问给一个接收容器。在本例中,web容器作为一个接收者,允许访问源容器redis的相关服务信息。Docker创建了一个安全隧道而不需要对外公开任何端口给外部容器,因此不需要在创建容器的时候添加-p或-P指定对外公开的端口,这也是链接容器的最大好处,也就是说redis无法对外提供服务,只能由web容器来调用。
上面也说了Docker compose不会根据服务的先后顺序来启动容器。而是经过compose自身重新编排,梳理出所有的依赖关系,并最终以一个没有冲突的顺序启动所有的service容器。如web依赖redis,那么compose就会先启动redis容器,后启动web容器。
解决依赖,先后启动。看似很美好的一个过程,虽然compose解决了依赖,先启动redis后再启动web。如果redis服务自身启动时间比web要长,比如redis启动5,而web启动只要3秒。这个时候用户访问一样会报错,也就是说compose无法判断被依赖的容器是否可以正常提供服务,如果正常后才启动依赖者。但是我猜想后面compose一定会解决这个问题的,而现在在GitHub上有人就为这个问题写了一个小工具。
四、docker-compose.yml参考
每个docker-compose.yml必须定义image或者build中的一个,其它的是可选的。
container_name
设置容器名称
如果image不存在,Compose会尝试拉取它,除非你也指定了build,在这种情况下,它使用指定的选项构建它,并用指定的标签标记它。
等等,更多可以参考
Docker:Compose file v2 reference
五、安装Compose
docker-compose是Python写的,所以可以直接使用pip去安装docker-compose。官方:
Compose file reference
$ docker-compose up -d
Creating network "root_default" with the default driver
Pulling redis (redis:latest)...
latest: Pulling from library/redis
75a822cd7888: Pull complete
e40c2fafe648: Pull complete
ce384d4aea4f: Pull complete
5e29dd684b84: Pull complete
29a3c975c335: Pull complete
a405554540f9: Pull complete
4b2454731fda: Pull complete
Digest: sha256:eed4da4937cb562e9005f3c66eb8c3abc14bb95ad497c03dc89d66bcd172fc7f
Status: Downloaded newer image for redis:latest
Creating root_redis_1
Attaching to root_redis_1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$
docker
-
compose
up
-
d
Creating
network
"root_default"
with
the
default
driver
Pulling
redis
(
redis
:
latest
)
.
.
.
latest
:
Pulling
from
library
/
redis
75a822cd7888
:
Pull
complete
e40c2fafe648
:
Pull
complete
ce384d4aea4f
:
Pull
complete
5e29dd684b84
:
Pull
complete
29a3c975c335
:
Pull
complete
a405554540f9
:
Pull
complete
4b2454731fda
:
Pull
complete
Digest
:
sha256
:
eed4da4937cb562e9005f3c66eb8c3abc14bb95ad497c03dc89d66bcd172fc7f
Status
:
Downloaded
newer
image
for
redis
:
latest
Creating
root_redis_1
Attaching
to
root_redis_1
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9ddfcb38c9b5 redis "docker-entrypoint.sh" 30 seconds ago Up 6 seconds 6379/tcp root_redis_1
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4c486384557e redis "docker-entrypoint.sh" 6 seconds ago Up 6 seconds 0.0.0.0:6370->6379/tcp test_redis_1
f98499e1acfe redis "docker-entrypoint.sh" 5 minutes ago Up 5 minutes 0.0.0.0:6379->6379/tcp root_redis_1
$
docker
ps
CONTAINER
ID
IMAGE
COMMAND
CREATED
STATUS
PORTS
NAMES
4c486384557e
redis
"docker-entrypoint.sh"
6
seconds
ago
Up
6
seconds
0.0.0.0
:
6370
->
6379
/
tcp
test_redis_1
f98499e1acfe
redis
"docker-entrypoint.sh"
5
minutes
ago
Up
5
minutes
0.0.0.0
:
6379
->
6379
/
tcp
root_redis_1
-p
:设置一个项目名称,默认是当前目录的名称。
Docker-compose的动作包括:
build
:构建yml中某个服务的镜像,本地需存在Dockerfile文件,才使用docker-compose build来构建服务的镜像。
config
:验证和查看yml文件。
$ docker-compose exec redis ss -nplt | grep 6379
LISTEN 0 128 *:6379 *:*
LISTEN 0 128 :::6379 :::*
docker-compose ps
Name Command State Ports
------------------------------------------------------------------------------
root_redis_1 docker-entrypoint.sh redis ... Up 0.0.0.0:6379->6379/tcp
docker
-
compose
ps
Name
Command
State
Ports
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
root_redis_1
docker
-
entrypoint
.sh
redis
.
.
.
Up
0.0.0.0
:
6379
->
6379
/
tcp
没有Daemon
没有Deamon,也就没有高可用、HA之说。 但是同时没有Deamon,所用动作需要用户自己触发。AutoScaling、self healing等也就没有办法提供。
模型相对简单,只有service。 缺乏诸如网络、存储之类的资源抽象和管理。也缺乏诸如kubernetes中Pod、RC、service proxy之类的抽象,由于servie本身粒度太细,操作管理起来相对麻烦。
Python编写
虽然我很喜欢Python语言,但是由于Docker社区大部分项目是Go编写的,Compose使用python不利于项目间代码共享。 所幸的是,Compose社区目前已经开始着手此事,并以lib方式提供。
跨节点能力
Docker容器跨节点主机部署的需求逐步增大,稍令人失望的是,Compose目前在这方面的功能依旧不令人满意。实际应用场景下,Docker用户往往希望将不同类型的容器部署在不同的Docker节点上,满足负载、安全、资源利用等多方面的考虑。虽然Compose目前不具备这样的能力,但并不以为着Docker会放弃这方面的市场,再等等……..
九、Compose与Swarm
Docker容器跨节点部署方案的发展,用”需求决定方向”来形容再准确不过。目前,Docker正在酝酿着Compose与Swarm的深度结合,目标是:使用户在一个Swarm集群上运行Compose来部署容器,效果和在单机上使用Compose完全一致。
先分析跨节点容器没有依赖的情况,容器之间一旦没有依赖,容器对自身所处的节点位置也就没有太多需求。这种情况下,理论上,Compose完全可以通过Swarm的label环境变量,将容器与满足条件的Docker Node联系在一起;同时也可以通过环境变量affinity,使几个容器部署在同一个Docker Node上或者避免在同一个Docker Node上。
再研究跨节点容器存在依赖的情况,跨节点容器有依赖,第一个需要解决的问题是跨节点容器的通信能力。而在Docker的范畴内,如果不借助其他工具,跨节点容器的通信目前还没有很好的支持。因此,如links、volumes_from、net:container等容器依赖的情况,目前还会默认将相应的容器部署在同一台机器上运行。
https://docs.docker.com/compose/
https://docs.docker.com/compose/compose-file/
如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (
1
)
赏
版权声明
本站的文章和资源来自互联网或者站长
的原创,按照 CC BY -NC -SA 3.0 CN
协议发布和共享,转载或引用本站文章
应遵循相同协议。如果有侵犯版权的资
源请尽快联系站长,我们会在24h内删
除有争议的资源。
Linux
Database
Network
Python
Ansible中文权威
运维进行时
DB-engines
Python 3
MySQL Blog
Redis中文指南
Redis命令手册
运维那点事
最新评论