更改镜像后如何升级Docker容器


518

假设我已经拉出了官方的mysql:5.6.21 image

我已经通过创建多个docker容器部署了此映像。

这些容器已经运行了一段时间,直到MySQL 5.6.22发布。mysql:5.6的官方映像已随新版本更新,但我的容器仍运行5.6.21。

如何将映像中的更改(即升级MySQL发行版)传播到所有现有容器?这样做的正确Docker方法是什么?

Answers:


578

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

Docker升级容器的方式似乎如下:

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

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

您可以将数据存储在主机上(作为卷安装的目录中)或用于特殊数据的容器中。进一步了解

在容器内升级应用程序(例如,使用yum / apt-get升级)被认为是一种反模式。应用程序容器应该是不可变的,这将保证可重现的行为。某些官方应用程序映像(尤其是mysql:5.6)甚至没有设计为自我更新(apt-get升级将不起作用)。

我要感谢提供答案的每个人,以便我们能看到所有不同的方法。


31
如果需要数据迁移怎么办?新服务器无法装载数据,因为它采用的是旧格式,它需要知道正在进行迁移并更改数据表示形式。
Dor Rotman

12
我认为,图像设计人员应对此负责,并允许在容器的首次运行期间启动自定义(例如,数据迁移)命令。
Yaroslav Stavnichiy 2015年


4
@static_rtti docker rename my-mysql-container trash-container在创建新的之前怎么样?
富兰克林·于

4
是否有任何一站式命令来更新容器,而不必手动停止它,将其删除并重新创建(基于已拉出的新映像)?
米歇尔·佩林

83

我不喜欢将卷安装为主机目录的链接,所以我想出了一种使用完全由Docker管理的容器升级Docker容器的模式。使用创建新的docker容器--volumes-from <container>将为具有更新的映像的新容器提供docker托管卷的共享所有权。

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

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

在这一点上,我通常将运行容器拥有的所有备份脚本,以防万一出问题

docker stop my_mysql_container
docker start my_mysql_container_tmp

现在,您可以确保希望将新容器中的数据保存在其中,并进行完整性检查。

docker rm my_mysql_container
docker rename my_mysql_container_tmp my_mysql_container

只要任何容器正在使用docker卷,它们都会一直存在,因此您可以安全地删除原始容器。删除原始容器后,新容器可以采用原始容器的名称,从而使一切都变得与开始时一样漂亮。

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


3
您为什么不喜欢在Docker容器中装载主机卷?(我正在这样做,所以我对反对这样做的论点很感兴趣./postgres-data/:/var/lib/postgres/data./postgres-data/
:-)

4
@KajMagnus我经常使用docker swarms,我喜欢编写自己的容器以在swarm中正常工作。当我在一个群集中旋转一个容器时,我不知道该容器将要驻留在哪个群集节点上,因此我不能依赖包含所需数据的主机路径。由于Docker 1.9(我认为)卷可以在主机之间共享,因此使用我描述的方法可以轻松升级和迁移容器。另一种选择是确保在所有群集节点上都安装了某些网络卷,但这听起来很难维护。
kMaiSmith '16

谢谢!好的,装载主机卷现在似乎也想避免。如果我的应用程序变得流行并且需要扩展到多个服务器
至少要过一会儿

32

只是为了提供一个更通用(不是特定于mysql)的答案...

  1. 简而言之

与服务映像注册表同步(https://docs.docker.com/compose/compose-file/#image):

docker-compose pull 

如果docker-compose文件或映像已更改,请重新创建容器:

docker-compose up -d
  1. 背景

容器映像管理是使用docker-compose的原因之一(请参阅https://docs.docker.com/compose/reference/up/

如果已有用于服务的容器,并且在创建容器后更改了服务的配置或映像,则docker-compose up通过停止并重新创建容器(保留已安装的卷)来获取更改。为防止Compose获取更改,请使用--no-recreate标志。

docker-compose还可以通过安装的外部“卷”(请参阅https://docs.docker.com/compose/compose-file/#volumes)或数据容器来涵盖数据管理方面。

这使得潜在的向后兼容性和数据迁移问题没有得到解决,但是这些是“应用性”问题,而不是特定于Docker的问题,必须根据发行说明和测试进行检查...


您如何使用版本控制?例如,新映像为foo / image:2,而docker-compose.yml具有映像:foo / image:1?
dman

谢谢。最佳答案!
米克

尽管这肯定是可行的方法,但应注意,一旦重新创建容器,对容器所做的任何更改仍将丢失。因此,仍然需要将容器更改保持在已装入的卷内。
彼得·波德纳(PetrBodnár)


20

考虑以下答案:

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

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

这被认为是不好的做法,因为如果丢失容器,则会丢失数据。尽管这是一个不好的做法,但是这是一种可行的方法:

1)将数据库转储为SQL:

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

2)更新图片:

docker pull mysql:5.6

3)更新容器:

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

4)还原数据库转储:

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

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

使用外部卷是管理数据的更好方法,并且使更新MySQL变得更加容易。松开容器不会丢失任何数据。您可以使用docker-compose来管理单个主机中的多容器Docker应用程序:

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

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文件相同的文件夹中):

docker-compose pull
docker-compose up -d

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


