单台 VPS 部署 API(下):拉取镜像、PostgreSQL、Nginx 与 HTTPS
单台 VPS 部署 API(下):拉取镜像、PostgreSQL、Nginx 与 HTTPS
上一篇把 API 镜像推送到了 Docker Hub。这一篇转到 Ubuntu VPS:拉取镜像,运行 API 和 PostgreSQL,再让 Nginx 接管公网流量和 HTTPS。
Internet → Nginx :80/:443 → API :3000(内部网络) → PostgreSQL :5432(内部网络)
一、前置条件与 Docker
开始前需要准备:
- Ubuntu LTS VPS 与 sudo 权限
- 已指向 VPS 的域名(示例
api.example.com) - 已开放 TCP 80/443
- Docker Hub 私有镜像
- API 的
GET /health健康检查端点
Let’s Encrypt 不为裸 IP 签发这里使用的普通 HTTPS 证书。域名必须正确解析到 VPS,公网也要能访问 80 端口,HTTP-01 验证才能通过。
安装 Docker 前,先按照 Docker Engine 在 Ubuntu 上使用官方仓库安装 配置 Docker 官方 apt 仓库。Ubuntu 默认软件源不包含 Docker CE,直接执行 apt install docker-ce 通常会看到 Unable to locate package。
完成仓库设置后,安装 Docker:
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
docker version
docker compose version
把当前用户加入 docker 组。这个改动要重新登录 SSH 才会生效:
sudo usermod -aG docker "$USER"
exit
重新登录后运行 docker ps,确认当前用户可以访问 Docker。
安全提醒:docker 组几乎拥有 root 级别的主机控制能力,只把可信用户加入该组。
二、部署目录、环境变量与拉取镜像
2.1 创建目录结构
sudo mkdir -p /opt/backend/nginx/conf.d /opt/backend/certbot/www /opt/backend/certbot/conf
sudo chown -R "$USER":"$USER" /opt/backend
cd /opt/backend
2.2 创建环境变量文件
创建 .env:
POSTGRES_USER=app_user
POSTGRES_PASSWORD=REPLACE_WITH_A_LONG_RANDOM_PASSWORD
POSTGRES_DB=app_db
DATABASE_URL=postgresql://app_user:***@postgres:5432/app_db
chmod 600 .env
2.3 拉取镜像
使用 Docker Hub Personal Access Token 登录,然后拉取镜像:
docker login
docker pull YOUR_DOCKERHUB_USER/backend-api:1.0.0
登录信息保存在当前用户的
~/.docker/config.json中,不要提交这个文件,也不要把其中的内容复制到脚本里。
三、docker-compose.yml
创建 /opt/backend/docker-compose.yml:
services:
api:
image: YOUR_DOCKERHUB_USER/backend-api:1.0.0
restart: unless-stopped
environment:
NODE_ENV: production
DATABASE_URL: ${DATABASE_URL}
expose:
- "3000"
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- postgres_data:/var/lib/postgresql/data
expose:
- "5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 10s
timeout: 5s
retries: 5
nginx:
image: nginx:1.27-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./certbot/www:/var/www/certbot:ro
- ./certbot/conf:/etc/letsencrypt:ro
depends_on:
- api
certbot:
image: certbot/certbot:latest
volumes:
- ./certbot/www:/var/www/certbot
- ./certbot/conf:/etc/letsencrypt
volumes:
postgres_data:
注意:API 和 PostgreSQL 只使用
expose,端口仅在 Compose 内部网络中可见;Nginx 才通过ports向公网开放 80/443。数据库保存在命名 volume 中。除非确实要删除数据库,否则不要执行docker compose down -v。
四、HTTP、Nginx 与证书
4.1 首次 HTTP 配置
第一次启动时还没有证书,先创建一份只监听 HTTP 的 nginx/conf.d/api.conf:
server {
listen 80;
server_name api.example.com;
location /.well-known/acme-challenge/ { root /var/www/certbot; }
location / {
proxy_pass http://api:3000;
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;
}
}
把示例域名换成自己的域名,然后启动服务:
docker compose pull
docker compose up -d
curl -i http://api.example.com/health
4.2 申请 SSL 证书
docker compose run --rm certbot certonly \
--webroot --webroot-path=/var/www/certbot \
--email YOUR_EMAIL@example.com --agree-tos --no-eff-email \
-d api.example.com
4.3 替换为 HTTPS 配置
证书签发成功后,把 nginx/conf.d/api.conf 更新为:
server {
listen 80;
server_name api.example.com;
location /.well-known/acme-challenge/ { root /var/www/certbot; }
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://api:3000;
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;
}
}
注意:保留 ACME 路径,否则续期验证可能失败。
ssl_certificate后面没有冒号。
重新加载 Nginx:
docker compose exec nginx nginx -t
docker compose exec nginx nginx -s reload
curl -I http://api.example.com/health
curl -i https://api.example.com/health
五、自动续期与更新
5.1 Certbot 自动续期
运行 crontab -e,加入下面这行:
17 3 * * * cd /opt/backend && docker compose run --rm certbot renew --quiet && docker compose exec -T nginx nginx -s reload
演练续期:
docker compose run --rm certbot renew --dry-run
5.2 更新 API 镜像
发布新版本时,先修改 Compose 文件中的 API 镜像标签,再执行:
docker compose pull api
docker compose up -d api
docker compose logs --tail=100 api
curl -fsS https://api.example.com/health
需要回滚时,把标签改回旧版本,再重新启动 API。
总结
现在,API 和 PostgreSQL 跑在 Compose 内部网络中,公网请求由 Nginx 转发,证书则交给 Certbot 续期。这套配置可以作为单机部署的起点,但数据库备份和日志管理还得单独补上。
接下来可以补充:
- PostgreSQL 异地备份策略
- Docker 日志轮转配置
- Docker Compose 健康检查告警