版本控制和 CI/CD 是所有 infra 操作的前提——程式碼要有地方存、變更要能被 review、build 和 deploy 要自動化。正常環境裡這些由 GitHub + GitHub Actions 提供,斷網環境裡這兩個服務都不存在,需要在內網自建替代品。

GitLab CE vs Gitea:選型判準

兩個主流的自建版本控制方案定位不同:

維度GitLab CEGitea
定位VCS + CI + Container Registry + Issue Tracker 一體純 VCS(輕量 Git 伺服器)
資源需求4GB+ RAM、推薦 8GB512MB RAM 即可運作
CI 內建GitLab CI(.gitlab-ci.yml無(搭配 Drone / Woodpecker / Jenkins)
Container Registry內建無(搭配 Harbor)
安裝複雜度中(Omnibus 包裝簡化了安裝、但設定項多)低(單一二進位檔、啟動即可用)
維護負擔高(PostgreSQL、Redis、Sidekiq 都在裡面)低(SQLite 或 MySQL、無背景服務)

選型判準是團隊規模和需要的功能範圍。5 人以下、只需要 VCS + 輕量 CI 的團隊,Gitea + Drone 的組合維護成本低。10 人以上、需要 MR review + CI pipeline + Container Registry 一站到位的團隊,GitLab CE 的整合度值得它的資源消耗。

接下來以 GitLab CE 為主線說明(功能最完整),Gitea 的差異在各段附註。

GitLab CE 離線安裝

GitLab Omnibus 包把所有依賴打包成單一安裝檔,不需要在目標機器上 apt install 任何前置套件。

在外網機器下載安裝包

1# Ubuntu/Debian
2wget https://packages.gitlab.com/gitlab/gitlab-ce/packages/ubuntu/jammy/gitlab-ce_17.0.0-ce.0_amd64.deb/download.deb
3
4# RHEL/CentOS
5wget https://packages.gitlab.com/gitlab/gitlab-ce/packages/el/9/gitlab-ce-17.0.0-ce.0.el9.x86_64.rpm/download.rpm

把下載的 .deb.rpm 透過內容搬運機制(USB、光碟、跨邊界傳輸站)帶進斷網環境。

在斷網機器安裝

1# Ubuntu/Debian
2sudo dpkg -i gitlab-ce_17.0.0-ce.0_amd64.deb
3
4# RHEL/CentOS
5sudo yum localinstall gitlab-ce-17.0.0-ce.0.el9.x86_64.rpm

離線設定

安裝後編輯 /etc/gitlab/gitlab.rb,把所有外部連線關掉:

 1# 設定內部域名(不是公網域名)
 2external_url 'https://gitlab.internal.example.com'
 3
 4# 關閉 Gravatar(頭像服務、需要外網)
 5gitlab_rails['gravatar_enabled'] = false
 6
 7# 關閉 usage ping(回報使用統計到 GitLab Inc)
 8gitlab_rails['usage_ping_enabled'] = false
 9
10# 關閉 version check
11gitlab_rails['gitlab_check_on_connect'] = false
12
13# 如果沒有內部 SMTP,用 sendmail 或關閉 email
14gitlab_rails['smtp_enable'] = false
15
16# TLS 憑證用內部 CA 簽發
17nginx['ssl_certificate'] = "/etc/gitlab/ssl/gitlab.crt"
18nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/gitlab.key"
1sudo gitlab-ctl reconfigure

Gitea 的離線安裝更簡單:下載單一二進位檔 gitea、設定 app.ini、用 systemd 管理即可。

升級策略

GitLab CE 的升級包也要從外部下載帶進來。升級前先備份(gitlab-backup create),升級路徑要按 GitLab 的版本跳級規則——不能任意跳版、某些大版本之間需要中繼版本。在斷網環境裡,每次升級要預先規劃中繼版本、一次帶進所有需要的安裝包。

CI Runner 離線設定

CI pipeline 在斷網環境裡跑的最大差異是 runner 不能即時拉依賴。

Runner 安裝與註冊

1# 下載 runner 二進位檔(外網下載、帶進來)
2# https://docs.gitlab.com/runner/install/linux-manually.html
3
4sudo gitlab-runner register \
5  --url https://gitlab.internal.example.com \
6  --token $RUNNER_TOKEN \
7  --executor docker \
8  --docker-image alpine:3.20

Executor 選擇

Executor隔離性前置條件斷網適用度
shell低(直接跑在 runner 機器上)高(最簡單)
docker高(每個 job 一個容器)需要 Docker + 預拉 image中(image 管理成本)
kubernetes高(每個 job 一個 pod)需要 K8s cluster低(斷網 K8s 維護重)

斷網環境推薦 shell executor(最少依賴)或 docker executor 搭配預拉好的 image。

Docker executor 的 image 管理

Docker executor 的每個 job 都基於一個 base image。斷網環境裡這些 image 必須預先存在於內網的 private registry

1# runner 的 /etc/docker/daemon.json 指向內部 registry
2{
3  "insecure-registries": ["registry.internal:5000"],
4  "registry-mirrors": ["https://registry.internal:5000"]
5}

CI pipeline 裡用到的每個 image(build 用的 golang/node/php、lint 用的 tflint/checkov、deploy 用的 awscli)都要事先搬進內部 registry。

依賴快取

沒有 npm registry / PyPI / Maven Central 可以拉,CI job 的依賴安裝必須用本地來源:

1# .gitlab-ci.yml — 使用內部 Nexus 作為套件來源
2variables:
3  NPM_CONFIG_REGISTRY: "https://nexus.internal/repository/npm-proxy/"
4  PIP_INDEX_URL: "https://nexus.internal/repository/pypi-proxy/simple/"

或者把 node_modules / vendor 打包成 CI artifact 快取,避免每次 job 都重新安裝。

Git Bundle 跨邊界傳輸

某些斷網環境不允許直接 git push 到內網 GitLab(例如開發在外網、部署在內網)。Git bundle 是把 commit 歷史打包成單一檔案的機制:

1# 外網開發機:打包最近的 commit
2git bundle create changes.bundle main~5..main
3
4# 帶進斷網環境後
5git bundle verify changes.bundle
6git fetch changes.bundle main:incoming
7git merge incoming

bundle 檔案包含完整的 Git 物件(commit、tree、blob),可以通過任何檔案傳輸方式帶過邊界——USB、光碟、審批後的檔案傳輸閘道。

跨邊界傳輸的安全考量:bundle 的內容應該在傳入前被掃描(至少 git bundle verify),確認不包含預期外的分支或異常大的物件。某些高安全環境要求所有跨邊界檔案經過人工審批。

MR Review 流程

斷網環境的 MR(Merge Request)review 流程跟模組七:infra 走 PR 流程的原則相同——變更走 MR → CI 跑 plan → reviewer 看 diff + plan 輸出 → 合併 → apply。差別在於所有環節都在內網:

 1# .gitlab-ci.yml — Terraform plan 貼回 MR comment
 2plan:
 3  stage: plan
 4  script:
 5    - terraform init -plugin-dir=/opt/terraform/plugins
 6    - terraform plan -no-color -out=plan.tfplan | tee plan.txt
 7    - |
 8      curl --request POST \
 9        --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
10        --data-urlencode "body=$(cat plan.txt)" \
11        "https://gitlab.internal/api/v4/projects/$CI_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes"
12  only:
13    - merge_requests

GitLab CI 的 merge_requests trigger 跟 GitHub Actions 的 pull_request 等價——MR 開啟或更新時自動跑 pipeline。

reviewer 在 GitLab 的 MR 頁面看 code diff + plan 輸出 comment,approve 後合併,合併觸發 apply pipeline。流程跟有網路時完全相同,只是所有元件(GitLab、runner、Terraform、provider plugin)都在內網。

時程與維護

項目初始設定持續維護
GitLab CE 安裝 + 設定1 天每季升級(含帶包 + 備份 + 升級 + 驗證)~半天
CI runner 設定半天image 更新隨 registry 同步
Gitea + Drone(替代方案)半天極低(二進位更新即可)
Git bundle 流程建立2 小時按需(有跨邊界需求時)

GitLab CE 的主要維護成本在升級——斷網環境的升級不能一鍵 apt upgrade,要預先下載正確版本的安裝包帶進來。跳版規則讓這個過程比正常環境多一層規劃。

跨分類引用