最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

k8s java集群接入istio案例詳解(完整步驟)

 更新時(shí)間:2025年10月30日 11:15:40   作者:完顏振江  
本文給大家介紹k8s java集群接入istio案例,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧

支持灰度v1/v2的版本

? 第一步:完整的操作步驟

?? 目標(biāo)

在不修改應(yīng)用代碼的前提下,將現(xiàn)有 Jenkins 流水線與 Istio 服務(wù)網(wǎng)格集成,支持通過 Ingress Gateway 訪問服務(wù),并實(shí)現(xiàn)基于 v1/v2 的灰度發(fā)布。

?? 操作流程

1. 確保 Istio 已安裝并啟用注入(已完成)

# cat istio-custom.yaml 
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  profile: default
  components:
    pilot:
      enabled: true
      k8s:
        resources:
          requests:
            cpu: 50m
            memory: 64Mi
          limits:
            cpu: 200m
            memory: 256Mi
    ingressGateways:
      - name: istio-ingressgateway
        enabled: true
        k8s:
          resources:
            requests:
              cpu: 50m
              memory: 128Mi
            limits:
              cpu: 200m
              memory: 512Mi
  values:
    global:
      hub: swr.cn-east-3.myhuaweicloud.com/bocheng-test/istio
      tag: 1.27.3
      proxy:
        image: proxyv2
      imagePullPolicy: IfNotPresent
    gateways:
      istio-ingressgateway:
        type: NodePort
        ports:
          - port: 80
            targetPort: 8080
            name: http2
          - port: 443
            targetPort: 8443
            name: https
# 安裝 Istio(使用你的私有鏡像)
istioctl install -f istio-custom.yaml -y
# 啟用命名空間自動(dòng)注入
kubectl label namespace bc-feature-202509-testcce istio-injection=enabled --overwrite

驗(yàn)證:

kubectl get pod -n bc-feature-202509-testcce -l app=bc-gateway
kubectl describe pod <pod-name> -n bc-feature-202509-testcce | grep istio-proxy

應(yīng)看到 istio-proxy 容器和兩個(gè) initContainers。

2. 創(chuàng)建 Istio 入口網(wǎng)關(guān)(只需一次)

創(chuàng)建文件 gateway.yaml

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: global-ingress-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"

應(yīng)用一次即可:

kubectl apply -f gateway.yaml -n istio-system

3. 創(chuàng)建 Istio 配置模板目錄

在項(xiàng)目根目錄下創(chuàng)建 istio/ 目錄,包含兩個(gè)模板文件:

文件:istio/virtualservice.yaml.tpl

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
meta
  name: ${MODULE_LOWERCASE}-vs
  namespace: ${CCE_NAMESPACE}
spec:
  hosts:
  - "${MODULE_LOWERCASE}.example.com"
  gateways:
  - istio-system/global-ingress-gateway
  http:
  - route:
    - destination:
        host: ${MODULE_LOWERCASE}.${CCE_NAMESPACE}.svc.cluster.local
        subset: ${DEPLOY_VERSION}
      weight: 100

文件:istio/destinationrule.yaml.tpl

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
meta
  name: ${MODULE_LOWERCASE}-dr
  namespace: ${CCE_NAMESPACE}
spec:
  host: ${MODULE_LOWERCASE}.${CCE_NAMESPACE}.svc.cluster.local
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2

4. 修改 Jenkinsfile 實(shí)現(xiàn)自動(dòng)部署 Istio 規(guī)則

stage('?? 部署到 CCE') 中,在更新鏡像后添加生成和部署 Istio 配置的步驟。

5. 外部訪問方式

獲取 Ingress Gateway 的 NodePort:

kubectl get svc -n istio-system istio-ingressgateway -o jsonpath='{.spec.ports[0].nodePort}'

訪問示例:

curl -H "Host: bc-gateway.example.com" http://192.168.122.190:31902/actuator/health

? 第二步:完整的適配后 Jenkinsfile

