Containers e Images
Se você está começando sua jornada com containers, é uma boa ideia começar estudando Docker. Temos um material completo disponível aqui no nosso site que aborda esse assunto detalhadamente.
Este estudo irá ajudá-lo a se familiarizar com a criação de imagens e a compreender como os containers funcionam.
O principal objetivo desse material é ensinar como containerizar uma aplicação, o que envolve a criação de uma imagem para a mesma.
Embora os containers já existissem no Linux muito antes do Docker, essa ferramenta revolucionou a maneira como trabalhamos com eles, estabelecendo um padrão para a criação de imagens. Ao construir um Dockerfile, estamos na verdade criando uma imagem que será executada em um container runtime.
Para manter esses padrões, foi criada a opencontainers.org. Um Dockerfile é simplesmente um arquivo que adere a esses padrões.
Não é função do desenvolvedor se importar com qual container runtime esta instalado nos nodes do cluster que a aplicação roda. Ele só precisa criar imagens seguindo a convenção e boas práticas.
Desde usemos os padrões do opencontainers.org podemos rodar a imagem em vários runtimes diferentes. Pode ser o containerd, ou o CRI-O, Podman, Rkt e por ai vai.
Apesar de não ser obrigatório, é recomendável que o arquivo que vamos declarar os passos para a criação da imagem seja nomeado Dockerfile. A principal vantagem é que isso elimina a necessidade de especificar o nome do arquivo como um parâmetro, já que esse é o padrão utilizado.
Podemos criar imagens usando várias ferramentas diferentes:
O único requisito é ter um container runtime instalado na máquina. Essas ferramentas simplesmente interagem com o runtime.
A instalação do Docker nos fornece todas essas ferramentas em uma única instalação, o que é ideal para ambientes de desenvolvimento. No entanto, para rodar os containers em um cluster, é melhor evitar instalar todas essas ferramentas e optar apenas pelo que é necessário, ou seja, um container runtime. Por essa razão, atualmente o containerd é a opção mais recomendada.
Então para o estudo, faça a instalação do Docker para o seu sistema operacional.
Imagens
Se você está rodando uma aplicação na sua máquina o que você precisou fazer?
1 - Ter um sistema operacional para rodar as coisas 2 - Ter o compilador ou interpretador da linguagem. Por exemplo o java, go, python e por ai vai. 3 - Instalar as dependências da aplicação, ou seja as bibliotecas, pacotes, etc 4 - Buildar a aplicação para gerar os executáveis 5 - Rodar os executáveis com as variáveis de ambientes definidas.
Mas no fim o que precisamos mesmo é ter um sistema que possam executar os arquivos gerados. Não precisamos ter o código fonte da aplicação dentro do container, só os executáveis e suas dependências.
Vou chamar de Dockerfile o arquivo que irá criar a rotina.
1 - Sempre é necessário declarar um FROM
FROM ubuntu
Vamos buildar esse cara. Crie um arquivo Dockerfile com esse conteúdo.
❯ docker build -t davidpuziol/ubuntu .
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
Install the buildx component to build images with BuildKit:
https://docs.docker.com/go/buildx/
Sending build context to Docker daemon 317.2MB
Step 1/1 : FROM ubuntu
latest: Pulling from library/ubuntu
fdcaa7e87498: Pull complete
Digest: sha256:562456a05a0dbd62a671c1854868862a4687bf979a96d48ae8e766642cd911e8
Status: Downloaded newer image for ubuntu:latest
---> de52d803b224
Successfully built de52d803b224
Successfully tagged davidpuziol/ubuntu:latest
O que foi feito? A partir do ubuntu ele criou a imagem chamada davidpuziol/ubuntu e nada mais. Se rodarmos essa imagem temos o ubuntu lá
❯ docker run -it davidpuziol/ubuntu:latest bash
root@e8e350471d95:/# cat /etc/os-release
PRETTY_NAME="Ubuntu 24.04 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo
# Não temos o python aqui
root@e8e350471d95:/# whereis python
python:
root@e8e350471d95:/#
a imagem davidpuziol/ubuntu é exatamente a mesma imagem do ubuntu. Veja que tem o mesmo tamanho, a mesma IMAGE_ID
❯ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
davidpuziol/ubuntu latest de52d803b224 2 days ago 76.2MB
ubuntu latest de52d803b224 2 days ago 76.2MB
kindest/node <none> 09c50567d34e 2 months ago 956MB
Essa será a imagem inicial que você vai usar para começar a fazer o que você precisa. Vamos imaginar que eu quero instalar o python nesse ubuntu. Cada comando RUN executa uma layer no seu container.
Precisamos fazer o update dos repositorios e depois instalar o python.
FROM ubuntu:22.04
RUN sudo apt-get update
RUN sudo apt-get install python -y
Buildando essa imagem com o mesmo comando acima teremos uma imagem que fez update e logo depois já instalou o python. Essa imagem utilizou o ubuntu 22.04. A primeira usou o 24.04.
Todo comando não deve ter interação com o usuário, lembre isso. Por isso o -y foi passado no parâmetro install.
Buildando novamente, vamos definir uma tag. Quando não definimos a tag é latest
docker build -t davidpuziol/ubuntu:v1 .
# vai mostrar todo o processo de instalação
#Rodando imagem
❯ docker run -it davidpuziol/ubuntu:v1 bash
root@db6927a8b536:/# whereis python3
python3: /usr/bin/python3 /usr/lib/python3 /etc/python3 /usr/share/python3
root@db6927a8b536:/# python3 --version
Python 3.10.12
Mas precisamos que esta imagem tenha variáveis de ambiente. Vamos colocar uma variavel chamada MYVAR=david
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install python -y
ENV MYVAR=david
# Vamos mudar para a v2
docker build -t davidpuziol/ubuntu:v2 .
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
Install the buildx component to build images with BuildKit:
https://docs.docker.com/go/buildx/
Sending build context to Docker daemon 317.2MB
Step 1/4 : FROM ubuntu:22.04
---> 437ec753bef3
Step 2/4 : RUN apt-get update
---> Using cache
---> 36ece36a9a1c
Step 3/4 : RUN apt-get install python3 -y
---> Using cache # OBSERVE QUE ELE UTILIZOU A IMAGEM QUE ELE JA TINHA ANTES
---> 243f9ff2aa56
# ADICIONOU SOMENTE O UTIMOS PASSO
Step 4/4 : ENV MYVAR=david
---> Running in 9200241f31f5
---> Removed intermediate container 9200241f31f5
---> ef48a65d545f
Successfully built ef48a65d545f
Successfully tagged davidpuziol/ubuntu:v2
docker run -it davidpuziol/ubuntu:v2 bash
root@ba9c48260bb4:/# env | grep MYVAR
MYVAR=david
root@ba9c48260bb4:/#
até agora nós somente instalamos ferramentas, variaveis de ambiente, mas ainda não colocamos nada para rodar.
Quando defimos para rodar a aplicação com o comando docker run -it davidpuziol/ubuntu:v2 bash nos forçamos que o bash seja o entrypoint desse container. Se o bash parar o container morrerá.
A função do container é morrer, o que esta segurando ele vivo é o bash que foi passado. Se o bash parar ele morrerá. Todo container precisa de um entrypoint. O entrypoint é o processo que irá segurar o container rodando.
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install python -y
ENV MYVAR=david
ENTRYPOINT ["bash"]
docker build -t davidpuziol/ubuntu:v3 .
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
Install the buildx component to build images with BuildKit:
https://docs.docker.com/go/buildx/
Sending build context to Docker daemon 317.2MB
Step 1/5 : FROM ubuntu:22.04
---> 437ec753bef3
Step 2/5 : RUN apt-get update
---> Using cache
---> 36ece36a9a1c
Step 3/5 : RUN apt-get install python3 -y
---> Using cache
---> 243f9ff2aa56
Step 4/5 : ENV MYVAR=david
---> Using cache
---> ef48a65d545f
Step 5/5 : ENTRYPOINT ["bash"]
---> Running in 952493e489a0
---> Removed intermediate container 952493e489a0
---> a2a274001f9a
Successfully built a2a274001f9a
Successfully tagged davidpuziol/ubuntu:v3
# Nao passamos o comando que ele irá executar. Quando entramos ele foi logo pro bash
❯ docker run -it davidpuziol/ubuntu:v2
root@e0ae401dc83b:/#
Vamos observar como a imagem cresceu.
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
davidpuziol/ubuntu v3 a2a274001f9a 2 minutes ago 158MB # entrypoint nao mudou o tmanho, mas mudou o ID
davidpuziol/ubuntu v2 ef48a65d545f 11 minutes ago 158MB # environment não muda o tamanho, mas mudou o ID
davidpuziol/ubuntu v1 243f9ff2aa56 18 minutes ago 158MB # S0 + python
davidpuziol/ubuntu latest de52d803b224 2 days ago 76.2MB # somente o SO
ubuntu latest de52d803b224 2 days ago 76.2MB
ubuntu 22.04 437ec753bef3 8 days ago 77.9MB
kindest/node <none> 09c50567d34e 2 months ago 956MB
Observação:
ENTRYPOINT represente no container do POD o command:
CMD representa no contianer do POD os args:
Se quisermos criar uma imagem que vai dormir 60 segundos
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install python -y
ENV MYVAR=david
ENTRYPOINT ["sleep"]
CMD ["60"]
Temos tb o COPY que irá copiar o conteúdo da máquian que esta buildando para dentro da imagem. Esse é o momento que você irá copiar a aplicação para dentro do container.
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install python -y
RUN outros comandos de dependencia que você precisa do python por exemplo
ENV MYVAR=david
# ORIGEM NA MAQUINA LOCAL DESTINO NO CONTAINER
copy . /opt/myapp
# mesma coisa que fazer um RUN cd /opt/myapp para mudar de diretório
WORKDIR /opt/myapp
# a porta do container que você quer expor.
EXPOSE 8080
ENTRYPOINT ["python3"]
CMD ["meuapp.py"]
Isso é o básico que você precisa saber. A porta será a comunicação com a aplicação. Sem expor uma porta um service no kubernetes não conseguirá alcança o container do pod.
É claro que sua aplicação também estará prepara para fazer alguma coisa na porta 8080 por isso estamos expondo esta porta.
tem muitos detalhes que envolvem a criação de uma imagem. Esse container acima esta rodando com usuário root que não seria ideal para segurança. Veremos mais detalhes ao longo do caminho.
Faça um estudo mais aprofundado sobre Docker que tudo ficará mais claro.
Confira mais informações sobre entrypoint e runtime
Esteja familiarizado com os comandos
docker image
docker inspect
docker run -p porthost:postcontainer image:tab
docker run -p porthost:postcontainer --entrypoint meuentrypoint image:tab arg1 arg2 arg3
docker run -e APP=test
# comandos mapeando volumes também são necessários para entender os containers e coisas as coisas funcionam dentro dos pods