Práticas recomendadas para criar contêineres

Last reviewed 2023-02-28 UTC

Neste artigo, descrevemos um conjunto de práticas recomendadas para criar contêineres. Elas abrangem uma ampla variedade de metas, incluindo o encurtamento do tempo de criação e a elaboração de imagens menores e mais resilientes. O objetivo é facilitar a criação de contêineres (por exemplo, com o Cloud Build) e a execução deles no Google Kubernetes Engine (GKE).

Essas práticas têm importâncias diferentes. Por exemplo, execute com sucesso uma carga de trabalho de produção sem algumas delas, mas outras são fundamentais. Em especial, a importância das práticas recomendadas em relação à segurança é subjetiva. A implementação delas depende do ambiente e das restrições.

Para aproveitar ao máximo este artigo, você precisa ter conhecimento sobre o Docker e o Kubernetes. Algumas práticas recomendadas discutidas aqui também se aplicam a contêineres do Windows, mas a maioria presume que você esteja trabalhando com contêineres do Linux. Você encontra dicas sobre como executar e operar contêineres em Práticas recomendadas para a operação de contêineres.

Empacotar um único aplicativo por contêiner

Importância: ALTA

No início, é um erro comum tratar contêineres como máquinas virtuais (VMs, na sigla em inglês) que podem executar muitos elementos diferentes ao mesmo tempo. Um contêiner pode funcionar dessa maneira, mas isso reduz a maioria das vantagens do modelo de contêiner. Por exemplo, pegue uma pilha clássica do Apache/MySQL/PHP: talvez você fique tentado a executar todos os componentes em um único contêiner. No entanto, a prática recomendada é usar dois ou três contêineres diferentes: um para o Apache, um para o MySQL e, possivelmente, um para o PHP se você estiver executando o PHP-FPM.

Como um contêiner é projetado para ter o mesmo ciclo de vida que o app que ele hospeda, cada um pode ter somente um app. Quando um contêiner é iniciado, o app também é, e quando o app é interrompido, o contêiner também é. O diagrama a seguir mostra essa prática recomendada.

Um diagrama do processo de inicialização sem uma imagem personalizada.

Figura 1. O contêiner à esquerda segue a prática recomendada. O contêiner à direita não.

Se você tiver diversos aplicativos em um contêiner, eles poderão ter ciclos de vida diferentes ou estar em estados distintos. Por exemplo, você pode acabar com um contêiner em execução, mas com um dos principais componentes dele com falha ou sem resposta. Sem uma verificação de integridade adicional, o sistema geral de gerenciamento de contêiner (Docker ou Kubernetes) não pode informar se o contêiner está íntegro. No caso do Kubernetes, isso significa que, se um componente principal não responder, o Kubernetes não reiniciará o contêiner automaticamente.

Você verá as ações a seguir em imagens públicas, mas não siga o exemplo delas:

Lidar adequadamente com PID 1, manipulação de sinal e processos zumbi

Importância: ALTA

Os sinais do Linux são a principal maneira de controlar o ciclo de vida dos processos dentro de um contêiner. De acordo com a prática recomendada anteriormente, para vincular firmemente o ciclo de vida de seu aplicativo ao contêiner em que ele está, garanta que o aplicativo manipule sinais Linux corretamente. O sinal mais importante é o SIGTERM, porque encerra um processo. O app também pode receber um sinal SIGKILL, que é usado para encerrar o processo de modo não otimizado, ou um sinal SIGINT, que é enviado quando você digita Ctrl+C e é geralmente tratado como SIGTERM.

