Instalando o ambiente para orquestração com Kubernetes e Docker

Ricardo Dias
Contexto Delimitado
14 min readJun 29, 2019

Muito se fala sobre computação na nuvem, orquestração de Contêineres, Devops e tantos outros termos que tem surgido nos últimos tempos.

Neste artigo tento oferecer uma receita de bolo para instalar o Kubernetes + Docker no Ubuntu 19.04 ou superiores. O objetivo não é explicar as tecnologias a fundo, mas sim em como deixá-las funcionando, de forma a possibilitar o ingresso do leitor no mundo da clusterização e gerenciamento de contêineres.

1. Um pouco de história

Sobre o Docker

Baseando-se inicialmente na tecnologia de contêineres Linux, o Docker se tornou Open Source em 2013. Hoje ele utiliza uma tecnologia própria não necessitando exclusivamente do Kernel Linux para funcionar. Pode ser instalado também no Windows e o MacOSX.

O Docker oferece mais do que a habilidade de executar contêineres, pois também facilita o processo de criação deles, o envio e o controle de versão de imagens, dentre muitas outras vantagens.

A documentação do Docker pode ser acessada aqui.

Sobre o Kubernetes

Criado pelos engenheiros da Google, uma das empresas pioneiras no desenvolvimento da tecnologia de contêineres, o Kubernetes é usado atualmente para executar grandes aplicações como o Google Docs e o Gmail. Em 2015 o Kubernetes foi doado para a “Cloud Native Computing Foundation”, se tornando um projeto Open Source. A palavra Kubernetes vem do grego “Kuvernetes”, que significa “a pessoa que pilota o navio”.

Baseado nos 15 anos de experiência da Google sobre execução de contêineres e nas melhores práticas da comunidade, o Kubernetes, k8s (K + 8 caracteres + s) ou simplesmente kube, é considerado por muitos como a melhor ferramenta Open Source do mercado para Orquestração de Contêineres. Sem dúvida, até o momento que este artigo é escrito, é a mais robusta!

Outra ferramenta que tem o mesmo princípio do Kubernetes é o “Docker Swarm” que, embora seja da mesma empresa do Docker, não é considerada pela maioria como a melhor solução para execução de contêineres em produção (pelo menos no momento em que este artigo é escrito). Embora o Swarm pareça ser mais fácil de começar usar, ele possui menos funcionalidades que o Kubernetes, que é bem mais maduro.

O propósito do Kubernetes é ser uma ferramenta para orquestração e gerenciamento de clusters para contêineres. Seus contêineres podem ser criados através de várias engines, sendo o Docker a mais famosa e também mais usada.

A documentação do Kubernetes pode ser acessada aqui.

2. Compatibilidade

Como já dito, o objetivo deste artigo não é abordar conceitos de funcionamento do Kubernetes ou do Docker. Vamos nos concentrar apenas em deixar as coisas funcionando.

Para tanto, a primeira coisa a ser feita é verificar se o computador no qual queremos configurar a orquestração suporta virtualização. Para checar isso no Ubuntu, execute o comando abaixo:

$ egrep --color 'vmx|svm' /proc/cpuinfoflags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pebs bts 
rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq
dtes64 monitor ds_cpl
vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm
pcid sse4_1 sse4_2 movbe popcnt tsc_deadline_timer aes xsave avx
f16c rdrand lahf_lm abm cpuid_fault epb invpcid_single pti ssbd …

Caso o resultado seja parecido com o mostrado em itálico no exemplo acima, e uma das opções estiverem presentes, colorindo as palavras vmx ou svm, o suporte está disponível.

Constatando-se o suporte a virtualização, o próximo passo será instalar as dependências necessárias para todo o processo.

sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common

3. Instalando do Docker

O Ubuntu 19.04 disponibiliza um pacote do Docker Comunity que pode ser instalado facilmente, usando o gerenciador de pacotes:

$ sudo apt install docker.io

Existe a possibilidade (que não é tão prática) de instalar diretamente pelo repositório do Docker. Para saber como acesse a documentação oficial clicando aqui.

Depois de instalar, verifique a versão do Docker usando:

$ docker --version
Docker version 18.09.5, build e8ff056

Para checar o status do serviço:

$ sudo service docker status
docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; disabled; vendor preset:
Active: active (running) since Sat 2019-05-18 16:09:48 EDT; 1h 15min ago
Docs:
https://docs.docker.com
Main PID: 2904 (dockerd)
Tasks: 10
Memory: 118.4M
CGroup: /system.slice/docker.service
└─2904 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/contain
mai 18 16:18:57 osboxes dockerd[2904]: time="2019-05-18T16:18:57.513752663-04:00

Por fim, para fazer um último teste, costumo usar o contêiner “ubuntu”. Trata-se de uma imagem contendo o núcleo do Ubuntu com todos os comandos do Linux distribuídos com o sistema operacional.

$ sudo docker run -it ubuntu date
  • sudo docker run: pede para o docker executar uma aplicação;
  • -it ubuntu: diz que a aplicação se encontra no contêiner “ubuntu”;
  • date: a aplicação que deve ser executada

Como estamos invocando o contêiner pela primeira vez, ele ainda não existe, portanto será baixado do docker hub.

Unable to find image ‘ubuntu:latest’ locally
latest: Pulling from library/ubuntu
6abc03819f3e: Pull complete
05731e63f211: Pull complete
0bd67c50d6be: Pull complete
Digest: sha256:f08638ec7ddc90065187e7eabdfac3c96e5ff0f6b2f1762cf31a4f49b53000a5
Status: Downloaded newer image for ubuntu:latest

Após o download do contêiner terminar, o programa date será executado:

Sat May 18 20:18:57 UTC 2019

Ok. Nosso Docker está funcionando.

Fazendo o Docker funcionar sem o “sudo”

Observando os comandos anteriores, pode-se perceber que o Docker foi executado usando o “sudo”. Se tentarmos executá-lo sem o “sudo”, uma mensagem de permissão insuficiente será emitida, como no exemplo a seguir:

$ docker run -it ubuntu datedocker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.38/containers/create: dial unix /var/run/docker.sock: connect: permission denied.
See 'docker run --help'.

Para corrigir isso, o usuário logado deve ter poderes para executar o Docker. Por isso vamos criar o grupo “docker” e adicionar o usuário nele.

