Go HTTP 服务器打包成容器镜像

1,666 阅读4分钟

Gin 简介

Gin 是 go 语言中的 Web 框架,非常的小巧,截止 1.4.0 版本,整个框架的源码仅 5K 左右。

Gin 特性

  • 快速:路由不使用反射,基于 Radix 树,内存占用小
  • 稳定性:新版本的发布不会破坏你的代码
  • 支持中间件:传入的 HTTP 请求可以由一系列中间件和最终操作来处理

Gin 也有不合理的地方,例如不合理的实现 context.Context 接口,Context 非接口无法扩展;validator 注册不合理,不同的 gin 版本可能注册到不同版本里去等,现在也变得越来越复杂了,不过对于框架来说,用的人多才是硬道理。

安装 Go&Gin

安装 Go 之前我们先了解一下 Go 语言的版本发布策略

Go 团队将版本发布的节奏稳定在每年两次大版本,Go 团队承诺最新的大版本会兼容之前的版本,我们不用担心新版本兼容问题。一般情况下,我们都可以采用最新的版本,因为 Go 团队的代码质量一直很高,很少出现重大 bug。有的开源项目会在 Go 最新版本发布不就将会使用,比如 Kubernetes;而有的开源项目,比如 Docker 会更加谨慎。

我选择 Go 最新发布版,Go 1.16.5 版本,Go 支持大部分主流操作系统,接下来详细描述一下在不同操作系统下的安装、配置。

在 Linux/Ubuntu 上安装 Go

下载并解压 Go Linux 安装包:

$ wget -c https://golang.google.cn/dl/go1.16.5.linux-amd64.tar.gz

将下载完毕的 Go 安装包解压到安装目录中:

$ tar -C /usr/local -xzf go1.16.5.linux-amd64.tar.gz

执行完解压命令后,我们就可以在 /usr/local 下看到 go 命名的目录了,这是 Go 官方推荐的安装目录。为了在任意路径下都可以使用 go 命令,我们将 Go 二进制文件所在的文件夹加入到环境变量 PAHT 中:

export PATH=$PATH:/usr/local/go/bin

执行下面的命令,让环境变量立即生效:

$ source ~/.profile

验证安装是否成功:

$ go version
# go version go1.16.5 linux/amd64

在 Mac 上安装 Go

在 Mac 上,可以通过 Homebrew 安装:

安装 Homebrew 我就不展开啦,它就像一个安装文件管理器,能够方便的安装、查看、卸载你的应用程序,非常推荐使用。

$ brew install go@1.16

设置一下环境变量,在 ~/.bashrc 中添加 GOPATH:

$ vim ~/.bashrc 

export GOPATH=~/go
export PATH=$PATH:$GOPATH/bin

添加完成后,执行下面的命令让环境变量生效:

$ source ~/.bashrc

验证安装是否成功:

$ go version
# go version go1.16.6 darwin/amd64

在 Windows 上安装 Go

在 Windows 上,可以通过图形界面引导的方式安装 Go。

打开 Go 安装包下载页面,找到 AMD64 架构 Go 1.16.5 版本的 Windows msi 安装包并下载。

安装程序会把 Go 默认安装在 C:\Program Files\Go 下面,并且 Go 安装程序自动设置好了 Go 所需要的用户环境变量,我们可以在任意路径下使用 Go 命令,打开终端验证 Go 是否安装成功:

$ go version
# go version go1.16.5 windows/amd64

由于国内的网络环境,我们可以通过配置 GOPROXY 避免 DNS 污染导致的模块拉取缓慢或失败的问题,加速你的构建。

打开终端 -> $ go env -w GOPROXY=goproxy.cn,direct

安装 Gin

$ go get -v -u github.com/gin-gonic/gin

-v:打印被构建的代码包的名称

-u:如果存在相关的代码包,则强制更新代码包以及依赖包

通过 Gin 编写 HTTP 服务器

在任意位置创建一个文件夹,首先我们初始化一个 go.mod 文件,它用于管理 Go 依赖的组件、组件版本等:

$ go mod init 

在该文件夹下创建 main.go 文件:

$ touch main.go
$ vim main.go

main.go 文件中添加如下内容:

