1. 项目背景与核心价值
最近在给一个客户做私有化部署,他们的研发团队规模不小,对容器镜像的管理和分发有明确的需求。客户要求镜像仓库必须安全、可控,并且能通过统一的域名访问,方便内部CI/CD流水线集成。Harbor作为企业级的容器镜像仓库,自然是首选。但直接暴露Harbor的端口(默认是80和443)给公网,或者让内部所有服务都直接访问Harbor的IP,在安全性和运维管理上都不是最佳实践。这就引出了我们今天要聊的核心:用Nginx给Harbor做反向代理。
简单来说,反向代理就像一个“前台接待”。外部请求(比如你的CI工具、开发者的docker pull命令)不再直接找Harbor,而是先找到Nginx。Nginx根据配置好的规则,把请求转发给后端的Harbor服务,再把Harbor的响应返回给客户端。这么做有几个实实在在的好处:第一,安全隔离,Harbor本身可以部署在内网,Nginx作为唯一出口,方便做统一的防火墙策略和访问控制;第二,负载均衡,如果Harbor是高可用部署,Nginx可以把流量分发给多个后端实例;第三,SSL/TLS终结,可以在Nginx这一层统一配置HTTPS证书,简化Harbor本身的配置;第四,灵活的域名和路径管理,你可以用像registry.your-company.com这样好记的域名,而不是IP加端口。
这个配置过程,网上教程不少,但很多只给了个配置文件片段,背后的原理、每一步的意图、以及实际部署中可能遇到的“坑”却讲得不多。我结合这次部署和以往的经验,把从零开始配置Nginx反向代理接入Harbor的完整过程、核心配置的逐行解读,以及几个关键问题的排查思路,系统地梳理出来。无论你是刚开始接触Harbor和Nginx,还是已经部署过但想优化配置,这篇文章都能给你提供一份可直接“抄作业”的实操指南。
2. 环境准备与前置条件梳理
在动手修改Nginx配置之前,我们需要先把“舞台”搭好。这一步看似基础,却决定了后续配置能否顺利进行。很多部署失败的问题,根源都出在环境准备不充分上。
2.1 Harbor的安装与基础访问验证
首先,你得有一个正在运行的Harbor实例。假设你已经通过离线安装包或者Helm Chart成功部署了Harbor。这里我以最常见的离线安装为例。部署完成后,你需要确认Harbor的核心服务(core,portal,registry等)都处于健康运行状态。可以通过docker-compose ps命令(如果你用的是docker-compose部署)或者kubectl get pods命令(如果是K8s部署)来查看。
关键验证点:确保Harbor本身在本地是可访问的。在部署Harbor的服务器上,尝试用curl命令访问其默认的HTTP端口(通常是80)或HTTPS端口(443,如果配置了证书)。例如:
curl -I http://localhost如果返回HTTP/1.1 200 OK或者302 Found(重定向到登录页),说明Harbor的Web服务是正常的。同时,也要测试Docker Registry API,这是docker push/pull的基础:
curl -I http://localhost/v2/这个请求应该返回401 Unauthorized,这反而是正常的,因为它表明Registry服务在运行并要求认证。如果返回404 Not Found或连接拒绝,说明Registry服务可能没起来。
注意:很多人在配置反向代理后才发现Harbor本身就有问题。务必先确保“后端服务是好的”,这是所有代理配置的前提。
2.2 Nginx的安装与基础配置
接下来是Nginx。它既可以和Harbor部署在同一台机器上,也可以部署在独立的服务器上。出于资源隔离和网络规划的考虑,我通常推荐分开部署。安装Nginx很简单,以CentOS/RHEL系列为例:
# 添加EPEL仓库(如果需要) sudo yum install epel-release # 安装Nginx sudo yum install nginx # 启动并设置开机自启 sudo systemctl start nginx sudo systemctl enable nginx安装完成后,访问服务器的IP,你应该能看到Nginx的默认欢迎页面。这证明Nginx的Web服务基础功能是正常的。
现在,找到Nginx的主配置文件。通常位于/etc/nginx/nginx.conf。这个文件里会通过include指令加载其他目录下的配置文件,例如/etc/nginx/conf.d/*.conf。为了管理清晰,我习惯为每个独立的服务(比如Harbor)创建一个单独的配置文件,放在conf.d目录下,比如harbor-proxy.conf。这样既避免了修改主配置文件的复杂性,也方便后续的启用、禁用和版本管理。
在开始编写Harbor的代理配置前,建议先备份一下Nginx的默认站点配置,或者直接将其移出sites-enabled(如果使用该目录结构)或重命名conf.d下的默认default.conf,防止对后续测试造成干扰。
2.3 网络与域名规划
这是配置前最容易忽略,但出问题后最难排查的一环。你需要明确以下几点:
- 访问域名:你希望用户通过什么地址访问Harbor?例如
harbor.example.com或registry.internal.com。这个域名需要在你公司的DNS服务器上做好解析,指向Nginx服务器的IP地址。如果只是测试,可以在客户端的/etc/hosts文件中手动添加一条记录。 - 网络连通性:确保Nginx服务器能够网络互通地访问到Harbor服务器上Harbor服务监听的端口(默认80/443,或者你自定义的端口)。可以用
telnet <harbor_server_ip> 80命令测试。 - 协议选择:是走HTTP还是HTTPS?对于生产环境,强烈建议使用HTTPS。这意味着你需要为你的域名准备SSL/TLS证书(可以是自签的用于内网,也可以是来自权威CA的)。我们会在配置中同时涵盖HTTP和HTTPS的示例,并重点讲解HTTPS的配置。
把这些都想清楚并落实后,我们才真正进入了配置的核心环节。
3. Nginx反向代理核心配置详解
现在,我们开始编写最关键的部分——Nginx的配置文件。我会创建一个名为/etc/nginx/conf.d/harbor-proxy.conf的文件,并将配置分成几个逻辑部分来讲解。
3.1 基础HTTP代理配置
我们先从最简单的HTTP代理开始,这有助于理解核心的代理指令。假设Harbor运行在IP为192.168.1.100的服务器上,使用默认的80端口。
server { listen 80; # 你规划的域名 server_name harbor.yourcompany.com; # 禁用不必要的服务器令牌,增强安全性 server_tokens off; # 核心:将根路径及所有请求代理到Harbor服务器 location / { # 后端Harbor服务器的地址和端口 proxy_pass http://192.168.1.100:80; # 以下是一组非常重要的代理头设置,它们确保了Harbor能接收到正确的客户端信息 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 一些超时和缓冲区的优化设置 proxy_connect_timeout 300s; proxy_send_timeout 300s; proxy_read_timeout 300s; proxy_buffering off; proxy_request_buffering off; } }逐行解读与避坑指南:
proxy_pass http://192.168.1.100:80;:这是反向代理的核心指令,告诉Nginx把匹配到的请求转发到哪里。地址可以是IP,也可以是主机名(需能解析)。proxy_set_header Host $host;:将原始请求的Host头(即harbor.yourcompany.com)传递给后端Harbor。这一点至关重要!Harbor的很多功能(特别是UI上的链接生成)依赖于正确的Host头。如果这里传的是后端IP,可能导致Harbor页面内的链接指向错误地址,引发一系列诡异问题。proxy_set_header X-Real-IP $remote_addr;和proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;:将客户端的真实IP传递给后端。否则,Harbor日志里看到的访问IP都将是Nginx服务器的IP,不利于审计和排查。proxy_set_header X-Forwarded-Proto $scheme;:告知后端客户端使用的原始协议(http或https)。这对于Harbor正确处理重定向和生成URL同样关键。- 超时设置(
300s):Docker的镜像推送(push)和拉取(pull)操作可能涉及大文件传输,需要较长时间。默认的超时时间可能太短,导致传输中断。根据你的网络情况和镜像大小适当调整。 proxy_buffering off;和proxy_request_buffering off;:对于大文件上传/下载场景(如Docker镜像),关闭代理缓冲可以避免Nginx先将整个文件缓存在磁盘上再转发,从而降低延迟和磁盘IO,实现流式传输。但在某些高并发或内存紧张的场景下,开启缓冲可能更稳定,需要根据实际情况权衡。
配置完成后,执行sudo nginx -t测试配置文件语法是否正确。如果显示syntax is ok和test is successful,就可以用sudo systemctl reload nginx重载配置(平滑重启,不影响已有连接)。
此时,你应该能通过http://harbor.yourcompany.com访问到Harbor的登录页面了。但这只是第一步,HTTP是不安全的。
3.2 启用HTTPS与SSL/TLS配置
生产环境必须使用HTTPS。你需要将SSL证书和私钥文件(例如harbor.yourcompany.com.crt和harbor.yourcompany.com.key)放到Nginx服务器上,比如/etc/nginx/ssl/目录下。
我们将修改配置,监听443端口,并启用SSL。同时,我们通常会将HTTP(80端口)的请求永久重定向(301)到HTTPS,强制使用安全连接。
# HTTP服务器块,用于重定向到HTTPS server { listen 80; server_name harbor.yourcompany.com; server_tokens off; # 301永久重定向到HTTPS版本 return 301 https://$server_name$request_uri; } # HTTPS服务器块,主配置 server { listen 443 ssl http2; # 启用SSL和HTTP/2 server_name harbor.yourcompany.com; server_tokens off; # SSL证书和密钥路径 ssl_certificate /etc/nginx/ssl/harbor.yourcompany.com.crt; ssl_certificate_key /etc/nginx/ssl/harbor.yourcompany.com.key; # SSL会话和协议优化 ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_protocols TLSv1.2 TLSv1.3; # 禁用不安全的旧协议 ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # HSTS头,告诉浏览器强制使用HTTPS(谨慎使用,特别是测试阶段) # add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; # 核心代理配置,与HTTP版本类似,但proxy_pass地址可能需要调整 location / { # 注意:如果Harbor后端也配置了HTTPS,这里应该是 https://... # 但通常我们在Nginx终结SSL,后端走HTTP即可(内网安全可控) proxy_pass http://192.168.1.100:80; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 因为客户端到Nginx是HTTPS,所以这里scheme是https proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Ssl on; # 额外告知后端是SSL连接 proxy_connect_timeout 300s; proxy_send_timeout 300s; proxy_read_timeout 300s; proxy_buffering off; proxy_request_buffering off; } }关键点解析:
- SSL终结:在这个架构里,Nginx负责处理复杂的SSL握手、加解密工作(即SSL终结),然后以普通的HTTP协议与后端的Harbor通信。这大大减轻了Harbor的负担,并且证书管理集中在Nginx,更加灵活。
proxy_set_header X-Forwarded-Proto $scheme;:此时$scheme变量的值是https,这能正确告知Harbor原始请求是安全的。proxy_set_header X-Forwarded-Ssl on;:这是一个非标准但有些应用(包括Harbor的某些组件)会检查的头,用于明确指示前端连接使用了SSL。- HSTS:
Strict-Transport-Security头非常强大,一旦浏览器接收,在有效期内会强制对该域名使用HTTPS。在测试和开发环境务必注释掉它,否则一旦配置错误,浏览器在缓存期内将无法通过HTTP访问,给调试带来麻烦。
配置好并重载Nginx后,访问http://harbor.yourcompany.com会自动跳转到https://harbor.yourcompany.com,并且浏览器地址栏应该显示安全锁标志。
3.3 针对Docker Client的特殊配置
通过Web界面访问正常,并不代表Docker客户端(docker login,docker push/pull)也能正常工作。Docker Daemon对Registry的交互有更严格的要求,需要额外的配置。
Docker Registry API(/v2/)在认证和传输上有其特殊性。我们需要确保Nginx能够正确处理大文件上传、分块传输编码以及Docker特有的错误响应格式。以下是在location /块内或之外可以添加的针对性优化:
server { ... # 前面的SSL等配置省略 # 针对Docker Registry API的优化设置,可以放在location /块内,也可以单独为/v2/设置location # 这里选择放在全局的server块内,对所有请求生效 client_max_body_size 0; # 取消客户端请求体大小限制,用于推送大镜像 chunked_transfer_encoding on; # 启用分块传输编码,对大文件传输友好 location / { proxy_pass http://192.168.1.100:80; ... # 其他proxy_set_header等设置 # 特别针对Docker Registry的响应头处理 proxy_set_header X-Original-URI $request_uri; } # 可选:单独为/v2/配置,进行更精细的控制 # location /v2/ { # proxy_pass http://192.168.1.100:80/v2/; # ... # 可以复用或覆盖父级的代理设置 # } }client_max_body_size 0;:这是解决docker push时报错413 Request Entity Too Large的关键。设置为0表示不限制大小。你也可以设为一个足够大的值,如10240m(10GB)。chunked_transfer_encoding on;:确保支持分块传输,这对于流式上传下载镜像层很重要。
完成这些配置后,就可以用Docker客户端进行测试了:
docker login harbor.yourcompany.com # 输入用户名密码 docker pull harbor.yourcompany.com/library/hello-world docker tag hello-world harbor.yourcompany.com/my-project/hello-world docker push harbor.yourcompany.com/my-project/hello-world4. 高级配置与性能调优
基础代理打通后,我们可以根据实际需求,进行一些增强配置。
4.1 负载均衡配置
如果后端部署了多个Harbor实例(例如通过Harbor的高可用方案),Nginx可以轻松实现负载均衡。这需要在http上下文中定义一个upstream块,然后在proxy_pass中引用它。
# 在http上下文中定义(通常放在nginx.conf的http块内,或conf.d目录的单独文件) upstream harbor_backend { # 负载均衡算法,默认是轮询(round-robin) # least_conn; # 最少连接数 # ip_hash; # 基于IP哈希,保持会话 server 192.168.1.100:80 weight=3 max_fails=3 fail_timeout=30s; server 192.168.1.101:80 weight=2 max_fails=3 fail_timeout=30s; server 192.168.1.102:80 backup; # 备份服务器,当主服务器全部不可用时启用 } server { listen 443 ssl http2; server_name harbor.yourcompany.com; ... # SSL配置省略 location / { # 指向upstream组,而不是单个服务器 proxy_pass http://harbor_backend; ... # 其他代理设置保持不变 } }weight:权重,值越大分配的请求越多。max_fails和fail_timeout:定义在fail_timeout时间内连续失败max_fails次,则将该服务器标记为不可用。backup:标记为备份服务器。
4.2 缓存与压缩优化
对于Harbor的静态资源(如UI的JS、CSS、图片),可以配置Nginx缓存,加快用户访问速度。同时,启用Gzip压缩可以减少网络传输量。
server { ... # 前面的配置省略 # Gzip压缩 gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json; location / { proxy_pass http://192.168.1.100:80; ... # 代理设置 # 静态资源缓存 - 示例:缓存Harbor UI的静态文件 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { proxy_pass http://192.168.1.100:80; # 缓存设置 expires 30d; add_header Cache-Control "public, immutable"; proxy_hide_header Set-Cookie; # 防止缓存因Cookie失效 proxy_ignore_headers Set-Cookie; } } }注意:对于Docker镜像层数据(/v2/*/blobs/*),绝对不要设置缓存,因为镜像是动态的、唯一的,缓存会导致客户端拉取到旧的、错误的镜像层。
4.3 访问控制与安全加固
可以在Nginx层增加一些基础的安全控制。
- IP白名单:限制只有特定的IP或网段可以访问管理界面或API。
location / { allow 10.0.0.0/8; # 允许内网网段 allow 192.168.1.50; # 允许某个特定IP deny all; # 拒绝所有其他IP ... # 代理配置 } - 速率限制:防止恶意爬虫或暴力破解。
http { limit_req_zone $binary_remote_addr zone=harbor_login:10m rate=10r/m; ... } server { location /c/login { limit_req zone=harbor_login burst=20 nodelay; ... # 代理配置 } } - 隐藏Nginx版本信息:在
http或server块中设置server_tokens off;(我们之前已经设置了)。
5. 配置验证与深度排错指南
配置完成后,全面的测试和问题排查是确保稳定运行的最后一道关卡。
5.1 分层次验证法
不要一上来就用Docker客户端测试,应该分层进行:
- Nginx配置语法验证:
sudo nginx -t。这是第一步,必须通过。 - Nginx服务状态:
sudo systemctl status nginx,确保服务是active (running)。 - 网络连通性测试:从Nginx服务器
curl -v http://192.168.1.100:80,确认能直接访问后端Harbor。 - 基础HTTP/HTTPS访问:用浏览器或
curl -v https://harbor.yourcompany.com访问,观察状态码和响应头。重点关注:- 状态码是否为200或302?
- 响应头中的
Server字段是否隐藏了版本?(server_tokens off生效) - 如果配置了HTTPS,证书是否有效?
- Docker API端点测试:
curl -v https://harbor.yourcompany.com/v2/。期望的响应是401 Unauthorized,并且响应头中包含Www-Authenticate: Bearer realm=...。这证明Docker Registry的认证接口工作正常。如果返回404,说明请求可能没有正确路由到Harbor的Registry组件。 - Docker客户端完整流程测试:依次进行
docker login,docker pull,docker tag,docker push。
5.2 常见问题与排查命令
当出现问题时,系统化的排查至关重要。以下是一个排查链路:
问题现象:浏览器能访问UI,但docker login失败(报错:Error response from daemon: Get "https://.../v2/": unauthorized或x509证书错误)。
排查思路1:证书问题
- 自签名证书:Docker默认不信任自签名证书。有两种解决方法:一是将自签名证书的CA根证书添加到Docker守护进程的信任链中(修改
/etc/docker/daemon.json,添加"insecure-registries": ["harbor.yourcompany.com"]}是不安全的,仅限测试);二是为你的域名申请一个受信任的CA签发的证书(如Let's Encrypt)。 - 证书不匹配:用
openssl s_client -connect harbor.yourcompany.com:443 -servername harbor.yourcompany.com 2>/dev/null | openssl x509 -noout -subject -dates检查证书的CN和有效期。确保证书的subject中的CN或SAN(主题备用名称)包含了你的域名。
- 自签名证书:Docker默认不信任自签名证书。有两种解决方法:一是将自签名证书的CA根证书添加到Docker守护进程的信任链中(修改
排查思路2:代理头传递问题
- 这是最常见的原因之一。Harbor的Core服务需要正确的
Host和X-Forwarded-Proto头来生成返回给客户端的链接(比如Token服务的地址)。如果这些头传递错误,Docker客户端拿到的认证地址会是错的。 - 检查方法:在Nginx配置中增加调试日志,或者更简单,在Harbor后端服务器上抓包,查看Harbor实际收到的请求头。也可以临时在Nginx配置里添加
add_header X-Debug-Proxy-Pass $proxy_host always;等自定义头来辅助调试。
- 这是最常见的原因之一。Harbor的Core服务需要正确的
排查思路3:Harbor配置问题
- Harbor自身的配置
/data/common/config/core/env或harbor.yml中的external_url必须设置为通过Nginx访问的完整地址,即https://harbor.yourcompany.com。这个配置直接影响Harbor生成的所有URL。 - 检查Harbor的各个服务日志,特别是
core、registry和token-service的日志。日志路径通常在/var/log/harbor/下。查看是否有关于主机名、协议或连接的错误信息。
- Harbor自身的配置
问题现象:docker push大镜像时失败,报错413 Request Entity Too Large。
- 原因:Nginx默认限制客户端请求体大小为1M。
- 解决:在Nginx配置的
http、server或location块中设置client_max_body_size 0;(无限制)或一个足够大的值,如10240m。记得重载Nginx配置。
问题现象:docker push/pull过程中连接超时或中断。
- 原因:默认的代理超时时间(如60秒)可能不够。
- 解决:适当增加Nginx配置中的
proxy_connect_timeout,proxy_send_timeout,proxy_read_timeout值,如设置为300s。 - 额外检查:检查Nginx与Harbor服务器之间的网络稳定性,是否有防火墙或安全组规则中断了长连接。
5.3 日志分析与监控配置
善用日志是运维的基本功。
- Nginx访问日志:默认格式可能信息不全。建议在
http块或server块中自定义日志格式,包含上游响应时间、后端服务器地址等信息。log_format harbor_proxy '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" "$http_user_agent" ' '"$upstream_addr" $upstream_response_time $request_time'; access_log /var/log/nginx/harbor-access.log harbor_proxy; - Nginx错误日志:
error_log /var/log/nginx/error.log warn;关注warn及以上级别的错误。 - Harbor服务日志:如前所述,在Harbor服务器上查看相关组件日志,这是定位Harbor内部问题的直接依据。
配置完成后,可以考虑将Nginx和Harbor的日志接入统一的日志收集系统(如ELK Stack),便于集中分析和设置告警。
6. 配置维护与版本升级考量
配置不是一劳永逸的,需要考虑后续的维护。
配置文件管理:建议使用版本控制系统(如Git)来管理你的Nginx配置文件(/etc/nginx/conf.d/harbor-proxy.conf)。任何修改都有迹可循,方便回滚。
Harbor升级:Harbor版本升级时,可能会引入新的API路径或对现有API有变更。在升级前,务必查阅官方Release Notes,确认是否有影响反向代理配置的改动。升级后,重复第5节的验证流程。
Nginx升级:升级Nginx版本时,要注意新版本是否废弃了某些旧指令,或者引入了更优化的新指令。在测试环境先验证配置兼容性。
证书续期:如果使用了Let‘s Encrypt等有有效期的证书,必须建立证书自动续期和Nginx配置重载的机制。Certbot等工具可以很好地自动化这个过程。
最后,我个人在多次配置中体会最深的一点是:理解数据流。一定要清晰地知道一个docker pull请求从客户端发出,经过Nginx,到达Harbor,再返回的完整路径。当出现问题时,按照这个路径,结合各级日志(Docker客户端日志、Nginx访问/错误日志、Harbor组件日志),层层递进地排查,绝大多数问题都能定位到根因。反向代理的配置就像搭桥,把每个环节的“接口”对齐了,路自然就通了。把这份配置当作一个活的文档,随着你的Harbor使用场景的深化(比如集成Clair扫描、Notary签名),可能还需要添加针对特定服务路径的代理规则,但核心原理和排查方法都是相通的。