Home > Blog > tech

Ansible คืออะไร? สอน Configuration Management และ IT Automation สำหรับ DevOps 2026

ansible configuration management guide
Ansible Configuration Management Guide 2026
2026-04-08 | tech | 3500 words

ในโลก DevOps ที่เซิร์ฟเวอร์มีจำนวนหลายสิบ หลายร้อย หรือหลายพันเครื่อง การตั้งค่าและบำรุงรักษาเซิร์ฟเวอร์ด้วยมือทีละเครื่องเป็นสิ่งที่เป็นไปไม่ได้ นี่คือเหตุผลที่ Configuration Management และ IT Automation กลายเป็นทักษะที่ DevOps Engineer ทุกคนต้องมี

Ansible เป็นเครื่องมือ Configuration Management และ IT Automation ที่ได้รับความนิยมมากที่สุดในปี 2026 ด้วยความเรียบง่ายของภาษา YAML การทำงานแบบ Agentless ผ่าน SSH และ Community ที่ใหญ่โต บทความนี้จะสอน Ansible ตั้งแต่พื้นฐานจนถึงการใช้งานจริงในระดับ Production

Configuration Management คืออะไร?

Configuration Management (CM) คือกระบวนการจัดการและรักษาสถานะของระบบ Infrastructure ให้เป็นไปตามที่กำหนดไว้อย่างสม่ำเสมอและเป็นอัตโนมัติ แทนที่จะ SSH เข้าไปในเซิร์ฟเวอร์แล้วพิมพ์คำสั่งด้วยมือ เราเขียนโค้ดที่อธิบายว่าเซิร์ฟเวอร์ควรมีสถานะอย่างไร แล้วให้ระบบจัดการให้อัตโนมัติ

ทำไมต้อง Configuration Management?

เปรียบเทียบเครื่องมือ Configuration Management

ด้านAnsiblePuppetChefSalt
ภาษาYAMLPuppet DSL (Ruby)RubyYAML/Python
ArchitectureAgentless (SSH)Agent-basedAgent-basedAgent/Agentless
Learning Curveง่ายปานกลางยากปานกลาง
Push/PullPushPullPullPush/Pull
ความนิยม 2026สูงที่สุดลดลงลดลงปานกลาง
เจ้าของRed Hat (IBM)Puppet IncProgressVMware
Enterprise VersionAAP (Ansible Automation Platform)Puppet EnterpriseChef AutomateSaltStack Enterprise

ทำไมต้อง Ansible?

Ansible โดดเด่นด้วยคุณสมบัติหลักหลายประการที่ทำให้เป็นตัวเลือกอันดับหนึ่ง

Agentless — ไม่ต้องติดตั้งอะไรบนเซิร์ฟเวอร์ปลายทาง

Ansible ใช้ SSH ในการเชื่อมต่อและรันคำสั่งบนเซิร์ฟเวอร์ปลายทาง ไม่ต้องติดตั้ง Agent หรือ Daemon ใดๆ แค่เครื่องปลายทางเปิด SSH และมี Python ติดตั้งอยู่ก็ใช้งานได้ทันที ซึ่งแตกต่างจาก Puppet และ Chef ที่ต้องติดตั้ง Agent บนทุกเครื่อง

YAML — อ่านง่าย เขียนง่าย

Ansible ใช้ YAML เป็นภาษาหลักในการเขียน Playbook ซึ่งเป็นภาษาที่อ่านง่ายมาก แม้คนที่ไม่ใช่โปรแกรมเมอร์ก็สามารถเข้าใจได้ ไม่ต้องเรียนรู้ภาษาเฉพาะทางอย่าง Puppet DSL หรือ Ruby

Idempotent — รันซ้ำได้ไม่กระทบ

หลักการ Idempotent หมายความว่า ไม่ว่าจะรัน Playbook กี่ครั้งก็ได้ผลลัพธ์เหมือนกัน ถ้าเซิร์ฟเวอร์อยู่ในสถานะที่ต้องการแล้ว Ansible จะไม่ทำอะไรเพิ่ม ทำให้ปลอดภัยในการรันซ้ำ

ติดตั้ง Ansible

# Ubuntu/Debian
sudo apt update
sudo apt install -y ansible

# หรือติดตั้งผ่าน pip (แนะนำ — ได้เวอร์ชันล่าสุด)
pip3 install ansible

# macOS
brew install ansible

# ตรวจสอบเวอร์ชัน
ansible --version

# ตรวจสอบว่าเชื่อมต่อเซิร์ฟเวอร์ได้
ansible all -m ping -i inventory.ini

Inventory — กำหนดเซิร์ฟเวอร์

Inventory คือไฟล์ที่บอก Ansible ว่าต้องจัดการเซิร์ฟเวอร์ไหนบ้าง แบ่งเป็นกลุ่มได้ตามต้องการ

Static Inventory (INI format)

# inventory.ini
[webservers]
web1.example.com ansible_host=192.168.1.10
web2.example.com ansible_host=192.168.1.11
web3.example.com ansible_host=192.168.1.12

[dbservers]
db1.example.com ansible_host=192.168.1.20
db2.example.com ansible_host=192.168.1.21

[loadbalancers]
lb1.example.com ansible_host=192.168.1.5

# กลุ่มรวม
[production:children]
webservers
dbservers
loadbalancers

# ตัวแปรของกลุ่ม
[webservers:vars]
ansible_user=deploy
ansible_port=22
http_port=80

[dbservers:vars]
ansible_user=dbadmin
db_port=5432

Static Inventory (YAML format)

# inventory.yml
all:
  children:
    webservers:
      hosts:
        web1.example.com:
          ansible_host: 192.168.1.10
          http_port: 80
        web2.example.com:
          ansible_host: 192.168.1.11
          http_port: 8080
      vars:
        ansible_user: deploy
        nginx_version: "1.24"

    dbservers:
      hosts:
        db1.example.com:
          ansible_host: 192.168.1.20
          db_role: primary
        db2.example.com:
          ansible_host: 192.168.1.21
          db_role: replica
      vars:
        ansible_user: dbadmin
        postgresql_version: "16"

Dynamic Inventory

สำหรับ Cloud Environment ที่เซิร์ฟเวอร์เปลี่ยนแปลงตลอดเวลา Dynamic Inventory จะ Query ข้อมูลเซิร์ฟเวอร์จาก Cloud Provider โดยอัตโนมัติ

# aws_ec2.yml — Dynamic Inventory สำหรับ AWS
plugin: amazon.aws.aws_ec2
regions:
  - ap-southeast-1
filters:
  tag:Environment: production
  instance-state-name: running
keyed_groups:
  - key: tags.Role
    prefix: role
  - key: placement.availability_zone
    prefix: az
hostnames:
  - private-ip-address
compose:
  ansible_host: private_ip_address

Ad-hoc Commands — คำสั่งด่วน

Ad-hoc Commands เป็นวิธีรันคำสั่งเดี่ยวๆ บนเซิร์ฟเวอร์หลายเครื่องพร้อมกัน เหมาะสำหรับงานเร่งด่วนที่ไม่ต้องเขียน Playbook

# Ping ทุกเครื่อง
ansible all -m ping -i inventory.ini

# รันคำสั่ง Shell
ansible webservers -m shell -a "uptime" -i inventory.ini
ansible dbservers -m shell -a "df -h" -i inventory.ini
ansible all -m shell -a "free -m" -i inventory.ini

# จัดการ Package
ansible webservers -m apt -a "name=nginx state=present" -i inventory.ini --become
ansible webservers -m apt -a "name=nginx state=latest" -i inventory.ini --become

# จัดการ Service
ansible webservers -m service -a "name=nginx state=started enabled=yes" -i inventory.ini --become

# Copy ไฟล์
ansible webservers -m copy -a "src=/local/file.conf dest=/etc/nginx/conf.d/app.conf" -i inventory.ini --become

# ดูข้อมูลเซิร์ฟเวอร์ (Facts)
ansible web1.example.com -m setup -i inventory.ini
ansible web1.example.com -m setup -a "filter=ansible_distribution*" -i inventory.ini

# จัดการ User
ansible all -m user -a "name=deploy state=present groups=sudo" -i inventory.ini --become
Flag สำคัญ: --become หรือ -b คือการรันด้วย sudo, -i ระบุ inventory file, -m ระบุ module, -a ระบุ arguments

Playbook — หัวใจของ Ansible

Playbook คือไฟล์ YAML ที่อธิบายชุดของ Tasks ที่ต้องทำบนเซิร์ฟเวอร์ เปรียบเสมือน "คู่มือการปฏิบัติงาน" ที่เขียนเป็นโค้ด

โครงสร้าง Playbook พื้นฐาน

# setup-webserver.yml
---
- name: ตั้งค่า Web Server
  hosts: webservers
  become: yes              # รันด้วย sudo
  vars:
    http_port: 80
    domain: example.com

  tasks:
    - name: อัปเดต apt cache
      apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: ติดตั้ง Nginx
      apt:
        name: nginx
        state: present

    - name: ติดตั้ง Package เพิ่มเติม
      apt:
        name:
          - curl
          - vim
          - htop
          - ufw
        state: present

    - name: คัดลอก Nginx Config
      template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/sites-available/default
        owner: root
        group: root
        mode: "0644"
      notify: restart nginx

    - name: เปิดใช้งาน Firewall
      ufw:
        rule: allow
        port: "{{ http_port }}"
        proto: tcp

    - name: เริ่ม Nginx Service
      service:
        name: nginx
        state: started
        enabled: yes

  handlers:
    - name: restart nginx
      service:
        name: nginx
        state: restarted
# รัน Playbook
ansible-playbook -i inventory.ini setup-webserver.yml

# รันแบบ Dry Run (ทดสอบโดยไม่เปลี่ยนแปลงจริง)
ansible-playbook -i inventory.ini setup-webserver.yml --check

# รันแบบ Verbose (ดูรายละเอียด)
ansible-playbook -i inventory.ini setup-webserver.yml -v    # verbose
ansible-playbook -i inventory.ini setup-webserver.yml -vvv  # very verbose

# รันเฉพาะบาง Task (ใช้ tags)
ansible-playbook -i inventory.ini setup-webserver.yml --tags "nginx,firewall"

# จำกัดเฉพาะบางเครื่อง
ansible-playbook -i inventory.ini setup-webserver.yml --limit web1.example.com

Modules ที่ใช้บ่อย

Ansible มี Modules มากกว่า 3,000 ตัว ที่ครอบคลุมทุกด้านของ System Administration นี่คือ Modules ที่ใช้บ่อยที่สุด

Package Management

# apt — สำหรับ Debian/Ubuntu
- name: ติดตั้ง Package
  apt:
    name: nginx
    state: present      # present=ติดตั้ง, absent=ลบ, latest=อัปเดต
    update_cache: yes

# yum — สำหรับ RHEL/CentOS
- name: ติดตั้ง Package
  yum:
    name: httpd
    state: present

# pip — Python packages
- name: ติดตั้ง Python packages
  pip:
    name:
      - flask
      - gunicorn
      - redis
    virtualenv: /opt/myapp/venv

File Management

# copy — คัดลอกไฟล์จาก Control Node
- name: คัดลอก Config File
  copy:
    src: files/app.conf
    dest: /etc/myapp/app.conf
    owner: root
    group: root
    mode: "0644"
    backup: yes          # สำรองไฟล์เก่าก่อนเขียนทับ

# template — ใช้ Jinja2 template
- name: สร้าง Config จาก Template
  template:
    src: templates/nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    validate: "nginx -t -c %s"    # ตรวจสอบ syntax ก่อน deploy

# file — จัดการ file/directory
- name: สร้าง Directory
  file:
    path: /opt/myapp/logs
    state: directory
    owner: deploy
    group: deploy
    mode: "0755"

- name: สร้าง Symbolic Link
  file:
    src: /opt/myapp/current
    dest: /var/www/html
    state: link

# lineinfile — แก้ไขบรรทัดในไฟล์
- name: ตั้งค่า SSH
  lineinfile:
    path: /etc/ssh/sshd_config
    regexp: "^PermitRootLogin"
    line: "PermitRootLogin no"
    state: present
  notify: restart sshd

Service & User Management

# service — จัดการ Systemd services
- name: เริ่ม Service
  service:
    name: nginx
    state: started       # started, stopped, restarted, reloaded
    enabled: yes         # เปิดเมื่อ Boot

# systemd — สำหรับ systemd โดยเฉพาะ
- name: Reload systemd daemon
  systemd:
    daemon_reload: yes

- name: Enable and start service
  systemd:
    name: myapp
    state: started
    enabled: yes

# user — จัดการ User
- name: สร้าง User
  user:
    name: deploy
    groups: sudo,docker
    shell: /bin/bash
    create_home: yes
    generate_ssh_key: yes
    ssh_key_bits: 4096

# authorized_key — จัดการ SSH Keys
- name: เพิ่ม SSH Key
  authorized_key:
    user: deploy
    key: "{{ lookup('file', 'files/deploy_key.pub') }}"
    state: present

Docker Module

# docker_container — จัดการ Docker containers
- name: รัน Redis Container
  docker_container:
    name: redis
    image: redis:7-alpine
    state: started
    restart_policy: always
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

# docker_image — จัดการ Docker images
- name: Pull Docker Image
  docker_image:
    name: nginx
    tag: latest
    source: pull

# docker_compose — ใช้ Docker Compose
- name: Deploy with Docker Compose
  community.docker.docker_compose_v2:
    project_src: /opt/myapp
    state: present

Roles — จัดระเบียบ Playbook

เมื่อ Playbook ใหญ่ขึ้น จำเป็นต้องแบ่งออกเป็น Roles เพื่อให้จัดการง่ายและนำกลับมาใช้ซ้ำได้ Role คือชุดของ Tasks, Handlers, Variables, Templates และ Files ที่จัดเป็นโครงสร้าง Directory มาตรฐาน

โครงสร้าง Role

roles/
├── nginx/
│   ├── tasks/
│   │   ├── main.yml          # Task หลัก
│   │   ├── install.yml       # Tasks สำหรับติดตั้ง
│   │   └── configure.yml     # Tasks สำหรับตั้งค่า
│   ├── handlers/
│   │   └── main.yml          # Handlers (restart, reload)
│   ├── templates/
│   │   ├── nginx.conf.j2     # Jinja2 templates
│   │   └── vhost.conf.j2
│   ├── files/
│   │   └── ssl-params.conf   # Static files
│   ├── vars/
│   │   └── main.yml          # Role variables
│   ├── defaults/
│   │   └── main.yml          # Default variables (override ได้)
│   ├── meta/
│   │   └── main.yml          # Dependencies
│   └── README.md
├── postgresql/
│   ├── tasks/main.yml
│   ├── handlers/main.yml
│   ├── templates/
│   │   ├── postgresql.conf.j2
│   │   └── pg_hba.conf.j2
│   └── defaults/main.yml
└── common/
    ├── tasks/main.yml
    └── handlers/main.yml

สร้าง Role: Nginx

# roles/nginx/defaults/main.yml
---
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
nginx_server_name: "_"
nginx_root: /var/www/html
nginx_http_port: 80

# roles/nginx/tasks/main.yml
---
- name: ติดตั้ง Nginx
  apt:
    name: nginx
    state: present
    update_cache: yes
  tags: [nginx, install]

- name: คัดลอก nginx.conf
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: "0644"
    validate: "nginx -t -c %s"
  notify: restart nginx
  tags: [nginx, config]

- name: สร้าง Document Root
  file:
    path: "{{ nginx_root }}"
    state: directory
    owner: www-data
    group: www-data
    mode: "0755"

- name: เปิดใช้งาน Nginx
  service:
    name: nginx
    state: started
    enabled: yes
  tags: [nginx, service]

# roles/nginx/handlers/main.yml
---
- name: restart nginx
  service:
    name: nginx
    state: restarted

- name: reload nginx
  service:
    name: nginx
    state: reloaded

# roles/nginx/templates/nginx.conf.j2
worker_processes {{ nginx_worker_processes }};

events {
    worker_connections {{ nginx_worker_connections }};
}

http {
    sendfile on;
    keepalive_timeout {{ nginx_keepalive_timeout }};

    server {
        listen {{ nginx_http_port }};
        server_name {{ nginx_server_name }};
        root {{ nginx_root }};

        location / {
            try_files $uri $uri/ =404;
        }
    }
}

ใช้ Role ใน Playbook

# site.yml — Main Playbook
---
- name: ตั้งค่าพื้นฐาน (ทุกเครื่อง)
  hosts: all
  become: yes
  roles:
    - common

- name: ตั้งค่า Web Servers
  hosts: webservers
  become: yes
  roles:
    - role: nginx
      vars:
        nginx_server_name: "mysite.com"
        nginx_http_port: 80
    - role: certbot
      when: use_ssl | default(false)

- name: ตั้งค่า Database Servers
  hosts: dbservers
  become: yes
  roles:
    - role: postgresql
      vars:
        postgresql_version: "16"
        postgresql_max_connections: 200

Ansible Galaxy — Roles สำเร็จรูป

Ansible Galaxy เป็นที่เก็บ Roles และ Collections สำเร็จรูปจากชุมชน ใช้ได้ฟรี ทำให้ไม่ต้องเขียนทุกอย่างเอง

# ค้นหา Role
ansible-galaxy search nginx
ansible-galaxy search postgresql

# ติดตั้ง Role จาก Galaxy
ansible-galaxy install geerlingguy.nginx
ansible-galaxy install geerlingguy.postgresql
ansible-galaxy install geerlingguy.docker

# ติดตั้งจาก requirements.yml
# requirements.yml
---
roles:
  - name: geerlingguy.nginx
    version: "3.2.0"
  - name: geerlingguy.postgresql
    version: "3.5.0"
  - name: geerlingguy.docker
    version: "7.1.0"

collections:
  - name: community.docker
    version: "3.8.0"
  - name: amazon.aws
    version: "7.0.0"

# ติดตั้งทั้งหมด
ansible-galaxy install -r requirements.yml
ansible-galaxy collection install -r requirements.yml

Variables — ตัวแปร

Ansible มีระบบตัวแปรที่ยืดหยุ่นมาก สามารถกำหนดได้หลายระดับ โดยมีลำดับความสำคัญ (Precedence) ที่ชัดเจน

ลำดับความสำคัญของตัวแปร (สูง → ต่ำ)

# 1. Extra vars (command line) — สูงสุด
ansible-playbook site.yml -e "http_port=8080"

# 2. Task vars (set_fact, register)
# 3. Block vars
# 4. Role vars (roles/x/vars/main.yml)
# 5. Play vars
# 6. Host vars (host_vars/hostname.yml)
# 7. Group vars (group_vars/groupname.yml)
# 8. Role defaults (roles/x/defaults/main.yml) — ต่ำสุด

host_vars และ group_vars

# โครงสร้าง Directory
inventory/
├── production/
│   ├── hosts.yml
│   ├── group_vars/
│   │   ├── all.yml           # ตัวแปรสำหรับทุกเครื่อง
│   │   ├── webservers.yml    # ตัวแปรสำหรับกลุ่ม webservers
│   │   └── dbservers.yml     # ตัวแปรสำหรับกลุ่ม dbservers
│   └── host_vars/
│       ├── web1.example.com.yml   # ตัวแปรเฉพาะเครื่อง
│       └── db1.example.com.yml
└── staging/
    ├── hosts.yml
    ├── group_vars/
    └── host_vars/

# group_vars/all.yml
---
ntp_server: time.google.com
dns_servers:
  - 8.8.8.8
  - 8.8.4.4
timezone: Asia/Bangkok
admin_email: admin@example.com

# group_vars/webservers.yml
---
nginx_worker_processes: 4
nginx_ssl_enabled: true
app_port: 3000

# host_vars/web1.example.com.yml
---
nginx_worker_processes: 8    # override สำหรับเครื่องนี้
ssl_cert_path: /etc/ssl/web1.crt

Templates ด้วย Jinja2

Jinja2 Templates ทำให้สร้าง Configuration Files แบบ Dynamic ได้ โดยใส่ตัวแปร เงื่อนไข และ Loops ลงในไฟล์ Template

# templates/nginx-vhost.conf.j2
server {
    listen {{ nginx_http_port | default(80) }};
    server_name {{ nginx_server_name }};

{% if nginx_ssl_enabled %}
    listen 443 ssl;
    ssl_certificate {{ ssl_cert_path }};
    ssl_certificate_key {{ ssl_key_path }};

    # Redirect HTTP to HTTPS
    if ($scheme = http) {
        return 301 https://$host$request_uri;
    }
{% endif %}

    root {{ nginx_root }};
    index index.html;

    # Upstream backends
{% if app_backends is defined %}
    location /api {
        proxy_pass http://backend;
    }
{% endif %}

    # Access log
    access_log /var/log/nginx/{{ nginx_server_name }}.access.log;
    error_log /var/log/nginx/{{ nginx_server_name }}.error.log;
}

{% if app_backends is defined %}
upstream backend {
{% for backend in app_backends %}
    server {{ backend.host }}:{{ backend.port }} weight={{ backend.weight | default(1) }};
{% endfor %}
}
{% endif %}

Conditionals และ Loops

when — เงื่อนไข

# รันเฉพาะ Ubuntu
- name: ติดตั้ง Nginx (Ubuntu)
  apt:
    name: nginx
    state: present
  when: ansible_distribution == "Ubuntu"

# รันเฉพาะ CentOS/RHEL
- name: ติดตั้ง Nginx (CentOS)
  yum:
    name: nginx
    state: present
  when: ansible_distribution == "CentOS" or ansible_distribution == "RedHat"

# เงื่อนไขจาก Variable
- name: Enable SSL
  template:
    src: ssl.conf.j2
    dest: /etc/nginx/conf.d/ssl.conf
  when:
    - nginx_ssl_enabled | bool
    - ssl_cert_path is defined

# เงื่อนไขจากผลลัพธ์ Task ก่อนหน้า
- name: ตรวจสอบว่า Docker ติดตั้งแล้ว
  command: docker --version
  register: docker_check
  ignore_errors: yes

- name: ติดตั้ง Docker
  include_role:
    name: docker
  when: docker_check.rc != 0

loop — ทำซ้ำ

# ติดตั้งหลาย Packages
- name: ติดตั้ง Packages ที่จำเป็น
  apt:
    name: "{{ item }}"
    state: present
  loop:
    - nginx
    - postgresql-client
    - redis-tools
    - htop
    - curl

# สร้างหลาย Users
- name: สร้าง Users
  user:
    name: "{{ item.name }}"
    groups: "{{ item.groups }}"
    shell: "{{ item.shell | default('/bin/bash') }}"
  loop:
    - { name: "deploy", groups: "sudo,docker" }
    - { name: "monitor", groups: "docker" }
    - { name: "backup", groups: "backup" }

# Loop กับ dict
- name: ตั้งค่า Sysctl
  sysctl:
    name: "{{ item.key }}"
    value: "{{ item.value }}"
    state: present
    reload: yes
  loop: "{{ sysctl_settings | dict2items }}"
  vars:
    sysctl_settings:
      net.core.somaxconn: "65535"
      vm.swappiness: "10"
      fs.file-max: "2097152"

Error Handling

block/rescue/always

- name: Deploy Application with Error Handling
  block:
    - name: ดึง Code ใหม่
      git:
        repo: "https://github.com/myorg/myapp.git"
        dest: /opt/myapp
        version: main
        force: yes

    - name: ติดตั้ง Dependencies
      pip:
        requirements: /opt/myapp/requirements.txt
        virtualenv: /opt/myapp/venv

    - name: Migrate Database
      command: /opt/myapp/venv/bin/python manage.py migrate
      args:
        chdir: /opt/myapp

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

  rescue:
    - name: Rollback — คืนค่า Code เวอร์ชันก่อน
      command: git checkout HEAD~1
      args:
        chdir: /opt/myapp

    - name: Restart Application (rollback version)
      service:
        name: myapp
        state: restarted

    - name: แจ้งเตือน Slack
      uri:
        url: "https://hooks.slack.com/services/xxx/yyy/zzz"
        method: POST
        body_format: json
        body:
          text: "DEPLOY FAILED on {{ inventory_hostname }} — rolled back!"

  always:
    - name: Cleanup temp files
      file:
        path: /tmp/deploy-cache
        state: absent

    - name: Log deployment result
      lineinfile:
        path: /var/log/deployments.log
        line: "{{ ansible_date_time.iso8601 }} — Deploy {{ 'FAILED' if ansible_failed_task is defined else 'SUCCESS' }}"
        create: yes

ignore_errors และ failed_when

# ignore_errors — ไม่หยุดเมื่อ Error
- name: ตรวจสอบ Service (อาจยังไม่มี)
  command: systemctl status myapp
  register: service_status
  ignore_errors: yes

# failed_when — กำหนดเงื่อนไข Failure เอง
- name: ตรวจสอบ Disk Space
  command: df -h /
  register: disk_check
  failed_when: "'100%' in disk_check.stdout"

# changed_when — กำหนดเมื่อไหร่ถือว่า Changed
- name: ตรวจสอบ Config Syntax
  command: nginx -t
  register: nginx_test
  changed_when: false    # คำสั่งนี้ไม่เปลี่ยนแปลงอะไร

Ansible Vault — จัดการ Secrets

Ansible Vault ใช้เข้ารหัสข้อมูลสำคัญ เช่น รหัสผ่าน API Keys และ Certificates เพื่อให้สามารถเก็บใน Git ได้อย่างปลอดภัย

# สร้างไฟล์ Encrypted
ansible-vault create secrets.yml

# แก้ไขไฟล์ Encrypted
ansible-vault edit secrets.yml

# เข้ารหัสไฟล์ที่มีอยู่
ansible-vault encrypt group_vars/production/vault.yml

# ถอดรหัส
ansible-vault decrypt secrets.yml

# ดูเนื้อหาโดยไม่ถอดรหัสถาวร
ansible-vault view secrets.yml

# เปลี่ยน Password
ansible-vault rekey secrets.yml

# รัน Playbook ที่ใช้ Vault
ansible-playbook site.yml --ask-vault-pass
ansible-playbook site.yml --vault-password-file ~/.vault_pass
# group_vars/production/vault.yml (encrypted)
---
vault_db_password: "SuperSecretPass123!"
vault_api_key: "sk-abcdef1234567890"
vault_ssl_private_key: |
  -----BEGIN PRIVATE KEY-----
  MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC...
  -----END PRIVATE KEY-----

# group_vars/production/vars.yml (ไม่ encrypted — อ้างอิง vault)
---
db_password: "{{ vault_db_password }}"
api_key: "{{ vault_api_key }}"
Best Practice: แยกตัวแปรปกติ (vars.yml) กับตัวแปรลับ (vault.yml) คนละไฟล์ แต่อยู่ใน Directory เดียวกัน ตัวแปรปกติอ้างอิงตัวแปรลับผ่าน prefix vault_

Ansible Tower / AWX — Web UI

Ansible Tower (เวอร์ชัน Enterprise) หรือ AWX (เวอร์ชัน Open Source) คือ Web-based UI สำหรับจัดการ Ansible ที่ช่วยให้ทีมทำงานร่วมกันได้ มีระบบ Role-Based Access Control (RBAC) สามารถ Schedule การรัน Playbook ดู Log ย้อนหลัง และ Integrate กับ CI/CD Pipeline

ฟีเจอร์หลัก

CI/CD Integration

# .github/workflows/deploy.yml — GitHub Actions + Ansible
name: Deploy with Ansible
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Python & Ansible
        run: |
          pip install ansible

      - name: Setup SSH Key
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan -H ${{ secrets.SERVER_IP }} >> ~/.ssh/known_hosts

      - name: Run Ansible Playbook
        run: |
          ansible-playbook -i inventory/production/hosts.yml             deploy.yml             --vault-password-file <(echo "${{ secrets.VAULT_PASSWORD }}")
        env:
          ANSIBLE_HOST_KEY_CHECKING: "false"

ตัวอย่างจริง: ติดตั้ง LAMP Stack

# lamp-stack.yml — ติดตั้ง Linux + Apache + MySQL + PHP
---
- name: LAMP Stack Setup
  hosts: webservers
  become: yes
  vars:
    mysql_root_password: "{{ vault_mysql_root_password }}"
    php_version: "8.3"
    document_root: /var/www/html

  tasks:
    # === Apache ===
    - name: ติดตั้ง Apache
      apt:
        name:
          - apache2
          - libapache2-mod-php{{ php_version }}
        state: present
        update_cache: yes

    - name: เปิด Apache Modules
      apache2_module:
        name: "{{ item }}"
        state: present
      loop: [rewrite, ssl, headers]
      notify: restart apache

    # === PHP ===
    - name: ติดตั้ง PHP และ Extensions
      apt:
        name:
          - "php{{ php_version }}"
          - "php{{ php_version }}-mysql"
          - "php{{ php_version }}-curl"
          - "php{{ php_version }}-gd"
          - "php{{ php_version }}-mbstring"
          - "php{{ php_version }}-xml"
          - "php{{ php_version }}-zip"
        state: present

    - name: ตั้งค่า PHP
      lineinfile:
        path: "/etc/php/{{ php_version }}/apache2/php.ini"
        regexp: "^{{ item.key }}"
        line: "{{ item.key }} = {{ item.value }}"
      loop:
        - { key: "upload_max_filesize", value: "64M" }
        - { key: "post_max_size", value: "64M" }
        - { key: "memory_limit", value: "256M" }
        - { key: "max_execution_time", value: "300" }
      notify: restart apache

    # === MySQL ===
    - name: ติดตั้ง MySQL
      apt:
        name:
          - mysql-server
          - python3-mysqldb
        state: present

    - name: ตั้ง Root Password
      mysql_user:
        name: root
        password: "{{ mysql_root_password }}"
        login_unix_socket: /var/run/mysqld/mysqld.sock

    - name: ลบ Anonymous Users
      mysql_user:
        name: ""
        host_all: yes
        state: absent
        login_user: root
        login_password: "{{ mysql_root_password }}"

    # === Firewall ===
    - name: ตั้งค่า UFW
      ufw:
        rule: allow
        port: "{{ item }}"
        proto: tcp
      loop: ["22", "80", "443"]

    - name: Enable UFW
      ufw:
        state: enabled
        policy: deny

  handlers:
    - name: restart apache
      service:
        name: apache2
        state: restarted

ตัวอย่างจริง: Security Hardening

