Giao diện
Terminal Lab Checklist — Thực Hành Trên Terminal
🎯 Mục tiêu
Trang này cung cấp 5 bài lab thực hành trên terminal được thiết kế có cấu trúc rõ ràng. Mỗi lab gồm: mục tiêu (objective), thiết lập (setup), các bước thực hiện (step-by-step commands), tiêu chí xác minh (verification criteria), và lỗi thường gặp (common mistakes). Hoàn thành tất cả 5 lab để củng cố kiến thức Phase 1.
Chuẩn bị môi trường
Bạn cần một môi trường Linux để thực hành. Sử dụng: Docker (docker run -it ubuntu:22.04 bash), WSL2 trên Windows, hoặc một VM/VPS.
Lab 1: Permissions — Tạo Môi Trường Deploy
Mục tiêu
Tạo cấu trúc thư mục deployment hoàn chỉnh với ownership và permissions đúng chuẩn — đảm bảo chỉ user/group được phép mới có thể truy cập file nhạy cảm.
Nhiệm vụ
- Tạo user
deployvà groupweb-team - Tạo cấu trúc thư mục:
/opt/myapp/,/opt/myapp/logs/,/opt/myapp/data/,/opt/myapp/config/ - Gán ownership:
deploy:web-team - Thiết lập permissions: thư mục
750, config files640, scripts750, SSH keys600 - Tạo file test và xác minh user khác không truy cập được file riêng tư
Các bước thực hiện
bash
# === Bước 1: Tạo user và group ===
sudo useradd -m -s /bin/bash deploy
sudo groupadd web-team
sudo usermod -aG web-team deploy
# === Bước 2: Tạo cấu trúc thư mục ===
sudo mkdir -p /opt/myapp/{logs,data,config}
# === Bước 3: Gán ownership ===
sudo chown -R deploy:web-team /opt/myapp
# === Bước 4: Thiết lập permissions ===
sudo chmod 750 /opt/myapp
sudo chmod 750 /opt/myapp/logs /opt/myapp/data
sudo chmod 750 /opt/myapp/config
# === Bước 5: Tạo file config mẫu ===
sudo -u deploy bash -c 'echo "DB_URL=postgres://..." > /opt/myapp/config/app.env'
sudo chmod 640 /opt/myapp/config/app.env
# === Bước 6: Tạo script mẫu ===
sudo -u deploy bash -c 'cat > /opt/myapp/start.sh << "EOF"
#!/bin/bash
echo "Starting application..."
EOF'
sudo chmod 750 /opt/myapp/start.sh
# === Bước 7: Tạo SSH key mẫu ===
sudo -u deploy bash -c 'mkdir -p /opt/myapp/config/.ssh && echo "PRIVATE_KEY" > /opt/myapp/config/.ssh/id_ed25519'
sudo chmod 600 /opt/myapp/config/.ssh/id_ed25519Xác minh kết quả
bash
# ✅ Check: deploy user có thể đọc config
sudo -u deploy cat /opt/myapp/config/app.env
# ✅ Check: user khác KHÔNG THỂ đọc config
sudo -u nobody cat /opt/myapp/config/app.env # Phải thất bại: Permission denied
# ✅ Check: permissions đúng
ls -la /opt/myapp/
stat -c "%a %U:%G %n" /opt/myapp/config/app.env
# Kết quả mong đợi: 640 deploy:web-team /opt/myapp/config/app.env
# ✅ Check: SSH key có permission 600
stat -c "%a %U:%G %n" /opt/myapp/config/.ssh/id_ed25519
# Kết quả mong đợi: 600 deploy:web-team /opt/myapp/config/.ssh/id_ed25519
# ✅ Check: script có permission 750
stat -c "%a %U:%G %n" /opt/myapp/start.sh
# Kết quả mong đợi: 750 deploy:web-team /opt/myapp/start.shLỗi thường gặp
Đừng dùng 777!
Khi gặp lỗi "Permission denied", nhiều người sẽ chạy chmod 777 cho nhanh. Đây là sai lầm nghiêm trọng — bất kỳ user nào trên hệ thống đều có thể đọc secrets (database password, API keys). Trong production, đây là lỗ hổng bảo mật cấp cao.
Nguyên tắc: Luôn bắt đầu với permission hạn chế nhất (600/640/750) rồi mở rộng khi cần, không bao giờ làm ngược lại.
Lab 2: Processes — Quản Lý Tiến Trình
Mục tiêu
Khởi chạy tiến trình, giám sát trạng thái, gửi signal, nhận diện zombie process, và dọn dẹp — hiểu trọn vẹn vòng đời của một tiến trình Linux.
Nhiệm vụ
- Khởi chạy một tiến trình chạy nền (background process)
- Tìm PID bằng
psvà/proc - Gửi tín hiệu SIGHUP, SIGTERM
- Tạo và nhận diện zombie process
- Dọn dẹp tiến trình
Các bước thực hiện
bash
# === Bước 1: Khởi chạy tiến trình với signal handler ===
python3 -c "
import time, signal, os
def handler(sig, frame):
print(f'Received signal {sig}')
signal.signal(signal.SIGHUP, handler)
signal.signal(signal.SIGTERM, handler)
print(f'PID: {os.getpid()}')
while True:
time.sleep(1)
" &
# === Bước 2: Tìm tiến trình ===
ps aux | grep python3
cat /proc/$(pgrep -f "import time")/status | grep -E "^(Name|State|Pid)"
# === Bước 3: Gửi signal ===
kill -HUP $(pgrep -f "import time") # SIGHUP — tiến trình in "Received signal 1"
kill $(pgrep -f "import time") # SIGTERM — yêu cầu dừng graceful
# === Bước 4: Tạo zombie process để quan sát ===
python3 -c "
import os, time
pid = os.fork()
if pid == 0:
# Child process — thoát ngay
os._exit(0)
else:
# Parent process — không gọi wait(), tạo zombie
print(f'Parent PID: {os.getpid()}, Child PID: {pid}')
time.sleep(30) # Giữ parent sống để child thành zombie
" &
# Quan sát zombie (sau 1-2 giây)
sleep 2
ps aux | grep -E "Z|defunct"
# === Bước 5: Dọn dẹp ===
kill $(pgrep -f "os.fork") # Kill parent → init nhận zombie → tự dọnXác minh kết quả
bash
# ✅ Check: tiến trình nhận được SIGHUP (xem output "Received signal 1")
# ✅ Check: tiến trình đã dừng sau SIGTERM
ps aux | grep python3 # Không còn xuất hiện
# ✅ Check: zombie process xuất hiện với trạng thái Z
ps aux | grep defunct
# Cột STAT hiển thị "Z+" — đây là zombie
# ✅ Check: sau khi kill parent, zombie biến mất
sleep 1
ps aux | grep defunct # Không còn zombieLỗi thường gặp
Đừng dùng kill -9 đầu tiên!
kill -9 (SIGKILL) buộc tiến trình chết ngay lập tức mà không cho cơ hội dọn dẹp — temp files không xóa, database connections không đóng, lock files vẫn còn. Luôn thử theo thứ tự: SIGTERM (15) → đợi vài giây → SIGKILL (9) chỉ khi thực sự cần thiết.
Lab 3: systemd — Chạy Service
Mục tiêu
Viết file unit systemd, khởi chạy và kích hoạt service, đọc logs — biến một script thành service chuyên nghiệp có thể tự khởi động lại khi lỗi.
Nhiệm vụ
- Tạo một Python HTTP server script đơn giản
- Viết file
.servicecho systemd - Khởi chạy và kích hoạt service
- Xác minh service đang chạy và sống sót qua restart
- Đọc logs bằng
journalctl
Các bước thực hiện
bash
# === Bước 1: Tạo script cho service ===
sudo mkdir -p /opt/mywebapp
sudo tee /opt/mywebapp/server.py > /dev/null << 'EOF'
#!/usr/bin/env python3
"""Simple HTTP server for systemd lab."""
from http.server import HTTPServer, SimpleHTTPRequestHandler
import logging
import os
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
logger = logging.getLogger(__name__)
PORT = int(os.environ.get('APP_PORT', 8080))
class HealthHandler(SimpleHTTPRequestHandler):
def do_GET(self):
if self.path == '/health':
self.send_response(200)
self.send_header('Content-Type', 'text/plain')
self.end_headers()
self.wfile.write(b'OK')
logger.info('Health check passed')
else:
self.send_response(200)
self.send_header('Content-Type', 'text/plain')
self.end_headers()
self.wfile.write(b'Hello from systemd service!')
if __name__ == '__main__':
server = HTTPServer(('0.0.0.0', PORT), HealthHandler)
logger.info(f'Server starting on port {PORT}')
server.serve_forever()
EOF
sudo chmod +x /opt/mywebapp/server.py
# === Bước 2: Tạo systemd unit file ===
sudo tee /etc/systemd/system/mywebapp.service > /dev/null << 'EOF'
[Unit]
Description=My Web Application
After=network.target
Documentation=https://example.com/docs
[Service]
Type=simple
User=deploy
Group=web-team
WorkingDirectory=/opt/mywebapp
ExecStart=/usr/bin/python3 /opt/mywebapp/server.py
Restart=on-failure
RestartSec=5
Environment=APP_PORT=8080
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/mywebapp/logs
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=mywebapp
[Install]
WantedBy=multi-user.target
EOF
# === Bước 3: Reload systemd, start và enable service ===
sudo systemctl daemon-reload
sudo systemctl start mywebapp.service
sudo systemctl enable mywebapp.service
# === Bước 4: Kiểm tra trạng thái ===
sudo systemctl status mywebapp.service
# === Bước 5: Đọc logs ===
# Xem logs gần nhất
sudo journalctl -u mywebapp.service -n 20 --no-pager
# Xem logs realtime (Ctrl+C để thoát)
sudo journalctl -u mywebapp.service -fXác minh kết quả
bash
# ✅ Check: service đang chạy
sudo systemctl is-active mywebapp.service
# Kết quả mong đợi: active
# ✅ Check: service được enable (tự khởi động khi boot)
sudo systemctl is-enabled mywebapp.service
# Kết quả mong đợi: enabled
# ✅ Check: HTTP server phản hồi
curl -s http://localhost:8080/health
# Kết quả mong đợi: OK
# ✅ Check: tự restart sau khi crash
sudo kill -9 $(pgrep -f "server.py")
sleep 6 # Đợi RestartSec (5s) + thêm 1s
sudo systemctl is-active mywebapp.service
# Kết quả mong đợi: active (systemd đã restart tự động)
# ✅ Check: logs ghi nhận restart
sudo journalctl -u mywebapp.service -n 10 --no-pager
# Phải thấy "Server starting on port 8080" xuất hiện 2 lầnLỗi thường gặp
Quên daemon-reload!
Sau khi sửa file .service, bạn phải chạy sudo systemctl daemon-reload trước khi restart. Nếu không, systemd vẫn dùng bản cũ đã cache — thay đổi của bạn sẽ không có hiệu lực và bạn sẽ mất hàng giờ debug "tại sao config mới không hoạt động".
Thêm lỗi phổ biến:
- Quên
User=→ service chạy bằng root (nguy hiểm) - Sai
ExecStartpath → service không start được, kiểm tra bằngjournalctl -xe - Thiếu
After=network.target→ service start trước khi network sẵn sàng, kết nối DB thất bại
Lab 4: Bash — Viết Deploy Script
Mục tiêu
Viết một deploy script hoàn chỉnh với error handling, backup trước khi deploy, health check sau khi deploy, và rollback tự động khi có lỗi — mô phỏng quy trình deploy thực tế trong production.
Nhiệm vụ
- Viết script với
set -euo pipefail - Thêm logic backup phiên bản hiện tại
- Thêm health check với retry mechanism
- Thêm rollback tự động khi lỗi (dùng
trap ERR) - Kiểm tra script bằng
shellcheck
Các bước thực hiện
bash
# === Bước 1: Tạo thư mục project ===
mkdir -p /opt/deploy-lab/{releases,backups,shared}
echo "v1.0.0 — old version" > /opt/deploy-lab/releases/current.txt
# === Bước 2: Viết deploy script ===
cat > /opt/deploy-lab/deploy.sh << 'SCRIPT'
#!/bin/bash
set -euo pipefail
# === Cấu hình ===
APP_DIR="/opt/deploy-lab"
RELEASES_DIR="${APP_DIR}/releases"
BACKUPS_DIR="${APP_DIR}/backups"
HEALTH_URL="http://localhost:8080/health"
MAX_RETRIES=5
RETRY_DELAY=2
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# === Logging ===
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
log_error() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ❌ ERROR: $1" >&2
}
# === Rollback khi có lỗi ===
rollback() {
log_error "Deploy thất bại! Đang rollback..."
local latest_backup
latest_backup=$(ls -t "${BACKUPS_DIR}"/*.tar.gz 2>/dev/null | head -1)
if [[ -n "${latest_backup}" ]]; then
log "Khôi phục từ backup: ${latest_backup}"
tar -xzf "${latest_backup}" -C "${RELEASES_DIR}/"
log "✅ Rollback thành công"
else
log_error "Không tìm thấy backup để rollback!"
exit 1
fi
}
trap rollback ERR
# === Bước 1: Backup phiên bản hiện tại ===
log "📦 Đang backup phiên bản hiện tại..."
if [[ -f "${RELEASES_DIR}/current.txt" ]]; then
tar -czf "${BACKUPS_DIR}/backup_${TIMESTAMP}.tar.gz" \
-C "${RELEASES_DIR}" current.txt
log "✅ Backup hoàn tất: backup_${TIMESTAMP}.tar.gz"
else
log "⚠️ Không có phiên bản hiện tại để backup"
fi
# === Bước 2: Deploy phiên bản mới ===
log "🚀 Đang deploy phiên bản mới..."
echo "v2.0.0 — new version deployed at ${TIMESTAMP}" > "${RELEASES_DIR}/current.txt"
log "✅ Deploy file mới thành công"
# === Bước 3: Health check với retry ===
log "🏥 Đang kiểm tra health check..."
health_ok=false
for i in $(seq 1 "${MAX_RETRIES}"); do
log " Lần thử ${i}/${MAX_RETRIES}..."
if curl -sf --max-time 5 "${HEALTH_URL}" > /dev/null 2>&1; then
health_ok=true
break
fi
if [[ "${i}" -lt "${MAX_RETRIES}" ]]; then
log " Đợi ${RETRY_DELAY}s trước lần thử tiếp..."
sleep "${RETRY_DELAY}"
fi
done
if [[ "${health_ok}" = true ]]; then
log "✅ Health check passed!"
else
log_error "Health check thất bại sau ${MAX_RETRIES} lần thử"
exit 1 # trap ERR sẽ gọi rollback()
fi
# === Bước 4: Dọn dẹp backup cũ (giữ 5 bản gần nhất) ===
log "🧹 Dọn dẹp backup cũ..."
ls -t "${BACKUPS_DIR}"/*.tar.gz 2>/dev/null | tail -n +6 | xargs -r rm -f
log "✅ Deploy hoàn tất thành công!"
SCRIPT
chmod +x /opt/deploy-lab/deploy.shbash
# === Bước 3: Cài shellcheck và kiểm tra ===
# Trên Ubuntu/Debian:
sudo apt-get update && sudo apt-get install -y shellcheck
# Chạy shellcheck
shellcheck /opt/deploy-lab/deploy.shbash
# === Bước 4: Chạy deploy script ===
# Lưu ý: Health check sẽ thất bại nếu không có server chạy trên port 8080
# Để test rollback, chạy khi KHÔNG có server:
bash /opt/deploy-lab/deploy.sh
# Để test thành công, start server từ Lab 3 trước:
sudo systemctl start mywebapp.service
bash /opt/deploy-lab/deploy.shXác minh kết quả
bash
# ✅ Check: shellcheck không có lỗi
shellcheck /opt/deploy-lab/deploy.sh
echo $? # Kết quả mong đợi: 0
# ✅ Check: backup được tạo
ls -la /opt/deploy-lab/backups/
# Phải thấy file backup_YYYYMMDD_HHMMSS.tar.gz
# ✅ Check: rollback hoạt động (test khi không có server)
cat /opt/deploy-lab/releases/current.txt
# Nếu health check thất bại → nội dung phải là "v1.0.0 — old version" (đã rollback)
# ✅ Check: deploy thành công (test khi có server)
sudo systemctl start mywebapp.service
bash /opt/deploy-lab/deploy.sh
cat /opt/deploy-lab/releases/current.txt
# Kết quả mong đợi: "v2.0.0 — new version deployed at ..."Lỗi thường gặp
Quên set -euo pipefail!
Không có set -euo pipefail, script sẽ tiếp tục chạy ngay cả khi có lệnh thất bại. Hậu quả: deploy phiên bản lỗi mà không rollback, dữ liệu production bị ảnh hưởng.
Giải thích từng flag:
set -e→ Thoát ngay khi có lệnh trả về exit code khác 0set -u→ Báo lỗi khi dùng biến chưa khai báo (tránhrm -rf $UNSET_VAR/→ xóa/)set -o pipefail→ Pipe trả về exit code của lệnh thất bại cuối cùng (không phải lệnh cuối)
Thêm lỗi phổ biến:
- Không quote biến (
$VARthay vì"${VAR}") → lỗi khi path có khoảng trắng - Dùng
rm -rfmà không kiểm tra biến có rỗng không → tai nạn xóa toàn bộ filesystem
Lab 5: SSH — Thiết Lập Key Auth & Tunnel
Mục tiêu
Thiết lập SSH key-based authentication, cấu hình SSH config với aliases, và tạo local port forward tunnel — loại bỏ password authentication, tăng cường bảo mật.
Nhiệm vụ
- Tạo cặp key Ed25519
- Thiết lập
~/.ssh/configvới aliases - Tạo local port forward tunnel
- Xác minh tunnel hoạt động
Lab này có thể thực hành trên localhost
Bạn không cần VPS để thực hành. Dùng localhost hoặc Docker container làm target: docker run -d --name ssh-lab -p 2222:22 ubuntu:22.04.
Các bước thực hiện
bash
# === Bước 1: Chuẩn bị SSH server (dùng Docker) ===
docker run -d --name ssh-lab -p 2222:22 ubuntu:22.04 bash -c "
apt-get update && apt-get install -y openssh-server sudo &&
mkdir -p /run/sshd &&
useradd -m -s /bin/bash labuser &&
echo 'labuser:labpass' | chpasswd &&
echo 'PasswordAuthentication yes' >> /etc/ssh/sshd_config &&
/usr/sbin/sshd -D
"
# Đợi container sẵn sàng
sleep 3
# === Bước 2: Tạo cặp key Ed25519 ===
ssh-keygen -t ed25519 -C "lab@penalgo" -f ~/.ssh/lab_ed25519 -N ""
# Xem public key
cat ~/.ssh/lab_ed25519.pub
# === Bước 3: Copy public key lên server ===
ssh-copy-id -i ~/.ssh/lab_ed25519.pub -p 2222 labuser@localhost
# Nhập password: labpass
# === Bước 4: Thiết lập SSH config ===
cat >> ~/.ssh/config << 'EOF'
# --- Lab SSH Config ---
Host lab-server
HostName localhost
Port 2222
User labuser
IdentityFile ~/.ssh/lab_ed25519
IdentitiesOnly yes
ServerAliveInterval 60
ServerAliveCountMax 3
Host lab-tunnel
HostName localhost
Port 2222
User labuser
IdentityFile ~/.ssh/lab_ed25519
IdentitiesOnly yes
LocalForward 9090 localhost:8080
EOF
# Bảo vệ file config
chmod 600 ~/.ssh/config
# === Bước 5: Test kết nối bằng alias ===
ssh lab-server "echo 'SSH key auth hoạt động!'"
# === Bước 6: Tắt password authentication trên server ===
docker exec ssh-lab bash -c "
sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config &&
kill -HUP \$(cat /run/sshd.pid)
"
# Xác minh: đăng nhập bằng password phải thất bại
ssh -p 2222 -o IdentitiesOnly=yes -o PreferredAuthentications=password labuser@localhost
# Kết quả mong đợi: Permission denied
# === Bước 7: Tạo local port forward tunnel ===
# Mở tunnel trong background: forward local:9090 → remote:8080
ssh -f -N lab-tunnel
# -f: chạy nền, -N: không chạy command, chỉ forward port
# === Bước 8: Test tunnel (cần service chạy trên port 8080 bên server) ===
# Nếu bạn đã hoàn thành Lab 3, start server trên container:
docker exec ssh-lab bash -c "
apt-get install -y python3 &&
python3 -c '
from http.server import HTTPServer, BaseHTTPRequestHandler
class H(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write(b\"Hello from tunnel!\")
HTTPServer((\"0.0.0.0\", 8080), H).serve_forever()
' &
"
sleep 2
# Truy cập qua tunnel
curl http://localhost:9090
# Kết quả mong đợi: Hello from tunnel!Xác minh kết quả
bash
# ✅ Check: key pair tồn tại với permission đúng
ls -la ~/.ssh/lab_ed25519*
stat -c "%a %n" ~/.ssh/lab_ed25519
# Kết quả mong đợi: 600 /home/user/.ssh/lab_ed25519 (private key)
stat -c "%a %n" ~/.ssh/lab_ed25519.pub
# Kết quả mong đợi: 644 /home/user/.ssh/lab_ed25519.pub (public key)
# ✅ Check: SSH config hoạt động
ssh lab-server "hostname && whoami"
# Kết quả: hostname của container + "labuser"
# ✅ Check: password auth đã bị tắt
ssh -p 2222 -o PreferredAuthentications=password -o PubkeyAuthentication=no labuser@localhost 2>&1
# Kết quả mong đợi: Permission denied
# ✅ Check: tunnel đang chạy
ss -tlnp | grep 9090
# Hoặc: lsof -i :9090
# Phải thấy ssh process lắng nghe trên port 9090
# ✅ Check: tunnel chuyển tiếp đúng
curl -s http://localhost:9090
# Kết quả mong đợi: Hello from tunnel!bash
# === Dọn dẹp sau lab ===
# Kill tunnel
kill $(pgrep -f "ssh -f -N lab-tunnel")
# Xóa container
docker stop ssh-lab && docker rm ssh-lab
# Xóa config lab (tuỳ chọn)
sed -i '/# --- Lab SSH Config ---/,/LocalForward/d' ~/.ssh/config
rm -f ~/.ssh/lab_ed25519 ~/.ssh/lab_ed25519.pubLỗi thường gặp
Permission quá mở trên SSH key!
SSH từ chối sử dụng private key nếu permission quá mở. Lỗi bạn sẽ gặp:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/home/user/.ssh/lab_ed25519' are too open.Quy tắc permission SSH:
- Private key (
id_ed25519): 600 (chỉ owner đọc/ghi) - Public key (
id_ed25519.pub): 644 (ai cũng đọc được, không sao) ~/.ssh/directory: 700~/.ssh/config: 600~/.ssh/authorized_keys: 600
Thêm lỗi phổ biến:
- Quên
IdentitiesOnly yes→ SSH thử tất cả key, server khoá sau 3 lần thử sai - Dùng RSA thay vì Ed25519 → RSA chậm hơn và key dài hơn, Ed25519 là chuẩn hiện đại
- Không thiết lập
ServerAliveInterval→ connection bị drop sau vài phút idle
Completion Checklist
✅ Checklist triển khai
Đánh dấu các lab bạn đã hoàn thành:
- [ ] Lab 1: Cấu trúc thư mục với permissions đúng (
640/750/600) - [ ] Lab 2: Quản lý vòng đời tiến trình với signals (SIGHUP, SIGTERM)
- [ ] Lab 3: systemd service đang chạy, tự restart, và ghi log
- [ ] Lab 4: Deploy script pass shellcheck, có backup + rollback
- [ ] Lab 5: SSH key auth hoạt động, tunnel forward đúng port
Mục tiêu
Hoàn thành tất cả 5 lab trước khi chuyển sang bài tiếp theo. Nếu bạn gặp khó ở lab nào, hãy quay lại bài lý thuyết tương ứng để đọc lại.