Docker-Compose容器编排工具

TOC

认识Docker Compose

什么是Docker Compose?

Docker Compose是用于定义和运行多容器Docker应用程序的工具,可以通过YAML文件来配置应用程序需要的所有服务,在使用命令读取YAML文件配置启动对应的服务。
举例比喻:
如果Docker是"单个房间",那么Docker Compose就是"整栋房子的建筑图纸"。
如果Docker容器是"单个演员",那么Docker Compose就是"整部戏的导演脚本"。

为什么要使用Docker Compose?

Docker Compose的主要优势是可以简化容器化应用程序的开发、部署和管理。我们停止容器和docker之后再次使用命令启动容器这样过程过于繁琐,还需要记住很多选项参数。比起来docker compose更加方便管理。

Docker Compose的主要优势:

  • 1.简化控制:在一个YAML文件中定义和管理多容器应用程序,简化编排和复制。
  • 2.高效协作:可共享的YAML文件支持开发人员和运营人员之间的顺畅协作,改善工作流程和问题解决,从而提高整体效率。
  • 3.快速应用程序开发:Compose会缓存用于创建容器的配置。当您重启未更改的服务时,Compose会重用现有容器。重用容器意味着您可以快速更改环境。
  • 4.跨环境可移植性:Compose支持在Compose文件中使用变量。您可以使用这些变量来针对不同的环境或不同的用户自定义您的合成。

核心概念

