首页 > 其他分享 >前后端分离项目实现CI-CD

前后端分离项目实现CI-CD

时间:2024-07-01 14:21:37浏览次数:1  
标签:CI name kubernetes app 分离 ingress CD nginx io

Ruoyi-Vue CI/CD

主机 IP 说明
K8s Master节点*3 10.0.0.103-105(VIP10.0.0.236) 管理node
K8s node节点*3 10.0.0.106-108 部署前端+后端
storage 10.0.0.144 MySQL、Redis、NFS
Jenkins(master01) 10.0.0.103 持续集成/持续交付的软件
Gitlab(master02) 10.0.0.104 代码仓库
harbor 10.0.0.138 镜像仓库

软件安装请看devops篇、storage的配置请看ruoyi-vue

1. Kubernetes 创建资源

Deployment 资源

Kubernetes 中的 Deployment 资源是一种用于定义和管理 Pod 副本的对象。它可以确保在集群中运行指定数量的 Pod 副本,并且在发生故障或需要扩展时能够进行滚动更新。Deployment 资源还允许您在容器镜像、标签选择器和副本数量等方面进行灵活的配置。

通过定义 Deployment 资源,您可以轻松管理应用程序的部署和升级,而无需手动管理每个 Pod 的创建和销毁。这使得在 Kubernetes 集群中部署和维护应用程序变得更加简单和可靠。

#cat ruoyi-deployment.yaml
---  
# 第一个 Deployment: ruoyi-frontend 
apiVersion: apps/v1  
kind: Deployment  
metadata:  
  labels:  
    app: ruoyi-frontend  
  name: ruoyi-frontend  
  namespace: default  
spec:  
  replicas: 3  
  minReadySeconds: 3  
  selector:  
    matchLabels:  
      app: ruoyi-frontend 
  template:  
    metadata:  
      labels:  
        app: ruoyi-frontend 
    spec:  
      containers:  
      - name: ruoyi-frontend   
        image: 10.0.0.138:5000/library/nginx:latest  
        ports:  
        - name: web  
          containerPort: 80  
  
---  
# 第二个 Deployment: ruoyi-backend  
apiVersion: apps/v1  
kind: Deployment  
metadata:  
  labels:  
    app: ruoyi-backend  
  name: ruoyi-backend  
  namespace: default  
spec:  
  replicas: 3  
  minReadySeconds: 3  
  selector:  
    matchLabels:  
      app: ruoyi-backend  
  template:  
    metadata:  
      labels:  
        app: ruoyi-backend  
    spec:  
      containers:  
      - name: ruoyi-backend  
        image: 10.0.0.138:5000/library/nginx:latest # 假设这里你有一个ruoyi-backend的镜像  
        ports:  
        - name: api  
          containerPort: 8080  
        volumeMounts:  
        - mountPath: /var/log/ruoyi  
          name: ruoyi-log  
        - mountPath: /data/ruoyi_data  
          name: nfs-volume  
      volumes:  
      - name: ruoyi-log  
        hostPath:  
          path: /var/log/ruoyi  
      - name: nfs-volume  
        nfs:  
          server: 10.0.0.144  
          path: /nfs_data
   
# kubectl apply -f ruoyi-deployment.yaml

Tips:需要在各节点安装nfs nfs-utils的客户端

image-20240522224834871

SVC资源

书写一个 ruoyi-service.yaml 资源文件用于服务发布。

#cat ruoyi-service.yaml
apiVersion: v1  
kind: Service  
metadata:  
  name: ruoyi-frontend  
  namespace: default  
spec:  
  selector:  
    app: ruoyi-frontend 
  ports:  
    - name: ruoyi-frontend    
      port: 80  
      targetPort: web  #对应pod中的ports name
  type: ClusterIP  
---  
apiVersion: v1  
kind: Service  
metadata:  
  name: ruoyi-backend  
  namespace: default  
spec:  
  selector:  
    app: ruoyi-backend  
  ports:  
    - name: ruoyi-backend  
      port: 8080  
      targetPort: api  
  type: ClusterIP
  
# kubectl apply -f ruoyi-service.yaml

image-20240522224907317

安装ingress-controller

已在 ingress-nginx-controller 下为 Deployment 改为 Daemonset 更改hostnetwork模式、以及增加标签 ingress: "true"

在master节点都打上 ingress: "true"标签(配置容忍度或需要去除污点)。ingress-controller 安装在 master 节点可使用vip即可访问到资源。由于我的master01和02的80端口已被占用所以只安装到master03上(master如需安装应用建议不用80端口,将此端口给ingress)

[root@k8s-master01 ruoyi-vue]# kubectl label nodes k8s-master03 ingress=true 
node/k8s-master03 labeled
#cat deploy-ingress.yaml
apiVersion: v1
kind: Namespace
metadata:
  labels:
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
  name: ingress-nginx
---
apiVersion: v1
automountServiceAccountToken: true
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx
  namespace: ingress-nginx
---
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx-admission
  namespace: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx
  namespace: ingress-nginx
rules:
- apiGroups:
  - ""
  resources:
  - namespaces
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - configmaps
  - pods
  - secrets
  - endpoints
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - services
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses/status
  verbs:
  - update
- apiGroups:
  - networking.k8s.io
  resources:
  - ingressclasses
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - coordination.k8s.io
  resourceNames:
  - ingress-nginx-leader
  resources:
  - leases
  verbs:
  - get
  - update
- apiGroups:
  - coordination.k8s.io
  resources:
  - leases
  verbs:
  - create
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - create
  - patch
- apiGroups:
  - discovery.k8s.io
  resources:
  - endpointslices
  verbs:
  - list
  - watch
  - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx-admission
  namespace: ingress-nginx
rules:
- apiGroups:
  - ""
  resources:
  - secrets
  verbs:
  - get
  - create
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx
rules:
- apiGroups:
  - ""
  resources:
  - configmaps
  - endpoints
  - nodes
  - pods
  - secrets
  - namespaces
  verbs:
  - list
  - watch
- apiGroups:
  - coordination.k8s.io
  resources:
  - leases
  verbs:
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - services
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - create
  - patch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses/status
  verbs:
  - update
- apiGroups:
  - networking.k8s.io
  resources:
  - ingressclasses
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - discovery.k8s.io
  resources:
  - endpointslices
  verbs:
  - list
  - watch
  - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx-admission
rules:
- apiGroups:
  - admissionregistration.k8s.io
  resources:
  - validatingwebhookconfigurations
  verbs:
  - get
  - update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx
  namespace: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: ingress-nginx
subjects:
- kind: ServiceAccount
  name: ingress-nginx
  namespace: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx-admission
  namespace: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: ingress-nginx-admission
subjects:
- kind: ServiceAccount
  name: ingress-nginx-admission
  namespace: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ingress-nginx
subjects:
- kind: ServiceAccount
  name: ingress-nginx
  namespace: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx-admission
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ingress-nginx-admission
subjects:
- kind: ServiceAccount
  name: ingress-nginx-admission
  namespace: ingress-nginx
---
apiVersion: v1
data:
  allow-snippet-annotations: "true"
kind: ConfigMap
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx-controller
  namespace: ingress-nginx
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - appProtocol: http
    name: http
    port: 80
    protocol: TCP
    targetPort: http
  - appProtocol: https
    name: https
    port: 443
    protocol: TCP
    targetPort: https
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
  type: NodePort
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx-controller-admission
  namespace: ingress-nginx
spec:
  ports:
  - appProtocol: https
    name: https-webhook
    port: 443
    targetPort: webhook
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
  type: ClusterIP
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  minReadySeconds: 0
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app.kubernetes.io/component: controller
      app.kubernetes.io/instance: ingress-nginx
      app.kubernetes.io/name: ingress-nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/component: controller
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
        app.kubernetes.io/version: 1.7.1
    spec:
      tolerations:
      - key: "node-role.kubernetes.io/master"
        operator: "Exists"
        effect: "NoSchedule"
      hostNetwork: true
      containers:
      - args:
        - /nginx-ingress-controller
        - --election-id=ingress-nginx-leader
        - --controller-class=k8s.io/ingress-nginx
        - --ingress-class=nginx
        - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
        - --validating-webhook=:8443
        - --validating-webhook-certificate=/usr/local/certificates/cert
        - --validating-webhook-key=/usr/local/certificates/key
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: LD_PRELOAD
          value: /usr/local/lib/libmimalloc.so
        image: registry.cn-beijing.aliyuncs.com/dotbalo/ingress-nginx-controller:v1.7.1
        imagePullPolicy: IfNotPresent
        lifecycle:
          preStop:
            exec:
              command:
              - /wait-shutdown
        livenessProbe:
          failureThreshold: 5
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        name: controller
        ports:
        - containerPort: 80
          name: http
          protocol: TCP
        - containerPort: 443
          name: https
          protocol: TCP
        - containerPort: 8443
          name: webhook
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        resources:
          requests:
            cpu: 100m
            memory: 90Mi
        securityContext:
          allowPrivilegeEscalation: true
          capabilities:
            add:
            - NET_BIND_SERVICE
            drop:
            - ALL
          runAsUser: 101
        volumeMounts:
        - mountPath: /usr/local/certificates/
          name: webhook-cert
          readOnly: true
      dnsPolicy: ClusterFirst
      nodeSelector:
        kubernetes.io/os: linux
        ingress: "true"
      serviceAccountName: ingress-nginx
      terminationGracePeriodSeconds: 300
      volumes:
      - name: webhook-cert
        secret:
          secretName: ingress-nginx-admission
