Skip to content

บทที่ 1 — Linux + Shell

← บทที่ 0 | สารบัญ | บทที่ 2 →

TL;DR: บทนี้ตอบ "ทำไม dev ทุกคนต้องรู้ Linux + shell" — filesystem (ระบบไฟล์), permission (สิทธิ์), process (โปรเซส), redirect/pipe (เปลี่ยนทาง/ต่อท่อ output), shell script, SSH (login เข้า server ระยะไกล), systemd (ตัวจัดการ service), cron (ตัวตั้งเวลา). ข้ามได้ถ้า: ใช้ Linux ในงานจริงมาแล้ว 1+ ปีและเขียน bash script ได้ (บทนี้ออกแบบมาสำหรับมือใหม่ที่ยังไม่เคยใช้ command line)

หลังจบบท คุณจะ:

  • ใช้ command line คล่อง — file, process, network
  • เข้าใจ permission + ownership (rwx + ทำไม chmod 755)
  • เขียน shell script พื้นฐานที่ใช้ใน production ได้
  • Debug + troubleshoot server จริงเป็น
  • ใช้ vim/nano เพื่อแก้ไฟล์บน server

ทุก server ใน production = Linux (เกือบ) — รู้ Linux = พื้นฐาน DevOps


0. ก่อนเริ่ม — Prerequisites + Setup

คุณควรมี:

  • ✅ เครื่องที่เปิด terminal ได้ (Mac, Linux, หรือ Windows + WSL2)
  • ✅ ติด Ubuntu บน WSL2 ถ้าเป็น Windows (wsl --install)
  • ✅ ไม่จำเป็นต้องเคยใช้ command line มาก่อน — เริ่มจากศูนย์ได้

💡 WSL2 = Windows Subsystem for Linux 2 — ระบบของ Microsoft ที่ให้รัน Linux kernel จริงบน Windows (ผ่าน virtual machine แบบเบา) ใช้ command line ของ Linux ได้ครบ โดยไม่ต้องลง dual-boot

สำหรับ Windows: ทำตามนี้ก่อน

powershell
# PowerShell as administrator (เปิด PowerShell แบบ run as admin)
wsl --install -d Ubuntu-24.04
# restart เครื่อง → เปิด Ubuntu จาก Start menu
# หมายเหตุ: ชื่อ distro เปลี่ยนตามปี — ดู `wsl --list --online` เพื่อเลือก LTS ล่าสุด

⚠️ ถ้าเจอ error "WSL feature is not enabled" ให้รันคำสั่งนี้ก่อนใน PowerShell as administrator แล้ว restart เครื่อง แล้วรัน wsl --install ใหม่:

powershell
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

หรือดูขั้นตอนแบบมีรูปได้ที่ https://docs.microsoft.com/en-us/windows/wsl/install

WSL2 = Linux จริง ๆ ที่ run อยู่บน Windows — ทุก command ในบทนี้ใช้ได้เหมือนกับ server จริง


0.2 Lab 0 — ทำมือก่อนทฤษฎี (5 นาที)

ถ้าคุณใช้ Windows + IDE มาตลอด และยังไม่เคยใช้ shell จริงจัง — ทำตามนี้ก่อนอ่านต่อ จะเข้าใจคำว่า "ทุกอย่างคือไฟล์" และวิธี "รวม error log + output log เข้าด้วยกัน" (ที่ shell เขียนสั้น ๆ ว่า 2>&1) ในข้อถัดไปง่ายขึ้นมาก

เปิด WSL2 Ubuntu (Windows) / Terminal (Mac/Linux) แล้วลอง:

bash
# 1. สร้างไฟล์ทดสอบ
echo "hello world" > test.txt

# 2. อ่านไฟล์ทดสอบ
cat test.txt
#   → hello world

# 3. ลอง command ที่ "สำเร็จ" และ "ล้มเหลว"
ls test.txt               # สำเร็จ — แสดง test.txt
ls notexist.txt           # ล้มเหลว — ขึ้น "No such file or directory"

# 4. แยก output 2 ทาง: ปกติ (stdout) + error (stderr)
ls test.txt notexist.txt > out.txt 2> err.txt
#   stdout (1) ไปไฟล์ out.txt
#   stderr (2) ไปไฟล์ err.txt

cat out.txt               # → test.txt  (อันที่สำเร็จ)
cat err.txt               # → ls: cannot access 'notexist.txt': No such file or directory

# 5. รวมทั้ง 2 ไปไฟล์เดียว
ls test.txt notexist.txt > all.txt 2>&1
cat all.txt               # → ทั้ง 2 บรรทัดอยู่ในไฟล์เดียวกัน

# 6. เก็บกวาด
rm test.txt out.txt err.txt all.txt

ตอนนี้คุณ "เห็น" แล้วว่า:

  • > เปลี่ยน "ทางออก" ของ output ปกติไปยังไฟล์ (ไม่ขึ้นบนจอ)
  • 2> เปลี่ยน "ทางออก" ของ error ไปยังไฟล์
  • 2>&1 = "ส่ง error ไปที่เดียวกับ output ปกติ"
  • เลข 1 และ 2 คือ "หมายเลขทางออก" — ศัพท์ทางการเรียกว่า "file descriptor" (fd) คือเลขประจำตัวที่ระบบใช้อ้างถึงไฟล์/ช่องทาง I/O ที่เปิดอยู่ (รายละเอียดในข้อ 0.5)

→ ทฤษฎีในข้อ 0.3 ข้างล่างจะเข้าใจง่ายขึ้นเพราะคุณเจอ pattern นี้แล้ว


0.3 Mental Model — "ทุกอย่างคือไฟล์"

ก่อนจำ command, ฝัง concept นี้ในหัว — มันเป็น ปรัชญาหลักของ Unix/Linux:

text
ใน Linux:
- ไฟล์ปกติ                → ไฟล์
- โฟลเดอร์ (directory)    → ไฟล์ (ที่ list ไฟล์ข้างใน)
- harddisk (ดิสก์เก็บข้อมูล) → ไฟล์ (/dev/sda)
- keyboard/mouse          → ไฟล์ (/dev/input/...)
- process ที่กำลังรันอยู่  → ไฟล์ (/proc/1234/)
- network socket          → file descriptor
- pipe ระหว่าง process     → ไฟล์

💡 คำศัพท์พื้นฐานของ Linux — เจอบ่อยตลอดบท:

  • file descriptor (fd) = เลขที่ระบบใช้อ้างไฟล์/socket ที่เปิดอยู่ (เช่น fd 0=stdin, 1=stdout, 2=stderr) — คล้าย "หมายเลขล็อกเกอร์" สำหรับช่อง I/O ที่เปิดอยู่
  • PID (Process ID) = เลขประจำตัวของ process ที่รันอยู่ (เช่น kill 1234 คือสั่งหยุด process หมายเลข 1234)
  • UID / GID (User ID / Group ID) = เลขประจำตัวของ user / group (root = UID 0)
  • mode bits = bit ที่เก็บสิทธิ์ของไฟล์ (rwx — อ่าน/เขียน/รัน) เห็นในข้อ Permission

ทำไมสำคัญ: ถ้าทุกอย่างคือไฟล์ → ใช้ command เดียวกัน (cat, >, <) จัดการทุกอย่างได้

bash
# echo "hello" > /dev/tty1   # เขียนไปที่ terminal หมายเลข 1 (ต้องเป็น root + มี console จริง)
cat /proc/cpuinfo              # อ่านข้อมูล CPU เหมือนอ่านไฟล์
cat /proc/meminfo              # อ่านข้อมูลหน่วยความจำ (RAM)

→ Linux ไม่มี "API พิเศษ" สำหรับ device — ใช้ file API กับทุกอย่าง


1. ทำไมต้อง Linux

  • 99% server ใน cloud = Linux
  • Docker container base = Linux
  • Kubernetes node = Linux
  • SSH เข้า server = ใช้ shell ของ Linux

→ ไม่รู้ Linux = ทำ DevOps ไม่ได้


2. Linux Distribution

text
Linux Kernel
├── Debian-based
│   ├── Ubuntu (popular, friendly)
│   └── Debian (stable, lean)
├── Red Hat-based
│   ├── RHEL (enterprise, paid)
│   ├── CentOS Stream (free)
│   ├── Rocky Linux (RHEL-compatible, free)
│   └── Fedora (cutting edge)
└── Alpine (ใช้ libc เล็กชื่อ musl, minimal, นิยมเป็น base ของ Docker image)

💡 musl (อ่าน "มัสเซิล") = ไลบรารี C พื้นฐาน (libc) เวอร์ชันเล็ก/เบา ที่ Alpine ใช้แทนตัวมาตรฐาน (glibc — GNU C Library, libc ที่ distro ส่วนใหญ่ใช้) — ทำให้ image เล็กมาก เหมาะกับ Docker แต่บางโปรแกรมที่ผูกกับ glibc อาจมีปัญหา

