标签 nginx 下的文章

0.0.0.0 和 127.0.0.1 的区别:为什么改个 IP 就能通了?

刚开始部署服务到服务器(或者在 Docker 容器里跑应用)的时候,很多同学都遇到过这样一个“灵异事件”

你在服务器上启动了一个 Web 服务,默认配置监听 127.0.0.1:8080。你满怀信心地在服务器本地用 curl 测试,一切正常。但是当你回到自己的电脑,试图通过服务器的公网 IP 访问时,浏览器却转圈转到超时,死活连不上。

经过一番搜索,老鸟告诉你:“把监听地址改成 0.0.0.0 试试。”
你半信半疑地改了,重启服务——通了!

这时候你可能会纳闷:都是代表“本机”,为什么 127.0.0.1 对外不通,0.0.0.0 就可以?它们到底有什么本质区别?


🎯 核心差异:你是在“自言自语”还是“广而告之”?

如果不理解网络接口(Network Interface)的概念,这两个地址看起来确实很像。但实际上,它们的监听范围完全不同。

我们可以用一个简单的比喻:

  • 127.0.0.1(回环地址):就像你在写日记

    • 只有你自己能看(本机访问)。
    • 无论你怎么喊,房间外面的人(外部网络)都听不到。
  • 192.168.x.x(局域网 IP):就像你在会议室里发言

    • 会议室里的人(同网段机器)能听到。
    • 会议室外面的人听不到。
  • 0.0.0.0(通配符地址):就像你在全频道广播

    • 你同时在写日记、在会议室发言、拿着大喇叭对着窗外喊。
    • 所有能连接到你的渠道,都能听到你的声音。

🧠 底层原理:Socket 绑定的艺术

在操作系统层面,服务器程序启动时需要创建一个 Socket 并绑定(Bind)到一个 IP 和端口上。这个“绑定”动作决定了操作系统会将哪些数据包交给这个进程处理。

1. 绑定 127.0.0.1

// 伪代码 (Go 语言)
net.Listen("tcp", "127.0.0.1:8080")

当你绑定 127.0.0.1 时,你告诉操作系统:“只接收目标地址是 127.0.0.1 的数据包。”
因为 127.0.0.1 是一个虚拟的回环接口(Loopback Interface),物理网卡(网线插口/Wi-Fi)根本不认识它。外部请求的数据包目标 IP 是你的局域网 IP(如 192.168.1.5)或公网 IP,操作系统一看:“这包是给 192.168.1.5 的,但那个进程只接 127.0.0.1 的客”,于是直接丢弃或拒绝。

2. 绑定 0.0.0.0 (INADDR_ANY)

// 伪代码 (Go 语言)
net.Listen("tcp", "0.0.0.0:8080")

0.0.0.0 在服务端编程中是一个特殊的通配符,代表“本机的所有 IP 地址”。
当你绑定它时,你告诉操作系统:“只要是发给这台机器的,不管目标 IP 是回环地址、局域网 IP 还是公网 IP,统统交给我处理。”


🔍 图解:数据包是如何“迷路”的

当你监听 0.0.0.0 时:

graph TD
    User[外部用户] -->|访问 192.168.1.5| NIC[物理网卡 eth0<br>192.168.1.5]
    Local[本机客户端] -->|访问 127.0.0.1| LO[回环接口 lo<br>127.0.0.1]
    
    NIC --> App[你的应用<br>监听 0.0.0.0:8080]
    LO --> App
    
    style App fill:#d4edda,stroke:#28a745,stroke-width:2px

当你监听 127.0.0.1 时:

graph TD
    User[外部用户] -->|访问 192.168.1.5| NIC[物理网卡 eth0<br>192.168.1.5]
    Local[本机客户端] -->|访问 127.0.0.1| LO[回环接口 lo<br>127.0.0.1]
    
    NIC -.->|❌ 被操作系统拦截| App[你的应用<br>监听 127.0.0.1:8080]
    LO --> App
    
    style App fill:#f8d7da,stroke:#dc3545,stroke-width:2px

💻 最常见的“坑”:Docker 容器

这是新人最容易踩坑的场景。

错误配置:
你在 Docker 容器里的代码写死监听 127.0.0.1

// main.go
http.ListenAndServe("127.0.0.1:5000", nil)

后果:
容器启动了,端口映射也做了(-p 5000:5000),但外部就是访问不了。

为什么?
因为 Docker 容器本身就是一个独立的网络环境(Network Namespace)。

  • 容器里的 127.0.0.1容器自己的回环接口
  • Docker 转发流量时,是从宿主机转发到容器的虚拟网卡(eth0)上。
  • 你的应用只监听了容器的“日记本”(lo),却无视了容器的“大门”(eth0)。

正确姿势:
在容器内,必须监听 0.0.0.0

// main.go
http.ListenAndServe("0.0.0.0:5000", nil)

🛡️ 安全思考:为什么不永远用 0.0.0.0?

既然 0.0.0.0 这么方便,为什么默认配置里(比如 Redis、MongoDB)经常还是 127.0.0.1

为了安全(Security by Default)。

想象一下,你在公司服务器上装了个 Redis 做缓存,没设密码。

  • 如果你监听 0.0.0.0:所有知道你服务器 IP 的人(包括公网上的黑客扫描器)都能直连你的 Redis,轻松拿走数据或植入挖矿脚本。
  • 如果你监听 127.0.0.1:只有这台服务器上的其他应用(比如你的后端代码)能访问 Redis。外部黑客扫描到了端口也连不上。

最佳实践案例:

  • Nginx/对外 API:监听 0.0.0.0(需要对外服务)。
  • 数据库/Redis/内部 Admin:监听 127.0.0.1(仅限本机微服务调用)。

📝 总结:一张表看懂怎么选
监听地址含义谁能访问?适用场景
127.0.0.1绑定回环接口只有本机的进程数据库、缓存、内部消息队列、本地调试
192.168.x.x绑定特定网卡同一局域网内的机器内网服务、公司内部工具
0.0.0.0绑定所有接口任何人(取决于防火墙)对外 Web 服务器、Docker 容器内部应用
💡 面试官的加分项

下次面试官问这个问题,你可以这样“降维打击”:

“这本质上是 Socket 绑定时 INADDR_LOOPBACKINADDR_ANY 的区别。
127.0.0.1 只能处理回环流量,数据包不走物理网卡;
而 0.0.0.0 是一个通配符,它让操作系统把所有网卡收到的、目标端口匹配的数据包都交给进程。
在云原生环境下,这个区别尤为重要,因为 Pod 或容器默认必须监听 0.0.0.0 才能接收来自 Service 或 Ingress 的流量,否则探针(Probe)会直接失败。”

懂了吗?想让世界听到你的声音,记得拿起广播(0.0.0.0),而不是躲在被窝里写日记(127.0.0.1)!

掘金、思否

⚡️ 别把时间浪费在低效复习上

很多人复习抓不住重点。作为过来人,我分析了100+份大厂面试记录,将 Go/Java/AI 的核心考察点、高频题、易错点 浓缩进了一份 PDF。

不搞虚的,全是干货。

加我微信:wangzhongyang1993,备注 【面经】 免费发你,立即纠正你的复习方向,把时间用在刀刃上。

在没有域名只有IP地址的情况下,实现HTTPS访问是可能的,但需要通过一系列步骤来确保安全性和可访问性。以下是实现这一目标的详细步骤:

一、确认公网IP地址

首先,确保你拥有一个固定的公网IP地址。公网IP地址是互联网上的基本寻址方案,用于唯一标识互联网上的计算机或服务器,是实现外部直接访问的前提条件。动态IP地址可能不适合此场景,因为它们会频繁改变,导致SSL证书失效。

二、申请IP地址SSL证书

公网IP证书申请入口

选择证书颁发机构(CA)

打开JoySSL官网,写注册码230970,获取大额优惠跟技术支持。


准备申请材料:

准备好对IP地址的所有权或管理权限的证明,因为申请过程中通常需要验证你对IP的控制权。

完成验证流程:

按照CA的要求完成验证流程,这可能包括通过文件验证、邮箱验证或其他方式证明你对IP地址的控制权。

购买证书:

购买合适的证书类型,如DV(域名验证)或OV(组织验证)证书。需要注意的是,虽然传统上IP地址SSL证书可能更多是针对企业或组织机构的,但近年来个人用户也可能有条件申请,具体需咨询CA。

三、安装SSL证书

下载证书:
一旦申请被批准,从CA处下载你的SSL证书文件和中间证书。

上传证书:
将证书文件和私钥上传至你的Web服务器软件上,如Apache、Nginx或IIS。

配置服务器:
在服务器配置中,将IP SSL证书绑定到特定的公网IP地址上,而非传统域名。在Nginx等服务器软件的配置文件中,可以指定IP地址作为server_name。
确保服务器配置正确监听HTTPS端口,并正确处理HTTPS请求。
如果需要,配置端口转发,确保即使使用非标准端口,HTTPS连接也能正确建立。

  1. 物理机直接安装 ubuntu, 所有应用都部署在 docker
  2. ssh 只允许密钥登录, 禁止 root 用户登录
  3. 所有访问( http, tcp)都通过 nginx 代理, ufw 只暴露固定的几个端口, nginx 开启 https 证书
  4. nginx 配置 geolite2, 禁止任何 国外 ip 访问, 异常访问基本都是国外 ip
  5. fail2ban 自动封禁所有 nginx 日志里面国外 ip
  6. 不安装 1panel,宝塔等任何 web 管理工具, 直接 ssh 到机器上命令行管理

分享下我的 nginx 配置

load_module "modules/ngx_http_geoip2_module.so";
load_module "modules/ngx_stream_geoip2_module.so";

worker_processes 4;

error_log /var/log/nginx/nginx_error.log;
error_log /var/log/nginx/nginx_error.log notice;
error_log /var/log/nginx/nginx_error.log info;

pid /var/log/nginx/nginx.pid;

events {
    worker_connections 1024;
}


