Ansible Beginner Guide

Ansible for beginnersAnsible สำหรับผู้เริ่มต้น

Ansible Beginner Guide

Describe your servers once, and let Ansible make them matchอธิบายเซิร์ฟเวอร์ครั้งเดียว แล้วให้ Ansible ทำให้เซิร์ฟเวอร์เป็นตามนั้น

Setting up one server by hand is fine. Setting up ten the same way, and keeping them the same, is not. Ansible lets you write down how your servers should be (packages, files, services, users) in YAML files, then connects to every server over SSH and changes only what differs. This guide builds a small setup step by step: an nginx web server on two machines and a database config on a third, with templates, handlers, roles and encrypted secrets. ตั้งค่าเซิร์ฟเวอร์เครื่องเดียวด้วยมือก็พอไหว แต่ตั้งค่าสิบเครื่องให้เหมือนกันและคงให้เหมือนกันตลอดไปนั้นไม่ไหว Ansible ให้เราเขียนไว้ในไฟล์ YAML ว่าเซิร์ฟเวอร์ควรเป็นอย่างไร (แพ็กเกจ ไฟล์ บริการ ผู้ใช้) แล้วเชื่อมต่อทุกเครื่องผ่าน SSH และเปลี่ยนเฉพาะสิ่งที่ไม่ตรง คู่มือนี้สร้างระบบเล็กๆ ทีละขั้น ได้แก่เว็บเซิร์ฟเวอร์ nginx บนสองเครื่อง และไฟล์ตั้งค่าฐานข้อมูลบนเครื่องที่สาม พร้อมเทมเพลต handler, role และความลับที่เข้ารหัสไว้

Every command marked with a terminal on this page was run for real against three Ubuntu servers running in Docker on one computer, and the output is what it printed. The files at each step come from the lab's git history. If Docker is new to you, start with the Docker guide. ทุกคำสั่งที่อยู่ในเทอร์มินัลในหน้านี้รันจริงกับเซิร์ฟเวอร์ Ubuntu สามเครื่องที่รันใน Docker บนคอมพิวเตอร์เครื่องเดียว และผลลัพธ์คือสิ่งที่มันพิมพ์ออกมา ไฟล์ในแต่ละขั้นมาจากประวัติ git ของแล็บ ถ้ายังไม่คุ้นกับ Docker ให้เริ่มจากคู่มือ Docker

  • Run withรันด้วย ansible-core 2.21
  • Ubuntu 24.04
  • Docker Compose 5.2

Lesson 1 · Setupบทที่ 1 · เตรียมพร้อม

Ansible and the labAnsible และแล็บ

Ansible runs on one computer, the control node, and manages other machines, the managed nodes. It is agentless: the servers need only SSH and Python, nothing Ansible-specific. For each step, Ansible sends a small program called a module (install a package, copy a file, start a service) over SSH, runs it, and reads back whether anything changed. Modules check first and act only when needed, so running the same thing twice is safe. That property is called idempotence, and the whole guide leans on it. Ansible รันบนคอมพิวเตอร์เครื่องเดียวที่เรียกว่า control node และจัดการเครื่องอื่นๆ ที่เรียกว่า managed node Ansible เป็นแบบ agentless คือเซิร์ฟเวอร์ต้องการแค่ SSH และ Python ไม่ต้องติดตั้งอะไรของ Ansible ในแต่ละขั้น Ansible ส่งโปรแกรมเล็กๆ ที่เรียกว่า module (ติดตั้งแพ็กเกจ คัดลอกไฟล์ เริ่มบริการ) ผ่าน SSH รันมัน แล้วอ่านผลว่ามีอะไร เปลี่ยน (changed) หรือไม่ module จะตรวจก่อนและลงมือเฉพาะเมื่อจำเป็น การรันสิ่งเดิมสองครั้งจึงปลอดภัย คุณสมบัตินี้เรียกว่า idempotence และทั้งคู่มือนี้อาศัยคุณสมบัตินี้

The lab: three Ubuntu 24.04 servers, web1, web2 and db1, run as Docker containers with systemd, SSH and Python, like fresh cloud VMs. Ansible logs in to each as the user deploy with an SSH key made for the lab. Install Ansible on your computer with pipx install ansible or brew install ansible; it runs on macOS and Linux (on Windows, inside WSL). แล็บนี้มีเซิร์ฟเวอร์ Ubuntu 24.04 สามเครื่อง คือ web1, web2 และ db1 รันเป็น container ของ Docker ที่มี systemd, SSH และ Python เหมือน VM บนคลาวด์ที่เพิ่งสร้าง Ansible ล็อกอินเข้าแต่ละเครื่องในชื่อผู้ใช้ deploy ด้วยกุญแจ SSH ที่สร้างไว้สำหรับแล็บ ติดตั้ง Ansible บนเครื่องของเราด้วย pipx install ansible หรือ brew install ansible ใช้ได้บน macOS และ Linux (บน Windows ให้ใช้ใน WSL)

Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ ssh-keygen -t ed25519 -f lab_key -N "" -C ansible-lab
Generating public/private ed25519 key pair.
Your identification has been saved in lab_key
Your public key has been saved in lab_key.pub
The key fingerprint is:
SHA256:f/0XwlQuzCjSBwx/SAu2rbDbJ099oAvE5Jb1W0EVRYI ansible-lab
The key's randomart image is:
+--[ED25519 256]--+
|       +o.   oo+=|
|      . *oo E  o |
|     . o.*..+.o  |
|      *.+ooo =.. |
|     . BS ooo..  |
|      =  .o ++ . |
|     . + +.o..o .|
|        * ...  ..|
|         o      o|
+----[SHA256]-----+

$ docker compose up -d --build
 Image ansible-node Building 
 Image ansible-node Building 
 Image ansible-node Building 
