Scheduled Task

Scheduled Task #

Tidak semua otomasi perlu dipicu oleh manusia atau oleh push kode. Backup database harus berjalan setiap malam. Security patch harus diterapkan setiap minggu. Certificate yang mendekati kadaluarsa harus diperbarui otomatis. Drift detection harus berjalan setiap hari. Semua ini adalah scheduled task — otomasi yang berjalan berdasarkan jadwal, tanpa intervensi manual. Artikel ini membahas cara menjadwalkan eksekusi Ansible secara andal.

Cron: Pendekatan Paling Sederhana #

Cara paling sederhana adalah menambahkan cronjob yang menjalankan ansible-playbook di control node:

# playbooks/setup-control-node.yml
- name: Setup scheduled Ansible jobs di control node
  hosts: localhost
  tasks:
    - name: Jadwalkan backup database harian
      cron:
        name: "Ansible — backup database"
        minute: "0"
        hour: "2"
        job: >
          /usr/bin/ansible-playbook
          -i /opt/ansible/inventory/production/
          /opt/ansible/playbooks/backup-database.yml
          --vault-password-file /opt/ansible/.vault_pass
          >> /var/log/ansible/backup.log 2>&1          
        state: present
        user: ansible

    - name: Jadwalkan patch management mingguan
      cron:
        name: "Ansible — weekly patch"
        minute: "0"
        hour: "3"
        weekday: "0"          # Minggu dini hari
        job: >
          /usr/bin/ansible-playbook
          -i /opt/ansible/inventory/production/
          /opt/ansible/playbooks/patch-management.yml
          --vault-password-file /opt/ansible/.vault_pass
          >> /var/log/ansible/patch.log 2>&1          
        state: present
        user: ansible

    - name: Jadwalkan drift detection harian
      cron:
        name: "Ansible — drift detection"
        minute: "30"
        hour: "6"
        job: >
          /usr/bin/ansible-playbook
          -i /opt/ansible/inventory/production/
          /opt/ansible/playbooks/detect-drift.yml
          --check
          >> /var/log/ansible/drift.log 2>&1          
        state: present
        user: ansible

Pola Playbook yang Aman untuk Scheduled Jobs #

Playbook yang dijalankan tanpa pengawasan harus ekstra defensif:

# playbooks/backup-database.yml
---
- name: Backup database otomatis
  hosts: dbservers
  gather_facts: true

  pre_tasks:
    - name: Log awal eksekusi
      local_action:
        module: lineinfile
        path: /var/log/ansible/job-history.log
        line: "{{ ansible_date_time.iso8601 }} START backup-database @ {{ inventory_hostname }}"
        create: true
      run_once: false

    - name: Cek disk space sebelum backup
      assert:
        that:
          - ansible_mounts | selectattr('mount', 'equalto', '/') | map(attribute='size_available') | first > 5368709120
        fail_msg: >
          Disk space tidak cukup untuk backup di {{ inventory_hostname }}!
          Tersedia: {{ (ansible_mounts | selectattr('mount', 'equalto', '/') | map(attribute='size_available') | first / 1073741824) | round(1) }}GB
          Minimal: 5GB          

  tasks:
    - name: Jalankan backup
      command: /opt/scripts/backup-db.sh
      register: backup_result

    - name: Verifikasi backup berhasil
      stat:
        path: "{{ backup_result.stdout | trim }}"
      register: backup_file
      failed_when: not backup_file.stat.exists

  post_tasks:
    - name: Log selesai eksekusi
      local_action:
        module: lineinfile
        path: /var/log/ansible/job-history.log
        line: "{{ ansible_date_time.iso8601 }} END backup-database @ {{ inventory_hostname }} — OK"
        create: true

  handlers:
    - name: Log kegagalan
      local_action:
        module: lineinfile
        path: /var/log/ansible/job-history.log
        line: "{{ ansible_date_time.iso8601 }} FAILED backup-database @ {{ inventory_hostname }}"
        create: true
      listen: job failed

Menghindari Job yang Tumpang Tindih #

Satu masalah umum dengan scheduled task adalah job yang berjalan lebih lama dari interval jadwalnya — job berikutnya mulai sebelum yang sebelumnya selesai, menyebabkan kondisi race:

# Gunakan lockfile untuk mencegah eksekusi tumpang tindih
- name: Cek apakah job sebelumnya masih berjalan
  stat:
    path: /tmp/ansible-backup.lock
  register: lockfile

- name: Hentikan jika job sebelumnya masih berjalan
  fail:
    msg: "Job backup masih berjalan (lockfile ada). Skip eksekusi ini."
  when: lockfile.stat.exists

- name: Buat lockfile
  file:
    path: /tmp/ansible-backup.lock
    state: touch

- name: Jalankan backup
  command: /opt/scripts/backup-db.sh
  register: backup_result

- name: Hapus lockfile setelah selesai
  file:
    path: /tmp/ansible-backup.lock
    state: absent
  when: true    # Selalu hapus, bahkan jika task sebelumnya gagal

Patch Management Otomatis #

Salah satu use case paling umum untuk scheduled task adalah patch management:

# playbooks/patch-management.yml
---
- name: Terapkan security patch secara terjadwal
  hosts: all
  serial: "25%"          # Update 25% server sekaligus (rolling)
  become: true

  pre_tasks:
    - name: Catat state sebelum patch
      command: uname -r
      register: kernel_before
      changed_when: false

  tasks:
    - name: Update apt cache
      apt:
        update_cache: true
      when: ansible_os_family == "Debian"

    - name: Install security updates saja
      apt:
        upgrade: dist
        update_cache: false
        only_upgrade: true
      when: ansible_os_family == "Debian"
      register: patch_result

    - name: Cek apakah reboot diperlukan
      stat:
        path: /var/run/reboot-required
      register: reboot_required

    - name: Reboot jika diperlukan
      reboot:
        reboot_timeout: 300
        post_reboot_delay: 30
        msg: "Reboot untuk menerapkan kernel update"
      when: reboot_required.stat.exists

  post_tasks:
    - name: Catat state setelah patch
      command: uname -r
      register: kernel_after
      changed_when: false

    - name: Laporkan jika kernel berubah
      debug:
        msg: "Kernel diupdate: {{ kernel_before.stdout }} → {{ kernel_after.stdout }}"
      when: kernel_before.stdout != kernel_after.stdout

Notifikasi Hasil Scheduled Job #

Scheduled job yang gagal tanpa notifikasi adalah masalah yang tidak terdeteksi. Tambahkan notifikasi:

# roles/notify/tasks/slack.yml
- name: Kirim notifikasi Slack
  uri:
    url: "{{ slack_webhook_url }}"
    method: POST
    body_format: json
    body:
      text: "{{ slack_message }}"
      attachments:
        - color: "{{ 'good' if job_status == 'success' else 'danger' }}"
          text: "{{ job_detail }}"
  delegate_to: localhost
  run_once: true
  no_log: true
# Di akhir playbook scheduled job
  post_tasks:
    - name: Notifikasi sukses ke Slack
      include_role:
        name: notify
      vars:
        slack_message: "✅ Backup database berhasil"
        job_status: success
        job_detail: "Backup selesai di {{ groups['dbservers'] | length }} server"
      run_once: true

  rescue:
    - name: Notifikasi gagal ke Slack
      include_role:
        name: notify
      vars:
        slack_message: "❌ Backup database GAGAL"
        job_status: failure
        job_detail: "Error di {{ ansible_failed_result.msg }}"
      run_once: true

Ringkasan #

  • Cron + ansible-playbook adalah cara termudah untuk scheduled task — sederhana, tidak butuh infrastruktur tambahan.
  • Selalu arahkan output ke log file di cronjob — >> /var/log/ansible/job.log 2>&1 untuk menyimpan semua output termasuk error.
  • Playbook scheduled task harus defensif: cek prasyarat (disk space, lockfile), log awal dan akhir, handle failure secara eksplisit.
  • Gunakan lockfile untuk mencegah job yang sama berjalan bersamaan saat eksekusi sebelumnya belum selesai.
  • Gunakan serial: untuk patch management — jangan update semua server sekaligus, lakukan bertahap untuk menghindari downtime total.
  • Notifikasi otomatis untuk hasil scheduled job — kegagalan yang tidak terdeteksi bisa berbahaya.

← Sebelumnya: CI/CD Integration   Berikutnya: Error Handling →

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