Як оновити контейнер докера після зміни його зображення


518

Скажімо, я витягнув офіційний файл mysql: 5.6.21 .

Я розгорнув це зображення, створивши кілька контейнерів для докерів.

Ці контейнери працюють деякий час до виходу MySQL 5.6.22. Офіційне зображення mysql: 5.6 оновлюється з новим випуском, але мої контейнери все ще працюють 5.6.21.

Як поширити зміни зображення (тобто оновити дистрибутив MySQL) на всі мої існуючі контейнери? Який правильний спосіб Докера це зробити?

Відповіді:


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
Що робити, якщо потрібна міграція даних? Новий сервер не може монтувати дані, оскільки він знаходиться у старому форматі, він повинен знати, що відбувається міграція та змінювати представлення даних.
Дор Ротман

12
Я думаю, дизайнери зображень повинні це врахувати і дозволити запускати власні (наприклад, міграція даних) команд під час першого запуску контейнера.
Ярослав Ставничий


4
@static_rtti Як щодо docker rename my-mysql-container trash-containerстворення нового?
Франклін Ю

4
Чи буде якась команда "все в одному", щоб оновити контейнер без необхідності вручну зупиняти його, видаляти та створювати його знову (на основі нового зображення, яке було витягнуто)?
Michaël Perrin

83

Мені не подобається монтажу томів як посилання на хост-каталог, тому я придумав схему оновлення контейнерів docker контейнерами, повністю керованими докерами. Створення нового контейнера докера з --volumes-from <container>дасть новий контейнер з оновленими зображеннями, що мають спільне володіння томами, керованими докер.

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

Томи докера будуть триматися навколо тих пір, поки будь-який контейнер використовує їх, тому ви можете безпечно видалити оригінальний контейнер. Після видалення оригінального контейнера новий контейнер може припустити тезку оригіналу, щоб зробити все таким же гарним, як і слід було почати.

Існує дві основні переваги використання цього шаблону для оновлення докерних контейнерів. По-перше, це виключає необхідність монтажу томів для розміщення каталогів, дозволяючи томи безпосередньо переноситися на оновлені контейнери. По-друге, ви ніколи не знаходитесь у становищі, де немає робочого докерного контейнера; тому якщо оновлення не вдалося, ви можете легко повернутися до того, як воно працювало раніше, знову відкрутивши оригінальний контейнер докера.


3
Чому ви не любите монтувати томи хостів усередині контейнера Docker? (Я роблю саме це, тому мене цікавлять аргументи проти цього: -) Я встановив, наприклад: ./postgres-data/:/var/lib/postgres/data- тобто встановив хост-dir ./postgres-data/, всередині мого контейнера PostgreSQL.)
KajMagnus

4
@KajMagnus Я дуже часто використовую докерські рої, і мені подобається писати свої контейнери, щоб добре працювати в рої. Коли я закручую контейнер у рій, я не маю уявлення, над яким роєм вузол буде жити, тому я не можу покластися на шлях хоста, що містить потрібні мені дані. Оскільки Docker 1.9 (я думаю) томи можна розділяти між хостами, що робить оновлення та переміщення контейнерів легким вітром, використовуючи описаний метод. Альтернативою було б переконатися, що деякий об'єм мережі встановлений на всіх вузлах рій, але це здається величезним болем у підтримці.
kMaiSmith

Дякую! Гаразд, встановлення томів хостів здається чимось, чого я теж хочу уникати. Принаймні трохи пізніше, якщо моя програма стане популярною та потребує масштабування на більш ніж одному сервері
KajMagnus

32

Просто для надання більш загальної (не специфічної для mysql) відповіді ...

  1. Коротко

