Bài tập tuần 7 – Docker Compose
Tóm tắt: Dựng toàn bộ starter-project (frontend, backend, postgres) bằng một file
compose.yamlcấu hình qua.env, thêm healthcheck và restart policy, chứng minh vòng đời dữ liệu bằng log thật, rồi (tùy chọn) tự động hóa backup/restore PostgreSQL.
Hạn nộp: trước buổi đầu tiên của tuần 08. Hình thức: Pull Request vào repo cá nhân devops-labs, thư mục lab-13/ và lab-14/. Bài lab liên quan: Buổi 13 – Docker Compose, Buổi 14 – Production-minded Compose. Mẫu nộp bài: templates/lab-template.md.
Bối cảnh
Đến tuần 6 bạn đã tự chạy từng container bằng docker run, tự tạo network và volume. Với ba thành phần của ứng dụng Club Management, bạn phải gõ nhiều lệnh dài theo đúng thứ tự, và chỉ cần quên một cờ là API không nối được database. Docker Compose gom toàn bộ mô tả đó vào một file để cả nhóm chạy lại giống hệt nhau bằng một lệnh.
Tuần này bạn làm hai việc nối tiếp nhau. Ở lab-13/, bạn dựng hệ thống ba service, hiểu rõ vai trò từng khóa trong compose.yaml và vòng đời dữ liệu (down khác down -v). Ở lab-14/, bạn nâng cấp cấu hình theo hướng gần với môi trường thật: healthcheck, depends_on có điều kiện, restart policy, và kiểm chứng hành vi khi một service chết hoặc chưa sẵn sàng.
Bài tập chỉ dùng kiến thức của Buổi 13 và Buổi 14. Nếu bị kẹt, đọc lại các phần "Lỗi thường gặp" và "Phá lỗi và debug" của hai bài lab trước khi hỏi mentor. Mọi lệnh chạy trong terminal WSL2, trong filesystem của WSL (ví dụ ~/devops-labs), không dùng /mnt/c.
Nhiệm vụ L1 (bắt buộc)
Nhiệm vụ 1. Dựng hệ thống ba service bằng Compose
Trong lab-13/source/, đưa starter-project vào (hoặc tham chiếu tới bản của bạn) và chạy toàn bộ hệ thống bằng docker compose up -d --build.
Tiêu chí hoàn thành:
lab-13/source/compose.yamlcó đủ ba servicefrontend,backend,postgres, một networkclub-networkvà một volumepostgres-data. Postgres không publish cổng ra máy chủ.- Mật khẩu database chỉ xuất hiện trong
.env(không commit). Repo có.env.examplechỉ chứa giá trị mẫu, và.gitignorechặn.env. docker compose config --quietthành công (có log, kèm exit code).docker compose pscho thấy cả ba service chạy;curl -s http://localhost:8080/api/members(hoặc cổngWEB_PORTcủa bạn) trả về JSON có Alice và Bob.- Có log chứng minh service gọi nhau bằng tên:
docker compose execvàofrontendhoặcbackendrồi gọibackend/postgrestheo tên service (không dùng IP).
Nhiệm vụ 2. Chứng minh vòng đời dữ liệu
Thêm một thành viên mới (Carol) vào database, rồi chứng minh dữ liệu sống sót hay mất qua từng thao tác.
Tiêu chí hoàn thành:
- Có đủ ba lần gọi
/api/membersđược ghi vào log theo thứ tự: (1) sau khi thêm Carol, (2) saudocker compose downrồiup -d, (3) saudocker compose down -vrồiup -d. - Kết quả (2) còn Carol, kết quả (3) mất Carol. Mỗi lần gọi có dòng lệnh đi kèm ngay phía trên trong cùng file log.
report.mdgiải thích bằng 3 đến 5 câu vì sao hai kết quả khác nhau, nhắc tới volumepostgres-datavàinit.sql.
Nhiệm vụ 3. Healthcheck cho cả ba service và thứ tự khởi động
Trong lab-14/source/, nâng cấp compose.yaml để mỗi service có healthcheck và thứ tự khởi động dựa trên trạng thái healthy.
Tiêu chí hoàn thành:
- Postgres có healthcheck dùng
pg_isready;backendvàfrontendcó healthcheck riêng (cóinterval,timeout,retries,start_period). backenddùngdepends_onvớicondition: service_healthytớipostgres;frontendtương tự tớibackend.docker compose pstrong log cho thấy cả ba service(healthy).- Có log
docker inspectphần.State.Health.Logcủa ít nhất một service, cho thấy các lần check thực tế. report.mdtrả lời bằng lời của bạn: nếu không cócondition: service_healthythì lần khởi động đầu có thể sai ở đâu.
Nhiệm vụ 4. Restart policy và hành vi khi DB dừng
Kiểm chứng bằng thực nghiệm chứ không chỉ đọc tài liệu.
Tiêu chí hoàn thành:
- Mỗi service có khóa
restartđược chọn có chủ đích (nêu lý do trongreport.md). - Log cho thấy một lần backend thoát bất thường (ví dụ gọi
/api/membersrồidocker compose stop postgres, hoặc OOM kill) làm backend tự chạy lại (kèm kết quảdocker inspectcủaRestartCountvà tên policy), trong khidocker compose stop backendlàm nó nằm yên ở trạng tháiExited. Lưu ý Docker Engine bản mới không restart container sau lệnhdocker compose kill; nếu bạn thửkill, hãy ghi lại kết quả thật và giải thích. - Log cho thấy khi tạm dừng
postgres:/healthcủa backend trả mã 200 còn/readytrả 503 (ghi mã trạng thái bằngcurl -s -o /dev/null -w "%{http_code}\n"), rồi trở lại 200 sau khi chạy lại postgres. report.mdgiải thích khác biệt giữa/healthvà/readyvà nên chọn endpoint nào cho healthcheck của Compose (3 đến 5 câu, có nêu hệ quả).
Nhiệm vụ 5. Break -> Debug: healthcheck quá ngặt
Tự gây một lỗi có kiểm soát theo tình huống trong Buổi 14, rồi điều tra như trong một sự cố thật.
Tiêu chí hoàn thành:
- Bạn sửa một tham số healthcheck (ví dụ
timeout,retries, đường dẫn kiểm tra hoặcstart_period) khiến service bịunhealthyhoặc chặn service phụ thuộc. Ghi lại thay đổi bằnggit difftrong log. report.mdcó mục Break -> Debug gồm bốn phần: triệu chứng quan sát được, các lệnh bạn đã dùng để điều tra (docker compose ps,logs,inspect), nguyên nhân gốc, cách sửa và cách phòng tránh.- Log cho thấy trạng thái trước khi sửa (
unhealthyhoặc chặn khởi động) và sau khi sửa (healthy), cóexit code:của lệnh kiểm tra. - Bạn trả cấu hình về trạng thái đúng và
docker compose pslại sạch (healthy) trước khi nộp.
Nhiệm vụ L2 (nâng cao)
Làm tối đa 2 trong 3 thử thách. Mỗi thử thách có tiêu chí riêng; phần này là điểm thưởng, tối đa 10 điểm.
Thử thách A. Backup và restore PostgreSQL có kiểm chứng
Viết lab-14/source/backup.sh và lab-14/source/restore.sh.
Tiêu chí hoàn thành:
backup.shtạo filebackups/clubdb-YYYYmmdd-HHMMSS.sqltừ container postgres bằngpg_dump, từ chối lưu file rỗng, và chỉ giữ 5 bản mới nhất.- Script không chứa mật khẩu và chạy được từ thư mục khác (dùng
cd "$(dirname "$0")"); dùngset -euo pipefail. - Log chạy
backup.shbảy lần cho kết quảls backups | wc -lbằng5. restore.shnhận đường dẫn file backup làm tham số, restore vào một database trống, và log cho thấy dữ liệu (Carol) quay lại sau khi bạn cố tình xóa nó. Thư mụcbackups/có trong.gitignore.
Thử thách B. Secret cho Postgres bằng file
Thay biến POSTGRES_PASSWORD bằng cơ chế đọc mật khẩu từ file.
Tiêu chí hoàn thành:
compose.yamldùng khóasecretsvà biếnPOSTGRES_PASSWORD_FILEcho postgres; file mật khẩu nằm trong thư mục bị.gitignorechặn.docker compose confighoặcdocker inspectcủa postgres không còn giá trị mật khẩu ở dạng biến môi trường thường.- Hệ thống vẫn khởi động sạch từ volume mới và
/readycủa backend trả 200. report.mdnêu một giới hạn của cách này (ví dụ file vẫn nằm trên đĩa máy chủ, backend vẫn cần mật khẩu theo cách khác).
Thử thách C. Resource limits và OOM có kiểm soát
Đặt giới hạn bộ nhớ cho backend và quan sát hành vi.
Tiêu chí hoàn thành:
- Đặt giới hạn bộ nhớ quá thấp, có log
docker inspectchoOOMKilledhoặc exit code 137 (hoặc ghi lại chính xác những gì xảy ra nếu máy bạn cho kết quả khác, kèm giải thích). - Chọn giá trị cuối cùng dựa trên đo đạc
docker stats --no-stream, không đoán; ghi số liệu vàoreport.md. - Compose vẫn cho ba service
(healthy)với giá trị cuối cùng.
Bằng chứng cần nộp
Cấu trúc thư mục theo mẫu lab:
lab-13/
├── README.md
├── report.md
├── source/
│ ├── compose.yaml
│ ├── .env.example
│ └── .gitignore
├── screenshots/
└── evidence/
├── command-output.txt
└── test-results.txt
lab-14/
├── README.md
├── report.md
├── source/
│ ├── compose.yaml
│ ├── .env.example
│ ├── backup.sh (L2, Thử thách A)
│ ├── restore.sh (L2, Thử thách A)
│ └── .gitignore
├── screenshots/
└── evidence/
├── command-output.txt
└── test-results.txtBằng chứng theo từng nhiệm vụ:
| Nhiệm vụ | Bằng chứng bắt buộc |
|---|---|
| 1 | compose.yaml, .env.example; log docker compose config --quiet kèm exit code, docker compose ps, curl /api/members, lệnh exec gọi service theo tên; git ls-files hoặc git status chứng minh .env không bị theo dõi |
| 2 | Ba lần gọi /api/members kèm lệnh down, down -v, up -d trong lab-13/evidence/command-output.txt; giải thích trong report.md |
| 3 | lab-14/source/compose.yaml có healthcheck và depends_on điều kiện; log docker compose ps (healthy) và docker inspect Health Log |
| 4 | Log thoát bất thường so với stop, RestartCount, mã HTTP của /health và /ready khi postgres dừng và khi chạy lại; ghi vào lab-14/evidence/test-results.txt |
| 5 | git diff của thay đổi gây lỗi, log trước và sau khi sửa, mục Break -> Debug trong lab-14/report.md |
| L2-A | backup.sh, restore.sh, log chạy bảy lần và ls backups đếm 5 file, log restore và /api/members sau restore |
| L2-B | compose.yaml dùng secrets, log docker inspect không lộ mật khẩu, /ready 200 |
| L2-C | Log OOMKilled/exit code, số liệu docker stats, giá trị cuối cùng và lý giải |
Quy ước: mỗi lệnh trong command-output.txt được ghi dưới dạng dòng bắt đầu bằng $ rồi tới output thật. Ảnh chụp màn hình chỉ bổ trợ, không thay cho log và source. Đừng đưa mật khẩu thật vào log; che giá trị nhạy cảm trước khi nộp.
Barem chấm
| Tiêu chí | Mô tả đạt | Không đạt | Điểm |
|---|---|---|---|
| Nhiệm vụ 1: hệ thống ba service chạy bằng Compose | compose.yaml đủ service, network, volume; mật khẩu nằm ở .env; /api/members trả dữ liệu; có log gọi theo tên service | Thiếu service, mật khẩu hard-code, hoặc không có log chạy thật | 20 |
| Nhiệm vụ 2: vòng đời dữ liệu | Ba lần gọi theo đúng thứ tự, kết quả (2) còn Carol, (3) mất Carol; giải thích đúng | Thiếu lần gọi, sai thứ tự, hoặc giải thích không nhắc tới volume | 15 |
Nhiệm vụ 3: healthcheck và depends_on điều kiện | Cả ba service healthy, condition: service_healthy đúng, có Health Log | Thiếu healthcheck của một service hoặc depends_on chưa có điều kiện | 20 |
Nhiệm vụ 4: restart policy và /health, /ready | Log thoát bất thường và stop đối chiếu đúng; mã 200 và 503 đúng; giải thích đúng | Chỉ nêu lý thuyết, không có log thực nghiệm | 15 |
| Nhiệm vụ 5: Break -> Debug | Có diff, triệu chứng, quy trình điều tra, nguyên nhân gốc và cách phòng tránh | Chỉ mô tả cách sửa, không có quy trình điều tra | 10 |
| Câu hỏi kiểm tra kiến thức | 5 câu trả lời đúng và ngắn gọn trong report.md (2 điểm mỗi câu) | Sai ý chính hoặc sao chép nguyên văn từ bài lab | 10 |
| Chất lượng nộp bài và an toàn | Cấu trúc thư mục đúng, README trả lời 7 câu của template, không lộ secret, PR được review | Thiếu README, commit .env hoặc secret | 10 |
| Tổng | 100 | ||
| Điểm thưởng L2 | Mỗi thử thách đạt đủ tiêu chí cộng tối đa 5 điểm; tổng thưởng tối đa 10 | Thiếu tiêu chí đo được thì không cộng | +10 |
Ghi chú chấm: mentor có thể chạy lại docker compose up -d từ source/ của bạn trên máy khác. Bài nộp không chạy lại được chỉ nhận tối đa một nửa điểm của các nhiệm vụ liên quan.
Câu hỏi kiểm tra kiến thức
Trả lời ngắn (2 đến 4 câu mỗi câu) trong phần cuối của lab-14/report.md, bằng lời của bạn.
depends_onmặc định khác gìdepends_oncócondition: service_healthy? Cho một ví dụ lỗi mà cách mặc định gây ra.- Vì sao dữ liệu PostgreSQL còn sau
docker compose downnhưng mất saudocker compose down -v? - Trong Compose, các service gọi nhau bằng tên (ví dụ
postgres) hoạt động nhờ đâu, và vì sao cấu hìnhDB_HOST: localhosttrong backend là sai? - Khi nào
restart: unless-stoppedgiúp được bạn, và khi nào nó che giấu một lỗi thật? - Vì sao không nên publish cổng của postgres ra máy chủ trong dự án này, và cách nào đưa mật khẩu vào cấu hình mà không commit nó?
Lỗi hay bị trừ điểm
- Commit file
.env, password hoặc token vào repo (trừ nặng ở tiêu chí an toàn). compose.yamlghi sẵn mật khẩu thật thay vì dùng biến từ.env.- Log không có dòng lệnh đi kèm, hoặc output được gõ tay thay vì lấy từ lần chạy thật.
- Chỉ nộp ảnh chụp màn hình, không có
source/vàevidence/. depends_onthiếucondition, nên chỉ đảm bảo thứ tự khởi động chứ không đảm bảo sẵn sàng.- Dùng
localhostđể healthcheck hoặc kết nối DB giữa các container thay vì127.0.0.1(trong healthcheck) hoặc tên service. - Publish cổng postgres ra máy chủ, gây xung đột với PostgreSQL cài sẵn.
- Làm việc trong
/mnt/choặc dùngdocker-compose(v1) thay vìdocker compose. - Gây lỗi ở Nhiệm vụ 5 nhưng không trả cấu hình về trạng thái đúng trước khi nộp.
- Nộp file backup hoặc thư mục secret vào repo.
- Trả lời câu hỏi kiểm tra bằng cách chép lại đoạn văn của bài lab.
Quy trình nộp
- Tạo branch trong repo cá nhân, ví dụ
git switch -c week-07. - Hoàn thành
lab-13/vàlab-14/theo cấu trúc ở trên, commit nhiều lần với thông điệp rõ ràng. - Chạy lại
docker compose up -dtừsource/trên một thư mục sạch để chắc chắn bài chạy lại được. - Push branch và mở Pull Request vào nhánh chính của repo cá nhân.
- Nhờ một bạn review PR: người review đối chiếu với barem, để lại ít nhất 2 nhận xét cụ thể (một điểm tốt, một điểm cần sửa).
- Sửa theo nhận xét, rồi gắn mentor vào PR để chấm.
- Hạn nộp: trước buổi đầu tiên của tuần 08.
Checklist trước khi nộp
-
lab-13/vàlab-14/có đủREADME.md,report.md,source/,evidence/ -
README.mdtrả lời 7 câu hỏi trong template và đánh dấu level đã làm -
docker compose config --quietthành công trên cả haicompose.yaml -
docker compose up -dchạy lại được từsource/và cả ba service(healthy) -
evidence/command-output.txtchứa lệnh và kết quả thật, mỗi lệnh có dòng$ - Log đủ ba lần gọi
/api/memberscho Nhiệm vụ 2 và mã 200/503 cho Nhiệm vụ 4 - Mục Break -> Debug trong
report.mdcó triệu chứng, điều tra, nguyên nhân gốc, phòng tránh - Đã trả lời 5 câu hỏi kiểm tra kiến thức
- Không commit
.env,secrets/, backup, password, token, private key - PR đã được ít nhất một bạn review