最近在学习k8s,生生感受到k8s那种生产环境的严谨和扩容性。但反过来想了下对于小规模环境k8s显得太重了,所以重新学习了下docker-compose,特此记录下。
在现版本中docker的stack功能以兼容docker-compose功能,以下内容在不安装docker-compose时同样可以使用
概述
docker-compose是docker的一个命令行工具,通过yml配置文件,进行一键式的项目部署,并支持依赖检测等功能。
docker-compose只是一个命令行工具,所以必须手动安装docker环境,才可以确保运行正常
其主要分成两部分,yml配置、命令操作。总体来说使用非常简单
这里不得不吐槽下,docker项目组肯定对于docker-compose没有投入太多的维护经历,官方文档系统对于常用的
services、services.build配置说的还是清楚的,但对于扩展配置networks、volumes这些就说的非常含糊。
YML配置
yml配置是整个docker-compose的核心
但他只是一个配置文件,所以可以放在任意一个地方,文件名随意,在命令操作有讲解如何引用。
个人建议,将yml配置文件 放在一个固定的地方比如:
/data/docker/docker-compose.yml
1 | version: "3.7" |
这就是整个配置文件的1、2级配置结构,你必须遵循这个结构,否则会运行失败
这里特别提醒下,yml语法特性(其实是解释器识别能力较弱),决定了所有的缩进必须遵循同一个规则,比如svrName的缩进用了1个tab,name下面netName也必须用1个tab,而不能改成2个空格,否则会造成block闭合失败。
对于yml来说,所有的配置就是键值对。具体使用中对于不希望更改对象配置的,可以使用数组方式指向。对于需要更改的内容通过键值对
说明
方式A
1
2volumes:
volumeName:方式B
1
2volumes:
- volumeName这两种方式,效果是一样的
字节
配置中会使用到内存、磁盘、数据量等容量单位,默认为
b
b:字节k:千字,替代kbm:兆字,替代mbg:吉字,替代gb
时间
配置中会使用到时间单位,默认为
s
us:纳秒ms:毫秒s:秒m:分钟h:小时
version & 配置版本
version用于指示配置文件所套用的版本号,参照标准是docker的版本
1 | $ docker -v |
| Version Code | Docker Engine release |
|---|---|
| 3.7 | 18.06.0+ |
| 3.6 | 18.02.0+ |
| 3.5 | 17.12.0+ |
| 3.4 | 17.09.0+ |
| 3.3 | 17.06.0+ |
| 3.2 | 17.04.0+ |
| 3.1 | 1.13.1+ |
| 3.0 | 1.13.0+ |
| 2.4 | 17.12.0+ |
| 2.3 | 17.06.0+ |
| 2.2 | 1.13.0+ |
| 2.1 | 1.12.0+ |
| 2.0 | 1.10.0+ |
| 1.0 | 1.9.1.+ |
基本上现在docker-ce的版本在18以上。所以,放心的使用3.7吧,如果用老版本docker的话,请自行选择。
大部分教程都会从 services 开始说,但我觉得 networks 、volumes 属于全局性的功能配置,会对 services 中的配置造成一定影响,所以先行说明。
除了 version 必须是第一个 networks、volumes、services 顺序没有严格要求。
networks & 网络配置
这个配置项对应了 docker network create [OPTIONS] 命令中的内容,可以用于创建私有网络。
networks 的配置,作用于整个docker全局,所以可以用于一键化项目部署。
在docker-compose起效时都会存在。手动 docker run --net 方式同样可以使用创建的网络
但同时,当docker-compose被终止时,创建的网络会被删除,引用网络的容器会造成异常
注意:networks必然会创建一个网络,所以名称不允许重复(包括已创建网络,指的是
name字段)
1 | networks: |
以上是我常用的配置方式,如果转换成docker命令的话就是:docker network create --driver bridge --subnet=100.0.0.0/16 net
这里特别需要注意
netName这个是关联名称,在services配置中需要用到,但也只是在yml配置中才有效docker环境中有效的是
name字段的内容建议把这两个用同一个名称便于管理,但(bridge、host、null、none)这几个名字属于保留字,不允许使用
netName:这是一个逻辑名称,在services配置中需要用到,在不指定name时网络名称会自动变为project_netNamename:真实网络名称,对应docker network –namedriver:网络类型,取值bridge(默认)、host、overlay详见docker网络配置,基本上小范围使用bridge就足够了
在使用docker stack时网络只能使用overlay模式
ipam:ip配置driver:作用未知config:网络配置subnet:Ipv4配置,网段及掩码,不能设置网关
networks配置下面还有很多的配置,但针对小范围使用来说,基本没用。
volumes & 挂载配置
这个配置对应了 docker volume create [OPTIONS] 命令中的内容,可以用于创建全局性的挂载目录或磁盘。
限制与
networks配置一致挂载的机制是通过类似ln的方式进行目录引用,在linux环境中类似mount效果,windows环境中类似共享连接配置
1 | volumes: |
services & 服务配置
这个配置对应了 docker run [OPTIONS] 命令中的内容,用于部署具体容器(包括打包)
在看具体配置说明前,要先明确两点
1、services下的内容,可以简单的理解成容器,但本质上是一种服务配置,即它可以用于多个容器的管理/分配或参与一个服务的整个生命周期(构建、运行、部署、销毁)
2、services下的内容,不存在运行的先后顺序,他们是并行执行的,即不存在svrName1启动后再启动svrName2,svrName1和svrName2是同时被启动
1 | services: |
这是一段标准的redis部署配置(depends_on除外)
所对应的docker命令为:docker run --net net --name redis -d -v /data/docker/redis:/data -p 6372:6372 redis:alpine redis-server --appendonly yes
svrName:与networks中一样,这里是个逻辑名字在不指定
container_name时容器名称会自动变为project_svrNamecontainer_name:真实容器名称对应了
docker run --name参数image:容器所属镜像如果本地不存在会尝试下载,与
docker images内容一致command:覆盖容器启动命令之所以redis举例是应为它是典型的,需要覆盖默认启动命令的例子。
支持字符串数组,或者直接的命令
restart:重启模式no默认设置,停止后不重启always总是重启on-failure遇到异常终止时重启unless-stopped遇到容器停止时重启
environment:容器带入的环境变量对应的有个
env_file的配置,可以通过引用外部文件形式读取内容1
2
3
4services:
svrName:
env_file:
- ./test.env- test.env
1
REDIS_PORT=6379
- test.env
networks:网络配置(这里引用的是networks中的netName)对应了
docker run --net参数网络配置还有中指定ip地址的方式,比较适用高安全级别的部署要求(比如严格控制容器间的访问情况)
1
2
3
4
5
6services:
svrName:
networks:
netName:
ipv4_address: 10.0.0.1
ipv6_address: 2001:3984:3989:10volumes:挂载配置(本地路径:容器路径)对应了
docker run -v参数挂载配置的本地路径部分,允许与根层的
volumes配置联合使用,引用volumeName即可1
2
3
4services:
svrName:
volumes:
- volumeName/test:/data以上效果,即引用根层的
volumeName下的test目录挂载至容器中的/data目录volumes配置时允许使用相对路径,相对路径以yml配置文件所在的目录为基准
ports:端口配置(容器端口:外部端口/协议类型)对应了
docker run -p参数depends_on:(这里引用的是services中的svrName)可以说
depends_on配置是docker-compose最重要的功能(个人觉得),它可以设置依赖项目,依赖项目未运行前,本项目处于等待状态。但这里要注意
depends_on中的项目运行顺序是没有先后顺序的我们可以通过依赖引用的方式达成顺序执行的目的,但这样会造成配置非常复杂
在services章节,我这里少写了很多配置,比如:links、expose、init、labels 诸如此类的,这些并不是无效,但在时间过程中作用有限,有兴趣的可以自行学习
services build、deploy & 服务配置 build、delploy
之前说过services不仅仅是个容器配置,而是参与了服务的整个生命周期,而这就依赖于 build、deploy 配置来实现。
这两个配置相对独立,分别对应了服务的构建(打包)、部署阶段
build & 构建打包
对应了
docker build [OPTIONS]命令build的所有配置在docker stack状态下是被忽略的!
1 | services: |
context:构建的工作目录,相对目录基于yml文件所在计算dockerfile:工作目录下的dockerfile文件名,允许使用其他名称image:镜像名称可指定tag,默认以laster为tag名称
args:构建带入参数在运行dockerfile时以字符串方式带入的参数
1
2
3ARG buildno
RUN echo "Build number: $buildno"
deploy & 部署
deploy是个特殊的配置,它只在docker stack状态下工作,docker-compose时被忽略
这里不得不说一下docker stack是什么。简单的说,它是一个被docker吸收为内部功能的py脚本。
常用于集群部署(在之后会单独讲下docker集群,主要是对这块知识我还没有学透),是docker生产环境下的一个非常好用的模式,天生带负载均衡。
我们可以简单的认为docker run用于开发环境/单机环境,docker stack用于生产环境/集群环境(分布式环境)
1 | services: |
deploy 必须指定一个image,因为docker stack会忽略build配置,所以必须制定一个存在的image作为内容
mode:节点模式global:每个节点只有一个容器
replicated:多个节点共用一个容器
replicas:容器数量,当mode=replicated时生效resources:配置cpu、内存资源限制资源配置需要非常谨慎,当容器尝试使用超过上限的资源时会触发系统级异常,并被管理器杀死。
因此当限制过低时,会造成容器陷入死循环状态。
limits:资源使用上限cpus:cpu使用情况,小数形式,0.5=50%memory:内存使用情况,单位 字节
reservations:保留资源,即在资源富余时仍划分限定资源给容器专属使用
restart_policy:配置如何重启会替换services中的restart配置
rollback_config:配置更新失败后如何回滚服务parallelism:一次回滚多少容器数,0=所有容器同时回滚此参数对应了
mode=replicated时会产生多个容器的状态delay:每个容器回滚之间的等待时间,单位 时间failure_action:回滚失败后的处理方式,默认pausecontinue:继续尝试pause:停止尝试
monitor:失败后检测持续时间,单位 时间monitor作用范围是容器启动失败之后,开始计算,启动成功则重置max_failure_ratio:回滚期间可以容忍的错误次数order:回滚期间的操作顺序stop-first:开始新的一个前停止start-first:首先启动类似建立一个顺序执行的回滚列表,初始内容为所有更新的命令,stop-first每次都放在列表第一个,start-first放在最后
update_config:配置如何更新服务常和
rollback_config联合使用parallelism:一次更新多少容器数,0=所有容器同时回滚此参数对应了
mode=replicated时会产生多个容器的状态delay:每个容器更新之间的等待时间,单位 时间failure_action:更新失败后的处理方式,默认pausecontinue:继续尝试pause:停止尝试rollback:回滚
monitor:失败后检测持续时间,单位 时间monitor作用范围是容器启动失败之后,开始计算,启动成功则重置max_failure_ratio:更新期间可以容忍的错误次数order:更新期间的操作顺序stop-first:开始新的一个前停止start-first:首先启动类似建立一个顺序执行的更新列表,初始内容是全部需要更新的命令,stop-first每次都放在列表第一个,start-first放在最后
update_config 与 rollback_config 常联合适用,独立对应 docker service 命令
它们的生效场景在于服务已经存在,而需要更新服务镜像的时候,因单独一个配置文件进行操作。
命令操作
docker-compose的命令非常丰富,这里只讲基本的几个
启动与卸载
之前我们讲了大段的YML配置,其实本质上就是docker命令的配置简化,通过 -f 参数进行指向,来加载配置。
注册:
docker-compose -f /data/docker/docker-compose.yml up -dup是注册命令,-d是静默方式运行(类似于docker run -d)这里要强调,运行结果会根据配置内容创建
网络/挂载/容器/服务,但这些在主动执行卸载命令前都是永久化的,因此如果单独通过命令方式加载服务,则需要特别注意docker当前运行的状态,避免造成重复操作。卸载:
docker-compose -f /data/docker/docker-compose.yml down执行卸载命令后,所有配置中的内容会被删除!!!
构建、启动、重启、停止
针对于已经执行过注册命令后,我们仍然会需要手动操作某些服务的情况。
重要:我们操作的所有内容是针对配置文件中的服务名(svrName),而非容器!!!
构建:
docker-compose -f /data/docker/docker-compose.yml build svrName启动:
docker-compose -f /data/docker/docker-compose.yml start svrName重启:
docker-compose -f /data/docker/docker-compose.yml restart svrName停止:
docker-compose -f /data/docker/docker-compose.yml stop svrName暂停:
docker-compose -f /data/docker/docker-compose.yml pause svrName取消暂停:
docker-compose -f /data/docker/docker-compose.yml unpause svrName
至于其他的指令,docker-compose也有支持,不过建议还是用docker本身的比较好
系统部署
开篇我们已经说过docker-compose只是一个命令行工具。因此要自动化使用,只有两种方式
把注册命令写入到启动脚本中,在开机自动运行。
当然,这种方式是我们应该放弃的,原因也很简单。你有个注册,那卸载呢?意外重启呢?
把docker-compose包装成一个服务。
建立服务脚本
1
2$ cd /etc/rc.d/init.d
$ vi ./docker-composedocker-compose 文件内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#!/bin/bash
# chkconfig: 2345 85 15
# description: docker-compose service
EXEC=/usr/local/bin/docker-compose
YML=/data/docker/docker-compose.yml
case $1 in
start)
$EXEC -f $YML up -d
;;
stop)
$EXEC -f $YML down
;;
restart)
$EXEC -f $YML restart nginx
;;
*)
echo "Usage: $0 (start|stop|restart)"
;;
esac这里说明下,我的部署服务中通过
depends_on配置让nginx在所有服务之后运行,因此可以把它看成一个总服务入口。
注册服务
1
2$ chmod +x ./docker-compose
$ chkconfig --add docker-compose启动服务
1
$ service start docker-compose
通过这样的方式,就可以完美的运行docker-compose了。
当然,如果使用docker stack方式进行集群部署的话,进行适当的脚本改造也可以满足功能
常用配置
这里提供下常用的几种工具配置。
docker-compose配置路径为 /data/docker/docker-compose.yml
docker工作路径为 /data/docker
global
1
2
3
4
5
6
7
8
9version: "3.7"
network:
net:
name: net
ipam:
driver: default
config:
- subnet: 100.0.0.0/16
services:redis
1
2
3
4
5
6
7
8
9redis:
container_name: redis
image: redis:alpine
command: redis-server --appendonly yes
restart: always
networks:
- net
volumes:
- /data/docker/redis:/datamongodb
1
2
3
4
5
6
7
8
9mongodb:
container_name: mongodb
image: mongo
command: mongod --directoryperdb
restart: always
networks:
- net
volumes:
- /data/docker/mongodb:/data/dbnginx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17nginx:
container_name: nginx
image: nginx:alpine
networks:
- net
volumes:
- /data/docker/nginx/nginx.conf:/etc/nginx/nginx.conf
- /data/docker/nginx/conf.d:/etc/nginx/conf.d
- /data/docker/nginx/log:/etc/nginx/log
- /data/docker/nginx/ssl:/ssl
- /data/docker/nginx/wwwroot:/wwwroot
ports:
- 80:80
- 443:443
depends_on:
- redis
- mongodb说明
volumes下面的ssl目录绑定,用于ssl证书的导入depends_on下面因放所有服务的列表