http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    geoip2 /etc/nginx/geoip/GeoLite2-Country.mmdb {
      auto_reload 24h;
      $geoip_country_code  default=Unknown source=$remote_addr country iso_code;
      $geoip_country_name  country  names  en;
    }
    geoip2 /etc/nginx/geoip/GeoLite2-City.mmdb {
      auto_reload 24h;
      $geoip_city   default=Unknown city names en;
    }

    map $geoip_country_code $allowed_country {
        default no;
        CN yes;
    }

    map $remote_addr $allowed {
        default $allowed_country;
        127.0.0.1 yes;
        ~^192\.168\.\\d+\.\\d+$ yes;
        ~^172\.16\.0\.\\d+$ yes;
        ~^172\.17\.\\d+\.\\d+$ yes;
    }

    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' "";
    }

    log_format json_analytics escape=json '{'
    '"timestamp": "$msec", ' # request unixtime in seconds with a milliseconds resolution
    '"request_id": "$request_id", ' # the unique request id
    '"request_length": "$request_length", ' # request length (including headers and body)
    '"body_bytes_sent": "$body_bytes_sent", '
    '"remote_addr": "$remote_addr", ' # client IP
    '"time_iso8601": "$time_iso8601", '
    '"request_uri": "$request_uri", ' # full path and arguments if the request
    '"code": "$status", ' # response status code
    '"http_host": "$http_host", ' # the request Host: header
    '"server_name": "$server_name", ' # the name of the vhost serving the request
    '"request_time": "$request_time", ' # request processing time in seconds with msec resolution
    '"upstream": "$upstream_addr", ' # upstream backend server for proxied requests
    '"request_method": "$request_method", ' # request method
    '"allowed": "$allowed", '
    '"geoip_country_code": "$geoip_country_code", '
    '"geoip_country_name": "$geoip_country_name", '
    '"geoip_city": "$geoip_city"'
    '}';

    access_log /var/log/nginx/access.log json_analytics;
    error_log /var/log/nginx/error.log warn;

    set_real_ip_from 0.0.0.0/0;
    real_ip_header X-Real-IP;
    real_ip_recursive on;

    sendfile on;
    server_tokens off;
    keepalive_timeout 65;

    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
    gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

    proxy_buffering off;
    proxy_buffers 4 128k;
    proxy_buffer_size 256k;
    proxy_busy_buffers_size 256k;



    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ALL:!DH:!EXPORT:!RC4:+HIGH:+MEDIUM:-LOW:!aNULL:!eNULL;

    ssl_certificate /etc/nginx/ssl/fullchain.cer;
    ssl_certificate_key /etc/nginx/ssl/xxx.cc.key;


    include /etc/nginx/conf.d/*.conf;

}


stream {

    geoip2 /etc/nginx/geoip/GeoLite2-Country.mmdb {
      auto_reload 24h;
      $geoip_country_code  default=Unknown source=$remote_addr country iso_code;
      $geoip_country_name  country  names  en;
    }
    geoip2 /etc/nginx/geoip/GeoLite2-City.mmdb {
      auto_reload 24h;
      $geoip_city   default=Unknown city names en;
    }



    map $geoip_country_code $allowed_country {
        default no;
        CN yes;
    }

    map $remote_addr $allowed {
        default $allowed_country;
        127.0.0.1 yes;
        ~^192\.168\.\\d+\.\\d+$ yes;
        ~^172\.16\.0\.\\d+$ yes;
        ~^172\.17\.\\d+\.\\d+$ yes;
    }

    log_format json_analytics escape=json '{'
    '"timestamp": "$msec", ' # request unixtime in seconds with a milliseconds resolution
    '"connection": "$connection", ' # connection serial number
    '"pid": "$pid", ' # process pid
    '"remote_addr": "$remote_addr", ' # client IP
    '"remote_port": "$remote_port", ' # client port
    '"time_iso8601": "$time_iso8601", ' # local time in the ISO 8601 standard format
    '"upstream": "$upstream_addr", '
    '"protocol": "$protocol", '
    '"allowed": "$allowed", '
    '"request_method": "STREAM", '
    '"geoip_country_code": "$geoip_country_code", '
    '"geoip_country_name": "$geoip_country_name", '
    '"geoip_city": "$geoip_city"'
    '}';

    access_log /var/log/nginx/access.log json_analytics;
    error_log /var/log/nginx/error.log warn;

    include /etc/nginx/stream.d/*.conf;
}

ssh 代理

map $allowed $ssh_server {
    yes ssh;
}

upstream ssh {
    server  192.168.5.1:1234;
}

server {
    listen    5678;
    listen [::]:5678;
    proxy_pass $ssh_server;
    proxy_connect_timeout 30s;
    proxy_timeout 60s;

    ssl_preread on;
}

http 代理

server {
    server_name x.x.com;
    listen 1233 ssl;
    listen [::]:1233 ssl;

    http2 on;
    charset "utf-8";

    if ($allowed != yes) {
        return 404;
    }

    error_page 497 =307 https://$host:$server_port$request_uri;

    client_max_body_size 512M;
    proxy_buffering off;


    set $backend "http://192.168.5.1:1234";
    include /etc/nginx/conf.d/basic/no_log.conf;

    location / {
        proxy_redirect off;
        proxy_set_header Host $host:$server_port;
        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_pass $backend;
    }

}

昨天突然被拉进一个其他的项目组群。

翻了下聊天记录,大佬们讨论得热火朝天,结论基本一致:是 nginx 配置问题,跟域名备案没关系。

群里各种“诊断”飞来飞去:
• “你的服务器 80 和 443 端口没开出来”
• “跟在哪备案有啥问题”
• “那个报错就是你 nginx 转发问题”

他们还很笃定地说:阿里那边都已经备案了,不需要什么备案接入,你把阿里备案过的域名 nginx 指一下,直接指到字节/火山就行。

我看着都不太敢插话,只弱弱提了一句:可能需要把备案接入到字节这边,但很快就被忽略了。因为当时他们把 80 端口的访问“拧”到 443 上之后,确实一度能打开页面,大家就更坚信是配置问题解决了。

结果第二天再访问,火山那边直接给了官方提示:

网站暂时无法访问
该网站域名未根据工信部相关法律规则在火山引擎完成备案或存在违规行为

嗯……最后还是回到了我一开始想说的那句:备案号在阿里不等于在火山就能直接用,接入还是得补。 🤷

2025 年下半年,存储价格又一次成为行业聚焦点。

多家市场机构统计显示,2025 年三季度跟四季度,DRAM 和 NAND 价格一路攀升。根据 Tom's Hardware 披露的数据,2025 年 DRAM 合同价同比上涨幅度高达 171.8%,创下历史新高。此轮上涨跟 AI 数据中心建设拓展、服务器需求集中释放紧密相联,还直接引发企业 IT 基础设施采购成本上升。

对于依赖自建数据中心或中小 IDC 的企业来说,这种变化带来的冲击尤为剧烈。硬件采购从一次性预算问题,演变为难以预测的长期成本风险。服务器、SSD 和内存条的价格不再稳定,交付周期也更不确定。企业在扩容时不得不承担高价买入、供货延迟的双重压力。

因此,将硬件采购压力转化为按需付费的运营支出,把价格波动风险转移给云服务商,正在成为越来越多企业的选择。

但问题并未因此结束。

随着业务迁移到云端,企业发现云账单中存储与内存的占比仍在持续上升,即便算力配置并未明显升级,总体成本依旧水涨船高。部分团队开始反思:问题是否仅和数据量增多有关,还是资源使用方式本身就存在不合理的地方?

目前,多数云实例依旧按固定的 CPU 与内存配比来交付,诸如 2 核 4GB、4 核 8GB 的规格。早期,这种设计可简化资源管理,推动了云计算普及,但如今业务形态有所改变,企业系统一般得同时支撑多样业务,各业务对于算力、内存的消耗不一样,固定规格愈发难以契合实际需求。这导致企业要么部分资源长期闲置,要么不得不面对业务在高峰阶段出现性能瓶颈的风险。

当内存价格进入上行周期,这种规格错配带来的浪费被进一步放大:闲置的不再只是资源本身,而是越来越昂贵的成本

正是基于这样的背景,云基础设施走到新的路径分岔口:是继续就资源本身实施配置,还是转变方向围绕应用需求设计算力供给方式?

在近期面向中国区合作伙伴召开的发布会上,华为云对 Flexus 云服务器系列规格及性能进行更新,并且展示了其在各种业务负载下的运行表现。该实例基于华为云首创的柔性算力技术,打破 CPU 与内存的固定绑定关系,使企业能够按真实业务需求配置资源,从源头减少内存浪费,并结合智能调度与应用级加速改善长期运行稳定性与算力资源投入产出比。本文将从行业环境变化与技术实现等层面,剖析这种模式背后的思路,以及它所代表的云服务器演进方向。

云服务器,开始不太“合身”了

云服务器长期采用固定 CPU 与内存的配比,是工程上的一种取舍考量。早期云平台首先得解决的是规模化交付和稳定调度的问题,采用固定规格利于资源池管理,同样便于容量规划及计费设计。当业务形态呈现相对单一阶段,这样的方式尚可接纳。但究其本质它是从平台管理成本角度设计的,并非从业务负载的角度出发。

如今业务已不再是单一模式,电商、内容分发、数据库、缓存、AI 推理在一套系统中同步协同运行,对 CPU 以及内存的需求差别明显,固定规格无法精准对应实际负载,企业只能采用超出实际所需的实例型号。云服务器规格跟应用需求普遍不匹配,用户往往被迫去为用不到的算力和内存付费,引发大量资源的闲置浪费。

资源浪费只不过是表象罢了,更深层的问题体现为性能优化的复杂度。现实的业务部署不仅涉及操作系统选定,还包含网络参数、系统参数以及应用配置参数。数量往往达到数千级别,缺少专家经验积累,难以达成稳定的最优配置。单是内核跟应用层的参数组合,就已超出普通团队可控范围,调优所用的周期漫长,效果也难以把控。

从较长的时间阶段看,云服务器本身一直在不断演变,最初的资源虚拟化阶段,是把物理服务器标准化成可租借的实例;紧接着进入弹性规模阶段,采取自动伸缩的方式去应对流量变化,这两个阶段处理的是存不存在以及是否充足的问题,当下已经迈入第三阶段,关注焦点转向使用是否高效。过去,固定实例曾是工程优势,如今却愈发像是一件穿着不合身的衣服。

柔性算力:从“卖规格”到“卖能力”

怎样让资源本身更贴近应用?在 Flexus 云服务器 X 实例产品的设计里,华为云引入了柔性算力这一概念。

在 Flexus X 实例里,柔性算力首先体现在规格形态的调整变化上。传统实例一般仅仅可在少量固定比例中选择 CPU 跟内存配置,而该实例支持按业务需求实施更精细的组合配置。发布会现场提到,所有 X 实例均支持多种非常规的 CPU/ 内存配比,包括 3:1、2:5、3:7 等组合。这可减少由规格不一致引起的资源闲置,让用户更接近按实际负载付费。

然而规格数量增加,并非表示问题自动就解决了,其关键是系统如何判断哪种配置更合适。传统调度大多依据节点上剩余的 CPU 与内存。新方式需要领会业务负载本身,涵盖资源使用结构,以及随时间的变化趋势。Flexus X 实例本质上不再是调度 CPU,而是实际的业务场景。

就工程实现而言,这种转变依赖底层架构的支撑,Flexus X 实例借助华为云自研的擎天 QingTian 架构和瑶光云脑调度系统得以实现,经由计算、存储和网络资源的解耦操作,提高了资源组合的自由度,也增强了非标准规格运行状态下的稳定性。

此外,柔性算力还意味着配置不再是一次性决定,实例运行时会一直对资源使用状况进行评估,系统会判断当前配置跟负载是否相符,进而给出调整建议,而且还支持算力规格热升降的独家能力。从这个层面看,Flexus X 实例的转变不只是规格数量增多,它更像是把算力从提前打包好的商品,变成可持续优化的能力,实现“应用驱动算力”的最优体验。

关键应用加速:算力之外的第二条性能曲线

Flexus X 实例不单单改变了资源形态,还进一步深入应用执行层,解决了算力配置合理系统却依旧不稳定的问题。

此次规格升级,华为云为数据库以及中间件类的负载引入专属应用级加速机制。Flexus X 实例针对 PostgreSQL、Memcached、MySQL、Redis、Nginx 提供了独立的一键加速能力,由 X-Turbo 应用加速引擎统一驱动。此类优化不会对用户的使用途径做出改变,实例创建结束之后即可启用,平台会把调优工作完成,用户无需插手复杂参数的配置。发布会现场,华为云对该能力实测演示,在 PostgreSQL 的使用场景下,Flexus X 实例的吞吐量达到 2.1 万 + TPS,大概为同规格业界旗舰型实例的 3.4 倍

就数据库这类系统而言,峰值性能仅仅属于一方面,更为关键的是高负载持续状态下的稳定输出能力。业务系统更易受诸如延迟抖动、连接堆积等问题的干扰,而不是单次压测形成的成绩。X-Turbo 的设计目标之一正是实现性能优化长期运行状态下的吞吐与响应稳定性。

跟应用级优化同步进行的是,实例规模的进一步扩展。新一代 Flexus X2e 实例的 x86 规格从原本的 32U128G 提升至 64U256G,多核算力提升了约 30%;新增 Flexus KX1 鲲鹏实例,最高可达 80U320G,以覆盖大数据处理、内存数据库这类资源密集型场景。这意味着应用加速机制不再受中小规格环境约束,能在规模更大的资源池里发挥作用。

这一系列的变化显示出云服务器性能边界正在转移。过去,性能更多由 CPU 规格和内存容量决定。而如今,应用执行路径、参数组合的方法及调度策略成为同等要紧的变量,在固定规格的时代里,这些优化由用户自己承担,而于 Flexus X 实例中,它们被纳入到算力交付范畴,正是从这一意义出发,云服务器竞争不再只是资源规模大小的比拼,而是发展为聚焦运行效率的系统工程。

从工程能力到真实落地:柔性算力如何进入生产系统

一项新的算力供给方式,能否切实进入生产系统,首要取决于它是否具备充足的稳定性与可用性。Flexus X 实例可靠性设计向华为云旗舰级云服务器标准看齐,实现单 AZ 99.975% 的可用水平,还有跨 AZ 99.995% 的可用性。这暗示柔性算力没有以牺牲稳定性为交换代价,而是可直接承受核心业务负载的基础设施形态。

除了稳定性这一点,规模化使用还取决于运维体系自身是否具有确定性,Flexus X 实例在华为云既有的 SRE 运维体系框架内运行,强调借助标准化变更、容量预测与故障演练减少系统行为的不确定性,实现大规模实例并发运行的可控性。

从行业落地的实际来看,柔性算力最先进入的并非那种单一业务场景,而是负载结构繁杂、资源使用波动大的系统类型。其已经在医疗电商平台迁移、连锁零售系统、医药行业信息化平台、游戏服务器迁移等场景大规模部署,用以承载数据库、中间件及核心交易服务。

中软国际智能集团云业务部副总经理王春玉在发布会上分享,团队为某大型生物医药集团搭建系统的时候,引入 Flexus X 实例作为数据库及业务服务的主要承载环境,在原有系统架构未改变的情形下完成迁移,而且在性能满足要求的前提下,达成约 30% 的综合成本下降。王春玉还谈到,其团队服务的一家专业酒水直营连锁品牌,把部分核心业务迁移到 Flexus X 实例而后,通过规格按需匹配与资源利用率优化,实现整体云资源成本约 15% 的下降。这些亮眼的结果主要源于两方面:一是实例规格跟业务负载的匹配度有所提升,降低了长期闲置资源的数量;二是借助应用级加速与调度优化,降低了单位业务量所需的算力规模。

从这些真实的实际部署案例能看出,Flexus X 实例的用户一般有几个共同特性:业务负载呈现明显波动,系统结构相对复杂,然而运维及架构团队的规模较为有限,同时对长期云资源的成本敏感度较高。Flexus X 实例在未对业务形态本身作出改变的情况下,却降低了基础设施对业务扩展所施加的约束强度,让按照业务形态去配置算力成为可践行的工程实践。

可以预见,未来企业买的不再是服务器,而是业务效率。Flexus X 实例凸显了云服务器设计思路的一次转向:由“卖规格”过渡到“交付能力”,从“静态资源”过渡到“智能算力”,在 AI 成为主流计算负载的未来,此种转变大概率不会再是差异化优势,而是云基础设施的必要门槛。

RustFS 默认通过 9001 端口登录控制台,9000 端口使用 API,为了安全合规,通常采用启用 HTTPS、反向代理(诸如 nginx、traefik、caddy 等)的方式来更加安全的使用 RustFS。本文分享一种更加安全的方式,通过 Cloudflare tunnel 来访问你的 RustFS 实例。

安装 RustFS

RustFS 支持二进制、Docker 以及 Helm Chart 的安装方式,详细方法可以查看官网安装指南。将如下内容写入 docker-compose.yml 文件:

services:
  rustfs:
    image: rustfs/rustfs:latest
    container_name: rustfs
    hostname: rustfs
    environment:
      - RUSTFS_VOLUMES=/data/rustfs{1...4}
      - RUSTFS_ADDRESS=0.0.0.0:9000
      - RUSTFS_CONSOLE_ENABLE=true
      - RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001
      - RUSTFS_ACCESS_KEY=rustfsadmin
      - RUSTFS_SECRET_KEY=rustfsadmin
      - RUSTFS_TLS_PATH=/opt/tls
    ports:
      - "9000:9000"  # API endpoint
      - "9001:9001"  # Console
    volumes:
      - data1:/data/rustfs1
      - data2:/data/rustfs2
      - data3:/data/rustfs3
      - data4:/data/rustfs4
      - ./certs:/opt/tls

    networks:
      - rustfs

networks:
  rustfs:
    driver: bridge
    name: rustfs

volumes:
  data1:
  data2:
  data3:
  data4:

运行如下命令

docker compose up -d

即可安装好一个 RustFS 实例:

docker compose ps
NAME      IMAGE                          COMMAND                  SERVICE   CREATED          STATUS          PORTS
rustfs    rustfs/rustfs:1.0.0-alpha.81   "/entrypoint.sh rust…"   rustfs    22 minutes ago   Up 22 minutes   0.0.0.0:9000-9001->9000-9001/tcp, [::]:9000-9001->9000-9001/tcp

配置 Cloudflare tunnel

配置 Cloudflare tunnel 大体分为 域名配置tunnel 配置 两部分。

域名配置

域名配置是为了后期能够更方便的访问 RustFS。

  • 使用 Cloudflare 账号登录 Cloudflare Domain 界面;
  • 在左侧导航栏,Account home,如果你已经有域名,则选择 Onboard a domain,否则可选择 Buy a domain
  • 如果选择 Onboard a domain,点击该选项后,在出现的界面中输入你的域名,然后继续往下走,直到在最后选择 Continue to activation
  • 如果一切顺利,可以在域名管理首页看到添加成功的域名,其 Status 会显示为 Active

image.png

tunnel 配置

  • 使用 Cloudflare 账号登录 Cloudflare Dashboard
  • 在左侧导航栏,选择 Networks -> Connectors,在右侧界面点击 Create a tunnel
  • 在 tunnel 类型中,选择 Select Clouflared
  • Install and run connectors 中,根据 RustFS 实例所在服务器的操作系统信息,选择相应的安装方式。安装完毕后,可以在服务器上查看 cloudflared 服务的状态。运行正常后点击 Next

    systemctl status cloudflared
    ● cloudflared.service - cloudflared
         Loaded: loaded (/etc/systemd/system/cloudflared.service; enabled; preset: enabled)
         Active: active (running) since Fri 2026-01-16 21:18:53 CST; 6 days ago
       Main PID: 2538004 (cloudflared)
          Tasks: 10 (limit: 4375)
         Memory: 31.3M (peak: 38.7M swap: 8.2M swap peak: 15.3M)
            CPU: 18min 16.159s
         CGroup: /system.slice/cloudflared.service
  • Route Traffic 中,配置 HostnameService 信息。

    • Hostname 中填写的域名可用于后续访问 RustFS 实例,可以在 Domain 字段中选择 域名配置 部分添加好的域名。如果想通过子域名访问,也可以在 Subdomain 字段中输入子域名名称。
    • Service 选择服务类型和 URL。对于上述安装的 RustFS 实例,Type 可以选择 HTTP/HTTPS(如果启用了 HTTPS,可选择 HTTPS,否则用 HTTP),URL 为 localhost:9001

    image.png

  • 点击 Complete setup 完成配置。

上述配置结束后,可以在 Connectors 界面看到添加好的 tunnel,如果一切顺利,则可以看到 Status 为绿色的 HEALTHY

在 Hostname 和 Service 设置页面的 Additional application settings 部分,点击 HTTP Settings,在 HTTP Host Header 部分,输入访问 RustFS 的域名,这是为了避免后续使用出现签名错误。

登录验证

恭喜你,如果你顺利完成了上述两部分的配置后,那么现在你就可以通过你配置好的域名来访问 RustFS 实例了。本文配置的域名为 rustfs.xiaomage.vip,所以在浏览器中输入 https://rustfs.xiaomage.vip 即可访问 RustFS 实例:

image.png

输入 rustfsadmin/rustfsadmin 即可登录。

接下来就可以通过多种方式来使用 RustFS 实例了,比如 mcrc 以及 rclone

通过 mc 使用 RustFS

mc 是 Minio 的专属客户端,由于 RustFS 是 S3 兼容的,而且是 Minio 的平替,所以可以用 mc 来操作 RustFS。

前提

mc --version
mc version RELEASE.2025-08-29T21-30-41Z (commit-id=f7560841be167a94b7014bf8a504e0820843247f)
Runtime: go1.24.6 darwin/arm64
Copyright (c) 2015-2025 MinIO, Inc.
MinIO Enterprise License

使用

# 添加 `alias`
mc alias set rustfs https://rustfs.xiaomage.vip rustfsadmin rustfsadmin

# 创建存储桶
mc mb rustfs/hello

# 列出存储桶
mc ls rustfs
[2026-01-23 21:39:36 CST]     0B hello/
[2026-01-23 20:12:59 CST]     0B test/

# 上传文件到存储桶
echo "123456" > 1.txt
mc cp 1.txt rustfs/hello
/tmp/1.txt:                         ██████████████████████████████████████████████████████████████████████████████████ 100.0% 7 B       1 B/s      

# 查看上传的文件
mc ls rustfs/hello
[2026-01-23 21:40:44 CST]     7B STANDARD 1.txt

更多用法可自行探索。

通过 rclone 使用 RustFS

rclone是一个命令行工具,可以对不同云提供商上的文件和目录进行同步。

前提

rclone --version
rclone v1.72.1
- os/version: ubuntu 24.04 (64 bit)
- os/kernel: 6.8.0-71-generic (x86_64)
- os/type: linux
- os/arch: amd64
- go/version: go1.25.5
- go/linking: static
- go/tags: none

使用

  • 配置 rclone

执行 rclone config 命令,根据 RustFS 实例信息,一步步进行配置。配置完成后,会生成一个 ~/.config/rclone/rclone.conf 文件,一般内容如下:

[rustfs]
type = s3
provider = Minio
access_key_id = rustfsadmin
secret_access_key = rustfsadmin
endpoint = https://rustfs.xiaomage.vip
region = us-east-1
force_path_style = true
由于目前 RustFS 还未向 rclone 官方提 PR 以增加 RustFS provider 信息,因此使用 Minio 作为 provider。
  • 开始使用
# 列出存储桶和对象

rclone ls rustfs: --s3-sign-accept-encoding=false
        7 hello/1.txt
    11792 test/1.log
   520512 test/123.mp3
     7394 test/2.log
   147240 test/321.mp3
   

# 查看某个对象内容
rclone cat rustfs:hello/1.txt --s3-sign-accept-encoding=false
123456 

对于其他用法,可以通过 rclone --help 来自行探索。

注意:添加 --s3-sign-accept-encoding=false 参数是因为 Cloudflare 会对 Accept-Encoding 参数进行修改,在 S3 协议中,这种变更会导致 SignatureDoesNotMatch 错误,详情可以查看 RustFS issue

通过 rc 使用 RustFS

rc 是 RustFS 的 Client,用来对 RustFS 进行操作。目前,刚发布 0.1.1。可以使用 cargo 或源码编译安装。

rc --version
rc 0.1.1

目前提供 aliaslsmbrb 等多种常规命令。使用方式和 mc 类似。

# 设置 alias
rc alias set rustfs https://rustfs.xiaomage.vip rustfsadmin rustfsadmin
✓ Alias 'rustfs' configured successfully.

# 列出存储桶
rc ls rustfs
[2026-01-23 13:39:36]         0B hello/
[2026-01-23 13:56:57]         0B rclone/
[2026-01-23 12:12:59]         0B test/

# 创建存储桶
rc mb rustfs/client
✓ Bucket 'rustfs/client' created successfully.

更多用法,可以通过 rc --help 进行查看并自行探索,使用过程中有任何问题,可以在 GitHub Issue中进行反馈。

let'sencrypt 发行证书支持的验证方式有:

  1. DNS-01
  2. HTTP-01
  3. TLS-ALPN-01

其中 DNS-01 可通过 CNAME 省去设置 apikey 和 secret ,当然前提至少有一个域名是可以修改记录 programmatically.

有些 http 服务器支持自动更新证书,知道是怎么实现的吗?

其实 nginx 和 haproxy 也可以轻易实现自动更新。是的,L4 级别的监听即可,不要 terminator TLS 就可以区分是否来自 acme challenge 。

测试每一种 challenge

写在前面,本人目前处于求职中,如有合适内推岗位,请加:lpshiyue 感谢。同时还望大家一键三连,赚点奶粉钱。本系列已完结,完整版阅读课联系本人

高可用不是简单的冗余堆砌,而是无状态化、水平扩展与故障转移三者协同的艺术品

在掌握了系统压测方法论,能够准确评估系统容量边界后,我们面临一个更根本的挑战:如何让系统在真实流量冲击和故障发生时保持稳定?高可用架构设计正是解决这一挑战的核心手段。本文将深入解析无状态化、水平扩展与故障转移三大支柱技术的协同设计,帮助构建真正弹性可靠的系统架构。

1 高可用的本质:从故障避免到故障容忍的哲学转变

1.1 高可用性的核心价值重估

传统观念中,高可用意味着尽可能避免故障,而在分布式系统环境下,这一理念已转变为快速发现和恢复故障。根据Gartner的统计,企业IT系统平均每分钟的宕机成本超过5600美元,对于大型电商平台,这个数字可能达到数万美元。

高可用设计的哲学转变体现在三个层面:

  • 从完美预防到快速恢复:接受故障必然性,专注于最小化MTTR(平均修复时间)
  • 从单体坚固到分布式韧性:通过系统设计而非组件质量保证可用性
  • 从人工干预到自动化愈合:建立系统自愈能力,减少人工依赖

这种转变使我们需要重新定义高可用的成功标准:不是追求100%无故障,而是确保故障发生时业务影响可控、恢复过程自动

1.2 可用性等级的理性定位

不同业务场景对可用性有不同要求,理性定位是避免过度设计的第一步:

99.9%可用性(年停机时间≤8.76小时)适合内部管理系统
99.95%可用性(年停机时间≤4.38小时)适合一般业务系统
99.99%可用性(年停机时间≤52.6分钟)适合核心业务系统
99.999%可用性(年停机时间≤5.26分钟)适合金融交易系统

确立合理的可用性目标后,我们才能有针对性地选择技术方案,在成本与可靠性间找到平衡点。

2 无状态化:弹性架构的基石

2.1 无状态设计的本质与价值

无状态化不是简单去除会话数据,而是将状态与计算分离,使应用实例变得可替代。这种分离是水平扩展和故障转移的基础。

有状态架构的典型问题

// 问题示例:会话绑定导致扩展困难
@RestController
public class StatefulController {
    // 会话状态存储在内存中
    private Map<String, UserSession> userSessions = new ConcurrentHashMap<>();
    
    @GetMapping("/userinfo")
    public String getUserInfo(HttpSession session) {
        UserSession userSession = (UserSession) session.getAttribute("currentUser");
        // 此实例绑定特定用户会话,无法随意替换
        return userSession.getUserInfo();
    }
}

状态内嵌导致实例不可替换

无状态化改造方案

@Configuration
@EnableRedisHttpSession // 启用Redis会话存储
public class StatelessConfig {
    // 会话外部化配置
}

@RestController
public class StatelessUserController {
    @GetMapping("/userinfo")
    public String getUserInfo(@RequestHeader("Authorization") String token) {
        // 从Redis获取用户信息,不依赖本地状态
        String userJson = redisTemplate.opsForValue().get("session:" + token);
        User user = JsonUtil.fromJson(userJson, User.class);
        return user.toString();
    }
}

状态外置使实例可任意替换

2.2 无状态化的多层次实践

无状态化需要在不同层级实施协同策略:

应用层无状态:会话数据外部化到专用存储(Redis Cluster)
服务层无状态:API设计保证请求自包含,不依赖服务实例内存状态
任务层无状态:计算任务参数和结果完全自包含,支持任意重调度

无状态设计的业务适配策略

  • 完全无状态:适合查询类、计算型业务(商品查询、价格计算)
  • 外部状态:适合需要会话保持但无需实例绑定的业务(用户登录状态)
  • 轻量状态:适合短暂业务流程,状态生命周期与请求周期一致

2.3 无状态架构的代价与应对

无状态化不是银弹,需要认识其代价并制定应对策略:

性能代价:状态外部化增加网络开销,需要通过缓存、批处理优化
一致性挑战:分布式状态需要处理并发更新,采用乐观锁或版本控制
复杂度增加:需要引入额外组件(Redis、ZooKeeper),增加运维复杂度

合理的无状态化是有选择的无状态,而非盲目去除所有状态。核心是确保实例可替换性,而非完全消除状态。

3 水平扩展:流量压力的分布式化解

3.1 水平扩展的本质与架构前提

水平扩展通过增加实例数量而非提升单机性能来应对流量增长,其有效性直接依赖于无状态化程度。

水平扩展的架构前提

  • 无状态设计:实例间无数据依赖,可任意增减
  • 负载均衡:流量按策略分发到多个实例
  • 服务发现:动态感知实例上下线,实时更新路由
  • 健康检查:自动隔离故障实例,保证流量只会到达健康节点

3.2 分层扩展策略

系统不同层级需要采用不同的水平扩展策略:

接入层扩展:通过DNS轮询、全局负载均衡实现流量入口扩展

# Nginx上游服务配置示例
upstream backend_servers {
    server 10.0.1.10:8080 max_fails=3 fail_timeout=30s;
    server 10.0.1.11:8080 max_fails=3 fail_timeout=30s;
    server 10.0.1.12:8080 backup;  # 备份节点
    least_conn;  # 最少连接负载均衡
}

接入层通过集群化实现扩展

应用层扩展:无状态服务实例水平扩展,结合自动伸缩策略

# Kubernetes HPA配置示例
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: frontend-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: frontend
  minReplicas: 3
  maxReplicas: 100
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

应用层根据负载自动伸缩

数据层扩展:通过分片、读写分离等技术实现数据访问扩展

-- 数据库分片示例:用户数据按ID分片
-- 分片1:用户ID以0-4结尾
CREATE TABLE users_1 (
    id BIGINT PRIMARY KEY,
    name VARCHAR(100),
    -- 其他字段
);

-- 分片2:用户ID以5-9结尾  
CREATE TABLE users_2 (
    id BIGINT PRIMARY KEY,
    name VARCHAR(100),
    -- 其他字段
);

数据层通过分片实现水平扩展

3.3 水平扩展的粒度控制

科学的水平扩展需要精细化粒度控制,避免过度或不足扩展:

单元化扩展:按业务单元而非整体系统进行扩展,如用户服务独立于订单服务扩展
弹性伸缩:基于预测和实时指标动态调整实例数量,平衡性能与成本
分级扩展:核心服务与非核心服务差异化扩展策略,确保关键业务资源

4 故障转移:从被动应对到主动容错

4.1 故障检测:快速发现的艺术

有效的故障转移始于精准的故障检测,需要在及时性与准确性间找到平衡:

多层次健康检查策略

# Kubernetes就绪与存活探针配置
apiVersion: v1
kind: Pod
metadata:
  name: web-application
spec:
  containers:
  - name: web
    image: nginx:latest
    livenessProbe:
      httpGet:
        path: /health
        port: 8080
      initialDelaySeconds: 30
      periodSeconds: 10
      timeoutSeconds: 5
      failureThreshold: 3
    readinessProbe:
      httpGet:
        path: /ready  
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 3
      failureThreshold: 1

通过探针机制实现精准故障检测

智能故障判定:结合多个指标(响应时间、错误率、资源使用率)综合判断,避免单指标误判。

4.2 故障隔离:防止雪崩的屏障

故障转移不仅是将流量从故障实例移走,更重要的是隔离故障影响

熔断器模式:在连续失败达到阈值时自动熔断,避免重试风暴

@Component
public class ProductService {
    @CircuitBreaker(name = "productService", 
                   fallbackMethod = "getProductFallback")
    public Product getProduct(Long productId) {
        return remoteProductService.getProduct(productId);
    }
    
    public Product getProductFallback(Long productId, Exception ex) {
        return cacheService.getBasicProduct(productId);
    }
}

熔断器防止故障扩散

隔离策略

  • 线程池隔离:不同服务使用独立线程池,避免资源竞争
  • 信号量隔离:控制并发调用数,防止资源耗尽
  • 超时控制:设置合理超时时间,避免长时间阻塞
  • 限流降级:流量超过阈值时自动降级,保护系统不被冲垮

4.3 流量切换:无缝转移的技术实现

故障转移的核心是流量重路由,需要在不同层级实现协同:

负载均衡器切换:健康检查失败时自动从路由表中移除故障节点

upstream backend {
    server 10.0.1.10:8080 max_fails=3 fail_timeout=30s;
    server 10.0.1.11:8080 max_fails=3 fail_timeout=30s;
    server 10.0.1.12:8080 backup;
    
    # 故障转移配置
    proxy_next_upstream error timeout http_500 http_502 http_503;
}

负载均衡器实现自动故障转移

服务网格流量管理:基于Istio等服务网格实现细粒度流量控制

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: product-service
spec:
  host: product-service
  trafficPolicy:
    outlierDetection:
      consecutiveErrors: 5
      interval: 10s
      baseEjectionTime: 30s
      maxEjectionPercent: 50

服务网格提供高级故障检测与转移能力

5 三大支柱的协同设计

5.1 协同工作的架构模式

无状态化、水平扩展与故障转移不是孤立技术,而是相互依赖的有机整体:

无状态化赋能水平扩展:只有无状态设计,才能实现真正的无缝水平扩展
水平扩展增强故障转移:多实例为故障转移提供目标节点,使转移成为可能
故障转移保障水平扩展:在扩展过程中,故障转移确保个别实例故障不影响整体

协同架构示例

用户请求 → 负载均衡器(故障检测/转移)
                   ↓
           无状态应用集群(水平扩展)
                   ↓  
          集中式状态存储(Redis集群)
                   ↓
          数据存储层(分片/主从)

5.2 协同设计的反模式与陷阱

伪无状态陷阱:表面无状态但实际存在隐性状态依赖(如本地缓存、文件存储)
不平衡扩展:计算层扩展但数据层成为瓶颈,或相反
过度转移:过于敏感的故障检测导致频繁转移,反而影响稳定性
单点转移:故障转移机制本身存在单点故障

5.3 协同效能的度量体系

三大支柱的协同效果需要可度量的指标验证:

无状态化程度指标

  • 实例启动时间(应小于30秒)
  • 请求路由一致性(任意实例处理结果相同)
  • 状态外部化比例(超过90%状态外部化)

水平扩展效能指标

  • 线性扩展比(实例增加与性能提升比例)
  • 扩展速度(从触发到完成扩展的时间)
  • 资源利用率(避免过度或不足扩展)

故障转移质量指标

  • 故障检测时间(秒级检测)
  • 转移恢复时间(分钟级恢复)
  • 转移成功率(超过99%的转移成功)

6 实战案例:电商平台高可用架构演进

6.1 单体架构的高可用改造

初始状态:单体应用,会话绑定,数据库单点

改造步骤

  1. 无状态化改造:用户会话外置到Redis集群
  2. 水平扩展准备:应用容器化,配置负载均衡
  3. 故障转移基础:数据库主从分离,读写分离
  4. 渐进式迁移:先读流量,后写流量;先非核心功能,后核心功能

改造效果:可用性从99.9%提升至99.95%,扩展时间从小时级降至分钟级

6.2 微服务架构的高可用深化

架构特点:服务拆分,分布式依赖,复杂调用链

深化措施

  • 精细化无状态:API网关无状态化,业务服务按需无状态
  • 弹性扩展策略:基于业务优先级差异化扩展策略
  • 智能故障转移:基于调用链分析的精准故障定位和隔离

深化效果:可用性提升至99.99%,故障恢复时间从30分钟降至5分钟以内

总结

高可用架构的本质是通过无状态化、水平扩展、故障转移三大支柱的协同设计,构建能够容忍故障、快速恢复的弹性系统。

核心洞察

  1. 无状态化是基础:只有解耦状态与计算,才能实现真正的弹性
  2. 水平扩展是手段:通过分布式架构将集中式风险分解为可管理单元
  3. 故障转移是保障:在故障发生时快速隔离和恢复,最小化业务影响
  4. 协同设计是关键:三大支柱必须统一设计,相互配合,而非孤立优化

成功的高可用架构不是追求零故障,而是确保在故障发生时:

  • 系统能够快速检测定位问题
  • 故障影响被有效隔离,防止扩散
  • 业务流量被无缝转移到健康实例
  • 系统能够自动恢复,减少人工干预

在云原生时代,随着Kubernetes、服务网格等技术的成熟,高可用能力已经日益平台化、标准化。然而,技术选型只是起点,真正的挑战在于根据业务特点合理运用这些能力,构建既可靠又经济的高可用体系。


📚 下篇预告
《CDN与边缘缓存策略——静态、动态与签名鉴权的组合拳》—— 我们将深入探讨:

  • 🌐 缓存层次体系:浏览器缓存、边缘缓存、中心缓存的协同分工
  • 动态内容加速:边缘计算、智能路由与协议优化技术
  • 🔐 安全缓存挑战:签名URL、权限验证与敏感内容保护
  • 📊 缓存效能优化:命中率提升、失效策略与成本平衡
  • 🚀 边缘架构演进:从内容分发到边缘计算的范式转变

点击关注,构建高效安全的全球内容分发体系!

今日行动建议

  1. 评估现有应用的无状态化程度,制定状态外部化改造路线
  2. 设计水平扩展的容量规划与自动伸缩策略
  3. 建立多层级的故障检测与转移机制,定期进行故障演练
  4. 制定三大支柱协同效能的度量体系,持续优化高可用能力

CVE-2024-3400 Palo Alto Networks PAN-OS命令注入漏洞

今天跟大家分享一个etcd的内存大量占用的问题,这是前段时间在我们开源软件Easegress中遇到的问题,问题是比较简单的,但是我还想把前因后果说一下,包括,为什么要用etcd,使用etcd的用户场景,包括etcd的一些导致内存占用比较大的设计,以及最后一些建议。希望这篇文章不仅仅只是让你看到了一个简单的内存问题,还能让你有更多的收获。当然,也欢迎您关注我们的开源软件,给我们一些鼓励。

为什么要用ETCD

先说一下为什么要用etcd。先从一个我们自己做的一个API网关 – Easegress(源码)说起。

Easegress 是我们开发并开源的一个API应用网关产品,这个API应用网关不仅仅只是像nginx那样用来做一个反向代理,这个网关可以做的事很多,比如:API编排、服务发现、弹力设计(熔断、限流、重试等)、认证鉴权(JWT,OAuth2,HMAC等)、同样支持各种Cloud Native的架构如:微服务架构,Service Mesh,Serverless/FaaS的集成,并可以用于扛高并发、灰度发布、全链路压力测试、物联网……等更为高级的企业级的解决方案。所以,为了达到这些目标,在2017年的时候,我们觉得在现有的网关如Nginx上是无法演进出来这样的软件的,必需重新写一个(后来其他人也应该跟我们的想法一样,所以,Lyft写了一个Envoy。只不过,Envoy是用C++写的,而我用了技术门槛更低的Go语言)

另外,Easegress最核心的设计主要有三个:

  • 一是无第三方依赖的自己选主组集群的能力
  • 二是像Linux管道命令行那样pipeline式的插件流式处理(支持Go/WebAssembly)
  • 三是内置一个Data Store用于集群控制和数据共享。

对于任何一个分布式系统,都需要有一个强一制性的基于Paxos/Raft的可以自动选主机制,并且需要在整个集群间同步一些关键的控制/配置和相关的共享数据,以保证整个集群的行为是统一一致的。如果没有这么一个东西的话,就没有办法玩分布式系统的。这就是为什么会有像Zookeeper/etcd这样的组件出现并流行的原因。注意,Zookeeper他们主要不是给你存数据的,而是给你组集群的。

Zookeeper是一个很流行的开源软件,也被用于各大公司的生产线,包括一些开源软件,比如:Kafka。但是,这会让其它软件有一个依赖,并且在运维上带来很大的复杂度。所以,Kafka在最新的版本也通过内置了选主的算法,而抛弃了外挂zookeeper的设计。Etcd是Go语言社区这边的主力,也是kubernetes组建集群的关键组件。Easegress在一开始(5年前)使用了gossip协议同步状态(当时想的过于超前,想做广域网的集群),但是后发现这个协议太过于复杂,而且很难调试,而广域网的API Gateway也没遇到相应的场景。所以,在3年前的时候,为了稳定性的考量,我们把其换成了内嵌版本的etcd,这个设计一直沿用到今天。

Easegress会把所有的配置信息都放到etcd里,还包括一些统计监控数据,以及一些用户的自定义数据(这样用户自己的plugin不但可以在一条pipeline内,还可以在整个集群内共享数据),这对于用户进行扩展来说是非常方便的。软件代码的扩展性一直是我们追求的首要目标,尤其是开源软件更要想方设法降低技术门槛让技术易扩展,这就是为什么Google的很多开源软件都会选使用Go语言的原因,也是为什么Go正在取代C/C++的做PaaS基础组件的原因。

背景问题

好了,在介绍完为什么要用etcd以后,我开始分享一个实际的问题了。我们有个用户在使用 Easegress 的时候,在Easegress内配置了上千条pipeline,导致 Easegress的内存飙升的非常厉害- 10+GB 以上,而且长时间还下不来。

用户报告的问题是——

在Easegress 1.4.1 上创建一个HTTP对象,1000个Pipeline,在Easegres初始化启动完成时的内存占用大概为400M,运行80分钟后2GB,运行200分钟后达到了4GB,这期间什么也没有干,对Easegress没有进行过一次请求。

一般来说,就算是API再多也不应该配置这么多的处理管道pipeline的,通常我们会使用HTTP API的前缀把一组属于一个类别的API配置在一个管道内是比较合理的,就像nginx下的location的配置,一般来说不会太多的。但是,在用户的这个场景下配置了上千个pipeline,我们也是头一次见,应该是用户想做更细粒度的控制。

经过调查后,我们发现内存使用基本全部来自etcd,我们实在没有想到,因为我们往etcd里放的数据也没有多少个key,感觉不会超过10M,但不知道为什么会占用了10GB的内存。这种时候,一般会怀疑etcd有内存泄漏,上etcd上的github上搜了一下,发现etcd在3.2和3.3的版本上都有内存泄露的问题,但都修改了,而 Easegress 使用的是3.5的最新版本,另外,一般来说内存泄漏的问题不会是这么大的,我们开始怀疑是我们哪里误用了etcd。要知道是否误用了etcd,那么只有一条路了,沉下心来,把etcd的设计好好地看一遍。

大概花了两天左右的时间看了一下etcd的设计,我发现了etcd有下面这些消耗内存的设计,老实说,还是非常昂贵的,这里分享出来,避免后面的同学再次掉坑。

首当其冲是——RaftLog。etcd用Raft Log,主要是用于帮助follower同步数据,这个log的底层实现不是文件,而是内存。所以,而且还至少要保留 5000 条最新的请求。如果key的size很大,这 5000条就会产生大量的内存开销。比如,不断更新一个 1M的key,哪怕是同一个key,这 5000 条Log就是 5000MB = 5GB 的内存开销。这个问题在etcd的issue列表中也有人提到过  issue #12548 ,不过,这个问题不了了之了。这个5000还是一个hardcode,无法改。(参看 DefaultSnapshotCatchUpEntries 相关源码

// DefaultSnapshotCatchUpEntries is the number of entries for a slow follower
// to catch-up after compacting the raft storage entries.
// We expect the follower has a millisecond level latency with the leader.
// The max throughput is around 10K. Keep a 5K entries is enough for helping
// follower to catch up.
DefaultSnapshotCatchUpEntries uint64 = 5000

另外,我们还发现,这个设计在历史上etcd的官方团队把这个默认值从10000降到了5000,我们估计etcd官方团队也意识到10000有点太耗内存了,所以,降了一半,但是又怕follwer同步不上,所以,保留了 5000条……(在这里,我个人感觉还有更好的方法,至少不用全放在内存里吧……)

另外还有下面几项也会导致etcd的内存会增加

  1. 索引。etcd的每一对 key-value 都会在内存中有一个 B-tree 索引。这个索引的开销跟key的长度有关,etcd还会保存版本。所以B-tree的内存跟key的长度以及历史版本号数量也有关系。
  2. mmap。还有,etcd 使用 mmap 这样上古的unix技术做文件映射,会把他的blotdb的内存map到虚拟内存中,所以,db-size越大,内存越大。
  3. Watcher。watch也会占用很大的内存,如果watch很多,连接数多,都会堆积内存。

(很明显,etcd这么做就是为了一个高性能的考虑)

Easegress中的问题更多的应该是Raft Log 的问题。后面三种问题我们觉得不会是用户这个问题的原因,对于索引和mmap,使用 etcd 的 compact 和 defreg (压缩和碎片整理应该可以降低内存,但用户那边不应该是这个问题的核心原因)。

针对用户的问题,大约有1000多条pipeline,因为Easegress会对每一条pipeline进行数据统计(如:M1, M5, M15, P99, P90, P50等这样的统计数据),统计信息可能会有1KB-2KB左右,但Easegress会把这1000条pipeline的统计数据合并起来写到一个key中,这1000多条的统计数据合并后会导致出现一个平均尺寸为2MB的key,而5000个in-memory的RaftLog导致etcd要消耗了10GB的内存。之前没有这么多的pipeline的场景,所以,这个内存问题没有暴露出来。

于是,我们最终的解决方案也很简单,我们修改我们的策略,不再写这么大的Value的数据了,虽然以前只写在一个key上,但是Key的值太大,现在把这个大Key值拆分成多个小的key来写,这样,实际保存的数据没有发生变化,但是RaftLog的每条数据量就小了,所以,以前是5000条 2M(10GB),现在是5000条 1K(500MB),就这样解决了这个问题。相关的PR在这里 PR#542

总结

要用好 etcd,有如下的实践

  • 避免大尺寸的key和value,一方面会通过一个内存级的 Raft Log 占大量内存,另一方面,B-tree的多版本索引也会因为这样耗内存。
  • 避免DB的尺寸太大,并通过 compact和defreg来压缩和碎片整理降低内存。
  • 避免大量的Watch Client 和 Watch数。这个开销也是比较大的。
  • 最后还有一个,就是尽可能使用新的版本,无论是go语言还是etcd,这样会少很多内存问题。比如:golang的这个跟LInux内核心相关的内存问题 —— golang 1.12的版sget的是 MADV_FREE 的内存回收机制,而在1.16的时候,改成了 MADV_DONTNEED ,这两者的差别是,FREE表示,虽然进程标记内存不要了,但是操作系统会保留之,直到需要更多的内存,而 DONTNEED 则是立马回收,你可以看到,在常驻内存RSS 上,前者虽然在golang的进程上回收了内存,但是RSS值不变,而后者会看到RSS直立马变化。Linux下对 MADV_FREE 的实现在某些情况下有一定的问题,所以,在go 1.16的时候,默认值改成了 MADV_DONTNEED 。而 etcd 3.4 是用 来1.12 编译的。

最后,欢迎大家关注我们的开源软件! https://github.com/megaease/ 

下面是我的简历

持有 CKA ccna 认证。
长期负责高并发电商系统的稳定性与性能治理,理解 Nginx 的事件驱动模型、Worker 并发机制与请求生命周期控制方式,并将其抽象为 “进入策略( Admission )— 排队策略( Queueing )— 满载策略( Overload )” 的系统行为诊断框架。

通过拆解全链路时间指标( Time-based Metrics ),结合 P95 / P99 延迟分位数,对比 request_timeupstream_response_time,判断请求阻塞发生在进入控制、系统排队或下游依赖阶段,用于快速界定网络层、应用层与数据层的性能边界。

参与并主导多次从单体架构向云原生、无状态化架构的演进,关注系统的可解释性、稳定性与成本控制。


技术技能

SRE 与性能诊断

  • 使用 Ingress / Queue / Worker / Egress 作为系统分层对象进行问题定位

  • 基于 P95 / P99 延迟分位数 分析尾延迟( Tail Latency )

  • 通过 request_timeupstream_response_time 的分位数差异判断系统内部是否发生排队

  • 区分整体性能退化与少量请求异常放大的不同问题模式

  • 依据进入控制、排队与满载策略分析系统稳定性取舍


Web 与中间件

  • Nginx 事件驱动模型、Worker 并发模型、连接与请求生命周期

  • 使用 limit / buffer / backlog / timeout 等配置参数实施系统行为控制

  • Nginx 与 PHP-FPM 的 Worker / Process / Queue 协同关系

  • 高并发 WordPress 架构的稳定性与性能治理


云原生与基础设施

  • 阿里云:ECS 、RDS 、OSS 、ALB

  • AWS

  • Docker 、Kubernetes ( CKA )

  • Terraform (基础设施即代码)

  • Calico ( Kubernetes 网络模型与 NetworkPolicy )


数据库与数据处理

  • MySQL 使用与常见性能问题定位

  • SQL 编写

  • PL/SQL 阅读

  • ETL 流程(数据抽取、清洗、加载)

  • 结合 P95 / P99 延迟分布 判断数据库是否为系统尾延迟来源


自动化与运维

  • Ansible Playbook

  • Shell 脚本

  • 配置一致性与变更管理


网络

  • TCP/IP

  • VPN

  • 负载均衡

  • CCNA 网络体系


工作经历

基础设施负责人 / 高级运维工程师

负责自营跨境电商平台的技术架构设计、云资源管理与系统稳定性。


性能瓶颈诊断与容量优化

  • 通过时间指标发现平均响应时间稳定,但 P95 / P99 延迟显著升高

  • upstream_connect_time 正常的情况下,response_time 分位数拉长

  • 判断请求未阻塞在网络或 TCP 层,而是在系统内部发生资源排队

  • 结合 Nginx 与 PHP-FPM 的并发模型,将问题定位为应用层并发策略导致的 Worker 争用

优化措施:

  • 引入 Redis 缓存,降低同步资源竞争

  • 调整 PHP-FPM Worker 数量,减少高分位请求等待时间

  • 调整 Nginx 超时与失败处理策略,防止慢请求放大系统负载

结果:

  • 页面 P95 延迟从约 5 秒下降至 2 秒以内

  • 尾延迟明显收敛

  • 高峰期服务稳定性显著提升


电商架构云原生改造

  • 数据库迁移至阿里云 RDS

  • 媒体资源迁移至 OSS

  • 前端通过 ALB 进行多节点负载

  • 应用层实现无状态化部署

结果:

  • 系统支持水平扩展

  • 综合成本下降约 30%

  • 大促期间可用性维持在 99.9%


日常运维

  • 使用 Ansible 管理 Nginx / PHP 配置

  • MySQL 备份与恢复演练

  • OpenVPN 远程访问环境维护


早期经历

网络管理员 / IT 支持
2011 年 – 2015 年

  • 企业网络规划与服务器维护

  • SQL 数据提取与分析


教育背景与证书

  • CKA ( Certified Kubernetes Administrator )

  • CCNA ( Cisco Certified Network Associate )

请教各位。我做了 10 年电商,从 23 岁就开始做了,这些爱好自学的。现在走下坡路了,要出来找工作了。
我学历也很低,是函授大专。今年 33 岁了。
我对内核,nginx,sre ,这些也有深入的理解,我不会开发软件,纯粹的想走运维路线
最近也打算学 python ,不会写脚本确实没有竞争力。
我知道我的问题,学历低,没项目经验和实际的工作经验,年纪也大。
上面每项技术,我确实认真的学了很久,而且做过大量的实验实操,理论也很扎实。
boss 直聘主动联系我的人很多,但是简历发了后基本就没下文了。
各位有某些负责招聘的老大,希望能说一下你们的观点
假如我真的没戏,我就不打算找这些工作了。
我也做外贸独立站。然后平时会搭建很多开源项目,也不是玩,确实是投入实际使用。所以不断的积累了相关的操作。就是把自己学的,都在开源项目里应用。

别再用 Nginx 配置折磨自己了,推荐 Zoraxy 让你 3 分钟搞定反向代理

免责声明:本文中信息来源于网络,作者不保证其绝对正确性。读者在依据本文内容做出任何决策或行动前,应自行进行充分的调查与核实。对于因使用本文内容而产生的任何直接或间接损失,作者不承担任何责任。

本文为专业文章, 适合运维、开发、self-hosted 需求人员观看。


你有没有这种经历?

新部署了一个服务,要去改 Nginx 配置文件。再部署一个,又要改。改完还得nginx -s reload

有时候改错了语法,reload 失败,服务全挂了。

这时候你突然意识到:学 Nginx 配置语法的时间,比学做饭的时间还长。

别问我是怎么知道的。

Zoraxy001

现状

反向代理在运维、开发、self-hosted  场景中经常用到,目前 Nginx 、Caddy 、Traefik 是主流选择。它们有个共同点:需要改配置文件

语法要记,改完要重载,错了要排查。对于不想折腾配置文件的人来说,这门槛不低。

今天介绍一个不一样的选择:Zoraxy 最大特点是全 UI 操作,支持动态应用规则的反向代理。

1

4

Zoraxy 是什么

Zoraxy 是一款基于 go 编写的动态反向代理工具。

最大的特点:Web UI 管理,零配置文件

项目简介里写得很直白——这可能是最适合新手的反向代理管理器之一。

想到了 python 的 solgan: 人生苦短,我用 python

它不是药,但可能治好你的"配置文件恐惧症"。

让我想起一个笑话。

有人问医生:"我每天都要吃止痛药才能工作,怎么办?"

医生说:"那你就别工作了。"

Zoraxy 就是那个让你不用"吃止痛药"的选择——你不需要每天和配置文件较劲。

能做什么

  • 反向代理:HTTP/2 、WebSocket 自动代理、虚拟目录、别名主机、自定义请求头、负载均衡。

  • SSL 证书:ACME 自动申请、Let's Encrypt 支持、DNS Challenge 。

  • 访问控制:IP 黑白名单、国家/地区封禁。

  • 流代理:TCP/UDP 代理。

  • 监控:集成 Uptime Monitor ,实时主机健康检查。

  • 其他:Web SSH 终端、插件系统、实时流量分析。

2
3
4
5
6
7
8
9

快速上手

安装

因为基于 go 编写,基本上主流系统上直接安装编译好的文件就成。以 Linux 为例:

wget https://github.com/tobychui/zoraxy/releases/latest/download/zoraxy_linux_amd64

chmod +x ./zoraxy_linux_amd64

sudo ./zoraxy_linux_amd64

启动后访问 http://localhost:8000 进行初始设置(无需配置文件,全部操作在 UI 中完成)。

就这么简单。

配置反向代理

登录 Web 界面后,添加反向代理规则很简单:

  1. 填写域名(比如 ftp.server.local, 注意提前配置好你的 dns 指向)
  2. 填写目标地址(比如 http://192.168.1.100:3000
  3. 保存就动态生效了

就这么简单。

zoraxy004_createrules

zoraxy004_http

SSL 证书

Zoraxy 内置 ACME 客户端功能,支持 Let's Encrypt 等服务商证书的自动申请:证书自动续期,不用担心过期。

下面以自定义 ACME 服务器为例,展示 ssl 证书的申请。

zoraxy005_ssl

Uptime Monitor

Zoraxy 还集成了主机健康检查功能。

实时监控服务可用性,支持 HTTP/TCP/UDP 检查,失败会告警。

在"Uptime Monitor"页面添加监控目标就行。

和 Nginx/Caddy 的区别

特性 Zoraxy Nginx Caddy
配置方式 Web UI 配置文件 配置文件
动态更新 ✅ 即时生效 ❌ 需 reload ✅ 自动
SSL 证书 ACME 自动 需手动配置 ACME 自动
学习曲线
插件系统
Uptime Monitor ✅ 内置

核心差异很明显:Zoraxy 全部通过 Web 界面操作,改完立即生效,不用重载服务。

不想记配置文件语法的话,这是最大的优势。

什么时候用 Zoraxy

总体来说,zoraxy 十分适合中小企业内部, 家用 self-hosted 场景。

人生苦短, 我用  zoraxy

适合

  • 家用 lab/自托管多个服务

  • 不想折腾配置文件

  • 需要快速添加/删除代理规则

  • 需要基本的健康检查

  • 新手入门反向代理

不适合

  • 需要极高性能( Nginx/Traefik 优化更好)

  • 需要复杂的高级配置

  • 配置即代码( IaC )需求

其他信息

Zoraxy 是开源项目,AGPL 许可。

因为 go 的特性支持跨平台:Windows 、Linux 、macOS 、ARM 设备、RISC-V 。也集成到 TrueNAS 、Umbrel 、YunoHost 等应用市场。

写在最后

Nginx/Caddy 依然是优秀的选择。

但如果你厌倦了改配置文件,想要更简单的管理方式,或者刚开始接触 self-hosted ,可以试试 Zoraxy 。

就像那个老笑话:当手里拿着锤子时,看什么都像钉子。

但有时候,你需要的不是更好的锤子,而是一把螺丝刀。

Zoraxy 就是那把螺丝刀——它不是要取代你的锤子,而是给你一个不同的选择。

希望小编文章能帮助到大家,欢迎关注本公众号;有问题留言交流。

其他

欢迎关注本公众号其他社媒平台

link_logo

点击以下链接关注我的数字名片!

https://muselink.cc/hamisay

"如果您觉得这篇文章对您或您的朋友有所帮助,不妨动动手指,关注我们、点赞并分享到朋友圈,让更多人受益。您的每一次互动都是对我们最大的支持和鼓励!"

「一键部署你的专属服务器」——WNMP 一键包,让 Web 环境搭建回归简单

还在为 Nginx + PHP + 数据库 的复杂安装而头疼吗?
WNMP 一键包,让这一切变成——一行命令搞定。

apt install -y curl && curl -fL https://wnmp.org/zh/wnmp.sh -o wnmp.sh && chmod +x wnmp.sh && bash wnmp.sh

一分钟安装完整 Web 环境:

  • Nginx 1.28.0 (支持 HTTP/2 、WebDAV 、Stream )
  • PHP 8.2–8.5
  • MariaDB 10.6 / 10.11 (内置 Mroonga 全文搜索引擎)
  • 自动 SSL 证书( acme.sh
  • WebDAV 云盘支持(拒绝明文 FTP )

系统自动优化:

  • 启用 BBR/FQ 网络加速
  • 关闭 THP ,优化内核参数
  • 全面适配 Debian 12/13 、Ubuntu 22–25 、WSL2
  • 自动生成安全配置,默认防止常见漏洞

安全为先 · 默认即最优:

  • 内置 SSH 密钥登录
  • PHP 默认关闭危险函数
  • phpMyAdmin 启用 BasicAuth 双重防护
  • SSL 证书全自动签发与续期

面向开发者与站长的真正“零阻力”方案:
无论你是独立开发者、云服务商、还是边缘节点运维者,WNMP 让服务器环境部署变得和安装浏览器一样简单。
轻量、稳定、可复制 —— 一次配置,永久受益。

官方网站: https://wnmp.org
社区支持:QQ 群 1075305476 | Telegram @wnmps
Github:[url]https://github.com/lowphpcom/wnmp[/url]
开源协议:GPLv3

WNMP 不仅仅是一个脚本,它是下一代 PHP 运行环境生态的起点 ——
基于 LOWPHP 的常驻内存架构,未来将带来原生级的高性能 PHP 体验。

使用的测试文件 info.php,调用 php.info();
现在网站需要放在其他路径底下,修改了 nginx 中的 root 之后就提示 No input file specified.
但是 index.html 静态文件显示正常

在网上查的和 gpt 问,试过以下几种方式还是不行,求大佬帮忙看下

1 ,php74/etc/php-fpm.d/www.conf 文件中 chroot 和 chdir 参数都是默认注释的,
在 info.php 中,参数显示如下
USER www-data
HOME /var/www

2 ,nginx 中的 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
$document_root 或者修改成绝对路径也不行

3 ,修改 cgi.fix_pathinfo=0

4 ,;cgi.force_redirect=1 取消注释

上述的 4 种方式最多就是某 2 个一起试过。
关于文件权限问题,/var/www 使用的是 777 ,html 和我想放的文件夹 site 的权限也是 777 ,site 和网站文件的属组和属主都是 www-data
php74/etc/php-fpm.d/www.conf 文件中


user = www-data

group = www-data

listen = /run/php74-fpm.sock

listen.owner = www-data

listen.group = www-data

listen.mode = 0660


/run/php74-fpm.sock 的属组和属主是 www-data

求大佬帮忙看下还有什么办法嘛,想放到/var/www/site 文件夹下

在没有域名只有IP地址的情况下,实现HTTPS访问是可能的,但需要通过一系列步骤来确保安全性和可访问性。以下是实现这一目标的详细步骤:

一、确认公网IP地址

首先,确保你拥有一个固定的公网IP地址。公网IP地址是互联网上的基本寻址方案,用于唯一标识互联网上的计算机或服务器,是实现外部直接访问的前提条件。动态IP地址可能不适合此场景,因为它们会频繁改变,导致SSL证书失效。

二、申请IP地址SSL证书

选择证书颁发机构(CA)
打开JoySSL官网,写注册码230970,获取大额优惠跟技术支持。

准备申请材料:
准备好对IP地址的所有权或管理权限的证明,因为申请过程中通常需要验证你对IP的控制权。

完成验证流程:
按照CA的要求完成验证流程,这可能包括通过文件验证、邮箱验证或其他方式证明你对IP地址的控制权。

购买证书:
购买合适的证书类型,如DV(域名验证)或OV(组织验证)证书。需要注意的是,虽然传统上IP地址SSL证书可能更多是针对企业或组织机构的,但近年来个人用户也可能有条件申请,具体需咨询CA。

三、安装SSL证书

下载证书:
一旦申请被批准,从CA处下载你的SSL证书文件和中间证书。

上传证书:
将证书文件和私钥上传至你的Web服务器软件上,如Apache、Nginx或IIS。

配置服务器:
在服务器配置中,将IP SSL证书绑定到特定的公网IP地址上,而非传统域名。在Nginx等服务器软件的配置文件中,可以指定IP地址作为server_name。
确保服务器配置正确监听HTTPS端口,并正确处理HTTPS请求。
如果需要,配置端口转发,确保即使使用非标准端口,HTTPS连接也能正确建立。

「一键部署你的专属服务器」——WNMP 一键包,让 Web 环境搭建回归简单

还在为 Nginx + PHP + 数据库 的复杂安装而头疼吗?
WNMP 一键包,让这一切变成——一行命令搞定。

apt install -y curl && curl -fL https://wnmp.org/zh/wnmp.sh -o wnmp.sh && chmod +x wnmp.sh && bash wnmp.sh

一分钟安装完整 Web 环境:

  • Nginx 1.28.0 (支持 HTTP/2 、WebDAV 、Stream )
  • PHP 8.2–8.5
  • MariaDB 10.6 / 10.11 (内置 Mroonga 全文搜索引擎)
  • 自动 SSL 证书( acme.sh
  • WebDAV 云盘支持(拒绝明文 FTP )

系统自动优化:

  • 启用 BBR/FQ 网络加速
  • 关闭 THP ,优化内核参数
  • 全面适配 Debian 12/13 、Ubuntu 22–25 、WSL2
  • 自动生成安全配置,默认防止常见漏洞

安全为先 · 默认即最优:

  • 内置 SSH 密钥登录
  • PHP 默认关闭危险函数
  • phpMyAdmin 启用 BasicAuth 双重防护
  • SSL 证书全自动签发与续期

面向开发者与站长的真正“零阻力”方案:
无论你是独立开发者、云服务商、还是边缘节点运维者,WNMP 让服务器环境部署变得和安装浏览器一样简单。
轻量、稳定、可复制 —— 一次配置,永久受益。

官方网站: https://wnmp.org
社区支持:QQ 群 1075305476 | Telegram @wnmps
Github:[url]https://github.com/lowphpcom/wnmp[/url]
开源协议:GPLv3

WNMP 不仅仅是一个脚本,它是下一代 PHP 运行环境生态的起点 ——
基于 LOWPHP 的常驻内存架构,未来将带来原生级的高性能 PHP 体验。

使用的测试文件 info.php,调用 php.info();
现在网站需要放在其他路径底下,修改了 nginx 中的 root 之后就提示 No input file specified.
但是 index.html 静态文件显示正常

在网上查的和 gpt 问,试过以下几种方式还是不行,求大佬帮忙看下

1 ,php74/etc/php-fpm.d/www.conf 文件中 chroot 和 chdir 参数都是默认注释的,
在 info.php 中,参数显示如下
USER www-data
HOME /var/www

2 ,nginx 中的 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
$document_root 或者修改成绝对路径也不行

3 ,修改 cgi.fix_pathinfo=0

4 ,;cgi.force_redirect=1 取消注释

上述的 4 种方式最多就是某 2 个一起试过。
关于文件权限问题,/var/www 使用的是 777 ,html 和我想放的文件夹 site 的权限也是 777 ,site 和网站文件的属组和属主都是 www-data
php74/etc/php-fpm.d/www.conf 文件中


user = www-data

group = www-data

listen = /run/php74-fpm.sock

listen.owner = www-data

listen.group = www-data

listen.mode = 0660


/run/php74-fpm.sock 的属组和属主是 www-data

求大佬帮忙看下还有什么办法嘛,想放到/var/www/site 文件夹下

故事背景

用 SolidVPS 75 刀大鸡(没有 GPU),建了个 open-webui。知识库文件 3000 + 份。本地跑 bge-m3 时 cpu 负载 50%,且速度慢。因此转入 外部 API 方案。期间顺便换到 qwen-embeding-8B 试了试。

遇到速率限制问题

无论 Nvidia 还是硅基流动,都有 TPM RPM 限制。而我没找到 open-webui 里对外部嵌入模型发起请求的速率限制,因此知识库重建索引时,Nvidia 和 硅基 都容易因为 TPM 返回失败,进而导致 open-webui 无法获取到正确的向量。

解决方案

建站了,自然自带 nginx(我用 nginx-ui)进行管理。加一个流控


    # ======================================================
    # 区域 1: Nvidia (Embedding)
    # 限制: 40 RPM (每分钟40次)
    # 策略: 严格排队
    # ======================================================
    # 使用 "global" 作为 key,表示全局限制,不是按 IP
    limit_req_zone "global" zone=nvidia_limit:10m rate=40r/m;

    # ======================================================
    # 区域 2: SiliconFlow (Reranker)
    # 限制: 2000 RPM (每分钟2000次)
    # 策略: 允许突发
    # ======================================================
    limit_req_zone "global" zone=silicon_limit:10m rate=2000r/m;
    
       # ------------------------------------------------------
    # 服务 1: 代理 Nvidia (监听 8090)
    # ------------------------------------------------------
    server {
        listen 8090;
        
        location / {
            # 允许突发 100 个请求排队
            limit_req zone=nvidia_limit burst=100;
            
            proxy_pass https://integrate.api.nvidia.com;
            proxy_ssl_server_name on;
            proxy_set_header Host integrate.api.nvidia.com;
            
            # 增加超时时间,防止排队太久断开
            proxy_read_timeout 600s;
        }
    }

    # ------------------------------------------------------
    # 服务 2: 代理 SiliconFlow (监听 8091)
    # ------------------------------------------------------
    server {
        listen 8091;

        location / {
            # 允许突发 500 个请求排队
            # 2000r/m 很快,加上 nodelay 可以让请求瞬间转发,
            # 只有超过突发阈值时才开始排队或拒绝。
            # 这里不加 nodelay,保持平滑流控效果。
            limit_req zone=silicon_limit burst=500;

            proxy_pass https://api.siliconflow.cn;
            proxy_ssl_server_name on;
            proxy_set_header Host api.siliconflow.cn;
            
            
             # 增加超时时间,防止排队太久断开
            proxy_read_timeout 600s;
        }
    }

# Server 2: 回源专用 (origin-chat.example.com)
# 强制校验 Header,校验失败直接 444
server {
    listen 2083 ssl;
    listen [::]:2083 ssl;
    http2 on;
    server_name origin-chat.example.com;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-RSA-AES128-GCM-SHA256';
    ssl_prefer_server_ciphers off;
    ssl_certificate /etc/nginx/ssl/cf_origin_server_2048/fullchain.cer;
    ssl_certificate_key /etc/nginx/ssl/cf_origin_server_2048/private.key;
    # --------------------------------------------------------
    # [核心] 请求头校验逻辑,仅限认证的前置CDN回源
    # --------------------------------------------------------
    # 假设 Header 为 X-Origin-Verify,值为 strict-token-123456
    # 如果不相等 (!=),则直接返回 403
    # if ($http_x_origin_verify != "strict-token-123456" ) {
    # return 444;
    # }
    # --------------------------------------------------------
    # 业务逻辑 (与上方保持一致,确保后端处理逻辑相同)
    location /.well-known/acme-challenge {
        proxy_set_header Host $host;
        proxy_set_header X-Real_IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
        proxy_pass http://127.0.0.1:9180;
    }
    location ~* ^/(auth|api|oauth|admin|signin|signup|signout|login|logout|sso)/ {
        proxy_pass http://openwebui:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        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;
        # 注意:这里依然传递 chat 域名给后端,保证应用识别正确的主站域名
        proxy_set_header Host chat.quarkmed.com;
        proxy_read_timeout 10m;
        proxy_buffering off;
        client_max_body_size 20M;
        proxy_no_cache 1;
        proxy_cache_bypass 1;
        add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
        add_header Pragma "no-cache" always;
        expires -1;
    }
    location ~* \.(css|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
        proxy_pass http://openwebui:8080;
        proxy_http_version 1.1;
        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_set_header Host chat.quarkmed.com;
        expires 7d;
        add_header Cache-Control "public, immutable";
    }
    location / {
        proxy_pass http://openwebui:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        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_set_header Host chat.quarkmed.com;
        proxy_read_timeout 10m;
        proxy_buffering off;
        client_max_body_size 20M;
        add_header Cache-Control "public, max-age=300, must-revalidate";
    }
}

open-webui api 地址改为 nginx: 端口


📌 转载信息
原作者:
lekai
转载时间:
2026/1/14 17:47:23

使用的测试文件 info.php,调用 php.info();
现在网站需要放在其他路径底下,修改了 nginx 中的 root 之后就提示 No input file specified.
但是 index.html 静态文件显示正常

在网上查的和 gpt 问,试过以下几种方式还是不行,求大佬帮忙看下

1 ,php74/etc/php-fpm.d/www.conf 文件中 chroot 和 chdir 参数都是默认注释的,
在 info.php 中,参数显示如下
USER www-data
HOME /var/www

2 ,nginx 中的 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
$document_root 或者修改成绝对路径也不行

3 ,修改 cgi.fix_pathinfo=0

4 ,;cgi.force_redirect=1 取消注释

上述的 4 种方式最多就是某 2 个一起试过。
关于文件权限问题,/var/www 使用的是 777 ,html 和我想放的文件夹 site 的权限也是 777 ,site 和网站文件的属组和属主都是 www-data
php74/etc/php-fpm.d/www.conf 文件中


user = www-data

group = www-data

listen = /run/php74-fpm.sock

listen.owner = www-data

listen.group = www-data

listen.mode = 0660


/run/php74-fpm.sock 的属组和属主是 www-data

求大佬帮忙看下还有什么办法嘛,想放到/var/www/site 文件夹下

前提:

  1. 有一个基于 Docker Compose 编排,且包含 Nginx 部署的前端服务项目。
  2. 有一个域名(证书会基于域名自动申请)。

配置流程:

  1. 修改 docker-compose.yml 文件,确保前端服务映射了 443 端口
    示例:
services: ruoyi-ui: container_name: ruoyi-ui build: context: . # Dockerfile.ui 及 nginx.conf 所在上下文路径 dockerfile: Dockerfile.ui ports: - "${FRONTEND_PORT}:80" - "443:443" # https端口 depends_on: xxx-service: condition: service_started # 后端服务及其它服务配置 xxx-service: ... 
  1. 在 Dockerfile.ui 中使用正确的基础镜像,需要支持 ACME 的 nginx 镜像,如:ghcr.io/3az7qmfd/ngacme:main:
FROM ghcr.io/3az7qmfd/ngacme:main

# 移除 Nginx 默认配置
RUN rm /etc/nginx/conf.d/default.conf

# 复制自定义的 Nginx 配置文件
COPY ./nginx.conf /etc/nginx/conf.d/default.conf

# 从构建阶段复制构建好的静态文件到 Nginx 托管目录
COPY ./dist /usr/share/nginx/html

# 暴露 Nginx 端口
EXPOSE 80

# Nginx 默认会启动,也可以指定 CMD
CMD ["nginx", "-g", "daemon off;"]
  1. nginx.conf 配置自动获取证书
# DNS 解析器(用于访问 ACME 服务器,推荐用可靠的如 Cloudflare 或 Google)
# 没有可用的 IPv6 出口时,可设置禁用ipv6:ipv6=off
resolver 8.8.8.8 1.1.1.1 ipv6=off valid=30s;

# ACME issuer 配置(这里用 Let's Encrypt 生产环境示例)
acme_issuer letsencrypt {
    uri         https://acme-v02.api.letsencrypt.org/directory;
    contact     xxx@qq.com;  # <----- 你的邮箱,用于通知
    state_path  /var/cache/nginx/acme-letsencrypt;  # 持久化存储路径,确保目录存在并可写
    accept_terms_of_service;
}

# 共享内存区(存储证书和挑战数据)
acme_shared_zone zone=ngx_acme_shared:1M;

server {
    listen 80;
    server_name xxx.com;  # 你的实际域名,证书会基于此自动申请

    # 可选:显式允许挑战路径
    # location /.well-known/acme-challenge/ { }

    location / {
        return 301 https://$server_name$request_uri;  # 重定向所有普通请求到 HTTPS
    }
}

server {
    listen 443 ssl; http2 on;
    server_name xxx.com;  # 你的实际域名,证书会基于此自动申请

    # ACME 自动管理证书
    acme_certificate letsencrypt;  # 使用上面定义的 issuer

    ssl_certificate       $acme_certificate;
    ssl_certificate_key   $acme_certificate_key;

    # 避免每次请求解析证书
    ssl_certificate_cache max=2;

    # 静态资源根目录(与原配置相同)
    root   /usr/share/nginx/html;
    index  index.html index.htm;

    # 处理前端 SPA 路由
    location / {
        try_files $uri $uri/ /index.html;
    }

    # API 请求反向代理
    location /api/ {
        proxy_set_header Host $http_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_pass http://xxx-service:8080/; # <---- 你的后端服务
    }

    # 错误页面
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # 其他 SSL 优化(可选推荐)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
}

参考连接:

  1. 以 Nginx 为反代服务端时以 Certbot 自动申请 HTTPS 证书的流程
  2. nginx-acme
    nginx-acme/README.md at main · nginx/nginx-acme · GitHub
  3. Module ngx_http_acme_module
    Module ngx_http_acme_module
  4. NGINX with ACME 模块 Docker 镜像
    Package ngacme · GitHub

📌 转载信息
转载时间:
2026/1/6 12:16:35

昨天用到,就让反重力糊了一个。想着可能有佬友说不定也许可能用得到,就把这个脚本发一下。


试用场景:

  1. docker 跑项目且把证书目录映射出宿主主机
  2. nginx 配置 ssl (也可以别的方式)

包含了首次申请证书、定时更新证书、更新证书重启项目,可按需删。
域名、路径等相关的自己调调。
scripts.zip


📌 转载信息
原作者:
vaaagle
转载时间:
2026/1/5 12:29:07

为什么你的反向代理需要 WAF?

如果你正利用科技 lion 脚本添加域名反向代理,那么本文将教你如何为服务加固:通过集成全球领先的 OWASP ModSecurity+OWASP CRS 组合,构建强大的 WAF(Web 应用防火墙)。
相比独立防护,这种嵌入 Nginx 运行的方案效率更高、识别更精准且完全开源。只需简单几步,即可有效阻断各类恶意攻击、非法爬虫与渗透尝试,为你的所有反代站点筑起一道坚实的安全屏障。

全文复制过来格式就乱了 发文章地址吧!


📌 转载信息
原作者:
kejilion
转载时间:
2026/1/5 12:15:13