1. 項目概述與痛點分析,為什麼 Harbor 也需要高可用
1.1 單節點 Harbor 的隱患,鏡像倉庫掛了會發生什麼
先講一個我親身經歷的案例。某公司在內部推行容器化改造,開發環境、測試環境、預生產環境的鏡像全都推到一台 Harbor 伺服器上。前期一切正常,直到某天上午,那台伺服器的磁碟因為鏡像堆積被寫滿,Harbor 直接拒絕服務。開發那邊 deploy 流程全部卡住,測試環境沒法更新版本,運維這邊也因為沒法拉取基礎鏡像,連故障排查的容器都起不來。整個研發節奏被打斷了將近半天,最後還是臨時騰了一台機器手動恢復數據,才勉強把服務拉起來。
鏡像倉庫這個東西,平時存在感很低,但它實際上是整個容器化體系裡的核心樞紐。CI/CD 流水線、Kubernetes 集群拉取鏡像、開發人員本地調試,全都依賴它。一旦它掛了,影響的不是某一個服務,而是整條交付鏈路。
單節點 Harbor 的問題,歸納起來主要是三類:
- 沒有故障轉移能力:硬體故障、系統崩潰、內存洩漏,任何一種情況都可能讓服務直接中斷。沒有備用節點,就只能等人工介入。
- 升級維護必須停機:Harbor 版本升級、配置變更、證書輪換,都需要重啟服務。單節點架構下,這些操作只能選在深夜低峰期做,或者直接冒著影響業務的風險硬上。
- 性能瓶頸無法橫向擴展:並發拉取量大、鏡像體積大,單節點的 CPU、內存、帶寬都會成為瓶頸,尤其在高併發 CI 場景下特別明顯。
當時我們團隊內部的結論很直接:鏡像倉庫必須做高可用。至少要保證一台節點掛了,服務還能繼續轉。這就是這篇文章的出發點——用 HAProxy 做入口調度,配合 Harbor 多節點部署,構建一套可靠的高可用架構。
1.2 方案選型,為什麼入口層選 HAProxy 而不是 Nginx 或 F5
在做入口選型的時候,市面上常見的無外乎三類:軟體負載均衡(HAProxy、Nginx)、雲廠商 LB、硬體負載均衡(F5 那類)。從成本和运维靈活度來考慮,我們當時直接排除了硬體設備,雲廠商 LB 在自建機房的場景下也壓根不適用,剩下就是 HAProxy 和 Nginx 之間的抉擇。
HAProxy 在負載均衡領域是專精選手,它的設計目標就是做高併發代理。跟 Nginx 相比,它有幾個很關鍵的優勢:
一是連接調度能力更強。HAProxy 對 TCP 層的代理支持非常成熟,可以做到連接級別的負載和健康檢查,而且支持基於 Cookie 的 session 保持。Harbor 的客戶端既有 Docker CLI 也有 HTTP API,連接模型複雜,HAProxy 處理起來更穩。
二是健康檢查手段豐富。除了基礎的端口探測,HAProxy 支持 HTTP 層的http-check,可以直接請求 Harbor 的/api/v2.0/ping接口來判斷節點是否真的健康。Nginx 被動式的健康檢查需要商業版才支持,開源版只能靠上游服務器返回的錯誤來間接判斷,靈活性差很多。
三是性能開銷極低。HAProxy 是事件驅動的單進程模型,在同樣的硬體條件下,能支撐的併發連接數遠高於 Nginx。作為流量入口,不希望它本身成為瓶頸,HAProxy 在這方面非常值得信賴。
另外,HAProxy 的配置語法非常直白,frontend、backend、listen三個段搞定所有的代理邏輯,沒有 Nginx 那麼多模塊化的規則。後期維護成本也低。
注意:如果你對 Nginx 特別熟悉,硬要用它來做也並非不行。但 Harbor 這種多協議、多端口(HTTP 443、Harbor 內部組件通信)的場景,HAProxy 的 TCP 代理能力和健康檢查機制明顯更合適。別在入口層複雜化問題。
2. Harbor 高可用的架構設計思路
2.1 整體架構與流量路徑,一張圖講清楚各組件關係
HAProxy 實現 Harbor 高可用的核心思路,就是「前面加調度,後面做冗餘」。整個架構可以分為四層:
客戶端層:包含 Docker CLI、Kubernetes 節點、CI/CD 工具。它們訪問的是統一的虛擬地址(VIP 或域名),而不是具體的 Harbor 節點。
入口調度層:也就是 HAProxy。它負責接收所有請求,按策略轉發到後端 Harbor 節點,同時通過健康檢查來剔除異常節點。
Harbor 節點層:兩個或以上的 Harbor 實例部署在不同的物理機或虛擬機上,互為主備或雙活。每個節點上都運行著完整的 Harbor 服務棧,包括核心服務、註冊表、入口控制器等。
共享數據層:這是整個架構能否成立的關鍵。Harbor 的運行狀態分為兩大塊——存儲在 PostgreSQL 和 Redis 中的元數據,以及存放在對象存儲或文件系統中的鏡像層數據。高可用架構下,這兩個部分必須由所有 Harbor 節點共享。
流量路徑是這樣的:客戶端請求到 HAProxy → HAProxy 通過負載均衡算法選中一個 Harbor 節點 → Harbor 節點處理請求,讀寫共享的 PostgreSQL/Redis/存儲 → 返回結果給客戶端。
關鍵點在於,任何一個 Harbor 節點收到請求後,讀寫的都是同一份底層數據,所以無論請求被轉發到哪個節點,最終結果都是一致的。這就是「共享一切」架構的含義。
2.2 兩種高可用模式,雙活模式和主備模式該怎麼選
Harbor 高可用有兩種常見的部署模式,區別在於 Harbor 配置文件中harbor.yml的external_url和資料庫連線設置。
雙活模式(Active-Active)
兩個節點同時工作,都對外提供服務。HAProxy 把流量均勻分散到兩個節點上。這種模式的優勢是資源利用率高,整個系統的吞吐能力是兩台節點的疊加。但前提是底層的共享層必須支持併發讀寫,PostgreSQL 和 Redis 都必須配置成集群或主從模式,存儲層則需要支持多節點同時掛載。
主備模式(Active-Standby)
只有主節點在提供服務,備用節點處於空閒狀態,但保持數據同步。HAProxy 通過健康檢查監控主節點狀態,一旦主節點掛了,流量自動切到備用節點。這種模式的好處是配置簡單,對共享層的併發壓力小,但也意味着有一台機器長期閒置,存在資源浪費。
我們的建議是:如果你的 Harbor 承載的是核心生產業務,雙活是值得投入成本的,因為它能同時解決高可用和性能擴展兩個問題。如果鏡像倉庫只是輔助角色,流量不大,主備模式就夠用了,後期也能平滑切換到雙活。
2.3 共享層的關鍵角色,存儲、資料庫、Redis 一個都不能少
為什麼說共享層是整個方案的成敗關鍵?我拆開來講。
鏡像存儲:Harbor 底層本質上是 Docker Distribution,鏡像的 blob 數據存儲在文件系統或對象存儲中。高可用架構下,如果兩個 Harbor 節點各存各的,就會出現「節點 A 能拉到的鏡像,節點 B 上根本不存在」的情況。所以必須讓所有節點訪問同一份存儲,通常是 NFS、CephFS、GlusterFS、MinIO 這類方案。
PostgreSQL:Harbor 的用戶、項目、鏡像清單、審計日誌、權限策略全都存在 PostgreSQL 裡。單節點 PostgreSQL 在高可用架構下就是一個新的單點。你可以給它做主從複製,也可以用 Patroni 這類工具做自動故障轉移,在雲環境下直接用託管的 PostgreSQL 服務也是一種思路。
Redis:Harbor 用 Redis 做緩存和會話管理。Redis 掛了,Harbor 雖然不一定立刻崩潰,但會出現會話丟失、掃描任務異常等問題。同樣需要考慮高可用,Redis Sentinel 或 Redis Cluster 都可以。
我當時踩過一個很典型的坑:Harbor 兩個節點都配好了,HAProxy 也轉發正常,但鏡像推送偶爾會出問題。排查了半天發現,問題出在兩個 Harbor 共享了同一個 PostgreSQL,但是 Redis 是各自獨立的。用戶的 session 信息沒有共享,導致請求被轉發到不同節點時,登入狀態會丟失。後來把 Redis 改成共享模式,問題立刻消失。
忠告:架構容易畫,但共享層往往決定成敗。動手之前,先把存儲、資料庫、緩存這三層的高可用方案確定下來,否則整個方案就是空中樓閣。
3. 實戰部署:從環境準備到 HAProxy 配置
3.1 環境規劃與前置準備
我們用一台 HAProxy 節點加兩台 Harbor 節點來演示,具體規劃如下。實際項目中為了避免 HAProxy 自身成為單點,建議再部署一台備用 HAProxy 節點,搭配 Keepalived 做 VIP 漂移。
| 角色 | 規劃 | 說明 |
|---|---|---|
| HAProxy 節點 | 10.10.1.10 | 入口調度層,對外提供服務 |
| Harbor Node1 | 10.10.1.21 | 第一台 Harbor 節點,雙活模式 |
| Harbor Node2 | 10.10.1.22 | 第二台 Harbor 節點,雙活模式 |
| NFS 共享存儲 | 10.10.1.30 | 提供 /data/nfs/harbor 目錄作為鏡像存儲 |
| PostgreSQL | 10.10.1.31 | 主從複製,對應 Harbor 元數據庫 |
| Redis | 10.10.1.32 | Sentinel 模式,供 Harbor 做會話和緩存 |
在動手之前,有幾個前置條件必須確認:
- 所有節點的時間要同步,建議配置 NTP。時間不一致會導致 token 校驗失敗,這是個很隱蔽的問題。
- 所有節點的主機名要解析正常,公私鑰認證不是必須,但 Harbor 節點之間如果要互相調用 API,配一下會方便不少。
- Harbor 的
harbor.yml配置中,external_url要設定成對外的虛擬地址(例如https://harbor.example.local),兩個節點上的配置保持一致。千萬別把external_url寫成節點自己的 IP。
3.2 共享存儲和共享資料庫的部署細節
NFS 是最容易上手的共享存儲方案。服務端在 10.10.1.30 上安裝nfs-kernel-server,編輯/etc/exports:
/data/nfs/harbor 10.10.1.21(rw,sync,no_root_squash,no_subtree_check) 10.10.1.22(rw,sync,no_root_squash,no_subtree_check)Harbor 節點上掛載:
mount -t nfs 10.10.1.30:/data/nfs/harbor /data/harbor需要特別注意:掛載參數的選擇直接影響性能和數據一致性。no_root_squash是必須的,否則 Harbor 容器內 root 使用者寫入的文件會因為權限被壓縮而報錯。sync是為了保證數據即時落盤,防止宕機時丟失資料。實測下來,NFS 在最壞情況下吞吐會有一定衰減,如果你的場景是高併發大量鏡像推送,建議直接上 CephFS 或 MinIO 這種更大規模的存儲方案,架構上更穩,後期也好擴展。
PostgreSQL 的配置相對獨立,這裡說幾個要點。Harbor 需要的是「專用的資料庫實例」,官方支援 PostgreSQL 9.6 及以上版本。如果資料庫和 Harbor 在同一台機器上,配置基本上不用改,直接裝好就行。分離部署的話,需要修改harbor.yml中database部分的連線信息:
database: type: external external: host: 10.10.1.31 port: 5432 username: harbor password: harbor_password db_name: harbor max_idle_conns: 50 max_open_conns: 100主從複製的配置不在本文的範圍內,網上文章很多,基本的思路就是pg_basebackup做基礎備份,再配置primary_conninfo做流式複製。Redis 也是同理,建議用 Sentinel 模式,兩個 Harbor 節點連的是同一個 Sentinel 集群,而不是各自連一個 Redis。
3.3 兩台 Harbor 節點的部署,harbor.yml 的關鍵配置
Harbor 的離線安裝包解壓後,會有一個harbor.yml.tmpl範本,複製成harbor.yml,然後逐項修改。兩個節點的配置要一致,除了hostname不同(但需要指向同一個對外域名)。
核心配置大概長這樣:
hostname: harbor.example.local http: port: 80 https: port: 443 certificate: /data/certs/harbor.crt private_key: /data/certs/harbor.key external_url: https://harbor.example.local harbor_admin_password: Harbor12345 database: type: external external: host: 10.10.1.31 port: 5432 username: harbor password: harbor_password db_name: harbor data_volume: /data/harbor storage_service: ca_bundle: /data/certs/harbor_ca.crt filesystem: maxkeys: 100000 maxsize: 1000000 redis: type: external external: addr: 10.10.1.32:6379 sentinel_master_set: mymaster sentinel_addrs: 10.10.1.32:26379 db: 0 password: redis_password幾個關鍵點:
data_volume必須指向共享存儲,也就是 NFS 掛載的那個目錄。Harbor 的所有持久化數據,包括鏡像層、資料庫備份、證書等,都會寫到這個目錄。storage_service裡的filesystem可以配置,但如果你用的是 S3 兼容的對象存儲,Harbor 也支援直接對接。實測中,對象存儲的並發讀寫能力比 NFS 強很多,是更推薦的生產方案。- 證書的配置要特別小心。所有節點的證書必須是同一套,不然客戶端在切換節點時會看到證書不匹配的警告。用內部 CA 簽發即可,但
docker login的客戶端需要信任這個 CA。
配置完成後,執行./install.sh。Harbor 會以 docker-compose 的方式啟動一堆容器,包括harbor-core、harbor-registry、harbor-portal、registryctl等。可以先用docker ps確認容器是否都在運行。
3.4 HAProxy 的安裝與配置
HAProxy 安裝很簡單,用發行版自帶的包管理器即可:
apt install haproxy主要是要做好配置。先看一下我們最終的完整配置,再逐段解釋。
# /etc/haproxy/haproxy.cfg global log /dev/log local0 maxconn 4096 chroot /var/lib/haproxy user haproxy group haproxy daemon defaults log global mode http option httplog option dontlognull option forwardfor option http-server-close timeout connect 5s timeout client 30s timeout server 30s frontend harbor_https bind *:443 mode tcp default_backend harbor_nodes backend harbor_nodes mode tcp balance roundrobin option tcp-check stick-table type ip size 1m expire 30m stick on src server harbor1 10.10.1.21:443 check inter 5s fall 3 rise 2 server harbor2 10.10.1.22:443 check inter 5s fall 3 rise 2等等,為啥用mode tcp而不是mode http?這個問題很多人問過。Harbor 的 443 端口上跑的是 HTTPS,如果 HAProxy 用mode http,就得承擔 SSL 終結和重新加密的任務,要麼你告訴它後端服務用的是 HTTP 還是 HTTPS,SSL 證書的管理也會變複雜。用mode tcp是最省事的方式,HAProxy 只做四層轉發,把 TLS 加密的裸流量直接透傳到後端,證書的驗證完全由 Harbor 自己處理。這樣一來,HAProxy 不需要管證書,後端節點換證書也不需要動 HAProxy 配置。
balance roundrobin是預設的負載均衡算法,兩個節點輪流接收請求。stick-table的作用是讓同一個來源 IP 的請求,在存在健康節點的情況下,盡量定向到同一個後端節點。這個特性對 Harbor 是加分的——有些客戶端(比如 Kubernetes 的 containerd)會持有多個連接,如果這些連接被分散到不同節點,雖然底層數據一致,但連接建立的成本會更高。
最重要的一段是健康檢查:
option tcp-check server harbor1 10.10.1.21:443 check inter 5s fall 3 rise 2inter 5s表示每 5 秒探測一次,fall 3表示連續失敗 3 次才判定節點不可用,rise 2表示連續成功 2 次才恢復可用。這個參數組合比較保守,能有效避免短暫抖動造成的誤判。
如果你希望健康檢查更貼近業務層,可以改用 HTTP 檢查。把後端節點的檢查切換成:
backend harbor_nodes mode http option httpchk GET /api/v2.0/ping http-check expect status 200 server harbor1 10.10.1.21:443 ssl verify none check inter 5s fall 3 rise 2 server harbor2 10.10.1.22:443 ssl verify none check inter 5s fall 3 rise 2ssl verify none的意思是檢查時不驗證後端證書,這樣可以避免內部 CA 證書未被信任的問題。expect status 200確保後端真的能回應請求,而不只是端口通。
3.5 Keepalived 做 HAProxy 高可用
HAProxy 自己掛了怎麼辦?這個問題得提前考慮。我們的做法是在 HAProxy 前面再架一層 Keepalived,用 VIP 提供虛擬位址。
配置非常簡單。兩台 HAProxy 節點上,安裝 Keepalived,/etc/keepalived/keepalived.conf寫成下面這樣:
vrrp_script chk_haproxy { script "/usr/bin/kill -0 $(cat /var/run/haproxy.pid)" interval 2 weight -20 } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 123456 } virtual_ipaddress { 10.10.1.10/24 } track_script { chk_haproxy } }解釋一下:Keepalived 的vrrp_script會去檢查 HAProxy 進程是否還活著,如果掛了,自身的優先級會降低 20,心跳間隔後,另一個節點就搶佔 VIP。服務對外始終是 10.10.1.10,對客戶端來說,HAPROXY 節點的故障是無感知的。
注意,兩台 HAProxy 節點的state可以都寫成MASTER(配合不同的priority),也可以一台MASTER一台BACKUP,後者比較直觀,但前者的 failover 速度更快。我個人傾向於都寫MASTER,讓 priority 決定誰優先。
3.6 客戶端側的配置,docker 如何信任自簽證書
這是整個方案落地時最容易出現問題,但往往被忽略的一步。Docker 默認只信任系統 CA 機構頒發的證書,你內部 CA 簽發的 Harbor 證書,如果不做配置,docker login必然失敗。
如果客戶端是和 Harbor 在同一網段內的伺服器,最簡單的方案是把 CA 證書放到 Docker 的信任目錄:
cp harbor_ca.crt /etc/docker/certs.d/harbor.example.local/ca.crt如果 Harbor 監聽的是非標準端口,比如harbor.example.local:8443,目錄名要寫成harbor.example.local:8443,格式對應 IP 和端口。
配置完重啟 Docker 即可:
systemctl restart docker如果客戶端是開發筆記型電腦,要確保使用正確的解析——域名要解析到 VIP(10.10.1.10),而不是某個 Harbor 節點的 IP。在/etc/hosts或者 DNS 伺服器裡配置好就行,否則流量根本不會走到 HAProxy。
4. 驗證與測試,如何確認高可用架構真的可用
4.1 基本功能驗證:推送、拉取、登入
配置完成後,第一個要驗證的是基本功能。兩台 Harbor 節點都正常運行時,執行以下操作:
# 登入 Harbor docker login harbor.example.local -u admin -p Harbor12345 # 打標籤並推送 docker tag nginx:latest harbor.example.local/library/nginx:latest docker push harbor.example.local/library/nginx:latest # 拉取 docker pull harbor.example.local/library/nginx:latest如果這三步都通了,說明整個鏈路是通的。接下來可以進一步觀察請求是否被輪流分發到了兩個節點。登入 Harbor 的 Web 管理介面,查看「審計日誌」,會看到來自同一客戶端的推拉操作時而落在節點 A,時而落在節點 B。在雙活模式下,這種現象是正常的,也是預期的。
4.2 故障注入測試:停掉一台 Harbor 節點
這一步是整個高可用驗證的核心。模擬一台 Harbor 節點宕機,看看整個系統是否還能正常提供服務。
這裡的操作順序很關鍵。先不要直接停服務,建議先觀察 HAProxy 的狀態頁面或日誌,確認它對後端每個節點的監控狀態都是綠色的。然後在 Node2 上執行:
docker-compose stop # 在 Node2 的 Harbor 安裝目錄下執行或者乾脆模擬硬體故障,直接把 VM 關機。然後觀察 HAProxy 節點上的日誌:
tail -f /var/log/haproxy.log正常情況下,幾秒內就能看到健康檢查開始失敗,fall 3之後,Node2 的狀態會變成紅色,HAProxy 自動停止向它轉發請求。
此時立刻執行之前的三步驗證流程:登入、推送、拉取。全部應該正常完成,沒有任何報錯。推送的鏡像會落到 Node1 上,但通過共享存儲,Node2 恢復後也能讀到同一份數據。
這就驗證了「業務無感知的故障轉移」。客戶端的請求被 HAProxy 自動切換到了健康的節點上。
4.3 恢復測試與資料最終一致性
讓 Node2 恢復服務。重啟 VM 後,執行:
cd /home/harbor/harbor docker-compose up -d等待所有容器啟動完成,大概 30 秒到 1 分鐘。HAProxy 的健康檢查通過後,Node2 自動回到負載池中。
關鍵驗證點:剛才在 Node2 宕機期間,我們向 Node1 推送了一個新的鏡像,現在要在 Node2 上確認這個鏡像是否存在。執行:
# 從 Node2 本機上拉取 docker pull harbor.example.local/library/nginx:latest如果拉取失敗或報找不到鏡像,說明共享存儲配置有問題,鏡像數據沒有被真正的共享。正常的結果是立即成功——因為鏡像數據存在共享存儲上,Node2 的 registry 服務只是作為一個讀入口,它本身不保存數據。
同樣的原理也適用於資料庫。在 Node2 恢復後,到 Harbor 的 Web 頁面上看一下,用戶、項目、權限設置都應該和宕機前保持一致,因為這些數據存在共享的 PostgreSQL 中。
4.4 性能與穩定性測試
高可用不光是「掛了能切」,還要驗證正常運行的性能。我建議做兩組測試:
RAID 隊列測試:用腳本並發推送幾十個鏡像,觀察兩個節點的 CPU、內存、網路吞吐曲線,確認負載是否被有效分散。
極端場景測試:找一台配置最低的客戶端,持續做大批量拉取,觀察整體吞吐是否有明顯下降。這能幫你發現 HAProxy 的連接數瓶頸。
5. 遇到的坑與排查經驗
5.1 健康檢查節點全部判定為 down,但 Harbor 本身是好的
這個問題出現的機率非常高。排查思路從以下幾步入手:
先用curl -k https://10.10.1.21/api/v2.0/ping在 HAProxy 節點上直接測試,確認 Harbor 介面是真的通。如果 curl 結果正常,說明問題在於 HAProxy 的檢查方式。
常見原因一:HAProxy 配置文件裡檢查的是 HTTP GET/api/v2.0/ping,但 Harbor 返回的不是純 200,而是帶了 JSON。確認expect status 200是否匹配實際回應碼。
常見原因二:證書問題。如果ssl verify參數不是none,HAProxy 在檢查時會去驗證後端證書,如果 CA 沒被信任,檢查就會失敗,節點被誤判為 down。
常見原因三:timeout 太短導致握手失敗。如果你的 Harbor 節點負載較高,SSL 握手可能會超過 HAProxy 配置的timeout connect值,健康檢查就會頻繁失敗。把timeout connect增到 10 秒左右。
5.2 docker login 成功,但 docker push 報 unauthorized
這個問題的本質是 Harbor 的 token 機制。Harbor 的 registry 元件在收到 push 請求時,會把 token 送回 core 元件驗證,如果 core 元件的資料庫連線的是不同的 PostgreSQL,token 驗證就會失敗。
這個問題在網上被討論過很多次,很多時候是因為配置的 PostgreSQL 不是同一個實例。你的兩個 Harbor 節點,必須連到同一個 PostgreSQL 和同一個 Redis,否則就會出現「A 節點登入成功,但請求到了 B 節點,B 節點驗證 token 時用的是自己的資料庫,查不到這個 token」的情況。
所以排查順序是:先確認 Redis 是否共享,再確認 PostgreSQL 是否共享,最後看兩個節點的時間是否同步。token 是有時效性的,時間不同步也可能導致驗證失敗。
5.3 切換節點後,Kubernetes 拉取鏡像偶爾失敗
Kubernetes 節點上的 kubelet 在拉取鏡像時,會並發建立多個連接。如果你的 HAProxy 沒有做 stick-table 配置,這些連接會被分散到兩個 Harbor 節點。理論上雙活架構下這完全沒問題,但實測中偶爾會出現 5xx 錯誤。
原因其實不在 Harbor,而在底層存儲的競爭。當兩個請求同時讀取同一個未緩存的 blob 層時,NFS 在高併發下可能出現短暫的延遲超限。解決方案有兩個方向:
一是給 HAProxy 加上 stick-table,讓同一個來源 IP 的請求盡量落在同一個節點,降低跨節點的存儲競爭。
二是把共享存儲從 NFS 換到對象存儲(比如 MinIO 或 Ceph RGW),對象存儲的併發讀寫能力遠超 NFS。
5.4 證書輪換後,docker 客戶端報 x509 驗證錯誤
Harbor 的證書快到期了,你重新簽發了一張新證書,更新到了 Harbor 節點上。然後客戶端報x509: certificate signed by unknown authority。
原因是 Docker 客戶端緩存了舊的 CA 證書。如果你使用的是內部 CA 或者自簽證書,CA 本身沒變,只是 Harbor 的服務端證書換了,客戶端理論上不需要任何操作。但如果 CA 也換了,就需要更新客戶端的/etc/docker/certs.d/harbor.example.local/ca.crt。
這裡要提醒的是,千萬只更新了 Harbor 節點上的證書,忽略了客戶端。這類問題通常不是 Harbor 的配置錯誤,而是證書鏈沒有傳遞完整。排查方法很簡單,在客戶端執行:
openssl s_client -connect harbor.example.local:443 -showcerts看輸出的證書鏈,和你更新後的證書是否一致。
5.5 升級 Harbor 版本時要注意什麼
高可用架構下,Harbor 的升級流程可以做滾動升級,不用全量停機。我們的做法是:
先把其中一個節點從 HAProxy 的負載池中摘除。在 HAProxy 的後端配置中,把對應的節點注釋掉:
# server harbor2 10.10.1.22:443 check inter 5s fall 3 rise 2重載 HAProxy,確認流量全部落在 Node1。然後對 Node2 執行升級操作:停止 Harbor、備份數據目錄、替換二進制包、執行遷移腳本、重啟。等 Node2 起來並通過健康檢查後,把它加回負載池。
接著重複同樣的流程升級 Node1。整個過程中,業務不會中斷,而且兩個節點的版本不會長時間不一致。
升級前一定要備份共享的 PostgreSQL 和存儲目錄,即使用了滾動升級,也要做全量備份,以防遷移腳本出問題。
5.6 高可用架構下的監控與告警配置
HAProxy 本身提供了一個統計頁面,通過 Web 介面可以看到每個後端節點的實時狀態。在配置中添加一個監聽段即可:
listen stats bind *:8404 mode http stats enable stats uri /stats stats auth admin:admin_password這樣就可以在瀏覽器訪問HTTP://10.10.1.10:8404/stats,輸入用戶名密碼後看到後端節點的運行狀態。
建議對以下三個指標做監控告警:
- 後端節點是否全部 down。如果兩個 Harbor 節點都不可用,說明整個服務已經中斷,需要緊急介入。
- 健康檢查失敗的次數。如果某個節點頻繁出現檢查失敗又恢復,說明該節點可能存在隱患,比如磁碟快滿了或記憶體洩漏。
- HAProxy 的連接數和會話數。這個數據能提前預警流量是否在快速增長,是否需要擴容節點或調整存儲。
6. 最終架構總結與運維建議
到這裡,整個 HAProxy 實現 Harbor 高可用的架構已經完整落地了。
流量入口是 Keepalived 提供的 VIP,底下掛著兩台 HAProxy 節點,HAProxy 把請求分發到兩台 Harbor 節點上,Harbor 底層共享同一套 PostgreSQL、Redis 和鏡像存儲。任何一個 Harbor 節點掛了,HAProxy 自動切走流量;HAProxy 本身掛了,Keepalived 自動切換 VIP;單台 Harbor 節點的升級維護也完全不影響業務。
這個方案我實際跑了將近一年,穩定性非常好。但有些經驗值得拿出來強調一下:
監控比架構本身更重要。高可用方案不是部署完就一勞永逸的,你需要知道節點的健康狀態,才能在故障發生前提前干預。磁碟空間尤其值得盯緊,鏡像倉庫的磁碟增長速度往往超出預期,存儲寫滿的時候,再好的高可用架構都白搭。
別把 HAProxy 本身搞成單點。很多團隊做完 Harbor 高可用就覺得萬事大吉,忘了 HAProxy 也是唯一的入口。配上 Keepalived 是必須的,這個成本很低,帶來的收益卻很大。
升級操作要走流程。高可用架構下,很多運維操作可以滾動進行,但前提是要有清晰的流程和預案。先摘除節點,再執行操作,最後加回節點。每一步之前都要想清楚「如果這一步失敗了,我該怎麼回滾」。
回顧整個過程,最有感觸的是:高可用架構的本質,是讓「故障」這件事從「事故」變成「演練」。鏡像倉庫掛掉的時長從數小時縮短到幾十秒,甚至業務層面完全無感知,這對研發團隊的交付效率提升是質變級的。如果你還沒做這套方案,趁早動手,別等鏡像倉庫真的掛了再後悔。