1. 项目信息

1.1 目录结构

# 项目目录信息
$ tree go-container
go-container
├── Dockerfile
├── go.mod
├── go.sum
└── main.go

1.2 主代码信息

main.go 代码如下:

package main

import (
"github.com/gin-gonic/gin"
"os"
"time"
)

func main() {
engine := gin.Default()
engine.GET("/", func(context *gin.Context) {
// 显示主机名字
hostName, _ := os.Hostname()
context.JSON(200, gin.H{
"version": "v1",
"hostName": hostName,
"time": time.Now().Format("2006-01-02 15:04:05"),
})
})
_ = engine.Run(":80")
}

2. 制作镜像

2.1 编写Dockerfile

# Golang版本;Alpine镜像的体积较小。
FROM golang:1.18 as builder

# 将代码复制到构建镜像中。
ADD . /workspace
WORKDIR /workspace

# 挂载构建缓存。
# GOPROXY防止下载失败。
RUN --mount=type=cache,target=/go \
env GOPROXY=https://goproxy.cn,direct \
go build -buildmode=pie -ldflags "-linkmode external -extldflags -static -w" \
-o /workspace/gin-hello

# 运行时镜像。
# Alpine兼顾了镜像大小和运维性。
FROM alpine:3.14
EXPOSE 8080
# 复制构建产物。
COPY --from=builder /workspace/gin-hello /app/

# 指定默认的启动命令。
CMD ["/app/gin-hello"]

2.2 构建镜像

# 进入项目目录执行
$ docker build -t liuqinghui/gin-hello:v1 .
[+] Building 30.7s (12/12) FINISHED
...
=> => naming to docker.io/liuqinghui/gin-hello:v1

2.3 推送镜像

这里将镜像推送到https://hub.docker.com/

# 先登陆
$ docker login
Authenticating with existing credentials...
Login Succeeded
# 推送
$ docker push liuqinghui/gin-hello:v1
The push refers to repository [docker.io/liuqinghui/gin-hello]
1320a677095b: Pushed
63493a9ab2d4: Mounted from library/alpine
v1: digest: sha256:25ae6ce90d8810450b9f84d89fe7499f6ce24167ec47b4fbd8aeb79a02632fa5 size: 739

查看推送结果

3. 部署到Kubernetes

3.1 编写yaml

文件:gin-hello.yaml

apiVersion: apps/v1 # 配置格式版本
kind: Deployment #资源类型,Deployment
metadata:
name: gin-hello-deploy #Deployment 的名称
spec:
replicas: 2 # 副本数量
selector: #标签选择器,
matchLabels: #选择包含标签app:gin_hello_pod的资源
app: gin_hello_pod
template: #Pod信息
metadata:
labels:
app: gin_hello_pod
spec:
containers:
- name: gin-hello
image: docker.io/liuqinghui/gin-hello:v1 # 我们构建的镜像

---
apiVersion: v1
kind: Service
metadata:
name: gin-hello-svc
spec:
type: NodePort
selector:
app: gin_hello_pod
ports:
- name: http
protocol: TCP
port: 8080 # ClusterIP监听的端口
targetPort: 80 # Pod监听的端口
nodePort: 30000

deployment、service等多个资源类型的配置可以写到一个文件,使用---间隔开即可。

3.2 部署

$ kubectl apply -f gin-hello.yaml
deployment.apps/gin-hello-deploy created
service/gin-hello-svc created

3.3 验证

# 查看 deployment
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
gin-hello-deploy 2/2 2 2 13m
# 查看pod
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
gin-hello-deploy-86f8cfb7b9-bkmhv 1/1 Running 0 14m
gin-hello-deploy-86f8cfb7b9-dwgwk 1/1 Running 0 14m

# 查看 service
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
gin-hello-svc NodePort 10.105.12.214 <none> 8080:30000/TCP 14m

访问宿主机ip

4. 服务升级

4.1 修改yaml

通过修改镜像版本,来实现服务升级,为了便于观察,把副本数量调大

