Merge pull request #85 from QingdaoU/new_deploy

New deploy
This commit is contained in:
李扬 2017-06-24 23:24:06 +08:00 committed by GitHub
commit 116377dc70
25 changed files with 206 additions and 176 deletions

1
.dockerignore Normal file
View File

@ -0,0 +1 @@
.git*

1
.gitignore vendored
View File

@ -68,4 +68,3 @@ custom_settings.py
docker-compose.yml
*.zip
rsyncd.passwd
oj.conf

View File

@ -1,4 +1,4 @@
# OnlineJudge [![Build Status](https://travis-ci.org/QingdaoU/OnlineJudge.svg?branch=master)](https://travis-ci.org/QingdaoU/OnlineJudge)
# OnlineJudge
由于作者工作学习繁忙, 目前只能保证有时间的时候修复部分BUG。
@ -18,7 +18,7 @@
- 提供 Virtual Judge 和单点登录使用 API不再繁琐的进行模拟登陆
- 后台管理判题服务器,轻松分离 web 和判题服务器
安装文档: https://github.com/QingdaoU/OnlineJudge/wiki
安装文档: https://github.com/QingdaoU/OnlineJudgeDeploy
OpenAPI文档: https://github.com/QingdaoU/OnlineJudgeOpenAPI
@ -44,4 +44,4 @@ TODO
![](http://7xk96g.com1.z0.glb.clouddn.com/oj-preview/FireShot%20Capture%2033%20-%20%E5%9C%A8%E7%BA%BF%E8%AF%84%E6%B5%8B%E7%B3%BB%E7%BB%9F%20-%20%E5%90%8E%E5%8F%B0%E7%AE%A1%E7%90%86%20-%20https___qduoj.com_admin_%23problem_problem_list.png)
![](http://7xk96g.com1.z0.glb.clouddn.com/oj-preview/FireShot%20Capture%2034%20-%20%E5%9C%A8%E7%BA%BF%E8%AF%84%E6%B5%8B%E7%B3%BB%E7%BB%9F%20-%20%E5%90%8E%E5%8F%B0%E7%AE%A1%E7%90%86%20-%20https___qduoj.com_admin_%23contest_contest_list.png)
![](http://7xk96g.com1.z0.glb.clouddn.com/oj-preview/FireShot%20Capture%2034%20-%20%E5%9C%A8%E7%BA%BF%E8%AF%84%E6%B5%8B%E7%B3%BB%E7%BB%9F%20-%20%E5%90%8E%E5%8F%B0%E7%AE%A1%E7%90%86%20-%20https___qduoj.com_admin_%23contest_contest_list.png)

1
build.sh Executable file
View File

@ -0,0 +1 @@
docker build -t registry.cn-hangzhou.aliyuncs.com/qduoj/oj_web_server -f dockerfiles/oj_web_server/Dockerfile .

View File

@ -1,20 +1,15 @@
FROM ubuntu:14.04
FROM ubuntu:16.04
ENV DEBIAN_FRONTEND noninteractive
RUN rm /etc/apt/sources.list
COPY sources.list /etc/apt/
RUN apt-get update
RUN apt-get -y install software-properties-common python-software-properties python python-dev gcc g++ git libtool python-pip libseccomp-dev
RUN add-apt-repository -y ppa:webupd8team/java
RUN echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections
RUN echo debconf shared/accepted-oracle-license-v1-1 seen true | sudo debconf-set-selections
RUN apt-get update
RUN apt-get install -y oracle-java7-installer
RUN cd /tmp && git clone https://github.com/QingdaoU/Judger && cd Judger && python setup.py install
RUN apt-get update && apt-get -y install software-properties-common python-software-properties python python-dev gcc g++ git libtool python-pip libseccomp-dev
RUN add-apt-repository -y ppa:webupd8team/java && echo debconf shared/accepted-oracle-license-v1-1 select true | debconf-set-selections && echo debconf shared/accepted-oracle-license-v1-1 seen true | debconf-set-selections
RUN apt-get update && apt-get install -y oracle-java8-installer
RUN cd /tmp && git clone -b master https://github.com/QingdaoU/Judger && cd Judger && python setup.py install
RUN mkdir -p /var/judger/run/ && mkdir /var/judger/test_case/ && mkdir /var/judger/code/
RUN chmod -R 777 /var/judger/run/
COPY policy /var/judger/run/
COPY supervisord.conf /etc
COPY dockerfiles/judger/policy /var/judger/run/
COPY dockerfiles/judger/supervisord.conf /etc
RUN pip install supervisor
ADD . /var/judger/code
WORKDIR /var/judger/code/judge/
EXPOSE 8080
CMD bash /var/judger/code/dockerfiles/judger/run.sh
CMD bash /var/judger/code/dockerfiles/judger/run.sh

View File

@ -1,15 +1,10 @@
FROM python:2.7
FROM ubuntu:16.04
ENV PYTHONBUFFERED 1
RUN mkdir -p /code/log /code/test_case /code/upload
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -i http://pypi.douban.com/simple -r requirements.txt --trusted-host pypi.douban.com
RUN rm /etc/apt/sources.list
ADD sources.list /etc/apt/
RUN apt-get update && apt-get install -y nginx python-pip libmysqlclient-dev curl nodejs
RUN curl -sL https://deb.nodesource.com/setup | bash -
RUN apt-get -y install nodejs
ADD gunicorn.conf /etc
ADD supervisord.conf /etc
ADD task_queue.conf /etc
EXPOSE 8080
CMD bash /code/dockerfiles/oj_web_server/run.sh
RUN apt-get install nodejs
ADD . /code
WORKDIR /code
RUN pip install -i https://pypi.douban.com/simple -r dockerfiles/oj_web_server/requirements.txt
RUN python tools/release_static.py
CMD bash /code/dockerfiles/oj_web_server/run.sh

View File

@ -1,12 +0,0 @@
[program:gunicorn]
command=gunicorn oj.wsgi:application -b 0.0.0.0:8080 --reload
directory=/code/
user=nobody
numprocs=1
stdout_logfile=/code/log/gunicorn.log
stderr_logfile=/code/log/gunicorn.log
autostart=true
autorestart=true
startsecs=5
stopwaitsecs = 5
killasgroup=true

View File

@ -0,0 +1,85 @@
worker_processes 1;
user www-data;
error_log /dev/stderr warn;
daemon off;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# access_log /dev/stdout main;
access_log off;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
gzip on;
gzip_types application/javascript text/css;
upstream backend {
server 127.0.0.1:8080;
keepalive 32;
}
server {
listen 80 default_server;
server_name _;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
client_max_body_size 200M;
location /static/upload {
expires max;
alias /code/upload;
}
location /static {
etag off;
expires max;
alias /code/static/release;
}
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
}
}
server {
listen 443 ssl http2 default_server;
server_name _;
ssl_certificate /code/ssl/server.crt;
ssl_certificate_key /code/ssl/server.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
client_max_body_size 200M;
location /static/upload {
expires max;
alias /code/upload;
}
location /static {
etag off;
expires max;
alias /code/static/release;
}
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
}
}
}

