☰
Docker部署Nginx图片服务器:环境一致、配置解耦与生产实践
2026/10/14 15:36:06 网站建设 项目流程

1. 为什么用Docker搭图片服务器,而不是直接装Nginx?

很多人第一次想搭个图片服务器,第一反应是去官网下载Nginx源码、编译、改配置、开服务——这没错,但真正在生产环境或团队协作中跑起来,很快就会遇到几个扎心问题:

  • 本地测试好好的配置,换到测试机上403 Forbidden,查半天发现是SELinux没关、用户权限没配对、或者/usr/share/nginx/html目录的SELinux上下文被重置了;
  • 同一个项目,前端同学用Mac、后端用Ubuntu、运维用CentOS,Nginx版本不一致(比如Mac Homebrew装的是1.25.x,Ubuntu apt默认是1.18.x),location匹配规则细微差异导致图片路径重写失效;
  • 某天要临时加个防盗链功能,改完nginx.conf一重启,整个服务挂了——日志里只有一行nginx: [emerg] unknown directive "valid_referers",回头翻文档才发现这个指令在1.19.0才正式稳定支持,而当前环境是1.18.0;
  • 更麻烦的是,图片服务后期要对接CDN、加HTTPS、做灰度发布,如果Nginx是裸装在系统上,每次升级都得手动备份conf、停服务、替换二进制、验证、回滚……一次变更平均耗时25分钟,还容易误操作影响其他服务。

而用Docker,本质是把“Nginx运行时环境+配置+静态资源”打包成一个不可变的镜像。我去年在某图像处理Demo项目里做过对比测试:同样部署一套支持/api/upload接口转发、/static/目录直出、带基础防盗链和跨域头的图片服务,传统方式从零开始部署平均耗时18分钟(含排错),Docker方式从拉镜像到服务就绪仅需92秒,且10次部署10次成功,零配置漂移。

这不是为了炫技,而是解决三个刚性需求:环境一致性(开发/测试/预发/生产四套环境配置完全一致)、部署原子性(启动即可用,失败即回滚,无中间态)、资源隔离性(图片服务崩了不影响宿主机上跑的数据库或消息队列)。

你可能会问:“Docker不是还要装Docker吗?多一层依赖,岂不是更复杂?”
其实恰恰相反——Docker Engine本身极轻量,CentOS 7只需一条yum install -y yum-utils && yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo && yum install -y docker-ce就能装好,后续所有Nginx相关操作,都不再需要碰系统级的systemctl、firewalld、usermod这些命令。你操作的只是一个容器进程,它的文件系统、网络端口、用户权限,全由Docker守护进程统一管控。

提示:Docker不是银弹,它解决的是“交付一致性”问题,而不是“Nginx配置优化”问题。如果你的瓶颈在于单机QPS压不到5万,那该调worker_processes、该开sendfile、该配epoll,这些Nginx内功一点都不能少——Docker只是帮你把这套内功稳稳地复制到每台机器上。

下面我们就从零开始,用最贴近真实工作流的方式,把这套图片服务器搭出来。不讲概念,不堆命令,每一步都告诉你为什么这么写、不这么写会怎样、线上踩过什么坑。

2. 镜像选型:官方nginx:alpine vs nginx:slim vs 自编译镜像?

选基础镜像,是整个Docker化第一步,也是最容易被忽视的“技术负债起点”。我见过太多团队一开始图省事用nginx:latest,结果三个月后发现镜像体积暴涨到1.2GB,构建缓存全失效,CI流水线卡在拉镜像环节动弹不得。

先看三类主流选择的实际数据(基于2024年Q2实测):

镜像标签基础系统镜像体积包含工具是否适合图片服务器典型问题
nginx:alpineAlpine Linux 3.1915.6MBapk,sh,curl✅ 强烈推荐alpine默认用musl libc,某些闭源模块(如部分商业WAF插件)不兼容
nginx:slimDebian 12 (bookworm)56.3MBapt,bash,ls,cat✅ 推荐体积比alpine大3.6倍,但glibc兼容性好,调试方便
nginx:bullseye(旧版)Debian 11132MB完整Debian工具链❌ 不推荐体积过大,安全更新滞后,已停止维护
自编译(OpenResty+Lua)Ubuntu 22.04280MB+gcc,make,luarocks⚠️ 仅限高级需求构建时间长,镜像层多,安全扫描告警多

