ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Kubernetes] 서비스, 로드 밸런스, 네트워킹 - Network Policies
    공부/데이터 2025. 2. 22. 17:06

    Kubernetes NetworkPolicy는 클러스터 내 파드 간 또는 파드과 외부 세계 간의 트래픽 흐름을 IP 주소 또는 포트 레벨(OSI 3/4 계층)에서 제어하기 위한 규칙을 정의합니다. NetworkPolicy를 사용하려면 클러스터가 NetworkPolicy 시행을 지원하는 네트워크 플러그인을 사용해야 합니다.

    핵심은 TCP, UDP, SCTP 프로토콜에 대해 IP 주소 또는 포트 레벨에서 트래픽 흐름을 제어하려면 Kubernetes NetworkPolicy를 고려할 수 있습니다. NetworkPolicy는 애플리케이션 중심의 구조로 파드가 네트워크 "엔터티"(일반적인 "엔드포인트" 및 "서비스"와 같은 Kubernetes 용어의 과용을 피하기 위해 "엔터티"라는 단어를 사용)와 어떻게 통신할 수 있는지 지정합니다. NetworkPolicy는 파드의 연결에 적용되며 다른 연결에는 관련이 없습니다.

    파드가 통신할 수 있는 엔터티는 다음 세 가지 식별자의 조합을 통해 식별됩니다

    1. 허용된 다른 파드
      1. 예외적으로 파드는 자체 접근을 차단할 수 없습니다.
    2. 허용된 Namespace
    3. IP 블록
      1. 예외적으로 파드 또는 노드의 IP 주소에 관계없이 파드가 실행 중인 노드와의 트래픽은 항상 허용됩니다.

    NetworkPolicy 정의 방법은 다음과 같습니다.

    • 파드 또는 Namespace 기반 NetworkPolicy: selector를 사용하여 어떤 트래픽이 selector와 일치하는 파드로/에서 허용되는지 지정합니다.
    • IP 기반 NetworkPolicy: IP 블록(CIDR 범위)을 기반으로 정책을 정의합니다.

    두 가지 종류의 파드 분리

    파드에는 두 가지 격리 유형이 있습니다.

    1. Egress 격리: 파드에서 나가는 연결(outbound connection)에 대한 격리
    2. Ingress 격리: 파드로 들어오는 연결(inbound connection)에 대한 격리

    "격리"는 절대적인 차단이 아닌 "일부 제한 적용"을 의미합니다. "비격리"는 해당 방향에 제한이 없음을 의미합니다. Egress와 Ingress 격리는 독립적으로 선언되며 파드 간 연결 모두에 적용됩니다.

    NetworkPolicy의 특징은 두가지가 있습니다.

    • 충돌 없음: NetworkPolicy는 충돌하지 않고 누적됩니다. 특정 파드에 특정 방향으로 하나 이상의 정책이 적용되면 해당 방향으로 파드에서 허용되는 연결은 적용 가능한 모든 정책에서 허용하는 연결의 합집합입니다. 따라서 평가 순서는 정책 결과에 영향을 미치지 않습니다.
    • 양방향 정책: 소스 파드에서 목적지 파드로의 연결이 허용되려면 소스 파드의 Egress 정책과 목적지 파드의 Ingress 정책 모두 해당 연결을 허용해야 합니다. 어느 한쪽에서 연결을 허용하지 않으면 연결은 이루어지지 않습니다.

    Egress (나가는 연결)

    파드는 기본적으로 Egress 비격리 상태이며 모든 나가는 연결이 허용됩니다. 파드를 선택하고 policyTypes에 "Egress"가 있는 NetworkPolicy가 존재하면 해당 파드는 Egress 격리 상태가 됩니다. 이러한 정책을 해당 파드의 Egress에 적용되는 정책이라고 합니다. Egress 격리된 파드에서 허용되는 연결은 해당 파드의 Egress에 적용되는 NetworkPolicy의 egress 목록에 정의된 연결뿐입니다. 허용된 연결에 대한 응답 트래픽은 암묵적으로 허용됩니다. egress 목록의 효과는 누적됩니다.

    Ingress (들어오는 연결)

    파드는 기본적으로 Ingress 비격리 상태이며 모든 들어오는 연결이 허용됩니다. 파드를 선택하고 policyTypes에 "Ingress"가 있는 NetworkPolicy가 존재하면 해당 파드는 Ingress 격리 상태가 됩니다. 이러한 정책을 해당 파드의 Ingress에 적용되는 정책이라고 합니다. Ingress 격리된 파드로 허용되는 연결은 파드가 실행 중인 노드에서의 연결과 해당 파드의 Ingress에 적용되는 NetworkPolicy의 ingress 목록에 정의된 연결뿐입니다. 허용된 연결에 대한 응답 트래픽은 암묵적으로 허용됩니다. ingress 목록의 효과는 누적됩니다.

    NetworkPolicy 리소스

    NetworkPolicy의 예는 다음과 같습니다.

    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: test-network-policy
      namespace: default
    spec:
      podSelector:
        matchLabels:
          role: db
      policyTypes:
      - Ingress
      - Egress
      ingress:
      - from:
        - ipBlock:
            cidr: 172.17.0.0/16
            except:
            - 172.17.1.0/24
        - namespaceSelector:
            matchLabels:
              project: myproject
        - podSelector:
            matchLabels:
              role: frontend
        ports:
        - protocol: TCP
          port: 6379
      egress:
      - to:
        - ipBlock:
            cidr: 10.0.0.0/24
        ports:
        - protocol: TCP
          port: 5978
    • 필수 필드: 다른 모든 Kubernetes 설정과 마찬가지로 NetworkPolicy는 apiVersion, kind, metadata 필드를 필요로 합니다. 설정 파일 작업에 대한 일반적인 정보는 "ConfigMap을 사용하도록 파드 구성" 및 "객체 관리"를 참조하세요.
    • spec: NetworkPolicy의 spec은 주어진 네임스페이스에서 특정 네트워크 정책을 정의하는 데 필요한 모든 정보를 담고 있습니다.
    • podSelector: 각 NetworkPolicy는 정책이 적용될 파드 그룹을 선택하는 podSelector를 포함합니다. 예제 정책은 "role=db" label이 있는 파드를 선택합니다. 빈 podSelector는 네임스페이스의 모든 파드를 선택합니다.
    • policyTypes: 각 NetworkPolicy는 Ingress, Egress 또는 둘 다를 포함할 수 있는 policyTypes 목록을 포함합니다. policyTypes 필드는 지정된 정책이 선택된 파드에 대한 수신 트래픽, 선택된 파드로부터의 발신 트래픽 또는 둘 다에 적용되는지 여부를 나타냅니다. NetworkPolicy에 policyTypes가 지정되지 않으면 기본적으로 Ingress는 항상 설정되고 Egress는 NetworkPolicy에 발신 규칙이 있는 경우 설정됩니다.
    • ingress: 각 NetworkPolicy는 허용된 ingress 규칙 목록을 포함할 수 있습니다. 각 규칙은 fromports 섹션 모두와 일치하는 트래픽을 허용합니다. 예제 정책에는 단일 포트에서 세 가지 소스 중 하나로부터의 트래픽과 일치하는 단일 규칙이 포함되어 있으며, 첫 번째는 ipBlock을 통해, 두 번째는 namespaceSelector를 통해, 세 번째는 podSelector를 통해 지정됩니다.
    • egress: 각 NetworkPolicy는 허용된 egress 규칙 목록을 포함할 수 있습니다. 각 규칙은 toports 섹션 모두와 일치하는 트래픽을 허용합니다. 예제 정책에는 10.0.0.0/24의 모든 대상에 있는 단일 포트의 트래픽과 일치하는 단일 규칙이 포함되어 있습니다.

    따라서 예제 NetworkPolicy는 다음을 수행합니다.

    • default 네임스페이스의 role=db 파드를 수신 및 발신 트래픽 모두에 대해 격리합니다 (이미 격리되지 않은 경우).
    • (ingress 규칙) 다음으로부터 TCP 포트 6379에서 role=db label이 있는 default 네임스페이스의 모든 파드에 대한 연결을 허용합니다.
      • role=frontend label이 있는 default 네임스페이스의 모든 파드
      • project=myproject label이 있는 네임스페이스의 모든 파드
      • IP 주소 범위 172.17.0.0 ~ 172.17.0.255172.17.2.0 ~ 172.17.255.255 (즉, 172.17.1.0/24를 제외한 172.17.0.0/16 전체)
    • (egress 규칙) role=db label이 있는 default 네임스페이스의 모든 파드에서 TCP 포트 5978에서 CIDR 10.0.0.0/24로의 연결을 허용합니다.

    to 와 from selector의 동작

    Ingress의 from 섹션이나 Egress의 to 섹션에서 사용할 수 있는 네 가지 종류의 selector가 있습니다.

    1. podSelector: NetworkPolicy와 동일한 Namespace 내의 특정 파드를 선택하여 Ingress 소스 또는 Egress 대상으로 허용합니다.
    2. namespaceSelector: 모든 파드가 Ingress 소스 또는 Egress 대상으로 허용되어야 하는 특정 Namespace를 선택합니다.
    3. namespaceSelectorpodSelector: namespaceSelectorpodSelector를 모두 지정하는 단일 to/from 항목은 특정 Namespace 내의 특정 파드를 선택합니다. 올바른 YAML 구문을 사용해야 합니다.
    4. ipBlock: ipBlock은 수신 소스 또는 발신 대상으로 허용할 특정 IP CIDR 범위를 선택합니다. 파드 IP는 임시적이고 예측할 수 없으므로(파드 IP가 바뀔 수 있기 때문에) 클러스터 외부 IP여야 합니다.
    ...
      ingress:
      - from:
        - namespaceSelector:
            matchLabels:
              user: alice
          podSelector:
            matchLabels:
              role: client
      ...

    이 정책은 user=alice label이 있는 네임스페이스의 role=client label이 있는 파드로부터의 연결을 허용하는 단일 from 요소를 포함합니다. 그러나 다음 정책은 다릅니다.

      ...
      ingress:
      - from:
        - namespaceSelector:
            matchLabels:
              user: alice
        - podSelector:
            matchLabels:
              role: client
      ...

    이 정책은 from 배열에 두 개의 요소를 포함하며 role=client label이 있는 로컬 네임스페이스의 파드 또는 user=alice label이 있는 모든 네임스페이스의 모든 파드로부터의 연결을 허용합니다.

    헷갈릴 때는 kubectl describe 로 확인하는 것이 좋습니다.

    기본 정책

    기본적으로 네임스페이스에 정책이 존재하지 않으면 해당 네임스페이스의 파드 간의 모든 수신 및 발신 트래픽이 허용됩니다. 다음 예제에서는 해당 네임스페이스의 기본 동작을 변경하는 방법을 보여줍니다.

    모든 inbound 트래픽 거부

    ---
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: default-deny-ingress
    spec:
      podSelector: {}
      policyTypes:
      - Ingress

    모든 inbound 트래픽 허용

    ---
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-all-ingress
    spec:
      podSelector: {}
      ingress:
      - {}
      policyTypes:
      - Ingress

    모든 outbound 트래픽 거부

    ---
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: default-deny-egress
    spec:
      podSelector: {}
      policyTypes:
      - Egress

    모든 outbound 트래픽 허용

    ---
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-all-egress
    spec:
      podSelector: {}
      egress:
      - {}
      policyTypes:
      - Egress

    네트워크 트래픽 필터링

    NetworkPolicy는 layer4 연결 (TCP, UDP, SCTP)에 대해 정의됩니다. 다른 모든 프로토콜의 경우 동작은 네트워크 플러그인에 따라 달라질 수 있습니다.

    "모두 거부" 네트워크 정책이 정의되면 TCP, UDP 및 SCTP 연결만 거부되는 것이 보장됩니다. ARP 또는 ICMP와 같은 다른 프로토콜의 경우 동작이 정의되지 않습니다. 허용 규칙에도 동일하게 적용됩니다. 특정 파드가 수신 소스 또는 발신 대상으로 허용되는 경우 (예: ICMP 패킷)에 어떤 일이 발생하는지는 정의되지 않습니다. ICMP와 같은 프로토콜은 일부 네트워크 플러그인에서 허용될 수 있고 다른 플러그인에서는 거부될 수 있습니다.

    포트 범위 필터링

    NetworkPolicy를 작성할 때 단일 포트 대신 포트 범위를 대상으로 지정할 수 있습니다. 이는 다음 예와 같이 endPort 필드를 사용하여 달성할 수 있습니다.

    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: multi-port-egress
      namespace: default
    spec:
      podSelector:
        matchLabels:
          role: db
      policyTypes:
        - Egress
      egress:
        - to:
            - ipBlock:
                cidr: 10.0.0.0/24
          ports:
            - protocol: TCP
              port: 32000
              endPort: 32768

    위 규칙은 default 네임스페이스의 role=db label이 있는 모든 파드가 목적지 포트의 32000 ~ 32768 범위 내에 있는 경우 TCP를 통해 10.0.0.0/24 범위 내의 모든 IP와 통신할 수 있도록 허용합니다.

    이 필드를 사용할 때 다음 제한 사항이 적용됩니다.

    • endPort 필드는 port 필드와 같거나 커야 합니다.
    • endPortport가 정의된 경우에만 정의할 수 있습니다.
    • 두 포트 모두 숫자여야 합니다.

    label로 여러 네임스페이스 필터링

    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: egress-namespaces
    spec:
      podSelector:
        matchLabels:
          app: myapp
      policyTypes:
      - Egress
      egress:
      - to:
        - namespaceSelector:
            matchExpressions:
            - key: namespace
              operator: In
              values: ["frontend", "backend"]

    NetworkPolicy에서 네임스페이스의 이름을 직접 지정하는 것은 불가능합니다. label을 기반으로 네임스페이스를 선택하려면 matchLabels 또는 matchExpressions가 있는 namespaceSelector를 사용해야 합니다.

    파드 라이프사이클

    새로운 NetworkPolicy 객체가 생성되면 네트워크 플러그인이 이를 처리하는 데 시간이 걸릴 수 있습니다. NetworkPolicy 처리 완료 전에 NetworkPolicy의 영향을 받는 파드가 생성되면 해당 파드는 보호되지 않은 상태로 시작될 수 있으며 NetworkPolicy 처리가 완료된 후에 격리 규칙이 적용됩니다.

    NetworkPolicy 처리 후 주어진 NetworkPolicy의 영향을 받는 새로 생성된 모든 파드는 시작되기 전에 격리됩니다. NetworkPolicy 구현은 파드 내의 컨테이너가 시작되는 순간부터 파드 수명 주기 전체에 걸쳐 필터링이 효과적으로 이루어지도록 해야 합니다. 파드 레벨에서 적용되기 때문에 NetworkPolicy는 init 컨테이너, 사이드카 컨테이너 및 일반 컨테이너에 동일하게 적용됩니다.

    모든 생성된 NetworkPolicy는 결국 네트워크 플러그인에 의해 처리되지만 Kubernetes API에서 정확히 언제 발생하는지 알 수 있는 방법은 없습니다. 따라서 파드는 예상과 다른 네트워크 연결 상태로 시작될 수 있는 상황에 대비해야 합니다. 파드가 시작되기 전에 특정 대상에 연결할 수 있는지 확인해야 하는 경우, init 컨테이너를 사용하여 kubelet이 애플리케이션 컨테이너를 시작하기 전에 해당 대상에 연결할 수 있을 때까지 기다릴 수 있습니다. 모든 NetworkPolicy는 결국 선택된 모든 파드에 적용됩니다. 네트워크 플러그인이 NetworkPolicy를 분산 방식으로 구현할 수 있기 때문에 파드는 처음 생성될 때 또는 파드 또는 정책이 변경될 때 네트워크 정책에 대해 약간 불일치된 뷰를 볼 수 있습니다. 예를 들어, 노드 1의 파드 A와 노드 2의 파드 B 모두에 연결할 수 있어야 하는 새로 생성된 파드는 파드 A에는 즉시 연결할 수 있지만 몇 초 후까지 파드 B에는 연결할 수 없다는 것을 알 수 있습니다.

    정리

    • NetworkPolicy는 기본적으로 Kubernetes 클러스터 내부의 파드 간 통신을 제어하는 데 사용
    • 외부에서 클러스터 내부의 파드로 들어오는 트래픽(Ingress) 또는 클러스터 내부의 파드에서 외부로 나가는 트래픽(Egress)을 제어하는 데에도 사용할 순 있음
    • 단 NetworkPolicy는 클러스터 레벨에서 적용되는 정책이므로 외부 트래픽 통제는 LoadBalancer 서비스와 함께 사용해야 좋음

    레퍼런스

    https://kubernetes.io/docs/concepts/services-networking/network-policies/

    댓글