DevOps & Cloud

Ansible: Automation Platform

TOKEN

Tutorial lengkap Ansible β€” playbooks, roles, inventory, modules, ad-hoc commands, variables, Jinja2 templates, dan best practices untuk automation

1. Pengenalan Ansible

Ansible adalah platform automation open-source yang dikembangkan oleh Red Hat (sekarang IBM). Ansible memungkinkan Anda mengotomatiskan provisioning, configuration management, application deployment, dan orchestration β€” semuanya tanpa menginstal agen di target server.

Keunggulan utama Ansible dibanding tool sejenis (Puppet, Chef) adalah pendekatan agentless. Ansible menggunakan SSH untuk berkomunikasi dengan target server, sehingga tidak perlu menginstal software tambahan. Cukup punya Python di target, dan Ansible sudah bisa bekerja.

Mengapa Ansible Penting?

Keunggulan Penjelasan
AgentlessTidak perlu instal agen di target β€” cukup SSH + Python
IdempotentMenjalankan playbook berkali-kali tetap aman β€” hanya melakukan perubahan jika diperlukan
YAML-BasedMudah dibaca dan ditulis β€” menggunakan format YAML yang human-readable
ModularRatusan module built-in untuk berbagai kebutuhan automation
GalaxyEkosistem roles dan collections dari komunitas
ScalabelBisa mengelola ratusan hingga ribuan server sekaligus
Cross-PlatformSupport Linux, Windows, macOS, network devices, cloud providers
Diagram: Arsitektur Ansible
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    ANSIBLE ARCHITECTURE                       β”‚
β”‚                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚  β”‚         CONTROL NODE (Ansible)        β”‚                   β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”‚                   β”‚
β”‚  β”‚  β”‚ Inventoryβ”‚ β”‚ Playbook β”‚           β”‚                   β”‚
β”‚  β”‚  β”‚ (hosts)  β”‚ β”‚ (tasks)  β”‚           β”‚                   β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           β”‚                   β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”‚                   β”‚
β”‚  β”‚  β”‚ Modules  β”‚ β”‚ Config   β”‚           β”‚                   β”‚
β”‚  β”‚  β”‚          β”‚ β”‚ (ansibleβ”‚ β”‚           β”‚                   β”‚
β”‚  β”‚  β”‚          β”‚ β”‚  .cfg)   β”‚           β”‚                   β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           β”‚                   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”‚                 β”‚ SSH (Agentless)                            β”‚
β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                          β”‚
β”‚    β”‚            β”‚                β”‚                           β”‚
β”‚    β–Ό            β–Ό                β–Ό                           β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”                           β”‚
β”‚ β”‚Server 1β”‚ β”‚Server 2β”‚ β”‚Server 3β”‚                           β”‚
β”‚ β”‚ (web)  β”‚ β”‚ (db)   β”‚ β”‚ (cache)β”‚                           β”‚
β”‚ β”‚ Python β”‚ β”‚ Python β”‚ β”‚ Python β”‚                           β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜                           β”‚
β”‚                                                              β”‚
β”‚  Kunci:                                                     β”‚
β”‚  - Tidak ada agen di target server                          β”‚
β”‚  - Komunikasi via SSH (Linux) atau WinRM (Windows)          β”‚
β”‚  - Hanya butuh Python di target                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Konsep dasar Ansible meliputi:

2. Instalasi Ansible

Ansible hanya perlu diinstall di control node. Target server hanya butuh Python dan SSH access.

Linux (Ubuntu/Debian)

Bash
# Tambah PPA repository
sudo apt update
sudo apt install -y software-properties-common
sudo add-apt-repository --yes --update ppa:ansible/ansible

# Install Ansible
sudo apt install -y ansible

# Verifikasi
ansible --version
# ansible [core 2.17.0]
#   config file = /etc/ansible/ansible.cfg
#   python version = 3.12.3

Linux (RHEL/CentOS/Fedora)

Bash
# Fedora
sudo dnf install -y ansible

# RHEL/CentOS
sudo yum install -y epel-release
sudo yum install -y ansible