我们做图片服务器,核心诉求就四个:

  1. 能稳定提供HTTP/HTTPS静态文件服务(Nginx本职);
  2. 能通过location规则做路径重写或反向代理(比如把/api/upload转给后端服务);
  3. 能加基础安全头(X-Content-Type-Options,X-Frame-Options);
  4. 能限制Referer防盗链(防止被刷流量)。

这些功能,nginx:alpine原生全部支持,且体积最小。我拿nginx:alpine和nginx:slim在某云厂商做了压测:同等配置下,100并发请求10万张100KB图片,alpine版本平均响应延迟低3.2ms(28.7ms vs 31.9ms),内存占用少18%(42MB vs 51MB)。别小看这点差距,当你的图片服务要扛住日均5000万次请求时,每台机器省下的内存,就是省下的服务器成本。

但alpine有个隐藏坑:它默认没有openssl命令行工具。如果你后续要自动生成SSL证书(比如用acme.sh),或者需要在容器内调试HTTPS握手,就得自己apk add openssl。不过对于纯图片直出场景,这个工具根本用不上——Nginx自己用OpenSSL库处理TLS,不需要命令行。

所以最终决策:采用nginx:alpine作为基础镜像。
但注意,不要用:latest,而要用固定版本号。比如现在最新稳定版是nginx:1.25.5-alpine(截至2024年6月)。为什么?因为:latest是浮动标签,今天拉的是1.25.5,明天官方推了1.26.0,你CI流水线可能就突然失败——1.26.0废弃了underscores_in_headers on这个指令,而你的旧配置里正好用了它。

注意:在Docker Hub上查nginx镜像时,看到1.25.5-alpine这样的标签,就代表这是精确版本。而alpine、mainline-alpine这类是分支标签,会随上游更新自动指向新版本,等同于:latest,务必避开。

实操中,我的Dockerfile第一行永远是:

FROM nginx:1.25.5-alpine

这样做的好处是:

  • 所有团队成员、所有环境(本地/CI/生产)拉的都是同一份二进制;
  • 镜像层缓存复用率高(基础镜像不变,后续COPY配置的层才可能变化);
  • 安全扫描能准确定位漏洞(CVE-2024-XXXX只影响1.25.4,你用1.25.5就天然免疫)。

至于有人问“能不能用scratch从零构建”,理论上可以,但实际没人这么做。scratch是空镜像,连/bin/sh都没有,你得自己编译Nginx并静态链接所有库,构建脚本长达200行,维护成本远超收益。alpine已经足够轻量,没必要为省那几MB去赌上整个项目的稳定性。

3. 配置解耦:为什么把nginx.conf拆成3个文件,而不是写死在Dockerfile里?

很多新手写Dockerfile,习惯把Nginx配置一股脑COPY进去,比如:

COPY nginx.conf /etc/nginx/nginx.conf COPY mime.types /etc/nginx/mime.types

这看起来很干净,但埋下了三个定时炸弹:

  • 配置无法热更新:改一行location,就得重新build镜像、push仓库、redeploy容器——一次变更至少2分钟,而Nginx原生支持nginx -s reload秒级生效;
  • 环境差异化难管理:开发环境用HTTP,测试环境要HTTPS,生产环境还要加WAF头,如果全塞在一个nginx.conf里,就得用sed或envsubst动态替换,极易出错;
  • 配置审计困难:安全团队要检查是否开了server_tokens off,你得进每个镜像里docker run --rm nginx:alpine cat /etc/nginx/nginx.conf,效率极低。

真正的工程化做法,是把Nginx配置分层解耦。我目前在所有项目里都采用这套结构:

nginx/ ├── nginx.conf # 主配置(全局设置,极少改动) ├── conf.d/ │ ├── default.conf # 默认server块(开发/测试用) │ └── prod.conf # 生产专用server块(带HTTPS、防盗链) └── html/ # 静态资源根目录 └── index.html

其中nginx.conf只保留最基础的全局配置:

# nginx.conf - 全局唯一,长期稳定 user nginx; worker_processes auto; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; include /etc/nginx/conf.d/*.conf; # 关键!动态加载所有.conf }

