Traefik 2.0와 Discourse 설정

스크린샷을 공유해 주시겠습니까?

traefik 대시보드에서 찍은 스크린샷 두 장을 첨부합니다. 서비스에는 discourse가 두 번 표시되지만, 라우터에는 아무것도 표시되지 않는 것을 확인할 수 있습니다. 솔직히 이것이 정확히 무엇을 의미하는지는 잘 모르지만, 이상해서 알아차렸습니다.

수정: 저는 discourse 신규 사용자라 한 게시물에 이미지를 두 개 올리면 안 되므로, 다른 게시물로 추가하겠습니다.

오늘 밤 도커를 실행해서 비교해 봐야 할 것 같네요. 결과를 알려드릴게요. 아니면 여기 다른 분들이 대시보드를 공유해서 비교해 주실 수도 있겠네요.

그것은 필요하지 않습니다. traefik v2.1로 전환하고 API를 사용하는 것이 더 좋습니다. 이렇게 하세요:
- "traefik.http.routers.traefik_dashboard-router.service=api@internal"
참고: Endless 502 / forwarding when calling dashboard via subdomain #6123 - #5 by ldez - Traefik v2 - Traefik Labs Community Forum

주석들을 제거해 주실 수 있나요?

그렇게 하면 우리가 읽기 더 쉬워집니다.
걱정 마세요, 저도 전에 같은 이야기를 들었습니다 :).
저는 주석을 별도의 줄에 작성해서 cat traefikV2.yaml | grep -v "#"을 사용하여 깔끔하게 출력하기 쉽게 합니다.

제 대시보드입니다

  1. 대시보드
  2. HTTP 라우터
  3. HTTP 서비스
  4. HTTP 미들웨어

음, 이 문제를 해결하는 데 며칠이 걸렸습니다. docker-compose에 모든 것을 넣는 대신 yaml 파일을 사용하도록 Traefik 구성을 다시 설정했습니다. 하지만 모든 것을 다시 연결한 후에도 동일하거나 비슷한 동작을 겪고 있습니다. 제 도메인에서 404 오류가 발생하고 있으며, Traefik 대시보드에서는 Services와 routers에 discourse 항목이 보이지만 routers 아래에는 아무것도 보이지 않습니다.

Traefik docker-compose
version: '3'

services:
  traefik:
    image: traefik:v2.0
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    ports:
      - 80:80
      - 443:443
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data/traefik.yml:/traefik.yml:ro
      - ./data/acme.json:/acme.json
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.entrypoints=http"
      - "traefik.http.routers.traefik.rule=Host(`monitor.example.com`)"
      - "traefik.http.middlewares.traefik-auth.basicauth.users=user:redacted"
      - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
      - "traefik.http.routers.traefik-secure.entrypoints=https"
      - "traefik.http.routers.traefik-secure.rule=Host(`monitor.example.com`)"
      - "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
      - "traefik.http.routers.traefik-secure.tls=true"
      - "traefik.http.routers.traefik-secure.tls.certresolver=http"
      - "traefik.http.routers.traefik-secure.service=api@internal"

networks:
  proxy:
    external: true
Traefik data/traefik.yml
api:
  dashboard: true

entryPoints:
  http:
    address: ":80"
  https:
    address: ":443"

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false

certificatesResolvers:
  http:
    acme:
      email: nick@innomadic.com
      storage: acme.json
      httpChallenge:
        entryPoint: http
containers/app.yml
templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"

expose:

params:
  db_default_text_search_config: "pg_catalog.english"

  db_shared_buffers: "128MB"



env:
  LANG: en_US.UTF-8

  UNICORN_WORKERS: 2

  DISCOURSE_HOSTNAME: forum.example.com


  DISCOURSE_DEVELOPER_EMAILS: 'info@example.com'

  DISCOURSE_SMTP_ADDRESS: redacted.com
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: redacted
  DISCOURSE_SMTP_PASSWORD: "redacted"

  LETSENCRYPT_ACCOUNT_EMAIL: info@example.com