View File

@ -1,18 +0,0 @@
server {
listen 80 default_server;
server_name _;
location /static/upload {
expires max;
alias /home/upload;
}
location /static {
etag off;
expires max;
alias /home/OnlineJudge/static/release;
}
location / {
client_max_body_size 100m;
proxy_pass http://oj_web_server:8080;
proxy_set_header Host $host;
}
}

View File

@ -2,7 +2,7 @@ django==1.9.6
MySQL-python
redis
django-redis-sessions
djangorestframework
djangorestframework==3.2.5
django-rest-swagger
gunicorn
coverage

View File

@ -1,10 +1,26 @@
#!/usr/bin/env bash
if [ ! -f "/code/oj/custom_settings.py" ]; then
cp /code/oj/custom_settings.example.py /code/oj/custom_settings.py
echo "SECRET_KEY=\"`cat /dev/urandom | head -1 | md5sum | head -c 32`\"" >> /code/oj/custom_settings.py
if [ ! -f "/code/oj/secret_key.py" ]; then
echo "SECRET_KEY=\"`cat /dev/urandom | head -1 | md5sum | head -c 32`\"" >> /code/oj/secret_key.py
fi
ca_base_dir="/code/ssl"
if [ ! -f "$ca_base_dir/server.key" ]; then
openssl req -x509 -newkey rsa:2048 -keyout "$ca_base_dir/server.key" -out "$ca_base_dir/server.crt" -days 1000 \
-subj "/C=CN/ST=Beijing/L=Beijing/O=Beijing OnlineJudge Technology Co., Ltd./OU=Service Infrastructure Department/CN=`hostname`" -nodes
fi
find /code -name "*.pyc" -delete
python -m compileall /code
chown -R nobody:nogroup /code/log /code/test_case /code/upload
echo "Waiting MySQL and Redis to start"
exec supervisord
# python -m compileall /code
chown -R nobody:nogroup /code/log /code/test_case /code/upload /code/ssl
cd /code
n=0
until [ $n -ge 5 ]
do
python tools/create_db.py &&
python manage.py migrate --no-input &&
python manage.py migrate --database=submission --no-input &&
python manage.py initadmin && break
n=$(($n+1))
sleep 8
done
exec supervisord -c /code/dockerfiles/oj_web_server/supervisord.conf