Identificadores de processo (PIDs, na sigla em inglês) são exclusivos e fornecidos pelo kernel do Linux a cada processo. Os PIDs usam namespace, o que significa que um contêiner tem o próprio conjunto de PIDs que são mapeados para PIDs no sistema de hospedagem. O primeiro processo executado ao iniciar um kernel do Linux tem o PID 1. Para um sistema operacional normal, esse processo é o sistema init, por exemplo, systemd ou SysV. Da mesma forma, o primeiro processo lançado em um contêiner recebe o PID 1. O Docker e o Kubernetes usam sinais para se comunicar com os processos dentro de contêineres, principalmente para encerrá-los. O Docker e o Kubernetes só podem enviar sinais para o processo que tenha o PID 1 dentro de um contêiner.

No contexto de contêineres, os sinais PIDs e Linux criam dois problemas a serem considerados.

Problema 1: como o kernel do Linux manipula sinais

O kernel do Linux manipula os sinais de maneira diferente para o processo que tem PID 1 do que para outros processos. Os manipuladores de sinais não são registrados automaticamente para este processo, o que significa que, por padrão, sinais como SIGTERM ou SIGINT não terão efeito. Por padrão, é necessário eliminar processos usando o SIGKILL, impedindo qualquer desligamento otimizado. Dependendo do aplicativo, o uso do SIGKILL pode resultar em erros do usuário, gravações interrompidas (no armazenamento de dados) ou alertas indesejados no sistema de monitoramento.

Problema 2: como os sistemas init clássicos gerenciam processos órfãos

Os sistemas init clássicos, como o systemd, também são usados para remover (colher) processos zumbis órfãos. Processos órfãos (com pais encerrados) são anexados novamente ao processo que tem o PID 1, que os colherá quando morrerem. Um sistema init normal faz isso. Mas em um contêiner, essa responsabilidade recai sobre qualquer processo que tenha PID 1. Se esse processo não manipular processar a coleta adequadamente, você corre o risco de ficar sem memória ou alguns outros recursos.

Esses problemas têm várias soluções comuns, descritas nas seções a seguir.

Solução 1: execute como PID 1 e registre manipuladores de sinal

Esta solução resolve apenas o primeiro problema. Ela é válida se o app gera processos filho com controle (o que geralmente é o caso), evitando o segundo problema.

A maneira mais fácil de implementar essa solução é iniciar o processo com as instruções CMD e/ou ENTRYPOINT no Dockerfile. Por exemplo, no Dockerfile a seguir, o nginx é o primeiro e único processo a ser iniciado.

FROM debian:11

RUN apt-get update && \
    apt-get install -y nginx

EXPOSE 80

CMD [ "nginx", "-g", "daemon off;" ]

Às vezes, talvez você precise preparar o ambiente no contêiner para que o processo seja executado corretamente. Nesse caso, a prática recomendada é fazer com que o contêiner execute um script de shell ao iniciar. Esse script de shell é encarregado de preparar o ambiente e iniciar o processo principal. No entanto, se você adotar essa abordagem, o script de shell terá o PID 1, e não seu processo. Por isso, use o comando exec integrado para iniciar o processo pelo script de shell. O comando exec substitui o script pelo programa que você quiser. Seu processo então herda o PID 1.

Solução 2: ativar o compartilhamento de namespace do processo no Kubernetes

Quando você ativa o compartilhamento de namespace de processo para um pod, o Kubernetes usa um único namespace de processo para todos os contêineres nesse pod. O contêiner de infraestrutura do pod do Kubernetes torna-se PID 1 e coleta automaticamente processos órfãos.

Solução 3: use um sistema init especializado

Como você faria em um ambiente Linux mais clássico, também é possível usar um sistema init para lidar com esses problemas. No entanto, os sistemas init normais, como systemd ou SysV, são muito complexos e grandes para essa finalidade. Por isso, recomendamos que você use um sistema init como tini (em inglês), criado especialmente para contêineres.

Se você usa um sistema init especializado, o processo init tem o PID 1 e executa estas ações:

  • Registra os gerenciadores de sinal corretos.
  • Garante que os sinais funcionem no aplicativo.
  • Coleta qualquer processo zumbi que venha a aparecer.