macOS

Bash
# Install menggunakan Homebrew
brew install ansible

# Verifikasi
ansible --version

menggunakan pip (Semua Platform)

Bash
# Install dengan pip (Python package manager)
pip install ansible

# Install dengan pipx (isolated environment)
pip install pipx
pipx install ansible

# Install specific version
pip install ansible==9.5.1

# Install collections dan roles
ansible-galaxy collection install community.general
ansible-galaxy install geerlingguy.nginx

Setup SSH Keys

Bash
# Generate SSH key pair
ssh-keygen -t ed25519 -C "ansible@beebanelabs" -f ~/.ssh/ansible_key

# Copy public key ke target servers
ssh-copy-id -i ~/.ssh/ansible_key.pub user@server1
ssh-copy-id -i ~/.ssh/ansible_key.pub user@server2
ssh-copy-id -i ~/.ssh/ansible_key.pub user@server3

# Test koneksi
ssh -i ~/.ssh/ansible_key user@server1 "echo OK"

3. Inventory

Inventory adalah daftar host (server) yang akan dikelola oleh Ansible. Inventory bisa berupa file statis (INI atau YAML) atau dinamis (script yang menghasilkan daftar host).

Inventory Statis (INI Format)

INI
# inventory/hosts.ini
# === Server Web ===
web1 ansible_host=192.168.1.10 ansible_user=deploy
web2 ansible_host=192.168.1.11 ansible_user=deploy
web3 ansible_host=192.168.1.12 ansible_user=deploy

# === Server Database ===
db1 ansible_host=192.168.1.20 ansible_user=dbadmin ansible_port=2222
db2 ansible_host=192.168.1.21 ansible_user=dbadmin ansible_port=2222

# === Server Cache ===
redis1 ansible_host=192.168.1.30 ansible_user=deploy

# === Server Monitoring ===
monitor1 ansible_host=192.168.1.40 ansible_user=admin

# === Groups ===
[webservers]
web1
web2
web3

[dbservers]
db1
db2

[cacheservers]
redis1

[monitoring]
monitor1

# === Group of Groups (Parent Group) ===
[production:children]
webservers
dbservers
cacheservers
monitoring

# === Group Variables ===
[webservers:vars]
http_port=80
https_port=443
nginx_version=1.27.0

[dbservers:vars]
mysql_port=3306
mysql_datadir=/var/lib/mysql

[all:vars]
ansible_python_interpreter=/usr/bin/python3
ansible_ssh_common_args='-o StrictHostKeyChecking=no'

Inventory Format YAML

YAML
# inventory/hosts.yaml
all:
  vars:
    ansible_python_interpreter: /usr/bin/python3
    ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
  children:
    production:
      children:
        webservers:
          hosts:
            web1:
              ansible_host: 192.168.1.10
              ansible_user: deploy
            web2:
              ansible_host: 192.168.1.11
              ansible_user: deploy
            web3:
              ansible_host: 192.168.1.12
              ansible_user: deploy
          vars:
            http_port: 80
            https_port: 443
            nginx_version: "1.27.0"

        dbservers:
          hosts:
            db1:
              ansible_host: 192.168.1.20
              ansible_user: dbadmin
              ansible_port: 2222
            db2:
              ansible_host: 192.168.1.21
              ansible_user: dbadmin
              ansible_port: 2222
          vars:
            mysql_port: 3306

        cacheservers:
          hosts:
            redis1:
              ansible_host: 192.168.1.30
              ansible_user: deploy

        monitoring:
          hosts:
            monitor1:
              ansible_host: 192.168.1.40
              ansible_user: admin

Perintah Inventory Berguna

Bash
# List semua host
ansible-inventory -i inventory/hosts.yaml --list

# List host dalam format grafik
ansible-inventory -i inventory/hosts.yaml --graph

# List host dari group tertentu
ansible-inventory -i inventory/hosts.yaml --graph webservers

# List semua group
ansible-inventory -i inventory/hosts.yaml --list --yaml

