likes
comments
collection
share

实战--使用All in one的方式将Go项目部署到Kubernetes

作者站长头像
站长
· 阅读数 52

什么是 Kubernetes

Kubernetes 是一个开源容器编排引擎,可以帮助开发者或运维人员部署和管理容器化的应用,能够轻松完成日常开发运维过程中诸如 滚动更新,横向自动扩容,服务发现,负载均衡等需求。

安装 Kubernetes

Kubernetes 术语介绍

Pod

Pod 是 Kubernetes 最小调度单位,是一个或一组容器的集合。

Deployment

提供对 Pod 的声明式副本控制。指定 Pod 模版,Pod 副本数量, 更新策略等。

Service

Service 定义了 Pod 的逻辑分组和一种可以访问它们的策略。借助Service,应用可以方便的实现服务发现与负载均衡。

Label & Selector

Kubernetes 中使用 Label 去关联各个资源。

  1. 通过资源对象(Deployment, etc.)上定义的 Label Selector 来筛选 Pod 数量。
  2. 通过 Service 的 Label Selector 来选择对应的 Pod, 自动建立起每个 Service 到对应 Pod 的请求转发路由表。
  3. 通过对某些 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 服务数据永久持久卷

  1. storageClassName 底层存储分布式服务单元

  2. 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

配置文件中主要是:

  1. 定义了 Pod 运行时的镜像 mariadb

  2. 暴露 mysql-service 服务到 3306 端口

  3. 挂载了一个容器中的存储数据地址 /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
评论
请登录