docker-compose之环境配置以及服务编排文件的使用讲解

柴星津
2023-12-01

本文主要讲解以下两块内容:环境配置的作用及常规使用、服务配置文件的解析以及常规使用

必备知识:

在讲解服务配置文件和环境配置文件之前,首先要对docker以及编排工具compose有一定的了解,然后,才能结合服务配置文件编排整个项目的服务以及其依赖关系等。

环境变量:在容器中生效的全局变量值

环境配置文件:可以替换compose服务配置文件中的属性变量。compose默认读取环境配置文件为“.env”,也可以通过--env-file指定相应的配置文件

compose file:服务编排文件,主要定义了一系列服务的配置信息以及依赖等,主要包括:端口、镜像、环境信息、依赖关系等

注意:此处的环境变量和.env类型的环境配置文件是两个概念。环境变量主要是在compose部署启动服务容器后,在容器中生效的变量;而.env中配置的变量值,主要是在定义服务配置文件时,去替换相应的属性。二者的作用范围不同,前者是服务启动后,在容器中作用的环境变量,后者是为了方便定义服务配置文件而构建变量文件。

在Compose文件中替换环境变量(.env)

在编辑Compose file时,可以通过环境变量的形式来填充变量的值

web:
  image: "webapp:${TAG}"

那么如何传入这些环境变量呢?主要是通过创建.env配置文件并定义相关属性,然后通过--env-file来指定我们定义的.env配置文件。

.env使用介绍

1.创建项目目录

mkdir env_test
cd env_test

2.创建编排配置文件docker-compose.yml

vi docker-compose.yml

#内容如下
services:
  webapp:
    image: '${image}:${tag}'

在该配置文件中,我们将image镜像使用${image}:${tag}替换成对应的变量值

3.创建.env

vi .env

#内容如下
image=training/webapp
tag=latest

注意:compose默认会从当前项目路径加载.env配置文件,如果.env在其他目录的话,需要通过--env-file=/path/.env的方式去加载;同样的,如果配置文件使用其他命名方式的话,也需要使用--env-file=/path/.env_rename来加载指定配置文件

4.通过docker-compose config来校验编排配置文件

#默认读取当前项目路径的.env
[shuchang@docker01 env_test]$ docker-compose config
services:
  webapp:
    image: training/webapp:latest
version: '3.9'

#读取指定配置文件.env_case2
#.env_case2内容如下
image=test
tag=1.1

[shuchang@docker01 env_test]$ docker-compose --env-file .env_case2 config
services:
  webapp:
    image: test:1.1
version: '3.9'

可以看到,我们在环境配置文件中定义的变量会作为environment配置项的内容加载到编排配置文件中;同时,通过--env-file指定环境配置文件时,同样能实现变量的赋值替换。

#在docker-compose.yml中追加ports配置项
services:
  webapp:
    image: '${image}:${tag}'
    ports:
      -  '${host_port}:${container_port}'

#并在.env中配置对应的变量
host_port=8080
container_port=80

#由于.env_case2中并未定义两个变量,会抛出异常
[shuchang@docker01 env_test]$ docker-compose --env-file .env_case2 config
WARNING: The host_port variable is not set. Defaulting to a blank string.
WARNING: The container_port variable is not set. Defaulting to a blank string.
ERROR: The Compose file './docker-compose.yml' is invalid because:
services.webapp.ports contains an invalid type, it should be a number, or an object

#去掉docker-compose.yml中的ports配置项后
[shuchang@docker01 env_test]$ docker-compose --env-file .env_case2 config
services:
  webapp:
    image: test:1.1
version: '3.9'

在容器中使用环境变量

容器中设置变量,主要是通过在docker-compose.yml中对应的服务下配置environment项,效果等同于docker-compose run -e variable=value

web:
  environment:
    - DEBUG=1

同时,还可以在配置env_file配置项,从指定的配置文件中加载环境变量

web:
  env_file:
    - web-variables.env

 首先,我们配置docker-compose.yml,在webapp服务下配置env_file配置项,加载文件中的环境变量到webapp服务容器内

vi docker-compose.yml
#配置内容如下
services:
  webapp:
    image: '${image}:${tag}'
    env_file:
      - './webapp.env'

 然后定义配置文件webapp.env

vi webapp.env
#内容如下
image=training/webapp
tag=latest
host_port=8080
container_port=80

最后通过docker-compose config来检验配置并输出,可以看到在webapp.env中定义的属性都合并到编排文件的environment配置项中了

[shuchang@docker01 env_test]$ docker-compose config
services:
  webapp:
    environment:
      container_port: '80'
      host_port: '8080'
      image: training/webapp
      tag: latest
    image: training/webapp:latest
version: '3.9'

Compose file解析及使用

compose file作为compose进行服务编排部署的配置文件,在整个过程中起到了决定性的作用。它可以定义服务的一些基本信息,例如:端口、镜像、数据卷等等;同时,还能声明服务之间的依赖关系,决定服务的启动顺序等。因此,了解compose file的常规配置项是十分有必要的!