# Verifikasi koneksi ke semua host
ansible all -i inventory/hosts.yaml -m ping

# Verifikasi koneksi ke group tertentu
ansible webservers -i inventory/hosts.yaml -m ping

4. Ad-Hoc Commands

Ad-hoc commands adalah perintah Ansible yang dijalankan langsung dari command line tanpa menggunakan playbook. Berguna untuk task satu kali atau testing koneksi.

Sintaks Dasar

Bash
# Sintaks umum
ansible <target> -m <module> -a "<args>" -i <inventory>

# Contoh: ping semua host
ansible all -m ping -i inventory/hosts.yaml
# web1 | SUCCESS => {"changed": false, "ping": "pong"}
# web2 | SUCCESS => {"changed": false, "ping": "pong"}
# db1  | SUCCESS => {"changed": false, "ping": "pong"}

# Jalankan command shell di semua host
ansible all -m command -a "uptime" -i inventory/hosts.yaml
ansible all -m shell -a "df -h" -i inventory/hosts.yaml

# Lihat info sistem
ansible all -m setup -i inventory/hosts.yaml
ansible all -m setup -a "filter=ansible_distribution" -i inventory/hosts.yaml

Contoh Ad-Hoc Commands

Bash
# Install package di semua web servers
ansible webservers -m apt -a "name=nginx state=present" \
  -i inventory/hosts.yaml --become

# Restart service nginx
ansible webservers -m service -a "name=nginx state=restarted" \
  -i inventory/hosts.yaml --become

# Copy file ke semua servers
ansible webservers -m copy \
  -a "src=./config/nginx.conf dest=/etc/nginx/nginx.conf mode=0644" \
  -i inventory/hosts.yaml --become

# Buat user baru
ansible all -m user \
  -a "name=deploy state=present shell=/bin/bash" \
  -i inventory/hosts.yaml --become

# Cek disk space
ansible all -m shell -a "df -h /" -i inventory/hosts.yaml

# Cek memory usage
ansible all -m shell -a "free -m" -i inventory/hosts.yaml

# Tambah SSH authorized key
ansible all -m authorized_key \
  -a "user=deploy key='{{ lookup(\"file\", \"~/.ssh/ansible_key.pub\") }}'" \
  -i inventory/hosts.yaml

# Jalankan dengan parallel (fork 10)
ansible all -m ping -i inventory/hosts.yaml -f 10

# Jalankan dengan sudo (--become)
ansible webservers -m shell -a "apt update" \
  -i inventory/hosts.yaml --become

# Jalankan dengan specific user
ansible webservers -m ping -i inventory/hosts.yaml \
  -u deploy --private-key ~/.ssh/ansible_key

5. Playbooks

Playbook adalah file YAML yang berisi daftar plays. Setiap play mendefinisikan sekumpulan tasks yang akan dijalankan pada host tertentu. Playbook adalah cara utama menggunakan Ansible untuk automation yang kompleks.

Struktur Playbook

YAML
# playbooks/setup-webserver.yaml
---
- name: Setup Web Server          # Nama play
  hosts: webservers               # Target hosts
  become: yes                     # Gunakan sudo
  vars:                           # Variables untuk play ini
    nginx_version: "1.27.0"
    app_port: 8080
  tasks:                          # Daftar tasks
    - name: Update apt cache
      apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: Install Nginx
      apt:
        name: "nginx={{ nginx_version }}"
        state: present

    - name: Copy Nginx config
      template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: '0644'
      notify: Restart Nginx

    - name: Ensure Nginx is running
      service:
        name: nginx
        state: started
        enabled: yes

    - name: Open firewall port
      ufw:
        rule: allow
        port: "{{ item }}"
        proto: tcp
      loop:
        - "22"
        - "80"
        - "443"

  handlers:                       # Handlers (triggered by notify)
    - name: Restart Nginx
      service:
        name: nginx
        state: restarted

Multi-Play Playbook

