Como atualizar o contêiner do docker após a alteração da imagem

518

Digamos que eu peguei a imagem oficial do mysql: 5.6.21 .

Implantei esta imagem criando vários contêineres de docker.

Esses contêineres estão em execução há algum tempo até o MySQL 5.6.22 ser lançado. A imagem oficial do mysql: 5.6 é atualizada com o novo lançamento, mas meus contêineres ainda executam a 5.6.21.

Como propagar as alterações na imagem (por exemplo, atualizar a distribuição do MySQL) para todos os meus contêineres existentes? Qual é a maneira correta do Docker de fazer isso?

Yaroslav Stavnichiy
fonte

Respostas:

579

Depois de avaliar as respostas e estudar o tópico, gostaria de resumir.

A maneira do Docker de atualizar contêineres parece ser o seguinte:

Os contêineres do aplicativo não devem armazenar dados do aplicativo . Dessa forma, você pode substituir o contêiner do aplicativo por sua versão mais recente a qualquer momento executando algo como isto:

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

Você pode armazenar dados no host (no diretório montado como volume) ou em contêineres especiais somente de dados . Leia mais sobre isso

A atualização de aplicativos (por exemplo, com a atualização yum / apt-get) dentro de contêineres é considerada um antipadrão . Os contêineres de aplicativos devem ser imutáveis , o que garantirá um comportamento reprodutível. Algumas imagens oficiais de aplicativos (mysql: 5.6 em particular) nem foram projetadas para serem atualizadas automaticamente (o apt-get upgrade não funcionará).

Gostaria de agradecer a todos que deram suas respostas, para que pudéssemos ver todas as abordagens diferentes.

Yaroslav Stavnichiy
fonte
31
E se a migração de dados for necessária? O novo servidor não pode montar os dados porque estão em um formato antigo, ele precisa saber que uma migração está acontecendo e alterar a representação dos dados.
precisa saber é o seguinte
12
Eu acho que os designers de imagem devem dar conta disso e permitir o lançamento de comandos personalizados (por exemplo, migração de dados) durante a primeira execução do contêiner.
Yaroslav Stavnichiy
4
@static_rtti Que tal docker rename my-mysql-container trash-containerantes de criar o novo?
Franklin Yu
4
Haveria algum comando multifuncional para atualizar o contêiner sem precisar pará-lo manualmente, removê-lo e criá-lo novamente (com base na nova imagem que foi extraída)?
Michaël Perrin
83

Como não gosto de montar volumes como um link para um diretório host, criei um padrão para atualizar contêineres de docker com contêineres gerenciados por docker. A criação de um novo contêiner de docker --volumes-from <container>fornecerá ao novo contêiner com as imagens atualizadas propriedade compartilhada dos volumes gerenciados pelo docker.

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

Ao não remover imediatamente o original my_mysql_containerainda, você poderá voltar ao contêiner em funcionamento conhecido se o contêiner atualizado não tiver os dados corretos ou falhar em um teste de sanidade.

Nesse momento, normalmente executarei todos os scripts de backup que tenho para o contêiner para fornecer uma rede de segurança, caso algo dê errado

docker stop my_mysql_container
docker start my_mysql_container_tmp

Agora você tem a oportunidade de garantir que os dados que você espera estar no novo contêiner estejam lá e executar uma verificação de integridade.

docker rm my_mysql_container
docker rename my_mysql_container_tmp my_mysql_container

Os volumes da janela de encaixe permanecerão enquanto houver um contêiner em uso, para que você possa excluir o contêiner original com segurança. Depois que o contêiner original é removido, o novo contêiner pode assumir o nome do original para deixar tudo tão bonito quanto deveria.

Há duas vantagens principais em usar esse padrão para atualizar contêineres do docker. Em primeiro lugar, elimina a necessidade de montar volumes nos diretórios host, permitindo que os volumes sejam transferidos diretamente para um contêiner atualizado. Em segundo lugar, você nunca está em uma posição em que não haja um contêiner de estivador funcionando; portanto, se a atualização falhar, você poderá reverter facilmente para o funcionamento anterior girando o contêiner de docker original novamente.