compose 默认是直接加载docker-compose.yml作为服务的配置文件的,如需指定其他配置文件,需要通过-f /path/docker-compose-customerized.yml来加载配置文件。

Compose file配置项解析

https://blog.csdn.net/weixin_43762303/article/details/123397714

 profiles配置规划服务

配置文件允许通过profiles配置项选择性地启用服务来针对各种用途和环境调整 Compose 应用程序模型。

version: "3.9"
services:
  frontend:
    image: frontend
    profiles: ["frontend"]

  phpmyadmin:
    image: phpmyadmin
    depends_on:
      - db
    profiles:
      - debug

  backend:
    image: ibmcom/backend

  db:
    image: mysql

当前服务配置文件中,frontend和phpmyadmin分别绑定了profiles['frontend']和profiles['debug'];

没有profiles属性的服务将始终启用,即在这种情况下运行docker-compose up只会启动backenddb如需启动上述两个服务,需要通过--profiles指定相应配置

[shuchang@docker01 profiles_example]$ docker-compose up
Starting profiles_example_db_1      ... done
Starting profiles_example_backend_1 ... done
Attaching to profiles_example_db_1, profiles_example_backend_1
db_1          | 2022-03-10 09:48:30+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.28-1debian10 started.
db_1          | 2022-03-10 09:48:31+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
db_1          | 2022-03-10 09:48:31+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.28-1debian10 started.
db_1          | 2022-03-10 09:48:31+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified
db_1          |     You need to specify one of the following:
db_1          |     - MYSQL_ROOT_PASSWORD
db_1          |     - MYSQL_ALLOW_EMPTY_PASSWORD
db_1          |     - MYSQL_RANDOM_ROOT_PASSWORD
backend_1     | App listening on port 3001!

[shuchang@docker01 profiles_example]$ docker-compose --profile debug up
Starting profiles_example_db_1      ... done
Starting profiles_example_backend_1 ... done
Starting profiles_example_phpmyadmin_1 ... done
Attaching to profiles_example_backend_1, profiles_example_db_1, profiles_example_phpmyadmin_1
backend_1     | App listening on port 3001!
db_1          | 2022-03-10 09:57:49+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.28-1debian10 started.
db_1          | 2022-03-10 09:57:49+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
db_1          | 2022-03-10 09:57:49+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.28-1debian10 started.
db_1          | 2022-03-10 09:57:49+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified
db_1          |     You need to specify one of the following:
db_1          |     - MYSQL_ROOT_PASSWORD
db_1          |     - MYSQL_ALLOW_EMPTY_PASSWORD
db_1          |     - MYSQL_RANDOM_ROOT_PASSWORD
profiles_example_db_1 exited with code 1
phpmyadmin_1  | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.22.0.4. Set the 'ServerName' directive globally to suppress this message
phpmyadmin_1  | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.22.0.4. Set the 'ServerName' directive globally to suppress this message
phpmyadmin_1  | [Thu Mar 10 09:57:50.215499 2022] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.52 (Debian) PHP/8.0.16 configured -- resuming normal operations
phpmyadmin_1  | [Thu Mar 10 09:57:50.216222 2022] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'

因此,通过在compose file中配置profiles属性时,可以配置在多种环境下生效的服务,方便快捷测试。

多个Compose file之间的配置共享

默认情况下,Compose 读取两个文件,一个docker-compose.yml和一个可选 docker-compose.override.yml文件。

如果两个文件中都定义了同样的服务,则会将两个服务的配置进行合并;若需要加载其他compose file,则需要通过-f /path/docker-compose-other.yml 指定不同名称的配置文件

注意:

compose针对image、command和mem_limit这种单值的配置项,会直接用新值替换旧值
针对ports、expose、external_links、dns、dns_search和tmpfs这种多值的配置项,会将多个配置项的值合并到一起,且不会覆盖
针对environment、labels、volumes和devices这几个多值的配置项,若存在交集的话,会用新的值替换旧的值,其他值合并到一起

样例:

分别构建两个配置文件:docker-compose.yml和docker-compose.override.yml

[shuchang@docker01 case1]$ cat docker-compose.yml 
version: "3.9"
services:
  web:
    build: .
    ports:
      - "8000:5000"
    volumes:
      - .:/code
    environment:
      FLASK_ENV: development
  redis:
    image: "redis:alpine"
  db:
    image: "mysql"
    ports:
      - "23306:3306"
    command: 'cat ./test.txt'

[shuchang@docker01 case1]$ cat docker-compose.override.yml 
services:
  db:
    ports:
      - 13306:3306

执行docker-compose config 校验服务编排配置并输出

[shuchang@docker01 case1]$ docker-compose config
services:
  db:
    command: cat ./test.txt
    image: mysql
    ports:
    - published: 23306
      target: 3306
    - published: 13306
      target: 3306
  redis:
    image: redis:alpine
  web:
    build:
      context: /home/shuchang/docker/docker-compose_example/case1
    environment:
      FLASK_ENV: development
    ports:
    - published: 8000
      target: 5000
    volumes:
    - /home/shuchang/docker/docker-compose_example/case1:/code:rw
