Jak zaktualizować kontener dokera po zmianie jego obrazu

518

Powiedzmy, że ściągnąłem oficjalny obraz mysql: 5.6.21 .

Wdrożyłem ten obraz, tworząc kilka kontenerów dokerów.

Te kontenery działały przez pewien czas, aż do wydania MySQL 5.6.22. Oficjalny obraz mysql: 5.6 zostaje zaktualizowany w nowej wersji, ale moje kontenery nadal działają 5.6.21.

Jak propagować zmiany w obrazie (tj. Uaktualnić dystrybucję MySQL) do wszystkich moich istniejących kontenerów? Jaki jest właściwy sposób na dokowanie?

Jarosław Stawnichij
źródło

Odpowiedzi:

578

Po przeanalizowaniu odpowiedzi i przestudiowaniu tematu chciałbym podsumować.

Wygląda na to, że Docker uaktualnia kontenery:

Kontenery aplikacji nie powinny przechowywać danych aplikacji . W ten sposób możesz w dowolnym momencie zastąpić kontener aplikacji nowszą wersją, wykonując coś takiego:

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

Możesz przechowywać dane na hoście (w katalogu zamontowanym jako wolumin) lub w specjalnych kontenerach tylko z danymi . Przeczytaj więcej na ten temat

Aktualizowanie aplikacji (np. Z aktualizacją yum / apt-get) w kontenerach jest uważane za anty-wzorzec . Pojemniki z aplikacjami powinny być niezmienne , co gwarantuje powtarzalne zachowanie. Niektóre oficjalne obrazy aplikacji (w szczególności mysql: 5.6) nie są nawet zaprojektowane do samodzielnej aktualizacji (aktualizacja apt-get nie będzie działać).

Chciałbym podziękować wszystkim, którzy udzielili odpowiedzi, abyśmy mogli zobaczyć różne podejścia.

Jarosław Stawnichij
źródło
31
Co jeśli potrzebna jest migracja danych? Nowy serwer nie może zamontować danych, ponieważ jest w starym formacie, musi wiedzieć, że trwa migracja i zmienić reprezentację danych.
Dor Rotman,
12
Myślę, że projektanci obrazów powinni wziąć to pod uwagę i pozwolić na uruchamianie niestandardowych poleceń (np. Migracji danych) podczas pierwszego uruchomienia kontenera.
Yaroslav Stavnichiy
4
@static_rtti Co powiesz na to docker rename my-mysql-container trash-containerprzed utworzeniem nowego?
Franklin Yu,
4
Czy byłoby jakieś uniwersalne polecenie, aby zaktualizować kontener bez konieczności ręcznego zatrzymywania go, usuwania go i tworzenia go ponownie (na podstawie nowego obrazu, który został wyciągnięty)?
Michaël Perrin
83

Nie lubię montować woluminów jako łącza do katalogu hosta, więc wymyśliłem wzorzec uaktualniania kontenerów dokowanych z kontenerami zarządzanymi całkowicie przez dokerów. Utworzenie nowego kontenera dokowanego za pomocą --volumes-from <container>spowoduje, że nowy kontener ze zaktualizowanymi obrazami będzie współwłasnością woluminów zarządzanych przez dokera.

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

Nie usuwając my_mysql_containerjeszcze oryginału , możesz wrócić do znanego działającego kontenera, jeśli zmodernizowany kontener nie ma odpowiednich danych lub nie przejdzie testu zdrowego rozsądku.

W tym momencie zwykle uruchamiam wszystkie skrypty tworzenia kopii zapasowych, które mam dla kontenera, aby zapewnić sobie siatkę bezpieczeństwa na wypadek, gdyby coś poszło nie tak

docker stop my_mysql_container
docker start my_mysql_container_tmp

Teraz masz możliwość upewnienia się, że dane, które mają znajdować się w nowym kontenerze, znajdują się na miejscu i przeprowadzenia kontroli poczytalności.

docker rm my_mysql_container
docker rename my_mysql_container_tmp my_mysql_container

Woluminy dokowania będą się trzymać tak długo, jak długo będzie z nich korzystać dowolny pojemnik, dzięki czemu można bezpiecznie usunąć oryginalny pojemnik. Po usunięciu oryginalnego pojemnika nowy pojemnik może przyjąć nazwę oryginału, aby wszystko było tak ładne, jak miało się rozpocząć.

Istnieją dwie główne zalety używania tego wzorca do aktualizacji kontenerów dokerów. Po pierwsze, eliminuje potrzebę montowania woluminów w katalogach hosta, umożliwiając bezpośrednie przenoszenie woluminów do zmodernizowanych kontenerów. Po drugie, nigdy nie jesteś w pozycji, w której nie ma działającego kontenera dokowanego; więc jeśli aktualizacja się nie powiedzie, możesz łatwo powrócić do poprzedniego stanu, ponownie rozpinając oryginalny pojemnik dokowania.

kMaiSmith
źródło
3
Dlaczego nie lubisz montować woluminów hosta w kontenerze Docker? (Robię to dokładnie, więc interesują mnie argumenty przeciwko temu: -) ./postgres-data/:/var/lib/postgres/data./postgres-data/
Zainstalowałem
4
@ KajMagnus Często używam roju dokerów i lubię pisać swoje pojemniki, aby dobrze działały w roju. Kiedy rozbijam kontener w roju, nie mam pojęcia, w którym węźle roju kontener będzie żył, więc nie mogę polegać na ścieżce hosta zawierającej dane, które chcę. Ponieważ woluminy Docker 1.9 (myślę) mogą być współużytkowane przez hosty, dzięki czemu uaktualnianie i migracja kontenerów jest dziecinnie proste przy użyciu opisanej przeze mnie metody. Alternatywą byłoby upewnienie się, że pewna liczba woluminów sieciowych jest zamontowana na wszystkich węzłach roju, ale brzmi to jak ogromny ból do utrzymania.
kMaiSmith
Dzięki! Ok, montowanie woluminów hosta wydaje się teraz czymś, czego ja też chcę uniknąć. Przynajmniej trochę później, jeśli moja aplikacja stanie się popularna i będzie
wymagała
32

Tylko dla zapewnienia bardziej ogólnej (nie specyficznej dla MySQL) odpowiedzi ...

  1. W skrócie

Synchronizuj z rejestrem obrazu usługi ( https://docs.docker.com/compose/compose-file/#image ):

docker-compose pull 

Odtwórz kontener, jeśli plik lub obraz skomponowany przez dokera zmienił się:

docker-compose up -d
  1. tło

Zarządzanie obrazami kontenerów jest jednym z powodów używania docker-compose (patrz https://docs.docker.com/compose/reference/up/ )

Jeśli istnieją istniejące kontenery dla usługi, a konfiguracja lub obraz usługi został zmieniony po utworzeniu kontenera, kompilacja dokowania przechwytuje zmiany, zatrzymując i ponownie tworząc kontenery (zachowując zamontowane woluminy). Aby zapobiec zbieraniu zmian przez Compose, użyj flagi --no-restore.

Aspekt zarządzania danymi jest również objęty komponentem dokującym poprzez zamontowane zewnętrzne „woluminy” (patrz https://docs.docker.com/compose/compose-file/#volumes ) lub kontener danych.

Pozostawia to potencjalne problemy z kompatybilnością wsteczną i migracją danych, ale są to problemy „aplikacyjne”, a nie specyficzne dla Dockera, które należy sprawdzić w informacjach o wersji i testach ...

Ronan Fauglas
źródło
Jak to zrobić z wersjonowaniem? przykład nowy obraz to foo / image: 2 i docker-compose.yml ma image: foo / image: 1?
dman
DZIĘKUJĘ CI. Najlepsza odpowiedź!
Mick
Chociaż jest to z pewnością właściwa droga, należy pamiętać, że wszelkie zmiany dokonane w kontenerze nadal zostaną utracone po odtworzeniu kontenera. Nadal konieczne jest utrzymywanie zmian w kontenerach tylko w zamontowanych woluminach.
Petr Bodnár
23

Chciałbym dodać, że jeśli chcesz wykonać ten proces automatycznie (pobierz, zatrzymaj i zrestartuj nowy pojemnik z tymi samymi ustawieniami, jak opisane przez @Yaroslav), możesz użyć Strażnicy. Program, który automatycznie aktualizuje pojemniki po ich zmianie https://github.com/v2tec/watchtower

Ricardo Polo Jaramillo
źródło
20

Rozważ te odpowiedzi:

  • Nazwa bazy danych to app_schema
  • Nazwa kontenera to app_db
  • Hasło roota to root123

Jak zaktualizować MySQL podczas przechowywania danych aplikacji w kontenerze

Jest to uważane za złą praktykę , ponieważ jeśli stracisz pojemnik, stracisz dane. Chociaż jest to zła praktyka, oto możliwy sposób, aby to zrobić:

1) Wykonaj zrzut bazy danych jako SQL:

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

2) Zaktualizuj obraz:

docker pull mysql:5.6

3) Zaktualizuj kontener:

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

