[每周一更]-(第137期):Go + Gin 实战:Docker Compose + Apache 反向代理全流程

发布于:2025-03-23 ⋅ 阅读:(18) ⋅ 点赞:(0)

在这里插入图片描述

访问路径ip+端口:端口可以了,但是小程序中不支持该格式,还需要配置nginx代理通过域名访问

1. Go 代码示例(main.go

这个示例是一个简单的 Gin Web 服务,监听 8085 端口:

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"message": "pong"})
    })
    r.Run(":8085") // 监听 8085 端口
}

2. Dockerfile 多段构建

使用 Alpine + Go 进行多阶段构建,以减少最终镜像大小:

FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod init api/upd-unionid && go mod tidy  # 依赖管理
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o upd-unionid updappunionidbyopenid.go  # 编译 Go 二进制文件

FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/upd-unionid .
# 监听 8085 端口
EXPOSE 8085
# 运行服务
CMD ["./upd-unionid"]

3.构建 Docker 镜像

通过dockerfile构建镜像,然后上传远程仓库,方便使用

## build  使用 Dockerfile 构建镜像(替换 yourusername)
docker build -t yourusername/myapp:latest .

## 打tag,做标记,用于后续推送远程仓库必备步骤
docker tag myapp:v0.1 firehmx/myapp:v0.1 

## 登录 Docker Hub(如果是私有仓库,替换仓库地址),本地运行docker账户,直接docker login 可快捷登录
docker login -u username -p password 

## 推送镜像到 Docker Hub
docker push yourusername/myapp:latest

docker logout

4. docker-compose.yml 直接拉取镜像

修改 docker-compose.yml,从远程镜像启动服务:

version: "3.8"
services:
  app:
    image: upd-unionid:v0.2   # 本地/远程构建的镜像
    ports:
      - "8085:8085"
    restart: always
    networks:
      - mynetwork

networks:
  mynetwork:
    driver: bridge

5. 运行容器

# 1. 拉取最新镜像(可选)
docker pull yourusername/myapp:latest

# 2. 启动容器
docker-compose up -d

# 3.关闭服务
docker-compose down

6. 测试 API

到这里,一个go应用的接口,通过ip+端口形式可以在容器环境中运行完成。

curl http://localhost:8085/ping

返回:

{"message":"pong"}

7、配置域名访问

  • 具体讲解下apache中配置反向代理,将域名请求代理到ip+端口,这期间走了些弯路。配置单独项目来转发该业务,但是无法正常访问,后来通过在泛域名所在服务中进行配置转发解决。
  • 通过问答形式来还原下场景

问: 在A服务器nginx环境下配置项目请求具体域名(wechatapi.xxx.com)转发ip+端口出错,因为该域名在B服务器有Apache环境的泛域名项目(*.xxx.com),没有请求我在A服务器中配置的具体域名,请求了B服务器中泛域名的配置的项目。

答:

7.1、先排查域名所在的ip是哪个。发现确实A服务器配置的项目,访问到了B服务器的项目。

1.验证某一个域名解析的ip地址: dig 域名 +short

场景需求

  • 泛域名 *.example.com 解析到默认服务器 1.1.1.1
  • 特定子域名 sub.example.com 解析到另一台服务器 2.2.2.2

排查泛域名:

  • dig random.example.com +short # 应返回 1.1.1.1

具体子域名:

  • dig sub.example.com +short # 应返回 2.2.2.2

7.2、直接将我需要的二级域名重新解析到对应服务器中,正常配置也行,不过稍微麻烦,毕竟解析DNS是在老板账户控制。

  • 这里是由于A服务器的nginx环境下,有一些域名a.xxx.com和b.xxx.com是可以配置走通,且指定下A服务器所在域名,我就通过复制修改该配置,想将容器化的项目改为新域名,但是由于项目长久,我记不大清了,后来验证,应该是这两域名配置过DNS解析导致的,我新增的服务区没有配置过DNS解析,导致无法请求到具体项目(容器化项目,我改为ip+8086端口是可以访问到)
  • 我也验证了C服务器Apache环境下的api.xxx.com,通过dig验证,也是指定C服务器的ip,这些都属于上边提到的泛域名所属域名,只不过均不在一个服务器中,这个C服务器的域名应该也是配置了DNS解析的。

后来我确定应该是DNS解析问题,全貌应该是:泛域名解析到了B服务器中,A服务器又有a.xxx.com和b.xxx.com项目都正常运行,C服务器的api.xxx.com也运行很久了,并且ABC三台服务器都是同一个域名下,子域名的解析会覆盖泛域名的解析,也就是子域名优先级更高,这就解释通了。

