DevSecOps

12 abr, 2016

Gerenciamento de recursos de hardware no Docker e instalação de pacotes de sistema operacional no docker-machine

Publicidade

Olá, pessoal!

Estava vendo os slides do Wellington Silva, da apresentação Aplicações 12 Fatores – Melhor com Docker, que ele fez no Darkmira Tour PHP 2016. Nela, ele faz de forma exemplar as relações entre a metodologia 12 factor, da galera da Heroku, com o Docker. Como estou cada dia mais conhecendo o Docker, me espantei quando ele mostrou dois comandos: docker run … –memory=512M e docker update –memory=1GB ….

No mundo da virtualização é normal pensar na definição da capacidade de hardware (além dos softwares e sistema operacional) que será reservada para aquela máquina virtual. Costumamos fazer na interface gráfica da ferramenta, ou via scripts diretos na CLI do hypervisor ou ainda por meio do Vagrant. Assim, quando pensamos na VM, já temos essa definição do “tamanho dos recursos”: quantidade de CPUs, quantidade de memória RAM e de disco etc.

Já no mundo do Docker sempre percebi que o foco é mais na aplicação e serviços rodando dentro do container. Pensei: mas no Dockerfile nunca vi essas definições de recurso: o arquivo geralmente é feito pensando mais no código e instalações da camada de software (faço um paralelo aqui com o Vagrantfile, onde as duas situações podem ser tratatadas). Fui buscar mais informações e achei duas fontes interessantes. A primeira é o artigo Resource management in Docker, onde Marek Goldmann faz uma visão geral sobre cgroups e como é possível gerenciar CPU, memória e disco nos containers.

O segundo foi um repositório no GitHub. O contendo o Dockerfile da imagem Docker agileek/cpuset-test para que fosse possível testar de forma rápida pelo menos a flag –cpuset-cpus do comando docker run. No vídeo de apresentação da imagem, o negócio é quase instantâneo: ele roda o container e as cpus começam a fritar. E usando a flag –cpuset-cpus, você só faz uma parte delas fritar. E você pode acompanhar tudo pelo htop.

É isso que fui fazer. Como a imagem é bem pequena (1.5MB apenas – é só um binário em golang e ele roda o cpuburn lá dentro), tudo parecia que ia ser muito rápido.

Como estou usando o OS X, primeiro subi minha docker-machine:

$ dockerup

# é um alias de docker-machine start dev && eval "$(docker-machine env dev)"
# e só criei isso pois sou fã de Vagrant e do "vagrant up" ;-)

Em seguida já fui rodar o docker run e, como não tinha a imagem ainda na minha máquina, ela foi baixada e já começou a executar:

$ docker run -it --rm --cpuset-cpus=0,1 agileek/cpuset-test

Unable to find image 'agileek/cpuset-test:latest' locally
latest: Pulling from agileek/cpuset-test
acc9a6499096: Pull complete
97ff6384ee3c: Pull complete
Digest: sha256:b1217ec9cf830819904b1e79c1b9c5acad07235bc8576bfb634d840d36995bed
Status: Downloaded newer image for agileek/cpuset-test:latest
Burning 8 CPUs/cores
10 seconds
20 seconds
30 seconds
40 seconds
...

Abri um outro terminal e como tenho um i7 quadcore (8 cores hyperthreading), fui olhar o htop para monitorar o uso de CPU. Ali em cima tinha pedido para ele fritar dois dos núcleos, o 0 e o 1:

1-htop-osx

2-htop-osx

Ué… Não apareceu o que eu estava esperando. Ele parecia estar fazendo o stress em quatro cores, e não nos dois como eu havia pedido.

Docker-machine: VM que permite usar Docker em sistemas não-Linux

Aí lembrei que, por estar no OS X, o docker engine/daemon (que é afetado pelo docker run) não está diretamente no meu computador: ele está dentro do docker-machine. O docker-machine é a máquina virtual oficial do Docker para que possamos usar os containers de Linux (sobre o qual o Docker é baseado) em outros sistemas operacionais.

A existência dessa camada ficou mais clara quando vi qual processo que estava consumindo aquele monte de CPU: era o VBoxHeadless, do VirtualBox, justo o hypervisor onde a VM está rodando. Fui até olhar como estava a configuração da VM via GUI:

virtualbox-gui-cpus-docker-machine-dev