YAML
# playbooks/full-stack.yaml
---
# Play 1: Setup Database Server
- name: Setup Database Server
  hosts: dbservers
  become: yes
  tasks:
    - name: Install MySQL
      apt:
        name:
          - mysql-server
          - mysql-client
          - python3-mysqldb
        state: present

    - name: Start MySQL
      service:
        name: mysql
        state: started
        enabled: yes

    - name: Create database
      mysql_db:
        name: "{{ app_db_name }}"
        state: present
        encoding: utf8mb4
        collation: utf8mb4_unicode_ci

    - name: Create database user
      mysql_user:
        name: "{{ app_db_user }}"
        password: "{{ app_db_password }}"
        priv: "{{ app_db_name }}.*:ALL"
        state: present

# Play 2: Setup Web Server
- name: Setup Web Server
  hosts: webservers
  become: yes
  vars:
    app_port: 8080
  tasks:
    - name: Install packages
      apt:
        name:
          - nginx
          - python3
          - python3-pip
          - certbot
          - python3-certbot-nginx
        state: present

    - name: Copy app config
      template:
        src: templates/app.conf.j2
        dest: /etc/nginx/sites-available/app.conf
      notify: Restart Nginx

    - name: Enable site
      file:
        src: /etc/nginx/sites-available/app.conf
        dest: /etc/nginx/sites-enabled/app.conf
        state: link
      notify: Restart Nginx

  handlers:
    - name: Restart Nginx
      service:
        name: nginx
        state: restarted

# Play 3: Setup Monitoring
- name: Setup Monitoring
  hosts: monitoring
  become: yes
  tasks:
    - name: Install Prometheus
      apt:
        name: prometheus
        state: present

    - name: Install Grafana
      apt:
        name: grafana
        state: present

Error Handling di Playbook

YAML
# Error handling dengan block/rescue/always
- name: Deploy application with error handling
  hosts: webservers
  become: yes
  tasks:
    - block:
        - name: Pull latest code
          git:
            repo: "https://github.com/app/repo.git"
            dest: /opt/app
            version: main

        - name: Install dependencies
          command: pip install -r requirements.txt
          args:
            chdir: /opt/app

        - name: Restart application
          service:
            name: myapp
            state: restarted

      rescue:
        - name: Rollback to previous version
          git:
            repo: "https://github.com/app/repo.git"
            dest: /opt/app
            version: "{{ previous_version }}"

        - name: Send failure notification
          mail:
            to: admin@example.com
            subject: "Deploy FAILED on {{ inventory_hostname }}"
            body: "Deploy gagal, rolling back ke {{ previous_version }}"

      always:
        - name: Log deployment result
          lineinfile:
            path: /var/log/deployments.log
            line: "{{ ansible_date_time.iso8601 }} - Deploy attempted on {{ inventory_hostname }}"
            create: yes

Menjalankan Playbook

Bash
# Jalankan playbook
ansible-playbook -i inventory/hosts.yaml playbooks/setup-webserver.yaml

# Dry-run (check mode) β€” tanpa eksekusi
ansible-playbook -i inventory/hosts.yaml playbooks/setup-webserver.yaml --check

# Verbose mode (-v, -vv, -vvv)
ansible-playbook -i inventory/hosts.yaml playbooks/setup-webserver.yaml -vvv

# Jalankan dengan specific tag
ansible-playbook -i inventory/hosts.yaml playbooks/setup-webserver.yaml --tags "nginx,firewall"

# Skip tags tertentu
ansible-playbook -i inventory/hosts.yaml playbooks/setup-webserver.yaml --skip-tags "firewall"

# Jalankan dengan extra variables
ansible-playbook -i inventory/hosts.yaml playbooks/setup-webserver.yaml \
  -e "nginx_version=1.27.1 app_port=9090"

# Limit ke host tertentu
ansible-playbook -i inventory/hosts.yaml playbooks/setup-webserver.yaml \
  --limit web1,web2

# Step-by-step (konfirmasi setiap task)
ansible-playbook -i inventory/hosts.yaml playbooks/setup-webserver.yaml --step

# Mulai dari task tertentu
ansible-playbook -i inventory/hosts.yaml playbooks/setup-webserver.yaml \
  --start-at-task="Copy Nginx config"