pipeline {
    agent any
    parameters {
        choice(
            name: 'MODULE',
            choices: [
                'bc-gateway',
                'bc-admin',
                'bc-invoice',
                'bc-job',
                'bc-oldBusiness',
                'bc-open',
                'bc-resource',
                'bc-system',
                'bc-third',
                'bc-wallet'
            ],
            description: '選擇要構(gòu)建的模塊'
        )
        string(
            name: 'VERSION',
            defaultValue: '2.2.2',
            description: '構(gòu)建版本號(hào)(必須與 pom.xml 中 <revision> 一致)'
        )
        booleanParam(
            name: 'SKIP_TESTS',
            defaultValue: true,
            description: '是否跳過單元測試'
        )
        booleanParam(
            name: 'CLEAN_M2_CACHE',
            defaultValue: false,
            description: '是否清理 .m2 中 org.dromara 緩存(首次構(gòu)建建議勾選)'
        )
        choice(
            name: 'DEPLOY_VERSION',
            choices: ['v1', 'v2'],
            description: '選擇部署版本(v1 或 v2),用于灰度發(fā)布'
        )
        booleanParam(
            name: 'DEPLOY_TO_CCE',
            defaultValue: false,
            description: '是否部署到華為云 CCE 集群'
        )
        booleanParam(
            name: 'ROLLBACK_LAST_VERSION',
            defaultValue: false,
            description: '是否回滾上一版本(跳過構(gòu)建)'
        )
        booleanParam(
            name: 'WAIT_FOR_ALL_REPLICAS',
            defaultValue: false,
            description: '是否等待所有副本完成滾動(dòng)更新(否則僅等待首個(gè)新 Pod Ready)'
        )
    }
    environment {
        MVN = '/usr/maven/apache-maven-3.9.9/bin/mvn'
        JAVA_HOME = '/usr/java/jdk-17.0.12'
        PATH = "${env.JAVA_HOME}/bin:${env.MVN}:${env.PATH}"
        SWR_REGISTRY = 'swr.cn-east-3.myhuaweicloud.com'
        IMAGE_REPO   = 'dbb-java-micro-test'
        CCE_NAMESPACE   = 'bc-feature-202509-testcce'
        CONTAINER_NAME  = 'container-1'
        KUBECTL_PATH    = '/usr/local/bin/kubectl'
        KUBECONFIG_FILE = '/root/.kube/config'
        PORT_MAPPING_STR = '''
            bc-gateway=8901
            bc-admin=8909
            bc-invoice=8902
            bc-job=8905
            bc-oldBusiness=8903
            bc-open=8910
            bc-resource=8904
            bc-system=8903
            bc-third=8906
            bc-wallet=8908
        '''
        HEALTH_PATH = '/actuator/health'
    }
    stages {
        stage('?? 是否執(zhí)行回滾') {
            when { expression { params.ROLLBACK_LAST_VERSION } }
            steps {
                script {
                    def ns = env.CCE_NAMESPACE
                    def deployName = params.MODULE.toLowerCase()
                    sh """
                        set -e
                        export KUBECONFIG=${env.KUBECONFIG_FILE}
                        ${env.KUBECTL_PATH} rollout undo deployment/${deployName} -n ${ns}
                        echo "? 等待回滾完成..."
                        ${env.KUBECTL_PATH} rollout status deployment/${deployName} -n ${ns} --timeout=120s
                        echo "? 回滾成功"
                    """
                }
            }
        }
        stage('?? 拉取代碼') {
            when { expression { !params.ROLLBACK_LAST_VERSION } }
            steps {
                checkout scm
            }
        }
        stage('?? 準(zhǔn)備工作') {
            when { expression { !params.ROLLBACK_LAST_VERSION } }
            steps {
                script {
                    def portMap = [:]
                    env.PORT_MAPPING_STR.stripIndent().split('\n').each {
                        def pair = it.trim().split('=')
                        if (pair.size() == 2) {
                            portMap[pair[0].trim()] = pair[1].trim()
                        }
                    }
                    def module = params.MODULE
                    def modulePath = module == 'bc-gateway' ? 'bc-gateway' : "bc-modules/${module}"
                    def port = portMap[module]
                    if (!port) error("? 未配置模塊 ${module} 的服務(wù)端口")
                    def timestamp = new Date().format('yyyyMMddHHmmss')
                    def imageTag = "${params.DEPLOY_VERSION}-${timestamp}"
                    def imageName = "${env.SWR_REGISTRY}/${env.IMAGE_REPO}/${module.toLowerCase()}"
                    def fullImageName = "${imageName}:${imageTag}"
                    env.MODULE = module
                    env.MODULE_PATH = modulePath
                    env.SERVICE_JAR = "${modulePath}/target/${module}.jar"
                    env.IMAGE_NAME = imageName
                    env.FULL_IMAGE_NAME = fullImageName
                    env.SERVICE_PORT = port
                }
            }
        }
        stage('?? 清理本地緩存(可選)') {
            when {
                allOf {
                    expression { !params.ROLLBACK_LAST_VERSION }
                    expression { params.CLEAN_M2_CACHE }
                }
            }
            steps {
                script {
                    def dromaraDir = "$HOME/.m2/repository/org/dromara"
                    sh "rm -rf ${dromaraDir} && echo '??? 已清理 .m2 緩存'"
                }
            }
        }
        stage('?? 構(gòu)建模塊') {
            when { expression { !params.ROLLBACK_LAST_VERSION } }
            steps {
                script {
                    def skipTestsFlag = params.SKIP_TESTS ? '-Dmaven.test.skip=true' : ''
                    sh """
                        set -e
                        cd \$WORKSPACE
                        \$MVN clean install \\
                            -pl \${MODULE_PATH} \\
                            -am \\
                            -P test \\
                            -T 4C \\
                            ${skipTestsFlag} \\
                            -Drevision=${params.VERSION} \\
                            -U --no-transfer-progress -B
                        echo "? 構(gòu)建成功"
                    """
                }
            }
        }
        stage('? 測試報(bào)告') {
            when {
                allOf {
                    expression { !params.ROLLBACK_LAST_VERSION }
                    expression { !params.SKIP_TESTS }
                }
            }
            steps {
                junit testResults: "${env.MODULE_PATH}/target/surefire-reports/*.xml"
            }
        }
        stage('?? 構(gòu)建鏡像') {
            when { expression { !params.ROLLBACK_LAST_VERSION } }
            steps {
                script {
                    sh '''
                        set -e
                        CONTEXT_DIR="$WORKSPACE/$MODULE_PATH"
                        JAR_PATH="$CONTEXT_DIR/target/$MODULE.jar"
                        DOCKERFILE="$CONTEXT_DIR/Dockerfile"
                        [ -f "$JAR_PATH" ] || { echo "? JAR 文件不存在"; exit 1; }
                        [ -f "$DOCKERFILE" ] || { echo "? Dockerfile 不存在"; exit 1; }
                        cd "$CONTEXT_DIR" && docker build -t "$FULL_IMAGE_NAME" .
                        echo "? 鏡像構(gòu)建成功: $FULL_IMAGE_NAME"
                    '''
                }
            }
        }
        stage('?? 登錄 SWR') {
            when { expression { !params.ROLLBACK_LAST_VERSION } }
            steps {
                script {
                    withCredentials([usernamePassword(
                        credentialsId: 'cba87dfc-e05d-4d08-b059-5dc83c79e7ec',
                        usernameVariable: 'SWR_USER',
                        passwordVariable: 'SWR_PASS'
                    )]) {
                        sh '''
                            set -e
                            echo "$SWR_PASS" | docker login $SWR_REGISTRY -u "$SWR_USER" --password-stdin
                            echo "? 登錄 SWR 成功"
                        '''
                    }
                }
            }
        }
        stage('?? 推送鏡像') {
            when { expression { !params.ROLLBACK_LAST_VERSION } }
            steps {
                sh '''
                    set -e
                    docker push $FULL_IMAGE_NAME
                    echo "? 鏡像推送成功: $FULL_IMAGE_NAME"
                '''
            }
        }
        stage('?? 部署到 CCE') {
            when {
                allOf {
                    expression { !params.ROLLBACK_LAST_VERSION }
                    expression { params.DEPLOY_TO_CCE }
                }
            }
            steps {
                script {
                    def ns = env.CCE_NAMESPACE
                    def container = env.CONTAINER_NAME
                    def deployName = env.MODULE.toLowerCase()
                    def timeoutSeconds = 300
                    def intervalSeconds = 45
                    def maxAttempts = timeoutSeconds / intervalSeconds
                    // === 1. 更新鏡像 ===
                    sh """
                        set -e
                        export KUBECONFIG=${env.KUBECONFIG_FILE}
                        ${env.KUBECTL_PATH} set image deployment/${deployName} ${container}=$FULL_IMAGE_NAME -n ${ns}
                        echo "? 鏡像已更新為: $FULL_IMAGE_NAME"
                    """
                    // === 2. 注入 version 標(biāo)簽到 Deployment(關(guān)鍵?。?==
                    sh """
                        set -e
                        export KUBECONFIG=${env.KUBECONFIG_FILE}
                        ${env.KUBECTL_PATH} patch deployment ${deployName} -n ${ns} -p='{"spec":{"template":{"metadata":{"labels":{"version":"${params.DEPLOY_VERSION}"}}}}}'
                        echo "? Deployment 已打 version=${params.DEPLOY_VERSION} 標(biāo)簽"
                    """
                    // === 3. 生成并部署 Istio VirtualService 和 DestinationRule ===
                    echo "?? 生成 Istio 配置文件..."
                    sh '''
                        set -e
                        export MODULE_LOWERCASE=''' + deployName + '''
                        export CCE_NAMESPACE=''' + ns + '''
                        export DEPLOY_VERSION=''' + params.DEPLOY_VERSION + '''
                        # 替換模板
                        cat istio/virtualservice.yaml.tpl | envsubst > /tmp/virtualservice.yaml
                        cat istio/destinationrule.yaml.tpl | envsubst > /tmp/destinationrule.yaml
                        # 應(yīng)用配置
                        export KUBECONFIG=/root/.kube/config
                        kubectl apply -f /tmp/virtualservice.yaml -n $CCE_NAMESPACE
                        kubectl apply -f /tmp/destinationrule.yaml -n $CCE_NAMESPACE
                        echo "? Istio 路由規(guī)則已更新"
                    '''
                    // === 4. 等待驗(yàn)證策略 ===
                    if (params.WAIT_FOR_ALL_REPLICAS) {
                        echo "? 等待所有副本完成滾動(dòng)更新..."
                        sh """
                            set -e
                            export KUBECONFIG=${env.KUBECONFIG_FILE}
                            ${env.KUBECTL_PATH} rollout status deployment/${deployName} -n ${ns} --timeout=${timeoutSeconds}s
                            echo "? 所有副本已成功更新"
                        """
                    } else {
                        echo "? 等待首個(gè)新 Pod 進(jìn)入 READY 狀態(tài)..."
                        def success = false
                        for (int i = 0; i < maxAttempts; i++) {
                            try {
                                def output = sh(
                                    script: """
                                        set +e
                                        export KUBECONFIG=${env.KUBECONFIG_FILE}
                                        kubectl get pods \\
                                          -l app=${deployName},version=${params.DEPLOY_VERSION} \\
                                          -n ${ns} \\
                                          --field-selector=status.phase=Running \\
                                          -o custom-columns=NAME:.metadata.name,READY:.status.containerStatuses[0].ready,IMAGE:.spec.containers[0].image \\
                                          --no-headers
                                    """,
                                    returnStdout: true
                                ).trim()
                                if (output && output.contains("${FULL_IMAGE_NAME}") && output.contains("true")) {
                                    echo "?? 新版本 Pod 已就緒"
                                    success = true
                                    break
                                }
                            } catch (Exception e) {
                                echo "?? 檢查異常: ${e}"
                            }
                            sleep(intervalSeconds)
                        }
                        if (!success) {
                            error("? 超時(shí):未能看到新版本 Pod 就緒")
                        }
                        echo "? 部署驗(yàn)證通過"
                    }
                }
            }
        }
        stage('?? 完成通知') {
            steps {
                script {
                    if (params.ROLLBACK_LAST_VERSION) {
                        echo "? 已完成回滾操作"
                    } else {
                        echo "? 構(gòu)建 & 部署成功"
                        if (params.DEPLOY_TO_CCE) {
                            echo "?? 已部署至 CCE: deployment/${env.MODULE.toLowerCase()} in ${env.CCE_NAMESPACE}"
                            echo "?? 鏡像: ${env.FULL_IMAGE_NAME}"
                            echo "?? 提示: 支持基于 v1/v2 的灰度發(fā)布"
                        }
                    }
                }
            }
        }
    }
    post {
        success {
            echo "?【流水線成功】構(gòu)建與部署已完成"
        }
        failure {
            echo "?【流水線失敗】請(qǐng)檢查上述日志"
        }
        always {
            cleanWs()
        }
    }
}

