如何在映像更改后升级docker容器

How to upgrade docker container after its image changed

假设我已经提取了官方的mysql:5.6.21图像。

我已经通过创建几个Docker容器部署了这个图像。

这些容器已经运行了一段时间,直到MySQL5.6.22发布。mysql:5.6的官方形象随着新版本的更新而更新,但是我的容器仍然运行5.6.21。

如何将映像中的更改(例如,升级mysql disro)传播到所有现有容器中?正确的码头工人方法是什么?


在评估答案和研究主题之后,我想总结一下。

Docker升级容器的方法如下:

应用程序容器不应存储应用程序数据。这样,您可以通过执行以下操作随时用其更新版本替换应用程序容器:

1
2
3
4
5
docker pull mysql
docker stop my-mysql-container
docker rm my-mysql-container
docker run --name=my-mysql-container --restart=always \
  -e MYSQL_ROOT_PASSWORD=mypwd -v /my/data/dir:/var/lib/mysql -d mysql

您可以将数据存储在主机(作为卷装入的目录中)或特殊的仅数据容器中。了解更多信息

  • 关于卷(Docker文档)
  • 松散连接的小型码头部件(由Tom Offermann提供)
  • 如何处理Docker中的持久存储(如数据库)(堆栈溢出问题)

在容器中升级应用程序(如yum/apt-get-upgrade)被认为是一种反模式。应用程序容器应该是不可变的,这将保证可重复的行为。一些官方应用程序映像(尤其是mysql:5.6)甚至不能自我更新(apt-get升级无法工作)。

我要感谢所有给出答案的人,这样我们就能看到所有不同的方法。


我不喜欢将卷作为到主机目录的链接来装载,所以我想出了一个模式,用完全由Docker管理的容器来升级Docker容器。使用--volumes-from 创建一个新的docker容器将使新容器具有更新后的映像,共享docker管理的卷的所有权。

1
2
docker pull mysql
docker create --volumes-from my_mysql_container [...] --name my_mysql_container_tmp mysql

如果升级后的容器没有正确的数据,或者没有通过健全性测试,那么通过不立即删除原来的my_mysql_container,您可以恢复到已知的工作容器。

此时,我通常会运行容器所拥有的任何备份脚本,以便在出现问题时为自己提供一个安全网。

1
2
docker stop my_mysql_container
docker start my_mysql_container_tmp

现在,您有机会确保您期望的新容器中的数据在那里,并运行健全性检查。

1
2
docker rm my_mysql_container
docker rename my_mysql_container_tmp my_mysql_container

只要任何容器正在使用,Docker卷就会一直保留,这样您就可以安全地删除原始容器。一旦原始容器被移除,新容器就可以假定原始容器的名称,以使所有东西都像开始时一样漂亮。

使用此模式升级Docker容器有两个主要优势。首先,通过允许卷直接传输到升级的容器,它消除了将卷装载到主机目录的需要。其次,您永远不会处于没有可工作的Docker容器的位置;因此,如果升级失败,您可以通过再次旋转原始Docker容器轻松恢复到之前的工作状态。


我想补充一点,如果您想自动执行这个过程(下载、停止和重新启动一个设置与@yaroslav所描述的相同的新容器),您可以使用WatchTower。当容器更改时自动更新容器的程序https://github.com/v2tec/watchtower


考虑这个答案:

  • 数据库名称为app_schema
  • 容器名称为app_db
  • 根密码是root123

在容器中存储应用程序数据时如何更新MySQL

这被认为是一种糟糕的做法,因为如果丢失容器,您将丢失数据。虽然这是一种不好的做法,但有一种可行的方法:

1)将数据库转储为SQL:

1
docker exec app_db sh -c 'exec mysqldump app_schema -uroot -proot123' > database_dump.sql

2)更新图像:

1
docker pull mysql:5.6

3)更新容器:

1
2
3
4
docker rm -f app_db
docker run --name app_db --restart unless-stopped \
-e MYSQL_ROOT_PASSWORD=root123 \
-d mysql:5.6

4)恢复数据库转储:

1
docker exec app_db sh -c 'exec mysql -uroot -proot123' < database_dump.sql

如何使用外部卷更新mysql容器

使用外部卷是一种更好的数据管理方法,而且它更容易更新MySQL。松开容器不会丢失任何数据。您可以使用Docker Compose在单个主机中方便地管理多容器Docker应用程序:

1)创建docker-compose.yml文件以管理您的应用程序:

1
2
3
4
5
6
7
8
version: '2'
services:
  app_db:
    image: mysql:5.6
    restart: unless-stopped
    volumes_from: app_db_data
  app_db_data:
    volumes: /my/data/dir:/var/lib/mysql