修改文件 gin-hello.yaml

apiVersion: apps/v1 
kind: Deployment
metadata:
name: gin-hello-deploy
spec:
replicas: 3 # 调大副本数量
selector:
matchLabels:
app: gin_hello_pod
template:
metadata:
labels:
app: gin_hello_pod
spec:
containers:
- name: gin-hello
image: docker.io/liuqinghui/gin-hello:v2 # 升级镜像为v2
---
...

4.2 发布

# 依旧通过 kubectl apply 发布
$ kubectl apply -f gin-hello.yaml
deployment.apps/gin-hello-deploy configured
service/gin-hello-svc unchanged

4.3 访问验证

$ curl 10.105.12.214:8080
{"hostName":"gin-hello-deploy-6bb7456d9b-bk9mf","time":"2022-08-10 09:12:04","version":"v2"}
$ curl 10.105.12.214:8080
{"hostName":"gin-hello-deploy-6bb7456d9b-vqds9","time":"2022-08-10 09:12:33","version":"v2"}
$ curl 10.105.12.214:8080
{"hostName":"gin-hello-deploy-6bb7456d9b-2j9p2","time":"2022-08-10 09:12:34","version":"v2"}

5. 服务回滚

5.1 准备yaml文件

为了方便操作和后续回滚,编写多个配置文件,具体版本对应如下:

配置文件 镜像版本
gin-hello-v1.yaml gin-hello:v1
gin-hello-v2.yaml gin-hello:v2
gin-hello-v3.yaml gin-hello:v3

5.2 保存发布记录

kubectl apply每次更新应用时,Kubernetes都会记录下当前的配置,保存为一个revision(版次),这样就可以回滚到某个特定revision发布时,携带参数 –record,可以将当前命令记录到revision记录中。

为了方便后面回滚,按照顺序都发布下。

# 发布 v1
$ kubectl apply -f gin-hello-v1.yaml --record
# 验证 v1
$ curl 10.105.12.214:8080
{"hostName":"gin-hello-deploy-86f8cfb7b9-4z8kj","time":"2022-08-10 11:03:45","version":"v1"}

# 发布 v2
$ kubectl apply -f gin-hello-v2.yaml --record
# 验证 v2
$ curl 10.105.12.214:8080
{"hostName":"gin-hello-deploy-6bb7456d9b-p8hw2","time":"2022-08-10 11:04:19","version":"v2"}

# 发布 v3
$ kubectl apply -f gin-hello-v3.yaml --record
# 验证 v3
$ curl 10.105.12.214:8080
{"hostName":"gin-hello-deploy-69c4fd68ff-4ppfh","time":"2022-08-10 11:06:17","version":"v3"}

5.3 查看发布历史

通过kubectl rollout history deployment 名称查看revison历史记录

# 查看发布历史
$ kubectl rollout history deployment gin-hello-deploy
deployment.apps/gin-hello-deploy
REVISION CHANGE-CAUSE
3 kubectl apply --filename=gin-hello-v1.yaml --record=true
4 kubectl apply --filename=gin-hello-v2.yaml --record=true
5 kubectl apply --filename=gin-hello-v3.yaml --record=true

5.4 回滚指定版本

通过执行命令kubectl rollout undo deployment 名称 --to-revision=?,其中--to-revision=的值,就历史记录中REVISION,所对应的值。

# 查看当前所在版本
$ curl 10.105.12.214:8080
{"hostName":"gin-hello-deploy-69c4fd68ff-4ppfh","time":"2022-08-10 11:06:17","version":"v3"}

# 回滚到v1,而v1对应的REVISION = 3
$ kubectl rollout undo deployment gin-hello-deploy --to-revision=3
deployment.apps/gin-hello-deploy rolled back
# 验证结果
$ curl 10.105.12.214:8080
{"hostName":"gin-hello-deploy-86f8cfb7b9-r2jvr","time":"2022-08-10 11:14:31","version":"v1"}