docker-compose构建的镜像重建后无法正确重新创建容器的问题

83次阅读
没有评论

问题描述

在使用docker-compose时,遇到一个问题。他在一个包含devops容器的环境中执行了docker builddocker-compose up命令,这些命令在同一台服务器或不同服务器上运行时,有时会在重新构建镜像后错误地无法重新创建兄弟容器。
用户的过程成功地重新构建了两个底层的docker镜像,但是有时docker-compose up命令无法检测到这个变化,并错误地显示刚刚重新构建的镜像为”up-to-date”,而不是应该显示为”Recreating”。
用户在多台服务器上执行相同的代码,每台服务器的操作系统和docker版本都有所不同(ubuntu 16.04或debian 8或debian 9,docker版本为18.04.0-ce-rc1或18.03.0-ce)。用户正在努力找出问题的根本原因,但是在上述每个操作系统和docker版本中都遇到了好的和坏的行为。
用户确认这个问题与https://github.com/docker/compose/issues/3574无关。
用户已经为镜像打了标签,并将其推送到本地docker仓库。
以下是用户的构建和推送镜像的命令示例:

docker build --tag localhost:5000/hygge/loudweb-admin .
docker push localhost:5000/hygge/loudweb-admin

用户的docker-compose.yaml文件如下:

version: '3'
services:
  nodejs-admin:
    image: ${GKE_APP_REPO_PREFIX}/${PROJECT_ID}/loudweb-admin
    container_name: loud_admin
    restart: always
    depends_on:
      - loudmongo
      - loudmail
    volumes:
      - /cryptdata4/var/log/loudlog-admin:/loudlog-admin
      - /cryptdata5/var/log/blobs:/blobs
      - /cryptdata5/var:/cryptdata5/var
      - /cryptdata5/var/tools:/tools
      - /cryptdata6/var/log/loudlog-enduser:/loudlog-enduser
      - $SOURCE_REPO_DIR/tests:/tmp/tests
      - ${TMPDIR_GRAND_PARENT}/curr/loud-build/${PROJECT_ID}/webapp/admin/bundle:/tmp
    environment:
      - MONGO_SERVICE_HOST=loudmongo
      - MONGO_SERVICE_PORT=$GKE_MONGO_PORT
      - MONGO_URL=mongodb://loudmongo:$GKE_MONGO_PORT/test
      - METEOR_SETTINGS=${METEOR_SETTINGS}
      - MAIL_URL=smtp://support@${GKE_DOMAIN_NAME}:blah@loudmail:587/
      - GKE_NOTIF_TASK_OVERDUE=$GKE_NOTIF_TASK_OVERDUE
      - GKE_NOTIF_TASK_PUSH=$GKE_NOTIF_TASK_PUSH
      - GKE_NOTIF_PLANS_RECUR=$GKE_NOTIF_PLANS_RECUR
    links:
      - loudmongo
      - loudmail
    ports:
      - 127.0.0.1:3001:3001
    working_dir: /tmp
    command: /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
  nodejs-enduser:
    image: ${GKE_APP_REPO_PREFIX}/${PROJECT_ID}/loudweb-enduser
    container_name: loud_enduser
    restart: always
    depends_on:
      - nodejs-admin
      - loudmongo
      - loudmail
    volumes:
      - /cryptdata6/var/log/loudlog-enduser:/loudlog-enduser
      - /cryptdata5/var/log/blobs:/blobs
      - ${TMPDIR_GRAND_PARENT}/curr/loud-build/${PROJECT_ID}/webapp/enduser/bundle:/tmp
      - ${TMPDIR_GRAND_PARENT}/curr/loud-build/${PROJECT_ID}/webapp/admin/bundle/programs/server/assets/app/config/apn-cert.pem:/private/config/apn-cert.pem
      - ${TMPDIR_GRAND_PARENT}/curr/loud-build/${PROJECT_ID}/webapp/admin/bundle/programs/server/assets/app/config/apn-key.pem:/private/config/apn-key.pem
    environment:
      - MONGO_SERVICE_HOST=loudmongo
      - MONGO_SERVICE_PORT=$GKE_MONGO_PORT
      - MONGO_URL=mongodb://loudmongo:$GKE_MONGO_PORT/test
      - METEOR_SETTINGS=${METEOR_SETTINGS}
      - MAIL_URL=smtp://support@${GKE_DOMAIN_NAME}:puMDcxNmEwMDU5MmJjZjNiNzQ3M2ZlNTJjNDYyOGI1NTggIC0Ksy@loudmail:587/
    links:
      - loudmongo
      - loudmail
    ports:
      - 127.0.0.1:3000:3000
    working_dir: /tmp
    command: /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
  loudmongo:
    image: mongo
    container_name: loud_mongo
    restart: always
    ports:
      - 127.0.0.1:$GKE_MONGO_PORT:$GKE_MONGO_PORT
    volumes:
      - /cryptdata7/var/data/db:/data/db
  loudmail:
    image: ${GKE_APP_REPO_PREFIX}/${PROJECT_ID}/loudmail
    hostname: mail
    domainname: ${GKE_DOMAIN_NAME}
    container_name: loud_mail
    restart: always
    environment:
      - DMS_DEBUG=1
      - ENABLE_MANAGESIEVE=1
      - ENABLE_FAIL2BAN=1
      - SA_TAG=2.0
      - SA_TAG2=6.31
      - SA_KILL=6.31
      - SSL_TYPE=letsencrypt
    ports:
      - 127.0.0.1:25:25
      - 127.0.0.1:110:110
      - 127.0.0.1:143:143
      - 127.0.0.1:587:587
      - 127.0.0.1:993:993
      - 127.0.0.1:995:995
      - 127.0.0.1:4190:4190
  loud-devops:
    image: dind
    container_name: loud_devops
    restart: always
    ports:
      - 127.0.0.1:9000:9000
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /usr/bin/docker:/usr/bin/docker
      - /usr/lib/x86_64-linux-gnu/libltdl.so.7:/usr/lib/x86_64-linux-gnu/libltdl.so.7
      - /usr/lib/x86_64-linux-gnu/libgpm.so.2:/usr/lib/x86_64-linux-gnu/libgpm.so.2
      - /home/asya/src/github.com/blahblah:/inner_home/asya/src/github.com/blahblah
      - /home/asya/.docker:/inner_home/asya/.docker
      - /cryptdata5/var/tools/inner_home/asya:/inner_home/asya
      - $GKE_DIND_SUPERVISOR_LOG_DIR:/var/log/supervisor
      - /cryptdata5/var/tools/usr/local/go:/usr/local/go
      - /cryptdata6/var/log/tmp/asya01:/cryptdata6/var/log/tmp/asya01
      - /cryptdata6/var/log/tmp/shared:/cryptdata6/var/log/tmp/shared
      - /cryptdata5/var/tools/usr/local/bin:/usr/local/bin
      - /cryptdata:/cryptdata
      - /cryptdata4:/cryptdata4
      - /cryptdata5:/cryptdata5
      - /cryptdata6:/cryptdata6
      - /cryptdata7:/cryptdata7
      - /etc/letsencrypt/live:/etc/letsencrypt/live
      - /usr/local/ssl:/usr/local/ssl
      - /var/lib/docker:/var/lib/docker
    command: /usr/bin/supervisord -c /etc/supervisor/supervisord.conf