volumes:
  - volume:
      host: /var/discourse/shared/standalone
      guest: /shared
  - volume:
      host: /var/discourse/shared/standalone/log/var-log
      guest: /var/log

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git

run:
  - exec: echo "Beginning of custom commands"
  - exec: echo "End of custom commands"

labels:
  app_name:                                                                     discourse

  traefik.enable:                                                               true
  traefik.docker.network:                                                       proxy
  traefik.http.routers.discourse.rule:                                          Host(`forum.example.com`)
  traefik.http.routers.discourse.entrypoints:                                   http
  traefik.http.routers.discourse.middlewares:                                   discourse_redirect2https
  traefik.http.services.discourse.loadbalancer.server.port:                     80

  traefik.http.routers.discourse_secure.rule:                                   Host(`forum.example.com`)
  traefik.http.routers.discourse_secure.entrypoints:                            https

  traefik.http.services.discourse_secure.loadbalancer.server.port:              80
  traefik.http.routers.discourse_secure.tls.certresolver:                       tlsChallenge_letsencrypt

  traefik.http.middlewares.discourse_redirect2https.redirectscheme.scheme:      https

docker_args:
  - "--network=proxy"
  - "--expose=80"

Traefik 네트워크에 이 컨테이너를 연결하기 위해 docker network connect proxy 명령어도 실행한 것 같습니다.

무엇이 부족한지 확인하는 데 도움이 주시면 감사하겠습니다. 또한 보안 설정이 제대로 되어 있는지 의견도 듣고 싶습니다.

무엇을 놓치고 계신지 모르겠네요. 저는 Ansible에서 traefik을 사용해 사이트를 빠르게 구축할 때 이렇게 합니다:

        --docker-args "-l traefik.frontend.rule=Host:{{discourse_hostname}} \
        -l traefik.frontend.entryPoints=https \
        -l traefik.backend={{discourse_shortname}} \
        -l traefik.port=80"

그리고 다음을 실행합니다.

       ./launcher start {{ discourse_yml }} {{ docker_args }}

traefik 규칙을 yml 파일이 아닌 docker_args에 추가하면, 부트스트래핑 중인 컨테이너에 traefik이 개입하지 않는다는 추가적인 이점이 있습니다.

그건 아마 Traefik v1일 것 같아요?

아, 죄송합니다. 네, 그건 traefik 1인 것 같은데, 구체적인 부분에 대해서는 도움을 드리기 어렵겠습니다.

app.yml에서 해당 레이블에 어떤 값도 설정하지 않은 것 같은데요? 규칙(rule)을 설정하고, 아마도 미들웨어(middleware)도 설정해야 할 것 같습니다.

부트스트랩(bootstrap) 중에 다운타임을 원하지 않으신다면, 제 예시처럼 ./launcher start 명령으로 설정을 하시는 것이 좋습니다.

아마도 "middleware"는 예전의 "backend"를 가리키는 용어일까요? Discourse 컨테이너가 원하는 서버임을 확인하고, 프론트엔드/URL을 적절한 백엔드/서버와 연결하기 위해 무언가를 설정해야 합니다.

값은 설정되어 있지만, 오른쪽으로 조금 스크롤해야 보입니다. 단순히 포맷 문제입니다.

감사합니다

ㅋㅋㅋ. 죄송합니다.

솔직히 제가 뭘 말씀드리는 건지 잘 모르지만, discourse@docker를 로드 밸런서로 설정하는 건 제 생각에 말이 안 됩니다.

아, 이제 알 것 같아.

아래 라인을 제거했어:
# traefik.http.services.discourse_secure.loadbalancer.server.port: 80

그리고 이 라인은 남겨뒀어:

traefik.http.services.discourse.loadbalancer.server.port: 80