มือใหม่ — เริ่มด้วย Ubuntu 24.04 LTS (Noble Numbat — Ubuntu ตั้งชื่อรุ่นเป็นคู่คำคล้องจอง+ชื่อสัตว์ เรียงตามตัวอักษร ไม่ต้องจำก็ได้ ใช้เลขเวอร์ชัน 24.04 พอ, release เมษา 2024 — support ถึง 2029 free, 2034 ESM)

⚠️ ณ เวลาอ่านหนังสือเล่มนี้อาจมี LTS รุ่นใหม่กว่าออกมาแล้ว เช็ครุ่นล่าสุดที่ ubuntu.com/about/release-cycle ก่อนติดตั้งจริง — ตัวอย่างในบทนี้ใช้ 24.04 ณ เวลาที่เขียน

💡 LTS (Long Term Support) = version ที่ Ubuntu สัญญาจะปล่อย security update นานหลายปี — ใช้ใน production ทุกที่. release ปกติ (เช่น 23.10, 24.10) ออกทุก 6 เดือนแต่ support แค่ 9 เดือน — ห้ามใช้ใน server


3. Filesystem

Linux จัดทุกอย่างเป็น "ต้นไม้" เดียวที่เริ่มจาก / (root) — ไม่มี drive C:/D: แบบ Windows การรู้ว่าโฟลเดอร์มาตรฐานแต่ละตัวเก็บอะไร (เช่น config อยู่ /etc, log อยู่ /var/log) ช่วยให้หาไฟล์และ debug ระบบได้เร็วขึ้นมาก:

text
/                           ← root (รากของระบบ)
├── bin/                     ← โปรแกรมพื้นฐานที่ผู้ใช้ทั่วไปใช้ได้ (ls, cat)
├── boot/                    ← kernel + bootloader (ไฟล์ที่ใช้ตอน boot)
├── dev/                     ← device file (ไฟล์อ้างถึงอุปกรณ์ เช่น /dev/sda)
├── etc/                     ← config ของระบบ (เช่น nginx.conf, hosts)
├── home/                    ← home directory ของแต่ละ user
│   └── alice/                ← home ของ user "alice" (เรียกย่อว่า ~)
├── lib/                     ← library (ไลบรารีที่โปรแกรมใน /bin, /sbin ใช้)
├── media/                   ← จุด mount ของสื่อภายนอก (USB, CD)
├── mnt/                     ← จุด mount ชั่วคราว (admin ใช้ mount เอง)
├── opt/                     ← software เสริมจาก 3rd party (เช่น Chrome, Slack)
├── proc/                    ← ข้อมูล process (virtual = ไม่ใช่ไฟล์จริงบนดิสก์ แต่ kernel สร้างให้อ่านเหมือนไฟล์)
├── root/                    ← home ของ user "root" (admin สูงสุด)
├── sbin/                    ← โปรแกรมระบบสำหรับ admin (ใช้ sudo)
├── srv/                     ← ข้อมูลของ service (เช่น web, ftp)
├── sys/                     ← ข้อมูล kernel + อุปกรณ์ (virtual เช่นเดียวกับ /proc)
├── tmp/                     ← ไฟล์ชั่วคราว (ล้างเมื่อ reboot)
├── usr/                     ← โปรแกรม + ข้อมูลของ user ทั่วไป
│   ├── bin/                  ← โปรแกรมส่วนใหญ่อยู่ตรงนี้
│   ├── local/                ← ของที่ติดตั้งเอง (ไม่ใช่ผ่าน package manager)
│   └── share/                ← ข้อมูลที่ใช้ร่วม (manual, icon)
└── var/                     ← ข้อมูลที่เปลี่ยนแปลงตลอด (variable)
    ├── log/                  ← log ของระบบและ service
    ├── lib/                  ← state ถาวร (เช่น database)
    └── www/                  ← ข้อมูล web server

4. Navigation

ทักษะพื้นฐานที่สุดบน terminal คือ "เดิน" ไปมาในระบบไฟล์และ "หา" ของให้เจอ — กลุ่มคำสั่งนี้ (cd/ls สำหรับเดินดู, find/grep สำหรับค้นหาไฟล์และเนื้อหา) คือเครื่องมือที่คุณจะใช้บ่อยที่สุดทุกวัน:

bash
pwd                  # แสดง directory ปัจจุบัน
cd /var/log           # เปลี่ยน directory
cd ..                 # ขึ้นไป directory แม่
cd ~                  # ไป home
cd -                  # กลับ directory ก่อนหน้า

ls                    # list ไฟล์
ls -la                # ทุกไฟล์ + รายละเอียด
ls -la /etc/          # ระบุ path
ls -lh                # ขนาดอ่านง่าย (KB/MB)
ls -ltr               # เรียงตามเวลา ย้อนกลับ (เก่าสุดขึ้นก่อน)

# ค้นหาไฟล์
find / -name "*.log" 2>/dev/null         # หาไฟล์ตามชื่อ
                                         # (2>/dev/null = ซ่อน error ที่เกิดจาก folder ที่เราไม่มีสิทธิ์เข้าถึง
                                         #  ถ้าไม่ใส่จะเห็น "Permission denied" เต็มหน้าจอจนผลลัพธ์หาย)
find / -xdev -name "*.log" 2>/dev/null   # -xdev = ไม่ข้าม filesystem (ไม่ลง /proc, /sys, mount ภายนอก)
find /var/log -mtime -7                  # แก้ไขภายใน < 7 วัน
find /var -size +100M                    # ใหญ่กว่า 100 MB
find . -type f -name "*.txt"             # เฉพาะไฟล์
find . -type d -name "src"               # เฉพาะ directory (โฟลเดอร์)

# ค้นหาในเนื้อหาไฟล์ — app.log ด้านล่างคือชื่อไฟล์ตัวอย่าง สลับเป็นไฟล์จริงที่คุณมี
# (ยังไม่มีไฟล์ทดสอบ ลองสร้างจาก Lab 0 ด้านบน หรือ echo "ERROR test" > app.log ก่อนก็ได้)
grep "ERROR" app.log
grep -r "TODO" .                         # ค้นทุก subfolder (recursive = ลึกลงทุกชั้น)
grep -i "error" app.log                  # ไม่สนตัวพิมพ์เล็ก/ใหญ่
grep -v "INFO" app.log                   # กลับเงื่อนไข (ตัดบรรทัดที่มี INFO ออก)
grep -A 3 "error" app.log                # โชว์ 3 บรรทัดหลังที่เจอ (After)
grep -B 3 "error" app.log                # 3 บรรทัดก่อนหน้า (Before)
grep -C 3 "error" app.log                # บริบท ก่อน+หลัง (Context)
grep -E "ERROR|WARN" app.log             # ใช้ regex แบบ extended

# ทางเลือกรุ่นใหม่ (ต้อง install ก่อน)
# sudo apt install ripgrep fd-find
rg "error" .                             # ripgrep — เร็วกว่า grep มาก
fd "\.log$"                              # fd — ใช้ regex (ไม่ใช่ glob เหมือน find)
# ⚠️ บน Debian/Ubuntu: คำสั่งจริงคือ `fdfind` (ชื่อ `fd` ชนกับ package เดิม)
#    ตั้ง alias ให้สะดวก:  alias fd=fdfind
fdfind -e log                            # หาไฟล์ extension .log (เขียนสั้นกว่า)

5. File Operations

ถัดจากการเดินดูคือการ "จัดการ" ไฟล์จริง ๆ — อ่าน (cat/less/tail -f), คัดลอก/ย้าย/ลบ (cp/mv/rm), สร้าง และแก้ไขด้วย editor บน terminal (nano/vim) กลุ่มคำสั่งนี้คือพื้นฐานที่ใช้ตอน deploy และดูแลเซิร์ฟเวอร์จริง:

bash
# อ่านไฟล์
cat file.txt
less file.txt              # ดูทีละหน้า (เลื่อนได้)
head file.txt              # 10 บรรทัดแรก
head -n 50 file.txt
tail file.txt              # 10 บรรทัดท้าย
tail -n 100 file.txt
tail -f file.log            # ตามดูแบบ real-time (เห็น log เพิ่มสด ๆ)
tail -F file.log            # -F ตามต่อแม้ไฟล์ถูก rotate (logrotate สร้างไฟล์ใหม่)

# คัดลอก / ย้าย / ลบ
cp src.txt dest.txt
cp -r src/ dest/            # recursive (ทั้ง directory)
mv old.txt new.txt          # เปลี่ยนชื่อหรือย้าย
rm file.txt
rm -rf directory/           # ลบทั้ง directory แบบบังคับ (⚠️ ระวัง!)

# สร้าง
touch newfile.txt
mkdir newdir
mkdir -p a/b/c               # สร้าง directory แม่ให้ด้วย

# เขียน
echo "hello" > file.txt       # เขียนทับ
echo "hello" >> file.txt       # ต่อท้าย

# แก้ไข
nano file.txt                 # ง่าย
vim file.txt                  # ทรงพลัง (เรียนรู้ยากกว่า)

