精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

基于Jenkins Pipeline構建企業級CI/CD

開發 前端
Lark Notice Plugin 是一個用于 Jenkins 的構建通知插件,可以將 Jenkins構建過程以及結果通知推送到 Lark、飛書、釘釘 協作平臺。 可配置多個的通知時機,包括 構建啟動時、構建中斷、構建失敗、構建成功時、構建不穩定等。

案例介紹

本案例通過若依項目作為案例,通過Jenkins構建企業級CI/CD平臺。

若依服務列表:

  • ruoyi-auth
  • ruoyi-system
  • ruoyi-gateway
  • ruoyi-ui

若依環境列表:

  • DEV
  • UAT
  • PROD

環境準備工作:

  • nacos安裝并配置完成
  • MySQL部署完成并初始化
  • Redis部署完成
  • Harbor鏡像倉庫
  • Gitlab部署完成
  • Kubernetes部署完成
  • Ingress部署完成

設計思路

觸發構建設計:

本設計通過Jenkins Generic Webhook Trigger 插件實現了基于Webhook自動觸發流水線構建。

圖片圖片

流程說明:

  • 研發項目負責人代碼開發完成后進行合并代碼并生成Tag
  • Gitlab通過Webhook自動觸發Jenkins Pipeline構建

流水線設計:

圖片圖片

Jenkins流水線完整圖:

圖片圖片

自定義基礎鏡像

在實際企業環境中,基礎鏡像都會根據具體得需求定義適合自己得基礎鏡像。

定義Maven鏡像:

用于代碼構建編譯打包,會把Ruoyi相關依賴包打到基礎鏡像內,避免分層構建失敗。

# 拉取源代碼并切換分支
$ https://gitee.com/y_project/RuoYi-Cloud.git
$ git checkout v3.6.3
$ cd ..

# 定義Dockerfile
$ cat Dockerfile
FROM maven:3.8.6-openjdk-8
ADD  RuoYi-Cloud /opt/RuoYi-Cloud
RUN cd /opt/RuoYi-Cloud  && mvn clean install -DskipTests
RUN rm -rf /opt/RuoYi-Cloud

# 構建鏡像
$ docker build uhub.service.ucloud.cn/kubesre/maven:jdk8 .
$ docker push uhub.service.ucloud.cn/kubesre/maven:jdk8

定義Java基礎鏡像:

根據需求定義適合自己的基礎鏡像。通過變量傳遞讓配置變得更靈活!

# 創建個目錄
$ mkdir base && cd base

# 創建啟動腳本
$ cat docker-entrypoint.sh
#!/bin/sh
java -server -Xms$JVM_XMS -Xmx$JVM_XMX -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/data/gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/heapdump.hprof  -jar app.jar --server.port=$SERVICE_PORT --spring.profiles.active=$PROFILES_ACTIVE --spring.cloud.nacos.config.server-addr=$NACOS_ADDRESS --spring.cloud.nacos.config.namespace=$NACOS_NAMESPACE_ID --spring.cloud.nacos.config.username=$NACOS_USERNAME --spring.cloud.nacos.config.password=$NACOS_PASSWORD --spring.cloud.nacos.discovery.server-addr=$NACOS_ADDRESS --spring.cloud.nacos.discovery.namespace=$NACOS_NAMESPACE_ID --spring.cloud.nacos.discovery.username=$NACOS_USERNAME --spring.cloud.nacos.discovery.password=$NACOS_PASSWORD

# 創建down-nacos腳本
cat down-nacos.sh 
#!/bin/sh
ipTrue=false
java_service_ip=""
code=false

getPodIp() {
    java_service_ip=`ip a  | grep inet | grep -v inet6 | grep -v '127.0.0.1' | awk '{print $2}' | awk -F / '{print$1}'`
    grep -w "${java_service_ip}" /etc/hosts > /dev/null
    if [ $? -eq 0 ];then
      echo "get java service ip success"
      ipTrue=true
    else
      echo "get java service ip failed"
    fi
}

downService(){
    accessToken=`curl -s -X POST http://$NACOS_ADDRESS/nacos/v1/auth/users/login --form username=$NACOS_USERNAME --form password=$NACOS_PASSWORD|jq -r .accessToken`
    curl -s -X  PUT "$NACOS_ADDRESS/nacos/v1/ns/instance?language=zh-CN&accessToken=$accessToken&username=$NACOS_USERNAME&serviceName=$JAVA_SERVICE_NAME&ip=$java_service_ip&port=$SERVICE_PORT&enabled=false&namespaceId=$NACOS_NAMESPACE_ID"
    if [ "$code" = "ok" ];then
      echo "java service down from nacos success"
      code=true
    else
      echo "java service down from nacos failed"
    fi
}

start(){
  getPodIp
  if $ipTrue;then
    downService
    sleep 30
  else
    echo "down $JAVA_SERVICE_NAME failed" >> down_service.log
  fi   
}
start


# 定義Dockerfile
$ cat Dockerfile 
FROM  openjdk:8-jre
WORKDIR /data
COPY ./down-nacos.sh .
COPY ./docker-entrypoint.sh .
RUN chmod +x docker-entrypoint.sh && chmod +x  down-nacos.sh
ENTRYPOINT ["./docker-entrypoint.sh"]

# 構建鏡像
$ docker build uhub.service.ucloud.cn/kubesre/java-base:v8 .
$ docker push uhub.service.ucloud.cn/kubesre/java-base:v8

變量說明:

  • JVM_XMS:最小JVM堆棧內存
  • JVM_XMX:最大JVM堆棧內存
  • SERVICE_PORT:應用服務端口
  • NACOS_ADDRESS:Nacos地址
  • NACOS_USERNAME:Nacos用戶名
  • NACOS_PASSWORD:Nacos密碼
  • NACOS_NAMESPACE_ID:Nacos命名空間ID
  • PROFILES_ACTIVE:環境名稱

Dockerfile編寫

分層構建好處:

  • 不依賴本地環境
  • 減小容器鏡像大小

Java Dockerfile(分層構建):

FROM uhub.service.ucloud.cn/kubesre/maven:jdk8 AS build
COPY  src /opt/src/
COPY pom.xml /opt/
RUN  cd /opt/ && mvn clean install -DskipTests

