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

文章目录

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

1. Go 代码示例(main.go

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

go 复制代码
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 进行多阶段构建,以减少最终镜像大小:

dockerfile 复制代码
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,从远程镜像启动服务:

yaml 复制代码
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

排查泛域名:

具体子域名:

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语言](https://humengxu.com/article/273)),结果:是可以行

toml 复制代码
# 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"
    }
}

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

操作步骤

这里说明下:提供了其他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

相关推荐
云原生指北1 分钟前
Docker Sandboxes 上手:从安装到第一次跑 Agent
运维·docker·容器
ziyun66688835 分钟前
Docker 容器化 Web 服务架构搭建与自动化运维项目
运维·docker·架构
骇客野人3 小时前
Docker Compose 部署多个独立 Jar 服务完整实操步骤
docker·容器·jar
圣殿骑士-Khtangc4 小时前
Go错误处理最佳实践进阶从error到panic的完整指南
golang
运维开发笔记5 小时前
3.6 Go defer 语句学习笔记
golang
云川之下5 小时前
【k8s】Docker inspect 命令
docker·容器·kubernetes
存在morning6 小时前
【Python 开发实践 一】Python vs Go vs Java 三门语言对比
java·python·golang
倔强的石头1066 小时前
飞牛OS部署Mtab:Docker搭建自托管书签导航并实现远程访问
运维·docker·容器
圣殿骑士-Khtangc18 小时前
Go字符串高效拼接性能对比与底层原理分析
服务器·前端·golang
apgk121 小时前
基于Canal实现mysql数据同步到消息队列(RabbitMQ/Kafka)详细操作教程
docker·kafka·rabbitmq