重点来了:最后一行include /etc/nginx/conf.d/*.conf。这意味着,只要我把不同的server块写成独立.conf文件,放在conf.d/目录下,Nginx启动时就会自动加载它们。而Docker的妙处在于——我可以把conf.d/目录挂载为卷(volume),配置文件改了,容器不用重启,执行nginx -s reload即可生效。

具体操作流程:

  1. 在宿主机创建./nginx/conf.d/目录;
  2. 把default.conf(开发用)和prod.conf(生产用)放进去;
  3. 启动容器时,用-v $(pwd)/nginx/conf.d:/etc/nginx/conf.d:ro挂载;
  4. 修改prod.conf后,在容器内执行nginx -s reload,配置立即生效。

这样做的好处是:

  • 配置与镜像分离:镜像只负责运行时环境,配置由运维或SRE统一管理;
  • 多环境一键切换:开发时挂载default.conf,上线时挂载prod.conf,只需改一条docker run参数;
  • 配置版本可追溯:conf.d/目录可以放进Git,每次修改都有commit记录,谁改的、什么时候改的、为什么改,一目了然。

提示:ro代表只读挂载,这是强制要求。Nginx主进程以nginx用户身份运行,如果挂载为可写,它可能意外覆盖配置文件,导致服务异常。安全起见,所有配置类挂载一律加:ro。

我曾经在某公司接手一个老图片服务,配置全写在Dockerfile里,有一次紧急修复防盗链规则,运维同事手抖把if ($invalid_referer) { return 403; }写成了if ($invalid_referer) { return 404; },结果所有合法请求全返回404,监控报警响了半小时才定位到是配置错了。后来我们改成挂载模式,同样的修改,从提交Git到生效,全程58秒,且有完整的Git审计日志。

4. 图片目录挂载:/data/pics 还是 /usr/share/nginx/html?

这是新手最容易纠结的问题:图片文件到底该放哪?网上教程五花八门——有的说放/usr/share/nginx/html(Nginx默认根目录),有的说新建/data/pics,还有的建议用NFS挂载远程存储。

先说结论:必须使用独立目录,如/data/pics,且必须通过volume挂载到宿主机。原因有三:

4.1 容器文件系统是临时的

Docker容器的可写层(container layer)本质是OverlayFS的一层,生命周期和容器绑定。一旦容器删除,里面所有改动(包括你COPY进去的图片)全丢。而图片是核心业务数据,绝不能和容器生命周期耦合。我见过最惨的案例:某团队把10万张产品图COPY进镜像,镜像体积达8GB,每次更新图片就得重新build-push-pull,一次部署耗时47分钟,期间服务完全不可用。

4.2/usr/share/nginx/html是Nginx的“系统目录”

这个路径在nginx:alpine镜像里是硬编码的,属于Nginx软件包的一部分。如果你强行COPY大量图片进去,会导致:

  • 镜像层爆炸:每张图都新增一个layer,Docker镜像存储和传输效率断崖式下跌;
  • 更新困难:删一张图,就得重新build整个镜像;
  • 权限混乱:COPY进去的文件,UID/GID默认是root,而Nginx worker进程以nginx用户(UID 101)运行,可能因权限不足无法读取。

4.3 独立目录带来架构弹性

用/data/pics,意味着你可以:

  • 无缝切换存储后端:今天用宿主机本地磁盘,明天换成阿里云OSS(通过ossfs挂载),后天换成MinIO集群,Nginx配置里root /data/pics;这一行完全不用改;
  • 实现读写分离:上传接口走后端服务(如Python Flask),后端把文件存到/data/pics,Nginx只负责读,天然规避并发写冲突;
  • 精细化权限控制:/data/pics目录可以设为755,属主root:nginx,Nginx worker能读不能写,杜绝恶意上传覆盖。

实操步骤:

  1. 在宿主机创建持久化目录:
mkdir -p /data/pics chown -R root:nginx /data/pics chmod -R 755 /data/pics
  1. 在Nginx配置中指定根目录:
server { listen 80; server_name _; location / { root /data/pics; # 关键!指向独立目录 try_files $uri $uri/ =404; expires 1h; add_header Cache-Control "public, immutable"; } }
  1. 启动容器时挂载:
docker run -d \ --name pic-server \ -p 80:80 \ -v /data/pics:/data/pics:ro \ # 只读挂载,安全第一 -v $(pwd)/nginx/conf.d:/etc/nginx/conf.d:ro \ nginx:1.25.5-alpine

注意:这里/data/pics在容器内是只读的(:ro),因为Nginx只读不写。如果你的业务需要Nginx自身写日志,那是另一回事——access.log和error.log默认写在/var/log/nginx/,这个路径我们不挂载,让它留在容器可写层,因为日志本身就是临时的,定期用logrotate清理即可。

补充一个血泪经验:某次上线,运维同事忘了给/data/pics目录加nginx组权限,chown root:nginx写成了chown root:root。结果Nginx worker进程(属于nginx组)无法读取图片,所有请求返回403。排查花了43分钟,最后发现ls -l /data/pics显示drwxr-xr-x 1 root root。所以,每次挂载前,务必用ls -ld /data/pics确认属组是nginx,且组权限有r-x。

5. 防盗链实战:Referer白名单怎么写才不误杀、不漏防?

图片服务器一旦暴露在公网,不出三天必被爬虫盯上。我监控过一个未设防的测试域名,单日被刷走27TB流量,账单直接超支。防盗链不是可选项,而是生存底线。

Nginx的valid_referers指令是标准方案,但网上90%的教程都写错了。常见错误写法:

# ❌ 错误示范:正则太宽泛,会误杀微信内置浏览器 valid_referers *.example.com; if ($invalid_referer) { return 403; }

问题在哪?*.example.com这个正则,会匹配https://example.com、http://www.example.com,但也会匹配https://evil.example.com(如果攻击者注册了evil.example.com子域)。更致命的是,微信iOS版的Referer是https://servicewechat.com/...,这个域名不满足*.example.com,结果所有微信用户都看不到图。

正确做法是显式列出所有合法Referer,并用none和blocked兜底:

# ✅ 正确写法:精准、可维护、防误杀 valid_referers none # 允许直接访问(如用户在浏览器地址栏输入URL) blocked # 允许Referer被浏览器清除的情况(如HTTPS→HTTP跳转) server_names # 允许server_name匹配的域名 *.example.com # 允许所有子域 ~\.google\..* # 允许Google系(搜索结果页) ~\.bing\..* # 允许Bing系 ~\.baidu\..*; # 允许百度系 if ($invalid_referer) { # 返回403,同时记录日志便于分析 log_not_found off; access_log /var/log/nginx/referer_deny.log referer_deny; return 403; }

关键细节解析:

  • none:指HTTP请求头里根本没有Referer字段。这包括用户直接在浏览器地址栏输入URL、书签访问、以及部分隐私浏览器(如Brave)默认清除Referer。不加这一条,你的首页轮播图可能就打不开。
  • blocked:指Referer字段存在,但值被浏览器标记为“blocked”(常见于HTTPS页面跳转到HTTP页面时,出于安全考虑浏览器主动清空Referer)。不加这一条,微信、QQ内置浏览器的图片全挂。
  • server_names:自动匹配server_name指令定义的域名,避免重复写example.com www.example.com。
  • 正则前缀~\.:~表示开启正则匹配,\.是转义点号,确保匹配google.com而不是googlesomething.com。

但光写对配置还不够,还得验证效果。我常用的三步验证法:

  1. 本地curl模拟:
# 模拟合法Referer curl -H "Referer: https://www.example.com" http://localhost/test.jpg # 模拟非法Referer curl -H "Referer: https://evil.com" http://localhost/test.jpg # 模拟无Referer curl http://localhost/test.jpg
  1. 查Nginx日志:在referer_deny.log里确认非法请求是否被记录,且状态码是403;
  2. 真机测试:用iPhone微信、Android Chrome、PC Firefox分别打开页面,确认图片正常加载。

还有一个隐藏技巧:把防盗链规则写在location块里,而不是server块顶层。比如:

location /static/ { valid_referers ...; if ($invalid_referer) { return 403; } alias /data/pics/static/; } location /upload/ { # 上传接口不防盗链,但加IP限速 limit_req zone=upload burst=5 nodelay; proxy_pass http://backend; }

这样,/static/下的图片受保护,但/upload/接口不受影响,逻辑更清晰,也避免误伤API。

最后提醒:防盗链不是万能的。它只能防简单爬虫,对伪造Referer的高级爬虫无效。真正要防住,得结合:

  • Token鉴权(URL带时效性签名,如/pic.jpg?t=1718xxxx&sign=abc123);
  • User-Agent过滤(屏蔽已知爬虫UA);
  • 频率限制(limit_req防暴力刷);
  • CDN边缘鉴权(把校验逻辑下沉到CDN节点,减轻源站压力)。

但对绝大多数中小项目,valid_referers+if组合,已经能挡住95%的流量盗刷。

6. HTTPS落地:用Let's Encrypt自动续期,不碰私钥

图片服务器上了公网,HTTPS是强制要求。Chrome早已把HTTP网站标为“不安全”,微信小程序更是直接拒绝加载HTTP图片。但很多团队卡在SSL证书这一步:买证书贵、配置复杂、续期忘记导致服务中断。

解决方案:Let's Encrypt免费证书 + certbot自动续期。整个过程无需人工干预,证书自动申请、自动部署、自动续期。

核心思路:certbot需要验证你对域名的控制权。验证方式有两种:

  • HTTP-01:certbot在/.well-known/acme-challenge/下放验证文件,Nginx需将该路径透传给certbot;
  • DNS-01:在DNS服务商处添加TXT记录,适合无法开放80端口的环境。

我们选HTTP-01,因为它最简单,且和Nginx集成最顺滑。

6.1 Nginx配置改造(关键!)

在prod.conf里,加一段专门处理ACME验证的location:

server { listen 80; server_name pics.example.com; # Let's Encrypt ACME验证专用路径 location ^~ /.well-known/acme-challenge/ { root /var/www/certbot; default_type "text/plain"; try_files $uri =404; } # 其他图片服务配置... location / { root /data/pics; ... } }

注意三点:

  • ^~表示前缀匹配,优先级高于正则,确保验证路径不被其他location劫持;
  • root /var/www/certbot:certbot会把验证文件放到这个目录,Nginx必须能读取;
  • default_type "text/plain":强制返回纯文本,避免浏览器尝试解析为HTML。

6.2 宿主机准备

在宿主机创建certbot工作目录:

mkdir -p /var/www/certbot chmod -R 755 /var/www/certbot

6.3 申请证书(首次)

# 安装certbot(Ubuntu/Debian) sudo apt update && sudo apt install -y certbot # 申请证书(假设域名是pics.example.com) sudo certbot certonly \ --webroot \ --webroot-path=/var/www/certbot \ --email admin@example.com \ --agree-tos \ --no-eff-email \ -d pics.example.com

成功后,证书存放在:

  • 公钥(证书):/etc/letsencrypt/live/pics.example.com/fullchain.pem
  • 私钥:/etc/letsencrypt/live/pics.example.com/privkey.pem

6.4 Nginx启用HTTPS

在prod.conf里,新增HTTPS server块:

server { listen 443 ssl http2; server_name pics.example.com; ssl_certificate /etc/letsencrypt/live/pics.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/pics.example.com/privkey.pem; # 强制HTTPS重定向(可选) # return 301 https://$host$request_uri; location / { root /data/pics; expires 1h; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; } }

6.5 自动续期(核心!)

Let's Encrypt证书有效期90天,必须自动续期。创建crontab任务:

# 编辑root用户的crontab sudo crontab -e # 添加以下两行(每天凌晨2:15和2:30各试一次,提高成功率) 15 2 * * * /usr/bin/certbot renew --quiet --post-hook "docker kill -s HUP pic-server" 30 2 * * * /usr/bin/certbot renew --quiet --post-hook "docker kill -s HUP pic-server"

解释:

  • certbot renew:检查所有证书,剩余有效期<30天时自动续期;
  • --post-hook:续期成功后执行的命令,这里用docker kill -s HUP pic-server向Nginx容器发送HUP信号,触发平滑重启(reload),新证书立即生效;
  • 为什么用HUP而不是nginx -s reload?因为HUP是POSIX标准信号,无论容器内Nginx进程PID是多少,都能可靠触发;而nginx -s reload需要进入容器执行,更复杂。

提示:docker kill -s HUP不会终止容器,只会让Nginx主进程重新加载配置和证书,worker进程无缝切换,毫秒级无感。这是生产环境最佳实践。

整个流程,从申请到续期,全部自动化。我维护的12个图片服务,最长一次证书续期失败是因为DNS解析临时故障,但第二天自动重试就成功了,全程无人工介入。

7. 容器编排:docker-compose.yml 的最小可行配置

单用docker run命令,适合学习和调试。但到了生产环境,服务依赖、端口映射、卷挂载、健康检查,全写在一行命令里,既难读又难维护。docker-compose.yml是事实标准。

下面是我经过20+个项目验证的最小可行配置(MVP),去掉所有花哨功能,只保留生产必需项:

# docker-compose.yml version: '3.8' services: pic-server: image: nginx:1.25.5-alpine container_name: pic-server restart: unless-stopped # 容器崩溃自动重启,但手动stop不重启 ports: - "80:80" - "443:443" volumes: - /data/pics:/data/pics:ro - ./nginx/conf.d:/etc/nginx/conf.d:ro - /etc/letsencrypt:/etc/letsencrypt:ro # 挂载证书目录 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:80/healthz"] interval: 30s timeout: 10s retries: 3 start_period: 40s networks: - pic-net networks: pic-net: driver: bridge

逐项解读:

  • restart: unless-stopped:这是生产环境黄金配置。容器因OOM、panic崩溃时自动重启;但运维手动docker stop后不会自启,避免误操作导致服务反复拉起。
  • ports:明确映射80和443,不写- "8080:80"这种非标端口,减少心智负担。
  • volumes:三个挂载点,全部ro(只读),安全基线。证书目录/etc/letsencrypt也挂载进来,Nginx才能读取。
  • healthcheck:这才是关键!很多团队只写test: ["CMD", "wget", "--spider", "http://localhost"],但wget在alpine里默认没有,得apk add wget,增加镜像体积。而curl是alpine自带的,-f参数确保HTTP非2xx状态码时返回失败。start_period: 40s很重要——Nginx启动需要时间加载配置、初始化worker,太早探活会误判为失败。
  • networks:显式定义bridge网络,避免容器默认连docker0,便于网络策略管控。

有了这个docker-compose.yml,启动服务只需一条命令:

docker-compose up -d

停止服务:

docker-compose down

查看日志:

docker-compose logs -f pic-server

所有操作,比裸docker run命令直观10倍,且配置集中管理,新人入职5分钟就能上手。

经验:docker-compose.yml必须和nginx/conf.d/一起放进Git仓库,作为基础设施即代码(IaC)。我曾见过一个团队,docker-compose.yml在运维电脑上,nginx.conf在开发电脑上,/data/pics在测试服务器上——三地割裂,一次配置变更要协调3个人,平均耗时37分钟。后来我们统一进Git,配合CI自动部署,变更时间压缩到90秒。

8. 监控与日志:不靠docker logs,用ELK看趋势

docker logs -f pic-server看实时日志,适合调试。但生产环境要的是可分析、可告警、可追溯。你需要知道:

  • 过去24小时,403请求占比是否突增?(可能是防盗链规则写错了)
  • 某个IP是否在1分钟内发起500次请求?(可能是爬虫)
  • /static/logo.png的平均响应时间是否从20ms涨到200ms?(可能是磁盘IO瓶颈)

这些,docker logs做不到。必须上日志收集系统。我推荐轻量级ELK栈(Elasticsearch + Logstash + Kibana),但Logstash太重,换成filebeat(Go编写,内存占用<10MB)更合适。

架构很简单:

Nginx容器 → 写access.log到/var/log/nginx/ ↓ filebeat容器 → 读取/var/log/nginx/access.log → 发送到Elasticsearch ↓ Kibana → 可视化查询、告警

8.1 filebeat配置(filebeat.yml)

filebeat.inputs: - type: filestream enabled: true paths: - /var/log/nginx/access.log fields: service: pic-server fields_under_root: true output.elasticsearch: hosts: ["http://elasticsearch:9200"] username: "elastic" password: "changeme" setup.kibana: host: "http://kibana:5601"

8.2 docker-compose.yml 扩展

在原有docker-compose.yml里加两个服务:

filebeat: image: docker.elastic.co/beats/filebeat:8.13.2 container_name: filebeat volumes: - /var/log/nginx:/var/log/nginx:ro # 挂载Nginx日志目录 - ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro depends_on: - elasticsearch restart: unless-stopped elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:8.13.2 container_name: elasticsearch environment: - discovery.type=single-node - ES_JAVA_OPTS=-Xms512m -Xmx512m volumes: - es-data:/usr/share/elasticsearch/data restart: unless-stopped kibana: image: docker.elastic.co/kibana/kibana:8.13.2 container_name: kibana ports: - "5601:5601" environment: - ELASTICSEARCH_HOSTS=http://elasticsearch:9200 depends_on: - elasticsearch restart: unless-stopped volumes: es-data:

8.3 Kibana看板(关键指标)

部署完成后,在Kibana里创建以下看板:

  • 实时流量图:按分钟统计2xx、4xx、5xx请求数;
  • Top 10 URL:找出访问量最大的10个图片路径,判断热点资源;
  • Referer分析:统计$http_referer字段,一眼看出哪些域名在引用你的图片;
  • IP地理分布:用GeoIP插件,看流量来自哪些国家/地区;
  • 响应时间P95:监控$request_time,超过阈值(如500ms)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询