0%

Golang程序与docker容器结合

背景

传统项目上线过程为将编译后的项目安装包手动部署到对应服务器上。近年容器化技术普及,将应用程序与容器化技术结合,提高应用管理能力及快速扩缩容能力。

具体动作如下

  1. Golang程序编写
  2. 项目维护Dockerfile文件,定义程序编译及运行命令
  3. 打包docker镜像
  4. 通过docker镜像启动docker容器
  5. 访问容器中Golang程序

docker命令

先了解常见docker命令

  1. 镜像基础命令

    1
    2
    3
    docker images # 获取本机镜像列表
    docker pull 镜像名称:tag # 从远程仓库拉取对应tag镜像到本地,具体镜像可在https://hub.docker.com 查看
    docker rmi 镜像id # 删除本地镜像
  2. 容器基础命令

    1
    2
    3
    4
    5
    6
    docker ps # 查看正在运行的容器
    docker ps -a # 查看所有构建的容器
    docker rm -v 容器id # 删除指定容器
    docker exec -it 容器id /bin/bash # 进入指定id的容器内部(/bin/bash 解释器需要跟进镜像对应改变,又可能是/bin/sh)
    docker start 容器id # 启动容器
    docker stop 容器id # 停止容器
  3. 创建容器

    1
    docker run -d -p 8080:8080 -v /Users/zhangmengnan/docker_test/ext_file:/mnt/engine/server/logs --name gin-server-0.0.1 镜像id

    根据基础镜像构建一个容器

    • -d 构建容器后,后台运行
    • -p 创建端口映射 宿主端口:容器端口
    • -v 创建磁盘挂载, 宿主路径:容器路径,注意宿主路径内容会覆盖容器路径
    • –name 给创建的容器指定名字

Golang与docker结合

基于镜像部署Golang程序

此方式逻辑为,将Golang项目整体拷贝到docker容器中,编译,然后运行。最终容器内文件如图

1) 创建Golang项目

1
2
3
4
5
go-docker-test
- Dockerfile
- server.go
- go.mod
- go.su

创建go项目,项目结构如上, server.go如下

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

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

func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run()
}

2) 编写Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#依赖镜像
FROM golang:1.19-alpine

#作者信息
MAINTAINER "zhangmengnan"

# 配置模块代理
ENV GO111MODULE=on
ENV GOPROXY=https://goproxy.cn,direct

#工作目录
WORKDIR /opt
# 将当前目录内容拷贝到容器的/opt目录
ADD . /opt

#在Docker工作目录下执行命令,编译服务
RUN go build -o server ./server.go

#声明服务端口
EXPOSE 8080

#执行项目的命令,启动服务
CMD ["/opt/server"]

3) 基于Dockerfile构建镜像

在项目根目录下执行命令,构建镜像

1
2
3
docker build -t 镜像名字 .
例如
docker build -t test-gin .

运行完成后 使用 docker images 即可看到构建的镜像

4) 基于镜像创建容器

镜像创建成功后,需要使用镜像创建运行对应容器,成功会打印创建的容器id

1
docker run -d -p 8080:8080 test-gin

基于 test-gin这个镜像,构建容器并后台运行,且指定端口映射。 成功后使用 docker ps 可以看到运行的该容器

5) 启动容器及服务

  1. 测试服务是否正常

    浏览器访问 http://localhost:8080/ping 响应正常

  2. 容器启动后可以通过命令可以进入容器中

    1
    docker exec -it 容器id /bin/sh
  3. 可以关闭和重启容器

编译镜像和部署镜像分离

前面的方式有个问题是编译镜像和运行镜像是同一个,会将项目源码暴露出去。现在将服务编译使用一个镜像,然后将编译后的结果拷贝到运行容器中。

最终容器内列表如下

1) 创建Golang项目

项目结构如下

1
2
3
4
5
6
7
go-docker-from-to
- Dockerfile
- build.sh
- go.mod
- go.sum
- server.go
- start.sh

项目代码 server.go

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

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

func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run()
}

项目编译脚本 build.sh

1
go build -o server server.go

项目启动脚本 start.sh

1
2
3
#!/bin/sh

./server >> ./logs/a.log

1) 修改Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#依赖镜像
FROM golang:1.19-alpine3.18 as builder

#作者信息
MAINTAINER "zhangmengnan"

# 配置模块代理
ENV GO111MODULE=on
ENV GOPROXY=https://goproxy.cn,direct

#工作目录
WORKDIR /mnt/engine
ADD . .

#在Docker工作目录下执行命令, 编译
RUN ./build.sh

FROM golang:1.19-alpine3.18

# 指定工作目录
WORKDIR /mnt/engine/server

# 创建日志目录
RUN mkdir /mnt/engine/server/logs

# 从编译机器拷贝二进制和启动脚本
COPY --from=builder /mnt/engine/server .
COPY --from=builder /mnt/engine/start.sh .

#执行项目的命令
CMD ["./start.sh"]

2) 基于Dockerfile构建镜像

执行命令基于dockerfile构建镜像

1
2
3
docker build -t 镜像命令:镜像tag .
例如
docker build -t gin-server:gin-server-0.0.1 .

3) 创建容器

命令创建且后台运行容器,指定端口映射,挂载磁盘,将日志挂载到本机

1
docker run -d -p 8080:8080 -v /Users/zhangmengnan/docker_test/ext_file:/mnt/engine/server/logs --name gin-server-0.0.1 gin-server:gin-server-0.0.1

小结

通过上述方法可实现Go程序和docker结合,且可以避免源码暴露