labels:
  app_name:                                                                     discourse

  #----Traefik lables------------------------
  traefik.enable:                                                               true
  traefik.docker.network:                                                       proxy
   #---HTTP ROUTER SECTION-------------------
  traefik.http.routers.discourse.rule:                                          Host(`forum.example.com`)
    #--HTTP SECTION--------------------------
  traefik.http.routers.discourse.entrypoints:                                   http
  traefik.http.routers.discourse.middlewares:                                   discourse_redirect2https
  traefik.http.services.discourse.loadbalancer.server.port:                     80

   #---HTTPS ROUTER SECTION
  traefik.http.routers.discourse_secure.rule:                                   Host(`forum.example.com`)
    #--HTTPS SECTION
  traefik.http.routers.discourse_secure.entrypoints:                            https

 # traefik.http.services.discourse_secure.loadbalancer.server.port:              80
    #--TLS SECTION
  traefik.http.routers.discourse_secure.tls.certresolver:                       tlsChallenge_letsencrypt

   #---MIDDLEWARE SECTION redirect http to https
  traefik.http.middlewares.discourse_redirect2https.redirectscheme.scheme:      https

이렇게 된 경위나 이유가 정확히 뭔지는 모르겠지만, 지금은 잘 작동해. 즉, 스레드에 올린 모든 설정을 사용하되 이 한 가지 변경 사항만 적용하면 내 환경에서는 정상적으로 작동하고 있어.

모두 감사해!

그것이 정확합니다.
저는 제 설정에 다음 내용을 넣었습니다:

#—SERVICE SECTION: 트래픽이 요청을 어디로 보내야 하는지 트래픽에게 알려주는 서비스 섹션
traefik.http.services.discourse.loadbalancer.server.port: 80

이것이 익숙한가요?
여기서 하는 일은 요청을 누구(서비스)에게, 어떤 포트로 보낼지 알려주는 것입니다.
expose를 사용하여 해결할 수도 있지만, 보안 관점에서 컨테이너를 최대한 적게 노출하는 것이 좋습니다. 또한, 여러 포트를 노출하면 트래픽은 첫 번째 포트를 선택하므로 loadbalancer를 사용하는 것이 더 깔끔합니다.


Traefik data/traefik.yml

이 경로는 기본 경로이므로 저는 이렇게 하지 않습니다.

여기서는 전체 경로가 아닌 기본 경로를 사용하고 있습니다. 경로를 기억해 두어야 하므로 저는 이렇게 하는 것을 좋아하지 않습니다. 저는 storage: /etc/ssl/certs/letsencrypt/acme.json을 사용합니다.

저는 네트워크를 정의합니다. 기본 설정을 좋아하지 않습니다.

providers:
  docker:
    exposedByDefault: false
    network: bridge_proxy_traefikv2

containers/app.yml

이것은 필요하지 않습니다. loadbalancer가 이 역할을 수행합니다.

Traefik docker-compose

이 네트워크에 이름을 붙이고 싶을 수 있습니다. 제 경우를 보면 다음과 같습니다:

networks:
  traefik:
    external:
      name: bridge_proxy_traefikv2

여기서 제안해 주신 내용들을 하나씩 적용해 보고 있습니다. 답변해 주셔서 감사합니다.

한 가지만 짚고 넘어가야 할 것 같습니다. 엔드포인트에 대해서는 기본값을 신뢰하라고 하셨는데, 저장 위치에는 기본값을 신뢰하지 않는다고 하셨네요. :slight_smile:

말씀해 주신 acme.json 변경 사항은 적용해 보았습니다:

하지만 그러자 제 traefik 컨테이너가 파일을 찾지 못했습니다. 로그에 오류로 표시되었습니다. 그래서 당분간 acme.json 그대로 두었습니다.

이 부분은 질문이 있어서요:

여기서는 단순히 네트워크 이름을 바꾸거나 로컬 이름을 부여하는 것 아닌가요? 다른 구성에서는 proxy라고 불렀고, 여기서는 로컬에서 그렇게 불렀을 뿐입니다. 여전히 제가 정의한 이름이 있으므로, 이름을 바꾸는 것은 아닙니다. 적어도 제 이해로는 그렇습니다.