⚠️ Vim เริ่มต้นในโหมด Normal (ไม่ใช่โหมดพิมพ์) — เมื่อเปิดไฟล์ด้วย vim ครั้งแรก ยังพิมพ์ข้อความไม่ได้ ต้องกด i ก่อนถึงจะพิมพ์ได้

  • ถ้ายังไม่ได้กด i แล้วพิมพ์ ตัวอักษรจะไม่ขึ้น (หรือทำ action แปลก ๆ)
  • ถ้าติดอยู่ใน Vim และออกไม่ได้: กด ESC แล้วพิมพ์ :q! แล้วกด Enter (ออกโดยไม่บันทึก)
bash
# คำสั่ง Vim พื้นฐาน
i        # เข้าโหมดพิมพ์ (insert mode = โหมดแก้ไขข้อความ)
ESC      # กลับโหมดปกติ
:w       # บันทึก
:q       # ออก
:wq      # บันทึก + ออก
:q!      # ออกโดยไม่บันทึก
/text    # ค้นหา
n        # ไปตัวที่เจอถัดไป
dd       # ลบบรรทัด
yy       # คัดลอกบรรทัด
p        # วาง
u        # undo
Ctrl+r   # redo
gg       # ไปบนสุด
G        # ไปล่างสุด

6. Permissions

อ่าน output ของ ls -l

bash
ls -l file.txt
# -rw-r--r-- 1 alice users 1024 May 18 10:00 file.txt
# ↑   ↑  ↑  ↑
# │   │  │  │
# │   │  │  └── others permission     (คนอื่น ๆ ที่เข้ามาในระบบ)
# │   │  └───── group permission      (สมาชิกของ group "users")
# │   └──────── owner permission      (alice)
# └──────────── type (- file, d directory, l link)

Permission ใน Linux มีแค่ 3 อย่าง

text
r = read    → อ่านไฟล์ได้ / list ไฟล์ใน folder ได้
w = write   → แก้ไฟล์ได้ / สร้าง+ลบไฟล์ใน folder ได้
x = execute → run ไฟล์ได้ (script/binary) / เข้า cd folder ได้

ตัวเลข rwx มาจากไหน — Binary System

ทุก permission = bit (เปิด/ปิด) → 3 bits = ค่า 0-7 ในเลขฐาน 8 (octal):

text
r w x   binary    octal
0 0 0  →  000  →  0  (---)
0 0 1  →  001  →  1  (--x)
0 1 0  →  010  →  2  (-w-)
0 1 1  →  011  →  3  (-wx)
1 0 0  →  100  →  4  (r--)
1 0 1  →  101  →  5  (r-x)
1 1 0  →  110  →  6  (rw-)
1 1 1  →  111  →  7  (rwx)

→ ที่มาของ "r=4, w=2, x=1" — ไม่ใช่เลขมั่ว, มาจาก binary bit value → บวกกัน: rwx = 4+2+1 = 7, rw- = 4+2 = 6, r-x = 4+1 = 5

chmod — 3 ตัวเลข = owner / group / others

bash
chmod 755 file              # rwxr-xr-x  (owner: รวมหมด, อื่น ๆ: อ่าน+run)
chmod 644 file              # rw-r--r--  (owner: rw, อื่น ๆ: r เท่านั้น) — default ของไฟล์
chmod 600 secret            # rw-------  (เฉพาะ owner) — สำหรับ SSH key
chmod 700 dir               # rwx------  (เฉพาะ owner เข้าได้) — folder ส่วนตัว
chmod 777 file              # rwxrwxrwx  ⚠️ NEVER ทำกับ production!

# Symbolic (เขียนแบบตัวอักษรแทนตัวเลข)
chmod u+x file              # u=user(owner) เพิ่มสิทธิ์ execute
chmod g-w file              # g=group ถอนสิทธิ์ write
chmod o=r file              # o=others ตั้งให้เหลือแค่ read
chmod a+r file              # a=all (user+group+others) เพิ่ม read ให้ทุกคน

# Recursive (ทำซ้ำลงทุก subfolder)
chmod -R 755 dir/

Special bit — setuid / setgid / sticky (chmod 4 หลัก)

💡 มือใหม่ข้ามส่วนนี้ได้ก่อน — นอกจาก 3 หลักปกติ (owner/group/others) Linux มีหลักแรก (special bits) ที่ใช้สำหรับ setuid, setgid, sticky ซึ่งใช้ในสถานการณ์เฉพาะเจาะจง กลับมาอ่านเมื่อจำเป็นจริง ๆ

นอกจาก rwx ปกติ Linux ยังมี bit พิเศษอีก 3 ตัว ที่ทำให้ chmod กลายเป็น 4 หลัก (หลักแรกคือ special):

bash
chmod 4755 binary           # setuid — รันแล้ว process ได้สิทธิ์ของ owner (เช่น /usr/bin/passwd)
chmod 2755 dir              # setgid บน directory — ไฟล์ใหม่ใน dir ได้ group เดียวกับ dir
chmod 1777 dir              # sticky bit — ลบไฟล์ได้เฉพาะเจ้าของไฟล์ (เช่น /tmp)
ls -ld /tmp                  # → drwxrwxrwt  ตัว t ท้าย = sticky

⚠️ setuid บน script เป็นช่องโหว่ความปลอดภัย — Linux ส่วนใหญ่ ignore setuid บน shell script. ใช้กับ binary เท่านั้นและด้วยความระมัดระวัง

ACL — สิทธิ์ละเอียดกว่า rwx (เมื่อ owner/group/others ไม่พอ)

bash
getfacl file.txt                    # ดู ACL ของไฟล์
setfacl -m u:bob:rw file.txt        # ให้ user "bob" อ่าน+เขียนได้ (นอกเหนือจาก owner/group)
setfacl -x u:bob file.txt           # ถอน ACL ของ bob

ACL = Access Control List — ให้สิทธิ์ user/group ทีละคนได้ ไม่จำกัดแค่ owner/group/others. systemd-tmpfiles และ container runtime จริงใช้กันบ่อย

chown

bash
chown alice file              # เปลี่ยน owner เป็น alice
chown alice:users file        # เปลี่ยน owner + group
chown -R alice:users dir/     # ทำซ้ำลงทุกชั้น

sudo

bash
sudo command                  # รันแบบ root
sudo -u other_user command    # รันในนามของ user อื่น
sudo -i                       # เปิด shell แบบ root
sudo !!                       # รันคำสั่งก่อนหน้าใหม่ในแบบ root
                              # ⚠️ !! ใช้ได้ใน bash/zsh ที่เปิด history expansion
                              #    บางสภาพแวดล้อม (script, sh ล้วน) จะไม่รู้จัก

7. Process

ทุกโปรแกรมที่รันอยู่คือ "process" — การดูว่ามีอะไรรันอยู่ (ps/top/htop), สั่งหยุด (kill), รันเบื้องหลัง (&/nohup) และเช็คทรัพยากร(free/df) คือสิ่งจำเป็นเวลาแอปค้าง กิน CPU/RAM เกิน หรือต้องรีสตาร์ต service บนเซิร์ฟเวอร์:

bash
# ดูรายการ process
ps aux                        # ทุก process
ps -ef                        # แบบ tree (เห็นพ่อ-ลูก)
ps aux | grep java            # หา process ของ java

# Top — ดูแบบ real-time
top
# ก่อนใช้ htop ครั้งแรกต้องลงก่อน:  sudo apt install htop
htop                          # ดีกว่า top (เลื่อน/sort/kill ได้ด้วยปุ่ม)

# สั่งหยุด process (kill)
kill 1234                     # SIGTERM (สัญญาณ 15) — ขอให้ปิดอย่างนุ่มนวล (default)
kill -9 1234                  # SIGKILL (สัญญาณ 9) — บังคับฆ่าทันที ⚠️ ใช้เป็นทางสุดท้าย
                              # SIGKILL ไม่ให้โอกาส process cleanup:
                              #   - ไฟล์ที่เปิดอยู่อาจเสีย / เขียนไม่ครบ
                              #   - DB transaction ค้างกลางคัน
                              #   - lock file ไม่ถูกลบ → restart ไม่ขึ้น
                              # ลำดับที่ถูก: kill (TERM) → รอ 5-10 วิ → kill -9 ถ้ายังไม่ตาย
killall java                  # ฆ่า process ชื่อ java ทั้งหมด (TERM)
pkill -f "node server.js"     # ฆ่าตาม pattern ของชื่อคำสั่งเต็ม (TERM)

# รันเบื้องหลัง (background)
./script.sh &                 # รันเบื้องหลัง
nohup ./script.sh &           # รันต่อแม้ logout ออกไป
disown                         # ตัดขาดจาก shell

# สลับ foreground / background
jobs                           # ดู job ที่รันเบื้องหลัง
fg %1                          # ดึง job 1 มาทำงานเบื้องหน้า
bg %1                          # ให้ job ทำงานต่อเบื้องหลัง
Ctrl+Z                         # พัก process ปัจจุบัน
Ctrl+C                         # หยุด process ปัจจุบัน

# ดูการใช้ทรัพยากร
free -h                        # หน่วยความจำ (RAM)
df -h                          # พื้นที่ดิสก์
du -sh /var/log/*              # ขนาดต่อ directory
uptime                         # ค่าเฉลี่ยภาระงาน (load average)

🚀 ขั้นสูง — ข้ามได้ cgroup v2 เป็นกลไกระดับ kernel ที่ใช้เบื้องหลังใน Docker/Kubernetes — มือใหม่ยังไม่ต้องใช้ กลับมาอ่านเมื่อเรียนถึงบท Container

bash
# cgroup v2 — กลไกของ kernel ที่จำกัด/วัดทรัพยากรต่อกลุ่ม process
# (เป็นรากฐานของ Docker, Podman, systemd resource limits — Ubuntu 22+/RHEL 9 ใช้ unified hierarchy
#  แปลว่า "จัดกลุ่ม resource ทุกชนิด (CPU/RAM/IO) ไว้ในต้นไม้เดียว" แทนที่จะแยกคนละต้นไม้แบบ cgroup v1)
systemd-cgls                   # แสดง process จัดกลุ่มตาม cgroup (สวยกว่า ps tree)
systemd-cgtop                  # top แต่จัดกลุ่มตาม cgroup
cat /sys/fs/cgroup/cgroup.controllers   # ดูว่า kernel เปิด controller อะไรบ้าง

8. I/O Redirection + Pipes

พลังที่แท้จริงของ shell คือการต่อคำสั่งเล็ก ๆ เข้าด้วยกัน — "redirect" (>/>>/2>) ส่ง output ไปเก็บในไฟล์, "pipe" (|) ส่ง output ของคำสั่งหนึ่งเป็น input ของอีกคำสั่ง ทำให้สร้าง one-liner ทรงพลังได้ เช่นกรอง log แบบ real-time นี่คือหัวใจของปรัชญา Unix "ทำอย่างเดียวให้ดี แล้วต่อกัน":

💡 เข้าใจ 2>&1 ก่อน: ทุกโปรแกรมมีช่องส่งออก 2 ช่อง — 1 = stdout (output ปกติ) และ 2 = stderr (ข้อความ error) 2>&1 แปลว่า "เอา stderr (2) ไปรวมกับ stdout (1)" → ทั้ง output ปกติและ error ไปที่เดียวกัน (เลข 1, 2 นี้คือ file descriptor ที่กางไว้ตอนต้นบท)

bash
# Redirect
command > file               # stdout (1) → file (เขียนทับ)
command >> file              # append (ต่อท้าย)
command 2> error.log          # stderr (2) → ไฟล์ error
command > out.log 2>&1        # ทั้ง stdout + stderr → ไฟล์เดียว (2>&1 = รวม stderr เข้า stdout)
command &> out.log             # เหมือนข้างบน (shell รุ่นใหม่)
command < input.txt           # stdin จากไฟล์

# Pipe (ส่ง output ของคำสั่งซ้ายเป็น input ของคำสั่งขวา)
ps aux | grep java | wc -l                   # นับจำนวน process java
cat file | sort | uniq | head                 # ต่อกันเป็นทอด ๆ
tail -f app.log | grep ERROR                  # กรอง log แบบ real-time

# /dev/null = ถังขยะ (ทิ้ง output ที่ไม่อยากเห็น)
command > /dev/null 2>&1                      # เงียบทั้ง output + error
command &>/dev/null                            # เหมือนกัน

9. Useful Commands

รวมคำสั่งที่ใช้บ่อยในงาน DevOps จริง แยกเป็นหมวด — เริ่มจากกลุ่ม network ที่ใช้ตอน debug การเชื่อมต่อ: ทดสอบเข้าถึงได้ไหม (ping/curl), เช็คว่ามีอะไรเปิด port อยู่ (ss/lsof) และตรวจ DNS (dig/nslookup):

Network

bash
ping google.com
curl https://example.com
curl -X POST -d '{"key":"val"}' -H "Content-Type: application/json" https://api.example.com
curl -I https://example.com                  # headers only
wget https://example.com/file.zip             # download

# Check ports — ดูว่ามีโปรแกรมไหนเปิด port อะไรค้างอยู่
ss -tlnp                       # ⭐ มาตรฐานปัจจุบัน (ติดมาทุก distro)
                               #   -t = TCP, -l = listening only,
                               #   -n = ไม่ resolve ชื่อ (เร็วกว่า), -p = แสดง process ที่เปิด
ss -tulnp                      # เพิ่ม -u = UDP ด้วย
# netstat -tlnp                # ⚠️ legacy — ไม่ติดมา default บน Ubuntu 18+/RHEL 8+
                               #    ต้อง `sudo apt install net-tools` ก่อน
lsof -i :8080                  # ใครเปิด port 8080 อยู่ (lsof = list open files)

# DNS — ระบบแปลงชื่อโดเมน (example.com) เป็น IP address (93.184.215.14)
dig example.com                # ⭐ ทันสมัยและละเอียดสุด
nslookup example.com           # legacy แต่ยังใช้ได้
host example.com               # สั้นที่สุด

# Network interfaces — ดู IP/MAC ของการ์ดเครือข่ายแต่ละใบ
ip addr                        # ⭐ มาตรฐานปัจจุบัน (ย่อ: `ip a`)
ip link                        # สถานะ link layer (up/down, MAC)
# ifconfig                     # ⚠️ legacy — ไม่ติดมา default บน Ubuntu 18+
                               #    ต้อง `sudo apt install net-tools` ก่อน

# Routes — ดูเส้นทางออกของ packet
ip route                       # ⭐ มาตรฐานปัจจุบัน (ย่อ: `ip r`)
traceroute example.com         # ไล่ดู hop ระหว่างทาง

Text Processing

🚀 โซนขั้นสูง — ข้ามได้ awk และ sed เป็นภาษาเล็ก ๆ ในตัวเองที่ทรงพลังแต่ syntax งง มือใหม่ยังไม่ต้องท่อง — รู้แค่ว่า "ใช้แยกคอลัมน์ / แทนที่ข้อความใน text ได้" ก็พอ แล้วกลับมาเปิดดูเป็นสูตรเมื่อต้องใช้จริง

bash
# sort + uniq
sort file.txt
sort -r                        # reverse — เรียงจากมากไปน้อย
sort -n                        # numeric — เรียงแบบตัวเลข (ไม่งั้น 10 มาก่อน 2)
sort -u                        # unique = sort แล้วตัดบรรทัดซ้ำ (เทียบเท่า sort | uniq)
sort file | uniq -c            # นับจำนวนซ้ำของแต่ละบรรทัด (count)

# cut — ตัดคอลัมน์ออกจากไฟล์ที่มี delimiter
cut -d',' -f1,3 file.csv       # delimiter=, → เอาเฉพาะ field 1 และ 3
cut -c1-10 file                # ตัดตัวอักษรที่ 1-10 ของแต่ละบรรทัด

# awk — จัดการคอลัมน์ (column manipulation)
awk '{print $1}' file          # พิมพ์คอลัมน์แรก
awk -F',' '{print $2}' file.csv   # ใช้ , เป็น delimiter แล้วเอา column 2
awk '$3 > 100 {print $1}' file  # filter — เอาเฉพาะแถวที่ column 3 > 100

# sed — แทนที่ข้อความ (replace/substitute)
sed 's/old/new/g' file          # แทนทุกครั้งที่เจอ (g = global)
sed -i 's/old/new/g' file       # in-place = แก้ในไฟล์เลย (ไม่ต้อง redirect)
sed -i.bak 's/old/new/g' file   # in-place แต่สำรองไฟล์เดิมเป็น file.bak

# wc — word count (นับ)
wc -l file                     # นับจำนวนบรรทัด (line count)
wc -w file                     # นับจำนวนคำ (word count)

# diff — เทียบไฟล์ 2 ไฟล์
diff file1 file2
diff -u file1 file2             # unified format (อ่านง่าย เหมือน git diff)

Archive

bash
# tar — สร้าง/แตก archive (ไฟล์รวม)
tar czf archive.tar.gz folder/        # สร้าง archive แบบ gzip (c=create, z=gzip, f=file)
tar xzf archive.tar.gz                # แตก archive (x=extract)
tar czf archive.tar.gz -C / etc/      # -C = เปลี่ยน directory ก่อน archive
                                      # (ตัวอย่างนี้: cd ไป / แล้ว archive etc/ → path ใน tar เริ่มที่ etc/)
tar tf archive.tar.gz                 # list — ดูรายชื่อไฟล์ใน archive (t=table)

# zip
zip -r archive.zip folder/            # -r = ทั้ง subfolder
unzip archive.zip

# Modern (เลือกใช้ถ้ามี — `sudo apt install p7zip-full` ก่อน)
7z a archive.7z folder/               # บีบอัดได้แน่นกว่า .tar.gz

Disk

bash
df -h                          # disk free
du -sh /var/log                # size of dir
du -h --max-depth=1 /var       # 1 level deep

10. Environment Variables

environment variable คือค่าตั้งค่าที่ส่งให้โปรแกรมผ่าน "สิ่งแวดล้อม" ของ shell แทนการ hardcode (เขียนค่าตายตัว) ลงในโค้ด ใช้เก็บ config ที่ต่างกันตาม environment (เช่น DATABASE_URL, PATH) — เพราะถ้า hardcode DB ของ production ไว้ในโค้ด พอ deploy ขึ้น server จริงจะต่อ DB ผิดตัวหรือรั่วรหัสผ่านออกไปกับ source code

นี่คือวิธีมาตรฐานของ 12-factor app (12-factor app = แนวทางมาตรฐานการออกแบบ app สำหรับ cloud ที่บอกว่า "แยก config ออกจาก code" เป็นหนึ่งใน 12 ข้อ) และจำเป็นมากเวลา deploy จริง:

bash
# Set
export PATH=$PATH:/opt/myapp/bin
export DATABASE_URL="postgresql://..."

# Read
echo $PATH
env                            # all env
printenv DATABASE_URL

# Inline
DATABASE_URL=postgres://... ./myapp

# Persist (Ubuntu)
echo 'export PATH=$PATH:/opt/myapp/bin' >> ~/.bashrc
source ~/.bashrc               # reload

Common variables

text
$HOME      # /home/alice
$USER      # alice
$PWD       # current dir
$PATH      # executable search path
$SHELL     # /bin/bash
$?         # exit code of last command (0 = success)
$$         # current process pid
$0         # script name
$1, $2 ... # script arguments

11. Shell Scripting

เมื่อคุณทำคำสั่งเดิมซ้ำ ๆ ก็ถึงเวลารวมมันเป็น "script" — shell scripting ให้คุณเขียนหลายคำสั่งไว้ในไฟล์เดียวแล้วรันรวดเดียว เช่น backup หรือ deploy นี่คือก้าวแรกจากการพิมพ์คำสั่งทีละบรรทัดสู่การ automate (ทำงานอัตโนมัติ) จริง โครงสร้างหลักที่ใช้บ่อยคือตัวแปร, เงื่อนไข (if), ลูป (for/while) และฟังก์ชัน — โค้ดด้านล่างมีตัวอย่างของแต่ละอย่างพร้อมคอมเมนต์กำกับ:

bash
#!/bin/bash
# script.sh

# Variables
NAME="World"
echo "Hello, $NAME"

# Read input (-r = ไม่ตีความ backslash, ใส่ทุกครั้งให้ติดนิสัย — shellcheck แนะนำ)
read -rp "Enter name: " USER_NAME
echo "Hi $USER_NAME"

# Conditional — if/elif/else
if [ -f /etc/passwd ]; then
    echo "File exists"
elif [ -d /var/log ]; then
    echo "Dir"
else
    echo "Neither"
fi

# Tests — เงื่อนไขที่ใช้ใน [ ... ]
[ -f file ]           # ไฟล์มีอยู่ (file exists)
[ -d dir ]            # เป็น directory
[ -z "$var" ]         # ตัวแปรว่าง (empty)
[ -n "$var" ]         # ตัวแปรไม่ว่าง (non-empty)
[ "$a" = "$b" ]       # string เท่ากัน
[ "$a" != "$b" ]      # string ไม่เท่ากัน
[ "$a" -eq "$b" ]     # numeric equal (ตัวเลข)
[ "$a" -lt "$b" ]     # less than (น้อยกว่า)
[ "$a" -gt "$b" ]     # greater than (มากกว่า)

# Loops — for/while
for i in 1 2 3; do
    echo $i
done

# ⚠️ for loop บน glob (*.txt) — ถ้า "ไม่มีไฟล์ที่ตรง pattern" loop จะรัน 1 รอบ
#    ด้วยค่า literal "*.txt" (Bash default behavior) → script ทำงานผิด
# ทางออก 2 แบบ:
shopt -s nullglob               # ตั้งให้ glob ที่ไม่ match → คืน list ว่าง (ไม่รัน loop)
for file in *.txt; do
    [[ -e $file ]] || continue  # หรือเช็คทีละไฟล์ในกรณีไม่ได้ตั้ง nullglob
    echo "Processing $file"
done

for i in $(seq 1 10); do
    echo $i
done

# While
count=0
while [ $count -lt 5 ]; do
    echo $count
    ((count++))
done

# Functions
greet() {
    local name=$1
    echo "Hello, $name"
}

greet "World"

# Exit code — ตรวจว่าโปรแกรมมีอยู่ในระบบไหม
if ! command -v git &> /dev/null; then
    echo "git not installed"
    exit 1
fi

Best practices

bash
#!/bin/bash
set -e          # หยุดทันทีเมื่อคำสั่งใดล้มเหลว (exit on error)
set -u          # error เมื่อใช้ตัวแปรที่ยังไม่ตั้งค่า (undefined variable)
set -o pipefail # ถ้าคำสั่งใน pipe ใดล้มเหลว → ถือว่าทั้ง pipe ล้มเหลว

# รวมเป็นบรรทัดเดียว
set -euo pipefail
IFS=$'\n\t'     # ตั้ง field separator เป็น newline+tab — จัดการชื่อไฟล์ที่มีช่องว่างได้ถูก
                # (default ของ bash คือ space+tab+newline → ตัดคำที่มีช่องว่างผิด)

# Trap — ดักจับ error/exit เพื่อ cleanup
trap 'echo "Error at line $LINENO"; exit 1' ERR
trap 'rm -f /tmp/myapp.lock' EXIT   # ลบ lock file ทุกกรณีก่อนจบ (สำเร็จ/ล้มเหลว/ถูก Ctrl+C)

12. Practical Examples

ทีนี้มาดูว่า concept ทั้งหมดประกอบกันเป็น script ใช้งานจริงยังไง — ตัวอย่างต่อไปนี้เป็น script ที่ DevOps เขียนกันจริง เช่น backup ฐานข้อมูลอัตโนมัติ สังเกตการใช้ set -euo pipefail และตัวแปร date เพื่อความปลอดภัยและตั้งชื่อไฟล์ตามเวลา:

Backup script

bash
#!/bin/bash
# Prerequisite (ติดตั้ง + ตั้งค่าก่อนใช้):
#   - PostgreSQL client:  sudo apt install postgresql-client
#     (pg_dump = คำสั่ง backup ฐานข้อมูล PostgreSQL — เรียนเพิ่มเติมใน Database section)
#   - AWS CLI v2:         curl + install (ดู docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)
#     (aws s3 cp = คำสั่ง AWS CLI สำหรับอัปโหลดไฟล์ขึ้น S3 — เรียนเพิ่มเติมในบทที่ 6 Cloud Platforms)
#   - Credential ของ AWS: แนะนำ IAM role (ติด instance/role) > access key
#     ถ้าจำเป็นต้องใช้ key ให้รัน `aws configure` หรือ set env
#   - Credential ของ PostgreSQL: ใช้ ~/.pgpass (chmod 600) หรือ PGPASSWORD
#     (ห้าม hardcode password ใน script)
set -euo pipefail
IFS=$'\n\t'

BACKUP_DIR=/var/backups/myapp
DATE=$(date +%Y%m%d-%H%M)
BACKUP_FILE="$BACKUP_DIR/db-$DATE.sql.gz"
S3_BUCKET="s3://my-backups/db/"

mkdir -p "$BACKUP_DIR"

# pg_dump — ใช้ ~/.pgpass (vault credential ฝั่ง postgres) แทนใส่ password ตรง ๆ
# format ของ ~/.pgpass:  hostname:port:database:username:password   (chmod 600)
pg_dump -h localhost -U backup_user -d mydb | gzip > "$BACKUP_FILE"
echo "สร้าง backup แล้ว: $BACKUP_FILE"

# เก็บไว้เฉพาะของ 7 วันล่าสุด (ลบไฟล์ที่เก่ากว่า 7 วัน)
find "$BACKUP_DIR" -name "db-*.sql.gz" -mtime +7 -delete

# Upload ขึ้น S3 พร้อม server-side encryption (SSE-KMS) — ปลอดภัยกว่า default
# แนะนำให้ EC2/host ใช้ IAM role แทน access key เพื่อไม่ต้องเก็บ secret ไว้บนเครื่อง
aws s3 cp "$BACKUP_FILE" "$S3_BUCKET" \
    --sse aws:kms \
    --storage-class STANDARD_IA

💡 ทำไมต้องเข้ารหัส + ใช้ IAM role: backup database คือ "ทรัพย์สินค่าสูง" ของบริษัท — ใส่ขึ้น S3 แบบ plain เท่ากับเสี่ยงข้อมูลรั่ว. SSE (--sse aws:kms) ให้ AWS เข้ารหัสฝั่ง server, IAM role ทำให้ไม่ต้องเก็บ access key บน server (โดน leak ยากกว่า)

Log analyzer

bash
#!/bin/bash
# สมมุติ log เป็น nginx access log (Combined Log Format):
#   $1 = IP ของ client, $7 = URL/endpoint ที่ขอ
# ถ้า log มี format อื่น (เช่น JSON) ต้องเปลี่ยนวิธี parse — ใช้ jq แทน awk
LOG=/var/log/app.log

echo "Total requests: $(wc -l < "$LOG")"
echo "Errors: $(grep -c ERROR "$LOG")"
echo ""
echo "Top 10 IPs:"
awk '{print $1}' "$LOG" | sort | uniq -c | sort -rn | head -10
echo ""
echo "Top 10 endpoints:"
awk '{print $7}' "$LOG" | sort | uniq -c | sort -rn | head -10

Health check

bash
#!/bin/bash
# Prerequisite: ตั้งค่า SLACK_WEBHOOK ก่อนใช้ — เช่น
#   export SLACK_WEBHOOK="https://hooks.slack.com/services/XXX/YYY/ZZZ"
# (เก็บใน secret manager หรือ ~/.bashrc — ห้าม hardcode ใน script)
set -euo pipefail

ENDPOINT="https://api.example.com/health"
ALERT_STATE_FILE="/tmp/healthcheck.down"   # flag กันแจ้งเตือนซ้ำ
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$ENDPOINT")

if [ "$STATUS" -ne 200 ]; then
    # Alert dedup — แจ้งครั้งเดียวต่อช่วง down (ไม่ flood Slack ทุก 5 นาที)
    if [ ! -f "$ALERT_STATE_FILE" ]; then
        echo "Service ล่ม! Status: $STATUS"
        curl -X POST -H "Content-Type: application/json" \
            -d "{\"text\":\"🚨 API ล่ม: status $STATUS\"}" \
            "$SLACK_WEBHOOK"
        touch "$ALERT_STATE_FILE"
    fi
    exit 1
fi

# Service กลับมาแล้ว — แจ้ง recover + ลบ flag
if [ -f "$ALERT_STATE_FILE" ]; then
    curl -X POST -H "Content-Type: application/json" \
        -d '{"text":"✅ API กลับมาแล้ว"}' \
        "$SLACK_WEBHOOK"
    rm -f "$ALERT_STATE_FILE"
fi

echo "Service ปกติ (OK)"

13. SSH

SSH (Secure Shell) คือวิธีที่คุณ login เข้าเซิร์ฟเวอร์ระยะไกลอย่างปลอดภัยผ่าน encrypted connection — DevOps ใช้ SSH แทบทุกวันในการเข้าไปดูแลเซิร์ฟเวอร์ การใช้ key (แทน password), ตั้ง ~/.ssh/config ให้ alias สั้น ๆ และ port forwarding คือทักษะที่ต้องคล่อง:

bash
# Connect — login เข้า server
ssh user@server.com
ssh -p 2222 user@server.com            # ระบุ port (default 22)
ssh -i key.pem user@server.com         # ใช้ private key เฉพาะ (with key file)

# Generate key — สร้างคู่ key (private + public)
ssh-keygen -t ed25519 -C "your@email.com"   # ⭐ มาตรฐานปัจจุบัน (2026) — เล็ก เร็ว ปลอดภัย
# ssh-keygen -t rsa -b 4096                 # legacy — เลี่ยงถ้าไม่จำเป็น
                                            # (NIST/GitHub แนะนำ ed25519 แล้ว)

# คัดลอก public key ไปฝากที่ server (วางใน ~/.ssh/authorized_keys ของฝั่งโน้น)
ssh-copy-id user@server.com

ไฟล์ใน ~/.ssh/

text
~/.ssh/
├── id_ed25519             ← private key (chmod 600! — ห้ามให้คนอื่นอ่าน)
├── id_ed25519.pub         ← public key (เปิดเผยได้)
├── known_hosts            ← server ที่เคยเชื่อมต่อมาแล้ว
├── authorized_keys        ← public key ที่อนุญาตให้ login เข้าเครื่องนี้
└── config                 ← SSH client config — alias ของ host

ตัวอย่าง ~/.ssh/config

sshconfig
# ตั้ง alias สั้น ๆ → พิมพ์แค่ `ssh myserver`
Host myserver
    Hostname server.com
    User alice
    Port 2222
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes        # ใช้เฉพาะ key ที่ระบุ — กัน "many keys offered" ที่ทำให้ GitHub block
    HashKnownHosts yes        # hash hostname ใน known_hosts (privacy: ไม่เห็น list server เป็น plain)

# ค่า default ที่ใช้ทุก host (วางท้ายไฟล์ — config แบบ first-match)
Host *
    ServerAliveInterval 60    # ส่ง keepalive ทุก 60 วิ (กันหลุดจาก NAT)
    HashKnownHosts yes
bash
# จากนั้นเรียกสั้น ๆ
ssh myserver

SCP / rsync — File transfer

bash
# scp
scp local.txt user@server:/path/
scp user@server:/path/file.txt ./
scp -r local-dir/ user@server:/path/

# rsync (better — resume, sync)
rsync -avz src/ user@server:/path/dest/
rsync -avz --delete src/ user@server:/path/dest/    # mirror

Port forwarding

bash
# Local forward — เปิด port ฝั่ง local ที่ map ไปยัง service ฝั่ง remote
# (ใช้บ่อย: เข้าถึง DB ของ server โดยไม่ต้องเปิด port public)
ssh -L 5432:localhost:5432 user@server
# ตอนนี้ localhost:5432 ของเครื่องเรา = DB ของ server แล้ว

# Remote forward — เปิด port ฝั่ง server ที่ map กลับมาที่ local
ssh -R 8080:localhost:3000 user@server
# port 8080 ของ server → เข้าถึงเครื่องเรา port 3000

14. System Service (systemd)

systemd คืออะไร — ทำไมต้องมี

ก่อน Ubuntu 16.04 / RHEL 7 — Linux ใช้ init.d (script ใน /etc/init.d/) start service. ปัญหา:

  • Start ทีละตัวเรียงกัน → boot ช้า
  • ไม่รู้ว่า service พังแล้วต้อง restart ไหม
  • Log กระจัดกระจาย ไม่มีกลาง
  • Dependency ระหว่าง service เขียนยาก

systemd (ปี 2010 โดย Lennart Poettering / Red Hat) แก้ปัญหานี้ — ปัจจุบันใช้ใน Ubuntu, Debian, RHEL, Fedora, Arch ทุกที่:

systemd แก้อะไรของเดิมsystemd
Parallel boot (boot พร้อมกันหลาย service)sequential — ทีละตัว, ช้าparallel ตาม dependency
Auto-restart (รีสตาร์ตเองเมื่อ crash)ต้องเขียน script เองRestart=on-failure
Logging (เก็บ log)กระจัดกระจาย หลายไฟล์journalctl รวมที่เดียว
Resource limit (จำกัด RAM/CPU)ทำยาก ต้องใช้ cgroup เองMemoryMax, CPUQuota built-in
Timer (ตัวตั้งเวลา แทน cron)cron + grep logsystemd-timer + journal

Concept หลัก: ทุกอย่างคือ unit — service, timer, socket, mount = unit แบบต่าง ๆ

bash
# Status
systemctl status nginx
systemctl status postgresql

# Start/Stop/Restart
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
sudo systemctl reload nginx           # graceful — reload config โดยไม่ทิ้ง connection ที่ค้างอยู่

# Auto-start on boot
sudo systemctl enable nginx
sudo systemctl disable nginx

# Logs
journalctl -u nginx
journalctl -u nginx -f                # follow
journalctl -u nginx --since "1 hour ago"
journalctl -u nginx --since today

Create service

Prerequisite: สร้าง user + directory ก่อน (service จะรันในนาม user นี้)

bash
sudo useradd --system --no-create-home --shell /usr/sbin/nologin alice
sudo mkdir -p /opt/myapp
sudo chown -R alice:alice /opt/myapp

Unit file (เก็บที่ /etc/systemd/system/myapp.service):

ini
# 3 section หลักของ unit file:
#   [Unit]    — metadata + dependency (boot order)
#   [Service] — บอกว่าโปรแกรมรันยังไง, restart อย่างไร, security limit
#   [Install] — บอกว่าจะ enable ตอน boot ในระดับไหน

[Unit]
Description=My App
After=network.target           # รันหลังจาก network พร้อม

[Service]
# Type=simple   — บอก systemd ว่า process หลักคือ ExecStart โดยตรง (start แล้วถือว่าเสร็จทันที)
#                 ⚠️ ปัญหา: systemd ถือว่า "started" ก่อนที่แอปจะพร้อมรับ traffic จริง
# ทางเลือกที่ดีกว่าใน 2026:
#   Type=notify  — แอปยิง sd_notify(READY=1) เมื่อพร้อม → systemd รู้สถานะจริง (แนะนำ)
#   Type=exec    — รอ exec() สำเร็จก่อน (ดีกว่า simple แต่ยังไม่รู้ว่าแอป ready)
Type=notify
NotifyAccess=main
User=alice
Group=alice
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/start.sh
Restart=on-failure             # restart อัตโนมัติเมื่อ crash
RestartSec=5                   # รอ 5 วิ ก่อน restart

# Security hardening (มาตรฐาน 2026 — copy ใส่ทุก service จริง)
NoNewPrivileges=true           # ห้าม process gain สิทธิ์เพิ่ม (block setuid escalation)
ProtectSystem=strict           # /usr, /boot, /etc → read-only สำหรับ process นี้
ProtectHome=true               # /home, /root → invisible
PrivateTmp=true                # /tmp แยกของตัวเอง (ไม่ปนกับ service อื่น)
ProtectKernelTunables=true     # block เขียน /proc/sys, /sys
ProtectKernelModules=true      # block load/unload kernel module
RestrictSUIDSGID=true          # ห้าม set suid/sgid bit ในไฟล์
LockPersonality=true           # ห้ามเปลี่ยน execution domain

# Resource limit (กันแอปกิน RAM/CPU จนเครื่องล่ม)
MemoryMax=512M
CPUQuota=50%

[Install]
WantedBy=multi-user.target      # enable ตอน boot ในระดับ multi-user (ปกติ)
bash
sudo systemctl daemon-reload    # โหลด unit file ใหม่หลังแก้
sudo systemctl enable myapp     # ตั้งให้ start ตอน boot
sudo systemctl start myapp      # start เลย

15. Package Manager

แทนที่จะ download ติดตั้งซอฟต์แวร์ทีละตัวเอง Linux ใช้ "package manager" จัดการให้ — มันดาวน์โหลด ติดตั้ง อัปเดต และลบโปรแกรมพร้อม dependency ให้อัตโนมัติ แต่ละ distro มีตัวต่างกัน (apt บน Ubuntu/Debian, dnf บน RHEL, apk บน Alpine ที่ใช้ใน Docker บ่อย):

bash
# Ubuntu/Debian
sudo apt update                       # โหลด list package ใหม่ (refresh)
sudo apt upgrade                      # upgrade ทุก package
sudo apt full-upgrade                 # upgrade รวมถึง kernel + remove ของเก่าที่ขัดกัน
sudo apt install nginx                # ติดตั้ง
sudo apt remove nginx
sudo apt search docker                # ค้นหา package
apt list --installed                  # ดูที่ติดตั้งไว้
# ⚠️ ใน script ที่ต้อง stable output → ใช้ `apt-get` แทน `apt`
#    เพราะ apt warns ว่า "CLI ไม่ stable" และเปลี่ยน format ได้ในเวอร์ชันใหม่
#    เช่น:  sudo apt-get -y install nginx
#    แต่ถ้าต้อง parse ผลลัพธ์แบบ machine-readable จริงจัง (เช่นดึงรายชื่อ package)
#    อย่า parse text output เลย ใช้ `dpkg-query --show` หรือ python3-apt แทน

# RHEL/CentOS/Rocky
sudo dnf update
sudo dnf install nginx

# Alpine (Docker base — บนหลายๆ image)
apk add nginx
apk update && apk upgrade

Snap / Flatpak

Modern Ubuntu — desktop apps:

bash
sudo snap install --classic code      # VS Code ผ่าน snap
# ⚠️ snap classic ในวงการ dev มีเสียงบ่นเรื่อง start ช้า + mount loop เยอะ
#    ทางเลือก: ติดตั้ง .deb จาก repo ของ Microsoft โดยตรง (ดู docs ของ VS Code)
flatpak install flathub org.mozilla.firefox   # ต้องระบุ remote (flathub) + namespace เต็ม

16. Cron — Schedule

cron คือ scheduler ในตัวของ Linux ที่สั่งให้คำสั่งรันตามเวลาที่กำหนดเอง — ใช้ทำงานประจำเช่น backup ทุกคืน, healthcheck ทุก 5 นาที, ล้าง log ทุกสัปดาห์ จุดที่มือใหม่งงคือ format 5 ช่อง (นาที ชั่วโมง วัน เดือน วันในสัปดาห์) ตัวอย่างด้านล่างอ่านทีละบรรทัดจะเข้าใจ:

bash
crontab -e                            # แก้ไข cron ของ user (เปิด editor)
crontab -l                            # ดูรายการ cron ที่ตั้งไว้

# Format: นาที ชั่วโมง วันที่ เดือน วันในสัปดาห์ command
#         min  hour    day    month  weekday(0=Sun)
0 3 * * *      /opt/backup.sh         # ทุกวัน เวลา 03:00 น.
*/5 * * * *    /opt/healthcheck.sh    # ทุก ๆ 5 นาที
0 0 * * 0      /opt/weekly.sh         # ทุกเที่ยงคืนวันอาทิตย์
# ตัวอย่างถัดไป: ทุกชั่วโมงตรง (นาที=0) ระหว่าง 9:00–17:00 (รวม 17:00 ด้วย)
#                เฉพาะวันจันทร์–ศุกร์ (1–5)
0 9-17 * * 1-5 /opt/work.sh           # เวลาทำงานปกติของวันธรรมดา

# ตัวแปร (ใส่ด้านบนของ crontab — มีผลกับทุกบรรทัด)
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=admin@example.com              # ส่ง output ของ cron ที่ไม่ silent ไป email นี้

# Shortcut แบบ predefined (เขียนสั้นลง)
@reboot  /opt/start.sh                # ⚠️ @reboot ไม่ใช่ทุก cron implementation รองรับ
                                      #    (เช่น systemd-cron อาจไม่มี) — ทดสอบในระบบจริงก่อน
@daily   /opt/backup.sh
@weekly  /opt/cleanup.sh
@monthly /opt/report.sh

💡 ทางเลือกใหม่ — systemd-timer (แนะนำสำหรับ production จริง): log ผ่าน journalctl ครบ, retry/dependency ดีกว่า, จับ lock ป้องกัน job ซ้อนได้ — cron เหมาะกับงานเล็ก ๆ ส่วนตัว/dev, timer เหมาะกับ production ที่ต้องการ observability


17. ⚠️ Dangerous Commands

บางคำสั่งทำลายระบบได้ในพริบตาและกู้คืนไม่ได้ — โดยเฉพาะ rm -rf กับ path ผิด หรือ dd ใส่ disk ผิด มือใหม่ควรรู้จักหน้าตาของมันเพื่อ "ไม่เผลอพิมพ์" และฝึกนิสัยป้องกัน (เช็คก่อนกด, ใช้ rm -i, ระวังทุกครั้งที่มี sudo):

bash
# ⚠️ ตรวจ 3 ครั้งก่อนกด Enter — คำสั่งกลุ่มนี้กู้คืนยาก/กู้ไม่ได้

rm -rf /                              # 🔥 พยายามลบทุกอย่าง
                                      #    (coreutils ใหม่บล็อกด้วย --preserve-root โดย default
                                      #     ตั้งแต่ปี 2006 → จะแจ้ง error แทนการลบจริง
                                      #     แต่ rm -rf /* ยังลบได้ — อย่าหลงคิดว่าปลอดภัย)
rm -rf ~/                             # ลบ home directory ตัวเองทั้งหมด
sudo rm -rf /                         # 🔥🔥 ระดับ super (ถ้าใส่ --no-preserve-root)
dd if=/dev/zero of=/dev/sda           # เขียน 0 ทับดิสก์ — wipe disk (ข้อมูลหาย)
# :(){ :|:& };:                       # fork bomb — สร้าง process ซ้อนกันจนเครื่องค้าง
                                      # (comment ไว้กันคุณ copy ทั้งบรรทัด → ตั้ง ulimit -u ป้องกัน)
chmod -R 777 /                        # ทำลายระบบ permission ทั้งระบบ (break permissions)
> file                                # ตัดไฟล์ให้ว่าง (truncate) — ไม่มี warning ใด ๆ

# นิสัยป้องกันตัว (defensive habits):
# - alias rm='rm -i'    # interactive — ถามก่อนลบทุกไฟล์
# - ls ก่อน rm เสมอ (ตรวจว่า pattern ถูกจริง)
# - ใช้ tab completion ช่วย (ไม่พิมพ์ path เอง)
# - ตรวจคำสั่งที่มี sudo อย่างน้อย 3 ครั้ง
# - ทดสอบ rm pattern ด้วย ls / echo ก่อน:   ls *.log    →   rm *.log

18. Productivity Tips

คนที่ทำงานบน terminal คล่องจะเร็วกว่าหลายเท่าเพราะรู้ "ทางลัด" — การค้นประวัติคำสั่ง (Ctrl+R), แป้นแก้บรรทัด, การตั้ง alias สั้น ๆ และ tmux (แบ่งหน้าจอ + รัน session ที่ไม่ตายตอน disconnect) เทคนิคกลุ่มนี้ลงทุนเรียนครั้งเดียวแต่คุ้มไปตลอด:

bash
# History — ประวัติคำสั่งที่เคยพิมพ์
history                                # ดูทั้งหมด
!42                                    # รันคำสั่งหมายเลข 42 ใน history
!!                                     # รันคำสั่งล่าสุดอีกครั้ง
Ctrl+R                                 # ค้นหาย้อนกลับ (reverse search) — กดแล้วพิมพ์ทีละตัว

# Edit cursor — ลัดบนบรรทัดคำสั่ง
Ctrl+A   # ไปต้นบรรทัด (start of line)
Ctrl+E   # ไปท้ายบรรทัด (end)
Ctrl+K   # ลบจาก cursor ไปท้าย
Ctrl+U   # ลบจาก cursor ไปต้นบรรทัด
Ctrl+W   # ลบ word ก่อนหน้า
Alt+B    # เลื่อน cursor ถอยหลังทีละ word
Alt+F    # เลื่อน cursor ไปข้างหน้าทีละ word

# Multi-line — ใส่ \ ท้ายบรรทัด = ขึ้นบรรทัดใหม่แต่ยังเป็นคำสั่งเดียวกัน

# Tab completion — เติมชื่อให้อัตโนมัติ (ใช้บ่อยที่สุด!)
git che<TAB>      # → git checkout

# Aliases (ใส่ใน ~/.bashrc) — ตั้งคำสั่งสั้น
alias ll='ls -la'
alias gs='git status'
alias k='kubectl'
alias dc='docker compose'

# tmux — terminal multiplexer (ตัวแบ่งหน้าจอ + เก็บ session ที่ไม่ตายเวลา disconnect)
tmux              # เปิด session ใหม่
Ctrl+B c          # สร้าง window ใหม่
Ctrl+B n          # ไป window ถัดไป (next)
Ctrl+B d          # ออก (detach) แต่ session ยังรันอยู่
tmux attach       # กลับเข้า session เดิม (reattach)

19. Modern Tools to Consider

เครื่องมือคลาสสิก (cat/ls/grep/find) ใช้ได้ดีและมีอยู่ทุกเครื่อง แต่บนเครื่องที่คุณคุมเองมีเวอร์ชันใหม่ที่เร็วกว่า สวยกว่า ใช้ง่ายกว่า — กลุ่มนี้ติดตั้งเสริมได้ ไม่บังคับ แต่ช่วยให้ทำงานประจำวันลื่นขึ้นมาก (เช่น bat, fzf, ripgrep):

bash
# ทดแทนเครื่องมือ classic ด้วยตัวที่ทันสมัยกว่า (ติดตั้งเสริม)
bat              # cat ที่มี syntax highlight (เน้นสี code)
eza / lsd        # ls ที่มีสี + icon (eza = fork ของ exa, exa หยุดพัฒนาตั้งแต่ปี 2023)
fd / fdfind      # ทดแทน find (ใช้ regex, syntax สั้น) — บน Ubuntu/Debian = fdfind
rg (ripgrep)     # ทดแทน grep — เร็วกว่า, default ignore .gitignore
fzf              # fuzzy finder — ค้นหาแบบพิมพ์ผิดก็เจอ
zoxide           # ทดแทน cd — จำ directory ที่เคยไปบ่อย (`z foo`)
btop             # ทางเลือกของ htop — UI ดีกว่า, ดู GPU/network ได้
delta            # ทำให้ git diff สวยขึ้น
direnv           # โหลด env variable ตาม directory อัตโนมัติ

20. Troubleshooting Workflow

เวลาแอปบน production มีปัญหา อย่าสุ่มลองมั่ว — มี "ลำดับการตรวจ" ที่ช่วยให้หาสาเหตุได้เร็วและเป็นระบบ ไล่จากเช็คสถานะ service → ดู log → เช็คทรัพยากร → network → process → config การทำตามลำดับนี้รวมคำสั่งทั้งบทมาใช้จริงในสถานการณ์ฉุกเฉิน:

Production issue → ทำตามนี้:

text
1. เช็คสถานะ service (Check status)
   systemctl status myapp
   curl localhost:8080/health

2. ดู log ล่าสุด + ไหลตามสด (last + tail)
   journalctl -u myapp -n 100
   tail -f /var/log/myapp.log

3. เช็คทรัพยากร (CPU/RAM/disk)
   top / htop
   df -h
   free -h

4. เช็ค network (port เปิดไหม, เรียกได้ไหม)
   ss -tlnp
   curl localhost:8080

5. เช็ค process (process รันอยู่จริงไหม)
   ps aux | grep myapp

6. ตรวจ config (Inspect config)
   cat /etc/myapp/config.yml

7. ถ้าตันจริง ๆ:
   strace -p <PID>   # ดู system call ที่ process กำลังเรียก (process ค้างที่อะไร?)
   restart           # restart เป็นทางสุดท้าย — ทำให้หลักฐานหาย, debug ต่อยาก

21. Checkpoint

🛠️ Checkpoint 1.1 — Basic Navigation
ลองทำเองบน Linux:

  • ค้นหาไฟล์ .log ขนาดเกิน 10MB ใน /var/log ทั้งหมด
  • นับจำนวนบรรทัด ERROR ในแต่ละไฟล์
  • จาก access.log — แสดง 3 อันดับ IP ที่เรียกเข้ามาบ่อยที่สุด

💡 ถ้า /var/log ว่างเปล่าบน WSL2 (ปกติมาก) ให้ลองหาใน /var/log/apt/ แทน หรือสร้างไฟล์ทดสอบขนาดใหญ่เองด้วย: dd if=/dev/urandom of=/tmp/test.log bs=1M count=15

🛠️ Checkpoint 1.2 — Permissions

  • สร้าง (Create) secret.txt ที่อ่านเฉพาะ owner
  • สร้าง (Create) shared/ ที่ group user share เขียนได้
  • ทดสอบ (Test) ด้วย user อื่น

🛠️ Checkpoint 1.3 — Shell Script
เขียน script:

  • รับ argument ที่ 1 เป็น directory
  • ค้นหาไฟล์ PDF ทั้งหมดใน directory นั้น
  • เรียงตามขนาดจากมากไปน้อย (Sort by size descending)
  • แสดง 10 อันดับแรก + รวมขนาดทั้งหมด (Print top 10 + total size)

🛠️ Checkpoint 1.4 — SSH Setup

  • สร้าง (Generate) key แบบ ed25519
  • คัดลอก key ไปยัง remote VPS (หรือ Docker ubuntu container บนเครื่อง)
  • ตั้งค่า alias ใน ~/.ssh/config
  • ล็อกอินโดยไม่ต้องใส่รหัสผ่าน (Login without password)

🛠️ Checkpoint 1.5 — Cron + systemd

  • Cron: backup database daily 3 AM, keep 7 days
  • systemd: create service for your app (auto-restart)
  • Verify journalctl logs

21.5 ต่อยอด — Linux ขั้นสูง

หลังจบบทนี้ ถ้าอยากลึกขึ้น:

หัวข้อเริ่มที่
เข้าใจ kernel (แกนกลาง OS)man syscalls, strace (ดู system call ที่โปรแกรมเรียก), หนังสือ The Linux Programming Interface (Kerrisk)
Performance tuning (ปรับจูนประสิทธิภาพ)perf, bpftrace, eBPF (extended Berkeley Packet Filter — รัน sandboxed program ใน kernel ใช้เก็บ metric/trace แบบ live โดยไม่ต้องแก้ kernel) — มาตรฐานของ observability + security ใน 2026
Networking ลึกip, tc (traffic control), iptables/nftables (firewall), network namespace
Container internalsnamespace + cgroup (รากฐานของ Docker) → ดูบท Docker
Security hardening (เสริมความปลอดภัย)SELinux (Security-Enhanced Linux, MAC ของ Red Hat), AppArmor (MAC ของ Ubuntu), auditd, CIS Benchmark (มาตรฐาน hardening จาก Center for Internet Security)
shell scripting ขั้นสูงbash complete reference, fish/zsh, นิยมเปลี่ยนไป Python/Go เมื่อ script เกิน 100 บรรทัด

หนังสือแนะนำ:

  • The Linux Command Line (William Shotts) — ฟรี, ดีที่สุดสำหรับมือใหม่
  • How Linux Works (Brian Ward) — เข้าใจว่า OS ทำงานยังไง
  • Linux Performance (Brendan Gregg) — สำหรับ production debugging

22. สรุปบท

ทบทวนสิ่งที่ได้เรียนในบทนี้ — ทักษะ Linux และ shell พื้นฐานทั้งหมดที่ DevOps ต้องใช้ ตั้งแต่เดินดูระบบไฟล์ จัดการ process จนถึงเขียน script และ SSH เข้าเซิร์ฟเวอร์:

✅ โครงสร้างไฟล์ของ Linux (filesystem hierarchy): /etc = config, /var/log = log, /home = user
✅ จัดการไฟล์ (File ops): cp/mv/rm (คัดลอก/ย้าย/ลบ), find (หาไฟล์), grep (ค้นเนื้อหา), tail -f (ดู log สด)
✅ Permission (สิทธิ์): rwx + chmod + chown — แบ่งเป็น owner/group/others
✅ Process (โปรเซส): ps, top/htop, kill, &, nohup, jobs
✅ Redirection (เปลี่ยนทาง output): >, >>, <, |, 2>&1
✅ ประมวลผลข้อความ (text processing): sort, uniq, cut, awk, sed
✅ เครือข่าย (network): curl, ss, dig, ssh, scp, rsync
✅ Environment variables + dotfiles (~/.bashrc)
✅ Shell scripting: #!/bin/bash, set -euo pipefail, functions
✅ SSH key-based auth + config file + port forwarding
✅ systemd service + journalctl + cron
✅ เครื่องมือใหม่ (bat, rg, fd, fzf) — เพิ่มความเร็วการทำงาน


← บทที่ 0 | บทที่ 2 → Git + GitHub