简要记录下近期在项目中遇到的多个docker-compose.yml文件容器启动时发生冲突的原因分析。

问题背景

出于多方面的考虑,公司的一些项目都逐渐的采用Docker作为部署环境,相对于互联网公司我们的Docker容器数据量没那么多,在容器管理工具的选型上我们采用的是Docker Compose而非常见的Kubernetes

Docker Compose需要我们将相关的Docker指令都写入到yaml文件中,相对于通过纯命令行操作Docker其在可维护性和可阅读性上都有很大的便利,故很快在部门内部推广使用了。

在此过程中,有同事偶然遇到多个docker-compose.yml文件启动时会冲突的问题,一番分析后虽然最终解决该问题,但由于该问题在多个docker-compose.yml文件时容易产生,简单记录下已被不时之需。

简化版的问题描述如下:

  1. 在某个目录下有两个docker-compose.yml文件,分别为nginx_test_1.ymlnginx_test_2.yml

    [root@fox docker_test]# pwd
    /root/docker_test
    [root@fox docker_test]# ls
    nginx_test_1.yml  nginx_test_2.yml
    
  2. 它们的内容分别如下

    nginx_test_1.yml配置:

    version: "3"
    services:
       nginx:
         privileged: true
         image: nginx
         restart: always
         container_name: nginx_test_1
         ports:
           - "8081:80"
    

    nginx_test_2.yml配置:

    version: "3"
    services:
       nginx:
         privileged: true
         image: nginx
         restart: always
         container_name: nginx_test_2
         ports:
           - "8082:80"
    
  3. 基于nginx_test_1.yml文件可正常启动

    docker compose正常启动

  4. 基于nginx_test_1.yml文件进行测试时,虽然也能启动,但结果不符合我们的预期,其把名为nginx_test_1的容器关闭了,而我们期望的是基于两个文件启动时,能分别启动不同的容器

    docker compose未按预期启动

  5. 利用docker ps -a查询也只有1个容器

    docker ps查询只有1个容器

发生了什么? 理论上不应该是采用不同的文件可分别正常启动么?!

原因分析

一开始自己以为是docker-compose.yml文件的问题,以为有语法问题,反复对这两个文件进行检查后并没有找出啥。

同时nginx_test_1.ymlnginx_test_2.yml按照下述的指令操作,可正常启动:

docker-compose -f nginx_test_1.yml up -d
docker ps
docker-compose -f nginx_test_1.yml down
docker-compose -f nginx_test_2.yml up -d
docker ps
docker-compose -f nginx_test_1.yml down

若按照之前的方式操作依旧有问题,至此可以确认的是单个docker-compose.yml使用时没有问题,多个docker-compose.yml文件一起使用时会有问题。

需要从docker-compos.yml的使用机制着手。

在网上搜索后,发现有人已经遇到类似的问题Docker is not creating new container but recreates running one,通过对该文章的阅读发现了问题根源:

Docker Compose通过项目名称和服务名称的组合来识别一个指定的服务 ,启动和关闭时基于该组合进行操作。


其中服务名称是在yaml文件中指定的,而服务名称可通过启动时-p参数指定,若没有该参数,则去环境变量中查找COMPOSE_PROJECT_NAME的值,若还是没有则以当前yaml文件的文件夹作为项目名称。

具体到本问题中,在启动时没有指定-p参数,也没有指定COMPOSE_PROJECT_NAME环境变量,同时这两个yaml文件都位于docker_test目录下,则项目名称为docker_test,查看其源码,发现其服务名称都为nginx,由此导致问题产生!

version: "3"
services:
   # 服务名称都为nginx
   nginx:
     privileged: true
     image: nginx
     restart: always
     container_name: nginx_test_1
     ports:
       - "8081:80"

解决方案

Docker Compose的官网找到关于COMPOSE_PROJECT_NAME说明如下

COMPOSE_PROJECT_NAME配置说明

上述说明前半部分说明了Docker Compose会将项目名和服务名组合作为容器名称供启动使用,基于该描述前述两个yaml文件理论上的容器名称都为docker_test_nginx-11,故而导致启动时冲突。

查看本文开始的截图,发现容器名并不是docker_test_nginx,其原因为我们在yaml文件中手工指定了container_name,若取消该配置重新启动,则会发现容器名称已经变为docker_test_nginx_1:

docker compose自动生成容器名

后半部分说明项目名称的计算规则,按照优先级从高到低如下:

  1. 启动容器时,在命令行中通过-p指定
  2. 基于环境变量COMPOSE_PROJECT_NAME获取
  3. 基于docker-compose.yml文件最顶层的name属性获取,有多个yaml文件时以最后一个为准
  4. 包含要当前运行的yaml文件的最底层文件夹名称
  5. 当前要运行的yaml文件所在的文件夹名称

其中4和5的表达的意思基本一样,主要是为是否通过文件夹指定yaml文件。

如通过docker-compose -f a/b/nginx_test_1.yml up -d来执行时,基于规则4其容器名称为Creating c_nginx_1

docker compose在有文件夹时自动生成容器名

而通过docker-compose -f nginx_test_1.yml up -d来执行时,则是基于规则5来指定容器名。

接下来演示通过前述规则解决冲突。

  • 基于命令行参数

    docker compose通过命令行指定项目名

  • 基于不同文件夹

    docker compose通过不同文件夹启动

  • 基于name属性,个人在v2.26.1版本上测试通过

    docker compose通过name属性启动

  • 基于COMPOSE_PROJECT_NAME环境变量,个人在v2.2.2版本上测试通过

    docker compose通过环境变量启动


  1. 其中的-1Docker Compose自动添加的序号 ↩︎