View File

@ -1,8 +0,0 @@
deb http://mirrors.aliyun.com/debian wheezy main contrib non-free
deb-src http://mirrors.aliyun.com/debian wheezy main contrib non-free
deb http://mirrors.aliyun.com/debian wheezy-updates main contrib non-free
deb-src http://mirrors.aliyun.com/debian wheezy-updates main contrib non-free
deb http://mirrors.aliyun.com/debian-security wheezy/updates main contrib non-free
deb-src http://mirrors.aliyun.com/debian-security wheezy/updates main contrib non-free

View File

@ -5,11 +5,44 @@ logfile_backups=10
loglevel=info
pidfile=/code/log/supervisord.pid
nodaemon=true
user=nobody
childlogdir=/code/log/
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock
[include]
files=gunicorn.conf task_queue.conf
[program:gunicorn]
command=gunicorn oj.wsgi:application --user nobody -b 127.0.0.1:8080 --reload
directory=/code/
numprocs=1
stdout_logfile=/code/log/gunicorn.log
stderr_logfile=/code/log/gunicorn.log
autostart=true
autorestart=true
startsecs=5
stopwaitsecs = 5
killasgroup=true
[program:task_queue]
command=celery -A oj worker -l warning
directory=/code/
user=nobody
numprocs=1
stdout_logfile=/code/log/task_queue.log
stderr_logfile=/code/log/task_queue.log
autostart=true
autorestart=true
startsecs=5
stopwaitsecs = 5
killasgroup=true
[program:nginx]
command=nginx -c /code/dockerfiles/oj_web_server/oj.conf
directory=/code/
numprocs=1
stdout_logfile=/code/log/nginx.log
stderr_logfile=/code/log/nginx.log
autostart=true
autorestart=true
startsecs=5
stopwaitsecs = 5
killasgroup=true

View File

@ -1,12 +0,0 @@
[program:task_queue]
command=celery -A oj worker --autoscale=30,4 -l DEBUG
directory=/code/
user=nobody
numprocs=1
stdout_logfile=/code/log/task_queue.log
stderr_logfile=/code/log/task_queue.log
autostart=true
autorestart=true
startsecs=5
stopwaitsecs = 5
killasgroup=true

View File

@ -6,7 +6,7 @@ from .models import JudgeServer
class CreateJudgesSerializer(serializers.Serializer):
name = serializers.CharField(max_length=30)
ip = serializers.IPAddressField()
ip = serializers.CharField(max_length=128)
port = serializers.IntegerField()
# 这个服务器最大可能运行的判题实例数量
max_instance_number = serializers.IntegerField()
@ -16,7 +16,7 @@ class CreateJudgesSerializer(serializers.Serializer):
class EditJudgesSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField(max_length=30)
ip = serializers.IPAddressField()
ip = serializers.CharField(max_length=128)
port = serializers.IntegerField()
# 这个服务器最大可能运行的判题实例数量
max_instance_number = serializers.IntegerField()

1
oj/secret_key.example.py Normal file
View File

@ -0,0 +1 @@
SECRET_KEY="123456"