kMaiSmith
fonte
3
Por que você não gosta de montar volumes de host dentro de um contêiner do Docker? (Eu estou fazendo exatamente isso que eu estou interessado em argumentos contra fazer isso: -) Eu montado por exemplo: ./postgres-data/:/var/lib/postgres/data- ou seja montada a dir acolhimento ./postgres-data/, dentro do meu recipiente PostgreSQL).
KajMagnus
4
@KajMagnus Eu uso muito enxames de encaixe e gosto de escrever meus contêineres para funcionar bem em um enxame. Quando eu giro um contêiner em um enxame, não tenho idéia em qual nó do enxame o container ficará, portanto, não posso confiar no caminho do host que contém os dados que desejo. Como os volumes do Docker 1.9 (eu acho) podem ser compartilhados entre hosts, o que facilita a atualização e a migração de contêineres usando o método descrito. Uma alternativa seria garantir que algum volume de rede seja montado em todos os nós do enxame, mas isso parece uma enorme dor de manter.
KMaiSmith
Obrigado! Ok, montar volumes de host parece algo que eu também quero evitar agora. Pelo menos um pouco mais tarde, se meu aplicativo se torna popular e precisa de escala para mais de um servidor
KajMagnus
32

Apenas por fornecer uma resposta mais geral (não específica do mysql) ...

  1. Em resumo

Sincronize com o registro de imagem de serviço ( https://docs.docker.com/compose/compose-file/#image ):

docker-compose pull 

Recrie o contêiner se o arquivo ou a imagem do docker-compose tiver sido alterada:

docker-compose up -d
  1. fundo

O gerenciamento de imagens de contêiner é um dos motivos para o uso do docker-compose (consulte https://docs.docker.com/compose/reference/up/ )

Se houver contêineres existentes para um serviço, e a configuração ou imagem do serviço foi alterada após a criação do contêiner, a composição do docker seleciona as alterações parando e recriando os contêineres (preservando volumes montados). Para impedir que o Compose pegue as alterações, use o sinalizador --no-recriar.

O aspecto de gerenciamento de dados também é coberto pelo docker-compose por meio de "volumes" externos montados (consulte https://docs.docker.com/compose/compose-file/#volumes ) ou contêiner de dados.

Isso deixa possíveis problemas de compatibilidade com versões anteriores e migração de dados, mas esses são problemas "aplicáveis", não específicos do Docker, que precisam ser verificados em relação às notas de versão e testes ...

Ronan Fauglas
fonte
Como você faz isso com o versionamento? exemplo, a nova imagem é foo / image: 2 e docker-compose.yml possui image: foo / image: 1?
dman
OBRIGADO. Melhor resposta!
Mick
Embora esse seja certamente o caminho a seguir, deve-se estar ciente de que quaisquer alterações feitas no contêiner ainda serão perdidas quando o contêiner for recriado. Portanto, manter as alterações de contêiner apenas dentro dos volumes montados ainda é necessário.
Petr Bodnár
23

Gostaria de acrescentar que, se você quiser executar esse processo automaticamente (faça o download, pare e reinicie um novo contêiner com as mesmas configurações descritas por @Yaroslav), poderá usar a WatchTower. Um programa que atualiza automaticamente seus contêineres quando eles são alterados https://github.com/v2tec/watchtower

Ricardo Polo Jaramillo
fonte
20

Considere para estas respostas:

  • O nome do banco de dados é app_schema
  • O nome do contêiner é app_db
  • A senha root é root123

Como atualizar o MySQL ao armazenar dados de aplicativos dentro do contêiner

Isso é considerado uma prática ruim , porque se você perder o contêiner, os dados serão perdidos. Embora seja uma má prática, aqui está uma maneira possível de fazê-lo:

1) Faça um despejo de banco de dados como SQL:

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

2) Atualize a imagem:

docker pull mysql:5.6

3) Atualize o contêiner:

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

4) Restaure o dump do banco de dados:

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

Como atualizar o contêiner MySQL usando um volume externo

Usar um volume externo é uma maneira melhor de gerenciar dados e facilita a atualização do MySQL. Perder o contêiner não perderá nenhum dado. Você pode usar o docker-compose para facilitar o gerenciamento de aplicativos Docker com vários contêineres em um único host:

1) Crie o docker-compose.ymlarquivo para gerenciar seus aplicativos:

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) Atualize o MySQL (da mesma pasta que o docker-compose.ymlarquivo):

docker-compose pull
docker-compose up -d

Nota: o último comando acima atualizará a imagem do MySQL, recriará e iniciará o contêiner com a nova imagem.

