---
week: 6
lesson: 6.2
title: 实战——部署到云服务器
duration: 45 分钟
---

# 6.2 实战：部署到云服务器

> **周次**: 第六周 | **课时**: 6.2 | **时长**: 45 分钟

## 学习目标

- 掌握购买和配置云服务器（阿里云/腾讯云）的完整流程
- 学会使用 SSH 远程管理服务器
- 能够在云服务器上安装 Docker 并部署 Agent 应用
- 掌握域名绑定和 SSL 证书配置
- 学会设置服务监控和自动重启

---

## 一、购买云服务器

### 1.1 阿里云 ECS 购买步骤

**第一步：注册/登录**

访问 [阿里云官网](https://www.aliyun.com/)，注册账号并完成实名认证。

**第二步：选择云服务器 ECS**

导航到 产品 → 弹性计算 → 云服务器 ECS → 立即购买

**第三步：配置选择**

| 配置项 | 推荐值 | 说明 |
|--------|--------|------|
| 地域 | 华北2（北京）/ 华东1（杭州） | 选离用户最近的 |
| 实例规格 | ecs.t6-c1m2.large（2C4G） | 入门够用 |
| 操作系统 | Ubuntu 22.04 LTS | 本教程使用 |
| 系统盘 | 40GB ESSD | 默认即可 |
| 带宽 | 按固定带宽 3Mbps | 小型项目 1-3M 够用 |
| 购买时长 | 1 年 | 新用户优惠最大 |

**新用户优惠**：通常 ¥99-150/年（2C2G 配置）。

### 1.2 腾讯云 CVM 购买步骤

访问 [腾讯云官网](https://cloud.tencent.com/)，流程类似：

导航到 云产品 → 云服务器 → 快速配置

推荐配置：标准型 SA3，2 核 4GB，Ubuntu 22.04，带宽 3Mbps。

---

## 二、SSH 连接服务器

### 2.1 获取服务器信息

购买完成后，在控制台找到：

- **公网 IP**：如 `123.45.67.89`
- **登录用户名**：Ubuntu 系统默认 `root` 或 `ubuntu`
- **密码**：购买时设置的，或通过控制台重置

### 2.2 使用 SSH 连接

**Windows 用户**（PowerShell 或 CMD）：

```bash
ssh root@123.45.67.89
# 首次连接会提示确认，输入 yes
# 然后输入密码
```

**推荐工具**：

| 工具 | 特点 |
|------|------|
| PowerShell / CMD 自带 ssh | 最简，够用 |
| MobaXterm | 免费，带文件浏览器 |
| Xshell | 个人免费，功能强大 |
| Tabby | 开源，现代化终端 |

### 2.3 配置密钥登录（更安全）

```bash
# 1. 本地生成密钥对（如果没有）
ssh-keygen -t ed25519 -C "your_email@example.com"
# 按三次回车使用默认设置

# 2. 将公钥复制到服务器
ssh-copy-id root@123.45.67.89
# 输入密码后，以后就可以免密登录

# 3. （可选）禁用密码登录
# SSH 到服务器后编辑：
sudo nano /etc/ssh/sshd_config
# 找到并修改：
# PasswordAuthentication no
# 然后重启 SSH 服务：
sudo systemctl restart sshd
```

---

## 三、服务器初始化配置

### 3.1 更新系统

```bash
# 更新软件包列表
apt update

# 升级已安装的软件包
apt upgrade -y

# 安装常用工具
apt install -y curl wget git vim htop net-tools ufw
```

### 3.2 配置防火墙

```bash
# 启用 UFW 防火墙
ufw enable

# 允许 SSH 连接（重要！否则会被锁在外面）
ufw allow 22/tcp

# 允许 HTTP 和 HTTPS
ufw allow 80/tcp
ufw allow 443/tcp

# 允许应用端口（根据实际端口调整）
ufw allow 8000/tcp

# 查看防火墙状态
ufw status verbose

# 输出应该类似：
# Status: active
#
# To                         Action      From
# --                         ------      ----
# 22/tcp                     ALLOW       Anywhere
# 80/tcp                     ALLOW       Anywhere
# 443/tcp                    ALLOW       Anywhere
# 8000/tcp                   ALLOW       Anywhere
```

### 3.3 配置时区

```bash
# 设置时区为中国时区
timedatectl set-timezone Asia/Shanghai

# 验证
timedatectl
```

---

## 四、安装 Docker 和 Docker Compose

### 4.1 安装 Docker

```bash
# 1. 卸载旧版本（如果有）
apt remove -y docker docker-engine docker.io containerd runc

# 2. 安装依赖
apt install -y ca-certificates curl gnupg lsb-release

# 3. 添加 Docker 官方 GPG 密钥
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
  | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg

# 4. 添加 Docker 仓库
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

# 5. 安装 Docker
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 6. 验证安装
docker --version
# 输出: Docker version 24.x.x, build xxxxxxx

docker compose version
# 输出: Docker Compose version v2.x.x
```

### 4.2 配置 Docker 非 root 用户

```bash
# 将当前用户加入 docker 组（可选，推荐）
usermod -aG docker $USER

# 使更改生效（重新登录或执行）
newgrp docker

# 启动 Docker 并设置开机自启
systemctl enable docker
systemctl start docker
```

### 4.3 配置 Docker 镜像加速（国内服务器必做）

```bash
# 创建或编辑 Docker 配置
mkdir -p /etc/docker
cat > /etc/docker/daemon.json << 'EOF'
{
  "registry-mirrors": [
    "https://docker.1ms.run",
    "https://docker.xuanyuan.me",
    "https://hub.rat.dev"
  ]
}
EOF

# 重启 Docker 使配置生效
systemctl daemon-reload
systemctl restart docker
```

---

## 五、部署 Agent 应用

### 5.1 上传项目到服务器

**方法一：Git 拉取（推荐）**

```bash
# 在服务器上
mkdir -p /opt/my-agent
cd /opt/my-agent

# 拉取代码（私有仓库需要配置 SSH key 或使用 token）
git clone https://github.com/yourname/my-agent-project.git .
```

**方法二：SCP 上传**

```bash
# 在本地电脑执行
scp -r ./my-agent-project/ root@123.45.67.89:/opt/my-agent/
```

**方法三：SFTP 文件传输**

用 MobaXterm 或 FileZilla 直接拖拽上传。

### 5.2 创建环境变量文件

```bash
cd /opt/my-agent

# 创建 .env 文件
cat > .env << 'EOF'
OPENAI_API_KEY=sk-your-api-key-here
DATABASE_URL=postgresql://agent_user:agent_pass@db:5432/agentdb
AGENT_MODEL=gpt-4o-mini
LOG_LEVEL=info
EOF

# 设置权限（防止其他人读取）
chmod 600 .env
```

### 5.3 编写 Dockerfile

```dockerfile
# /opt/my-agent/Dockerfile

FROM python:3.11-slim

WORKDIR /app

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PIP_NO_CACHE_DIR=1

# 安装系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    && rm -rf /var/lib/apt/lists/*

# 安装 Python 依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制代码
COPY . .

# 创建非 root 用户运行应用（安全最佳实践）
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD curl -f http://localhost:8000/health || exit 1

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]
```

### 5.4 编写 docker-compose.yml

```yaml
# /opt/my-agent/docker-compose.yml

version: '3.8'

services:
  # ===== Agent 应用 =====
  app:
    build: .
    container_name: agent-app
    ports:
      - "8000:8000"
    env_file:
      - .env
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped
    networks:
      - agent-net
    # 资源限制
    deploy:
      resources:
        limits:
          memory: 1G
          cpus: '1.0'
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

  # ===== PostgreSQL 数据库 =====
  db:
    image: postgres:15-alpine
    container_name: agent-db
    environment:
      POSTGRES_USER: agent_user
      POSTGRES_PASSWORD: agent_pass
      POSTGRES_DB: agentdb
    volumes:
      - pgdata:/var/lib/postgresql/data
    restart: unless-stopped
    networks:
      - agent-net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U agent_user"]
      interval: 10s
      timeout: 5s
      retries: 5
    deploy:
      resources:
        limits:
          memory: 512M

  # ===== Redis 缓存 =====
  redis:
    image: redis:7-alpine
    container_name: agent-redis
    command: redis-server --appendonly yes
    volumes:
      - redisdata:/data
    restart: unless-stopped
    networks:
      - agent-net

  # ===== Nginx 反向代理 =====
  nginx:
    image: nginx:alpine
    container_name: agent-nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certbot/conf:/etc/letsencrypt:ro
    depends_on:
      - app
    restart: unless-stopped
    networks:
      - agent-net

volumes:
  pgdata:
  redisdata:

networks:
  agent-net:
    driver: bridge
```

### 5.5 编写 Nginx 配置

```nginx
# /opt/my-agent/nginx.conf

events {
    worker_connections 1024;
}

http {
    # 限流配置
    limit_req_zone $binary_remote_addr zone=api:10m rate=30r/m;

    # 上游 Agent 应用
    upstream agent_backend {
        server app:8000;
    }

    server {
        listen 80;
        server_name your-domain.com;  # 替换为你的域名

        # 让 Let's Encrypt 验证通过
        location /.well-known/acme-challenge/ {
            root /var/www/certbot;
        }

        # 所有请求转发到 Agent
        location / {
            limit_req zone=api burst=10 nodelay;

            proxy_pass http://agent_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;

            # SSE / WebSocket 支持
            proxy_buffering off;
            proxy_cache off;
            proxy_read_timeout 300s;
        }
    }

    # HTTPS 配置（SSL 证书配置好后取消注释）
    # server {
    #     listen 443 ssl http2;
    #     server_name your-domain.com;
    #
    #     ssl_certificate     /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    #     ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
    #
    #     location / {
    #         proxy_pass http://agent_backend;
    #         proxy_set_header Host $host;
    #         proxy_set_header X-Real-IP $remote_addr;
    #     }
    # }
}
```

### 5.6 启动服务

```bash
cd /opt/my-agent

# 构建并启动所有服务
docker compose up -d --build

# 查看服务状态
docker compose ps

# 输出应显示所有容器 running：
# NAME           STATUS          PORTS
# agent-app      Up (healthy)    0.0.0.0:8000->8000/tcp
# agent-db       Up (healthy)    5432/tcp
# agent-redis    Up              6379/tcp
# agent-nginx    Up              0.0.0.0:80->80/tcp, 443/tcp

# 查看应用日志
docker compose logs -f app

# 测试健康检查
curl http://127.0.0.1:8000/health
```

---

## 六、配置域名和 SSL 证书

### 6.1 购买域名

在国内服务商购买域名（如阿里云万网、腾讯云 DNSPod）：

| 域名后缀 | 价格（约） | 备注 |
|---------|-----------|------|
| .com | ¥60-70/年 | 最通用 |
| .cn | ¥30-40/年 | 国内推荐 |
| .net | ¥60-70/年 | 备选 |

购买后需要将域名解析到服务器 IP：

```
记录类型    主机记录    记录值          TTL
A          @          123.45.67.89   600
A          www        123.45.67.89   600
```

等待 10-30 分钟 DNS 生效。

### 6.2 使用宝塔面板（推荐新手）

宝塔面板提供可视化管理界面，内置 SSL 配置。

```bash
# 安装宝塔面板（Ubuntu）
wget -O install.sh https://download.bt.cn/install/install-ubuntu_6.0.sh
sudo bash install.sh

# 安装完成后，控制台会显示面板地址和登录信息
# 例如: http://123.45.67.89:8888/xxxxxx
# 用户名: xxxxxxxx
# 密码: xxxxxxxx
```

在宝塔面板中：

1. 进入 网站 → 添加站点
2. 填写域名，PHP 版本选"纯静态"
3. 点击 SSL → Let's Encrypt → 申请
4. 勾选域名，点击申请
5. 开启"强制 HTTPS"

### 6.3 手动配置 Let's Encrypt（不用宝塔）

```bash
# 安装 certbot
apt install -y certbot

# 停止 nginx（避免端口冲突）
docker compose stop nginx

# 申请证书
certbot certonly --standalone \
  -d your-domain.com \
  -d www.your-domain.com \
  --email your-email@example.com \
  --agree-tos \
  --non-interactive

# 证书位置: /etc/letsencrypt/live/your-domain.com/

# 将证书映射到 docker compose 卷
mkdir -p ./certbot/conf
cp -r /etc/letsencrypt/live ./certbot/conf/
cp -r /etc/letsencrypt/archive ./certbot/conf/

# 修改 nginx.conf 取消 HTTPS 部分的注释
# 然后重启服务
docker compose up -d nginx
```

### 6.4 自动续期证书

```bash
# 添加定时任务
crontab -e

# 添加这一行（每月 1 号凌晨 3 点检查续期）
0 3 1 * * certbot renew --quiet && docker compose -f /opt/my-agent/docker-compose.yml restart nginx
```

---

## 七、服务监控和自动重启

### 7.1 Docker 自动重启策略

```yaml
# docker-compose.yml 中已配置
restart: unless-stopped
```

`unless-stopped` 表示容器崩溃或服务器重启后自动恢复，除非你手动停止它。

### 7.2 查看容器资源使用

```bash
# 实时查看
docker stats

# 输出示例：
# CONTAINER ID   NAME        CPU %   MEM USAGE   MEM LIMIT
# abc123         agent-app   5.2%    256MiB      1GiB
# def456         agent-db    2.1%    128MiB      512MiB
```

### 7.3 简易监控脚本

```bash
# /opt/my-agent/monitor.sh

#!/bin/bash

LOG_FILE="/opt/my-agent/monitor.log"

check_container() {
    local container=$1
    local status=$(docker inspect -f '{{.State.Status}}' $container 2>/dev/null)

    if [ "$status" != "running" ]; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') [ALERT] $container is $status - restarting..." >> $LOG_FILE
        docker compose -f /opt/my-agent/docker-compose.yml restart $container
    fi
}

check_container "agent-app"
check_container "agent-db"
check_container "agent-nginx"
```

```bash
# 设置定时执行（每 5 分钟）
chmod +x /opt/my-agent/monitor.sh

crontab -e
# 添加：
*/5 * * * * /opt/my-agent/monitor.sh
```

### 7.4 使用 Docker Compose 管理日志

```bash
# 查看所有日志
docker compose logs

# 只看应用日志
docker compose logs app

# 实时跟踪日志
docker compose logs -f app

# 限制输出行数
docker compose logs --tail=100 app
```

---

## 八、测试部署

### 8.1 本地测试

```bash
# 在服务器上测试
curl http://127.0.0.1:8000/health

# 通过 Nginx 测试
curl http://127.0.0.1/health

# 测试 Agent API 端点
curl -X POST http://127.0.0.1/api/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "你好"}'
```

### 8.2 远程测试

```bash
# 从本地电脑测试
curl https://your-domain.com/health

# 浏览器访问
# https://your-domain.com
# https://your-domain.com/docs  (FastAPI 自动文档)
```

### 8.3 压力测试

```bash
# 安装 ab（Apache Bench）
apt install -y apache2-utils

# 发送 100 个请求，并发 10 个
ab -n 100 -c 10 https://your-domain.com/health

# 关键指标关注：
# Requests per second: 每秒处理请求数
# Time per request: 平均响应时间
# Failed requests: 失败请求数（应为 0）
```

---

## 九、常见问题排查

### 问题 1：容器启动后立即退出

```bash
# 查看退出原因
docker compose logs app

# 常见原因：
# - 端口被占用 → 修改 docker-compose.yml 的端口映射
# - 缺少环境变量 → 检查 .env 文件
# - 依赖服务未就绪 → 检查 depends_on 配置
```

### 问题 2：数据库连接失败

```bash
# 确认数据库容器健康
docker compose ps db

# 手动测试数据库连接
docker compose exec db psql -U agent_user -d agentdb

# 检查应用日志中的数据库错误
docker compose logs app | grep -i postgres
```

### 问题 3：Nginx 返回 502 Bad Gateway

```bash
# 检查 Agent 容器是否在运行
docker compose ps app

# 检查 Agent 日志
docker compose logs app

# 测试 Nginx 到 Agent 的连通性
docker compose exec nginx curl http://app:8000/health
```

### 问题 4：SSL 证书过期

```bash
# 检查证书有效期
echo | openssl s_client -connect your-domain.com:443 -servername your-domain.com 2>/dev/null \
  | openssl x509 -noout -dates

# 手动续期
certbot renew
docker compose restart nginx
```

---

## 十、动手练习

### 练习：完成一次完整的云服务器部署（30 分钟）

按照以下步骤完成部署：

**阶段一：服务器准备（5 分钟）**
- [ ] 购买一台 2C4G 的 Ubuntu 22.04 云服务器
- [ ] SSH 登录到服务器
- [ ] 执行系统更新和安装常用工具

**阶段二：安装 Docker（5 分钟）**
- [ ] 安装 Docker 和 Docker Compose
- [ ] 配置镜像加速
- [ ] 验证安装成功

**阶段三：部署应用（15 分钟）**
- [ ] 上传或拉取项目代码到 `/opt/my-agent/`
- [ ] 配置 .env 环境变量文件
- [ ] 编写/调整 Dockerfile 和 docker-compose.yml
- [ ] 执行 `docker compose up -d --build`
- [ ] 确认所有容器健康运行

**阶段四：域名和 SSL（5 分钟）**
- [ ] 配置域名 DNS 解析到服务器 IP
- [ ] 使用宝塔面板或 certbot 配置 SSL
- [ ] 通过 HTTPS 访问应用

**阶段五：验证（5 分钟）**
- [ ] 通过域名 HTTPS 访问应用
- [ ] 测试 API 端点功能正常
- [ ] 检查容器自动重启配置生效

---

## 总结

本节课完成了一次从零到上线的云服务器部署全流程：

1. **购买云服务器**：选 Ubuntu 22.04，2C4G 起步，新用户有优惠
2. **SSH 远程管理**：配置密钥登录更安全
3. **Docker 环境**：安装 Docker + Compose，配置国内镜像加速
4. **应用部署**：通过 docker-compose 一键拉起应用、数据库、Redis、Nginx
5. **域名 SSL**：宝塔面板最省事，certbot 最灵活
6. **监控运维**：Docker 自动重启 + cron 监控脚本

核心配置文件回顾：
- `Dockerfile`：定义应用构建方式
- `docker-compose.yml`：编排多容器服务
- `nginx.conf`：反向代理和限流
- `.env`：环境变量（不要提交到代码库）

掌握这些后，你的 Agent 项目就具备了生产级部署能力。
