Pesquisa de site

Como executar contêineres Podman no Systemd com Quadlet


Quadlet é uma ferramenta gratuita e de código aberto escrita em C que nos permite criar e executar contêineres Podman no Systemd. A ferramenta permite declarar contêineres, volumes, redes e seus relacionamentos, usando unidades Systemd dedicadas.

Neste tutorial, aprendemos como usar o Quadlet para criar contêineres, redes e volumes Podman, e como criar pilhas de vários contêineres.

Neste tutorial você aprenderá:

  • Como criar contêineres, volumes e redes com as unidades Systemd correspondentes
  • Como criar pilhas de vários contêineres usando Quadlet

Um exemplo básico: criando um contêiner MariaDB

Neste primeiro exemplo básico, definimos uma unidade para um servidor de banco de dados MariaDB. Aqui está o que parece:

[Unit]
Description=MariaDB container

[Container]
Image=docker.io/mariadb:latest
Environment=MYSQL_ROOT_PASSWORD=rootpassword
Environment=MYSQL_USER=testuser
Environment=MYSQL_PASSWORD=testpassword
Environment=MYSQL_DATABASE=testdb

[Install]
WantedBy=multi-user.target

Com essas poucas linhas, definimos um contêiner baseado na última imagem oficial do MariaDB, disponível no Dockerhub. Como você pode ver, um “.container” é um tipo dedicado de unidade Systemd. O que é exclusivo deste tipo de unidade é a seção “Container”, na qual podemos especificar opções que são equivalentes àquelas que podemos passar para a linha de comando do Podman. A única opção obrigatória na seção “Container” é Image, para especificar a imagem base do container.

Se quisermos que o contêiner seja executado como root, salvamos o arquivo no diretório /etc/containers/systemd; para executá-lo como nosso próprio usuário sem privilégios, em vez disso, salvamos o arquivo em ~/.config/containers/systemd. Neste exemplo, usaremos a última abordagem e salvaremos nossa unidade “.container ” como ~/.config/containers/systemd/mariadb-service.container. Para permitir que o systemd gere um serviço baseado na unidade do contêiner, executamos o seguinte comando:

$ systemctl --user daemon-reload

Depois de executarmos o comando, podemos dar uma olhada no serviço gerado executando:

$ systemctl --user cat mariadb-service

Para iniciar o contêiner, assim como qualquer outro serviço, podemos executar:

$ systemctl --user start mariadb-service

Na primeira vez que o iniciarmos, o serviço poderá demorar um pouco para iniciar, caso a imagem base do contêiner não exista em nosso sistema. Podemos usar a linha de comando podman para verificar o status do contêiner:

$ podman ps

O comando retorna a seguinte saída, que confirma que o contêiner está funcionando:

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
151226b10a1c docker.io/library/mariadb:latest mariadbd 59 seconds ago Up About a minute systemd-mariadb-service

Como você deve ter notado, por padrão, o contêiner recebe o nome do arquivo da unidade “.container” mais o prefixo “systemd-” (“systemd-mariadb-service”, neste caso). Para atribuir explicitamente um nome ao contêiner, podemos, entretanto, passá-lo como o valor da opção ContainerName dentro da seção “[Container]”.

Criando uma unidade de “volume”

Usamos volumes para persistir os dados dos contêineres. Nesse caso, queremos que os dados do nosso banco de dados persistam mesmo se descartarmos o contêiner systemd-mariadb-service. Definimos um volume nomeado na unidade “.volume” correspondente. No exemplo abaixo, fornecemos apenas uma descrição do volume. Opções específicas de volume podem ser especificadas na sub-rotina [Volume]:

[Unit]
Description=MariaDB Volume

[Volume]

Salvamos a unidade como ~/.config/containers/systemd/mariadb-volume.volume. Para instruir o contêiner MariaDB a usar o volume, usamos a opção Volume dentro da seção [Container] da unidade “.container ” e mapeamos o volume para o Diretório /var/lib/mysql dentro do contêiner:

[Unit]
Description=MariaDB container

[Container]
Image=docker.io/mariadb:latest
Environment=MYSQL_ROOT_PASSWORD=rootpassword
Environment=MYSQL_USER=testuser
Environment=MYSQL_PASSWORD=testpassword
Environment=MYSQL_DATABASE=testdb
Volume=mariadb-volume.volume:/var/lib/mysql

[Install]
WantedBy=multi-user.target

Quando usamos um volume nomeado, o Systemd atribui automaticamente uma dependência a ele na unidade de serviço que gera para nosso contêiner. Para permitir que o systemd regenere os serviços necessários, mais uma vez, usamos o comando daemon-reload. Assim que (re)iniciarmos o contêiner MariaDB, o volume será criado automaticamente, como podemos verificar usando o utilitário de linha de comando podman:

$ podman volume ls

Como esperado, nosso volume aparece na lista gerada pelo comando:

DRIVER        VOLUME NAME
local         systemd-mariadb-volume

Também podemos verificar se o volume está montado em /var/lib/mysql dentro do container usando o comando podman inspeciona, passando o nome do container como argumento, e dando uma olhada na seção “Montagens” seção:

$ podman inspect systemd-mariadb-service

Aqui está a seção relevante da saída:

"Mounts": [
     {
         "Type": "volume",
         "Name": "systemd-mariadb-volume",
         "Source": "/home/doc/.local/share/containers/storage/volumes/systemd-mariadb-volume/_data",
         "Destination": "/var/lib/mysql",
         "Driver": "local",
         "Mode": "",
         "Options": [
              "nosuid",
              "nodev",
              "rbind"
         ],
         "RW": true,
         "Propagation": "rprivate"
     }
],