Alexandre V.
fonte
Digamos que eu tenha um banco de dados enorme (vários GB); meus dados ficarão inacessíveis até que todo o banco de dados seja importado? Isso poderia ser um enorme "downtime"
hellimac
Desde que você mencionou docker-compose, isso funcionará? stackoverflow.com/a/31485685/65313
sivabudh
1
volumes_fromA chave agora está obsoleta (mesmo removida na versão 3 do arquivo de composição) em favor da nova volumeschave.
Franklin Yu
docker pull image_uri:tag && docker restart container_running_that_imagetrabalhou para mim. Não há necessidade docker-compose pull && docker-compose up -d.
Yuriy Pozniak
16

Resposta semelhante à acima

docker images | awk '{print $1}' | grep -v 'none' | grep -iv 'repo' | xargs -n1 docker pull
Eddie Jaoude
fonte
1
Brilhante! Muito surpreso por não ter recebido mais votos. Agora, a única coisa que falta seria acionar uma reinicialização de todos os contêineres que foram atualizados.
sorin
7
Infelizmente, isso não atualizará o contêiner existente. Isso apenas atualizará a imagem extraída, mas o contêiner existente é imutável e ainda está usando a imagem original usada para criá-lo. Isso funciona apenas se você criar um novo contêiner a partir da imagem, mas qualquer contêiner existente ainda será baseado na imagem original.
Eric B.
Surpreendente. Se você precisar extrair a versão específica do contêiner, faça o seguinte: docker images | awk '{print $ 1 ":" $ 2}' | grep -v 'nenhum' | grep -iv 'repo' | xargs -n1 docker pull
rogervila
11

Aqui está o que parece usar docker-composeao criar um personalizado Dockerfile.

  1. Crie seu Dockerfile personalizado primeiro, acrescentando um próximo número de versão para diferenciar. Ex: docker build -t imagename:version . Isso armazenará sua nova versão localmente.
  2. Corre docker-compose down
  3. Edite seu docker-compose.ymlarquivo para refletir o novo nome de imagem que você definiu na etapa 1.
  4. Corre docker-compose up -d . Ele procurará localmente a imagem e usará a atualizada.

-EDITAR-

Meus passos acima são mais detalhados do que precisam. Otimizei meu fluxo de trabalho incluindo obuild: . parâmetro no meu arquivo de composição de encaixe. As etapas parecem agora:

  1. Verifique se meu Dockerfile é como eu quero que seja.
  2. Defina o número da versão do nome da minha imagem no meu arquivo de composição de encaixe.
  3. Se minha imagem ainda não foi criada: execute docker-compose build
  4. Corre docker-compose up -d

Eu não percebi na época, mas o docker-compose é inteligente o suficiente para simplesmente atualizar meu contêiner para a nova imagem com o comando one, em vez de precisar trazê-lo primeiro.

gdbj
fonte
Em uma situação real, você não pode usar suas próprias mãos e fazer essas alterações. Sua solução não suporta maneiras automáticas de resolver o problema.
Carlos Vázquez Losada
7
então você está dizendo que, como minha solução não é automatizada, não é válida? Isso é um requisito do OP? E as outras respostas estão implicando automação? Muito confuso. E acho que os votos negativos estão fazendo um desserviço a outros que vêm aqui. Minha resposta é 100% válida para a pergunta.
Gdbj
obrigado por esta resposta, também não sabia que você poderia simplesmente correr docker-compose up -dsem precisar parar tudo primeiro.
Radicand
4

Se você não deseja usar o Docker Compose, posso recomendar o porttainer . Possui uma função de recriação que permite recriar um contêiner enquanto puxa a imagem mais recente.

beruic
fonte
2

Você precisa reconstruir todas as imagens e reiniciar todos os contêineres ou, de alguma forma, atualizar o software e reiniciar o banco de dados. Não há caminho de atualização, mas você mesmo projeta.

seanmcl
fonte
O que exatamente você quer dizer com reiniciar contêineres? Existe um docker restartcomando, mas não tenho certeza de que ele captará alterações na imagem. E o que acontece com meus dados dentro de contêineres?
Yaroslav Stavnichiy 4/11/14
1
Desculpe, não quis dizer reinicialização do docker. Quero dizer docker rm -f CONTANER; docker, execute NEW_IMAGE. Os dados no seu contêiner sql desaparecerão. É por isso que as pessoas geralmente usam volumes para armazenar os dados.
Seanmcl
Se você tiver todos os seus dados montados em volumes em contêineres separados ou em uma máquina host, o nas @seanmcl disse apenas criar novos contêineres com o novo mysql conectado aos mesmos dados. Se você não fez isso (deveria), mas pode usar o comando docker exec disponível no docker 1.3 para atualizar o mysql e reiniciá-lo dentro do contêiner.
perfil completo de Isman Ismail
2