4) Przywróć zrzut bazy danych:

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

Jak zaktualizować kontener MySQL przy użyciu zewnętrznego woluminu

Korzystanie z zewnętrznego woluminu jest lepszym sposobem zarządzania danymi i ułatwia aktualizację MySQL. Utrata pojemnika nie spowoduje utraty żadnych danych. Możesz użyć komponowania dokującego, aby ułatwić zarządzanie aplikacjami dokującymi z wieloma kontenerami na jednym hoście:

1) Utwórz docker-compose.ymlplik, aby zarządzać aplikacjami:

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) Zaktualizuj MySQL (z tego samego folderu co docker-compose.ymlplik):

docker-compose pull
docker-compose up -d

Uwaga: ostatnie polecenie powyżej zaktualizuje obraz MySQL, odtworzy i uruchom kontener z nowym obrazem.

Alexandre V.
źródło
Powiedzmy, że mam ogromną bazę danych (kilka GB). Czy moje dane będą niedostępne, dopóki cała baza danych nie zostanie zaimportowana? To może być ogromny „przestój”
hellimac
Jak już wspomniałeś docker-compose, czy to zadziała? stackoverflow.com/a/31485685/65313
sivabudh
1
volumes_fromKlucz jest teraz przestarzały (nawet usunięty w wersji 3 pliku tworzenia) na korzyść nowego volumesklucza.
Franklin Yu
docker pull image_uri:tag && docker restart container_running_that_imagepracował dla mnie. Nie ma potrzeby docker-compose pull && docker-compose up -d.
Yuriy Pozniak
16

Podobna odpowiedź do powyższej

docker images | awk '{print $1}' | grep -v 'none' | grep -iv 'repo' | xargs -n1 docker pull
Eddie Jaoude
źródło
1
Znakomity! Zaskoczony, że nie otrzymał więcej głosów. Teraz jedyne, czego brakuje, to ponowne uruchomienie wszystkich zaktualizowanych kontenerów.
sorin,
7
Niestety nie spowoduje to aktualizacji istniejącego kontenera. To po prostu zaktualizuje pobrany obraz, ale istniejący kontener jest niezmienny i nadal używa oryginalnego obrazu użytego do jego utworzenia. Działa to tylko wtedy, gdy utworzysz nowy kontener z obrazu, ale każdy istniejący kontener jest nadal oparty na oryginalnym obrazie.
Eric B.
Niesamowity. Jeśli chcesz pobrać konkretną wersję kontenera, zrób to tak: obrazy dokerów | awk '{print $ 1 ":" $ 2}' | grep -v 'none' | grep -iv 'repo' | xargs -n1 puller docker
rogervila
11

Oto, jak to wygląda przy docker-composetworzeniu niestandardowego Dockerfile.

  1. Najpierw skompiluj swój niestandardowy plik Docker, dołączając kolejny numer wersji w celu rozróżnienia. Przykład: docker build -t imagename:version . lokalnie zapisze nową wersję.
  2. Biegać docker-compose down
  3. Edytuj docker-compose.ymlplik, aby odzwierciedlić nową nazwę obrazu ustawioną w kroku 1.
  4. Uruchom docker-compose up -d. Lokalnie wyszuka obraz i użyje zaktualizowanego.