6. Modules

Module adalah unit kerja dasar Ansible. Setiap task menggunakan satu module. Ansible memiliki ratusan module built-in dan ribuan lagi dari komunitas.

Module Populer

Module Fungsi Contoh
aptManage packages Debian/Ubuntuapt: name=nginx state=present
yumManage packages RHEL/CentOSyum: name=nginx state=present
serviceManage system servicesservice: name=nginx state=started
copyCopy file dari local ke remotecopy: src=./file dest=/etc/file
templateRender Jinja2 template lalu copytemplate: src=t.j2 dest=/etc/t
fileManage file/directoryfile: path=/opt/app state=directory
userManage user accountsuser: name=deploy state=present
gitManage Git repositoriesgit: repo=... dest=/opt/app
commandRun command (no shell)command: ls -la /opt
shellRun shell commandshell: cat /etc/passwd | grep root
docker_containerManage Docker containersdocker_container: name=web image=nginx
pipManage Python packagespip: name=flask state=present
cronManage cron jobscron: name="backup" job="/opt/backup.sh"
lineinfileManage lines in text filelineinfile: path=/etc/hosts line="..."
ufwManage firewall (UFW)ufw: rule=allow port=80
systemdManage systemd servicessystemd: name=nginx state=restarted

Contoh Penggunaan Module

YAML
# Module apt β€” install packages
- name: Install required packages
  apt:
    name:
      - nginx
      - python3
      - git
      - curl
    state: present
    update_cache: yes

# Module file β€” buat direktori
- name: Create app directory
  file:
    path: /opt/myapp
    state: directory
    owner: deploy
    group: deploy
    mode: '0755'

# Module git β€” clone repository
- name: Clone application repository
  git:
    repo: "https://github.com/app/repo.git"
    dest: /opt/myapp
    version: main
    force: yes

# Module template β€” render dan copy config
- name: Generate nginx config
  template:
    src: templates/nginx.conf.j2
    dest: /etc/nginx/sites-available/myapp.conf
    owner: root
    group: root
    mode: '0644'
  notify: Restart nginx

# Module service β€” manage service
- name: Ensure nginx is running
  service:
    name: nginx
    state: started
    enabled: yes

# Module user β€” buat user
- name: Create application user
  user:
    name: deploy
    shell: /bin/bash
    groups: sudo
    append: yes
    create_home: yes

# Module cron β€” tambah cron job
- name: Add backup cron job
  cron:
    name: "Database backup"
    minute: "0"
    hour: "2"
    job: "/opt/scripts/backup.sh >> /var/log/backup.log 2>&1"
    user: deploy

# Module lineinfile β€” edit file
- name: Set timezone
  lineinfile:
    path: /etc/environment
    regexp: "^TZ="
    line: "TZ=Asia/Jakarta"

7. Variables & Facts

Variables memungkinkan playbook menjadi fleksibel dan reusable. Facts adalah informasi tentang sistem target yang dikumpulkan otomatis oleh Ansible.

Definisi Variables

YAML
# Definisi di playbook
- name: Configure web server
  hosts: webservers
  vars:
    app_name: "myapp"
    app_port: 8080
    app_env: "production"
    db_host: "192.168.1.20"
    max_clients: 200
  tasks:
    - name: Print variables
      debug:
        msg: "Deploying {{ app_name }} on port {{ app_port }}"

# Definisi di file terpisah
# group_vars/webservers.yaml
app_name: "myapp"
app_port: 8080
db_host: "192.168.1.20"

# group_vars/dbservers.yaml
mysql_root_password: "{{ vault_mysql_root_password }}"
mysql_datadir: "/var/lib/mysql"

# host_vars/web1.yaml
app_port: 9090   # Override untuk web1 saja

Facts

YAML
# Lihat semua facts dari host
# ansible web1 -m setup -i inventory/hosts.yaml