假设我有一个巨大的数据库(几个GB),在导入整个数据库之前,我的数据将无法访问吗?那可能是一个巨大的“停机时间”
hellimac'1

既然您提到过docker-compose,这行得通吗?stackoverflow.com/a/31485685/65313
sivabudh

1
volumes_from现在不推荐使用key(甚至在compose文件的版本3中已将其删除),以使用新的volumeskey。
富兰克林·于

docker pull image_uri:tag && docker restart container_running_that_image为我工作。不需要docker-compose pull && docker-compose up -d
尤里·波兹尼亚克

16

与上述类似的答案

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

1
辉煌!很惊讶它没有获得更多的选票。现在唯一缺少的是触发所有已更新容器的重启。
索林

7
不幸的是,这不会更新现有的容器。这将仅更新提取的图像,但是现有容器是不可变的,并且仍使用用于创建它的原始图像。仅当您从图像创建新容器时,此方法才有效,但是任何现有容器仍基于原始图像。
Eric B.

惊人。如果您需要拉出容器的特定版本,请按照以下步骤操作:docker images | awk'{print $ 1“:” $ 2}'| grep -v'none'| grep -iv'repo'| xargs -n1
docker

11

这是docker-compose构建自定义时使用的外观Dockerfile

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

-编辑-

我上面的步骤比他们需要的更为冗长。我通过将build: .参数包含到docker-compose文件中来优化了我的工作流程。步骤如下所示:

  1. 确认我的Dockerfile是我想要的样子。
  2. 在我的docker-compose文件中设置我的映像名称的版本号。
  3. 如果尚未建立我的映像:运行 docker-compose build
  4. docker-compose up -d

当时我没有意识到,但是docker-compose足够聪明,只需使用一个命令即可将容器更新为新映像,而不必先将其关闭。


在实际情况下,您无法用自己的双手进行更改。您的解决方案不支持自动解决问题的方法。
卡洛斯·巴斯克斯·洛萨达(CarlosVázquezLosada)

7
所以您是说因为我的解决方案不是自动化的,所以无效吗?OP的要求吗?还有其他答案暗示着自动化吗?真的很困惑。而且,我认为反对派人士对来这里的其他人不利。我的回答对于所提问题是100%有效的。
gdbj

感谢您提供此答案,我也没有意识到您可以先运行docker-compose up -d而不必先停止所有操作。
开方

4

如果您不想使用Docker Compose,建议您使用portainer。它具有重新创建功能,可让您在提取最新映像的同时重新创建容器。


2

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


重新启动容器到底是什么意思?有docker restart命令,但是我不确定它将获取图像更改。容器中的数据又如何处理?
Yaroslav Stavnichiy 2014年

1
抱歉,我并不是说docker restart。我的意思是docker rm -f CONTANER; docker运行NEW_IMAGE。您的sql容器中的数据将消失。这就是为什么人们通常使用卷来存储数据的原因。
seanmcl 2014年

如果您将所有数据装入到单独容器或主机中的卷中,则nas @@ seanmcl表示只需使用连接到相同数据的新mysql创建新容器。如果不这样做(应该这样做),但是可以使用docker 1.3中提供的docker exec命令来更新mysql并在容器内重新启动它。
Usman Ismail 2014年


2

确保对存储在容器上的所有持久性数据(配置,日志或应用程序数据)使用卷,该卷与该容器内的进程状态相关。更新您的Dockerfile并使用所需的更改重建映像,然后在将卷安装到适当位置后重新启动容器。


1

对于我自己的图像,这也是我一直在努力的事情。我有一个从中创建Docker映像的服务器环境。更新服务器时,我希望所有基于Docker映像运行容器的用户能够升级到最新服务器。

理想情况下,我希望生成一个新版本的Docker映像,并基于该映像的先前版本将所有容器自动地“更新”到新映像。但是这种机制似乎并不存在。

因此,到目前为止,我能想到的下一个最佳设计是提供一种使容器自身更新的方法,类似于桌面应用程序检查更新然后自我升级的方式。就我而言,这可能意味着要编写一个脚本,该脚本涉及到来自知名标签的Git拉取。

图像/容器实际上并没有更改,但是该容器的“内部”更改了。您可以想象使用apt-get,yum或任何适合您的环境的方法进行相同的操作。与此同时,我将更新注册表中的myserver:latest映像,以便所有新容器都将基于最新映像。

我想听听是否有任何现有技术可以解决这种情况。


7
它违背了不变基础设施的概念及其某些好处。您可以测试您的应用程序/环境,以查看它是否有效,如果您更新其中的组件,则不能保证。从配置数据中分离出容器代码后,您就可以更新,测试我们当前正在工作并部署到生产中,同时知道从被测试映像到生产映像没有不同的代码行。无论如何,该系统也让您按自己说的进行管理,这是您的选择。
gmuslera 2014年

非常好点的musculera。同意更新现有Docker容器的“内部”是一种反模式。
bjlevine 2014年

1

更新资料

这主要是查询容器不更新,因为构建映像是一种方法

我遇到了同样的问题,所以我创建了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'


这是一个好主意吗?(如果您这样做apt update; apt upgrade,图像将会增长。)
ctrl-alt-delor

@yaroslav的图像是解决此问题的更好方法。上面的并不是真正的Docker服务方式。
Joost van der Laan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.