$ ls ~yifei/notes/

Docker Compose 教程

Posted on:

Last modified:

2022-04 更新:k8s 已经取得了绝对优势,我现在实在找不到再使用 docker-compose 的理由了,在 本地搭建测试环境可以参考这篇:使用 multipass+autok3s 快速搭建本地 k3s 集群

------正文的分隔线------

使用 docker run 运行容器的时候经常需要加很多的参数,每次都输入这么多参数很容易出错。另外 我们经常需要同时运行若干个容器来构成一个服务,此时还是涉及到网络的联通等操作。

docker-compose 可以把 docker 执行时的参数写成 yaml 文件,运行的时候只需要

docker-compose up -d

一下就可以了。

话不多说,下面通过一个例子来学习一下 docker-compose.yml 文件的语法。

version: "3"  # 版本号,目前最新版是 3,不同版本的语法是不太兼容的
services:  # 定义的服务,注意是一个字典
  web:
    build: .
    environment:  # 定义环境变量
      - ENV=prod
    depends_on:  # 该服务依赖下面的两个服务,也就是指定了
      - db
      - redis
    command: python app.py
  redis:
    image: redis  # 使用 dockerhub 上的 redis 镜像,这里也可以填自己的私有地址
    network_mode: host|none
    ports:
     - "3000"
     - "3000-3005"
     - "8000:8000"
     - "9090-9091:8080-8081"
     - "49100:22"
     - "127.0.0.1:8001:8001"
     - "127.0.0.1:5000-5010:5000-5010"
     - "6060:6060/udp"
  db:
    image: postgres
    volumes:
      - "/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock"
      - "dbdata:/var/lib/postgresql/data"  # 挂载的库,为了和 yaml 语法兼容,必须用引号

volumes:  # 定义的卷
  dbdata:
  1. version 指定了当前 docker-compose 文件的版本
  2. services 服务列表,是一个 "服务名:配置" 的字典。这里定义了三个服务: web/redis/db
  3. build,docker build 命令的参数,用来指定需要构建的 dockerfile
  4. image,如果镜像不需要自己构建,而是使用 docker hub 上的基础镜像,可以直接使用 image 指令
  5. depends_on 指定当前的服务依赖的服务,从而确定启动顺序
  6. ports 开放的端口的数组,有三种形式:
    1. "3000" 容器中开放的端口
    2. "3000:3000" 开放容器中的端口到宿主机,默认绑定到 0.0.0.0
    3. "127.0.0.1:3000:3000" 开放容器中的端口到宿主机并绑定 IP
  7. environment 环境变量
  8. volumes 挂载的卷,可以使用 named volume 或者是挂载目录,建议不要使用匿名卷。如果使用 named volume,必须在 volumes 下声明

运行服务

docker-compose 有以下 3 个常用命令:

  1. docker-compose up [-d] [SERVICE] 启动服务,-d 表示以 daemon 形式后台运行,并且会在 重启后自动启动。
  2. docker-compose run
  3. docker-compose stop

网络

默认情况下,一个文件中的所有服务会绑定到同一个网络,互相可以通过服务的名字作为域名访问。 不过要注意,依赖其他容器提供服务的容器要使用 depends_on 表明依赖关系,否则可能在启动 过程中发现找不到服务,进而启动失败。

横向伸缩拓展

docker-compose 提供了 --scale 选项来实现横向伸缩。

--scale SERVICE=NUM        Scale SERVICE to NUM instances. Overrides the
                           `scale` setting in the Compose file if present.

但是前提是不能在宿主机上绑定了同一个端口。比如说这样是不行的:

services:
  web:
    ports:
      - "5000:5000"

那么改成 ports: ["5000"] 行吗?实际上这还是在宿主机上暴露了一个随机端口,其实没有必要。 在接下来的方法中,我们使用 expose 指令。

我们使用 expose 指令,只在容器网络内部暴露端口而不绑定宿主机任何端口,只有 container 网络内部才能访问。另外需要注意的是 Dockerfile 中的 EXPOSE 命令现在已经变成空指令了, 加不加没有任何区别。

expose:
  - "5000"

那么这时候怎么在外部访问服务呢?我们可以加一个 load balancer, 比如 nginx

user nginx;

events {
  worker_connections   1000;
}
http {
  server {
    listen 4000;
    location / {
      proxy_pass http://web:5000;
    }
  }
}

docker-compose.yaml

services:
  web:
    expose:
      - "5000"
  nginx:
    image: nginx:latest
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - web
    ports:
      - "4000:4000"

这样就可以起多个副本啦:

docker-compose up --scale web=5

另外,还可以使用这两个镜像:

  1. https://github.com/nginx-proxy/nginx-proxy
  2. https://nginxproxymanager.com/screenshots/

几个神坑

目录重命名之后

docker-compose 中默认的 project name 就是目录的名字,而 volume 的名字会添加上 project 的 前缀,也就是 <project>_<volume>. 这就带来了一个问题,当我们把 docker-compose.yml 移动 路径的时候,原来的 volume name 就和新的对应不上了,简直神坑。

docker volume create --name <new_volume>
docker run --rm -it \
  -v <old_volume>:/from \
  -v <new_volume>:/to \
  alpine ash -c "cd /from ; cp -av . /to"
docker volume rm <old_volume>

所以,最好使用 --project-name 来指定项目的名字,而不要让 docker-compose 自己生成。

挂载当前目录的文件

如果要挂载目录或者文件到 docker 中的话,默认是以 docker-compose.yml 文件的路径为依据的, 简直奇葩。

volumes:
  - ./deploy/prom.yml:/etc/prometheus/prometheus.yml

要改成 $PWD 才以运行命令的目录为根目录查找文件:

volumes:
  - $PWD/deploy/prom.yml:/etc/prometheus/prometheus.yml

参考

  1. https://github.com/moby/moby/issues/31154
  2. https://stackoverflow.com/questions/50630932/rename-docker-compose-project-without-deleting-volumes
  3. https://pspdfkit.com/blog/2018/how-to-use-docker-compose-to-run-multiple-instances-of-a-service-in-development/
  4. https://stackoverflow.com/questions/40801772/what-is-the-difference-between-docker-compose-ports-vs-expose
  5. https://docs.docker.com/compose/networking/
WeChat Qr Code

© 2016-2022 Yifei Kong. Powered by ynotes

All contents are under the CC-BY-NC-SA license, if not otherwise specified.

Opinions expressed here are solely my own and do not express the views or opinions of my employer.

友情链接: MySQL 教程站