Para executar um aplicativo ASP.NET Core, gerei um dockerfile que constrói o aplicativo e copia o código-fonte no contêiner, que é buscado por Git usando Jenkins. Portanto, em meu espaço de trabalho, faço o seguinte no dockerfile:
WORKDIR /app
COPY src src
Embora o Jenkins atualize os arquivos em meu host corretamente com Git, o Docker não aplica isso à minha imagem.
Meu script básico para construção:
#!/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
Eu tentei coisas diferentes, como --rm
e --no-cache
parâmetro para docker run
e também parar / retirar o recipiente antes de o novo é de criação. Não tenho certeza do que estou fazendo de errado aqui. Parece que o docker está atualizando a imagem corretamente, pois a chamada de COPY src src
resultaria em um ID de camada e nenhuma chamada de cache:
Step 6 : COPY src src
---> 382ef210d8fd
Qual é a maneira recomendada de atualizar um contêiner?
Meu cenário típico seria: O aplicativo está sendo executado no servidor em um contêiner do Docker. Agora, partes do aplicativo são atualizadas, por exemplo, modificando um arquivo. Agora, o contêiner deve executar a nova versão. O Docker parece recomendar a construção de uma nova imagem em vez de modificar um contêiner existente, então acho que a maneira geral de reconstruir como eu faço é a correta, mas alguns detalhes na implementação precisam ser melhorados.
Respostas:
Depois de algumas pesquisas e testes, descobri que tinha alguns mal-entendidos sobre a vida útil dos contêineres Docker. Simplesmente reiniciar um contêiner não faz com que o Docker use uma nova imagem, quando a imagem foi reconstruída nesse meio tempo. Em vez disso, o Docker busca a imagem apenas antes de criar o contêiner. Portanto, o estado após a execução de um contêiner é persistente.
Por que a remoção é necessária
Portanto, reconstruir e reiniciar não é suficiente. Achei que os containers funcionassem como um serviço: parando o serviço, faça suas alterações, reinicie-o e eles seriam aplicados. Esse foi meu maior erro.
Como os contêineres são permanentes, você deve removê-los usando
docker rm <ContainerName>
primeiro. Depois que um contêiner é removido, você não pode simplesmente iniciá-lo pordocker start
. Isso deve ser feito usandodocker run
, que por si só usa a imagem mais recente para criar uma nova instância de contêiner.Os recipientes devem ser o mais independentes possível
Com esse conhecimento, é compreensível por que o armazenamento de dados em contêineres é qualificado como uma prática ruim, e o Docker recomenda volumes de dados / montagem de diretórios de host em vez disso: Como um contêiner deve ser destruído para atualizar aplicativos, os dados armazenados dentro dele também seriam perdidos. Isso causa trabalho extra para desligar serviços, fazer backup de dados e assim por diante.
Portanto, é uma solução inteligente excluir completamente esses dados do contêiner: não precisamos nos preocupar com nossos dados, quando estão armazenados com segurança no host e o contêiner contém apenas o próprio aplicativo.
Por
-rf
que não pode realmente ajudá-loO
docker run
comando tem um switch de limpeza chamado-rf
. Isso interromperá o comportamento de manter os containers docker permanentemente. Usando-rf
, o Docker destruirá o contêiner após sua saída. Mas essa mudança tem dois problemas:-d
chaveEmbora a
-rf
opção seja uma boa opção para economizar trabalho durante o desenvolvimento para testes rápidos, é menos adequada na produção. Especialmente por causa da falta da opção de executar um contêiner em segundo plano, o que na maioria das vezes seria necessário.Como remover um recipiente
Podemos contornar essas limitações simplesmente removendo o contêiner:
A opção
--force
(ou-f
) que usa SIGKILL em contêineres em execução. Em vez disso, você também pode parar o contêiner antes de:Ambos são iguais.
docker stop
também está usando SIGTERM . Mas usar a--force
opção encurtará seu script, especialmente ao usar servidores CI:docker stop
gera um erro se o contêiner não estiver em execução. Isso faria com que o Jenkins e muitos outros servidores de CI considerassem erroneamente o build como com falha. Para corrigir isso, você deve primeiro verificar se o contêiner está funcionando como eu fiz na pergunta (consulte acontainerRunning
variável).Script completo para reconstruir um contêiner Docker
De acordo com esse novo conhecimento, corrigi meu script da seguinte maneira:
Isso funciona perfeitamente :)
fonte
--force-recreate
opção no docker compose é semelhante ao que você descreve aqui? E se sim, não valeria a pena usar esta solução em vez disso (desculpe se essa pergunta for idiota, mas eu sou um docker noob ^^)docker-compose
é mais inteligente do que osdocker
comandos simples . Eu trabalho regularmente comdocker-compose
e a detecção de alterações funciona bem, então eu uso--force-recreate
muito raramente. Sódocker-compose up --build
é importante quando você está construindo uma imagem personalizada (build
diretiva no arquivo de composição) em vez de usar uma imagem, por exemplo, do hub do Docker.Sempre que alterações forem feitas no dockerfile ou composição ou requisitos, execute-o novamente usando
docker-compose up --build
. Para que as imagens sejam reconstruídas e atualizadasfonte
/opt/mysql/data:/var/lib/mysql
?--build
em ambientes de desenvolvimento locais. A velocidade na qual o docker copia novamente os arquivos que, de outra forma, poderia assumir que não precisam ser copiados leva apenas alguns milissegundos e economiza um grande número de momentos WTF.Você pode executar
build
para um serviço específico executandodocker-compose up --build <service name>
onde o nome do serviço deve corresponder a como você o chamou no arquivo docker-compose.Exemplo Vamos supor que seu arquivo docker-compose contenha muitos serviços (aplicativo .net - banco de dados - vamos criptografar ... etc) e você deseja atualizar apenas o aplicativo .net nomeado como
application
no arquivo docker-compose. Você pode então simplesmente executardocker-compose up --build application
Parâmetros extras Caso você deseje adicionar parâmetros extras ao seu comando, como
-d
para execução em segundo plano, o parâmetro deve estar antes do nome do serviço:docker-compose up --build -d application
fonte