⋯ 71 lines of image build output ⋯⋯ 71 บรรทัด: ผลลัพธ์ระหว่าง build image ⋯
 Image ansible-node Built 
 Image ansible-node Built 
 Image ansible-node Built 
 Network ansible-lab_default Creating 
 Network ansible-lab_default Created 
 Container ansible-lab-web2-1 Creating 
 Container ansible-lab-db1-1 Creating 
 Container ansible-lab-web1-1 Creating 
 Container ansible-lab-web2-1 Created 
 Container ansible-lab-web1-1 Created 
 Container ansible-lab-db1-1 Created 
 Container ansible-lab-web2-1 Starting 
 Container ansible-lab-db1-1 Starting 
 Container ansible-lab-web1-1 Starting 
 Container ansible-lab-db1-1 Started 
 Container ansible-lab-web2-1 Started 
 Container ansible-lab-web1-1 Started

$ docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"
NAME                 STATUS        PORTS
ansible-lab-db1-1    Up 1 second   0.0.0.0:2203->22/tcp, [::]:2203->22/tcp
ansible-lab-web1-1   Up 1 second   0.0.0.0:2201->22/tcp, [::]:2201->22/tcp, 0.0.0.0:8101->80/tcp, [::]:8101->80/tcp
ansible-lab-web2-1   Up 1 second   0.0.0.0:2202->22/tcp, [::]:2202->22/tcp, 0.0.0.0:8102->80/tcp, [::]:8102->80/tcp

The key pair belongs to this lab only: lab_key stays on your computer, and the image gets lab_key.pub. Each server's SSH port is published on your computer: 2201, 2202 and 2203. The web servers also publish port 80 as 8101 and 8102.คู่กุญแจนี้ใช้กับแล็บนี้เท่านั้น lab_key อยู่บนเครื่องของเรา ส่วน image ได้ lab_key.pub พอร์ต SSH ของแต่ละเซิร์ฟเวอร์ถูกเปิดบนเครื่องเราเป็น 2201, 2202 และ 2203 ส่วนเว็บเซิร์ฟเวอร์เปิดพอร์ต 80 เป็น 8101 และ 8102 ด้วย

Ansible needs to know which servers exist and how to reach them. That list is the inventory. Next to it, ansible.cfg holds settings for every command run in this folder. Ansible ต้องรู้ว่ามีเซิร์ฟเวอร์อะไรบ้างและเข้าถึงอย่างไร รายการนั้นเรียกว่า inventory และข้างๆ กันมี ansible.cfg ซึ่งเก็บการตั้งค่าสำหรับทุกคำสั่งที่รันในโฟลเดอร์นี้

Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ ansible --version | head -n 2
ansible [core 2.21.4]
  config file = ~/ansible-lab/ansible.cfg

$ ansible all -m ping
db1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
web1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
web2 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Ansible found ansible.cfg in the current folder. ping is not a network ping: Ansible logged in over SSH, ran a tiny Python module, and got pong back, so login, Python and the inventory all work. The order of the answers changes from run to run because Ansible works on all servers in parallel.Ansible พบ ansible.cfg ในโฟลเดอร์ปัจจุบัน ping ไม่ใช่ ping ของเครือข่าย Ansible ล็อกอินผ่าน SSH รัน module ของ Python ตัวเล็กๆ และได้ pong กลับมา แปลว่าการล็อกอิน Python และ inventory ใช้ได้ทั้งหมด ลำดับของคำตอบเปลี่ยนไปในแต่ละครั้ง เพราะ Ansible ทำงานกับทุกเซิร์ฟเวอร์พร้อมกัน

Lab onlyเฉพาะแล็บ

The two SSH settings in ansible.cfg skip host key checks, because these containers get new host keys whenever they are recreated. With real servers, keep checking on: an unexpected host key can mean you are talking to the wrong machine.การตั้งค่า SSH สองบรรทัดใน ansible.cfg ข้ามการตรวจ host key เพราะ container เหล่านี้ได้ host key ใหม่ทุกครั้งที่ถูกสร้างใหม่ กับเซิร์ฟเวอร์จริงให้เปิดการตรวจไว้ เพราะ host key ที่ไม่คาดคิดอาจแปลว่าเรากำลังคุยกับเครื่องที่ผิด

Lesson 2 · Reach your serversบทที่ 2 · เข้าถึงเซิร์ฟเวอร์

Inventory and ad-hoc commandsInventory และคำสั่ง ad-hoc

The inventory puts hosts in groups: [web] and [db] here. Every host is also in the group all. Variables can belong to one host (ansible_port=2201) or to a group ([all:vars]). An ad-hoc command runs one module on a group of hosts, straight from the terminal: ansible <hosts> -m <module> -a <arguments>. Without -m, the module is command, which runs a program. inventory จัด host ไว้เป็น กลุ่ม (group) ในที่นี้คือ [web] และ [db] และทุก host อยู่ในกลุ่ม all ด้วย ตัวแปรเป็นของ host เดียว (ansible_port=2201) หรือของทั้งกลุ่ม ([all:vars]) ก็ได้ คำสั่ง ad-hoc รัน module หนึ่งตัวบนกลุ่มของ host ตรงจากเทอร์มินัล: ansible <hosts> -m <module> -a <arguments> ถ้าไม่ใส่ -m module จะเป็น command ซึ่งรันโปรแกรมหนึ่งตัว

Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ ansible-inventory --graph
@all:
  |--@ungrouped:
  |--@web:
  |  |--web1
  |  |--web2
  |--@db:
  |  |--db1

$ ansible all -a "hostname"
db1 | CHANGED | rc=0 >>
db1
web2 | CHANGED | rc=0 >>
web2
web1 | CHANGED | rc=0 >>
web1

$ ansible db1 -m setup -a "filter=ansible_distribution*"
db1 | SUCCESS => {
    "ansible_facts": {
        "ansible_distribution": "Ubuntu",
        "ansible_distribution_file_parsed": true,
        "ansible_distribution_file_path": "/etc/os-release",
        "ansible_distribution_file_variety": "Debian",
        "ansible_distribution_major_version": "24",
        "ansible_distribution_release": "noble",
        "ansible_distribution_version": "24.04"
    },
    "changed": false
}

