Docker-compose
Overview | Docker Documentation
Docker-compose介绍
Docker-Compose将所管理的容器分为三层,分别是:
- 工程(project)
- 服务(service)
- 容器(container)。
Docker-Compose运行目录下的所有文件(docker-compose.yml,extends文件或环境变量文件等)组成一个工程,若无特殊指定工程名即为当前目录名。
一个工程当中可包含多个服务,每个服务中定义了容器运行的镜像,参数,依赖。一个服务当中可包括多个容器实例,Docker-Compose并没有解决负载均衡的问题,因此需要借助其它工具实现服务发现及负载均衡。 Docker-Compose的工程配置文件默认为docker-compose.yml,可通过环境变量COMPOSE_FILE或-f参数自定义配置文件,其定义了多个有依赖关系的服务及每个服务运行的容器。 使用一个Dockerfile模板文件,可以让用户很方便的定义一个单独的应用容器。在工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个Web项目,除了Web服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等。 Compose允许用户通过一个单独的docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。 Docker-Compose项目由Python编写,调用Docker服务提供的API来对容器进行管理。因此,只要所操作的平台支持Docker API,就可以在其上利用Compose来进行编排管理。
Docker-compose模板文件
Compose允许用户通过一个docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。 Compose模板文件是一个定义服务、网络和卷的YAML文件。Compose模板文件默认路径是当前目录下的docker-compose.yml,可以使用.yml或.yaml作为文件扩展名。 Docker-Compose标准模板文件应该包含version、services、networks 三大部分,最关键的是services和networks两个部分。
我在这里写几个常用的,其它的看docker中文文档 docker官方文档
Docker-compose可以通过服务名去访问其它的服务,而服务的IP地址不是通过/etc/hosts文件解析的,是通过
先看一个示例
在给service命名的时候,要尽可能的去遵守域名的命令规范,或者在设置容器的
hostname的时候,也要遵循,有些软件在使用域名或者host解析的时候会特别严格,如果不写对,可能会出现问题,导致无法启动,或者无法解析到正确的容器上面例如:
nacos我在部署的时候使用服务名,无法启动,因为service的name为nacos_1,这是不符合域名规范的,在nacos_2去解析nacos_1时,就无法被解析,无法获得nacos_2的ip,所以启动失败
version: "3"
services:
redis:
image: redis:alpine #选择这个serveice的image为redis:alpine
ports:
- "6379" #将container的这个端口暴露出去,主机的端口为随机端口
networks:
- frontend #接入这个网络,这里是一个列表,可以接入多个网络,同时Container中也会有多张网卡
#网络需要在下面定义
db:
image: postgres:9.4
volumes:
#将卷db-data映射到/var/lib/postgresql/data中,需要在下面定义这个volume
- db-data:/var/lib/postgresql/data
networks:
- backend
vote:
image: dockersamples/examplevotingapp_vote:before
ports:
- "5000:80" #将宿主机的5000端口映射到Container中的80上,可以指定映射带主机的那个IP上
networks:
- frontend
depends_on:
- redis #需要redis这个service起来后,他才会起来
result:
image: dockersamples/examplevotingapp_result:before
ports:
- "5001:80"
networks:
- backend
depends_on:
- db
worker:
image: dockersamples/examplevotingapp_worker
networks: #同时接入两个网络
- frontend
- backend
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8080:8080"
stop_grace_period: 1m30s
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
##将本机的/var/run/docker.sock映射到Container中
#定义两个网络和一个容器卷,这个时候,如果主机中有相同的卷,他不会去使用,
networks:
frontend:
backend:
volumes:
db-data:Build
指定
Dockerfile所在文件夹的路径(可以是绝对路径,或者相对 docker-compose.yml 文件的路径)。Compose将会利用它自动构建这个镜像,然后使用这个镜像。
version: '3'
services:
webapp:
build: ./dir你也可以使用
context指令指定Dockerfile所在文件夹的路径。使用
dockerfile指令指定Dockerfile文件名。使用
arg指令指定构建镜像时的变量。
version: '3'
services:
webapp:
build:
context: ./dir
dockerfile: Dockerfile-alternate
args:
buildno: 1使用
cache_from指定构建镜像的缓存
build:
context: .
cache_from:
- alpine:latest
- corp/web_app:3.14Command
覆盖容器启动后默认执行的命令。
command: echo "hello world"ports
暴露端口信息。
使用宿主端口:容器端口
(HOST:CONTAINER)格式,或者仅仅指定容器的端口(宿主将会随机选择端口)都可以。也可以指定宿主机的IP,用这种方式:HOSTIP:HOST_PORT:CONTAINER_PORTshellports: - "3000" - "8000:8000" - "49100:22" - "127.0.0.1:8001:8001"
volumes
数据卷所挂载路径设置。可以设置为宿主机路径(
HOST:CONTAINER)或者数据卷名称(VOLUME:CONTAINER),并且可以设置访问模式 (HOST:CONTAINER:ro(或者rw))shellversion: "3" services: web: image: nginx ports: - "80:80" volumes: - html:/usr/share/nginx/html/:ro volumes: html:这样设置会创建在外面创建[project_name]_[volumes] 的volumes
shell[root@docker ~]# docker volume ls DRIVER VOLUME NAME local test_html在volumes下加一个参数
external: trueshellversion: "3" services: web: image: nginx ports: - "80:80" volumes: - html:/usr/share/nginx/html/:ro volumes: html: external: true这样就可以使用外部的volumes
networks
配置容器的网络
shellversion: "3" services: some-service: networks: - some-network - other-network networks: some-network: other-network:这样设置会重新创建网络,
shell[root@docker ~/test]# docker network ls NETWORK ID NAME DRIVER SCOPE eef5d1484448 bridge bridge local 5987468e9e42 host host local e71fb6d1afcb none null local 9553e9f69faf test_test-network-1 bridge local 22e2620258fb test_test-network-2 bridge local需要向上面的
volumes选项下加上external:trueshell[root@docker ~/test]# cat docker-compose.yaml version: "3" services: test: image: busybox networks: - test-network-1 - test-network-2 command: ip addr show networks: test-network-1: external: true test-network-2: external: true执行
docker-compose up后,会报错shell[root@docker ~/test]# docker-compose up ERROR: Network test-network-1 declared as external, but could not be found. Please create the network manually using `docker network create test-network-1` and try again.未发现网络,需要我们手动在外部创建网络
shell[root@docker ~/test]# docker network create test-network-1 a81bbe0595c12466c07e0e473f3a40b09af072c333f44835f76c116dea9dbcf7 [root@docker ~/test]# docker network create test-network-2 6df9082b8908a14f6885f0db01f6c1e08c6a9ca9c82fd9662f8477489b784966再次执行
shell[root@docker ~/test]# docker-compose up Recreating test_test_1 ... done Attaching to test_test_1 test_1 | 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 test_1 | link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 test_1 | inet 127.0.0.1/8 scope host lo test_1 | valid_lft forever preferred_lft forever test_1 | 25: eth1@if26: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue test_1 | link/ether 02:42:ac:17:00:02 brd ff:ff:ff:ff:ff:ff test_1 | inet 172.23.0.2/16 brd 172.23.255.255 scope global eth1 test_1 | valid_lft forever preferred_lft forever test_1 | 27: eth0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue test_1 | link/ether 02:42:ac:16:00:02 brd ff:ff:ff:ff:ff:ff test_1 | inet 172.22.0.2/16 brd 172.22.255.255 scope global eth0 test_1 | valid_lft forever preferred_lft forever test_test_1 exited with code 0
environment
设置环境变量。你可以使用数组或字典两种格式。
只给定名称的变量会自动获取运行 Compose 主机上对应变量的值,可以用来防止泄露不必要的数据。
environment有两种定义方法第一种
shellversion: "3" services: test: image: busybox environment: TEST_ENV: test command: env第二种
shell[root@docker ~/test]# cat docker-compose.yaml version: "3" services: test: image: busybox environment: - TEST_ENV=test command: env用列表的方式和字典的方式:
- 列表需要加
=,例如: 上面的- TEST_ENV=test- 字典用应该用
:,例如:上面的TEST_ENV: test一下docker-compose文件,在
command无法获取环境变量,shellversion: "3" services: test: image: busybox environment: - TEST_ENV=test command: echo $TEST_ENV情况如下:
shell[root@docker ~/test]# docker-compose up WARNING: The TEST_ENV variable is not set. Defaulting to a blank string. Recreating test_test_1 ... done Attaching to test_test_1 test_1 | test_test_1 exited with code 0在command无法获取环境变量
猜测
我在使用
docker-compose config查看配置文件时,发现command在environment上shell[root@docker ~/test]# docker-compose config WARNING: The TEST_ENV variable is not set. Defaulting to a blank string. services: test: command: 'echo ' environment: TEST_ENV: test image: busybox version: '3.0'在执行之前已经被解析,我在宿主机定义一个
TEST_ENV,也是无法无法获取,说明,这个变量不是从宿主机获取的,只能是在原来的image中获取的,我编写一个Dockerfile,加入了一个环境变量,在次执行,还是无法获取变量,又将command执行的命令改变为echo $PATH,发现docker-compose config的输出不为空了,我有理由怀疑是从环境变量中获取的变量,我将TEST_ENV设置为环境变量,最终可以获取到了结论
在
docker-compose.yaml中的command使用变量时,会先直接从宿主机的环境变量直接获取,不会将变量带到Container再解析如果变量名称或者值中用到
true|false,yes|no等表达布尔 (opens new window)含义的词汇,最好放到引号里,避免 YAML 自动解析某些内容为对应的布尔语义。这些特定词汇,包括shelly|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF
env_file
从文件中获取环境变量,可以为单独的文件路径或列表。
如果通过
docker-compose -f FILE方式来指定 Compose 模板文件,则env_file中变量的路径会基于模板文件路径。如果有变量名称与
environment指令冲突,则按照惯例,以后者为准。shell[root@docker ~/test]# cat docker-compose.yaml version: "3.4" services: test: image: busybox env_file: .env command: env restart: alwaysshell[root@docker ~/test]# docker-compose up Recreating test_test_1 ... done Attaching to test_test_1 test_1 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin test_1 | HOSTNAME=f5b981a374fd test_1 | TEST_ENV=env test_1 | HOME=/root支持以
#开头的注释
extra_hosts
类似 Docker 中的
--add-host参数,指定额外的 host 名称映射信息。shellextra_hosts: - "googledns:8.8.8.8" - "dockerhub:52.1.157.61"会在启动后的服务容器中
/etc/hosts文件中添加如下两条条目。shell8.8.8.8 googledns 52.1.157.61 dockerhub
healthcheck
通过命令检查容器是否健康运行。
shellhealthcheck: test: ["CMD", "curl", "-f", "http://localhost"] interval: 1m30s timeout: 10s retries: 3
test必须是一个列表或者一串字符串,如果是一个列表,第一个选项必须是NONE,CMD,CMD-SHELL,如果是一个字符串,它等于是CMD-shell用来健康检查的命令需要在镜像中有,不然会直接
unhealthy
Container状态为unhealthy,不会退出或者重启
depends_on
解决容器的依赖、启动先后的问题。以下例子中会先启动
redisdb再启动webshellversion: '3' services: web: build: . depends_on: - db - redis redis: image: redis db: image: postgres注意:
web服务不会等待redisdb「完全启动」之后才启动。
container_name
指定容器名称。默认将会使用
项目名称_服务名称_序号这样的格式。shellcontainer_name: docker-web-container注意: 指定容器名称后,该服务将无法进行扩展(scale),因为 Docker 不允许多个容器具有相同的名称。