Acessando a VM via docker-machine ssh

Tranquilo; então, era só entrar na VM e rodar o htop por lá. Meu foco era apenas enxergar como que se refletiria o ajuste de CPUs no sistema operacional hospedeiro do Docker (para quem usa Linux, ele próprio, para outros SOs, o Linux da VM do docker-machine) depois do comando docker run e a flag `cpuset.

$ docker-machine ssh dev

                        ##         .
                  ## ## ##        ==
               ## ## ## ## ##    ===
           /"""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
           \______ o           __/
             \    \         __/
              \____\_______/
 _                 _   ____     _            _
| |__   ___   ___ | |_|___ \ __| | ___   ___| | _____ _ __
| '_ \ / _ \ / _ \| __| __) / _` |/ _ \ / __| |/ / _ \ '__|
| |_) | (_) | (_) | |_ / __/ (_| | (_) | (__|   <  __/ |
|_.__/ \___/ \___/ \__|_____\__,_|\___/ \___|_|\_\___|_|
Boot2Docker version 1.10.3, build master : 625117e - Thu Mar 10 22:09:02 UTC 2016
Docker version 1.10.3, build 20f81dd
docker@dev:~$

Lá dentro fui rodar o htop e apareceu comando não encontrado:

docker@dev:~$ htop

-sh: htop: not found

Tudo bem: fui tentar instalar o htop via apt-get ou yum, mas nenhum dos dois também estavam disponíveis:

docker@dev:~$ apt-get install htop

-sh: apt-get: not found

docker@dev:~$ yum install htop

-sh: yum: not found

O que acontece é que é a distribuição Linux da docker-machine não é nem Debian-like (que costumam usar apt-get) nem RedHat-like (que costumam usar yum). A distribuição Linux que é utilizada é a Tiny Core Linux, que usa como gerenciador de pacotes a ferramenta tce-load (segue lista contendo vários dos pacotes oficiais).

Então, para finalmente completar a instalação, foi só rodar:

docker@dev:~$ tce-load -wi htop

htop.tcz.dep OK
Downloading: ncurses.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
ncurses.tcz          100% |***************************************************|   196k  0:00:00 ETA
ncurses.tcz: OK
Downloading: htop.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
htop.tcz             100% |***************************************************|   116k  0:00:00 ETA
htop.tcz: OK

E agora, abrir o htop:

docker@dev:~$ htop

Error opening terminal: xterm-256color.

Poxa, mais um erro. Agora o problema era com a variável de ambiente $TERM. Foi só mudá-la para xterm da seguinte forma:

$ export TERM=xterm

E agora, de uma vez por todas, abrir o htop e ver tudo funcionar!

docker@dev:~$ htop

# HTOP instalado via gerenciador de pacotes tce-load

Teste efetivo do docker run com a flag –cpuset-cpus

Agora, sim, consegui testar aquela imagem docker com flag –cpuset-cpus do docker run e ver tudo acontecendo:

1-htop-docker-machine

2-htop-docker-machine

3-htop-docker-machine

4-htop-docker-machine

5-htop-docker-machine

6-htop-docker-machine

Considerações finais

Primeiro, o Docker está me fazendo aprender um monte de coisas, e isso é muito bom!

Aqui, vimos primeiro que é possível, sim, fazer o gerenciamento de recursos em nível de hardware nos containers usando flags nos comandos que interagem com o docker engine/daemon, por exemplo o docker run –cpuset-cpus.

Em seguida, percebemos que se existir uma camada de VM, os recursos que estão sendo gerenciados (como CPU, memória ou disco) não são da máquina hospedeira diretamente, mas sim dessa máquina virtual.

Também que a distribuição Linux usada na VM criada pelo docker-machine é a Tiny Core Linux, que usa como gerenciador de pacotes o tce-load, e se for preciso, conseguimos fazer as instalações necessárias.

Sobre a instalação de pacotes nesse sistema operacional, um ponto importante é que tudo que você fez será perdido na próxima atualização do docker-machine, que entende quaisquer alterações no SO feita pelo usuário como efêmeras, trazendo sempre uma nova versão com apenas os pacotes de fábrica. O que isso tudo quer dizer? Anote bem os comandos que você fizer quando usar o tce-load, para poder reutilizá-los no futuro – quem sabe num Gist.

É isso aí! Até a próxima!