command always reports CHANGED, because Ansible can't tell whether a program changed anything. setup collects facts, information about the server such as its OS, addresses and memory; playbooks collect them automatically, and filter picks a few.command รายงาน CHANGED เสมอ เพราะ Ansible บอกไม่ได้ว่าโปรแกรมเปลี่ยนอะไรหรือไม่ setup เก็บ fact ซึ่งเป็นข้อมูลของเซิร์ฟเวอร์ เช่น ระบบปฏิบัติการ ที่อยู่ IP และหน่วยความจำ playbook เก็บ fact ให้อัตโนมัติ และ filter เลือกมาแค่บางตัว

Now a module that changes something. -b (become) runs it as root through sudo. The same command, twice: ต่อไปเป็น module ที่เปลี่ยนแปลงบางอย่าง -b (become) รันในฐานะ root ผ่าน sudo คำสั่งเดียวกัน สองครั้ง

Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ ansible web1 -b -m file -a "path=/opt/shop state=directory mode=0755"
web1 | CHANGED => {
    "changed": true,
    "gid": 0,
    "group": "root",
    "mode": "0755",
    "owner": "root",
    "path": "/opt/shop",
    "size": 4096,
    "state": "directory",
    "uid": 0
}

$ ansible web1 -b -m file -a "path=/opt/shop state=directory mode=0755"
web1 | SUCCESS => {
    "changed": false,
    "gid": 0,
    "group": "root",
    "mode": "0755",
    "owner": "root",
    "path": "/opt/shop",
    "size": 4096,
    "state": "directory",
    "uid": 0
}

You don't tell file to create a folder; you say the folder should exist (state=directory). The first run created it (CHANGED, yellow). The second found it already right and did nothing (SUCCESS, "changed": false, green). That is idempotence.เราไม่ได้สั่งให้ file สร้าง โฟลเดอร์ แต่บอกว่าโฟลเดอร์ควรมีอยู่ (state=directory) ครั้งแรกมันสร้างให้ (CHANGED สีเหลือง) ครั้งที่สองพบว่าถูกต้องอยู่แล้วจึงไม่ทำอะไร (SUCCESS, "changed": false สีเขียว) นี่คือ idempotence

Lesson 3 · Write playbooksบทที่ 3 · เขียน playbook

Your first playbookPlaybook แรก

Ad-hoc commands are for quick jobs. For a setup you want to keep, write a playbook: a YAML file with one or more plays. A play picks hosts and lists tasks; each task calls one module with its arguments. Tasks run in order, each on all the play's hosts in parallel. ansible.builtin. is the full name of the modules that ship with Ansible. คำสั่ง ad-hoc เหมาะกับงานด่วน ถ้าเป็นการตั้งค่าที่อยากเก็บไว้ ให้เขียน playbook ซึ่งเป็นไฟล์ YAML ที่มี play หนึ่งตัวหรือมากกว่า play หนึ่งตัวเลือก host และระบุ task แต่ละ task เรียก module หนึ่งตัวพร้อม argument task ทำงานตามลำดับ แต่ละ task รันบนทุก host ของ play พร้อมกัน ansible.builtin. คือชื่อเต็มของ module ที่มาพร้อมกับ Ansible

Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ ansible-playbook site.yml
PLAY [Web servers] *************************************************************

TASK [Gathering Facts] *********************************************************
ok: [web1]
ok: [web2]

TASK [Install nginx] ***********************************************************
changed: [web1]
changed: [web2]

TASK [Start nginx now and at every boot] ***************************************
changed: [web2]
changed: [web1]

TASK [Home page] ***************************************************************
changed: [web2]
changed: [web1]

PLAY RECAP *********************************************************************
web1                       : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
web2                       : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

$ curl -s localhost:8101
Hello from Ansible

$ ansible-playbook site.yml
PLAY [Web servers] *************************************************************

TASK [Gathering Facts] *********************************************************
ok: [web1]
ok: [web2]

TASK [Install nginx] ***********************************************************
ok: [web2]
ok: [web1]

TASK [Start nginx now and at every boot] ***************************************
ok: [web2]
ok: [web1]

TASK [Home page] ***************************************************************
ok: [web1]
ok: [web2]

PLAY RECAP *********************************************************************
web1                       : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
web2                       : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Gathering Facts runs first, by itself. Read the PLAY RECAP at the end: the first run changed 3 things on each server, the second changed nothing, because every server already matched the playbook. A playbook you can rerun at any time, to fix drift or to add a new server, is the point of Ansible.Gathering Facts รันก่อนเสมอโดยอัตโนมัติ ให้อ่าน PLAY RECAP ตอนท้าย ครั้งแรกเปลี่ยน 3 อย่างบนแต่ละเซิร์ฟเวอร์ ครั้งที่สองไม่เปลี่ยนอะไรเลย เพราะทุกเซิร์ฟเวอร์ตรงกับ playbook อยู่แล้ว playbook ที่รันซ้ำได้ทุกเมื่อ ทั้งเพื่อแก้ส่วนที่คลาดเคลื่อนหรือเพื่อเพิ่มเซิร์ฟเวอร์ใหม่ คือหัวใจของ Ansible

Lesson 4 · Write playbooksบทที่ 4 · เขียน playbook

Variables, facts and templatesตัวแปร fact และเทมเพลต

Values that differ between groups go in group_vars: a file named after a group applies to every host in it. A template is a file with Jinja2 placeholders, {{ ... }}, filled in for each server from variables and facts. inventory_hostname is the host's name in the inventory. ค่าที่ต่างกันในแต่ละกลุ่มให้เก็บไว้ใน group_vars ไฟล์ที่ตั้งชื่อตามกลุ่มจะใช้กับทุก host ในกลุ่มนั้น เทมเพลต (template) คือไฟล์ที่มีช่องว่างแบบ Jinja2 {{ ... }} ซึ่งถูกเติมสำหรับแต่ละเซิร์ฟเวอร์จากตัวแปรและ fact ส่วน inventory_hostname คือชื่อของ host ใน inventory

