Как обновить докер-контейнер после изменения его образа

518

Допустим, я вытащил официальное изображение mysql: 5.6.21 .

Я развернул этот образ, создав несколько док-контейнеров.

Эти контейнеры работали в течение некоторого времени, пока не будет выпущен MySQL 5.6.22. Официальный образ mysql: 5.6 обновляется с новым выпуском, но мои контейнеры по-прежнему работают 5.6.21.

Как распространить изменения в образе (т.е. обновить дистрибутив MySQL) на все мои существующие контейнеры? Как правильно Docker сделать это?

Ярослав Ставничий
источник

Ответы:

579

Оценив ответы и изучив тему, я хотел бы подвести итог.

Способ обновления контейнеров с помощью 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 upgrade не будет работать).

Я хотел бы поблагодарить всех, кто дал свои ответы, чтобы мы могли видеть все разные подходы.

Ярослав Ставничий
источник
31
Что если миграция данных необходима? Новый сервер не может подключить данные, потому что он в старом формате, он должен знать, что происходит миграция, и изменить представление данных.
Дор Ротман
12
Я думаю, что дизайнеры изображений должны учитывать это и разрешать запуск пользовательских команд (например, перенос данных) во время первого запуска контейнера.
Ярослав Ставничий
2
Соответствующая статья: intercityup.com/blog/running-upgrading-mysql-server-docker
Дор Ротман
4
@static_rtti Как насчет docker rename my-mysql-container trash-containerтого, чтобы создать новый?
Франклин Ю
4
Будет ли какая-либо команда «все в одном» обновить контейнер, не останавливая его вручную, не удаляя и не создавая его снова (на основе нового извлеченного образа)?
Микаэль Перрен
83

Мне не нравится монтировать тома как ссылку на каталог хоста, поэтому я придумал шаблон для обновления докерских контейнеров с полностью управляемыми докерами контейнерами. Создание нового док-контейнера с--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

Тома докеров будут храниться, пока их использует любой контейнер, поэтому вы можете безопасно удалить исходный контейнер. Как только оригинальный контейнер удален, новый контейнер может принять тезку оригинала, чтобы сделать все так же красиво, как и должно было начаться.

Есть два основных преимущества использования этого шаблона для обновления док-контейнеров. Во-первых, это устраняет необходимость монтировать тома в каталоги хостов, позволяя напрямую переносить тома в обновленные контейнеры. Во-вторых, вы никогда не окажетесь в положении, когда нет рабочего док-контейнера; поэтому, если обновление завершится неудачно, вы можете легко вернуться к тому, как оно работало раньше, снова раскрутив исходный док-контейнер.

kMaiSmith
источник
3
Почему вам не нравится монтировать тома хоста внутри контейнера Docker? (Я делаю именно это, поэтому мне интересны аргументы против этого: -) Я смонтировал, например: ./postgres-data/:/var/lib/postgres/data- т.е. смонтировал ./postgres-data/
директорию
4
@KajMagnus Я часто использую докер, и мне нравится писать свои контейнеры, чтобы они хорошо работали в рое. Когда я раскручиваю контейнер в рое, я понятия не имею, на каком узле роя будет жить контейнер, поэтому я не могу полагаться на путь хоста, содержащий данные, которые мне нужны. Поскольку Docker 1.9 (я думаю), тома могут быть разделены между хостами, что делает обновление и миграцию контейнеров быстрым, используя метод, который я описал. Альтернативой было бы убедиться, что некоторый сетевой том смонтирован на всех узлах роя, но это звучит как огромная боль в обслуживании.
kMaiSmith
Спасибо! Хорошо, монтирование томов хоста похоже на то, чего я тоже хочу сейчас избежать. По крайней мере, чуть позже, если мое приложение станет популярным и его нужно будет масштабировать до более чем одного сервера
KajMagnus
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 обрабатывает изменения, останавливая и воссоздавая контейнеры (сохраняя подключенные тома). Чтобы запретить Compose принимать изменения, используйте флаг --no-воссоздать.

