【问题标题】:How to automatically update your docker containers, if base-images are updated如果基础镜像已更新,如何自动更新您的 docker 容器
【发布时间】:2014-10-17 10:56:28
【问题描述】:

假设我有一个基于ubuntu:latest 的简单容器。现在有一个安全更新,ubuntu:latest 在 docker repo 中更新。

  1. 我如何知道我的本地映像及其容器在后面运行?

  2. 是否有一些自动更新本地映像和容器以遵循 docker repo 更新的最佳实践,这实际上会为您提供与在传统 ubuntu 机器上运行无人值守升级相同的好处

【问题讨论】:

  • 我从 docker 开始就在寻找这个问题的答案。它甚至更复杂一些。如果我安装了 apache(例如)并且它得到了更新,那么基本图像不会改变,因为我是在之后安装它的。我仍然希望对 apache 进行自动更新。我实际上在 IRC 中询问过这个问题,并得到了“跟随上游并重建更新”作为答案......
  • 很高兴我不是唯一一个想知道的人。对于 docker 开发者来说,开发和可重复性似乎比我们多年来拥有的合理更新机制更重要。
  • 感谢所有回答的人。很抱歉我不能分配赏金。尽管我的问题没有最终解决方案,但大家都提出了很好的意见。
  • 对于@Mathias,我刚刚添加的解决方案有一个脚本,用于检查安装在容器中的软件包的安全更新。它还有一个单独的脚本来检查基础镜像。
  • 来自这个博客:Watching Images for Updates,我知道Docker CI/CD的一个好产品。但是anchore-engine open source 需要在你的 VPS 中运行服务器。提供一个轻量级的锚服务器仍然是个问题

标签: docker automatic-updates


【解决方案1】:

我们使用一个脚本来检查正在运行的容器是否使用最新的图像启动。我们还使用 upstart 初始化脚本来启动 docker 镜像。

    #!/usr/bin/env bash
    set -e
    BASE_IMAGE="registry"
    REGISTRY="registry.hub.docker.com"
    IMAGE="$REGISTRY/$BASE_IMAGE"
    CID=$(docker ps | grep $IMAGE | awk '{print $1}')
    docker pull $IMAGE

    for im in $CID
    do
        LATEST=`docker inspect --format "{{.Id}}" $IMAGE`
        RUNNING=`docker inspect --format "{{.Image}}" $im`
        NAME=`docker inspect --format '{{.Name}}' $im | sed "s/\///g"`
        echo "Latest:" $LATEST
        echo "Running:" $RUNNING
        if [ "$RUNNING" != "$LATEST" ];then
            echo "upgrading $NAME"
            stop docker-$NAME
            docker rm -f $NAME
            start docker-$NAME
        else
            echo "$NAME up to date"
        fi
    done

初始化看起来像

docker run -t -i --name $NAME $im /bin/bash

【讨论】:

  • 非常感谢您的宝贵贡献。这似乎是更新基础映像的好方法。剩下的问题是,如何更新由 dockerfile 中的发行版安装的应用程序(如 apache)?还是你只使用现成的基础镜像,只需要你的应用程序代码(如网站)?
  • 我们使用 packer 和 puppet 来配置我们的镜像。我们的图像在创建后就可以投入生产了
  • @Mathias,查看我编辑的答案我有一个小工具docker-run,我用它来更新所有正在运行的容器中的 linux(当前是 debian/ubuntu)包。
  • 如果图像与容器同名(例如redis),LATEST=`docker inspect --format "{{.Id}}" $IMAGE` 将获取容器信息。添加--type image 来解决这个问题。
  • 感谢您的帖子。我对其进行了修改以将整个内容包装在一个循环中以从 docker 获取图像:for IMAGE in $(docker ps --format {{.Image}} -q | sort -u)
【解决方案2】:

您可以使用Watchtower 来监视实例化容器的映像的更新,并自动拉取更新并使用更新后的映像重新启动容器。但是,当它所基于的上游映像发生更改时,这并不能解决重建您自己的自定义映像的问题。您可以将此视为一个由两部分组成的问题:(1) 了解上游映像何时更新,以及 (2) 进行实际的映像重建。 (1) 可以相当容易地解决,但 (2) 在很大程度上取决于您本地的构建环境/实践,因此为此创建通用解决方案可能要困难得多。