$ sudo addgroup --system docker
Adding group `docker' (GID 130) ...
Done.
$ sudo adduser $USER docker
Adding user ricardo to group docker
Done.

O usuário está configurado mas ainda não pertence ao grupo, como pode ser conferido com o comando “id”:

$ id
uid=1000(ricardo) gid=1000(ricardo) grupos=1000(ricardo),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),118(lpadmin),129(sambashare)

Para efetivar a mudança é preciso fazer uma das duas opções abaixo:

  1. Sair do sistema e entrar novamente (reiniciar a máquina resolve);
  2. Usar o comando “newgrp docker”, que adiciona temporariamente o grupo “docker” para o usuário. Após reiniciar o sistema não será mais necessário usar esse comando.

Usando a segunda opção:

$ newgrp docker$ id
uid=1000(ricardo) gid=130(docker) groups=130(docker),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),118(lpadmin),129(sambashare),1000(ricardo)

Atenção: o comando newgrp só funciona no terminal em que ele foi executado, fazendo que o usuário pertença temporariamente ao grupo. Dar o comando exit faz que o usuário “saia” desta sessão temporária, ou seja, ao abrir outros terminais o usuário não possuirá as permissões do grupo “docker”. Isso é uma solução paliativa, portanto, para aplicar definitivamente a mudança, o melhor é reiniciar o sistema operacional.

Feito isso, podemos invocar o Docker sem “sudo” e o comando date será executado normalmente:

$ docker run -it ubuntu date
Fri May 17 21:25:04 UTC 2019

4. Instalando o Kubernetes

Nosso objetivo é instalar uma versão completa do Kubernetes só que de forma local, sem a necessidade de possuir uma conta em algum servidor público de nuvem. Para isso é recomendado pelo menos uma máquina com 32 GB de RAM e 250 GB de armazenamento SSD.

Opcional: alternativa leve para desenvolvedores

Caso não possua uma máquina com esses requisitos ou quiser uma maneira mais leve com Kubernetes puro, use o microk8s. De acordo com a Documentação do Kubernetes, o microk8s é uma versão mínima do Kubernetes para desenvolvedores, possuindo o ambiente completo mas com um único node de clusterização.

Para instalar o microk8s, basta usar o seguinte comando:

$ snap install microk8s --classic

Para instalar a versão normal do Kubernetes, prossiga com a leitura.

Instalando o LXD

A base para a execução de contêineres com Kubernetes na máquina local são os Linux Containers. No terminal, os contêineres são gerenciados através de duas ferramentas:

  1. LXC: é bem conhecido como um kit de ferramentas, modelos, bibliotecas e muito mais. É um programa de baixo nível, muito flexível e abrange quase todos os recursos suportados pelo kernel;
  2. LXD: é a nova experiência com LXC. Oferece uma experiência de usuário completamente nova e intuitiva com uma única ferramenta de linha de comando para gerenciar seus contêineres. Os recipientes podem ser gerenciados pela rede de forma transparente através de uma API REST. Não é um novo LXC, pois o LXD usa o LXC para suas operações e sua finalidade é diferente.

Como iremos configurar uma máquina local, antes de instalar o Kubernetes será necessário instalar e configurar o LXD e permitir sua execução pelo usuário logado.

A documentação informa que “Snaps são o método de instalação recomendado. Nos próximos lançamentos do Ubuntu, a versão snap do LXD será a única maneira instalá-lo e usá-lo”. Portanto, vamos instalar através do snap.

$ sudo snap install lxd

Configurando as permissões para o comando lxc

Por padrão, o LXC só pode ser executado por um usuário root ou usando sudo. Isso deve ser mudado, permitindo que usuários não-root possam executá-lo. Rodando o comando abaixo sem sudo, a seguinte mensagem aparecerá:

$ lxc list
Error: Get http://unix.socket/1.0: dial unix /var/snap/lxd/common/lxd/unix.socket: connect: permission denied

Usando sudo tudo funciona normalmente:

$ sudo lxc list
+ — — — + — — — -+ — — — + — — — + — — — + — — — — — +
| NAME | STATE | IPV4 | IPV6 | TYPE | LOCATION |
+ — — — + — — — -+ — — — + — — — + — — — + — — — — — +

Com sudo funciona porque o usuário root pertence ao grupo lxd:

$ sudo groups root
root: root lxd

E o usuário atual (“ricardo”) não:

$ sudo groups ricardo
ricardo: ricardo adm cdrom sudo dip plugdev lpadmin sambashare

Para solucionar o problema de permissão, basta o usuário em questão no grupo lxd. No terminal, a variável $USER contém o nome do usuário atualmente logado, no meu caso, ricardo. Por isso, vou usar a variável nos comandos:

$ sudo usermod --append --groups lxd $USER

Para verificar se o usuário agora pertence também ao grupo lxd, basta checar o arquivo /etc/group:

$ cat /etc/group | grep ricardoadm:x:4:syslog,ricardo
cdrom:x:24:ricardo
sudo:x:27:ricardo
dip:x:30:ricardo
plugdev:x:46:ricardo
lpadmin:x:118:ricardo
ricardo:x:1000:
sambashare:x:129:ricardo
lxd:x:998:root,ricardo

O usuário está configurado mas ainda não pertence ao grupo, como pode ser conferido com o comando id:

$ id
uid=1000(ricardo) gid=1000(ricardo) grupos=1000(ricardo),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),118(lpadmin),129(sambashare)

Para efetivar a mudança é preciso fazer uma das duas opções abaixo:

  1. Sair do sistema e entrar novamente (reiniciar a máquina resolve);
  2. Usar o comando “newgrp lxd”, que muda temporariamente o grupo principal do usuário para o lxd. Após reiniciar o sistema não será mais necessário usar esse comando.

Usando a segunda opção:

$ newgrp lxd
$ id
uid=1000(ricardo) gid=998(lxd) grupos=998(lxd),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),118(lpadmin),129(sambashare),1000(ricardo)

Agora o usuário “ricardo” pode executar diretamente o lxc sem necessidade de sudo:

$ lxc list
+ — — — + — — — -+ — — — + — — — + — — — + — — — — — +
| NAME | STATE | IPV4 | IPV6 | TYPE | LOCATION |
+ — — — + — — — -+ — — — + — — — + — — — + — — — — — +

Atenção: o comando newgrp só funciona no terminal em que ele foi executado, ou seja, na sessão atual. Se outro terminal for aberto, o usuário não pertencerá ao grupo lxd e o comando deverá ser repetido. Isso é uma solução paleativa, portanto, para aplicar definitivamente a mudança, o melhor é reiniciar o sistema operacional.

Bônus: Para ver os grupos disponíveis no sistema, digite no terminal: cut -d: -f1 /etc/group e para ver os usuários disponiveis: cut -d: -f1 /etc/passwd.

Testando o clusterizador LXD

Em seguida, será necessário iniciar o serviço de clusterização. O comando abaixo precisa ser executado com sudo e as perguntas deverão ser respondidas de acordo com suas necessidades.

$ lxd init

Abaixo, as perguntas correspondentes a esse artigo e uma uma explicação sobre suas respectivas respostas:

  • Would you like to use LXD clustering? Você gostaria de usar o clustering LXD? Resposta: no. Pois queremos configurar um contêiner manualmente.
  • Do you want to configure a new storage pool? Deseja configurar um novo pool de armazenamento? Resposta: yes.
  • Name of the new storage pool. Nome do novo pool de armazenamento. Resposta: digite um nome. Este nome serve para identificar o contêiner na lista de armazenamentos. Neste artigo, chamaremos o pool de teste.
  • Name of the storage backend to use. Nome do back-end de armazenamento a ser usado. Resposta: dir. As opções são btrfs, ceph, dir, lvm e zfs, porém, para publicações locais, existem limitações para o zfs e btrfs. Escolhemos o dir, pois iremos usar um diretório físico do sistema.
  • Would you like to connect to a MAAS server? Gostaria de se conectar a um servidor MAAS? Resposta: no.
  • Would you like to create a new local network bridge? Você gostaria de criar uma nova ponte de rede local? Resposta: yes.
  • What should the new bridge be called? Como dever ser chamada a nova ponte? Resposta: use a sugestão padrão: lxdbr0.
  • What IPv4 address should be used? Qual IPv4 deverá ser usado? Resposta: auto.
  • What IPv6 address should be used? Qual IPv6 deverá ser usado? Resposta: sempre escolha “none” na instalação local.
  • Would you like LXD to be available over the network? Você gostaria que o LXD estivesse disponível na rede? Resposta: no.
  • Would you like stale cached images to be updated automatically? Deseja que as imagens obsoletas em cache sejam atualizadas automaticamente? Resposta: yes.
  • Would you like a YAML “lxd init” preseed to be printed? Gostaria que um YAML “lxd init” fosse impressa? Resposta: no.

Após responder às perguntas, o LXD estará executando com um armazenamento criado, como pode-se conferir com o seguinte comando:

$ lxc storage list
+-------+-------------+--------+---------+---------+
| NAME | DESCRIPTION | DRIVER | STATE | USED BY |
+-------+-------------+--------+---------+---------+
| teste | | dir | CREATED | 0 |
+-------+-------------+--------+---------+---------+

E a ponte de rede:

$ lxc network show lxdbr0config:
ipv4.address: 10.227.213.1/24
ipv4.nat: "true"
ipv6.address: none
description: ""
name: lxdbr0
type: bridge
used_by: []
managed: true
status: Created
locations:
- none

E as informações do perfil:

$ lxc profile show defaultconfig: {}
description: Default LXD profile
devices:
eth0:
name: eth0
nictype: bridged
parent: lxdbr0
type: nic
root:
path: /
pool: teste
type: disk
name: default
used_by: []

A próxima etapa é verificar a criação do contêiner e a acessibilidade da rede. Para criar um contêiner, vamos fazer algo parecido com o que foi feito no exemplo do Docker, baixando um contêiner com o ubuntu:

$ lxc launch ubuntu:19.04 meu-conteiner
Creating meu-conteiner
Starting meu-conteiner

Com o contêiner criado, vamos testá-lo executando o programa “date”:

$ lxc exec meu-conteiner date
Sun May 19 00:05:27 UTC 2019

Para verificar se ele possui acesso à rede, vamos executar o programa “ping”, apontando para o site do google, que deverá devolver o IP do google:

$ lxc exec meu-conteiner ping google.comPING google.com (172.217.172.142) 56(84) bytes of data.
64 bytes from google.com (172.217.172.142): icmp_seq=1 ttl=61 time=11.1 ms
64 bytes from google.com (172.217.172.142): icmp_seq=2 ttl=61 time=11.7 ms
64 bytes from google.com (172.217.172.142): icmp_seq=3 ttl=61 time=11.5 ms
--- google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 6ms
rtt min/avg/max/mdev = 11.131/11.421/11.660/0.251 ms

Perfeito. O contêiner está funcionando! Para parar a execução do contêiner:

$ lxc stop meu-conteiner

Com o contêiner parado, o podemos dar uma última olhada no status:

$ lxc list
+---------------+---------+------+------------+-------------+
| NAME | STATE | IPV4 | TYPE | LOCATION |
+---------------+---------+------+------------+-------------+
| meu-conteiner | STOPPED | | PERSISTENT | ricardo-bnw |
+---------------+---------+------+------------+-------------+

Para executar novamente, basta digitar:

$ lxc start meu-conteiner

Para mais informações sobre o LXD, veja a documentação.

Instalando e configurando o Kubernetes

O Kubernetes consiste em várias ferramentas que, trabalhando juntas, fazem a mágica da orquestração de contêineres acontecer. São uma variedade de componentes e plugins (addons) para produtividade. Para ter uma noção, confira na documentação oficial.

Para instalar o Kubernetes, várias configurações precisam ser feitas. A boa notícia é que existe um programa chamado conjure-up, que facilita muito o processo todo, permitindo que tudo aconteça mediante um passo a passo autoexplicativo. Para mais informações sobre o conjure-up, acesse a documentação oficial.

O próximo passo é instalar o conjure-up e configurar o kubernetes:

$ sudo snap install conjure-up --classic

Rodando o conjure-up com privilégios de administração, uma mensagem avisará que ele não pode ser executado como root ou com sudo:

$ sudo conjure-up
!! This should _not_ be run as root or with sudo. !!

Execute o conjure-up como usuário normal. Note que este usuário deve poder executar o LXC sem precisar ser root ou usar sudo.

$ conjure-up kubernetes

Nota: Executando o comando “conjure-up” sem a opção “kubernetes”, todas as opções de virtualização serão exibidas para poderem ser selecionadas. Neste artigo optamos por exibir apenas as opções que dizem respeito ao Kubernetes.

Na tela SPELL SELECTION, selecione “Charmed Distribution of Kubernetes”, a distribuição do Kubernetes da Canonical. Em seguida, pressione a tecla Enter.

Na próxima tela, ADD-ON SELECTION, é possível selecionar componentes adicionais. Para selecionar, pressione a tecla Espaço. Para finalizar e prosseguir para a próxima tela pressione Enter, ou TAB até focalizar o botão Continue.

A seguir, será perguntado sobre qual nuvem desejada para implantar o Kubernetes. Neste ponto escolha “localhost”, pois usaremos o LXD local.

A próxima etapa é a configuração do LXD. Perceba que a ponte de rede que criamos com o lxc (lxdbr0) está presente, juntamente com o pool de armazenamento “teste”. Selecione a ponte, o pool e pressione Enter para prosseguir.

A próxima tela pede para selecionar o spell. Neste ponto escolha flannel, pressione Tab para ativar o botão Next e Enter para prosseguir.

Na próxima tela, digite a sua senha de sudo para poder instalar o kubectl e avance para a próxima tela.

A próxima tela irá possibilitar a configuração de 6 aplicações que acompanharão o Kubernetes. Para configurar, basta posicionar o foco em Configurar e pressionar Enter.

Para finalizar, focalize o botão Deploy e pressione Enter. O instalador irá ser iniciado.

Durante o processo de deploy, será possível acompanhar cada aplicação sendo instalada e configurada:

Após o término, as informações de conclusão serão exibidas.

Conclusão

Se tudo correu bem, o sistema agora possui um ambiente de virtualização real com Kubernetes e Docker e pode ser usado para orquestração local.

Novos artigos sobre assunto serão feitos assim que surgir a oportunidade. Espero que as informações contidas aqui possam ser úteis, auxiliando o leitor na descoberta e nos estudos sobre essas fantásticas ferramentas. Até a próxima!

--

--

Ricardo Dias
Contexto Delimitado

Apaixonado por padrões, programação clara, elegante e principalmente manutenível. Trabalha como desenvolvedor deste 2000, incrementando a cada ano este loop…