# Gunakan facts di playbook
- name: Print system info
  hosts: all
  tasks:
    - name: Show OS info
      debug:
        msg: "{{ ansible_distribution }} {{ ansible_distribution_version }}"

    - name: Show IP address
      debug:
        msg: "IP: {{ ansible_default_ipv4.address }}"

    - name: Show memory
      debug:
        msg: "RAM: {{ ansible_memtotal_mb }} MB"

    - name: Conditional berdasarkan OS
      apt:
        name: nginx
        state: present
      when: ansible_distribution == "Ubuntu"

    - name: Conditional berdasarkan versi
      yum:
        name: nginx
        state: present
      when: ansible_distribution == "CentOS" and ansible_distribution_major_version == "8"

Register Variables

YAML
# Simpan output task ke variable
- name: Check service status
  hosts: webservers
  tasks:
    - name: Check nginx status
      command: systemctl is-active nginx
      register: nginx_status
      ignore_errors: yes

    - name: Print status
      debug:
        msg: "Nginx status: {{ nginx_status.stdout }}"

    - name: Start nginx if not running
      service:
        name: nginx
        state: started
      when: nginx_status.rc != 0

    - name: Get disk usage
      command: df -h /
      register: disk_info

    - name: Print disk usage
      debug:
        msg: "{{ disk_info.stdout_lines }}"

Loops

YAML
# Simple loop
- name: Install multiple packages
  apt:
    name: "{{ item }}"
    state: present
  loop:
    - nginx
    - python3
    - git
    - curl

# Loop dengan dictionary
- name: Create multiple users
  user:
    name: "{{ item.name }}"
    groups: "{{ item.groups }}"
    shell: "{{ item.shell }}"
  loop:
    - { name: "deploy", groups: "sudo", shell: "/bin/bash" }
    - { name: "monitor", groups: "adm", shell: "/bin/bash" }
    - { name: "backup", groups: "www-data", shell: "/bin/sh" }

# Loop dengan register
- name: Check multiple services
  command: systemctl is-active {{ item }}
  loop:
    - nginx
    - mysql
    - redis
  register: service_results
  ignore_errors: yes

- name: Show failed services
  debug:
    msg: "{{ item.item }} is {{ item.stdout }}"
  loop: "{{ service_results.results }}"
  when: item.rc != 0

8. Roles

Roles adalah cara Ansible untuk mengorganisir playbook, tasks, variables, handlers, dan files ke dalam struktur direktori yang terstruktur dan reusable. Role mempromosikan reusability dan separation of concerns.

Struktur Role

Struktur Direktori
# Membuat role baru dengan ansible-galaxy
ansible-galaxy init roles/nginx
# Hasil:
roles/nginx/
β”œβ”€β”€ defaults/
β”‚   └── main.yml          # Default variables (lowest priority)
β”œβ”€β”€ files/                 # Static files
β”œβ”€β”€ handlers/
β”‚   └── main.yml          # Handlers
β”œβ”€β”€ meta/
β”‚   └── main.yml          # Metadata & dependencies
β”œβ”€β”€ tasks/
β”‚   └── main.yml          # Main tasks
β”œβ”€β”€ templates/             # Jinja2 templates
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ inventory
β”‚   └── test.yml
β”œβ”€β”€ vars/
β”‚   └── main.yml          # Variables (higher priority)
└── README.md

Contoh Role Lengkap: Nginx

YAML (defaults/main.yml)
# roles/nginx/defaults/main.yml
nginx_port: 80
nginx_worker_processes: "auto"
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
nginx_client_max_body_size: "16m"
nginx_server_name: "localhost"
nginx_root_dir: "/var/www/html"
nginx_enable_ssl: false
nginx_ssl_certificate: ""
nginx_ssl_certificate_key: ""
YAML (tasks/main.yml)
# roles/nginx/tasks/main.yml
---
- name: Install Nginx
  apt:
    name: nginx
    state: present
    update_cache: yes
  tags: install

- name: Remove default config
  file:
    path: /etc/nginx/sites-enabled/default
    state: absent
  tags: config

- name: Copy Nginx configuration
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
  notify: Restart Nginx
  tags: config

