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_tomenjalankan task di host yang ditentukan sambil mempertahankan konteks (variabel,inventory_hostname) dari host target — ideal untuk koordinasi antar-server.local_actionadalah singkatan daridelegate_to: localhost— untuk logging, notifikasi, atau operasi lain di control node.run_oncememastikan 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-balanceradalah pola klasik untuk rolling deploy zero-downtime.delegate_facts: truemenyimpan facts yang dikumpulkan di host yang menjalankan task, bukan host target — berguna saathostvarsperlu diisi sebelum digunakan.