Traefik + Portainer + GoAccess로 홈랩 구축하기

Traefik 리버스 프록시, Portainer 컨테이너 관리, GoAccess 로그 분석을 Docker Compose로 통합 구성하는 실전 가이드

3 min read

Traefik 기반 홈랩 인프라 구축 가이드

Traefik v3를 중심으로 Portainer(컨테이너 관리)와 GoAccess(실시간 로그 분석)를 통합한 홈랩 환경을 구성한다. Cloudflare DNS Challenge로 와일드카드 TLS 인증서를 발급받고, Basic Auth로 대시보드를 보호한다.


디렉터리 구조

서비스별로 독립된 디렉터리를 사용해 관리 복잡도를 낮춘다.

homelab/
├── .env                 # 공통 환경변수
├── traefik/
│   ├── docker-compose.yml
│   ├── traefik.yml
│   ├── logs/
│   │   └── access.log   # Traefik 접근 로그
│   └── secrets/
│       ├── cf-dns-api-token.secret
│       └── usersfile.secret
├── portainer/
│   └── docker-compose.yml
└── access/
    └── docker-compose.yml

사전 준비

1. 환경변수 파일 생성

cat > .env << 'EOF'
HOST_DOMAIN=yourdomain.com
HOST_EMAIL=you@yourdomain.com
TZ=Asia/Seoul
GOACCESS_EXCLUDE_IPS=127.0.0.1
EOF

HOST_DOMAIN, HOST_EMAIL을 실제 값으로 수정한다.

2. Cloudflare DNS 레코드 설정

Cloudflare Dashboard → 도메인 선택 → DNS → Records:

TypeNameContentProxy
A@홈랩 서버 공인 IPProxied
A*홈랩 서버 공인 IPProxied

3. Cloudflare API 토큰 생성

  1. API Tokens → Create Token
  2. Edit zone DNS 템플릿 선택
  3. Permissions: Zone / DNS / Edit, Zone / Zone / Read
  4. Zone Resources: 대상 도메인 선택
  5. 토큰 복사 후 시크릿 파일 생성:
mkdir -p traefik/secrets
echo "your-cloudflare-api-token" > traefik/secrets/cf-dns-api-token.secret

4. Basic Auth 인증 파일 생성

Traefik 대시보드와 GoAccess에 적용할 인증 파일:

sudo apt install -y apache2-utils
htpasswd -nB admin > traefik/secrets/usersfile.secret

비밀번호 입력 프롬프트가 나오면 원하는 비밀번호를 입력한다.


서비스 구성

Traefik (리버스 프록시)

traefik/docker-compose.yml
services:
  traefik:
    image: traefik:3
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    labels:
      - traefik.enable=true
      - traefik.http.routers.dashboard.rule=Host(`traefik.${HOST_DOMAIN}`)
      - traefik.http.routers.dashboard.entrypoints=websecure
      - traefik.http.routers.dashboard.tls.certresolver=letsencrypt
      - traefik.http.routers.dashboard.service=api@internal
      - traefik.http.routers.dashboard.middlewares=secure-auth
      - traefik.http.middlewares.dashboard-auth.basicauth.usersfile=/run/secrets/traefik-usersfile
      # Rate limiting: 1분당 평균 10회, 버스트 5회
      - traefik.http.middlewares.auth-ratelimit.ratelimit.average=10
      - traefik.http.middlewares.auth-ratelimit.ratelimit.period=1m
      - traefik.http.middlewares.auth-ratelimit.ratelimit.burst=5
      # 체인 미들웨어: Rate Limit + BasicAuth
      - traefik.http.middlewares.secure-auth.chain.middlewares=auth-ratelimit,dashboard-auth
    ports:
      - 80:80
      - 443:443
    environment:
      - CF_DNS_API_TOKEN_FILE=/run/secrets/cf-dns-api-token
      - TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_EMAIL=${HOST_EMAIL}
      - TRAEFIK_ENTRYPOINTS_WEBSECURE_HTTP_TLS_DOMAINS_0_MAIN=${HOST_DOMAIN}
      - TRAEFIK_ENTRYPOINTS_WEBSECURE_HTTP_TLS_DOMAINS_0_SANS=*.${HOST_DOMAIN}
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - traefik-letsencrypt:/letsencrypt
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      - ./logs:/logs
    secrets:
      - cf-dns-api-token
      - traefik-usersfile
    networks:
      - proxy
 
secrets:
  cf-dns-api-token:
    file: ./secrets/cf-dns-api-token.secret
  traefik-usersfile:
    file: ./secrets/usersfile.secret
 
networks:
  proxy:
    name: proxy
 
volumes:
  traefik-letsencrypt:
traefik/traefik.yml
api:
  dashboard: true
 
entryPoints:
  web:
    address: :80
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: :443
 
providers:
  docker:
    endpoint: unix:///var/run/docker.sock
    exposedByDefault: false
 
certificatesResolvers:
  letsencrypt:
    acme:
      storage: /letsencrypt/acme.json
      dnsChallenge:
        provider: cloudflare
        resolvers:
          - 1.1.1.1:53
          - 8.8.8.8:53
 
accessLog:
  filePath: /logs/access.log
  format: common

핵심 설정 설명

  • Docker Secrets: cf-dns-api-token.secret, usersfile.secret을 컨테이너 /run/secrets/에 마운트
  • DNS Challenge: Cloudflare API로 와일드카드 인증서(*.yourdomain.com) 발급
  • Rate Limiting: 브루트포스 공격 방지 (1분당 평균 10회, 순간 버스트 5회)
  • 체인 미들웨어: secure-auth = auth-ratelimit + dashboard-auth

Portainer (컨테이너 관리 UI)

portainer/docker-compose.yml
services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - portainer-data:/data
    labels:
      - traefik.enable=true
      - traefik.http.routers.portainer.rule=Host(`portainer.${HOST_DOMAIN}`)
      - traefik.http.routers.portainer.entrypoints=websecure
      - traefik.http.routers.portainer.tls.certresolver=letsencrypt
      - traefik.http.services.portainer.loadbalancer.server.port=9000
    networks:
      - proxy
 
networks:
  proxy:
    external: true
 
volumes:
  portainer-data:

Portainer는 별도 인증을 제공하므로 Basic Auth를 적용하지 않는다.

GoAccess (실시간 로그 분석)

access/docker-compose.yml
services:
  goaccess:
    image: xavierh/goaccess-for-nginxproxymanager:latest
    container_name: goaccess
    restart: unless-stopped
    environment:
      - TZ=${TZ:-Asia/Seoul}
      - SKIP_ARCHIVED_LOGS=False
      - DEBUG=False
      - ENABLE_BROWSERS_LIST=True
      - CUSTOM_BROWSERS=Kuma:Uptime
      - LOG_TYPE=TRAEFIK
      - EXCLUDE_IPS=${GOACCESS_EXCLUDE_IPS}
    volumes:
      - ../traefik/logs:/opt/log:ro
    labels:
      - traefik.enable=true
      - traefik.http.routers.goaccess.rule=Host(`access.${HOST_DOMAIN}`)
      - traefik.http.routers.goaccess.entrypoints=websecure
      - traefik.http.routers.goaccess.tls.certresolver=letsencrypt
      - traefik.http.routers.goaccess.middlewares=secure-auth@docker
      - traefik.http.services.goaccess.loadbalancer.server.port=7880
    networks:
      - proxy
 
networks:
  proxy:
    external: true

핵심 설정

  • LOG_TYPE=TRAEFIK: Traefik 로그 포맷 자동 파싱
  • middlewares=secure-auth@docker: Traefik 컨테이너에 정의된 Rate Limit + BasicAuth 체인 미들웨어 참조
  • 볼륨: ../traefik/logs를 읽기 전용으로 마운트해 실시간 로그 분석

배포

Traefik을 먼저 띄운 후 나머지 서비스를 시작한다.

docker compose --env-file .env -f traefik/docker-compose.yml up -d
docker compose --env-file .env -f portainer/docker-compose.yml up -d
docker compose --env-file .env -f access/docker-compose.yml up -d

로그 확인:

docker compose --env-file .env -f traefik/docker-compose.yml logs -f traefik

접속 URL

서비스URL인증
Traefik Dashboardhttps://traefik.yourdomain.comBasicAuth + Rate Limit
Portainerhttps://portainer.yourdomain.comPortainer 자체 인증
GoAccesshttps://access.yourdomain.comBasicAuth + Rate Limit

새 서비스 연동

다른 컨테이너를 Traefik 프록시에 연결하려면 다음 라벨을 추가한다.

services:
  myapp:
    image: myapp:latest
    labels:
      - traefik.enable=true
      - traefik.http.routers.myapp.rule=Host(`myapp.${HOST_DOMAIN}`)
      - traefik.http.routers.myapp.entrypoints=websecure
      - traefik.http.routers.myapp.tls.certresolver=letsencrypt
      - traefik.http.services.myapp.loadbalancer.server.port=8080
    networks:
      - proxy
 
networks:
  proxy:
    external: true

외부 노출을 제한하려면 secure-auth@docker 미들웨어를 추가한다:

labels:
  - traefik.http.routers.myapp.middlewares=secure-auth@docker

참고자료