---
title: "整合Windows Docker Desktop、Jenkins in Docker、GitHub SSH憑證與Docker Image建置佈署的開發環境"
description: "整合 Jenkins in Docker、GitHub SSH 憑證與 Windows Docker Desktop，實現 Git Push 後自動建置並部署 Docker Image 的 CI/CD 流程。"
canonical_url: "https://blog.markkulab.net/post/docker-jenkins-build-docker-image-and-github-integration-20230703"
author: "Mark Ku"
author_url: "https://blog.markkulab.net/author/mark-ku"
site: "Mark Ku's Blog"
date_published: "2023-07-03 01:01:35 +0800"
category: "DevOps"
tags: ["jenkins", "docker", "github", "cicd", "ssh", "devops", "pipeline", "automation"]
language: "zh-TW"
license: "CC BY 4.0"
license_url: "https://creativecommons.org/licenses/by/4.0/"
attribution: "轉載或引用請註明作者並附上原文連結"
---

# 整合Windows Docker Desktop、Jenkins in Docker、GitHub SSH憑證與Docker Image建置佈署的開發環境

## 目的
先前用 [Powershell 遠端建置測試機的 DockerDesktop](https://blog.markkulab.net/remote-build-docker-image-with-powershell/)，開發人員一多後，發現太麻煩了，最後還是弄了Jenkins，並整合 Github 憑證及遠端佈署 DockerDesktop

## 開發環境說明
預期開發者 Push Commit 後，Jenkins 自動將 Github Repo 拉下來，並自動建置，最後佈署到最終的 Windows Docker Desktop 主機。

## 首先，建立 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解鎖畫面，提示輸入管理員初始密碼](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/uy3OV4o.png)
### 接著，安裝建議的插件

![Jenkins自訂外掛介面，選擇安裝建議外掛](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/IWKJnTV.png)
### 等待安裝完成，就能使用 localhost:8080 進入 Jenkins 網頁
![Jenkins Getting Started 頁面顯示建議插件列表](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/tzP40Nk.png)

### 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](https://github.com/settings/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私鑰憑證設定畫面](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/eKKkFOm.png)

### Create 後會取得一個 ID 之後會將複製到 pipeline 中

![Jenkins 全域憑證頁面顯示 SSH 私鑰](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/JeIwzXP.png)

### 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建立新工作介面，輸入NextJS.MemberSite專案名稱](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/HbhNSug.png)

### 設定 Github hook trigger 
![Jenkins設定GitHub專案與Hook觸發器](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/Dpp0ywG.png)

### 撰寫
```
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:  'git@github.com: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 專案的 Stage View 介面](https://blog.markkulab.net/content/markku/posts/docker-jenkins-build-docker-image-and-github-integration-20230703/images/WaHng5f.png)

### 補充 - 解决Linux环境非root用户无法运行docker报错unix:///var/run/docker.sock: connect: permission denied 
1. **新增服務文件**：
   ```bash
   sudo vim /etc/systemd/system/docker-sock-permissions.service
   ```

2. **添加以下內容**：
   ```ini
   [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. **啟用服務**：
   ```bash
   sudo systemctl enable docker-sock-permissions.service
   ```

4. **啟動服務**（或等待下次開機自動執行）：
   ```bash
   sudo systemctl start docker-sock-permissions.service
   ```


## 參考資料
[參考資料1](https://medium.com/@nestor.panu/building-an-asp-net-6-application-on-docker-based-jenkins-d6c8aaf94756)  
[參考資料2](https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=jenkins%20docker%20github&fenlei=256&oq=jenkins%2520docker&rsv_pq=94ae33af001910d3&rsv_t=b7b8i%2Bsui%2B0AJ%2FMYfQlpp%2FhXCfNXIDDSFm5zUnkMf2BWDdx8uxSiQ184PmM&rqlang=cn&rsv_dl=tb&rsv_enter=1&rsv_btype=t&inputT=2203&rsv_sug3=47&rsv_sug1=11&rsv_sug7=100&rsv_sug2=0&rsv_sug4=2374)

## 備忘錄 - 找回 admin 初始密碼
```
cat /var/jenkins_home/secrets/initialAdminPassword
```

---

## 關於本文與作者

本文出自 [Mark Ku's Blog](https://blog.markkulab.net/post/docker-jenkins-build-docker-image-and-github-integration-20230703)

授權條款： [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) — 轉載或引用請註明作者並附上原文連結

### 關於作者

**[Mark Ku](https://blog.markkulab.net/author/mark-ku)** — Software Solution Provider

- 10+ 年資深軟體工程師，現為 AI 應用 Builder
- 專注大型平台架構設計，從北美電商到AI SaaS訂閱收費系統
- 結合 AI Agent 與自動化，打造高效可演進的產品技術基礎

### 作者開發的免費工具

以下工具皆可免費使用：

- [免費 PDF 簽名工具](https://blog.markkulab.net/tools/pdf-sign): 線上 PDF 簽名工具，瀏覽器內完成手繪、打字、上傳簽名，可拖曳放置、縮放、下載。所有處理都在你的裝置完成，檔案不會上傳。
- [VS Code Refactory](https://blog.markkulab.net/tools/refactory): Refactory 是一款 VS Code 重構擴充套件：34 個重構動作、37 條 code smell 檢查、Code Health 儀表板、18 種語言、534 支測試。懂你的專案慣例：介面放哪、DI 註冊寫在哪、'use client' 該不該加；還會用 git 修改頻率 × 複雜度排出「該先修哪個檔案」，並一鍵把壞味道交給你自己電腦上的 Claude Code 修。免費使用，原始碼不離開你的機器。
- [DB-Kit 資料庫管理工具](https://blog.markkulab.net/tools/db-kit): DB-Kit 是一個用 Tauri + Rust + React 打造的輕量跨平台資料庫管理工具，用單一一致的介面同時管理 MySQL、MariaDB、PostgreSQL、SQL Server、Oracle、SQLite、MongoDB、Redis、Kafka、Elasticsearch 與 RabbitMQ 十一種資料來源：連線密碼以 OS keychain 加密、SSH Tunnel、完整 CRUD、視覺化查詢建構器、多結果集同時顯示、跨連線資料傳輸與比對同步、Excel / CSV 匯入匯出、執行計畫視覺化、ER 圖、排程備份、SQL 壓力測試（p50～p99 延遲百分位）、15 條規則的 SQL 審查、Kafka 訊息瀏覽與監控告警；繁中 / 英文雙語介面，內建 AI 助手（自然語言生成 SQL、AI 審查與調校建議）與命令列工具 dbk。免費開源（MIT），提供 Windows / macOS / Linux 安裝檔。
- [VS Code Super Mermaid](https://blog.markkulab.net/tools/super-mermaid): Super Mermaid 是一款 VS Code 擴充套件：開箱即用的漂亮 Mermaid 圖表，自動上色、即時預覽、滑鼠平移縮放、PNG / SVG 高解析匯出，內建 21 種範本與多種主題。免費開源（MIT）。
- [React Super Mermaid](https://blog.markkulab.net/tools/react-super-mermaid): react-super-mermaid 是一個開源 React 元件庫：一行 <MermaidViewer> 即可渲染漂亮的 Mermaid 圖表，內建 colorful / sketch 主題、平移縮放、圖內搜尋、SVG / PNG 高解析匯出。輕量、SSR 安全、完整 TypeScript 型別。免費開源（MIT）。
- [Jira / Confluence Super Mermaid](https://blog.markkulab.net/tools/jira-super-mermaid): Atlassian Forge app：在 Jira issue 與 Confluence 內文直接寫 Mermaid 語法，畫流程圖、時序圖、狀態機與甘特圖。11 種圖表、SVG / PNG 匯出、明暗主題、完整中日韓文字支援。取得 Runs on Atlassian 資格：圖表存在你自己的站台，app 不呼叫任何第三方服務。免費，即將上架 Atlassian Marketplace。
- [Mermaid 線上預覽](https://blog.markkulab.net/tools/mermaid-preview): 在瀏覽器裡寫 Mermaid、即時看圖，整張圖表壓進網址就能分享。免註冊、不上傳伺服器，相容 mermaid.live 的分享連結。
- [React Intl Phone Number](https://blog.markkulab.net/tools/react-intl-phone-number): react-intl-phone-number 是一個開源 React 元件：framework-agnostic、不依賴 antd，提供 E.164 進出、可搜尋國旗 / 國碼下拉、可配置驗證等級（strict / mobile-strict / loose）、可主題化 CSS 與 i18n，電話邏輯由 google-libphonenumber 驅動。輕量、完整 TypeScript 型別。免費開源（MIT）。
- [Uptime Kuma Cluster](https://blog.markkulab.net/tools/uptime-kuma-cluster): 把單機版 Uptime Kuma 改造成高可用叢集：OpenResty + Lua 智慧負載平衡、MariaDB 共享狀態、健康檢查與自動 Failover，附叢集管理 REST API，一行 Docker Compose 啟動。免費開源（MIT）。
- [特教專案](https://blog.markkulab.net/education): 為特殊教育學生製作的學習教材

### 每日 Podcast

- [科技新鮮事](https://blog.markkulab.net/category/tech-news): 每日精選 AI 與科技趨勢，透過語音摘要快速掌握最新技術動態，涵蓋 AI 應用、軟體架構、DevOps 與工程實戰。 — RSS: https://blog.markkulab.net/feed.xml
- [AI股市蝦聊](https://blog.markkulab.net/category/ai-stock-chat): 每個交易日用 AI 分析台股盤勢，以雙人對話聊當天的盤中觀察與隔日預測。 — RSS: https://blog.markkulab.net/ai-stock-chat/feed.xml

### 電子報

[訂閱電子報](https://blog.markkulab.net/subscribe) — 第一時間收到新文章通知，無垃圾信、隨時可取消訂閱。