Before changing anything, preview: --check runs the playbook without making changes, and --diff shows what would change in each file. ก่อนเปลี่ยนอะไร ให้ดูตัวอย่างก่อน --check รัน playbook โดยไม่เปลี่ยนแปลงจริง และ --diff แสดงว่าแต่ละไฟล์จะเปลี่ยนอย่างไร

Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ ansible-playbook site.yml --check --diff
PLAY [Web servers] *************************************************************

TASK [Gathering Facts] *********************************************************
ok: [web2]
ok: [web1]

TASK [Install nginx] ***********************************************************
ok: [web1]
ok: [web2]

TASK [Start nginx now and at every boot] ***************************************
ok: [web2]
ok: [web1]

TASK [Home page] ***************************************************************
--- before: /var/www/html/index.html
+++ after: ~/.ansible/tmp/ansible-local-91116hkjtm2at/tmpaidktoyo/index.html.j2
@@ -1 +1,3 @@
-Hello from Ansible
+<h1>Shop</h1>
+<p>Served by web2 on Ubuntu 24.04.</p>
+<p>Managed by the platform team with Ansible.</p>

changed: [web2]
--- before: /var/www/html/index.html
+++ after: ~/.ansible/tmp/ansible-local-91116hkjtm2at/tmpaeohhgoh/index.html.j2
@@ -1 +1,3 @@
-Hello from Ansible
+<h1>Shop</h1>
+<p>Served by web1 on Ubuntu 24.04.</p>
+<p>Managed by the platform team with Ansible.</p>

changed: [web1]

PLAY RECAP *********************************************************************
web1                       : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
web2                       : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

$ ansible-playbook site.yml
PLAY [Web servers] *************************************************************

TASK [Gathering Facts] *********************************************************
ok: [web2]
ok: [web1]

TASK [Install nginx] ***********************************************************
ok: [web2]
ok: [web1]

TASK [Start nginx now and at every boot] ***************************************
ok: [web2]
ok: [web1]

TASK [Home page] ***************************************************************
changed: [web2]
changed: [web1]

PLAY RECAP *********************************************************************
web1                       : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
web2                       : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

$ curl -s localhost:8101; curl -s localhost:8102
<h1>Shop</h1>
<p>Served by web1 on Ubuntu 24.04.</p>
<p>Managed by the platform team with Ansible.</p>
<h1>Shop</h1>
<p>Served by web2 on Ubuntu 24.04.</p>
<p>Managed by the platform team with Ansible.</p>

One template, two different pages: each server got its own name. In the diff, after is the copy Ansible rendered in a temporary folder on your computer before sending it.เทมเพลตเดียว ได้สองหน้าที่ต่างกัน แต่ละเซิร์ฟเวอร์ได้ชื่อของตัวเอง ใน diff ส่วน after คือสำเนาที่ Ansible เติมค่าไว้ในโฟลเดอร์ชั่วคราวบนเครื่องของเราก่อนส่งออกไป

Lesson 5 · Write playbooksบทที่ 5 · เขียน playbook

Handlers: act only after a changeHandler: ทำงานเฉพาะหลังมีการเปลี่ยนแปลง

Some changes need a follow-up: nginx must reload after its configuration changes, but reloading on every run would be wasteful. A task notifies a handler by name; handlers run once, at the end of the play, and only if something notified them. This configuration adds a header that names the server. การเปลี่ยนแปลงบางอย่างต้องมีขั้นตอนต่อ เช่น nginx ต้อง reload หลังการตั้งค่าเปลี่ยน แต่ถ้า reload ทุกครั้งที่รันก็สิ้นเปลือง task หนึ่ง แจ้ง (notify) handler ตามชื่อ handler จะรันครั้งเดียวตอนท้ายของ play และรันเฉพาะเมื่อมีคนแจ้งเท่านั้น การตั้งค่านี้เพิ่ม header ที่บอกชื่อเซิร์ฟเวอร์

Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ ansible-playbook site.yml
PLAY [Web servers] *************************************************************

TASK [Gathering Facts] *********************************************************
ok: [web1]
ok: [web2]

TASK [Install nginx] ***********************************************************
ok: [web1]
ok: [web2]

TASK [Start nginx now and at every boot] ***************************************
ok: [web1]
ok: [web2]

TASK [Site configuration] ******************************************************
changed: [web2]
changed: [web1]

TASK [Home page] ***************************************************************
ok: [web1]
ok: [web2]

RUNNING HANDLER [Reload nginx] *************************************************
changed: [web2]
changed: [web1]

PLAY RECAP *********************************************************************
web1                       : ok=6    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
web2                       : ok=6    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

$ curl -sI localhost:8102 | grep -i x-served-by
X-Served-By: web2

$ ansible-playbook site.yml
PLAY [Web servers] *************************************************************

TASK [Gathering Facts] *********************************************************
ok: [web2]
ok: [web1]

TASK [Install nginx] ***********************************************************
ok: [web1]
ok: [web2]

TASK [Start nginx now and at every boot] ***************************************
ok: [web2]
ok: [web1]

TASK [Site configuration] ******************************************************
ok: [web2]
ok: [web1]

TASK [Home page] ***************************************************************
ok: [web2]
ok: [web1]

PLAY RECAP *********************************************************************
web1                       : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
web2                       : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

The first run changed the configuration, so RUNNING HANDLER [Reload nginx] appeared at the end. The second run changed nothing, so the handler didn't run at all. reloaded makes nginx re-read its configuration without dropping connections.ครั้งแรกการตั้งค่าเปลี่ยน จึงมี RUNNING HANDLER [Reload nginx] ตอนท้าย ครั้งที่สองไม่มีอะไรเปลี่ยน handler จึงไม่รันเลย reloaded ทำให้ nginx อ่านการตั้งค่าใหม่โดยไม่ตัดการเชื่อมต่อที่มีอยู่

Lesson 6 · Write playbooksบทที่ 6 · เขียน playbook

