Docker Compose como um serviço do Systemd

Galera aqui vamos ver a configuração de serviços do Docker Compose com o systemd, em muitos casos precisamos que os serviços subam junto com o SO ou queremos manipular eles como serviços do systemd, desta forma precisamos preparar os arquivos de manipulação do systemd para esta tarefa.

O que eu vou utilizar:

  • Diretório para armazenar os docker-compose.yml: /srv/docker/compose
  • Diretório para armazenar os dados dos containers: /srv/docker/data
  • IP do Docker Host: 10.3.0.91
  • App que vamos utilizar para testar: Wordpress + MySQL

Vamos criar o arquivo que vai manipular os serviços do docker-compose

vim /etc/systemd/system/docker-compose@.service
[Unit]
Description=%i service with docker compose
Requires=docker.service
After=docker.service

[Service]
Restart=always

#Diretório que vai armazenar os arquivos de compose
WorkingDirectory=/srv/docker/compose/%i

# Remove containers antigos, images e volumes
# Sempre tente manter a sua estrutura de docker organizada e sem danglings
#ExecStartPre=/usr/local/bin/docker-compose down -v
#ExecStartPre=/usr/local/bin/docker-compose rm -fv

## Habilitar os comandos abaixo caso queira sempre garantir que não temos dangling volumes/netwoks/containers
#ExecStartPre=-/bin/bash -c 'docker volume ls -qf "name=%i_" | xargs docker volume rm'
#ExecStartPre=-/bin/bash -c 'docker network ls -qf "name=%i_" | xargs docker network rm'
#ExecStartPre=-/bin/bash -c 'docker ps -aqf "name=%i_*" | xargs docker rm'

# Subindo o Compose
ExecStart=/usr/bin/docker-compose up

# Compose down, remove containers e volumes
#ExecStop=/usr/bin/docker-compose down -v

# Parando o Compose
ExecStop=/usr/bin/docker-compose down

[Install]
WantedBy=multi-user.target

Agora vamos criar o diretório para armazenar os arquivos de compose

mkdir -p /srv/docker/compose/

Agora vamos criar o diretório para armazenar os dados dos containers

mkdir -p /srv/docker/data

Agora vamos criar um serviço para testar, vamos começar criando o diretório para armazenar o nosso novo serviço.

mkdir -p /srv/docker/compose/wordpress

Agora vamos criar o nosso docker-compose file para o wordpress

vim /srv/docker/compose/wordpress/docker-compose.yml
version: '3'
services:
  db:
    container_name: wp-mysql
    image: mysql:5.7
    volumes:
      - ${DATA_DIR_MYSQL-/srv/docker/data/wordpress/data}:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: 1234
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress
    ports:
      - "3306:3306"
    networks:
      - my_net
  wp:
    container_name: wp-web
    volumes:
      - ${DATA_DIR_WP-/srv/docker/data/wordpress/html}:/var/www/html
    depends_on:
      - db
    image: wordpress
    ports:
      - "80:80"
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
    networks:
      - my_net
networks:
  my_net:

Agora vamos criar o arquivo .env

vim /srv/docker/compose/wordpress/.env
# Local para armazenar os dados do Wordpress
DATA_DIR_WP=/srv/docker/data/wordpress/html
# Local para armazenar os dados do MySQL
DATA_DIR_MYSQL=/srv/docker/data/wordpress/data

Agora vamos testar o nosso docker-compose.yml

cd /srv/docker/compose/wordpress && docker-compose config
networks:
  my_net: {}
services:
  db:
    container_name: wp-mysql
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_PASSWORD: wordpress
      MYSQL_ROOT_PASSWORD: 1234
      MYSQL_USER: wordpress
    image: mysql:5.7
    networks:
      my_net: null
    ports:
    - 3306:3306/tcp
    volumes:
    - /srv/docker/data/wordpress/data:/var/lib/mysql:rw
  wp:
    container_name: wp-web
    depends_on:
    - db
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_PASSWORD: wordpress
      WORDPRESS_DB_USER: wordpress
    image: wordpress
    networks:
      my_net: null
    ports:
    - 80:80/tcp
    volumes:
    - /srv/docker/data/wordpress/html:/var/www/html:rw