---
apiVersion: batch/v1
kind: Job
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx-admission-create
  namespace: ingress-nginx
spec:
  template:
    metadata:
      labels:
        app.kubernetes.io/component: admission-webhook
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
        app.kubernetes.io/version: 1.7.1
      name: ingress-nginx-admission-create
    spec:
      containers:
      - args:
        - create
        - --host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc
        - --namespace=$(POD_NAMESPACE)
        - --secret-name=ingress-nginx-admission
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        image: registry.cn-beijing.aliyuncs.com/dotbalo/kube-webhook-certgen:v20230312
        imagePullPolicy: IfNotPresent
        name: create
        securityContext:
          allowPrivilegeEscalation: false
      nodeSelector:
        kubernetes.io/os: linux
      restartPolicy: OnFailure
      securityContext:
        fsGroup: 2000
        runAsNonRoot: true
        runAsUser: 2000
      serviceAccountName: ingress-nginx-admission
---
apiVersion: batch/v1
kind: Job
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx-admission-patch
  namespace: ingress-nginx
spec:
  template:
    metadata:
      labels:
        app.kubernetes.io/component: admission-webhook
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
        app.kubernetes.io/version: 1.7.1
      name: ingress-nginx-admission-patch
    spec:
      containers:
      - args:
        - patch
        - --webhook-name=ingress-nginx-admission
        - --namespace=$(POD_NAMESPACE)
        - --patch-mutating=false
        - --secret-name=ingress-nginx-admission
        - --patch-failure-policy=Fail
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        image: registry.cn-beijing.aliyuncs.com/dotbalo/kube-webhook-certgen:v20230312
        imagePullPolicy: IfNotPresent
        name: patch
        securityContext:
          allowPrivilegeEscalation: false
      nodeSelector:
        kubernetes.io/os: linux
      restartPolicy: OnFailure
      securityContext:
        fsGroup: 2000
        runAsNonRoot: true
        runAsUser: 2000
      serviceAccountName: ingress-nginx-admission
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: nginx
spec:
  controller: k8s.io/ingress-nginx
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.7.1
  name: ingress-nginx-admission
webhooks:
- admissionReviewVersions:
  - v1
  clientConfig:
    service:
      name: ingress-nginx-controller-admission
      namespace: ingress-nginx
      path: /networking/v1/ingresses
  failurePolicy: Fail
  matchPolicy: Equivalent
  name: validate.nginx.ingress.kubernetes.io
  rules:
  - apiGroups:
    - networking.k8s.io
    apiVersions:
    - v1
    operations:
    - CREATE
    - UPDATE
    resources:
    - ingresses
  sideEffects: None

image-20240522224945293

配置Ingress

# cat ruoyi-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  creationTimestamp: null
  name: ruoyi-ingress
  annotations:
spec:
  ingressClassName: nginx # 指定Ingress Class的名字,确保与您的Ingress控制器匹配
  rules:
  - host: ruoyi.zzb.com
    http:
      paths:
      - path: /
        pathType: ImplementationSpecific
        backend:
          service:
            name: ruoyi-frontend
            port:
              number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  creationTimestamp: null
  name: ruoyi-ingress-rewrite
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2 #配置ingress重写
spec:
  ingressClassName: nginx
  rules:
  - host: ruoyi.zzb.com
    http:
      paths:
      - path: '/prod-api(/|$)(.*)' #使用正则表达式,增加它的匹配项能访问到后端/prod-api
        pathType: ImplementationSpecific
        backend:
          service:
            name: ruoyi-backend
            port:
              number: 8080