Loops, conditions and resultsลูป เงื่อนไข และผลลัพธ์

A second play runs on all servers. loop repeats a task for each item of a list, with the item in item. when runs a task only if a condition is true; group_names lists the groups of the current host. register saves a task's result in a variable for later tasks, and changed_when: false tells Ansible that this command only reads. play ที่สองรันบนเซิร์ฟเวอร์ทั้งหมด (all) loop ทำ task ซ้ำสำหรับแต่ละรายการในลิสต์ โดยรายการนั้นอยู่ใน item when รัน task เฉพาะเมื่อเงื่อนไขเป็นจริง group_names คือรายชื่อกลุ่มของ host ปัจจุบัน register เก็บผลของ task ไว้ในตัวแปรให้ task ถัดไปใช้ และ changed_when: false บอก Ansible ว่าคำสั่งนี้แค่อ่านข้อมูล

Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ ansible-playbook site.yml
⋯ 22 lines: the Web servers play, all ok ⋯⋯ 22 บรรทัด: play ของ Web servers ซึ่ง ok ทั้งหมด ⋯
PLAY [Every server] ************************************************************

TASK [Gathering Facts] *********************************************************
ok: [db1]
ok: [web1]
ok: [web2]

TASK [Team accounts] ***********************************************************
changed: [web1] => (item={'name': 'mya', 'groups': 'sudo'})
changed: [web2] => (item={'name': 'mya', 'groups': 'sudo'})
changed: [db1] => (item={'name': 'mya', 'groups': 'sudo'})
changed: [web2] => (item={'name': 'somchai', 'groups': 'sudo'})
changed: [web1] => (item={'name': 'somchai', 'groups': 'sudo'})
changed: [db1] => (item={'name': 'somchai', 'groups': 'sudo'})
changed: [web2] => (item={'name': 'aung', 'groups': 'users'})
changed: [db1] => (item={'name': 'aung', 'groups': 'users'})
changed: [web1] => (item={'name': 'aung', 'groups': 'users'})

TASK [Login message on web servers] ********************************************
skipping: [db1]
changed: [web1]
changed: [web2]

TASK [Check free disk space] ***************************************************
ok: [web2]
ok: [db1]
ok: [web1]

TASK [Show it] *****************************************************************
ok: [web1] => {
    "msg": "overlay         453G   41G  389G  10% /"
}
ok: [web2] => {
    "msg": "overlay         453G   41G  389G  10% /"
}
ok: [db1] => {
    "msg": "overlay         453G   41G  389G  10% /"
}

PLAY RECAP *********************************************************************
db1                        : ok=4    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
web1                       : ok=10   changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
web2                       : ok=10   changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

$ ansible all -a "id mya"
web1 | CHANGED | rc=0 >>
uid=1002(mya) gid=1002(mya) groups=1002(mya),27(sudo)
web2 | CHANGED | rc=0 >>
uid=1002(mya) gid=1002(mya) groups=1002(mya),27(sudo)
db1 | CHANGED | rc=0 >>
uid=1002(mya) gid=1002(mya) groups=1002(mya),27(sudo)

Three users on three servers: nine results. db1 was skipping the login message because it isn't in web. df reads the disk of the Docker machine that all three containers share, so the numbers are the same.ผู้ใช้สามคนบนเซิร์ฟเวอร์สามเครื่อง ได้ผลลัพธ์เก้ารายการ db1 ข้าม (skipping) ข้อความต้อนรับ เพราะไม่ได้อยู่ในกลุ่ม web ส่วน df อ่านดิสก์ของเครื่อง Docker ที่ container ทั้งสามใช้ร่วมกัน ตัวเลขจึงเหมือนกัน

Lesson 7 · Organise and protectบทที่ 7 · จัดระเบียบและปกป้อง

Roles: reusable building blocksRole: ชิ้นส่วนที่นำกลับมาใช้ได้

As a playbook grows, group what belongs together into a role: a folder with a fixed layout for tasks, handlers, templates and default values. A role can be used by many playbooks and shared on Ansible Galaxy. ansible-galaxy creates the layout: เมื่อ playbook ใหญ่ขึ้น ให้รวมสิ่งที่เกี่ยวข้องกันไว้ใน role ซึ่งเป็นโฟลเดอร์ที่มีโครงสร้างตายตัวสำหรับ task, handler, เทมเพลต และค่าเริ่มต้น role หนึ่งตัวใช้ได้กับหลาย playbook และแชร์บน Ansible Galaxy ได้ ansible-galaxy สร้างโครงสร้างนี้ให้

Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ ansible-galaxy role init roles/nginx
- Role roles/nginx was created successfully

Move the four nginx tasks into tasks/main.yml, the handler into handlers/main.yml and both templates into templates/. Inside a role, src: looks in the role's own templates/ folder. defaults/main.yml holds fallback values with the lowest priority, so group_vars/web.yml still wins. ย้าย task ของ nginx ทั้งสี่ตัวไปไว้ใน tasks/main.yml ย้าย handler ไปไว้ใน handlers/main.yml และย้ายเทมเพลตทั้งสองไปไว้ใน templates/ ภายใน role src: จะหาไฟล์ในโฟลเดอร์ templates/ ของ role เอง defaults/main.yml เก็บค่าสำรองที่มีลำดับความสำคัญต่ำที่สุด ค่าใน group_vars/web.yml จึงยังชนะ

Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ find roles -type f | sort
roles/nginx/README.md
roles/nginx/defaults/main.yml
roles/nginx/handlers/main.yml
roles/nginx/meta/main.yml
roles/nginx/tasks/main.yml
roles/nginx/templates/index.html.j2
roles/nginx/templates/shop.conf.j2
roles/nginx/tests/inventory
roles/nginx/tests/test.yml
roles/nginx/vars/main.yml

$ ansible-playbook site.yml
PLAY [Web servers] *************************************************************

TASK [Gathering Facts] *********************************************************
ok: [web2]
ok: [web1]