version: '3.0'

Caso de algum erro acima valide o arquivo, geralmente espaços ou tabs geram problemas.

Caso precise de um arquivo de configuração do vim para trabalhar com yml

mv ~/.vimrc ~/.vimrc.bkp

Agora vamos obter o novo arquivo de configuração

wget -c https://raw.githubusercontent.com/douglasqsantos/scripts_shell/master/olds/vimrc -O ~/.vimrc

Agora vamos criar os diretórios que vão armazenar os dados do wodpress

mkdir -p /srv/docker/data/wordpress/html
mkdir -p /srv/docker/data/wordpress/data

Agora vamos habilitar o serviço do wordpress para o serviço do Docker Compose

systemctl enable docker-compose@wordpress

Agora vamos subir o serviço

systemctl start docker-compose@wordpress

Nota: Aqui estamos trabalhando com portas padrões dos serviços: 3306 e 80 então caso tenha algum container trabalhando nestas portas precisa alterar o docker-compose.yml ou parar os containers com as mesmas portas para não gerar conflitos.

Agora vamos vamos verificar o nosso serviço

systemctl status docker-compose@wordpress -l
● docker-compose@wordpress.service - wordpress service with docker compose
   Loaded: loaded (/etc/systemd/system/docker-compose@.service; enabled; vendor preset: disabled)
   Active: active (running) since Qui 2019-10-10 11:31:18 -03; 29s ago
  Process: 29240 ExecStop=/usr/bin/docker-compose down (code=exited, status=0/SUCCESS)
 Main PID: 29402 (docker-compose)
   CGroup: /system.slice/system-docker\x2dcompose.slice/docker-compose@wordpress.service
           ├─29402 /usr/bin/docker-compose up
           └─29403 /usr/bin/docker-compose up

Out 10 11:31:33 centos7.dqs.local docker-compose[29402]: wp-mysql | 2019-10-10T14:31:33.926861Z 0 [Note]   - '::' resolves to '::';
Out 10 11:31:33 centos7.dqs.local docker-compose[29402]: wp-mysql | 2019-10-10T14:31:33.927006Z 0 [Note] Server socket created on IP: '::'.
Out 10 11:31:33 centos7.dqs.local docker-compose[29402]: wp-mysql | 2019-10-10T14:31:33.943536Z 0 [Warning] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
Out 10 11:31:33 centos7.dqs.local docker-compose[29402]: wp-mysql | 2019-10-10T14:31:33.954892Z 0 [Note] Event Scheduler: Loaded 0 events
Out 10 11:31:33 centos7.dqs.local docker-compose[29402]: wp-mysql | 2019-10-10T14:31:33.955557Z 0 [Note] mysqld: ready for connections.
Out 10 11:31:33 centos7.dqs.local docker-compose[29402]: wp-mysql | Version: '5.7.27'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
Out 10 11:31:36 centos7.dqs.local docker-compose[29402]: wp-web | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 192.168.160.3. Set the 'ServerName' directive globally to suppress this message
Out 10 11:31:36 centos7.dqs.local docker-compose[29402]: wp-web | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 192.168.160.3. Set the 'ServerName' directive globally to suppress this message
Out 10 11:31:36 centos7.dqs.local docker-compose[29402]: wp-web | [Thu Oct 10 14:31:36.891482 2019] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.38 (Debian) PHP/7.3.10 configured -- resuming normal operations
Out 10 11:31:36 centos7.dqs.local docker-compose[29402]: wp-web | [Thu Oct 10 14:31:36.891566 2019] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'

Agora vamos listar os serviços via compose

cd /srv/docker/compose/wordpress/ && docker-compose ps
  Name                Command               State                 Ports              
-------------------------------------------------------------------------------------
wp-mysql   docker-entrypoint.sh mysqld      Up      0.0.0.0:3306->3306/tcp, 33060/tcp
wp-web     docker-entrypoint.sh apach ...   Up      0.0.0.0:80->80/tcp

Podemos consultar os serviços via docker ps

docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
78601c7b90dc        wordpress           "docker-entrypoint.s…"   3 minutes ago       Up 3 minutes        0.0.0.0:80->80/tcp                  wp-web
f4cb74224fcd        mysql:5.7           "docker-entrypoint.s…"   3 minutes ago       Up 3 minutes        0.0.0.0:3306->3306/tcp, 33060/tcp   wp-mysql

Agora precisamos acessar: http://10.3.0.91/wp-admin/install.php e configurar o Wordpress.

Após efetuar a configuração vamos parar o serviço do wordpress

systemctl stop docker-compose@wordpress

Agora vamos consultar o status do serviço

systemctl status docker-compose@wordpress -l
● docker-compose@wordpress.service - wordpress service with docker compose
   Loaded: loaded (/etc/systemd/system/docker-compose@.service; enabled; vendor preset: disabled)
   Active: inactive (dead) since Qui 2019-10-10 11:36:59 -03; 22s ago
  Process: 30104 ExecStop=/usr/bin/docker-compose down (code=exited, status=0/SUCCESS)
  Process: 29402 ExecStart=/usr/bin/docker-compose up (code=exited, status=0/SUCCESS)
 Main PID: 29402 (code=exited, status=0/SUCCESS)

Out 10 11:36:59 centos7.dqs.local docker-compose[29402]: wp-mysql | 2019-10-10T14:36:59.034113Z 0 [Note] Shutting down plugin 'mysql_native_password'
Out 10 11:36:59 centos7.dqs.local docker-compose[29402]: wp-mysql | 2019-10-10T14:36:59.034302Z 0 [Note] Shutting down plugin 'binlog'
Out 10 11:36:59 centos7.dqs.local docker-compose[29402]: wp-mysql | 2019-10-10T14:36:59.036576Z 0 [Note] mysqld: Shutdown complete
Out 10 11:36:59 centos7.dqs.local docker-compose[29402]: wp-mysql |
Out 10 11:36:59 centos7.dqs.local docker-compose[29402]: wp-mysql exited with code 0
Out 10 11:36:59 centos7.dqs.local docker-compose[30104]: [101B blob data]
Out 10 11:36:59 centos7.dqs.local docker-compose[30104]: Removing wp-mysql ...
Out 10 11:36:59 centos7.dqs.local docker-compose[30104]: [113B blob data]
Out 10 11:36:59 centos7.dqs.local docker-compose[29402]: 
Out 10 11:36:59 centos7.dqs.local systemd[1]: Stopped wordpress service with docker compose.

Agora vamos consultar os containers

docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

Como podemos notar não temos nenhum container ou seja, quando o docker compose subir novamente ele vai recriar os containers e utilizar os dados que temos armazenados via a configuração de volumes do docker-compose.yml

Agora vamos subir novamente o serviço e testar

systemctl start docker-compose@wordpress

Agora vamos consultar o status do serviço

systemctl status docker-compose@wordpress -l
● docker-compose@wordpress.service - wordpress service with docker compose
   Loaded: loaded (/etc/systemd/system/docker-compose@.service; enabled; vendor preset: disabled)
   Active: active (running) since Qui 2019-10-10 11:39:01 -03; 8s ago
  Process: 30104 ExecStop=/usr/bin/docker-compose down (code=exited, status=0/SUCCESS)
 Main PID: 30258 (docker-compose)
   CGroup: /system.slice/system-docker\x2dcompose.slice/docker-compose@wordpress.service
           ├─30258 /usr/bin/docker-compose up
           └─30259 /usr/bin/docker-compose up

