Mark Ku's Blog

目的

先前用 Powershell 遠端建置測試機的 DockerDesktop,開發人員一多後,發現太麻煩了,最後還是弄了Jenkins,並整合 Github 憑證及遠端佈署 DockerDesktop

開發環境說明

開發者 Push Commit 後,Github 會知 Jenkins 執行Job,並將 Github Repo 拉下來,並自動建置,最後佈署到最終的 Windows Docker Desktop 主機。 兩種常見的Jenkins 佈署Docker 的方式 P.S. Watch tower 並不會主動建建立容器應用,只會抽換容器應用,第一次得自己手動建立。

首先,建立 Jenkins 環境

開啟 Powershell ,建立資料夾作為 Jenkins 的工作目錄

$workspacePath = "C:\jenkins_workspace"
New-Item -ItemType Directory -Path $workspacePath -Force | Out-Null

執行以下指令,Docker 運行 Jenkins 容器,並將 workspace 掛載到本地磁碟

docker run -d -p 8080:8080  -p 50000:50000 -v ${hostWorkspacePath}:/var/jenkins_home/workspace  -v /var/run/docker.sock:/var/run/docker.sock --name jenkins --restart=always jenkins/jenkins:lts 

P.S. hostWorkspacePath 為宿主主機的資料夾路徑

看到這畫面,要進去容器拿這密碼

Jenkins解鎖畫面要求管理員密碼
Jenkins解鎖畫面要求管理員密碼

接著,安裝建議的插件

Jenkins安裝建議外掛程式的設定畫面
Jenkins安裝建議外掛程式的設定畫面

等待安裝完成,就能使用 localhost:8080 進入 Jenkins 網頁

Jenkins Getting Started頁面顯示插件列表
Jenkins Getting Started頁面顯示插件列表

Manage Jenkins > Plugins >Available plugins,安裝常用套件

  • SSH
  • Publish Over SSH
  • Msbuild
  • .NET SDK Support
  • Office 365 Connector Version
  • PowerShell
  • Docker
  • Docker Pipeline
  • HTTP Request
  • Stage View
  • Pipeline Utility Steps
  • Pipeline: Input Step

因為我比較熟 docker 指令,用以下指令在 Jenkins 容器中安裝 Docker

docker logs -f jenkins // 獲得 init password 
docker exec -it -uroot jenkins bash
apt-get update && apt-get -y install apt-transport-https ca-certificates curl gnupg2 software-properties-common && curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") $(lsb_release -cs) stable" && apt-get update && apt-get -y install docker-ce

Jenkins Github 全域憑證設定

首先,透過 docker 指令連入 Jenkins 容器

docker exec -it -uroot jenkins bash

安裝 docker

apt-get update
apt-get install -y apt-transport-https ca-certificates curl gnupg2 software-properties-common
curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
echo "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list
apt-get update
apt-get install -y docker-ce-cli

接著,產生ssh key,一直按 Enter 就好

ssh-keygen -t rsa -C "root" 

讀取產生的公鑰,複製到github SSH and GPG keys 設定

cat /root/.ssh/id_rsa.pub

讀取產生的私鑰,複製到 Jenkins 全域設定,Manage Jenkins > Credentials > System > Global credentials (unrestricted) > Add credentials ( -----BEGIN OPENSSH PRIVATE KEY-----也要一起複製) > Create

cat /root/.ssh/id_rsa
Jenkins新增SSH私鑰憑證的設定頁面
Jenkins新增SSH私鑰憑證的設定頁面

Create 後會取得一個 ID 之後會將複製到 pipeline 中

Jenkins全域憑證頁面顯示SSH金鑰
Jenkins全域憑證頁面顯示SSH金鑰

Manage Jenkins > Security > Git Host Key Verification Configuration > No verification

到 Jenkins 的宿主主機,賦與相關資料夾

sudo chmod -R 777 /var/jenkins_home/
sudo chown $USER /var/run/docker.sock
sudo gpasswd -a $USER docker
newgrp docker

建立新的建置工作

回到 Dashboard > New Item > Pipeline

Jenkins新建工作介面,輸入專案名稱並選擇Pipeline
Jenkins新建工作介面,輸入專案名稱並選擇Pipeline

設定 Github hook trigger

Jenkins設定GitHub專案與Hook觸發器
Jenkins設定GitHub專案與Hook觸發器

撰寫