Retirado de http://blog.stefanxo.com/2014/08/update-all-docker-images-at-once/

Você pode atualizar todas as suas imagens existentes usando o seguinte pipeline de comandos:

docker images | awk '/^REPOSITORY|\<none\>/ {next} {print $1}' | xargs -n 1 docker pull
gvlx
fonte
6
Isso atualizará as imagens, mas não o contêiner. O contêiner é imutável e sua imagem base não pode ser alterada sem a criação de um novo contêiner a partir de uma imagem atualizada.
Eric B.
2

Verifique se você está usando volumes para todos os dados persistentes (configuração, logs ou dados do aplicativo) armazenados nos contêineres relacionados ao estado dos processos dentro desse contêiner. Atualize seu Dockerfile e reconstrua a imagem com as alterações desejadas e reinicie os contêineres com seus volumes montados no local apropriado.

Daniel Dinnyes
fonte
1

Isso é algo com o qual também tenho lutado por minhas próprias imagens. Eu tenho um ambiente de servidor a partir do qual crio uma imagem do Docker. Quando atualizo o servidor, gostaria que todos os usuários que estão executando contêineres baseados na minha imagem do Docker pudessem atualizar para o servidor mais recente.

Idealmente, eu preferiria gerar uma nova versão da imagem do Docker e ter todos os contêineres baseados em uma versão anterior dessa imagem atualizados automaticamente para a nova imagem "no local". Mas esse mecanismo parece não existir.

Portanto, o próximo melhor design que consegui apresentar até agora é fornecer uma maneira de atualizar o contêiner - semelhante à maneira como um aplicativo de desktop verifica se há atualizações e depois se atualiza. No meu caso, isso provavelmente significará a elaboração de um script que envolva o Git pull de uma tag conhecida.

A imagem / contêiner não é realmente alterada, mas os "internos" desse contêiner são alterados. Você pode imaginar fazer o mesmo com o apt-get, yum ou o que for apropriado para o seu ambiente. Junto com isso, eu atualizaria a imagem myserver: latest no registro para que quaisquer novos contêineres fossem baseados na imagem mais recente.

Eu estaria interessado em saber se existe alguma arte anterior que resolva esse cenário.

bjlevine
fonte
7
Isso vai contra o conceito de infraestrutura imutável e alguns de seus benefícios. Você pode testar seu aplicativo / ambiente para verificar se ele funciona e não é garantido se você atualizar os componentes internos. A divisão do código do contêiner dos dados da configuração permite atualizar, testar se estamos trabalhando e implantar atualmente na produção, sabendo que não há uma linha de código diferente da imagem testada para a produção. De qualquer forma, o sistema permite que você gerencie como você diz também, a escolha é sua.
gmuslera
Muito bom ponto gmuslera. Concordou que é um antipadrão atualizar os 'internos' de um contêiner de docker existente.
precisa saber é o seguinte
Então, qual é a melhor solução para atualizar automaticamente o contêiner do Docker com base nas atualizações de imagem do Docker para entregar as atualizações a todos os contêineres sem esforço?
tarek salem
1

Atualizar

Isso é principalmente para consultar o contêiner para não atualizar, pois a construção de imagens é o caminho a ser feito

Como tive o mesmo problema, criei docker-run , uma ferramenta de linha de comando muito simples que é executada dentro de um contêiner de docker para atualizar pacotes em outros contêineres em execução.

Ele usa docker-py para se comunicar com os contêineres do docker em execução e atualizar os pacotes ou executar qualquer comando único arbitrário

Exemplos:

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

por padrão, isso executará o datecomando em todos os contêineres em execução e retornará resultados, mas você pode emitir qualquer comando, por exemplodocker-run exec "uname -a"

Para atualizar pacotes (atualmente usando apenas o apt-get):

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

Você pode criar um apelido e usá-lo como uma linha de comando regular, por exemplo

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

iTech
fonte
isso é uma boa ideia? (Se o fizer apt update; apt upgrade, a imagem aumentará.)
ctrl-alt-delor
A imagem de @yaroslav é uma solução melhor para esse problema. O exposto acima não é realmente o modo Docker de fazer as coisas.
Joost van der Laan