2)更新mysql(来自与docker-compose.yml文件相同的文件夹):

1
2
docker-compose pull
docker-compose up -d

注意:上面的最后一个命令将更新mysql映像,重新创建并用新映像启动容器。


只是为了提供一个更一般(不是特定于MySQL)的答案…

  • 简而言之
  • 与服务映像注册表同步(https://docs.docker.com/compose/compose file/image):

    1
    docker-compose pull

    如果Docker撰写文件或图像已更改,请重新创建容器:

    1
    docker-compose up -d
  • 背景
  • 容器图像管理是使用Docker Compose的原因之一(见https://docs.docker.com/compose/reference/up/)

    If there are existing containers for a service, and the service’s configuration or image was changed after the container’s creation, docker-compose up picks up the changes by stopping and recreating the containers (preserving mounted volumes). To prevent Compose from picking up changes, use the --no-recreate flag.

    Docker Compose还通过安装的外部"卷"(请参见https://docs.docker.com/compose/compose-file/volumes)或数据容器覆盖数据管理方面。

    这使得潜在的向后兼容性和数据迁移问题没有受到影响,但这些问题是"适用的"问题,而不是特定于Docker的问题,必须对照发行说明和测试进行检查…


    类似于上面的答案

    1
    docker images | awk '{print $1}' | grep -v 'none' | grep -iv 'repo' | xargs -n1 docker pull


    下面是在构建自定义Dockerfile时使用docker-compose的情况。

  • 首先构建自定义Dockerfile,附加下一个版本号以区分。例:docker build -t imagename:version .这将在本地存储您的新版本。
  • 运行docker-compose down
  • 编辑您的docker-compose.yml文件,以反映在步骤1中设置的新图像名称。
  • 运行docker-compose up -d。它将在本地查找图像并使用升级后的图像。
  • -编辑-

    我上面的步骤比它们需要的更详细。我已经通过将build: .参数包含到docker compose文件中优化了我的工作流程。步骤如下:

  • 验证我的dockerfile是我想要的样子。
  • 在Docker撰写文件中设置图像名称的版本号。
  • 如果我的形象还没有建立:运行docker-compose build
  • 运行docker-compose up -d
  • 当时我还没有意识到,但是Docker Compose足够聪明,只需用一个命令将容器更新到新图像,而不必先将其放下。


    如果你不想使用Docker Compose,我可以推荐Portainer。它有一个重新创建功能,允许您在提取最新图像时重新创建容器。


    摘自http://blog.stefanxo.com/2014/08/update-all-docker-images-at-once/

    可以使用以下命令管道更新所有现有图像:

    1
    docker images | awk '/^REPOSITORY|\<none\>/ {next} {print $1}' | xargs -n 1 docker pull


    您需要重建所有图像并重新启动所有容器,或者以某种方式yum更新软件并重新启动数据库。没有升级路径,只有您自己设计。


    确保将卷用于存储在与容器内进程状态相关的容器上的所有持久数据(配置、日志或应用程序数据)。更新dockerfile并使用所需的更改重新生成映像,然后重新启动容器,并将卷装入它们的适当位置。


    我也有同样的问题,所以我创建了docker run,一个非常简单的命令行工具,它运行在docker容器中,以更新其他运行容器中的包。

    它使用docker py与运行docker容器和更新包通信,或者运行任意的单个命令。

    实例:

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

    默认情况下,这将在所有正在运行的容器中运行date命令并返回结果,但您可以发出任何命令,例如docker-run exec"uname -a"命令。

    要更新包(当前仅使用apt get):

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

    您可以创建和别名,并将其用作常规命令行例如

    alias docker-run='docker run --rm -v /var/run/docker.sock:/tmp/docker.sock itech/docker-run'


    这也是我一直在为自己的照片苦苦挣扎的事情。我有一个服务器环境,从中创建Docker映像。当我更新服务器时,我希望所有运行基于我的Docker映像的容器的用户都能够升级到最新的服务器。

    理想情况下,我更喜欢生成Docker映像的新版本,并让所有容器基于该映像的早期版本自动更新到新映像的"适当位置"。但这种机制似乎不存在。

    因此,到目前为止,我能想到的下一个最好的设计是提供一种让容器自己更新的方法——类似于桌面应用程序检查更新然后自我升级的方法。在我的例子中,这可能意味着要从一个著名的标签中创建一个涉及git的脚本。

    图像/容器实际上不会改变,但容器的"内部"会改变。你可以想象用apt-get、yum或任何适合你环境的工具也可以这样做。除此之外,我还将更新注册表中的myserver:latest映像,以便任何新容器都将基于最新映像。

    我很想知道是否有现有的技术可以解决这个问题。