Skip to content

Golang 项目生产环境完整部署方案

适用场景

  • 预发布环境 / 灰度验证环境 / 内部测试环境
  • 核心需求:需要对外网隐藏,仅允许团队成员或特定 IP 段访问,但不希望在应用代码中实现登录逻辑。

本指南遵循 Linux 文件系统层次规范 (FHS),结合 Systemd、Nginx 与 Cloudflare,提供一套从代码编译到上线的标准化、可复用的部署流程。

1. 项目构建 (本地)

假设您的 Golang 项目名为 myapp,主程序入口输出为 myapp-apiserver

bash
# 启用 Go 模块代理
go env -w GOPROXY=https://goproxy.cn,direct

# 为 Linux 环境交叉编译
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o myapp-apiserver .
  • CGO_ENABLED=0: 生成静态依赖的可执行文件,避免运行时动态库缺失
  • -ldflags="-s -w": 剥离调试信息,显著减小二进制文件体积
  • 编译产物: myapp-apiserver

2. 服务器环境准备 (以 Ubuntu/Debian 为例)

bash
# 更新系统并安装必要工具
sudo apt update && sudo apt upgrade -y
sudo apt install -y nginx apache2-utils

# 创建专用服务用户 (安全最佳实践,可选)
# sudo groupadd -r myapp
# sudo useradd -r -g myapp -s /sbin/nologin -M myapp

3. 标准化文件部署 (遵循 FHS)

根据 FHS 规范,将文件放置到标准目录:

bash
# 1. 可执行文件 -> /usr/local/bin
sudo cp myapp-apiserver /usr/local/bin/
sudo chmod 755 /usr/local/bin/myapp-apiserver
# 如使用专用用户: sudo chown myapp:myapp /usr/local/bin/myapp-apiserver

# 2. 配置文件 -> /etc/myapp/
sudo mkdir -p /etc/myapp
sudo cp config.yaml /etc/myapp/config.yaml
sudo chown -R root:root /etc/myapp
sudo chmod 755 /etc/myapp
sudo chmod 644 /etc/myapp/config.yaml

# 3. 工作目录与数据持久化 -> /var/lib/myapp/
sudo mkdir -p /var/lib/myapp
sudo chown root:root /var/lib/myapp   # 如使用专用用户,则 chown myapp:myapp

# 4. 日志目录 -> /var/log/myapp/
sudo mkdir -p /var/log/myapp
sudo touch /var/log/myapp/apiserver.log
sudo chown root:root /var/log/myapp/apiserver.log

4. 创建 Systemd 服务文件

创建 /usr/lib/systemd/system/myapp-apiserver.service

ini
[Unit]
Description="MyApp API Server"
Requires=network-online.target
After=network-online.target

[Service]
WorkingDirectory=/var/lib/myapp
StandardOutput=append:/var/log/myapp/apiserver.log
StandardError=append:/var/log/myapp/apiserver.log
User=root
Group=root
ExecStart=/usr/local/bin/myapp-apiserver --config-dir /etc/myapp --config-name config.yaml
ExecReload=/bin/kill --signal HUP $MAINPID
KillMode=process
KillSignal=SIGTERM
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
Alias=myapp.service

操作命令:

bash
# 重载 systemd 并启动服务
sudo systemctl daemon-reload
sudo systemctl enable myapp-apiserver.service  # 开机自启
sudo systemctl start myapp-apiserver.service

# 使用别名管理(可选)
sudo systemctl enable myapp.service --now

# 查看状态
sudo systemctl status myapp-apiserver.service

# 优雅重载配置(发送 HUP 信号)
sudo systemctl reload myapp-apiserver.service

# 查看日志(直接查看文件,因为配置了 StandardOutput/Error)
sudo tail -f /var/log/myapp/apiserver.log

# 通过 journal 查看(如果同时需要)
sudo journalctl -u myapp-apiserver -f

5. Basic Auth 灰度访问控制

5.1 为什么需要 Basic Auth?(灰度发布场景)

在正式上线前,预发布环境(Staging/Preview)通常需要对外网隐藏,仅限团队成员访问。开启 Nginx auth_basic 有以下优势:

  • 零代码侵入:无需在 Go 应用中实现登录逻辑,由 Nginx 层统一拦截
  • 快速生效:修改 .htpasswd 文件即可增删成员,无需重启服务
  • 配合灰度策略:可叠加 IP 白名单,实现灵活的访问控制

5.2 生成密码文件