마지막으로 주목할 만한 우려 사항이 하나 있습니다. 위 “성공!” 게시글 이후에 HTTPS는 작동하지만 혼합 콘텐츠(mixed content) 경고가 뜨는 것을 알게 되었습니다. 즉, 모든 것이 암호화되는 것은 아닙니다.

같은 서버에 설치된 Wordpress에서는 혼합 콘텐츠 오류가 나오지 않지만, 제 Discourse 설치에서는 오류가 납니다. 따라서 이는 제 Discourse 설정에 특화된 문제일 것입니다.

개발자 콘솔을 살펴보니 다음과 같은 메시지가 보입니다:

안전한 페이지에서 혼합(비보안) 디스플레이 콘텐츠 “http://talk.redacted.com/uploads/default/optimized/1X/_129430568242d1b7f853bb13ebea28b3f6af4e7_2_180x180.png” 로드 중 FaviconLoader.jsm:174:19

그러면 제 리디렉션이 제대로 작동하지 않는 건가요?

검토를 위해 관련 파일들을 아래에 첨부합니다.

Traefik.yml

api:
dashboard: true

entryPoints:
http:
address: “:80”
https:
address: “:443”

providers:
docker:
exposedByDefault: false
network: proxy

certificatesResolvers:
http:
acme:
email: info@private.com
storage: acme.json
httpChallenge:
entryPoint: http

Traefik docker-compose

version: ‘3’

services:
traefik:
image: traefik:v2.0
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- proxy
ports:
- 80:80
- 443:443
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data/traefik.yml:/traefik.yml:ro
- ./data/acme.json:/acme.json
labels:
- “traefik.enable=true”
- “traefik.http.routers.traefik.entrypoints=http”
- “traefik.http.routers.traefik.rule=Host(monitor.private.com)”
- "traefik.http.middlewares.traefik-auth.basicauth.users=private:private”
- “traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https”
- “traefik.http.routers.traefik.middlewares=traefik-https-redirect”
- “traefik.http.routers.traefik-secure.entrypoints=https”
- “traefik.http.routers.traefik-secure.rule=Host(monitor.private.com)”
- “traefik.http.routers.traefik-secure.middlewares=traefik-auth”
- “traefik.http.routers.traefik-secure.tls=true”
- “traefik.http.routers.traefik-secure.tls.certresolver=http”
- “traefik.http.routers.traefik-secure.service=api@internal”

networks:
proxy:
external: true

app.yml

templates:

  • “templates/postgres.template.yml”
  • “templates/redis.template.yml”
  • “templates/web.template.yml”
  • “templates/web.ratelimited.template.yml”

expose:

params:
db_default_text_search_config: “pg_catalog.english”

db_shared_buffers: “128MB”

env:
LANG: en_US.UTF-8

UNICORN_WORKERS: 2

DISCOURSE_HOSTNAME: talk.private.com

DISCOURSE_DEVELOPER_EMAILS: 'info@private.com’

LETSENCRYPT_ACCOUNT_EMAIL: info@private.com

volumes:

  • volume:
    host: /var/discourse/shared/standalone
    guest: /shared
  • volume:
    host: /var/discourse/shared/standalone/log/var-log
    guest: /var/log

hooks:
after_code:
- exec:
cd: $home/plugins
cmd:
- git clone GitHub - discourse/docker_manager: Plugin for use with discourse docker image · GitHub

run:

  • exec: echo “Beginning of custom commands”
  • exec: echo “End of custom commands”

labels:
app_name: discourse

traefik.enable: true
traefik.docker.network: proxy
traefik.http.routers.discourse.rule: Host(talk.private.com)
traefik.http.routers.discourse.entrypoints: http
traefik.http.routers.discourse.middlewares: discourse_redirect2https
traefik.http.services.discourse.loadbalancer.server.port: 80