? 總結(jié)

你現(xiàn)在擁有了一個(gè)完全適配 Istio 的 Jenkins 流水線,實(shí)現(xiàn)了:

  • 自動(dòng)注入 Sidecar
  • 動(dòng)態(tài)生成 VirtualServiceDestinationRule
  • 基于 v1/v2 的灰度發(fā)布
  • 外部通過統(tǒng)一入口訪問

外部訪問只需:

curl -H "Host: bc-gateway.example.com" http://<INGRESS_IP>:<NODEPORT>/actuator/health

不要灰度的版本

你可能在某些模塊(如 bc-job、bc-third)上不需要灰度,但仍希望接入 Istio 以獲得 mTLS、可觀測性等能力。這種情況下,我們簡化 DestinationRuleVirtualService 的配置。

? 第一步:完整的操作步驟(非灰度場景)

?? 目標(biāo)

將現(xiàn)有 Jenkins 流水線適配 Istio,支持:

  • Sidecar 自動(dòng)注入
  • 外部通過 Ingress Gateway 訪問服務(wù)
  • 啟用 mTLS 加密通信
  • 不使用 v1/v2 灰度,直接路由到默認(rèn)版本
  • 不修改應(yīng)用代碼

?? 操作流程

1. 確保 Istio 已安裝并啟用注入(已完成)

root@k8s-master:/data/service/ISTIO# cat istio-custom.yaml 
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  profile: default
  components:
    pilot:
      enabled: true
      k8s:
        resources:
          requests:
            cpu: 50m
            memory: 64Mi
          limits:
            cpu: 200m
            memory: 256Mi
    ingressGateways:
      - name: istio-ingressgateway
        enabled: true
        k8s:
          resources:
            requests:
              cpu: 50m
              memory: 128Mi
            limits:
              cpu: 200m
              memory: 512Mi
  values:
    global:
      hub: swr.cn-east-3.myhuaweicloud.com/bocheng-test/istio
      tag: 1.27.3
      proxy:
        image: proxyv2
      imagePullPolicy: IfNotPresent
    gateways:
      istio-ingressgateway:
        type: NodePort
        ports:
          - port: 80
            targetPort: 8080
            name: http2
          - port: 443
            targetPort: 8443
            name: https
