Diretrizes para executar o Buildah dentro de um contêiner

Qual é a beleza de separar o tempo de execução do contêiner em componentes de ferramenta separados? Em particular, o fato de que essas ferramentas podem começar a ser combinadas para que se protejam.

Diretrizes para executar o Buildah dentro de um contêiner

Muitas pessoas são atraídas pela ideia de construir imagens de contêiner OCI dentro Kubernetes ou sistema semelhante. Digamos que temos um CI/CD que constantemente constrói imagens, então algo como Red Hat OpenShift/Kubernetes seria muito útil em termos de balanceamento de carga de construção. Até recentemente, a maioria das pessoas simplesmente concedia aos contêineres acesso a um soquete do Docker e permitia que eles executassem o comando docker build. Mostramos há alguns anosque isso é muito inseguro, na verdade, é ainda pior do que dar root ou sudo sem senha.

Portanto, as pessoas estão constantemente tentando executar o Buildah em um contêiner. Resumindo, criamos exemplo como, em nossa opinião, é melhor rodar o Buildah dentro de um container, e postamos as imagens correspondentes no quay.io/buildah. Vamos começar...

Fixação

Essas imagens são construídas a partir de Dockerfiles, que podem ser encontrados no repositório Buildah na pasta construa uma imagem.
Aqui vamos considerar versão estável do Dockerfile.

# stable/Dockerfile
#
# Build a Buildah container image from the latest
# stable version of Buildah on the Fedoras Updates System.
# https://bodhi.fedoraproject.org/updates/?search=buildah
# This image can be used to create a secured container
# that runs safely with privileges within the container.
#
FROM fedora:latest

# Don't include container-selinux and remove
# directories used by dnf that are just taking
# up space.
RUN yum -y install buildah fuse-overlayfs --exclude container-selinux; rm -rf /var/cache /var/log/dnf* /var/log/yum.*

# Adjust storage.conf to enable Fuse storage.
RUN sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' /etc/containers/storage.conf

Em vez do OverlayFS, implementado no nível do kernel Linux do host, usamos o programa dentro do contêiner sobreposição de fusíveis, porque atualmente o OverlayFS só pode ser montado se você conceder permissões SYS_ADMIN por meio dos recursos do Linux. E queremos executar nossos contêineres Buildah sem privilégios de root. Fuse-overlay é muito rápido e tem melhor desempenho do que o driver de armazenamento VFS. Observe que ao executar um contêiner Buildah usando o Fuse, o dispositivo /dev/fuse precisa ser fornecido.

podman run --device /dev/fuse quay.io/buildahctr ...
RUN mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock

Em seguida, criamos um diretório para repositórios adicionais. recipiente/armazenamento suporta o conceito de conectar repositórios de imagens somente leitura adicionais. Por exemplo, você pode configurar uma área de armazenamento de sobreposição em uma máquina e, em seguida, usar o NFS para montar esse armazenamento em outra máquina e usar imagens dela sem fazer o download via pull. Precisamos desse armazenamento para poder conectar algum armazenamento de imagem do host como um volume e usá-lo dentro do contêiner.

# Set up environment variables to note that this is
# not starting with user namespace and default to
# isolate the filesystem with chroot.
ENV _BUILDAH_STARTED_IN_USERNS="" BUILDAH_ISOLATION=chroot

Por fim, usamos a variável de ambiente BUILDAH_ISOLATION para informar ao contêiner Buildah para iniciar com o isolamento chroot por padrão. Isolamento adicional não é necessário aqui, pois já estamos trabalhando em um contêiner. Para que Buildah crie seus próprios contêineres separados por namespace, o privilégio SYS_ADMIN é necessário, o que exigiria afrouxar as regras SELinux e SECCOMP do contêiner, o que entraria em conflito com nossa configuração para construir a partir de um contêiner seguro.

Executar Buildah dentro de um contêiner

O esquema de imagem do contêiner Buildah discutido acima permite que você varie com flexibilidade como esses contêineres são iniciados.

Velocidade versus segurança

A segurança do computador é sempre um compromisso entre a velocidade de um processo e quanta proteção está envolvida nele. Esta afirmação também é verdadeira ao montar contêineres, portanto, a seguir, consideraremos as opções para tal compromisso.

A imagem do contêiner discutida acima manterá seu armazenamento em /var/lib/containers. Portanto, precisamos montar o conteúdo nessa pasta, e como faremos isso afetará muito a velocidade de construção das imagens do contêiner.

Vamos considerar três opções.

