Відновіть контейнер Docker за змінами файлів


86

Для запуску програми ASP.NET Core я створив файл docker, який створює програму та копіює вихідний код у контейнері, який отримує Git за допомогою Jenkins. Отже, у своїй робочій області я роблю наступне у файлі docker:

WORKDIR /app
COPY src src

Поки Дженкінс правильно оновлює файли на моєму хості за допомогою Git, Докер не застосовує це до мого зображення.

Мій базовий сценарій для побудови:

#!/bin/bash
imageName=xx:my-image
containerName=my-container

docker build -t $imageName -f Dockerfile  .

containerRunning=$(docker inspect --format="{{ .State.Running }}" $containerName 2> /dev/null)

if [ "$containerRunning" == "true" ]; then
        docker stop $containerName
        docker start $containerName
else
        docker run -d -p 5000:5000 --name $containerName $imageName
fi

Я спробував різні речі, такі як --rmі --no-cacheпараметр, docker runа також зупинку / видалення контейнера перед тим, як будувати новий. Я не впевнений, що я тут роблю неправильно. Здається, докер правильно оновлює зображення, оскільки виклик COPY src srcрезультату призведе до ідентифікації шару та відсутності виклику кешу:

Step 6 : COPY src src
 ---> 382ef210d8fd

Який рекомендований спосіб оновити контейнер?

Моїм типовим сценарієм було б: додаток працює на сервері в контейнері Docker. Тепер частини програми оновлюються, наприклад, шляхом зміни файлу. Тепер контейнер повинен запустити нову версію. Здається, Докер рекомендує створювати новий образ замість того, щоб модифікувати існуючий контейнер, тому я думаю, що загальний спосіб відновлення, як і я, є правильним, але деякі деталі у реалізації повинні бути вдосконалені.


Чи можете ви перелічити точні кроки, які ви зробили для побудови вашого контейнера, включаючи команду збірки та весь вихідний результат кожної команди?
BMitch

Відповіді:


136

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

Чому потрібно видалення

Тому відновлення та перезавантаження недостатньо. Я думав, що контейнери працюють як служба: Зупинивши службу, внесіть зміни, перезапустіть, і вони застосуються. Це була моя найбільша помилка.

Оскільки контейнери постійні, вам доведеться їх видалити, використовуючи docker rm <ContainerName>спочатку. Після видалення контейнера ви не можете просто запустити його docker start. Це потрібно зробити за допомогою docker run, яка сама використовує останнє зображення для створення нового екземпляра контейнера.

Контейнери повинні бути максимально незалежними

Маючи ці знання, зрозуміло, чому зберігання даних у контейнерах кваліфікується як погана практика, і замість цього Docker рекомендує обсяги даних / монтування хост-директорій : Оскільки контейнер повинен бути знищений для оновлення програм, збережені дані всередині також будуть втрачені. Це спричиняє додаткову роботу щодо вимкнення служб, резервного копіювання даних тощо.

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

Чому -rfможе насправді вам не допомогти

docker runКоманда має Очистіть перемикач під назвою -rf. Це зупинить поведінку постійного зберігання контейнерів для докерів. Використовуючи -rf, Docker знищить контейнер після його виходу. Але цей перемикач має дві проблеми:

  1. Docker також видаляє томи без імені, пов’язаного з контейнером, що може вбити ваші дані
  2. За допомогою цієї опції неможливо запускати контейнери у фоновому режимі за допомогою -dперемикача

Незважаючи на те, що -rfкомутатор є хорошим варіантом для економії роботи під час розробки для швидких випробувань, він менш підходить для виробництва. Особливо через відсутність опції запускати контейнер у фоновому режимі, що в основному було б потрібно.

Як вийняти контейнер

Ми можемо обійти ці обмеження, просто видаливши контейнер:

docker rm --force <ContainerName>

--force(Або -fперемикач) , які використовують SIGKILL на управління контейнерами. Натомість ви також можете зупинити контейнер раніше:

docker stop <ContainerName>
docker rm <ContainerName>

Обидва рівні. docker stopтакож використовує SIGTERM . Але використання --forceперемикача скоротить ваш сценарій, особливо при використанні серверів CI: docker stopвидає помилку, якщо контейнер не працює. Це призвело б до того, що Jenkins та багато інших серверів CI вважали побудову помилково помилковою. Щоб це виправити, спочатку потрібно перевірити, чи працює контейнер, як це було у запитанні (див. containerRunningЗмінну).

Повний сценарій відновлення контейнера Docker

Згідно з цими новими знаннями, я закріпив свій сценарій наступним чином:

#!/bin/bash
imageName=xx:my-image
containerName=my-container

docker build -t $imageName -f Dockerfile  .

echo Delete old container...
docker rm -f $containerName

echo Run new container...
docker run -d -p 5000:5000 --name $containerName $imageName

Це чудово працює :)


4
"Я виявив, що у мене виникли непорозуміння щодо терміну експлуатації контейнерів Docker", ви вийняли ці слова з мого вуста. Дякую за таке детальне пояснення. Я б порекомендував це докеру для початківців. Це уточнює різницю між VM та контейнером.
Вінс Банзон, 02.03.18

2
Після вашого пояснення я зробив це, щоб взяти до відома те, що зробив із наявним іміджем. Для того, щоб зберегти зміни, я створив новий Dockerfile для створення нового образу, який вже включає зміни, які я хочу додати. Таким чином, створене нове зображення (дещо) оновляється.
Вінс Банзон, 02.03.18

Чи є --force-recreateваріант компонування докера подібним до того, що ви описуєте тут? І якщо так, то чи не варто використовувати замість цього рішення (вибачте, якщо це питання німе, але я докер noob ^^)
cglacet

2
@cglacet Так, це схоже, не можна порівняти безпосередньо. Але docker-composeрозумніший за звичайні dockerкоманди. Я регулярно працюю, docker-composeі виявлення змін працює добре, тому я використовую --force-recreateдуже рідко. Просто docker-compose up --buildважливо, коли ви створюєте власне зображення ( buildдиректива у файлі створення), а не використовуєте зображення, наприклад, з концентратора Docker.
Лев

33

Щоразу, коли вносяться зміни у файл докерного файлу, компонування чи вимоги, запустіть його знову за допомогою docker-compose up --build. Так що зображення відновлюються та оновлюються


1
Маючи контейнер докера MySQL як одну службу, чи буде БД порожньою після цього, якщо для цього використовується том /opt/mysql/data:/var/lib/mysql?
Мартін Тома,

Для мене не здається жодним мінусом у тому, щоб завжди використовувати --buildв локальних середовищах розробників. Швидкість, з якою Docker повторно копіює файли, які він міг би припустити, не потребують копіювання, займає всього пару мілісекунд, і це економить велику кількість моментів WTF.
Данак

0

Ви можете запустити buildпевну службу, запустивши docker-compose up --build <service name>там, де назва служби має відповідати тому, як ви її називали у своєму файлі складання докера.

Приклад Припустимо, що ваш файл docker-compose містить багато служб (.net додаток - база даних - давайте зашифруємо ... і т.д.), і ви хочете оновити лише програму .net, названу як applicationу файлі docker-compose. Потім можна просто бігтиdocker-compose up --build application

Додаткові параметри У випадку, якщо ви хочете додати додаткові параметри до своєї команди, наприклад, -dдля запуску у фоновому режимі, параметр повинен стояти перед назвою служби: docker-compose up --build -d application

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.