全栈开发(五)服务器、运维与部署
全栈开发运维部署实战 本项目演示全栈开发中的关键运维部署技术,包含: 容器化技术:使用Docker进行多阶段构建和容器编排 CI/CD自动化:通过GitHub Actions实现持续集成/部署 负载均衡:Nginx反向代理与负载均衡配置 数据库集成:PostgreSQL和Redis容器化部署 核心实现: 基于Node.js的Express应用示例 Docker多阶段构建优化镜像大小 Docker
全栈开发:服务器、运维与部署
本项目将深入演示全栈开发中的运维部署核心技能:Linux 基础操作、Docker 容器化、多阶段构建、Docker Compose 编排、CI/CD 自动化流水线、Nginx 反向代理与负载均衡。通过一个完整的 Web 应用部署示例,展示从代码提交到上线运行的全流程。
一、UML 建模(Mermaid)
1. 部署架构图
2. CI/CD 流水线
3. Nginx 负载均衡配置
二、项目文件结构组织
我们将创建一个示例项目 deployment-demo,包含一个简单的 Node.js 应用,并展示完整的部署配置。
deployment-demo/
├── .github/
│ └── workflows/
│ └── deploy.yml # GitHub Actions CI/CD 流水线
├── app/ # 应用代码
│ ├── src/
│ │ └── index.js # 简单 Express 应用
│ ├── package.json
│ ├── Dockerfile # 多阶段构建文件
│ └── .dockerignore
├── nginx/
│ ├── nginx.conf # Nginx 主配置
│ ├── conf.d/
│ │ └── app.conf # 站点配置(反向代理、负载均衡)
│ └── ssl/ # SSL 证书目录
│ ├── fullchain.pem # 证书(示例)
│ └── privkey.pem # 私钥(示例)
├── docker-compose.yml # 多容器编排
├── scripts/
│ ├── deploy.sh # 部署脚本(SSH 远程执行)
│ └── ssl-renew.sh # 证书续期脚本(示例)
├── .env.example # 环境变量模板
└── README.md
三、源代码实现
1. 示例应用(Node.js + Express)
app/package.json
{
"name": "deployment-demo",
"version": "1.0.0",
"description": "Demo app for deployment",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js",
"test": "echo 'Running tests...' && exit 0",
"lint": "eslint src/"
},
"dependencies": {
"express": "^4.18.2",
"pg": "^8.11.0",
"redis": "^4.6.0"
},
"devDependencies": {
"eslint": "^8.40.0"
}
}
app/src/index.js
const express = require('express');
const { Pool } = require('pg');
const redis = require('redis');
const app = express();
const port = process.env.PORT || 3000;
// 数据库连接
const pool = new Pool({
host: process.env.DB_HOST || 'postgres',
user: process.env.DB_USER || 'postgres',
password: process.env.DB_PASSWORD || 'postgres',
database: process.env.DB_NAME || 'appdb',
});
// Redis 连接
const redisClient = redis.createClient({
url: `redis://${process.env.REDIS_HOST || 'redis'}:6379`,
});
redisClient.connect();
app.get('/', (req, res) => {
res.json({ message: 'Hello from deployment demo!', host: process.env.HOSTNAME });
});
app.get('/health', (req, res) => {
res.status(200).send('OK');
});
app.get('/db-test', async (req, res) => {
try {
const result = await pool.query('SELECT NOW()');
res.json({ time: result.rows[0].now });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.get('/redis-test', async (req, res) => {
await redisClient.set('test', 'ok');
const val = await redisClient.get('test');
res.json({ redis: val });
});
app.listen(port, () => {
console.log(`App listening on port ${port}`);
});
2. Dockerfile(多阶段构建)
app/Dockerfile
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# 运行阶段
FROM node:18-alpine
WORKDIR /app
# 从构建阶段复制依赖
COPY --from=builder /app/node_modules ./node_modules
COPY . .
# 创建非 root 用户运行
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
USER nodejs
EXPOSE 3000
CMD ["node", "src/index.js"]
3. Docker Compose 编排
docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:14-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: appdb
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
networks:
- app-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
app:
build: ./app
environment:
- PORT=3000
- DB_HOST=postgres
- DB_USER=postgres
- DB_PASSWORD=postgres
- DB_NAME=appdb
- REDIS_HOST=redis
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app-network
deploy:
replicas: 2 # 多实例,用于负载均衡演示(需 swarm 或 compose 版本)
# 这里仅定义单个服务,负载均衡由 Nginx 实现
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- app
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
postgres_data:
redis_data:
注意:由于 Docker Compose 默认不支持 deploy.replicas,如需多实例需使用 docker-compose up --scale app=2 或使用 Docker Swarm。上面仅作为示意,实际部署时可通过 docker-compose up --scale app=2 启动两个 app 容器,Nginx 配置 upstream 指向它们。
4. Nginx 配置
nginx/nginx.conf(主配置,包含 gzip 和基础设置)
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
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 /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
include /etc/nginx/conf.d/*.conf;
}
nginx/conf.d/app.conf(反向代理与负载均衡)
# 定义后端服务组(负载均衡)
upstream backend {
# 轮询策略(默认)
server app:3000; # 当 docker-compose 有多个 app 实例时,可添加多个
# server app_2:3000; # 示例,实际需根据容器名
# 权重轮询示例
# server app:3000 weight=3;
# server app_2:3000 weight=1;
# 保持会话的 IP 哈希策略
# ip_hash;
}
server {
listen 80;
server_name example.com; # 替换为实际域名
# 重定向到 HTTPS(如果有证书)
# return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 根路径反向代理
location / {
proxy_pass http://backend;
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;
}
# 健康检查端点
location /health {
proxy_pass http://backend/health;
access_log off;
}
# 静态资源缓存(示例)
location /static/ {
alias /var/www/static/;
expires 30d;
}
}
5. CI/CD 流水线(GitHub Actions)
.github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches:
- main # 当推送 main 分支时触发
env:
DOCKER_IMAGE_NAME: your-dockerhub-username/deployment-demo
DOCKER_TAG: ${{ github.sha }}
jobs:
test-and-build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 18
- name: Install dependencies
run: npm ci
working-directory: ./app
- name: Lint code
run: npm run lint
working-directory: ./app
- name: Run tests
run: npm test
working-directory: ./app
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build Docker image
run: docker build -t ${{ env.DOCKER_IMAGE_NAME }}:${{ env.DOCKER_TAG }} ./app
- name: Push Docker image
run: docker push ${{ env.DOCKER_IMAGE_NAME }}:${{ env.DOCKER_TAG }}
- name: Tag as latest
run: |
docker tag ${{ env.DOCKER_IMAGE_NAME }}:${{ env.DOCKER_TAG }} ${{ env.DOCKER_IMAGE_NAME }}:latest
docker push ${{ env.DOCKER_IMAGE_NAME }}:latest
deploy:
needs: test-and-build
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Deploy to server via SSH
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/deployment-demo
docker-compose pull app
docker-compose up -d --no-deps app
# 可选:清理旧镜像
docker image prune -f
6. 部署辅助脚本
scripts/deploy.sh(本地或服务器执行)
#!/bin/bash
# 在服务器上拉取最新镜像并重启服务
set -e
cd /opt/deployment-demo
echo "Pulling latest images..."
docker-compose pull
echo "Restarting services..."
docker-compose up -d
echo "Cleaning up old images..."
docker image prune -f
echo "Deployment completed."
scripts/ssl-renew.sh(使用 Certbot 续期证书)
#!/bin/bash
# 通过 Certbot 自动续期 SSL 证书
# 停止 Nginx 以释放 80/443 端口(如果使用 standalone 模式)
docker-compose stop nginx
# 运行 Certbot
certbot renew --standalone --preferred-challenges http --non-interactive --agree-tos
# 重新启动 Nginx
docker-compose start nginx
echo "SSL certificates renewed."
四、深入解析与最佳实践
1. Docker 多阶段构建
- 构建阶段:使用完整开发环境安装依赖(
npm ci),只保留生产依赖,减少镜像层数。 - 运行阶段:切换到更小的基础镜像,仅复制必要的文件,避免包含构建工具和源代码中的无关文件。
- 用户隔离:创建非 root 用户运行应用,增强安全性。
- 镜像瘦身:最终镜像大小通常从几百 MB 降至几十 MB。
2. Docker Compose 多容器编排
- 服务依赖:使用
depends_on结合condition: service_healthy确保数据库和缓存启动后再启动应用。 - 网络隔离:通过自定义网络
app-network实现服务间通信,无需暴露端口到宿主机。 - 数据持久化:使用 volumes 挂载数据库和 Redis 数据,防止容器重启后丢失。
- 环境变量管理:使用
.env文件或直接写入docker-compose.yml的environment字段。
3. Nginx 反向代理与负载均衡
- upstream 定义:支持轮询、加权轮询、IP 哈希等策略。当应用容器数量变化时,需要动态更新 upstream(可用 Consul、Nginx Plus 或重新加载)。
- SSL 配置:证书使用 Let’s Encrypt 免费证书,通过 Certbot 自动续期。示例中挂载证书文件,实际生产环境建议使用
nginx-proxy配合acme-companion自动化。 - Gzip 压缩:对常见 MIME 类型开启压缩,减少传输体积。
- 健康检查:配置
/health端点,供负载均衡器或监控使用。
4. CI/CD 流水线
- 分支策略:触发
main分支的推送,自动执行完整流程。 - 环境变量与密钥:使用 GitHub Secrets 存储敏感信息(Docker 凭证、服务器 SSH 密钥)。
- 部署策略:
- 使用
docker-compose pull拉取新镜像 docker-compose up -d重启服务(如有滚动更新需求可结合--no-deps)- 清理旧镜像释放空间
- 使用
- 安全加固:部署用户使用 SSH 密钥而非密码,限制权限。
5. Linux 常用命令(部署相关)
- 进程监控:
top,htop,ps aux - 网络诊断:
netstat -tulpn,ss -tulpn,curl -I - 日志查看:
tail -f /var/log/nginx/access.log,docker logs container_name - 文本处理:
grep,awk,sed用于日志分析和配置修改 - Shell 脚本:使用
#!/bin/bash编写自动化部署脚本,结合set -e使脚本在错误时退出。
6. 证书续期(Let’s Encrypt)
- Certbot 模式:standalone 需要临时占用 80/443 端口,适用于无 Web 服务器运行的情况;webroot 模式通过已有 Web 服务器验证,更适合生产环境。
- 自动续期:通过 crontab 定期执行
certbot renew,并在续期后重载 Nginx。 - Nginx 热重载:
docker exec nginx nginx -s reload无需重启容器。
五、总结
本项目通过一个完整的 Web 应用示例,系统性地实践了全栈开发中的服务器运维与部署核心技能:
- 容器化:Docker 多阶段构建、Docker Compose 编排
- 反向代理与负载均衡:Nginx 配置 upstream、gzip、SSL
- CI/CD:GitHub Actions 自动化构建、测试、推送、部署
- 自动化运维:Shell 脚本、证书续期
全栈开发者通过掌握这些技能,能够独立完成从代码提交到线上运行的完整闭环,并确保应用的高可用性和安全性。实际生产中可根据业务规模选择更高级的编排工具(如 Kubernetes)和 CI/CD 平台(如 GitLab CI、Jenkins),但核心思想与本方案一致。
更多推荐


所有评论(0)