# 安裝 Istio
istioctl install -f istio-custom.yaml -y
# 啟用命名空間自動(dòng)注入
kubectl label namespace bc-feature-202509-testcce istio-injection=enabled --overwrite

驗(yàn)證:

kubectl get pod -n bc-feature-202509-testcce -l app=bc-gateway
kubectl describe pod <pod-name> -n bc-feature-202509-testcce | grep istio-proxy

應(yīng)看到 istio-proxy 容器。

2. 創(chuàng)建 Istio 入口網(wǎng)關(guān)(只需一次)

創(chuàng)建文件 gateway.yaml

apiVersion: networking.istio.io/v1beta1
kind: Gateway
meta
  name: global-ingress-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"

應(yīng)用一次即可:

kubectl apply -f gateway.yaml -n istio-system

3. 創(chuàng)建 Istio 配置模板目錄(非灰度版)

在項(xiàng)目根目錄下創(chuàng)建 istio/ 目錄,包含兩個(gè)模板文件:

文件:istio/virtualservice-nogray.yaml.tpl

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
meta
  name: ${MODULE_LOWERCASE}-vs
  namespace: ${CCE_NAMESPACE}
spec:
  hosts:
  - "${MODULE_LOWERCASE}.example.com"
  gateways:
  - istio-system/global-ingress-gateway
  http:
  - route:
    - destination:
        host: ${MODULE_LOWERCASE}.${CCE_NAMESPACE}.svc.cluster.local
        port:
          number: ${SERVICE_PORT}
      weight: 100

文件:istio/destinationrule-nogray.yaml.tpl

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
meta
  name: ${MODULE_LOWERCASE}-dr
  namespace: ${CCE_NAMESPACE}
spec:
  host: ${MODULE_LOWERCASE}.${CCE_NAMESPACE}.svc.cluster.local
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
    loadBalancer:
      simple: ROUND_ROBIN

?? 注意:沒有 subsets,直接路由到服務(wù)后端。

4. 修改 Jenkinsfile 實(shí)現(xiàn)自動(dòng)部署 Istio 規(guī)則

stage('?? 部署到 CCE') 中,在更新鏡像后添加生成和部署 Istio 配置的步驟。

5. 外部訪問方式

獲取 Ingress Gateway 的 NodePort:

kubectl get svc -n istio-system istio-ingressgateway -o jsonpath='{.spec.ports[0].nodePort}'

訪問示例:

curl -H "Host: bc-gateway.example.com" http://192.168.122.190:31902/actuator/health

? 第二步:完整的適配后 Jenkinsfile(非灰度版)

