Imagine o seguinte cenário: existe uma imagem Docker que faz quase-tudo que você precisa, mas falta alguma coisinha, um pacote, um arquivinho, um redirect. Logo, o que você faz? Cria um Dockerfile
, utiliza aquela imagem como base em um FROM
, executa meia dúzia de RUN
, COPY
, e cria uma imagem nova, adiciona uma tag e pronto. Problema resolvido, certo?
Uma outra possibilidade é utilizar o Packer para gerar a imagem. Ele, diferente do Docker, não utilizará um arquivo Dockerfile
, mas gerará a nova imagem provisionando um container, fazendo as coisas que tem que fazer lá dentro e quando tudo estiver pronto, ele executará um commit, criando assim uma nova imagem.
Se você optou pelo segundo método, pode ter se frustrado no momento de executar um container baseado na segunda imagem. Um serviço não subiu, o container não fez o que ele deveria ter feito, e você ficou com uma cara de "ué".
O motivo disso é que, diferentemente de um Dockerfile
, o Packer não faz com que a nova imagem herde o ENTRYPOINT e o CMD da imagem base. Para contornar esse comportamento do Packer, escreva o seu arquivo de template da seguinte forma:
source "docker" "my-jenkins" {
image = "jenkins/jenkins:lts-jdk11"
commit = true
run_command = ["-d", "-i", "-t", "--", "{{.Image}}"]
changes = [
"ENTRYPOINT /sbin/tini -- /usr/local/bin/jenkins.sh",
"CMD bash"
]
exec_user = "root"
}
build {
sources = [
"source.docker.my-jenkins"
]
provisioner "shell" {
inline = [
"apt update",
"apt install -y htop"
]
}
post-processor "docker-tag" {
repository = "my-jenkins"
}
}
Neste exemplo, nós estamos criando uma nova imagem do Jenkins com o pacote htop, utilizando a imagem base do mesmo. Para isso, fizemos o seguinte:
run_command
: opções utilizados pelo Packer ao inicializar o container que gerará a imagem. Por padrão, há uma opção--entrypoint=/bin/sh
, que nós estamos removendo;changes
: esta é a parte mais importante. Precisamos restaurar o ENTRYPOINT e o CMD originais da imagem, e para isso, vá até a página da mesma no Docker Hub e verifique-os consultando oDockerfile
da mesma;exec_user
: o usuário utilizado (UID) para executar o provisioner da imagem. Um provisioner é a parte do template responsável pela instalação dos pacotes, arquivos, etc. Como estamos instalando um novo pacote através doapt
, precisamos que esse comando seja executado como root;
Ao final, estamos colocando a tag my-jenkins na imagem por meio do post-processor docker-tag
.
Conclusão
Embora muitas pessoas ainda utilizem Dockerfiles para a construção de suas imagens - e não há nada de errado nisso - o Packer é uma opção interessante e que pode nos auxiliar a fazer o mesmo. O intuito deste artigo não é o apresentar como tal, mas sim resolver um problema comum que provavelmente afetará as pessoas que escolheram tal caminho.
Top comments (0)