Skip to main content

Como e quando usar rótulos do Docker/anotações de contêiner OCI

Escrito por
blog feature docker labels

3 de novembro de 2021

0 minutos de leitura

A maioria das imagens de contêiner é criada com Dockerfiles que combinam instruções como FROM, RUN, COPY, ENTRYPOINT etc. para criar as camadas de uma imagem compatível com OCI. Uma instrução surpreendentemente pouco usada, porém, é LABEL. Neste artigo, vamos explorar os rótulos ("anotações" na especificação de imagem OCI): o que são, alguns usos padronizados e práticas que você pode adotar para reforçar a segurança dos seus contêineres.

No restante deste artigo, vamos chamá-los de rótulos — em vez de anotações —, pois esse é o termo mais usado. Vamos começar!

O que são rótulos de imagem do Docker?

Os rótulos de imagem do Docker permitem adicionar metadados de chave-valor à própria imagem. Esses dados não ficam disponíveis para um contêiner que executa a imagem, mas são úteis para registrar, por exemplo, onde está o código-fonte da imagem, quem oferece suporte a ela ou qual build de CI a criou.

Entenda os metadados de imagens Docker/OCI

Se você já criou algum tipo de pacote de software, sabe que, em geral, esses pacotes incluem o software, a configuração e, às vezes, dados funcionais, além de metadados sobre o próprio pacote.

Por exemplo, embora os arquivos Java .jar sejam basicamente arquivos .zip, todos têm um diretório META-INF no nível superior, que contém vários arquivos e diretórios. De acordo com a especificação Java 2 Platform, eles “... são reconhecidos e interpretados pela Java 2 Platform para configurar aplicativos, extensões, carregadores de classes e serviços”. Se abrirmos um .jar criado pela popular ferramenta de build Maven, normalmente encontraremos, entre outras coisas, um diretório maven com conteúdo como o pom.xml efetivo do Maven e o pom.properties usados para criar o .jar. (A propósito, “POM” significa Project Object Model do Maven.)

RPM, APT, NPM e a maioria das outras ferramentas de empacotamento também armazenam metadados semelhantes, usados pelas ferramentas durante a instalação ou execução do software incluído, ou para fins utilitários em repositórios e sistemas de monitoramento em tempo de execução.

Captura de tela do histórico de uma imagem Docker no terminal, mostrando comandos, ponto de entrada, usuário, variável de ambiente, argumentos, rótulos e tamanhos das imagens.

As imagens de contêiner também armazenam metadados em suas camadas. Ao listar o “histórico” de uma imagem, você frequentemente verá camadas de tamanho zero byte. Elas não contêm alterações no sistema de arquivos; em vez disso, contêm metadados usados em tempo de execução, geralmente adicionados por comandos do Dockerfile:

  • USER: usuário com o qual o processo será executado

  • ENV: variável (e seu valor) a ser definida no ambiente dos processos

  • ARG: argumento de build passado para o contêiner de build e usado como uma variável de ambiente no escopo do build

  • CMD: comando e/ou parâmetros para iniciar o processo (ENTRYPOINT tem função semelhante)

  • LABEL: pares de chave-valor que não são usados pelo mecanismo de execução

A maioria desses itens é conhecida e usada em todos os Dockerfiles. Como os metadados de rótulos não são necessários para executar os contêineres, porém, eles costumam ser ignorados.

Por que usar rótulos em imagens de contêiner?

Há muitos motivos para usar rótulos nas suas imagens, como documentar versões, incluir informações de contato dos responsáveis pelo projeto ou até dados sobre o uso em tempo de execução. Um dos casos de uso mais comuns é registrar informações sobre como a imagem foi criada, que podem ser usadas na cadeia de suprimentos de software do artefato de imagem.

Tipos de metadados de rótulos do Docker/anotações de imagem OCI

Rótulos padronizados

Os metadados sobre a criação e a origem de imagens são tão comuns que a equipe da OCI publica um conjunto padronizado de chaves, todas com o prefixo “org.opencontainers.image.”, incluindo:

  • source: URL para acessar o código-fonte usado para criar a imagem

  • revision: identificador da revisão do controle de versão do software empacotado

  • base.digest: digest (hash) da imagem usada como base

  • base.name: referência da imagem usada como base

  • version: versão do software empacotado

Rótulos personalizados

Como são apenas pares de chave-valor, seu projeto ou sua organização pode especificar praticamente qualquer coisa. Algumas ideias (todas poderiam usar um prefixo como “com.mycorp.myteam.”):

  • ci-build: URL da execução do projeto de CI que criou a imagem

  • releasenotes: notas da versão do software empacotado

  • healthz: endpoint HTTP para verificações de integridade

  • docker.run: exemplo de comando do Docker para executar a imagem

  • k8s.deployment: YAML em Base64 para uma implantação do Kubernetes que usa esta imagem

O último exemplo é interessante porque, sim, você pode armazenar praticamente qualquer conteúdo que consiga codificar em Base64 como valor de uma chave de rótulo. Isso significa que você pode extrair a imagem e executar algo como o comando a seguir para obter um arquivo YAML de implantação de exemplo, que depois pode ser usado em um cluster Kubernetes:

$ docker image inspect myimage:tag | jq -r ".[].Config.Labels.\"com.mycorp.myteam.k8s.deployment\"" | base64 -d
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: snyk
  name: snyk
spec:
  replicas: 1
  selector:
    matchLabels:
      app: snyk
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: snyk
    spec:
      containers:
      - image: ericsmalling/snyklabeldemo:m
        name: snyklabeldemo
        resources: {}
status: {}

Você pode até direcionar a saída diretamente para kubectl e fazer a implantação na hora, sem precisar de gráficos Helm ou arquivos YAML separados no repositório Git!

