11. 选课系统的持续交付:Jenkins 与 k8s 的实战应用

88 阅读6分钟

回顾上文

由于本篇跳过了前两篇进行撰写,本期旨在探讨如何借助 Jenkins + k8s 实现快速持续交付。此前,虽对 k8s 有过简单学习,但尚未进行实际操作。近期恰逢华为云服务器优惠力度大,便购入 3 台,由此萌生了撰写此文的想法。在本文中,我将详细介绍如何通过 Jenkins 和 k8s 实现快速持续交付,同时分享在此过程中遇到的一些坑及解决方法。

能学到什么

  • Jenkins快速安装

  • k8s部署

涉及技术

  • Docker

  • k8s

  • Jenkins

架构设计

暂时无法在飞书文档外展示此内容

image.png

前提准备

机器选购

为了这期文,我大出血在华为云选购了三台云服务非常的便宜(28元),有兴趣的友友可以选购选购,话不多说开干。

配置

  1. master(2c4g)
  2. node1(2c2g)
  3. node2(2c2g)

注意:

这里由于node1处于华北区域机器,master和node2处于华南区域,无法通过内网互相访问(华为提供云链接等方案可以解决)。

k8s集群部署

  • K8s 1.28.0

  • Docker 26.1.4

k8s搭建可以参考:实战Kubernetes之快速部署 K8s 集群 v1.28.0-云社区-华为云

master作为控制节点及主节点,node2作为从节点。由于node1无法通过内网与master通信,至此node1节点就作为服务依赖节点(mysql,redis,mq,consul)。

在网上找过许多方案通过修改iptable表来访问,但是加入节点始终加入不进去。

当访问192.168.14.109时通过路由表访问120.46.15.126 。
iptables -t nat -A OUTPUT -d 192.168.14.109 -j DNAT --to-destination 120.46.15.126

服务依赖节点 node1

Jenkisn

部署

这里jenkins部署通过运行jar包的形式使用。

需要jenkins.war的请留言。

  • Java 11
java -jar jenkins.war

配置

  1. 下载插件 Generic Webhook TriggerVersion2.2.2

  2. 创建任务

    1. 新建任务
    2. 选择自由风格

  1. 配置

    1. 源码管理

    2. 如果遇到如上错误可能就是没有安装git,yum install git

    3. 构建触发器

      1. 选择Generic Webhook Trigger形式
      2. 找到仓库的webhooks功能
    4. 保持测试

sudo docker push swr.cn-north-4.myhuaweicloud.com/bbz/select:1.1

资源编写

这里只有简单的两个服务

user服务

  • 10000

user比较简单这里只进行分配到一台机器即可。

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  namespace: select-course
  labels:
    app: user-service
spec:
  replicas: 1
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      name: user-service
      labels:
        app: user-service
        version: "BUILD_HASH"
    spec:
      containers:
        - name: user-service
          image: swr.cn-north-4.myhuaweicloud.com/bbz/select-course:latest
          imagePullPolicy: IfNotPresent
          command: [ "/bin/sh", "-c", "export BASE_HOST=`hostname -i` && ./services/user/UserService" ]
          ports:
            - name: grpc-10000
              containerPort: 10000
              protocol: TCP
          volumeMounts:
            - name: config-env
              mountPath: /project/.env
              subPath: .env
            - name: project-logs-volume
              mountPath: /project/logs
      imagePullSecrets:
        - name: harborsecret
      volumes:
        - name: config-env
          configMap:
            name: config-env
            items:
              - key: .env
                path: .env
        - name: project-logs-volume
          hostPath:
            path: /data/logs
            type: DirectoryOrCreate
      restartPolicy: Always
      terminationGracePeriodSeconds: 30

course服务

course服务分配到两台机器包括master(由于资源有限不得不使用master也作为服务节点)。

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: course-service
  namespace: select-course
  labels:
    app: course-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: course-service
  template:
    metadata:
      name: course-service
      labels:
        app: course-service
        version: "BUILD_HASH"
    spec:
      containers:
        - name: course-service
          image: swr.cn-north-4.myhuaweicloud.com/bbz/select-course:latest
          imagePullPolicy: IfNotPresent
          command: [ "/bin/sh", "-c", "export BASE_HOST=`hostname -i` && ./services/course/CourseService" ]
          ports:
            - name: grpc-10001
              containerPort: 10001
              protocol: TCP
          volumeMounts:
            - name: config-env
              mountPath: /project/.env
              subPath: .env
            - name: project-logs-volume
              mountPath: /project/logs
      imagePullSecrets:
        - name: harborsecret
      volumes:
        - name: config-env
          configMap:
            name: config-env
            items:
              - key: .env
                path: .env
        - name: project-logs-volume
          hostPath:
            path: /data/logs
            type: DirectoryOrCreate
      restartPolicy: Always
      terminationGracePeriodSeconds: 30

