)
1. 先把需求摸清楚私有鏡像倉庫到底要解決什么問題云主機上搭一套私有 Docker 鏡像倉庫這件事聽起來像運維活但真正動手的人往往是開發(fā)者自己——團隊里沒人愿意把公司內部鏡像丟到公共倉庫也沒有預算一開始就買商業(yè)方案于是先在云上開一臺機器把鏡像倉庫跑起來邊學邊用。我這次做的就是這個在云主機的實例上用 Harbor 搭一套帶 Web 界面、帶用戶權限、帶鏡像掃描能力的私有倉庫同時把整條 login / tag / push / pull 鏈路走通。這個倉庫能解決三件具體的麻煩內部鏡像不再散落在各人筆記本上、CI 機器不用重復從外網拉基礎鏡像、私有鏡像有獨立的訪問控制。它適合誰我的判斷是三類人剛接觸容器、想弄明白鏡像倉庫底層是怎么運轉的初學者小團隊里被安排“順手把倉庫搭一下”的后端或運維以及已經用過托管版鏡像服務、想搞清楚里面那些項目、成員、機器人賬號概念從哪來的人。整篇內容我會按“選型 — 環(huán)境 — 部署 — 接入 — 踩坑 — 加固”的順序寫每一步都給到可以直接抄的參數和命令中間穿插我自己踩過的坑你可以照著復現也可以只看自己卡住的那一段。需要先說明一個前提云廠商之間的差異主要在控制臺點選和網絡配置上實例內部的路徑、端口、組件和開源版本的行為是一致的。所以下文統一稱呼為“云主機”“實例”具體的規(guī)格選型和安全組配置你按手頭那家云的控制臺界面去找對應項就行邏輯完全對得上。1.1 四種方案擺在天平上我為什么落在 Harbor把可選項鋪開其實只有四條路。第一條是本地起一個registry:2容器五行命令搞定但它只有存儲和推拉能力沒有用戶體系、沒有 Web 界面、沒有項目隔離刪鏡像還得手動調 API學兩天就摸到天花板了。第二條是直接用云廠商的托管鏡像服務省心但按量計費、權限模型是別人設計的學不到東西也不適合“學習”這個目標。第三條是在自己機器上用 Docker Desktop 自帶的小倉庫只能本機用沒有意義。第四條就是 Harbor它是從開源鏡像倉庫項目演進出來的完整發(fā)行版帶 Nginx、Core、Registry、PostgreSQL、Redis、JobService、Trivy 一整套組件用 Compose 拉起一臺機器就能跑。我選 Harbor 的關鍵理由不是功能多而是它的組件拆分方式本身就是一堂容器架構課。你會發(fā)現 Harbor 把“認證”和“存儲”分開了Core 服務負責用戶、項目、權限、APIRegistry 只負責最原始的 Blob 與 Manifest 存取JobService 干異步的復制、掃描、GC 這類重活Nginx 在最前面做 TLS 終止和路由。理解了這個分層你再看任何一個鏡像倉庫產品的文檔都能對上號。這是學習場景下最值回票價的部分。另外 Harbor 官方提供離線安裝包里面把鏡像都打包好了不用一個個去docker pull在帶寬不穩(wěn)定的環(huán)境里這一點非常關鍵。整個部署過程用prepare生成 compose 文件、用install.sh一鍵拉起失敗時日志位置固定排查路徑清晰很適合拿來練手。1.2 上線前必須釘死的四件事很多人部署失敗不是因為技術難而是四件事沒提前定域名、證書、存儲目錄、端口。我這邊的定法是這樣的——域名用一個只在內部解析的名字比如harbor.internal故意不用公網域名避免證書簽發(fā)和被掃描證書用自簽學習環(huán)境沒必要走自動簽發(fā)流程但自簽證書的 SAN 一定要寫全后面會細講存儲目錄統一放在獨立數據盤的/data下Harbor 的數據卷放/data/harborDocker 引擎自己的數據目錄放/data/docker這樣系統盤只裝系統重建實例時數據盤可以直接掛回來端口上HTTPS 占 443HTTP 占 8443 并且只做跳轉用。這里有個容易被忽略的細節(jié)Harbor 配置里的hostname字段不能帶端口。寫成harbor.internal:443會讓生成的證書配置、令牌服務和倉庫地址全部錯位癥狀是網頁能打開但docker push報 401 或 404。這個坑我第一遍就踩了后面會再提一次。還有一件事需要提前想清楚這臺機器是“學習環(huán)境”還是“準生產環(huán)境”。學習環(huán)境可以單機、可以自簽、可以只做冷備份一旦有同事真的往里推鏡像你就得考慮備份、磁盤配額、垃圾回收周期。我的建議是先用單機把鏈路走通把備份腳本寫好再考慮往高可用架構演進——單機 Harbor 掛了的恢復動作其實比很多人想象的簡單只要harbor.yml、證書、數據卷和數據庫備份四樣東西在。2. 云主機與基礎環(huán)境的落地準備這一節(jié)全部是“動手之前的體力活”但它們決定了后面能不能順。我見過太多人在 Harbor 起不來之后去翻日志最后發(fā)現是內存不夠被 OOM Killer 殺掉了 Core 服務或者磁盤寫滿導致 PostgreSQL 拒絕寫入。所以環(huán)境和規(guī)格這部分值得單獨花時間。2.1 實例規(guī)格怎么估從內存和磁盤兩頭算先說內存。Harbor 默認組件跑起來Nginx 大約 30MB、Core 約 100MB、Registry 約 60MB、PostgreSQL 約 100MB、Redis 約 20MB、JobService 約 80MB、Trivy 適配器再加一點空載合計在 400MB 到 600MB 之間。聽起來 2GB 就夠但實際推拉鏡像時 Nginx 和 Registry 會有緩沖區(qū)開銷JobService 做掃描時內存能沖到 500MB 以上加上 Docker 引擎自身、系統進程和頁緩存4GB 是安全帶。我用的就是 2 核 4GB跑起來比較從容如果你還要在這臺機器上跑 CI runner直接上 4 核 8GB。磁盤要算兩筆賬。第一筆是鏡像本體假設你打算放 100 個鏡像標簽平均壓縮后 300MBHarbor 按層存儲且層可共享實際占用約為總量的 60%也就是 100 × 300MB × 0.6 ≈ 18GB。第二筆是回收余量垃圾回收不會立刻釋放空間而且回收前要把倉庫切成只讀中間可能堆積一批待清理對象建議預留 1.5 倍。18GB × 1.5 ≈ 27GB再加上日志、掃描數據庫、系統獨立數據盤開 100GB 是很舒服的配置學習場景下 40GB 也能跑但要盯著用。系統盤 40GB 足夠但請務必確認 Harbor 的數據目錄在數據盤上。默認安裝會把data_volume放在安裝目錄下的data文件夾里如果安裝目錄在系統盤鏡像很快就吃掉系統盤而系統盤寫滿會導致 SSH 都登不上這是最難受的一種故障。項目學習/單機小團隊使用說明vCPU24編譯、掃描占 CPU內存4GB8GB低于 2GB 易被 OOM系統盤40GB40GB僅裝系統與鏡像緩存數據盤100GB200GB 起存 Harbor 數據與 Docker 數據帶寬按量 5Mbps固定帶寬拉基礎鏡像時體現明顯帶寬這塊補充一句學習環(huán)境用按量帶寬、用完就停是最省的做法但要注意鏡像拉取那一瞬間的峰值如果帶寬只有 1Mbps拉幾百兆的基礎鏡像會非常痛苦。我的做法是第一次把常用基礎鏡像拉完并推到私有倉庫后面所有構建都走內網拉取外網帶寬就只在少數時候打滿。2.2 安全組與端口規(guī)劃清單安全組是云上最容易配錯的一層。原則很簡單只開必須的其余全關。我這次開放的端口如下表注意 2375 這個 Docker 遠程端口絕對不要對公網開放一旦暴露對方可以直接在你的機器上起容器做任何事這是常見的高危配置。端口協議來源用途22TCP你的固定出口 IP運維登錄443TCP內網網段Harbor HTTPS8443TCP內網網段Harbor HTTP 跳轉80TCP不開由 443 承接2375/2376TCP不開Docker 遠程 API5432/6379TCP不開數據庫與緩存僅容器內如果機器需要被其他網絡的同事訪問把 443 的來源改成對應網段不要圖省事寫0.0.0.0/0。另外很多云廠商還提供“安全組規(guī)則命中日志”配完跑一次docker login去日志里確認命中的是你預期的規(guī)則比盲猜快得多。2.3 數據盤掛載與目錄體系拿到新實例第一件事是看盤。lsblk確認數據盤設備名通常系統盤是vda數據盤是vdb但不同鏡像不一樣穩(wěn)妥做法是看容量和掛載點。lsblk # 假設輸出中 vdb 沒有掛載點容量符合你買的數據盤 mkfs.ext4 /dev/vdb mkdir -p /data blkid /dev/vdb # 記下輸出的 UUID然后寫/etc/fstab。這里一定要用 UUID 而不是/dev/vdb因為設備名在重啟、擴容、換盤后可能變化用設備名會導致開機掛載失敗進而進不了系統。寫入一行UUID你的UUID值 /data ext4 defaults,nofail 0 2nofail這個選項很關鍵它保證盤掛不上時系統仍能啟動方便你遠程修復否則一個 fstab 寫錯就得走控制臺的 VNC。寫完執(zhí)行mount -a驗證再df -h看一眼。接著建目錄體系我的劃分是這樣/data/harbor放 Harbor 數據卷/data/harbor-certs放證書也可以放/data/harbor/certs/data/docker給 Docker 引擎當># 以某主流發(fā)行版為例先裝依賴 sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg # 添加軟件源并安裝源地址請?zhí)鎿Q為你環(huán)境可用的地址 sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin裝完必須驗證三件事docker version能看到 Client 和 Server 兩段、docker compose version有輸出、systemctl is-enabled docker返回enabled。第三點經常被忽略機器重啟后 Docker 沒自啟Harbor 自然也就沒起來第二天上班發(fā)現倉庫掛了。Compose 這里有個版本差異要提醒Harbor 的不同版本對docker composev2 插件和docker-composev1 獨立二進制的探測邏輯不同新版安裝腳本優(yōu)先用 v2。如果你機器上兩個都裝了偶爾會出現腳本調錯的情況癥狀是install.sh報命令不存在但手動docker compose ps又是好的。最省事的做法是只保留一個我一般只留 v2 插件版。配置 Docker 守護進程這一步先做一半把>{ data-root: /data/docker, log-driver: json-file, log-opts: { max-size: 50m, max-file: 3 }, live-restore: true }寫到/etc/docker/daemon.jsonsystemctl daemon-reload systemctl restart docker。日志輪轉這兩個參數千萬不要省容器日志默認不限制大小某天一個異常容器反復報錯幾十 GB 日志能把盤寫滿這個場景我遇到過兩次都是深夜。3. Harbor 部署的完整實操鏈路環(huán)境就緒進入正題。這一節(jié)我會把配置文件逐項拆開講因為 Harbor 的配置文件里幾乎每一個改動都對應一個具體的后果抄作業(yè)可以但知道為什么抄更重要。3.1 離線包獲取與解壓從官方 release 頁面下載離線安裝包文件名形如harbor-offline-installer-v2.10.x.tgz注意選 offline 而不是 online離線包里面已經包含所有鏡像 tar 包。下載后校驗一下哈希然后在數據盤解壓cd /data wget 離線安裝包地址/harbor-offline-installer-v2.10.2.tgz tar -xzf harbor-offline-installer-v2.10.2.tgz cd harbor ls -l目錄里應該有harbor.yml.tmpl、install.sh、prepare、harbor.v2.10.2.tar.gz。第一件事是把模板復制成正式配置cp harbor.yml.tmpl harbor.yml。不要直接改 tmpl因為升級時新包會覆蓋模板你的配置就丟了而且排查問題時也分不清是默認值還是你的改動。3.2 harbor.yml 逐項拆解與參數計算配置文件不長但每一段都有坑。我按順序講關鍵項。hostname: harbor.internal http: port: 8443 https: port: 443 certificate: /data/harbor-certs/harbor.crt private_key: /data/harbor-certs/harbor.key harbor_admin_password: 換成你自己的強密碼 data_volume: /data/harbor database: password: 換成獨立強密碼 max_idle_conns: 50 max_open_conns: 100 jobservice: max_job_workers: 10 trivy: ignore_unfixed: true skip_update: true insecure: false log: level: info local: rotate_count: 50 rotate_size: 200M location: /data/harbor/loghostname前面說過純域名不帶端口而且要和證書的 CN/SAN 完全一致否則客戶端會報證書不匹配。http.port我設成 8443是給那些還在用明文訪問的老腳本留一條跳轉路實際使用時 Nginx 會把 HTTP 請求 301 到 HTTPS。https.port用 443這樣客戶端docker push的時候不用特殊寫端口省掉很多麻煩。data_volume指向數據盤這一行決定了幾十 GB 鏡像落在哪。database.password單獨設不要和harbor_admin_password一樣雖然學習環(huán)境無所謂但養(yǎng)成習慣很重要——數據庫密碼泄露意味著整庫憑據握在別人手里而管理員密碼泄露最多是倉庫被改。trivy.skip_update: true在離線或帶寬有限的環(huán)境下很有用否則 Trivy 每次啟動都去拉漏洞庫拉不動就一直重試日志刷屏。jobservice.max_job_workers控制異步任務并發(fā)10 是個平衡值調到 30 以上在小機器上會把 CPU 打滿導致 Web 界面卡頓。log.local的輪轉參數是保命的rotate_count: 50配合rotate_size: 200M理論最大 10GB實際很少到但比不限制安全得多。如果你不用漏洞掃描可以在安裝時加--without-trivy能省 300MB 左右內存和一點磁盤。學習階段我建議保留掃一次看看報告長什么樣挺有意思。3.3 自簽證書生成SAN 是重點自簽證書最容易出問題的地方不是加密強度而是Subject Alternative Name?,F在所有主流 TLS 實現都只看 SAN不看 CN只寫 CN 的證書在瀏覽器和 Docker 客戶端里會被判定為無效。生成命令mkdir -p /data/harbor-certs cd /data/harbor-certs openssl req -x509 -nodes -newkey rsa:4096 -days 3650 \ -keyout harbor.key \ -out harbor.crt \ -subj /OInternal/OULearning/CNharbor.internal \ -addext subjectAltNameDNS:harbor.internal,IP:10.0.0.12這里幾個參數值得說-days 3650是十年學習環(huán)境夠用生產環(huán)境建議 90 到 365 天并配自動續(xù)期-addext需要 OpenSSL 1.1.1 以上如果你的系統版本老得改用寫配置文件的方式SAN 里同時寫 DNS 和 IP是因為我偶爾會用 IP 直連調試不寫 IP 的話那條路就報證書錯誤。生成完檢查一下openssl x509 -in harbor.crt -noout -text | grep -A2 Subject Alternative Name確認輸出里有你寫的兩個值。這一步花十秒鐘能省掉后面半小時的x509: certificate signed by unknown authority排查。順手把證書文件的權限收緊chmod 600 harbor.key。私鑰文件在容器掛載時會被讀取權限太松雖然不直接報錯但不符合最小暴露原則。3.4 執(zhí)行 install.sh 與組件狀態(tài)驗證配置寫完執(zhí)行cd /data/harbor ./install.sh --with-trivy腳本會依次做幾件事檢查 Docker 和 Compose 可用性、調用prepare渲染出docker-compose.yml、加載離線鏡像包、拉起全部容器、等待健康檢查。整個過程兩三分鐘中途輸出會滾得很快注意看有沒有紅色的 ERROR。安裝成功的標志是最后一屏打印出訪問地址和默認賬號提示。然后驗證docker compose -f docker-compose.yml ps你應該看到harbor-core、harbor-db、harbor-jobservice、harbor-log、harbor-portal、nginx、redis、registry、registryctl、trivy-adapter全部是running或healthy。healthy狀態(tài)是核心服務的健康檢查探針剛起來時可能是starting等 30 秒再看。如果某個容器反復重啟先看日志docker logs --tail 100 harbor-core。日志目錄在容器內是/var/log/harbor宿主機上通過harbor-log容器集中收集位置是data_volume下的log目錄。這個設計挺巧妙所有組件日志統一落盤排查時不用一個個進容器。常用運維命令我記成三行出問題先敲這三條docker compose -f docker-compose.yml ps # 看狀態(tài) docker compose -f docker-compose.yml logs -f core # 看核心服務日志 tail -f /data/harbor/log/core/core.log # 看落盤日志3.5 首次登錄與基礎初始化動作瀏覽器打開https://harbor.internal自簽證書會有安全警告點繼續(xù)訪問即可這也是為什么自簽只適合內部環(huán)境。用默認管理員賬號登錄密碼必須是Harbor12345這是官方默認值第一次登錄會強制要求改密碼。新密碼的規(guī)則是長度至少 8 位、包含大小寫字母和數字比如Lx2024Harbor。改完密碼做幾個初始化動作新建一個項目比如dev勾選私有這樣匿名用戶拉不了。默認的library項目是公開的很多人第一次push就推到這里結果所有人都能拉很不合適。在系統管理 - 用戶管理里建幾個普通用戶分配給不同同事。進項目dev在成員里加成員角色選開發(fā)者可推可拉或訪客只拉。Harbor 的角色模型是三層系統管理員、項目成員、匿名用戶權限從項目設置里繼承。在配置管理里把項目創(chuàng)建權限關掉避免任意用戶隨便建項目把磁盤塞滿。這些動作不復雜但做了之后整個倉庫才像個“能給別人用”的東西而不只是你自己玩的沙盒。4. 客戶端接入從 login 到 push 的完整閉環(huán)服務端起得來只算完成一半。真正讓人卡住的往往是客戶端這一側證書信任、域名解析、守護進程配置隨便一個不對docker push就報一堆看不懂的錯。這一節(jié)我把客戶端要改的東西全部列清楚。4.1 Docker 守護進程的配置與證書信任客戶端有兩條路走要么讓 Docker 信任你的自簽證書要么把倉庫標記為“非安全倉庫”。生產環(huán)境必須走前者學習環(huán)境圖省事可以用后者但要知道它的含義——insecure-registries會讓 Docker 在推拉時跳過 TLS 校驗中間人只要在你和倉庫之間就能看到鏡像內容。先講后者快{ data-root: /data/docker, insecure-registries: [harbor.internal:443], insecure-registries: [harbor.internal], log-driver: json-file, log-opts: { max-size: 50m, max-file: 3 } }注意上面這段我故意寫了兩遍同名 key只是想說明格式實際只能留一個正確寫法是數組里放harbor.internal不帶端口。如果倉庫啟用了 HTTPS 且用 443insecure-registries里寫主機名即可如果是自定義端口才需要帶端口。再講正規(guī)做法讓客戶端信任證書sudo mkdir -p /etc/docker/certs.d/harbor.internal sudo cp harbor.crt /etc/docker/certs.d/harbor.internal/ca.crt sudo systemctl restart docker目錄名必須是hostname:port格式如果端口是 443 可以省略端口部分但有些版本比較嚴格我建議統一寫成harbor.internal:443和harbor.internal各建一個目錄兩邊都放上證書避免猜版本行為。文件名同樣是固定的自簽根證書叫ca.crt客戶端證書叫client.cert客戶端私鑰叫client.key名字錯了不生效且不報錯只是繼續(xù)報證書不受信任非常迷惑。改完 daemon.json 一定要systemctl daemon-reload再重啟直接 restart 有時不讀新配置。驗證方式是docker info看輸出里Insecure Registries一欄有沒有你的域名。4.2 域名解析的三種兜底方式harbor.internal這類內部域名在公網 DNS 上是不存在的??蛻舳藞骴ial tcp: lookup harbor.internal: no such host就是解析沒通。三種解法從輕到重第一種改 hosts。每臺客戶端機器加一行10.0.0.12 harbor.internal最簡單機器多了難維護學習環(huán)境夠用。第二種內網 DNS 加一條 A 記錄。如果你們本來就有內網 DNS加一條記錄所有機器自動生效這是最干凈的方式。第三種用云廠商的私有 DNS 服務。在 VPC 內建私有解析區(qū)把域名指向內網 IP新機器加入 VPC 就自動生效。這個適合有一定規(guī)模的團隊配置略復雜。不管用哪種配完在客戶端執(zhí)行getent hosts harbor.internal或nslookup確認能拿到正確的內網 IP不要用ping判斷ICMP 可能被安全組攔掉但 TCP 443 是通的。4.3 項目、用戶與機器人賬號的權限模型Harbor 的權限模型剛接觸會覺得繞講清楚其實就三層。項目是最小隔離單位鏡像地址的格式是hostname/項目名/鏡像名:tag那個中間段就是項目。成員是用戶和項目的綁定關系同一個用戶在不同項目可以有不同角色。機器人賬號是給自動化系統用的憑據名字自動帶robot$前綴可以精確限定到某個項目、某類操作。為什么要有機器人賬號因為 CI 系統里存明文密碼風險太大機器人賬號的特點是只能操作指定項目、可以設置過期時間、權限只給推或只給拉。實踐中的配法是CI 用一個只有推送權限的機器人賬號部署節(jié)點用一個只有拉取權限的機器人賬號人類用戶用自己賬號三條線分離出事能定位。創(chuàng)建路徑是進入項目 → 機器人賬戶 → 新建 → 勾選權限推送、拉取、刪除等→ 設置過期天數 → 保存。生成的令牌只顯示一次關掉頁面就再也看不到必須當場復制到 CI 的密鑰管理里。這個設計我第一次用的時候差點吃虧復制晚了只能刪了重建。4.4 一次完整的推送與拉取演練鏈路走一遍心里才踏實。假設本地有個鏡像叫myapp:1.0要推到dev項目。# 1. 登錄回車后輸入密碼或令牌 docker login harbor.internal -u robot\$devci # 2. 打標簽格式必須是 主機/項目/鏡像:標簽 docker tag myapp:1.0 harbor.internal/dev/myapp:1.0 # 3. 推送 docker push harbor.internal/dev/myapp:1.0 # 4. 換臺機器拉取 docker pull harbor.internal/dev/myapp:1.0幾個細節(jié)值得強調。第一docker login的-u參數里如果有$在命令行里要轉義或加單引號否則 shell 會當成變量展開結果是用戶名變成空字符串報unauthorized。第二鏡像地址的斜杠結構是強制的harbor.internal/myapp:1.0這種少一段的寫法會被 Docker 解釋成 Docker Hub 上的倉庫推不上去。第三標簽里的 tag 不要用latest做唯一版本Harbor 支持不可變標簽規(guī)則可以在項目配置里開啟防止同名標簽被覆蓋導致線上拉到的鏡像悄悄變了。推上去之后回 Web 界面看dev項目里應該出現myapp倉庫點進去能看到標簽、大小、拉取次數、漏洞掃描結果。第一次看到掃描報告可能會有不少條目別慌大部分是基礎鏡像里的歷史組件勾選“僅顯示可修復”會清爽很多。4.5 配額、垃圾回收與容量治理這一小節(jié)是很多人忽略、但遲早會撞上的問題。刪除鏡像不等于釋放磁盤。Harbor 的存儲是分層共享的刪掉一個 tag 只是刪了引用底層的 Blob 還在必須跑垃圾回收才會真正釋放。流程是這樣的先在配置管理里把倉庫設為只讀Repository read only打開然后在系統管理 - 垃圾回收里新建一次回收任務勾選“刪除未被引用的 Blob”等它跑完再把只讀關掉。為什么必須只讀因為 GC 在掃描時如果有新鏡像正在推送可能把正在寫的層判定為無引用而刪掉后果是鏡像損壞。這個順序我吃過一次教訓當時沒開只讀回收完有個鏡像拉下來manifest unknown只能重新推一遍。配額可以按項目設在項目配置里填存儲上限比如 20GB超了之后推送會直接報denied: requested access to the resource is denied這個報錯信息起得不太好配額和權限共用同一個錯誤碼排查時容易走偏。配額的好處是防止某個人把整盤塞滿導致所有人都用不了。容量監(jiān)控靠兩件事一看df -h /data的剩余空間二看 Harbor 首頁的存儲統計。我在機器上加了一條定時任務磁盤超過 80% 就發(fā)通知這個簡單動作能避免絕大多數“倉庫突然掛了”的事故。5. 踩坑實錄與排查速查表這一節(jié)是我認為整篇最有價值的部分。前面所有步驟理論上都能一次成功但實際動手時總有各種偏差下面這些錯我基本都親手撞過一遍。5.1 證書與協議類報錯最常見的兩條。第一條是http: server gave HTTP response to HTTPS client意思是客戶端用 HTTPS 去連服務端返回的是 HTTP。原因通常是 Harbor 只啟了 HTTP 沒啟 HTTPS而你客戶端按默認 HTTPS 發(fā)起。解決辦法要么給 Harbor 配上證書要么把insecure-registries加上。不要嘗試把倉庫地址改成http://前綴Docker 不認這個寫法docker push http://xxx會直接把地址當成鏡像名的一部分。第二條是x509: certificate signed by unknown authority。這說明證書鏈沒被信任檢查三件事/etc/docker/certs.d/hostname/ca.crt是否存在且內容正確、目錄名里的主機名和端口是否完全匹配、改完有沒有重啟 Docker。還有一種隱藏情況你配的證書 SAN 里只有harbor.internal但客戶端用的是 IP 訪問那自然不匹配補齊 SAN 或者改了訪問方式。第三條是瀏覽器能打開但 curl 報證書錯誤這多半是瀏覽器點了“繼續(xù)訪問”的例外而命令行沒有這個例外機制本質上還是證書沒進信任鏈。5.2 認證與權限類報錯unauthorized: authentication required就是沒登錄或者登錄失效。Docker 的憑據存在~/.docker/config.json有時候換了賬號但憑據還是舊的docker logout再login能解決大半。另一個高頻原因是密碼里帶特殊字符在命令行里被 shell 吃掉了建議用--password-stdin從標準輸入傳echo 你的密碼 | docker login harbor.internal -u youruser --password-stdindenied: requested access to the resource is denied有兩個完全不同的原因一是項目不存在或者你沒有該項目權限二是鏡像地址的項目段寫錯了。第二種特別隱蔽因為報錯一樣。我當時推一個鏡像報這個錯折騰半天最后發(fā)現項目名叫dev我寫成了developHarbor 不會自動建項目就直接拒了。還有一種情況是用了機器人賬號但權限沒勾全比如只給了拉取沒給推送推的時候報同樣的錯。排查方式是去 Web 界面看該賬號的權限列表一目了然。5.3 存儲與性能類報錯no space left on device是硬故障處理順序是先df -h確認是哪個分區(qū)滿再看/data/docker和/data/harbor占用清掉無用鏡像和容器跑一次 GC。預防手段前面說過日志輪轉和磁盤告警。context deadline exceeded出現在推送大鏡像時通常是網絡抖動或者 Nginx 緩沖區(qū)配置問題。Harbor 的 Nginx 模板里對 registry 路徑已經關掉了proxy_buffering一般不會有問題但如果中間又套了一層反向代理那層代理的client_max_body_size和超時時間就要單獨調大。默認client_max_body_size 1m是 Nginx 的出廠值它會把大層上傳直接掐斷癥狀是推送到一半失敗、日志里是 413。還有一個性能問題值得提Harbor 的數據庫在小機器上并發(fā)高了會慢表現是 Web 界面點擊后轉圈很久。這時候看max_open_conns和機器內存往往內存吃緊導致頁緩存不足PostgreSQL 頻繁讀盤。加內存或者把max_job_workers調小都能緩解。5.4 一頁紙速查表現象最可能原因處理動作server gave HTTP response to HTTPS client客戶端用 HTTPS 連 HTTP 服務配證書或加 insecure-registriesx509: certificate signed by unknown authority證書未進信任鏈或 SAN 不匹配放 ca.crt 到 certs.d 并核對 SANlookup harbor.internal: no such host域名無法解析改 hosts 或配內網 DNSunauthorized: authentication required未登錄或憑據過期重新 login用 stdin 傳密碼denied: requested access to the resource項目名錯、無權限、超配額核對地址、成員角色、項目配額no space left on device磁盤滿清空間后執(zhí)行 GC413 請求實體過大前置代理體積限制調大 client_max_body_size頁面 502后端服務未就緒或崩潰看 core 與 nginx 日志、看內存推送成功但列表看不到推到了別的項目或標簽錯看 Web 界面全量搜索鏡像名刪除鏡像后空間沒釋放未執(zhí)行垃圾回收開只讀 → 跑 GC → 關只讀6. 學習環(huán)境之外的加固思路與備份恢復單機能跑之后接下來做什么取決于這套東西會不會真的承載業(yè)務。如果只是學習這一節(jié)可以挑著看如果已經有同事在往里推鏡像了下面幾件事越早做越省心。6.1 證書續(xù)期與自動簽發(fā)自簽證書十年的有效期在內部環(huán)境夠用但有一個隱患換機器、改 IP、加域名的時候要重新簽發(fā)而所有客戶端都得同步更新。機器一多這件事會變成負擔。更穩(wěn)的做法是走自動簽發(fā)用一個能訪問的 ACME 服務把 80 端口暴露給驗證流程讓工具自動簽發(fā)和續(xù)期證書放固定路徑Harbor 配置里指向它續(xù)期后重啟 Nginx 容器即可生效。如果不想引入外部依賴也可以自建一個內部簽發(fā)機構用它簽服務端證書然后把根證書分發(fā)到所有客戶端的certs.d目錄。好處是內部證書鏈條可控換服務端證書不用動客戶端。不管哪種方式都要有一個到期提醒。我見過最尷尬的情況是某天早上所有人都推不了鏡像查了半天是證書過期而前一天還沒有任何異常。加一條定時檢查證書剩余天數的腳本少于 30 天就發(fā)通知成本極低。6.2 備份與恢復演練Harbor 的備份只需要四樣東西harbor.yml、證書目錄、數據卷主要是 registry 的存儲目錄、數據庫導出。數據庫導出用容器里的 pg_dumpdocker exec harbor-db pg_dump -U postgres -d registry /data/backup/registry_$(date %F).sql數據卷可以直接用tar打包但要注意體積幾十 GB 的鏡像打包會很慢。更聰明的方式是只備份元數據和配置鏡像本身在極端情況下可以重新推送——如果你有 CI重新構建推一遍就行。所以我的策略是數據庫每天導數據卷每周打一次配置文件隨改隨備份。恢復流程也要演練一次不然真出事手忙腳亂。大致步驟是新機器裝 Docker、恢復配置文件與證書、把數據卷掛回去、導入數據庫、執(zhí)行./install.sh。整個過程我實際跑過一次從零到服務可用大概二十分鐘前提是備份文件都在。6.3 反向代理與端口收斂Harbor 自帶 Nginx已經夠用但有時候你需要在一個公網入口后面掛多個服務那就得在前面再加一層反向代理。這時候要注意幾個配置proxy_set_header Host $host必須帶上否則 Core 服務生成的跳轉地址會錯client_max_body_size要調到至少 1Gproxy_request_buffering off讓 Docker 的分塊上傳直通后端避免大鏡像先落盤再轉發(fā)。端口收斂的意思是對外只開 443把 8443 關掉。Harbor 的 HTTP 端口本來是跳轉用的但它在某些配置下會直接暴露 API如果安全組沒管好就是個意外入口。收斂之后客戶端行為也更統一。6.4 資源監(jiān)控與告警的最小配置不需要上完整監(jiān)控體系先把三個指標盯住就夠磁盤使用率、容器重啟次數、接口響應時間。磁盤用系統自帶的定時任務加腳本容器重啟次數用docker inspect里的RestartCount定期采樣響應時間用一次curl -o /dev/null -s -w %{time_total}記日志。再進一步可以給 Harbor 自身的/api/v2.0/health接口做探活返回 200 說明核心服務正常。這個接口不需要認證適合放在外部撥測里。我配了一個每五分鐘跑一次的腳本連續(xù)三次失敗就告警比等用戶反饋快得多。7. 一些踩過之后的個人體會整套東西跑通之后我最大的感受是私有鏡像倉庫的難點根本不在“裝起來”裝起來只要半小時真正的功夫在“讓它穩(wěn)定地被別人用”。我第一次部署完興奮了半天結果第二天同事來推鏡像一個證書問題卡了兩小時因為他機器上的certs.d目錄名帶了端口我沒有。這類問題的共同點是配置項的名字看起來都差不多但差一個字就不生效而且不生效的時候往往不報錯只是行為不對。第二個體會是關于“學習環(huán)境”的邊界。一開始我什么都圖省事自簽證書、單機、不開備份覺得反正壞了重裝。但當我真的往里推了幾十個鏡像之后心態(tài)就變了重裝意味著重新推一遍代價不小。所以哪怕是學習環(huán)境我也建議從第一天就把data_volume和數據盤規(guī)劃對把備份腳本寫好這兩件事的成本很低收益是把“隨便折騰”變成“折騰不心疼”。第三個是給準備復現這套流程的人一個提醒遇到報錯先看日志不要猜。Harbor 的日志集中在data_volume/log下core、jobservice、registry 各有文件Nginx 的在nginx目錄里。我踩過的坑里有一半是因為我根據錯誤信息想象了一個原因結果改了半天配置實際問題在另一層。養(yǎng)成先tail日志再動手的習慣能省掉大量時間。最后分享一個我常用的排查套路當docker push失敗時先用curl -k https://harbor.internal/api/v2.0/systeminfo確認服務端到底通不通再docker login確認認證最后才看推送。三步走下來問題落在哪一層非常明確比一上來就翻 Docker 日志高效得多。