Skip to main content Link Menu Expand (external link) Document Search Copy Copied

AWS VPC CNI 소개

네트워크 기본 정보 확인

# CNI 정보 확인
kubectl describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2

# kube-proxy config 확인 : 모드 iptables 사용 >> ipvs 모드로 변경 해보자!
kubectl describe cm -n kube-system kube-proxy-config
...
mode: "iptables"
...

# 노드 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

노드에 네트워크 정보 확인

# CNI 정보 확인
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i tree /var/log/aws-routed-eni; echo; done
ssh ec2-user@$N1 sudo cat /var/log/aws-routed-eni/plugin.log | jq
ssh ec2-user@$N1 sudo cat /var/log/aws-routed-eni/ipamd.log | jq
ssh ec2-user@$N1 sudo cat /var/log/aws-routed-eni/egress-v6-plugin.log | jq
ssh ec2-user@$N1 sudo cat /var/log/aws-routed-eni/ebpf-sdk.log | jq
ssh ec2-user@$N1 sudo cat /var/log/aws-routed-eni/network-policy-agent.log | jq

# 네트워크 정보 확인 : eniY는 pod network 네임스페이스와 veth pair
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -br -c addr; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c addr; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c route; echo; done
ssh ec2-user@$N1 sudo iptables -t nat -S
ssh ec2-user@$N1 sudo iptables -t nat -L -n -v

노드에서 기본 네트워크 정보 확인

# coredns 파드 IP 정보 확인
kubectl get pod -n kube-system -l k8s-app=kube-dns -owide
NAME                       READY   STATUS    RESTARTS   AGE   IP              NODE                                               NOMINATED NODE   READINESS GATES
coredns-6777fcd775-57k77   1/1     Running   0          70m   192.168.1.142   ip-192-168-1-251.ap-northeast-2.compute.internal   <none>           <none>
coredns-6777fcd775-cvqsb   1/1     Running   0          70m   192.168.2.75    ip-192-168-2-34.ap-northeast-2.compute.internal    <none>           <none>


노드의 라우팅 정보 확인

➜  ~  for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -i ~/.ssh/eks-demo ec2-user@$i sudo ip -c route; echo; done

>> node 3.35.234.190 <<
default via 192.168.3.1 dev ens5 proto dhcp src 192.168.3.46 metric 1024
192.168.0.2 via 192.168.3.1 dev ens5 proto dhcp src 192.168.3.46 metric 1024
192.168.3.0/24 dev ens5 proto kernel scope link src 192.168.3.46 metric 1024
192.168.3.1 dev ens5 proto dhcp scope link src 192.168.3.46 metric 1024

>> node 3.39.0.92 <<
default via 192.168.1.1 dev ens5 proto dhcp src 192.168.1.51 metric 1024
192.168.0.2 via 192.168.1.1 dev ens5 proto dhcp src 192.168.1.51 metric 1024
192.168.1.0/24 dev ens5 proto kernel scope link src 192.168.1.51 metric 1024
192.168.1.1 dev ens5 proto dhcp scope link src 192.168.1.51 metric 1024
192.168.1.17 dev enie50c6729a7f scope link
192.168.1.23 dev enicb6cd19a76e scope link
192.168.1.95 dev eni9f32e929b5b scope link
192.168.1.225 dev eni39975045e30 scope link

>> node 3.38.133.223 <<
default via 192.168.2.1 dev ens5 proto dhcp src 192.168.2.147 metric 1024
192.168.0.2 via 192.168.2.1 dev ens5 proto dhcp src 192.168.2.147 metric 1024
192.168.2.0/24 dev ens5 proto kernel scope link src 192.168.2.147 metric 1024
192.168.2.1 dev ens5 proto dhcp scope link src 192.168.2.147 metric 1024

2. AWS VPC CNI - 파드 기본 통신

[ec2-user@ip-192-168-1-51 ~]$ ip -br -c addr

lo               UNKNOWN        127.0.0.1/8 ::1/128
ens5             UP             192.168.1.51/24 metric 1024 fe80::e8:14ff:fe85:b2df/64
enie50c6729a7f@if3 UP             fe80::44a8:c1ff:fef9:cd12/64
eni9f32e929b5b@if3 UP             fe80::209b:dbff:fe6f:7260/64
enicb6cd19a76e@if3 UP             fe80::382b:e7ff:fea1:d724/64
eni39975045e30@if3 UP             fe80::b88c:9eff:fed1:7a6e/64
ens6             UP             192.168.1.248/24 fe80::a4:2fff:fe65:9781/64

(admin:N/A) [root@operator-host .ssh]# kubectl get pod -n kube-system -o=custom-columns=NAME:.metadata.name,IP:.status.podIP,STATUS:.status.phase

