ServiceEntry 가시성

ServiceEntry 가시성 (ServiceEntry visibility)

ServiceEntry는 Istio의 내부 서비스 레지스트리에 서비스를 추가해요. 기본적으로 ServiceEntry는 메시 안의 모든 워크로드에 보여요. 어떤 네임스페이스에서든 ServiceEntry를 만들 수 있는 사람은 어떤 호스트 이름 — 예를 들어 example.com — 도 정의하고, 전체 메시가 그것을 해석하고 라우팅하는 방식에 영향을 줄 수 있어요.

출처: Istio 문서

본문

exportTo 필드는 이것을 보호하지 못해요. 그것은 ServiceEntry 작성자 자신이 선언하기 때문이에요. 서비스 소유자가 자신이 게시하는 것의 범위를 정하게 해주지만, 확고한 제약을 두지는 않아요. 역사적으로 메시 관리자는 ValidatingAdmissionPolicy나 webhook 같은 외부 승인 제어로 ServiceEntry 생성을 제한해야 했어요.

Istio 1.31부터 serviceEntryVisibility 메시 구성 설정이 메시 관리자에게 그 제어를 한 곳에서 제공해요. 앰비언트 데이터 플레인(ztunnel과 웨이포인트)은 구성될 때마다 가시성을 존중해요. 사이드카와 게이트웨이는 옵트인할 수 있어요. 이 기능은 구성하지 않으면 비활성화돼요. serviceEntryVisibility가 설정되지 않으면 아무것도 바뀌지 않아요.

이 설계는 의도적으로 친숙한 쿠버네티스 패턴을 반영해요. RoleBinding은 자기 네임스페이스 안에서만 효과가 있고, ClusterRoleBinding으로 클러스터 전체 효과를 만드는 것은 클러스터 관리자에게 예약되어 있어요. serviceEntryVisibility는 메시 관리자가 같은 모델을 ServiceEntry에 적용할 수 있게 해줘요. defaultVisibility: NAMESPACE를 설정함으로써, 관리자는 모든 ServiceEntry가 다른 네임스페이스 범위 리소스처럼 동작하게 만들고, ServiceEntry의 자기 네임스페이스 너머의 가시성은 관리자가 명시적으로 부여하는 능력이 돼요.

가시성이 어떻게 해석되는지 (How visibility is resolved)

serviceEntryVisibility가 구성되면 istiod가 각 ServiceEntry에 대한 가시성을 해석해요:

  1. policies 목록이 순서대로 평가돼요. matchingRules가 모두 일치하는(AND 의미론) 첫 번째 정책이 가시성을 결정해요.
  2. 일치하는 정책이 없으면 defaultVisibility가 적용돼요.

오늘날 유일한 매칭 규칙은 namespaceSelector로, ServiceEntry가 정의된 네임스페이스의 레이블에 대해 평가되는 표준 쿠버네티스 레이블 선택자예요.

ServiceEntry는 세 가지 가시성 중 하나로 해석돼요:

가시성 의미
PUBLIC 이 컨트롤 플레인에 연결된 모든 워크로드에게 보임. 이것이 기존 동작이며, defaultVisibility가 설정되지 않을 때의 기본값이에요.
NAMESPACE ServiceEntry가 정의된 네임스페이스 안에서만 보임.
NONE 아무에게도 안 보임. ServiceEntry는 작성될 수 있지만, Istio는 그것에 대한 데이터 플레인을 구성하지 않아요. 특정 부류의 ServiceEntry를 명시적으로 금지할 때 유용해요.

전체 필드 문서는 메시 구성 참조를 참조하세요.

가시성 구성하기 (Configure visibility)

다음 구성은 모든 ServiceEntry를 자기 네임스페이스에만 비공개로 유지하면서, 오직 메시 관리자만 쓸 수 있는 네임스페이스인 istio-system의 ServiceEntry 리소스가 전체 메시에 게시되도록 허용해요:

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  meshConfig:
    serviceEntryVisibility:
      # ServiceEntries matching no policy below stay in their own namespace.
      defaultVisibility: NAMESPACE
      policies:
        # ServiceEntries in istio-system are visible mesh-wide.
        - visibility: PUBLIC
          matchingRules:
            - namespaceSelector:
                matchLabels:
                  kubernetes.io/metadata.name: istio-system

정책은 네임스페이스 그룹에 가시성을 부여할 수도 있어요. 예를 들어 trusted: "true" 네임스페이스 레이블과 매칭하는 정책은, 네임스페이스에 레이블을 붙여서 메시 전체 ServiceEntry 리소스를 게시하는 능력을 위임할 수 있게 해줘요.

앰비언트 모드에서의 가시성 (Visibility in ambient mode)

serviceEntryVisibility가 구성되면 앰비언트 데이터 플레인이 항상 그것을 존중해요. istiod가 각 ServiceEntry의 가시성을 해석해 서비스 정의와 함께 배포하고, ztunnel은 요청하는 워크로드의 네임스페이스에 기반해 클라이언트 측에서 적용해요:

  • PUBLIC 서비스는 이전과 정확히 동일하게 동작해요.
  • NAMESPACE 서비스는 자기 네임스페이스의 워크로드가 발견하고 해석할 수 있어요. 다른 모든 네임스페이스의 워크로드에게는 ServiceEntry가 존재하지 않는 것과 같아요.
  • NONE 서비스는 어떤 데이터 플레인에도 전혀 전달되지 않아요.

숨겨짐은 차단이 아니라 부재 (Hidden means absent, not blocked)

