Go在docker环境下部署

给大家分享一个Go如何在docker环境下部署Go应用程序,例如:已经写好一个简单的Go应用程序,下面是目录结构。

Go 复制代码
.
├── go.mod
└── main.go

main.go 中的代码如下:

Go 复制代码
package main

// 导入gin包
import "github.com/gin-gonic/gin"

// 入口函数
func main() {
    // 初始化一个http服务对象
    r := gin.Default()

    // 设置一个get请求的路由,url为/ping, 处理函数(或者叫控制器函数)是一个闭包函数。
    r.GET("/ping", func(c *gin.Context) {
        // 通过请求上下文对象Context, 直接往客户端返回一个json
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })

    r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}

Go 代码构建出 docker 镜像,其中分为三步:

  • 本机编译 Go 代码,如果牵涉到 cgo 跨平台编译就会比较麻烦了。
  • 用编译出的可执行文件构建 docker 镜像。
  • 编写 shell 脚本或者 makefile 让这几步通过一个命令可以获得

多阶段构建就是把这一切都放到一个 Dockerfile 里,既没有源码泄漏,又不需要用脚本去跨平台编译,还获得了最小的镜像。

什么是多阶段构建?

在Docker Engine 17.05 中引入了多阶段构建,以此降低构建复杂度,同时使缩小镜像尺寸更为简单。

在一个Dockerfile中使用多个FROM指令,每个FROM都可以使用不同的基镜像,并且每条指令都将开始新阶段构建。在多阶段构建中,我们可以将资源从一个阶段复制到另一个阶段,在最终镜像中只保留我们所需要的内容。

默认情况下构建阶段没有名称,我们可以通过整数0~N来引用,即第一个from0开始。其实我们还可以在FROM指令中添加AS <NAME> 来命名构建阶段,接着在COPY指令中通过<NAME>引用。

  • 只构建某个阶段

构建镜像时,您不一定需要构建整个 Dockerfile,我们可以通过--target参数指定某个目标阶段构建,比如我们开发阶段我们只构建builder阶段进行测试。

arduino 复制代码
#docker build --target builder -t builder_app:v2 .
  • 使用外部镜像 使用多阶段构建时,我们局限于从之前在 Dockerfile 中创建的阶段进行复制。还可以使用COPY --from指令从单独的镜像复制,如本地镜像名称、本地或 Dockerhub上可用的标签或标签 IDDocker 客户端在必要时会拉取需要的镜像到本地。
bash 复制代码
COPY --from  httpd:latest /usr/local/apache2/conf/httpd.conf ./httpd.conf
  • 从上一阶段创建新的阶段 我们可以通过FROM指令来引用上一阶段作为新阶段的开始。

DockerCOPYADD的区别是:

COPY指令不支持从远程URL获取资源,只能从执行docker build所在的主机上读取资源并复制到镜像中;而ADD指令支持从远程URL获取资源,可以通过URL从远程服务器读取资源并复制到镜像中。

ldflags在golang编译中的2个作用

  1. ldflags用于链接过程。 主要是控制打包过程。
  2. ldflags在编译golang的时候,可以传入一些值用来配置golang的应用。

本章主要是讲解Go的打包,更深入的可以参考: ldflags在golang编译中的2个作用

docker访问外部https数字证书问题

一般构建 docker 镜像使用的都是 alpine linux 系统,默认是不带 ca-certificates 根证书的,导致无法识别外部 https 携带的数字证书。

在访问的时候,会抛出509:certificate signed by unknown authority错误,导致 docker 容器的接口服务返回报错。

为了解决证书验证的问题,我们需要在构建 docker 镜像的时候将 ca-certificates 根证书装上。 在 Dockerfile 中加入如下内容:

Dockerfile 复制代码
RUN apk --no-cache add ca-certificates && update-ca-certificates

了解了上面关于Docker的基本知识后,看如下的 Dockerfile 文件用于构建镜像,里面已经包含了详细的注释。

Dockerfile 复制代码
# 阶段1命名为builder
FROM golang:alpine AS builder 

## Labels允许你为Docker对象指定metadata。 可以通过 docker image inspect main:v
LABEL stage=gobuilder

ENV CGO_ENABLED 0
ENV GOPROXY https://goproxy.cn,direct

# RUN apk --no-cache add tzdata 
# 因为alpine 基础镜像中没有包含时区信息文件,当代码中有调用类似下面这样的通过名称获取时区信息的时候

RUN apk update --no-cache && apk add --no-cache tzdata

# 使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。

WORKDIR /build

ADD go.mod .
ADD go.sum .
RUN go mod download

# 把当前宿主机中的文件复制进来
COPY . .
RUN go build -ldflags="-s -w" -o /app/main ./main.go


FROM alpine

# 解决证书 和 时区问题,这里时区文件直接复制过来的
RUN apk update --no-cache && apk add --no-cache ca-certificates
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
ENV TZ Asia/Shanghai

# 从builder阶段的镜像中复制过来
WORKDIR /app
COPY --from=builder /app/main /app/main

# run起来后启动服务
CMD ["./main"]

文件内容简要说明:

  1. 第一个 FROM 开始的部分是构建一个 builder 镜像,目的是在其中编译出可执行文件 main,第二个 From 开始的部分是从第一个镜像里 copy 出来可执行文件 main,并且用尽可能小的基础镜像 alpine 以保障最终镜像尽可能小,alpine 大概是5MB,对我们的服务不会构成多少影响。
  2. 默认禁用了 cgo
  3. 启用了 GOPROXY 加速 go mod download
  4. 去掉了调试信息 -ldflags="-s -w" 以减小镜像尺寸
  5. 安装了 ca-certificates,这样使用 TLS 证书就没问题了
  6. tzdatabuilder 镜像安装,并在最终镜像只拷贝了需要的时区
  7. 自动设置了本地时区,这样我们在日志里看到的是北京时间了

执行docker images 查看镜像编译结果。

shell 复制代码
> docker images
REPOSITORY       TAG       IMAGE ID       CREATED         SIZE
main             v1        746f285b01f7   24 hours ago    14.9MB

下面我们启动一个容器实验一下

Dockerfile 复制代码
docker run -it --rm -p 8080:8080 --name main_v1 -d main:v1

docker run 加上--rm退出容器以后,这个容器就被删除了,方便在临时测试使用。 不加--rm 退出容器后,容器只是停止运行,数据任然被保留。

--name main_v1 对容器的命名

最后本地执行: curl http://localhost:8080/ping

看到正确的响应,表示服务部署成功。

相关推荐
Serverless社区2 小时前
一键部署QwQ-32B推理模型,2种方式简单、快速体验
go
大鹏dapeng2 小时前
Gone V2 Provider 机制介绍
后端·go·github
Nathan__275 小时前
go-文件缓存与锁
缓存·go·
一个热爱生活的普通人5 小时前
如何用golang实现一个MCP Server
llm·go·mcp
江湖十年8 小时前
如何基于 Go 语言设计一个简洁优雅的分布式任务系统
分布式·后端·go
upsilon1 天前
golang切片slice
后端·go
upsilon1 天前
golang数组
后端·go
aricvvang1 天前
Go结构体优化(对齐方式)
后端·go
湫qiu1 天前
手写 Protobuf —— Java 代码生成
java·go·protobuf
侯大宝1 天前
开箱即用的go-zero示例
go·go-zero