É possível usar essa solução no próprio Docker usando a opção --init do comando docker run. Para usar essa solução no Kubernetes, você precisa instalar o sistema init na imagem do contêiner e usá-lo como ponto de entrada para o contêiner.

Otimizar o cache de criação do Docker

Importância: ALTA

O cache de criação do Docker pode acelerar bastante a criação de imagens de contêiner. As imagens são criadas camada por camada e, em um Dockerfile, cada instrução cria uma camada na imagem resultante. Durante uma criação, quando possível, o Docker reutiliza uma camada de uma criação anterior e ignora uma etapa custosa em potencial. O Docker pode usar o cache de criação somente se todas as etapas de criação anteriores o utilizarem. Esse comportamento geralmente é positivo e faz com que as criações sejam mais rápidas, mas é necessário considerar alguns casos.

Por exemplo, para aproveitar ao máximo o cache de criação do Docker, você precisa posicionar as etapas de criação que são alteradas com frequência na parte inferior do Dockerfile. Se você colocá-las na parte superior, o Docker não poderá usar o cache de criação para as outras etapas que estão sendo alteradas com menos frequência. Como uma nova imagem do Docker é geralmente criada para cada nova versão do código-fonte, adicione esse código à imagem o mais tarde possível no Dockerfile. No diagrama a seguir, observe que, se estiver alterando STEP 1, o Docker poderá reutilizar apenas as camadas da etapa FROM debian:11. No entanto, se você alterar STEP 3, o Docker poderá reutilizar as camadas para STEP 1 e STEP 2.

Exemplos de como usar o cache de criação do Docker

Figura 2. Exemplos de como usar o cache de criação do Docker. Em verde, as camadas que você pode reutilizar. Em vermelho, as que precisam ser recriadas.

A reutilização de camadas tem outra consequência: se uma etapa de criação depender de qualquer tipo de cache armazenado no sistema de arquivos local, esse cache precisará ser gerado na mesma etapa. Se esse cache não estiver sendo gerado, sua etapa de criação poderá ser executada com um cache desatualizado proveniente de uma criação anterior. Você vê esse comportamento mais comumente com gerenciadores de pacotes como o apt ou o yum: é necessário atualizar seus repositórios no mesmo comando RUN que a instalação do pacote.

Se você alterar a segunda etapa RUN no Dockerfile a seguir, o comando apt-get update não será executado novamente, deixando você com um cache desatualizado.

FROM debian:11

RUN apt-get update
RUN apt-get install -y nginx

Em vez disso, mescle os dois comandos em uma única etapa RUN:

FROM debian:11

RUN apt-get update && \
    apt-get install -y nginx

Remover ferramentas desnecessárias

Importância: MÉDIA

Para proteger os aplicativos contra invasores, reduza a superfície de ataque deles removendo todas as ferramentas desnecessárias. Por exemplo, remova utilitários como o netcat, que você usa para criar um shell reverso no sistema. Se o netcat não estiver no contêiner, o invasor terá que encontrar outro caminho.

Essa prática recomendada é válida para qualquer carga de trabalho, mesmo que não seja em contêiner. A diferença é que essa prática recomendada é otimizada para contêineres, em vez de VMs clássicas ou servidores bare metal.

Ao remover ferramentas desnecessárias, isso também pode ajudar a melhorar os processos de depuração. Por exemplo, se você implementar essa prática completamente, registros completos, sistemas de rastreamentos e criação de perfis podem se tornar quase obrigatórios. Na verdade, não é mais possível depender de ferramentas de depuração locais porque elas geralmente têm alto privilégio.

Conteúdo do sistema de arquivos

Na primeira parte desta prática recomendada, falaremos sobre o conteúdo da imagem do contêiner. Mantenha a imagem com o mínimo de elementos possível. Se for possível compilar o app em um único binário vinculado estaticamente, adicione-o à imagem de rascunho para gerar uma imagem final que contenha apenas o app. Ao reduzir o número de ferramentas empacotadas na imagem, você diminui a ação de um invasor em potencial no contêiner. Para mais informações, consulte Criar a menor imagem possível.