Docker Compose的三大核心概念:

  • 服务(Service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例,比如:web服务器、数据库、工具应用等
  • 项目(Project):由一组关联的应用容器组成的一个完整业务单元,在docker-compose.yml文件中定义
  • 容器(Container):服务运行的实例

一、安装Docker Compose

下载安装并添加可执行权限

wget -O /usr/local/bin/docker-compose https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-linux-x86_64
chmod +x /usr/local/bin/docker-compose

查看安装版本

docker-compose --version

docker-compose模板版本对应docker版本的关系:

docker-compose版本 docker版本
3.8 19.03.0+
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+

温馨提示:从 Dockerv20.10.13版本之后,官方推荐的Compose v2已经作为docker compose的子命令插件存在,而不是单独的docker-compose可执行文件,命令使用docker compose即可。

二、开始使用Docker Compose

Docker Compose使用的三个步骤:
1.确认当前环境是否配置成功,比如:docker、docker-compose是否安装成功。
2.编写compose文件,定义服务和项目。
3.使用命令启动服务应用。

YAML格式

YAML编写格式(如下所示):

version: '3.8'   #指定docker-compose模板版本

services:        #添加该项目所需要的服务,可添加多个
    app1:        #服务名称
       ......    #服务的运行参数
       ......
    app2:
       ......
       ......

Compose允许用户通过一个单独的docker-compose.yaml或者docker-compose.yml模板文件(YAML格式)运行起来的一系列操作叫做一个项目(project),Compose每个项目里面跑起来的每个应用叫做一个服务(service)。
文件中首先定义version和services两个部分,services部分编写该项目中的服务,服务下面添加模板指令内容。

温馨提示:在较新的版本中,version字段已经弃用,可以不用配置version字段即可。

开始第一个简单例子

1.先创建一个app的工作目录

mkdir -p /data/app

2.在/data/app目录下编写一个简单的docker-compose.yaml文件实例

# /data/app/docker-compose.yaml
version: '3.8' 

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    ports:
      - 80:80

3.在/data/app目录下使用docker-compose命令启动

cd /data/app
docker-compose up -d

可以使用-f选项指定compose文件启动,如下所示:

docker-compose -f /data/app/docker-compose.yaml up -d

温馨提示:不指定compose文件,会自动寻找当前工作目录下的docker-compose.yaml或者docker-compose.yml文件进行加载使用,如果不存在该文件则会报错。

Docker Compose常用次级命令

up:启动服务,会自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器的一系列操作,链接的服务都将会被自动启动,除非已经处于运行状态
down:此命令将会停止 up 命令所启动的容器,并移除网络
exec:进入指定的容器
config:验证Compose文件语法格式是否正确
start:启动已经存在的服务容器
stop:停止已经处于运行状态的容器,但不删除它
restart:重启项目中的服务,默认重启所有
kill:杀掉服务
top:查看各个服务容器内运行的进程
ps:查看服务状态
rm:删除所有停止的服务
logs:查看日志

三、Docker Compose常用命令

项目管理命令

up命令

使用up命令来启动或者更新项目。
命令语法:docker-compose up【命令选项】
选项和参数:

  • -f:指定使用的compose模板文件,默认为当前目录下的docker-compose.yml文件,可以多次指定
  • -p:指定项目的名称,默认将使用所在目录名称作为项目名
  • -d:在后台运行服务容器

【示例】

docker-compose up -d

补充说明:
1.up命令会根据compose文件依次启动服务,创建网络、卷等操作。
2.up命令还可以用来更新服务,当你修改了compose文件,使用up命令会自动更新你修改了的内容,无论服务是否存在,会确保里面的内容是最新的状态。

down命令

使用down命令来停止删除项目。
命令语法:docker-compose down【命令选项】
选项和参数:

  • -v:删除所有命名的数据卷,这可能会导致数据丢失
  • –rmi:删除相关镜像
    • local:删除由compose构建的镜像
    • all:删除服务依赖的所有镜像。

执行down命令会做的操作:

  • 1.停止并删除容器:停止由当前docker-compose.yml管理的所有容器。
  • 2.删除网络:默认会删除compose自动创建的网络,不会删除external: true的外部网络,因为compose不管理它们。
  • 3.删除默认卷:如果加了-v--volumes选项,会一并删除容器挂载的匿名卷和项目里声明的卷。不会删除external卷(即external: true的卷)。

【示例】

# 停止项目
docker-compose down
# 停止项目并删除由compose构建的镜像
docker-compose down --rmi local

还可以指定某一个服务来停止,如下所示:

docker-compose down app1

温馨提示: 谨慎使用-v选项,会清除掉使用docker-compose.yml文件定义的所有容器、网络、卷(通过 -v 参数指定的数据卷)以及所有的网络桥接。

服务管理命令

命令语法:docker-compose【管理命令】【服务名】

start命令

用于启动之前已经创建但已停止的容器。
【示例】

docker-compose start app1

stop命令

用于停止已启动的compose应用。
【示例】

docker-compose stop app1

restart命令

重启已停止的compose应用。
【示例】

docker-compose restart app1

ps命令

用于查看compose应用中的各个容器的状态信息。
命令语法:docker-compose ps【服务名(可选)】
【示例】

# 查看所有的应用
docker-compose ps
# 查看指定应用
docker-compose ps app1

rm命令

命令语法:docker-compose rm【选项】【服务名】
选项和参数:

  • -f, --file FILE:指定使用的 Compose 配置文件,默认为docker-compose.yml
  • -v, --volumes:删除容器的同时删除由 docker-compose.yml 文件定义的卷(如果它们存在并且是容器专用的)
  • -a, --all:删除所有服务容器,而不仅仅是停止的容器。默认情况下,docker-compose rm 只删除停止的容器
  • –rmi TYPE:删除镜像。这个选项会删除构建镜像时产生的中间镜像和构建缓存。TYPE 可以是 all(删除所有镜像)、local(只删除本地镜像)或 built(只删除通过 docker-compose 构建的镜像)
  • -s, --stop:在删除容器之前先停止它们。这是默认行为,可以通过添加--no-stop标志来覆盖
  • –no-stop:在删除容器之前不停止它们(通常与 -f 一起使用)

【示例】

# 删除所有停止的容器
docker-compose rm
# 删除所有容器,不管状态
docker-compose rm -a
# 删除容器和卷
docker-compose rm -v
# 所有通过docker-compose构建且在本地存在的镜像
docker-compose rm --rmi local

logs命令

命令语法:docker-compose logs【选项】【服务名(可选)】
选项和参数:

  • –follow, -f:实时跟踪日志输出,类似于tail -f
  • –tail, -t “all”:显示日志的最后几行。默认值是all,也可以指定一个数字
  • –no-color:禁用颜色输出,使日志更适合重定向到文件或其他工具
  • –timestamps, -T:在日志输出中包含时间戳

【示例】

# 查看所有服务日志
docker-compose logs
# 查看指定服务日志
docker-compose logs app1
# 实时查看指定服务日志
docker-compose logs -f app1
# 查看指定服务日志最后10行
docker-compose logs --tail 10 app1
# 查看所有服务日志包含时间戳
docker-compose logs -T

四、Docker Compose模板指令

模板指令是写在docker-compose.yml文件中的,使用来为容器服务的,也就是用来定义该服务容器运行参数

镜像构建指令

build

用来将指定的Dockerfile打包成对应镜像,然后再运行该镜像

build: .
#或者用context指定构建目录
build: 
      context: /docker/app  #Dockerfile的目录
      #可以是git仓库的url也可以是绝对/相对路径
      dockerfile: Dockerfile #文件名

args

添加构建镜像的参数,环境变量只能在构建过程中访问
编写格式如下:

build:
  context: .
  args:
    - url="192.168.31.101"
    - password="123456"

常用模板指令

image

指定为镜像名称或镜像ID,如果镜像在本地不存在,Compose则会拉取该镜像
编写格式如下:

image: nginx
image: ubuntu:20.04
image: a4bc65fd

container_name

指定一个自定义容器名称
编写格式如下:

container_name: my-nginx

ports

暴露端口信息,使用宿主端口:容器端HOST:CONTAINER格式,或者仅仅指定容器的端口,宿主将会随机选择端口

注意:因为在容器中可能会暴露多个端口,端口的值可以为多个,所以用数组的格式展现。

编写格式如下:

ports:
  - "80"
  - "80-88"
  - "80:80"
  - "80-88:8080-8088"
  - "8888:80"
  - "127.0.0.1:80:80"
  - "127.0.0.1:80-88:8080-8088"
  - "8080:8080/udp"

温馨提示:当使用HOST:CONTAINER格式来映射端口时,如果你使用的容器端口小于60你可能会得到错误得结果,因为YAML将会解析xx:yy这种数字格式为60进制,所以建议采用字符串格式。

environment

添加环境变量
编写格式如下:

environment:
  JAVA_HOME: /usr/local/jdk/bin
  JENKINS_HOME: /var/lib/jenkins
  test: 'linux'
environment:
  - JAVA_HOME=/usr/local/jdk/bin
  - JENKINS_HOME=/var/lib/jenkins
  - test='linux'

env_file

从一个文件中加入环境变量,带入到容器中去,在容器中可以用printenv打印该环境变量
编写格式如下:

env_file : .env
env_file:
 - ./apps/new.env
 - /test/secrets.env

command

使用 command 可以覆盖容器启动后默认执行的命令。
编写格式如下:

command: /usr/local/nginx/sbin/nginx -g daemon off
command: ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]