-EDYTOWAĆ-

Moje kroki powyżej są bardziej szczegółowe, niż powinny. Zoptymalizowałem przepływ pracy, włączając build: .parametr do mojego pliku dokowania. Kroki wyglądają teraz tak:

  1. Sprawdź, czy mój plik Docker powinien wyglądać tak, jak powinien.
  2. Ustaw numer wersji mojej nazwy obrazu w pliku redakcji.
  3. Jeśli mój obraz nie jest jeszcze zbudowany: uruchom docker-compose build
  4. Biegać docker-compose up -d

Nie zdawałem sobie wtedy sprawy, ale kompilacja dokerów jest wystarczająco inteligentna, aby po prostu zaktualizować mój kontener do nowego obrazu za pomocą jednego polecenia, zamiast najpierw go obniżać.

gdbj
źródło
W prawdziwej sytuacji nie możesz użyć własnych rąk i dokonać tych zmian. Twoje rozwiązanie nie obsługuje automatycznych sposobów rozwiązania problemu.
Carlos Vázquez Losada
7
więc mówisz, że ponieważ moje rozwiązanie nie jest zautomatyzowane, jest nieważne? Czy jest to wymóg PO? A czy inne odpowiedzi sugerują automatyzację? Naprawdę zdezorientowany. I myślę, że głosujący wyrządzają krzywdę innym, którzy tu przyjeżdżają. Moja odpowiedź jest w 100% poprawna na zadane pytanie.
gdbj
dzięki za tę odpowiedź, nie zdawałem sobie również sprawy, że możesz po prostu biec docker-compose up -dbez konieczności zatrzymywania wszystkiego w pierwszej kolejności.
radicand
4

Jeśli nie chcesz używać Docker Compose, mogę polecić portainer . Ma funkcję odtwarzania, która pozwala odtworzyć kontener podczas pobierania najnowszego obrazu.

beruic
źródło
2

Musisz albo odbudować wszystkie obrazy i zrestartować wszystkie kontenery, albo jakoś zaktualizować oprogramowanie i zrestartować bazę danych. Nie ma ścieżki uaktualnienia, ale którą sam zaprojektujesz.

seanmcl
źródło
Co dokładnie masz na myśli przez ponowne uruchomienie kontenerów? Jest docker restartpolecenie, ale nie jestem pewien, czy wykryje zmiany obrazu. A co dzieje się z moimi danymi w kontenerach?
Yaroslav Stavnichiy
1
Przepraszam, nie miałem na myśli ponownego uruchomienia dokera. Mam na myśli dokera rm -f CONTANER; uruchom okno dokowane NEW_IMAGE. Dane w kontenerze sql znikną. Dlatego ludzie zazwyczaj używają woluminów do przechowywania danych.
seanmcl
Jeśli masz wszystkie dane zamontowane w woluminach w osobnych kontenerach lub maszynie hosta, nas @seanmcl powiedział, że po prostu stwórz nowe kontenery z nowym mysql połączonym z tymi samymi danymi. Jeśli tego nie zrobiłeś (powinieneś), ale możesz użyć polecenia exec docker dostępnego w oknie dokowanym 1.3, aby zaktualizować mysql i zrestartować go w kontenerze.
Usman Ismail
2

Biorąc z http://blog.stefanxo.com/2014/08/update-all-docker-images-at-once/

Możesz zaktualizować wszystkie istniejące obrazy za pomocą następującego potoku poleceń:

docker images | awk '/^REPOSITORY|\<none\>/ {next} {print $1}' | xargs -n 1 docker pull
gvlx
źródło
6
Spowoduje to zaktualizowanie obrazów, ale nie kontenera. Kontener jest niezmienny i jego obrazu podstawowego nie można zmienić bez utworzenia nowego kontenera na podstawie zaktualizowanego obrazu.
Eric B.
2

Upewnij się, że używasz woluminów dla wszystkich trwałych danych (konfiguracji, dzienników lub danych aplikacji), które przechowujesz w kontenerach związanych ze stanem procesów w tym kontenerze. Zaktualizuj plik Docker i odbuduj obraz z żądanymi zmianami, a następnie ponownie uruchom kontenery z woluminami zamontowanymi w odpowiednim miejscu.