Синхронізуйте з реєстром зображень служб ( https://docs.docker.com/compose/compose-file/#image ):

docker-compose pull 

Відтворіть контейнер, якщо файл або зображення докер-композиції змінилися:

docker-compose up -d
  1. Фон

Управління зображеннями контейнерів є однією з причин використання docker-compose (див. Https://docs.docker.com/compose/reference/up/ )

Якщо існують контейнери для послуги, а конфігурація або зображення служби були змінені після створення контейнера, докер-композиція підбирає зміни, зупиняючи та відтворюючи контейнери (зберігаючи встановлені томи). Щоб запобігти появі змін від Compose, використовуйте прапор --no-відтворити.

Аспект управління даними також охоплюється докер-композицією через встановлені зовнішні "томи" (див. 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
  • Корінний пароль є 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
Блискуче! Досить здивований, що не отримав більше голосів. Тепер єдине, чого бракує, - це запустити перезапуск усіх оновлених контейнерів.
sorin

7
На жаль, це не оновить існуючий контейнер. Це просто оновить витягнуте зображення, але існуючий контейнер незмінний і все ще використовує оригінальне зображення, яке використовується для його створення. Це працює лише в тому випадку, якщо ви створюєте новий контейнер із зображення, але будь-який існуючий контейнер все ще заснований на вихідному зображенні.
Ерік Б.

Дивовижний. Якщо вам потрібно витягнути конкретну версію контейнера, зробіть це так: docker images | awk '{print $ 1 ":" $ 2}' | grep -v 'жоден' | grep -iv 'repo' | 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. Він буде шукати зображення на локальному рівні та використовувати оновлений.

-EDIT-

Мої кроки вище - більш багатослівні, ніж повинні бути. Я оптимізував свій робочий процес, включивши build: .параметр до мого докер-композиційного файлу. Етапи виглядають так:

  1. Переконайтесь, що мій Dockerfile такий, яким я хочу, щоб він виглядав.
  2. Встановіть номер версії мого імені зображення у моєму докер-композиційному файлі.
  3. Якщо моє зображення ще не побудоване: запустіть docker-compose build
  4. Біжи docker-compose up -d

Я тоді не усвідомлював, але docker-compose досить розумний, щоб просто оновити мій контейнер на нове зображення за допомогою однієї команди, замість того, щоб спочатку збивати його.


У реальній ситуації ви не можете користуватися своїми руками і вносити ці зміни. Ваше рішення не підтримує автоматичні способи вирішення проблеми.
Карлос Васкес Лосада

7
тому ви говорите, що моє рішення не автоматизоване, воно недійсне? Це вимога від ОП? А інші відповіді мають на увазі автоматизацію? Дійсно розгублений. І, я думаю, низовики роблять сумніви для інших, хто приходить сюди. Моя відповідь стовідсотково справедлива на поставлене запитання.
gdbj

дякую за цю відповідь, я також не зрозумів, що ти можеш просто бігти docker-compose up -dбез необхідності зупиняти все спочатку.
підкореневе

4

Якщо ви не хочете використовувати Docker Compose, я можу порекомендувати portainer . Він має функцію відтворення, яка дозволяє відтворити контейнер, витягуючи останнє зображення.


2

Вам потрібно або відновити всі зображення та перезапустити всі контейнери, або якось оновити програмне забезпечення та перезапустити базу даних. Не існує шляху оновлення, але ви самі спроектували.


Що саме ви маєте на увазі, перезавантаживши контейнери? 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

6
Це оновить зображення, але не контейнер. Контейнер незмінний, і його базове зображення неможливо змінити без створення нового контейнера з оновленого зображення.
Ерік Б.

2

Переконайтеся, що ви використовуєте томи для всіх постійних даних (конфігурація, журнали чи дані програми), які ви зберігаєте у контейнерах, пов’язаних зі станом процесів всередині цього контейнера. Оновіть свій Dockerfile та відновіть зображення із потрібними змінами та перезапустіть контейнери, розмістивши їх у відповідному місці.


1

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

В ідеалі я вважаю за краще створити нову версію зображення Docker, а всі контейнери на основі попередньої версії цього зображення автоматично оновити до нового зображення "на місці". Але цей механізм, здається, не існує.

Отже, наступний найкращий дизайн, який мені вдалося придумати, - це створити спосіб оновлення контейнера - подібний до того, як настільний додаток перевіряє оновлення, а потім оновлення. У моєму випадку це, ймовірно, означатиме складання сценарію, який передбачає витягування Git із відомого тегу.

Зображення / контейнер насправді не змінюється, але змінюються "внутрішні місця" цього контейнера. Ви можете собі уявити, що ви робите те саме з apt-get, yum, або будь-яким іншим, що підходить для вас. Поряд з цим я б оновив myserver: останнє зображення в реєстрі, щоб будь-які нові контейнери базувалися на останньому зображенні.

Мені було б цікаво почути, чи є якесь попереднє мистецтво, яке стосується цього сценарію.


7
Це суперечить концепції незмінної інфраструктури та деяких її переваг. Ви можете перевірити свою програму / середовище, щоб переконатися, що вона працює, і це не гарантується, якщо ви оновлюєте компоненти всередині. Розщеплення коду контейнера від даних із конфігурації дозволяє вам оновлювати, перевіряти, чи ми працюємо в даний час і розгортати у виробництво, знаючи, що немає різного рядка коду від тестованого зображення до виробничого. У будь-якому випадку система дозволяє вам керувати нею, як ви також говорите, свій вибір.
gmuslera

Дуже гарна точкова гмуслера. Погодився, що це анти-шаблон для оновлення "внутрішніх" існуючих контейнерів докера.
bjlevine

Тож яке найкраще рішення для автоматичного оновлення контейнера докера на основі оновлень зображення докера, щоб доставляти оновлення до всіх контейнерів без зусиль?
tarek salem

1

Оновлення

Це головним чином для запиту контейнера не оновлювати, оскільки створення зображень - це спосіб зробити

У мене була така сама проблема, тому я створив docker-run , дуже простий інструмент командного рядка, який працює всередині контейнера 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 s є кращим рішенням цієї проблеми. Сказане насправді не є докерським способом робити речі.
Joost van der Laan
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.