Usando montagens de ligação

E se quisermos usar montagens vinculadas em vez de volumes nomeados? Bind-mounts são úteis durante o desenvolvimento, pois nos permitem montar arquivos host dentro do contêiner. A desvantagem de usar montagens vinculadas é que os contêineres se tornam dependentes do sistema de arquivos host. Para usar um bind-mount dentro de uma unidade “.container”, apenas especificamos o caminho relativo ou absoluto do arquivo host que queremos montar. Suponha, por exemplo, que queiramos montar o diretório /var/lib/mysql dentro do contêiner, ao diretório ~/mysql no host. Aqui está o que poderíamos escrever na unidade “.container”:

[Unit]
Description=MariaDB container

[Container]
Image=docker.io/mariadb:latest
Environment=MYSQL_ROOT_PASSWORD=rootpassword
Environment=MYSQL_USER=testuser
Environment=MYSQL_PASSWORD=testpassword
Environment=MYSQL_DATABASE=testdb
Volume=%h/mysql:/var/lib/mysql

[Install]
WantedBy=multi-user.target

No exemplo acima, você pode notar que usamos o espaço reservado %h, que é automaticamente expandido pelo Systemd para o caminho absoluto do nosso próprio diretório HOME. Também é possível utilizar caminhos relativos, que são resolvidos relativamente à posição do arquivo unitário.

Criando Redes

Para criar uma pilha composta por vários containers, à semelhança do que fazemos com o docker-compose, devemos colocar os containers na mesma rede, para que eles possam se comunicar entre si. Para criar uma rede com Quadlet, podemos usar unidades com extensão “.network”.

Assim como acontece com contêineres e volumes, por padrão, as redes são nomeadas de acordo com o arquivo da unidade em que estão definidas, mais o prefixo “systemd-”. Podemos fornecer explicitamente um nome, ou qualquer outra opção específica de rede, na seção [Network] do arquivo. No exemplo abaixo, especificamos a sub-rede e o endereço do gateway da rede (isso equivale a executar podman com --subnet 192.168.30.0/24 e --gateway 192.168.30.1 opções):

[Unit]
Description=MariaDB Network

[Network]
Subnet=192.168.30.0/24
Gateway=192.168.30.1

Salvamos o arquivo como ~/.config/containers/systemd/mariadb.network. Para “colocar” um container em uma rede específica, usamos a opção Network dentro da seção “Container” de sua unidade container. O contêiner MariaDB que definimos anteriormente passa a ser:

[Unit]
Description=MariaDB container

[Container]
Image=docker.io/mariadb:latest
Environment=MYSQL_ROOT_PASSWORD=rootpassword
Environment=MYSQL_USER=testuser
Environment=MYSQL_PASSWORD=testpassword
Environment=MYSQL_DATABASE=testdb
Volume=mariadb-volume.volume:/var/lib/mysql
Network=mariadb.network

Criando uma pilha de vários contêineres

Temos um contêiner executando nosso servidor MariaDB. Para adicionar uma maneira fácil de gerenciar nosso banco de dados através de uma interface web, podemos criar um contêiner baseado em phpMyAdmin. Para que os dois contêineres possam se comunicar, devemos colocá-los na mesma rede.

Como o container phpMyAdmin, para funcionar corretamente, requer que o servidor MariaDB esteja ativo e em execução, podemos declarar esta dependência como faríamos para qualquer outro serviço Systemd, com os Requires e Após opções na seção “[Unit]”. A primeira estabelece uma forte dependência do serviço passado como seu valor; o último garante que nosso serviço comece depois dele. A propósito, se você não está familiarizado com o Systemd, pode dar uma olhada em nosso tutorial sobre como criar um serviço Systemd. Esta é a aparência da unidade de contêiner do phpMyAdmin:

[Unit]
Description=phpMyAdmin container
Requires=mariadb-service.service
After=mariadb-service.service
 
[Container]
Image=docker.io/phpmyadmin:latest
Network=mariadb.network
Environment=PMA_HOST=systemd-mariadb-service
PublishPort=8080:80

[Install]
WantedBy=multi-user.target

No exemplo acima, também usamos a opção PublishPort, que é equivalente a podman -p  (--publish): é usada para mapear uma porta host para uma porta de contêiner. Neste caso, mapeamos a porta 8080 no sistema host, para a porta 80 dentro do contêiner.

Também fornecemos um valor para a variável de ambiente PMA_HOST, através da opção Environment: ela é usada para especificar o endereço do servidor de banco de dados (neste caso usamos o nome do contêiner MariaDB, que é resolvido para o endereço real). Observe que usamos o nome do contêiner MariaDB como o valor da variável de ambiente, não o nome da unidade “.container”. Para iniciar nossa nova configuração, mais uma vez, executamos:

$ systemd --user daemon-reload

Então, podemos iniciar o serviço “phpmyadmin-service” gerado, que, por sua vez, executa o contêiner phpMyAdmin. Por ser fortemente dependente do MariaDB, este também será iniciado automaticamente. Devemos conseguir acessar o phpMyadmin no endereço “localhost:8080”. Para fazer login, podemos usar as credenciais especificadas na unidade de contêiner MariaDB:

Pensamentos finais

Neste tutorial aprendemos como criar e executar contêineres, volumes e redes Podman no Systemd usando Quadlet. Em vez de definir pilhas de vários contêineres em um único arquivo, como fazemos ao usar o docker-compose, com o Quadlet, definimos contêineres, volumes e redes usando unidades Systemd dedicadas.

Artigos relacionados: