强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 实例
K8sDeployment + Service + Ingress
生产环境自定义镜像(含 GeoIP)+ Basic Auth

下一章

下一章将总结 GoAccess 的最佳实践,包括性能优化、运维效率提升、监控策略和安全分析。

12 - 最佳实践


扩展阅读