使用Docker部署Go Web应用

270 阅读4分钟

为什么使用Docker

使用docker的主要目标是容器化。也就是为你的应用程序提供一致的环境,而不依赖于它运行的主机。想象一下你是否也会遇到下面这个场景,你在本地开发了你的应用程序,它很可能有很多的依赖环境或包,甚至对依赖的具体版本都有严格的要求,当开发过程完成后,你希望将应用程序部署到web服务器。这个时候你必须确保所有依赖项都安装正确并且版本也完全相同,否则应用程序可能会崩溃并无法运行。如果你想在另一个web服务器上也部署该应用程序,那么你必须从头开始重复这个过程。这种场景就是Docker发挥作用的地方。对于运行我们应用程序的主机,不管是笔记本电脑还是web服务器,我们唯一需要做的就是运行一个docker容器平台。从以后,你就不需要担心你使用的是MacOS,Ubuntu,Arch还是其他。你只需定义一次应用,即可随时随地运行。

Docker官方文档

Docker部署示例

先来写段代码如下:

package main

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"

	config "GoProject/ConfigClass"
	test "GoProject/TestClass"
)

// func main() {
// 	fmt.Print("hello world\n")

// 	f, _ := os.Create("gin.testlog")
// 	gin.DefaultWriter = io.MultiWriter(f)

// 	r := gin.Default()
// 	r.GET("/ping", func(c *gin.Context) {
// 		c.JSON(200, gin.H{
// 			"message":     "success",
// 			"descprition": "这是一个尝试",
// 			"vendor":      "1",
// 		})
// 	})
// 	r.Run()
// }
// 绑定为 JSON
type Login struct {
	User     string `form:"user" json:"user" xml:"user"  binding:"required"`
	Password string `form:"password" json:"password" xml:"password" binding:"required"`
}

func main() {

	gin.ForceConsoleColor()

	test.Log()
	config.ConfigLog()
	fmt.Println("有意思")

	router := gin.Default()

	// JSON 绑定示例 ({"user": "manu", "password": "123"})
	router.POST("/loginJSON", func(c *gin.Context) {
		var json Login
		if err := c.ShouldBindJSON(&json); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		if json.User != "manu" || json.Password != "123" {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
			return
		}

		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})

	})

	router.POST("/loginXML", func(c *gin.Context) {
		var xml Login
		if err := c.ShouldBindXML(&xml); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		if xml.User != "manu" || xml.Password != "123" {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
			return
		}

		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
	})

	// 绑定HTML表单的示例 (user=manu&password=123)
	router.POST("/loginForm", func(c *gin.Context) {
		var form Login
		//这个将通过 content-type 头去推断绑定器使用哪个依赖。
		if err := c.ShouldBind(&form); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		if form.User != "manu" || form.Password != "123" {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
			return
		}

		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
	})

	router.GET("/getJson", func(c *gin.Context) {

		c.JSON(http.StatusOK, gin.H{"message": "恭喜你", "id": "123"})
		fmt.Println("请求成功了")
	})

	router.Run(":8080")

}

创建Docker镜像

镜像(image)包含运行应用程序所需的所有东西——代码或二进制文件、运行时、依赖项以及所需的任何其他文件系统对象。或者简单地说,镜像(image)是定义应用程序及其运行所需的一切。

编写Dockerfile 要创建Docker镜像(image)必须在配置文件中指定步骤。这个文件默认我们通常称之为Dockerfile。现在我们开始编写Dockerfile,具体内容如下:

FROM golang:alpine

# 为我们的镜像设置必要的环境变量
ENV GO111MODULE=on \
    CGO_ENABLED=0 \
    GOOS=linux \
    GOARCH=amd64

# 移动到工作目录:/build
WORKDIR /build

# 将代码复制到容器中
COPY . .

# 将我们的代码编译成二进制可执行文件app
RUN go build -o app .

# 移动到用于存放生成的二进制文件的 /dist 目录
WORKDIR /dist

# 将二进制文件从 /build 目录复制到这里
RUN cp /build/app .

# 声明服务端口
EXPOSE 8080

# 启动容器时运行的命令
CMD ["/dist/app"]

Dockerfile解析

From
我们正在使用基础镜像golang:alpine来创建我们的镜像。这和我们要创建的镜像一样是一个我们能够访问的存储在Docker仓库的基础镜像。这个镜像运行的是alpine Linux发行版,该发行版的大小很小并且内置了Go,非常适合我们的用例。有大量公开可用的Docker镜像,请查看https://hub.docker.com/_/golang

Env
用来设置我们编译阶段需要用的环境变量。

WORKDIR,COPY,RUN
这几个命令做的事都写在注释里了,很好理解。

EXPORT,CMD
最后,我们声明服务端口,因为我们的应用程序监听的是这个端口并通过这个端口对外提供服务。并且我们还定义了在我们运行镜像的时候默认执行的命令CMD ["/dist/app"]。

构建镜像

cd到项目目录下,执行如下命令创建镜像,命名为goproject_app。注意:名称必须小写

docker build . -t goproject_app

现在我们已经准备好了镜像,但是目前它什么也没做。我们接下来要做的是运行我们的镜像,以便它能够处理我们的请求。运行中的镜像称为容器。执行下面的命令来运行镜像:

docker run -p 8080:8080 goproject_app

标志位-p用来定义端口绑定。由于容器中的应用程序在端口8888上运行,我们将其绑定到主机端口也是8080。如果要绑定到另一个端口,则可以使用-p $HOST_PORT:8080。例如-p 5000:8080。现在就可以测试下我们的web程序是否工作正常,浏览器输入http://127.0.0.1:8080/getJson 就能看到响应内容如下:

截屏2021-04-20 下午4.15.56.png

go语言项目Demo