TASK [nginx : Install nginx] ***************************************************
ok: [web2]
ok: [web1]

TASK [nginx : Start nginx now and at every boot] *******************************
ok: [web2]
ok: [web1]

TASK [nginx : Site configuration] **********************************************
ok: [web1]
ok: [web2]

TASK [nginx : Home page] *******************************************************
ok: [web2]
ok: [web1]
⋯ 40 lines: the Every server play, all ok ⋯⋯ 40 บรรทัด: play ของ Every server ซึ่ง ok ทั้งหมด ⋯
PLAY RECAP *********************************************************************
db1                        : ok=4    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
web1                       : ok=10   changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
web2                       : ok=10   changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Task names now start with the role, nginx :. changed=0 everywhere proves the refactor: the playbook is organised differently but describes exactly the same servers. README.md, meta/, tests/ and vars/ came from the skeleton and can stay empty.ตอนนี้ชื่อ task ขึ้นต้นด้วยชื่อ role คือ nginx : changed=0 ทุกเครื่องเป็นหลักฐานว่าการจัดโครงสร้างใหม่นี้ถูกต้อง playbook ถูกจัดต่างไปแต่อธิบายเซิร์ฟเวอร์แบบเดิมทุกประการ README.md, meta/, tests/ และ vars/ มาจากโครงสร้างตั้งต้นและปล่อยว่างไว้ได้

Lesson 8 · Organise and protectบทที่ 8 · จัดระเบียบและปกป้อง

Ansible Vault: secrets in the repositoryAnsible Vault: ความลับใน repository

The database server needs a password, and the playbooks live in Git. Ansible Vault encrypts a file (or a single value) with a password, so the encrypted file can be committed and Ansible decrypts it while it runs. A common pattern: the real secret sits in vault.yml as vault_db_password, and the readable vars.yml next to it points to it, so you can still search for db_password. เซิร์ฟเวอร์ฐานข้อมูลต้องใช้รหัสผ่าน และ playbook อยู่ใน Git Ansible Vault เข้ารหัสไฟล์ (หรือค่าเดียว) ด้วยรหัสผ่าน ไฟล์ที่เข้ารหัสแล้วจึง commit ได้ และ Ansible จะถอดรหัสขณะรัน รูปแบบที่ใช้กันบ่อยคือเก็บความลับจริงไว้ใน vault.yml ในชื่อ vault_db_password แล้วให้ vars.yml ที่อ่านได้ข้างๆ ชี้ไปหามัน เราจึงยังค้นหา db_password ได้

group_vars/db/vault.yml before encryption:ก่อนเข้ารหัส

Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ echo "lab-vault-password" > .vault_pass && chmod 600 .vault_pass

$ ansible-vault encrypt group_vars/db/vault.yml

$ head -n 3 group_vars/db/vault.yml
$ANSIBLE_VAULT;1.1;AES256
38336633333761326230633333363134336234653863373433373430623161353465653935386434
3761333633653563373430346639346635396234383736640a336232353731326232383066636133

$ ansible-vault view group_vars/db/vault.yml
# encrypted with ansible-vault; Ansible decrypts it when it runs
vault_db_password: S3cret-from-the-vault

$ ansible-playbook site.yml --limit db
PLAY [Web servers] *************************************************************
skipping: no hosts matched

PLAY [Every server] ************************************************************
⋯ 20 lines: the Every server play, all ok ⋯⋯ 20 บรรทัด: play ของ Every server ซึ่ง ok ทั้งหมด ⋯
PLAY [Database servers] ********************************************************

TASK [Gathering Facts] *********************************************************
ok: [db1]

TASK [Database settings for the app, readable only by root] ********************
changed: [db1]

PLAY RECAP *********************************************************************
db1                        : ok=6    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

$ ansible db1 -a "cat /etc/shop-db.env"
[ERROR]: Task failed: Module failed: The command exited with a non-zero return code.
Origin: <adhoc 'command' task>

{'action': 'command', 'args': {'_raw_params': 'cat /etc/shop-db.env'}, 'timeout': 0, 'async_val': 0, 'poll': 15}

db1 | FAILED | rc=1 >>
cat: /etc/shop-db.env: Permission deniedThe command exited with a non-zero return code.

$ ansible db1 -b -a "cat /etc/shop-db.env"
db1 | CHANGED | rc=0 >>
DB_NAME=shop
DB_USER=shop_app
DB_PASSWORD=S3cret-from-the-vault

On disk, vault.yml is now AES-256 ciphertext; ansible-vault view and the playbook decrypt it with the password file. --limit db ran only on the db group. The settings file reached the server with the real password, and with mode 0600 only root can read it: deploy got Permission denied.บนดิสก์ vault.yml กลายเป็นข้อความที่เข้ารหัสด้วย AES-256 แล้ว ส่วน ansible-vault view และ playbook ถอดรหัสด้วยไฟล์รหัสผ่าน --limit db รันเฉพาะกลุ่ม db ไฟล์ตั้งค่าไปถึงเซิร์ฟเวอร์พร้อมรหัสผ่านจริง และด้วยสิทธิ์ 0600 มีเพียง root ที่อ่านได้ deploy จึงได้ Permission denied

Password fileไฟล์รหัสผ่าน

Commit the encrypted vault.yml, never .vault_pass: it is in .gitignore. In CI, write the vault password from the CI system's secret store into that file, or use --ask-vault-pass when you run by hand.ให้ commit ไฟล์ vault.yml ที่เข้ารหัสแล้ว แต่ห้าม commit .vault_pass ซึ่งอยู่ใน .gitignore แล้ว ใน CI ให้เขียนรหัสผ่านของ vault จากที่เก็บความลับของระบบ CI ลงไฟล์นั้น หรือใช้ --ask-vault-pass เมื่อรันด้วยมือ

Lesson 9 · Your turnบทที่ 9 · ถึงตาคุณแล้ว

Practiceแบบฝึกหัด