bash
# 创建密码文件 (首次需 -c)
sudo htpasswd -c /etc/nginx/.htpasswd your_username

# 添加更多用户时去掉 -c
sudo htpasswd /etc/nginx/.htpasswd another_user

# 验证密码文件
sudo cat /etc/nginx/.htpasswd

5.3 进阶配置:Basic Auth + IP 白名单(推荐)

如果您希望公司内部 IP 免密码访问,外部访问需要密码,可以使用以下配置:

nginx
location / {
    # 满足任一条件即可
    satisfy any;

    # 允许公司出口 IP 免密码访问
    allow 203.0.113.0/24;   # 公司办公网段
    allow 10.0.0.0/8;        # 内网 VPN 网段
    deny all;                # 拒绝其他所有 IP

    # 不在白名单的 IP 需要输入密码
    auth_basic "Restricted Access - Internal Preview";
    auth_basic_user_file /etc/nginx/.htpasswd;

    # ... 其他 proxy_pass 配置
}

6. 配置 Nginx 反向代理 + 基础认证

6.0 SSL 证书方案选择

在配置 Nginx HTTPS 之前,您需要选择一种 SSL 证书方案。以下是两种主流免费方案的对比:

特性Cloudflare Origin CALet's Encrypt
适用场景域名托管在 Cloudflare任意域名(通用)
配置复杂度简单中等
证书续期15 年有效期,无需续期90 天,自动续期
CDN 加速✅ 需开启 Cloudflare 代理
DDoS 防护
独立部署❌ 依赖 Cloudflare✅ 完全独立

选择建议

以下以 Cloudflare Origin CA 为例进行配置,若使用 Let's Encrypt 可参考上方链接博文。

6.1 准备 Cloudflare Origin CA 证书

从 Cloudflare Dashboard → SSL/TLS → Origin Server 下载证书:

注意

Cloudflare Origin CA 证书是按域区(Zone)签发的,一张证书可以覆盖主域名及所有子域名(如 *.yourdomain.com)。因此证书文件名应使用主域名

bash
# 放置证书(使用主域名命名)
sudo mkdir -p /etc/nginx/ssl
sudo cp yourdomain.com.pem /etc/nginx/ssl/
sudo cp yourdomain.com.key /etc/nginx/ssl/
sudo chmod 600 /etc/nginx/ssl/yourdomain.com.key

6.2 创建 Nginx 站点配置

创建 /etc/nginx/sites-available/api.yourdomain.com.conf