这就联系到最经典的面试题:浏览器输入网址到最终页面展示的过程( DNS解析 → 网络连接 → 请求处理 → 数据渲染 ),这里温习下DNS解析过程:

DNS解析:将域名转换为IP地址
  1. 浏览器缓存:检查浏览器是否缓存过该域名的IP。
  2. 系统缓存:查询操作系统(如Windows的hosts文件或DNS缓存)。
  3. 路由器缓存:向本地路由器查询缓存记录。
  4. ISP的DNS服务器:向互联网服务提供商(ISP)的DNS服务器发起请求。
  5. 递归查询
    • 若ISP无缓存,依次查询根域名服务器(.)→ 顶级域名服务器(.com)→ 权威域名服务器(example.com),最终获取IP。
  • 结果:获得服务器的IP地址。
DNS寻址示例

以查询 www.example.com 为例:

  1. 浏览器检查缓存 → 未命中。
  2. 操作系统检查缓存 → 未命中。
  3. 路由器检查缓存 → 未命中。
  4. 请求发送到ISP的DNS服务器 → 未命中。
  5. ISP的DNS服务器发起递归查询:
    • 查询根域名服务器,获得 .com 顶级域名服务器地址。
    • 查询 .com 顶级域名服务器,获得 example.com 权威域名服务器地址。
    • 查询 example.com 权威域名服务器,获得 www.example.com 的IP地址(如 93.184.216.34)。
  6. ISP的DNS服务器将结果返回给客户端,客户端缓存结果。

DNS记录类型

  • A记录:将域名映射到IPv4地址。

  • AAAA记录:将域名映射到IPv6地址。

  • CNAME记录:将域名指向另一个域名(别名)。

  • MX记录:指定邮件服务器地址。

  • NS记录:指定域名服务器。

总结:由于再进行DNS解析,还需要找老板配置,并且还有单独配置项目,毕竟没有项目,这个逻辑就是想配置转发到ip+端口接口,这个就较为繁琐,不是最优方案,接着继续有最优方案。

7.3、在B服务器的项目中配置代理转发是最优方案,且指定具体的路由

在泛域名所在项目的apache的ssl.conf中配置转发ip+端口的可用服务(nginx转发也一样,参考:[每周一更]-(第88期):Nginx 之 proxy_pass使用详解 - 胡梦旭博客,技术博客,个人博客模板, php博客系统,go语言,Python语言),结果:是可以行

# xxx.com泛域名  + 增加了ip+端口的转发  - 完整请求配置 - xxx替换你自己的域名
<VirtualHost *:443>
    DocumentRoot "/var/www/html/gkmobile/public"
    ServerName xxx.com:443
    ServerAlias *.xxx.com:443
    Header set Access-Control-Allow-Origin "http://ip"
    Header set Access-Control-Allow-Origin "https://域名"

    ErrorLog "logs/xxx.com-error_log"
    CustomLog "logs/xxx.com-access_log" common
    LogLevel warn

    SSLEngine on
    SSLProtocol all -SSLv2 -SSLv3
    SSLCipherSuite HIGH:!RC4:!MD5:!aNULL:!eNULL:!NULL:!DH:!EDH:!EXP:+MEDIUM
    SSLHonorCipherOrder on
    SSLCertificateFile /etc/httpd/conf/xxx-ssl/xxx.com.cer
    SSLCertificateKeyFile /etc/httpd/conf/xxx-ssl/xxx.com.key
    SSLCertificateChainFile /etc/httpd/conf/xxx-ssl/fullchain.cer

    <Files ~ "\.(cgi|shtml|phtml|php3?)$">
        SSLOptions +StdEnvVars
    </Files>

    <Directory "/var/www/cgi-bin">
        SSLOptions +StdEnvVars
    </Directory>

    BrowserMatch "MSIE [2-5]" \
        nokeepalive ssl-unclean-shutdown \
        downgrade-1.0 force-response-1.0

    CustomLog logs/ssl_request_log \
        "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"

    SSLProxyEngine on
    ProxyPass /api/small https://api.xxx.com/version1/small/
    ProxyPassReverse /api/small https://api.xxx.com/version1/small/


    # ADD 核心配置:仅在特定子域名+路径时触发转发
    ProxyRequests Off
    ProxyPreserveHost On

    # ADD 条件判断:仅当请求域名为 wechatapi.xxx.com 且路径为 /update_unionid 时转发
    RewriteEngine On
    RewriteCond %{HTTP_HOST} ^wechatapi\.xxx\.com$ [NC]
    RewriteRule ^/update_unionid(.*)$ http://IP:端口/update_unionid$1 [P,L]