image-20240522230948525

2.Gitlab代码

项目代码地址:https://gitee.com/y_project/RuoYi-Vue

导入项目代码

image-20240522225330297

image-20240522225357220

修改后端配置

RuoYi-Vue/ ruoyi-admin/src/main/resources/application-druid.yml

image-20240522232029407

RuoYi-Vue/ ruoyi-admin/src/main/resources/application.yml

image-20240522232158622

image-20240522232258674

RuoYi-Vue/ ruoyi-admin/src/main/resources/logback.xml

修改日志输出编码格式,使用UTF-8。在该文件下的每一个<pattern>${log.pattern}</pattern>下添加<charset>UTF-8</charset>

image-20240523001727407

前端Jenkinsfile

新增Jenkinsfile-frontend

image-20240522225519728

pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes-study'
      slaveConnectTimeout 1200
      workspaceVolume hostPathWorkspaceVolume(hostPath: "/opt/workspace", readOnly: false)
      yaml '''
apiVersion: v1
kind: Pod
spec:
  hostAliases:
    - ip: "10.0.0.105"
      hostnames:
      - "gitlab.zzb.com"
  containers:
    - name: jnlp
      image: '10.0.0.138:5000/kubernetes/inbound-agent:latest-jdk17'
      args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
      imagePullPolicy: IfNotPresent
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "localtime"
          readOnly: false

    - name: "build"
      env:
        - name: "LANGUAGE"
          value: "en_US:en"
        - name: "LC_ALL"
          value: "en_US.UTF-8"
        - name: "LANG"
          value: "en_US.UTF-8"
      image: "10.0.0.138:5000/kubernetes/node:lts-alpine3.19"
      imagePullPolicy: "IfNotPresent"
      command:
        - "cat"
      tty: true
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "localtime"
        - mountPath: "/root/.m2/"
          name: "cachedir"
          readOnly: false

    - name: "kubectl"
      env:
        - name: "LANGUAGE"
          value: "en_US:en"
        - name: "LC_ALL"
          value: "en_US.UTF-8"
        - name: "LANG"
          value: "en_US.UTF-8"
      image: "10.0.0.138:5000/kubernetes/kubectl:self-1.17"
      imagePullPolicy: "IfNotPresent"
      command:
        - "cat"
      tty: true
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "localtime"
          readOnly: false
 
    - name: "docker"
      env:
        - name: "LANGUAGE"
          value: "en_US:en"
        - name: "LC_ALL"
          value: "en_US.UTF-8"
        - name: "LANG"
          value: "en_US.UTF-8"
      image: "10.0.0.138:5000/kubernetes/docker:19.03.9-git"
      imagePullPolicy: "IfNotPresent"
      command:
        - "cat"
      tty: true
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "localtime"
          readOnly: false
        - mountPath: "/var/run/docker.sock"
          name: "dockersock"
          readOnly: false
  restartPolicy: "Never"
  nodeSelector:
    build: "true"
  securityContext: {}
  volumes:
    - hostPath:
        path: "/var/run/docker.sock"
      name: "dockersock"
    - hostPath:
        path: "/usr/share/zoneinfo/Asia/Shanghai"
      name: "localtime"
    - name: "cachedir"
      hostPath:
        path: "/opt/m2"
'''
    }
}
  stages {
    stage('Pulling Code') {
      parallel {
        stage('Pulling Code by Jenkins') {
          when {
            expression {
              env.gitlabBranch == null
            }

          }
          steps {
            git(changelog: true, poll: true, url: '[email protected]:RuoYi-Vue.git', branch: "${BRANCH}", credentialsId: 'Jenkins-key')
            script {
              COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
              TAG = BUILD_TAG + '-' + COMMIT_ID
              println "Current branch is ${BRANCH}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"
              
            }

          }
        }

        stage('Pulling Code by trigger') {
          when {
            expression {
              env.gitlabBranch != null
            }

          }
          steps {
            git(url: '[email protected]:RuoYi-Vue.git', branch: env.gitlabBranch, changelog: true, poll: true, credentialsId: 'Jenkins-key')
            script {
              COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
              TAG = BUILD_TAG + '-' + COMMIT_ID
              println "Current branch is ${env.gitlabBranch}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"
             
            }

          }
        }

      }
    }

    stage('Building') {
      steps {
        container(name: 'build') {
          dir('ruoyi-ui') {  
            sh """   
                npm config set registry https://registry.npmmirror.com
                npm install
                npm run build:prod
             """  
                    }  
                }  
            }  
        }  

    stage('Docker build for creating image') {
      environment {
        HARBOR_USER = credentials('HARBOR_ACCOUNT')
    }
      steps {
        container(name: 'docker') {
          sh """
          echo ${HARBOR_USER_USR} ${HARBOR_USER_PSW} ${TAG}
          docker build -t ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -f Dockerfile-frontend .
          docker login -u ${HARBOR_USER_USR} -p ${HARBOR_USER_PSW} ${HARBOR_ADDRESS}
          docker push ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG}
          """
        }
      }
    }

    stage('Deploying to K8s') {
      environment {
        MY_KUBECONFIG = credentials('Kubernetes-v1.23.17')
    }
      steps {
        container(name: 'kubectl'){
           sh """
           /usr/local/bin/kubectl --kubeconfig $MY_KUBECONFIG set image deploy -l app=${IMAGE_NAME} ${IMAGE_NAME}=${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -n $NAMESPACE
           """
        }
      }
    }

  }
  environment {
    COMMIT_ID = ""
    HARBOR_ADDRESS = "10.0.0.138:5000"
    REGISTRY_DIR = "kubernetes"
    IMAGE_NAME = "ruoyi-frontend"
    NAMESPACE = "default"
    TAG = ""
  }
  parameters {
    gitParameter(branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: 'Branch for build and deploy', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH')
  }
}

