Melhores práticas do Kubernetes. Crie contêineres pequenos



A primeira etapa da implantação no Kubernetes é colocar seu aplicativo em um contêiner. Nesta série, veremos como você pode criar uma imagem de um contêiner pequeno e seguro.
Graças ao Docker, a criação de imagens de contêiner nunca foi tão fácil. Especifique a imagem base, adicione suas alterações e crie um contêiner.



Embora essa técnica seja ótima para começar, o uso de imagens básicas por padrão pode levar a um trabalho inseguro com imagens grandes cheias de vulnerabilidades.

Além disso, a maioria das imagens no Docker usa o Debian ou Ubuntu como imagem de base e, embora isso ofereça excelente compatibilidade e fácil adaptação (o arquivo do Docker ocupa apenas duas linhas de código), as imagens básicas podem adicionar centenas de megabytes de carga extra ao seu contêiner. Por exemplo, um arquivo node.js simples do aplicativo Go Hello World leva cerca de 700 megabytes, enquanto o tamanho do aplicativo em si é de apenas alguns megabytes.



Assim, toda essa carga adicional é um desperdício de espaço digital e um excelente cache para vulnerabilidades e erros no sistema de segurança. Então, vejamos duas maneiras de reduzir o tamanho de uma imagem de contêiner.

O primeiro é o uso de imagens básicas de tamanho pequeno, o segundo é o uso do padrão de design Builder Pattern. Usar imagens de base menores é provavelmente a maneira mais fácil de reduzir o tamanho do seu contêiner. Provavelmente, seu idioma ou a pilha que você está usando fornece uma imagem original do aplicativo muito menor que a imagem padrão. Vamos dar uma olhada no nosso contêiner node.js.



Por padrão, no Docker, o tamanho da imagem do nó base: 8 é de 670 MB e o tamanho do nó: 8-alpino é de apenas 65 MB, ou seja, 10 vezes menor. O uso da imagem base Alpine menor reduzirá significativamente o tamanho do seu contêiner. Alpine é uma distribuição Linux pequena e leve que é muito popular entre os usuários do Docker, pois é compatível com muitos aplicativos, mantendo o tamanho pequeno dos contêineres. Ao contrário da imagem padrão do "nó" do Docker, o "nó: alpino" remove muitos arquivos e programas utilitários, deixando apenas aqueles que são suficientes para executar seu aplicativo.

Para alternar para uma imagem base menor, basta atualizar o arquivo do Docker para começar a trabalhar com a nova imagem base:



Agora, diferentemente da imagem onbuild antiga, você precisa copiar seu código no contêiner e instalar quaisquer dependências. No novo arquivo do Docker, o contêiner começa com o nó: alpine image, cria um diretório para o código, instala dependências usando o gerenciador de pacotes do NPM e, finalmente, inicia o server.js.



Com essa atualização, um contêiner é 10 vezes menor. Se sua linguagem de programação ou pilha não puder reduzir a imagem base, use o Alpine Linux. Também fornecerá a capacidade de gerenciar totalmente o conteúdo do contêiner. Usar imagens básicas de tamanho pequeno é uma ótima maneira de criar rapidamente pequenos contêineres. Mas você pode obter uma redução ainda maior usando o Padrão do Construtor.



Nas linguagens interpretadas, o código-fonte é passado primeiro para o intérprete e depois diretamente executado. Em idiomas compilados, o código-fonte é primeiro convertido em código compilado. No entanto, a compilação geralmente usa ferramentas que não são realmente necessárias para executar o código. Isso significa que você pode remover completamente essas ferramentas do contêiner final. Você pode usar o Padrão do Construtor para isso.



O código é criado no primeiro contêiner e compilado. Em seguida, o código compilado é empacotado no contêiner final sem os compiladores e ferramentas necessárias para compilar esse código. Vamos pular o aplicativo Go nesse processo. Primeiro, passaremos da imagem onbuild para o Alpine Linux.



