Linux教程 / 第 160 节
第16章:Web项目部署实战
从零开始部署完整的Web应用,掌握生产环境配置
本章目标
- 掌握完整的Web项目部署流程
- 学会配置Nginx反向代理
- 能够部署Node.js/Python/Java应用
- 了解SSL证书配置和HTTPS
- 掌握自动化部署脚本编写
16.1 部署准备
16.1.1 服务器环境检查
# 检查系统信息
cat /etc/os-release
uname -a
# 检查可用资源
free -h
df -h
nproc
# 更新系统
sudo apt update && sudo apt upgrade -y
# 安装基础工具
sudo apt install -y \
git \
curl \
wget \
vim \
htop \
ufw
16.1.2 创建部署用户
# 创建专用部署用户
sudo useradd -m -s /bin/bash deploy
sudo passwd deploy
# 添加sudo权限(可选)
sudo usermod -aG sudo deploy
# 配置SSH密钥登录
sudo -u deploy mkdir -p /home/deploy/.ssh
sudo -u deploy chmod 700 /home/deploy/.ssh
# 从本地复制公钥
ssh-copy-id deploy@server-ip
# 测试登录
ssh deploy@server-ip
16.1.3 配置防火墙
# 启用UFW
sudo ufw enable
# 允许SSH
sudo ufw allow 22/tcp
# 允许HTTP和HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# 查看状态
sudo ufw status
16.2 部署Node.js应用
16.2.1 安装Node.js
# 方法1: 使用NodeSource仓库
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs
# 方法2: 使用nvm(推荐)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
source ~/.bashrc
nvm install 18
nvm use 18
# 验证安装
node --version
npm --version
16.2.2 部署Express应用
项目结构:
my-app/
├── package.json
├── server.js
├── .env
└── public/
部署步骤:
# 1. 克隆代码
cd /home/deploy
git clone https://github.com/username/my-app.git
cd my-app
# 2. 安装依赖
npm install --production
# 3. 配置环境变量
cat > .env <<EOF
PORT=3000
NODE_ENV=production
DATABASE_URL=postgresql://user:pass@localhost/dbname
EOF
# 4. 测试运行
node server.js
# 5. 使用PM2管理进程
sudo npm install -g pm2
# 启动应用
pm2 start server.js --name my-app
# 查看状态
pm2 status
# 查看日志
pm2 logs my-app
# 设置开机自启
pm2 startup systemd
sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u deploy --hp /home/deploy
pm2 save
# 重启应用
pm2 restart my-app
# 停止应用
pm2 stop my-app
PM2配置文件 (ecosystem.config.js):
module.exports = {
apps: [{
name: 'my-app',
script: './server.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000
},
error_file: './logs/err.log',
out_file: './logs/out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss'
}]
};
# 使用配置文件启动
pm2 start ecosystem.config.js
16.3 部署Python应用
16.3.1 安装Python和依赖
# 安装Python
sudo apt install -y python3 python3-pip python3-venv
# 创建虚拟环境
cd /home/deploy/my-flask-app
python3 -m venv venv
# 激活虚拟环境
source venv/bin/activate
# 安装依赖
pip install -r requirements.txt
16.3.2 部署Flask应用
使用Gunicorn:
# 安装Gunicorn
pip install gunicorn
# 测试运行
gunicorn --bind 0.0.0.0:8000 app:app
# 创建systemd服务
sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My Flask Application
After=network.target
[Service]
User=deploy
Group=deploy
WorkingDirectory=/home/deploy/my-flask-app
Environment="PATH=/home/deploy/my-flask-app/venv/bin"
ExecStart=/home/deploy/my-flask-app/venv/bin/gunicorn \
--workers 4 \
--bind 127.0.0.1:8000 \
--access-logfile /var/log/myapp/access.log \
--error-logfile /var/log/myapp/error.log \
app:app
Restart=always
[Install]
WantedBy=multi-user.target
# 创建日志目录
sudo mkdir -p /var/log/myapp
sudo chown deploy:deploy /var/log/myapp
# 启动服务
sudo systemctl daemon-reload
sudo systemctl start myapp
sudo systemctl enable myapp
# 查看状态
sudo systemctl status myapp
# 查看日志
sudo journalctl -u myapp -f
16.4 配置Nginx反向代理
16.4.1 安装Nginx
# 安装Nginx
sudo apt install -y nginx
# 启动Nginx
sudo systemctl start nginx
sudo systemctl enable nginx
# 验证
curl http://localhost
16.4.2 配置反向代理
Node.js应用配置:
# 创建配置文件
sudo nano /etc/nginx/sites-available/myapp
server {
listen 80;
server_name example.com www.example.com;
# 日志
access_log /var/log/nginx/myapp_access.log;
error_log /var/log/nginx/myapp_error.log;
# 反向代理到Node.js
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_cache_bypass $http_upgrade;
}
# 静态文件
location /static/ {
alias /home/deploy/my-app/public/;
expires 30d;
add_header Cache-Control "public, immutable";
}
}
# 启用配置
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
# 测试配置
sudo nginx -t
# 重新加载Nginx
sudo systemctl reload nginx
16.4.3 负载均衡配置
# 定义上游服务器
upstream myapp_backend {
least_conn; # 最少连接负载均衡
server 127.0.0.1:3000 weight=1;
server 127.0.0.1:3001 weight=1;
server 127.0.0.1:3002 weight=1;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://myapp_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
16.5 配置HTTPS
16.5.1 使用Let’s Encrypt免费证书
# 安装Certbot
sudo apt install -y certbot python3-certbot-nginx
# 获取证书(自动配置Nginx)
sudo certbot --nginx -d example.com -d www.example.com
# 测试自动续期
sudo certbot renew --dry-run
# 查看证书信息
sudo certbot certificates
Certbot会自动修改Nginx配置:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://localhost:3000;
# ... 其他配置
}
}
16.5.2 手动配置SSL
# 生成自签名证书(测试用)
sudo mkdir -p /etc/nginx/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/nginx/ssl/nginx.key \
-out /etc/nginx/ssl/nginx.crt
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
# SSL配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://localhost:3000;
}
}
16.6 数据库配置
16.6.1 安装MySQL
# 安装MySQL
sudo apt install -y mysql-server
# 安全配置
sudo mysql_secure_installation
# 登录MySQL
sudo mysql
# 创建数据库和用户
CREATE DATABASE myapp_db;
CREATE USER 'myapp_user'@'localhost' IDENTIFIED BY 'strong_password';
GRANT ALL PRIVILEGES ON myapp_db.* TO 'myapp_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
# 测试连接
mysql -u myapp_user -p myapp_db
16.6.2 安装PostgreSQL
# 安装PostgreSQL
sudo apt install -y postgresql postgresql-contrib
# 切换到postgres用户
sudo -u postgres psql
# 创建数据库和用户
CREATE DATABASE myapp_db;
CREATE USER myapp_user WITH PASSWORD 'strong_password';
GRANT ALL PRIVILEGES ON DATABASE myapp_db TO myapp_user;
\q
# 配置远程访问(如需要)
sudo nano /etc/postgresql/14/main/postgresql.conf
# listen_addresses = '*'
sudo nano /etc/postgresql/14/main/pg_hba.conf
# host all all 0.0.0.0/0 md5
# 重启PostgreSQL
sudo systemctl restart postgresql
16.6.3 安装Redis
# 安装Redis
sudo apt install -y redis-server
# 配置Redis
sudo nano /etc/redis/redis.conf
# bind 127.0.0.1
# requirepass your_strong_password
# 重启Redis
sudo systemctl restart redis-server
# 测试连接
redis-cli
AUTH your_strong_password
PING
16.7 自动化部署脚本
16.7.1 简单部署脚本
#!/bin/bash
# deploy.sh - 自动化部署脚本
set -e
# 配置
APP_DIR="/home/deploy/my-app"
APP_NAME="my-app"
BRANCH="main"
echo "====== Starting Deployment ======"
# 1. 拉取最新代码
echo "Pulling latest code..."
cd $APP_DIR
git pull origin $BRANCH
# 2. 安装依赖
echo "Installing dependencies..."
npm install --production
# 3. 运行数据库迁移(如果有)
echo "Running database migrations..."
npm run migrate
# 4. 构建项目(如果需要)
echo "Building project..."
npm run build
# 5. 重启应用
echo "Restarting application..."
pm2 restart $APP_NAME
# 6. 验证
echo "Verifying deployment..."
sleep 3
if pm2 list | grep -q "$APP_NAME.*online"; then
echo "✓ Deployment successful!"
else
echo "✗ Deployment failed!"
exit 1
fi
echo "====== Deployment Completed ======"
16.7.2 零停机部署脚本
#!/bin/bash
# zero-downtime-deploy.sh
set -e
APP_DIR="/home/deploy/my-app"
APP_NAME="my-app"
BACKUP_DIR="/home/deploy/backups"
DATE=$(date +%Y%m%d_%H%M%S)
echo "====== Zero-Downtime Deployment ======"
# 1. 备份当前版本
echo "Backing up current version..."
mkdir -p $BACKUP_DIR
cp -r $APP_DIR "${BACKUP_DIR}/backup_${DATE}"
# 2. 拉取新代码
echo "Pulling new code..."
cd $APP_DIR
git fetch origin
git checkout $BRANCH
git pull origin $BRANCH
# 3. 安装依赖
echo "Installing dependencies..."
npm install --production
# 4. 运行测试
echo "Running tests..."
npm test || {
echo "Tests failed! Rolling back..."
rm -rf $APP_DIR
cp -r "${BACKUP_DIR}/backup_${DATE}" $APP_DIR
exit 1
}
# 5. 平滑重启(PM2集群模式)
echo "Reloading application..."
pm2 reload $APP_NAME
# 6. 健康检查
echo "Health check..."
sleep 5
response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/health)
if [ $response -eq 200 ]; then
echo "✓ Deployment successful!"
# 清理旧备份(保留最近5个)
ls -t $BACKUP_DIR | tail -n +6 | xargs -I {} rm -rf "$BACKUP_DIR/{}"
else
echo "✗ Health check failed! Rolling back..."
rm -rf $APP_DIR
cp -r "${BACKUP_DIR}/backup_${DATE}" $APP_DIR
pm2 restart $APP_NAME
exit 1
fi
echo "====== Deployment Completed ======"
16.7.3 使用GitHub Actions自动部署
.github/workflows/deploy.yml:
name: Deploy to Production
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Deploy to server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /home/deploy/my-app
git pull origin main
npm install --production
pm2 reload my-app
16.8 监控和日志
16.8.1 应用监控
# PM2监控
pm2 monit
# PM2 Web界面
pm2 install pm2-server-monit
# 查看资源使用
pm2 list
pm2 show my-app
16.8.2 日志管理
# PM2日志
pm2 logs my-app
pm2 logs my-app --lines 100
pm2 logs my-app --err # 只看错误日志
# 日志轮转
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
# Nginx日志
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log
# 系统日志
sudo journalctl -u myapp -f
sudo journalctl -u nginx -f
16.8.3 性能监控
# 安装htop
sudo apt install htop
htop
# 查看网络连接
sudo netstat -tulpn
sudo ss -tulpn
# 查看磁盘IO
sudo iotop
# 查看进程
ps aux --sort=-%cpu | head -10
ps aux --sort=-%mem | head -10
16.9 备份策略
16.9.1 数据库备份
#!/bin/bash
# backup-db.sh
BACKUP_DIR="/backup/database"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="myapp_db"
DB_USER="myapp_user"
mkdir -p $BACKUP_DIR
# MySQL备份
mysqldump -u $DB_USER -p$DB_PASS $DB_NAME | gzip > "${BACKUP_DIR}/mysql_${DATE}.sql.gz"
# PostgreSQL备份
pg_dump -U $DB_USER $DB_NAME | gzip > "${BACKUP_DIR}/postgres_${DATE}.sql.gz"
# 删除7天前的备份
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete
echo "Database backup completed: ${DATE}"
16.9.2 文件备份
#!/bin/bash
# backup-files.sh
BACKUP_DIR="/backup/files"
DATE=$(date +%Y%m%d_%H%M%S)
SOURCE_DIR="/home/deploy/my-app"
mkdir -p $BACKUP_DIR
# 备份应用文件
tar -czf "${BACKUP_DIR}/app_${DATE}.tar.gz" \
--exclude='node_modules' \
--exclude='.git' \
$SOURCE_DIR
# 上传到远程服务器(可选)
# rsync -avz "${BACKUP_DIR}/app_${DATE}.tar.gz" user@backup-server:/backups/
# 删除30天前的备份
find $BACKUP_DIR -name "app_*.tar.gz" -mtime +30 -delete
echo "File backup completed: ${DATE}"
16.9.3 定时备份
# 编辑crontab
crontab -e
# 每天凌晨2点备份数据库
0 2 * * * /home/deploy/scripts/backup-db.sh
# 每周日凌晨3点备份文件
0 3 * * 0 /home/deploy/scripts/backup-files.sh
16.10 故障排查
16.10.1 常见问题检查清单
# 1. 检查应用是否运行
pm2 status
sudo systemctl status myapp
# 2. 检查端口是否监听
sudo netstat -tulpn | grep :3000
sudo lsof -i :3000
# 3. 检查Nginx配置
sudo nginx -t
sudo systemctl status nginx
# 4. 检查防火墙
sudo ufw status
# 5. 检查磁盘空间
df -h
# 6. 检查内存
free -h
# 7. 查看日志
pm2 logs my-app --err
sudo journalctl -u nginx -n 50
16.10.2 性能优化
# Node.js优化
# 1. 启用集群模式
pm2 start app.js -i max
# 2. 增加内存限制
pm2 start app.js --max-memory-restart 500M
# Nginx优化
sudo nano /etc/nginx/nginx.conf
# worker进程数(通常等于CPU核心数)
worker_processes auto;
# 每个worker的最大连接数
events {
worker_connections 1024;
}
http {
# 启用gzip压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# 缓存配置
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g;
# 连接超时
keepalive_timeout 65;
client_max_body_size 10M;
}
本章总结
部署流程回顾
准备阶段
- 配置服务器环境
- 创建部署用户
- 配置防火墙
应用部署
- 安装运行时环境
- 部署应用代码
- 配置进程管理器
反向代理
- 安装配置Nginx
- 设置反向代理
- 配置HTTPS
数据库
- 安装数据库
- 创建用户和数据库
- 配置连接
自动化
- 编写部署脚本
- 配置CI/CD
- 设置监控和备份
最佳实践
- ✅ 使用专用部署用户
- ✅ 配置HTTPS加密
- ✅ 使用进程管理器(PM2/systemd)
- ✅ 配置反向代理和负载均衡
- ✅ 实施备份策略
- ✅ 设置监控和日志
- ✅ 编写自动化部署脚本
下一步
- 学习日常开发工作流(第17章)
- 掌握更多运维技巧
- 优化部署流程
练习题:
- 部署一个简单的Node.js应用并配置Nginx反向代理
- 配置Let’s Encrypt SSL证书
- 编写一个自动化部署脚本
- 设置数据库定时备份
💡 提示: 部署是一个系统工程,需要考虑安全、性能、可靠性等多个方面!