Automatizando o deploy com o Ansible numa VPS
Antes de qualquer coisa, gostaria de agradecer a paciência do colega de trabalho Márcio Henrique Rodrigues de Lima, para sanar as dúvidas relativas ao deploy e configuração correta do docker-compose.
Na jornada do desenvolvimento de um sistema uma parte bastante importante é colocar a aplicação no ar, após claro passar pelas etapas de teste e homologação, podendo haver algumas outras etapas anteriores ao "deploy".
Na grande maioria das vezes essa operação é uma receita de bolo tendo etapas a serem seguidas numa ordem determinada pela empresa.
Vamos abstrair um pouco e imaginar que na nossa empresa XPTO o processo de colocar a aplicação no ar siga os passos abaixo:
- Acessar o servidor da aplicação via SSH
- Baixar a nova versão do código de um ambiente de versionamento, iremos utilizar aqui o Github como exemplo
- Parar o servidor web da aplicação
- Verificar se existem novas dependências de bibliotecas a serem instaladas, caso existam instalar
- Verificar se há alterações a serem realizadas no banco de dados, caso existam aplicar as alterações
- Atualizar o código no diretório da aplicação com o código baixado da etapa 2
- Reiniciar o servidor web da aplicação
Novamente, estou abstraindo vários detalhes, para não ficar um texto muito extenso.
No exemplo fictício acima, quando o setor responsável não utiliza nenhuma ferramenta de automação desse processo, todas as etapas acima são executadas de forma manual podendo gerar falhas, além de ser uma atividade que é praticamente a mesma para o deploy de qualquer aplicação.
O problema aumenta quando há a necessidade de realizar esse processo diversas vezes ao dia, muito comum nos dias de hoje nas grandes empresas.
Já imaginou ter que fazer o deploy de cinco aplicações todos os dias?
Difícil né, por isso foram desenvolvidas várias ferramentas para agilizar o processo, e aqui falarei sobre o https://www.ansible.com/ que é uma ferramenta desenvolvida em #Python e mantida pela Red Hat.
O mais interessante é que a ferramenta nos permite automatizar o processo de deploy de praticamente qualquer projeto independente da linguagem na qual foi desenvolvido.
No exemplo irei publicar um sistema desenvolvido em Django com banco de dados PostgresSQL numa VPS na locaweb, utilizando Docker. O deploy da aplicação baixará o projeto de um repositório no Github.
Então vamos lá.
Instalando o ansible
O primeiro passo de tudo é instalar o Ansible no nosso computador. O processo de instalação é bem simples, um detalhe importante é que se você usa windows deverá utilizar o WSL2 para poder funcionar.
Para a instalação basta seguir o tutorial Instalando o Ansible.
Configurando o servidor para acesso remoto, via SSH
Essa etapa é muito importante, pois sem ela o projeto Ansible não conseguirá acessar remotamente a máquina de destino (servidor).
Para que o código do projeto Ansible possa acessar a máquina remota (servidor) via SSH temos que executar as etapas abaixo:
- Configurar o SSH no servidor
- Criar uma chave SSH na nossa máquina
- Copiar a chave, etapa 2, para o servidor remoto
- Verificar se a chave foi copiada com sucesso para o servidor remoto
Veja como realizar as configurações aqui
Instalando os pacotes no projeto Ansible
Como o Ansible nos permite trabalhar com diversas abordagens, é necessário conforme nossa necessidade, adicionar pacotes (collections ansible).
Para fazer a instalação dos pacotes usaremos o Ansible Galaxy, para aprender como instalar sugiro ler a documentação.
Pacotes (Collections Ansible)
Iniciando um projeto simples para entendimento
Para trabalhar com o Ansible não é necessário nenhum comando para "iniciar" um projeto, o que devemos fazer é criar o arquivo de hosts. O arquivo hosts (inventário) é um arquivo que servirá para o Ansible saber qual o endereço (IP) da(s) máquina(s) deverá ser utilizado para executar o comando.
Podemos por exemplo declarar no arquivo hosts o IP da nossa máquina desta forma
[all]
192.xxx.xxx.100
191.xxx.xxx.200
No arquivo acima temos duas máquinas de destino para a execução do comando(s).
Podemos por exemplo executar um comando de ping via Ansible, claro que não faria muito sentido usar o Ansible apenas para um comando como o ping, mas o exemplo serve para entender o funcionamento do arquivo host.
Vamos executar o ping com o comando abaixo
ansible -i hosts all -m ping
As flag's do comando acima -i e -m servem especificamente para informar em quais máquinas devem ser executados os comando e o comando a ser executado, no nosso exemplo será um ping.
Playbook
Como dito anteriormente não faz sentido utilizar o Ansible para comandos "simples", sendo mais indicado o uso do Ansible para tarefas mais complexas.
Para isso existe o que chamamos de playbook que nada mais é que um conjunto de comandos a serem executados em sequência que no nosso exemplo servirão para realizar as etapas de deploy da aplicação, podemos fazer um paralelo com uma receita de bolo, as etapas devem ser executadas numa ordem determinada.
Vamos primeiro criar um novo playbook.
ansible-galaxy init nginx_install
Na estrutura criada com o comando acima o arquivo mais relevante é o tasks/main.yml, pois é nele que iremos declarar as ações a serem executadas em cada etapa do processo.
O uso de playbook's específicos para cada operação é muito recomendado para antes de tudo facilitar a manutenção e evitar de termos um único arquivo com diversas responsabilidades, além de facilitar o reaproveitamento em outros projetos.
Configurando o servidor com as libs básicas.
Essa etapa é necessária para que possamos instalar os pacotes necessários para utilização do Docker na etapa de deploy da aplicação.
Lembre-se que a nossa máquina (servidor) está apenas com o sistema operacional instalado.
Vamos criar o primeiro playbook
ansible-galaxy init configure_server
Após a criação do playbook precisamos editar o código do tasks/main.yml com o código abaixo.
---
# tasks file for roles/configure_server
- name: install python and virtualenv
apt:
name:
- python3-pip
- python3-venv
- python3-setuptools
state: present
- name: install piptools
pip:
name: pip-tools
state: present
- name: install git
apt:
name: git
state: present
- name: install openssh-server
apt:
name: openssh-server
state: present
- name: generate ssh key
community.crypto.openssh_keypair:
path: "{{ ssh_key_path }}"
register: open_ssh_pub_key
No playbook acima fizemos a instalação básica para que seja possível executar o Docker e conectar ao Github utilizando acesso ssh.
Instalar o docker
Como nosso projeto fará uso do Docker, nada mais óbvio do que instalar o Docker no nosso servidor. Para tal seguirei o tutorial de instalação do Docker, utilizando claro o Ansible.
Criando o playbook para o docker
ansible-galaxy init docker_install
Após a criação do playbook precisamos editar o código do tasks/main.yml com o código abaixo.
---
# tasks file for roles/docker_install
- name: Install libs
apt: # Utilizando o módulo apt
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
state: present # Informando que deve ser instalado
update_cache: yes # Atualizando o cache do apt
- name: Add Dockers official GPG key
apt_key: # Adicionando a chave GPG do Docker
url: https://download.docker.com/linux/ubuntu/gpg # URL da chave GPG
state: present
- name: Add Docker repository
apt_repository: # Adicionando o repositório do Docker
repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable
state: present
- name: Install Docker
apt: # Utilizando o módulo apt para instalar os pacotes
name:
- docker-ce
- docker-ce-cli
- containerd.io
state: present # Informando que deve ser instalado
update_cache: yes # Atualizando o cache do apt
# Instalando o Docker Compose
- name: Install Docker Compose
apt: # Utilizando o módulo apt para instalar os pacotes
name: docker-compose
state: present # Informando que deve ser instalado
Clonando o projeto do github
Agora vamos clonar o projeto do repositório GIT para o servidor, no caso usaremos o comando git do Ansible para realizar a atualização do projeto, vamos criar o playbook. Mais abaixo explicaremos o que são as entradas {{ ... }}
ansible-galaxy init checkout_project
Após a criação do playbook precisamos editar o código do task/main.yml com o código abaixo.
---
- name: Clonando o projeto do Github
git:
repo: "{{ project_repo }}"
dest: "{{ project_path }}"
key_file: "{{ ssh_key_path }}"
accept_hostkey: true
Projeto a ser publicado
No meu caso o projeto a ser publicado faz uso de Docker para instalar todos os componentes do projeto de forma isolada compreendendo:
- PostgreSQL
- Projeto Django
- Nginx
O mais interessante em utilizarmos o Docker é que deixamos o Ansible praticamente inalterável para realizar o deploy de vários projetos, mesmo que usem outras tecnologias. Mas se for necessário transferir a instalação por exemplo do PostgreSQL no seu servidor e utilizado para todos os projetos basta criar um playbook para instalar o PostgreSQL.
A intenção desse artigo não é explicar o Docker, por isso não irei detalhar o código do compose.
código docker-compose.yml
version: "3.7"
networks:
xpto_network:
name: xpto_network
services:
xpto_database:
container_name: xpto_database
image: postgres:16.1
restart: always
volumes:
- xpto-db:...
environment:
- ...
networks:
- xpto_network
ports:
- "5437:5432"
xpto_django:
container_name: xpto_django
image: xpto:1.0
environment:
- ...
build:
context: .
dockerfile: ./Dockerfile
entrypoint: ...
networks:
- xpto_network
volumes:
- xpto-media:...
- xpto-static:...
depends_on:
- xpto_database
gateway:
image: nginx:1.19-alpine
container_name: xpto_gateway
restart: unless-stopped
networks:
- xpto_network
expose:
- 443
- 80
ports:
- "8004:80"
depends_on:
- xpto_django
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- xpto-media:...
- xpto-static:...
volumes:
xpto-db:
xpto-media:
xpto-static:
Fazendo o deploy
Vamos criar o playbook para realizar o deploy
ansible-galaxy init deploy_with_docker
Aqui preciso explicar o código sem usar comentários no próprio código, para um melhor entendimento.
Primeiro vamos entender o que vem a ser o {{ project_path }}. Para auxiliar o reaproveitamento de código é possível trabalhar com as variáveis, que nada mais são que apontamentos para valores que são utilizados em várias partes do projeto, como por exemplo o caminho do projeto no servidor.
Para mais detalhes basta acessar aqui.
Para utilizar uma variável basta declará-la no vars/main.yml e usá-la no tasks/main.yml
Por fim outro componente "novo" no nosso arquivo é o trecho:
- name: Deploy Stack
community.docker.docker_compose:
project_src: "{{ project_path }}"
recreate: always
build: true
nocache: true
files:
- "docker-compose.yml"
Nesse bloco de código utilizamos um "pacote" da comunidade para realizar ações com o Docker. Caso deseje saber mais sobre o pacote basta acessar aqui.
Código completo:
---
# tasks file for roles/deploy_with_docker
- name: Install Python3 and pip
apt:
name:
- python3-pip
- python3-setuptools
state: present
update_cache: yes
- name: Install Jsondiff
pip:
name: jsondiff
state: present
- name: Deploy Stack
community.docker.docker_compose:
project_src: "{{ project_path }}"
recreate: always
build: true
nocache: true
files:
- "docker-compose.yml"
Configurando o main raiz do projeto.
Para que o Ansible entenda como deve executar todas as etapas conforme planejado precisamos criar um arquivo para controlar a execução. O arquivo deve ficar na pasta roles para que o Ansible possa entender.
Todos os playbook´s que criamos anteriormente também deve estar dentro do diretório roles
Configurando o arquivo main.yml na raiz do projeto que será responsável por "chamar" as etapas na ordem definida acima
---
- hosts: all
remote_user: root
become: yes
roles:
## Configurando o servidor para acesso remoto
- configure_server
## Instalando o docker
- docker_install
## Clonar o projeto
- checkout_project
## Fazendo o deploy da aplicação com docker-compose
- deploy_with_docker
Agora vamos executar nosso playbook com o comando:
ansible-playbook -i hosts CAMINHO_PARA_ARQUIVO_PLAYBOOK.yml
Tentei transcrever a minha experiência no uso do Ansible e de uma VPS para realizar o deploy de uma aplicação.
Não sou expert na área e estou aprendendo, mas achei legal tentar explicar da melhor forma possível, baseado nos meus estudos e experiência, como realizar o deploy de forma profissional menos amadora, já que tenho muito o que aprender ainda nesse novo desafio que coloquei para 2024 que é aprender mais sobre a parte de DevOPS.
Abraço a todos.