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
EOFHOST_DOMAIN, HOST_EMAIL을 실제 값으로 수정한다.
2. Cloudflare DNS 레코드 설정
Cloudflare Dashboard → 도메인 선택 → DNS → Records:
| Type | Name | Content | Proxy |
|---|---|---|---|
A | @ | 홈랩 서버 공인 IP | Proxied |
A | * | 홈랩 서버 공인 IP | Proxied |
3. Cloudflare API 토큰 생성
- API Tokens → Create Token
- Edit zone DNS 템플릿 선택
- Permissions:
Zone / DNS / Edit,Zone / Zone / Read - Zone Resources: 대상 도메인 선택
- 토큰 복사 후 시크릿 파일 생성:
mkdir -p traefik/secrets
echo "your-cloudflare-api-token" > traefik/secrets/cf-dns-api-token.secret4. Basic Auth 인증 파일 생성
Traefik 대시보드와 GoAccess에 적용할 인증 파일:
sudo apt install -y apache2-utils
htpasswd -nB admin > traefik/secrets/usersfile.secret비밀번호 입력 프롬프트가 나오면 원하는 비밀번호를 입력한다.
서비스 구성
Traefik (리버스 프록시)
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: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)
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 (실시간 로그 분석)
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 Dashboard | https://traefik.yourdomain.com | BasicAuth + Rate Limit |
| Portainer | https://portainer.yourdomain.com | Portainer 자체 인증 |
| GoAccess | https://access.yourdomain.com | BasicAuth + 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