GoAccess 日志分析完全指南 / 11 - Docker 部署
11 - Docker 部署
11.1 概述
Docker 容器化部署 GoAccess 有以下优势:
- 环境一致:无需担心系统依赖和编译问题
- 快速部署:一条命令即可启动
- 易于维护:版本升级只需拉取新镜像
- 资源隔离:不影响宿主机环境
11.2 快速开始
11.2.1 最简运行
# 一次性分析日志
docker run --rm -v /var/log/nginx:/var/log/nginx \
allinurl/goaccess \
/var/log/nginx/access.log --log-format=COMBINED
11.2.2 生成 HTML 报告
# 生成 HTML 报告到宿主机
docker run --rm \
-v /var/log/nginx:/var/log/nginx \
-v /var/www/html:/var/www/html \
allinurl/goaccess \
/var/log/nginx/access.log \
--log-format=COMBINED \
-o /var/www/html/report.html
11.2.3 实时终端监控
# 实时监控(交互式终端)
docker run --rm -it \
-v /var/log/nginx:/var/log/nginx \
allinurl/goaccess \
/var/log/nginx/access.log --log-format=COMBINED
11.2.4 实时 HTML 面板
# 实时 HTML 面板(需要暴露 WebSocket 端口)
docker run --rm \
-v /var/log/nginx:/var/log/nginx \
-v /var/www/html:/var/www/html \
-p 7890:7890 \
allinurl/goaccess \
sh -c 'tail -f /var/log/nginx/access.log | \
goaccess --log-format=COMBINED \
-o /var/www/html/report.html \
--real-time-html \
--ws-url=ws://your-server-ip:7890'
11.3 Docker 镜像
11.3.1 官方镜像
# 拉取官方镜像
docker pull allinurl/goaccess
# 查看镜像版本
docker run --rm allinurl/goaccess goaccess --version
11.3.2 自定义镜像(含 GeoIP)
# Dockerfile
FROM allinurl/goaccess:latest
# 安装 GeoIP 更新工具
RUN apk add --no-cache geoipupdate curl bash
# 创建 GeoIP 数据目录
RUN mkdir -p /usr/share/GeoIP
# 配置 GeoIP 更新
COPY GeoIP.conf /etc/GeoIP.conf
# 下载 GeoIP 数据库
RUN geoipupdate || echo "GeoIP update failed, using default"
# 复制配置文件
COPY goaccess.conf /etc/goaccess/goaccess.conf
# 复制启动脚本
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
EXPOSE 7890
ENTRYPOINT ["/entrypoint.sh"]
entrypoint.sh:
#!/bin/bash
set -e
# 更新 GeoIP 数据库(可选)
geoipupdate 2>/dev/null || true
# 执行传入的命令
exec "$@"
构建自定义镜像:
docker build -t my-goaccess:latest .
11.3.3 多阶段构建(从源码编译)
# Dockerfile.build
FROM debian:bookworm-slim AS builder
RUN apt-get update && apt-get install -y \
build-essential \
libncursesw5-dev \
libgeoip-dev \
libssl-dev \
autopoint \
gettext \
autoconf \
autoconf-archive \
git \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# 克隆并编译 GoAccess
RUN git clone https://github.com/allinurl/goaccess.git /src
WORKDIR /src
RUN autoreconf -fi && \
./configure \
--enable-utf8 \
--enable-geoip=mmdb \
--with-openssl \
--with-getline \
--prefix=/usr/local && \
make -j$(nproc) && \
make install
# 生产镜像
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y \
libncursesw5 \
libgeoip1 \
libssl3 \
geoipupdate \
bash \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local/bin/goaccess /usr/local/bin/goaccess
COPY --from=builder /usr/local/etc/goaccess /usr/local/etc/goaccess
RUN mkdir -p /var/log/nginx /var/www/html /usr/share/GeoIP /etc/goaccess
COPY goaccess.conf /etc/goaccess/goaccess.conf
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
EXPOSE 7890
ENTRYPOINT ["/entrypoint.sh"]
11.4 Docker Compose 编排
11.4.1 基础 Compose 配置
# docker-compose.yml
version: '3.8'
services:
goaccess:
image: allinurl/goaccess:latest
container_name: goaccess
restart: unless-stopped
volumes:
- nginx-logs:/var/log/nginx:ro
- goaccess-reports:/var/www/html
ports:
- "7890:7890"
command: >
sh -c 'tail -f /var/log/nginx/access.log |
goaccess --log-format=COMBINED
-o /var/www/html/report.html
--real-time-html
--ws-url=ws://localhost:7890
--html-title="网站访问报告"
--html-prefs={"theme":"bright"}
--exclude="(bot|crawler|spider)"
--exclude="\.(css|js|jpg|png|gif|ico|svg|woff2?)$$"'
nginx:
image: nginx:alpine
container_name: goaccess-nginx
restart: unless-stopped
ports:
- "8080:80"
volumes:
- goaccess-reports:/usr/share/nginx/html:ro
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
volumes:
nginx-logs:
external: true
goaccess-reports:
nginx.conf:
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index report.html;
# WebSocket 代理
location /ws {
proxy_pass http://goaccess:7890/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
# 目录浏览
location / {
try_files $uri $uri/ =404;
autoindex on;
}
}
# 启动服务
docker compose up -d
# 查看日志
docker compose logs -f goaccess
# 停止服务
docker compose down
11.4.2 完整生产配置
# docker-compose.prod.yml
version: '3.8'
services:
# ===== GoAccess 实时分析 =====
goaccess:
image: my-goaccess:latest # 自定义镜像(含 GeoIP)
container_name: goaccess
restart: unless-stopped
volumes:
# Nginx 日志(只读挂载)
- /var/log/nginx:/var/log/nginx:ro
# GeoIP 数据库
- /usr/share/GeoIP:/usr/share/GeoIP:ro
# GoAccess 报告输出
- goaccess-reports:/var/www/html
# GoAccess 持久化数据
- goaccess-data:/var/lib/goaccess
# 自定义配置文件
- ./goaccess.conf:/etc/goaccess/goaccess.conf:ro
ports:
- "127.0.0.1:7890:7890" # 仅本地访问
environment:
- TZ=Asia/Shanghai
command: >
sh -c 'tail -f /var/log/nginx/access.log |
goaccess --log-format=COMBINED
-o /var/www/html/report.html
--real-time-html
--ws-url=ws://localhost:7890
--geoip-database=/usr/share/GeoIP/GeoLite2-City.mmdb
--html-title="网站访问报告"
--html-prefs={"theme":"bright","perPage":30}
--exclude="(bot|crawler|spider|Bytespider|GPTBot)"
--exclude="\.(css|js|jpg|jpeg|png|gif|ico|svg|woff2?|ttf|eot)$$"
--exclude="/(health|status|ping|readyz|livez|metrics)"'
healthcheck:
test: ["CMD", "test", "-f", "/var/www/html/report.html"]
interval: 60s
timeout: 10s
retries: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# ===== Nginx 反向代理 =====
nginx:
image: nginx:alpine
container_name: goaccess-nginx
restart: unless-stopped
ports:
- "443:443"
- "80:80"
volumes:
- goaccess-reports:/usr/share/nginx/html:ro
- ./nginx-ssl.conf:/etc/nginx/conf.d/default.conf:ro
- /etc/ssl/certs/stats.pem:/etc/ssl/certs/stats.pem:ro
- /etc/ssl/private/stats.key:/etc/ssl/private/stats.key:ro
- ./.htpasswd:/etc/nginx/.htpasswd:ro
depends_on:
- goaccess
# ===== 定时报告生成 =====
report-generator:
image: my-goaccess:latest
container_name: goaccess-reporter
restart: "no"
volumes:
- /var/log/nginx:/var/log/nginx:ro
- goaccess-reports:/var/www/html
- ./scripts:/scripts:ro
environment:
- TZ=Asia/Shanghai
entrypoint: /bin/bash
command: /scripts/daily_report.sh
profiles:
- report
volumes:
goaccess-reports:
goaccess-data:
nginx-ssl.conf:
server {
listen 80;
server_name stats.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name stats.example.com;
ssl_certificate /etc/ssl/certs/stats.pem;
ssl_certificate_key /etc/ssl/private/stats.key;
root /usr/share/nginx/html;
index report.html;
# Basic Auth
auth_basic "GoAccess Statistics";
auth_basic_user_file /etc/nginx/.htpasswd;
# WebSocket 代理
location /ws {
proxy_pass http://goaccess:7890/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 86400;
}
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# 目录浏览
location / {
try_files $uri $uri/ =404;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
}
11.5 与 Nginx 容器集成
11.5.1 共享日志卷
# docker-compose.nginx-goaccess.yml
version: '3.8'
services:
nginx:
image: nginx:alpine
container_name: web-nginx
restart: unless-stopped
ports:
- "80:80"
volumes:
- ./nginx-site.conf:/etc/nginx/conf.d/default.conf:ro
- nginx-logs:/var/log/nginx
- ./html:/usr/share/nginx/html:ro
goaccess:
image: allinurl/goaccess:latest
container_name: goaccess
restart: unless-stopped
volumes:
- nginx-logs:/var/log/nginx:ro
- goaccess-reports:/var/www/html
ports:
- "7890:7890"
command: >
sh -c 'tail -f /var/log/nginx/access.log |
goaccess --log-format=COMBINED
-o /var/www/html/report.html
--real-time-html
--ws-url=ws://localhost:7890'
depends_on:
- nginx
goaccess-viewer:
image: nginx:alpine
container_name: goaccess-viewer
restart: unless-stopped
ports:
- "8080:80"
volumes:
- goaccess-reports:/usr/share/nginx/html:ro
depends_on:
- goaccess
volumes:
nginx-logs:
goaccess-reports:
11.5.2 Nginx 日志配置(JSON 格式)
# nginx-site.conf
log_format json_combined escape=json
'{'
'"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"request":"$request",'
'"status":"$status",'
'"body_bytes_sent":"$body_bytes_sent",'
'"http_referer":"$http_referer",'
'"http_user_agent":"$http_user_agent",'
'"request_time":"$request_time"'
'}';
server {
listen 80;
server_name _;
access_log /var/log/nginx/access.log combined;
# 或使用 JSON 格式:
# access_log /var/log/nginx/access.log json_combined;
root /usr/share/nginx/html;
index index.html;
}
11.6 日志收集方案
11.6.1 使用 Docker Logging Driver
# 配置 Nginx 容器使用 local logging driver
services:
nginx:
image: nginx:alpine
logging:
driver: local
options:
max-size: "100m"
max-file: "5"
volumes:
- nginx-logs:/var/log/nginx
11.6.2 使用 Filebeat 收集日志
# docker-compose.filebeat.yml
version: '3.8'
services:
nginx:
image: nginx:alpine
volumes:
- nginx-logs:/var/log/nginx
filebeat:
image: elastic/filebeat:8.12.0
volumes:
- nginx-logs:/var/log/nginx:ro
- filebeat-data:/usr/share/filebeat/data
- ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
depends_on:
- nginx
goaccess:
image: allinurl/goaccess:latest
volumes:
- nginx-logs:/var/log/nginx:ro
- goaccess-reports:/var/www/html
command: >
sh -c 'tail -f /var/log/nginx/access.log |
goaccess --log-format=COMBINED
-o /var/www/html/report.html
--real-time-html
--ws-url=ws://localhost:7890'
volumes:
nginx-logs:
filebeat-data:
goaccess-reports:
11.6.3 使用 Fluentd 收集
# docker-compose.fluentd.yml
version: '3.8'
services:
nginx:
image: nginx:alpine
logging:
driver: fluentd
options:
fluentd-address: localhost:24224
tag: nginx.access
fluentd:
image: fluent/fluentd:v1.16
volumes:
- ./fluentd.conf:/fluentd/etc/fluent.conf:ro
- nginx-logs:/var/log/fluentd
ports:
- "24224:24224"
- "24224:24224/udp"
goaccess:
image: allinurl/goaccess:latest
volumes:
- nginx-logs:/var/log/fluentd:ro
- goaccess-reports:/var/www/html
command: >
sh -c 'tail -f /var/log/fluentd/nginx.access.*.log |
goaccess --log-format=COMBINED
-o /var/www/html/report.html
--real-time-html
--ws-url=ws://localhost:7890'
volumes:
nginx-logs:
goaccess-reports:
11.7 Kubernetes 部署
11.7.1 基本部署
# k8s/goaccess-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: goaccess
labels:
app: goaccess
spec:
replicas: 1
selector:
matchLabels:
app: goaccess
template:
metadata:
labels:
app: goaccess
spec:
containers:
- name: goaccess
image: allinurl/goaccess:latest
command:
- sh
- -c
- |
tail -f /var/log/nginx/access.log |
goaccess --log-format=COMBINED \
-o /var/www/html/report.html \
--real-time-html \
--ws-url=ws://localhost:7890
ports:
- containerPort: 7890
name: websocket
volumeMounts:
- name: nginx-logs
mountPath: /var/log/nginx
readOnly: true
- name: goaccess-reports
mountPath: /var/www/html
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
volumes:
- name: nginx-logs
persistentVolumeClaim:
claimName: nginx-logs-pvc
- name: goaccess-reports
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: goaccess
spec:
selector:
app: goaccess
ports:
- port: 7890
targetPort: 7890
name: websocket
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: goaccess-ingress
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: goaccess-basic-auth
spec:
rules:
- host: stats.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: goaccess
port:
number: 7890
11.8 高可用与扩展
11.8.1 多实例负载均衡
# 为多个 Nginx 实例分别运行 GoAccess
version: '3.8'
services:
nginx-1:
image: nginx:alpine
volumes:
- nginx-1-logs:/var/log/nginx
nginx-2:
image: nginx:alpine
volumes:
- nginx-2-logs:/var/log/nginx
goaccess-1:
image: allinurl/goaccess:latest
volumes:
- nginx-1-logs:/var/log/nginx:ro
- goaccess-reports:/var/www/html/site1
command: >
sh -c 'tail -f /var/log/nginx/access.log |
goaccess --log-format=COMBINED
-o /var/www/html/site1/report.html
--real-time-html
--ws-url=ws://localhost:7890'
goaccess-2:
image: allinurl/goaccess:latest
volumes:
- nginx-2-logs:/var/log/nginx:ro
- goaccess-reports:/var/www/html/site2
command: >
sh -c 'tail -f /var/log/nginx/access.log |
goaccess --log-format=COMBINED
-o /var/www/html/site2/report.html
--real-time-html
--ws-url=ws://localhost:7890'
volumes:
nginx-1-logs:
nginx-2-logs:
goaccess-reports:
11.9 日志管理最佳实践
11.9.1 日志轮转
# 使用 Docker 的日志轮转
services:
nginx:
image: nginx:alpine
logging:
driver: json-file
options:
max-size: "50m"
max-file: "5"
volumes:
- nginx-logs:/var/log/nginx
11.9.2 日志清理
#!/bin/bash
# cleanup_logs.sh — 清理 Docker 日志
# 清理容器日志
find /var/lib/docker/containers/ -name "*.log" -size +100M -exec truncate -s 10M {} \;
# 清理旧的 GoAccess 报告
docker exec goaccess find /var/www/html -name "*.html" -mtime +90 -delete
# 清理悬空镜像
docker image prune -f
# 清理未使用的卷
docker volume prune -f
11.10 故障排查
问题一:容器无法启动
# 查看容器日志
docker logs goaccess
# 检查镜像是否存在
docker images | grep goaccess
# 检查端口占用
ss -tlnp | grep 7890
问题二:日志文件挂载失败
# 检查宿主机日志文件
ls -la /var/log/nginx/
# 检查容器内挂载点
docker exec goaccess ls -la /var/log/nginx/
# 检查 SELinux/AppArmor 限制
docker run --rm -v /var/log/nginx:/var/log/nginx:ro,Z allinurl/goaccess ls /var/log/nginx
问题三:WebSocket 连接失败
# 检查容器网络
docker network ls
docker inspect goaccess | grep -A 20 "Networks"
# 测试 WebSocket 连接
wscat -c ws://localhost:7890
# 检查防火墙
sudo iptables -L -n | grep 7890
问题四:内存不足
# 查看容器资源使用
docker stats goaccess
# 限制容器内存
docker run --memory=256m --memory-swap=512m allinurl/goaccess ...
11.11 小结
| 场景 | 推荐方案 |
|---|---|
| 快速分析 | docker run --rm -v logs:/logs allinurl/goaccess |
| 实时监控 | Docker Compose + WebSocket + Nginx 代理 |
| 多站点 | 每个站点独立的 GoAccess 实例 |
| K8s | Deployment + Service + Ingress |
| 生产环境 | 自定义镜像(含 GeoIP)+ Basic Auth |
下一章
下一章将总结 GoAccess 的最佳实践,包括性能优化、运维效率提升、监控策略和安全分析。