diff --git a/Dockerfile b/Dockerfile index d764cfa..076bd46 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,5 @@ RUN pip3 install --upgrade -r requirements.txt # to avoid copying credentials inside container # COPY ./ /opt/hackergame/ -CMD ["/usr/local/bin/uwsgi", "--master", "--ini", "conf/uwsgi.ini", \ - "--ini", "conf/uwsgi-apps/hackergame-docker.ini", \ - "--set-placeholder", "appname=hackergame-docker"] +EXPOSE 2018 +CMD ["docker/start.sh"] diff --git a/README.md b/README.md index bf9817f..7b6f7f9 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,17 @@ 1. 其他配置文件:`cp conf/pgbouncer.ini /etc/pgbouncer/`, `systemctl restart pgbouncer`。 1. 配置反向代理的客户端 IP 透传:前置反向代理需使用 `X-Real-IP` 请求头传递客户端 IP,`/etc/nginx/sites-enabled/hackergame` 中需添加一行 `set_real_ip_from ` 以信任来自 `reverse-proxy-ip` 的指示客户端 IP 的请求头,否则平台不能正确获取用户 IP。 -另外我们提供 [docker compose 样例](./docker-compose.yml),但是实际部署不使用该容器版本。 +### Docker compose + +另外我们提供包括数据库与 nginx 等在内的 [docker compose 样例](./docker-compose.yml),但是**实际部署不使用该容器版本**。 + +与部署版本不同,该样例**默认开启了调试模式(环境变量 `DEBUG`),并且允许 hostname 为 localhost 等本地地址**。 + +1. 复制 `docker/.env.example` 到 `.env`:`cp docker/.env.example .env`。并修改其中的环境变量(为数据库设置密码)。 +1. 执行 `docker compose up` 启动环境。 +1. 执行 `docker exec -it hackergame ./manage.py migrate` 初始化数据库。 +1. 执行 `docker exec -it hackergame ./manage.py collectstatic` 初始化 Static 目录。 +1. 执行 `docker exec -it hackergame ./manage.py setup` 写入 Google 与 Microsoft app secret。 ### uWSGI 运行模型 diff --git a/conf/nginx-sites/hackergame-docker b/conf/nginx-sites/hackergame-docker new file mode 100644 index 0000000..2260612 --- /dev/null +++ b/conf/nginx-sites/hackergame-docker @@ -0,0 +1,39 @@ +server { + listen 80; + #server_name hack.lug.ustc.edu.cn; + server_name _; + + charset utf-8; + sendfile on; + tcp_nopush on; + tcp_nodelay on; + server_tokens off; + log_not_found off; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + access_log /var/log/nginx/hackergame.log; + error_log /var/log/nginx/hackergame.error.log; + + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; + + location /media { + root /var/opt/hackergame; + add_header Content-Type application/octet-stream; + expires -1; + } + location /static { + root /var/opt/hackergame; + expires 1h; + } + location / { + uwsgi_pass hackergame:2018; + client_max_body_size 500M; + include /etc/nginx/uwsgi_params; + } +} diff --git a/conf/settings/docker.py b/conf/settings/docker.py index 867b5ed..8fd23ff 100644 --- a/conf/settings/docker.py +++ b/conf/settings/docker.py @@ -1,9 +1,26 @@ from .hackergame import * +import os +# Official domain and localhost for local test +ALLOWED_HOSTS = ["hack.lug.ustc.edu.cn", '.localhost', '127.0.0.1', '[::1]'] +# For local test +DEBUG = os.environ.get('DEBUG', 'False') == 'True' +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'hackergame', + 'USER': 'hackergame', + 'CONN_MAX_AGE': 0, + 'ATOMIC_REQUESTS': True, + 'HOST': 'hackergame-pgbouncer', + 'PORT': 5432, + 'PASSWORD': os.environ.get('DB_PASSWORD', ''), + }, +} CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', - 'LOCATION': 'memcached:11211', + 'LOCATION': 'hackergame-memcached:11211', 'TIMEOUT': 3600, 'KEY_PREFIX': 'hackergame', }, diff --git a/conf/uwsgi-apps/hackergame-docker.ini b/conf/uwsgi-apps/hackergame-docker.ini index 152766f..ca6df1b 100644 --- a/conf/uwsgi-apps/hackergame-docker.ini +++ b/conf/uwsgi-apps/hackergame-docker.ini @@ -1,17 +1,16 @@ [uwsgi] -socket=unix:///run/uwsgi/app/hackergame-docker/socket +socket=:2018 chdir=/opt/hackergame #plugin=python3,gevent_python3 module=frontend.wsgi:application -env=DJANGO_SETTINGS_MODULE=conf.settings.hackergame env=PSYCOPG_WAIT_FUNC=wait_selector master=true #processes=16 gevent=1024 gevent-monkey-patch=true vacuum=true -home=/usr/local -uid=www-data -gid=www-data +#home=/usr/local +#uid=www-data +#gid=www-data stats=/run/uwsgi/app/hackergame-docker/stats.socket harakiri=60 diff --git a/docker-compose.yml b/docker-compose.yml index b02611f..af4af6e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,23 +4,59 @@ services: hackergame: container_name: &name hackergame hostname: *name - image: ghcr.io/ustclug/hackergame:latest + build: . restart: always environment: - DJANGO_SETTINGS_MODULE=conf.settings.docker + - DB_PASSWORD=${DB_PASSWORD} + # 调试用 + - DEBUG=True volumes: - .:/opt/hackergame/:ro - # 容器外使用 /run/uwsgi/app/hackergame-docker/socket 提供服务 - # 需要提前将 /run/uwsgi/app/hackergame-docker/ 的 owner 修改为 www-data - # 参考 conf/nginx-sites/hackergame 的配置修改 nginx 配置 - - /run/uwsgi/app/hackergame-docker/:/run/uwsgi/app/hackergame-docker/ # 存储静态网页与题目文件 - - /var/opt/hackergame/:/var/opt/hackergame/ - # 数据库,需要在容器外配置好 postgresql 和 pgbouncer - - /var/run/postgresql/:/var/run/postgresql/ + - hackergame-static:/var/opt/hackergame/ + # 很不幸,你可能还需要 bind 完整的题目目录进来(不然不方便导入) depends_on: - memcached + - pgbouncer memcached: - container_name: memcached + container_name: hackergame-memcached image: memcached restart: always + postgresql: + container_name: hackergame-postgresql + image: postgres:15 + restart: always + environment: + - POSTGRES_USER=hackergame + - POSTGRES_PASSWORD=${DB_PASSWORD} + - POSTGRES_DB=hackergame + pgbouncer: + container_name: hackergame-pgbouncer + image: edoburu/pgbouncer:latest + restart: always + environment: + - DB_USER=hackergame + - DB_PASSWORD=${DB_PASSWORD} + - DB_HOST=postgresql + - POOL_MODE=transaction + # 坑: pg14+ 默认使用 scram-sha-256, 而 pgbouncer 默认是 md5 + - AUTH_TYPE=scram-sha-256 + depends_on: + - postgresql + nginx: + container_name: hackergame-nginx + image: nginx:1.25 + restart: always + volumes: + - ./conf/nginx-sites/hackergame-docker:/etc/nginx/conf.d/default.conf:ro + - hackergame-static:/var/opt/hackergame/:ro + - nginx-log:/var/log/nginx/:rw + ports: + - 12345:80 + depends_on: + - hackergame + +volumes: + hackergame-static: + nginx-log: diff --git a/docker/.env.example b/docker/.env.example new file mode 100644 index 0000000..e7eaa61 --- /dev/null +++ b/docker/.env.example @@ -0,0 +1 @@ +DB_PASSWORD=youshouldmodifythis \ No newline at end of file diff --git a/docker/start.sh b/docker/start.sh new file mode 100755 index 0000000..5b32f6d --- /dev/null +++ b/docker/start.sh @@ -0,0 +1,17 @@ +#!/bin/sh -e + +# Set permission for /run/uwsgi/app/hackergame-docker/ +mkdir -p /run/uwsgi/app/hackergame-docker/ +chown www-data:www-data /run/uwsgi/app/hackergame-docker/ + +echo "Note that /opt/hackergame/ shall be readable by uwsgi (www-data in container)." +echo "You could set it to be readable by everyone: chmod -R a+rX hackergame/" + +echo "If this is your first time to run this container, you should run:" +echo " docker exec -it hackergame ./manage.py migrate" +echo " docker exec -it hackergame ./manage.py collectstatic" + +# Start uwsgi +exec /usr/local/bin/uwsgi --master --ini conf/uwsgi.ini \ + --ini conf/uwsgi-apps/hackergame-docker.ini \ + --set-placeholder appname=hackergame-docker