Segurança do sistema de arquivos

Não ter ferramentas na sua imagem não é suficiente: é necessário evitar que invasores em potencial instalem as próprias ferramentas. Você pode combinar dois métodos aqui:

  • Evite executar como raiz dentro do container: este método oferece uma primeira camada de segurança e pode impedir, por exemplo, que invasores modifiquem arquivos de propriedade da raiz usando um gerenciador de pacotes incorporado em sua imagem (como apt-get ou apk). Para que esse método seja útil, desative ou desinstale o comando sudo. Este tópico é abordado mais amplamente em Evitar executar como root.

  • Inicie o contêiner no modo somente leitura, usando a sinalização --read-only do comando docker run ou a opção readOnlyRootFilesystem no Kubernetes.

Criar a menor imagem possível

Importância: MÉDIA

Criar uma imagem menor oferece vantagens como tempos de upload e download mais rápidos, o que é importante principalmente na hora de inicialização a frio de um pod no Kubernetes: quanto menor a imagem, mais rápido o node poderá fazer o download dela. No entanto, criar uma imagem pequena pode ser difícil, porque você pode incluir inadvertidamente dependências de criação ou camadas não otimizadas na imagem final.

Usar a menor imagem base possível

A imagem base é aquela referenciada na instrução FROM no Dockerfile. Todas as outras instruções no Dockerfile fazem criações sobre esta imagem. Quanto menor a imagem base, menor a imagem resultante e mais rápido o tempo de download dela. Por exemplo, a imagem alpine:3.17 é 23 MB menor que a ubuntu:22.04.

É possível até usar a imagem base de rascunho, que é uma imagem vazia em que você cria o próprio ambiente de execução. Se o app for um binário vinculado estaticamente, será possível usar a imagem de base da seguinte forma:

FROM scratch
COPY mybinary /mybinary
CMD [ "/mybinary" ]

O vídeo a seguir sobre práticas recomendadas do Kubernetes aborda estratégias adicionais para a criação de pequenos contêineres, reduzindo sua exposição a vulnerabilidades de segurança.

Reduzir a quantidade de desordem na imagem

Para reduzir o tamanho da imagem, instale apenas o que é estritamente necessário dentro dela. Pode ser tentador instalar pacotes extras e, em seguida, removê-los em uma etapa posterior. No entanto, essa abordagem não é suficiente. Como cada instrução do Dockerfile cria uma camada, a remoção de dados da imagem em uma etapa posterior àquela em que ela foi criada não reduz o tamanho da imagem geral, porque os dados ainda estarão lá, apenas ocultos em uma camada mais profunda. Veja este exemplo:

Dockerfile ruim Dockerfile bom

