카이도스의 Tech Blog
PKOS(쿠버네티스)2주차 - 쿠버네티스 네트워크 본문
실습 환경 구성
-AWS kOps 윈클릭 배포 가이드
CloudFormation 으로 자동 배포(배포 전 s3, IAM 유저 먼저 생성 후 진행하기)
*접속 후 확인진행
# 설치 확인
kops validate cluster --wait 10m
# 환경변수 정보 확인
export | egrep 'ACCOUNT|AWS|KOPS|KUBERNETES'
export | egrep 'ACCOUNT|AWS|KOPS|KUBERNETES' | grep -v SECRET
# default 네임스페이스 적용
kubectl ns default
*kops 정보 확인
# kops 클러스터 정보 확인
kops get cluster
kops get cluster -o yaml
# 인스턴스그룹 정보 확인
kops get ig
kops get ig -o yaml
# 인스턴스 정보 확인
kops get instances
*k8s 정보 확인 & krew
# 클러스터 정보 확인
kubectl cluster-info kubectl cluster-info dump
# 노드 정보 확인
kubectl get nodes -o wide
kubectl get nodes -v6
# 파드 정보 확인
kubectl get pod -A
# krew 플러그인 설치 확인
kubectl krew list
# kube-system 오브젝트 전체 확인
kubectl get-all -n kube-system
# 모니터링
kubectl ktop
# 노드 인스턴스 타입 확인
kubectl describe nodes | grep "node.kubernetes.io/instance-type"
1. AWS VPC CNI 소개
K8S CNI : Container Network Interface 는 k8s 네트워크 환경을 구성해준다 , 다양한 플러그인이 존재
AWS VPC CNI : 파드의 IP를 할당해준다, 파드의 IP 네트워크 대역과 노드(워커)의 IP 대역이 같아서 직접 통신이 가능하다.
- supports native VPC networking with the Amazon VPC Container Network Interface (CNI) plugin for Kubernetes.
- VPC 와 통합 : VPC Flow logs , VPC 라우팅 정책,
보안 그룹(Security group)을 사용 가능함 → 아쉽지만 kOps 는 SG for Pod 미지원 - This plugin assigns an IP address from your VPC to each pod.
- VPC ENI 에 미리 할당된 IP를 파드에서 사용할 수 있음
K8S Calico CNI 와 AWS VPC CNI 차이
- 네트워크 통신의 최적화(성능, 지연)를 위해서 노드와 파드의 네트워크 대역을 동일하게 설정함
- 파드간 통신 시 일반적으로 K8S CNI는 오버레이(VXLAN, IP-IP 등) 통신을 하고, AWS VPC CNI는 동일 대역으로 직접 통신을 한다.
워커 노드에 생성 가능한 최대 파드 갯수
- Secondary IPv4 addresses : 인스턴스 유형에 최대 ENI 갯수와 할당 가능 IP 수를 조합하여 선정
- IPv4 Prefix Delegation : IPv4 28bit 서브넷(prefix)를 위임하여 할당 가능 IP 수와 인스턴스 유형에 권장하는 최대 갯수로 선정
[실습] 네트워크 기본 정보 확인
# CNI 정보 확인
kubectl describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2
# 노드 IP 확인
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output table
# 파드 IP 확인
kubectl get pod -n kube-system -o=custom-columns=NAME:.metadata.name,IP:.status.podIP,STATUS:.status.phase
# 파드 이름 확인
kubectl get pod -A -o name
# 파드 갯수 확인
kubectl get pod -A -o name | wc -l
kubectl ktop
# [master node] aws vpc cni log
ssh -i ~/.ssh/id_rsa ubuntu@api.$KOPS_CLUSTER_NAME ls /var/log/aws-routed-eni
master node에 SSH 접속 후 확인
# [master node] SSH 접속
ssh -i ~/.ssh/id_rsa ubuntu@api.$KOPS_CLUSTER_NAME
--------------------------------------------------
# 툴 설치
sudo apt install -y tree jq net-tools
# CNI 정보 확인
ls /var/log/aws-routed-eni
cat /var/log/aws-routed-eni/plugin.log | jq
cat /var/log/aws-routed-eni/ipamd.log | jq
# 네트워크 정보 확인 : eniY는 pod network 네임스페이스와 veth pair
ip -br -c addr
ip -c addr ip -c route
sudo iptables -t nat -S
sudo iptables -t nat -L -n -v
# 빠져나오기
exit
--------------------------------------------------
워커 노드에 SSH 접속 후 확인 : 워커 노드의 public ip 로 SSH 접속
# 워커 노드 Public IP 확인
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value}" --filters Name=instance-state-name,Values=running --output table
# 워커 노드 Public IP 변수 지정
W1PIP=<워커 노드 1 Public IP>
W2PIP=<워커 노드 2 Public IP>
W1PIP=13.125.197.119
W2PIP=15.165.205.171
# 워커 노드 SSH 접속
ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP
exit
ssh -i ~/.ssh/id_rsa ubuntu@$W2PIP
exit
# [워커 노드1~2] SSH 접속 : 접속 후 아래 툴 설치 등 정보 각각 확인
ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP
ssh -i ~/.ssh/id_rsa ubuntu@$W2PIP
--------------------------------------------------
# 툴 설치
sudo apt install -y tree jq net-tools
# CNI 정보 확인
ls /var/log/aws-routed-eni
cat /var/log/aws-routed-eni/plugin.log | jq
cat /var/log/aws-routed-eni/ipamd.log | jq
# 네트워크 정보 확인
ip -br -c addr
ip -c addr
ip -c route
sudo iptables -t nat -S
sudo iptables -t nat -L -n -v
# 빠져나오기
exit
--------------------------------------------------
2. 노드에서 기본 네트워크 정보 확인
워커 노드1 기본 네트워크 구성 : 워커 노드2 는 구성이 유사하여 생략
- Network 네임스페이스는 호스트(Root)와 파드 별(Per Pod)로 구분된다
- 특정한 파드(kube-proxy, aws-node)는 호스트(Root)의 IP를 그대로 사용한다
- t3.medium 의 경우 ENI 에 최대 6개의 IP를 가질 수 있다
- ENI0, ENI1 으로 2개의 ENI는 자신의 IP 이외에 추가적으로 5개의 보조 프라이빗 IP를 가질수 있다
- coredns 파드는 veth 으로 호스트에는 eniY@ifN 인터페이스와 파드에 eth0 과 연결되어 있다
워커 노드1 인스턴스의 네트워크 정보 확인 : 프라이빗 IP와 보조 프라이빗 IP 확인
[실습] 보조 IPv4 주소를 파드가 사용하는지 확인
# ebs-csi-node 파드 IP 정보 확인
kubectl get pod -n kube-system -l app=ebs-csi-node -owide
# 노드의 라우팅 정보 확인 >> EC2 네트워크 정보의 '보조 프라이빗 IPv4 주소'와 비교해보자
ssh -i ~/.ssh/id_rsa ubuntu@api.$KOPS_CLUSTER_NAME ip -c route
ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP ip -c route
ssh -i ~/.ssh/id_rsa ubuntu@$W2PIP ip -c route
[실습] 테스트용 파드 생성
# [터미널1~2] 워커 노드 1~2 모니터링
ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP
watch -d "ip link | egrep 'ens5|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"
ssh -i ~/.ssh/id_rsa ubuntu@$W2PIP
watch -d "ip link | egrep 'ens5|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"
# 작업용 EC2 파드 2개 생성
cat ~/pkos/2/netshoot-2pods.yaml | yh
kubectl apply -f ~/pkos/2/netshoot-2pods.yaml
# 파드 확인
kubectl get pod -o wide
kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP
3. 노드 간 파드 통신
파드간 통신 흐름 : 별도의 오버레이(Overlay) 통신 기술 없이, VPC Native 하게 파드간 직접 통신이 가능하다
[실습] 파드간 통신 테스트 및 확인 : 별도의 NAT 동작 없이 통신 가능!
# 파드 IP 변수 지정
POD1=$(kubectl get pod pod-1 -o jsonpath={.status.podIP})
POD2=$(kubectl get pod pod-2 -o jsonpath={.status.podIP})
# 워커 노드 EC2 : TCPDUMP 확인 - ens6 에서 패킷 덤프 확인이 되나요?
sudo tcpdump -i any -nn icmp
sudo tcpdump -i ens5 -nn icmp
sudo tcpdump -i ens6 -nn icmp
# 파드1 Shell 에서 파드2로 ping 테스트
kubectl exec -it pod-1 -- ping -c 2 $POD2
# 파드2 Shell 에서 파드1로 ping 테스트
kubectl exec -it pod-2 -- ping -c 2 $POD1
4. 파드에서 외부 통신
파드에서 외부 통신 흐름 : iptable 에 SNAT 을 통하여 노드의 eth0 IP로 변경되어서 외부와 통신됨
- VPC CNI 의 External source network address translation (SNAT) 설정에 따라, 외부(인터넷) 통신 시 SNAT 하거나 혹은 SNAT 없이 통신을 할 수 있다.
[실습] 파드에서 외부 통신 테스트 및 확인
# 작업용 EC2 : pod-1 Shell 에서 외부로 ping
kubectl exec -it pod-1 -- ping -c 1 www.google.com
kubectl exec -it pod-1 -- ping -i 0.1 www.google.com
# 워커 노드 EC2 : 퍼블릭IP 확인, TCPDUMP 확인
curl -s ipinfo.io/ip ; echo
sudo tcpdump -i any -nn icmp
sudo tcpdump -i ens5 -nn icmp
# 작업용 EC2 : pod-1 Shell 에서 외부 접속 확인 - 공인IP는 어떤 주소인가?
kubectl exec -it pod-1 -- curl -s ipinfo.io/ip ; echo
kubectl exec -it pod-1 -- curl -s wttr.in/seoul
kubectl exec -it pod-1 -- curl -s wttr.in/seoul?format=3
kubectl exec -it pod-1 -- curl -s wttr.in/Moon
kubectl exec -it pod-1 -- curl -s wttr.in/:help
# 워커 노드 EC2
ip rule
ip route show table main
sudo iptables -L -n -v -t nat
sudo iptables -t nat -S
다음 실습을 위해서 파드 삭제: kubectl delete pod pod-1 pod-2
5. 노드에 파드 생성 갯수 제한
Secondary IPv4 addresses : 인스턴스 유형에 최대 ENI 갯수와 할당 가능 IP 수를 조합하여 선정
워커 노드의 인스턴스 타입 별 파드 생성 갯수 제한
- 인스턴스 타입 별 ENI 최대 갯수와 할당 가능한 최대 IP 갯수에 따라서 파드 배치 갯수가 결정됨
- 단, aws-node 와 kube-proxy 파드는 호스트의 IP를 사용함으로 최대 갯수에서 제외함
최대 파드 생성 갯수 : (Number of network interfaces for the instance type × (the number of IP addressess per network interface - 1)) + 2
워커 노드의 인스턴스 정보 확인 : 현재 t3.medium 사용 중
# t3 타입의 정보(필터) 확인
(pjhtest:default) [root@kops-ec2 ~]# aws ec2 describe-instance-types --filters Name=instance-type,Values=t3.* \
> --query "InstanceTypes[].{Type: InstanceType, MaxENI: NetworkInfo.MaximumNetworkInterfaces, IPv4addr: NetworkInfo.Ipv4AddressesPerInterface}" \
> --output table
--------------------------------------
| DescribeInstanceTypes |
+----------+----------+--------------+
| IPv4addr | MaxENI | Type |
+----------+----------+--------------+
| 15 | 4 | t3.2xlarge |
| 15 | 4 | t3.xlarge |
| 2 | 2 | t3.nano |
| 6 | 3 | t3.medium |
| 12 | 3 | t3.large |
| 4 | 3 | t3.small |
| 2 | 2 | t3.micro |
+----------+----------+--------------+
# 파드 사용 가능 계산 예시 : aws-node 와 kube-proxy 파드는 host-networking 사용으로 IP 2개 남음
((MaxENI * (IPv4addr-1)) + 2)
t3.medium 경우 : ((3 * (6 - 1) + 2 ) = 17개 >> aws-node 와 kube-proxy 2개 제외하면 15개
# 워커노드 상세 정보 확인 : 노드 상세 정보의 Allocatable 에 pods 에 17개 정보 확인
kubectl describe node | grep Allocatable: -A6
Allocatable:
cpu: 2
ephemeral-storage: 59763732382
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3854320Ki
pods: 17
--
최대 파드 생성 및 확인
# 워커 노드 EC2 - 모니터링
watch -d "ip link | egrep 'ens|eni'"
while true; do ip -br -c addr show && echo "--------------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done
# 작업용 EC2 - 터미널1
watch -d 'kubectl get pods -o wide'
# 작업용 EC2 - 터미널2
# 디플로이먼트 생성
cat ~/pkos/2/nginx-dp.yaml | yh
kubectl apply -f ~/pkos/2/nginx-dp.yaml
# 파드 확인
kubectl get pod -o wide
kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP
kubectl ktop
# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인
kubectl scale deployment nginx-deployment --replicas=8
# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인 >> 어떤일이 벌어졌는가?
kubectl scale deployment nginx-deployment --replicas=10
# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인 >> 어떤일이 벌어졌는가?
kubectl scale deployment nginx-deployment --replicas=30
# 파드 생성 실패!
kubectl get pods | grep Pending
nginx-deployment-6fb79bc456-5l2vl 0/1 Pending 0 23s
nginx-deployment-6fb79bc456-9bsd2 0/1 Pending 0 23s
nginx-deployment-6fb79bc456-g67k7 0/1 Pending 0 23s
nginx-deployment-6fb79bc456-lxxtq 0/1 Pending 0 23s
nginx-deployment-6fb79bc456-w7mjb 0/1 Pending 0 23s
kubectl describe pod nginx-deployment-6fb79bc456-5l2vl | grep Events: -A5
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 2m48s (x2 over 2m49s) default-scheduler 0/3 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, 2 Too many pods. preemption: 0/3 nodes are available: 1 Preemption is not helpful for scheduling, 2 No preemption victims found for incoming pod.
[과제2]
해결 및 과제 2 진행
워커 노드 1대에 100대이상의 파드가 배포되게 설정하고 관련 스샷을 올려주세요
# 노드 인스턴스 타입 확인
(pjhtest:default) [root@kops-ec2 ~]# kubectl describe nodes | grep "node.kubernetes.io/instance-type"
node.kubernetes.io/instance-type=t3.medium
node.kubernetes.io/instance-type=t3.medium
node.kubernetes.io/instance-type=t3.medium
kops 클러스터와 인스턴스 그룹에 파라미터 수정
# 파드 갯수 0으로 줄이기
kubectl scale deployment nginx-deployment --replicas=0
# LimitRanges 기본 정책 삭제
kubectl delete limitranges limits
kubectl get limitranges
# 수정 전 env 정보 확인 : WARM_PREFIX_TARGET은 기본값이 1로 이미 되어 있음
kubectl describe ds -n kube-system aws-node | grep ADDITIONAL_ENI_TAGS: -A22
ADDITIONAL_ENI_TAGS: {"KubernetesCluster":"pjhtest.click","kubernetes.io/cluster/pjhtest.click":"owned"}
AWS_VPC_CNI_NODE_PORT_SUPPORT: true
AWS_VPC_ENI_MTU: 9001
AWS_VPC_K8S_CNI_CONFIGURE_RPFILTER: false
AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG: false
AWS_VPC_K8S_CNI_EXTERNALSNAT: false
AWS_VPC_K8S_CNI_LOGLEVEL: DEBUG
AWS_VPC_K8S_CNI_LOG_FILE: /host/var/log/aws-routed-eni/ipamd.log
AWS_VPC_K8S_CNI_RANDOMIZESNAT: prng
AWS_VPC_K8S_CNI_VETHPREFIX: eni
AWS_VPC_K8S_PLUGIN_LOG_FILE: /var/log/aws-routed-eni/plugin.log
AWS_VPC_K8S_PLUGIN_LOG_LEVEL: DEBUG
DISABLE_INTROSPECTION: false
DISABLE_METRICS: false
DISABLE_NETWORK_RESOURCE_PROVISIONING: false
ENABLE_IPv4: true
ENABLE_IPv6: false
ENABLE_POD_ENI: false
ENABLE_PREFIX_DELEGATION: false
WARM_ENI_TARGET: 1
WARM_PREFIX_TARGET: 1
MY_NODE_NAME: (v1:spec.nodeName)
CLUSTER_NAME: pjhtest.click
# kops 클러스터와 인스턴스 그룹에 파라미터 수정 : 노드에 kubelet 에 maxPods 110개로 수정, Prefix Assign 활성화
kops edit cluster
.....
kubelet:
anonymousAuth: false
maxPods: 110
.....
networking:
amazonvpc:
env:
- name: ENABLE_PREFIX_DELEGATION
value: "true"
.......
# 적용을 위해서 반드시 노드 롤링업데이트 필요 : 삭제 -> 재생성, 이 과정에서 master node 의 IP(Public, Private) 가 자동으로 변경됨 >> 10분 정도 소요
kops update cluster --yes && echo && sleep 5 && kops rolling-update cluster --yes
....
I0127 13:14:06.173070 22292 rollingupdate.go:214] Rolling update completed for cluster "pjhtest.click"!
# 변경 정보 반영된 env 정보 확인
kubectl describe daemonsets.apps -n kube-system aws-node | egrep 'ENABLE_PREFIX_DELEGATION|WARM_PREFIX_TARGET'
ENABLE_PREFIX_DELEGATION: true
WARM_PREFIX_TARGET: 1
# 노드 상세 정보 확인 : 노드 상세 정보의 Allocatable 에 노드별 최대 생성 가능한 pods 정보 확인 - 각각 마스터 노드, 워커 노드
Allocatable:
cpu: 2
ephemeral-storage: 59763732382
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3854320Ki
pods: 110
--
Allocatable:
cpu: 2
ephemeral-storage: 119703055367
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3854320Ki
pods: 110
--
Allocatable:
cpu: 2
ephemeral-storage: 119703055367
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3854320Ki
pods: 110
# 파드 갯수 110으로 늘리기
kubectl scale deployment nginx-deployment --replicas=110
kubectl get replicasets
NAME DESIRED CURRENT READY AGE
nginx-deployment-6fb79bc456 110 110 110 71m
# 삭제
kubectl delete deploy nginx-deployment
7. ExternalDNS
소개 : K8S 서비스/인그레스 생성 시 도메인을 설정하면, AWS(Route 53), Azure(DNS), GCP(Cloud DNS) 에 A 레코드(TXT 레코드)로 자동 생성/삭제
설치
# 모니터링
watch -d kubectl get pod -A
# dns 관련 파드 갯수 줄이기(2개)
kubectl delete deploy -n kube-system coredns-autoscaler
kubectl scale deploy -n kube-system coredns --replicas 1
# 정책 생성 -> 마스터/워커노드에 정책 연결
curl -s -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/AKOS/externaldns/externaldns-aws-r53-policy.json https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/AKOS/externaldns/externaldns-aws-r53-policy.jso
aws iam create-policy --policy-name AllowExternalDNSUpdates --policy-document file://externaldns-aws-r53-policy.json
# example: arn:aws:iam::XXXXXXXXXXXX:policy/AllowExternalDNSUpdates
aws iam list-policies --query 'Policies[?PolicyName==`AllowExternalDNSUpdates`].Arn' --output text
export POLICY_ARN=$(aws iam list-policies --query 'Policies[?PolicyName==`AllowExternalDNSUpdates`].Arn' --output text)
# EC2 instance profiles 에 IAM Policy 추가(attach)
aws iam attach-role-policy --policy-arn $POLICY_ARN --role-name masters.$KOPS_CLUSTER_NAME
aws iam attach-role-policy --policy-arn $POLICY_ARN --role-name nodes.$KOPS_CLUSTER_NAME
# 설치
kops edit cluster
--------------------------
spec:
externalDns:
provider: external-dns
--------------------------
# 업데이트 적용
kops update cluster --yes && echo && sleep 5 && kops rolling-update cluster
# 버전 확인 : v0.12.2
kubectl describe deploy -n kube-system external-dns | grep Image | cut -d "/" -f 3
# externalDns 컨트롤러 파드 확인
kubectl get pod -n kube-system -l k8s-app=external-dns
NAME READY STATUS RESTARTS AGE
external-dns-8ff94bc85-k6xvv 1/1 Running 0 24s
서비스(NLB)/파드 배포 시 ExternalDNS 설정해보기
# ExternalDNS 로그 모니터링
kubectl logs -n kube-system -f $(kubectl get po -n kube-system | egrep -o 'external-dns[A-Za-z0-9-]+')
# 서비스/파드 배포
kubectl apply -f ~/pkos/2/echo-service-nlb.yaml
# 확인
kubectl get deploy,pod
kubectl get svc,ep,targetgroupbindings
# 자신의 도메인 정보 입력
MyDOMAIN1=web.pjhtest.click
kubectl annotate service svc-nlb-ip-type "external-dns.alpha.kubernetes.io/hostname=$MyDOMAIN1."
kubectl describe svc svc-nlb-ip-type | grep Annotations: -A5
# 확인
dig +short $MyDOMAIN1
43.200.197.20
3.37.54.155
# 분산 접속 확인
curl -s $MyDOMAIN1
for i in {1..100}; do curl -s $MyDOMAIN1 | grep Hostname ; done | sort | uniq -c | sort -nr
51 Hostname: deploy-echo-5c4856dfd6-szd48
49 Hostname: deploy-echo-5c4856dfd6-78lng
# 지속적인 접속 시도 : 아래 상세 동작 확인 시 유용(패킷 덤프 등)
while true; do curl -s --connect-timeout 1 $MyDOMAIN1 | egrep 'Hostname|client_address'; echo "----------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done
[과제3]
목표 : 서비스(NLB)/파드 배포 시 ExternalDNS 설정해서, 각자 자신의 도메인으로 NLB를 통해 애플리케이션(파드)로 접속해보고 관련 스샷을 올려주세요.
'KOPS' 카테고리의 다른 글
PKOS 6주차 - Alerting 얼럿매니저 로깅시스템 (0) | 2023.02.23 |
---|---|
PKOS 5주차 - 프로메테우스 그라파나 (0) | 2023.02.14 |
PKOS 4주차 - Harbor Gitlab ArgoCD (0) | 2023.02.07 |
PKOS(쿠버네티스)3주차 - Ingress & Storage (0) | 2023.02.01 |
PKOS(쿠버네티스) 1주차 - AWS kOps 설치 및 기본 사용 (4) | 2023.01.11 |