</VirtualHost>

详细逻辑:

  • 泛域名 *.xxx.com 已经配置了一个虚拟主机(例如处理静态网站)。
  • 新增需求:当访问 wechatapi.xxx.com/update_unionid 时,自动转发到另一台服务器的 IP:Port/update_unionid,而其他子域名(如 blog.xxx.com)或路径仍由当前项目处理。

直接访问 wechatapi.xxx.com/update_unionid 请求正常了,到此整个逻辑就完整了。

8.错误记录

5.1、问:ERROR: failed to solve: golang:1.23-alpine: failed to resolve source metadata for docker.io/library/golang:1.23-alp
ine: failed to authorize: failed to fetch oauth token: Post “https://auth.docker.io/token”: dial tcp 31.13.69.245:4
43: connectex: A connection attempt failed because the connected party did not properly respond after a period of t
ime, or established connection failed because connected host has failed to respond.

该问题集中在服务器中拉去镜像超时连接失败。

答:

Linux系统

步骤 1:修改 Docker Daemon 配置 执行:

vim /etc/docker/daemon.json

添加以下内容:

{
  "registry-mirrors": [
    "https://hub-mirror.c.163.com",
    "https://mirror.baidubce.com",
    "https://docker.mirrors.ustc.edu.cn",
    "https://registry.docker-cn.com",
    "https://dockerproxy.com"
  ]
}

步骤 2:重启 Docker

systemctl daemon-reload
systemctl restart docker

报错拉取不到镜像就直接拉去: golang:1.23-alpine

Windows

步骤 1:打开 Docker Desktop 设置

  1. 确保 Docker Desktop 已经启动
  2. 点击任务栏 Docker 图标 → 进入 “Settings”(设置)
  3. 在左侧导航栏中,找到 “Docker Engine”

步骤 2:修改 daemon.json 配置

“Docker Engine” 选项卡里,你会看到一个 JSON 配置文件,找到 "registry-mirrors",修改或添加如下内容:

{
  "registry-mirrors": [
    "https://hub-mirror.c.163.com",
    "https://mirror.baidubce.com",
    "https://docker.mirrors.ustc.edu.cn",
    "https://registry.docker-cn.com",
    "https://dockerproxy.com"
  ]
}

注意:如果 "registry-mirrors" 字段不存在,直接添加进去。

步骤 3:应用配置并重启 Docker

点击 “Apply & Restart”(应用并重启),等待 Docker Desktop 重启完成。

2、问:本地docker部署运行正常,但是linux报错:Using default tag: latest
Error response from daemon: Get “https://registry-1.docker.io/v2/”: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)

答:

windows返回如下证明本地API接通:

{
    "code": -1,
    "msg": "fail",
    "data": {
        "error": "微信 API 错误: invalid code, rid: 67d7db58-4d4f29d9-5c73463c"
    }
}

阿里云工程师提供的代理镜像就可以(配置一些其他加速地址):

操作步骤

  • vim /etc/docker/daemon.json,将如下配置添加进去

  • service restart docker

{
  "registry-mirrors": [
  	"https://docker.registry.cyou",
    "https://docker-cf.registry.cyou",
    "https://dockercf.jsdelivr.fyi",
    "https://docker.jsdelivr.fyi",
    "https://dockertest.jsdelivr.fyi",
    "https://mirror.aliyuncs.com",
    "https://dockerproxy.com",
    "https://mirror.baidubce.com",
    "https://docker.m.daocloud.io",
    "https://docker.nju.edu.cn",
    "https://docker.mirrors.sjtug.sjtu.edu.cn",
    "https://docker.mirrors.ustc.edu.cn",
    "https://mirror.iscas.ac.cn",
    "https://docker.rainbond.cc"]
}

这里说明下:提供了其他registry-mirrors,寻求了各大AI工具,均不行,且测试本地流程都可以,就是ECS不行(采用阿里云给的就可以)

如本地可拉取, 可docker save导出为tar.gz文件,然后上传到自己的机器,使用docker load -i XXX.tar.gz解压出来使用。

如果是正式环境业务,可评估将镜像上传到自建仓库管理,通过自建仓库拉取,避免依赖外部仓库通信,影响后续业务稳定性。、

容器镜像个人版免费管理可参考:https://help.aliyun.com/zh/acr/user-guide/use-a-container-registry-personal-edition-instance-to-push-and-pull-images