pipeline {
    agent any
    parameters {
        choice(
            name: 'MODULE',
            choices: [
                'bc-gateway',
                'bc-admin',
                'bc-invoice',
                'bc-job',
                'bc-oldBusiness',
                'bc-open',
                'bc-resource',
                'bc-system',
                'bc-third',
                'bc-wallet'
            ],
            description: '選擇要構(gòu)建的模塊'
        )
        string(
            name: 'VERSION',
            defaultValue: '2.2.2',
            description: '構(gòu)建版本號(hào)(必須與 pom.xml 中 <revision> 一致)'
        )
        booleanParam(
            name: 'SKIP_TESTS',
            defaultValue: true,
            description: '是否跳過單元測試'
        )
        booleanParam(
            name: 'CLEAN_M2_CACHE',
            defaultValue: false,
            description: '是否清理 .m2 中 org.dromara 緩存(首次構(gòu)建建議勾選)'
        )
        booleanParam(
            name: 'DEPLOY_TO_CCE',
            defaultValue: false,
            description: '是否部署到華為云 CCE 集群'
        )
        booleanParam(
            name: 'ROLLBACK_LAST_VERSION',
            defaultValue: false,
            description: '是否回滾上一版本(跳過構(gòu)建)'
        )
        booleanParam(
            name: 'WAIT_FOR_ALL_REPLICAS',
            defaultValue: false,
            description: '是否等待所有副本完成滾動(dòng)更新(否則僅等待首個(gè)新 Pod Ready)'
        )
    }
    environment {
        MVN = '/usr/maven/apache-maven-3.9.9/bin/mvn'
        JAVA_HOME = '/usr/java/jdk-17.0.12'
        PATH = "${env.JAVA_HOME}/bin:${env.MVN}:${env.PATH}"
        SWR_REGISTRY = 'swr.cn-east-3.myhuaweicloud.com'
        IMAGE_REPO   = 'dbb-java-micro-test'
        CCE_NAMESPACE   = 'bc-feature-202509-testcce'
        CONTAINER_NAME  = 'container-1'
        KUBECTL_PATH    = '/usr/local/bin/kubectl'
        KUBECONFIG_FILE = '/root/.kube/config'
        PORT_MAPPING_STR = '''
            bc-gateway=8901
            bc-admin=8909
            bc-invoice=8902
            bc-job=8905
            bc-oldBusiness=8903
            bc-open=8910
            bc-resource=8904
            bc-system=8903
            bc-third=8906
            bc-wallet=8908
        '''
        HEALTH_PATH = '/actuator/health'
    }
    stages {
        stage('?? 是否執(zhí)行回滾') {
            when { expression { params.ROLLBACK_LAST_VERSION } }
            steps {
                script {
                    def ns = env.CCE_NAMESPACE
                    def deployName = params.MODULE.toLowerCase()
                    sh """
                        set -e
                        export KUBECONFIG=${env.KUBECONFIG_FILE}
                        ${env.KUBECTL_PATH} rollout undo deployment/${deployName} -n ${ns}
                        echo "? 等待回滾完成..."
                        ${env.KUBECTL_PATH} rollout status deployment/${deployName} -n ${ns} --timeout=120s
                        echo "? 回滾成功"
                    """
                }
            }
        }
        stage('?? 拉取代碼') {
            when { expression { !params.ROLLBACK_LAST_VERSION } }
            steps {
                checkout scm
            }
        }
        stage('?? 準(zhǔn)備工作') {
            when { expression { !params.ROLLBACK_LAST_VERSION } }
            steps {
                script {
                    def portMap = [:]
                    env.PORT_MAPPING_STR.stripIndent().split('\n').each {
                        def pair = it.trim().split('=')
                        if (pair.size() == 2) {
                            portMap[pair[0].trim()] = pair[1].trim()
                        }
                    }
                    def module = params.MODULE
                    def modulePath = module == 'bc-gateway' ? 'bc-gateway' : "bc-modules/${module}"
                    def port = portMap[module]
                    if (!port) error("? 未配置模塊 ${module} 的服務(wù)端口")
                    def timestamp = new Date().format('yyyyMMddHHmmss')
                    def imageTag = "latest-${timestamp}"
                    def imageName = "${env.SWR_REGISTRY}/${env.IMAGE_REPO}/${module.toLowerCase()}"
                    def fullImageName = "${imageName}:${imageTag}"
                    env.MODULE = module
                    env.MODULE_PATH = modulePath
                    env.SERVICE_JAR = "${modulePath}/target/${module}.jar"
                    env.IMAGE_NAME = imageName
                    env.FULL_IMAGE_NAME = fullImageName
                    env.SERVICE_PORT = port
                }
            }
        }
        stage('?? 清理本地緩存(可選)') {
            when {
                allOf {
                    expression { !params.ROLLBACK_LAST_VERSION }
                    expression { params.CLEAN_M2_CACHE }
                }
            }
            steps {
                script {
                    def dromaraDir = "$HOME/.m2/repository/org/dromara"
                    sh "rm -rf ${dromaraDir} && echo '??? 已清理 .m2 緩存'"
                }
            }
        }
        stage('?? 構(gòu)建模塊') {
            when { expression { !params.ROLLBACK_LAST_VERSION } }
            steps {
                script {
                    def skipTestsFlag = params.SKIP_TESTS ? '-Dmaven.test.skip=true' : ''
                    sh """
                        set -e
                        cd \$WORKSPACE
                        \$MVN clean install \\
                            -pl \${MODULE_PATH} \\
                            -am \\
                            -P test \\
                            -T 4C \\
                            ${skipTestsFlag} \\
                            -Drevision=${params.VERSION} \\
                            -U --no-transfer-progress -B
                        echo "? 構(gòu)建成功"
                    """
                }
            }
        }
        stage('? 測試報(bào)告') {
            when {
                allOf {
                    expression { !params.ROLLBACK_LAST_VERSION }
                    expression { !params.SKIP_TESTS }
                }
            }
            steps {
                junit testResults: "${env.MODULE_PATH}/target/surefire-reports/*.xml"
            }
        }
        stage('?? 構(gòu)建鏡像') {
            when { expression { !params.ROLLBACK_LAST_VERSION } }
            steps {
                script {
                    sh '''
                        set -e
                        CONTEXT_DIR="$WORKSPACE/$MODULE_PATH"
                        JAR_PATH="$CONTEXT_DIR/target/$MODULE.jar"
                        DOCKERFILE="$CONTEXT_DIR/Dockerfile"
                        [ -f "$JAR_PATH" ] || { echo "? JAR 文件不存在"; exit 1; }
                        [ -f "$DOCKERFILE" ] || { echo "? Dockerfile 不存在"; exit 1; }
                        cd "$CONTEXT_DIR" && docker build -t "$FULL_IMAGE_NAME" .
                        echo "? 鏡像構(gòu)建成功: $FULL_IMAGE_NAME"
                    '''
                }
            }
        }
        stage('?? 登錄 SWR') {
            when { expression { !params.ROLLBACK_LAST_VERSION } }
            steps {
                script {
                    withCredentials([usernamePassword(
                        credentialsId: 'cba87dfc-e05d-4d08-b059-5dc83c79e7ec',
                        usernameVariable: 'SWR_USER',
                        passwordVariable: 'SWR_PASS'
                    )]) {
                        sh '''
                            set -e
                            echo "$SWR_PASS" | docker login $SWR_REGISTRY -u "$SWR_USER" --password-stdin
                            echo "? 登錄 SWR 成功"
                        '''
                    }
                }
            }
        }
        stage('?? 推送鏡像') {
            when { expression { !params.ROLLBACK_LAST_VERSION } }
            steps {
                sh '''
                    set -e
                    docker push $FULL_IMAGE_NAME
                    echo "? 鏡像推送成功: $FULL_IMAGE_NAME"
                '''
            }
        }
        stage('?? 部署到 CCE') {
            when {
                allOf {
                    expression { !params.ROLLBACK_LAST_VERSION }
                    expression { params.DEPLOY_TO_CCE }
                }
            }
            steps {
                script {
                    def ns = env.CCE_NAMESPACE
                    def container = env.CONTAINER_NAME
                    def deployName = env.MODULE.toLowerCase()
                    def timeoutSeconds = 300
                    def intervalSeconds = 45
                    def maxAttempts = timeoutSeconds / intervalSeconds
                    // === 1. 更新鏡像 ===
                    sh """
                        set -e
                        export KUBECONFIG=${env.KUBECONFIG_FILE}
                        ${env.KUBECTL_PATH} set image deployment/${deployName} ${container}=$FULL_IMAGE_NAME -n ${ns}
                        echo "? 鏡像已更新為: $FULL_IMAGE_NAME"
                    """
                    // === 2. 生成并部署 Istio VirtualService 和 DestinationRule(非灰度版)===
                    echo "?? 生成 Istio 配置文件..."
                    sh '''
                        set -e
                        export MODULE_LOWERCASE=''' + deployName + '''
                        export CCE_NAMESPACE=''' + ns + '''
                        export SERVICE_PORT=''' + env.SERVICE_PORT + '''
                        # 替換模板
                        cat istio/virtualservice-nogray.yaml.tpl | envsubst > /tmp/virtualservice.yaml
                        cat istio/destinationrule-nogray.yaml.tpl | envsubst > /tmp/destinationrule.yaml
                        # 應(yīng)用配置
                        export KUBECONFIG=/root/.kube/config
                        kubectl apply -f /tmp/virtualservice.yaml -n $CCE_NAMESPACE
                        kubectl apply -f /tmp/destinationrule.yaml -n $CCE_NAMESPACE
                        echo "? Istio 路由規(guī)則已更新(非灰度模式)"
                    '''
                    // === 3. 等待驗(yàn)證策略 ===
                    if (params.WAIT_FOR_ALL_REPLICAS) {
                        echo "? 等待所有副本完成滾動(dòng)更新..."
                        sh """
                            set -e
                            export KUBECONFIG=${env.KUBECONFIG_FILE}
                            ${env.KUBECTL_PATH} rollout status deployment/${deployName} -n ${ns} --timeout=${timeoutSeconds}s
                            echo "? 所有副本已成功更新"
                        """
                    } else {
                        echo "? 等待首個(gè)新 Pod 進(jìn)入 READY 狀態(tài)..."
                        def success = false
                        for (int i = 0; i < maxAttempts; i++) {
                            try {
                                def output = sh(
                                    script: """
                                        set +e
                                        export KUBECONFIG=${env.KUBECONFIG_FILE}
                                        kubectl get pods \\
                                          -l app=${deployName} \\
                                          -n ${ns} \\
                                          --field-selector=status.phase=Running \\
                                          -o custom-columns=NAME:.metadata.name,READY:.status.containerStatuses[0].ready,IMAGE:.spec.containers[0].image \\
                                          --no-headers
                                    """,
                                    returnStdout: true
                                ).trim()
                                if (output && output.contains("${FULL_IMAGE_NAME}") && output.contains("true")) {
                                    echo "?? 新版本 Pod 已就緒"
                                    success = true
                                    break
                                }
                            } catch (Exception e) {
                                echo "?? 檢查異常: ${e}"
                            }
                            sleep(intervalSeconds)
                        }
                        if (!success) {
                            error("? 超時(shí):未能看到新版本 Pod 就緒")
                        }
                        echo "? 部署驗(yàn)證通過"
                    }
                }
            }
        }
        stage('?? 完成通知') {
            steps {
                script {
                    if (params.ROLLBACK_LAST_VERSION) {
                        echo "? 已完成回滾操作"
                    } else {
                        echo "? 構(gòu)建 & 部署成功"
                        if (params.DEPLOY_TO_CCE) {
                            echo "?? 已部署至 CCE: deployment/${env.MODULE.toLowerCase()} in ${env.CCE_NAMESPACE}"
                            echo "?? 鏡像: ${env.FULL_IMAGE_NAME}"
                            echo "?? 提示: 非灰度模式,流量直接路由到服務(wù)后端"
                        }
                    }
                }
            }
        }
    }
    post {
        success {
            echo "?【流水線成功】構(gòu)建與部署已完成"
        }
        failure {
            echo "?【流水線失敗】請(qǐng)檢查上述日志"
        }
        always {
            cleanWs()
        }
    }
}

