Stories

Detail Return Return

多台服務器jenkins+自建gitlab+docker搭建項目自動化部署 - Stories Detail

一、場景:

公司有三台服務器,一台是測試服務器,一台是正式服務器,還有一台是內部服務器。測試服務器就是公司研發用來調試的服務器,正式服務器是生產環境的服務器,內部服務區是用來部署公司gitlab、jenkins、api接口文檔等服務。

目前想通過內部服務器部署jenkins+docker實現自動化部署功能,要想實現不同服務器的互通,最好通過配對的ssh公鑰和秘鑰實現,既不用輸入密碼也能保證服務的安全。

二、使用ssh-keygen生成秘鑰

1.新OpenSSH和舊OpenSSH的區別

deepseek上説低版本的jenkins,Sch 庫(Jenkins 底層 SSH 庫)可能無法解析 OpenSSH 新格式的私鑰。為了避免這種情況,在生成公鑰和私鑰的時候可以指定格式。

在生成秘鑰之前,首先展示一下什麼是OpenSSH新格式,第一行是以 -----BEGIN OPENSSH PRIVATE KEY-----開頭,如下圖所示。
image.png

之前的老格式OpenSSH私鑰,第一行是-----BEGIN RSA PRIVATE KEY-----開頭,如下圖所示。
image.png

2.指定生成的 PEM 格式密鑰對
  • 備份原密鑰(避免覆蓋,操作步驟可選)
  cd ~/.ssh
  cp id_rsa id_rsa.backup
  cp id_rsa.pub id_rsa.pub.backup
  • 生成 PEM 格式密鑰
  # OpenSSH 7.4p1 默認生成舊版 PEM 格式
  ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa

在用户~/.ssh目錄下生成 id_rsa.pub公鑰和id_rsa私鑰,如下圖所示。
image.png

  • 部署公鑰到目標服務器
  ssh-copy-id -i ~/.ssh/id_rsa.pub user@target-server

3.驗證密鑰是否生效

在jenkins服務器鏈接目標服務器,驗證不用輸入密碼可以直接登錄。

  ssh -i ~/.ssh/id_rsa user@target-server
成功標誌:

無需輸入密碼即可登錄。

失敗處理:
  • 檢查目標服務器的 ~/.ssh/authorized_keys 是否包含新公鑰。
  • 檢查目標服務器 SSH 配置是否允許公鑰認證(PubkeyAuthentication yes)。

    在/etc/ssh/ssh_config文件裏面查找PubkeyAuthentication關鍵字,將其值修改為yes
    image.png

4.創建Jenkins用户秘鑰

比如,我使用的jenkins用户執行的。因此,我需要在jenkins根目錄執行生成rsa密鑰對指令,生成jenkins用户自己的秘鑰,按照上面添加流程,將jenkins公鑰加到目標服務器的 ~/.ssh/authorized_keys 文件當中。

在jenkins用户的根目錄,執行生成秘鑰命令,生成id_rsa.pub和id_rsa密鑰對。

image.png

三、jenkins配置

1.憑據配置

在jenkins控制面板,找到憑證管理模塊,配置添加憑證。
image.png

選擇新建SSH Username with private key 類型的憑證,通過秘鑰訪問,無需輸入密碼就可以進行服務器之間的通信。
image.png

username可以隨便填寫,Private Key必須是jenkins用户生成的rsa密鑰對的私鑰。用來執行Dockerfile文件的腳本。

2.編寫Dockerfile文件
env.GIT_URL="git@git.xxxxxx.com:micro-service/xxx-api.git"
env.PROJECT_NAME="xxx-api"
env.IMAGE_NAME="xxx-api"
env.CONFIG_ADDR="/var/lib/jenkins/configs/${PROJECT_NAME}/configs"
env.DES_CONFIG_FILE="config.yaml"  //項目中的配置文件
env.SCRIPT_ADDR="sh -x /root/deploy-xxx-api.sh"


//飛書
 
def FeiShu(users){
    sh """
        curl --location --request POST 'https://open.feishu.cn/open-apis/bot/v2/hook/f170debc-cbca-xxxx-b27f-xxxxx' \
        --header 'Content-Type: application/json' \
        --data '{
            "msg_type": "interactive",
            "card": {
                "config": {
                        "wide_screen_mode": true,
                        "enable_forward": true
                },
                "elements": [{
                        "tag": "div",
                        "text": {
                                "content": "## ${JOB_NAME}作業構建信息: \\n### 構建ID: ${BUILD_ID} \\n### 構建人:${users} \\n### 作業狀態: ${currentBuild.currentResult} \\n### 運行時長: ${currentBuild.durationString} \\n###### 更多詳細信息點擊 [構建日誌](${BUILD_URL}/console) \\n",
                                "tag": "lark_md"
                        }
                }, {
                        "actions": [{
                                "tag": "button",
                                "text": {
                                        "content": "作業鏈接",
                                        "tag": "lark_md"
                                },
                                "url": "${JOB_URL}",
                                "type": "default",
                                "value": {}
                        }],
                        "tag": "action"
                }],
                "header": {
                        "title": {
                                "content": "DEVOPS作業構建信息",
                                "tag": "plain_text"
                        }
                }
            }
        } '
    """
}