Three tasks on the lab servers. Each solution was run; the output is under it. งานสามข้อบนเซิร์ฟเวอร์ของแล็บ ทุกเฉลยรันจริงแล้ว ผลลัพธ์อยู่ด้านล่าง

1. Is nginx running everywhere?1. nginx รันอยู่ทุกเครื่องหรือไม่

With one ad-hoc command, ask every server whether the nginx service is active.ใช้คำสั่ง ad-hoc คำสั่งเดียว ถามทุกเซิร์ฟเวอร์ว่าบริการ nginx ทำงานอยู่หรือไม่

Show solutionดูเฉลย
Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ ansible all -b -a "systemctl is-active nginx"
web2 | CHANGED | rc=0 >>
active
[ERROR]: Task failed: Module failed: The command exited with a non-zero return code.
Origin: <adhoc 'command' task>

{'action': 'command', 'args': {'_raw_params': 'systemctl is-active nginx'}, 'timeout': 0, 'async_val': 0, 'poll': 15}

db1 | FAILED | rc=4 >>
inactiveThe command exited with a non-zero return code.
web1 | CHANGED | rc=0 >>
active

db1 has no nginx, so systemctl printed inactive and exited with code 4, which Ansible reports as FAILED. Here that is the answer, not a problem. -b is needed because these minimal servers have no D-Bus, so only root can ask systemd.db1 ไม่มี nginx systemctl จึงพิมพ์ inactive และจบด้วยรหัส 4 ซึ่ง Ansible รายงานเป็น FAILED ในกรณีนี้นั่นคือคำตอบ ไม่ใช่ปัญหา ต้องใช้ -b เพราะเซิร์ฟเวอร์ขนาดเล็กเหล่านี้ไม่มี D-Bus จึงมีแค่ root ที่ถาม systemd ได้

2. Try a change on one server2. ลองเปลี่ยนบนเซิร์ฟเวอร์เครื่องเดียว

Give web2 the title Beta without editing any file, and leave web1 alone.ตั้งชื่อหน้าเว็บของ web2 เป็น Beta โดยไม่แก้ไฟล์ใดๆ และไม่แตะ web1

Show solutionดูเฉลย
Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ ansible-playbook site.yml --limit web2 -e site_title=Beta
PLAY [Web servers] *************************************************************

TASK [Gathering Facts] *********************************************************
ok: [web2]

TASK [nginx : Install nginx] ***************************************************
ok: [web2]

TASK [nginx : Start nginx now and at every boot] *******************************
ok: [web2]

TASK [nginx : Site configuration] **********************************************
ok: [web2]

TASK [nginx : Home page] *******************************************************
changed: [web2]
⋯ 25 lines: the other plays, all ok or skipped ⋯⋯ 25 บรรทัด: play อื่นๆ ซึ่ง ok หรือถูกข้ามทั้งหมด ⋯
PLAY RECAP *********************************************************************
web2                       : ok=10   changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

$ curl -s localhost:8101 | head -n 1; curl -s localhost:8102 | head -n 1
<h1>Shop</h1>
<h1>Beta</h1>

--limit narrows the hosts, and -e (an extra variable) beats every other source, including group_vars. Run the playbook again without -e to put Shop back.--limit จำกัดเครื่องที่รัน และ -e (ตัวแปรเพิ่มเติม) ชนะค่าจากทุกแหล่ง รวมถึง group_vars รัน playbook อีกครั้งโดยไม่ใส่ -e เพื่อเปลี่ยนกลับเป็น Shop

3. Tools for the database server3. เครื่องมือสำหรับเซิร์ฟเวอร์ฐานข้อมูล

Write a separate playbook that installs htop and curl on the db group only.เขียน playbook แยกที่ติดตั้ง htop และ curl บนกลุ่ม db เท่านั้น

Show solutionดูเฉลย
Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ ansible-playbook tools.yml
PLAY [Tools on the database server] ********************************************

TASK [Gathering Facts] *********************************************************
ok: [db1]

TASK [Install htop and curl] ***************************************************
changed: [db1]

PLAY RECAP *********************************************************************
db1                        : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

apt takes a list, so both packages install in one step.apt รับลิสต์ได้ แพ็กเกจทั้งสองจึงติดตั้งในขั้นเดียว

Lesson 10 · Avoiding troubleบทที่ 10 · หลีกเลี่ยงปัญหา

Common mistakesข้อผิดพลาดที่พบบ่อย

Using shell where a module existsใช้ shell ในที่ที่มี module อยู่แล้ว

shell and command run whatever you give them, every time. Here each run appended the line again and reported changed=1, so you can no longer trust the recap. lineinfile describes the result instead: the line should be in the file. It added the line once, then reported changed=0.shell และ command รันทุกอย่างที่เราให้ทุกครั้ง ในที่นี้การรันแต่ละครั้งเพิ่มบรรทัดซ้ำและรายงาน changed=1 เราจึงเชื่อ recap ไม่ได้อีกต่อไป ส่วน lineinfile อธิบายผลลัพธ์แทน คือบรรทัดนี้ควรอยู่ในไฟล์ มันเพิ่มบรรทัดครั้งเดียว แล้วรายงาน changed=0

✗ Wrong✗ ผิด
✓ Right✓ ถูก
Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ ansible-playbook mistakes/notes-shell.yml | grep changed=
$ ansible-playbook mistakes/notes-shell.yml | grep changed=
$ ansible web1 -a "cat /etc/shop-notes"
web1                       : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
web1                       : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
web1 | CHANGED | rc=0 >>
Backups run at 02:00
Backups run at 02:00

$ ansible-playbook mistakes/notes-lineinfile.yml | grep changed=
$ ansible-playbook mistakes/notes-lineinfile.yml | grep changed=
$ ansible web2 -a "cat /etc/shop-notes"
web2                       : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
web2                       : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
web2 | CHANGED | rc=0 >>
Backups run at 02:00

Forgetting becomeลืม become