No novo arquivo do Docker, o contêiner começa com a golang: imagem alpina. Ele então cria um diretório para o código, copia-o para o código-fonte, cria esse código-fonte e inicia o aplicativo. Esse contêiner é muito menor que o contêiner onbuild, mas ainda contém o compilador e outras ferramentas Go de que realmente não precisamos. Então, vamos apenas extrair o programa compilado e colocá-lo em nosso próprio contêiner.



Você pode perceber algo estranho neste arquivo do Docker: ele contém duas linhas FROM. A primeira seção de 4 linhas parece exatamente igual ao arquivo Docker anterior, exceto pelo fato de usar a palavra-chave AS para nomear essa etapa. Na próxima seção, há uma nova linha FROM que permite iniciar uma nova imagem e, em vez da imagem golang: alpine, usaremos Raw alpine como imagem base.

O Raw Alpine Linux não possui nenhum certificado SSL instalado, o que fará com que a maioria das chamadas da API HTTPS falhe; portanto, vamos instalar alguns certificados raiz da CA.

E agora o mais interessante: para copiar o código compilado do primeiro contêiner para o segundo, você pode simplesmente usar o comando COPY localizado na 5ª linha da segunda seção. Ele copiará apenas um arquivo de aplicativo e não afetará as ferramentas do utilitário Go. O novo arquivo do Docker de vários estágios conterá uma imagem de contêiner de apenas 12 megabytes de tamanho, enquanto a imagem original do contêiner tinha 700 megabytes, e essa é uma grande diferença!
Portanto, o uso de pequenas imagens básicas e padrões do Builder são ótimas maneiras de criar contêineres muito menores sem muito trabalho.
É possível que, dependendo da pilha de aplicativos, haja outras maneiras de reduzir o tamanho da imagem e do contêiner, mas os contêineres pequenos realmente têm uma vantagem mensurável? Vejamos dois aspectos em que pequenos contêineres são extremamente eficazes - desempenho e segurança.

Para avaliar o aumento de desempenho, considere a duração do processo de criação de um contêiner, inserindo-o no registro (push) e recuperando a partir daí (pull). Você pode ver que um contêiner menor tem uma vantagem inegável sobre um contêiner maior.



O Docker armazenará em cache as camadas, portanto as compilações subsequentes serão muito rápidas. No entanto, em muitos sistemas de IC usados ​​para criar e testar contêineres, as camadas não são armazenadas em cache, portanto, há uma economia de tempo significativa. Como você pode ver, o tempo para construir um contêiner grande, dependendo da potência da sua máquina, é de 34 a 54 segundos e, ao usar um contêiner reduzido com o Padrão do Construtor, de 23 para 28 segundos. Para operações desse tipo, os ganhos de produtividade serão de 40 a 50%. Então, pense quantas vezes você cria e testa seu código.

Após a construção do contêiner, é necessário inserir sua imagem (enviar imagem do contêiner) no registro do contêiner para usar o Kubernetes no cluster. Eu recomendo usar o registro de contêiner do Google.



Usando o Google Container Registry (GCR), você paga apenas pelo armazenamento bruto e pela rede, e não há taxa adicional de gerenciamento de contêiner. É confidencial, seguro e muito rápido. O GCR usa muitos truques para acelerar a operação de extração. Como você pode ver, a inserção de uma imagem do contêiner Docker Container Image usando go: onbuild, dependendo do desempenho do computador, levará de 15 a 48 segundos, e a mesma operação com um contêiner menor levará de 14 a 16 segundos e, para máquinas menos eficientes, a vantagem na velocidade de operação aumenta em Três vezes. Para máquinas grandes, o tempo é aproximadamente o mesmo, já que o GCR usa o cache global para um banco de dados comum de imagens, ou seja, você não precisa fazer o download delas. Em um computador de baixa potência, a CPU é um gargalo,portanto, a vantagem de usar pequenos contêineres aqui é muito mais tangível.

Se você usa o GCR, é altamente recomendável usar o Google Container Builder (GCB) como parte do seu sistema de criação.