traefik.http.routers.discourse_secure.rule: Host(talk.private.com)
traefik.http.routers.discourse_secure.entrypoints: https
traefik.http.routers.discourse_secure.tls: true
traefik.http.routers.discourse_secure.service: discourse
traefik.http.routers.discourse_secure.tls.certresolver: http

traefik.http.middlewares.discourse_redirect2https.redirectscheme.scheme: https

docker_args:

  • “–network=proxy”

그러면, 왜 혼합 콘텐츠 오류가 나는지에 대해 어떤 생각이 있으신가요?

글쎄, 한동안 discourse가 의식체인지(의식이 생긴 건지) 싶을 정도였어요. 관리자 화면에서 이런 알림을 받았거든요:

하지만 이 변경 사항을 적용했는데도 여전히 혼합 콘텐츠(mixed content) 오류가 발생했습니다. 로그아웃 후 캐시를 삭제하고 다시 로그인해 보았지만, 결과는 동일했어요.

로그인 요청은 여전히 암호화되어 있는 것 같으니 그 점은 좋은 것 같지만, 이 혼합 콘텐츠 오류를 제거해야 합니다. 설정은 이전 게시글에 있습니다.

추가 업데이트: 혼합 콘텐츠 경고가 이제 사라졌습니다. 왜 그런지 정확히 파악되지는 않습니다. force_https 설정과 관련이 있는 것 같지만, 위에서 언급했듯이 캐시를 지우고 로그아웃 후 다시 로그인했음에도 불구하고 설정을 변경한 후 약 30분 동안 깨끗한 페이지 로딩을 받지 못했습니다.

좋은 소식은 Traefic 리버스 프록시 뒤에 제대로 작동하는 Discourse 설치를 갖추게 되었다는 점입니다.


http 연결을 통해 소싱된 이미지들이 문제였습니다. 같은 문제를 겪은 사례를 확인하세요:


볼륨을 올바르게 마운트했나요? traefik은 경로 관련해서 꽤 혼란스러울 수 있습니다.
예를 들어 저는 이렇게 합니다:

volumes:       
  - /etc/ssl/certs/traefik/letsencrypt:/etc/ssl/certs/letsencrypt
  - /opt/traefik/traefik-config.yaml:/etc/traefik/traefik.yaml
  - /etc/passwd.traefik.dashboard:/etc/passwd.BasicAuth.dashboard
  - /etc/passwd.traefik.whoami:/etc/passwd.BasicAuth.whoami

정확히 기억나지 않지만, 그 이유는 네트워크 목록을 CLI에서 볼 때 잘 보이게 하려는 것이었을 수도 있습니다.

아니요, 그렇게 마운트하지 않았습니다. 여기 제 traefik용 docker-compose의 volumes 섹션입니다:

    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data/traefik.yml:/traefik.yml:ro
      - ./data/acme.json:/acme.json

제 설정에는 let’s encrypt가 언급되지 않은 반면, 당신의 설정에는 언급되어 있다는 것을 알아차렸습니다. 아마도 당신은 docker-compose 디렉터리 내에 그 깊게 중첩된 폴더를 만들고 거기에 acme.json을 넣은 것 같네요.

또 다른 눈에 띄는 점은 제가 docker/sock와 localtime을 가지고 있다는 것입니다. localtime이 무엇인지는 확실하지 않고, docker.sock 선언이 보안 문제일 수도 있습니다. 그 부분에 대해 더 조사해 봐야겠습니다.

Let’s Encrypt 인증서 생성은 Traefik이 처리합니다. 볼륨을 마운트하면 도커 컨테이너 내부가 아닌 호스트에 인증서가 저장되도록 할 수 있습니다.
모든 마운팅 설정을 완료하는 것이 정말 까다로웠던 것 같아요. docker exec -it 명령으로 모든 항목을 확인했습니다.

  • ls
  • dir
  • vi