如果您能够使用 Docker Hub 的 automated builds,则可以使用 repository links 功能相对干净地解决整个问题,该功能可让您在链接存储库(可能是上游存储库)更新时自动触发重建.您还可以配置 webhook 以在发生自动构建时通知您。如果您需要电子邮件或短信通知,您可以将 webhook 连接到 IFTTT Maker。我发现 IFTTT 用户界面有点令人困惑,但您可以将 Docker webhook 配置为发布到 https://maker.ifttt.com/trigger/`docker_xyz_image_built`/with/key/`your_key`

如果您需要在本地构建,您至少可以通过在 Docker Hub 中创建一个链接到您感兴趣的存储库的虚拟存储库来解决更新上游映像时收到通知的问题。虚拟存储库的唯一目的是在重建时触发 webhook(这意味着其链接的存储库之一已更新)。如果您能够收到此 webhook,您甚至可以使用它来触发您这边的重建。

【讨论】:

  • Watchtower 使用 docker 套接字。从安全的角度来看,它会放弃对主机的 root 访问权限。
  • 此外,Watchtower 似乎无法从 Docker Hub 以外的私有存储库更新映像。对于使用 Azure 的我们来说,这真是太可惜了。
  • 您可以使用 REPO_USERREPO_PASS 环境变量来使用私有注册表。查看 Watchtower 的 readme.md 以获取更多信息:github.com/v2tec/watchtower#usage
  • 警告,瞭望塔已被其维护者遗弃,DockerHub 中的图像甚至与 github 中的图像都不是最新的。
  • Watchtower repo 似乎已迁移到 containrrr/watchtower。正如this answer on a similar question 所指出的那样,Dockerhub 上的链接自动构建存在一些问题。
【解决方案3】:

“码头方式”是使用docker hubautomated buildsRepository Links 功能将在上游容器重建时重建您的容器,Webhooks 功能将向您发送通知。

看起来 webhook 仅限于 HTTP POST 调用。您需要设置一个服务来捕获它们,或者可能使用其中一个 POST 发送电子邮件服务。

我还没有研究过,但新的Docker Universal Control Plane 可能具有检测更新容器和重新部署的功能。

【讨论】:

【解决方案4】:

其中一种方法是通过您的 CI/CD 系统来驱动它。构建父映像后,请使用该父映像扫描您的 git 存储库以查找图像。如果找到,然后您将发送拉取请求以碰撞到图像的新版本。如果所有测试都通过,拉取请求将被合并,并且您将拥有一个基于更新父级的新子图像。可以在此处找到采用这种方法的工具示例:https://engineering.salesforce.com/open-sourcing-dockerfile-image-update-6400121c1a75

如果你不控制你的父图像,如果你依赖官方的ubuntu图像,你可以编写一些工具来检测父图像标签或校验和的变化(不是一回事,标签是可变的)并相应地调用子图像构建。

【讨论】:

  • 哇,这是一个大锤子,说:自从我提出这个问题以来,我也开始意识到构建服务器是解决这个问题的地方。我很高兴看到一些工具。如果您用通用概念(而不是您的确切工具/实现)解释您的方法并将其包含在答案中,我可能会接受它。
  • 谢谢@hbogert 我编辑了上面的内容,还包括一个关于如果您正在处理公共图像该怎么做的想法
【解决方案5】:

我遇到了同样的问题,并认为可以通过每天调用 unattended-upgrade 的 cron 作业来解决。

我的目的是将其作为一种自动且快速的解决方案,以确保生产容器的安全和更新,因为我可能需要一些时间来更新我的映像并使用最新的安全更新部署一个新的 docker 映像。

还可以使用Github hooks 自动构建和部署映像

我创建了一个基本的docker image,每天自动检查和安装安全更新(可以直接由docker run itech/docker-unattended-upgrade 运行)。

我还遇到了另一个不同的approach 来检查容器是否需要更新。

我的完整实现:​​

Dockerfile

FROM ubuntu:14.04   

