首页 / 资讯中心 / 文章详情

Nginx从入门到实战:反向代理、负载均衡、SSL配置与高并发调优全解析

Nginx从入门到实战:反向代理、负载均衡、SSL配置与高并发调优全解析 ★ FEATURED ARTICLE
做后端、运维或者全栈的朋友估计没人没听过“Nginx”这个名字。它在Web服务里的地位差不多相当于水电在小区里的地位——平时默默扛着流量一旦配置出问题整条业务链都跟着遭殃。这篇文我不打算讲空话而是把Nginx核心功能、常用配置、生产环境里真正用得上手的操作以及我这些年踩过的坑摊开聊聊。网上Nginx教程多如牛毛但要么是文档复读要么只讲单点问题。这篇文章会把“Nginx能做什么、怎么装、怎么配、出问题怎么查”串成一条线既适合刚接触Nginx的入门读者也适合部署过几次但一直停留在“能用就行”阶段的朋友。1. Nginx到底解决了什么问题核心功能全景拆解很多人一提到Nginx就想到“反向代理”其实它远不止这一个身份。先把全景图理清楚后面配置起来才不会迷茫。1.1 静态资源服务Nginx的看家本领Nginx最原始的强项是处理静态文件。图片、CSS、JS、HTML这类不经过计算的资源Nginx的处理效率非常高因为它采用事件驱动架构一个进程可以同时照看成千上万个连接。对比传统的Apache每个连接占用一个线程或进程的模型Nginx在高并发场景下占用的内存和CPU要小得多。实际场景里一个纯静态站点、一个前端打包出来的dist目录、或者一堆用户上传的图片都可以直接丢给Nginx托管。配置起来也最简单server { listen 80; server_name static.example.com; root /data/www/static; index index.html; }这里有几个细节值得注意。root指定的路径会直接拼接URL比如请求/images/logo.png实际读取的是/data/www/static/images/logo.png。如果你希望URL和磁盘路径不一致可以用alias替代rootlocation /download/ { alias /data/files/; }这时请求/download/a.zip实际读取的是/data/files/a.zip。这点很容易记混我见过不少同事在这个上面栽过跟头。处理静态资源时还需要配合几个优化项。一是开启gzip压缩减少传输体积二是给静态文件设置合理的缓存过期时间让浏览器减少重复请求三是留意大文件下载场景下sendfile指令的配置。sendfile on可以让数据直接从磁盘拷贝到网卡跳过用户态和内核态的多次复制对性能提升非常明显。1.2 反向代理与负载均衡后端服务的入口闸门这是Nginx在生产环境中的核心价值。所谓反向代理就是客户端请求先进NginxNginx再按照规则把请求转发给后端的应用服务器。对外部来说客户端只跟Nginx打交道后端服务器完全被屏蔽在内部网络里。为什么要多这一层几个现实考量一是安全后端不直接暴露公网降低被攻击面二是统一入口多个服务共用一个域名和端口用路径区分路由三是可以做负载均衡把请求分摊到多台后端机器上四是可以在入口层统一处理SSL证书、超时、重试、限流等横切逻辑。负载均衡是反向代理的进阶形态。Nginx内置了多种调度算法默认的轮询、加权轮询、ip_hash按客户端IP哈希固定分配、least_conn分配给当前连接数最少的后端。如果后端服务需要保持会话比如存了Session在本地ip_hash非常有用但要注意它可能造成负载不均。更推荐的方案是让后端服务本身无状态化Session放到Redis里这样负载均衡策略可以更加自由。1.3 容易被忽略的SSL终止与动静分离SSL证书配置是Nginx承担的一项重要职责。所谓SSL终止就是TLS握手、证书校验、加解密这些计算全部在Nginx这一层完成后端应用服务只接收明文HTTP请求。这样做的好处是解密压力集中在Nginx上后端可以专注于业务逻辑证书统一管理不用在每个应用里各自配置一遍。动静分离则是上一节静态服务的延伸。常见架构是请求进来后Nginx先判断是静态资源还是动态接口。静态资源直接在Nginx层返回动态请求才转发给后端。这样既能大幅减轻后端压力也能让静态资源享受Nginx的高效IO和缓存能力。判断逻辑通常就是location匹配加proxy_pass转发。2. 从零上手Nginx安装与第一个站点这一节解决“怎么把Nginx跑起来”的问题。不同操作系统、不同网络环境选择完全不同。2.1 安装方式怎么选包管理器还是源码编译主流的安装方式有三种系统包管理器安装、官方源码编译安装、二进制包直接解压运行。每种方式都有各自适用的场景。如果是CentOS或Ubuntu等常见发行版直接用包管理器最省事# CentOS / RHEL 系列 yum install nginx # Ubuntu / Debian 系列 apt install nginx包管理器安装的优点是快速、稳定、依赖自动解决还能跟随系统更新补丁。缺点也很明显版本通常落后于官方最新版而且默认编译参数是发行版维护者定死的比如有的默认不带http_ssl_module想加模块就得折腾。源码编译安装能让你完全掌控编译参数适合需要定制模块、追求最新版本或者离线内网部署的场景。经典三步走# 1. 下载源码并解压 wget https://nginx.org/download/nginx-1.24.0.tar.gz tar -zxvf nginx-1.24.0.tar.gz cd nginx-1.24.0 # 2. 配置编译参数 ./configure --prefix/usr/local/nginx \ --with-http_ssl_module \ --with-http_gzip_static_module \ --with-stream # 3. 编译安装 make make install这里重点说下configure参数怎么定。--prefix指定安装目录建议放在/usr/local/nginx后续升级、卸载都方便。--with-http_ssl_module一定要加否则后面配HTTPS会提示找不到ssl模块。--with-stream是四层TCP/UDP代理模块做数据库或Redis代理时会用到提前编译进去能省很多事。编译前还需要准备好依赖最常见的是PCRE正则支持、zlibgzip压缩支持和OpenSSLSSL支持。有些系统默认有有些不全缺什么补什么即可。nginx -V命令可以查看当前Nginx的编译参数这个信息在平滑升级时非常关键。2.2 三步完成基础配置并启动服务安装完成后最关键的动作是确认Nginx能正常启动。先看默认配置# 查看版本和编译参数 nginx -V # 检查配置语法 nginx -tnginx -t这步非常推荐每次都执行。它会提示配置文件里哪里写错了、哪里路径不存在比启动时再报错容易排查得多。确认无错后启动# 直接启动 nginx # 或者通过sbin目录下的可执行文件启动 /usr/local/nginx/sbin/nginx -t /usr/local/nginx/sbin/nginx启动后访问服务器的80端口能看到Nginx的默认欢迎页说明这一套已经跑通了。日常运维中最常用的几个命令也一并记住# 停止 /usr/local/nginx/sbin/nginx -s stop # 优雅退出处理完当前请求再退出 /usr/local/nginx/sbin/nginx -s quit # 重载配置平滑不中断服务 /usr/local/nginx/sbin/nginx -s reload # 重新打开日志文件 /usr/local/nginx/sbin/nginx -s reopenreload几乎是你改完配置后必用的命令。它不会粗暴杀掉worker进程而是主进程重新读取配置、启动新的worker进程再把旧的worker平滑替换掉。这意味着线上改配置可以做到用户无感知。2.3 离线环境与Windows环境安装的特别提醒很多朋友在企业内网部署时会遇到“纯内网、没有外网”的限制。这种场景下源码编译安装反而是最靠谱的路径。提前在一台能上网的机器上把源码包、依赖源码包全部下载好打包拷进内网按顺序解压编译。关键点是依赖版本要提前验证。PCRE建议用8.45版本跟Nginx的兼容性比较稳OpenSSL和zlib也要选稳定版本不然编译到一半报错会非常折磨人。Windows下安装Nginx则简单很多。去官网下载Windows版本压缩包解压到某个目录直接运行nginx.exe即可。需要注意两点一是目录路径不要带中文否则可能出现奇怪的报错二是Windows版Nginx缺少一些Unix下的信号机制停止服务要执行nginx.exe -s stop不能像Linux一样直接kill主进程。我见过有人把Linux的停止命令习惯带到Windows上结果进程僵在那里半天新配置死活加载不了。3. 反向代理实战把后端服务安全地暴露出去反向代理配置是生产环境中需求最旺盛的场景。这一节我把从“接到部署需求”到“配置落地”的完整流程拆开讲。3.1 后端部署时你需要先准备好这些信息很多人问“后端部署时Nginx需要什么信息”其实核心就是四样东西后端服务的访问地址IP 端口比如192.168.1.10:8080或者内网域名。路径映射规则哪些URL前缀要转发给这个后端比如/api/、/user/等。协议类型后端是HTTP还是HTTPS是普通Web服务还是WebSocket。是否需要透传客户端信息比如真实IP、原始Host这对后端做日志分析和权限校验很重要。我遇到过最典型的沟通失误是后端同学只报了端口没报路径前缀。结果前端请求/api/loginNginx把请求转发到了后端根的/login后端接口根本没这个路由直接404。提前把路径匹配规则对齐能省去大量联调时间。3.2 一个经典反向代理配置的逐行拆解看一个完整的反代配置示例server { listen 80; server_name api.example.com; # 后端接口转发 location /api/ { proxy_pass http://192.168.1.10:8080; 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 5s; proxy_read_timeout 60s; } # 静态资源直接本地返回 location /static/ { alias /data/web/static/; expires 7d; } }这里特别提一下proxy_pass两个写法之间的区别。proxy_pass http://192.168.1.10:8080;不带URI部分Nginx会把原始请求的完整URI原样转发过去比如请求/api/login后端收到的还是/api/login。但如果写成proxy_pass http://192.168.1.10:8080/;带上了/Nginx会用location匹配到的部分替换掉原URI实际转发到后端的路径会被截掉/api前缀变成/login。这个差异极其隐蔽我在生产环境里排查过不止一次这种“明明配置对了但接口就是404”的诡异问题。另一个关键是proxy_set_header。后端为了获取客户端真实IP依赖的就是X-Real-IP和X-Forwarded-For这两个头。如果不设置后端看到的IP全是Nginx内网IP日志分析和黑白名单就全废了。3.3 负载均衡的三种常见玩法负载均衡配置结构上是把后端服务器列表抽到upstream模块里upstream backend { ip_hash; server 192.168.1.10:8080 weight3 max_fails2 fail_timeout30s; server 192.168.1.11:8080 weight1 max_fails2 fail_timeout30s; keepalive 32; } server { listen 80; server_name api.example.com; location / { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ; } }第一最简单的轮询。默认方式请求轮流发给每台后端。如果后端机器配置一样用这种方式最稳。第二加权轮询。通过weight参数控制流量比例比如配置高的机器权重设大一点适合后端机器性能不一致的场景。第三ip_hash。同一客户端IP总是转发到同一台后端。适合后端依赖本地Session、一时半会改不了无状态的场景。keepalive 32这个参数容易被忽略但影响很大。它表示Nginx和后端之间保持32个空闲的长连接避免每次请求都重新建TCP连接。对高并发接口来说能明显降低延迟和后端负载。启用后还需要配套设置proxy_http_version 1.1;和proxy_set_header Connection ;否则HTTP/1.0下长连接不生效。3.4 Nginx Proxy Manager这类可视化工具何时用现在越来越多团队用Nginx Proxy Manager这类带Web界面的Nginx管理工具。它的价值在于把证书申请、反代规则、访问控制这些高频操作图形化小白也能快速上手。我自己的看法是个人项目、小团队、API数量不多的场景用可视化工具确实省心但一旦管理面复杂起来比如几十个域名、上百条路由、需要精细的rewrite策略还是回到手写配置更可控。配置文件本身就是文档git里一存就能追溯历史这是可视化界面很难替代的。4. SSL证书与多站点部署从单站到多站的进阶HTTPS已经是标配这节讲讲证书怎么配、多站点怎么组织。4.1 证书从哪来、怎么配置到Nginx证书来源基本两条路云服务商或证书服务商平台申请的免费/付费证书或者用acme.sh等工具自动申请Lets Encrypt免费证书。不管哪种方式最终落到Nginx配置里的就是两个文件证书文件.pem或.crt格式和私钥文件.key格式。从证书服务商平台下载证书时选择Nginx类型的压缩包解压后里面就是这两个文件。配置方法如下server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/certs/example.com.pem; ssl_certificate_key /etc/nginx/certs/example.com.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; location / { root /var/www/example; index index.html; } }几条经验。第一私钥文件权限建议设为600或640避免其他用户可读。第二ssl_protocols建议只保留TLSv1.2和TLSv1.3老旧的TLSv1.0、TLSv1.1已不建议再开。第三ssl_session_cache是提升性能的关键它让同一客户端复用SSL会话参数减少重复握手对HTTPS请求量大的站点收益明显。还需要处理HTTP强制跳转HTTPS。用单独一个80端口的server块即可实现server { listen 80; server_name example.com; return 301 https://$host$request_uri; }这样用户访问http://example.com时会被自动跳到https://example.com体验统一且安全。4.2 主域名和二级域名共存的服务配置一个Nginx实例托管多个站点是最常见的需求。推荐做法是每个站点一个独立的配置文件放在/etc/nginx/conf.d/目录下文件名用域名标识比如example.com.conf和blog.example.com.conf。这样维护起来一目了然想临时禁用哪个站点改个后缀名就行。配置内容大致是这样# example.com.conf server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/certs/example.com.pem; ssl_certificate_key /etc/nginx/certs/example.com.key; root /var/www/example; index index.html; }# blog.example.com.conf server { listen 443 ssl; server_name blog.example.com; ssl_certificate /etc/nginx/certs/blog.example.com.pem; ssl_certificate_key /etc/nginx/certs/blog.example.com.key; root /var/www/blog; index index.html; }Nginx匹配server块时拿请求的Host头去跟各server_name比对。最常见的坑是两个server块都写了listen 443 ssl但证书分别对应不同域名一旦证书和域名不匹配浏览器就会提示证书错误。这里强烈建议主域名和二级域名各用各的证书虽然也可以配置多域名SAN证书但当站点独立、证书来源不同的时候分开配置更清晰到期续期也不会互相影响。还有一点需要注意server_name也支持通配符写法比如*.example.com可以匹配所有二级域名。如果二级域名数量不确定用一个通配符证书加一个server_name *.example.com即可覆盖。4.3 常见SSL报错与排查清单SSL相关报错大概是Nginx配置里最让人头疼的一部分。我系统性整理一下no required SSL certificate was sent这个报错意思是TLS握手过程中服务端没有发送证书。看到它优先查三件事证书文件路径是否存在、Nginx用户是否有读权限、ssl_certificate和ssl_certificate_key是否配对。我有一个同事曾经把生产证书路径写成了测试环境路径配置文件语法完全正确nginx -t能过但就是握手失败最后排查半天才发现是路径串了。这种情况nginx -t不会报错只能通过抓包或查看错误日志发现。另一个高频报错是证书链不完整。很多证书服务商除了签发证书还会给一个中间证书文件。如果只配置了域名证书没有配置中间证书部分客户端尤其是手机端会握手失败。Nginx里通过把域名证书和中间证书合并到一个.pem文件解决或者直接指定ssl_trusted_certificate参数。如果配置完还是不断报错建议开一下SSL错误日志定位error_log /var/log/nginx/error.log debug;然后申请日志里的关键字排查方向会清晰很多。排查完记得把日志级别调回warn或errordebug级别的日志量在线上是很恐怖的。5. 日常运维平滑升级、高并发调优与排错速查跑起来只是开始维护好才是真正的考验。这一节集中讲三个高频主题。5.1 平滑升级Nginx的正确姿势Nginx版本升级最怕的就是中断服务。好在Nginx天生支持平滑升级基本原理是通过信号控制旧主进程和新主进程交替让旧worker处理完手上请求后自动退出新worker接管新请求。完整流程如下# 1. 查看当前编译参数新版本configure时保持一致 nginx -V # 2. 下载新版本源码解压后configure make # 注意不要 make install否则会覆盖运行中的旧版本 ./configure --prefix/usr/local/nginx --with-http_ssl_module --with-stream make # 3. 备份旧二进制 cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old # 4. 用新编译出的二进制替换旧的 cp objs/nginx /usr/local/nginx/sbin/nginx # 5. 给旧主进程发送USR2信号让它启动新主进程 kill -USR2 $(cat /usr/local/nginx/logs/nginx.pid) # 6. 给旧主进程发送WINCH信号让它优雅关闭旧worker kill -WINCH $(cat /usr/local/nginx/logs/nginx.pid.oldbin) # 7. 确认新进程正常服务后彻底退出旧主进程 kill -QUIT $(cat /usr/local/nginx/logs/nginx.pid.oldbin)整套流程执行完后ps -ef | grep nginx看到的是新主进程和新worker进程旧进程全部清退。升级过程中有一个必须盯住的点第5步执行后立刻检查新worker是否成功启动。如果新worker启动失败旧worker又已经被WINCH信号关闭服务就断了。稳妥的做法是先不执行WINCH确认新进程稳定运行一两分钟后再收尾。平滑升级这个技术在面试中也经常被问到原理答清楚比背命令更能体现功底Nginx主进程收到USR2后启动新的master新旧master共存旧worker处理完存量请求后由新worker承接增量请求攻击者视角下服务从未中断。5.2 高并发场景下的核心调优参数“Nginx高并发”几乎是每个面试和技术交流都会聊的话题。先纠正一个常见误解高并发不是单靠调一个参数就能实现的而是配置层面、系统层面、架构层面共同作用的结果。Nginx配置层面核心参数其实就几个worker_processes auto; worker_rlimit_nofile 65535; events { worker_connections 10240; use epoll; } http { keepalive_timeout 65; gzip on; gzip_types text/plain text/css application/json application/javascript; upstream backend { server 192.168.1.10:8080; keepalive 32; } }worker_processes建议设成auto或等于CPU核心数。设太多反而会因为上下文切换消耗性能。worker_connections表示每个worker进程能同时打开的最大连接数乘以worker数就是Nginx能支撑的最大并发连接数粗略值。use epoll是Linux下高性能事件模型Nginx会自动选择不用手动指定但了解它是怎么工作的对理解高并发原理有帮助。系统层面要注意文件描述符限制。Nginx每处理一个连接就要占用一个文件描述符系统默认的1024上限肯定不够。通过ulimit -n 65535或者修改/etc/security/limits.conf提高上限。另外net.core.somaxconn、net.ipv4.tcp_max_syn_backlog这类内核参数也需要同步调整否则大量连接进来时会因为半连接队列满而丢包。gzip配置属于性价比极高的优化。特别是JSON接口和前端静态资源开启gzip后传输体积能减少60%以上。但要注意gzip会消耗CPU对极高并发的小响应体场景收益可能不明显建议按实际压测数据决定开还是不开。5.3 真实运维中那些让人头疼的报错这一节整理几类实战中高频出现的报错直接给排查思路。nginx: [emerg] createfile() d:/phpstudy_pro/www/admin2.com/nginx.htaccess failed这类报错常见于Windows下的集成环境。createfile失败说明Nginx向某个路径写入文件时失败了优先检查三件事这个路径是否已存在、当前用户有没有写权限、目录路径里是否包含中文或特殊字符。我还遇到过一次是因为磁盘满了写入失败后Nginx直接拒绝启动。所以看到createfile先看磁盘空间、再看目录权限顺序不要反。sbin目录下停止nginx操作时报错多半是使用方式不对。Nginx在sbin目录下正常停止是./nginx -s stop但前提是当前Nginx确实是以这个目录下的二进制启动的。如果曾经用系统包管理安装过另一份Nginx两套二进制共用同一份pid文件停止时就会出现互相干扰。排查时先ps -ef | grep nginx看清楚进程路径再决定用哪套命令。还有一类是启动时报[emerg] bind() to 0.0.0.0:80 failed说明80端口已被占用。多半是上一份Nginx还在运行或者Apache等其他Web服务占着端口。lsof -i:80看谁占着处理掉再启动。5.4 关于安全漏洞的应对思路Nginx也会爆出安全漏洞比如曾经公开的CVE-2022-41742属于缓冲区溢出类问题影响特定模块。面对这类通告正确的应对思路不是恐慌而是按部就班处理先看公告影响范围确认自己的版本和启用模块是否受影响。结合自身网络环境评估实际风险公网暴露的服务优先级最高。在维护窗口内按平滑升级流程把Nginx升级到修复版本。升级后回归验证HTTPS、反向代理、WebSocket等核心功能。平时养成习惯关注官方安全通告页面或者让监控系统定时去抓取版本更新信息。安全这件事没有一劳永逸补丁管理是运维日常的一部分。另外没事不要去用太激进的第三方编译参数官方默认参数加常用模块是稳定性与功能的平衡点。6. 写在最后一点个人经验和面试高频考点Nginx用到现在最深的感受是它不是一个“配完就扔”的工具而是一个需要持续理解、持续调优的基础设施。最后分享几个我觉得受用的小习惯和知识点。6.1 我踩过的几个坑每次改配置前先备份原文件再动手。我的习惯是这样的cp nginx.conf nginx.conf.bak.$(date %Y%m%d%H%M%S)不要嫌麻烦。线上改配置时经常出现改着改着发现思路错了想回退却发现没备份的情况。有了带时间戳的备份随时可以回滚到改之前的状态这个动作几秒钟但能在事故场景下节省大量时间。第二个习惯是改完重载前一定执行nginx -t。这个命令不会花多少时间但它能拦截掉至少80%的配置语法错误。我甚至会在CI流程里把nginx -t作为发布流水线的一步配置有问题就直接阻断发布。第三个习惯是善用nginx -T命令。它会打印出当前生效的完整配置包括所有include进来的文件。多域名多配置文件场景下想快速确认某条配置最终生效值长什么样nginx -T比逐个翻文件高效太多。我排查线上问题时第一步永远是nginx -T | grep -n server_name先看清楚当前实际生效的路由表再定位具体问题避免被一些“以为改了但其实没生效”的假象误导。6.2 面试中关于Nginx的高频考点如果正在准备面试Nginx相关的高频考点基本集中在以下几个方向反向代理和正向代理的区别。正向代理代理的是客户端反向代理代理的是服务端。这个基础概念必须严格说清楚。Nginx为什么能支撑高并发。核心是事件驱动与异步非阻塞IO模型而不是多线程。负载均衡有哪些算法各自优缺点。轮询、加权轮询、ip_hash、least_conn、fair等能结合场景解释选型。平滑升级的原理和步骤。USR2、WINCH、QUIT三个信号的作用要解释清楚。worker_processes和worker_connections怎么配置为什么不能无脑调高。502 Bad Gateway和504 Gateway Timeout的排查思路。前者通常是后端服务挂了或没启动后者通常是后端响应超时。SSL证书如何配置HTTP怎么跳转HTTPS证书链不完整怎么处理。面试官真正想听到的不是背出来的答案而是你踩过坑之后形成的判断力。比如聊到502时如果只回答“后端挂了”就很浅如果能补充“先确认本机能否直接访问后端端口、再通过Nginx错误日志确认具体是哪台后端不可用、再判断是服务问题还是网络问题”这个回答的质感完全不同。Nginx这个工具值得投入时间。它不复杂但它的每一条指令对应着真实世界的某个问题。配置懂了架构上的很多事都会顺很多。
阅读完成 · 觉得有帮助?
咨询建站