Аспект управления данными также охватывается docker-compose через подключенные внешние «тома» (см. Https://docs.docker.com/compose/compose-file/#volumes ) или контейнер данных.

Это оставляет потенциальные проблемы обратной совместимости и миграции данных нетронутыми, но это «аппликативные» проблемы, а не специфичные для Docker, которые необходимо проверять на предмет замечаний к выпуску и тестов ...

Ронан Фауглас
источник
Как вы это с версиями? пример нового изображения foo / image: 2, а docker-compose.yml имеет изображение: foo / image: 1?
Дман
БЛАГОДАРЮ ВАС. Лучший ответ!
Мик
Хотя это, безусловно, путь, следует помнить, что любые изменения, сделанные в контейнере, все равно будут потеряны, когда контейнер будет создан заново. Таким образом, хранение изменений контейнера только в подключенных томах все еще необходимо.
Петр Боднар
23

Я хотел бы добавить, что если вы хотите выполнить этот процесс автоматически (загрузить, остановить и перезапустить новый контейнер с такими же настройками, как описано @Yaroslav), вы можете использовать WatchTower. Программа, которая автоматически обновляет ваши контейнеры при их изменении https://github.com/v2tec/watchtower

Рикардо Поло Харамильо
источник
20

Рассмотрим для этого ответы:

  • Имя базы данных app_schema
  • Имя контейнера app_db
  • Пароль root 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, заново создаст и запустит контейнер с новым образом.

Александр В.
источник
Допустим, у меня огромная база данных (несколько ГБ). Будут ли мои данные недоступны до тех пор, пока вся база данных не будет импортирована? Это может быть огромным «простоем»
hellimac
Так как вы упомянули docker-compose, будет ли это работать? stackoverflow.com/a/31485685/65313
sivabudh
1
volumes_fromключ теперь устарел (даже удален в версии 3 составного файла) в пользу нового volumesключа.
Франклин Ю
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
Brilliant! Совершенно удивлен, что не получил больше голосов. Теперь единственное, чего не хватает, это запустить перезапуск всех контейнеров, которые были обновлены.
сорин
7
К сожалению, это не обновит существующий контейнер. Это просто обновит вытащенное изображение, но существующий контейнер является неизменным и все еще использует исходное изображение, использованное для его создания. Это работает, только если вы создаете новый контейнер из изображения, но любой существующий контейнер по-прежнему основан на исходном изображении.
Эрик Б.
Удивительно. Если вам нужно вытащить конкретную версию контейнера, сделайте это так: docker images | awk '{print $ 1 ":" $ 2}' | grep -v 'нет' | grep -iv 'репо' | xargs -n1 docker pull
rogervila
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 достаточно умен, чтобы просто обновить мой контейнер до нового образа с помощью одной команды, вместо того, чтобы сначала его выключать.

gdbj
источник
В реальной ситуации вы не можете использовать свои руки и вносить эти изменения. Ваше решение не поддерживает автоматические способы решения проблемы.
Карлос Васкес Лосада
7
так вы говорите, что, поскольку мое решение не автоматизировано, оно недействительно? Это требование от ОП? И другие ответы подразумевают автоматизацию? Действительно смущен. И я думаю, что отрицательные голоса оказывают плохую услугу другим, приходящим сюда. Мой ответ на 100% действителен на заданный вопрос.
gdbj
спасибо за этот ответ, я также не осознавал, что ты можешь просто бежать, docker-compose up -dне останавливая все сначала.
подкоренное
4

Если вы не хотите использовать Docker Compose, я могу порекомендовать portainer . Он имеет функцию воссоздания, которая позволяет вам воссоздавать контейнер при извлечении последнего изображения.

beruic
источник
2

Вам необходимо либо перестроить все образы и перезапустить все контейнеры, либо каким-либо образом обновить программное обеспечение и перезапустить базу данных. Там нет пути обновления, но вы сами разрабатываете.

seanmcl
источник
Что именно вы имеете в виду, перезапуская контейнеры? Существует docker restartкоманда, но я не уверен , что будет забрать изменения изображений. А что происходит с моими данными внутри контейнеров?
Ярослав Ставничий
1
Извините, я не имел в виду перезапуск докера. Я имею в виду docker rm -f CONTANER; Докер запускается NEW_IMAGE. Данные в вашем контейнере sql исчезнут. Вот почему люди обычно используют объемы для хранения данных.
seanmcl
Если у вас есть все ваши данные, смонтированные в томах в отдельных контейнерах или на хост-машине, nas @seanmcl говорит, что просто создайте новые контейнеры с новым mysql, подключенным к тем же данным. Если вы этого не сделали (вы должны), но вы можете использовать команду docker exec, доступную в Docker 1.3, чтобы обновить mysql и перезапустить его внутри контейнера.
Усман Исмаил
2

Принимая от http://blog.stefanxo.com/2014/08/update-all-docker-images-at-once/

Вы можете обновить все существующие образы, используя следующий конвейер команд:

docker images | awk '/^REPOSITORY|\<none\>/ {next} {print $1}' | xargs -n 1 docker pull
gvlx
источник
6
Это обновит изображения, но не контейнер. Контейнер является неизменным, и его базовое изображение не может быть изменено без создания нового контейнера из обновленного изображения.
Эрик Б.
2

Убедитесь, что вы используете тома для всех постоянных данных (конфигурации, журналов или данных приложения), которые вы храните в контейнерах, связанных с состоянием процессов внутри этого контейнера. Обновите ваш Dockerfile и перестройте образ с нужными вам изменениями, а затем перезапустите контейнеры с вашими томами, смонтированными в соответствующем месте.

Даниэль Динниес
источник
1

Это то, с чем я тоже боролся за свои собственные изображения. У меня есть серверная среда, из которой я создаю образ Docker. Когда я обновляю сервер, я бы хотел, чтобы все пользователи, использующие контейнеры на основе моего образа Docker, могли обновиться до последней версии сервера.

В идеале я предпочел бы сгенерировать новую версию образа Docker, и все контейнеры на основе предыдущей версии этого образа автоматически обновляются до нового образа «на месте». Но этот механизм, кажется, не существует.

Итак, следующий лучший дизайн, который я смог придумать, - это предоставить способ обновления самого контейнера - подобно тому, как настольное приложение проверяет наличие обновлений, а затем обновляет себя. В моем случае это, вероятно, будет означать создание сценария, включающего Git-трюки из известного тега.

Изображение / контейнер на самом деле не меняется, но меняются «внутренние» этого контейнера. Вы можете представить, что делаете то же самое с apt-get, yum или с тем, что подходит для вашей среды. Наряду с этим я обновил бы myserver: последнее изображение в реестре, чтобы любые новые контейнеры основывались на последнем образе.

Мне было бы интересно услышать, есть ли какой-либо уровень техники, который обращается к этому сценарию.

bjlevine
источник
7
Это идет вразрез с концепцией неизменной инфраструктуры и некоторыми из ее преимуществ. Вы можете протестировать свое приложение / среду и убедиться, что оно работает, и если вы обновляете компоненты внутри, это не гарантируется. Разделение кода контейнера из данных конфигурации позволяет вам обновлять, тестировать, что мы в данный момент работаем, и развертывать в рабочей среде, зная, что нет строки кода, отличной от протестированного образа и рабочей. В любом случае, система позволяет вам управлять ею, как вы говорите, по вашему выбору.
Гмуслера
Очень хорошая точка Гмуслера. Договорились, что это анти-паттерн для обновления «внутренних компонентов» существующего док-контейнера.
бьлевине
Итак, что является лучшим решением для автоматического обновления док-контейнера на основе обновлений образа докера, чтобы доставить обновления во все контейнеры без особых усилий?
Тарек Салем
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'

ITech
источник
Это хорошая идея? (Если вы это сделаете apt update; apt upgrade, изображение будет расти.)
ctrl-alt-delor
Изображение @yaroslav s - лучшее решение этой проблемы. Вышесказанное - не совсем Docker-способ ведения дел.
Йост ван дер Лаан