본문 바로가기
WIKI 기술 지식 베이스

Traefik HTTP 라우터 규칙과 우선순위

원문 보기 위키 갱신

Traefik HTTP 라우터 규칙과 우선순위 (Rules & Priority)

출처: Traefik HTTP Routers Rules & Priority Documentation

본문

Rules & Priority

HTTP 라우터는 들어오는 요청을 처리할 수 있는 서비스에 연결하는 역할을 담당해요. Traefik은 매칭 규칙을 정의하고 라우트에 우선순위를 부여할 수 있게 해 줘요.

Rules (규칙)

규칙은 특정 요청이 특정 기준과 일치하는지 판단하도록 값으로 구성된 매처(matcher) 집합이에요. 규칙이 검증되면 라우터가 활성화되고, 미들웨어를 호출한 뒤 요청을 서비스로 전달해요.

  • 규칙 값을 설정할 때는 백틱 ` 이나 이스케이프된 큰따옴표 \"를 사용해요. 값이 Go의 String Literal이기 때문에 작은따옴표 '는 허용되지 않아요.

  • 정규 표현식(regexp)을 받는 매처는 Go 스타일 문법을 사용해요.

  • 일반적인 AND(&&)와 OR(||) 논리 연산자를 예상되는 우선순위 규칙과 함께 사용할 수 있고, 복잡한 규칙을 표현하려면 괄호도 사용할 수 있어요.

  • NOT(!) 연산자는 매처를 반전시킬 수 있어요.

아래 표는 사용 가능한 모든 매처를 나열한 거예요:

| Matcher | Description | | Header(key, value) | 값이 value로 설정된 key라는 이름의 헤더를 포함한 요청과 매칭돼요. | | HeaderRegexp(key, regexp) | regexp와 일치하는 key라는 이름의 헤더를 포함한 요청과 매칭돼요. | | Host(domain) | 호스트가 domain으로 설정된 요청과 매칭돼요. 와일드카드 서브도메인 매칭(예: *.example.com)을 지원해요. | | HostRegexp(regexp) | 호스트가 regexp와 일치하는 요청과 매칭돼요. | | Method(method) | 메서드가 method로 설정된 요청과 매칭돼요. | | Path(path) | 경로가 path로 설정된 요청과 매칭돼요. | | PathPrefix(prefix) | 경로 접두사가 prefix로 설정된 요청과 매칭돼요. | | PathRegexp(regexp) | regexp를 사용해 요청 경로와 매칭돼요. | | Query(key, value) | 값이 value로 설정된 key라는 이름의 쿼리 파라미터를 가진 요청과 매칭돼요. | | QueryRegexp(key, regexp) | regexp와 일치하는 key라는 이름의 쿼리 파라미터를 가진 요청과 매칭돼요. | | ClientIP(ip) | ip를 사용해 클라이언트 IP와 매칭돼요. IPv4, IPv6, CIDR 형식을 지원해요. |

Header와 HeaderRegexp

Header와 HeaderRegexp 매처는 특정 헤더를 포함한 요청과 매칭되게 해 줘요.

| Behavior | Rule | | Content-Type 헤더가 application/yaml로 설정된 요청과 매칭 | Header(Content-Type, application/yaml) | | Content-Type 헤더가 application/json 또는 application/yaml로 설정된 요청과 매칭 | HeaderRegexp(Content-Type, ^application/(json|yaml)$) | | 헤더를 대소문자 구분 없이 매칭 | HeaderRegexp(Content-Type, (?i)^application/(json|yaml)$) |

Host와 HostRegexp

Host와 HostRegexp 매처는 특정 호스트로 향하는 요청과 매칭되게 해 줘요.

이 매처들은 비-ASCII 문자를 지원하지 않으므로, 그러한 도메인을 매칭하려면 푸니코드(punycode)로 인코딩된 값(rfc 3492)을 사용해요.

요청 URL에 Host가 설정되어 있지 않다면(예: IP 주소인 경우), 이 매처들은 Host 헤더를 살펴봐요.

이 매처들은 요청의 호스트를 소문자로 매칭해요.

와일드카드 서브도메인 매칭

Host 매처는 단일 레벨 와일드카드 접두사(*.example.com)를 지원해서 example.com의 모든 직접 서브도메인과 매칭할 수 있어요. TLS 옵션을 연결할 수 있고 더 효율적이므로 HostRegexp 매처보다 이쪽을 권장해요.

와일드카드는 정확히 하나의 서브도메인 레이블과 매칭돼요: *.example.com은 foo.example.com과 매칭되지만 foo.bar.example.com이나 example.com 자체와는 매칭되지 않아요.

이 기능은 v3 규칙 문법(기본값)에서만 사용할 수 있어요.

예외: 단독 와일드카드는 모든 요청과 매칭

위 규칙의 예외로, 단독 *는 서브도메인 와일드카드가 아니라 캐치-올(catch-all)로 취급돼요: Host()는 호스트와 무관하게 모든 요청(호스트가 전혀 없는 요청 포함)과 매칭돼요. 이는 TCP의 HostSNI() 매처 동작을 반영한 것으로, 두 매처가 일관성을 유지해요.

| Behavior | Rule | | Host가 example.com으로 설정된 요청과 매칭 | Host(example.com) | | 호스트와 무관하게 모든 요청과 매칭(위 예외 참고) | Host(*) | | example.com의 모든 서브도메인으로 보내진 요청과 매칭 | HostRegexp(^.+\.example\.com$) | | Host가 example.com 또는 example.org로 설정된 요청과 매칭 | HostRegexp(^example\.(com|org)$) | | Host를 대소문자 구분 없이 매칭 | HostRegexp((?i)^example\.(com|org)$) |

Method

Method 매처는 HTTP 메서드(요청 동사라고도 해요)를 기준으로 보내진 요청과 매칭되게 해 줘요.

| Behavior | Rule | | OPTIONS 요청과 매칭 | Method(OPTIONS) |

Path, PathPrefix, PathRegexp

이 매처들은 요청의 URL 경로를 기반으로 매칭되게 해 줘요.

정확한 매칭에는 Path와 그 접두사 변형인 PathPrefix를, 정규식 매칭에는 PathRegexp를 사용해요.

경로는 PathRegexp를 제외하고 항상 /로 시작해요.

| Behavior | Rule | | /products와 매칭하지만 /products/shoes나 /products/와는 매칭하지 않음 | Path(/products) | | /products 및 /products 아래의 모든 것(/products/shoes, /products/, 그리고 /products-for-sale까지)과 매칭 | PathPrefix(/products) | | ID가 있는 /products/shoes와 /products/socks 모두와 매칭(예: /products/shoes/31) | PathRegexp(^/products/(shoes|socks)/[0-9]+$) | | 경로가 .jpeg, .jpg, .png로 끝나는 요청과 매칭 | PathRegexp(\.(jpeg|jpg|png)$) | | /products 및 /products 아래의 모든 것과 대소문자 구분 없이 매칭 | PathRegexp((?i)^/products) |

Query와 QueryRegexp

Query와 QueryRegexp 매처는 쿼리 파라미터를 기반으로 요청과 매칭되게 해 줘요.

| Behavior | Rule | | mobile 쿼리 파라미터가 true로 설정된 요청과 매칭(예: /search?mobile=true) | Query(mobile, true) | | 값을 가지지 않는 mobile 쿼리 파라미터를 가진 요청과 매칭(예: /search?mobile) | Query(mobile) | | mobile 쿼리 파라미터가 true 또는 yes로 설정된 요청과 매칭 | QueryRegexp(mobile, ^(true|yes)$) | | mobile 쿼리 파라미터가 어떤 값(빈 값 포함)으로도 설정된 요청과 매칭 | QueryRegexp(mobile, ^.*$) | | 쿼리 파라미터를 대소문자 구분 없이 매칭 | QueryRegexp(mobile, (?i)^(true|yes)$) |

ClientIP

ClientIP 매처는 주어진 클라이언트 IP에서 보내진 요청과 매칭되게 해 줘요.

요청의 클라이언트 IP만 매칭하며, 매칭에 X-Forwarded-For 헤더는 사용하지 않아요.

| Behavior | Rule | | 주어진 IP(Ipv4)에서 온 요청과 매칭 | ClientIP(10.76.105.11) | | 주어진 IP(Ipv6)에서 온 요청과 매칭 | ClientIP(::1) | | 주어진 서브넷(Ipv4)에서 온 요청과 매칭 | ClientIP(192.168.1.0/24) | | 주어진 서브넷(Ipv6)에서 온 요청과 매칭 | ClientIP(fe80::/10) |

RuleSyntax

⚠️ 경고

RuleSyntax 옵션은 더 이상 사용되지 않으며(deprecated) 다음 메이저 버전에서 제거될 예정이에요. 이 필드를 사용하지 말고 라우터 규칙을 v3 문법으로 다시 작성해 주세요.

Traefik v3에서 새로운 규칙 문법이 도입됐어요(마이그레이션 가이드 참고). ruleSyntax 옵션은 라우터별로 규칙을 파싱하는 데 사용할 규칙 문법을 설정할 수 있게 해 줘요. 덕분에 이질적인 라우터 구성을 가질 수 있고 마이그레이션이 쉬워져요.

ruleSyntax 옵션의 기본값은 인스톨 구성(이전의 정적 구성)의 core.defaultRuleSyntax 옵션에서 상속돼요. 기본적으로 core.defaultRuleSyntax 정적 옵션은 v3이므로, 기본 규칙 문법도 v3이에요.

Configuration Example (설정 예시)

아래 구성은 File Provider(Structured)를 사용해 ruleSyntax를 설정해서 Router-v2는 v2 문법을, Router-v3는 v3 문법을 사용하도록 하는 예시예요.

Structured (YAML)

## Dynamic configuration
http:
  routers:
    Router-v3:
      rule: HostRegexp(`[a-z]+\\\\.traefik\\\\.com`)
      ruleSyntax: v3
    Router-v2:
      rule: HostRegexp(`{subdomain:[a-z]+}.traefik.com`)
      ruleSyntax: v2

Structured (TOML)

## Dynamic configuration
[http.routers]
  [http.routers.Router-v3]
    rule = "HostRegexp(`[a-z]+\\\\.traefik\\\\.com`)"
    ruleSyntax = "v3"
  [http.routers.Router-v2]
    rule = "HostRegexp(`{subdomain:[a-z]+}.traefik.com`)"
    ruleSyntax = "v2"

Labels

labels:
  - "traefik.http.routers.Router-v3.rule=HostRegexp(`[a-z]+\\\\.traefik\\\\.com`)"
  - "traefik.http.routers.Router-v3.ruleSyntax=v3"
  - "traefik.http.routers.Router-v2.rule=HostRegexp(`{subdomain:[a-z]+}.traefik.com`)"
  - "traefik.http.routers.Router-v2.ruleSyntax=v2"

Tags

{
  // ...
  "Tags": [
    "traefik.http.routers.Router-v3.rule=HostRegexp(`[a-z]+\\\\.traefik\\\\.com`)",
    "traefik.http.routers.Router-v3.ruleSyntax=v3"
    "traefik.http.routers.Router-v2.rule=HostRegexp(`{subdomain:[a-z]+}.traefik.com`)",
    "traefik.http.routers.Router-v2.ruleSyntax=v2"
  ]
},

Priority Calculation (우선순위 계산)

기본 우선순위 계산 방식

Structured (YAML)

http:
  routers:
    Router-1:
      rule: "HostRegexp(`[a-z]+\\.traefik\\.com`)"
      # ...
    Router-2:
      rule: "Host(`foobar.traefik.com`)"
      # ...

Structured (TOML)

[http.routers]
  [http.routers.Router-1]
    rule = "HostRegexp(`[a-z]+\\\\.traefik\\\\.com`)"
    # ...
  [http.routers.Router-2]
    rule = "Host(`foobar.traefik.com`)"
    # ...

Labels

labels:
  - "traefik.http.routers.Router-1.rule=HostRegexp(`[a-z]+\\\\.traefik\\\\.com`)"
  - "traefik.http.routers.Router-2.rule=Host(`foobar.traefik.com`)"

Tags

{
    // ...
    "Tags": [
      "traefik.http.routers.Router-1.rule=HostRegexp(`[a-z]+\\\\.traefik\\\\.com`)",
      "traefik.http.routers.Router-2.rule=Host(`foobar.traefik.com`)"
    ]
  }

이 경우, 호스트가 foobar.traefik.com인 모든 요청은 Router-2가 아니라 Router-1을 통해 라우팅돼요.

| Name | Rule | Priority | | Router-1 | HostRegexp([a-z]+\.traefik\.com) | 34 | | Router-2 | Host(foobar.traefik.com) | 26 |

위 표는 Router-1이 Router-2보다 우선순위가 높다는 걸 보여줘요.

이 문제를 해결하려면 우선순위를 설정해야 해요.

경로 중복을 피하기 위해 라우트는 기본적으로 규칙 길이를 기준으로 내림차순 정렬돼요. 우선순위는 규칙의 길이와 정확히 같으므로, 길이가 가장 긴 규칙이 가장 높은 우선순위를 가져요.

우선순위 값 0은 무시돼요: priority: 0은 기본 규칙 길이 정렬을 사용한다는 뜻이에요.

음수 우선순위 값도 지원돼요.

Traefik은 내부 라우터를 위해 일정 범위의 우선순위를 예약해 두는데, 사용자 정의 라우터 우선순위의 최대 값은 다음과 같아요:

  • 32-bit 플랫폼: (MaxInt32 - 1000) = 2147482647

  • 64-bit 플랫폼: (MaxInt64 - 1000) = 9223372036854774807

Provider 우선순위(precedence)

서로 다른 provider의 두 라우트가 같은 숫자 우선순위를 공유할 때, Traefik은 어떤 라우트가 우선하는지 결정하기 위해 providers.precedence 인스톨 구성 옵션을 사용해요. precedence에 먼저 나열된 provider가 동점에서 이겨요.

Example (예시)

Structured (YAML)

## Dynamic configuration
http:
  routers:
    Router-1:
      rule: "HostRegexp(`[a-z]+\\\\.traefik\\\\.com`)"
      entryPoints:
      - "web"
      service: service-1
      priority: 1
    Router-2:
      rule: "Host(`foobar.traefik.com`)"
      entryPoints:
      - "web"
      priority: 2
      service: service-2

Structured (TOML)

## Dynamic configuration
[http.routers]
  [http.routers.Router-1]
    rule = "HostRegexp(`[a-z]+\\\\.traefik\\\\.com`)"
    entryPoints = ["web"]
    service = "service-1"
    priority = 1
  [http.routers.Router-2]
    rule = "Host(`foobar.traefik.com`)"
    entryPoints = ["web"]
    priority = 2
    service = "service-2"

Labels

labels:
  - "traefik.http.routers.Router-1.rule=HostRegexp(`[a-z]+\\\\.traefik\\\\.com`)"
  - "traefik.http.routers.Router-1.entryPoints=web"
  - "traefik.http.routers.Router-1.service=service-1"
  - "traefik.http.routers.Router-1.priority=1"
  - "traefik.http.routers.Router-2.rule=Host(`foobar.traefik.com`)"
  - "traefik.http.routers.Router-2.entryPoints=web"
  - "traefik.http.routers.Router-2.service=service-2"
  - "traefik.http.routers.Router-2.priority=2"

Tags

  {
    // ...
    "Tags": [
      "traefik.http.routers.Router-1.rule=HostRegexp(`[a-z]+\\\\.traefik\\\\.com`)",
      "traefik.http.routers.Router-1.entryPoints=web",
      "traefik.http.routers.Router-1.service=service-1",
      "traefik.http.routers.Router-1.priority=1"
      "traefik.http.routers.Router-2.rule=Host(`foobar.traefik.com`)",
      "traefik.http.routers.Router-2.entryPoints=web",
      "traefik.http.routers.Router-2.service=service-2",
      "traefik.http.routers.Router-2.priority=2"
    ]
  }

위 예시에서 우선순위는 Router-2가 foobar.traefik.com 호스트를 가진 요청을 처리하도록 설정됐어요.

더 알아보기 (Learn more)