이것은 의도적이에요. ztunnel이 숨겨진 호스트 이름에 대한 DNS 조회를 실패시켰다면, 한 네임스페이스에서 example.com을 주장하는 ServiceEntry가 전체 메시의 example.com을 깨뜨릴 텐데, 바로 이 기능이 제어하려는 문제예요. 구체적으로 ServiceEntry 네임스페이스 밖의 클라이언트에 대해:

  • DNS: 숨겨진 호스트 이름에 대한 조회는 업스트림 해석기로 전달되어 실제 답(또는 공개적으로 존재하지 않는 이름이라면 실제 NXDOMAIN)을 반환해요.
  • 주소: 숨겨진 ServiceEntry만 주장하는 IP 주소로의 트래픽은 알 수 없는 트래픽으로 취급되어 원래 목적지로 통과돼요.
  • 공유 호스트 이름: 숨겨진 ServiceEntry와 PUBLIC 것이 같은 호스트 이름을 정의하면, 숨겨진 항목의 네임스페이스 밖의 클라이언트는 항상 PUBLIC 정의가 서빙해요.

웨이포인트 (Waypoints)

다른 네임스페이스의 웨이포인트를 NAMESPACE-가시성 ServiceEntry에 부착하면 트래픽과 구성이 네임스페이스 경계 밖으로 빠져나갈 수 있으므로, 컨트롤 플레인은 그것들에 대한 크로스-네임스페이스 웨이포인트 바인딩을 거부해요. 거부는 ServiceEntry 상태에 istio.io/WaypointBound: False 조건과 CrossNamespaceWaypointForbidden 이유로 보고돼요.

같은 네임스페이스의 웨이포인트 바인딩은 정상적으로 동작하며, PUBLIC ServiceEntry 리소스는 영향을 받지 않아요.

사이드카와 게이트웨이로 가시성 확장하기 (Extending visibility to sidecars and gateways)

사이드카 모드에서 서비스 소유자는 이미 exportTo로 ServiceEntry 리소스의 범위를 정해요. 그래서 가시성 존중은 사이드카에 대해 옵트인이에요. 이는 동작하는 사이드카 배포를 바꾸지 않고 앰비언트 마이그레이션 중에 점진적으로 도입할 수 있게 해줘요:

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  meshConfig:
    serviceEntryVisibility:
      defaultVisibility: NAMESPACE
      applyToSidecars: true

applyToSidecars가 활성화되면, 해석된 가시성이 ServiceEntry의 exportTo에 대한 상한(ceiling)으로 작용해요. 유효 범위는 선언된 exportTo와 해석된 가시성의 교집합이에요. 필드 이름에도 불구하고, 이것은 인그레스·이그레스 게이트웨이를 포함한 모든 Envoy 기반 프록시에 적용돼요.

가시성은 exportTo가 선언한 것을 좁힐 수 있지만, 결코 넓히지 않아요. 사이드카 모드에서 exportTo는 istiod가 각 프록시에 보내는 구성의 양을 제어하는 구성 범위 지정 메커니즘이에요. 그것을 더 넓은 가시성과 일치하도록 넓히면, 소유자가 의도적으로 범위를 제외했던 프록시로 ServiceEntry 구성이 다시 밀려들 거예요. 그래서 가시성은 결코 상한일 뿐이에요. exportTo가 가시성보다 더 좁은 ServiceEntry는 더 좁은 범위를 유지해요.

가시성 검증하기 (Verify visibility)

serviceEntryVisibility가 구성되면, istiod는 데이터 플레인이 받은 가시성을 istio.io/VisibilityApplied 유형의 ServiceEntry 상태 조건으로 보고하며, 이유(reason)가 적용된 가시성을 나타내요:

$ kubectl get serviceentry my-service -n team-a -o jsonpath='{.status.conditions[?(@.type=="istio.io/VisibilityApplied")].reason}'
Namespace

조건의 status는 항상 True예요. reason 필드가 해석된 가시성(Public 또는 Namespace)을 담아요. serviceEntryVisibility가 구성되지 않으면 조건이 기록되지 않아요.

ztunnel이 알고 있는 모든 서비스에 적용하고 있는 가시성을 검사할 수도 있어요. istioctl ztunnel-config service의 JSON과 YAML 출력에는 visibility 필드가 포함돼요:

$ istioctl ztunnel-config service --service-namespace team-a -o yaml

쿠버네티스 Service는 항상 Public을 보고해요. ServiceEntry 기반 서비스는 해석된 가시성을 보고해요. 또한 ztunnel은 해석 과정에서 클라이언트로부터 서비스를 숨길 때마다 debug 수준으로 로그를 남겨요.

가시성이 하지 않는 것 (What visibility does not do)

  • 가시성은 인가가 아니에요. 가시성은 클라이언트가 서비스를 발견하고 해석할 수 있는지 제어해요. 인바운드 요청이 허용되는지 여부를 결정하지는 않아요. 어떤 클라이언트가 워크로드에 접근할 수 있는지 제어하려면 AuthorizationPolicy를 사용하세요 — Layer 4 보안 정책 참조.
  • 가시성은 비밀 유지가 아니에요. ServiceEntry의 가시성을 줄이는 것은 클라이언트 워크로드가 해석하는 것을 규제해요. 서비스의 존재를 숨기지는 않아요. 리소스는 RBAC에 따라 쿠버네티스 API를 통해 계속 읽을 수 있고, 서비스는 istioctl ztunnel-config service 같은 데이터 플레인 구성 덤프에도 계속 나타나요.
  • ServiceEntry에만 적용돼요. 쿠버네티스 Service는 앰비언트 모드에서 항상 메시 전체에 보여요.
  • 감사 또는 경고 전용 모드가 없어요. 구성되면 가시성이 항상 적용돼요.

함께 보기 (See also)

더 알아보기 (Learn more)