nginx
# HTTP 重定向到 HTTPS
server {
    listen 80;
    server_name api.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

# HTTPS 配置
server {
    listen 443 ssl http2;
    server_name api.yourdomain.com;

    # 安全响应头
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # 全局基础认证 (灰度发布控制)
    auth_basic "Restricted Access - Internal Preview";
    auth_basic_user_file /etc/nginx/.htpasswd;

    # Cloudflare Origin CA 证书(使用主域名证书)
    ssl_certificate /etc/nginx/ssl/yourdomain.com.pem;
    ssl_certificate_key /etc/nginx/ssl/yourdomain.com.key;

    # SSL 优化配置
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;

    # 日志
    access_log /var/log/nginx/api.yourdomain.com-access.log;
    error_log /var/log/nginx/api.yourdomain.com-error.log;

    # Cloudflare IP 地址范围(获取真实客户端 IP)
    set_real_ip_from 173.245.48.0/20;
    set_real_ip_from 103.21.244.0/22;
    set_real_ip_from 103.22.200.0/22;
    set_real_ip_from 103.31.4.0/22;
    set_real_ip_from 141.101.64.0/18;
    set_real_ip_from 108.162.192.0/18;
    set_real_ip_from 190.93.240.0/20;
    set_real_ip_from 188.114.96.0/20;
    set_real_ip_from 197.234.240.0/22;
    set_real_ip_from 198.41.128.0/17;
    set_real_ip_from 162.158.0.0/15;
    set_real_ip_from 104.16.0.0/13;
    set_real_ip_from 104.24.0.0/14;
    set_real_ip_from 172.64.0.0/13;
    set_real_ip_from 131.0.72.0/22;
    real_ip_header CF-Connecting-IP;

    # 主端点代理
    location / {
        proxy_pass http://127.0.0.1:2759;  # 假设 Go 服务监听此端口
        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;
        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 ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf)$ {
        proxy_pass http://127.0.0.1:2759;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # 健康检查免认证(供负载均衡器探测)
    location = /health {
        auth_basic off;
        proxy_pass http://127.0.0.1:2759;
        proxy_set_header Host $host;
    }
}

6.3 启用站点并重载 Nginx

bash
# 启用站点配置
sudo ln -s /etc/nginx/sites-available/api.yourdomain.com.conf /etc/nginx/sites-enabled/

# 检查语法并重载
sudo nginx -t && sudo systemctl reload nginx

7. Cloudflare DNS 与 SSL 模式配置

7.1 添加 DNS A 记录

类型名称内容代理状态
Aapi您的服务器公网 IP✅ 开启(橙色云朵)

7.2 SSL/TLS 设置

在 Cloudflare SSL/TLS 面板中,将模式设置为 Full (strict)

这要求源站具有有效证书,我们已配置 Origin CA 证书,满足此要求

8. 部署后验证

bash
# 1. 检查 Go 服务状态
sudo systemctl status myapp-apiserver

# 2. 检查 Nginx 代理连通性 (本地)
curl -H "Host: api.yourdomain.com" http://127.0.0.1/health

# 3. 通过 Cloudflare 外网测试 (应要求认证)
curl -I https://api.yourdomain.com

# 4. 带认证访问
curl -I -u your_username:your_password https://api.yourdomain.com

# 5. 验证 HSTS 和安全头
curl -I https://api.yourdomain.com | grep -i "strict-transport-security"

# 6. 验证健康检查免认证
curl -I https://api.yourdomain.com/health

9. 常见故障排查

现象可能原因解决方法
访问显示默认 Nginx 页面sites-enabled 中有其他 default_server 配置冲突确保删除或移除非必要的 default 配置链接
502 Bad GatewayGo 服务未启动或端口不匹配sudo systemctl status myapp-apiserver;检查 Nginx proxy_pass 端口
无认证提示 (直接 200)auth_basic 未正确继承或被覆盖检查 location 块中是否有 auth_basic off; 或语法错误
日志中显示客户端 IP 为 Cloudflare 节点real_ip_header 配置缺失或顺序错误确认 set_real_ip_from 包含 Cloudflare 所有范围,且 real_ip_header CF-Connecting-IP; 正确
证书报错 SSL_ERROR_BAD_CERT_DOMAIN证书域名与实际访问域名不匹配确认证书是主域名签发(如 yourdomain.com),可覆盖 *.yourdomain.com

10. 快速维护命令速查

bash
# 重新编译并替换二进制文件
go build -o myapp-apiserver .
sudo systemctl stop myapp-apiserver
sudo cp myapp-apiserver /usr/local/bin/
sudo systemctl start myapp-apiserver

# 修改配置文件后重载(发送 HUP 信号)
sudo vi /etc/myapp/config.yaml
sudo systemctl reload myapp-apiserver

# 查看实时日志
sudo tail -f /var/log/myapp/apiserver.log
sudo tail -f /var/log/nginx/api.yourdomain.com-access.log

# 更新 Nginx 配置后重载
sudo nginx -t && sudo systemctl reload nginx

# 管理 Basic Auth 用户
sudo htpasswd /etc/nginx/.htpasswd new_user      # 添加用户
sudo htpasswd -D /etc/nginx/.htpasswd old_user   # 删除用户

11. 遵循 FHS 原则的总结

文件类型存放路径说明
可执行文件/usr/local/bin/myapp-apiserver本地编译的程序,不属于发行版仓库
配置文件/etc/myapp/config.yaml全局配置目录与文件
日志文件/var/log/myapp/apiserver.log应用日志(追加模式)
工作/持久化目录/var/lib/myapp/应用状态数据、工作目录
系统服务/usr/lib/systemd/system/myapp-apiserver.service系统单元
Web 代理配置/etc/nginx/sites-available//etc/nginx/sites-enabled/Nginx 站点配置

快速记忆口诀

二进制找 bin,配置找 etc

日志找 var,静态找 share

自己编译 local,大厂软件放 opt

这套布局确保了系统的整洁性、可维护性,并与其他 Linux 软件包的行为保持一致。

附录:占位符替换清单

部署时请将以下占位符替换为实际值:

占位符示例值说明
myappyourappGo 项目名称(目录/服务名前缀)
myapp-apiserveryourapp-apiserver可执行文件名
api.yourdomain.comapi.example.comAPI 服务完整域名
yourdomain.comexample.comCloudflare 证书主域名
27598080Go 服务监听端口