Escolar Documentos
Profissional Documentos
Cultura Documentos
Search
Share More
De acordo com um estudo da Flexera, 78% das pequenas e médias empresas estão
usando containers. Entre as Fortune 500, 50% estão utilizando Kubernetes.
Dockerfile
O Dockerfile é um script de configuração de texto simples que contém instruções
para construir uma imagem específica.
É ainda muito comum ouvir o termo "imagem docker", apesar de não ser um termo
assertivo, gerando muita confusão entre as pessoas.
Apesar do nome Dockerfile, outras containers engine (daemonless ou não), também
podem ser usadas para criar imagens a partir do Dockerfile, como Podman.
Agora que estamos todos na mesma página, vamos entender alguns conceitos mais
a fundo, preparado?
Image Layers
Todos que fizeram algum docker pull ou docker push (irei usar docker por
convenção, o mesmo se aplica para podman) devem ter reparado que ambos
comandos tem um output baseado em pequenos chunks, nesse caso podemos ver
9fda…, 60e4…, daa2… e outros pedaços sendo baixados.
Muito se ouve falar sobre redução de tamanho de imagens e entender o conceito por
trás de Image Layers é imprescindível para atingir esse resultado.
# Base image
FROM alpine:latest
Fiquem a vontade para tagear (através da flag -t) como acharem melhor.
Esse comando irá te retornar muitas informações sobre a imagem, inclusive, muito
útil no dia dia!
O que estamos buscando é a quantidade de layers dessa imagem, podem verificar
isso no campo RootFS:
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:4693057ce2364720d39e57e85a5b8e0bd9ac3573716237736d6470e
]
}
No seu caso, o SHA256 será diferente, mas o que importa é ver que existe apenas
uma única layer dentro do array de Layers.
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:b2191e2be29d816fa6fbde954316d54e10df9a882c7ca38e3e087d9
]
}
Ótimo, também temos apenas uma layer para a nossa base image, faz sentido,
certo? Nosso Dockerfile apenas altera o CMD da nossa imagem base, alpine:latest,
portanto, não adicionando nenhuma layer.
Por outro lado, comandos como COPY, ADD, RUN, FROM, geram layers.
# Base image
FROM alpine:latest
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:b2191e2be29d816fa6fbde954316d54e10df9a882c7ca38e3e087d9
"sha256:776a98989b1849ba22587754e65429864bc6602dea2b6d1ebe11bb9
]
}
Como esperado, temos agora 2 layers. Vamos entender no detalhe o que cada layer
dessa representa.
Para isso, podemos salvar nossa imagem como um tar file e verificar a imagem
mais profundamente:
Aqui podemos ver nosso manifest.json, que contem informacões sobre a imagem e
nossas 2 layers, 6d52… e 6b9a…
Acessem cada uma das layers e extraia o arquivo layer.tar dentro de cada um deles.
Com essa visão conseguimos descobrir várias coisas interessantes sobre nossa
imagem:
1. Toda estrutura do FS da nossa base image alpine está visível na sua respectiva
layer.
2. Nosso arquivo abc.txt pode ser acessado diretamente por a layer que o cria, não
estando disponível para a outra layer.
3. Uma layer é completamente diferente da outra, se um arquivo for criado em
uma layer e deletado em outra, acessando a imagem nesse formato, conseguimos
ver qual arquivo foi deletado.
Esse ponto 3 tem uma relevância enorme do ponto de vista de segurança, vamos
entender melhor.
# Base image
FROM alpine:latest
RUN rm abc.txt
Como devem imaginar, temos 3 instruções que geram layers, logo nossa imagem
agora possui 3 layers:
Extraia todas as layers e procure pela layer que contém o arquivo abc.txt:
Voilà!! Conseguimos encontrar o nosso valor sensível dentro do arquivo abc.txt.
Esse erro é muito comum de ser encontrado e pode gerar grandes prejuízos. No
nosso caso, o valor sensível está também no Dockerfile, apenas para ilustrar o
mecanismo, porém, isso também acontece ao copiar arquivos sensíveis para dentro
do container durante o build e apagarem logo na sequência, deixando a sensação de
que estão seguros. Qualquer um com acesso a imagem pode facilmente inspecionar
suas layers e buscar arquivos que foram deletados entre uma layer e outra, portanto,
muita atenção.
Multi-stage build
Agora que entendemos o que são layers, vamos falar melhor sobre uma estratégia
que utiliza do conceito de layers para prover imagens menores.
3. Após o build, os artefatos são gerados: artefatos nesse caso estamos falando de
outputs de mecanismos de build, como mvn package, go build e semelhantes para
outras linguagens.
4. Uma vez que temos nossos artefatos criados, usamos uma outra imagem,
normalmente chamada de run image, a image que será usada de fato para rodar sua
aplicação.
5. Os artefatos são copiados da imagem build para a imagem run, apenas eles.
6. Nossa imagem run não possui as bibliotecas necessárias para executar o build,
apenas o artefato e qualquer ferramenta necessária para rodar aquele artefato,
como por exemplo jdk para java ou o linux kernel para Golang.
Com esses passos em mente, podemos aplicar o mesmo conceito para qualquer tipo
de tecnologia. O grande truque aqui é sempre se provocar olhando para sua imagem
final e pensar: minha imagem tem apenas o que é necessário para rodar minha
aplicação? Se a resposta for sim, você aplicou corretamente o multi-stage build.
Vamos agora partir para a criação da nossa imagem, para isso, clonem este
repositório e sigam a partir dele, para facilitar o entendimento.
Ao visualizar o Dockerfile, vocês irão perceber que existem múltiplos passos para
criação da imagem, entre eles a compilação da nossa aplicação Golang:
# Start from the official Golang image to build the binary file
FROM golang:1.20 as builder -> GERA LAYER
ENV GO111MODULE=on
WORKDIR /app
WORKDIR /app/cmd/repositories-service
WORKDIR /root/
EXPOSE 8080
CMD ["./main"]
Ao visualizar o Dockerfile pela primeira vez e identificar todos comandos que geram
Layer, nos deparamos com 6 comandos que geram novas layers, porém se
inspecionarmos a imagem, apenas 4 estão presente:
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:4693057ce2364720d39e57e85a5b8e0bd9ac3573716237736d6470e
"sha256:b628a3cc1206e750fe115340e766b8ac4f3122cbde95dfc0888e023
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf1
"sha256:598affc186cb2864cda396e5d722430beaef1c28d88fdee620e8919
]
}
Uma dessas layers é vazia, pois essa imagem foi buildada juntamente com outra,
portanto apenas 3 layers com conteúdo estão na imagem final.
Com isso, conseguimos atingir uma imagem de apenas 28MB, pequena e segura :)
COPY vs ADD
Outro conceito que gera grande confusão e que muita das vezes funciona de
maneira semelhantes, podendo trazer comportamentos indesejados.
A regra de ouro aqui é a seguinte: caso seu objetivo seja apenas copiar um arquivo
do build context para o container:
# Base image
FROM alpine:latest
# Base image
FROM alpine:latest
2. URLs remotas
# Base image
FROM alpine:latest
Nesse cenário, que funciona apenas com ADD, estamos copiando um arquivo tar.gz
para dentro do container e também adicionando uma URL remota.
Por mais que pareça inofensivo, o comando ADD pode abrir brechas para ataques ao
seu sistema, ao permitir que conteúdos remotos sejam baixados e inseridos dentro
da imagem de maneira transparente, portanto, caso seu intuito seja apenas copiar
um arquivo, o que é a realidade para maioria dos casos, use COPY e seja feliz :)
Entrypoint vs CMD
Esses são outros dois comandos frequentemente confundidos quando se trabalha
com Dockerfiles. Ambos são usados para especificar um comando que será
executado quando um contêiner for iniciado a partir da imagem, mas eles são
usados de maneiras ligeiramente diferentes e podem ser combinados de maneiras
interessantes.
A regra de ouro é: use ENTRYPOINT quando você quer que o container execute um
comando específico como um executável, ou seja, sempre executar aquele comando
na inicialização, e CMD quando você quer definir um comando padrão que pode ser
substituído na linha de comando quando o contêiner é iniciado.
# Base image
FROM alpine:latest
ENTRYPOINT ["echo"]
Builde e rode sua imagem, desta vez passando "Hello, World!" como parametro de
execução:
Voce vera o mesmo “Hello, World!”, mesmo não tendo especificado que este
comando deveria ser inserido como parametro do echo.
Combinando ambos:
# Base image
FROM alpine:latest
ENTRYPOINT ["echo"]
Desta vez, podemos rodar a imagem sem nenhum parametro, ou com parametros:
Ao executar com parametros, voce verá que o parametro sera printado, ao invés de
Hello, World!. Isso acontece porque o parametro substitui o CMD do container,
sendo executado como um parametro para o ENTRYPOINT, neste caso, a função
echo.
Entender esses dois parametros é essencial tanto para desenvolvedores quanto para
adminstradores e devops engineers, pois eles definem como o seu container irá
rodar. Uma outra dica, caso esteja lidando com uma imagem pública ou até mesmo
privada mas que você não tenha ciencia de como foi criada, utilize o docker inspect
para validar tanto CMD quanto ENTRYPOINT defaults da imagem e poder utiliza-la
da forma como faz sentido para o seu caso de uso:
Mesmo sem saber o código que buildou a imagem, consegui entender exatamente o
que irá acontecer quando executar a imagem grafana/grafana: um script chamado
/run.sh irá executar, portanto posso acessar esse script para entender melhor sobre
a sua inicialização.
Outra dica muito importante para troubleshooting: Caso esteja tendo algum
problema na inicialização de um container mas não consegue debugar pois ele
morre antes, sobrescreva o ENTRYPOINT e o CMD de maneira que o comando
executado não falhe.
Vamos aplicar isso na prática para podermos ver o arquivo run.sh do grafana,
supondo que o container esteja dando erro na execução.
PERMISSIONS_OK=0
if [ ! -r "$GF_PATHS_CONFIG" ]; then
echo "GF_PATHS_CONFIG='$GF_PATHS_CONFIG' is not readable."
PERMISSIONS_OK=1
fi
Follow
Written by Igoreulalio Ie
41 Followers
25
12
Igoreulalio Ie
8
Igoreulalio Ie
58
595 9
RimonTawadrous
Why UUID7 is better than UUID4 as clustered index in RDBMS
In the Introduction To Database Indexing Article, We discussed database indexes, Their type,
representations, and use cases.
680 6
Lists
756 9
William Donze
55
Nidhey Indurkar
How did PayPal handle a billion daily transactions with eight virtual
machines?
I recently came across a reddit post that caught my attention: ‘How PayPal Scaled to Billions of
Transactions Daily Using Just 8VMs’…
3.3K 36
4.3K 55