Out 10 11:39:03 centos7.dqs.local docker-compose[30258]: wp-mysql | 2019-10-10T14:39:03.741574Z 0 [Note]   - '::' resolves to '::';
Out 10 11:39:03 centos7.dqs.local docker-compose[30258]: wp-mysql | 2019-10-10T14:39:03.741598Z 0 [Note] Server socket created on IP: '::'.
Out 10 11:39:03 centos7.dqs.local docker-compose[30258]: wp-mysql | 2019-10-10T14:39:03.743589Z 0 [Warning] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
Out 10 11:39:03 centos7.dqs.local docker-compose[30258]: wp-mysql | 2019-10-10T14:39:03.755647Z 0 [Note] Event Scheduler: Loaded 0 events
Out 10 11:39:03 centos7.dqs.local docker-compose[30258]: wp-mysql | 2019-10-10T14:39:03.755834Z 0 [Note] mysqld: ready for connections.
Out 10 11:39:03 centos7.dqs.local docker-compose[30258]: wp-mysql | Version: '5.7.27'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
Out 10 11:39:04 centos7.dqs.local docker-compose[30258]: wp-web | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 192.168.176.3. Set the 'ServerName' directive globally to suppress this message
Out 10 11:39:04 centos7.dqs.local docker-compose[30258]: wp-web | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 192.168.176.3. Set the 'ServerName' directive globally to suppress this message
Out 10 11:39:04 centos7.dqs.local docker-compose[30258]: wp-web | [Thu Oct 10 14:39:04.384978 2019] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.38 (Debian) PHP/7.3.10 configured -- resuming normal operations
Out 10 11:39:04 centos7.dqs.local docker-compose[30258]: wp-web | [Thu Oct 10 14:39:04.385066 2019] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'

Agora vamos consultar os containers

docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
0ed73e309e33        wordpress           "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes        0.0.0.0:80->80/tcp                  wp-web
a6bf2323c534        mysql:5.7           "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes        0.0.0.0:3306->3306/tcp, 33060/tcp   wp-mysql

Agora vamos consultar o nosso Wordpress novamente em: http://10.3.0.91

Como podemos notar o nosso serviço está funcionando corretamente e temos os dados persistidos via volumes.

Criando mais serviços para o Systemd

Agora parar criarmos um serviço para, precisamos começar criando o diretório para armazenar o nosso novo serviço.

mkdir -p /srv/docker/compose/drupal

Agora crie o seu arquivo docker-compose.yml dentro do diretório /srv/docker/compose/drupal

vim /srv/docker/compose/drupal/docker-compose.yml
version: '3'
services:
  drupal:
    image: drupal:8-apache
    ports:
      - "8080:80"
    volumes:
      - drupal-modules:/var/www/html/modules
      - drupal-profiles:/var/www/html/profiles
      - drupal-themes:/var/www/html/themes
      - drupal-sites:/var/www/html/sites
    networks:
      - net
    restart: always
  postgres:
    image: postgres:10
    environment:
      POSTGRES_PASSWORD: example
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      - net
    restart: always
volumes:
  drupal-modules:
  drupal-profiles:
  drupal-sites:
  drupal-themes:
  db_data:
networks:
  net:

Para habilitar o novo serviço

systemctl enable docker-compose@drupal

Agora basta iniciar o novo serviço

systemctl start docker-compose@drupal

Agora já podemos configurar o Drupal em http://10.3.0.91:8080

Cleanup para o Docker

Agora vamos configurar um cleanup para o Docker, em alguns casos temos informações temporárias que não seram mais utilizadas, desta forma é bom manter sempre o servidor limpo.

Caso executemos o docker prune manualmente vamos ter

docker system prune
WARNING! This will remove:
  - all stopped containers
  - all networks not used by at least one container
  - all dangling images
  - all dangling build cache

Are you sure you want to continue? [y/N] y
Deleted Networks:
test_network
docker-compose_default
prestashop_my_net
new_network
net1
net2
docker-compose_test_net

Total reclaimed space: 0B

Caso não queira remover os arquivos não configure este serviço.

Vamos criar o arquivo de controle do serviço

vim /etc/systemd/system/docker-cleanup.timer

[Unit]
Description=Docker cleanup timer

[Timer]
OnUnitInactiveSec=12h

[Install]
WantedBy=timers.target

Agora vamos criar o serviço

vim /etc/systemd/system/docker-cleanup.service
[Unit]
Description=Docker cleanup
Requires=docker.service
After=docker.service

[Service]
Type=oneshot
WorkingDirectory=/tmp
User=root
Group=root
ExecStart=/usr/bin/docker system prune -f

[Install]
WantedBy=multi-user.target

Agora vamos habilitar o serviço

systemctl enable docker-cleanup.timer

Referências