View File

@ -7,8 +7,8 @@ DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': "oj",
'CONN_MAX_AGE': 0.1,
'HOST': os.environ["MYSQL_PORT_3306_TCP_ADDR"],
'CONN_MAX_AGE': 10,
'HOST': "oj_mysql",
'PORT': 3306,
'USER': os.environ["MYSQL_ENV_MYSQL_USER"],
'PASSWORD': os.environ["MYSQL_ENV_MYSQL_ROOT_PASSWORD"]
@ -16,8 +16,8 @@ DATABASES = {
'submission': {
'NAME': 'oj_submission',
'ENGINE': 'django.db.backends.mysql',
'CONN_MAX_AGE': 0.1,
'HOST': os.environ["MYSQL_PORT_3306_TCP_ADDR"],
'CONN_MAX_AGE': 10,
'HOST': "oj_mysql",
'PORT': 3306,
'USER': os.environ["MYSQL_ENV_MYSQL_USER"],
'PASSWORD': os.environ["MYSQL_ENV_MYSQL_ROOT_PASSWORD"]
@ -25,13 +25,13 @@ DATABASES = {
}
REDIS_CACHE = {
"host": os.environ["REDIS_PORT_6379_TCP_ADDR"],
"host": "oj_redis",
"port": 6379,
"db": 1
}
REDIS_QUEUE = {
"host": os.environ["REDIS_PORT_6379_TCP_ADDR"],
"host": "oj_redis",
"port": 6379,
"db": 2
}

View File

@ -23,6 +23,7 @@ elif ENV == "server":
from .server_settings import *
from .custom_settings import *
from .secret_key import *
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@ -124,18 +125,6 @@ LOGGING = {
# 日志格式
},
'handlers': {
'django_error': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': LOG_PATH + 'django.log',
'formatter': 'standard'
},
'app_info': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': LOG_PATH + 'app_info.log',
'formatter': 'standard'
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
@ -144,12 +133,12 @@ LOGGING = {
},
'loggers': {
'app_info': {
'handlers': ['app_info', "console"],
'handlers': ["console"],
'level': 'DEBUG',
'propagate': True
},
'django.request': {
'handlers': ['django_error', 'console'],
'handlers': ['console'],
'level': 'DEBUG',
'propagate': True,
},

View File

@ -28,7 +28,7 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
return temp.indexOf("&")>=0 ? temp.split("&")[0] : temp;
}
var from = getLocationVal("__from");
if(from != ""){
if(from != "" && from.substring(0, 1) == "/"){
window.location.href = from;
}
else{
@ -48,4 +48,4 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
}
});
});
});

View File

@ -47,8 +47,8 @@
</div>
<div class="col-md-4">
<div class="form-group">
<label>IP</label>
<input name="ip" type="text" class="form-control" ms-duplex="ipAddress" placeholder="IP" required data-error="请填写合法的IP地址">
<label>地址</label>
<input name="ip" type="text" class="form-control" ms-duplex="ipAddress" placeholder="IP 或域名" required data-error="请填写合法的地址">
<div class="help-block with-errors"></div>
</div>
</div>
@ -100,8 +100,8 @@
</div>
<div class="col-md-4">
<div class="form-group">
<label>IP</label>
<input name="ip" type="text" class="form-control" id="ipAddress" placeholder="IP" required data-error="请填写合法的IP地址">
<label>地址</label>
<input name="ip" type="text" class="form-control" id="ipAddress" placeholder="IP 或域名" required data-error="请填写合法的地址">
<div class="help-block with-errors"></div>
</div>
</div>

View File

@ -2,13 +2,13 @@
<label>选择语言</label>
<div>
<label class="radio-inline">
<input type="radio" name="language" value="1" checked> C (GCC 4.8)
<input type="radio" name="language" value="1" checked> C (GCC 5.4)
</label>
<label class="radio-inline">
<input type="radio" name="language" value="2"> C++ (G++ 4.3)
<input type="radio" name="language" value="2"> C++ (G++ 5.4)
</label>
<label class="radio-inline">
<input type="radio" name="language" value="3"> Java (Oracle JDK 1.7)
<input type="radio" name="language" value="3"> Java (Oracle JDK 1.8)
</label>
</div>
</div>
@ -22,4 +22,4 @@
提交代码
</button>
<img src="/static/img/loading.gif" id="loading-gif">
</div>
</div>

View File

@ -25,11 +25,11 @@
<h4>编译参数</h4>
<ul>
<li>CGCC 4.8</li>
<li>CGCC 5.4</li>
<pre>gcc -DONLINE_JUDGE -O2 -w -std=c99 {src_path} -lm -o {exe_path}main</pre>
<li>C++G++ 4.3</li>
<li>C++G++ 5.4</li>
<pre>g++ -DONLINE_JUDGE -O2 -w -std=c++11 {src_path} -lm -o {exe_path}main</pre>
<li>JavaOracle JDK 1.7</li>
<li>JavaOracle JDK 1.8</li>
<pre>
//编译
javac {src_path} -d {exe_path}

View File

@ -2,22 +2,10 @@
import os
import time
import MySQLdb
"""
docker-compose启动的时候是并行启动的,可能执行本脚本的时候MySQL还没启动完
"""
i = 3
while i:
try:
conn = MySQLdb.connect(host=os.environ["MYSQL_PORT_3306_TCP_ADDR"],
user=os.environ["MYSQL_ENV_MYSQL_USER"],
passwd=os.environ["MYSQL_ENV_MYSQL_ROOT_PASSWORD"])
conn.cursor().execute("create database if not exists oj default character set utf8;")
conn.cursor().execute("create database if not exists oj_submission default character set utf8;")
print "Create database successfully"
exit(0)
except Exception as e:
print "Failed to create database, error: " + str(e) + ", will retry in 3 seconds"
i -= 1
time.sleep(3)
print "Failed to create database"
exit(1)
conn = MySQLdb.connect(host="oj_mysql",
user=os.environ["MYSQL_ENV_MYSQL_USER"],
passwd=os.environ["MYSQL_ENV_MYSQL_ROOT_PASSWORD"])
conn.cursor().execute("create database if not exists oj default character set utf8;")
conn.cursor().execute("create database if not exists oj_submission default character set utf8;")
print "Create database successfully"

View File

@ -11,7 +11,7 @@ static_src_path = "static/src/"
static_release_path = "static/release/"
print "Begin to compress js"
if os.system("node static/src/js/r.js -o static/src/js/build.js"):
if os.system("nodejs static/src/js/r.js -o static/src/js/build.js"):
print "Failed to compress js, exit"
exit()

View File

@ -1,36 +1,13 @@
# coding=utf-8
from django.core.management.base import BaseCommand
from account.models import User, SUPER_ADMIN, UserProfile
from utils.shortcuts import rand_str
class Command(BaseCommand):
def handle(self, *args, **options):
try:
admin = User.objects.get(username="root")
if admin.admin_type == SUPER_ADMIN:
self.stdout.write(self.style.WARNING("Super admin user 'root' already exists, "
"would you like to reset it's password?\n"
"Input yes to confirm: "))
if raw_input() == "yes":
rand_password = rand_str(length=6)
admin.set_password(rand_password)
admin.save()
self.stdout.write(self.style.SUCCESS("Successfully created super admin user password.\n"
"Username: root\nPassword: %s\n"
"Remember to change password and turn on two factors auth "
"after installation." % rand_password))
else:
self.stdout.write(self.style.SUCCESS("Nothing happened"))
else:
self.stdout.write(self.style.ERROR("User 'root' is not super admin."))
except User.DoesNotExist:
user = User.objects.create(username="root", real_name="root", email="root@oj.com", admin_type=SUPER_ADMIN)
rand_password = rand_str(length=6)
user.set_password(rand_password)
user.save()
UserProfile.objects.create(user=user)
self.stdout.write(self.style.SUCCESS("Successfully created super admin user.\n"
"Username: root\nPassword: %s\n"
"Remember to change password and turn on two factors auth "
"after installation." % rand_password))
if User.objects.exists():
return
user = User.objects.create(username="root", real_name="root", email="root@oj.com", admin_type=SUPER_ADMIN)
user.set_password("password@root")
user.save()
UserProfile.objects.create(user=user)