NAME                              IP              STATUS
aws-node-dk9kl                    192.168.1.51    Running
aws-node-gqrbs                    192.168.3.46    Running
aws-node-hvlz5                    192.168.2.147   Running
coredns-9b5bc9468-g9hxl           192.168.1.95    Running
coredns-9b5bc9468-whh8w           192.168.1.17    Running
kube-proxy-l9cft                  192.168.3.46    Running
kube-proxy-mbmt2                  192.168.2.147   Running
kube-proxy-zq94c                  192.168.1.51    Running
metrics-server-86bbfd75bb-t6v7q   192.168.1.23    Running
metrics-server-86bbfd75bb-vdclj   192.168.1.225   Running

스크린샷 2025-02-11 오후 10.27.04

테스트용 파드 생성 및 확인

# 워커 노드 - 모니터링
watch -d "ip link | egrep 'eth|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"

# 작업용 EC2 - 파드 2개 생성
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: pod-1
  labels:
    app: pod
spec:
  containers:
  - name: netshoot-pod
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-2
  labels:
    app: pod
spec:
  containers:
  - name: netshoot-pod
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

# 파드 확인
kubectl get pod -o wide
(admin:N/A) [root@operator-host .ssh]# kubectl get pod -o wide
NAME    READY   STATUS              RESTARTS   AGE   IP       NODE                                               NOMINATED NODE   READINESS GATES
pod-1   0/1     ContainerCreating   0          7s    <none>   ip-192-168-3-46.ap-northeast-2.compute.internal    <none>           <none>
pod-2   0/1     ContainerCreating   0          7s    <none>   ip-192-168-2-147.ap-northeast-2.compute.internal   <none>           <none>


kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP
NAME    IP
pod-1   192.168.3.218
pod-2   192.168.2.215

파드간 통신

파드간 통신 흐름 : 별도의 오버레이(Overlay) 통신 기술 없이, VPC Native 하게 파드간 직접 통신이 가능하다.

  • pod 내부에 route 는 virtual router 인 169.254.1.1 를 통해 통신한다.
 pod-1  ~  ping 192.168.2.215
PING 192.168.2.215 (192.168.2.215) 56(84) bytes of data.
64 bytes from 192.168.2.215: icmp_seq=1 ttl=125 time=1.82 ms
64 bytes from 192.168.2.215: icmp_seq=2 ttl=125 time=1.47 ms
^C
--- 192.168.2.215 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 1.473/1.648/1.824/0.175 ms

 pod-1  ~  ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host proto kernel_lo
       valid_lft forever preferred_lft forever
3: eth0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
    link/ether de:8d:5b:09:d1:ea brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.3.218/32 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::dc8d:5bff:fe09:d1ea/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever

 pod-1  ~  routt -n
zsh: command not found: routt

 pod-1  ~  route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         169.254.1.1     0.0.0.0         UG    0      0        0 eth0
169.254.1.1     0.0.0.0         255.255.255.255 UH    0      0        0 eth0

파드 → 외부(인터넷) 통신

iptable 에 SNAT 을 통하여 노드의 eth0 IP로 변경되어서 외부와 통신됨

  • VPC CNI 의 External source network address translation (SNAT) 설정에 따라, 외부(인터넷) 통신 시 SNAT 하거나 혹은 SNAT 없이 통신을 할 수 있다.
  • 파드가 VPC peering, Transit VPC, or Direct Connect 등으로 연결된 곳과 통신 시 문제 발생 시 VPC CNI v1.8 이상으로 업데이트를 하자
# 작업용 EC2
# pod-1 Shell 실행
kubectl exec -it pod-2 -- zsh

# 아래부터는 pod-1 Shell 에서 실행 : 파드 통신 확인, 외부 통신 확인
----------------------------
ping -c 1 8.8.8.8   # 아래 tcpdump 시 실행
ping -i 0.1 8.8.8.8 # 아래 iptables 카운트 확인 시
curl ifconfig.co
...
# 모든 동작 확인 후 빠져나오기
exit
----------------------------

# 워커 노드 EC2
# TCPDUMP 확인
tcpdump -i any -nn icmp
tcpdump -i eth0 -nn icmp
tcpdump -i eth1 -nn icmp
tcpdump -i eniY -nn icmp

# 출력된 결과를 보고 어떻게 빠져나가는지 고민해보자!
ip rule
ip route show table main
iptables -L -n -v -t nat
iptables -t nat -S