- name: Copy site configuration
  template:
    src: site.conf.j2
    dest: /etc/nginx/sites-available/{{ nginx_server_name }}.conf
    owner: root
    group: root
    mode: '0644'
  notify: Reload Nginx
  tags: config

- name: Enable site
  file:
    src: /etc/nginx/sites-available/{{ nginx_server_name }}.conf
    dest: /etc/nginx/sites-enabled/{{ nginx_server_name }}.conf
    state: link
  notify: Reload Nginx
  tags: config

- name: Ensure Nginx is running
  service:
    name: nginx
    state: started
    enabled: yes
  tags: service
YAML (handlers/main.yml)
# roles/nginx/handlers/main.yml
---
- name: Restart Nginx
  service:
    name: nginx
    state: restarted

- name: Reload Nginx
  service:
    name: nginx
    state: reloaded

Menggunakan Role di Playbook

YAML
# playbooks/site.yaml
---
- name: Configure Web Servers
  hosts: webservers
  become: yes
  roles:
    - role: common         # Role dasar (selalu dipakai)
    - role: nginx           # Role Nginx
      vars:
        nginx_port: 80
        nginx_server_name: "app.example.com"
        nginx_enable_ssl: true
    - role: app             # Role aplikasi custom
      tags: app

- name: Configure Database Servers
  hosts: dbservers
  become: yes
  roles:
    - role: common
    - role: mysql
      vars:
        mysql_port: 3306
    - role: monitoring

Role dari Ansible Galaxy

Bash
# Cari role di Galaxy
ansible-galaxy search nginx

# Install role
ansible-galaxy install geerlingguy.nginx
ansible-galaxy install geerlingguy.mysql
ansible-galaxy install geerlingguy.redis

# Install collection
ansible-galaxy collection install community.general
ansible-galaxy collection install community.docker

# Install dari requirements file
ansible-galaxy install -r requirements.yml

# List installed roles
ansible-galaxy list

9. Jinja2 Templates

Ansible menggunakan Jinja2 sebagai template engine. Template memungkinkan Anda membuat file konfigurasi yang dinamis berdasarkan variables.

Contoh Template Nginx

Jinja2
# templates/nginx.conf.j2
# Managed by Ansible β€” jangan edit manual!
# Generated: {{ ansible_date_time.iso8601 }}

worker_processes {{ nginx_worker_processes }};

