Ansible service Module: Start Stop Restart Services

Ansible service module ใช้จัดการ system daemon บน Linux ผ่าน init system ที่หลากหลาย ไม่ว่าจะเป็น systemd, SysV init, หรือ Upstart โดยไม่ต้องรู้ว่าเซิร์ฟเวอร์ใช้ init system อะไร module จะจัดการให้อัตโนมัติ ทำให้ Playbook ทำงานได้ข้ามหลาย distro

บทความนี้ครอบคลุม syntax พื้นฐาน, state ทั้งหมดที่รองรับ, การตั้งค่า enabled/disabled สำหรับ auto-start, ความแตกต่างระหว่าง reload กับ restart, การใช้ร่วมกับ loop และ handler และ pattern สำหรับ deploy พร้อม enable daemon แบบ idempotent

service Module พื้นฐาน

service module มี parameters หลักสองตัวที่ต้องรู้จัก คือ name (ชื่อ daemon) และ state (สถานะที่ต้องการ) ส่วน enabled ใช้ควบคุม auto-start เมื่อ boot

---
- name: Basic service module usage
  hosts: all
  become: true
  tasks:
    - name: Start nginx
      service:
        name: nginx
        state: started

    - name: Stop mysql
      service:
        name: mysql
        state: stopped

    - name: Restart redis
      service:
        name: redis
        state: restarted

    - name: Reload nginx config (ไม่ drop connections)
      service:
        name: nginx
        state: reloaded

module จะตรวจสอบสถานะปัจจุบันของ daemon ก่อน ถ้า nginx กำลัง started อยู่แล้วและสั่ง state: started จะไม่ทำอะไร — ทำให้ Playbook เป็น idempotent โดยอัตโนมัติ

state ที่รองรับทั้งหมด

service module รองรับ state 4 แบบ แต่ละแบบมีพฤติกรรมต่างกัน ควรเลือกให้ตรงกับสิ่งที่ต้องการทำ

---
- name: service states demo
  hosts: all
  become: true
  tasks:
    # started: เริ่ม daemon ถ้ายังไม่ได้ run (idempotent)
    - name: Ensure nginx is started
      service:
        name: nginx
        state: started

    # stopped: หยุด daemon ถ้ากำลัง run อยู่ (idempotent)
    - name: Ensure nginx is stopped
      service:
        name: nginx
        state: stopped

    # restarted: หยุดแล้วเริ่มใหม่เสมอ (ไม่ idempotent — ทำทุกครั้ง)
    - name: Restart nginx unconditionally
      service:
        name: nginx
        state: restarted

    # reloaded: สั่ง reload config โดยไม่ kill process (graceful)
    # ถ้า daemon ไม่รองรับ reload จะ fallback เป็น restart อัตโนมัติ
    - name: Reload nginx config
      service:
        name: nginx
        state: reloaded

enabled — ตั้งค่า Auto-start เมื่อ Boot

enabled ควบคุมว่า daemon จะ start อัตโนมัติเมื่อ boot หรือไม่ สามารถใช้ร่วมกับ state หรือใช้แยกกันได้ ในหลายกรณีควรตั้งทั้งคู่พร้อมกัน

---
- name: Managing enabled state
  hosts: all
  become: true
  tasks:
    # เริ่ม daemon ทันทีและตั้งให้ auto-start เมื่อ boot
    - name: Start and enable nginx
      service:
        name: nginx
        state: started
        enabled: true

    # หยุด daemon และปิด auto-start
    - name: Stop and disable nginx
      service:
        name: nginx
        state: stopped
        enabled: false

    # เปิด auto-start โดยไม่เปลี่ยน running state ปัจจุบัน
    - name: Enable nginx on boot (without changing current state)
      service:
        name: nginx
        enabled: true

    # pattern มาตรฐานสำหรับ install + enable daemon
    - name: Install and enable sshd
      service:
        name: sshd
        state: started
        enabled: true

ข้อควรระวัง: enabled: true เพียงอย่างเดียวไม่ได้ start daemon ทันที — ต้องใส่ state: started ด้วยถ้าต้องการให้ทำงานทันที

reload vs restart — ใช้อย่างไรให้ถูก

ความแตกต่างระหว่าง reload และ restart มีผลกระทบต่อ availability ของระบบ ควรเลือกให้เหมาะกับสถานการณ์

---
- name: reload vs restart comparison
  hosts: webservers
  become: true
  tasks:
    # reload: ส่ง SIGHUP — process เดิมยังอยู่, โหลด config ใหม่
    # เหมาะสำหรับ: nginx, apache, haproxy, postfix
    # ข้อดี: ไม่ drop connections ที่กำลังใช้งานอยู่
    - name: Reload nginx after config change (graceful)
      service:
        name: nginx
        state: reloaded

    # restart: หยุดแล้วเริ่มใหม่ — มี downtime สั้น ๆ
    # เหมาะสำหรับ: เปลี่ยน port, เปลี่ยน binary, daemon ที่ไม่รองรับ reload
    - name: Restart mysql after major config change
      service:
        name: mysql
        state: restarted

    # ตรวจ reload support ก่อนสั่ง
    - name: Check if daemon supports reload
      command: systemctl show nginx --property=ExecReload
      register: reload_check
      changed_when: false

    - name: Reload if supported, else restart
      service:
        name: nginx
        state: "{{ 'reloaded' if reload_check.stdout != 'ExecReload=' else 'restarted' }}"

ใช้ loop — จัดการหลาย Daemon พร้อมกัน

เมื่อต้องจัดการหลาย daemon พร้อมกัน ใช้ loop แทนการเขียน task ซ้ำหลาย task ทำให้ Playbook สั้นและดูแลง่าย