package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()
	r.GET("/", func(c *gin.Context) {
		c.String(200, "Hello, World")
	})
	r.Run("0.0.0.0:80")
}
  • 我们使用 gin.Default() 生成了一个实例
  • 再使用 r.Get("/", ...) 声明了一个路由,并且访问这个路由时,会如何处理,这里代表访问/时,会打印 Hello, World
  • 最后用 r.Run() 函数来让程序启动,并且指定端口为 80。让我们来运行它:
$ go run main.go
[GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on 0.0.0.0:80

通过 curl 访问本地 80 端口:

$ curl http://localhost:80
Hello, World

安装 Docker

Docker 可以安装在 64 位的 x86 平台或 ARM 平台上。Ubuntu 发行版中,LTS(Long-Term-Support)长期支持版本,会获得 5 年的升级维护支持,版本会更加稳定,因此在生产环境中推荐使用 LTS 版本。

在 Ubuntu 上安装 Docker

apt 源使用 HTTPS 的方式确保软件下载过程不被篡改,所以我们需要先添加 HTTPS 的软件包以及 CA 证书:

$ sudo apt-get update
$ sudo apt-get install \
  apt-transport-https \
  ca-certificates \
  curl \
  gnupg-agent \
  software-properties-common

由于国内环境问题,替换使用国内源。为了确认下载软件包的合法性,我们还需要添加软件源的密钥:

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

添加 Docker 软件源:

$ sudo add-apt-repository \
  "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \
  stable"

更新 apt 软件包的换从,并安装 docker

$ sudo apt-get update
$ sudo apt-get install docker-ce  docker-ce-cli containerd.io

在其他操作系统安装 Docker

因为篇幅问题,Docker 的安装就不展开了,可以从开源图书获取安装方式。

制作容器镜像

我们在 main.go 的同级目录下,制作一个 Dockerfile 容器镜像,如下:

# 使用官方提供的 Go 开发镜像作为基础镜像
FROM golang:1.17

# 切换工作目录为 /demo
WORKDIR /demo

# 将当前目录下的内容复制到 /demo 
ADD . /demo

# 开启 GO MOD, 用于安装 HTTP 需要的 Gin 依赖
RUN go env -w GO111MODULE=on &&\
    go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct

# 允许外接访问容器的 80 端口
EXPOSE 80

# 运行这个容器中的 HTTP 实例
CMD ["go", "run", "main.go"]

通过这个文件,我们可以看到 Dockerfile 使用了一些标准的源语(大写的词语,例如 FROM),来描述一个 Docker 镜像,这些命令从上往下按顺序处理,并且 Dockerfile 中的内容,主要以两种形式呈现,一种是注释行,另一种是指令行。

接下来,我们就可以让 Docker 制作这个镜像了,在当前目录执行:

docker build -t go-http-demo .

-t:给镜像起一个名字。

docker build 会自动加载当前目录下的 Dockerfile 文件,然后执行文件中的命令。

docker build 中的每个原语执行后,都会生成一个对应的镜像层。操作完成后,可以通过 docker images 命令查看结果:

$ docker images

REPOSITORY              TAG       IMAGE ID       CREATED        SIZE
go-http-demo            latest    ac2608f814a4   26 hours ago   941MB

通过这个镜像 ID,可以查看新增的层在 overlay2 路径下对应的文件和目录。

接下来,通过 docker run 命令启动容器:

$ docker run -p 4000:80 go-http-demo

我们在 Dockerfile 中指定了 CMD ,否则就需要把进程启动命令加载后面:

$ docker run -p 4000:80 go-http-demo go run main.go

容器启动之后,可以使用 docker ps 命令看到:

$ docker ps
CONTAINER ID   IMAGE          COMMAND            CREATED        STATUS        PORTS                  NAMES
40a5676ed61a   go-http-demo   "go run main.go"   26 hours ago   Up 26 hours   0.0.0.0:4000->80/tcp   relaxed_wing

同时,我们已经通过 -p 4000:80 告诉了 Docker,请把容器内的 80 端口映射在宿主机的 4000 端口上。这样做的目的是,只要访问宿主机的 4000 端口,就可以看到容器里应用返回的结果:

$ curl http://localhost:4000
Hello, World

至此,我们就完成了安装 Go 、Docker,通过 Go 编写 Gin HTTP 服务器,并且通过容器的方式来进行部署。