쿠버네티스 노드 보호
Calico가 관리하는 호스트 엔드포인트로 쿠버네티스 노드 인터페이스를 보호하는 방법을 설명해요. 네트워크 정책을 pod뿐 아니라 노드 자체로 확장할 수 있어요. 자동 호스트 엔드포인트 활성화부터 노드별 정책 적용, 실전 튜토리얼까지 순서대로 다뤄요.
출처: 문서
본문
큰 그림
Calico가 관리하는 호스트 엔드포인트로 쿠버네티스 노드를 보호해요.
가치
Calico는 쿠버네티스 노드의 호스트 엔드포인트를 자동으로 만들 수 있어요. 클러스터가 변해도 호스트 엔드포인트 수명 주기를 Calico가 관리하므로 노드가 항상 정책으로 보호받아요.
개념
호스트 엔드포인트
각 호스트는 외부와 통신하는 네트워크 인터페이스를 하나 이상 갖고 있어요. Calico에서는 이 인터페이스를 호스트 엔드포인트로 표현하고 네트워크 정책으로 보호할 수 있어요.
Calico 호스트 엔드포인트에는 레이블을 붙일 수 있고, 워크로드 엔드포인트의 레이블과 똑같이 동작해요. 네트워크 정책 규칙은 레이블 셀렉터로 워크로드와 호스트 엔드포인트 모두에 적용될 수 있어요.
자동 호스트 엔드포인트는 호스트의 모든 인터페이스(즉 Linux에서 호스트 네트워크 네임스페이스의 모든 인터페이스)를 보호해요. interfaceName: "*"로 만들어져요.
자동 호스트 엔드포인트
Calico는 각 노드에 와일드카드 호스트 엔드포인트를 만들고, 이 엔드포인트는 해당 노드와 같은 레이블과 IP 주소를 담고 있어요. Calico는 주기적인 동기화로 이 관리형 호스트 엔드포인트가 노드와 같은 레이블과 IP를 유지하도록 보장해요. 그래서 시간이 지나 노드의 IP나 레이블이 바뀌어도, 이 자동 호스트 엔드포인트를 대상으로 하는 정책은 노드를 선택하도록 만들어 둔 정책과 함께 정상 동작해요.
Calico는 사용자가 지정한 템플릿을 바탕으로 노드의 커스텀 호스트 엔드포인트를 만들고 관리할 수도 있어요. 이러면 호스트 엔드포인트에 어떤 인터페이스를 포함할지, 어떤 노드에 만들지 세밀하게 조정할 수 있어요. 호스트 엔드포인트 템플릿은 KubeControllersConfig를 갱신해서 활성화해요.
자동 호스트 엔드포인트는 projectcalico.org/created-by: calico-kube-controllers 레이블로 다른 호스트 엔드포인트와 구분돼요. 기본 KubeControllersConfiguration 리소스를 구성해서 자동 호스트 엔드포인트를 켜거나 끌 수 있어요.
시작하기 전에
calicoctl이 설치된 실행 중인 Calico 클러스터가 필요해요.
방법
자동 호스트 엔드포인트 활성화
자동 호스트 엔드포인트를 활성화하려면 기본 KubeControllersConfiguration 인스턴스를 편집하고 spec.controllers.node.hostEndpoint.autoCreate를 true로 설정하세요:
calicoctl patch kubecontrollersconfiguration default --patch='{"spec": {"controllers": {"node": {"hostEndpoint": {"autoCreate": "Enabled"}}}}}'
성공하면 클러스터의 각 노드에 호스트 엔드포인트가 만들어져요:
calicoctl get heps -owide
출력은 다음과 비슷할 거예요:
calicoctl get heps -owide
NAME NODE INTERFACE IPS PROFILES
ip-172-16-101-147.us-west-2.compute.internal-auto-hep ip-172-16-101-147.us-west-2.compute.internal * 172.16.101.147,192.168.228.128 projectcalico-default-allow
ip-172-16-101-54.us-west-2.compute.internal-auto-hep ip-172-16-101-54.us-west-2.compute.internal * 172.16.101.54,192.168.107.128 projectcalico-default-allow
ip-172-16-101-79.us-west-2.compute.internal-auto-hep ip-172-16-101-79.us-west-2.compute.internal * 172.16.101.79,192.168.91.64 projectcalico-default-allow
ip-172-16-101-9.us-west-2.compute.internal-auto-hep ip-172-16-101-9.us-west-2.compute.internal * 172.16.101.9,192.168.71.192 projectcalico-default-allow
ip-172-16-102-63.us-west-2.compute.internal-auto-hep ip-172-16-102-63.us-west-2.compute.internal * 172.16.102.63,192.168.108.192 projectcalico-default-allow
호스트 엔드포인트에 커스텀 템플릿 사용
템플릿 HostEndpoints는 맞춤형 자동 HostEndpoint 생성을 가능하게 해 줘요. spec.controllers.node.hostEndpoint.templates에 정의하고, 특정 노드나 인터페이스 조건을 기준으로 만들 수 있어서 기본 자동 HostEndpoints보다 커스터마이징 폭이 넓어요.
템플릿 호스트 엔드포인트를 활성화하고 기본 호스트 엔드포인트를 비활성화하려면, 기본 KubeControllersConfiguration 인스턴스를 편집하고 spec.controllers.node.hostEndpoint.templates에 커스텀 템플릿 스펙을 넣으세요.
-
커스텀 템플릿 스펙을 만들어요:
cat << 'EOF' > hostEndpointTemplate.yaml spec: controllers: node: hostEndpoint: autoCreate: Enabled createDefaultHostEndpoint: Disabled templates: - generateName: custom-host-endpoint interfaceCIDRs: - 192.168.0.0/16 nodeSelector: "!has(node-role.kubernetes.io/control-plane)" labels: custom-label: template-host-endpoint annotations: custom-annotation: template-host-endpoint EOF -
템플릿 스펙을
kubecontrollersconfiguration리소스에 병합해요:kubectl patch kubecontrollersconfiguration default --patch-file hostEndpointTemplate.yaml --type merge -
kubecontrollersconfiguration리소스가 갱신됐는지 확인해요:kubectl get kubecontrollersconfiguration default -o yaml출력은 다음과 비슷해야 해요:
apiVersion: projectcalico.org/v3 kind: KubeControllersConfiguration metadata: name: default spec: controllers: node: hostEndpoint: autoCreate: Enabled createDefaultHostEndpoint: Enabled templates: - generateName: custom-host-endpoint interfaceCIDRs: - 192.168.0.0/16 labels: custom-label: template-host-endpoint annotations: custom-annotation: template-host-endpoint -
성공하면 노드 셀렉터에 맞는 노드에 호스트 엔드포인트가 만들어져요:
kubectl get heps -o=custom-columns='NAME:.metadata.name,NODE:.spec.node,INTERFACES:.spec.interfaceName,IPS:.spec.expectedIPs,PROFILES:.spec.profiles'출력은 다음과 비슷할 거예요:
NAME NODE INTERFACE IPS PROFILES 192.168.83.64.compute.internal-custom-host-endpoint-auto-hep 192.168.83.64.compute.internal 192.168.83.64 projectcalico-default-allow 192.168.0.128.compute.internal-custom-host-endpoint-auto-hep 192.168.0.128.compute.internal 192.168.0.128 projectcalico-default-allow 192.168.157.128.compute.internal-custom-host-endpoint-auto-hep 192.168.157.128.compute.internal 192.168.157.128 projectcalico-default-allow
자동 호스트 엔드포인트에 네트워크 정책 적용
모든 쿠버네티스 노드를 대상으로 하는 정책을 적용하려면 먼저 노드에 레이블을 추가하세요. 이 레이블은 자동 호스트 엔드포인트로 동기화돼요.
예를 들어 모든 노드와 그 호스트 엔드포인트에 kubernetes-host 레이블을 추가하려면:
kubectl label nodes --all kubernetes-host=
그리고 정책 스니펫 예시:
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: all-nodes-policy
spec:
selector: has(kubernetes-host)
#<rest of the policy>
특정 호스트 엔드포인트 집합(그에 대응하는 쿠버네티스 노드)을 선택하려면, 그 집합에 고유한 레이블을 선택하는 정책 셀렉터를 사용하세요. 예를 들어 node1, node2라는 이름의 노드에 environment=dev 레이블을 추가하고 싶다면:
kubectl label node node1 environment=dev
kubectl label node node2 environment=dev
레이블이 준비되고 자동 호스트 엔드포인트가 활성화되어 있으면 node1과 node2의 호스트 엔드포인트가 environment=dev 레이블로 갱신돼요. 이제 셀렉터를 조합해서 그 노드 집합을 선택하는 정책을 쓸 수 있어요:
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: some-nodes-policy
spec:
selector: has(kubernetes-host) && environment == 'dev'
#<rest of the policy>
튜토리얼
이 튜토리얼은 쿠버네티스 노드의 ingress를 SSH와 쿠버네티스 동작에 필요한 포트만 허용하도록 잠가요. 정책 두 개를 적용해요. 컨트롤 플레인 노드용 하나, 워커 노드용 하나예요.
SECONDARY: 참고: 이 튜토리얼은 AWS에서 kubeadm v1.18.2로 만든 클러스터, "stacked etcd" 토폴로지에서 테스트했어요. Stacked etcd 토폴로지는 etcd pod가 마스터에서 실행된다는 뜻이고, kubeadm은 기본으로 stacked etcd를 사용해요. 쿠버네티스 클러스터가 다른 플랫폼에 있거나, 다른 쿠버네티스 변종을 쓰거나, 외부 etcd 클러스터 토폴로지라면 클러스터의 컨트롤 플레인/워커 노드에 필요한 포트를 확인하고 이 튜토리얼의 정책을 알맞게 조정하세요.
먼저 컨트롤 플레인 노드의 ingress 트래픽을 제한해 볼게요. 아래 ingress 정책에는 규칙 세 개가 있어요. 첫 번째 규칙은 어디서든 API 서버 포트 접근을 허용해요. 두 번째 규칙은 localhost로 오가는 모든 트래픽을 허용해서 쿠버네티스가 컨트롤 플레인 프로세스에 접근할 수 있게 해 줘요. 이 컨트롤 플레인 프로세스에는 etcd 서버 클라이언트 API, 스케줄러, 컨트롤러 매니저가 있어요. 이 규칙은 kubelet API와 calico/node 헬스 체크에 대한 localhost 접근도 허용해요. 마지막 규칙은 etcd pod끼리 서로 피어링할 수 있게 하고, 마스터가 서로의 kubelet API에 접근할 수 있게 해 줘요.
failsafe 포트를 수정하지 않았다면 이 정책을 적용한 뒤에도 노드로의 SSH 접근은 유지돼요. 이제 쿠버네티스 마스터의 ingress 정책을 적용하세요:
calicoctl apply -f - << EOF
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: ingress-k8s-masters
spec:
selector: has(node-role.kubernetes.io/control-plane)
# This rule allows ingress to the Kubernetes API server.
ingress:
- action: Allow
protocol: TCP
destination:
ports:
# kube API server
- 6443
# This rule allows all traffic to localhost.
- action: Allow
destination:
nets:
- 127.0.0.0/8
# This rule is required in multi-master clusters where etcd pods are colocated with the masters.
# Allow the etcd pods on the masters to communicate with each other. 2380 is the etcd peer port.
# This rule also allows the masters to access the kubelet API on other masters (including itself).
- action: Allow
protocol: TCP
source:
selector: has(node-role.kubernetes.io/control-plane)
destination:
ports:
- 2380
- 10250
EOF
위 정책은 kubeadm이 컨트롤 플레인 노드에 설정하는 표준 node-role.kubernetes.io/control-plane 레이블을 선택한다는 점에 유의하세요.
다음으로 쿠버네티스 워커의 ingress를 제한하는 정책을 적용해야 해요. 정책을 추가하기 전에 모든 워커 노드에 레이블을 추가할게요. 이 레이블은 자동 호스트 엔드포인트에도 추가돼요. 이 튜토리얼에서는 kubernetes-worker를 쓸 거예요. 워커 노드에 레이블을 추가하는 명령 예시:
kubectl get node -l '!node-role.kubernetes.io/control-plane' -o custom-columns=NAME:.metadata.name | tail -n +2 | xargs -I{} kubectl label node {} kubernetes-worker=
워커의 ingress 정책은 규칙 두 개로 이루어져 있어요. 첫 번째 규칙은 localhost로 오가는 모든 트래픽을 허용해요. 마스터와 마찬가지로 워커 노드도 자기 localhost의 kubelet API와 calico/node 헬스 체크에 접근해야 하거든요. 두 번째 규칙은 마스터가 워커의 kubelet API에 접근할 수 있게 해 줘요. 이제 정책을 적용해요:
calicoctl apply -f - << EOF
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: ingress-k8s-workers
spec:
selector: has(kubernetes-worker)
# Allow all traffic to localhost.
ingress:
- action: Allow
destination:
nets:
- 127.0.0.0/8
# Allow only the masters access to the nodes kubelet API.
- action: Allow
protocol: TCP
source:
selector: has(node-role.kubernetes.io/control-plane)
destination:
ports:
- 10250
EOF
추가 리소스
더 알아보기 (Learn more)
자동 호스트 엔드포인트는 KubeControllersConfiguration의 autoCreate 옵션으로 켜고, 노드의 레이블이 주기적으로 동기화돼요. 노드별로 정책을 나누려면 kubernetes-host, environment 같은 레이블 셀렉터를 조합하면 돼요. 튜토리얼의 정책은 노드 ingress를 필요한 포트만 남기고 잠그는 출발점으로 쓰기 좋아요.