너무나 쉬운 Traefik v2 리버스 프록시
이 내용은 아직 번역본이 없습니다.
들어 가는 글
Traefik 공식 문서를 보면 foo bar 이렇게 해놔서 헷갈리는 부분이 많죠.
띄어쓰기에 엄격한 Yaml, 이스케이핑이 필요한 문법, 등등 같이 결합해서 아주 그냥 환상의 하모니를 자랑합니다.
1. Traefik의 구성
간단하게 정리해 보겠습니다. Traefik은 크게 세 가지로 구성됩니다.
Service(서비스)
Middleware(미들웨어)
Router(라우터)
2. 글을 읽을 때 기억할 것 2가지
큰 틀에서 Traefik의 문법을 따라간다.
이름을 붙이고 그걸 연결한다
3. Traefik 설정의 핵심
Traefik 설정에서 핵심은 1. 서비스
와 3. 라우터
입니다.
서비스와 라우터만 제대로 설정하면 크게 문제없고 미들웨어는 라우터를 보조한다고 생각하시면 편합니다.
사용자가 이름을 부여 한대로 된다는 것에서 쿠버네티스와 비슷한 성격을 갖고 있습니다.
Traefik의 설정은 큰 틀은 Traefik의 문법을 따라가지만, 그 안에서 이름붙이는 것은 사용자의 맘입니다.
your-container: # image: your-docker-image
labels: # Attach add-foo-prefix@file middleware (declared in file) - 'traefik.http.routers.my-container.middlewares=add-foo-prefix@file'
이렇게 foo, bar, my-container 이렇게만 달랑 해놓고 이름붙이는 것에 대한 설명을 안해주니 문서를 보고 있으면 정신이 아득해 집니다.
4. docker-compose.yml 예제
traefik: image: traefik:v2.10 restart: unless-stopped logging: options: max-size: "10m" command: - "--log.level=INFO" - "--accesslog=true" - "--api.dashboard=true" - "--ping=true" - "--ping.entrypoint=ping"
- "--entryPoints.ping.address=:8082" - "--entryPoints.web.address=:80" - "--entryPoints.websecure.address=:443"
- "--entryPoints.web.http.redirections.entryPoint.to=websecure" - "--entrypoints.web.http.redirections.entryPoint.scheme=https" - "--entrypoints.web.http.redirections.entrypoint.permanent=true"
- "--providers.docker=true" - "--providers.docker.watch=true" - "--providers.docker.network=traefik-network" - "--providers.docker.endpoint=unix:///var/run/docker.sock" - "--providers.docker.exposedByDefault=false"
# Let's Encrypt ACME 설정 - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true" - "--certificatesresolvers.letsencrypt.acme.keyType=EC256" - "--certificatesresolvers.letsencrypt.acme.email=webmaster@example.com" - "--certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme/acme.json"
# Let's Encrypt ACME DNS Challenge (CLoudflare) #- "--certificatesResolvers.letsencrypt.acme.dnsChallenge=true" #- "--certificatesResolvers.letsencrypt.acme.dnsChallenge.provider=cloudflare" #- "--certificatesResolvers.letsencrypt.acme.dnsChallenge.resolvers=1.1.1.1:53,8.8.8.8:53" #- "--certificatesresolvers.letsencrypt.acme.dnschallenge.delaybeforecheck=0"
# Prometheus 메트릭 설정 - "--metrics.prometheus=true" - "--metrics.prometheus.buckets=0.1,0.3,1.2,5.0" - "--metrics.prometheus.addServicesLabels=true" - "--metrics.prometheus.addrouterslabels=true" - "--metrics.prometheus.addEntryPointsLabels=true"
# 버전 확인 및 익명 사용 통계 설정 - "--global.checkNewVersion=true" - "--global.sendAnonymousUsage=false"
# HTTP3 설정 - "--experimental.http3=true" - "--entrypoints.websecure.http3" - "--entrypoints.websecure.http3.advertisedport=443"
# environment: # CF_DNS_API_TOKEN:
healthcheck: test: ["CMD", "wget", "http://localhost:8082/ping", "--spider"] interval: 10s timeout: 2s retries: 3 start_period: 5s
labels: - "traefik.enable=true" - "traefik.http.services.dashboard.loadbalancer.server.port=8080" - "traefik.http.services.dashboard.loadbalancer.passhostheader=true"
- "traefik.http.routers.dashboard.rule=Host(`web.example.com`)" - "traefik.http.routers.dashboard.entrypoints=websecure" - "traefik.http.routers.dashboard.service=api@internal" - "traefik.http.routers.dashboard.tls=true" - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
volumes: - /var/run/docker.sock:/var/run/docker.sock - ./traefik-certificates:/etc/traefik/acme
networks: - traefik-network
ports: - target: 80 published: 80 mode: host - target: 443 published: 443 mode: host protocol: tcp - target: 443 published: 443 mode: host protocol: udp
rhymix: image: navystack/rhymix:latest restart: unless-stopped depends_on: - rhymix-db
logging: options: max-size: "10m"
labels: - "traefik.enable=true" - "traefik.docker.network=traefik-network"
- "traefik.http.services.rhymix-srv.loadbalancer.server.port=80" - "traefik.http.services.rhymix-srv.loadbalancer.passhostheader=true"
- "traefik.http.middlewares.www-redir.redirectregex.regex=^https://www.(.*)" - "traefik.http.middlewares.www-redir.redirectregex.replacement=https://$${1}" - "traefik.http.middlewares.www-redir.redirectregex.permanent=true"
- "traefik.http.middlewares.compresstraefik.compress=true"
- "traefik.http.routers.rhymix-rt.rule=Host(`example.com`) || Host(`www.example.com`)" - "traefik.http.routers.rhymix-rt.entrypoints=websecure" - "traefik.http.routers.rhymix-rt.service=rhymix-srv" - "traefik.http.routers.rhymix-rt.middlewares=www-redir, compresstraefik"
- "traefik.http.routers.rhymix-rt.tls=true" - "traefik.http.routers.rhymix-rt.tls.certresolver=letsencrypt" - "traefik.http.routers.rhymix-rt.tls.domains[0].main=example.com" - "traefik.http.routers.rhymix-rt.tls.domains[0].sans=*.example.com"
volumes: - rhymix-data:/var/www/html
networks: - traefik-network
volumes: rhymix-data: rhymix-db:
networks: traefik-network: external: true
위의 코드 스니펫은 docker-compose의 일부입니다.
처음 볼 때는 한숨만 나오죠 길기도 길 뿐더러 낯선 개념들 뿐입니다.
5. traefik command Tear Down
천천히 뜯어보겠습니다. 아래의 모든 줄 번호는 docker-compose.yml
예제에 있는 번호입니다.
처음 말씀 드린것 처럼 기억할 것은 딱 두가지 입니다.
핵심은 서비스와 라우터다
큰 틀은 Traefik의 문법을 따르지만 이름 붙이는 것은 내 마음이다.
1. 로그, 대시보드와 PING
- '--log.level=INFO'- '--accesslog=true'- '--api.dashboard=true'- '--ping=true'- '--ping.entrypoint=ping'
전반적으로 Traefik 문법입니다. 규칙 2번(큰 틀은 Traefik의 문법을 따르지만 이름 붙이는 것은 내 마음이다.)입니다. ⓐ 역시 규칙 2번입니다. --ping.entrypoint=
여기까지가 Traefik 문법이고 ping
여기는 내가 지은 이름입니다.
2. 엔트리포인트의 정의
- '--entryPoints.ping.address=:8082'- '--entryPoints.web.address=:80'- '--entryPoints.websecure.address=:443'
각각의 앞부분
--entryPoints.
까지는 Traefik 문법이고.
을 기준으로 각각ping
,web
,websecure
은 내가 지은 이름입니다.그리고 그다음부터 각각의 줄에서 이어지는 address부터는 역시 Traefik 문법이고
:
을 기준으로 나오는 숫자들은 역시 내가 부여한 번호들입니다. (물론 HTTP, HTTPS를 위한 포트이기는 하지만요)
앞에서
- '--ping.entrypoint=ping'
라고 ping
이라고 이름 붙였죠? (“=
” 다음에 오는 ping
) 앞에서 선언한 엔트리포인트 ping
에 대해서 나는
- '--entryPoints.ping.address=:8082'
8082번을 “사용하겠다.” 입니다.
3. 리다이렉션의 정의
- '--entryPoints.web.http.redirections.entryPoint.to=websecure'- '--entrypoints.web.http.redirections.entryPoint.scheme=https'- '--entrypoints.web.http.redirections.entrypoint.permanent=true'
각각의
--entryPoints
.는 트래픽 문법입니다. 그다음에 오는web
은 내가 위에서지은 이름입니다. 나머지는 Traefik 문법 이고, 그 중에서 redirections에 대해서 정의하고 있습니다.위에 세 줄을 뜯으면 내가 정의한
web
에대해서 내가 정의한websecure
로 리다이렉션 하겠다. 이 때의 스키마는https
이고,permanent
즉 301 리다이렉션 하겠다 입니다.간단히 이야기해서 http 80으로 들어오면 나는 https 443으로 돌리겠다 입니다.
4. Providers의 정의
- '--providers.docker=true'- '--providers.docker.watch=true'- '--providers.docker.network=traefik-network'- '--providers.docker.endpoint=unix:///var/run/docker.sock'- '--providers.docker.exposedByDefault=false'
여기서는 traefik 문법입니다.
ⓐ의 첫 번째 줄은
docker
를 사용하겠다. 두 번째 줄은label
이 변경되면 즉시 적용하겠다.ⓑ는 traefik이 감시할 네트워크는 내가 이름붙인
traefik-network
이다.ⓒ의 첫번째 줄: 사용할 도커의 엔드포인트는
unix:///var/run/docker.sock
이다. 두 번째 줄: 내가true
한 것만 노출해라
5. 인증서의 정의 (TLS)
# Let's Encrypt ACME 설정- '--certificatesresolvers.letsencrypt.acme.tlschallenge=true'- '--certificatesresolvers.letsencrypt.acme.keyType=EC256'- '--certificatesresolvers.letsencrypt.acme.email=webmaster@example.com'- '--certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme/acme.json'
각각의 줄에서 --certificatesresolvers.
은 Traefik 문법입니다. 그리고 그다음에 오는 letsencrypt
는 letsencrypt를 사용할 것이니 그냥 내가 지은
이름입니다.
풀어서 설명하면.
certificatesresolvers
로 나는letsencrypt
라고 이름붙인 것을 사용할거고acme
프로토콜이고tlschallenge
를 사용할 것이다.인증서는
EC256
로 발급해라사용할 이메일은
webmaster@example.com
이다인증서를 저장할 장소는 Traefik에 바인드된
/etc/traefik/acme/acme.json
이다.
6. 인증서의 정의 (DNS)
마찬가지로 현재는 주석처리 되어있지만 아래는 DNS챌린지를 사용하는 경우입니다.
# Let's Encrypt ACME DNS Challenge (CLoudflare)#- "--certificatesResolvers.letsencrypt.acme.dnsChallenge=true"#- "--certificatesResolvers.letsencrypt.acme.dnsChallenge.provider=cloudflare"#- "--certificatesResolvers.letsencrypt.acme.dnsChallenge.resolvers=1.1.1.1:53,8.8.8.8:53"#- "--certificatesresolvers.letsencrypt.acme.dnschallenge.delaybeforecheck=0"
이거는 한번 해석해 보세요 :)
7. prometheus metrics의 정의
# Prometheus 메트릭 설정- '--metrics.prometheus=true'- '--metrics.prometheus.buckets=0.1,0.3,1.2,5.0'- '--metrics.prometheus.addServicesLabels=true'- '--metrics.prometheus.addrouterslabels=true'- '--metrics.prometheus.addEntryPointsLabels=true'
# 버전 확인 및 익명 사용 통계 설정- '--global.checkNewVersion=true'- '--global.sendAnonymousUsage=false'
여기는 그냥 Traefik 문법입니다. 자세한 사항은 공식문서 보시면 됩니다.
8. HTTP3 QUIC의 정의
# HTTP3 설정- '--experimental.http3=true'- '--entrypoints.websecure.http3'- '--entrypoints.websecure.http3.advertisedport=443'
여기는 현재 HTTP3를 사용하려면 활성화 해야하는 설정입니다.
현재는 experimental 이고
--entrypoints.websecure.http3
여기에서websecure
는 https라서 websecure가 아니고, 우리가websecure
라고 이름지었기 때문에websecure
인겁니다.
자 쭉 넘어가서
labels: - 'traefik.enable=true' - 'traefik.http.services.dashboard.loadbalancer.server.port=8080' - 'traefik.http.services.dashboard.loadbalancer.passhostheader=true'
- 'traefik.http.routers.dashboard.rule=Host(`web.example.com`)' - 'traefik.http.routers.dashboard.entrypoints=websecure' - 'traefik.http.routers.dashboard.service=api@internal' - 'traefik.http.routers.dashboard.tls=true' - 'traefik.http.routers.dashboard.tls.certresolver=letsencrypt'
자 여기 보시면 이제
핵심은 서비스와 라우터다
큰 틀은 Traefik의 문법을 따르지만 이름 붙이는 것은 내 마음이다. 요기에서 1번이 나옵니다.
9. services의 정의
- 'traefik.enable=true'- 'traefik.http.services.dashboard.loadbalancer.server.port=8080'- 'traefik.http.services.dashboard.loadbalancer.passhostheader=true'
- 아까 26번 줄에서 선언했기 때문에 활성화하는 옵션입니다.
- '--providers.docker.exposedByDefault=false'
라고 선언했기 때문에 활성화하는 옵션입니다.
여기에서
- "traefik.http.services.
는 서비스를 정의하겠다.
이름은dashboard
이고 로드밸런스(리버스 프록싱)할 포트는8080
이다 라고 선언하는 겁니다.passhostheader헤더
를 넘기겠다.
여기까지가 서비스 정의를 한것입니다. 이제 핵심 중 서비스를 정의했으며 라우터를 정의하겠습니다.
10. routers의 정의
- 'traefik.http.routers.dashboard.rule=Host(`web.example.com`)'- 'traefik.http.routers.dashboard.entrypoints=websecure'- 'traefik.http.routers.dashboard.service=api@internal'- 'traefik.http.routers.dashboard.tls=true'- 'traefik.http.routers.dashboard.tls.certresolver=letsencrypt'
traefik.http.routers.
라우터를 정의하겠다 이름은dashboard
라고 지을거고 도메인(Host)는web.example.com
이다.내가 이름 붙인
dashboard
가 들어올 곳은 내가 이름붙인websecure
이다.중간에 뺀건 traefik문법입니다. :)
tls(ssl)사용할거고
인증서는 내가 이름 붙인
letsencrypt
로 제공해라 입니다.
6. rhymix labels Tear Down
자 거의 다왔습니다.
11. rhymix 서비스 정의
labels: - 'traefik.enable=true' - 'traefik.docker.network=traefik-network'
- traefik아 감시해라, 감시할 네트워크는 내가 이름 붙인
traefik-network
이다.
12. 서비스 포트의 정의
- 'traefik.http.services.rhymix-srv.loadbalancer.server.port=80'- 'traefik.http.services.rhymix-srv.loadbalancer.passhostheader=true'
서비스의 이름은
rhymix-srv
이고 리버스프록싱(로드밸런싱)할 포트는80 포트
이다.헤더 넘긴다.
13. www를 root로 미들웨어
- 'traefik.http.middlewares.www-redir.redirectregex.regex=^https://www.(.*)'- 'traefik.http.middlewares.www-redir.redirectregex.replacement=https://$${1}'- 'traefik.http.middlewares.www-redir.redirectregex.permanent=true'
www-redir
라는 이름의 미들웨어를 정의하는데 그게 무엇이냐하면 redirectregex 정규식으로 리다이렉션 하겠다. 정규식은^https://www.(.*)
로 시작하면이다.- redirectregex 할 것인데 어떻게 할 것인가 하면
replacement
(교체) 하겠다.(.*)
인 부분을 잘라서${1}
여기에 넣겠다
https://$${1}
에 넣을 건데 왜 $가 2개냐 하면 제가 달러를 좋아해서가 아니고 이스케이핑 하는겁니다.$${1}
이렇게 적어줘야 안으로 넘어갈 때는${1}
로 넘어갑니다.
- 301(permanent)로 하겠다
14. 압축하는 미들웨어
- 'traefik.http.middlewares.compresstraefik.compress=true'
- 117번 줄 미들웨어를 하나 더 정의할 것인데 이름은
compresstraefik
이다
15. rhymix 라우터
- 'traefik.http.routers.rhymix-rt.rule=Host(`example.com`) || Host(`www.example.com`)'- 'traefik.http.routers.rhymix-rt.entrypoints=websecure'- 'traefik.http.routers.rhymix-rt.service=rhymix-srv'- 'traefik.http.routers.rhymix-rt.middlewares=www-redir, compresstraefik'
라우터의 이름은
rhymix-rt
이고 도메인은example.com
혹은www.example.com
이다.이름이
rhymix-rt
인 라우터가 들어올 곳은websecure
이고라우팅할 서비스의 이름은
rhymix-srv
이다이름이
rhymix-rt
에 적용할 미들웨어는 내가 앞에서 정의한www-redir
,compresstraefik
이다.
16. 라우터의 인증서 정의
- 'traefik.http.routers.rhymix-rt.tls=true'- 'traefik.http.routers.rhymix-rt.tls.certresolver=letsencrypt'- 'traefik.http.routers.rhymix-rt.tls.domains[0].main=example.com'- 'traefik.http.routers.rhymix-rt.tls.domains[0].sans=*.example.com'
tls(ssl)
적용할거고내가 정의한
letsencrypt
로 제공해라혹시 인증서 없으면 메인은
example.com
로 발급하고 SANS로*.example.com
로 발급해라.와일드카드 발급은
DNS 챌린지
로 하셔야 발급됩니다.
이게 전부입니다.
가끔 보면 이름짓는다는 것에 착안해서
labels: - 'traefik.enable=true' - 'traefik.docker.network=traefik-network'
- 'traefik.http.services.rhymix.loadbalancer.server.port=80' - 'traefik.http.services.rhymix.loadbalancer.passhostheader=true'
- 'traefik.http.middlewares.www-redir.redirectregex.regex=^https://www.(.*)' - 'traefik.http.middlewares.www-redir.redirectregex.replacement=https://$${1}' - 'traefik.http.middlewares.www-redir.redirectregex.permanent=true'
- 'traefik.http.middlewares.compresstraefik.compress=true'
- 'traefik.http.routers.rhymix.rule=Host(`example.com`) || Host(`www.example.com`)' - 'traefik.http.routers.rhymix.entrypoints=websecure' - 'traefik.http.routers.rhymix.service=rhymix' - 'traefik.http.routers.rhymix.middlewares=www-redir, compresstraefik'
- 'traefik.http.routers.rhymix.tls=true' - 'traefik.http.routers.rhymix.tls.certresolver=letsencrypt' - 'traefik.http.routers.rhymix.tls.domains[0].main=example.com' - 'traefik.http.routers.rhymix.tls.domains[0].sans=*.example.com'
서비스의 이름이 rhymix
이고 라우터의 이름도 rhymix
인 것은 아무런 인과 관계가 없습니다.
이렇게 기술하면 이해하기 어려울 수 있지만, 전역으로 정의하는 방법도 있습니다. 또한 다이나믹 라우팅이나 명령어를 줄여서 기록할 수도 있습니다. 나중에 기회가 되면 이에 대해 더 자세히 설명하겠습니다.
docker-compose.yml
로 직접 해보면서 해보실 분은 github에 코드를 올려놓았습니다.
확인해 보시면서 하면 금방 이해하실 것 같습니다.
수고 많으셨습니다. 감사합니다.
Askfront.com (에스크프론트)
에스크프론트
에서는 NavyStack 가이드뿐만 아니라, 궁금한 모든 질문을 환영합니다. 😊