좋은 기회로 EKS 워크샵 스터디에 참여해서 전체 과정과 과제를 남겨보려고 합니다.
1주차 주제
- EKS 아키텍처
- Public/Private endpoint 사용 별 Cluster endpoint로의 접근 방식
- Control Plane <-> Data Plane 통신 경로
- 클러스터 생성 실습
- CloudFormation을 통한 bastion host, VPC 배포
- eksctl cli를 사용한 클러스터 생성
EKS 아키텍처
EKS Cluster 생성 시 Managed VPC에 Control Plane이 배포되고 통신을 위한 eni가 설정한 VPC의 각 subnet에 생성됩니다.
EKS API server endpoint(NLB)에 연결하여 kubectl
명령어를 수행할 수 있습니다.
EKS Control Plane은 아래 컴포넌트로 이루어져 있습니다.
- API server
- 컨트롤러
- 스케쥴러
- etcd
이 중 etcd는 별도 노드로 배포됩니다(external etcd).
통신 방식
1) public endpoint만 사용 시 :
kubectl (client) <-> API server endpoint뿐만 아니라 kube-proxy <-> API Server 간 통신도 인터넷을 통하는 것을 볼 수 있습니다.
2) public + private endpoint 사용 시 :
kubectl (client) <-> API server endpoint는 인터넷 kube-proxy <-> API server는 Private 도메인을 활용해 VPC 간 통신합니다.
3) private endpoint만 사용 시 :
kubectl (client) <-> API server 통신이 EKS 소유 eni를 통해 이루어집니다.
[실습] 클러스터 생성하기
CloudFormation yaml파일을 통해 VPC와 bastion host 등을 생성한 후에 eksctl 명령어를 사용하여 클러스터를 생성합니다.
클러스터 생성 전에 dry-run을 통해서 어떤 설정으로 클러스터가 생길 예정인지 확인할 수 있습니다.
이번 실습에서는 NAT G/W 비용을 절약하기 위해 public subnet에 클러스터를 만들었습니다.
## dry-run
eksctl create cluster --name $CLUSTER_NAME --region=$AWS_DEFAULT_REGION --nodegroup-name=$CLUSTER_NAME-nodegroup --node-type=t3.medium \
> --node-volume-size=30 --vpc-public-subnets "$PubSubnet1,$PubSubnet2" --version 1.24 --ssh-access --external-dns-access --dry-run | yh
apiVersion: eksctl.io/v1alpha5
cloudWatch:
clusterLogging: {}
iam:
vpcResourceControllerPolicy: true
withOIDC: false
kind: ClusterConfig
kubernetesNetworkConfig:
ipFamily: IPv4
managedNodeGroups:
- amiFamily: AmazonLinux2
desiredCapacity: 2
disableIMDSv1: false
disablePodIMDS: false
iam:
withAddonPolicies:
albIngress: false
appMesh: false
appMeshPreview: false
autoScaler: false
awsLoadBalancerController: false
certManager: false
cloudWatch: false
ebs: false
efs: false
externalDNS: true
fsx: false
imageBuilder: false
xRay: false
instanceSelector: {}
instanceType: t3.medium
labels:
alpha.eksctl.io/cluster-name: myeks
alpha.eksctl.io/nodegroup-name: myeks-nodegroup
maxSize: 2
minSize: 2
name: myeks-nodegroup
privateNetworking: false
releaseVersion: ""
securityGroups:
withLocal: null
withShared: null
ssh:
allow: true
publicKeyPath: ~/.ssh/id_rsa.pub
tags:
alpha.eksctl.io/nodegroup-name: myeks-nodegroup
alpha.eksctl.io/nodegroup-type: managed
volumeIOPS: 3000
volumeSize: 30
volumeThroughput: 125
volumeType: gp3
metadata:
name: myeks
region: ap-northeast-2
version: "1.24"
privateCluster:
enabled: false
skipEndpointCreation: false
vpc:
autoAllocateIPv6: false
cidr: 192.168.0.0/16
clusterEndpoints:
privateAccess: false
publicAccess: true
id: vpc-07ab2103bf53ee970
manageSharedNodeSecurityGroupRules: true
nat:
gateway: Disable
subnets:
public:
ap-northeast-2a:
az: ap-northeast-2a
cidr: 192.168.1.0/24
id: subnet-09d11f7d57d0df9ad
ap-northeast-2c:
az: ap-northeast-2c
cidr: 192.168.2.0/24
id: subnet-04c78c245f54c56ab
## 클러스터 생성
eksctl create cluster --name $CLUSTER_NAME --region=$AWS_DEFAULT_REGION --nodegroup-name=$CLUSTER_NAME-nodegroup --node-type=t3.medium \
--node-volume-size=30 --vpc-public-subnets "$PubSubnet1,$PubSubnet2" --version 1.24 --ssh-access --external-dns-access --verbose 4
클러스터 endpoint 설정은 아래와 같습니다.
clusterEndpoints:
privateAccess: false
publicAccess: true
명령어 실행 후 10~15분 정도 기다리면 EKS 클러스터가 생성된 것을 확인할 수 있습니다.
[실습] 노드에서 확인하기
eksctl을 사용하여 클러스터 배포 후에 생성된 노드에 접속해서 여러 가지 설정을 살펴보았습니다.
eksctl로 배포 후에 kubeconfig가 자동으로 설정되어 클러스터에 바로 접근할 수 있었습니다.
실제 실행 중인 프로세스를 확인했을 때 containerd가 사용되는 것을 볼 수 있습니다.
ssh -i ~/.ssh/id_rsa ec2-user@$N1 sudo pstree
systemd-+-2*[agetty]
|-amazon-ssm-agen-+-ssm-agent-worke---8*[{ssm-agent-worke}]
| `-8*[{amazon-ssm-agen}]
|-auditd---{auditd}
|-chronyd
|-containerd---15*[{containerd}]
|-containerd-shim-+-bash-+-aws-k8s-agent---7*[{aws-k8s-agent}]
| | `-tee
| |-pause
| `-11*[{containerd-shim}]
|-containerd-shim-+-kube-proxy---6*[{kube-proxy}]
| |-pause
| `-11*[{containerd-shim}]
|-containerd-shim-+-coredns---7*[{coredns}]
| |-pause
| `-12*[{containerd-shim}]
|-containerd-shim-+-coredns---7*[{coredns}]
| |-pause
| `-11*[{containerd-shim}]
|-crond
|-dbus-daemon
|-2*[dhclient]
|-gssproxy---5*[{gssproxy}]
|-irqbalance---{irqbalance}
|-kubelet---14*[{kubelet}]
|-lvmetad
|-master-+-pickup
| `-qmgr
|-rngd
|-rpcbind
|-rsyslogd---2*[{rsyslogd}]
|-sshd---sshd---sshd---sudo---pstree
|-systemd-journal
|-systemd-logind
`-systemd-udevd
프로세스를 조금 더 자세히 보면, 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com 와 같은 주소를 확인할 수 있습니다.
이는 AWS에서 제공하는 컨테이너 이미지 레지스트리로 각 리전마다 다른 주소를 갖고 있습니다. (참고)
kube-proxy 이미지 또는 각종 add-on도 해당 레지스트리를 통해 다운로드 받습니다.
root 2842 1.1 2.5 1905692 101352 ? Ssl 11:49 1:20 /usr/bin/kubelet --config /etc/kubernetes/kubelet/kubelet-config.json --kubeconfig /var/lib/kubelet/kubeconfig --container-runtime-endpoint unix:///run/containerd/containerd.sock --image-credential-provider-config /etc/eks/ecr-credential-provider/ecr-credential-provider-config --image-credential-provider-bin-dir /etc/eks/ecr-credential-provider --node-ip=192.168.2.96 --pod-infra-container-image=602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/pause:3.5 --v=2 --cloud-provider=aws --container-runtime=remote --node-labels=eks.amazonaws.com/sourceLaunchTemplateVersion=1,alpha.eksctl.io/cluster-name=myeks,alpha.eksctl.io/nodegroup-name=myeks-nodegroup,eks.amazonaws.com/nodegroup-image=ami-0d6ea2d1e34171e07,eks.amazonaws.com/capacityType=ON_DEMAND,eks.amazonaws.com/nodegroup=myeks-nodegroup,eks.amazonaws.com/sourceLaunchTemplateId=lt-0e181e733a4e23753 --max-pods=17
또한, 각 노드의 /etc/kubernetes/manifests/
디렉터리 하위에 파일이 없는 것을 봤을 때, static pod는 생성되지 않는 것을 알 수 있었습니다.
노드의 kubelet
과 kube-proxy
가 어떤 ip와 통신하는지를 확인해 보면, 클러스터 endpoint의 ip인 것을 확인할 수 있습니다.
kubelet과 kube-proxy는 클러스터 endpoint를 통해서 API Server와 통신을 합니다.
현재는 Public endpoint만 사용 중이므로 인터넷을 통해 통신하는 것을 볼 수 있습니다.
ssh -i ~/.ssh/id_rsa ec2-user@$N1 sudo ss -tnp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 192.168.2.96:44428 3.xx.xx.81:443 users:(("kubelet",pid=2842,fd=42))
ESTAB 0 0 192.168.2.96:40936 13.xx.xx.157:443 users:(("kube-proxy",pid=3088,fd=11))
...
## 클러스터 엔드포인트 ip
$ nslookup Cxxxxxxxx2.gr7.ap-northeast-2.eks.amazonaws.com
Server: 192.168.0.2
Address: 192.168.0.2#53
Non-authoritative answer:
Name: Cxxxxxxxx2.gr7.ap-northeast-2.eks.amazonaws.com
Address: 3.xx.xx.81
Name: Cxxxxxxxx2.gr7.ap-northeast-2.eks.amazonaws.com
Address: 13.xx.xx.157
추가로, kubectl exec
명령어로 pod에서 bash
명령어를 수행한 뒤 다시 소켓을 확인했을 때 아래와 같은 결과를 볼 수 있습니다.
ssh -i ~/.ssh/id_rsa ec2-user@$N1 sudo ss -tnp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 56 192.168.2.96:22 192.168.1.100:58562 users:(("sshd",pid=17729,fd=3),("sshd",pid=17697,fd=3))
ESTAB 0 0 192.168.2.96:44428 3.xx.xx.81:443 users:(("kubelet",pid=2842,fd=42))
ESTAB 0 0 192.168.2.96:33064 52.95.195.99:443 users:(("ssm-agent-worke",pid=2444,fd=16))
ESTAB 0 0 127.0.0.1:36106 127.0.0.1:33833 users:(("kubelet",pid=2842,fd=17))
ESTAB 0 0 192.168.2.96:52960 10.100.0.1:443 users:(("aws-k8s-agent",pid=3344,fd=7))
ESTAB 0 0 127.0.0.1:33833 127.0.0.1:36106 users:(("containerd",pid=2733,fd=55))
ESTAB 0 0 192.168.2.96:40936 13.xx.xx.157:443 users:(("kube-proxy",pid=3088,fd=11))
ESTAB 0 0 192.168.2.96:45660 52.95.194.65:443 users:(("ssm-agent-worke",pid=2444,fd=13))
ESTAB 0 0 [::ffff:192.168.2.96]:10250 [::ffff:192.168.2.51]:53884 users:(("kubelet",pid=2842,fd=7))
제일 마지막 줄인 ESTAB 0 0 [::ffff:192.168.2.96]:10250 [::ffff:192.168.2.51]:53884 ...
를 보면 192.168.2.51
ip가 보이고, 해당 ip를 eni 메뉴에서 확인하면 Owner와 Requester ID가 다른 것을 볼 수 있습니다.
해당 eni가 EKS Control Plane과 Data Plane이 통신하기 위해 자동으로 생성되는 EKS 소유 eni 입니다.
이론으로만 알고 있었던 점들을 직접 소켓 상태에서 ip를 찾고 확인해 보니 노드가 어디와 통신하는지를 확실하게 알아볼 수 있었습니다.
기타
- kubectl exec / logs 명령어의 실행과 요청 과정은 어떻게 될까요?
- client (kubectl) 요청 -> API server -> Node의 kubelet (CRI를 통해 컨테이너와 통신하여 컨테이너에서 명령어 수행 후 결과 전송)-> API server -> client
이상으로 1주차 스터디 내용을 마칩니다. 🙂
출처)
'Kubernetes > EKS Study' 카테고리의 다른 글
[4주차] EKS Observability (0) | 2023.05.21 |
---|---|
[3주차] EKS Storage (0) | 2023.05.09 |
EKS에서 볼륨 snapscheduler 설치하기 (0) | 2023.05.09 |
EKS에서 pvc로 생성한 ebs에 태그 추가하기 (0) | 2023.05.09 |
[2주차] EKS Networking (0) | 2023.05.01 |