Como você pode ver, usá-lo permite obter resultados muito melhores na redução da duração da operação Build + Push do que em uma máquina produtiva - nesse caso, o processo de construção e envio de contêineres para o host é quase duas vezes mais rápido. Além disso, todos os dias você recebe 120 minutos de montagem gratuitamente, o que na maioria dos casos atende às necessidades de criação de contêineres.

A seguir, vem a métrica de desempenho mais importante - a velocidade com a qual você recupera ou baixa os contêineres Pull. E se você realmente não se importa com o tempo gasto na operação de envio, a duração do processo de recebimento afeta seriamente o desempenho geral do sistema. Suponha que você tenha um cluster de três nós e um deles trava. Se você usar um sistema de gerenciamento, como o Google Kubernetes Engine, ele substituirá automaticamente o nó ocioso por um novo. No entanto, esse novo nó estará completamente vazio e você precisará arrastar todos os seus contêineres para fazê-lo funcionar. Se a operação de extração for longa o suficiente, todo esse tempo seu cluster funcionará com desempenho inferior.

Há muitos casos em que isso pode acontecer: adicionar um novo nó a um cluster, atualizar nós ou até mudar para um novo contêiner para implantação. Assim, minimizar o tempo de extração por extração torna-se um fator chave. É indiscutível que um contêiner pequeno é baixado muito mais rápido que um grande. Se você usar vários contêineres em um cluster Kubernetes, a economia de tempo pode ser muito significativa.



Dê uma olhada na comparação: a operação de tração ao trabalhar com contêineres pequenos leva 4-9 vezes menos tempo, dependendo da potência da máquina, do que a mesma operação usando go: onbuild. O uso de imagens básicas comuns de pequenos contêineres acelera bastante o tempo e a velocidade com que os novos nós do Kubernetes podem implantar e ficar online.

Vamos olhar para a questão da segurança. Pensa-se que os contêineres menores sejam muito mais seguros do que os contêineres grandes, porque possuem uma superfície de ataque menor. É realmente? Um dos recursos mais úteis do Registro de contêineres do Google é a capacidade de verificar automaticamente seus contêineres em busca de vulnerabilidades. Alguns meses atrás, criei contêineres onbuild e multiestágio, então vamos ver se há alguma vulnerabilidade lá.



O resultado é surpreendente: apenas três vulnerabilidades médias foram encontradas em um contêiner pequeno e 16 críticas e 376 outras em um grande. Se você observar o conteúdo de um contêiner grande, poderá ver que a maioria dos problemas de segurança não tem nada a ver com nosso aplicativo, mas está relacionada a programas que nem usamos. Então, quando as pessoas falam sobre uma grande superfície para ataques, elas querem dizer exatamente isso.



A conclusão é óbvia: crie contêineres pequenos porque eles oferecem benefícios reais no desempenho e na segurança do seu sistema.

Melhores práticas do Kubernetes. Organização Kubernetes com espaço para nome


Um pouco de publicidade :)


Obrigado por ficar com a gente. Você gosta dos nossos artigos? Deseja ver materiais mais interessantes? Ajude-nos fazendo um pedido ou recomendando aos seus amigos o VPS baseado em nuvem para desenvolvedores a partir de US $ 4,99 , um analógico exclusivo de servidores básicos que foi inventado por nós para você: Toda a verdade sobre o VPS (KVM) E5-2697 v3 (6 núcleos) 10GB DDR4 480GB SSD 1Gbps de 10GB de US $ 19 ou como dividir o servidor? (as opções estão disponíveis com RAID1 e RAID10, até 24 núcleos e até 40GB DDR4).

Dell R730xd 2 vezes mais barato no data center Equinix Tier IV em Amsterdã? Somente nós temos 2 TVs Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV a partir de US $ 199 na Holanda!Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - a partir de US $ 99! Leia sobre como construir infraestrutura classe c usando servidores Dell R730xd E5-2650 v4 que custam 9.000 euros por um centavo?

All Articles