1 Opção. Se for necessária segurança máxima, para cada contêiner, você pode criar sua própria pasta para contêineres / imagem e conectá-la ao contêiner por meio de montagem de volume. E além disso, coloque o diretório de contexto no próprio container, na pasta /build:

# mkdir /var/lib/containers1
# podman run -v ./build:/build:z -v /var/lib/containers1:/var/lib/containers:Z quay.io/buildah/stable
buildah  -t image1 bud /build
# podman run -v /var/lib/containers1:/var/lib/containers:Z quay.io/buildah/stable buildah  push  image1 registry.company.com/myuser
# rm -rf /var/lib/containers1

Segurança. Um Buildah rodando em tal container tem segurança máxima: ele não recebe privilégios de root por capacidades, e todas as restrições SECOMP e SELinux se aplicam a ele. Tal container pode até ser rodado com isolamento de User Namespace adicionando uma opção como --uidmap 0:100000:10000.

Atuação. Mas o desempenho aqui é mínimo, pois todas as imagens dos registros de contêiner são copiadas para o host a cada vez e o cache não funciona com a palavra "de jeito nenhum". Ao terminar seu trabalho, o contêiner Buildah deve enviar a imagem para o registro e destruir o conteúdo no host. Na próxima vez que a imagem do contêiner for construída, ela terá que ser baixada novamente do registro, pois nada será deixado no host nesse momento.

2 Opção. Se você precisar de desempenho no nível do Docker, poderá montar o contêiner/armazenamento do host diretamente no contêiner.

# podman run -v ./build:/build:z -v /var/lib/containers:/var/lib/containers --security-opt label:disabled quay.io/buildah/stable buildah  -t image2 bud /build
# podman run -v /var/lib/containers:/var/lib/containers --security-opt label:disabled  quay.io/buildah/stable buildah push image2 registry.company.com/myuser

Segurança. Essa é a maneira menos segura de criar contêineres, pois permite que o contêiner modifique o armazenamento no host e pode potencialmente inserir uma imagem maliciosa no Podman ou no CRI-O. Além disso, você precisará desabilitar a separação do SELinux para que os processos no contêiner Buildah possam interagir com o repositório no host. Observe que esta opção ainda é melhor do que um soquete do Docker, pois o contêiner é bloqueado pelos recursos de segurança restantes e não pode simplesmente selecionar e executar qualquer contêiner no host.

Atuação. Aqui é máximo, já que o cache está totalmente envolvido. Se Podman ou CRI-O já baixou a imagem desejada para o host, o processo Buildah dentro do contêiner não precisará baixá-lo novamente e as compilações subsequentes com base nessa imagem também poderão obter o necessário do cache .

3 Opção. A essência desse método é combinar várias imagens em um projeto com uma pasta comum para imagens de contêiner.

# mkdir /var/lib/project3
# podman run --security-opt label_level=s0:C100, C200 -v ./build:/build:z 
-v /var/lib/project3:/var/lib/containers:Z quay.io/buildah/stable buildah  -t image3 bud /build
# podman run --security-opt label_level=s0:C100, C200 
-v /var/lib/project3:/var/lib/containers quay.io/buildah/stable buildah push image3  registry.company.com/myuser

Neste exemplo, não excluímos a pasta do projeto (/var/lib/project3) entre as execuções, portanto, todas as compilações subsequentes dentro do projeto aproveitam o armazenamento em cache.

Segurança. Algo entre as opções 1 e 2. Por um lado, os contêineres não têm acesso ao conteúdo do host e, portanto, não podem inserir algo ruim no armazenamento de imagens do Podman / CRI-O. Por outro lado, dentro de um projeto próprio, um container pode interferir na montagem de outros containers.

Atuação. Aqui é pior do que usar um cache compartilhado no nível do host, pois você não pode usar imagens que já foram baixadas usando o Podman / CRI-O. No entanto, uma vez que Buildah tenha baixado a imagem, essa imagem pode ser usada em qualquer compilação subsequente dentro do projeto.

Armazenamento adicional

У recipientes/armazenamento existe uma coisa legal como armazenamentos adicionais (armazenamentos adicionais), graças aos quais, ao iniciar e construir contêineres, os mecanismos de contêiner podem usar armazenamentos de imagens externas no modo de sobreposição somente leitura. Na verdade, você pode adicionar um ou mais armazenamentos somente leitura ao arquivo storage.conf, para que, quando o contêiner for iniciado, o mecanismo do contêiner procure a imagem desejada neles. Além disso, ele fará o download da imagem do registro apenas se não a encontrar em nenhum desses armazenamentos. O mecanismo do contêiner só poderá gravar no armazenamento gravável...