FROM  uhub.service.ucloud.cn/kubesre/java-base:v8
# 復制jar文件到路徑
COPY  --from=build /opt/target/*.jar /data/app.jar

Vue Dockerfile(分層構建):

FROM node:16 AS builder

# 設置工作目錄
WORKDIR /usr/src/app

# 將項目文件復制到 Docker 鏡像中
COPY . .

# 安裝項目依賴
RUN npm install --registry=https://registry.npmmirror.com

# 構建靜態文件
RUN npm run build:prod

# 使用 Nginx 鏡像作為基礎鏡像,用于托管靜態文件
FROM nginx:stable-alpine

WORKDIR /home/ruoyi/projects/ruoyi-ui
# 將 VuePress 構建的靜態文件復制到 Nginx 的網站目錄
COPY --from=builder /usr/src/app/dist /home/ruoyi/projects/ruoyi-ui
COPY ./nginx/conf/nginx.conf /etc/nginx/nginx.conf

# 暴露 80 端口
EXPOSE 80

# 啟動 Nginx
CMD ["nginx", "-g", "daemon off;"]

Pipeline編寫

如下所有Pipeline文件,需要自行修改內容:

  • credentialsId
  • robot
  • 鏡像倉庫地址

如何查找credentialsId:

圖片圖片

如何查找robot:

圖片圖片

Java Pipeline:

pipeline {
    agent any
    triggers {
        GenericTrigger(
            genericVariables: [
                [key: 'ref', value: '$.ref'],  //獲取分支
                [key: 'user_username', value: '$.user_username'],     //獲取自動構建用戶名
                [key: 'GitRepository', value: '$.project.git_http_url'],  //獲取gitlab ssh項目地址
                [key: 'project', value: '$.project.name'],      //獲取項目名稱
                [key: 'repository', value: '$.repository.name'],
            ],
            token: "$JOB_NAME",
            causeString: 'Triggered on $branch',
            printContributedVariables: true,
            printPostContent: true,
            silentResponse: false,
        )
    }
    environment {
        // pipeline配置路徑
        pipeline_dir="/var/lib/jenkins/workspace/pipeline"
        // 項目版本
        Tag=sh(script: 'echo "${ref}" | awk -F"/" \'{print $3}\'', returnStdout: true).trim()
        // 項目名稱
        Project_Name="${project}"
        // 上一次版本
        Revsion_Prod=''
        //Depolyment名稱
        DeploymentName=''
        // 生產名稱空間
        Namespace_Prod=''
        // 灰度模式
        GrayHeaderMode=''
        // 灰度Depolyment名稱
        GrayDeploymentName=''
        // 灰度Service名稱
        GrayServiceName=''
        // 灰度Ingress名稱
        GrayIngressName=''
        // 是否灰度
        GrayEnable='yes'
    }
    options {
        // 表示保留10次構建歷史
        buildDiscarder(logRotator(numToKeepStr: '10'))
    }
    stages {
        stage('Pull Code') {
            // 拉取代碼
            steps {
                checkout([
                    $class: 'GitSCM', 
                    branches: [[name: "$ref"]],
                    doGenerateSubmoduleConfigurations: false,
                    extensions: [], 
                    userRemoteConfigs: [[
                        credentialsId: 'ac66550d-6999-485c-af3a-7e6189f765f0',
                        url: "$GitRepository"
                    ]]
                ])

                script{
                    currentBuild.displayName = "#${BUILD_NUMBER} - ${Project_Name} - ${Tag}"
                }
            }
        }
        //     // 代碼構建
        //   stage('Build Code') {
        //         steps {
        //              sh '/application/maven/bin/mvn -f pom.xml -s settings.xml clean package -DskipTests'
        //         }
        //     } 
        // 鏡像構建
        stage('Build Image') {
            steps {
                sh '''
                /usr/bin/docker build -t uhub.service.ucloud.cn/kubesre/$Project_Name:$Tag .
                /usr/bin/docker push  uhub.service.ucloud.cn/kubesre/$Project_Name:$Tag
                '''
            }
            post {
                success {
                    wrap([$class: 'BuildUser']) {
                        lark (
                            robot: "9f7c94cd-491e-4309-83b4-9290d01fc285",
                            type: "CARD",
                            title: "??  Jenkins 鏡像構建成功",
                            text: [
                                "?? **任務名稱**:[${JOB_NAME}](${JOB_URL})",
                                "?? **任務編號**:[${BUILD_DISPLAY_NAME}](${BUILD_URL})",
                                "?? **構建狀態**: <font color='green'>成功</font>",
                                "?? **鏡像版本**: $Tag",
                                "?? **鏡像倉庫**: uhub.service.ucloud.cn/kubesre/$Project_Name",
                                "?? **構建用時**: ${currentBuild.duration} ms",
                                "?? **執  行 者**: ${env.BUILD_USER}",
                                "<at id=all></at>"
                            ],
                            buttons: [
                                [
                                    title: "更改記錄",
                                    url: "${BUILD_URL}changes"
                                ],
                                [
                                    title: "控制臺",
                                    type: "danger",
                                    url: "${BUILD_URL}console"
                                ]
                            ]
                        )}
                }
            }
        }
        stage('DeployDev'){
            steps {
                echo "部署開發環境"
                script {
                    def userInput = input (
                        message: '確定要發布到DEV環境嗎?',
                        parameters:[
                            choice(name: '操作', choices: ['發布', '跳過'])
                        ],
                        ok: '確定',
                        submitter: 'admin',
                        submitterParameter: 'APPROVER'
                    )
                    if (userInput['操作'] == '發布'){
                        echo "部署Dev環境開始"
                        sh '''
                        echo $pipeline_dir
                        echo "打印編排文件詳細信息"
                        if [ -e "$pipeline_dir/dev/$Project_Name/deployment.yml" ]; then
                        cat $pipeline_dir/dev/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g"
                        cat $pipeline_dir/dev/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/dev/$Project_Name/service.yml" ]; then
                        cat $pipeline_dir/dev/$Project_Name/service.yml
                        cat $pipeline_dir/dev/$Project_Name/service.yml  | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/dev/$Project_Name/ingress.yml" ]; then
                        cat $pipeline_dir/dev/$Project_Name/ingress.yml 
                        cat $pipeline_dir/dev/$Project_Name/ingress.yml  | /usr/bin/kubectl apply -f  -
                        fi
                        '''
                    } else {
                        echo "不發布"
                    }
                }
            }
        } 
        stage('DeployUat'){
            steps {
                echo "部署測試環境"
                script {
                    def userInput = input (
                        message: '確定要發布到UAT環境嗎?',
                        parameters:[
                            choice(name: '操作', choices: ['發布', '跳過'])
                        ],
                        ok: '確定',
                        submitter: 'admin',
                        submitterParameter: 'APPROVER'
                    )
                    if (userInput['操作'] == '發布'){
                        echo "發布"
                        sh '''
                        echo $pipeline_dir
                        echo "打印編排文件詳細信息"

                        if [ -e "$pipeline_dir/uat/$Project_Name/deployment.yml" ]; then
                        cat $pipeline_dir/uat/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g"
                        cat $pipeline_dir/uat/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/uat/$Project_Name/service.yml" ]; then
                        cat $pipeline_dir/uat/$Project_Name/service.yml
                        cat $pipeline_dir/uat/$Project_Name/service.yml  | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/uat/$Project_Name/ingress.yml" ]; then
                        cat $pipeline_dir/uat/$Project_Name/ingress.yml 
                        cat $pipeline_dir/uat/$Project_Name/ingress.yml  | /usr/bin/kubectl apply -f  -
                        fi
                        '''
                    } else {
                        echo "不發布"
                    }
                }
            }
        } 
        stage('DeployGray'){
            steps {
                echo "部署灰度環境"
                script {
                    def GraysMode = input (
                        message: '確定要灰度驗證嗎?',
                        parameters:[
                            choice(name: 'operation', choices: ['基于權重灰度','基于請求頭灰度','跳過'])
                        ],
                        ok: '確定',
                        submitter: 'admin',
                        submitterParameter: 'APPROVER'
                    )
                    if (GraysMode['operation'] == '基于權重灰度'){
                        def WeightMode = input (
                            message: '請輸入權重比例!',
                            parameters:[
                                string(name: 'workload_weight',defaultValue: '',description: ''),
                                string(name: 'grayload_weight',defaultValue: '',description: '')
                            ],
                            ok: '確定',
                            submitter: 'admin',
                            submitterParameter: 'APPROVER'
                        )
                        sh """
                        echo $pipeline_dir
                        echo "打印編排文件詳細信息"
                        if [ -e "$pipeline_dir/prod/$Project_Name/deployment-gray.yml" ]; then
                        cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed  "s/TAG/${Tag}/g" 
                        cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi

                        echo "配置權重"

                        echo ${WeightMode['grayload_weight']}
                        if [ -e "$pipeline_dir/prod/$Project_Name/ingress-gray-weight.yml" ]; then
                        cat $pipeline_dir/prod/$Project_Name/ingress-gray-weight.yml | sed  "s/WEIGHT-VALUE/${WeightMode['grayload_weight']}/g" 
                        cat $pipeline_dir/prod/$Project_Name/ingress-gray-weight.yml | sed  "s/WEIGHT-VALUE/${WeightMode['grayload_weight']}/g" | /usr/bin/kubectl apply -f  -
                        fi
                        """
                    }
                    if (GraysMode['operation'] == '基于請求頭灰度'){
                        GrayHeaderMode = input (
                            message: '請輸入請求頭!',
                            parameters:[
                                string(name: 'header_key',defaultValue: '',description: ''),
                                string(name: 'header_value',defaultValue: '',description: '')
                            ],
                            ok: '確定',
                            submitter: 'admin',
                            submitterParameter: 'APPROVER'
                        )

                        sh """
                        echo ${GrayHeaderMode['header_value']}
                        echo $pipeline_dir
                        echo "打印編排文件詳細信息"

                        if [ -e "$pipeline_dir/prod/$Project_Name/deployment-gray.yml" ]; then
                        cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed  "s/TAG/${Tag}/g" 
                        cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi

                        echo "配置請求頭"
                        echo ${GrayHeaderMode['header_key']}
                        echo ${GrayHeaderMode['header_value']}

                        if [ -e "$pipeline_dir/prod/$Project_Name/ingress-gray-header.yml" ]; then
                        cat $pipeline_dir/prod/$Project_Name/ingress-gray-header.yml | sed  "s/header-key/${GrayHeaderMode['header_key']}/g" | sed  "s/header-value/${GrayHeaderMode['header_value']}/g"
                        cat $pipeline_dir/prod/$Project_Name/ingress-gray-header.yml | sed  "s/header-value/${GrayHeaderMode['header_key']}/g" | sed  "s/header-value/${GrayHeaderMode['header_value']}/g" | /usr/bin/kubectl apply -f  -
                        fi
                        """
                    }
                    // 默認模式為yes,如果跳過為no
                    if (GraysMode['operation'] == '跳過'){
                        GrayEnable='no'
                    }
                }
            }
        } 
        stage('DeployProd'){
            steps {
                echo "部署生產環境"
                script {
                    def userInput = input (
                        message: '確定要發布到生產環境嗎?',
                        parameters:[
                            choice(name: '操作', choices: ['發布', '跳過'])
                        ],
                        ok: '確定',
                        submitter: 'test',
                        submitterParameter: 'APPROVER'
                    )
                    if (userInput['操作'] == '發布'){
                        echo "發布"
                        Namespace_Prod = sh(script: "cat $pipeline_dir/prod/$Project_Name/deployment.yml | grep namespace | awk -F ':' '{print \$2}'", returnStdout: true).trim()
                        DeploymentName = sh(script: "cat $pipeline_dir/prod/$Project_Name/deployment.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
                        Revsion_Prod = sh(script: "kubectl get deployment $DeploymentName -n ${Namespace_Prod} -o=jsnotallow='{.spec.template.spec.containers[*].image}' | awk -F ':' '{print \$NF}'", returnStdout: true).trim()
                        GrayDeploymentName = sh(script: "cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
                        GrayServiceName = sh(script: "cat $pipeline_dir/prod/$Project_Name/service-gray.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
                        GrayIngressName = sh(script: "cat $pipeline_dir/prod/$Project_Name/ingress-gray-header.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()

                        sh '''
                        echo $pipeline_dir
                        echo "開始部署生產環境"
                        echo "打印編排文件詳細信息"

                        if [ -e "$pipeline_dir/prod/$Project_Name/deployment.yml" ]; then
                          cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g"
                          cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/prod/$Project_Name/service.yml" ]; then
                           cat $pipeline_dir/prod/$Project_Name/service.yml
                           cat $pipeline_dir/prod/$Project_Name/service.yml  | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/prod/$Project_Name/ingress.yml" ]; then
                          cat $pipeline_dir/prod/$Project_Name/ingress.yml
                          cat $pipeline_dir/prod/$Project_Name/ingress.yml  | /usr/bin/kubectl apply -f  -
                        fi
                        '''
                        if (GrayEnable == 'yes'){
                            sh """
                        kubectl delete deployment ${GrayDeploymentName} -n ${Namespace_Prod}
                        kubectl delete service  ${GrayServiceName} -n ${Namespace_Prod}
                        kubectl delete ingress  ${GrayIngressName} -n ${Namespace_Prod}
                        """
                        }

                    } else {
                        echo "不發布"
                    }
                }
            }
            post {
                success {
                    wrap([$class: 'BuildUser']) {
                    lark (
                        robot: "9f7c94cd-491e-4309-83b4-9290d01fc285",
                        type: "CARD",
                        title: "??  Jenkins 應用發布成功",
                        text: [
                            "?? **應用名稱**:[${JOB_NAME}](${JOB_URL})",
                            "?? **應用環境**:Prod",
                            "?? **任務編號**:[${BUILD_DISPLAY_NAME}](${BUILD_URL})",
                            "?? **發布狀態**: <font color='green'>成功</font>",
                            "?? **鏡像版本**: $Tag",
                            "?? **鏡像倉庫**: harbor.kubesre.com:8443/kubesre/$Project_Name",
                            "?? **執  行 者**: ${env.BUILD_USER}",
                            "<at id=all></at>"
                        ],
                        buttons: [
                           [
                              title: "更改記錄",
                              url: "${BUILD_URL}changes"
                           ],
                           [
                              title: "控制臺",
                              type: "danger",
                              url: "${BUILD_URL}console"
                           ]
                        ]
                    )}
                }
            }
        } 

        stage('RollBack'){
            steps {
                echo "版本回滾操作"
                script {
                  def RollBack = input (
                        message: '確定要執行回滾操作嗎?',
                        parameters:[
                            choice(name: '是否回滾', choices: ['是', '否']),
                            string(name: '回滾版本', defaultValue: Revsion_Prod,description: '默認上一個版本')
                        ],
                        ok: '確定',
                        submitter: 'admin',
                        submitterParameter: 'APPROVER'
                    )
                  if (RollBack['是否回滾'] == '是'){
                        echo "版本回滾成功"
                        echo RollBack['回滾版本']
                        sh """
                        if [ -e "$pipeline_dir/prod/$Project_Name/deployment.yml" ]; then
                          cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed  "s/TAG/${RollBack['回滾版本']}/g"
                          cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed  "s/TAG/${RollBack['回滾版本']}/g" | /usr/bin/kubectl apply -f  -
                        fi
                        """
                  } else {
                         echo "放棄版本回滾"
                         echo RollBack['回滾版本']
                  }
            }
         }
        }
    }
}

Vue Pipeline:

pipeline {
    agent any
    triggers {
            GenericTrigger(
                genericVariables: [
                    [key: 'ref', value: '$.ref'],  //獲取分支
                    [key: 'user_username', value: '$.user_username'],     //獲取自動構建用戶名
                    [key: 'GitRepository', value: '$.project.git_http_url'],  //獲取gitlab ssh項目地址
                    [key: 'project', value: '$.project.name'],      //獲取項目名稱
                    [key: 'repository', value: '$.repository.name'],
                ],
                token: "$JOB_NAME",
                causeString: 'Triggered on $branch',
                printContributedVariables: true,
                printPostContent: true,
                silentResponse: false,
            )
        }
    environment {
        // pipeline配置路徑
        pipeline_dir="/var/lib/jenkins/workspace/pipeline"
        // 項目版本
        Tag=sh(script: 'echo "${ref}" | awk -F"/" \'{print $3}\'', returnStdout: true).trim()
        // 項目名稱
        Project_Name="${project}"
        // 上一次版本
        Revsion_Prod=''
        //Depolyment名稱
        DeploymentName=''
        // 生產名稱空間
        Namespace_Prod=''
        // 灰度模式
        GrayHeaderMode=''
        // 灰度Depolyment名稱
        GrayDeploymentName=''
        // 灰度Service名稱
        GrayServiceName=''
        // 灰度Ingress名稱
        GrayIngressName=''
        // 是否灰度
        GrayEnable='yes'
    }
    options {
  // 表示保留10次構建歷史
  buildDiscarder(logRotator(numToKeepStr: '10'))
    }
    stages {
        stage('Pull Code') {
             // 拉取代碼
            steps {
                checkout([
                    $class: 'GitSCM', 
                    branches: [[name: "$ref"]],
                    doGenerateSubmoduleConfigurations: false,
                    extensions: [], 
                    userRemoteConfigs: [[
                        credentialsId: 'ac66550d-6999-485c-af3a-7e6189f765f0',
                        url: "$GitRepository"
                    ]]
                ])

                script{
                    currentBuild.displayName = "#${BUILD_NUMBER} - ${Project_Name} - ${Tag}"
                }
            }
        }
    //     // 代碼構建
    //   stage('Build Code') {
    //         steps {
    //              sh '/application/maven/bin/mvn -f pom.xml -s settings.xml clean package -DskipTests'
    //         }
    //     } 
         // 鏡像構建
        stage('Build Image') {
            steps {
                 sh '''
                   /usr/bin/docker build -t uhub.service.ucloud.cn/kubesre/$Project_Name:$Tag .
                   /usr/bin/docker push  uhub.service.ucloud.cn/kubesre/$Project_Name:$Tag
                 '''
            }
            post {
                success {
                    wrap([$class: 'BuildUser']) {
                    lark (
                        robot: "9f7c94cd-491e-4309-83b4-9290d01fc285",
                        type: "CARD",
                        title: "??  Jenkins 鏡像構建成功",
                        text: [
                            "?? **任務名稱**:[${JOB_NAME}](${JOB_URL})",
                            "?? **任務編號**:[${BUILD_DISPLAY_NAME}](${BUILD_URL})",
                            "?? **構建狀態**: <font color='green'>成功</font>",
                            "?? **鏡像版本**: $Tag",
                            "?? **鏡像倉庫**: uhub.service.ucloud.cn/kubesre/$Project_Name",
                            "?? **構建用時**: ${currentBuild.duration} ms",
                            "?? **執  行 者**: ${env.BUILD_USER}",
                            "<at id=all></at>"
                        ],
                        buttons: [
                           [
                              title: "更改記錄",
                              url: "${BUILD_URL}changes"
                           ],
                           [
                              title: "控制臺",
                              type: "danger",
                              url: "${BUILD_URL}console"
                           ]
                        ]
                    )}
                }
            }
        }
        stage('DeployDev'){
            steps {
                echo "部署開發環境"
                script {
                    def userInput = input (
                        message: '確定要發布到DEV環境嗎?',
                        parameters:[
                            choice(name: '操作', choices: ['發布', '跳過'])
                        ],
                        ok: '確定',
                        submitter: 'admin',
                        submitterParameter: 'APPROVER'
                    )
                    if (userInput['操作'] == '發布'){
                        echo "部署Dev環境開始"
                        sh '''
                        echo $pipeline_dir
                        echo "打印編排文件詳細信息"
                        if [ -e "$pipeline_dir/dev/$Project_Name/deployment.yml" ]; then
                            cat $pipeline_dir/dev/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g"
                            cat $pipeline_dir/dev/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi
                        
                        if [ -e "$pipeline_dir/dev/$Project_Name/service.yml" ]; then
                            cat $pipeline_dir/dev/$Project_Name/service.yml
                            cat $pipeline_dir/dev/$Project_Name/service.yml  | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/dev/$Project_Name/ingress.yml" ]; then
                            cat $pipeline_dir/dev/$Project_Name/ingress.yml 
                            cat $pipeline_dir/dev/$Project_Name/ingress.yml  | /usr/bin/kubectl apply -f  -
                        fi
                        '''
                    } else {
                        echo "不發布"
                    }
                }
            }
        } 
        stage('DeployUat'){
            steps {
                echo "部署測試環境"
                 script {
                    def userInput = input (
                        message: '確定要發布到UAT環境嗎?',
                        parameters:[
                            choice(name: '操作', choices: ['發布', '跳過'])
                        ],
                        ok: '確定',
                        submitter: 'admin',
                        submitterParameter: 'APPROVER'
                    )
                    if (userInput['操作'] == '發布'){
                        echo "發布"
                         sh '''
                        echo $pipeline_dir
                        echo "打印編排文件詳細信息"

                        if [ -e "$pipeline_dir/uat/$Project_Name/deployment.yml" ]; then
                            cat $pipeline_dir/uat/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g"
                            cat $pipeline_dir/uat/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi
                        
                        if [ -e "$pipeline_dir/uat/$Project_Name/service.yml" ]; then
                            cat $pipeline_dir/uat/$Project_Name/service.yml
                            cat $pipeline_dir/uat/$Project_Name/service.yml  | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/uat/$Project_Name/ingress.yml" ]; then
                            cat $pipeline_dir/uat/$Project_Name/ingress.yml 
                            cat $pipeline_dir/uat/$Project_Name/ingress.yml  | /usr/bin/kubectl apply -f  -
                        fi
                        '''
                    } else {
                        echo "不發布"
                    }
                }
            }
        } 
        stage('DeployProd'){
            steps {
                echo "部署生產環境"
                 script {
                    def userInput = input (
                        message: '確定要發布到生產環境嗎?',
                        parameters:[
                            choice(name: '操作', choices: ['發布', '跳過'])
                        ],
                        ok: '確定',
                        submitter: 'test',
                        submitterParameter: 'APPROVER'
                    )
                    if (userInput['操作'] == '發布'){
                        echo "發布"
                        Namespace_Prod = sh(script: "cat $pipeline_dir/prod/$Project_Name/deployment.yml | grep namespace | awk -F ':' '{print \$2}'", returnStdout: true).trim()
                        DeploymentName = sh(script: "cat $pipeline_dir/prod/$Project_Name/deployment.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
                        Revsion_Prod = sh(script: "kubectl get deployment $DeploymentName -n ${Namespace_Prod} -o=jsnotallow='{.spec.template.spec.containers[*].image}' | awk -F ':' '{print \$NF}'", returnStdout: true).trim()
                        GrayDeploymentName = sh(script: "cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
                        GrayServiceName = sh(script: "cat $pipeline_dir/prod/$Project_Name/service-gray.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
                        GrayIngressName = sh(script: "cat $pipeline_dir/prod/$Project_Name/ingress-gray-header.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()

                        sh '''
                        echo $pipeline_dir
                        echo "開始部署生產環境"
                        echo "打印編排文件詳細信息"

                        if [ -e "$pipeline_dir/prod/$Project_Name/deployment.yml" ]; then
                          cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g"
                          cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/prod/$Project_Name/service.yml" ]; then
                           cat $pipeline_dir/prod/$Project_Name/service.yml
                           cat $pipeline_dir/prod/$Project_Name/service.yml  | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/prod/$Project_Name/ingress.yml" ]; then
                          cat $pipeline_dir/prod/$Project_Name/ingress.yml
                          cat $pipeline_dir/prod/$Project_Name/ingress.yml  | /usr/bin/kubectl apply -f  -
                        fi
                        '''

                    } else {
                        echo "不發布"
                    }
                }
            }
            post {
                success {
                    wrap([$class: 'BuildUser']) {
                    lark (
                        robot: "9f7c94cd-491e-4309-83b4-9290d01fc285",
                        type: "CARD",
                        title: "??  Jenkins 應用發布成功",
                        text: [
                            "?? **應用名稱**:[${JOB_NAME}](${JOB_URL})",
                            "?? **應用環境**:Prod",
                            "?? **任務編號**:[${BUILD_DISPLAY_NAME}](${BUILD_URL})",
                            "?? **發布狀態**: <font color='green'>成功</font>",
                            "?? **鏡像版本**: $Tag",
                            "?? **鏡像倉庫**: harbor.kubesre.com:8443/kubesre/$Project_Name",
                            "?? **執  行 者**: ${env.BUILD_USER}",
                            "<at id=all></at>"
                        ],
                        buttons: [
                           [
                              title: "更改記錄",
                              url: "${BUILD_URL}changes"
                           ],
                           [
                              title: "控制臺",
                              type: "danger",
                              url: "${BUILD_URL}console"
                           ]
                        ]
                    )}
                }
            }
        } 
    }
}

配置Jenkins

依賴的組件(自行安裝):

  • Generic Webhook Trigger
  • Pipeline(所有以Pipeline開頭的組件)
  • build user vars
  • Blue Ocean
  • Lark Notice(通過上傳文件的方式安裝)https://721806280.github.io/lark-notice-plugin-doc/

配置Lark Notice:

Lark Notice Plugin 是一個用于 Jenkins 的構建通知插件,可以將 Jenkins構建過程以及結果通知推送到 Lark、飛書、釘釘 協作平臺。 可配置多個的通知時機,包括 構建啟動時、構建中斷、構建失敗、構建成功時、構建不穩定 等。 支持多種不同類型的消息,包括 文本消息、圖片消息, 群名片消息、富文本消息、卡片消息; 同時該插件還提供了自定義模板和變量的功能,使您能夠根據自己的需求來定制通知消息的內容和格式。(本次案例是基于飛書進行驗證)

準備工作,在飛書群新建一個機器人(https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot):

在飛書群,點擊設置:

圖片圖片

然后點擊群機器人:

圖片圖片

在飛書群,選擇添加機器人

圖片圖片

填寫相應配置信息并點擊保存:

圖片圖片

圖片圖片

選擇系統管理-Lark Notice:

圖片圖片

通知時機全部勾選:

圖片圖片

配置機器人信息并保存

圖片圖片

配置Java Pipeline

新建任務:

圖片圖片

填寫任務名稱,并選擇流水線,點擊確定:

圖片圖片

配置Pipeline SCM:

圖片圖片

修改腳本路徑,點擊確定:

圖片圖片

點擊構建讓配置生效:

圖片圖片

其他Java項目配置都一樣!

配置Vue Pipeline

新建任務:

圖片圖片

填寫任務名稱,選擇流水線:

圖片圖片

配置Pipeline SCM:

圖片圖片

修改腳本路徑,點擊確定:

圖片圖片

點擊構建讓配置生效:

圖片圖片

其他Vue項目配置都一樣!

配置Gitlab Webhook

進入項目,選擇webhook:

圖片圖片

選擇添加Webhook:

圖片圖片

配置webhook URL,Token以Job Name:

圖片圖片

勾選標簽推送事件,也就是說只有標簽推送事件才會觸發流水線:

圖片圖片

到此為止,Webook已配置完畢!所有項目配置都一樣。

觸發驗證

觸發Java Pipeline:

進入標簽管理:

圖片圖片

新建標簽:

圖片圖片

填寫信息并點擊創建標簽(此標簽名稱也是容器鏡像的Tag):

圖片圖片

進入Jenkins可以看到Gateway Pipeline已經觸發了:

圖片圖片

圖片圖片

選擇發布,并點擊確定,將新版本發布到Dev環境:

圖片圖片

選擇發布,并點擊確定,將新版本發布到Uat環境:

圖片圖片

選擇對應的灰度發布方式或者跳過:

圖片圖片

選擇發布,并點擊確定,將新版本發布到Prod環境:

圖片圖片

也可以回滾,默認是上一個版本也可修改成想要回滾到的版本:

圖片圖片

觸發 Vue流水線:

進入標簽管理:

圖片圖片

創建標簽:

圖片圖片

填寫信息并點擊創建標簽(此標簽名稱也是容器鏡像的Tag):

圖片圖片

圖片圖片

進入Jenkins可以看到ruoyi-ui Pipeline已經觸發了:

圖片圖片

選擇發布,并點擊確定,將新版本發布到Dev環境:

圖片圖片

選擇發布,并點擊確定,將新版本發布到Uat環境:

圖片圖片

選擇發布,并點擊確定,將新版本發布到Prod環境:

圖片圖片

構建通知

圖片圖片

責任編輯:武曉燕 來源: 云原生運維圈
相關推薦

2020-10-21 14:10:28

工具測試開發

2019-11-07 09:00:39

Jenkins流水線開源

2022-08-31 22:25:53

微服務架構DevOPs

2022-02-22 09:00:00

軟件開發CI/CD 管道工具

2021-10-11 14:28:25

TypeScript企業級應用

2009-01-03 14:54:36

ibmdwWebSphere

2009-06-03 14:24:12

ibmdwWebSphere

2015-11-10 15:14:20

CIO時代網

2023-02-19 15:28:39

CI/CD 管道集成開發

2020-06-05 07:20:41

測試自動化環境

2021-07-27 08:01:22

CICD平臺

2018-08-24 09:00:00

DevOps持續集成連續部署

2023-09-11 12:57:00

大數據大數據中臺

2021-08-31 09:00:00

開發Azure DevOp集成

2023-03-13 14:46:32

CI/CD工具軟件開發

2020-10-12 07:00:00

JenkinsDevOps測試工具

2010-04-07 08:55:00

OSGiSpring

2014-09-09 14:10:01

企業級HadoopSpark

2016-10-12 17:18:26

私有云持續交付華為

2018-02-02 11:21:25

云計算標準和應用大會
點贊
收藏

51CTO技術棧公眾號

欧美韩日高清| se01亚洲视频| 成人久久视频在线观看| 午夜精品蜜臀一区二区三区免费| 伊人网综合视频| 欧美日韩精品免费观看视完整| 亚洲国产精品v| 99re国产在线播放| 好看的av在线| 大色综合视频网站在线播放| 这里只有精品电影| 国产成人无码a区在线观看视频| 男女视频在线观看免费| 久久精品国产网站| 国内精品一区二区三区| 国产精品免费无码| 视频一区日韩精品| 在线观看视频欧美| 日本一本中文字幕| 日本不卡三区| 91麻豆免费观看| 91色精品视频在线| 亚洲欧美日韩激情| 国产精品a久久久久| 亚洲欧美中文字幕| 久久无码人妻一区二区三区| 成人福利av| 夜色激情一区二区| 亚洲精品免费在线看| 黄色av小说在线观看| 蜜桃久久精品一区二区| 性色av香蕉一区二区| 国内毛片毛片毛片毛片毛片| 任你弄精品视频免费观看| 欧美一区二区三区视频免费播放 | 五月久久久综合一区二区小说| 精品少妇一区二区三区在线视频| 五月婷婷丁香色| 成人永久免费视频| 久久97精品久久久久久久不卡| 国内综合精品午夜久久资源| 91精品国产一区二区| 性欧美大战久久久久久久| 一区二区三区视频网站| av中文字幕不卡| 国产在线一区二区三区| 香蕉影院在线观看| 一区二区三区四区五区精品视频 | 国产亚洲精彩久久| 精品久久中文字幕| av在线播放天堂| av在线看片| 国产精品剧情在线亚洲| 日韩影视精品| 黄色片在线免费看| 国产三级三级三级精品8ⅰ区| 久久99精品久久久久久秒播放器| 国产极品久久久| 国产精品一区二区三区网站| 91精品久久久久久久久久入口| 高潮毛片又色又爽免费| 国产日韩欧美三区| 欧美亚洲一区在线| 欧美一区二区三区四| 亚洲日本免费| 青青草国产精品一区二区| 久久午夜免费视频| 麻豆91精品| 国产第一区电影| 中文字幕乱码中文字幕| 麻豆精品在线看| 国产在线观看不卡| h片在线免费看| 成人免费三级在线| 久久久久久99| 成人午夜影视| 亚洲天堂久久久久久久| 亚洲欧美日韩不卡| 免费毛片在线看片免费丝瓜视频| 一区二区三区蜜桃| 国内精品在线观看视频| 另类激情视频| 欧美日本在线观看| 中文字幕久久久久久久| youjizz亚洲| 亚洲女人天堂色在线7777| 337人体粉嫩噜噜噜| 99久久.com| 久久久久久久久电影| 永久免费无码av网站在线观看| 日韩精品视频网站| 69堂成人精品视频免费| 亚洲 国产 欧美 日韩| 国产喂奶挤奶一区二区三区| 国产又大又长又粗又黄| 国产蜜臀在线| 欧美优质美女网站| 手机在线观看日韩av| 麻豆一区二区麻豆免费观看| 国产香蕉97碰碰久久人人| 性生交大片免费全黄| 亚洲视频一区| 国产精品日韩在线播放| 午夜精品一二三区| 国产日韩精品一区二区三区 | 九色网友自拍视频手机在线| 日本一二三不卡| 女人被男人躁得好爽免费视频| 亚洲最新无码中文字幕久久| 欧美日韩电影一区| av av在线| 欧美3p视频| 性欧美亚洲xxxx乳在线观看| 夜夜躁狠狠躁日日躁av| 成人av在线资源| 一区二区高清视频| 中文字幕在线中文字幕在线中三区 | 青青草成人网| 久久免费电影| 欧美肥胖老妇做爰| 88久久精品无码一区二区毛片| 91精品一区二区三区综合| 2019av中文字幕| av免费在线不卡| 中文字幕高清一区| 91免费视频网站在线观看| 国产精品白丝久久av网站| 亚洲欧美日韩第一区| 久久久久久久久久久97| 九色综合狠狠综合久久| 日韩精品资源| 亚洲小少妇裸体bbw| 日韩欧美在线123| www.涩涩爱| 久久三级视频| 久久精品二区| 狠狠操一区二区三区| 日韩欧美一级二级三级| 手机在线免费看片| 七七婷婷婷婷精品国产| 欧美一区二区三区电影在线观看 | 亚洲区小说区图片区qvod按摩| 最近2019免费中文字幕视频三| 日本三级一区二区| 成人精品小蝌蚪| 香港三级日本三级a视频| 福利一区二区| 在线观看国产欧美| 成人h动漫精品一区二区下载| 成人一区二区视频| av一区二区三区免费观看| 国产精品一区二区精品视频观看| 中文字幕精品网| 人妻中文字幕一区二区三区| 久久亚区不卡日本| 欧美色图另类小说| 亚洲+变态+欧美+另类+精品| 97婷婷涩涩精品一区| 秋霞网一区二区| 亚洲成人免费影院| 国产 中文 字幕 日韩 在线| 亚洲久色影视| 欧美日韩国产一二| 91亚洲精品| www.日韩不卡电影av| 国产伦理吴梦梦伦理| 亚洲欧美国产毛片在线| 农村末发育av片一区二区| 影院欧美亚洲| 麻豆成人av| 91国拍精品国产粉嫩亚洲一区| 色999日韩欧美国产| 91禁在线观看| 亚洲一区二区五区| 久久久久久婷婷| 天堂av在线一区| 一个色的综合| 99精品中文字幕在线不卡| 2024亚洲男人天堂| 1024视频在线| 日韩美女在线视频| 69成人免费视频| 国产精品久线观看视频| 男生和女生一起差差差视频| 樱桃成人精品视频在线播放| 欧美激情www| 欧美天堂一区二区| 久久99国产精品久久久久久久久| 涩涩视频免费看| 色嗨嗨av一区二区三区| 极品美妇后花庭翘臀娇吟小说| 高清国产一区二区| 成年人在线观看视频免费| 影视亚洲一区二区三区| 久久久久一区二区| 亚洲老司机网| 8x海外华人永久免费日韩内陆视频| 国产中文字幕在线| 日韩欧美成人激情| 亚洲中文字幕无码爆乳av| 亚洲男人天堂av网| 国产免费无遮挡吸奶头视频| 国产精品综合一区二区三区| 热久久精品国产| 欧美精品一卡| 亚洲bbw性色大片| 国产精品巨作av| 成人欧美一区二区三区在线湿哒哒| av电影院在线看| 日韩亚洲第一页| 日韩一区av| 日韩精品一区二| 中文文字幕一区二区三三| 亚洲永久精品国产| 免费91在线观看| www成人在线观看| 国产精品熟女一区二区不卡| 日韩主播视频在线| 黄色一级片在线看| 亚洲免费二区| 亚洲欧洲久久| 视频精品在线观看| 国精产品一区二区| 亚洲欧洲国产精品一区| 国产精品视频自拍| 校园春色亚洲色图| 性色av一区二区三区红粉影视| 大片免费在线观看| 最近2019中文字幕mv免费看 | 99精品久久免费看蜜臀剧情介绍| 中文av字幕在线观看| 三级不卡在线观看| 国产高清精品在线观看| 99亚洲一区二区| 日韩 欧美 视频| 午夜精品影院| av磁力番号网| 国产精品国内免费一区二区三区| 日韩国产高清一区| 国产成人精品999在线观看| 成人在线免费观看一区| 天堂久久av| 亚洲影院色在线观看免费| 视频91a欧美| 91精品国产综合久久久久久蜜臀| 激情开心成人网| 奇米影视亚洲狠狠色| 国产精品原创| 97在线精品视频| 免费成人在线电影| 欧洲亚洲免费视频| 日本综合字幕| 国产精品自拍网| 欧美jizz18| 91在线直播亚洲| 久久久国产精品入口麻豆 | 久久久久久毛片免费看| 国产精品一区二区三区在线 | 成人免费观看在线观看| 国产69精品久久久久9999| heyzo在线播放| 97超级碰在线看视频免费在线看| av中文在线资源库| 91精品国产91久久久久福利| 神马久久午夜| 国产成人一区二区在线| 欧美123区| 成人性生交大片免费看小说| 警花av一区二区三区| 99国产超薄肉色丝袜交足的后果| 视频精品一区二区三区| 国产精品一区而去| 免费看av成人| 亚洲精品国产一区| 欧美 日韩 国产精品免费观看| 日韩在线视频在线| 国产精品久久久久久久久久妞妞| 超碰网在线观看| 久久精品国产一区二区三区免费看 | 一个人www视频在线免费观看| 国产mv久久久| 日韩国产91| 国产欧美一区二区三区不卡高清| 综合亚洲色图| 中文字幕成人一区| 狠狠入ady亚洲精品| 茄子视频成人免费观看| 久久99精品国产麻豆婷婷| 好吊操视频这里只有精品| 久久午夜色播影院免费高清 | 天堂av网在线| 日韩视频免费在线| 操人在线观看| 成人a在线视频| 国产精品香蕉| 伊人情人网综合| 99热精品在线观看| 在线看免费毛片| 91免费小视频| 青娱乐国产在线视频| 色婷婷一区二区三区四区| 国产视频www| 亚洲男人av电影| 污污的网站在线免费观看| 国产91免费看片| 丁香五月缴情综合网| 少妇免费毛片久久久久久久久 | 99久久国产免费| 亚洲欧美日韩直播| heyzo在线播放| 成人黄色av免费在线观看| 日本国产精品| 黑人巨茎大战欧美白妇| 久久久久久黄| 精品国产av色一区二区深夜久久 | 你懂得影院夜精品a| 99久久伊人精品影院| 日韩免费在线| 日本精品一区二区三区四区| 国产精品911| 女人18毛片毛片毛片毛片区二 | 日本一区二区三区免费乱视频 | 看黄网站在线| 国产精品av在线| 夜夜躁狠狠躁日日躁2021日韩| 高清无码一区二区在线观看吞精| 蜜臀国产一区二区三区在线播放| 日本少妇毛茸茸| 亚洲制服丝袜在线| 99久久精品国产成人一区二区 | 日韩精品一二区| 人妻在线日韩免费视频| 亚洲影视在线观看| 999国产精品视频免费| 日韩在线视频中文字幕| 另类专区亚洲| 欧美色欧美亚洲另类七区| 一区在线免费观看| 樱花草www在线| 国产精品毛片久久久久久久| 潘金莲一级淫片aaaaaa播放| 日韩国产一区三区| 精品众筹模特私拍视频| 亚洲最大成人在线| 中文字幕一区二区av| 97超碰人人爽| 亚洲人精品午夜| 国产又粗又黄又爽视频| 日韩中文视频免费在线观看| 日韩欧美一区二区三区在线观看| 欧美区高清在线| 日韩电影免费在线观看网站| 亚洲精品午夜视频| 欧美日韩一级视频| 一广人看www在线观看免费视频| 国产精品免费电影| 国产精品久久久久久影院8一贰佰| 日日躁夜夜躁aaaabbbb| 国产精品麻豆视频| 97人妻精品一区二区三区软件| 亚洲视频自拍偷拍| 激情小说亚洲| 女同性恋一区二区| 成人国产视频在线观看| 免费毛片一区二区三区| 国产视频精品久久久| 在线观看精品| 日本一区二区免费高清视频| 国产一区二区三区久久久| 欧美精品入口蜜桃| 亚洲国产日韩欧美在线动漫| 涩涩网在线视频| 亚洲aⅴ天堂av在线电影软件| 九九在线精品视频| 久久在线视频精品| 国产网站欧美日韩免费精品在线观看| 久久91导航| 日本高清xxxx| 久久综合网色—综合色88| 中文字幕 国产| 久久69精品久久久久久国产越南| 国产精品视频3p| 手机看片福利日韩| 亚洲精品成人少妇| 日本一级在线观看| 国产一区二区在线免费视频| 在线看片一区| 国产免费嫩草影院| 亚洲国产精品中文| av成人在线看| 和岳每晚弄的高潮嗷嗷叫视频| 国产亚洲污的网站| 不卡av中文字幕| 国产成人精品电影久久久| 欧美在线日韩| 午夜精产品一区二区在线观看的| 欧美一区二区三区免费| 欧美7777| 人妻av无码专区| 国产精品久久网站|