Ansible logs in as deploy, an ordinary user. Installing packages, writing to /etc and managing services need root, so add become: true to the play (or to the task). The error says it plainly: are you root?Ansible ล็อกอินเป็น deploy ซึ่งเป็นผู้ใช้ธรรมดา การติดตั้งแพ็กเกจ การเขียนไฟล์ใน /etc และการจัดการบริการต้องใช้ root จึงต้องเพิ่ม become: true ที่ play (หรือที่ task) ข้อความผิดพลาดบอกไว้ชัดเจน: are you root?

Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ ansible-playbook mistakes/no-become.yml
PLAY [Wrong - installing a package without become] *****************************

TASK [Gathering Facts] *********************************************************
ok: [db1]

TASK [Install tree] ************************************************************
[ERROR]: Task failed: Module failed: '/usr/bin/apt-get -y -o "Dpkg::Options::=--force-confdef" -o "Dpkg::Options::=--force-confold" -o DPkg::Lock::Timeout=60       install 'tree=2.1.1-2ubuntu3.24.04.2'' failed: E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?

Origin: ~/ansible-lab/mistakes/no-become.yml:4:7

2   hosts: db1
3   tasks:
4     - name: Install tree
        ^ column 7

fatal: [db1]: FAILED! => {"cache_update_time": 1791231561, "cache_updated": false, "changed": false, "msg": "'/usr/bin/apt-get -y -o \"Dpkg::Options::=--force-confdef\" -o \"Dpkg::Options::=--force-confold\" -o DPkg::Lock::Timeout=60       install 'tree=2.1.1-2ubuntu3.24.04.2'' failed: E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)\nE: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?\n", "rc": 100, "stderr": "E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)\nE: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?\n", "stderr_lines": ["E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)", "E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?"], "stdout": "", "stdout_lines": []}

PLAY RECAP *********************************************************************
db1                        : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

Wrong indentationย่อหน้าผิด

In YAML, indentation is structure. state is one space short of name, so it belongs to nothing. Run --syntax-check before a real run (and in CI); it points at the line and column.ใน YAML การย่อหน้าคือโครงสร้าง state ย่อหน้าน้อยกว่า name หนึ่งช่อง จึงไม่ได้เป็นของอะไรเลย ให้รัน --syntax-check ก่อนรันจริง (และใน CI) มันจะชี้ไปที่บรรทัดและคอลัมน์ที่ผิด

Terminal, in ~/ansible-labเทอร์มินัล ใน ~/ansible-labansible-core 2.21 · Ubuntu 24.04
$ ansible-playbook mistakes/indent.yml --syntax-check
[ERROR]: YAML parsing failed: While parsing a block mapping did not find expected key.
Origin: ~/ansible-lab/mistakes/indent.yml:7:8

5       ansible.builtin.apt:
6         name: nginx
7        state: present
         ^ column 8

Passwords in plain textรหัสผ่านที่เป็นข้อความธรรมดา

Anything committed to Git stays in its history, even after you delete it. Keep secrets in a vault file from the start (Lesson 8), and add no_log: true to tasks whose output would print a secret.ทุกอย่างที่ commit ลง Git จะอยู่ในประวัติตลอดไป แม้จะลบทิ้งภายหลัง ให้เก็บความลับในไฟล์ vault ตั้งแต่แรก (บทที่ 8) และเพิ่ม no_log: true ให้ task ที่ผลลัพธ์อาจพิมพ์ความลับออกมา

✗ Wrong✗ ผิด
✓ Right✓ ถูก

Lesson 11 · Referenceบทที่ 11 · อ้างอิง

Glossaryคำศัพท์

EnglishไทยMeaningความหมาย
control nodecontrol nodeThe computer Ansible runs onคอมพิวเตอร์ที่ Ansible รันอยู่
managed node / hostmanaged node / hostA server Ansible configuresเซิร์ฟเวอร์ที่ Ansible ตั้งค่า
inventory / groupinventory / กลุ่มThe list of hosts / a named set of themรายการของ host / ชุดของ host ที่มีชื่อ
modulemoduleA small program that does one job on a host, such as apt or copyโปรแกรมเล็กๆ ที่ทำงานหนึ่งอย่างบน host เช่น apt หรือ copy
ad-hoc commandคำสั่ง ad-hocOne module run from the terminal, without a playbookการรัน module หนึ่งตัวจากเทอร์มินัล โดยไม่ใช้ playbook
task / play / playbooktask / play / playbookOne module call / hosts plus their tasks / a file of playsการเรียก module หนึ่งครั้ง / host พร้อม task ของมัน / ไฟล์ที่รวม play
idempotentidempotentSafe to repeat: a second run changes nothingทำซ้ำได้อย่างปลอดภัย รันครั้งที่สองแล้วไม่มีอะไรเปลี่ยน
factsfactInformation Ansible collects about each hostข้อมูลที่ Ansible เก็บจากแต่ละ host
becomebecomeRun as another user, usually root through sudoรันในนามผู้ใช้อื่น ส่วนใหญ่คือ root ผ่าน sudo
group_varsgroup_varsVariables for every host in a groupตัวแปรสำหรับทุก host ในกลุ่ม
template (Jinja2)เทมเพลต (Jinja2)A file with placeholders filled in for each hostไฟล์ที่มีช่องว่างซึ่งถูกเติมค่าสำหรับแต่ละ host
handler / notifyhandler / notifyA task that runs only when another task reports a changetask ที่รันเฉพาะเมื่อ task อื่นรายงานว่ามีการเปลี่ยนแปลง
roleroleA reusable folder of tasks, handlers, templates and defaultsโฟลเดอร์ที่นำกลับมาใช้ได้ ซึ่งรวม task, handler, เทมเพลต และค่าเริ่มต้น
Ansible VaultAnsible VaultEncrypts files or values so secrets can live in Gitเข้ารหัสไฟล์หรือค่า เพื่อให้เก็บความลับไว้ใน Git ได้
check mode / diffcheck mode / diffA dry run / showing what would change in filesการรันแบบทดลอง / การแสดงว่าไฟล์จะเปลี่ยนอย่างไร