? 總結(jié)

你現(xiàn)在擁有了一個(gè)完全適配 Istio 的非灰度發(fā)布流水線,實(shí)現(xiàn)了:

  • 自動(dòng)注入 Sidecar
  • 啟用 mTLS 加密通信
  • 外部通過統(tǒng)一入口訪問
  • 無需 v1/v2 標(biāo)簽,直接路由到服務(wù)

適用于 bc-job、bc-third 等無需灰度的模塊。

將 Istio Ingress Gateway 的NodePort模式*。*

? 第一步:istio-custom.yaml(NodePort 模式)

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  profile: default
  components:
    pilot:
      enabled: true
      k8s:
        resources:
          requests:
            cpu: 50m
            memory: 64Mi
          limits:
            cpu: 200m
            memory: 256Mi
    ingressGateways:
      - name: istio-ingressgateway
        enabled: true
        k8s:
          resources:
            requests:
              cpu: 50m
              memory: 128Mi
            limits:
              cpu: 200m
              memory: 512Mi
  values:
    global:
      hub: swr.cn-east-3.myhuaweicloud.com/bocheng-test/istio
      tag: 1.27.3
      proxy:
        image: proxyv2
      imagePullPolicy: IfNotPresent
    gateways:
      istio-ingressgateway:
        # 使用 NodePort 模式
        type: NodePort
        ports:
          - port: 80
            targetPort: 8080
            name: http2
          - port: 443
            targetPort: 8443
            name: https

?? 提示:NodePort 端口會(huì)自動(dòng)分配在 30000-32767 范圍內(nèi)。

? 第二步:安裝 Istio

# 卸載舊版本(如有)
istioctl uninstall -y --purge
# 安裝 NodePort 配置
istioctl install -f istio-custom.yaml -y

? 第三步:驗(yàn)證 NodePort 是否創(chuàng)建成功

kubectl get svc -n istio-system istio-ingressgateway

輸出示例:

NAME                   TYPE       CLUSTER-IP     PORT(S)                      AGE
istio-ingressgateway   NodePort   10.96.123.45   80:31234/TCP,443:30987/TCP   2m
  • TYPENodePort
  • 記錄 31234(HTTP)和 30987(HTTPS),用于外部訪問

? 第四步:創(chuàng)建 Gateway(入口網(wǎng)關(guān)規(guī)則)

# gateway.yaml
apiVersion: networking.istio.io/v1beta1
kind: Gateway
meta
  name: global-ingress-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
kubectl apply -f gateway.yaml

? 第五步:創(chuàng)建 VirtualService(路由規(guī)則)

# virtualservice-nogray.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
meta
  name: bc-gateway-vs
  namespace: bc-feature-202509-testcce
spec:
  hosts:
  - "bc-gateway.example.com"
  gateways:
  - istio-system/global-ingress-gateway
  http:
  - route:
    - destination:
        host: bc-gateway.bc-feature-202509-testcce.svc.cluster.local
        port:
          number: 8901
      weight: 100
kubectl apply -f virtualservice-nogray.yaml

? 第六步:外部訪問方式(使用任意節(jié)點(diǎn) IP + NodePort)

獲取 NodePort:

kubectl get svc -n istio-system istio-ingressgateway -o jsonpath='{.spec.ports[0].nodePort}'
# 輸出:31234

使用任意 worker 節(jié)點(diǎn)的 IP 訪問:

export NODE_IP=192.168.122.190
export NODE_PORT=31234
curl -H "Host: bc-gateway.example.com" http://$NODE_IP:$NODE_PORT/actuator/health

