Delegation & Local Action

Delegation & Local Action #

Tidak semua task perlu dijalankan di managed node yang sedang menjadi target play. Kadang kamu perlu mendaftarkan server ke load balancer dari node load balancer itu sendiri, mencatat log deployment di control node, atau menjalankan health check dari mesin yang berbeda. Delegation dan local action adalah mekanisme Ansible untuk mengarahkan eksekusi task ke host yang tepat, terlepas dari siapa yang sedang menjadi target play.

delegate_to: Jalankan Task di Host Lain #

delegate_to mengarahkan eksekusi sebuah task ke host yang ditentukan, bukan ke host yang sedang diproses:

- name: Deploy aplikasi ke server web
  hosts: webservers
  tasks:
    - name: Deploy kode ke server web
      git:
        repo: https://github.com/company/app.git
        dest: /opt/app
        version: "{{ deploy_version }}"
      # Task ini berjalan di setiap webserver (default behavior)

    - name: Keluarkan server dari load balancer sebelum deploy
      uri:
        url: "http://lb-01.internal/api/backend/{{ inventory_hostname }}/disable"
        method: POST
        status_code: 200
      delegate_to: lb-01.internal   # Task ini berjalan di lb-01, bukan di webserver
      # Tapi variabel seperti inventory_hostname masih merujuk ke webserver

    - name: Tambahkan kembali ke load balancer setelah deploy
      uri:
        url: "http://lb-01.internal/api/backend/{{ inventory_hostname }}/enable"
        method: POST
        status_code: 200
      delegate_to: lb-01.internal

Di contoh ini, task deployment berjalan di setiap webserver, tapi task enable/disable load balancer berjalan di lb-01.internal. Namun, inventory_hostname di dalam task tetap merujuk ke webserver yang sedang diproses — inilah yang membuat delegation sangat berguna untuk koordinasi multi-host.


local_action: Jalankan di Control Node #

local_action adalah singkatan dari delegate_to: localhost — menjalankan task di control node tempat Ansible dijalankan:

- name: Deploy ke semua server
  hosts: webservers
  tasks:
    - name: Log awal deployment per server
      local_action:
        module: lineinfile
        path: /var/log/deployments.log
        line: "{{ ansible_date_time.iso8601 }} | Mulai deploy ke {{ inventory_hostname }}"
        create: true
      # Berjalan di control node, bukan di webserver

    - name: Deploy aplikasi
      git:
        repo: https://github.com/company/app.git
        dest: /opt/app
        version: "{{ deploy_version }}"
      # Berjalan di webserver

    - name: Log selesai deployment
      local_action:
        module: lineinfile
        path: /var/log/deployments.log
        line: "{{ ansible_date_time.iso8601 }} | Selesai deploy ke {{ inventory_hostname }}"
        create: true

Sintaks alternatif yang lebih ringkas:

- name: Ambil file dari server ke control node
  fetch:
    src: /var/log/app/error.log
    dest: /tmp/logs/{{ inventory_hostname }}/
  # fetch sudah secara implisit menyimpan hasil di control node

# Atau gunakan sintaks module langsung:
- name: Kirim notifikasi dari control node
  delegate_to: localhost
  uri:
    url: "{{ slack_webhook }}"
    method: POST
    body_format: json
    body:
      text: "Deploy ke {{ inventory_hostname }} selesai"

run_once: Jalankan Hanya Sekali #

run_once memastikan task hanya dieksekusi satu kali, terlepas dari berapa banyak host yang ada di play. Ansible secara default menjalankan task ini di host pertama dalam inventory:

- name: Migrasi database (hanya perlu sekali, bukan di setiap server)
  hosts: appservers
  tasks:
    - name: Jalankan migrasi database
      command: python manage.py migrate
      run_once: true            # Hanya di appservers[0]

    - name: Clear cache aplikasi
      command: python manage.py clear_cache
      run_once: true

    - name: Deploy kode ke semua server
      git:
        repo: https://github.com/company/app.git
        dest: /opt/app
        version: "{{ deploy_version }}"
      # Tanpa run_once — berjalan di semua appservers

Kombinasi run_once dengan delegate_to untuk menjalankan task satu kali di host tertentu:

- name: Update DNS record (hanya sekali, dari control node)
  command: >
    nsupdate -k /etc/bind/keys/update.key
    -v /tmp/dns-update.txt    
  run_once: true
  delegate_to: dns-primary.internal

Pola: Deploy dengan Drain dan Restore Load Balancer #

Menggabungkan delegation untuk pola deployment zero-downtime yang klasik:

- name: Rolling deploy dengan drain load balancer
  hosts: webservers
  serial: 1              # Satu server sekaligus

  tasks:
    - name: Keluarkan dari load balancer
      uri:
        url: "http://haproxy.internal:9000/stats"
        method: POST
        body: "s=be_web/{{ inventory_hostname }}&action=disable"
      delegate_to: haproxy.internal

    - name: Tunggu koneksi aktif habis
      pause:
        seconds: 10

    - name: Deploy kode baru
      git:
        repo: https://github.com/company/app.git
        dest: /opt/app
        version: "{{ deploy_version }}"

    - name: Restart aplikasi
      systemd:
        name: myapp
        state: restarted

    - name: Verifikasi aplikasi berjalan
      uri:
        url: "http://{{ inventory_hostname }}:{{ app_port }}/health"
        status_code: 200
      register: health
      until: health.status == 200
      retries: 10
      delay: 5

    - name: Kembalikan ke load balancer
      uri:
        url: "http://haproxy.internal:9000/stats"
        method: POST
        body: "s=be_web/{{ inventory_hostname }}&action=enable"
      delegate_to: haproxy.internal

delegate_facts: Simpan Facts di Host Lain #

delegate_facts memungkinkan facts yang dikumpulkan dalam task ter-delegasi disimpan untuk host yang menjalankan task tersebut, bukan host target play:

- name: Kumpulkan facts dari database server untuk digunakan di web server
  hosts: webservers
  tasks:
    - name: Ambil IP database server
      setup:
        filter: ansible_default_ipv4
      delegate_to: "{{ groups['dbservers'][0] }}"
      delegate_facts: true    # Simpan facts untuk dbservers[0], bukan webserver

    - name: Gunakan IP database dalam konfigurasi
      template:
        src: app.conf.j2
        dest: /etc/app/app.conf
      vars:
        db_ip: "{{ hostvars[groups['dbservers'][0]]['ansible_default_ipv4']['address'] }}"

Ringkasan #

  • delegate_to menjalankan task di host yang ditentukan sambil mempertahankan konteks (variabel, inventory_hostname) dari host target — ideal untuk koordinasi antar-server.
  • local_action adalah singkatan dari delegate_to: localhost — untuk logging, notifikasi, atau operasi lain di control node.
  • run_once memastikan task hanya dieksekusi satu kali di seluruh play — untuk migrasi database, pembersihan cache, atau registrasi yang tidak perlu diulang per-server.
  • Kombinasi serial: 1 + delegate_to: load-balancer adalah pola klasik untuk rolling deploy zero-downtime.
  • delegate_facts: true menyimpan facts yang dikumpulkan di host yang menjalankan task, bukan host target — berguna saat hostvars perlu diisi sebelum digunakan.

← Sebelumnya: Error Handling   Berikutnya: Performance →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact