ในโลก 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?
- Consistency (ความสม่ำเสมอ): เซิร์ฟเวอร์ทุกเครื่องมีการตั้งค่าเหมือนกันเป๊ะ ไม่มีปัญหา "เครื่องนี้ทำงานได้ แต่เครื่องนั้นไม่ได้"
- Reproducibility (ทำซ้ำได้): สามารถสร้างเซิร์ฟเวอร์ใหม่ที่เหมือนเดิมทุกประการได้ภายในไม่กี่นาที
- Scalability (ขยายขนาดได้): จะมี 10 หรือ 10,000 เครื่อง ก็ใช้โค้ดชุดเดียวกัน
- Version Control: เก็บ Configuration เป็นโค้ดใน Git ดูประวัติการเปลี่ยนแปลง ย้อนกลับได้
- Documentation: โค้ด CM ทำหน้าที่เป็นเอกสารที่อธิบายว่าระบบตั้งค่ายังไง
- Compliance: ตรวจสอบได้ว่าเซิร์ฟเวอร์ทุกเครื่องเป็นไปตาม Security Policy
เปรียบเทียบเครื่องมือ Configuration Management
| ด้าน | Ansible | Puppet | Chef | Salt |
|---|---|---|---|---|
| ภาษา | YAML | Puppet DSL (Ruby) | Ruby | YAML/Python |
| Architecture | Agentless (SSH) | Agent-based | Agent-based | Agent/Agentless |
| Learning Curve | ง่าย | ปานกลาง | ยาก | ปานกลาง |
| Push/Pull | Push | Pull | Pull | Push/Pull |
| ความนิยม 2026 | สูงที่สุด | ลดลง | ลดลง | ปานกลาง |
| เจ้าของ | Red Hat (IBM) | Puppet Inc | Progress | VMware |
| Enterprise Version | AAP (Ansible Automation Platform) | Puppet Enterprise | Chef Automate | SaltStack 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
--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 }}"
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
ฟีเจอร์หลัก
- Dashboard: ดูสถานะ Job ทั้งหมดในหน้าเดียว
- RBAC: กำหนดสิทธิ์ว่าใครรัน Playbook ไหนได้
- Scheduling: ตั้งเวลารัน Playbook อัตโนมัติ
- Notifications: แจ้งเตือนผ่าน Slack, Email, Webhook
- Inventory Sync: Sync inventory จาก Cloud Provider อัตโนมัติ
- Credential Management: จัดเก็บ Credentials อย่างปลอดภัย
- REST API: เรียกใช้ Ansible ผ่าน API ได้
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 — เมื่อไหร่ใช้อะไร
| ด้าน | Ansible | Terraform |
|---|---|---|
| หน้าที่หลัก | Configuration Management | Infrastructure Provisioning |
| จัดการอะไร | Software, Config, Services ภายในเครื่อง | สร้าง/ลบ Infrastructure (VM, Network, Storage) |
| State | Stateless (ไม่มี state file) | Stateful (มี terraform.tfstate) |
| Approach | Procedural (ทำตามลำดับ) | Declarative (ประกาศสถานะปลายทาง) |
| ภาษา | YAML | HCL |
| ตัวอย่างการใช้ | ติดตั้ง Nginx, ตั้งค่า Firewall, Deploy App | สร้าง EC2, VPC, RDS, S3 |
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
เคล็ดลับสำคัญ
- ใช้ Tags: ทุก Task ควรมี Tags เพื่อให้รันเฉพาะส่วนที่ต้องการได้
- ใช้ Handlers: แทนที่จะ Restart Service หลังทุก Config Change ให้ใช้ Handlers ที่จะ Restart เฉพาะตอนมีการเปลี่ยนแปลงจริง
- ใช้ check mode: รัน
--checkก่อนเสมอเพื่อดูว่าจะเกิดอะไรขึ้น - เขียน Task ให้ Idempotent: ใช้ Modules ของ Ansible แทน
commandหรือshellเมื่อเป็นไปได้ เพราะ Modules ถูกออกแบบมาให้ Idempotent - ใช้ Vault: อย่าเก็บ Password หรือ API Key เป็น Plain Text ใน Git
- ทดสอบด้วย Molecule: ใช้ Molecule สำหรับ Testing Roles อัตโนมัติ
- เขียน name ที่ชัดเจน: ทุก Task ต้องมี name ที่อธิบายว่าทำอะไร เป็นภาษาที่คนอ่านเข้าใจ
- แยก Environment: ใช้ Inventory แยกสำหรับ Production, Staging, Development
สรุป
Ansible เป็นเครื่องมือที่ทรงพลังและเรียนรู้ง่ายที่สุดในกลุ่ม Configuration Management Tools ด้วยความเป็น Agentless ใช้ YAML ที่อ่านง่าย และมี Modules มากมายพร้อมใช้งาน ทำให้เป็นตัวเลือกแรกสำหรับ DevOps Engineer ที่ต้องการ Automate Infrastructure
เริ่มต้นจากการเขียน Ad-hoc Commands ง่ายๆ แล้วค่อยขยับไปเขียน Playbook จากนั้นจัดระเบียบเป็น Roles เรียนรู้ Vault สำหรับจัดการ Secrets และ Integrate กับ CI/CD Pipeline เพื่อให้การ Deployment เป็นอัตโนมัติอย่างสมบูรณ์ Ansible จะเปลี่ยนวิธีการทำงานของคุณไปตลอด ขอให้สนุกกับการเรียนรู้ครับ!
