O blog da AWS
Construindo Imagens de Containers no Kubernetes sem o docker.sock
Por Douglas Ramiro, Arquiteto de Soluções na AWS e
Lucas Duarte,Arquiteto Especialista na AWS
Em 2020, a Cloud Native Computing Foundation (CNCF), que mantem a distribuição open source do Kubernetes, anunciou que o Docker, como ambiente de execução, seria depreciado, sendo trocado por ambientes de execução que utilizam o Container Runtime Interface (CRI) criado especificamente para o Kubernetes. No entanto, imagens construídas com o Docker continuaram funcionando no seu cluster com todos os ambientes de execução. Para o Amazon EKS, desde a versão 1.21, o ambiente de execução containerd é suportado, porém não é a opção padrão. Na próxima versão do EKS, que será baseado no Kubernet die es 1.22, o ambiente de execução padrão será substituído de Docker para containerd.
Usualmente as tarefas de CI/CD que necessitam gerar uma imagem Docker realizam a construção dentro de um container Docker, como por exemplo, agentes do Jenkins rodando no EKS, GitLab Runners, etc. Essa técnica é chamada de Docker dind (Docker in Docker). Para fazer isso, você precisar montar o socket /var/run/docker.sock disponível na máquina host que está rodando o container dentro do container que vai construir a nova imagem.
Com o socket montado, você tem a possibilidade de rodar comandos Docker dentro de containers. Observe que montar o socket não te disponibiliza um novo ambiente de Docker. Ao invés disso, a instalação do Docker existente será utilizada dentro do container. Dessa forma, você pode construir imagens de Docker dentro de um container Docker.
Como você vai construir uma imagem de container com Docker in Docker se esse ambiente de execução vai ser depreciado no Kubernetes?
Uma possibilidade para essa questão é utilizar o Img. Img é um construtor de imagem de container independente, compatível com Dockerfile e OCI, sem daemon e sem privilégios. O Img permite fazer pequenos ajustes nas suas pipelines para construir suas imagens. Basicamente você irá substituir o comando docker build pelo comando img build.
Solução
Para começar a utilizar o Img, você precisa seguir os passos abaixo:
- Crie um cluster EKS que irá hospedar o pod do Img.
- Construa uma imagem de container que contenha o Img através de um Dockerfile.
- Publique essa imagem em um repositório utilizando o AWS Elastic Container Registry (ECR).
- Implante um novo pod a partir da imagem gerada.
- Inicie uma nova construção simulando uma ferramenta de CI como Jenkins, Gitlab Ci ou outra. Para fazer isso, você vai acessar o container que contêm o Img e vai iniciar uma nova construção sem mapear o socket.
Você vai precisar dos seguintes pré requisitos para completar esse tutorial:
Exportando Variáveis de Ambiente
Algumas variáveis de ambiente são necessárias para criar um cluster EKS. Abra um novo terminal e exporte essas variáveis de ambiente:
export AWS_REGION="us-east-1" export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account) export AZS=($(aws ec2 describe-availability-zones --query 'AvailabilityZones[].ZoneName' --output text --region $AWS_REGION)) export AMI_ID=$(aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.21/amazon-linux-2/recommended/image_id --region $AWS_REGION --query "Parameter.Value" --output text)
Crie um cluster EKS
Você irá utilizar o eksctl para provisionar o seu cluster. Observe que você está definindo o ambiente de execução continerd. Crie o arquivo de manifesto do cluster:
cat << EOF > containerd-eks.yaml apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: containerd-cluster region: ${AWS_REGION} version: '1.21' availabilityZones: ["${AZS[1]}", "${AZS[2]}", "${AZS[3]}"] iam: withOIDC: true managedNodeGroups: - name: default-ng ami: "${AMI_ID}" instanceType: m5.large minSize: 1 maxSize: 3 overrideBootstrapCommand: | #!/bin/bash /etc/eks/bootstrap.sh containerd-cluster --container-runtime containerd desiredCapacity: 2 labels: {role: mngworker} cloudWatch: clusterLogging: enableTypes: ["*"] EOF
Aplique o manifesto para criar o cluster, executando o seguinte comando abaixo:
eksctl create cluster -f containerd-eks.yaml
Caso não tenha instalado o eksctl em seu ambiente, siga estes passos para instalá-lo. O cluster será criado em aproximadamente 15 minutos.
Crie o Dockerfile com o Img
Agora, é necessário criar o arquivo Dockerfile que vai instalar o Img. Você pode utilizar como imagem base o alpine. O pacote do Img vem por padrão na imagem base alpine. Crie o arquivo Dockerfile:
cat << EOF > Dockerfile FROM alpine:latest LABEL maintainer="lucasdu@amazon.com" RUN apk add img RUN apk add vim RUN apk add --no-cache \ python3 \ py3-pip \ && pip3 install --upgrade pip \ && pip3 install --no-cache-dir \ awscli \ && rm -rf /var/cache/apk/* CMD exec /bin/sh -c "while true; do sleep 30; done" EOF
Observe a diretiva CMD no Dockerfile. O loop infinito (“while true”) serve para que o pod fique sempre rodando dentro do cluster EKS.
Crie um repositório ECR e construa a imagem
Para hospedar a sua imagem Docker, primeiro você precisa criar um repositório de imagens no ECR:
aws ecr create-repository –repository-name img –region ${AWS_REGION}
Depois de criar o repositório, você precisa recuperar o token de autenticação e autenticar o cliente Docker ao servidor de hospedagem:
aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com
Agora você pode construir a imagem que contêm o Img:
docker build -t ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/img:latest .
Quando a construção for finalizada, envie a imagem para o repositório:
docker push ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/img:latest
Criar o manifesto de Deployment
Antes de criar o manifesto de implantação (deployment), você precisar criar uma conta de serviço associada a uma IAM role. Isso é necessário porque o container que estará rodando precisa de permissões para enviar a nova imagem de container para o repositório no ECR. Crie a conta de serviço usando os seguintes comandos do eksctl:
eksctl utils associate-iam-oidc-provider --region=us-east-1 --cluster=containerd-cluster --approve eksctl create iamserviceaccount \ --name img-service-account \ --namespace default \ --cluster containerd-cluster \ --attach-policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess \ --approve \ --override-existing-serviceaccounts
Para implantar a imagem que contem o Img dentro do cluster EKS, com o objetivo de construir uma imagem de container dentro de um container rodando, você precisa primeiro criar um manifesto de implantação do Kubernetes:
cat << EOF > img-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: labels: app: img-deploy name: img-deploy namespace: default spec: replicas: 1 selector: matchLabels: app: img-deploy template: metadata: labels: app: img-deploy spec: serviceAccountName: img-service-account containers: - image: ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/img:latest name: img securityContext: privileged: true EOF
Aplique o manifesto para implantar a aplicação:
kubectl apply -f img-deployment.yaml
Verifique se o Pod está rodando:
kubectl get pods -ndefault | grep -i img
Aguarde até que pod esteja com o status de Running antes de proseguir.
img-deploy-5d74bd95b4-46b9r 1/1 Running 0 3d17h
Construa uma imagem de container dentro de um pod
Nessa parte desse blog, você irá simular uma ferramenta de CI que constrói uma imagem de container. Você irá acessar o terminal do pod que está rodando o Img e construir a imagem de container sem a necessidade de montar o docker.sock
Acesso ao terminal do pod:
kubectl exec -it po/$(kubectl get pods -ndefault | grep -i img | awk '{print $1}') -ndefault -- /bin/sh
Após o acesso, crie um Dockerfile simples para testar o build:
cat << EOF > /opt/Dockerfile FROM alpine:latest RUN apk add nginx RUN mkdir -p /run/nginx RUN touch /run/nginx/nginx.pid RUN adduser -D -g 'www' www RUN mkdir /www RUN chown -R www:www /var/lib/nginx RUN chown -R www:www /www RUN ["./usr/sbin/nginx"] EOF
Construa uma nova imagem de container utilizando a ferramenta Img:
cd /opt/ && img build -t test-app .
O resultado será similar ao log abaixo:
=> exporting to image 0.0s => => exporting layers 0.0s => => exporting manifest sha256:fb751b3025262b3391ba4dd8347c758cd326c1485e104dd81a9cdac58b023763 0.0s => => exporting config sha256:85d5328384a59576394b174291c6afb17376c15b114231c6f2ba8b278c2e1980 0.0s => => naming to docker.io/library/test-app:latest 0.0s => exporting cache 0.0s => => preparing build cache for export 0.0s Successfully built docker.io/library/test-app:latest
Concluído. Agora você tem uma imagem de container construída pelo Img. Neste momento você já pode enviar essa imagem para o Amazon ECR e implanta-la aonde você quiser.
Conclusão
Primeiro de tudo, não fique em pânico. A única coisa que será depreciado é o Docker como ambiente de execução. Amazon EKS irá continuar funcionando com imagens de containers que foram construídas com o Docker. Entretanto, containerd será o ambiente de execução padrão.
Existem diversas soluções para readaptar as suas pipelines devido a esta mudança, algumas delas mais complexas e outras mais simples como essa apresentada.
Sobre os autores
Douglas Ramiro é arquiteto de soluções com especialização em Spot e Graviton na AWS. Todos os dias, os clientes da AWS economizam ao adotar essas tecnologias. Douglas tem a missão de disseminar essas tecnologias em LATAM para que os clientes AWS possam reduzir seus custos.
Lucas Duarte é um Arquiteto Especialista em Containers na AWS com focos em clientes LATAM. Entusiasta de automação, Cloud, e cultura DevOps. Com experiência prévia em projetos focados nesse segmento em empresas como iFood, Guiabolso e Mandic. Tem trabalhado em diferentes projetos relacionados principalmente a orquestração de containers e microserviços.