Biến môi trường
Tra cứu các file cấu hình trong
dmoj/environment/(site.env,mysql.env,mysql-admin.env): từng biến làm gì, mặc định ra sao, thiết lập nào không đổi được qua biến môi trường và cách áp dụng khi sửa.⏱ ~10 phút đọc · 👤 Người vận hành · 🔑 SSH vào máy chủ, quyền sửa file trong
dmoj/và chạydocker compose
Khi nào cần trang này
- Khi cài mới: điền
site.envvà hai file MySQL ở Bước 4 của trang Cài đặt. - Khi đổi tên miền, cổng, khóa Google OAuth hoặc mật khẩu database.
- Khi sửa cấu hình mà không thấy tác dụng: xem Áp dụng thay đổi.
Biến môi trường (environment variable) là cặp TÊN=giá_trị mà Docker đưa vào container lúc tạo; Django đọc chúng khi khởi động. Các thuật ngữ khác xem Thuật ngữ.
Tất cả đường dẫn bên dưới tính từ thư mục dmoj/ của repo lcoj-docker.
Các file môi trường
| File | Tạo từ file mẫu | Được nạp vào dịch vụ | Nội dung |
|---|---|---|---|
environment/site.env | environment/site.env.example | site, celery, bridged, wsevent, nginx | Cấu hình website Django |
environment/mysql.env | environment/mysql.env.example | db, site, celery, bridged, wsevent | Tên database, user, mật khẩu |
environment/mysql-admin.env | environment/mysql-admin.env.example | db | Mật khẩu root của MariaDB |
Các file *.env chứa bí mật nên đã được đưa vào .gitignore. Chỉ các file *.example được commit.
Tạo file lần đầu
Sao chép file mẫu:
shcd dmoj cp environment/site.env.example environment/site.env cp environment/mysql.env.example environment/mysql.env cp environment/mysql-admin.env.example environment/mysql-admin.envThay các giá trị
<secret key>và<password>bằng giá trị thật (xem Tạo SECRET_KEY).Sửa
HOST,SITE_FULL_URL,MEDIA_URLtheo tên miền của bạn.
TIP
./scripts/initialize không tạo các file .env. Script này chỉ tạo thư mục và sao chép file cấu hình mẫu (xem Các script hỗ trợ).
Cách LCOJ đọc biến môi trường
Django nạp cấu hình theo thứ tự:
dmoj/repo/dmoj/settings.pychứa giá trị mặc định. Không sửa file này.- Cuối file,
settings.pychạydmoj/repo/dmoj/local_settings.py. File này được./scripts/initializesao chép từdmoj/config/local_settings.py. - Trong
local_settings.py, một số thiết lập được đọc bằngos.environ.get('TÊN_BIẾN', 'mặc định'). Với những thiết lập này, giá trị trong file.envđược ưu tiên; nếu biến không được đặt thì dùng giá trị mặc định ghi trong code.
Nói cách khác, biến môi trường chỉ ghi đè được những thiết lập mà local_settings.py đọc từ môi trường, liệt kê ở các bảng dưới đây. Các thiết lập khác phải sửa trực tiếp trong local_settings.py (xem Thiết lập không phải biến môi trường).
site.env
| Biến | Bắt buộc? | Mặc định (nếu không đặt) | Ý nghĩa |
|---|---|---|---|
HOST | Có | localhost | Tên miền công khai, không kèm http:// và không kèm cổng. Dùng cho ALLOWED_HOSTS = [HOST] và địa chỉ WebSocket ws://HOST/event/, wss://HOST/event/ |
SITE_FULL_URL | Nên có | http://localhost/ | URL đầy đủ của site, dùng để tạo link tuyệt đối (ví dụ link trong webhook). Dùng https:// khi site chạy sau reverse proxy HTTPS |
MEDIA_URL | Có | http://localhost/ | URL gốc để truy cập file media. nginx phục vụ media ngay tại gốc site (/martor, /pdf...), nên thường trùng với SITE_FULL_URL (kể cả https://). Phải kết thúc bằng / |
DEBUG | Không | 0 | Chỉ bật khi giá trị đúng bằng 1. Mọi giá trị khác (true, yes...) đều là tắt |
SECRET_KEY | Có | rỗng | Khóa bí mật của Django. Để trống thì Django không khởi động được |
EVENT_DAEMON_POST | Không | ws://wsevent:15101/ | Địa chỉ site gửi sự kiện tới wsevent |
REDIS_CACHING_URL | Không | redis://redis:6379/0 | Redis dùng làm cache |
CELERY_BROKER_URL | Không | redis://redis:6379/1 | Hàng đợi tác vụ của Celery |
CELERY_RESULT_BACKEND | Không | redis://redis:6379/1 | Nơi Celery lưu kết quả tác vụ |
BRIDGED_HOST | Không | bridged | Tên máy của bridged. Site kết nối tới BRIDGED_HOST:9998, bridged lắng nghe judge tại BRIDGED_HOST:9999 |
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY | Có (khi OAUTH_ONLY = True) | rỗng | Client ID của Google OAuth |
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET | Có (khi OAUTH_ONLY = True) | rỗng | Client secret của Google OAuth |
MOSS_API_KEY | Không | rỗng | Khóa MOSS để kiểm tra đạo code trong kỳ thi |
Ví dụ site.env cho một bản cài chạy HTTPS tại lcoj.example.com (giá trị bí mật để dạng placeholder):
HOST=lcoj.example.com
SITE_FULL_URL=https://lcoj.example.com/
MEDIA_URL=https://lcoj.example.com/
DEBUG=0
SECRET_KEY=<chuỗi ngẫu nhiên dài>
# Event server
EVENT_DAEMON_POST=ws://wsevent:15101/
# Redis and Celery
REDIS_CACHING_URL=redis://redis:6379/0
CELERY_BROKER_URL=redis://redis:6379/1
CELERY_RESULT_BACKEND=redis://redis:6379/1
# Bridge
BRIDGED_HOST=bridged
# Đăng nhập Google
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY=<client-id>.apps.googleusercontent.com
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET=<client-secret>
# Kiểm tra đạo code (tùy chọn)
MOSS_API_KEY=<moss-user-id>Một số lưu ý:
- Chỉ có một tên miền.
ALLOWED_HOSTSchỉ chứa đúngHOST. Nếu cần phục vụ thêm tên miền khác (ví dụwww.lcoj.example.com), hãy sửaALLOWED_HOSTS(vàCSRF_TRUSTED_ORIGINSnếu dùng HTTPS) tronglocal_settings.py. - Chạy HTTPS cần thêm thiết lập Django. Đổi
SITE_FULL_URL/MEDIA_URLsanghttps://là chưa đủ: phải thêmCSRF_TRUSTED_ORIGINSvàSECURE_PROXY_SSL_HEADERvàolocal_settings.py, xem Cài đặt: Cấu hình Django cho HTTPS. - Đăng nhập chỉ qua OAuth. File
local_settings.pyđi kèm đặtOAUTH_ONLY = True, nên đăng ký bằng mật khẩu bị tắt. Thiếu hai biến Google OAuth thì người dùng mới không có cách nào đăng ký. SITE_FULL_URLvà dấu/cuối. File mẫu có/ở cuối và site vẫn chạy bình thường. Tuy nhiên một số chỗ (webhook) nối chuỗi trực tiếp nhưSITE_FULL_URL + '/user/...', nên link sinh ra có thể bị//. Nếu dùng webhook, cân nhắc bỏ/cuối.MOSS_API_KEYđể trống. Tab MOSS vẫn hiện trong trang kỳ thi (với người có quyềnmoss_contest) ngay cả khi chưa có khóa, và sẽ báo lỗi khi chạy. Chỉ dùng tab này sau khi đã đặtMOSS_API_KEY.- Các giá trị Docker nội bộ (
EVENT_DAEMON_POST,REDIS_*,CELERY_*,BRIDGED_HOST) dùng tên dịch vụ trongdocker-compose.yml. Chỉ đổi khi bạn tách dịch vụ sang máy khác.
Không bật DEBUG trên production
DEBUG=1 làm Django hiển thị trang lỗi chi tiết, lộ cấu hình và đường dẫn nội bộ cho bất kỳ ai. Chỉ dùng trên máy phát triển.
NGINX_PORT
docker-compose.yml publish nginx bằng ${NGINX_PORT:-8071}:80. Đây là biến thay thế của Docker Compose, được đọc khi Compose phân tích file YAML, không phải biến bên trong container.
- Mặc định:
8071. Reverse proxy HTTPS trên máy chủ (Caddy hoặc nginx) chuyển tiếp tới cổng này; nên chỉ bind nó vào127.0.0.1, xem Cài đặt: HTTPS trên VPS. - Compose chỉ lấy giá trị từ biến môi trường của shell hoặc file
dmoj/.env(nằm cạnhdocker-compose.yml). ĐặtNGINX_PORTtrongenvironment/site.envkhông đổi được cổng publish; nó chỉ được đưa vào bên trong container nginx và không có tác dụng gì.
Muốn đổi cổng, tạo hoặc sửa dmoj/.env:
NGINX_PORT=8080rồi chạy docker compose up -d nginx để tạo lại container nginx.
mysql.env và mysql-admin.env
| Biến | File | Bắt buộc? | Mặc định trong Django | Ý nghĩa |
|---|---|---|---|---|
MYSQL_HOST | mysql.env | Không | db | Máy chủ database mà Django kết nối. Image MariaDB bỏ qua biến này |
MYSQL_DATABASE | mysql.env | Có | dmoj | Tên database |
MYSQL_USER | mysql.env | Có | dmoj | User database của ứng dụng |
MYSQL_PASSWORD | mysql.env | Có | rỗng | Mật khẩu của MYSQL_USER |
MYSQL_ROOT_PASSWORD | mysql-admin.env | Có | — | Mật khẩu root MariaDB, chỉ container db nhận được |
Ví dụ:
# environment/mysql.env
MYSQL_HOST=db
MYSQL_DATABASE=dmoj
MYSQL_USER=dmoj
MYSQL_PASSWORD=<mật khẩu mạnh># environment/mysql-admin.env
MYSQL_ROOT_PASSWORD=<mật khẩu root khác>Tên database và user mặc định là dmoj theo quy ước của DMOJ; bạn có thể giữ nguyên.
Đổi mật khẩu sau khi đã cài
Image MariaDB chỉ dùng MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD, MYSQL_ROOT_PASSWORD để khởi tạo lần đầu, khi thư mục dmoj/database/ còn trống. Sau đó, sửa các biến này không đổi mật khẩu trong database, chỉ làm Django kết nối bằng mật khẩu mới và bị từ chối. Muốn đổi mật khẩu, hãy đổi trong MariaDB trước (ALTER USER ...), rồi mới cập nhật file .env.
./scripts/moderate_comments cũng đọc MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE trực tiếp từ environment/mysql.env.
Thiết lập không phải biến môi trường
Những thiết lập sau được ghi cứng trong local_settings.py, không đổi được qua file .env:
| Thiết lập | Giá trị hiện tại | Ghi chú |
|---|---|---|
SITE_NAME | 'LCOJ' | Tên ngắn hiển thị trên site |
SITE_LONG_NAME | 'LCOJ: Luyện Code Online Judge' | Tên đầy đủ |
SITE_ADMIN_EMAIL | '[email protected]' | Email quản trị |
SERVER_EMAIL | 'LCOJ: Luyện Code Online Judge <[email protected]>' | Người gửi email báo lỗi |
LANGUAGE_CODE | 'vi' | Ngôn ngữ mặc định |
DEFAULT_USER_TIME_ZONE | 'Asia/Ho_Chi_Minh' | Múi giờ mặc định của người dùng |
OAUTH_ONLY | True | Tắt đăng ký bằng mật khẩu |
ALLOWED_HOSTS | [HOST] | Suy ra từ HOST |
EVENT_DAEMON_GET, EVENT_DAEMON_GET_SSL | ws://{HOST}/event/, wss://{HOST}/event/ | Suy ra từ HOST |
EVENT_DAEMON_POLL | '/channels/' | Đường dẫn long polling |
CSRF_TRUSTED_ORIGINS, SECURE_PROXY_SSL_HEADER | chưa đặt | Cần thêm khi chạy sau reverse proxy HTTPS, xem Cài đặt |
DMOJ_PROBLEM_DATA_ROOT, MEDIA_ROOT, STATIC_ROOT | /problems/, /media/, /assets/static/ | Khớp với volume trong docker-compose.yml |
VNOJ_CP_TICKET | 5 | Thiết lập kế thừa từ VNOJ |
Email (EMAIL_BACKEND...), ADMINS | chưa cấu hình (đang comment) |
Để thay đổi:
Sửa file đang chạy
dmoj/repo/dmoj/local_settings.py.Sửa cả bản mẫu
dmoj/config/local_settings.pycho giống, vì chạy lại./scripts/initializesẽ sao chép đè bản mẫu lên bản đang chạy.Khởi động lại các dịch vụ chạy Django:
shcd dmoj docker compose restart site celery bridged
Đừng ghi bí mật vào local_settings.py
Nếu cần thêm một thiết lập bí mật mới, hãy đọc nó từ môi trường giống cách làm với MOSS_API_KEY: MY_KEY = os.environ.get('MY_KEY', ''), rồi đặt giá trị trong site.env.
Biến dùng cho lệnh generate_editorials
Lệnh quản trị generate_editorials đọc OPENAI_API_KEY (bắt buộc) và OPENAI_BASE_URL (tùy chọn) trực tiếp từ môi trường. Hai biến này không có trong file mẫu và không được docker-compose.yml nạp sẵn. Nếu cần, truyền khi chạy lệnh, ví dụ docker compose exec -e OPENAI_API_KEY=<key> site python3 manage.py generate_editorials ....
Áp dụng thay đổi
docker compose restart không đọc lại file env_file. Container giữ nguyên biến môi trường từ lúc được tạo. Sau khi sửa file .env, cần tạo lại container bằng docker compose up -d:
| Bạn đã sửa | Chạy (trong dmoj/) |
|---|---|
environment/site.env | docker compose up -d site celery bridged |
environment/mysql.env | docker compose up -d site celery bridged (xem cảnh báo đổi mật khẩu ở trên) |
dmoj/.env (NGINX_PORT) | docker compose up -d nginx |
local_settings.py | docker compose restart site celery bridged |
docker compose up -d chỉ tạo lại những container có cấu hình thay đổi, các container khác giữ nguyên.
Tạo SECRET_KEY
SECRET_KEY dùng để ký session và token. Hãy tạo một chuỗi ngẫu nhiên dài, không dùng lại giữa các môi trường (production, dev).
python3 -c 'import secrets; print(secrets.token_urlsafe(50))'cd dmoj
docker compose exec site python3 -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'Lệnh thứ nhất chỉ sinh các ký tự A–Z a–z 0–9 - _, an toàn khi dán vào file .env. Lệnh thứ hai là cách Django gợi ý (có trong chú thích của local_settings.py) nhưng có thể sinh ký tự $, #, (; nếu dùng, hãy kiểm tra lại giá trị sau khi dán.
Đổi SECRET_KEY
Đổi SECRET_KEY trên site đang chạy sẽ làm mất hiệu lực session hiện tại (mọi người bị đăng xuất). Giữ khóa bí mật, không commit vào git và không dán vào issue hay chat.
Tiếp theo
- Cài đặt: nếu bạn đang cài mới, quay lại Bước 5 (build image).
- Các script hỗ trợ:
initializesao chéplocal_settings.pymẫu vàorepo/. - Vận hành hằng ngày: đổi mật khẩu database đúng cách.
- Cấu hình trang web: các thiết lập chỉnh trong trang quản trị, không cần sửa file.
Cần hỗ trợ?
- Tạo issue tại GitHub Issues
- Tham khảo thêm tại behitek.com
- LCOJ hỗ trợ cài đặt miễn phí: luyencode.net/about/#lien-he