RUN apt-get update \
&& apt-get install -y supervisor unattended-upgrades \
&& rm -rf /var/lib/apt/lists/*

COPY install /install
RUN chmod 755 install
RUN /install

COPY start /start
RUN chmod 755 /start

帮助脚本

安装

#!/bin/bash
set -e

cat > /etc/supervisor/conf.d/cron.conf <<EOF
[program:cron]
priority=20
directory=/tmp
command=/usr/sbin/cron -f
user=root
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
EOF

rm -rf /var/lib/apt/lists/*

ENTRYPOINT ["/start"]

开始

#!/bin/bash

set -e

echo "Adding crontab for unattended-upgrade ..."
echo "0 0 * * * root /usr/bin/unattended-upgrade" >> /etc/crontab

# can also use @daily syntax or use /etc/cron.daily

echo "Starting supervisord ..."
exec /usr/bin/supervisord -n -c /etc/supervisor/supervisord.conf

编辑

我开发了一个小工具docker-run,它作为docker容器运行,可用于更新所有或选定的运行容器中的包,也可用于运行任意命令。

可以使用以下命令轻松测试:

docker run --rm -v /var/run/docker.sock:/tmp/docker.sock itech/docker-run exec

默认情况下会在所有正在运行的容器中执行date 命令并显示结果。如果您传递update 而不是exec,它将在所有正在运行的容器中执行apt-get update,然后是apt-get upgrade -y

【讨论】:

  • 我对无人值守升级的引用只是为了在非 docker 环境中进行类比。我的意图是通过 docker 方式解决这个问题(如果存在 oc。)在容器中拥有一个额外的进程胜过 docker imo 的目的。它确实解决了上游更新其映像与您(用户)将其实际部署到当前容器之间的延迟问题。尽管无人值守升级也可能需要长达 1 天的时间,所以.. github 参考也不令人满意,因为更新机制现在严重依赖于主机操作系统。
  • “docker 方式”不会阻止您在同一个容器上运行其他进程,如果它们紧密相关并且不会造成可伸缩性瓶颈。这个特殊的用例是一个很好的例子,说明何时可以拥有一个包含另一个正在运行的进程的容器。 (例如,查看gitlab 的图像,因为它在同一个容器上运行多个强制进程)。
  • 我不会调用与图像的主要功能紧密相关的更新机制。这种解决方案就像为传统机器上的每个应用程序提供了自己的更新机制,而不是将负担放在包管理器上。虽然这是一个解决方案,但它并没有回答我的问题,即自动更新本地图像,然后应该重新运行容器。随着容器本身的更新,我们再次引入了很多我们不知道的状态,这违反了 docker 方式(再次恕我直言)。
  • 您可能需要比 docker 更高级别的东西,例如 Kubernetes,这对于大型基础架构部署很有用,但 Google 仍在大力开发它。目前,您可以使用 Ansible 等配置工具以相当简单的方式自动执行此操作。
  • 此处描述的解决方案实际上并不能修复您容器中可能存在的许多漏洞。如果您更新正在运行的程序正在使用的软件包,那么它并不会真正改变该正在运行的程序的任何内容。它必须重新启动才能获取新安装的包/文件。因此,您可能看起来没有任何安全问题,但您肯定仍然存在。
【解决方案6】:

如果不运行 docker pull,您将不会知道您的容器落后。然后你需要rebuildrecompose 你的图片。

docker pull image:tag
docker-compose -f docker-compose.yml -f production.yml up -d --build

这些命令可以与完成升级所需的任何其他内容一起放入脚本中,尽管适当的容器不需要任何额外内容。

【讨论】:

  • 1:好的,但是我必须查看我所有的本地图像,获取它们的基本图像,然后提取它们。然后重建基础镜像发生变化的镜像。然后停止镜像已更改的容器,并使用“docker run”和所需参数重新创建容器。这似乎过于手动。但如果这是现状,那我会接受答案。
  • 请稍候再接受。也许外面有什么东西。我已经使用 docker 6 个月了,但一直没有跟上最新的发展。
  • 不知何故,在内部,Docker 能够比较图像以执行其“缓存”功能。也许您可以找到一种方法来利用它。换句话说,检查底层图像(一直回到基础)是否发生了变化,然后触发重建过程。不幸的是,在这种情况下,缓存对您没有帮助:整个图像将被重建,因为基础图像已更改。
【解决方案7】:

这是自动更新 docker 容器的最简单方法

通过$ crontab -e提交作业:

0 * * * * sh ~/.docker/cron.sh

使用文件cron.sh创建目录~/.docker

#!/bin/sh
if grep -Fqe "Image is up to date" << EOF
`docker pull ubuntu:latest`
EOF
then
    echo "no update, just do cleaning"
    docker system prune --force
else
    echo "newest exist, recompose!"
    cd /path/to/your/compose/file
    docker-compose down --volumes
    docker-compose up -d
fi

【讨论】:

    【解决方案8】:

    Docker 镜像的依赖管理是一个真正的问题。我是构建工具MicroBadger 的团队的一员,通过监视容器映像和检查元数据来帮助解决这个问题。它的一个功能是让你设置一个通知 webhook,当你感兴趣的图像(例如基础图像)发生变化时,它会被调用。

    【讨论】:

    • microbadger.com 似乎已不复存在……我知道已经过去了六年,但发生了什么?
    【解决方案9】:

    这里有很多答案,但没有一个适合我的需要。我想要对提问者的 #1 问题的实际答案。我如何知道 hub.docker.com 上的图像何时更新?

    以下脚本可以每天运行。首次运行时,它会从 HUB 注册表中获取标签的基线和更新日期,并将它们保存在本地。从那时起,每次运行它都会检查注册表中的新标签和更新日期。由于每次存在新图像时都会更改,因此它会告诉我们基础图像是否已更改。这是脚本:

    #!/bin/bash
    
    DATAPATH='/data/docker/updater/data'
    
    if [ ! -d "${DATAPATH}" ]; then
            mkdir "${DATAPATH}";
    fi
    IMAGES=$(docker ps --format "{{.Image}}")
    for IMAGE in $IMAGES; do
            ORIGIMAGE=${IMAGE}
            if [[ "$IMAGE" != *\/* ]]; then
                    IMAGE=library/${IMAGE}
            fi
            IMAGE=${IMAGE%%:*}
            echo "Checking ${IMAGE}"
            PARSED=${IMAGE//\//.}
            if [ ! -f "${DATAPATH}/${PARSED}" ]; then
                    # File doesn't exist yet, make baseline
                    echo "Setting baseline for ${IMAGE}"
                    curl -s "https://registry.hub.docker.com/v2/repositories/${IMAGE}/tags/" > "${DATAPATH}/${PARSED}"
            else
                    # File does exist, do a compare
                    NEW=$(curl -s "https://registry.hub.docker.com/v2/repositories/${IMAGE}/tags/")
                    OLD=$(cat "${DATAPATH}/${PARSED}")
                    if [[ "${VAR1}" == "${VAR2}" ]]; then
                            echo "Image ${IMAGE} is up to date";
                    else
                            echo ${NEW} > "${DATAPATH}/${PARSED}"
                            echo "Image ${IMAGE} needs to be updated";
                            H=`hostname`
                            ssh -i /data/keys/<KEYFILE> <USER>@<REMOTEHOST>.com "{ echo \"MAIL FROM: root@${H}\"; echo \"RCPT TO: <USER>@<EMAILHOST>.com\"; echo \"DATA\"; echo \"Subject: ${H} - ${IMAGE} needs update\"; echo \"\"; echo -e \"\n${IMAGE} needs update.\n\ndocker pull ${ORIGIMAGE}\"; echo \"\"; echo \".\"; echo \"quit\"; sleep 1; } | telnet <SMTPHOST> 25"
                    fi
    
            fi
    done;
    

    您需要更改顶部的DATAPATH 变量,并在末尾更改电子邮件通知命令以满足您的需要。对我来说,我将它通过 SSH 连接到我的 SMTP 所在的另一个网络上的服务器。但是您也可以轻松使用mail 命令。

    现在,您还想检查容器本身内的更新包。这实际上可能比在容器工作后进行“拉动”更有效。这是实现它的脚本:

    #!/bin/bash
    
    
    function needsUpdates() {
            RESULT=$(docker exec ${1} bash -c ' \
                    if [[ -f /etc/apt/sources.list ]]; then \
                    grep security /etc/apt/sources.list > /tmp/security.list; \
                    apt-get update > /dev/null; \
                    apt-get upgrade -oDir::Etc::Sourcelist=/tmp/security.list -s; \
                    fi; \
                    ')
            RESULT=$(echo $RESULT)
            GOODRESULT="Reading package lists... Building dependency tree... Reading state information... Calculating upgrade... 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded."
            if [[ "${RESULT}" != "" ]] && [[ "${RESULT}" != "${GOODRESULT}" ]]; then
                    return 0
            else
                    return 1
            fi
    }
    
    function sendEmail() {
            echo "Container ${1} needs security updates";
            H=`hostname`
            ssh -i /data/keys/<KEYFILE> <USRER>@<REMOTEHOST>.com "{ echo \"MAIL FROM: root@${H}\"; echo \"RCPT TO: <USER>@<EMAILHOST>.com\"; echo \"DATA\"; echo \"Subject: ${H} - ${1} container needs security update\"; echo \"\"; echo -e \"\n${1} container needs update.\n\n\"; echo -e \"docker exec ${1} bash -c 'grep security /etc/apt/sources.list > /tmp/security.list; apt-get update > /dev/null; apt-get upgrade -oDir::Etc::Sourcelist=/tmp/security.list -s'\n\n\"; echo \"Remove the -s to run the update\"; echo \"\"; echo \".\"; echo \"quit\"; sleep 1; } | telnet <SMTPHOST> 25"
    }
    
    CONTAINERS=$(docker ps --format "{{.Names}}")
    for CONTAINER in $CONTAINERS; do
            echo "Checking ${CONTAINER}"
            if needsUpdates $CONTAINER; then
                    sendEmail $CONTAINER
            fi
    done
    

    【讨论】:

    • 第一个脚本中的 mkdir 应该是: mkdir -p 另外,第一个脚本比较 VAR1 和 VAR2,假设应该比较 OLD 和 NEW。如果是真的,这意味着这个脚本不会真正做 OP 想要的,除非它是在安装时第一次运行。也就是说,它并没有真正确定安装了什么,只要结果与以前的运行不同......
    • 此外,它仅从 Docker Hub Web 服务检索结果的第一页。对于带有大量标签的图像,安装的可能根本不在第一页上。
    【解决方案10】:

    另一种方法是假设您的基础映像很快就落后了(这很可能会发生),并定期(例如每周)强制构建您的应用程序的另一个映像,然后在它发生变化时重新部署它.

    据我所知,像官方 Debian 或 Java 这样的流行基础镜像会更新它们的标签以适应安全修复,因此标签不是不可变的(如果你想要更强有力的保证,你需要使用参考 [image :@digest],在更新的 Docker 版本中可用)。因此,如果您要使用docker build --pull 构建映像,那么您的应用程序应该获得您所引用的最新和最好的基本映像标记。

    由于可变标签可能会造成混淆,因此最好每次执行此操作时都增加应用程序的版本号,这样至少在您这边事情会更干净。

    所以我不确定先前答案之一中建议的脚本是否可以完成这项工作,因为它不会重建您的应用程序映像 - 它只是更新基本映像标记,然后重新启动容器,但新的容器仍然引用旧的基础镜像哈希。

    我不提倡在容器(或任何其他进程,除非真的必要)中运行 cron 类型的作业,因为这违背了每个容器只运行一个进程的口头禅(关于为什么这样做更好,有各种争论,所以这里就不多说了)。

    【讨论】:

      【解决方案11】:

      更新:使用 Dependabot - https://dependabot.com/docker/

      BLUF:找到合适的插入点来监控容器的变化是一项挑战。如果 DockerHub 能解决这个问题,那就太好了。 (已经提到了存储库链接,但在 DockerHub 上设置它们时请注意 - “只要在 Docker Hub 上更新基础映像,就在此存储库中触发构建。仅适用于非官方映像。”

      在尝试自己解决这个问题时,我看到了一些关于 webhook 的建议,所以我想详细说明我使用过的几个解决方案。

      1. 使用 microbadger.com 跟踪容器中的更改并使用它的 通知 webhook 功能以触发操作。我设置了这个 使用 zapier.com(但您可以使用任何可自定义的 webhook 服务) 在我的 github 存储库中创建一个新问题,使用 Alpine 作为 基础图像。

        • 优点:在采取行动之前,您可以在 github 中查看 microbadger 报告的更改。
        • 缺点:Microbadger 不允许您跟踪特定标签。看起来它只跟踪“最新”。
      2. 跟踪 git 提交到上游容器的 RSS 提要。前任。 https://github.com/gliderlabs/docker-alpine/commits/rootfs/library-3.8/x86_64。我使用 zapier.com 来监控这个提要,并在提交任何内容时触发 Travis-CI 中容器的自动构建。这有点极端,但您可以更改触发器以执行其他操作,例如在 git 存储库中打开问题以进行手动干预。

        • 优点:更接近自动化管道。 Travis-CI 构建只是检查您的容器是否与提交到基础映像存储库的任何内容存在问题。如果您的 CI 服务采取任何进一步的措施,这取决于您。
        • 缺点:跟踪提交提要并不完美。许多不影响基础镜像构建的东西被提交到存储库。未考虑任何与提交频率/数量以及任何 API 限制有关的问题。

      【讨论】:

        【解决方案12】:

        我不会讨论您是否希望在生产中进行无人值守更新的整个问题(我认为不会)。我只是将其留在这里以供参考,以防有人发现它有用。在终端中使用以下命令将所有 docker 镜像更新到最新版本:

        # docker images | awk '(NR&gt;1) &amp;&amp; ($2!~/none/) {print $1":"$2}' | xargs -L1 docker pull

        【讨论】:

        • 该命令对于更新所有图像很有用,但它不会改变生产中运行的任何东西。容器仍然来自旧图像,现在没有标记。
        • 是的。这里还有一本书……使用# docker system prune -a --volumes -f 清理旧的(悬空的)图像、卷等。
        【解决方案13】:

        以我的回答为前提:

        1. 容器使用标签运行。
        2. 同一个标签可以指向不同的图像 UUID,我们可以随意/觉得合适。
        3. 对图像所做的更新可以提交到新的图像层

        接近

        1. 首先使用安全补丁更新脚本构建所有容器
        2. 为以下项目构建自动化流程
          • 使用安全补丁脚本作为命令将现有映像运行到新容器
          • 提交对图像的更改为
            • existing tag -> 依次重启容器
            • 新版本标签 -> 用新标签替换少数容器 -> 验证 -> 将所有容器移动到新标签

        此外,可以升级基础镜像/可以定期构建具有全新基础镜像的容器,维护者认为有必要

        优势

        1. 我们在创建新的安全补丁镜像的同时保留旧版本的镜像,因此我们可以在必要时回滚到之前运行的镜像
        2. 我们保留了 docker 缓存,因此减少了网络传输(只有更改后的层才能上网)
        3. 升级过程可以在转移到 prod 之前在 staging 中进行验证
        4. 这可以是一个受控过程,因此只有在必要/认为重要的时候才能推送安全补丁。

        【讨论】:

        • 在生产环境中,虽然它们是安全更新,但我怀疑您是否想要无人参与的更新!如果需要进行无人值守的更新,则可以定期(根据需要)将进程作为 cron 作业运行。
        • 我的前提是安全更新应该来自上游/基础镜像。
        • @hbogert 我宁愿说理论和实践之间存在细微差别。当事情付诸实践时,将需要考虑许多外部方面,例如:与实施相关的成本(不仅是美元价值,还有时间)。
        【解决方案14】:

        以上答案也正确

        有两种方法

        1. 使用网络钩子
        2. 每特定分钟运行一次脚本以获取最新的 docker 镜像

        我只是分享脚本可能对你有帮助! 您可以将它与 cronjob 一起使用,我在 OSX 上成功尝试过

        #!/bin/bash
        ##You can use below commented line for setting cron tab for running cron job and to store its O/P in one .txt file  
        #* * * * * /usr/bin/sudo -u admin -i bash -c /Users/Swapnil/Documents/checkimg.sh > /Users/Swapnil/Documents/cron_output.log 2>&1
        # Example for the Docker Hub V2 API
        # Returns all images and tags associated with a Docker Hub organization account.
        # Requires 'jq': https://stedolan.github.io/jq/
        
        # set username, password, and organization
        # Filepath where your docker-compose file is present
        FILEPATH="/Users/Swapnil/Documents/lamp-alpine"
        # Your Docker hub user name
        UNAME="ur username"
        # Your Docker hub user password
        UPASS="ur pwd"
        # e.g organisation_name/image_name:image_tag
        ORG="ur org name"
        IMGNAME="ur img name"
        IMGTAG="ur img tag"
        # Container name
        CONTNAME="ur container name"
        # Expected built mins
        BUILDMINS="5"
        #Generally cronjob frequency
        CHECKTIME="5"
        NETWORKNAME="${IMGNAME}_private-network"
        #After Image pulling, need to bring up all docker services?
        DO_DOCKER_COMPOSE_UP=true
        # -------
        echo "Eecuting Script @ date and time in YmdHMS: $(date +%Y%m%d%H%M%S)"
        set -e
        PIDFILE=/Users/Swapnil/Documents/$IMGNAME/forever.pid
        if [ -f $PIDFILE ]
        then
          PID=$(cat $PIDFILE)
          ps -p $PID > /dev/null 2>&1
          if [ $? -eq 0 ]
          then
            echo "Process already running"
            exit 1
          else
            ## Process not found assume not running
            echo $$
            echo $$ > $PIDFILE
            if [ $? -ne 0 ]
            then
              echo "Could not create PID file"
              exit 1
            fi
          fi
        else
          echo $$ > $PIDFILE
          if [ $? -ne 0 ]
          then
            echo "Could not create PID file"
            exit 1
          fi
        fi
        
        # Check Docker is running or not; If not runing then exit
        if docker info|grep Containers ; then
            echo "Docker is running"
        else
            echo "Docker is not running"
            rm $PIDFILE
            exit 1
        fi
        
        # Check Container is running or not; and set variable
        CONT_INFO=$(docker ps -f "name=$CONTNAME" --format "{{.Names}}")
        if [ "$CONT_INFO" = "$CONTNAME" ]; then
            echo "Container is running"
            IS_CONTAINER_RUNNING=true
        else
            echo "Container is not running"
            IS_CONTAINER_RUNNING=false
        fi
        
        
        # get token
        echo "Retrieving token ..."
        TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d '{"username": "'${UNAME}'", "password": "'${UPASS}'"}' https://hub.docker.com/v2/users/login/ | jq -r .token)
        
        # get list of repositories
        echo "Retrieving repository list ..."
        REPO_LIST=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/${ORG}/?page_size=100 | jq -r '.results|.[]|.name')
        
        # output images & tags
        echo "Images and tags for organization: ${ORG}"
        echo
        for i in ${REPO_LIST}
        do
          echo "${i}:"
          # tags
          IMAGE_TAGS=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/${ORG}/${i}/tags/?page_size=100 | jq -r '.results|.[]|.name')
          for j in ${IMAGE_TAGS}
          do
            echo "  - ${j}"
          done
          #echo
        done
        
        # Check Perticular image is the latest or not
        #imm=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/${ORG}/${IMGNAME}/tags/?page_size=100)
        echo "-----------------"
        echo "Last built date details about Image ${IMGNAME} : ${IMGTAG} for organization: ${ORG}"
        IMAGE_UPDATED_DATE=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/${ORG}/${IMGNAME}/tags/?page_size=100 | jq -r '.results|.[]|select(.name | contains("'${IMGTAG}'")).last_updated')
        echo "On Docker Hub IMAGE_UPDATED_DATE---$IMAGE_UPDATED_DATE"
        echo "-----------------"
        
        IMAGE_CREATED_DATE=$(docker image inspect ${ORG}/${IMGNAME}:${IMGTAG} | jq -r '.[]|.Created')
        echo "Locally IMAGE_CREATED_DATE---$IMAGE_CREATED_DATE"
        
        updatedDate=$(date -jf '%Y-%m-%dT%H:%M' "${IMAGE_UPDATED_DATE:0:16}" +%Y%m%d%H%M%S) 
        createdDate=$(date -jf '%Y-%m-%dT%H:%M' "${IMAGE_CREATED_DATE:0:16}" +%Y%m%d%H%M%S)
        currentDate=$(date +%Y%m%d%H%M%S)
        
        start_date=$(date -jf "%Y%m%d%H%M%S" "$currentDate" "+%s")
        end_date=$(date -jf "%Y%m%d%H%M%S" "$updatedDate" "+%s")
        updiffMins=$(( ($start_date - $end_date) / (60) ))
        if [[ "$updiffMins" -lt $(($CHECKTIME+1)) ]]; then
                if [ ! -d "${FILEPATH}" ]; then
                    mkdir "${FILEPATH}";
                fi
                cd "${FILEPATH}"
                pwd
                echo "updatedDate---$updatedDate" > "ScriptOutput_${currentDate}.txt"
                echo "createdDate---$createdDate" >> "ScriptOutput_${currentDate}.txt"
                echo "currentDate---$currentDate" >> "ScriptOutput_${currentDate}.txt"
                echo "Found after regular checking time -> Docker hub's latest updated image is new; Diff ${updiffMins} mins" >> "ScriptOutput_${currentDate}.txt"
                echo "Script is checking for latest updates after every ${CHECKTIME} mins" >> "ScriptOutput_${currentDate}.txt"
                echo "Fetching all new"
                echo "---------------------------"
                if $IS_CONTAINER_RUNNING ; then
                    echo "Container is running"         
                else
                    docker-compose down
                    echo "Container stopped and removed; Network removed" >> "ScriptOutput_${currentDate}.txt"
                fi
                echo "Image_Created_Date=$currentDate" > ".env"
                echo "ORG=$ORG" >> ".env"
                echo "IMGNAME=$IMGNAME" >> ".env"
                echo "IMGTAG=$IMGTAG" >> ".env"
                echo "CONTNAME=$CONTNAME" >> ".env"
                echo "NETWORKNAME=$NETWORKNAME" >> ".env"
                docker-compose build --no-cache
                echo "Docker Compose built" >> "ScriptOutput_${currentDate}.txt"
                if $DO_DOCKER_COMPOSE_UP ; then
                    docker-compose up -d
                    echo "Docker services are up now, checked in" >> "ScriptOutput_${currentDate}.txt"  
                else
                    echo "Docker services are down, checked in" >> "ScriptOutput_${currentDate}.txt"
                fi
        elif [[ "$updatedDate" -gt "$createdDate" ]]; then 
            echo "Updated is latest"
            start_date=$(date -jf "%Y%m%d%H%M%S" "$updatedDate" "+%s")
            end_date=$(date -jf "%Y%m%d%H%M%S" "$createdDate" "+%s")
            diffMins=$(( ($start_date - $end_date) / (60) ))
            if [[ "$BUILDMINS" -lt "$diffMins" ]]; then
                if [ ! -d "${FILEPATH}" ]; then
                    mkdir "${FILEPATH}";
                fi
                cd "${FILEPATH}"
                pwd
                echo "updatedDate---$updatedDate" > "ScriptOutput_${currentDate}.txt"
                echo "createdDate---$createdDate" >> "ScriptOutput_${currentDate}.txt"
                echo "currentDate---$currentDate" >> "ScriptOutput_${currentDate}.txt"
                echo "Found after comparing times -> Docker hub's latest updated image is new; Diff ${diffMins} mins" >> "ScriptOutput_${currentDate}.txt"
                echo "Actual image built time is less i.e. ${diffMins} mins than MAX expexted BUILD TIME i.e. ${BUILDMINS} mins" >> "ScriptOutput_${currentDate}.txt"
                echo "Fetching all new" >> "ScriptOutput_${currentDate}.txt"
                echo "-----------------------------"
                if $IS_CONTAINER_RUNNING ; then
                    echo "Container is running"         
                else
                    docker-compose down
                    echo "Container stopped and removed; Network removed" >> "ScriptOutput_${currentDate}.txt"
                fi
                echo "Image_Created_Date=$currentDate" > ".env"
                echo "ORG=$ORG" >> ".env"
                echo "IMGNAME=$IMGNAME" >> ".env"
                echo "IMGTAG=$IMGTAG" >> ".env"
                echo "CONTNAME=$CONTNAME" >> ".env"
                echo "NETWORKNAME=$NETWORKNAME" >> ".env"
                docker-compose build --no-cache
                echo "Docker Compose built" >> "ScriptOutput_${currentDate}.txt"
                if $DO_DOCKER_COMPOSE_UP ; then
                    docker-compose up -d
                    echo "Docker services are up now" >> "ScriptOutput_${currentDate}.txt"  
                else
                    echo "Docker services are down" >> "ScriptOutput_${currentDate}.txt"
                fi
            elif [[ "$BUILDMINS" -gt "$diffMins" ]]; then
                echo "Docker hub's latest updated image is NOT new; Diff ${diffMins} mins"
                echo "Docker images not fetched"
            else
                echo "Docker hub's latest updated image is NOT new; Diff ${diffMins} mins"
                echo "Docker images not fetched"
            fi
        elif [[ "$createdDate" -gt "$updatedDate" ]]; then 
            echo "Created is latest"
            start_date=$(date -jf "%Y%m%d%H%M%S" "$createdDate" "+%s")
            end_date=$(date -jf "%Y%m%d%H%M%S" "$updatedDate" "+%s")
            echo "Docker hub has older docker image than local; Older than $(( ($start_date - $end_date) / (60) ))mins"
        fi
        echo 
        echo "------------end---------------"
        rm $PIDFILE
        

        这是我的 docker-compose 文件

        version:  "3.2"
        services:
          lamp-alpine:
            build:
              context: .
            container_name: "${CONTNAME}"
            image: "${ORG}/${IMGNAME}:${IMGTAG}"
            ports:
              - "127.0.0.1:80:80"
            networks:
              - private-network 
        
        networks:
          private-network:
            driver: bridge
        

        【讨论】:

          【解决方案15】:

          您是否尝试过:https://github.com/v2tec/watchtower。 它是一个在 docker 容器中运行的简单工具,用于监视其他容器,如果它们的基本映像发生更改,它将拉取并重新部署。

          【讨论】:

            【解决方案16】:

            一个简单而伟大的解决方案是shepherd

            【讨论】:

            • iiuc,这在一般意义上没有帮助,因为它与 Swarm 耦合并且仅在上游重新启动,而我们想要对上游更改做出反应、重建等不是简单的重启。
            • 这听起来像是你应该在 CI 管道中做的事情
            猜你喜欢
            • 2022-01-22
            • 1970-01-01
            • 1970-01-01
            • 2021-12-02
            • 1970-01-01
            • 1970-01-01
            • 2015-07-14
            • 2020-01-20
            • 1970-01-01
            相关资源
            最近更新 更多