写在最前

在前面,我们成功使用 KubeSphere 部署了若依管理系统。接下来我们可以对其进行小幅改造,以实现与 SkyWalking 的接入,从而实现可视化链路的监控与分析。

1. 前置条件

2. 官方例子

https://hub.docker.com/r/apache/skywalking-java-agent

使用此镜像作为 Kubernetes 服务的 sidecar

apiVersion: v1
kind: Pod
metadata:
  name: agent-as-sidecar
spec:
  restartPolicy: Never

  volumes:
    - name: skywalking-agent
      emptyDir: { }

  containers:
    - name: agent-container
      image: apache/skywalking-java-agent:8.4.0-alpine
      volumeMounts:
        - name: skywalking-agent
          mountPath: /agent
      command: [ "/bin/sh" ]
      args: [ "-c", "cp -R /skywalking/agent /agent/" ]

    - name: app-container
      image: springio/gs-spring-boot-docker
      volumeMounts:
        - name: skywalking-agent
          mountPath: /skywalking
      env:
        - name: JAVA_TOOL_OPTIONS
          value: "-javaagent:/skywalking/agent/skywalking-agent.jar"

3. 配置变更

3.1 configmap

我在代码中的Dockerfile中已经预先配置好了一个叫JAVA_OPS的环境变量了,configmap配置到这个变量就能动态变更jvm参数。

image-vwrn.png

kind: ConfigMap
apiVersion: v1
metadata:
  name: java-ops
  namespace: ruoyi
  annotations:
    kubesphere.io/creator: admin
data:
  JAVA_OPS: >
    -server
    -Dskywalking.collector.backend_service=skywalking-oap.basic.svc.cluster.local:11800 
    -javaagent:/skywalking/agent/skywalking-agent.jar

3.2 Jenkinsfile

使用 initContainers 运行 skywalking-java-agent,并将其复制到一个 skywalking-agent的emptyDir 中,在运行业务容器时挂载该卷目录以获取 skywalking-agent.jar,通过读取 java-ops 的configmap并将配置放到JAVA_OPS环境变量以动态生成jvm连接参数,并利用 SW_AGENT_NAME 变量设置skywalking模块名称。