---
- name: Manage multiple daemons with loop
  hosts: all
  become: true
  tasks:
    # start + enable หลาย daemon
    - name: Ensure web stack is running
      service:
        name: "{{ item }}"
        state: started
        enabled: true
      loop:
        - nginx
        - mysql
        - redis
        - php8.1-fpm

    # หยุดและปิด daemon ที่ไม่ต้องการ
    - name: Disable unnecessary daemons
      service:
        name: "{{ item }}"
        state: stopped
        enabled: false
      loop:
        - cups
        - bluetooth
        - avahi-daemon

    # ใช้ loop กับ dict เพื่อระบุ state ต่างกันแต่ละ daemon
    - name: Set different states per daemon
      service:
        name: "{{ item.name }}"
        state: "{{ item.state }}"
        enabled: "{{ item.enabled }}"
      loop:
        - { name: nginx, state: started, enabled: true }
        - { name: mysql, state: started, enabled: true }
        - { name: memcached, state: stopped, enabled: false }

ใช้ร่วมกับ Handler — Pattern มาตรฐาน

การใช้ service module ร่วมกับ Handler เป็น pattern ที่พบบ่อยที่สุดใน Ansible โดย Handler จะ restart/reload daemon เฉพาะเมื่อ config มีการเปลี่ยนแปลงจริง ๆ เท่านั้น

---
- name: service with handler pattern
  hosts: webservers
  become: true
  handlers:
    - name: Reload nginx
      service:
        name: nginx
        state: reloaded

    - name: Restart mysql
      service:
        name: mysql
        state: restarted

  tasks:
    - name: Deploy nginx config
      copy:
        src: files/nginx.conf
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: '0644'
      notify: Reload nginx   # trigger handler เฉพาะเมื่อ config เปลี่ยน

    - name: Deploy nginx vhost
      template:
        src: templates/vhost.conf.j2
        dest: /etc/nginx/conf.d/myapp.conf
      notify: Reload nginx   # หลาย task notify handler เดียวกัน — reload แค่ครั้งเดียว

    - name: Deploy mysql config
      copy:
        src: files/my.cnf
        dest: /etc/mysql/my.cnf
      notify: Restart mysql

ข้อดีของ Handler: ถ้า task ทั้งหมดไม่มีการเปลี่ยนแปลง (เช่น config เหมือนเดิม) Handler จะไม่ถูก trigger — ทำให้ Playbook เป็น idempotent อย่างแท้จริงและไม่ restart daemon โดยไม่จำเป็น

register — ตรวจสอบสถานะก่อนดำเนินการ

ใช้ register ร่วมกับ command เพื่อดึงสถานะ daemon มาตรวจสอบหรือตัดสินใจใน task ถัดไป ป้องกันการสั่ง reload daemon ที่ยังไม่ได้ start

---
- name: Check status before action
  hosts: all
  become: true
  tasks:
    # ตรวจสถานะก่อนดำเนินการ
    - name: Check nginx status
      command: systemctl is-active nginx
      register: nginx_status
      changed_when: false
      failed_when: false

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

    # ดำเนินการเฉพาะเมื่อ daemon active
    - name: Reload nginx only if running
      service:
        name: nginx
        state: reloaded
      when: nginx_status.stdout == 'active'

    # ตรวจหลาย daemon พร้อมกัน
    - name: Check all required daemons
      command: systemctl is-active "{{ item }}"
      register: svc_status
      changed_when: false
      failed_when: false
      loop:
        - nginx
        - mysql
        - redis

    - name: Report stopped daemons
      debug:
        msg: "Daemon {{ item.item }} is NOT running!"
      loop: "{{ svc_status.results }}"
      when: item.stdout != 'active'

Pattern: Deploy และ Enable

ตัวอย่าง Playbook ครบวงจร ติดตั้ง package, deploy config, เปิด daemon และตรวจสอบว่า endpoint พร้อมรับ request ก่อนจบ

---
- name: Deploy and enable Nginx
  hosts: webservers
  become: true
  handlers:
    - name: Reload nginx
      service:
        name: nginx
        state: reloaded

  tasks:
    - name: Install nginx
      apt:
        name: nginx
        state: present
        update_cache: true

    - name: Deploy nginx configuration
      template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: '0644'
        validate: 'nginx -t -c %s'   # ตรวจ syntax ก่อน deploy
      notify: Reload nginx

    - name: Deploy virtual host
      template:
        src: templates/vhost.conf.j2
        dest: /etc/nginx/conf.d/{{ app_name }}.conf
      notify: Reload nginx

    - name: Start and enable nginx
      service:
        name: nginx
        state: started
        enabled: true

    - name: Verify nginx is responding
      uri:
        url: "http://{{ inventory_hostname }}"
        status_code: 200
      register: health_check
      retries: 3
      delay: 5
      until: health_check.status == 200

สรุป

service module เป็นเครื่องมือหลักสำหรับจัดการ system daemon ใน Ansible รองรับ state ครบทั้ง started, stopped, restarted, reloaded และ parameter enabled สำหรับ auto-start ควรใช้ reload แทน restart เมื่อทำได้ เพื่อไม่ให้กระทบ connections ที่กำลังใช้งาน

Pattern ที่ควรจำ: ใช้ state: started + enabled: true คู่กันเสมอเมื่อ deploy daemon ใหม่, ใช้ Handler สำหรับ reload/restart เพื่อให้ทำงานเฉพาะเมื่อ config จริง ๆ เปลี่ยน และใช้ loop เมื่อต้องจัดการหลาย daemon แทนการเขียน task ซ้ำ ทั้งหมดนี้ทำให้ Playbook เป็น idempotent และดูแลง่ายในระยะยาว