
Ansible คืออะไร? ทำไม System Admin ต้องรู้?
Ansible คือ Open-source IT Automation Tool ที่พัฒนาโดย Red Hat ใช้สำหรับ Configuration Management, Application Deployment และ Orchestration ทำให้ System Admin สามารถจัดการ Server หลายร้อยเครื่องพร้อมกันด้วยคำสั่งเดียว
สิ่งที่ทำให้ Ansible โดดเด่นกว่า Tools อื่น (Chef, Puppet, SaltStack) คือ Agentless — ไม่ต้องติดตั้งอะไรบน Target Server เลย ใช้ SSH เชื่อมต่อแล้วรันคำสั่งได้เลย ทำให้เริ่มต้นง่ายมากสำหรับ Sysadmin ที่คุ้นเคยกับ SSH อยู่แล้ว
Ansible vs Manual SSH — ทำไมต้องเปลี่ยน?
| เปรียบเทียบ | Manual SSH | Shell Script | Ansible |
|---|---|---|---|
| จัดการ 1 Server | ง่าย | ง่าย | ง่าย |
| จัดการ 50 Servers | เหนื่อยมาก | พอไหว | ง่ายเหมือน 1 Server |
| Idempotent | ไม่ (รันซ้ำอาจพัง) | ต้องเขียนเอง | ใช่ (รันกี่ครั้งก็ผลเหมือนกัน) |
| Error Handling | ต้องดูเอง | ต้องเขียนเอง | มี Built-in |
| Documentation | ไม่มี | อ่าน Script เอา | Playbook คือ Documentation |
| ติดตั้งบน Target | – | ต้อง copy script ไป | ไม่ต้อง (Agentless) |
| Learning Curve | ต่ำ | ต่ำ-กลาง | กลาง (YAML) |
Ansible Architecture
# Ansible Architecture
#
# ┌─────────────────────────┐
# │ Control Node │ ← เครื่องที่ติดตั้ง Ansible
# │ (Your Laptop/Server) │ (Linux/macOS เท่านั้น)
# │ │
# │ - ansible.cfg │ Config file
# │ - inventory │ รายชื่อ Servers
# │ - playbook.yml │ สิ่งที่ต้องทำ
# │ - roles/ │ Reusable tasks
# └─────────┬───────────────┘
# │ SSH (Port 22)
# │ (ไม่ต้องติดตั้ง Agent!)
# ┌───────┼───────────────────┐
# │ │ │
# ┌──▼──┐ ┌──▼──┐ ┌──▼──┐ ┌──▼──┐
# │ Web │ │ Web │ │ DB │ │ DB │ ← Managed Nodes
# │ 1 │ │ 2 │ │ 1 │ │ 2 │ (Target Servers)
# └─────┘ └─────┘ └─────┘ └─────┘
#
# Requirements สำหรับ Managed Node:
# - SSH access
# - Python 3 (ส่วนใหญ่มาพร้อม OS)
# - ไม่ต้องติดตั้ง Ansible!
ติดตั้ง Ansible
Ubuntu/Debian
# วิธีที่ 1: apt (เวอร์ชันอาจเก่า)
sudo apt update
sudo apt install -y ansible
# วิธีที่ 2: pip (ได้เวอร์ชันล่าสุด — แนะนำ!)
sudo apt install -y python3-pip python3-venv
python3 -m venv ~/ansible-venv
source ~/ansible-venv/bin/activate
pip install ansible
# ทดสอบ
ansible --version
# ansible [core 2.17.x]
# python version = 3.12.x
CentOS/RHEL/Rocky Linux
# EPEL Repository
sudo dnf install -y epel-release
sudo dnf install -y ansible-core
# หรือใช้ pip
sudo dnf install -y python3-pip
pip3 install ansible --user
macOS
# Homebrew
brew install ansible
# หรือ pip
pip3 install ansible
Inventory — รายชื่อ Servers ที่จะจัดการ
INI Format (Simple)
# inventory.ini
# กำหนดกลุ่มของ Servers
[webservers]
web1.example.com
web2.example.com
192.168.1.10
[dbservers]
db1.example.com ansible_host=10.0.0.50
db2.example.com ansible_host=10.0.0.51
[loadbalancers]
lb1.example.com
# Variables สำหรับทั้งกลุ่ม
[webservers:vars]
ansible_user=deploy
ansible_port=22
http_port=8080
[dbservers:vars]
ansible_user=dbadmin
mysql_port=3306
# กลุ่มรวม (Parent Group)
[production:children]
webservers
dbservers
loadbalancers
# Variables สำหรับ Server เดี่ยว
[all:vars]
ansible_python_interpreter=/usr/bin/python3
YAML Format (สำหรับโปรเจคใหญ่)
# inventory.yml
all:
children:
webservers:
hosts:
web1.example.com:
ansible_host: 192.168.1.10
http_port: 8080
web2.example.com:
ansible_host: 192.168.1.11
http_port: 8080
vars:
ansible_user: deploy
dbservers:
hosts:
db1.example.com:
ansible_host: 10.0.0.50
mysql_port: 3306
db2.example.com:
ansible_host: 10.0.0.51
mysql_port: 3307
vars:
ansible_user: dbadmin
Ad-hoc Commands — คำสั่งเดี่ยวแบบรวดเร็ว
# Ping ทุก Server (ทดสอบ Connection)
ansible all -i inventory.ini -m ping
# ผลลัพธ์:
# web1.example.com | SUCCESS => {
# "changed": false,
# "ping": "pong"
# }
# รัน Command บนทุก Web Server
ansible webservers -i inventory.ini -m command -a "uptime"
ansible webservers -i inventory.ini -m shell -a "df -h | grep /dev/sda"
# ดูข้อมูล Server (Facts)
ansible web1.example.com -i inventory.ini -m setup
# ติดตั้ง Package
ansible webservers -i inventory.ini -m apt -a "name=nginx state=present" --become
# Copy ไฟล์ไปทุก Server
ansible webservers -i inventory.ini -m copy -a "src=./config.conf dest=/etc/app/config.conf"
# Restart Service
ansible webservers -i inventory.ini -m service -a "name=nginx state=restarted" --become
# สร้าง User
ansible all -i inventory.ini -m user -a "name=deploy state=present groups=sudo" --become
Playbook — หัวใจของ Ansible
Playbook แรก — ติดตั้ง Nginx
# install_nginx.yml
---
- name: Install and configure Nginx
hosts: webservers
become: yes # ใช้ sudo
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install Nginx
apt:
name: nginx
state: present
- name: Start and enable Nginx
service:
name: nginx
state: started
enabled: yes
- name: Copy custom index.html
copy:
content: |
<html>
<body>
<h1>Server: {{ inventory_hostname }}</h1>
<p>Managed by Ansible</p>
</body>
</html>
dest: /var/www/html/index.html
owner: www-data
group: www-data
mode: '0644'
- name: Ensure Nginx is running
service:
name: nginx
state: started
รัน Playbook
# รัน Playbook
ansible-playbook -i inventory.ini install_nginx.yml
# Dry-run (ทดสอบก่อน ไม่ทำจริง)
ansible-playbook -i inventory.ini install_nginx.yml --check
# แสดงรายละเอียดมากขึ้น
ansible-playbook -i inventory.ini install_nginx.yml -v # verbose
ansible-playbook -i inventory.ini install_nginx.yml -vvv # very verbose
# รันเฉพาะบาง Host
ansible-playbook -i inventory.ini install_nginx.yml --limit web1.example.com
# ถาม sudo password
ansible-playbook -i inventory.ini install_nginx.yml --ask-become-pass
Common Modules — Module ที่ใช้บ่อย
apt / yum — จัดการ Package
# apt Module (Debian/Ubuntu)
- name: Install multiple packages
apt:
name:
- nginx
- python3
- git
- curl
- htop
state: present
update_cache: yes
- name: Remove package
apt:
name: apache2
state: absent
- name: Upgrade all packages
apt:
upgrade: dist
update_cache: yes
# yum Module (CentOS/RHEL)
- name: Install packages on CentOS
yum:
name:
- httpd
- php
- mariadb-server
state: present
copy / template — จัดการไฟล์
# copy Module — Copy ไฟล์ตรง ๆ
- name: Copy config file
copy:
src: files/nginx.conf # Local file
dest: /etc/nginx/nginx.conf # Remote path
owner: root
group: root
mode: '0644'
backup: yes # Backup ไฟล์เดิมก่อนเขียนทับ
# template Module — Copy พร้อมแทนค่า Variables (Jinja2)
- name: Deploy Nginx virtual host
template:
src: templates/vhost.conf.j2
dest: /etc/nginx/sites-available/{{ domain }}.conf
owner: root
group: root
mode: '0644'
notify: Reload Nginx # Trigger Handler
service / systemd — จัดการ Services
# service Module
- name: Start and enable Nginx
service:
name: nginx
state: started # started, stopped, restarted, reloaded
enabled: yes # เปิด Auto-start ตอน Boot
# systemd Module (มี Feature มากกว่า)
- name: Restart MySQL with systemd
systemd:
name: mysql
state: restarted
daemon_reload: yes # Reload systemd ก่อน (ถ้าแก้ unit file)
user / group — จัดการ Users
# สร้าง User
- name: Create deploy user
user:
name: deploy
groups: sudo,www-data
shell: /bin/bash
create_home: yes
state: present
# ตั้ง SSH Key
- name: Add SSH key for deploy user
authorized_key:
user: deploy
key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
state: present
# สร้าง Group
- name: Create app group
group:
name: appgroup
state: present
file — จัดการ Directories และ Permissions
# สร้าง Directory
- name: Create app directory
file:
path: /opt/myapp
state: directory
owner: deploy
group: appgroup
mode: '0755'
# สร้าง Symbolic Link
- name: Create symlink
file:
src: /opt/myapp/current
dest: /var/www/html
state: link
# เปลี่ยน Permission
- name: Set permissions
file:
path: /var/log/myapp
state: directory
owner: deploy
mode: '0750'
recurse: yes
Variables และ Templates (Jinja2)
กำหนด Variables
# ใน Playbook
---
- name: Deploy web application
hosts: webservers
become: yes
vars:
app_name: myapp
app_port: 8080
app_user: deploy
domain: example.com
max_workers: 4
tasks:
- name: Create app directory
file:
path: "/opt/{{ app_name }}"
state: directory
owner: "{{ app_user }}"
- name: Deploy config
template:
src: app.conf.j2
dest: "/opt/{{ app_name }}/config.conf"
Jinja2 Template ตัวอย่าง
# templates/vhost.conf.j2
server {
listen {{ http_port | default(80) }};
server_name {{ domain }};
root /opt/{{ app_name }}/public;
location / {
proxy_pass http://127.0.0.1:{{ app_port }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# Generated by Ansible on {{ ansible_date_time.iso8601 }}
# Hostname: {{ inventory_hostname }}
# OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
{% if enable_ssl | default(false) %}
listen 443 ssl;
ssl_certificate /etc/ssl/certs/{{ domain }}.crt;
ssl_certificate_key /etc/ssl/private/{{ domain }}.key;
{% endif %}
{% for backend in app_backends | default([]) %}
upstream backend_{{ loop.index }} {
server {{ backend }};
}
{% endfor %}
}
Handlers — ทำงานเมื่อมีการเปลี่ยนแปลง
# Handlers จะรันเฉพาะเมื่อ Task ที่ notify มี changed=true
---
- name: Configure Nginx
hosts: webservers
become: yes
tasks:
- name: Copy nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Reload Nginx # ← Trigger handler
- name: Copy virtual host
template:
src: vhost.conf.j2
dest: /etc/nginx/sites-available/myapp.conf
notify:
- Test Nginx Config # ← สามารถ notify หลาย handler
- Reload Nginx
handlers:
- name: Test Nginx Config
command: nginx -t
- name: Reload Nginx
service:
name: nginx
state: reloaded
- name: Restart Nginx
service:
name: nginx
state: restarted
Roles — จัดระเบียบ Playbook
โครงสร้าง Role
# สร้าง Role
ansible-galaxy role init roles/nginx
# โครงสร้าง:
# roles/
# └── nginx/
# ├── defaults/
# │ └── main.yml # Default variables (priority ต่ำสุด)
# ├── files/
# │ └── nginx.conf # Static files
# ├── handlers/
# │ └── main.yml # Handlers
# ├── meta/
# │ └── main.yml # Role metadata
# ├── tasks/
# │ └── main.yml # Main tasks
# ├── templates/
# │ └── vhost.conf.j2 # Jinja2 templates
# └── vars/
# └── main.yml # Variables (priority สูง)
ตัวอย่าง Role: nginx
# roles/nginx/defaults/main.yml
---
nginx_port: 80
nginx_worker_processes: auto
nginx_worker_connections: 1024
# roles/nginx/tasks/main.yml
---
- name: Install Nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Deploy nginx.conf
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Reload Nginx
- name: Enable and start Nginx
service:
name: nginx
state: started
enabled: yes
# roles/nginx/handlers/main.yml
---
- name: Reload Nginx
service:
name: nginx
state: reloaded
# ใช้ Role ใน Playbook
# site.yml
---
- name: Setup web servers
hosts: webservers
become: yes
roles:
- nginx
- { role: app, app_port: 8080 }
- { role: monitoring, when: env == "production" }
Ansible Galaxy — ใช้ Role สำเร็จรูป
# ค้นหา Role
ansible-galaxy search nginx
ansible-galaxy search mysql
# ติดตั้ง Role จาก Galaxy
ansible-galaxy install geerlingguy.nginx
ansible-galaxy install geerlingguy.mysql
ansible-galaxy install geerlingguy.docker
# ใช้ requirements.yml (แนะนำ!)
# requirements.yml
---
roles:
- name: geerlingguy.nginx
version: "3.2.0"
- name: geerlingguy.mysql
version: "4.0.0"
- name: geerlingguy.docker
collections:
- name: community.general
- name: ansible.posix
# ติดตั้งทั้งหมด
ansible-galaxy install -r requirements.yml
# ดู Role ที่ติดตั้งแล้ว
ansible-galaxy list
ตัวอย่างจริง — ติดตั้ง LAMP Stack
# lamp_setup.yml
---
- name: Setup LAMP Stack (Linux, Apache, MySQL, PHP)
hosts: webservers
become: yes
vars:
mysql_root_password: "Str0ngP@ss2026!"
php_version: "8.3"
app_domain: "myapp.example.com"
tasks:
# --- Apache ---
- name: Install Apache
apt:
name:
- apache2
- libapache2-mod-php{{ php_version }}
state: present
update_cache: yes
- name: Enable Apache modules
apache2_module:
name: "{{ item }}"
state: present
loop:
- rewrite
- ssl
- headers
notify: Restart Apache
# --- PHP ---
- name: Install PHP and 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
# --- MySQL ---
- name: Install MySQL
apt:
name:
- mysql-server
- python3-pymysql
state: present
- name: Start MySQL
service:
name: mysql
state: started
enabled: yes
- name: Set MySQL root password
mysql_user:
name: root
password: "{{ mysql_root_password }}"
login_unix_socket: /var/run/mysqld/mysqld.sock
host: localhost
state: present
# --- App Setup ---
- name: Create web root
file:
path: "/var/www/{{ app_domain }}"
state: directory
owner: www-data
group: www-data
mode: '0755'
- name: Deploy test PHP page
copy:
content: |
<?php
echo "<h1>LAMP Stack Working!</h1>";
echo "<p>PHP Version: " . phpversion() . "</p>";
echo "<p>Server: {{ inventory_hostname }}</p>";
phpinfo();
dest: "/var/www/{{ app_domain }}/index.php"
owner: www-data
mode: '0644'
handlers:
- name: Restart Apache
service:
name: apache2
state: restarted
ตัวอย่างจริง — Configure Firewall (UFW)
# firewall_setup.yml
---
- name: Configure UFW Firewall
hosts: all
become: yes
vars:
allowed_ports:
- { port: 22, proto: tcp, comment: "SSH" }
- { port: 80, proto: tcp, comment: "HTTP" }
- { port: 443, proto: tcp, comment: "HTTPS" }
trusted_ips:
- 10.0.0.0/8
- 192.168.1.0/24
tasks:
- name: Install UFW
apt:
name: ufw
state: present
- name: Set default policies
ufw:
direction: "{{ item.direction }}"
policy: "{{ item.policy }}"
loop:
- { direction: incoming, policy: deny }
- { direction: outgoing, policy: allow }
- name: Allow specific ports
ufw:
rule: allow
port: "{{ item.port }}"
proto: "{{ item.proto }}"
comment: "{{ item.comment }}"
loop: "{{ allowed_ports }}"
- name: Allow trusted IPs (all ports)
ufw:
rule: allow
from_ip: "{{ item }}"
comment: "Trusted network"
loop: "{{ trusted_ips }}"
- name: Enable UFW
ufw:
state: enabled
logging: 'on'
- name: Show UFW status
command: ufw status verbose
register: ufw_status
changed_when: false
- name: Display firewall status
debug:
var: ufw_status.stdout_lines
ตัวอย่างจริง — Manage Users
# manage_users.yml
---
- name: Manage system users
hosts: all
become: yes
vars:
users:
- name: deploy
groups: sudo,www-data
shell: /bin/bash
ssh_key: "ssh-rsa AAAA... deploy@company"
- name: monitor
groups: www-data
shell: /bin/bash
ssh_key: "ssh-rsa AAAA... monitor@company"
- name: backup
groups: backup
shell: /bin/bash
ssh_key: "ssh-rsa AAAA... backup@company"
removed_users:
- oldadmin
- tempuser
tasks:
- name: Create user groups
group:
name: "{{ item }}"
state: present
loop:
- appgroup
- backup
- name: Create users
user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
shell: "{{ item.shell }}"
create_home: yes
state: present
loop: "{{ users }}"
- name: Set SSH keys
authorized_key:
user: "{{ item.name }}"
key: "{{ item.ssh_key }}"
exclusive: yes
state: present
loop: "{{ users }}"
- name: Configure sudoers for deploy
copy:
content: "deploy ALL=(ALL) NOPASSWD:ALL
"
dest: /etc/sudoers.d/deploy
mode: '0440'
validate: 'visudo -cf %s'
- name: Remove old users
user:
name: "{{ item }}"
state: absent
remove: yes
loop: "{{ removed_users }}"
Debugging Ansible
# 1. Debug Module — แสดงค่า Variable
- name: Show variable value
debug:
var: ansible_distribution
- name: Show custom message
debug:
msg: "Server {{ inventory_hostname }} is running {{ ansible_os_family }}"
# 2. Register — เก็บผลลัพธ์ของ Task
- name: Check disk space
command: df -h /
register: disk_result
- name: Show disk space
debug:
var: disk_result.stdout_lines
# 3. Verbose Mode
# ansible-playbook playbook.yml -v # basic
# ansible-playbook playbook.yml -vv # more detail
# ansible-playbook playbook.yml -vvv # connection debug
# ansible-playbook playbook.yml -vvvv # full debug
# 4. Step Mode (ถามทีละ Task)
# ansible-playbook playbook.yml --step
# 5. Start at Task (ข้ามไป Task ที่ต้องการ)
# ansible-playbook playbook.yml --start-at-task="Install Nginx"
# 6. Tags (รันเฉพาะ Task ที่ต้องการ)
- name: Install packages
apt:
name: nginx
tags: [install, nginx]
# ansible-playbook playbook.yml --tags install
# ansible-playbook playbook.yml --skip-tags install
Best Practices สำหรับ Ansible Beginners
| หมวด | Best Practice | เหตุผล |
|---|---|---|
| Naming | ตั้งชื่อ Task ให้ชัดเจนเสมอ | อ่านง่าย Debug ง่าย |
| Idempotent | ใช้ Module แทน command/shell | รันซ้ำได้ไม่พัง |
| Variables | ใช้ defaults สำหรับค่า Default | Override ง่าย |
| Secrets | ใช้ Ansible Vault เก็บ Password | ไม่ Commit Password ลง Git |
| Testing | ใช้ –check ก่อนรันจริงเสมอ | ป้องกันความผิดพลาด |
| Version Control | เก็บ Playbook ใน Git | Track changes + Collaboration |
| Roles | แยก Task เป็น Roles ตั้งแต่ต้น | Reusable + Organized |
| Inventory | แยก Inventory ตาม Environment | ไม่รัน Production ผิด |
Ansible Vault — เก็บ Secrets อย่างปลอดภัย
# สร้าง Encrypted file
ansible-vault create secrets.yml
# จะเปิด Editor ให้ใส่ข้อมูลลับ:
# mysql_root_password: "SuperSecret123!"
# api_key: "sk-abc123..."
# แก้ไข Encrypted file
ansible-vault edit secrets.yml
# ใช้ Vault ใน Playbook
# playbook.yml
---
- name: Deploy with secrets
hosts: webservers
become: yes
vars_files:
- secrets.yml
tasks:
- name: Set DB password
mysql_user:
name: root
password: "{{ mysql_root_password }}"
# รันพร้อมถาม Vault password
ansible-playbook playbook.yml --ask-vault-pass
# หรือใช้ Password file
ansible-playbook playbook.yml --vault-password-file ~/.vault_pass
ansible.cfg — Configuration File
# ansible.cfg (วางไว้ใน Project directory)
[defaults]
inventory = inventory.ini
remote_user = deploy
private_key_file = ~/.ssh/id_rsa
host_key_checking = False
retry_files_enabled = False
stdout_callback = yaml
timeout = 30
[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 สำหรับ System Admin มือใหม่
Ansible เป็น Automation Tool ที่ดีที่สุดสำหรับ System Admin เพราะ Agentless (ใช้ SSH ไม่ต้องติดตั้งอะไร), YAML-based (อ่านง่าย), Idempotent (รันซ้ำได้ปลอดภัย) และ Ansible Galaxy (มี Role สำเร็จรูปมากมาย)
ลำดับการเรียนรู้ที่แนะนำ: ติดตั้ง Ansible → สร้าง Inventory → ลอง Ad-hoc commands → เขียน Playbook แรก → เรียนรู้ Modules ที่ใช้บ่อย (apt, copy, template, service, user) → ใช้ Variables + Templates → จัดระเบียบด้วย Roles → Ansible Vault สำหรับ Secrets
เริ่มจากจัดการ Server 2-3 เครื่อง แล้วค่อย ๆ ขยาย เมื่อชำนาญแล้วจะสามารถจัดการ Server หลายร้อยเครื่องได้อย่างมั่นใจ!