Podman + minikube
Published: 2022-04-30 04:15:25

Agora que já instalamos o podman no WSL é hora de começar a briga para fazer o kubernetes usar o podman no lugar do docker. Neste post vou instalar e configurar o [minikube](https://minikube.sigs.k8s.io/docs/) pelo fato dele ser a distribuição mais popular de kubernetes para ambiente de desenvolvimento.

Para fazer o podman rodar em conjunto com o minikube, precisaremos instalar 3 ferramentas:

- O próprio minikube
- O kubectl
- Um runtime (escolhi o CRI-O)

Como o post ficou grande e cheio de seções que podem ser puladas dependendo do que já esta instalado a configurado na máquina, julguei necessário colocar um índice.

De nada :wink:

## Índice

  - [Índice](#índice)
  - [Entendendo os conceitos de driver e runtime](#entendendo-os-conceitos-de-driver-e-runtime)
    - [Runtime](#runtime)
    - [Driver](#driver)
  - [Instalando o minikube](#instalando-o-minikube)
  - [Instalando o kubectl](#instalando-o-kubectl)
  - [Instalando o CRI-O](#instalando-o-cri-o)
  - [Rodando o minikube](#rodando-o-minikube)
    - [Adiconando o Podman ao sudoers](#adiconando-o-podman-ao-sudoers)
    - [Erro ao executar o minikube](#erro-ao-executar-o-minikube)
  - [Deploy da aplicação](#deploy-da-aplicação)
    - [Adicionando a imagem ao cache do minikube](#adicionando-a-imagem-ao-cache-do-minikube)
  - [Criando a service](#criando-a-service)
  - [Expondo a aplicação para fora do Kubernetes](#expondo-a-aplicação-para-fora-do-kubernetes)
    - [Minikube Service](#minikube-service)
  - [Acessando a aplicação fora do WSL](#acessando-a-aplicação-fora-do-wsl)
  - [Alternativa rootless para o minikube](#alternativa-rootless-para-o-minikube)
  - [Impressões e Conclusão](#impressões-e-conclusão)
  - [Referencias](#referencias)

## Entendendo os conceitos de driver e runtime

> Sessão opcional: se você já entende o que são os conceitos de driver e runtime ou quer ir direto para a instalação pode pular para a [instalação do minikube](#instalando-o-minikube)
Antes de começar com o hands on de verdade, primeiro é necessário entender o básico de driver e runtime no mundo de containers.

### Runtime

O runtime é responsável por controlar todo o ciclo de vida do container. Isso vai desde baixar um container até rodá-lo.
[Este artigo](https://medium.com/techbeatly/container-runtimes-deep-dive-77eb0e511939) escrito por [Nived Velayudhan](https://nivedv.medium.com/) aprofunda mais sobre o assunto. Também vale mencionar a [própria documentação oficial da especificação do runtime](https://github.com/opencontainers/runtime-spec/blob/main/spec.md)

### Driver

O driver é responsável por prover o conjunto de apis nas quais o usuário irá interagir com o ecossistema de containers. Em resumo, provê os comandos usados nos CLIs e interfaces gráficas.

## Instalando o minikube

Para instalar a minikube não tem muito mistério, vamos seguir a [documentação oficial](https://minikube.sigs.k8s.io/docs/start/).
Existem 2 formas de instalar para quem usa distribuições baseadas em debian (meu caso): baixando os binários ou baixando o pacote debian. No tutorial vou mostrar como instalar utilizando o pacote debian, pois foi a forma que eu instalei.

O primeiro passo é rodar o comando curl e baixar a última versão:

```sh
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_latest_amd64.deb
```

Em seguida instalar o pacote:

```sh
sudo dpkg -i minikube_latest_amd64.deb
```

Pronto! Minikube instalado

## Instalando o kubectl

Não podemos executar nossos comandos kubernetes sem a ferramenta que serve para isso.
Novamente indo para a [documentação oficial](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/), basta seguir os passos informados lá. Para o meu caso utilizei o passo-a-passo do Ubuntu.

Primeiro é necessário atualizar o package index e instalar algumas ferramentas necessárias para poder baixar o Kubernetes via apt:

```sh
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
```

Em seguida baixar a chave pública do GCP e adicionar o repositório do Kubernetes ao repositório do apt:

```sh
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
```

Por fim, reatualizar o package index para buscar as informações do novo repositório e instalar o kubectl:

```sh
sudo apt-get update
sudo apt-get install -y kubectl
```

## Instalando o CRI-O

De acordo com a [documentação do minikube](https://minikube.sigs.k8s.io/docs/drivers/podman/#usage), para utilizar o podman é necessário instalar um runtime. E o runtime recomendado pela própria documentação é o CRI-O.

Para instalá-lo vamos usar a [documentação oficial do CRI-O](https://cri-o.io/)

Assim como no último post, vamos começar importanto o arquivo /etc/os-release para as variáveis de ambiente:

```sh
. /etc/os-release
```

Com essas variáveis importadas, podemos baixar os repositórios necessários:

```sh
CRIO_VERSION=1.23
sudo echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/x{$Name}_{$VERSION_ID}/ /" > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
sudo echo "deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/{$CRIO_VERSION}/x{$Name}_{$VERSION_ID}/ /" > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:{$CRIO_VERSION}.list
```

> A variável CRIO_VERSION deve ser preenchida com a versão desejada. Na data desta publicação é a 1.23.

Agora adicionamos as chaves ao apt-key:

```sh
sudo curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/x{$Name}_{$VERSION_ID}/Release.key | apt-key add -
sudo curl -L https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:{$CRIO_VERSION}/x{$Name}_{$VERSION_ID}/Release.key | apt-key add -
```

E por fim instalamos a nossa última ferramenta necessária para rodar o minikube com o podman:

```sh
sudo apt-get update
sudo apt-get install -y cri-o cri-o-runc
```

## Rodando o minikube

Depois dessa pequena odisséia de instalação, é hora de colocar o minikube para rodar com o podman. O comando exige passar as opções de driver e container runtime, porém nada de extraordinário:

```sh
minikube start --driver=podman --container-runtime=cri-o
```

Ao executar o comando recebemos a seguinte mensagem:

```sh
😄  minikube v1.25.2 on Ubuntu 20.04 (amd64)
✨  Using the podman driver based on existing profile

💣  Exiting due to PROVIDER_PODMAN_NOT_RUNNING: "sudo -k -n podman version --format " exit status 1: sudo: a password is required
💡  Suggestion: Add your user to the 'sudoers' file: 'myuser ALL=(ALL) NOPASSWD: /usr/bin/podman'
📘  Documentation: https://podman.io
```

Se o leitor pensou a mesma coisa que eu a primeira vez que viu essa mensagem, certamente deve ter tentado rodar o minikube com sudo e recebeu o erro abaixo:

```sh
😄  minikube v1.25.2 on Ubuntu 20.04 (amd64)
✨  Using the podman driver based on user configuration
🛑  The "podman" driver should not be used with root privileges.
💡  If you are running minikube within a VM, consider using --driver=none:
📘    https://minikube.sigs.k8s.io/docs/reference/drivers/none/

❌  Exiting due to DRV_AS_ROOT: The "podman" driver should not be used with root privileges.
```

O podman por ser construído utilizando a premissa rootless, não permite que o minikube seja rodado como rootfull quando utilizá-o como driver.

A solução é seguir a sugestão dada na primeira execução

### Adicionando o Podman ao sudoers

Apesar de ser uma etapa rápida de fazer, vale parar 1 minuto para entender o que é o sudoers e o que ele faz antes de executar o comando.

> sudoers é um arquivo utilizado para definir o nível de permissão de execução de comandos no modo super usuário. Neste arquivo é possível definir desde permissão de execução de comandos em um programa em específico até execução total e irrestrita.
> Para mais informações e entendimento recomendo a [leitura deste post](https://www.linux.com/training-tutorials/configuring-linux-sudoers-file/) e a [leitura do man do arquivo sudoers](https://linux.die.net/man/5/sudoers).

Execute o comando `sudo visudo` e adicione a seginte linha no final do arquivo:

```sh
myuser ALL=(ALL) NOPASSWD: /usr/bin/podman
```

Explicando o nível de permissão que estamos atribuindo:

O usuário *myuser* pode executar qualquer comando dentro do programa */usr/bin/podman* sem a necessidade de informar o password.

Com a configuração adicionada podemos rodar o minikube sem grandes problemas.

### Erro ao executar o minikube

Ao tentar rodar o minikube pela primeira vez talvez ocorra o erro de download de imagem KIC (perdi o texto certo do erro :sweat_smile:). Para resolver esse problema é necessário baixar a imagem manualmente com o comando:

```sh
minikube start --driver=podman --container-runtime=cri-o --download-only=true
```

Após o término do download basta executar o minikube novamente

## Deploy da aplicação

Com tudo configurado, é hora de criar um deploy e publicar nossa aplicação de teste.
Para isso crie um arquivo *.yaml* igual ao exemplo abaixo (ou pega ele pronto no repositório :wink:)

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: juridico-deployment
  labels:
    app: juridico
spec:
  replicas: 2
  selector:
    matchLabels:
      app: juridico
  template:
    metadata:
      labels:
        app: juridico
    spec:
      containers:
      - name: juridicoapp
        image: localhost/juridico:latest
        imagePullPolicy: IfNotPresent
        ports: 
        - containerPort: 3001
```

Com o **deployment.yaml** criado, execute o comando

```sh
kubectl apply -f <path-to-deployment-file>/deployment.yaml
```

Para ver se o deployment foi feito rode o comando:

```sh
kubectl get deployments juridico-deployment
```

o resultado deve ser algo parecido com:

```sh
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
juridico-deployment   2/2     2            2           15d
```

E para ver os pods criados:

```sh
kubectl get pods -l app=juridico
```

A saída deve ser algo parecido com:

```sh
NAME                                    READY   STATUS             RESTARTS   AGE
juridico-deployment-86b6b47b6-j5l9n     1/1     Running            4          15d
juridico-deployment-86b6b47b6-lzkjq     1/1     Running            4          15d
```

Se por ventura no status estiver mensagens do tipo `ImagePullBackOff` ou `ErrImagePull`, provavelmente você deve possuir a imagem somente no cache do podman. Se esse for o caso, é necessário fazer um dos 3 passos:

- Subir a imagem para algum registry remoto (dockerhub, quay.io)
- Criar um [registry local](https://minikube.sigs.k8s.io/docs/handbook/registry/)
- Subir a imagem para o cache do minikube

### Adicionando a imagem ao cache do minikube

Se você assim como eu está tentando fazer um teste 100% local, sem dependender de registrys externos e subir mais uma aplicação, antes de ter tudo rodando é necessário executar mais esse passo. O processo é tedioso, porém pouco complexo

Primeiro é necessário exportar a imagem para um arquivo *.tar* com o comando:

```sh
podman save -o juridico.tar localhost/juridico:latest
```

Depois basta importar a imagem no minikube utilizando o comando:

```sh
minikube image load juridico.tar
```

Feito isso, basta reiniciar o minikube que o problema deve estar solucionado.

## Criando a service

O próximo passo é criar uma forma de nos comunicarmos com o sistema independente da quantidade de pods instanciados e qual o IP do pod que está rodando no momento.

Para criar a service seguimos o mesmo passo do deployment e criamos um arquivo *.yaml* conforme abaixo:

```yaml
apiVersion: v1
kind: Service
metadata:
  name: juridico-service
  labels:
    app: juridico
spec:
  type: NodePort
  selector:
    app: juridico
  ports:
    - port: 3001
      protocol: TCP
      nodePort: 30001
```

Com o **service.yaml** criado, execute o comando

```sh
kubectl apply -f <path-to-service-file>/service.yaml
```

Para ver se o service foi criado rode o comando:

```sh
kubectl get services juridico-service
```

o resultado deve ser algo parecido com:

```sh
NAME       TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
juridico   NodePort   10.109.72.98   <none>        3001:30001/TCP   15d
```

Com a service configurada as outras aplicações já conseguem resolver o nome do sistema independente do scale up ou down dos pods.

## Expondo a aplicação para fora do Kubernetes

Mesmo com a service configurada ainda não é possível realizar um acesso de fora do kubernetes. Para isso é necessária a utilização de um componente de rotas para expor a aplicação para fora e rotear as requisições para a aplicação. Nas minhas pesquisas encontrei duas formas de fazer o encaminhamento: usando o próprio minikube e usando o ingress. Aqui irei apresentar somente a primeira.

### [Minikube Service](https://minikube.sigs.k8s.io/docs/commands/service/)

O próprio minikube possui um comando que expõe a service para fora do kubernetes de forma simples e direta, podendo expor todas as services ou somente uma em específico. Para expor a aplicação deste post execute o comando abaixo:

```sh
minikube service --url juridico-service
```

O resultado será algo parecido com:

```sh
 minikube service juridico
🏃  Starting tunnel for service juridico.
🎉  Opening service default/juridico in default browser...
👉  http://192.168.49.2:30001
```

Rodando o comando curl na url mostrada o valor retornado é a lista de todos os processos presentes no db.

## Acessando a aplicação fora do WSL

Até o momento foram realizados inúmeros testes dentro do WSL e tudo tem ocorrido bem. Porém ao tentar chamar a URL a partir da máquina host(Windows) ela não encontrará a rota (404). Isso acontece porque o minikube assume um IP interno acessível somente pelo próprio WSL. Para tornar o sistema acessível é necessário utilizar o comando de port forwarding.

Para habilitar port fowarding para a service execute o comando:

```sh
kubectl port-forward service/juridico-deployment 3001:3001
```

O resultado deve ser algo semelhante ao que segue:

```sh
Forwarding from 127.0.0.1:3001 -> 3001
Forwarding from [::1]:3001 -> 3001
Handling connection for 3001
Handling connection for 3001
```

> Infelizmente ao executar o comando de port forward o terminal fica preso e só é liberado ao encerrar o port-forward

Agora sim é possível chamar o sistema utilizando a URL <http://localhost:3001> diretamente da máquina host.
<!-- // Aqui posso criar um novo post para isso -->

## Alternativa rootless para o minikube

Como mencionado [nesta issue](https://github.com/kubernetes/minikube/issues/8719), os drivers do minikube devem executar em modo rootful. O principal risco de rodar o driver em modo root é a chance de ocorrer uma escalada de privilégios até o host. Para evitar este tipo de problema vamos recorrer a uma alternativa rootless: O usernetes

## Impressões e Conclusão

Rodar o minikube utilizando o podman como runtime se provou um desafio grande e demandou muito estudo para fazer tudo rodar corretamente. O principal problema foi tornar a aplicação acessível da máquina host.
Um ponto que me chamou atenção é que durante a configuração do ecossistema é a incapacidade que o minikube tem de operar em modo rootless e as alterções necessárias para "forçar" o podman em modo rootful. Isso me levou a começar a estudar como funciona o userneter e quem sabe trazer um post novo para a série.

Por fim, vejo que o podman ainda precisa percorrer algum caminho para se tornar fácil de ser integrado e configurado como driver das distribuições mais populares de kubernetes para desenvolvedores assim como o docker é hoje.

Todos os arquivos e sistemas utilizados nesse post estão disponíveis no meu github no repositório [wsl plus podman](https://github.com/julianopoveda/wsl2-plus-podman).

## Referencias

- [documentação oficial de instalação do minikube](https://minikube.sigs.k8s.io/docs/start/)
- [documentação oficial de instalação do kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/)
- [documentação oficial de instalação do CRI-O](https://cri-o.io/)
- [documentação minikube utilizando podman como driver](https://minikube.sigs.k8s.io/docs/drivers/podman/)
- [post conceitos de driver e runtime. Autor: Nived Velayuhan](https://medium.com/techbeatly/container-runtimes-deep-dive-77eb0e511939)
  - [link alternativo do post](https://opensource.com/article/20/12/containers-101)
- [documentação da Spec do runtime pela OCI](https://github.com/opencontainers/runtime-spec/blob/main/spec.md)
- [discussão do porque o podman deve rodar em rootful com minikube](https://github.com/kubernetes/minikube/issues/8719)
- [usernetes: alternativa rootless para o minikube](https://github.com/rootless-containers/usernetes)
- [problema de download das imagens](https://github.com/kubernetes/minikube/pull/8417)