Ferramenta Agentless para automatizar a gestão (Instalações, configurações, provisionamento, etc) de CD (Continuous Delivery) em diferentes SOs simultaneamente. Entre vários nodes (Nós-Máquinas), tem-se o master node (Controlador), onde o Ansible é usado, informando os demais nodes e respectivas credenciais/configurações. Para isso, recomenda-se SSH ativo nos demais nodes (workers ou Slaves - Controlados). O Ansible possui vários Módulos, que exercem funções específicas. Ansible Automation Controller, contendo o Ansible Tower, é a ferramenta oficial GUI do Ansible, recurso premium (AWX é a versão community).
Arquivo YAML (nomePlaybook.yml) com informações para a gestão do Ansible nos nodes. Após criado o arquivo, executa-se o comando 'ansible-playbook nomePlaybook.yml'. Pode-se colocar vários códigos de playbooks dentro de um mesmo arquivo de playbook (O exemplo abaixo possui somente 1 código). O código abaixo instalará, via módulo 'package', o nginx nos workers (Após executado, pode-se testar com 'curl ipWorker'):
- name: Instalar o nginx
hosts: distros
become: yes #executar como sudo
tasks:
- name: Instalar servidor web
package:
name: nginx
state: present #present: Instalado, absent: Não deseja no servidor, latest: Instalar última versão
Playbook para transferir arquivos:
Via módulo copy, copiar arquivo do master para Worker(s). Criar arquivo 'teste.txt' no master node e, após isso, criar arquivo 'playbook1.yml', com o seguinte conteúdo abaixo. Após criado o arquivo, executá-lo com 'ansible playbook playbook1.yml'. Pode-se conferir se o arquivo encontra-se nos workers com 'ansible all -m shell -a "cat /etc/teste.txt" '.
- name: Playbook de transferir arquivos
hosts: all
tasks:
- name: Atualizar arquivo 'teste.txt'
become: true
copy:
src: teste.txt
dest: /etc/teste.txt
Via módulo user. Criar arquivo playbook2.yml, com o seguinte conteúdo abaixo. Após criado o arquivo, executá-lo com 'ansible playbook playbook2.yml'. Pode-se verificar se o usuário fora criado com 'ansible all -m shell -a "getent passwd ubsocial" '.
- name: Playbook para criação de usuários
hosts: all
vars:
- server_name: servidor01
- user_name: ubsocial
- conf_file: /opt/app/app.conf
tasks:
- name: Criando o usuário
become: true
user:
name: {{ user_name }}
O playbook anterior possuia variáveis internas. Para utilizar variáveis externas, criaremos arquivo 'config.yml', com o seguinte conteúdo abaixo. Pode-se, também, criar variáveis diretamente via comando, exemplo 'ansible-playbook nomePlaybook.yml -e username=ubsocial'.
config.yml:
server_name: servidor01
username: ubsocial
playbook2b.yml:
- name: Playbook 2 para criação de usuários
hosts: all
vars_files:
- config.yml
tasks:
- name: Criando o usuário
become: true
user:
name: {{ username }}
Informações coletadas pelo Ansible quando executado em workers. Com isso, pode-se refinar comandos (Ex: Verificar se o worker é Ubuntu e, se sim, execute o 'apt update').
ansible debian -m setup #Mostrará informações dos hosts do grupo
ansible debian -m setup -a "filter=ansible_distribution" #Com filtragem de informações
Utilizar loops em playbooks para repetição de tasks (Playbook 3b abaixo). Após criado o arquivo, executá-lo com 'ansible-playbook playbook3.yml'. Pode-se verificar a instalação dos pacotes no CentOS com 'ansible centos -m shell -a "rpm -qa | egrep 'wget|vim|tree'" '. No Debian, pode-se verificar com 'ansible debian -m shell -a "dpkg -l | egrep 'wget|git|figlet'" '.
playbook3.yml:
- name: Instalar pacotes no CentOS
hosts: centos
vars:
- packages:
- wget
- vim
- tree
tasks:
- name: Instalar pacotes
become: true
yum:
name: "{{ packages }}"
playbook3b.yml:
- name: Instalar pacotes no Debian
hosts: debian
tasks:
- name: Instalar pacotes Debian
become: true
apt:
name: "{{ item }}"
loop:
- wget
- git
- figlet
Pode-se utilizar condicionais 'when' para aplicar filtros em playbooks, conforme abaixo.
- name: Instalar pacotes
hosts: all
tasks:
- name: Instalar pacotes
become: true
apt:
name: "{{ item }}"
loop:
- wget
- git
- figlet
when: ansible_distribution == "Debian"
frontend.yml:
---
- name: Implantar frontend
hosts: frontend
become: yes
vars:
frontend_deploy_dest: /srv/frontend/
tasks:
- name: Instalar servidor web Nginx
package:
name: nginx
state: present
- name: Copiar os artefatos de frontend
copy:
src: app-src/public/
dest: "{{ frontend_deploy_dest }}"
setype: httpd_sys_content_t
notify:
- Reiniciar o serviço do Nginx
- name: Serviço do Nginx deve estar habilitado
service:
name: nginx
enabled: yes
state: started
- name: Configura o Nginx para o frontend
template: #Pegar arquivo de template e enviá-lo ao destino
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
notify:
- Reiniciar o serviço do Nginx
handlers:
- name: Reiniciar o serviço do Nginx #O name do handlers precisa ser idêntico ao notify
service:
name: nginx
state: restarted
backend.yml:
---
- name: Implantar backend
hosts: backend
become: yes
roles: #Importar roles abaixo
- backend
Exemplo de conteúdo em 'roles':
roles/backend/defaults/main.yml:
---
backend_deno_version: v1.2.0
backend_required_packages:
- unzip
backend_deploy_dest: /srv/backend
backend_src_path: app-src/api/
backend_entrypoint: app.ts
backend_runtime_args: --allow-read --allow-net
backend_bind_address: 0.0.0.0
backend_bind_port: 8080
roles/backend/handlers/main.yml:
---
- name: Reiniciar serviço de backend
systemd:
name: deno-backend.service
state: restarted
daemon_reload: yes
roles/backend/tasks/main.yml:
---
- name: Instalando dependências (pacotes)
package:
name: "{{ backend_required_packages }}"
state: present
- name: Instalando ambiente de execução Deno (binário)
unarchive:
src: "https://github.com/denoland/deno/releases/download/{{ backend_deno_version }}/deno-x86_64-unknown-linux-gnu.zip"
dest: /usr/local/bin
remote_src: yes
notify:
- Reiniciar serviço de backend
- name: Copiar artefatos de backend para diretório de final
copy:
src: "{{ backend_src_path }}"
dest: "{{ backend_deploy_dest }}"
notify:
- Reiniciar serviço de backend
- name: Configurar backend para ambiente produtivo
template:
src: env.j2
dest: "{{ backend_deploy_dest }}/.env"
notify:
- Reiniciar serviço de backend
- name: Configurar systemd como supervisor do backend
template:
src: systemd-unit.j2
dest: /etc/systemd/system/deno-backend.service
owner: root
group: root
notify:
- Reiniciar serviço de backend
- name: Serviço precisa estar ativo e inicializado
systemd:
name: deno-backend.service
enabled: yes
daemon_reload: yes
roles/backend/templates/systemd-unit.js:
# {{ ansible_managed }}
[Unit]
Description=Deno Backend Service
[Service]
WorkingDirectory={{ backend_deploy_dest }}
ExecStart=/usr/local/bin/deno run {{ backend_runtime_args }} {{ backend_entrypoint }}
Restart=always
[Install]
WantedBy=multi-user.target
roles/backend/templates/env.j2:
# {{ ansible_managed }}
HOST={{ backend_bind_address }}
PORT={{ backend_bind_port }}
playbook_remove.yml:
---
- name: Remover componentes
hosts: all
become: yes
gather_facts: no
tasks:
- name: Parar os serviços
service:
name: "{{ item }}"
state: stopped
enabled: no
loop:
- deno-backend
- nginx
ignore_errors: yes
- name: Remover dependências (pacotes)
package:
name:
- nginx
- unzip
state: absent
- name: Remover dependências (binarios)
file: #Forma para remover arquivo
path: /usr/local/bin/deno
state: absent
- name: Remove vestígios do Nginx
file:
path: /etc/nginx
state: absent
- name: Remover configuração systemd
file:
path: /etc/systemd/system/deno-backend.service
state: absent
- name: Remover diretórios de implantação
file:
path: "{{ item }}"
state: absent
loop:
- /srv/frontend
- /srv/backend
playbook_validate.yml:
---
- name: Verifica componente frontend
hosts: frontend
gather_facts: no
vars:
frontend_bind_port: 80
tasks:
- name: Executando requisição HTTP
uri: #Verificar se frontend subiu, e retornar o h1 em caso de falha
url: "http://{{ ansible_host }}:{{ frontend_bind_port }}"
return_content: yes
register: response
failed_when: "'<h1>Youtube Chapter Extractor</h1>' not in response.content"
retries: 3
delay: 3
until: response.status == 200
- name: Verifica componente backend
hosts: backend
vars:
backend_bind_port: 8080
gather_facts: no
tasks:
- name: Executando requisição HTTP
uri:
url: "http://{{ ansible_host }}:{{ backend_bind_port }}/api/v1/video-chapters/notreal"
register: response
retries: 3
delay: 3
until: response.status == 200
Ambiente Vagrant composto por master node (Ubuntu - 'ansible') e 2 workers (Debian e CentOS - 'machine01 e machine02').
[all:vars]
ansible_python_interpreter=/usr/bin/python
[debian]
machine01.ubsocial
[centos]
machine02.ubsocial
[linuxservers]
machine01.ubsocial
machine02.ubsocial
Elaborado por Mateus Schwede
ubsocial.github.io