# 파드가 외부와 통신시에는 아래 처럼 'AWS-SNAT-CHAIN-0, AWS-SNAT-CHAIN-1' 룰(rule)에 의해서 SNAT 되어서 외부와 통신!
# 참고로 뒤 IP는 eth0(ENI 첫번째)의 IP 주소이다
# --random-fully 동작 - 링크1  링크2
iptables -t nat -S | grep 'A AWS-SNAT-CHAIN'
-A AWS-SNAT-CHAIN-0 ! -d 192.168.0.0/16 -m comment --comment "AWS SNAT CHAIN" -j AWS-SNAT-CHAIN-1
-A AWS-SNAT-CHAIN-1 ! -o vlan+ -m comment --comment "AWS, SNAT" -m addrtype ! --dst-type LOCAL -j SNAT --to-source 192.168.1.64 --random-fully

## 아래 'mark 0x4000/0x4000' 매칭되지 않아서 RETURN 됨!
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
-A KUBE-POSTROUTING -j MARK --set-xmark 0x4000/0x0
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE --random-fully
...

# 카운트 확인 시 AWS-SNAT-CHAIN-0, AWS-SNAT-CHAIN-1 에 매칭되어, 목적지가 192.168.0.0/16 아니고 외부 빠져나갈때 SNAT 192.168.1.64 변경되어 나간다!
iptables -t filter --zero; iptables -t nat --zero; iptables -t mangle --zero; iptables -t raw --zero
watch -d 'iptables -v --numeric --table nat --list AWS-SNAT-CHAIN-0; echo ; iptables -v --numeric --table nat --list AWS-SNAT-CHAIN-1; echo ; iptables -v --numeric --table nat --list KUBE-POSTROUTING'

conntrack -L -n |grep -v '169.254.169'
icmp     1 29 src=192.168.1.249 dst=8.8.8.8 type=8 code=0 id=12386 src=8.8.8.8 dst=192.168.1.64 type=0 code=0 id=8247 mark=128 use=1

# 다음 실습을 위해서 파드 삭제
kubectl delete pod pod-1 pod-2

5. 노드에 파드 생성 갯수 제한

  • 인스턴스 타입 별 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 : 15개
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80
EOF
 kubectl scale deployment nginx-deployment --replicas=50
(admin:N/A) [root@operator-host ~]# k get pod | grep Pending
nginx-deployment-6c8cb99bb9-47f5g   0/1     Pending   0          16s
nginx-deployment-6c8cb99bb9-9qwmz   0/1     Pending   0          16s
nginx-deployment-6c8cb99bb9-9t9fk   0/1     Pending   0          16s
nginx-deployment-6c8cb99bb9-ft8fp   0/1     Pending   0          16s
nginx-deployment-6c8cb99bb9-kzmcj   0/1     Pending   0          16s
nginx-deployment-6c8cb99bb9-pdss6   0/1     Pending   0          16s
nginx-deployment-6c8cb99bb9-q4qdr   0/1     Pending   0          16s
nginx-deployment-6c8cb99bb9-rc2pt   0/1     Pending   0          16s
nginx-deployment-6c8cb99bb9-tm297   0/1     Pending   0          16s

6. Service & AWS LoadBalancer Controller

서비스/파드 배포 테스트 with NLB

# 모니터링
watch -d kubectl get pod,svc,ep,endpointslices

# 디플로이먼트 & 서비스 생성
cat << EOF > echo-service-nlb.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-echo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: deploy-websrv
  template:
    metadata:
      labels:
        app: deploy-websrv
    spec:
      terminationGracePeriodSeconds: 0
      containers:
      - name: aews-websrv
        image: k8s.gcr.io/echoserver:1.5
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: svc-nlb-ip-type
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
    service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
    service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "8080"
    service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
spec:
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP
  type: LoadBalancer
  loadBalancerClass: service.k8s.aws/nlb
  selector:
    app: deploy-websrv
EOF
kubectl apply -f echo-service-nlb.yaml


# 확인
aws elbv2 describe-load-balancers --query 'LoadBalancers[*].State.Code' --output text
kubectl get deploy,pod
kubectl get svc,ep,ingressclassparams,targetgroupbindings
kubectl get targetgroupbindings -o json | jq
# 빠른 실습을 위해서 등록 취소 지연(드레이닝 간격) 수정 : 기본값 300초
echo-service-nlb.yaml 파일 IDE(VS code)에서 수정
..
apiVersion: v1
kind: Service
metadata:
  name: svc-nlb-ip-type
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
    service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
    service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "8080"
    service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
    service.beta.kubernetes.io/aws-load-balancer-target-group-attributes: deregistration_delay.timeout_seconds=60
...
kubectl apply -f echo-service-nlb.yaml