node {
    try {

        if ("${BRANCH_NAME}" == 'test') {
            env.IMAGE_ADDR="registry-vpc.cn-xxxx.com/fx_test"
            env.SRC_CONFIG_FILENAME='config.yaml' //配置中心的配置文件
        }
        else if("${BRANCH_NAME}" == 'master') {
            env.SRC_CONFIG_FILENAME='config.master.yaml'
            env.IMAGE_ADDR="registry-vpc.cn-xxxx.com/fx_production"
        }
        else {
            echo 'unknow branch'
            return 1
        }
    
        echo "I am ${BRANCH_NAME}"

        stage('prepare set config files') {
            checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: '/var/lib/jenkins/configs']], submoduleCfg: [], userRemoteConfigs: [[url: 'git@git.fuxingtech.com:micro-service/configcenter.git', credentialsId: 'f4a56057-af3f-4685-84b7-c4a497d7634b']]])
            git branch: '${BRANCH_NAME}', credentialsId: 'f4a56057-af3f-xxxx-84b7-xxxxx', url: "${GIT_URL}"
            sh "cp -f ${CONFIG_ADDR}/${SRC_CONFIG_FILENAME} ${WORKSPACE}/configs/${DES_CONFIG_FILE}"
        }
    

        stage('Build & Push image') {
            sh "cd ${WORKSPACE};docker build --network host --build-arg BRANCH=${BRANCH_NAME} -t ${IMAGE_ADDR}/${IMAGE_NAME}:${BRANCH_NAME}.1.`date '+%m%d'`.${BUILD_NUMBER} ."
            sh "docker images |grep ${IMAGE_NAME}"
            sh "sh -x /var/lib/jenkins/scripts/acr-login.sh"
            sh "docker push ${IMAGE_ADDR}/${IMAGE_NAME}:${BRANCH_NAME}.1.`date '+%m%d'`.${BUILD_NUMBER}"
        }

    
        stage("exec script to deploy image") {

            if ("${BRANCH_NAME}" == 'test') {
                def remote = [:]
                remote.name = 'test'
                remote.host = '172.xx.xx.150'
                remote.port = 22
                remote.allowAnyHosts = true
                //通過withCredentials調用Jenkins憑據中已保存的憑據,credentialsId需要填寫,其他保持默認即可
                withCredentials([usernamePassword(credentialsId: '93e8ae89-xxx-4e25-9113-xxxxx', passwordVariable: 'password', usernameVariable: 'userName')]) {
                    remote.user = "${userName}"
                    remote.password = "${password}"
                }
                println(remote)
                sshCommand remote: remote, command: "${SCRIPT_ADDR} ${IMAGE_NAME} ${IMAGE_ADDR}/${IMAGE_NAME}:${BRANCH_NAME}.1.`date '+%m%d'`.${BUILD_NUMBER}"
            }
            else if("${BRANCH_NAME}" == 'master') {
                def remote = [:]
                remote.name = 'master'
                remote.host = '121.xxx.xx.112'
                remote.port = 22
                remote.allowAnyHosts = true
                //通過withCredentials調用Jenkins憑據中已保存的憑據,credentialsId需要填寫,其他保持默認即可
                withCredentials([usernamePassword(credentialsId: 'cc0a532e-xxx-4f87-9d82-xxxx', passwordVariable: 'password', usernameVariable: 'userName')]) {
                    remote.user = "${userName}"
                    remote.password = "${password}"
                }
                sshCommand remote: remote, command: "${SCRIPT_ADDR} ${IMAGE_NAME} registry.cn-xxxx.xxx.com/fx_production/${IMAGE_NAME}:${BRANCH_NAME}.1.`date '+%m%d'`.${BUILD_NUMBER}"
            }
            else {
                echo 'unknow branch'

                return 1
            }
        }
    }
    catch (Exception err) {
        echo 'test failed'
        println(err)
        currentBuild.result = 'FAILURE'
    }
    finally {
        wrap([$class: 'BuildUser']) {
            script {
                def BUILD_USER = "${env.BUILD_USER}"
                FeiShu("${BUILD_USER}")
            }
        }
    }
}
3.創建jenkins任務

image.png

4.修改任務配置

image.png

5.項目部署

image.png

user avatar ji_jason Avatar meiduyandechengzi Avatar duiniwukenaihe_60e4196de52b7 Avatar kubeexplorer Avatar yayahonghong Avatar kaika1 Avatar
Favorites 6 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.