前端Dockerfile

Dockerfile-frontend

image-20240523001115937

FROM 10.0.0.138:5000/library/nginx:latest
COPY ruoyi-ui/dist/ /usr/share/nginx/html/
COPY ruoyi.zzb.com.conf /etc/nginx/conf.d/default.conf 

ruoyi.zzb.com.conf

image-20240522225708722

server {
        listen       80;
        server_name  ruoyi.zzb.com;

       location / {
          # 静态文件地址
            root /usr/share/nginx/html/;
            index  index.html index.htm index;
            try_files $uri $uri/ /index.html;
        }
}

后端Jenkinsfile

Jenkinsfile-backend

image-20240522225738329

pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes-study'
      slaveConnectTimeout 1200
      workspaceVolume hostPathWorkspaceVolume(hostPath: "/opt/workspace", readOnly: false)
      yaml '''
apiVersion: v1
kind: Pod
spec:
  hostAliases:
    - ip: "10.0.0.105"
      hostnames:
      - "gitlab.zzb.com"
  containers:
    - name: jnlp
      image: '10.0.0.138:5000/kubernetes/inbound-agent:latest-jdk17'
      args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
      imagePullPolicy: IfNotPresent
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "localtime"
          readOnly: false

    - name: "build"
      env:
        - name: "LANGUAGE"
          value: "en_US:en"
        - name: "LC_ALL"
          value: "en_US.UTF-8"
        - name: "LANG"
          value: "en_US.UTF-8"
      image: "10.0.0.138:5000/kubernetes/aliyun-maven:3.5.3"
      imagePullPolicy: "IfNotPresent"
      command:
        - "cat"
      tty: true
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "localtime"
        - mountPath: "/root/.m2/"
          name: "cachedir"
          readOnly: false

    - name: "kubectl"
      env:
        - name: "LANGUAGE"
          value: "en_US:en"
        - name: "LC_ALL"
          value: "en_US.UTF-8"
        - name: "LANG"
          value: "en_US.UTF-8"
      image: "10.0.0.138:5000/kubernetes/kubectl:self-1.17"
      imagePullPolicy: "IfNotPresent"
      command:
        - "cat"
      tty: true
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "localtime"
          readOnly: false
 
    - name: "docker"
      env:
        - name: "LANGUAGE"
          value: "en_US:en"
        - name: "LC_ALL"
          value: "en_US.UTF-8"
        - name: "LANG"
          value: "en_US.UTF-8"
      image: "10.0.0.138:5000/kubernetes/docker:19.03.9-git"
      imagePullPolicy: "IfNotPresent"
      command:
        - "cat"
      tty: true
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "localtime"
          readOnly: false
        - mountPath: "/var/run/docker.sock"
          name: "dockersock"
          readOnly: false
  restartPolicy: "Never"
  nodeSelector:
    build: "true"
  securityContext: {}
  volumes:
    - hostPath:
        path: "/var/run/docker.sock"
      name: "dockersock"
    - hostPath:
        path: "/usr/share/zoneinfo/Asia/Shanghai"
      name: "localtime"
    - name: "cachedir"
      hostPath:
        path: "/opt/m2"
'''
    }
}
  stages {
    stage('Pulling Code') {
      parallel {
        stage('Pulling Code by Jenkins') {
          when {
            expression {
              env.gitlabBranch == null
            }

          }
          steps {
            git(changelog: true, poll: true, url: '[email protected]:root/RuoYi-Vue.git', branch: "${BRANCH}", credentialsId: 'Jenkins-key')
            script {
              COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
              TAG = BUILD_TAG + '-' + COMMIT_ID
              println "Current branch is ${BRANCH}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"
              
            }

          }
        }

        stage('Pulling Code by trigger') {
          when {
            expression {
              env.gitlabBranch != null
            }

          }
          steps {
            git(url: '[email protected]:root/RuoYi-Vue.git', branch: env.gitlabBranch, changelog: true, poll: true, credentialsId: 'Jenkins-key')
            script {
              COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
              TAG = BUILD_TAG + '-' + COMMIT_ID
              println "Current branch is ${env.gitlabBranch}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"
            }

          }
        }

      }
    }

    stage('Building') {
      steps {
        container(name: 'build') {
            sh """ 
              mvn clean package -DskipTests
            """
        }
      }
    }

    stage('Docker build for creating image') {
      environment {
        HARBOR_USER = credentials('HARBOR_ACCOUNT')
    }
      steps {
        container(name: 'docker') {
          sh """
          echo ${HARBOR_USER_USR} ${HARBOR_USER_PSW} ${TAG}
          docker build -t ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -f Dockerfile-backend .
          docker login -u ${HARBOR_USER_USR} -p ${HARBOR_USER_PSW} ${HARBOR_ADDRESS}
          docker push ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG}
          """
        }
      }
    }

    stage('Deploying to K8s') {
      environment {
        MY_KUBECONFIG = credentials('Kubernetes-v1.23.17')
    }
      steps {
        container(name: 'kubectl'){
           sh """
           /usr/local/bin/kubectl --kubeconfig $MY_KUBECONFIG set image deploy -l app=${IMAGE_NAME} ${IMAGE_NAME}=${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -n $NAMESPACE
           """
        }
      }
    }

  }
  environment {
    COMMIT_ID = ""
    HARBOR_ADDRESS = "10.0.0.138:5000"
    REGISTRY_DIR = "kubernetes"
    IMAGE_NAME = "ruoyi-backend"
    NAMESPACE = "default"
    TAG = ""
  }
  parameters {
    gitParameter(branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: 'Branch for build and deploy', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH')
  }
}