? 成功!

? 總結(jié):NodePort 模式特點(diǎn)

特性說明
類型NodePort
外部訪問<worker-node-ip>:<nodeport>
端口范圍30000-32767
是否需要云廠商支持? 不需要
適合場景測試、開發(fā)、無 ELB 的環(huán)境

適配目前的k8s環(huán)境需要變更嗎

# cat istio-custom.yaml 
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  profile: default
  components:
    pilot:
      enabled: true
      k8s:
        resources:
          requests:
            cpu: 50m
            memory: 64Mi
          limits:
            cpu: 200m
            memory: 256Mi
    ingressGateways:
      - name: istio-ingressgateway
        enabled: true
        k8s:
          resources:
            requests:
              cpu: 50m
              memory: 128Mi
            limits:
              cpu: 200m
              memory: 512Mi
  values:
    global:
      hub: swr.cn-east-3.myhuaweicloud.com/bocheng-test/istio
      tag: 1.27.3
      proxy:
        image: proxyv2
      imagePullPolicy: IfNotPresent
    gateways:
      istio-ingressgateway:
        type: NodePort
        ports:
          - port: 80
            targetPort: 8080
            name: http2
          - port: 443
            targetPort: 8443
            name: https

? 為什么說它“不需要變更”?

配置項(xiàng)狀態(tài)說明
profile: default? 合理使用默認(rèn) profile,功能完整
pilot 資源限制? 合理在小集群中降低了 CPU/Mem 請(qǐng)求(50m/64Mi),避免調(diào)度失敗
ingressGateways 資源限制? 合理設(shè)置了合理的 limits 和 requests
hub + tag 指定鏡像? 最佳實(shí)踐使用 swr.cn-east-3.myhuaweicloud.com/bocheng-test/istio 統(tǒng)一管理鏡像,比手動(dòng)寫 PILOT_IMAGE 更可靠
image: proxyv2? 正確明確指定 sidecar 鏡像名稱
type: NodePort? 適合你的環(huán)境在非云 LB 環(huán)境下使用 NodePort 是標(biāo)準(zhǔn)做法
targetPort: 8080/8443? 正確匹配 Istio ingressgateway 容器內(nèi) Envoy 監(jiān)聽端口

??? 這個(gè)配置的作用總結(jié)

你通過這個(gè) YAML 實(shí)現(xiàn)了:

  • 使用華為云 SWR 私有鏡像倉庫,解決拉取問題
  • 降低資源請(qǐng)求,適應(yīng)測試/開發(fā)集群
  • 開啟 NodePort,暴露入口網(wǎng)關(guān)
  • 所有配置清晰、可維護(hù)、可納入 Git 版本控制

?? 什么時(shí)候才需要修改它?

除非遇到以下情況,否則無需改動(dòng):

場景是否需要修改
新增 Egress Gateway? 需要在 components 中添加
改用 LoadBalancer 類型? 修改 gateways.istio-ingressgateway.type: LoadBalancer
啟用 mTLS STRICT 全局默認(rèn)? 在 values.global 中添加策略
升級(jí) Istio 版本? 修改 tag: 1.28.0
添加自定義證書? 添加 secretVolumes 或更新 tls 配置

? 建議操作

你現(xiàn)在應(yīng)該:

# 確保只安裝一次(避免重復(fù))
istioctl uninstall -y --purge
# 使用當(dāng)前 yaml 安裝
istioctl install -f istio-custom.yaml -y

然后按之前的流程創(chuàng)建 Gateway、VirtualServiceDestinationRule。

將 Istio Ingress Gateway 從NodePort模式適配為LoadBalancer模式的完整配置和操作流程。

? 第一步:修改istio-custom.yaml啟用 LoadBalancer

保持其他配置不變,只修改 values.gateways.istio-ingressgateway.typeLoadBalancer

更新后的istio-custom.yaml

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  profile: default
  components:
    pilot:
      enabled: true
      k8s:
        resources:
          requests:
            cpu: 50m
            memory: 64Mi
          limits:
            cpu: 200m
            memory: 256Mi
    ingressGateways:
      - name: istio-ingressgateway
        enabled: true
        k8s:
          resources:
            requests:
              cpu: 50m
              memory: 128Mi
            limits:
              cpu: 200m
              memory: 512Mi
  values:
    global:
      hub: swr.cn-east-3.myhuaweicloud.com/bocheng-test/istio
      tag: 1.27.3
      proxy:
        image: proxyv2
      imagePullPolicy: IfNotPresent
    gateways:
      istio-ingressgateway:
        # 改為 LoadBalancer
        type: LoadBalancer
        ports:
          - port: 80
            targetPort: 8080
            name: http2
          - port: 443
            targetPort: 8443
            name: https
        # 可選:指定服務(wù)注解,用于云廠商 ELB 配置
        serviceAnnotations:
          # 華為云 ELB 示例注解(按需啟用)
          # service.beta.kubernetes.io/huawei-elb-vpcid: your-vpc-id
          # service.beta.kubernetes.io/huawei-elb-classic-listener-port-protocol: tcp

?? 提示:serviceAnnotations 可用于指定 ELB 類型、帶寬、公網(wǎng)IP等,具體參考 華為云 CCE 文檔。

? 第二步:重新安裝 Istio

# 卸載舊版本
istioctl uninstall -y --purge
# 安裝新配置
istioctl install -f istio-custom.yaml -y

? 第三步:驗(yàn)證 LoadBalancer 是否創(chuàng)建成功

kubectl get svc -n istio-system istio-ingressgateway

你應(yīng)該看到:

NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                      AGE
istio-ingressgateway   LoadBalancer   10.96.123.45   121.36.88.123   80:31234/TCP,443:30987/TCP   2m
  • TYPELoadBalancer
  • EXTERNAL-IP 顯示公網(wǎng) IP(可能需要幾分鐘)

? 第四步:創(chuàng)建 Gateway + VirtualService(非灰度版示例)

創(chuàng)建gateway.yaml

apiVersion: networking.istio.io/v1beta1
kind: Gateway
meta
  name: global-ingress-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
kubectl apply -f gateway.yaml

創(chuàng)建virtualservice-nogray.yaml

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
meta
  name: bc-gateway-vs
  namespace: bc-feature-202509-testcce
