| #!/bin/bash |
|
|
| |
| |
|
|
| set -e |
|
|
| echo "==========================================" |
| echo " Ramex项目 Linux服务器自动部署脚本" |
| echo "==========================================" |
|
|
| |
| RED='\033[0;31m' |
| GREEN='\033[0;32m' |
| YELLOW='\033[1;33m' |
| NC='\033[0m' |
|
|
| |
| print_status() { |
| echo -e "${GREEN}[INFO]${NC} $1" |
| } |
|
|
| print_warning() { |
| echo -e "${YELLOW}[WARNING]${NC} $1" |
| } |
|
|
| print_error() { |
| echo -e "${RED}[ERROR]${NC} $1" |
| } |
|
|
| |
| if [ "$EUID" -eq 0 ]; then |
| print_error "请不要以root用户运行此脚本" |
| exit 1 |
| fi |
|
|
| |
| PROJECT_DIR=$(pwd) |
| PROJECT_NAME="ramex" |
| USER_NAME=$(whoami) |
|
|
| print_status "开始部署Ramex项目..." |
| print_status "项目目录: $PROJECT_DIR" |
| print_status "用户: $USER_NAME" |
|
|
| |
| print_status "更新系统包..." |
| sudo apt update && sudo apt upgrade -y |
|
|
| |
| print_status "安装系统依赖..." |
| sudo apt install -y \ |
| python3 \ |
| python3-pip \ |
| python3-venv \ |
| python3-dev \ |
| build-essential \ |
| libssl-dev \ |
| libffi-dev \ |
| libxml2-dev \ |
| libxslt1-dev \ |
| libjpeg-dev \ |
| libpng-dev \ |
| zlib1g-dev \ |
| git \ |
| nginx \ |
| dirmngr \ |
| gnupg \ |
| apt-transport-https \ |
| ca-certificates \ |
| software-properties-common |
|
|
| |
| print_status "安装R语言环境..." |
| if ! command -v R &> /dev/null; then |
| sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9 || true |
| sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/' -y || true |
| sudo apt update |
| sudo apt install -y r-base r-base-dev |
| else |
| print_status "R已经安装" |
| fi |
|
|
| |
| print_status "安装必要的R包..." |
| Rscript -e " |
| if (!require('devtools', quietly = TRUE)) install.packages('devtools', repos='https://cloud.r-project.org/') |
| if (!require('Rcpp', quietly = TRUE)) install.packages('Rcpp', repos='https://cloud.r-project.org/') |
| if (!require('RcppArmadillo', quietly = TRUE)) install.packages('RcppArmadillo', repos='https://cloud.r-project.org/') |
| if (!require('ggplot2', quietly = TRUE)) install.packages('ggplot2', repos='https://cloud.r-project.org/') |
| if (!require('dplyr', quietly = TRUE)) install.packages('dplyr', repos='https://cloud.r-project.org/') |
| if (!require('tidyr', quietly = TRUE)) install.packages('tidyr', repos='https://cloud.r-project.org/') |
| " |
|
|
| |
| print_status "创建Python虚拟环境..." |
| if [ ! -d "venv" ]; then |
| python3 -m venv venv |
| fi |
|
|
| |
| source venv/bin/activate |
|
|
| |
| print_status "安装Python依赖..." |
| pip install --upgrade pip |
| pip install -r requirements.txt |
|
|
| |
| print_status "配置Django应用..." |
|
|
| |
| python manage.py makemigrations |
| python manage.py migrate |
|
|
| |
| python manage.py collectstatic --noinput |
|
|
| |
| print_status "创建生产环境配置..." |
|
|
| |
| SERVER_IP=$(hostname -I | awk '{print $1}') |
|
|
| |
| if [ ! -f "ramex/settings_prod.py" ]; then |
| cp ramex/settings.py ramex/settings_prod.py |
| |
| |
| cat >> ramex/settings_prod.py << EOF |
| |
| # 生产环境设置 |
| DEBUG = False |
| ALLOWED_HOSTS = ['$SERVER_IP', 'localhost', '127.0.0.1'] |
| |
| # 静态文件设置 |
| STATIC_ROOT = '$PROJECT_DIR/staticfiles' |
| MEDIA_ROOT = '$PROJECT_DIR/media' |
| |
| # 日志配置 |
| LOGGING = { |
| 'version': 1, |
| 'disable_existing_loggers': False, |
| 'handlers': { |
| 'file': { |
| 'level': 'INFO', |
| 'class': 'logging.FileHandler', |
| 'filename': '$PROJECT_DIR/logs/django.log', |
| }, |
| 'console': { |
| 'level': 'INFO', |
| 'class': 'logging.StreamHandler', |
| }, |
| }, |
| 'loggers': { |
| 'django': { |
| 'handlers': ['file', 'console'], |
| 'level': 'INFO', |
| 'propagate': True, |
| }, |
| }, |
| } |
| EOF |
| fi |
|
|
| |
| mkdir -p logs |
|
|
| |
| print_status "创建Gunicorn配置..." |
| cat > gunicorn.conf.py << EOF |
| bind = "127.0.0.1:8000" |
| workers = 4 |
| worker_class = "sync" |
| worker_connections = 1000 |
| max_requests = 1000 |
| max_requests_jitter = 100 |
| timeout = 300 |
| keepalive = 2 |
| preload_app = True |
| EOF |
|
|
| |
| print_status "创建systemd服务..." |
| sudo tee /etc/systemd/system/ramex.service > /dev/null << EOF |
| [Unit] |
| Description=Ramex Django Application |
| After=network.target |
| |
| [Service] |
| Type=exec |
| User=$USER_NAME |
| Group=$USER_NAME |
| WorkingDirectory=$PROJECT_DIR |
| Environment="DJANGO_SETTINGS_MODULE=ramex.settings_prod" |
| ExecStart=$PROJECT_DIR/venv/bin/gunicorn -c gunicorn.conf.py ramex.wsgi:application |
| ExecReload=/bin/kill -s HUP \$MAINPID |
| Restart=always |
| RestartSec=5 |
| |
| [Install] |
| WantedBy=multi-user.target |
| EOF |
|
|
| |
| print_status "配置Nginx..." |
| sudo tee /etc/nginx/sites-available/ramex > /dev/null << EOF |
| server { |
| listen 80; |
| server_name $SERVER_IP localhost; |
| |
| client_max_body_size 100M; |
| |
| location /static/ { |
| alias $PROJECT_DIR/staticfiles/; |
| expires 1y; |
| add_header Cache-Control "public, immutable"; |
| } |
| |
| location /media/ { |
| alias $PROJECT_DIR/media/; |
| expires 1y; |
| add_header Cache-Control "public"; |
| } |
| |
| location / { |
| proxy_pass http://127.0.0.1:8000; |
| proxy_set_header Host \$host; |
| proxy_set_header X-Real-IP \$remote_addr; |
| proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; |
| proxy_set_header X-Forwarded-Proto \$scheme; |
| proxy_read_timeout 300; |
| proxy_connect_timeout 300; |
| proxy_send_timeout 300; |
| } |
| } |
| EOF |
|
|
| |
| sudo ln -sf /etc/nginx/sites-available/ramex /etc/nginx/sites-enabled/ |
| sudo rm -f /etc/nginx/sites-enabled/default |
|
|
| |
| sudo nginx -t |
|
|
| |
| print_status "启动服务..." |
|
|
| |
| sudo systemctl daemon-reload |
|
|
| |
| sudo systemctl enable ramex |
| sudo systemctl start ramex |
|
|
| |
| sudo systemctl enable nginx |
| sudo systemctl restart nginx |
|
|
| |
| print_status "创建备份脚本..." |
| mkdir -p backups |
|
|
| cat > backup.sh << EOF |
| #!/bin/bash |
| BACKUP_DIR="$PROJECT_DIR/backups" |
| DATE=\$(date +%Y%m%d_%H%M%S) |
| |
| mkdir -p \$BACKUP_DIR |
| |
| # 备份SQLite数据库 |
| if [ -f "$PROJECT_DIR/db.sqlite3" ]; then |
| cp $PROJECT_DIR/db.sqlite3 \$BACKUP_DIR/db_\$DATE.sqlite3 |
| fi |
| |
| # 备份媒体文件 |
| if [ -d "$PROJECT_DIR/media" ]; then |
| tar -czf \$BACKUP_DIR/media_\$DATE.tar.gz $PROJECT_DIR/media/ |
| fi |
| |
| # 清理30天前的备份 |
| find \$BACKUP_DIR -name "*.sqlite3" -mtime +30 -delete |
| find \$BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete |
| |
| echo "备份完成: \$DATE" |
| EOF |
|
|
| chmod +x backup.sh |
|
|
| |
| print_status "检查服务状态..." |
|
|
| sleep 5 |
|
|
| if systemctl is-active --quiet ramex; then |
| print_status "Ramex服务运行正常" |
| else |
| print_error "Ramex服务启动失败" |
| sudo systemctl status ramex |
| fi |
|
|
| if systemctl is-active --quiet nginx; then |
| print_status "Nginx服务运行正常" |
| else |
| print_error "Nginx服务启动失败" |
| sudo systemctl status nginx |
| fi |
|
|
| |
| echo "==========================================" |
| print_status "部署完成!" |
| echo "==========================================" |
| echo "" |
| echo "访问信息:" |
| echo " 网站地址: http://$SERVER_IP" |
| echo " 管理后台: http://$SERVER_IP/admin/" |
| echo "" |
| echo "常用命令:" |
| echo " 查看服务状态: sudo systemctl status ramex" |
| echo " 重启服务: sudo systemctl restart ramex" |
| echo " 查看日志: sudo journalctl -u ramex -f" |
| echo " 运行备份: ./backup.sh" |
| echo "" |
| print_warning "请记得:" |
| print_warning "1. 创建Django超级用户: python manage.py createsuperuser" |
| print_warning "2. 根据需要修改 ramex/settings_prod.py 中的配置" |
| print_warning "3. 配置防火墙开放80端口" |
| print_warning "4. 考虑配置HTTPS证书" |
| echo "" |
|
|
| |
| read -p "是否现在创建Django超级用户?(y/n): " -n 1 -r |
| echo |
| if [[ $REPLY =~ ^[Yy]$ ]]; then |
| source venv/bin/activate |
| python manage.py createsuperuser |
| fi |
|
|
| print_status "自动部署脚本执行完成!" |