version: '3.9'

可以看到,两个配置文件中配置的db服务已经合并到了一起,同时暴露了23306:3306,13306:3306端口。因此,可以得知compose默认会读取docker-compose.yml和docker-compose.override.yml,并将配置合并到一起

但是,如果合并配置后存在冲突的话,就会出现异常!例如:上述合并端口时,是以主机的两个端口映射到了容器的同一个端口,这样是可行的。但是如果是把主机的同一个端口映射到容器的两个不同的端口呢?那么必然会导致端口冲突的问题,无法正常启动!

这里我们修改docker-compose.override.yml文件中的db服务,将ports配置项改为23306:3307,这样就能复现上述的问题

[shuchang@docker01 case1]$ docker-compose up
Starting case1_web_1 ... 
Starting case1_redis_1 ... 
Creating case1_db_1    ... 
Creating case1_db_1    ... error
Starting case1_web_1   ... done
Starting case1_redis_1 ... donebaa2cfa2fcb378b28919b45cbf67e): Bind for 0.0.0.0:23306 failed: port is already allocated

ERROR: for db  Cannot start service db: driver failed programming external connectivity on endpoint case1_db_1 (c26e2d7638cf0a470b82295dd0636a9e04fbaa2cfa2fcb378b28919b45cbf67e): Bind for 0.0.0.0:23306 failed: port is already allocated
ERROR: Encountered errors while bringing up the project.

通过-f <filename>指定compose file

在使用-f指定compose file时,需注意如果只指定一个文件时,compose只会读取指定的配置文件,如docker-compose -f docker-compose.origin.yml config,只会加载docker-compose.origin.yml一个文件中定义的服务;而docker-compose -f docker-compose.yml -f docker-compose.origin.yml config 则会将两个配置文件中的服务进行合并。

[shuchang@docker01 case1]$ cat docker-compose.origin.yml 
#compose针对image、command和mem_limit这种单值的配置项,会直接用新值替换旧值
#针对ports、expose、external_links、dns、dns_search和tmpfs这种多值的配置项,会将多个配置项的值合并到一起,且不会覆盖
#针对environment、labels、volumes和devices这几个多值的配置项,若存在交集的话,会用新的值替换旧的值,其他值合并到一起
services:
  db:
    command: '-d'
    ports:
      - "3306:3306"
  web:
    ports:
      - "8000:4000"

[shuchang@docker01 case1]$ cat docker-compose.yml 
version: "3.9"
services:
  web:
    build: .
    ports:
      - "8000:5000"
    volumes:
      - .:/code
    environment:
      FLASK_ENV: development
  redis:
    image: "redis:alpine"
  db:
    image: "mysql"
    ports:
      - "23306:3306"
    command: 'cat ./test.txt'

[shuchang@docker01 case1]$ docker-compose -f docker-compose.origin.yml -f docker-compose.yml config
services:
  db:
    command: cat ./test.txt
    image: mysql
    ports:
    - published: 3306
      target: 3306
    - published: 23306
      target: 3306
  redis:
    image: redis:alpine
  web:
    build:
      context: /home/shuchang/docker/docker-compose_example/case1
    environment:
      FLASK_ENV: development
    ports:
    - published: 8000
      target: 4000
    - published: 8000
      target: 5000
    volumes:
    - /home/shuchang/docker/docker-compose_example/case1:/code:rw
version: '3.9'

服务扩展之extends

Docker Compose 的extends关键字可以在不同的文件甚至完全不同的项目之间共享通用配置。如果存在多个重用通用配置选项的服务,那么extends能发挥关键作用。使用extends您可以在一个地方定义一组通用的服务选项,并从任何地方引用它。

注意:volume_from 和 depend_on 无法通过extends进行共享配置,这样会导致依赖之间的引用冲突;且volume在本地文件定义的话,能够确保各个服务对于数据卷的内容进行更改后,不会影响其他服务的运作。

extends的使用

首先创建compose file,通过extends引用共享配置

vi docker-compose.yml
#内容如下
services:
  web:
    extends:
      file: common-services.yml
      service: webapp

上述文件中通过extends引用共享配置common-services.yml中的webapp服务,因此这里需要创建一个common-services配置文件,并构建共享服务webapp

vi common-services.yml
#内容如下
services:
  webapp:
    build: .
    ports:
      - "8000:8000"
    volumes:
      - "/data"

然后,我们通过docker-compose config 校验服务配置并输出相应内容

[shuchang@docker01 extends_example]$ docker-compose config
services:
  web:
    build:
      context: /home/shuchang/docker/docker-compose_example/extends_example
    ports:
    - published: 8000
      target: 8000
    volumes:
    - /data
version: '3.9'

通过上述内容可以看到extends确实能达到引用共享配置的作用,但是不推荐在共享配置中定义volume和depend_on配置项,存在服务之间的引用错乱,以及数据卷内容变更影响服务运行的问题。

 类似资料: