Utilizando containers em Linux embarcado
- por Sergio Prado
Seria uma boa idéia utilizar containers em um sistema com Linux embarcado?
Há algum tempo atrás escrevi um artigo introdutório sobre containers em Linux. Comentei no artigo que já existem diversas soluções de distribuições baseadas em containers para Linux embarcado, incluindo o balenaOS, o Linux microPlatform da Foundries.io e o Torizon da Toradex.
O que estes projetos fazem é prover uma distribuição Linux base com uma infraestrutura pronta baseada em Docker para a execução de containers. A idéia é você instalar a distribuição na sua plataforma de hardware e desenvolver/executar suas aplicações em containers.
Ao final do artigo, deixei algumas questões em aberto. Será que o hardware de um sistema embarcado tem a capacidade de processamento necessária para executar diversas aplicações isoladas 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?
Vamos falar sobre estas e muitas outras questões neste artigo, começando pelas motivações.
Porque utilizaríamos containers em um sistema com Linux embarcado?
Motivações para o uso de containers
A frustração com sistemas de build pode ser uma grande motivação para o uso de containers. Muitos desenvolvedores iniciantes na área, principalmente aqueles que estão vindo do mundo dos microcontroladores, tem dificuldade para entender os conceitos e as ferramentas providas por um sistema de build como o Buildroot ou o OpenEmbedded/Yocto Project. É muito mais confortável e produtivo trabalhar com uma distribuição Linux pronta. A curva de aprendizado e o trabalho para criar e manter uma distribuição Linux customizada utilizando um sistema de build é muito maior comparado à criação de uma imagem de container.
Utilizando containers, o processo de desenvolvimento é mais rápido e ágil. O foco é maior no desenvolvimento da aplicação, e menos tempo é investido na criação e manutenção da infraestrutura para executar a aplicação. Estender a distribuição com novas aplicações pode ser questão de minutos através da criação de uma imagem de container com o Docker, mesmo porque já existem diversas imagens de containers Docker disponibilizadas pela comunidade. O desenvolvimento do produto pode ser mais produtivo com containers.
Com projetos cada vez mais complexos, soluções baseadas em containers podem facilitar a criação e manutenção da infraestrutura necessária para executar as aplicações que contém as regras de negócio da empresa. Os principais componentes do sistema (aplicação de interface com o usuário, servidor web, servidor de banco de dados, aplicações de rede, etc) podem ser divididos em containers, facilitando seu gerenciamento (instalação e desinstalação, configuração, atualização, etc).
Em específico, containers facilitam o desenvolvimento de um sistema de atualização mais robusto, rápido e menos suscetível a falhas. Como a imagem de um container é muito menor do que a imagem da distribuição Linux, evita-se a transferência de arquivos muito grandes pela rede, essencial para dispositivos com conectividade limitada.
Parar e iniciar um container é bem simples, o que também facilita a atualização das imagens. E a atualização de um container não irá influenciar na execução de outros containers (pelo menos não diretamente). Com um risco menor de problemas de atualização, existe um incentivo natural para aumentar a frequência de atualizações do sistema, melhorando a qualidade e a segurança do produto.
Containers facilitam o particionamento dos recursos de hardware, e desta forma temos um controle mais preciso do uso de PCU, memória e I/O pelas aplicações.
Dividindo as funcionalidades do sistema em imagens de containers, podemos limitar as permissões de acesso de cada container e controlar a comunicaçao entre os containers, diminuindo os vetores de ataque e melhorando a segurança do produto.
Imagine a situação onde uma aplicação legada precisa de uma versão antiga de determinada biblioteca. Criar e manter um ambiente de execução que suporte diferentes versões de uma mesma biblioteca pode ser trabalhoso. Como em containers as aplicações são empacotadas junto com suas dependências, este problema é resolvido facilmente.
Containers também incentivam o desenvolvimento de um sistema mais modular, facilitando o reuso. Uma imagem de container pode ser reutilizada em diferentes projetos, produtos e dispositivos baseados na mesma arquitetura de hardware e com um kernel compatível.
Com containers, trazemos a tecnologia de microserviços e DevOps para o mundo dos sistemas embarcados!
Mas ainda temos alguns desafios pela frente…
Desafios no uso de containers em Linux embarcado
Um dos desafios mais claros é um maior consumo de recursos de hardware do sistema (CPU, memória, I/O). A distribuição Linux base deverá ter todos os componentes necessários para gerenciar as imagens de containers (a maioria das distribuições baseadas em containers utilizam o Docker). Como cada imagem de container é um ambiente isolado de execução, teremos muitas bibliotecas duplicadas dentro dos containers, exigindo mais espaço de armazenamento (flash NAND, eMMC, etc.) e memória RAM (durante a execução). Para solucionar este problema, as imagens de container precisam ser desenvolvidas com foco em economia de recursos.
Outro problema pode ser um desgaste mais rápido da flash com escritas frequentes no sistema de arquivos para o gerenciamento e atualização dos containers. Um sistema embarcado custuma ficar no mercado por muitos e muitos anos, e se a distribuição Linux base mais as imagens de container não se preocuparem com o consumo da flash, em pouco tempo a memória pode começar a apresentar falhas e corromper o sistema de arquivos. Aqui não tem segredo, a solução é minimizar a quantidade de escritas na flash.
Criar uma imagem de container com os binários compilados de forma cruzada para a plataforma alvo também não é algo trivial. Por exemplo, como criar no host (ambiente de desenvolvimento x86-64) uma imagem de container com as bibliotecas do Qt5, glibc, libusb e aplicação Qt5, tudo compilado para ARMv7? Ferramentas como o Docker Buildx podem ajudar, mas o código gerado pode não estar otimizado para o hardware. Um sistemas de build (Buildroot, OpenEmbedded/Yocto Project) pode ser uma solução, mas traz de volta o fato de que uma das motivações do uso de containers é a curva de aprendizado destas ferramentas.
Este é um problema que os fornecedores de distribuições Linux baseadas em containers vão precisar resolver. Por exemplo, A Toradex está resolvendo este problema no Torizon através de um plugin para o Visual Studio. Com um único clique, o plugin é capaz de compilar uma aplicação, empacotar em uma imagem de container Docker, fazer o deploy no target e executar o container!
Outro desafio é a manutenção da imagem final do sistema. Sob o ponto de vista do desenvolvimento, o processo é simples. Instalamos uma imagem base na plataforma de hardware e desenvolvemos as aplicações dentro de containers. Mas como gerenciar as alterações na distribuição base do sistema? E como gerar a imagem final de produção com a distribuição base e todas as imagens de container?
Um problema sem solução aparente é o gerenciamento das licenças de software. Tendo um produto baseado em software livre, uma das obrigações do fornecedor (fabricante do dispositivo) é prover para seu cliente uma lista de todos os softwares livres utilizados e suas respectivas licenças. Mas como mapear e integrar as licenças da distribuição base e de todas as imagens de container do produto em um único arquivo de manifesto?
Para desenvolver um produto seguro e eficiente baseado em containers, os engenheiros vão precisar se aprofundar neste tipo de solução. Entender como os containers funcionam e como trabalhar com o Docker será essencial.
Apesar de facilitar o processo de atualização, containers podem dificultar o gerenciamento das versões dos dispositivos em produção. Para um produto com milhares de dispositivo em produção, como mapear e gerenciar a versão da distribuição Linux base e das diversas imagens de container em cada dispositivo?
Por fim, certificar um produto com containers que se atualizam frequentemente também pode ser um problema.
O que tudo isso significa?
Conclusão?
Se o futuro das soluções em Linux embarcado será utilizando containers eu realmente não sei dizer. Neste artigo procurei levantar os prós e contras para iniciar uma discussão sobre o assunto.
Uma solução com containers em Linux embarcado é inovadora e quebra alguns paradigmas, trazendo o mundo e a cultura do servidor para a ponta. É uma boa opção para a prototipação rápida do produto. E em algumas situações específicas pode ser a melhor solução, como por exemplo para criar um ambiente de execução dependente de bibliotecas de diferentes versões.
Mas confesso que ainda tenho sentimentos mistos sobre o assunto. Uma parte considerável das soluções com Linux embarcado são simples e podem ser resolvidas com o Busybox mais algumas bibliotecas e aplicações. Me sinto bastante confortável para preparar um distribuição simples com o Buildroot ou o OpenEmbedded/Yocto Project e desenvolver as aplicações por cima. Gosto do controle que tenho sobre o produto final do sistema. Também venho de uma época onde todo byte e cada ciclo de CPU tinha sua importância, então uma solução com containers, apesar de produtiva sob o ponto de vista do desenvolvimento, me parece bastante ineficiente.
Mas cada vez mais hardware é commodity, não é verdade? Talvez eu precise mesmo quebrar alguns paradigmas para absorver melhor como essa cultura de microserviços e DevOps pode ser aplicada em Linux embarcado. Estou me preparando para este futuro, esperando uma oportunidade para colocar em prática e ver o resultado. Só o tempo (e o mercado) dirá. ;-)
Um abraço!
Sergio Prado
Sem Comentários
Nenhum comentário até agora... é a sua chance de ser o primeiro a comentar!