FROM debian:11
RUN apt-get update && \ apt-get install -y \ [buildpackage] RUN [build my app] RUN apt-get autoremove --purge \ -y [buildpackage] && \ apt-get -y clean && \ rm -rf /var/lib/apt/lists/*

FROM debian:11
RUN apt-get update && \ apt-get install -y \ [buildpackage] && \ [build my app] && \ apt-get autoremove --purge \ -y [buildpackage] && \ apt-get -y clean && \ rm -rf /var/lib/apt/lists/*

Na versão ruim do Dockerfile, o [buildpackage] e os arquivos em /var/lib/apt/lists/* ainda existem na camada correspondente ao primeiro RUN. Essa camada faz parte da imagem e precisa ser carregada e transferida por download, mesmo que os dados contidos nela não possam ser acessados na imagem resultante.

Na versão boa do Dockerfile, tudo é feito em uma única camada que contém apenas o app criado. O [buildpackage] e os arquivos em /var/lib/apt/lists/* não existem em nenhum lugar na imagem resultante, nem mesmo ocultos em uma camada mais profunda.

Para mais informações sobre camadas de imagem, consulte Otimizar o cache de criação do Docker.

Outra ótima maneira de reduzir a quantidade de desordem na imagem é usar criações em vários estágios (implantadas no Docker 17.05). Com elas, você desenvolve o app em um primeiro contêiner "de criação" e usa o resultado em outro, com o mesmo Dockerfile.

Processo de criação de vários estágios do Docker

Figura 3. O processo de criação de vários estágios do Docker.

No Dockerfile a seguir, o binário hello é criado em um primeiro contêiner e injetado em um segundo. Como o segundo contêiner é baseado em um rascunho, a imagem resultante contém apenas o binário hello, e não o arquivo de origem e os objetos necessários durante a criação. O binário precisa estar vinculado estaticamente para funcionar sem a necessidade de qualquer biblioteca externa na imagem de rascunho.

FROM golang:1.20 as builder

WORKDIR /tmp/go
COPY hello.go ./
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -ldflags '-s' -o hello

FROM scratch
CMD [ "/hello" ]
COPY --from=builder /tmp/go/hello /hello

Tentar criar imagens com camadas comuns

Se você precisar fazer download de uma imagem do Docker, ele primeiro verificará se você já tem algumas das camadas que estão nela. Se você tiver essas camadas, o download não será feito. Essa situação pode ocorrer se você fez download anteriormente de outra imagem que tem a mesma base da imagem que você está fazendo download no momento. O resultado é que a quantidade de dados de download é muito menor para a segunda imagem.

Em nível organizacional, você aproveita essa redução fornecendo aos desenvolvedores um conjunto de imagens base comuns e padrão. Seus sistemas precisam fazer download de cada imagem base somente uma vez. Após o download inicial, apenas as camadas que tornam cada imagem única são necessárias. Na verdade, quanto mais suas imagens tiverem em comum, mais rápido será o download delas.

Tentar criar imagens com camadas comuns

Figura 4. Como criar imagens com camadas comuns.

Verificar vulnerabilidades em imagens

Importância: MÉDIA

Vulnerabilidades de software são um problema bem conhecido no mundo dos servidores bare-metal e máquinas virtuais. Uma maneira comum de lidar com essas vulnerabilidades é usar um sistema de inventário centralizado que lista os pacotes instalados em cada servidor. Assine os feeds de vulnerabilidades dos sistemas operacionais de upstream para ser informado quando uma vulnerabilidade afetar seus servidores e corrigi-los de acordo.

No entanto, como os contêineres devem ser imutáveis (consulte Contêineres imutáveis e sem estado para mais detalhes), não os corrija no local em caso de vulnerabilidade. A prática recomendada é recriar a imagem, incluindo os patches, e reimplantá-la. Os contêineres têm um ciclo de vida muito mais curto e uma identidade menos definida do que os servidores. Portanto, usar um sistema de inventário centralizado semelhante é uma maneira ruim de detectar vulnerabilidades em contêineres.

Para ajudar a resolver esse problema, o Artifact Analysis verifica suas imagens em busca de vulnerabilidades de segurança em pacotes monitorados publicamente. As seguintes opções estão disponíveis:

Verificação de vulnerabilidades automática

Quando ativado, esse recurso identifica as vulnerabilidades do pacote em suas imagens de contêiner. As imagens são verificadas quando são carregadas no Artifact Registry ou no Container Registry e os dados são monitorados continuamente para encontrar novas vulnerabilidades por até 30 dias após o envio da imagem. É possível agir de acordo com as informações relatadas por esse recurso de várias maneiras:

  • Crie um job do tipo cron que liste vulnerabilidades e acione o processo para corrigi-las, onde houver uma correção.
  • Assim que uma vulnerabilidade for detectada, use a integração do Pub/Sub para acionar o processo de correção que sua empresa usa.
API On-Demand Scanning

Quando ativado, é possível verificar manualmente imagens locais ou armazenadas no Artifact Registry ou no Container Registry. Esse recurso ajuda a detectar e resolver vulnerabilidades no início do pipeline de compilação. Por exemplo, é possível usar o Cloud Build para verificar uma imagem após a criação e, em seguida, bloquear o upload para o Artifact Registry se a verificação detectar vulnerabilidades em um nível de gravidade especificado. Se você também tiver ativado a verificação automática de vulnerabilidades, o Artifact Registry também verificará as imagens enviadas para o registro.

Recomendamos automatizar o processo de correção e contar com o canal de integração contínua atual, inicialmente usado para criar a imagem. Se estiver confiante em seu canal de implantação contínua, talvez também queira implantar automaticamente a imagem fixa quando estiver pronta. No entanto, a maioria das pessoas prefere uma etapa de verificação manual antes da implantação. O processo a seguir faz isso:

  1. Armazene suas imagens no Container Registry e ative a varredura de vulnerabilidades.
  2. Configure um job que busque regularmente novas vulnerabilidades do Artifact Registry e acione uma recriação das imagens, se necessário.
  3. Quando as imagens novas forem criadas, faça com que o sistema de implantação contínua as implante em um ambiente de preparação.
  4. Verifique manualmente os problemas no ambiente de preparação.
  5. Se nenhum problema for encontrado, acione manualmente a implantação para produção.

Atribuir as tags corretas às imagens

Importância: MÉDIA

As imagens do Docker geralmente são identificadas por dois componentes: o nome e a tag. Por exemplo, para a imagem google/cloud-sdk:419.0.0, google/cloud-sdk é o nome e 419.0.0 é a tag. A tag latest será usada por padrão se você não fornecer uma tag nos comandos do Docker. O par nome e tag é exclusivo a qualquer momento. No entanto, você reatribui uma tag a uma imagem diferente, conforme necessário.

Quando você cria uma imagem, cabe a você atribuir a tag a ela corretamente. Siga uma política coerente e consistente de atribuição de tags. Documente sua política de atribuição de tags para que os usuários da imagem possam entendê-la facilmente.

As imagens de contêiner são uma forma de empacotar e lançar um software. Atribuir tags à imagem permite que os usuários identifiquem uma versão específica do software para fazer o download. Por esse motivo, vincule o sistema de tags nas imagens do contêiner à política de lançamento do seu software.

Atribuir tags usando controle de versão semântico

Uma maneira comum de lançar um software é "marcar" (como no comando git tag) uma versão específica do código-fonte com um número de versão. A Especificação de controle de versão semântico fornece uma maneira limpa de lidar com números de versão. Nesse sistema, seu software tem um número de versão de três partes: X.Y.Z, em que:

  • X é a versão principal, incrementada apenas para alterações de API incompatíveis;
  • Y é a versão secundária, incrementada para novos recursos;
  • Z é a versão do patch, incrementada para correções de bugs.

Cada incremento no número de versão secundária ou de patch precisa ser para uma alteração compatível com versões anteriores.

Se você usa este sistema, ou um similar, atribua uma tag às suas imagens de acordo com a política a seguir:

  • A tag latest sempre se refere à imagem mais recente (possivelmente estável). Essa tag é movida assim que uma imagem nova é criada.
  • A tag X.Y.Z refere-se a uma versão específica do seu software. Não a mova para outra imagem.
  • A tag X.Y refere-se à versão mais recente do patch do branch secundário X.Y do seu software. Ela é movida quando uma nova versão de patch é lançada.
  • A tag X se refere à versão de patch mais recente da última versão secundária do branch principal X. Ela é movida quando uma nova versão de patch ou uma nova versão secundária é lançada.

O uso dessa política oferece aos usuários a flexibilidade de escolher qual versão do seu software eles querem usar. Eles podem escolher uma versão X.Y.Z específica e ter a garantia de que a imagem nunca será alterada ou podem receber atualizações automaticamente, escolhendo uma tag menos específica.

Atribuir tags usando o hash de commit do Git

Se você tem um sistema avançado de entrega contínua e lança seu software com frequência, provavelmente não usa números de versão, conforme descrito na Especificação de controle de versão semântica. Nesse caso, uma maneira comum de manipular números de versão é usar o hash de commit SHA-1 do Git (ou uma versão curta dele) como o número da versão. Por definição, o hash de commit do Git é imutável e faz referência a uma versão específica do seu software.

Use esse hash de commit como um número de versão para seu software, mas também como uma tag para a imagem do Docker criada dessa versão específica do software. Isso faz com que as imagens do Docker sejam rastreáveis: como nesse caso a tag da imagem é imutável, você sabe instantaneamente qual versão específica do seu software está sendo executada dentro de um determinado contêiner. Em seu pipeline de entrega contínua, automatize a atualização do número da versão usada para suas implantações.

Refletir cuidadosamente sobre quando usar uma imagem pública

Importância: N/A

Uma das grandes vantagens do Docker é o grande número de imagens disponíveis publicamente, para todos os tipos de software. Essas imagens permitem que você dê os primeiros passos rapidamente. No entanto, ao projetar uma estratégia de contêiner para sua organização, pode haver restrições que imagens fornecidas publicamente não conseguirão atender. Aqui estão alguns exemplos de restrições que podem impossibilitar o uso de imagens públicas:

  • Você quer controlar exatamente o que está dentro de suas imagens.
  • Você não quer depender de um repositório externo.
  • Você quer controlar estritamente as vulnerabilidades em seu ambiente de produção.
  • Você quer o mesmo sistema operacional de base em todas as imagens.

A resposta a todas essas restrições é a mesma e, infelizmente, é cara: você precisa criar suas próprias imagens. Isso é bom para um número limitado de imagens, mas esse número tem uma tendência a crescer rapidamente. Para ter alguma chance de gerenciar tal sistema em escala, pense nestas possibilidades:

  • Uma maneira automatizada e confiável de criar imagens, mesmo para aquelas criadas raramente. Os acionadores de criação no Cloud Build são uma boa maneira de realizar isso.
  • Uma imagem de base padronizada. O Google fornece algumas imagens de base que podem ser usadas.
  • Uma maneira automatizada de propagar atualizações para a imagem de base para imagens "filhas".
  • Uma maneira de direcionar vulnerabilidades em suas imagens. Para mais informações, consulte Verificar se há vulnerabilidades nas imagens.
  • Uma maneira de aplicar seus padrões internos em imagens criadas pelas diferentes equipes em sua empresa.

Várias ferramentas estão disponíveis para ajudá-lo a aplicar políticas nas imagens que você cria e implanta:

  • O container-diff pode analisar o conteúdo das imagens e até comparar duas imagens.
  • O container-structure-test pode testar se o conteúdo de uma imagem está em conformidade com um conjunto de regras que você define.
  • O Grafeas é uma API de metadados de artefatos, em que você armazena metadados das suas imagens para verificar posteriormente se elas estão em conformidade com suas políticas.
  • O Kubernetes tem controladores de admissão que podem ser usados para verificar vários pré-requisitos antes da implantação de uma carga de trabalho.

Também pode ser útil adotar um sistema híbrido: usando uma imagem pública como Debian ou Alpine como a imagem base e criando tudo sobre essa imagem. Ou usar imagens públicas para algumas de suas imagens não críticas e criar suas próprias imagens em outros casos. Essas perguntas não têm respostas certas ou erradas, mas você precisa abordá-las.

Uma observação sobre licenças

Antes de incluir bibliotecas e pacotes de terceiros na imagem do Docker, verifique se as respectivas licenças permitem isso. As licenças de terceiros também podem impor restrições à redistribuição, que se aplicam quando você publica uma imagem do Docker em um registro público.

A seguir

Confira arquiteturas de referência, diagramas e práticas recomendadas do Google Cloud. Confira o Centro de arquitetura do Cloud.