實戰 GitOps:使用 k3s、ArgoCD 與 GitHub Actions 部署私有 Python 服務
在現代化的 DevOps 流程中,GitOps 已經成為管理 Kubernetes 應用程式的標準方式。本文將帶領你從零開始,搭建一個輕量級的 Kubernetes (k3s) 叢集,安裝 ArgoCD,並實作一套完整的 CI/CD 流程,將一個「私有網路性質」的 Python 服務自動部署到叢集中。
🎯 目標架構
我們的目標是建立以下流程:
- 開發 (Dev): 在本地編寫 Python 程式碼。
- 持續整合 (CI): 推送程式碼到 GitHub,觸發 GitHub Actions 進行 Docker Image 建置與推送。
- 版本更新: CI 流程自動修改 Git Repository 中的 Kubernetes Manifest,更新 Image Tag。
- 持續部署 (CD): ArgoCD 偵測到 Git 變更,自動將新版本同步到 k3s 叢集。
- 私有服務 (Private Service): 部署的 Python 服務僅在叢集內部可訪問,不對公網開放。
TIP為什麼選擇 k3s? k3s 是由 Rancher 開發的輕量級 Kubernetes 發行版,二進制檔案不到 100MB,卻通過了 CNCF 認證。它非常適合開發環境、邊緣運算以及資源有限的伺服器。
🛠️ 環境準備
在開始之前,你需要:
- 一台 Linux 伺服器: 可以是雲端 VPS (如 AWS EC2, GCP, DigitalOcean) 或本地虛擬機。建議至少 2CPU, 4GB RAM。
- GitHub 帳號: 用於存放程式碼與設定檔。
- 基本知識: 對 Docker 與 Kubernetes 有基礎了解。
Step 1: 搭建 k3s 輕量級 Kubernetes 叢集
1. 安裝 k3s
在你的 Linux 伺服器上執行以下指令:
curl -sfL https://get.k3s.io | sh -2. 驗證安裝
安裝完成後,確認節點狀態:
sudo kubectl get nodes如果看到狀態為 Ready,恭喜你,Kubernetes 叢集已經準備就緒!
3. 設定權限 (可選)
為了方便操作,可以將 kubeconfig 複製到使用者目錄:
mkdir -p ~/.kubesudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/configsudo chown $(id -u):$(id -g) ~/.kube/configStep 2: 安裝與設定 ArgoCD
ArgoCD 是專為 Kubernetes 設計的宣告式 GitOps 持續交付工具。
1. 建立 Namespace 並安裝
kubectl create namespace argocdkubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml2. 等待 Pod 啟動
檢查 ArgoCD 的所有組件是否都已順利執行:
kubectl get pods -n argocd3. 取得初始密碼
ArgoCD 會自動產生一個初始的 admin 密碼:
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo4. 存取 UI
由於我們是在伺服器上安裝,可以透過 Port Forwarding 將 UI 導出:
# 在伺服器上執行 (如果你是本機連線)kubectl port-forward svc/argocd-server -n argocd 8080:443現在你可以透過瀏覽器訪問 https://<伺服器IP>:8080,帳號為 admin,密碼為上一步取得的字串。
NOTE遠端存取提示 若你在遠端伺服器操作且無法直接瀏覽 localhost,建議使用 SSH Tunnel:
ssh -L 8080:localhost:8080 user@your-server-ip然後在本地瀏覽器開啟https://localhost:8080。
Step 3: 準備 Python 服務 (Private Service)
我們要建立一個簡單的 Python Web 服務,並將其設計為僅供內部存取。
1. 專案結構
在你的電腦上建立一個新資料夾,結構如下:
my-python-service/├── app.py├── requirements.txt├── Dockerfile└── k8s/ ├── deployment.yaml └── service.yaml2. 應用程式程式碼 (app.py)
使用 Flask 建立一個簡單的 API:
from flask import Flaskimport os
app = Flask(__name__)
@app.route('/')def hello(): version = os.environ.get('VERSION', 'v1') return f"Hello from Private Python Service! Version: {version}\n"
if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)3. 依賴檔 (requirements.txt)
flask4. Dockerfile
FROM python:3.9-slim
WORKDIR /appCOPY requirements.txt .RUN pip install -r requirements.txtCOPY app.py .
CMD ["python", "app.py"]5. Kubernetes Manifests (定義私有服務)
這是關鍵部分。為了確保服務是私有的,我們將 Service Type 設為 ClusterIP (預設值)。
k8s/deployment.yaml
apiVersion: apps/v1kind: Deploymentmetadata: name: python-private-service namespace: defaultspec: replicas: 1 selector: matchLabels: app: python-private template: metadata: labels: app: python-private spec: containers: - name: app image: ghcr.io/<YOUR_GITHUB_USER>/python-private-service:latest ports: - containerPort: 5000 env: - name: VERSION value: "v1"IMPORTANT請務必將
<YOUR_GITHUB_USER>替換為你的 GitHub 使用者名稱。同時,為了讓 ArgoCD 能夠管理版本,我們將在 CI 階段動態更新image的 tag。
k8s/service.yaml
apiVersion: v1kind: Servicemetadata: name: python-private-service namespace: defaultspec: selector: app: python-private ports: - protocol: TCP port: 80 targetPort: 5000 type: ClusterIP # 關鍵:ClusterIP 確保不對外公開 IPStep 4: 處理私有 Registry 權限 (重要)
如果你的 GitHub Repository 是私有的,或者你的 Container Registry (GHCR) 權限設為私有,Kubernetes 將無法直接拉取 Image。你需要建立一個 imagePullSecret。
1. 產生 GitHub PAT
前往 GitHub Settings > Developer settings > Personal access tokens 產生一個新的 Token (Classic),權限需包含 read:packages。
2. 在 k3s 中建立 Secret
在伺服器上執行:
kubectl create secret docker-registry ghcr-secret \ --docker-server=ghcr.io \ --docker-username=<YOUR_GITHUB_USER> \ --docker-password=<YOUR_PAT_TOKEN> \ --docker-email=<YOUR_EMAIL>3. 修改 Deployment
在 k8s/deployment.yaml 中加入 imagePullSecrets:
spec: imagePullSecrets: - name: ghcr-secret containers: ...WARNING常見錯誤 如果你看到 Pod 狀態為
ImagePullBackOff或ErrImagePull,通常就是忘記設定這個 Secret,或者是 Token 權限不足。
Step 5: 建立 GitOps 流程 (CI - GitHub Actions)
我們需要設定 GitHub Actions 來自動建置 Docker Image,並將新的 Image Tag (SHA) 寫回 Git Repo,這樣 ArgoCD 才會知道有新版本。
1. 建立 Workflow 檔案
在專案根目錄建立 .github/workflows/ci.yaml:
name: CI/CD Pipeline
on: push: branches: [ "main" ] paths-ignore: - 'k8s/**' # 避免修改 k8s yaml 後再次觸發 CI 造成無限迴圈
env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }}
jobs: build-and-push: runs-on: ubuntu-latest permissions: contents: write # 需要寫入權限來更新 manifest packages: write
steps: - name: Checkout repository uses: actions/checkout@v3
- name: Log in to the Container registry uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image uses: docker/build-push-action@v4 with: context: . push: true tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
- name: Update Kubernetes Manifest run: | # 設定 Git 使用者 (用於 commit) git config --global user.name "GitHub Actions" git config --global user.email "actions@github.com"
# 更新 deployment.yaml 中的 image tag # 這裡使用 sed 來替換 image 欄位 sed -i "s|image: .*|image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}|g" k8s/deployment.yaml
# 提交變更 git add k8s/deployment.yaml git commit -m "chore: update image tag to ${{ github.sha }}" git pushCAUTION權限設定 請確保你的 GitHub Repository 的
Settings > Actions > General > Workflow permissions已設定為 Read and write permissions,否則git push會失敗。
Step 6: 設定 ArgoCD (CD)
現在回到 ArgoCD UI 來設定自動部署。
1. 建立 Application
在 ArgoCD 介面上點擊 New App:
- Application Name:
python-private-service - Project:
default - Sync Policy:
Automatic(勾選 Prune Resources 與 Self Heal)
Source:
- Repository URL: 輸入你的 GitHub Repo URL
- Path:
k8s
Destination:
- Cluster URL:
https://kubernetes.default.svc - Namespace:
default
2. 觀察同步
當你在本地修改程式碼並 push 到 GitHub 後:
- GitHub Actions 會自動打包新 Image。
- Actions 會修改
k8s/deployment.yaml的 Image Tag。 - ArgoCD 偵測到 Git 變更,自動將新版本同步到 k3s 叢集。
Step 7: 驗證與測試
因為這是私有服務,我們無法直接透過瀏覽器或 curl 從外部訪問它。我們必須進入叢集內部進行測試。
1. 啟動一個臨時 Pod 來測試
kubectl run -it --rm debug-pod --image=curlimages/curl --restart=Never -- sh2. 在叢集內部呼叫服務
在臨時 Pod 的 Shell 中,嘗試連線到我們的服務:
# 格式為: http://<service-name>.<namespace>curl http://python-private-service.default如果看到以下回應,代表成功了!
Hello from Private Python Service! Version: v13. 確認外部無法存取
你可以嘗試在伺服器外部或伺服器本身的 Shell 中執行 curl http://<Service-IP>,應該都是無法連線的。這證實了我們的服務成功隔離在私有網路中。
結語
恭喜!你已經成功搭建了一套完整的 GitOps 流程。
- k3s: 提供了輕量穩定的運行環境。
- Private Service: 透過 ClusterIP 確保了服務的隔離性。
- GitHub Actions: 自動化了建置與版本更新 (CI)。
- ArgoCD: 確保了叢集狀態與 Git 保持一致 (CD)。
TIP進階學習 在真實的生產環境中,你可能需要考慮使用 Helm 或 Kustomize 來管理更複雜的環境配置,或者使用 ArgoCD Image Updater 來處理自動更新,以進一步優化流程。