后端Dockerfile

Dockerfile-backend

image-20240522225922556

FROM 10.0.0.138:5000/library/jre:8u211-data
COPY ruoyi-admin/target/ruoyi-admin.jar .
CMD java -jar ruoyi-admin.jar

由于后端使用jre镜像,默认用户是tomcat的普通用户,无法将日志写入到挂载的宿主机目录上,所以需要增加权限。

#在deployment部署的节点上。
useradd tomcat -u 1001 #可进入jre镜像内使用whoami和id命令查看用户和id号,随后在宿主机上创建相同用户并指定uid号(要和容器内相同!)
chown tomcat:tomcat /var/log/ruoyi  #设置所属人和所属组
chmod 755 -R /var/log/ruoyi #设置目录权限

3.Jenkins

创建流水线(前端同理)

image-20240522233352763

image-20240522233308952

image-20240522233530782

image-20240522234147511

运行流水线

第一次不会有Build with Parameters,需要先构建一次。(立马点取消也行)前端后端同理

image-20240522233722987

image-20240523001318856

4.访问测试

主机添加hosts(ingress-controller)后进行访问测试

image-20240523001923026

更改头像后可在在nfs服务器上看到同步信息

image-20240523002306564

image-20240523002902323

5.扩展(如果使用jre环境)

容器内目录和宿主机目录存在挂载关系时,jre环境内默认为普通用户不够权限对目录进行写入操作,需要对宿主机目录进行修改权限操作(创建与容器相同uid以及用户名的账户,修改权限755和目录所有人、所属组)