events {
    worker_connections {{ nginx_worker_connections }};
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile      on;
    keepalive_timeout {{ nginx_keepalive_timeout }};

    # Logging
    log_format main '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

    access_log /var/log/nginx/access.log main;
    error_log  /var/log/nginx/error.log warn;

    # Gzip
    gzip on;
    gzip_types text/plain text/css application/json application/javascript;

    server {
        listen {{ nginx_port }};
        server_name {{ nginx_server_name }};
        root {{ nginx_root_dir }};
        client_max_body_size {{ nginx_client_max_body_size }};

{% if nginx_enable_ssl %}
        listen 443 ssl http2;
        ssl_certificate {{ nginx_ssl_certificate }};
        ssl_certificate_key {{ nginx_ssl_certificate_key }};
        ssl_protocols TLSv1.2 TLSv1.3;
{% endif %}

        location / {
            try_files $uri $uri/ /index.html;
        }

        location /api {
            proxy_pass http://127.0.0.1:{{ app_port }};
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

{% for server in upstream_servers %}
        upstream backend_{{ server.name }} {
            server {{ server.address }}:{{ server.port }};
        }
{% endfor %}
    }
}

Loop di Template

Jinja2
# templates/hosts.j2
# Managed by Ansible
127.0.0.1   localhost
{% for host in groups['webservers'] %}
{{ hostvars[host]['ansible_default_ipv4']['address'] }}  {{ host }}
{% endfor %}
{% for host in groups['dbservers'] %}
{{ hostvars[host]['ansible_default_ipv4']['address'] }}  {{ host }}
{% endfor %}

10. Ansible Vault

Ansible Vault memungkinkan Anda mengenkripsi sensitive data seperti password, API keys, dan certificates. Data terenkripsi bisa disimpan bersama playbook di Git tanpa khawatir kebocoran.

Bash
# Buat encrypted file
ansible-vault create secrets.yml

# Edit encrypted file
ansible-vault edit secrets.yml

# Encrypt file yang sudah ada
ansible-vault encrypt group_vars/production/secrets.yml

# Decrypt file
ansible-vault decrypt secrets.yml

# View encrypted file (tanpa decrypt)
ansible-vault view secrets.yml

# Change vault password
ansible-vault rekey secrets.yml

# Jalankan playbook dengan vault password
ansible-playbook site.yml --ask-vault-pass

# Jalankan dengan vault password file
ansible-playbook site.yml --vault-password-file ~/.vault_pass
YAML (Encrypted)
# group_vars/production/secrets.yml (encrypted)
$ANSIBLE_VAULT;1.1;AES256
38326665383036346463356266363964323531306363656238393539383232323838376537656439
...

# Referensi di playbook
- name: Configure database
  mysql_user:
    name: "{{ app_db_user }}"
    password: "{{ vault_db_password }}"  # Disimpan di file terenkripsi
    priv: "{{ app_db_name }}.*:ALL"

11. Best Practices

Struktur Proyek Ansible

Struktur
ansible-project/
β”œβ”€β”€ ansible.cfg              # Ansible configuration
β”œβ”€β”€ inventory/
β”‚   β”œβ”€β”€ production/
β”‚   β”‚   β”œβ”€β”€ hosts.yaml       # Production hosts
β”‚   β”‚   β”œβ”€β”€ group_vars/
β”‚   β”‚   β”‚   β”œβ”€β”€ all.yaml
β”‚   β”‚   β”‚   β”œβ”€β”€ webservers.yaml
β”‚   β”‚   β”‚   └── dbservers.yaml
β”‚   β”‚   └── host_vars/
β”‚   β”‚       └── web1.yaml
β”‚   └── staging/
β”‚       β”œβ”€β”€ hosts.yaml
β”‚       └── group_vars/
β”œβ”€β”€ roles/
β”‚   β”œβ”€β”€ common/
β”‚   β”œβ”€β”€ nginx/
β”‚   β”œβ”€β”€ mysql/
β”‚   └── app/
β”œβ”€β”€ playbooks/
β”‚   β”œβ”€β”€ site.yaml            # Main playbook
β”‚   β”œβ”€β”€ webservers.yaml
β”‚   β”œβ”€β”€ dbservers.yaml
β”‚   └── deploy.yaml
β”œβ”€β”€ templates/
β”œβ”€β”€ files/
β”œβ”€β”€ requirements.yml         # Galaxy dependencies
└── README.md

Checklist Best Practices

Praktik Rekomendasi
IdempotencyPastikan playbook idempotent β€” jalankan berkali-kali hasilnya sama
TagsGunakan tags untuk menjalankan sebagian playbook
LintJalankan ansible-lint sebelum commit
VaultEnkripsi semua sensitive data dengan Ansible Vault
RolesGunakan roles untuk organisasi playbook yang baik
VersionsPin versi roles di requirements.yml
TestingGunakan --check mode sebelum deploy ke production
DocumentationTambahkan README.md di setiap role
HandlersGunakan handlers untuk restart service (bukan task biasa)
Minimal PrivilegeGunakan become: yes hanya saat diperlukan
⚠️ Peringatan

Jangan pernah menyimpan password atau API keys dalam plaintext di inventory atau playbook. Selalu gunakan Ansible Vault atau external secret management (HashiCorp Vault, AWS Secrets Manager) untuk data sensitif.

12. Quiz Pemahaman

1. Apa arti "agentless" pada Ansible?

2. Apa perbedaan antara "command" dan "shell" module?

3. Apa fungsi Ansible Vault?

4. Apa fungsi "handlers" di Ansible playbook?

5. Apa itu "idempotent" dalam konteks Ansible?

πŸ” Zoom
100%
🎨 Tema