O que é um container Linux?
- por Sergio Prado
Muito tem se falado sobre a tecnologia de containers nos últimos anos. Seu uso em data centers e ambientes de computação em nuvem tem crescido cada vez mais e ferramentas como Docker e Kubernetes têm ficado bastante populares. E agora começaram a aparecer algumas soluções de distribuições Linux baseadas em containers para sistemas embarcados.
Mas o que são containers? Que problema eles resolvem? Quais são as tecnologias envolvidas? Vou tentar responder estas e muitas outras perguntas neste artigo! :-)
CONTAINERS
Imagine que você esteja desenvolvendo uma aplicação que tenha diversas dependências. A aplicação pode ter dependências de bibliotecas, arquivos de configuração ou mesmo de outras aplicações.
Agora imagine que você precise distribuir esta aplicação para ambientes com características diferentes. Por exemplo, você está desenvolvendo no Ubuntu 18.04 e quer executar a aplicação em máquinas com Fedora, Debian ou mesmo outra versão do Ubuntu.
Como garantir que o ambiente de execução atenda as dependências da sua aplicação?
Uma forma de resolver este problema é distribuir o código-fonte da aplicação com um sistema de build (autotools, cmake, etc) capaz de verificar e notificar o usuário caso alguma dependência não seja atendida. Junto com o código-fonte você pode distribuir um manual (ex: README) documentando o processo de instalação da aplicação, incluindo os procedimentos para instalar suas dependências.
Funciona. Mas quem já precisou desenvolver e distribuir aplicações capazes de rodar em múltiplas distribuições GNU/Linux sabe o trabalho que dá.
Estes sistemas de build (autotools, cmake, etc) vão exigir um investimento grande de tempo para serem configurados e mantidos conforme as dependências da aplicação mudarem. E documentar o processo de instalação da aplicação e suas dependências também será bastante trabalhoso. Por exemplo, se você quiser suportar 10 distribuições Linux diferentes, precisará incluir neste manual os procedimentos para instalar a aplicação nestas 10 distribuições diferentes! É uma solução que não escala. Sem contar que você irá forçar o seu usuário a instalar diversas dependências que talvez ele não precise, o que pode inclusive causar problemas no ambiente dele.
Perceba que o fato de não existir um isolamento entre o ambiente de execução e a aplicação (e suas dependências) dificulta o processo de deploy de aplicações em distribuições GNU/Linux.
Para resolver este problema podemos virtualizar a execução da aplicação em uma máquina virtual. Desta forma, bastaria criar uma imagem de máquina virtual com todas as dependências da aplicação e rodar esta imagem em uma ferramenta de virtualização (QEMU, VirtualBox, VMWare, etc).
Porém, uma máquina virtual é muito pesada. Ela roda uma instância totalmente isolada do sistema operacional (kernel + rootfs), aumentando consideravelmente o consumo de recursos (CPU, memória, I/O).
É aí que entram os containers!
Um container permite a execução de um processo ou grupo de processos de forma totalmente isolada do restante do sistema.
Conseguimos então criar uma imagem de container com todas as dependências e controlar a execução da aplicação de forma isolada, resolvendo o problema de distribuição da aplicação para ambientes com características diferentes. E diferentemente da solução de virtualização com máquina virtual, a solução com container é mais leve e consome bem menos recursos, já que o kernel é compartilhado com outros processos e containers em execução no sistema operacional.
Um container é portanto uma instância da camada de usuário do Linux, possui uma quantidade de recursos alocados para ele (CPU, memória, I/O) e roda de forma isolada do restante do sistema. Um container pode rodar apenas uma aplicação ou todo o sistema de arquivos (rootfs), sendo possível iniciar e executar diversos containers ao mesmo tempo.
Se você acessar um terminal de comandos dentro de um container e executar o comando ps, verá apenas os processos do container. Se rodar o comando mount, verá apenas os pontos de montagem do container. Se rodar ls, verá apenas os arquivos do container. Se executar o comando reboot, apenas as aplicações do container reiniciam, e isso pode levar menos de 1 segundo!
COMO UM CONTAINER É IMPLEMENTADO?
A implementação de containers utiliza diversos recursos do kernel Linux, incluindo cgroups e namespaces.
Control groups ou cgroups é uma funcionalidade bem interessante do kernel Linux que permite particionar os recursos do sistema (CPU, memória, I/O) por grupo de processos. Um tempo atrás escrevi um artigo sobre cgroups em “Gerenciando acesso a recursos no Linux com control groups“.
Já namespaces possibilitam isolar a execução de um processo no Linux (PID, usuários, conexões de rede, pontos de montagem, etc).
Utilizando estes recursos, é possível criar um ambiente de execução isolado para as aplicações.
VANTAGENS
Podemos então perceber as diversas vantagens que um container pode trazer.
Um container facilita a distribuição de aplicações e possibilita sua execução em ambientes totalmente diferentes. O processo de atualização de aplicações também fica mais simples, já que para atualizar uma aplicação e suas dependências basta atualizar a imagem do container.
Uma solução com containers escala muito melhor, sendo possível executar e controlar diversas instâncias de um container ao mesmo tempo.
O fator segurança também é importante. Se bem configurado, um container pode rodar de forma totalmente isolada do sistema operacional hospedeiro, aumentando a segurança do sistema.
Por fim, containers possuem melhor performance e economia de recursos quando comparados à solução de virtualização com máquina virtual.
FERRAMENTAS E PADRÕES
Diversas ferramentas estão disponíveis para trabalhar com containers no Linux, e dentre as mais populares temos LXC, systemd-nspawn, Docker e Kubernetes.
O LXC (Linux Containers) é um projeto composto por bibliotecas e aplicações que fornecem um conjunto de ferramentas de linha de comando para criar e gerenciar a execução de containers no Linux.
O systemd-nspawn (parte do systemd) é uma ferramenta de linha de comando bem simples capaz de rodar aplicações dentro de containers.
O Docker é possivelmente a ferramenta de gerenciamento de containers mais popular no momento que escrevo este artigo. Desenvolvido pela Docker Inc., é mais amigável e possui mais funcionalidades quando comparado com ferramentas de mais baixo nível como o LXC e o systemd-nspawn.
Por fim, Kubernetes é um sistema de orquestração de containers open-source que automatiza a implantação, o dimensionamento e a gestão de aplicações em containers. Enquanto que o Docker e o LXC são capazes de criar e executar containers, o Kubernetes é capaz de automatizar o gerenciamento de clusters de containers, escalando a solução conforme a necessidade.
Existe um projeto da Linux Foundation chamado Open Container Initiative que possui o objetivo de definir e manter padrões abertos sobre a tecnologia de containers no Linux.
Atualmente existem duas especificações definidas, a Runtime Specification (runtime-spec) que define padrões para gerenciar a execução de containers, e a Image Specification (image-spec), que padroniza o formato das imagens de containers.
CONTAINERS EM LINUX EMBARCADO?
Apesar dos containers não serem uma idéia nova, seu uso se multiplicou nos últimos anos e agora estão aparecendo diversas soluções de containers para Linux embarcado, incluindo balenaOS (antigo ResinOS), microPlatforms, SeaShell, Pantahub e o recentemente lançado Torizon da Toradex.
Existem ainda muitos desafios e problemas que precisam ser resolvidos. Será que o hardware de um sistema embarcado terá a capacidade de rodar as aplicações em containers? Como empacotar facilmente uma aplicação e suas dependências em uma imagem de container? Como fica o gerenciamento das licenças de software de uma imagem de container? Haverá um padrão para as imagens de containers, garantindo interoperabilidade entre as ferramentas de gerenciamento de containers?
No próximo artigo falarei sobre as vantagens e desvantagens do uso de containers em Linux embarcado.
Um abraço e até lá!
Sergio Prado