如果更新了基础图像,如何自动更新您的Docker容器

说我有一个微不足道的容器基于ubuntu:latest 。 现在有一个安全更新和ubuntu:latest更新在docker回购。

1)我怎么知道我的本地图像,它的容器在后面运行?

2)是否有一些最佳做法是自动更新本地映像和容器以遵循docker repo更新,这在实践中会使您在常规ubuntu-machine上运行无人值守升级

我们使用一个脚本来检查正在运行的容器是否启动了最新的映像。 我们也使用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 

和init看起来像

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

我有同样的问题,并认为它可以简单地解决一个cron工作每天调用unattended-upgrade

我的意图是将此作为自动快速解决scheme,以确保生产容器的安全性和更新,因为可能需要一些时间来更新我的映像,并使用最新的安全更新部署新的Docker映像。

也可以使用Github钩子自动化图像构build和部署

我创build了一个基本的docker镜像 ,每天自动检查和安装安全更新(可以通过docker run itech/docker-unattended-upgrade直接docker run itech/docker-unattended-upgrade )。

我还遇到另一种不同的方法来检查容器是否需要更新。

我的完整实现:

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容器运行 ,可以用来更新所有或选定的运行容器中的包,也可以用来运行任意的命令。

可以使用以下命令轻松testing:

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中心 自动构build 。 Repository Linksfunction将在重build上游容器时重build您的容器, Webhooksfunction将向您发送通知。

它看起来像webhooks仅限于HTTP POST调用。 你需要设置一个服务来捕捉它们,或者使用POST之一来发送电子邮件。

我没有看到它,但新的Docker通用控制平面可能具有检测更新的容器和重新部署的function。

您可以使用Watchtower监视容器实例化的映像的更新,并自动提取更新并使用更新的映像重新启动容器。 但是,这并不能解决在上游图像发生变化时重build自定义图像的问题。 您可以将其视为一个两部分问题:(1)知道上游映像何时更新,以及(2)是否进行实际映像重build。 (1)可以很容易地解决,但(2)很大程度上取决于您当地的构build环境/实践,所以创build一个广义的解决scheme可能更困难。

如果你能够使用Docker Hub的自动化版本 ,那么可以使用版本库链接function相对干净地解决整个问题,当链接库(可能是上游版本)更新时,可以自动触发重build。 您也可以configurationwebhook来在发生自动构build时通知您。 如果您需要电子邮件或短信通知,则可以将Webhook连接到IFTTT Maker 。 我发现IFTTT用户界面有点令人困惑,但是您可以configurationDocker webhook以发布到https://maker.ifttt.com/trigger/ docker_xyz_image_built / with / key / your_key

如果您需要在本地构build,至less可以通过在Docker Hub中创build一个与您感兴趣的仓库关联的虚拟仓库来更新上游映像,从而解决通知的问题。 虚拟回购的唯一目的是触发webhook,当它被重build(这意味着其中一个连接的回购更新)。 如果你能够收到这个webhook,你甚至可以用它来触发你的重build。

你不知道你的容器是在没有运行docker pull的情况下 。 那么你需要重build或重构你的形象。

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

尽pipe一个合适的容器不需要额外的东西,但是这些命令可以放在一个脚本中,以及其他任何必需的东西来完成升级。

我不打算是否要在生产中进行无人值守的更新(我认为不是)。 如果有人发现它有用,我只是把它留在这里作为参考。 在terminal中使用以下命令将所有Docker镜像更新到最新版本:

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

这里有很多答案,但是没有一个适合我的需求。 我想要一个真正的答案提问者的第一个问题。 如何知道在hub.docker.com上更新图片的时间?

下面的脚本可以每天运行。 首次运行时,它会从HUBregistry中获取标记的基线和更新date,并将其保存在本地。 从那时起,每次运行它都会检查registry以查找新标签并更新date。 由于每次存在新图像时都会发生变化,因此会告诉我们基础图像是否已更改。 这是脚本:

 #!/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; 

您将需要更改顶部的DATAPATHvariables,并在最后更改电子邮件通知命令以满足您的需要。 对于我来说,我已经将SSH连接到SMTP所在的另一个networking上的服务器上。 但是您也可以轻松使用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 

另一种方法是假设你的基础镜像很快就落后了(这很可能会发生),并且定期(例如每周)强制你的应用程序的另一个镜像构build,如果它发生了变化,就重新部署它。

据我所知,官方的Debian或Java等stream行的基础镜像更新他们的标签,以迎合安全问题,所以标签并不是不可变的(如果你想更强有力的保证你需要使用这个参考文件, ],在更新的Docker版本中可用)。 因此,如果要使用docker build --pull构build图像,则应用程序应该获取所引用的docker build --pull的图像标记。

由于可变标签可能会造成混淆,因此最好每次增加应用程序的版本号,这样至less可以在你身边做得更干净。

所以我不确定在之前的答案中提到的脚本是否能完成这个工作,因为它不会重build应用程序的映像 – 它只是更新基本映像标记,然后重新启动容器,但是新的容器仍然引用旧的基础图像散列。

我不会主张在容器(或任何其他进程,除非真的有必要)上运行crontypes的作业,因为这违背了每个容器只运行一个进程的口号(关于为什么这个更好,有各种各样的争论,我不打算在这里进入)。

前提我的回答:

  1. 容器与标签一起运行。
  2. 同样的标签可以指向不同的图像UUID,只要我们愿意/感觉合适。
  3. 对图像进行的更新可以提交给新的图像层

途径

  1. 首先用安全补丁更新脚本构build所有容器
  2. 为以下内容构build一个自动化过程
    • 使用安全修补程序脚本作为命令将现有映像运行到新容器
    • 将更改提交到图像
      • 现有标签 – >,然后逐个重新启动容器
      • 新版本标签 – >用新标签replaceless量容器 – >validation – >将所有容器移动到新标签

此外,基础图像可以升级/具有完整的新基础图像的容器可以定期build立,维护者觉得有必要

优点

  1. 我们在创build新的安全补丁图像时保留旧版本的图像,因此如有必要,我们可以回滚到之前的运行图像
  2. 我们正在保留dockercaching,因此减less了networking传输(只有更改后的图层才能在networking上传输)
  3. 在升级之前,可以在升级中validation升级过程
  4. 这可以是一个受控制的过程,因此只有在必要/认为重要的情况下才可以推送安全补丁。

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