pipeline {
  agent {
    node {
      label 'maven'
    }

  }
  stages {
    stage('拉取项目') {
      agent none
      when {
        environment name: 'BUILD_ENV', value: 'UAT_CICD'
      }
      steps {
        container('maven') {
          git(credentialsId: 'gitee-secret', branch: 'tanqidi_v3.6.4', url: 'https://gitee.com/tanqidi/RuoYi-Cloud_1.git', changelog: true, poll: false)
        }

      }
    }

    stage('项目编译') {
      agent none
      when {
        environment name: 'BUILD_ENV', value: 'UAT_CICD'
      }
      steps {
        container('maven') {
          sh 'mvn -Dmaven.test.skip=true clean package'
        }

      }
    }

    stage('镜像构建') {
      agent none
      when {
        environment name: 'BUILD_ENV', value: 'UAT_CICD'
      }
      steps {
        container('maven') {
          script {
            def environment = params.MODULE_NAME
            if (environment == "ruoyi-auth" || environment == "ruoyi-gateway") {
              sh 'cd $MODULE_NAME && sed -i \'s/dev/uat/g\' ./Dockerfile && docker build -t $REGISTRY/$HARBOR_NAMESPACE/$MODULE_NAME:$MODULE_VERSION .'
            } else if (environment == "ruoyi-monitor") {
              sh 'cd ruoyi-visual/$MODULE_NAME && sed -i \'s/dev/uat/g\' ./Dockerfile && docker build -t $REGISTRY/$HARBOR_NAMESPACE/$MODULE_NAME:$MODULE_VERSION .'
            } else {
              sh 'cd ruoyi-modules/$MODULE_NAME && sed -i \'s/dev/uat/g\' ./Dockerfile && docker build -t $REGISTRY/$HARBOR_NAMESPACE/$MODULE_NAME:$MODULE_VERSION .'
            }
          }

        }

      }
    }

    stage('镜像推送') {
      agent none
      when {
        environment value: 'UAT_CICD', name: 'BUILD_ENV'
      }
      steps {
        container('maven') {
          withCredentials([usernamePassword(credentialsId : 'aliyun-harbor-secret' ,passwordVariable : 'name' ,usernameVariable : 'passwd' ,)]) {
            sh 'echo "$name" | docker login $REGISTRY -u "$passwd" --password-stdin'
            sh 'docker push $REGISTRY/$HARBOR_NAMESPACE/$MODULE_NAME:$MODULE_VERSION'
          }

        }

      }
    }

    stage('部署到UAT环境') {
      agent none
      steps {
        container('maven') {
          withCredentials([kubeconfigFile(credentialsId: "$KUBECONFIG_CREDENTIAL_ID", variable: 'KUBECONFIG')]) {
            script {
              sh "echo \'${DEPLOY_YAML}\' > deploy.yaml"
              sh "cat deploy.yaml"
              sh "envsubst < deploy.yaml | kubectl apply -f -"
            }

          }

        }

      }
    }

  }
  environment {
    KUBECONFIG_CREDENTIAL_ID = 'k8s-kubeconfig'
    REGISTRY = 'registry.cn-hangzhou.aliyuncs.com'
    HARBOR_NAMESPACE = 'tanqidi-temp'
    PROJECT_NAMESPACE = 'ruoyi'
    DEPLOY_YAML = '''

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: ${MODULE_NAME}
  name: ${MODULE_NAME}
  namespace: ${PROJECT_NAMESPACE}
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ${MODULE_NAME}
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%
      maxSurge: 25%
  revisionHistoryLimit: 10
  progressDeadlineSeconds: 600

  template:
    metadata:
      labels:
        app: ${MODULE_NAME}
        appType: ruoyi-service
    spec:
      volumes:
        - name: skywalking-agent
          emptyDir: { }
      imagePullSecrets:
        - name: harbor

      initContainers:
        - name: skywalking-java-agent
          image: 'apache/skywalking-java-agent:9.3.0-java21'
          command:
            - /bin/sh
          args:
            - '-c'
            - cp -R /skywalking/agent /agent/
          volumeMounts:
            - name: skywalking-agent
              mountPath: /agent

      containers:
        - name: ${MODULE_NAME}
          imagePullPolicy: Always
          image: $REGISTRY/$HARBOR_NAMESPACE/$MODULE_NAME:$MODULE_VERSION
          volumeMounts:
            - name: skywalking-agent
              mountPath: /skywalking
          ports:
            - containerPort: 8080
              protocol: TCP
          env:
            - name: JAVA_OPS
              valueFrom:
                configMapKeyRef:
                  name: java-ops
                  key: JAVA_OPS
            - name: SW_AGENT_NAME
              value: ${MODULE_NAME}
          resources:
            limits:
              cpu: \'1\'
              memory: 2Gi
            requests:
              cpu: 25m
              memory: 850Mi
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
  name: ${MODULE_NAME}
  namespace: ${PROJECT_NAMESPACE}
  labels:
    app: ${MODULE_NAME}
spec:
  ports:
    - port: 8080
      targetPort: 8080
  selector:
    app: ${MODULE_NAME}
  type: ClusterIP
  sessionAffinity: None

'''
  }
  parameters {
    choice(name: 'MODULE_NAME', choices: '''ruoyi-auth
ruoyi-gateway
ruoyi-system
ruoyi-job
ruoyi-monitor
ruoyi-file''', description: '模块构建')
    choice(name: 'BUILD_ENV', choices: '''UAT_CICD
UAT_CD''', description: '构建环境')
    string(name: 'MODULE_VERSION', defaultValue: 'v0.0Beta', description: '模块版本')
  }
}

image-horn.png