# security-hardening.yml
---
- name: Security Hardening
  hosts: all
  become: yes
  vars:
    ssh_port: 2222
    allowed_users: [deploy, admin]

  tasks:
    - name: อัปเดต Packages ทั้งหมด
      apt:
        upgrade: dist
        update_cache: yes
        cache_valid_time: 3600

    - name: ติดตั้ง Security Tools
      apt:
        name:
          - fail2ban
          - unattended-upgrades
          - auditd
          - rkhunter
        state: present

    - name: ตั้งค่า SSH Hardening
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "^{{ item.key }}"
        line: "{{ item.key }} {{ item.value }}"
      loop:
        - { key: "Port", value: "{{ ssh_port }}" }
        - { key: "PermitRootLogin", value: "no" }
        - { key: "PasswordAuthentication", value: "no" }
        - { key: "X11Forwarding", value: "no" }
        - { key: "MaxAuthTries", value: "3" }
        - { key: "AllowUsers", value: "{{ allowed_users | join(' ') }}" }
        - { key: "Protocol", value: "2" }
      notify: restart sshd

    - name: ตั้งค่า Fail2ban
      template:
        src: templates/jail.local.j2
        dest: /etc/fail2ban/jail.local
      notify: restart fail2ban

    - name: ปิด Unused Services
      service:
        name: "{{ item }}"
        state: stopped
        enabled: no
      loop: [cups, avahi-daemon, rpcbind]
      ignore_errors: yes

    - name: ตั้งค่า Sysctl สำหรับ Security
      sysctl:
        name: "{{ item.key }}"
        value: "{{ item.value }}"
        state: present
        reload: yes
      loop:
        - { key: "net.ipv4.ip_forward", value: "0" }
        - { key: "net.ipv4.conf.all.rp_filter", value: "1" }
        - { key: "net.ipv4.conf.all.accept_redirects", value: "0" }
        - { key: "net.ipv4.conf.all.send_redirects", value: "0" }
        - { key: "net.ipv4.icmp_echo_ignore_broadcasts", value: "1" }
        - { key: "kernel.randomize_va_space", value: "2" }

  handlers:
    - name: restart sshd
      service:
        name: sshd
        state: restarted

    - name: restart fail2ban
      service:
        name: fail2ban
        state: restarted

Ansible vs Terraform — เมื่อไหร่ใช้อะไร

ด้านAnsibleTerraform
หน้าที่หลักConfiguration ManagementInfrastructure Provisioning
จัดการอะไรSoftware, Config, Services ภายในเครื่องสร้าง/ลบ Infrastructure (VM, Network, Storage)
StateStateless (ไม่มี state file)Stateful (มี terraform.tfstate)
ApproachProcedural (ทำตามลำดับ)Declarative (ประกาศสถานะปลายทาง)
ภาษาYAMLHCL
ตัวอย่างการใช้ติดตั้ง Nginx, ตั้งค่า Firewall, Deploy Appสร้าง EC2, VPC, RDS, S3
ใช้ร่วมกัน: Best Practice คือใช้ Terraform สร้าง Infrastructure (เครื่อง, Network) แล้วใช้ Ansible ตั้งค่า Software บนเครื่องเหล่านั้น ทั้งสองเครื่องมือเสริมกัน ไม่ใช่ทดแทนกัน

Best Practices สำหรับ Ansible

การจัดโครงสร้างโปรเจกต์

# โครงสร้างที่แนะนำ
ansible-project/
├── ansible.cfg              # Ansible configuration
├── requirements.yml         # Galaxy dependencies
├── site.yml                 # Main playbook
├── webservers.yml          # Web server playbook
├── dbservers.yml           # Database playbook
├── inventory/
│   ├── production/
│   │   ├── hosts.yml
│   │   ├── group_vars/
│   │   │   ├── all/
│   │   │   │   ├── vars.yml
│   │   │   │   └── vault.yml
│   │   │   └── webservers.yml
│   │   └── host_vars/
│   └── staging/
│       ├── hosts.yml
│       └── group_vars/
├── roles/
│   ├── common/
│   ├── nginx/
│   ├── postgresql/
│   └── app/
├── files/                  # Global static files
├── templates/              # Global templates
└── filter_plugins/         # Custom filters

ansible.cfg ที่แนะนำ

# ansible.cfg
[defaults]
inventory = inventory/production/hosts.yml
roles_path = roles
remote_user = deploy
host_key_checking = False
retry_files_enabled = False
stdout_callback = yaml
callbacks_enabled = timer, profile_tasks

[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False

[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s

เคล็ดลับสำคัญ

สรุป

Ansible เป็นเครื่องมือที่ทรงพลังและเรียนรู้ง่ายที่สุดในกลุ่ม Configuration Management Tools ด้วยความเป็น Agentless ใช้ YAML ที่อ่านง่าย และมี Modules มากมายพร้อมใช้งาน ทำให้เป็นตัวเลือกแรกสำหรับ DevOps Engineer ที่ต้องการ Automate Infrastructure

เริ่มต้นจากการเขียน Ad-hoc Commands ง่ายๆ แล้วค่อยขยับไปเขียน Playbook จากนั้นจัดระเบียบเป็น Roles เรียนรู้ Vault สำหรับจัดการ Secrets และ Integrate กับ CI/CD Pipeline เพื่อให้การ Deployment เป็นอัตโนมัติอย่างสมบูรณ์ Ansible จะเปลี่ยนวิธีการทำงานของคุณไปตลอด ขอให้สนุกกับการเรียนรู้ครับ!


Back to Blog | iCafe Forex | SiamLanCard | Siam2R