spec:
  hosts:
  - "bc-gateway.example.com"
  gateways:
  - istio-system/global-ingress-gateway
  http:
  - route:
    - destination:
        host: bc-gateway.bc-feature-202509-testcce.svc.cluster.local
        port:
          number: 8901
      weight: 100
kubectl apply -f virtualservice-nogray.yaml

? 第五步:外部訪問方式(使用 LB 公網(wǎng) IP)

獲取公網(wǎng)地址:

export LB_IP=$(kubectl get svc -n istio-system istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $LB_IP

訪問服務(wù):

curl -H "Host: bc-gateway.example.com" http://$LB_IP/actuator/health

? 成功!

? Jenkinsfile 中如何適配?

如果你希望在 Jenkins 中動(dòng)態(tài)判斷是否使用 LoadBalancer,可以加一個(gè)參數(shù):

choice(
    name: 'INGRESS_TYPE',
    choices: ['NodePort', 'LoadBalancer'],
    description: '選擇入口類型'
)

然后在部署 Istio 時(shí)傳入不同配置,或直接使用固定 LoadBalancer。

? 總結(jié):NodePortvsLoadBalancer

特性NodePortLoadBalancer
是否需要云廠商支持? 不需要? 需要(如華為云 ELB)
外部訪問方式<node-ip>:<port><lb-ip>:80/443
端口范圍30000-32767標(biāo)準(zhǔn)端口(80/443)
適合場景測試環(huán)境生產(chǎn)環(huán)境
自動(dòng)創(chuàng)建公網(wǎng) IP? 否? 是

你現(xiàn)在可以將 istio-custom.yaml 中的 type: NodePort 改為 LoadBalancer,并立即獲得一個(gè)公網(wǎng)可訪問的 Istio 入口網(wǎng)關(guān)。

需要我為你生成一份 華為云 ELB 注解完整模板自動(dòng)獲取 LB IP 并測試連通性的 Shell 腳本 嗎?

到此這篇關(guān)于k8s java集群接入istio案例的文章就介紹到這了,更多相關(guān)k8s集群接入istio內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java操作itextpdf實(shí)現(xiàn)PDF添加文字,圖片和簽名

    Java操作itextpdf實(shí)現(xiàn)PDF添加文字,圖片和簽名

    這篇文章主要為大家詳細(xì)介紹了Java如何操作itextpdf實(shí)現(xiàn)PDF添加文字,圖片和簽名等功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下
    2025-01-01
  • Java取整與四舍五入

    Java取整與四舍五入

    本文詳細(xì)講解了Java取整與四舍五入,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • java的三種隨機(jī)數(shù)生成方式

    java的三種隨機(jī)數(shù)生成方式

    主要介紹了java的三種隨機(jī)數(shù)生成方式的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下
    2021-07-07
  • IDEA反編譯jar,查看源碼方式

    IDEA反編譯jar,查看源碼方式

    該篇文章總結(jié)了查看Java本地jar包注釋的幾種方法,包括使用快捷鍵CTRL+q和在設(shè)置中設(shè)置自動(dòng)浮現(xiàn)注釋
    2024-11-11
  • Java String類字符串的理解與認(rèn)知

    Java String類字符串的理解與認(rèn)知

    String字符串和char字符不同,char使用單引號(hào),只能表示一個(gè)字符,字符串就是一段文本。String是個(gè)類。這個(gè)類使用final修飾,所以這個(gè)類是不可以繼承擴(kuò)充和修改它的方法的
    2021-10-10
  • Java數(shù)據(jù)類型、運(yùn)算符和方法重點(diǎn)總結(jié)大全

    Java數(shù)據(jù)類型、運(yùn)算符和方法重點(diǎn)總結(jié)大全

    在大學(xué)Java編程課程中,數(shù)據(jù)類型和運(yùn)算符是構(gòu)建程序設(shè)計(jì)邏輯的基石,這篇文章主要介紹了Java數(shù)據(jù)類型、運(yùn)算符和方法重點(diǎn)總結(jié)的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2026-02-02
  • idea將maven項(xiàng)目改成Spring boot項(xiàng)目的方法步驟

    idea將maven項(xiàng)目改成Spring boot項(xiàng)目的方法步驟

    這篇文章主要介紹了idea將maven項(xiàng)目改成Spring boot項(xiàng)目的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • springboot的yml配置文件通過db2的方式整合mysql的教程

    springboot的yml配置文件通過db2的方式整合mysql的教程

    這篇文章主要介紹了springboot的yml配置文件通過db2的方式整合mysql的教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • java.net.ConnectException異常的正確解決方法(親測有效!)

    java.net.ConnectException異常的正確解決方法(親測有效!)

    java.net.ConnectException異常是與網(wǎng)絡(luò)相關(guān)的最常見的Java異常之一,建立從客戶端應(yīng)用程序到服務(wù)器的TCP連接時(shí),我們可能會(huì)遇到它,這篇文章主要給大家介紹了關(guān)于java.net.ConnectException異常的正確解決方法,需要的朋友可以參考下
    2024-01-01
  • SpringBoot整合MyBatis Plus實(shí)現(xiàn)基本CRUD與高級(jí)功能

    SpringBoot整合MyBatis Plus實(shí)現(xiàn)基本CRUD與高級(jí)功能

    Spring Boot是一款用于快速構(gòu)建Spring應(yīng)用程序的框架,而MyBatis Plus是MyBatis的增強(qiáng)工具,本文將詳細(xì)介紹如何在Spring Boot項(xiàng)目中整合MyBatis Plus,并展示其基本CRUD功能以及高級(jí)功能的實(shí)現(xiàn)方式,需要的朋友可以參考下
    2024-02-02

最新評(píng)論

临西县| 荔浦县| 扬中市| 白山市| 长沙县| 新泰市| 于田县| 宕昌县| 五河县| 隆安县| 镇雄县| 天全县| 益阳市| 万宁市| 兴海县| 贵德县| 思南县| 阳东县| 凤山市| 泾川县| 康定县| 昌邑市| 怀宁县| 镇巴县| 成武县| 台北县| 息烽县| 和林格尔县| 红河县| 宁都县| 麻江县| 滁州市| 南丹县| 哈尔滨市| 曲阳县| 高要市| 平遥县| 诸城市| 德江县| 武安市| 进贤县|