Se rolarmos para cima e olharmos o Dockerfile que usamos para construir a imagem quay.io/buildah/stable, veremos linhas como esta:

# Adjust storage.conf to enable Fuse storage.
RUN sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' /etc/containers/storage.conf
RUN mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock

Na primeira linha, modificamos /etc/containers/storage.conf dentro da imagem do contêiner, informando ao driver de armazenamento para usar "additionalimagestores" na pasta /var/lib/shared. E na próxima linha, criamos uma pasta compartilhada e adicionamos alguns arquivos de bloqueio para que não haja abuso de contêineres / armazenamento. Essencialmente, estamos apenas criando um armazenamento de imagem de contêiner vazio.

Se você montar containers/storage um nível acima desta pasta, o Buildah poderá utilizar as imagens.

Agora vamos voltar à opção 2 discutida acima, quando o contêiner Buildah pode ler e gravar em contêineres / armazenar em hosts e, portanto, tem desempenho máximo devido ao cache de imagem no nível Podman / CRI-O, mas oferece um mínimo de segurança, uma vez que pode gravar diretamente no armazenamento. E agora vamos adicionar armazenamento adicional aqui e obter o melhor dos dois mundos.

# mkdir /var/lib/containers4
# podman run -v ./build:/build:z -v /var/lib/containers/storage:/var/lib/shared:ro -v  /var/lib/containers4:/var/lib/containers:Z  quay.io/buildah/stable 
 buildah  -t image4 bud /build
# podman run -v /var/lib/containers/storage:/var/lib/shared:ro  
-v >/var/lib/containers4:/var/lib/containers:Z quay.io/buildah/stable buildah push image4  registry.company.com/myuser
# rm -rf /var/lib/continers4

Observe que o /var/lib/containers/storage do host é montado em /var/lib/shared dentro do contêiner em modo somente leitura. Portanto, trabalhando em um container, o Buildah pode usar qualquer imagem que já tenha sido baixada usando Podman / CRI-O (alô, velocidade), mas só pode gravar em seu próprio armazenamento (alô, segurança). Observe também que isso é feito sem desabilitar a separação do SELinux para o contêiner.

Nuance importante

Sob nenhuma circunstância nenhuma imagem deve ser excluída do repositório subjacente. Caso contrário, o contêiner Buildah pode travar.

E isso não é todos os benefícios.

As possibilidades de armazenamento adicional não se limitam ao cenário acima. Por exemplo, você pode colocar todas as imagens de container em um armazenamento de rede compartilhado e dar acesso a ele para todos os containers Buildah. Digamos que temos centenas de imagens que nosso sistema de CI/CD usa regularmente para criar imagens em contêineres. Concentramos todas essas imagens em um único host de armazenamento e, usando as ferramentas de armazenamento de rede preferidas (NFS, Gluster, Ceph, iSCSI, S3 ...), compartilhamos esse armazenamento com todos os nós Buildah ou Kubernetes.

Agora basta montar esse armazenamento de rede no contêiner Buildah em /var/lib/shared e pronto - os contêineres Buildah não precisam mais baixar imagens via pull. Assim, descartamos a fase de pré-povoamento e estamos imediatamente prontos para lançar os contêineres.

E, claro, isso pode ser usado em um sistema Kubernetes ativo ou em uma infraestrutura de contêiner para iniciar e executar contêineres em qualquer lugar sem nenhuma extração de imagem. Além disso, quando um registro de contêiner recebe uma solicitação push para fazer upload de uma imagem atualizada, ele pode enviar automaticamente essa imagem para um armazenamento de rede compartilhado, onde fica instantaneamente disponível para todos os nós.

Às vezes, as imagens de contêiner podem ter muitos gigabytes de tamanho. A funcionalidade de armazenamentos adicionais elimina a necessidade de clonagem dessas imagens por nós e torna o lançamento de contêineres quase instantâneo.

Além disso, estamos trabalhando em um novo recurso de montagem de volume de sobreposição que tornará a construção de contêineres ainda mais rápida.

Conclusão

Executar o Buildah dentro de um container em um ambiente Kubernetes/CRI-O, Podman, ou mesmo Docker é bem possível, e é simples e muito mais seguro do que usar docker.socket. Aumentamos muito a flexibilidade de trabalhar com imagens e agora você pode executá-las de várias maneiras para obter o melhor equilíbrio entre segurança e desempenho.

A funcionalidade de armazenamentos adicionais permite acelerar ou mesmo eliminar completamente o download de imagens para os nós.

Fonte: habr.com

Adicionar um comentário