实战--使用All in one的方式将Go项目部署到Kubernetes
什么是 Kubernetes
Kubernetes 是一个开源容器编排引擎,可以帮助开发者或运维人员部署和管理容器化的应用,能够轻松完成日常开发运维过程中诸如 滚动更新,横向自动扩容,服务发现,负载均衡等需求。
安装 Kubernetes
Kubernetes 术语介绍
Pod
Pod 是 Kubernetes 最小调度单位,是一个或一组容器的集合。
Deployment
提供对 Pod 的声明式副本控制。指定 Pod 模版,Pod 副本数量, 更新策略等。
Service
Service 定义了 Pod 的逻辑分组和一种可以访问它们的策略。借助Service,应用可以方便的实现服务发现与负载均衡。
Label & Selector
Kubernetes 中使用 Label 去关联各个资源。
- 通过资源对象(Deployment, etc.)上定义的 Label Selector 来筛选 Pod 数量。
- 通过 Service 的 Label Selector 来选择对应的 Pod, 自动建立起每个 Service 到对应 Pod 的请求转发路由表。
- 通过对某些 Node 定义特定的 Label,并且在 Pod 中添加 NodeSelector 属性,可以实现 Pod 的定向调度(运行在哪些节点上)。
k8s-app项目
GitHub - horzions/k8s-example-app 是一个go+mysql编写的后端示例应用。我们在这里使用gin来作为路由网络框架,使用mysql作为有状态持久化数据,基本功能有curd 注册登录以及中间件登录权限拦截。
项目分析
分析app.yaml
,我们将使用mariadb 作为mysql的替代,因为相对在各项性能优化对小配置机器比较友好。
在项目根目录添加 Dockerfile:
FROM golang:alpine as builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o k8s-example-app .
FROM scratch
WORKDIR /app
COPY --from=builder /app/k8s-example-app /app
COPY --from=builder /app/app.yaml /app
EXPOSE 3000
ENTRYPOINT ["/app/k8s-example-app"]
在这里因为硬件小内存vps优化 需要做到极限压缩镜像,所以我们把打包Dockerfile分为两部分。
-
builder:golang-alpine 负责go程序编译工作
-
scratch:scratch是一个最小化debian的空壳,我们把编译好的go程序放到这个环境里形成镜像组合。形成实际上的打包运行镜像,即scratch + 最小化的go程序。
打包镜像
podman build -t k8s-app .
查看镜像
root@control:~/k8s-example-app# podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/k8s-app latest e617c44b4637 28 seconds ago 11.9 MB
localhost/horzion/k8s-app beta e617c44b4637 28 seconds ago 11.9 MB
<none> <none> 20d74f90a960 30 seconds ago 773 B
<none> <none> ab58b427b4d7 37 seconds ago 575 MB
docker.io/library/golang alpine 6e31dcd72d8f 1 days ago 363 MB
推送镜像到 Docker Hub
podman login docker.io
podman tag k8s-app horzion/k8s-app:latest
podman push k8s-app horzion/k8s-app:latest
部署 MariaDB
我们把这个项目分为两个微服务,一个是跑go程序的k8s-example-app
服务,一个是负责持久化存储服务的mariaDB。
mariadb 使用 Docker Hub来作为镜像,底层pvc存储数据则用到了rancher家的分布式存储服务longhorn-system
,我们通过Service
方式 将服务暴露在k8s集群内部。
mysql-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 2Gi
创建mysql 服务数据永久持久卷
-
storageClassName
底层存储分布式服务单元 -
storage
给这个mysql服务分配了2G容量
在 kubernetes 集群中执行 kubectl
部署
kubectl apply -f mysql-pvc.yaml
查看 pvc :
➜ k8s-example-app git:(main) ✗ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-pvc Bound pvc-a63fcfb3-08d8-49a0-9709-af00b0a8c411 2Gi RWO longhorn 42h
mysql.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
labels:
app: mysql
namespace: default
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
restartPolicy: Always
containers:
- image: mariadb
name: mysql
livenessProbe:
exec:
command:
- ls
- /var/lib/mysql/lost+found
initialDelaySeconds: 5
periodSeconds: 5
ports:
- containerPort: 3306
protocol: TCP
name: http-mysql
volumeMounts:
- name: mysql-volume
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "rancher"
volumes:
- name: mysql-volume
persistentVolumeClaim:
claimName: mysql-pvc
配置文件中主要是:
-
定义了 Pod 运行时的镜像
mariadb
-
暴露 mysql-service 服务到
3306
端口 -
挂载了一个容器中的存储数据地址
/var/lib/mysql
在 kubernetes 集群中执行 kubectl
部署
kubectl apply -f mysql.yaml
查看 pod 运行情况:
➜ k8s-example-app git:(main) ✗ kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-74f6f8c96b-vqg47 1/1 Running 0 65m
k8s-app-v1-75c5d78ddb-ln6h8 1/1 Running 0 65m
pod 已成功运行起来
查看 mysql
服务日志:
➜ k8s-example-app git:(main) ✗ kubectl logs mysql-74f6f8c96b-vqg47
2022-11-06 07:22:00+00:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:10.9.3+maria~ubu2204 started.
2022-11-06 07:22:01+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2022-11-06 07:22:01+00:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:10.9.3+maria~ubu2204 started.
2022-11-06 07:22:02+00:00 [Note] [Entrypoint]: MariaDB upgrade not required
2022-11-06 7:22:02 0 [Note] mariadbd (server 10.9.3-MariaDB-1:10.9.3+maria~ubu2204) starting as process 1 ...
...
2022-11-06 7:22:08 0 [Warning] You need to use --log-bin to make --expire-logs-days or --binlog-expire-logs-seconds work.
2022-11-06 7:22:08 0 [Note] Server socket created on IP: '0.0.0.0'.
2022-11-06 7:22:08 0 [Note] Server socket created on IP: '::'.
2022-11-06 7:22:08 0 [Note] InnoDB: Buffer pool(s) load completed at 221106 7:22:08
2022-11-06 7:22:10 0 [Note] mariadbd: ready for connections.
Version: '10.9.3-MariaDB-1:10.9.3+maria~ubu2204' socket: '/run/mysqld/mysqld.sock' port: 3306 mariadb.org binary distribution
从日志可以看到 mysql
已成功启动,并监听 3306
端口, 但此时在集群中还无法访问该端口,需要创建一个服务将端口映射到集群。
创建 Mysql 服务
mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
spec:
ports:
- port: 3306
name: http-mysql
protocol: TCP
targetPort: 3306
selector:
app: mysql
clusterIP: None
在 kubernetes 集群中执行 kubectl
创建服务
kubectl apply -f mysql-service.yaml
查看服务详情
➜ k8s-example-app git:(main) ✗ kubectl describe svc/mysql-service
Name: mysql-service
Namespace: default
Labels: app=mysql
Annotations: <none>
Selector: app=mysql
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: None
IPs: None
Port: http-mysql 3306/TCP
TargetPort: 3306/TCP
Endpoints: 10.244.143.163:3306
Session Affinity: None
Events: <none>
透过 Kubernetes DNS,在集群内部可以使用 mysql-service.default:3306
访问到 mysql 服务。
部署 k8s-example-app
k8s-example-app 因为日志存储没开 所以无需挂在存储卷,我们直接通过 mysql-service.default 来访问 mysql 服务。该端口无需暴露到主机上,service 类型选择使用 ClusterIP。
app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: default
labels:
version: v1
app: k8s-app
name: k8s-app-v1
spec:
replicas: 1
selector:
matchLabels:
version: v1
app: k8s-app
template:
metadata:
labels:
version: v1
app: k8s-app
spec:
containers:
- name: k8s-app-container
image: horzion/k8s-app:beta
imagePullPolicy: IfNotPresent
ports:
- name: http-app
protocol: TCP
containerPort: 3000
serviceAccount: default
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
在 kubernetes 集群中执行 kubectl
部署
kubectl apply -f app.yaml
查看 Pod 状态及日志
➜ k8s-example-app git:(main) ✗ kubectl logs k8s-app-v1-75c5d78ddb-ln6h8
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] POST /v1/auth/login --> github.com/horzions/k8s-example-app/app/account.(*AccountService).Login-fm (3 handlers)
[GIN-debug] POST /v1/auth/register --> github.com/horzions/k8s-example-app/app/account.(*AccountService).Register-fm (3 handlers)
[GIN-debug] POST /v1/auth/forget --> github.com/horzions/k8s-example-app/app/account.(*AccountService).ResetPassword-fm (3 handlers)
[GIN-debug] POST /v1/account/add --> github.com/horzions/k8s-example-app/app/account.(*AccountService).AddAccount-fm (4 handlers)
[GIN-debug] POST /v1/account/modify --> github.com/horzions/k8s-example-app/app/account.(*AccountService).ModifyAccount-fm (4 handlers)
[GIN-debug] POST /v1/account/delete --> github.com/horzions/k8s-example-app/app/account.(*AccountService).DeleteAccount-fm (4 handlers)
[GIN-debug] POST /v1/account/info --> github.com/horzions/k8s-example-app/app/account.(*AccountService).AccountInfo-fm (4 handlers)
{"level":"info","remote_ip":"10.244.42.192","method":"POST","status_code":404,"body_size":-1,"path":"/v1/v1/account/add","latency":"1.836µs","time":"2022-11-06T07:23:09Z"}
{"level":"info","remote_ip":"10.244.42.192","method":"POST","status_code":404,"body_size":-1,"path":"/v1/v1/account/add","latency":"6.757µs","time":"2022-11-06T07:23:50Z"}
{"level":"info","remote_ip":"10.244.42.192","method":"POST","status_code":404,"body_size":-1,"path":"/v1/v1/account/add","latency":"1.734µs","time":"2022-11-06T07:23:51Z"}
k8s-example-app 程序已成功启动,现在需将服务暴露主机节点上访问,以便主机网络将端口映射到外网。service
类型选择 NodePort
创建 k8s-example-app 服务到并暴露到主机
apiVersion: v1
kind: Service
metadata:
namespace: default
labels: &ref_0
version: v1
app: k8s-app
name: k8s-app
spec:
type: NodePort
sessionAffinity: ClientIP
selector: *ref_0
ports:
- name: http-app
protocol: TCP
port: 3000
targetPort: 3000
nodePort: 30443
type: NodePort
在 kubernetes 集群中执行 kubectl
创建服务
kubectl apply -f app-service.yaml
查看服务详情
➜ k8s-example-app git:(main) ✗ kubectl describe svc/k8s-app
Name: k8s-app
Namespace: default
Labels: app=k8s-app
version=v1
Annotations: <none>
Selector: app=k8s-app,version=v1
Type: NodePort
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.43.124.170
IPs: 10.43.124.170
Port: http-app 3000/TCP
TargetPort: 3000/TCP
NodePort: http-app 30443/TCP
Endpoints: 10.244.143.164:3000
Session Affinity: ClientIP
External Traffic Policy: Cluster
Events: <none>
在节点上访问:
➜ k8s-example-app git:(main) ✗ curl 173.82.225.70:30443
404 page not found%
可见 k8s-example-app 可以正常访问,不过仍需要确认是否能正常访问 mysql。
k8s-example-app 容器需要等待 mysql service 可用时, 才可以正常启动。在 mysql service 可用后, 手动删除旧的 k8s-example-app Pod 即可。
创建k8sapp应用路由映射
在这里需要一个泛域名解析到这个IP,我这里域名是 *.moutfire.com
与k8s不同的是,在这里k3s 默认的ingress
网关为traefik。
k8sapp-ingress-traefik.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: k8sapp-ing-traefik
namespace: default
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- host: "k8sapp.moutfire.com"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: k8s-app
port:
number: 3000
在 kubernetes 集群中执行 kubectl
创建应用路由
kubectl apply -f k8sapp-ingress-traefik.yaml
测试 k8s-example-app 服务
注册账户
➜ k8s-example-app git:(main) ✗ curl --location --request POST 'http://k8sapp.moutfire.com/v1/auth/register' \
--header 'Content-Type: application/json' \
--data-raw '{
"email":"demo@live.com",
"password":"demo",
"account_name":"demo"
}'
{"msg":"account created success."}%
账户登录
➜ k8s-example-app git:(main) ✗ curl --location --request POST 'http://k8sapp.moutfire.com/v1/auth/login' \
--header 'Content-Type: application/json' \
--data-raw '{
"email":"demo@live.com",
"password":"demo"
}'
{"msg":"login success.","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50TmFtZSI6ImRlbW8iLCJlbWFpbCI6ImRlbW9AbGl2ZS5jb20iLCJ0b2tlbkV4cCI6MCwiSnd0S2V5IjoiIiwiZXhwIjoxNjY3NzQ1NjU2LCJuYmYiOjE2Njc3NDIwNTYsImlhdCI6MTY2Nzc0MjA1Nn0.e22849U_pddfieZU1YYbzvgMnrpSPzUyQQZNNdRXB_w"}%
账户信息
➜ k8s-example-app git:(main) ✗ curl --location --request POST 'http://k8sapp.moutfire.com/v1/account/info' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50TmFtZSI6ImRlbW8iLCJlbWFpbCI6ImRlbW9AbGl2ZS5jb20iLCJ0b2tlbkV4cCI6MCwiSnd0S2V5IjoiIiwiZXhwIjoxNjY3NzQ1NjU2LCJuYmYiOjE2Njc3NDIwNTYsImlhdCI6MTY2Nzc0MjA1Nn0.e22849U_pddfieZU1YYbzvgMnrpSPzUyQQZNNdRXB_w'
{"msg":"account info."}%
通过测试可发现 k8s-example-app 与 mysql 服务均运行正常。
转载自:https://juejin.cn/post/7165879889311055909