depends_on

解决容器的依赖、启动先后的问题。
编写格式如下:

version: "3.8"
services:
  web:
    image: nginx
    depends_on: #依赖以下服务,先启动这两个服务再启动该服务
      - mysql #注意此处是服务名,而不是容器名
      - redis 
  redis:
    image: redis
  mysql:
    image: mysql

restart

配置服务的重启策略,直接就是docker的重启策略。
重启策略有:

  • no(默认):不自动重启
  • always:无论退出状态码是什么,总是会自动重启容器
  • unless-stopped:和always类似,但如果容器被手动docker stop,那么Docker服务重启后不会再启动它
  • on-failure:仅当容器因错误退出(退出码非 0)时才会重启,可选的max-retries表示最多重启几次,超过次数就不再重启

编写格式如下:

restart: aways

healthcheck

通过命令检查容器是否健康运行。需要给每个service都添加。
编写格式如下:

healthcheck:
  test: [ "CMD", "curl", "-f", “http://localhost"]
  interval: 1m30s  #前多少秒不检查
  timeout: 10s     #等待时间
  sretries: 3      #尝试三次

sysctls

配置容器内核参数,下面两种写法都可以。
编写格式如下:

sysctls :
  net.core.somaxconn: 1024
  net.ipv4.tcp_syncookies: 0

sysctls :
  - net.core.somaxconn=1024
  - net.ipv4.tcp_syncookies=0

ulimits

指定容器的ulimits限制值
编写格式如下:

ulimits:
  nproc: 65535 #修改容器内操作系统的最大进程数,可根据服务的要求修改
  nofile:
    soft: 2000
    hard: 4000

数据持久化:volumes

卷挂载路径设置,可以设置宿主机路径(HOST:CONTAINER)
访问权限设置:
加上访问模(HOST:CONTAINER:ro),挂载数据卷的默认权限是读写(rw),可以通过ro指定为只读。

注意:因为挂在卷在容器中也可能是多个挂载,所以用数组的格式展现。

编写格式如下:

volumes:
  - /app  #只需指定一个路径,让引擎创建一个卷
  - /test:/app/data  # 指定绝对路径映射,需要事先创建
  - /test:/app/data:ro  # 添加只读权限

补充说明:命名卷,需要额外的声明,其创建的卷名为:项目名+datavolume,如果就需要使用自定义卷名,需要添加external,但是启动时事先需要先创建卷名,比较麻烦。

创建定义卷

docker volume create mydata

编写配置文件

services:
  app:
    volumes:
      - mydata:/app/data

volumes:    #声明上面的自动卷,如果是绝对路径或相对路径,不用声明
  mydata:
    external: true  #确定使用自定义卷名

网络配置:networks

如果在compose文件中没有networks的配置,系统会默认新建一个匿名_default的网络

普通写法

显式定义非default网络,需要每个服务加入指定networks配置,有两种写法:
第一种写法:compose内部和外部都使用mynet网络

networks:
  mynet:
    external: true

第二种写法:compose内部还是使用mynet网络,外部实际使用名为shared的网络

networks:
  mynet:
    name: shared
    external: true

【示例】

services:
  app:
    image: nginx
    networks:
      - mynet

networks:
  mynet:
    external: true

这里mynet是Compose里的“别名”,因为没有写name,所以Compose会认为外部网络的名字就是mynet,宿主机必须存在一个名叫mynet的网络。

指定外部网络作为default

改方式会把默认网络替换成一个已经存在的外部网络,每个服务都不需要加入指定networks配置,会默认加入到该网络中。
写法格式:

networks:
  default:
    name: mynet
    external: true

自动创建不同类别的网络

上面的都需要网络本身就存在的情况下使用才能有效,是因为一个配置项:external: true
external: true的作用:不创建新的网络,使用宿主机上已经存在的一个网络,如果没有该网络则会报错。
我们只需要将external: true配置删除,替换成driver: 网络类型即可,再执行命令docker-compose up -d的时候就会自动创建该类型的网络,执行docker-compose down的时候就会删除该网络。

网络类型对比:

网络类型 是否独立 IP 容器间通信 与外部通信方式 典型用途
bridge ✅(同网络) 端口映射 -p 默认,单机服务
host ❌(用宿主机IP) ✅(和宿主机同一网) 直接使用宿主机网络 高性能网络
none 无(需手动配置) 安全隔离
overlay ✅(跨主机) Swarm 内置发现 集群/微服务
macvlan ✅(真实IP) ✅(局域网互通) 容器直接加入物理网段 需要真实 IP 的应用
自定义 bridge ✅(DNS 名称解析) 端口映射 -p 多容器应用(推荐)

bridge(默认)

默认类型,容器会连接到一个虚拟网桥(bridge)上,比如 docker0,如果不写networks,compose会自动给项目建一个bridge网络,比如:<项目名>_default
【示例】

$ docker compose up -d
[+] Running 2/2
 ✔ Network devops_default  Created     0.0s
 ✔ Container nginx         Started     0.2s

自定义bridge

自己在compose文件里建一个网络,容器之间通过服务名互联,不会和系统默认的bridge混淆。
【示例】

networks:
  mynet:
    driver: bridge

host

容器直接共享宿主机的网络栈,访问localhost就是访问宿主机。
【示例】

services:
  web:
    image: nginx
    network_mode: host

none

容器只有lo,没有网络配置,需要你自己配置或用docker network connect
【示例】

services:
  db:
    image: mysql
    network_mode: none

overlay

如果你在Swarm模式下用docker stack deploy,可以用overlay网络,容器能跨主机互联。
【示例】

networks:
  front:
    driver: overlay

docker-compose文件实例

以下以minio部署运行为例:

version: '3.8'

services:
  minio:
    image: minio/minio:latest
    container_name: minio
    labels:
      - traefik.enable=true
      - traefik.http.routers.pan.rule=Host(`minio.example.com`)
      - traefik.http.routers.pan.entrypoints=websecure
      - traefik.http.routers.pan.tls=true
      - traefik.http.routers.pan.tls.certresolver=alidns
      - traefik.http.routers.pan.tls.domains[0].main=*.example.com
      - traefik.http.routers.pan.service=pan-service
      - traefik.http.services.pan-service.loadbalancer.server.port=9000
    restart: unless-stopped
    environment:
      - "MINIO_ROOT_USER=minio"
      - "MINIO_ROOT_PASSWORD=minio"
    command: server /data --address :9000 --console-address :9001
    ports:
      - 9000:9000
      - 9001:9001
    volumes:
      - ./minio/data:/data
      - ./minio/config:/root/.minio
    healthcheck:
      test: [ "CMD", "curl", "-f", "http://localhost:9000/minio/health/live" ]
      interval: 30s
      timeout: 20s
      retries: 3

networks:
  default:
    name: minio
    driver: bridge