Vận hành LCOJ
Các thao tác hằng ngày với một bản cài LCOJ bằng Docker: bật/tắt, xem log, áp dụng cấu hình, xóa cache, sao lưu và khôi phục.
⏱ ~15 phút đọc; sao lưu/khôi phục tùy dung lượng dữ liệu · 👤 Người vận hành · 🔑 SSH + quyền chạy
dockertrên máy chủ
TIP
Mọi lệnh trong trang này chạy từ thư mục lcoj-docker/dmoj/. Danh sách service và cổng xem ở Kiến trúc hệ thống, các script trong scripts/ xem ở Script hỗ trợ.
Trước khi bắt đầu
Bật, tắt và khởi động lại
| Việc cần làm | Lệnh |
|---|---|
| Bật toàn bộ (tạo lại container nếu cấu hình đổi) | docker compose up -d |
| Xem trạng thái | docker compose ps |
| Khởi động lại một service | docker compose restart site |
| Tạm dừng / bật lại một service | docker compose stop site / docker compose start site |
| Tắt và xóa container, giữ dữ liệu | docker compose down |
Service base chỉ dùng để build image và luôn ở trạng thái đã thoát. Đó không phải lỗi.
docker compose down -v xóa những gì?
Cờ -v xóa các named volume khai báo trong docker-compose.yml. Dữ liệu chính của LCOJ nằm trong thư mục bind mount trên máy chủ nên không bị xóa.
| Dữ liệu | Nơi lưu | down -v có xóa? |
|---|---|---|
| Cơ sở dữ liệu | ./database/ (bind mount) | Không |
| Dữ liệu test | ./problems/ (bind mount) | Không |
| File tải lên | ./media/ (bind mount) | Không |
| Mã nguồn, cấu hình | ./repo/, ./environment/, ./nginx/ | Không |
| CSS/JS đã build, static | volume assets | Có, tạo lại bằng ./scripts/copy_static |
| File tải dữ liệu người dùng / kỳ thi | volume userdatacache, contestdatacache | Có, người dùng phải yêu cầu tạo lại |
| Cache dùng chung site–nginx | volume cache | Có |
| Dữ liệu Redis (cache, hàng đợi Celery) | volume ẩn danh của redis | Có |
DANGER
Sau docker compose down -v, trang web sẽ mất CSS cho đến khi bạn chạy lại ./scripts/copy_static. Muốn xóa sạch cơ sở dữ liệu thì phải xóa thư mục ./database/. Việc này không thể hoàn tác, hãy sao lưu trước.
Xem log
docker compose logs -f site # theo dõi log site
docker compose logs --tail=100 celery # 100 dòng cuối
docker compose logs --since 1h bridged # log trong 1 giờ qua
docker compose logs -f # tất cả serviceService cần xem theo từng triệu chứng:
| Triệu chứng | Service |
|---|---|
| Lỗi 500, trang không tải | site |
| Tác vụ nền (chấm lại, xuất dữ liệu) bị treo | celery |
| Máy chấm không kết nối, bài nộp đứng ở trạng thái chờ | bridged |
| Kết quả không tự cập nhật trên trang | wsevent |
| Lỗi 502, file tĩnh 404 | nginx |
Vào bên trong container
./scripts/enter_site # shell bash trong container site
./scripts/manage.py dbshell # shell SQL bằng tài khoản của site
./scripts/manage.py <lệnh> # chạy lệnh quản trị DjangoMở shell MariaDB bằng root mà không phải gõ mật khẩu ra dòng lệnh (biến môi trường có sẵn trong container db):
docker compose exec db sh -c 'MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mariadb -u root "$MYSQL_DATABASE"'INFO
Service db dùng image mariadb (bản mới nhất). Hãy dùng các lệnh mariadb, mariadb-dump, mariadb-admin, mariadb-check. Từ MariaDB 11, image chính thức không còn kèm các tên cũ mysql, mysqldump…
Danh sách lệnh quản trị xem Management Commands.
Áp dụng thay đổi cấu hình
| Bạn sửa | Cần làm |
|---|---|
environment/*.env | docker compose up -d (lệnh restart không nạp lại file env) |
repo/dmoj/local_settings.py | docker compose restart site celery bridged |
repo/uwsgi.ini | docker compose restart site |
repo/websocket/config.js | docker compose restart wsevent |
nginx/conf.d/nginx.conf | docker compose restart nginx |
SCSS, JS, ảnh trong repo/resources/, file dịch | ./scripts/copy_static rồi docker compose restart site |
| Code Python, template | docker compose restart site celery bridged |
Code không cần build lại image vì thư mục ./repo được mount thẳng vào container. Khi nào cần build lại, xem Cập nhật LCOJ.
Cache
LCOJ dùng Redis: database số 0 cho cache Django (REDIS_CACHING_URL), database số 1 cho hàng đợi Celery (CELERY_BROKER_URL). Không có lệnh clear_cache. Để xóa cache, dùng một trong hai cách:
docker compose exec site python3 manage.py shell -c "from django.core.cache import cache; cache.clear()"docker compose exec redis redis-cli -n 0 FLUSHDBWARNING
Đừng dùng FLUSHALL. Lệnh này xóa cả database số 1, làm mất các tác vụ Celery đang chờ.
Celery
docker compose exec celery celery -A dmoj_celery inspect active # tác vụ đang chạy
docker compose exec celery celery -A dmoj_celery inspect scheduled # tác vụ đã hẹn giờ
docker compose restart celeryCelery chạy với --concurrency=2 (đặt trong celery/Dockerfile).
Sao lưu
Cần sao lưu bốn thứ:
| Thành phần | Vị trí | Ghi chú |
|---|---|---|
| Cơ sở dữ liệu | container db | Dump bằng mariadb-dump khi đang chạy, không chép thô thư mục database/ |
| Dữ liệu test | problems/ | Thường là phần lớn nhất |
| File tải lên | media/ | Ảnh, PDF, file đính kèm |
| Cấu hình | environment/*.env, repo/dmoj/local_settings.py, repo/uwsgi.ini, repo/websocket/config.js, nginx/conf.d/, cùng .env và docker-compose.override.yml nếu bạn có tạo | Chứa bí mật, hãy lưu ở nơi an toàn. Cấu hình reverse proxy trên host (/etc/caddy/Caddyfile hoặc /etc/nginx/sites-available/) nằm ngoài dmoj/, hãy lưu riêng |
Luồng sao lưu và khôi phục tổng quát:
Sao lưu thủ công
Dump cơ sở dữ liệu:
shmkdir -p backups docker compose exec -T db sh -c \ 'MYSQL_PWD="$MYSQL_ROOT_PASSWORD" exec mariadb-dump -u root --single-transaction "$MYSQL_DATABASE"' \ | gzip > backups/db_$(date +%F_%H%M).sql.gzNén dữ liệu test, file tải lên và cấu hình:
shtar -czf backups/files_$(date +%F_%H%M).tar.gz \ problems media environment nginx/conf.d \ repo/dmoj/local_settings.py repo/uwsgi.ini repo/websocket/config.jsChép thư mục
backups/sang máy khác hoặc kho lưu trữ ngoài. Bản sao lưu nằm cùng máy chủ không giúp được gì khi hỏng ổ đĩa.
WARNING
Thư mục dmoj/backups/ chứa mật khẩu và không nằm trong .gitignore. Hãy thêm nó vào .gitignore (hoặc lưu bản sao lưu ngoài repo) để không lỡ commit.
Sao lưu tự động
Lưu script sau thành dmoj/backup.sh và chmod +x:
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")" # thư mục dmoj/
DEST=backups
STAMP=$(date +%F_%H%M)
mkdir -p "$DEST"
docker compose exec -T db sh -c \
'MYSQL_PWD="$MYSQL_ROOT_PASSWORD" exec mariadb-dump -u root --single-transaction "$MYSQL_DATABASE"' \
| gzip > "$DEST/db_$STAMP.sql.gz"
tar -czf "$DEST/files_$STAMP.tar.gz" \
problems media environment nginx/conf.d \
repo/dmoj/local_settings.py repo/uwsgi.ini repo/websocket/config.js
# Giữ bản sao lưu trong 7 ngày
find "$DEST" -type f -mtime +7 -deleteChạy lúc 2 giờ sáng hằng ngày (crontab -e):
0 2 * * * /đường/dẫn/tới/lcoj-docker/dmoj/backup.sh >> /var/log/lcoj_backup.log 2>&1Cờ -T trong docker compose exec là bắt buộc khi chạy từ cron vì không có terminal.
Khôi phục
Trên máy chủ đang chạy
Dừng các service ghi vào cơ sở dữ liệu, giữ
dbchạy:shdocker compose stop site celery bridgedNạp lại dump (ghi đè các bảng hiện có):
DANGER
Bước này thay dữ liệu hiện tại bằng dữ liệu trong bản sao lưu.
shgunzip -c backups/db_2026-09-19_0200.sql.gz | docker compose exec -T db sh -c \ 'MYSQL_PWD="$MYSQL_ROOT_PASSWORD" exec mariadb -u root "$MYSQL_DATABASE"'Nếu cần, giải nén file (chạy trong
dmoj/, đường dẫn trong file nén là tương đối):shtar -xzf backups/files_2026-09-19_0200.tar.gzBật lại và kiểm tra:
shdocker compose up -d ./scripts/migrate # chỉ cần nếu code mới hơn bản sao lưu
Sang máy chủ mới
Làm Bước 1–2 của Cài đặt: cài Docker, clone repo.
Trong
dmoj/, giải nén file sao lưu. Thao tác này khôi phụcproblems/,media/,environment/, cấu hình nginx và các file cấu hình trongrepo/(không cần chạyinitialize).Build image:
docker compose build base && docker compose build.Bật
dbvới thư mụcdatabase/trống để MariaDB tạo database và user từmysql.env:shdocker compose up -d db docker compose logs -f db # đợi "ready for connections"Nạp dump như bước 2 ở trên.
Bật phần còn lại, tạo lại static:
shdocker compose up -d site celery ./scripts/migrate ./scripts/copy_static docker compose up -dTrỏ bản ghi DNS sang IP của máy chủ mới và cài lại reverse proxy HTTPS trên host, xem Cài đặt: HTTPS trên VPS.
Trang bảo trì
Nginx đã cấu hình error_page 502 504 /502.html. Khi site bị dừng, người dùng sẽ thấy trang này thay vì lỗi trống. Vì vậy cách bật "chế độ bảo trì" đơn giản nhất là:
docker compose stop site # người dùng thấy trang 502.html
# ... bảo trì ...
docker compose start siteNội dung trang nằm ở repo/502.html. Sau khi sửa, chạy ./scripts/copy_static để chép sang volume assets.
Đổi mật khẩu cơ sở dữ liệu
Đổi mật khẩu trong MariaDB (thay
dmojnếuMYSQL_USERcủa bạn khác):shdocker compose exec db sh -c 'MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mariadb -u root'sqlALTER USER 'dmoj'@'%' IDENTIFIED BY '<mật khẩu mới>';Sửa
MYSQL_PASSWORDtrongenvironment/mysql.env.Tạo lại container để chúng nhận mật khẩu mới:
shdocker compose up -d
Kiểm tra kết quả
Sau khi sao lưu:
ls -lh backups/có filedb_*.sql.gzvàfiles_*.tar.gzmới, dung lượng khác 0.gunzip -t backups/db_<thời điểm>.sql.gzkhông báo lỗi (file nén còn nguyên vẹn).tar -tzf backups/files_<thời điểm>.tar.gz | headliệt kê đượcproblems/,media/,environment/…
Sau khi bật/tắt, đổi cấu hình hoặc khôi phục:
docker compose pscho thấy các service đang chạy (baseở trạng thái đã thoát là bình thường).- Trang chủ tải được, có CSS; đăng nhập được; nộp thử một bài và thấy kết quả tự cập nhật.
TIP
Thỉnh thoảng hãy thử khôi phục bản sao lưu lên một máy thử. Bản sao lưu chưa từng được khôi phục thử thì chưa chắc dùng được.
Sự cố thường gặp
| Triệu chứng | Cách xử lý |
|---|---|
| Mất CSS, file tĩnh 404 | ./scripts/copy_static && docker compose restart nginx |
| Lỗi kết nối cơ sở dữ liệu | docker compose ps db, docker compose logs db, kiểm tra environment/mysql.env |
| Tác vụ Celery bị treo | docker compose logs -f celery, rồi docker compose restart celery |
| Kết quả chấm không tự cập nhật | docker compose ps wsevent, kiểm tra EVENT_DAEMON_POST. Nếu site chạy HTTPS, kiểm tra SECURE_PROXY_SSL_HEADER (Cài đặt) |
| Container khởi động lại liên tục | docker compose logs --tail=100 <service> |
| Đầy ổ đĩa | docker image prune, docker builder prune, kiểm tra dung lượng problems/ và backups/ |
Tiếp theo
- Cập nhật LCOJ: kéo code mới và build lại image, nhớ sao lưu trước.
- Biến môi trường: ý nghĩa từng biến trong
environment/*.env. - Management Commands: các lệnh
./scripts/manage.pydùng khi quản trị. - Cài đặt: nếu cần dựng lại từ đầu trên máy chủ mới.
Cần hỗ trợ?
Tạo issue tại lcoj-docker, hoặc liên hệ qua behitek.com và luyencode.net/about/#lien-he.