Ingress

ingress-nginx部署

这里通过使用DaemonSet模式让ingress代理主机80端口。

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-nginx
  namespace: select-course
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - host: nginx.kubernetes-devops.cn
      http:
        paths:
          - pathType: Prefix
            path: "/"
            backend:
              service:
                name: gateway-svc
                port:
                  number: 8888

打包进行上传到hub

这里由于国内访问docker hub被墙,所有这里使用华为云免费的私有hub仓库进行管理。

k8s配置认证

你也可以通过如下操作进行拉取,当前镜像属于公开的

 docker pull swr.cn-north-4.myhuaweicloud.com/bbz/select-course:latest

通过k8s运行此项目

你可以通过项目根目录下的/k8s文件夹直接启动项目,在启动项目前你需要部署项目的依赖文件,同级目录下的docker-compose.yaml

  1. 部署依赖服务
docker-compose up -d

2. 启动项目

cd k8s
# 如果没有部署ingress-nginx
# kubectl apply -f ingress-nginx

kubectl apply -f .

模拟

模拟推送事件

通过请求推送来进行触发构建操作

注意:

在实际开发场景下,并不是每次请求推送事件进行构建操作,这种常用于test分支,针对于主分支变更是非常少的。

推送

这里进行手动方式触发构建

构建步揍

 #!/bin/bash

if [ ! "$(basename "$PWD")" ] = "select-course"; then
  echo "please run this script in select-course"
  exit 1
fi

# start
echo "starting..."
# 获取Jenkins构建号(如果在Jenkins环境中)
BUILD_NUMBER=${BUILD_NUMBER:-"unknown"}
BUILD_HASH=${BUILD_NUMBER}-${GIT_COMMIT}
IMAGE_NAME=select-course:${BUILD_HASH}
PUSH_IMAGE_NAME=swr.cn-north-4.myhuaweicloud.com/bbz/select-course:${BUILD_HASH}

# 构建镜像并使用唯一的标签
docker build -t  "${IMAGE_NAME}" .
echo "build success"

# 推送带有唯一标签的镜像
docker tag "${IMAGE_NAME}"  "${PUSH_IMAGE_NAME}" # 提交带有唯一标签的镜像
docker tag "${IMAGE_NAME}"  select-course:latest # 提交最新的镜像
docker push "${PUSH_IMAGE_NAME}"
docker push select-course:latest
echo "push with unique tag success"

# 清理本地镜像
docker rmi -f "${IMAGE_NAME}"
docker rmi -f select-course:latest
docker rmi -f "${PUSH_IMAGE_NAME}"
echo "clear success"

# k8 replace

# 遍历目录中的所有 YAML 文件
find "./k8s" -name "*.yaml" | while read FILE; do
  sed -i "s|BUILD_HASH|${BUILD_HASH}|" "$FILE"
done
# load configmap
# kubectl create configmap config-env -n select-course --from-file=./.env
kubectl apply -f k8s
echo "deploy success"

以下是流程图

一杯咖啡的时间。。。

image.png 暂时无法在飞书文档外展示此内容

坑集合

Jenkins

作者在网上找到了jenkins的打包文件,可以快速运行无需安装,需要的可以留言。无坑

K8s

  • 同网段问题

    • 问题原因:我在购置三台云服务器时这三台云服务器并非在同一个地区(网段),只有两台节点在同一个地区,我尝试过许多方式将非同一个网段的地区加入到master节点,但还是未见效。

    • 解决:

Ingress

ingress的问题也是卡我最长时间的问题,主要是对ingress操作不熟练。

在我项目里面也有ingress-nginx的已经修改的资源文件。

  • 与k8s版本问题

总结

在篇文章中,我们通过搭建 k8s 集群,并部署 Jenkins,来实现选课系统的快速持续交付。其中,master 作为控制节点及主节点,node2 作为从节点,node1 由于无法与 master 通过内网通信,作为服务依赖节点(mysql,redis,mq,consul)。通过运行 jar 包的形式部署 Jenkins。通过仓库的webhook机制进行预订事件进行自动化构建。

注意:

该文章只是一个简单的持续交付流程,在实际的场景内比这复杂得多。不限于:版本控制、滚动更新、自动化测试等等。

请见下期文章。

本期代码请见:github.com/bbz1024/sel…

若阁下感兴趣可以clone下来玩一玩。

bash
 代码解读
复制代码
git clone --branch demo6 https://github.com/bbz1024/select-course.git