Executando verificação de segurança...
6

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:

  1. Acessar o servidor da aplicação via SSH
  2. Baixar a nova versão do código de um ambiente de versionamento, iremos utilizar aqui o Github como exemplo
  3. Parar o servidor web da aplicação
  4. Verificar se existem novas dependências de bibliotecas a serem instaladas, caso existam instalar
  5. Verificar se há alterações a serem realizadas no banco de dados, caso existam aplicar as alterações
  6. Atualizar o código no diretório da aplicação com o código baixado da etapa 2
  7. 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:

  1. Configurar o SSH no servidor
  2. Criar uma chave SSH na nossa máquina
  3. Copiar a chave, etapa 2, para o servidor remoto
  4. 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)

  1. Community.Docker

  2. Community Openssl

  3. Community Git


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:

  1. PostgreSQL
  2. Projeto Django
  3. 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.

1
1
0