docker image inspect myimage:tag | jq -r ".[].Config.Labels.\"com.mycorp.myteam.k8s.deployment\"" | base64 -d | kubectl apply -f - 

deployment.apps/snyk created

Como aproveitar os rótulos do Docker/anotações OCI

Como você pode imaginar, os rótulos podem receber qualquer valor acessível pelo seu sistema de CI e ajudar a relacionar imagens e/ou contêineres em execução às respectivas origens, documentações etc., bastando inspecionar seus rótulos. Por exemplo, digamos que sua organização publique imagens com o rótulo padrão da OCI org.opencontainers.image.source, que contém a URL do repositório de controle de código-fonte de origem da imagem. Para descobrir quais imagens de repositórios estão em execução em um determinado host Docker, você poderia executar algo assim:

$ docker inspect $(docker ps -q) --format='{{ .Id }} {{ index .Config.Labels "org.opencontainers.image.source" }}'

17f4ee967870c49d3ffeb1c49973071c99c63377b2f9bbf987f7c3e4a21d331c https://repo.mycorp.com/team-volton/redlion
c958ffc87c2bd5af500d24eff1ccb3ee21992a5cbcb429fafcc651aa182b66ba <no value>

A saída mostra os IDs de dois contêineres em execução. Um deles tem o rótulo, então seu valor foi exibido.

As coisas ficam um pouco mais complicadas em um cluster Kubernetes, onde você provavelmente não terá acesso aos sockets do mecanismo de contêiner nos nós do cluster. Infelizmente, não há uma API para acessar esses rótulos com kubectl, então precisamos usar uma abordagem mais criativa. O script Bash a seguir encontra as imagens em execução no namespace do contexto atual, consulta o registro de imagens para obter os metadados e retorna as informações dos rótulos:

$ cat labelgrep.sh
#!/bin/bash
FINDLABEL=$1
FINDVAL=$2

IMAGES=$(kubectl get pods -o json | jq -r ".items[].spec.containers[].image" | uniq)

for i in $IMAGES; do
	VAL=$(regctl image inspect ${i} --format '{{ index .Config.Labels "'${FINDLABEL}'" }}')
	if [[ "$VAL" != "" && ( "$FINDVAL" == "" || "$VAL" == "$FINDVAL") ]]; then
	  echo "[${i}] ${FINDLABEL}=${VAL}"
  fi
done

Vale destacar que estou usando a excelente ferramenta regctl, do projeto de código aberto regclient. Ela permite obter informações sobre imagens diretamente de um registro, sem precisar baixá-las para meu ambiente local para inspecioná-las. Assim, também posso executar esse script onde quiser, sem precisar de um mecanismo de execução de contêineres.

Agora, vamos executar isso em um cluster e procurar os repositórios das imagens em execução nele:

$ ./labelgrep.sh org.opencontainers.image.source
[images.mycorp.com/voltron/redlion] org.opencontainers.image.source=https://repo.mycorp.com/team-volton/redlion
[images.mycorp.com/voltron/bluelion] org.opencontainers.image.source=https://repo.mycorp.com/team-volton/bluelion

Como você pode ver, no momento há duas imagens no meu cluster em execução em pods que têm o rótulo org.opencontainers.image.source.

Embora esses exemplos sejam relativamente simples, tenho certeza de que você pode desenvolver os conceitos e criar seus próprios scripts ou chamadas de API para atender às necessidades da sua organização.

Integração com a Snyk

Um dos aspectos mais interessantes para a segurança é que a análise de imagens da Snyk agora oferece suporte à associação automática da imagem analisada ao Dockerfile correspondente, usando rótulos de imagem.

A integração da Snyk com repositórios de código-fonte já consegue detectar e analisar estaticamente os Dockerfiles encontrados no seu código, além de imagens importadas dos seus registros de contêineres. Até pouco tempo, porém, era preciso fazer essa associação cruzada por conta própria. Você tinha que adicionar manualmente a referência ao Dockerfile na imagem ou implementar sua própria automação para fazer isso por meio de chamadas de API.

Agora, basta incluir o rótulo padrão da OCI org.opencontainers.image.source com a URL do repositório que contém o Dockerfile. Quando a imagem for importada, a Snyk fará a associação cruzada automaticamente.

Página de detalhes da imagem Docker mostrando a imagem vinculada “docker-imageleric” destacada com anotações em vermelho.

Exemplo de projeto de análise de imagem vinculado automaticamente

Conclusão

Em resumo, o rótulo/anotação de imagem, muitas vezes esquecido, é uma ferramenta poderosa que permite inserir metadados diretamente nas imagens. Usar chaves padronizadas pode ajudar não apenas a documentar a origem de uma imagem, mas também permitir que ferramentas de implantação e segurança entendam melhor o ambiente implantado.

Você já pode começar a aproveitar o recurso de vinculação automática de imagens da Snyk: basta adicionar o rótulo padrão da OCI org.opencontainers.image.source aos Dockerfiles que estão sendo analisados na sua conta. Ainda não tem uma conta Snyk? Cadastre-se gratuitamente e comece a usar o recurso agora mesmo!

Quero saber como você está usando rótulos. Essas ideias são novas ou seus projetos já os utilizam? Que outras formas interessantes vocês estão usando para anotar imagens? Há novas integrações que você gostaria de ver, com a Snyk ou outras ferramentas? Marque-me (@ericsmalling) no Twitter e compartilhe suas ideias. Vou adorar saber o que você pensa!

Segurança de contêineres que prioriza os desenvolvedores

O Snyk encontra e corrige automaticamente vulnerabilidades em imagens de contêiner e workloads do Kubernetes.