Daniel Dinnyes
źródło
1

Z tym też walczyłem o własne zdjęcia. Mam środowisko serwerowe, z którego tworzę obraz Docker. Gdy aktualizuję serwer, chciałbym, aby wszyscy użytkownicy, którzy używają kontenerów opartych na moim obrazie Docker, mogli uaktualnić do najnowszego serwera.

Idealnie wolałbym wygenerować nową wersję obrazu Docker i wszystkie kontenery oparte na poprzedniej wersji tego obrazu aktualizowały się automatycznie do nowego obrazu „na swoim miejscu”. Ale ten mechanizm wydaje się nie istnieć.

Kolejnym najlepszym projektem, jaki udało mi się do tej pory wymyślić, jest zapewnienie sposobu, aby sama aktualizacja kontenera była podobna do tego, w jaki sposób aplikacja komputerowa sprawdza dostępność aktualizacji, a następnie sama się aktualizuje. W moim przypadku prawdopodobnie będzie to oznaczać wykonanie skryptu, który będzie pobierał Git ze znanego tagu.

Obraz / kontener tak naprawdę się nie zmienia, ale zmieniają się „elementy wewnętrzne” tego kontenera. Możesz sobie wyobrazić, że robisz to samo z apt-get, yum lub czymkolwiek odpowiednim dla twojego środowiska. Wraz z tym zaktualizowałbym mój serwer: najnowszy obraz w rejestrze, aby wszelkie nowe kontenery były oparte na najnowszym obrazie.

Chciałbym dowiedzieć się, czy istnieje jakikolwiek stan techniki, który dotyczy tego scenariusza.

bjlevine
źródło
7
Jest to sprzeczne z koncepcją niezmiennej infrastruktury i niektórymi z jej zalet. Możesz przetestować swoją aplikację / środowisko, aby sprawdzić, czy działa i nie jest gwarantowane, jeśli zaktualizujesz składniki w środku. Rozdzielenie kodu kontenera od danych z konfiguracji pozwala zaktualizować, przetestować, że obecnie pracujemy i wdrożyć do produkcji, wiedząc, że nie ma innej linii kodu od testowanego obrazu do obrazu produkcyjnego. W każdym razie system pozwala ci zarządzać nim tak, jak mówisz, to twój wybór.
gmuslera
Bardzo dobry punkt gmuslera. Zgodził się, że aktualizowanie wewnętrznych elementów istniejącego kontenera dokowanego jest anty-wzorcem.
bjlevine,
Więc jakie jest najlepsze rozwiązanie do automatycznej aktualizacji kontenera dokowanego na podstawie aktualizacji obrazu dokera, aby bezproblemowo dostarczać aktualizacje do wszystkich kontenerów?
tarek salem
1

Aktualizacja

Służy to głównie zapytaniu o kontener, aby nie aktualizować, ponieważ sposobem jest budowanie obrazów

Miałem ten sam problem, więc utworzyłem okno dokowane , bardzo proste narzędzie wiersza polecenia, które działa wewnątrz kontenera dokowanego w celu aktualizacji pakietów w innych działających kontenerach.

Używa docker-py do komunikowania się z uruchomionymi kontenerami dokerów i aktualizacji pakietów lub uruchamiania dowolnych pojedynczych poleceń

Przykłady:

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

domyślnie uruchomi to datepolecenie we wszystkich działających kontenerach i zwróci wyniki, ale możesz wydać dowolne polecenie npdocker-run exec "uname -a"

Aby zaktualizować pakiety (aktualnie tylko przy użyciu apt-get):

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

Możesz tworzyć i alias i używać go jako zwykłej linii poleceń, np

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

iTech
źródło
Czy to dobry pomysł? (Jeśli to zrobisz apt update; apt upgrade, obraz wzrośnie.)
ctrl-alt-delor,
Obraz @yaroslav jest lepszym rozwiązaniem tego problemu. Powyższe nie jest tak naprawdę sposobem dokowania.
Joost van der Laan