Cloudnet@에서 진행하는 쿠버네티스 실무 실습 스터디를 진행하면서 작성한 글입니다.
스터디에서 사용하는 교재는 24단계 실습으로 정복하는 쿠버네티스 입니다.
Cloudnet@
24단계 실습으로 정복하는 쿠버네티스
K8S CNI
(Container Network Interface)
AWS VPC CNI
:
# CNI 정보 확인
kubectl describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2
amazon-k8s-cni-init:v1.12.2
amazon-k8s-cni:v1.12.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
확인해보면, node와 pod의 네트워크 대역대는 172.30.0.0로 동일
# [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
-----------------
# 워커 노드 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>
# 워커 노드 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
--------------
# [터미널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"
# 테스트용 파드 netshoot-pod 생성
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: netshoot-pod
spec:
replicas: 2
selector:
matchLabels:
app: netshoot-pod
template:
metadata:
labels:
app: netshoot-pod
spec:
containers:
- name: netshoot-pod
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
# 파드 이름 변수 지정
PODNAME1=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[0].metadata.name})
PODNAME2=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[1].metadata.name})
# 파드 확인
kubectl get pod -o wide
kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP
# 노드에서 네트워크 인터페이스 정보 확인
ip -br -c addr show
ip -c link
ip -c addr
ip route # 혹은 route -n
# 마지막 생성된 네임스페이스 정보 출력 -t net(네트워크 타입)
sudo lsns -o PID,COMMAND -t net | awk 'NR>2 {print $1}' | tail -n 1
# 마지막 생성된 네임스페이스 net PID 정보 출력 -t net(네트워크 타입)를 변수 지정
MyPID=$(sudo lsns -o PID,COMMAND -t net | awk 'NR>2 {print $1}' | tail -n 1)
# PID 정보로 파드 정보 확인
sudo nsenter -t $MyPID -n ip -c addr
sudo nsenter -t $MyPID -n ip -c route
# 파드 IP 변수 지정
PODIP1=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[0].status.podIP})
PODIP2=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[1].status.podIP})
# 파드1 Shell 에서 파드2로 ping 테스트
kubectl exec -it $PODNAME1 -- ping -c 2 $PODIP2
# 파드2 Shell 에서 파드1로 ping 테스트
kubectl exec -it $PODNAME2 -- ping -c 2 $PODIP1
# 워커 노드 EC2 : TCPDUMP 확인 - ens6 에서 패킷 덤프 확인이 되나요?
sudo tcpdump -i any -nn icmp
sudo tcpdump -i ens5 -nn icmp
sudo tcpdump -i ens6 -nn icmp
[워커 노드1]
# routing policy database management 확인
ip rule
# routing table management 확인
ip route show table local
# 디폴트 네트워크 정보를 ens5 을 통해서 빠져나간다
ip route show table main
default via 172.30.64.1 dev ens5 proto dhcp src 172.30.85.242 metric 100
...
VPC CNI 의 External source network address translation (SNAT) 설정에 따라, 외부(인터넷) 통신 시 SNAT 하거나 혹은 SNAT 없이 통신을 할 수 있다.
# 작업용 EC2 : pod-1 Shell 에서 외부로 ping
kubectl exec -it $PODNAME1 -- ping -c 1 www.google.com
kubectl exec -it $PODNAME1 -- 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는 어떤 주소인가?
## The right way to check the weather - 링크
kubectl exec -it $PODNAME1 -- curl -s ipinfo.io/ip ; echo
kubectl exec -it $PODNAME1 -- curl -s wttr.in/seoul
kubectl exec -it $PODNAME1 -- curl -s wttr.in/seoul?format=3
kubectl exec -it $PODNAME1 -- curl -s wttr.in/Moon
kubectl exec -it $PODNAME1 -- 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
# 파드가 외부와 통신시에는 아래 처럼 'AWS-SNAT-CHAIN-0, AWS-SNAT-CHAIN-1' 룰(rule)에 의해서 SNAT 되어서 외부와 통신!
# 뒤 IP는 eth0(ENI 첫번째)의 IP 주소이다
sudo iptables -t nat -S | grep 'A AWS-SNAT-CHAIN'
# 카운트 확인 시 AWS-SNAT-CHAIN-0, AWS-SNAT-CHAIN-1 에 매칭되어, 목적지가 172.30.0.0/16 아니고 외부 빠져나갈때 SNAT 172.30.40.153로 변경되어 나간다!
sudo iptables -t filter --zero; sudo iptables -t nat --zero; sudo iptables -t mangle --zero; sudo iptables -t raw --zero
watch -d 'sudo iptables -v --numeric --table nat --list AWS-SNAT-CHAIN-0; echo ; sudo iptables -v --numeric --table nat --list AWS-SNAT-CHAIN-1; echo ; sudo iptables -v --numeric --table nat --list KUBE-POSTROUTING'
# conntrack 확인
sudo conntrack -L -n |grep -v '169.254.169'
conntrack v1.4.5 (conntrack-tools): 23 flow entries have been shown.
tcp 6 53 TIME_WAIT src=172.30.57.192 dst=5.9.243.187 sport=55954 dport=80 src=5.9.243.187 dst=172.30.40.153 sport=80 dport=55815 [ASSURED] mark=128 use=1
다음 실습을 위해서 파드 삭제:kubectl delete deploy netshoot-pod
최대 파드 생성 갯수 : (Number of network interfaces for the instance type × (the number of IP addressess per network interface - 1)) + 2
eni-max-Pods.txt
# t3 타입의 정보(필터) 확인
aws ec2 describe-instance-types --filters Name=instance-type,Values=t3.* \
--query "InstanceTypes[].{Type: InstanceType, MaxENI: NetworkInfo.MaximumNetworkInterfaces, IPv4addr: NetworkInfo.Ipv4AddressesPerInterface}" \
--output table
```bash
# 워커 노드 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-7fb7fd49b4-d4bk9 0/1 Pending 0 3m37s
nginx-deployment-7fb7fd49b4-qpqbm 0/1 Pending 0 3m37s
...
kubectl describe pod <Pending 파드> | grep Events: -A5
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 45s 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.
# 디플로이먼트 삭제
kubectl delete deploy nginx-deployment
# 파드 갯수 모니터링
watch "kubectl get pod | grep -v NAME | wc -l"
# 파드 100개 배포
kubectl apply -f ~/pkos/2/nginx-dp.yaml
kubectl scale deployment nginx-deployment --replicas=0
kubectl scale deployment nginx-deployment --replicas=10
kubectl scale deployment nginx-deployment --replicas=30
kubectl scale deployment nginx-deployment --replicas=100
# Nitro 인스턴스 유형 확인
aws ec2 describe-instance-types --filters Name=hypervisor,Values=nitro --query "InstanceTypes[*].[InstanceType]" --output text | sort | egrep 't3\.|c5\.|c5d\.'
# 노드 인스턴스 타입 확인
kubectl describe nodes | grep "node.kubernetes.io/instance-type"
# 노드 상세 정보 확인 : 노드 상세 정보의 Allocatable 에 노드별 최대 생성 가능한 pods 정보 확인 - 각각 마스터 노드, 워커 노드
kubectl describe node | grep Allocatable: -A6
# 파드 배포 확인
kubectl get pod
kubectl get pod | grep -v NAME | wc -l
kubectl get replicasets
# LimitRanges 기본 정책 확인
kubectl describe limitranges
# 수정 전 env 정보 확인
kubectl describe ds -n kube-system aws-node | grep ADDITIONAL_ENI_TAGS: -A22
kubectl describe daemonsets.apps -n kube-system aws-node | egrep 'ENABLE_PREFIX_DELEGATION|WARM_PREFIX_TARGET'
https://learnk8s.io/kubernetes-instance-calculator
https://docs.google.com/spreadsheets/d/1yhkuBJBY2iO2Ax5FcbDMdWD5QLTVO6Y_kYt_VumnEtI/edit#gid=1994017257
# 기본 EC2 profile 권한 확인
aws elbv2 describe-load-balancers
# 계정 Account ID 변수 지정
ACCOUNT_ID=`aws sts get-caller-identity --query 'Account' --output text`
echo $ACCOUNT_ID
# 생성된 IAM Policy Arn 확인
aws iam list-policies --scope Local
aws iam get-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy
aws iam get-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --query
# EC2 instance profiles 이름 확인
aws iam list-instance-profiles
aws iam list-instance-profiles --query 'InstanceProfiles[*].InstanceProfileName'
# kOps 클러스터 편집 : 아래 내용 추가
## 인증서 구성을 웹훅에 삽입할 수 있도록 cert-manager를 설치합니다.
## Cert-manager는 쿠버네티스 클러스터 내에서 TLS인증서를 자동으로 프로비저닝 및 관리하는 오픈 소스입니다
kops edit cluster --name ${KOPS_CLUSTER_NAME}
-----
spec:
certManager:
enabled: true
awsLoadBalancerController:
enabled: true
-----
# 업데이트 적용 : 적용이 안될 경우 한번 더 아래 명령 실행
kops update cluster --yes && echo && sleep 5 && kops rolling-update cluster
# aws-load-balancer-controller 파드 확인 : 서비스 정상 상태로 변경까지 대략 2분 30초 정도 소요
watch kubectl get pod -A
# 버전 확인 : v2.4.3
kubectl describe deploy -n kube-system aws-load-balancer-controller | grep Image | cut -d "/" -f 2
# crds 확인
kubectl get crd
# 작업용 EC2 - 디플로이먼트 & 서비스 생성
cat ~/pkos/2/echo-service-nlb.yaml | yh
kubectl apply -f ~/pkos/2/echo-service-nlb.yaml
# 확인
kubectl get deploy,pod
kubectl get crd
kubectl get svc,ep,ingressclassparams,targetgroupbindings
kubectl get targetgroupbindings -o json | jq
# AWS ELB(NLB) 정보 확인
aws elbv2 describe-load-balancers | jq
aws elbv2 describe-load-balancers --query 'LoadBalancers[*].State.Code' --output text
# 웹 접속 주소 확인
kubectl get svc svc-nlb-ip-type -o jsonpath={.status.loadBalancer.ingress[0].hostname} | awk '{ print "Pod Web URL = http://"$1 }'
------------------------------
# CLB에 ExternanDNS 로 도메인 연결
kubectl annotate service svc-nlb-ip-type "external-dns.alpha.kubernetes.io/hostname=nginx.$KOPS_CLUSTER_NAME"
# 확인
dig +short nginx.$KOPS_CLUSTER_NAME
kubectl logs -n kube-system -l k8s-app=external-dns
# 웹 접속 주소 확인 및 접속
echo -e "Nginx Web URL = http://nginx.$KOPS_CLUSTER_NAME"
------------------------------
# 파드 로깅 모니터링
kubectl logs -l app=deploy-websrv -f
# 분산 접속 확인
NLB=$(kubectl get svc svc-nlb-ip-type -o jsonpath={.status.loadBalancer.ingress[0].hostname})
curl -s $NLB
for i in {1..100}; do curl -s $NLB | grep Hostname ; done | sort | uniq -c | sort -nr
52 Hostname: deploy-echo-55456fc798-2w65p
48 Hostname: deploy-echo-55456fc798-cxl7z
# 지속적인 접속 시도 : 아래 상세 동작 확인 시 유용(패킷 덤프 등)
while true; do curl -s --connect-timeout 1 $NLB | egrep 'Hostname|client_address'; echo "----------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done
자동으로 반영 가능! => AWS와 k8s의 role
# 작업용 EC2 - 파드 1개 설정
kubectl scale deployment deploy-echo --replicas=1
# 확인
kubectl get deploy,pod,svc,ep
curl -s nginx.$KOPS_CLUSTER_NAME
curl -s $NLB
for i in {1..100}; do curl -s --connect-timeout 1 nginx.$KOPS_CLUSTER_NAME | grep Hostname ; done | sort | uniq -c | sort -nr
for i in {1..100}; do curl -s --connect-timeout 1 $NLB | grep Hostname ; done | sort | uniq -c | sort -nr
# 작업용 EC2 - 파드 3개 설정
kubectl scale deployment deploy-echo --replicas=3
# 확인
kubectl get deploy,pod,svc,ep
curl -s nginx.$KOPS_CLUSTER_NAME
curl -s $NLB
for i in {1..100}; do curl -s --connect-timeout 1 nginx.$KOPS_CLUSTER_NAME | grep Hostname ; done | sort | uniq -c | sort -nr
for i in {1..100}; do curl -s --connect-timeout 1 $NLB | grep Hostname ; done | sort | uniq -c | sort -nr
# [AWS LB Ctrl] 클러스터 롤 바인딩 정보 확인
kubectl describe clusterrolebindings.rbac.authorization.k8s.io aws-load-balancer-controller-rolebinding
# [AWS LB Ctrl] 클러스터롤 확인
kubectl describe clusterroles.rbac.authorization.k8s.io aws-load-balancer-controller-role
클러스터 내부의 서비스(ClusterIP, NodePort, Loadbalancer)를 외부로 노출(HTTP/HTTPS) - Web Proxy 역할
# 게임 파드와 Service, Ingress 배포
cat ~/pkos/3/ingress1.yaml | yh
kubectl apply -f ~/pkos/3/ingress1.yaml
# 생성 확인
kubectl get-all -n game-2048
kubectl get ingress,svc,ep,pod -n game-2048
kubectl get targetgroupbindings -n game-2048
NAME SERVICE-NAME SERVICE-PORT TARGET-TYPE AGE
k8s-game2048-service2-e48050abac service-2048 80 ip 87s
# Ingress 확인
kubectl describe ingress -n game-2048 ingress-2048
# 게임 접속 : ALB 주소로 웹 접속
kubectl get ingress -n game-2048 ingress-2048 -o jsonpath={.status.loadBalancer.ingress[0].hostname} | awk '{ print "Game URL = http://"$1 }'
kubectl get logs -n game-2048 -l app=game-2048
# 파드 IP 확인
kubectl get pod -n game-2048 -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deployment-2048-6bc9fd6bf5-7f4mr 1/1 Running 0 72s 172.30.53.54 i-000e0b051c37cf359 <none> <none>
deployment-2048-6bc9fd6bf5-g4h4f 1/1 Running 0 72s 172.30.95.128 i-0a5c718b3b0a7996b <none> <none>
AWS Load Balancer Controller 참고
AWS_ACM_ARN=`aws acm list-certificates --query 'CertificateSummaryList[1].CertificateArn' --output text`
k apply -f imok-games.yaml
k get ingress,svc,ep,pod -n game
k delete -f imok-games.yaml
# imok-games.yaml
apiVersion: v1
kind: Namespace
metadata:
name: game
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: game
name: tetris
labels:
app: tetris
spec:
replicas: 1
selector:
matchLabels:
app: tetris
template:
metadata:
labels:
app: tetris
spec:
containers:
- name: tetris
image: bsord/tetris
---
apiVersion: v1
kind: Service
metadata:
namespace: game
name: tetris
annotations:
alb.ingress.kubernetes.io/healthcheck-path: /tetris/index.html
spec:
selector:
app: tetris
ports:
- port: 80
protocol: TCP
targetPort: 80
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: game
name: mario
labels:
app: mario
spec:
replicas: 1
selector:
matchLabels:
app: mario
template:
metadata:
labels:
app: mario
spec:
containers:
- name: mario
image: pengbai/docker-supermario
---
apiVersion: v1
kind: Service
metadata:
namespace: game
name: mario
annotations:
alb.ingress.kubernetes.io/healthcheck-path: /mario/index.html
spec:
selector:
app: mario
ports:
- port: 80
protocol: TCP
targetPort: 8080
type: NodePort
externalTrafficPolicy: Local
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: game
name: ingress-game-imok
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/certificate-arn: ${AWS_ACM_ARN}
alb.ingress.kubernetes.io/healthcheck-port: traffic-port
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
alb.ingress.kubernetes.io/ssl-redirect: '443'
spec:
ingressClassName: alb
rules:
- host: albweb.imokapp.net
http:
paths:
- path: /tetris
pathType: Prefix
backend:
service:
name: tetris
port:
number: 80
- path: /mario
pathType: Prefix
backend:
service:
name: mario
port:
number: 80