# AWS ELB(NLB) 정보 확인
aws elbv2 describe-load-balancers | jq
aws elbv2 describe-load-balancers --query 'LoadBalancers[*].State.Code' --output text
ALB_ARN=$(aws elbv2 describe-load-balancers --query 'LoadBalancers[?contains(LoadBalancerName, `k8s-default-svcnlbip`) == `true`].LoadBalancerArn' | jq -r '.[0]')
aws elbv2 describe-target-groups --load-balancer-arn $ALB_ARN | jq
TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups --load-balancer-arn $ALB_ARN | jq -r '.TargetGroups[0].TargetGroupArn')
aws elbv2 describe-target-health --target-group-arn $TARGET_GROUP_ARN | jq
{
  "TargetHealthDescriptions": [
    {
      "Target": {
        "Id": "192.168.2.153",
        "Port": 8080,
        "AvailabilityZone": "ap-northeast-2b"
      },
      "HealthCheckPort": "8080",
      "TargetHealth": {
        "State": "initial",
        "Reason": "Elb.RegistrationInProgress",
        "Description": "Target registration is in progress"
      }
    },
...

# 웹 접속 주소 확인
kubectl get svc svc-nlb-ip-type -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' | awk '{ print "Pod Web URL = http://"$1 }'

# 파드 로깅 모니터링
kubectl logs -l app=deploy-websrv -f
kubectl stern -l  app=deploy-websrv

# 분산 접속 확인
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 NLB의 대상 그룹 확인 : IP를 확인해보자
  • 파드 2개 → 1개 → 3개 설정 시 동작 : auto discovery ← 어떻게 가능할까?
# (신규 터미널) 모니터링
while true; do aws elbv2 describe-target-health --target-group-arn $TARGET_GROUP_ARN --output text; echo; done

# 작업용 EC2 - 파드 1개 설정 
kubectl scale deployment deploy-echo --replicas=1

# 확인
kubectl get deploy,pod,svc,ep
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 --connect-timeout 1 $NLB | grep Hostname ; done | sort | uniq -c | sort -nr

# 파드 3개 설정
kubectl scale deployment deploy-echo --replicas=3

# 확인 : NLB 대상 타켓이 아직 initial 일 때 100번 반복 접속 시 어떻게 되는지 확인해보자!
kubectl get deploy,pod,svc,ep
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 --connect-timeout 1 $NLB | grep Hostname ; done | sort | uniq -c | sort -nr

# 
kubectl describe deploy -n kube-system aws-load-balancer-controller | grep -i 'Service Account'
  Service Account:  aws-load-balancer-controller

# [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

리소스 삭제

kubectl delete deploy deploy-echo; kubectl delete svc svc-nlb-ip-type

7. Ingress

# 게임 파드와 Service, Ingress 배포
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  name: game-2048
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: game-2048
  name: deployment-2048
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: app-2048
  replicas: 2
  template:
    metadata:
      labels:
        app.kubernetes.io/name: app-2048
    spec:
      containers:
      - image: public.ecr.aws/l6m2t8p7/docker-2048:latest
        imagePullPolicy: Always
        name: app-2048
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  namespace: game-2048
  name: service-2048
spec:
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
  type: NodePort
  selector:
    app.kubernetes.io/name: app-2048
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: game-2048
  name: ingress-2048
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
spec:
  ingressClassName: alb
  rules:
    - http:
        paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: service-2048
              port:
                number: 80
EOF

# 모니터링
watch -d kubectl get pod,ingress,svc,ep,endpointslices -n game-2048

# 생성 확인
kubectl get ingress,svc,ep,pod -n game-2048
kubectl get-all -n game-2048
kubectl get targetgroupbindings -n game-2048

# ALB 생성 확인
aws elbv2 describe-load-balancers --query 'LoadBalancers[?contains(LoadBalancerName, `k8s-game2048`) == `true`]' | jq
ALB_ARN=$(aws elbv2 describe-load-balancers --query 'LoadBalancers[?contains(LoadBalancerName, `k8s-game2048`) == `true`].LoadBalancerArn' | jq -r '.[0]')
aws elbv2 describe-target-groups --load-balancer-arn $ALB_ARN
TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups --load-balancer-arn $ALB_ARN | jq -r '.TargetGroups[0].TargetGroupArn')
aws elbv2 describe-target-health --target-group-arn $TARGET_GROUP_ARN | jq

# Ingress 확인
kubectl describe ingress -n game-2048 ingress-2048
kubectl get ingress -n game-2048 ingress-2048 -o jsonpath="{.status.loadBalancer.ingress[*].hostname}{'\n'}"

# 게임 접속 : ALB 주소로 웹 접속
kubectl get ingress -n game-2048 ingress-2048 -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' | awk '{ print "Game URL = http://"$1 }'

# 파드 IP 확인
kubectl get pod -n game-2048 -owide