properties([pipelineTriggers([githubPush()])])
pipeline {
    agent any 

    environment {
        tag = ':latest'
        imageShortName = 'next-ap'
        imageName = "${imageShortName}${tag}"
        containerName = "${imageShortName}-1"
        containerUrl = "192.168.50.49:2375"
        dockerfile = "./Dockerfile"
        port = "30000:80"
    }   
   
    stages {
        stage("GitHub Pull") {
             steps {
                git branch: 'main', 
                credentialsId: 'b2ef50dd-xxxx-xxx-a4ef-xxx', 
                url:  '[email protected]:markku636/ec.git/'
            }
            
        }


        stage('Stop containers') {
            steps {
                script {
                    containerStatus = sh(script: "docker -H=\"${containerUrl}\" ps -a --filter=name=${containerName} -q", returnStdout: true).trim()
                    if (containerStatus != '') {
                        echo "Stopping container ${containerName}"
                        sh "docker -H=\"${containerUrl}\" stop ${containerName}"
                    } else {
                        echo "Container ${containerName} does not exist"
                    }
                }
            }
        }

        stage('Remove containers') {
            steps {
                script {
                    containerStatus = sh(script: "docker -H=\"${containerUrl}\" ps -a --filter=name=${containerName} -q", returnStdout: true).trim()
                    if (containerStatus != '') {
                        echo "Removing container ${containerName}"
                        sh "docker -H=\"${containerUrl}\" rm -f ${containerName}"
                    } else {
                        echo "Container ${containerName} does not exist"
                    }
                }
            }
        }

        stage('Remove image') {
            steps {
                script {
                    existingImages = sh(script: "docker -H=\"${containerUrl}\" images --filter=reference='${imageName}' -q", returnStdout: true).trim()
                    if (existingImages != '') {
                        echo "[Removing image] Removing the existing image.."
                        sh "docker -H=\"${containerUrl}\" rmi -f '${imageName}'"
                    } else {
                        echo "[Removing image] The image does not exist"
                    }
                }
            }
        }
        
        stage('Build image remotely') {
            steps {
                sh 'docker -H="${containerUrl}" build -t "${imageName}" . -f "${dockerfile}"'
            }
        }

        stage('Create and start container application') {
            steps {
                sh 'docker -H="${containerUrl}" run -d --name "${containerName}" --restart=always -p "${port}" "${imageShortName}"'
            }
        }
        
    }
}

設定這麼久,終於大功告成,自從有了容器化,建置開發環境,越來越簡單及方便遷移。

Jenkins NextJS.MemberSite專案的Pipeline
Jenkins NextJS.MemberSite專案的Pipeline

補充 - 解决Linux环境非root用户无法运行docker报错unix:///var/run/docker.sock: connect: permission denied

  1. 新增服務文件

    sudo vim /etc/systemd/system/docker-sock-permissions.service
    
  2. 添加以下內容

    [Unit]
    Description=Set permissions for Docker socket
    After=docker.service
    
    [Service]
    Type=oneshot
    ExecStart=/bin/chmod 666 /var/run/docker.sock
    RemainAfterExit=true
    
    [Install]
    WantedBy=multi-user.target
    
  3. 啟用服務

    sudo systemctl enable docker-sock-permissions.service
    
  4. 啟動服務(或等待下次開機自動執行):

    sudo systemctl start docker-sock-permissions.service
    

參考資料

參考資料1
參考資料2

備忘錄 - 找回 admin 初始密碼

cat /var/jenkins_home/secrets/initialAdminPassword

作者

Mark Ku

擁有 10+ 年經驗的資深軟體工程師,現為 AI 應用 Builder,專注於大型平台架構與簡化複雜系統設計,從電商系統到訂閱與收費平台,結合 AI Agent、AI 整合與自動化開發,打造高效率且可持續演進的產品技術基礎。閱讀更多

覺得這篇有幫助?

作者做的免費工具、每日 Podcast 與電子報,都在這裡。

Mark Ku · 本文採用 CC BY 4.0 授權,轉載請註明作者並附上原文連結。

留言

訂閱電子報

訂閱後即時收到新文章通知,不錯過任何技術分享。

提交即表示同意接收電子報,隨時可

熱門文章

View all
Mark Ku
··597

Oracle Cloud 永久免費方案 Linux 主機及固定 IP :0 元打造雲端解決方案

Oracle Cloud 永久免費方案 Linux 主機及固定 IP :0 元打造雲端解決方案
Mark Ku
··469

告別 Postman 收費陷阱!開源 Git 原生 API 測試神器 Bruno 實戰指南

告別 Postman 收費陷阱!開源 Git 原生 API 測試神器 Bruno 實戰指南
Mark Ku
··327

一款免費開源類似於 Notion 類知識庫系統 — Outline Wiki 佈署與備份全攻略

一款免費開源類似於 Notion 類知識庫系統 — Outline Wiki 佈署與備份全攻略
Mark Ku
··239

打造高效 API 管理平台:從 0 開始部署 Kong Gateway - Part 1

打造高效 API 管理平台:從 0 開始部署 Kong Gateway - Part 1
Mark Ku
··233

訓練自己的 AI 語音:硬體門檻、開源模型比較與 LoRA 微調

訓練自己的 AI 語音:硬體門檻、開源模型比較與 LoRA 微調
Mark Ku
··220

在 Ubuntu 上設置 Samba 來共享資料夾,讓 Windows 11 用戶可以存取

在 Ubuntu 上設置 Samba 來共享資料夾,讓 Windows 11 用戶可以存取