用户在上述loud-devops容器中有一个webhook守护程序,用于监听git push事件以触发重新构建循环。在容器内部,用户使用docker命令执行所有的docker命令,包括为两个容器(nodejs-admin和nodejs-enduser)创建新的镜像,然后在容器内部执行以下命令:

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

有时,这个命令会错误地显示以下内容:

loud_devops is up-to-date
loud_mongo is up-to-date
loud_mail is up-to-date
loud_admin is up-to-date
loud_enduser is up-to-date

而有时它会正确地显示以下内容:

docker-compose -f /docker-compose.yml up -d
Recreating loud_devops ...
Recreating loud_mongo ...
Recreating loud_mail ...

这种情况绝不应该发生,因为这三个镜像从未被重新构建过,对我来说,这是一个明显的docker逻辑错误。
用户在每台服务器上都安装了docker,并且如上所述,在loud-devops容器中挂载了各种docker目录,以便从容器内部执行docker in docker。
用户提供了一个关键的证据:在已经完成重新构建周期的服务器上,当它成功地重新构建了loudweb-enduser和loudweb-admin两个镜像后,错误地显示以下内容:

docker-compose -f /docker-compose.yml up -d
loud_mail is up-to-date
loud_mongo is up-to-date
loud_devops is up-to-date
loud_admin is up-to-date
loud_enduser is up-to-date

如果我手动登录到同一台服务器,然后手动执行相同的命令,它会正确地工作:

docker-compose -f /docker-compose.yml up -d
loud_devops is up-to-date
loud_mail is up-to-date
loud_mongo is up-to-date
Recreating loud_admin ... done
Recreating loud_enduser ... done

解决方案

请注意以下操作注意版本差异及修改前做好备份。

方案1

用户解决了这个问题,他在启动脚本中加入了docker pull命令。在执行docker-compose up命令之前,先执行以下命令:

docker-compose -f ${GKE_COMPOSE_YAML} pull

这个解决方案可能看起来有些奇怪,因为大部分时间它可以正常工作而不需要执行pull操作。用户正在使用本地docker仓库。

方案2

使用脚本或工具来管理容器的启动顺序可能会增加复杂性,并且需要确保容器A和容器B之间的依赖关系正确设置。
另一种方法是编写脚本或使用工具来控制容器的运行顺序。你可以使用docker run命令来手动控制容器的启动顺序,或者使用一些第三方工具来管理容器的依赖关系。

示例:

以下是一个简单的bash脚本示例,可以在容器A启动后启动容器B:

#!/bin/bash
# 启动容器A
docker run -d --name container_a your_image_a
# 等待容器A完全启动
while ! docker exec container_a echo "Container A is ready"; do
  sleep 1
done
# 启动容器B
docker run -d --name container_b your_image_b

在这个示例中,我们首先使用docker run命令启动容器A,并将其命名为container_a。然后,使用一个循环来等待容器A完全启动(这里是通过在容器内运行echo命令来测试)。一旦容器A就绪,我们再使用docker run命令启动容器B,并将其命名为container_b

正文完