Skip to content

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ạy docker compose

Khi nào cần trang này

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

FileTạo từ file mẫuĐược nạp vào dịch vụNội dung
environment/site.envenvironment/site.env.examplesite, celery, bridged, wsevent, nginxCấu hình website Django
environment/mysql.envenvironment/mysql.env.exampledb, site, celery, bridged, wseventTên database, user, mật khẩu
environment/mysql-admin.envenvironment/mysql-admin.env.exampledbMậ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

  1. Sao chép file mẫu:

    sh
    cd 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.env
  2. Thay các giá trị <secret key><password> bằng giá trị thật (xem Tạo SECRET_KEY).

  3. Sửa HOST, SITE_FULL_URL, MEDIA_URL theo 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ự:

  1. dmoj/repo/dmoj/settings.py chứa giá trị mặc định. Không sửa file này.
  2. Cuối file, settings.py chạy dmoj/repo/dmoj/local_settings.py. File này được ./scripts/initialize sao chép từ dmoj/config/local_settings.py.
  3. Trong local_settings.py, một số thiết lập được đọc bằng os.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ếnBắt buộc?Mặc định (nếu không đặt)Ý nghĩa
HOSTlocalhostTê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_URLNê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_URLhttp://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 /
DEBUGKhông0Chỉ bật khi giá trị đúng bằng 1. Mọi giá trị khác (true, yes...) đều là tắt
SECRET_KEYrỗngKhóa bí mật của Django. Để trống thì Django không khởi động được
EVENT_DAEMON_POSTKhôngws://wsevent:15101/Địa chỉ site gửi sự kiện tới wsevent
REDIS_CACHING_URLKhôngredis://redis:6379/0Redis dùng làm cache
CELERY_BROKER_URLKhôngredis://redis:6379/1Hàng đợi tác vụ của Celery
CELERY_RESULT_BACKENDKhôngredis://redis:6379/1Nơi Celery lưu kết quả tác vụ
BRIDGED_HOSTKhôngbridgedTê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_KEYCó (khi OAUTH_ONLY = True)rỗngClient ID của Google OAuth
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRETCó (khi OAUTH_ONLY = True)rỗngClient secret của Google OAuth
MOSS_API_KEYKhôngrỗngKhó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):

ini
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_HOSTS chỉ chứa đúng HOST. Nếu cần phục vụ thêm tên miền khác (ví dụ www.lcoj.example.com), hãy sửa ALLOWED_HOSTS (và CSRF_TRUSTED_ORIGINS nếu dùng HTTPS) trong local_settings.py.
  • Chạy HTTPS cần thêm thiết lập Django. Đổi SITE_FULL_URL/MEDIA_URL sang https:// là chưa đủ: phải thêm CSRF_TRUSTED_ORIGINSSECURE_PROXY_SSL_HEADER vào local_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 đặt OAUTH_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_URL và 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ền moss_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 đã đặt MOSS_API_KEY.
  • Các giá trị Docker nội bộ (EVENT_DAEMON_POST, REDIS_*, CELERY_*, BRIDGED_HOST) dùng tên dịch vụ trong docker-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ào 127.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ạnh docker-compose.yml). Đặt NGINX_PORT trong environment/site.env khô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:

ini
NGINX_PORT=8080

rồi chạy docker compose up -d nginx để tạo lại container nginx.

mysql.envmysql-admin.env

BiếnFileBắt buộc?Mặc định trong DjangoÝ nghĩa
MYSQL_HOSTmysql.envKhôngdbMáy chủ database mà Django kết nối. Image MariaDB bỏ qua biến này
MYSQL_DATABASEmysql.envdmojTên database
MYSQL_USERmysql.envdmojUser database của ứng dụng
MYSQL_PASSWORDmysql.envrỗngMật khẩu của MYSQL_USER
MYSQL_ROOT_PASSWORDmysql-admin.envMật khẩu root MariaDB, chỉ container db nhận được

Ví dụ:

ini
# environment/mysql.env
MYSQL_HOST=db
MYSQL_DATABASE=dmoj
MYSQL_USER=dmoj
MYSQL_PASSWORD=<mật khẩu mạnh>
ini
# 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ậpGiá trị hiện tạiGhi 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_ONLYTrueTắt đăng ký bằng mật khẩu
ALLOWED_HOSTS[HOST]Suy ra từ HOST
EVENT_DAEMON_GET, EVENT_DAEMON_GET_SSLws://{HOST}/event/, wss://{HOST}/event/Suy ra từ HOST
EVENT_DAEMON_POLL'/channels/'Đường dẫn long polling
CSRF_TRUSTED_ORIGINS, SECURE_PROXY_SSL_HEADERchưa đặtCầ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_TICKET5Thiết lập kế thừa từ VNOJ
Email (EMAIL_BACKEND...), ADMINSchưa cấu hình (đang comment)

Để thay đổi:

  1. Sửa file đang chạy dmoj/repo/dmoj/local_settings.py.

  2. Sửa cả bản mẫu dmoj/config/local_settings.py cho giống, vì chạy lại ./scripts/initialize sẽ sao chép đè bản mẫu lên bản đang chạy.

  3. Khởi động lại các dịch vụ chạy Django:

    sh
    cd 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ửaChạy (trong dmoj/)
environment/site.envdocker compose up -d site celery bridged
environment/mysql.envdocker 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.pydocker 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).

sh
python3 -c 'import secrets; print(secrets.token_urlsafe(50))'
sh
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ần hỗ trợ?