标签:CI,name,kubernetes,app,分离,ingress,CD,nginx,io
From: https://www.cnblogs.com/ggbaooo/p/18277907

相关文章

  • 前后端分离项目案例
    docker部署ruoyi介绍:基于SpringBoot、SpringSecurity、Jwt、Vue的前后端分离的后台管理系统前端:Vue后端:JavaSpringBoot(jar包)项目代码地址:https://gitee.com/y_project/RuoYi-Vue部署项目:Ruo-Yi环境说明(需要提前安装docker)主机IP说明frontend0110.0.0.140前端......
  • 【打卡】002 p2 CIFAR10彩色图片识别
    打卡~555我的环境:●语言环境:Python●编译器:jupyternotebook●深度学习环境:Pytorch>-**......
  • DH11温湿度检测模块、lcd1602、HC-08蓝牙构成温湿度数据管理系统
    目录温湿度通过串口上传PC: 温湿度数据管理系统:温湿度通过串口上传PC:sendByte(datas[0]/10+0x30);sendByte(datas[0]%10+0x30);这两行代码用于将一个字节(是DHT11传感器读取的湿度或温度的整数部分)转换为两个ASCII字符,并通过UART串口发送出去。+0x30:在ASCII......
  • 什么是SCI, SCIE, JCR和影响因子(IF)?
    SCI(ScientificCitationIndex):是美国科学信息研究所(ISI)编辑出版的引文索引类刊物,创刊于1964年。分印刷版、光盘版和联机板等载体。印刷版、光盘版从全球数万种期刊中选出3700种科技期刊,涉及基础科学的100余个领域。每年报道60余万篇最新文献,涉及引文900万条。进入SCI这一刊物的......
  • Codeforces Round 894 (Div. 3) A-E cd 894 div3
    A.GiftCarpet每道题都是伸缩代码框有ac代码请不要漏掉--------------------------题解-----------------------------按先行便然后列再变循环设置jud每满足一个条件就让jud++只有jud==相应值的时候才让其++点击查看代码#include<bits/stdc++.h>usingnamespacestd;ch......
  • Financial - 直接合约,基差合约,期差合约
    直接合约,基差合约,期差合约三个的区别如下:1、直接合约:直接合约是指即时交割的合约,买卖双方立即达成交易并履行合约。在外汇市场中,即时交割的外汇交易就是一种直接合约。直接合约没有期限限制,交割时间通常是T+0或稍晚。2、基差合约:基差合约是指衍生品合约与现货标的物之间的价格......
  • Nginx和Tomcat负载均衡,动静分离
    Nginx和Tomcat负载均衡,动静分离目录Nginx和Tomcat负载均衡,动静分离一、反向代理1、反向代理的概念2、反向代理的优势3、Nginx四层反向代理和七层反向代理二、Nginx动静分离实现原理1、动静分离的概念2、动静分离的原理3、Nginx静态处理优势三、Nginx负载均衡调度算法(6种)1、轮询(......
  • Java毕业设计-高校学生课堂考勤打卡系统 Vue前后端分离
    传统信息的管理大部分依赖于管理人员的手工登记与管理,然而,随着近些年信息技术的迅猛发展,让许多比较老套的信息管理模式进行了更新迭代,班级信息因为其管理内容繁杂,管理数量繁多导致手工进行处理不能满足广大用户的需求,因此就应运而生出相应的高校学生课堂考勤系统。......
  • CEOI2024 Segregacija
    CEOI2024Segregacija我们把P看成\(1\),C看成\(0\)。如果只有一行,那么代价肯定是\(0,1\)顺序对数量,换个容易计算的描述,设\(c\)个\(1\),下标和为\(s\),那么代价就是\(s-\frac{c(c+1)}{2}\)。有两行的时候,我们一定可以先通过一些操作交换两行,然后再两行分别按照一行的时......
  • 英文sci投稿后返修意见模板分享-涵盖回复审稿人的要点
    如果你收到了审稿人意见的返修,无论是大修还是小修,这都是一件值得高兴的事情。在目前这个学术投稿量巨大的时代,能进入underreview,这本身不仅仅需要论文实力,还需要很大的好运一.前言本次给大家分享的是关于返修内容的书写要点,我以我自身不久前写的返修意见为例,仅供参考如果......