> For the complete documentation index, see [llms.txt](https://aws.youngwjung.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://aws.youngwjung.com/eks/lab.md).

# 실습

## 실습환경 구성 <a href="#setting" id="setting"></a>

### CLI 도구 설치

1. 바이너리를 저장할 디렉토리 생성

   ```
   {
       mkdir ~/bin
       echo export PATH="$HOME/bin:$PATH" >> ~/.bashrc
       export PATH="$HOME/bin:$PATH"
   }
   ```
2. eksctl 설치&#x20;

   ```
   {
       curl -sL "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
       sudo mv /tmp/eksctl $HOME/bin
       echo 'source <(eksctl completion bash)' >>~/.bashrc
       source <(eksctl completion bash)
   }
   ```
3. helm 설치&#x20;

   ```
   curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | HELM_INSTALL_DIR=$HOME/bin bash
   ```
4. Krew 설치&#x20;

   ```
   {
       curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/krew-linux_amd64.tar.gz"
       mkdir krew && tar zvxf krew-linux_amd64.tar.gz -C krew
       ./krew/krew-linux_amd64 install krew
       rm krew-linux_amd64.tar.gz
       rm -rf krew
       echo export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH" >> ~/.bashrc
       export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"
   }
   ```

### EKS 클러스터 생성

1. ClusterConfig 생성

   ```
   cat <<EOF > mycluster.yaml
   apiVersion: eksctl.io/v1alpha5
   kind: ClusterConfig

   metadata:
     name: mycluster
     region: $AWS_REGION
     version: "1.32"

   availabilityZones:
   - ${AWS_REGION}a
   - ${AWS_REGION}b
   - ${AWS_REGION}c
   - ${AWS_REGION}d

   managedNodeGroups:
   - name: nodegroup
     instanceType: t3.small
     minSize: 2
     desiredCapacity: 2
     maxSize: 5
     volumeSize: 20
     iam:
       attachPolicyARNs:
       - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
       - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
       - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
       - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
   addonsConfig:
     disableDefaultAddons: true
   addons:
     - name: vpc-cni
     - name: kube-proxy
     - name: coredns
   EOF
   ```
2. EKS 클러스터 생성&#x20;

   ```
   eksctl create cluster --config-file=mycluster.yaml
   ```
3. 클러스터 생성 확인

   ```
   kubectl get node
   ```

## Authentication

1. *kubeconfig* 파일 리뷰

   ```
   kubectl config view
   ```
2. *kubeconfig*에 명시된 *users* 확인 - <https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins>

   ```
   kubectl config view -o=jsonpath='{.users}' | jq
   ```
3. *user*에 명시된 명령어 실행

   ```
   $(kubectl config view \
   -o=jsonpath='{.users[0].user.exec.command} {.users[0].user.exec.args[*]}')
   ```
4. 토큰값을 환경변수로 지정 - Base64로 인코딩된 부분만 캡쳐

   ```
   {
       export TOKEN=$($(kubectl config view \
       -o=jsonpath='{.users[0].user.exec.command} {.users[0].user.exec.args[*]}') \
       | jq -r '.status.token' | grep -oP '(?<=k8s-aws-v1.).*')
       echo $TOKEN
   }
   ```
5. Base64URL 유틸리티 설치

   ```
   {
       mkdir $HOME/node
       sudo npm install -g --prefix $HOME/node base64-url-cli
       echo export PATH="$HOME/node/bin:$PATH" >> ~/.bashrc
       export PATH="$HOME/node/bin:$PATH"
   }
   ```
6. 토큰값을 디코딩해서 환경변수로 지정

   ```
   {
       export DECODED_URL=$(base64url decode $TOKEN)
       echo $DECODED_URL 
   }
   ```
7. 디코딩한 URL 호출

   ```
   curl $DECODED_URL
   ```
8. HTTP 헤더에 클러스터 이름 추가해서 디코딩한 URL 호출

   ```
   {
       export CLUSTER_NAME=$(kubectl get node \
       -o=jsonpath='{.items[0].metadata.labels.alpha\.eksctl\.io\/cluster-name}')
       
       curl -H "x-k8s-aws-id: $CLUSTER_NAME" $DECODED_URL
   }

   ```
9. 현재 설정된 AWS 자격증명을 확인  &#x20;

   ```
   aws sts get-caller-identity
   ```
10. API 서버 주소를 확인하고 환경변수로 지정&#x20;

    ```
    {
        export K8S_SERVER=$(kubectl config view \
        -o=jsonpath='{.clusters[*].cluster.server}')
        echo $K8S_SERVER
    }
    ```
11. Node 목록을 보는 API 호출

    ```
    curl -k -X GET \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    $K8S_SERVER/api/v1/nodes
    ```
12. 토큰값을 환경변수로 지정

    ```
    {
        export TOKEN=$($(kubectl config view \
        -o=jsonpath='{.users[0].user.exec.command} {.users[0].user.exec.args[*]}') \
        | jq -r '.status.token')
        
        echo $TOKEN
    }
    ```
13. Node 목록을 보는 API 호출

    ```
    curl -k -X GET \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    $K8S_SERVER/api/v1/nodes
    ```
14. IAM 유저를 생성하고 Access Key 발급

    ```
    {
        aws iam create-user --user-name john
        aws iam create-access-key --user-name john > key.txt
        cat key.txt
    }
    ```
15. 위에서 생성한 Access Key를 AWS CLI 자격증명 파일에 반영

    ```
    {
    mkdir -p ~/.aws 
    touch ~/.aws/credentials
    touch ~/.aws/config
    cat <<EOF >> ~/.aws/credentials

    [john]
    aws_access_key_id=$(cat key.txt | jq -r '.AccessKey.AccessKeyId')
    aws_secret_access_key=$(cat key.txt | jq -r '.AccessKey.SecretAccessKey')
    EOF

    cat <<EOF >> ~/.aws/config

    [profile john]
    region = $AWS_REGION
    EOF
    }
    ```
16. 위에서 명시한 프로필을 통해서 AWS API 호출

    ```
    aws sts get-caller-identity --profile john
    ```
17. *kubeconfig* 파일 삭제

    ```
    rm -rf ~/.kube/config
    ```
18. 쿠버네티스 API 호출 시도

    ```
    kubectl get pod -A
    ```
19. 새로 생성한 IAM 유저의 자격증명으로 AWS CLI를 이용해서 kubeconfig 파일 생성

    ```
    aws eks update-kubeconfig --name $CLUSTER_NAME --profile john
    ```
20. IAM 유저에서 *eks:DescribeCluster* 권한 부여

    ```
    aws iam put-user-policy --user-name john --policy-name eks-describe --policy-document \
    '{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": [
                    "eks:DescribeCluster"
                ],
                "Resource": "*",
                "Effect": "Allow"
            }
        ]
    }'
    ```
21. 새로 생성한 IAM 유저의 자격증명으로 AWS CLI를 이용해서 kubeconfig 파일 생성 - *정책 적용까지 시간이 걸릴수 있음*

    ```
    aws eks update-kubeconfig --name $CLUSTER_NAME --profile john
    ```
22. 새로 생성된 kubeconfig 파일 리뷰

    ```
    kubectl config view
    ```
23. 쿠버네티스 API 호출 시도

    ```
    kubectl get pod -A
    ```
24. 인증 토큰 검증&#x20;

    ```
    {
        export AWS_PROFILE=john
        export TOKEN=$($(kubectl config view \
        -o=jsonpath='{.users[0].user.exec.command} {.users[0].user.exec.args[*]}') \
        | jq -r '.status.token' | grep -oP '(?<=k8s-aws-v1.).*')
        export DECODED_URL=$(base64url decode $TOKEN)
        curl -v -H "x-k8s-aws-id: $CLUSTER_NAME" $DECODED_URL
    }
    ```
25. EKS 클러스터를 생성한 IAM 유저의 자격증명으로 AWS CLI를 이용해서 kubeconfig 파일 업데이트&#x20;

    ```
    {
        rm -rf ~/.kube/config
        unset AWS_PROFILE
        aws eks update-kubeconfig --name $CLUSTER_NAME
    }
    ```

## Authorization

1. EKS 클러스터에 설정된 Access Entry 확인

   ```
   aws eks list-access-entries --cluster-name $CLUSTER_NAME
   ```
2. EKS 클러스터를 생성한 IAM 유저의 Access Entry 확인

   ```
   aws eks describe-access-entry --cluster-name $CLUSTER_NAME \
   --principal-arn $(aws sts get-caller-identity --query 'Arn' --output text)
   ```
3. AWS Workshop Studio를 통해서 로그인할 경우에는 IAM 역할을 사용하므로 아래의 명령어 실행

   ```
   aws eks describe-access-entry --cluster-name $CLUSTER_NAME \
   --principal-arn $(aws sts get-caller-identity --query 'Arn' --output text |\
   sed -e 's#assumed-role#role#' -e 's#/[^/]*$##' | sed -e 's#sts#iam#')
   ```
4. EKS 클러스터를 생성한 IAM 유저에 부여된 Access Policy 확인

   ```
   aws eks list-associated-access-policies --cluster-name $CLUSTER_NAME \
   --principal-arn $(aws sts get-caller-identity --query 'Arn' --output text)
   ```
5. AWS Workshop Studio를 통해서 로그인할 경우에는 IAM 역할을 사용하므로 아래의 명령어 실행

   ```
   aws eks list-associated-access-policies --cluster-name $CLUSTER_NAME \
   --principal-arn $(aws sts get-caller-identity --query 'Arn' --output text |\
   sed -e 's#assumed-role#role#' -e 's#/[^/]*$##' | sed -e 's#sts#iam#')
   ```
6. EKS Access Policy 목록 확인 - <https://docs.aws.amazon.com/eks/latest/userguide/access-policy-permissions.html>
7. 현재 kubeconfig에 설정된 유저의 자격증명으로 수행할수 있는 API 목록 확인

   ```
   kubectl auth can-i --list
   ```
8. EKS 클러스터에 설정된 Access Entry 확인

   ```
   aws eks list-access-entries --cluster-name $CLUSTER_NAME
   ```
9. Node에 부여된 IAM 역할을 확인하고 환경변수로 지정

   ```
   {
       export INSTANCE_ID=$(kubectl get node -o jsonpath='{.items[0].spec.providerID}' \
       | grep -oE "i-[a-z0-9]+")
       export INSTANCE_PROFILE=$(aws ec2 describe-instances --instance-ids $INSTANCE_ID \
       --query 'Reservations[0].Instances[0].IamInstanceProfile.Arn' \
       --output text | grep -oE "[a-z0-9-]+$")
       export INSTANCE_ROLE=$(aws iam get-instance-profile --instance-profile-name $INSTANCE_PROFILE \
       --query 'InstanceProfile.Roles[0].Arn' --output text)
       echo $INSTANCE_ROLE
   }
   ```
10. Node에 부여된 IAM 역할의 Access Entry 확인

    ```
    aws eks describe-access-entry --cluster-name $CLUSTER_NAME \
    --principal-arn $INSTANCE_ROLE
    ```
11. Node에 부여된 IAM 역할에 부여된 Access Policy 확인

    ```
    aws eks list-associated-access-policies --cluster-name $CLUSTER_NAME \
    --principal-arn $INSTANCE_ROLE
    ```
12. Kubernetes RBAC을 쉽게 확인할수 있는 플러그인 설치

    ```
    kubectl krew install rbac-tool
    ```
13. Node에 부여된 IAM 역할에 연동된 쿠버네티스 그룹에 부여된 권한 확인

    ```
    kubectl rbac-tool lookup -e system:nodes
    ```
14. *eks:node-bootstrapper* ClusterRole 리뷰

    ```
    kubectl describe clusterrole eks:node-bootstrapper
    ```
15. 위에서 새로 생성한 IAM 유저에 EKS Access Entry 생성&#x20;

    ```
    {
        export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
        aws eks create-access-entry --cluster-name $CLUSTER_NAME \
        --principal-arn arn:aws:iam::$ACCOUNT_ID:user/john
    }
    ```
16. IAM 유저에서 kube-system 네임스페이스에 대한 읽기 권한을 부여

    ```
    aws eks associate-access-policy \
    --cluster-name $CLUSTER_NAME \
    --principal-arn arn:aws:iam::$ACCOUNT_ID:user/john \
    --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSAdminViewPolicy \
    --access-scope type=namespace,namespaces=kube-system
    ```
17. IAM 유저의 자격증명으로 AWS CLI를 이용해서 kubeconfig 파일 업데이트

    ```
    {
        rm -rf ~/.kube/config
        aws eks update-kubeconfig --name $CLUSTER_NAME --profile john
    }
    ```
18. 클러스터에 생성된 모든 Pod 목록 확인

    ```
    kubectl get pod -A 
    ```
19. kube-system 네임스페이스에 생성된 모든 Pod 목록 확인

    ```
    kubectl get pod -n kube-system
    ```
20. kube-system 네임스페이스에 Pod 생성 시도&#x20;

    ```
    kubectl run nginx --image=nginx -n kube-system
    ```
21. IAM 역할 생성

    ```
    {
    cat > trust-policy.json <<EOF
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "AWS": [
                        "arn:aws:iam::$ACCOUNT_ID:root"
                    ]
                },
                "Action": "sts:AssumeRole"
            }
        ]
    }
    EOF
    aws iam create-role --role-name eks-admin-role \
    --assume-role-policy-document file://trust-policy.json
    }
    ```
22. 위에서 새로 생성한 IAM 역할에 EKS Access Entry 생성&#x20;

    ```
    aws eks create-access-entry \
    --cluster-name $CLUSTER_NAME \
    --principal-arn arn:aws:iam::$ACCOUNT_ID:role/eks-admin-role
    ```
23. IAM 역할에 EKS 클러스터 어드민 권한 부여

    ```
    aws eks associate-access-policy \
    --cluster-name $CLUSTER_NAME \
    --principal-arn arn:aws:iam::$ACCOUNT_ID:role/eks-admin-role \
    --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy \
    --access-scope type=cluster
    ```
24. IAM 유저의 자격증명으로 위에서 생성한 IAM 역할 전환하는 설정 추가해서 kubeconfig 파일 업데이트&#x20;

    ```
    {
        rm -rf ~/.kube/config
        aws eks update-kubeconfig --name $CLUSTER_NAME --profile john \
        --role-arn arn:aws:iam::$ACCOUNT_ID:role/eks-admin-role
    }
    ```
25. 새로 생성된 kubeconfig 파일 리뷰

    ```
    kubectl config view
    ```
26. 쿠버네티스 API 호출 시도

    ```
    kubectl get pod -A
    ```
27. IAM 유저에서 *sts:AssumeRole* 권한 부여

    ```
    aws iam put-user-policy --user-name john --policy-name assume-role --policy-document \
    '{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": [
                    "sts:AssumeRole"
                ],
                "Resource": "*",
                "Effect": "Allow"
            }
        ]
    }'
    ```
28. 쿠버네티스 API 호출 시도 - *정책 적용까지 시간이 걸릴수 있음*

    ```
    kubectl get pod -A
    ```
29. EKS 인증 토큰 확인&#x20;

    ```
    {
        export AWS_PROFILE=john
        export TOKEN=$($(kubectl config view \
        -o=jsonpath='{.users[0].user.exec.command} {.users[0].user.exec.args[*]}') \
        | jq -r '.status.token' | grep -oP '(?<=k8s-aws-v1.).*')
        export DECODED_URL=$(base64url decode $TOKEN)
        curl -H "x-k8s-aws-id: $CLUSTER_NAME" $DECODED_URL
    }
    ```
30. AWS 관리콘솔에 로그인된 IAM 자격증명으로 AWS CLI를 이용해서 kubeconfig 파일 업데이트&#x20;

    ```
    {
        rm -rf ~/.kube/config
        unset AWS_PROFILE
        aws eks update-kubeconfig --name $CLUSTER_NAME
    }
    ```
31. 리소스 삭제&#x20;

    ```
    {
        aws iam delete-access-key --user-name john --access-key-id $(cat key.txt | jq -r '.AccessKey.AccessKeyId')
        aws iam delete-user-policy --user-name john --policy-name eks-describe
        aws iam delete-user-policy --user-name john --policy-name assume-role
        aws iam delete-user --user-name john
        aws iam delete-role --role-name eks-admin-role
        aws eks delete-access-entry --cluster-name $CLUSTER_NAME \
        --principal-arn arn:aws:iam::$ACCOUNT_ID:user/john
        aws eks delete-access-entry \
        --cluster-name $CLUSTER_NAME \
        --principal-arn arn:aws:iam::$ACCOUNT_ID:role/eks-admin-role
        rm ~/.aws/credentials ~/.aws/config 
        rm key.txt trust-policy.json
    }
    ```

## Scheduling

### Manual Scheduling

1. Pod 생성&#x20;

   ```
   cat <<EOF | kubectl apply -f -
   apiVersion: v1
   kind: Pod
   metadata:
     name: nginx
   spec:
     containers:
     - image: nginx
       name: nginx
     nodeName: worker-1
   EOF
   ```
2. Pod 상태 확인&#x20;

   ```
   kubectl get pod nginx
   ```
3. 위의 명령어를 실행하면 아래와 같은 결과를 확인할수 있습니다.&#x20;

   ```
   NAME         READY   STATUS    RESTARTS   AGE
   nginx        0/1     Pending   0          4s
   ```
4. Pod 상태를 모니터링&#x20;

   ```
   watch kubectl get pod nginx
   ```
5. Kubernetes 공식문서 리뷰 - <https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename>
6. Pod Garbage Collector 코드 리뷰 - <https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/podgc/gc_controller.go#L223>
7. 노드 목록 확인&#x20;

   ```
   kubectl get node
   ```
8. *spec.nodeName*에 위의 명령어의 결과로 나온 첫번째 노드 이름을 넣고 Pod를 생성&#x20;

   ```
   cat <<EOF | kubectl apply -f -
   apiVersion: v1
   kind: Pod
   metadata:
     name: nginx
   spec:
     containers:
     - image: nginx
       name: nginx
     nodeName: $(kubectl get node -o=jsonpath='{.items[0].metadata.name}')
   EOF
   ```
9. Pod 상태 확인&#x20;

   ```
   kubectl get pod nginx
   ```
10. *spec.nodeName*을 명시하지 않고 Pod 생성

    ```
    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
      name: httpd
    spec:
      containers:
      - image: httpd
        name: httpd
    EOF
    ```
11. *spec.nodeName*을 명시하지 않고 생성한 Pod에 발생한 Event 확인&#x20;

    ```
    kubectl describe pod httpd
    ```
12. *spec.nodeName*을 명시하고 생성한 Pod에 발생한 Event 확인&#x20;

    ```
    kubectl describe pod nginx
    ```
13. Pod 삭제&#x20;

    ```
    kubectl delete pod nginx httpd
    ```
14. kube-scheduler 코드 리뷰 - <https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/schedule_one.go#L64>

### Node Selector

1. Pod 생성&#x20;

   ```
   cat <<EOF | kubectl apply -f -
   apiVersion: v1
   kind: Pod
   metadata:
     name: nginx
   spec:
     containers:
     - image: nginx
       name: nginx
     nodeSelector:
       env: dev
   EOF
   ```
2. Pod 상태 확인&#x20;

   ```
   kubectl get pod nginx
   ```
3. 위의 명령어를 실행하면 아래와 같은 결과를 확인할수 있습니다.&#x20;

   ```
   NAME          READY   STATUS    RESTARTS   AGE
   nginx         0/1     Pending   0          16s
   ```
4. 아래의 명령어를 사용해서 Pod가 배포되지 않는 이유를 찾으세요.&#x20;

   ```
   kubectl describe pod nginx
   ```
5. Node에 부여된 Label 확인&#x20;

   ```
   kubectl get node --show-labels
   ```
6. 두번째 노드에 키(Key)는 *env* 이고 값(Value)은 *dev* 인 Label 부여&#x20;

   ```
   {
       kubectl label node \
       $(kubectl get node -o=jsonpath='{.items[1].metadata.name}') env=dev
       kubectl get node --show-labels | grep -E 'env=dev|$'
   }
   ```
7. Pending 상태였던 Pod가 배포됐는지 확인&#x20;

   ```
   kubectl get pod nginx
   ```
8. Pod에 발생한 Event 확인&#x20;

   ```
   kubectl describe pod nginx
   ```
9. Node에 부여한 Label 삭제&#x20;

   ```
   kubectl label node \
   $(kubectl get node -o=jsonpath='{.items[1].metadata.name}') env-
   ```
10. Node에 Label이 삭제되었는지 확인

    ```
    kubectl get node --show-labels | grep -E 'env=dev|$'
    ```
11. Pod 상태 확인&#x20;

    ```
    kubectl get pod nginx
    ```
12. Pod 삭제&#x20;

    ```
    kubectl delete pod nginx
    ```

### Spread Pod Across Cluster

1. Node에 부여된 Label을 통해서 Node가 생성된 가용영역 확인

   ```
   kubectl get node \
   -o custom-columns=NAME:.metadata.name,AvailabilityZone:".metadata.labels.topology\.kubernetes\.io/zone"
   ```
2. *nodeSelector*를 명시한 Pod 생성

   ```
   for s in web api storage database
   do
     cat <<EOF | kubectl apply -f -
     apiVersion: v1
     kind: Pod
     metadata:
       name: ${s}
       labels:
         app: myapp
     spec:
       nodeSelector:
         topology.kubernetes.io/zone: $(kubectl get node -o=jsonpath='{.items[0].metadata.labels.topology\.kubernetes\.io/zone}')
       containers:
       - image: busybox
         name: busybox
         command: ["/bin/sleep"]
         args: ["infinity"]
   EOF
   done
   ```
3. 각 Pod가 배포된 Node 확인

   ```
   kubectl get pod -l app=myapp -o wide -A --sort-by=.spec.nodeName
   ```
4. 각 Node별로 배포된 Pod 갯수 확인

   ```
   kubectl describe node | grep -E "(^Name:|^Non-terminated)"
   ```
5. *nodeSelector*를 명시하지 않은 Pod 생성

   ```
   for i in {1..4}
   do
     cat <<EOF | kubectl apply -f -
     apiVersion: v1
     kind: Pod
     metadata:
       name: nginx-${i}
       labels:
         app: nginx
     spec:
       containers:
       - image: nginx
         name: nginx
   EOF
   done
   ```
6. 각 Pod가 배포된 Node 확인

   ```
   kubectl get pod -l app=nginx -o wide --sort-by=.spec.nodeName
   ```
7. 각 Node별로 배포된 Pod 갯수 확인

   ```
   kubectl describe node | grep -E "(^Name:|^Non-terminated)"
   ```
8. 위에서 *nodeSelector*를 명시하지 않고 생성한 Pod 삭제

   ```
   kubectl delete pod -l app=nginx
   ```
9. 각 Node별로 배포된 Pod 갯수 확인

   ```
   kubectl describe node | grep -E "(^Name:|^Non-terminated)"
   ```
10. Deployment 생성

    ```
    cat <<EOF | kubectl apply -f -
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: nginx
      name: nginx
    spec:
      replicas: 4
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - image: nginx
            name: nginx
    EOF
    ```
11. Deployment를 통해서 생성된 Pod들이 배포된 Node 확인

    ```
    kubectl get pod -l app=nginx -o wide --sort-by=.spec.nodeName
    ```
12. EKS 설정창에서 Scheduler의 로깅을 활성화하면 CloudWatch를 통해서 아래와 같은 구성으로 *kube-scheduler*가 구동되는 것을 확인 가능

    ```
    apiVersion: kubescheduler.config.k8s.io/v1beta1
    clientConnection:
      acceptContentTypes: ""
      burst: 100
      contentType: application/vnd.kubernetes.protobuf
      kubeconfig: /etc/kubernetes/scheduler.conf
      qps: 50
    enableContentionProfiling: true
    enableProfiling: true
    healthzBindAddress: 0.0.0.0:10251
    kind: KubeSchedulerConfiguration
    leaderElection:
      leaderElect: true
      leaseDuration: 15s
      renewDeadline: 10s
      resourceLock: leases
      resourceName: kube-scheduler
      resourceNamespace: kube-system
      retryPeriod: 2s
    metricsBindAddress: 0.0.0.0:10251
    parallelism: 16
    percentageOfNodesToScore: 0
    podInitialBackoffSeconds: 1
    podMaxBackoffSeconds: 10
    profiles:
    - pluginConfig:
      - args:
          apiVersion: kubescheduler.config.k8s.io/v1beta1
          kind: DefaultPreemptionArgs
          minCandidateNodesAbsolute: 100
          minCandidateNodesPercentage: 10
        name: DefaultPreemption
      - args:
          apiVersion: kubescheduler.config.k8s.io/v1beta1
          hardPodAffinityWeight: 1
          kind: InterPodAffinityArgs
        name: InterPodAffinity
      - args:
          apiVersion: kubescheduler.config.k8s.io/v1beta1
          kind: NodeAffinityArgs
        name: NodeAffinity
      - args:
          apiVersion: kubescheduler.config.k8s.io/v1beta1
          kind: NodeResourcesFitArgs
        name: NodeResourcesFit
      - args:
          apiVersion: kubescheduler.config.k8s.io/v1beta1
          kind: NodeResourcesLeastAllocatedArgs
          resources:
          - name: cpu
            weight: 1
          - name: memory
            weight: 1
        name: NodeResourcesLeastAllocated
      - args:
          apiVersion: kubescheduler.config.k8s.io/v1beta1
          defaultingType: System
          kind: PodTopologySpreadArgs
        name: PodTopologySpread
      - args:
          apiVersion: kubescheduler.config.k8s.io/v1beta1
          bindTimeoutSeconds: 600
          kind: VolumeBindingArgs
        name: VolumeBinding
      plugins:
        bind:
          enabled:
          - name: DefaultBinder
            weight: 0
        filter:
          enabled:
          - name: NodeUnschedulable
            weight: 0
          - name: NodeName
            weight: 0
          - name: TaintToleration
            weight: 0
          - name: NodeAffinity
            weight: 0
          - name: NodePorts
            weight: 0
          - name: NodeResourcesFit
            weight: 0
          - name: VolumeRestrictions
            weight: 0
          - name: EBSLimits
            weight: 0
          - name: GCEPDLimits
            weight: 0
          - name: NodeVolumeLimits
            weight: 0
          - name: AzureDiskLimits
            weight: 0
          - name: VolumeBinding
            weight: 0
          - name: VolumeZone
            weight: 0
          - name: PodTopologySpread
            weight: 0
          - name: InterPodAffinity
            weight: 0
        permit: {}
        postBind: {}
        postFilter:
          enabled:
          - name: DefaultPreemption
            weight: 0
        preBind:
          enabled:
          - name: VolumeBinding
            weight: 0
        preFilter:
          enabled:
          - name: NodeResourcesFit
            weight: 0
          - name: NodePorts
            weight: 0
          - name: PodTopologySpread
            weight: 0
          - name: InterPodAffinity
            weight: 0
          - name: VolumeBinding
            weight: 0
          - name: NodeAffinity
            weight: 0
        preScore:
          enabled:
          - name: InterPodAffinity
            weight: 0
          - name: PodTopologySpread
            weight: 0
          - name: TaintToleration
            weight: 0
          - name: NodeAffinity
            weight: 0
        queueSort:
          enabled:
          - name: PrioritySort
            weight: 0
        reserve:
          enabled:
          - name: VolumeBinding
            weight: 0
        score:
          enabled:
          - name: NodeResourcesBalancedAllocation
            weight: 1
          - name: ImageLocality
            weight: 1
          - name: InterPodAffinity
            weight: 1
          - name: NodeResourcesLeastAllocated
            weight: 1
          - name: NodeAffinity
            weight: 1
          - name: NodePreferAvoidPods
            weight: 10000
          - name: PodTopologySpread
            weight: 2
          - name: TaintToleration
            weight: 1
      schedulerName: default-scheduler
    ```
13. [Pod Topology Spread Constraints](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) 리뷰
14. Deployment 삭제

    ```
    kubectl delete deploy nginx
    ```
15. 각 Node별로 배포된 Pod 갯수 확인

    ```
    kubectl describe node | grep -E "(^Name:|^Non-terminated)"
    ```
16. podAntiAffinity를 명시하고 Pod 생성

    ```
    for i in {1..4}
    do
      cat <<EOF | kubectl apply -f -
      apiVersion: v1
      kind: Pod
      metadata:
        name: nginx-${i}
        labels:
          app: nginx
      spec:
        affinity:
          podAntiAffinity:
            preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                  - key: app
                    operator: In
                    values:
                    - nginx
                topologyKey: kubernetes.io/hostname
        containers:
        - image: nginx
          name: nginx
    EOF
    done
    ```
17. 위에서 생성한 Pod들이 배포된 Node 확인

    ```
    kubectl get pod -l app=nginx -o wide --sort-by=.spec.nodeName
    ```
18. 위에서 생성한 Pod 삭제

    ```
    kubectl delete pod -l app=nginx
    ```
19. *topologySpreadConstraints*를 명시하고 Pod 생성&#x20;

    ```
    for i in {1..4}
    do
      cat <<EOF | kubectl apply -f -
      apiVersion: v1
      kind: Pod
      metadata:
        name: nginx-${i}
        labels:
          app: nginx
      spec:
        topologySpreadConstraints:
        - maxSkew: 3
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: ScheduleAnyway
          labelSelector:
            matchLabels:
              app: nginx
        containers:
        - image: nginx
          name: nginx
    EOF
    done
    ```
20. 위에서 생성한 Pod들이 배포된 Node 확인

    ```
    kubectl get pod -l app=nginx -o wide --sort-by=.spec.nodeName
    ```
21. 리소스 삭제

    ```
    kubectl delete pod -l 'app in (nginx,myapp)'
    ```

## Scaling

### HPA (Horizontal Pod Autoscaler)

1. 데모 애플리케이션 배포 - <https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#run-and-expose-php-apache-server>

   ```
   cat <<EOF | kubectl apply -f -
   apiVersion: apps/v1
   kind: Deployment
   metadata:
     name: php-apache
   spec:
     selector:
       matchLabels:
         app: php-apache
     replicas: 1
     template:
       metadata:
         labels:
           app: php-apache
       spec:
         containers:
         - name: php-apache
           image: k8s.gcr.io/hpa-example
           ports:
           - containerPort: 80
           resources:
             limits:
               cpu: 500m
             requests:
               cpu: 200m
   ---
   apiVersion: v1
   kind: Service
   metadata:
     name: php-apache
     labels:
       app: php-apache
   spec:
     ports:
     - port: 80
     selector:
       app: php-apache
   EOF
   ```
2. Pod의 리소스 사용량 확인

   ```
   kubectl top pod -l app=php-apache
   ```
3. Metrics Server 설치 - <https://github.com/kubernetes-sigs/metrics-server#kubernetes-metrics-server>

   ```
   {
       export CLUSTER_NAME=$(kubectl get node \
       -o=jsonpath='{.items[0].metadata.labels.alpha\.eksctl\.io\/cluster-name}')
       eksctl create addon --name metrics-server --cluster $CLUSTER_NAME
   }
   ```
4. Metrics Server 설치 확인&#x20;

   ```
   kubectl get pod -n kube-system -l app.kubernetes.io/name=metrics-server
   ```
5. API 서버에 등록된 API 목록 확인

   ```
   kubectl get apiservices.apiregistration.k8s.io
   ```
6. *v1beta1.metrics.k8s.io* API의 상세 내용 확인&#x20;

   ```
   kubectl describe apiservices.apiregistration.k8s.io v1beta1.metrics.k8s.io
   ```
7. 모든 Pod의 리소스 사용량 확인&#x20;

   ```
   kubectl top pod -A
   ```
8. 모든 Node의 리소스 사용량 확인&#x20;

   ```
   kubectl top node
   ```
9. Metrics Server 로그 확인

   ```
   kubectl -n kube-system logs deploy/metrics-server
   ```
10. Metrics Server 로그 레벨 변경&#x20;

    ```
    kubectl -n kube-system patch deployment metrics-server --type=json \
    -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--v=6"}]'
    ```
11. Metrics Server 로그 확인 - *새로운 Pod가 뜬 다음에 확인*

    ```
    kubectl -n kube-system logs deploy/metrics-server
    ```
12. kubelet에서 제공하는 지표에 대한 접근 권한을 가진 Pod 생성&#x20;

    ```
    cat <<EOF | kubectl apply -f -
    kind: ClusterRole
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: kubelet-access
    rules:
    - apiGroups: [""]
      resources: 
        - nodes/stats
        - nodes/metrics
      verbs: ["get"]
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: kubelet-access
    ---
    kind: ClusterRoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: kubelet-access
      namespace: default
    subjects:
    - kind: ServiceAccount
      name: kubelet-access
      namespace: default
    roleRef:
      kind: ClusterRole
      name: kubelet-access
      apiGroup: rbac.authorization.k8s.io
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: curl
    spec:
      serviceAccountName: kubelet-access
      containers:
      - image: curlimages/curl
        name: curl
        command: ["sleep", "3600"]
        env:
          - name: HOST_IP
            valueFrom:
              fieldRef:
                fieldPath: status.hostIP
    EOF
    ```
13. 위에서 생성한 Pod에서 Metrics Server가 지표를 수집할때 호출하는 Endpoint 호출 - <https://github.com/kubernetes-sigs/metrics-server/blob/4436807eec6b07ea649444529eb3b46ddbbd8914/pkg/scraper/client/resource/client.go#L77>

    ```
    kubectl exec curl -- \
    sh -c 'curl -s -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" https://$HOST_IP:10250/metrics/resource -k'
    ```
14. kubelet에서 제공하는 모든 지표 확인

    ```
    kubectl exec curl -- \
    sh -c 'curl -s -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" https://$HOST_IP:10250/stats/summary -k'
    ```
15. kubelet에서 제공하는 지표중에서 CPU 및 Memory에 대한 지표만 확인

    ```
    kubectl exec curl -- \
    sh -c 'curl -s -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" https://$HOST_IP:10250/stats/summary?only_cpu_and_memory=true -k'
    ```
16. API 서버를 통해서 Metrics Server가 지표를 수집할때 호출하는 Endpoint 호출

    ```
    kubectl get --raw /api/v1/nodes/$(kubectl get node -o=jsonpath='{.items[0].metadata.name}')/proxy/metrics/resource
    ```
17. Autoscaling (HPA)설정&#x20;

    ```
    kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=5
    ```
18. 위에서 생성한 HPA 상태 확인&#x20;

    ```
    kubectl get hpa php-apache
    ```
19. 데모 애플리케이션에 부하를 발생시키는 Pod 생성

    ```
    kubectl run load-generator --image=busybox:1.28 -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"
    ```
20. HPA 상태 모니터링

    ```
    kubectl get hpa php-apache -w
    ```
21. Ctrl+C를 입력해서 HPA 모니터링을 중지하고 실제로 Pod가 생겼는지 확인&#x20;

    ```
    kubectl get pod -l app=php-apache
    ```
22. Pod의 리소스 사용량 확인&#x20;

    ```
    kubectl top pod -l app=php-apache
    ```
23. 데모 애플리케이션에 부하를 발생시키는 Pod 삭제

    ```
    kubectl delete pod load-generator --now
    ```
24. HPA 상태 모니터링

    ```
    kubectl get hpa php-apache -w
    ```
25. Ctrl+C를 입력해서 HPA 모니터링을 중지하고 HPA 상세 내용 확인

    ```
    kubectl describe hpa php-apache
    ```
26. Pod의 리소스 사용량 확인&#x20;

    ```
    kubectl top pod -l app=php-apache
    ```
27. HPA 스케일링 동작방식 확인 - <https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior>
28. stabilizationWindowSeconds의 기본값 확인&#x20;

    ```
    kubectl explain hpa.spec.behavior.scaleDown.stabilizationWindowSeconds
    ```
29. stabilizationWindowSeconds의 값을 60으로 조정&#x20;

    ```
    kubectl patch hpa php-apache -p '{"spec": {"behavior": {"scaleDown": {"stabilizationWindowSeconds": 60}}}}'
    ```
30. HPA 상태 확인

    ```
    kubectl describe hpa php-apache
    ```
31. Pod 갯수 확인

    ```
    kubectl get pod -l app=php-apache
    ```
32. 데모 애플리케이션 및 리소스 삭제&#x20;

    ```
    {
        kubectl delete hpa php-apache
        kubectl delete deployment php-apache
        kubectl delete svc php-apache
        kubectl delete pod curl --now
        kubectl delete clusterrole kubelet-access
        kubectl delete clusterrolebinding kubelet-access
        kubectl delete sa kubelet-access
    }
    ```

### Cluster Autoscaler

1. Deployment 생성&#x20;

   ```
   cat <<EOF | kubectl apply -f -
   apiVersion: apps/v1
   kind: Deployment
   metadata:
     name: nginx
     labels:
       app: nginx
   spec:
     replicas: 5
     selector:
       matchLabels:
         app: nginx
     template:
       metadata:
         labels:
           app: nginx
       spec:
         containers:
         - name: nginx
           image: nginx
           ports:
           - containerPort: 80
           resources:
             requests:
               cpu: 1
               memory: 1Gi
             limits:
               cpu: 2
               memory: 2Gi
   EOF
   ```
2. 생성된 Deployment 및 Pod 확인&#x20;

   ```
   kubectl get deploy,pod -l app=nginx
   ```
3. Pending 상태의 Pod가 있다면 아래의 명령어를 통해서 그 이유를 확인

   ```
   kubectl describe pod \
   $(kubectl get pod -o=jsonpath='{.items[?(@.status.phase=="Pending")].metadata.name}')
   ```
4. Cluster Autoscaler에게 부여할 IAM 정책 생성 &#x20;

   ```
   cat <<EOF > cluster-autoscaler-policy.json
   {
       "Version": "2012-10-17",
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "autoscaling:DescribeAutoScalingGroups",
                   "autoscaling:DescribeAutoScalingInstances",
                   "autoscaling:DescribeLaunchConfigurations",
                   "autoscaling:DescribeScalingActivities",
                   "ec2:DescribeImages",
                   "ec2:DescribeInstanceTypes",
                   "ec2:DescribeLaunchTemplateVersions",
                   "ec2:GetInstanceTypesFromInstanceRequirements",
                   "eks:DescribeNodegroup"
               ],
               "Resource": [
                   "*"
               ]
           },
           {
               "Effect": "Allow",
               "Action": [
                   "autoscaling:SetDesiredCapacity",
                   "autoscaling:TerminateInstanceInAutoScalingGroup"
               ],
               "Resource": [
                   "*"
               ]
           }
       ]
   }
   EOF

   aws iam create-policy \
   --policy-name AmazonEKSClusterAutoscalerPolicy \
   --policy-document file://cluster-autoscaler-policy.json
   ```
5. IAM OIDC 제공자 활성화&#x20;

   ```
   {
       export CLUSTER_NAME=$(kubectl get node \
       -o=jsonpath='{.items[0].metadata.labels.alpha\.eksctl\.io\/cluster-name}')
       export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
       
       eksctl utils associate-iam-oidc-provider --region=$AWS_REGION \
       --cluster=$CLUSTER_NAME --approve
   }
   ```
6. ServiceAccount 생성&#x20;

   ```
   eksctl create iamserviceaccount \
   --cluster=$CLUSTER_NAME \
   --namespace=kube-system \
   --name=cluster-autoscaler \
   --attach-policy-arn=arn:aws:iam::${ACCOUNT_ID}:policy/AmazonEKSClusterAutoscalerPolicy \
   --override-existing-serviceaccounts \
   --approve \
   --region $AWS_REGION
   ```
7. ServiceAccount가 생성되었는지 확인 - Annotations 확인

   ```
   kubectl -n kube-system describe sa cluster-autoscaler
   ```
8. ServiceAccount와 연동된 IAM 역할에 부여된 IAM 정책 확인

   ```
   {
      IAM_ROLE_NAME=$(kubectl -n kube-system get sa cluster-autoscaler \
      -o=jsonpath='{.metadata.annotations.eks\.amazonaws\.com\/role-arn}' \
      | grep -oP '(?<=role.).*')
    
      aws iam list-attached-role-policies --role-name $IAM_ROLE_NAME  
   }
   ```
9. ServiceAccount와 연동된 IAM 역할에 부여된 신뢰관계 정책 확인

   ```
   aws iam get-role --role-name $IAM_ROLE_NAME
   ```
10. Cluster Autoscaler 설치&#x20;

    ```
    kubectl apply -f https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/aws/examples/cluster-autoscaler-autodiscover.yaml
    ```
11. Cluster Autoscaler 로그 확인 - *ASG map 확인*

    ```
    kubectl -n kube-system logs deploy/cluster-autoscaler
    ```
12. Cluster Autoscaler 설정값 확인

    ```
    kubectl -n kube-system get deploy cluster-autoscaler \
    -o=jsonpath='{.spec.template.spec.containers[*].command}' | jq
    ```
13. EKS 노드그룹와 연동된 오토스케일링 그룹의 인스턴스 현황 확인

    ```
    {
        export ASG_NAME=$(aws autoscaling describe-auto-scaling-groups --query \
        "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='$CLUSTER_NAME']].AutoScalingGroupName" --output text)
        
        aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names $ASG_NAME \
        --query "AutoScalingGroups[0].{MinSize: MinSize, MaxSize: MaxSize, DesiredCapacity: DesiredCapacity}"
    }
    ```
14. 오토스케일링 그룹에 부여된 태그 확인

    ```
    aws autoscaling describe-auto-scaling-groups \
    --auto-scaling-group-names $ASG_NAME --query "AutoScalingGroups[0].Tags"
    ```
15. Node 갯수 확인

    ```
    kubectl get node
    ```
16. Cluster Autoscaler 설정값 수정&#x20;

    ```
    kubectl -n kube-system patch deployment cluster-autoscaler --type=json \
    -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/command", "value": [
    "./cluster-autoscaler",
    "--v=4",
    "--stderrthreshold=info",
    "--cloud-provider=aws",
    "--skip-nodes-with-local-storage=false",
    "--expander=least-waste",
    "--node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/'${CLUSTER_NAME}'",
    "--balance-similar-node-groups",
    "--skip-nodes-with-system-pods=false"
    ]}]'
    ```
17. Cluster Autoscaler 로그 확인 - 오토스케일링그룹이 정상 등록되었는지 확인

    ```
    kubectl -n kube-system logs deploy/cluster-autoscaler
    ```
18. Pending 상태였던 Pod가 생성 되었는지 확인

    ```
    kubectl get pod -l app=nginx
    ```
19. Node 갯수 확인

    ```
    kubectl get node
    ```
20. 오토스케일링 그룹의 인스턴스 현황 확인

    ```
    aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names $ASG_NAME \
    --query "AutoScalingGroups[0].{MinSize: MinSize, MaxSize: MaxSize, DesiredCapacity: DesiredCapacity}" 
    ```
21. 오토스케일링 그룹의 활동 로그 확인

    ```
    aws autoscaling describe-scaling-activities --auto-scaling-group-name $ASG_NAME
    ```
22. Deployment 삭제&#x20;

    ```
    kubectl delete deployment nginx
    ```
23. Pod 목록 확인

    ```
    kubectl get pod -l app=nginx
    ```
24. Node가 삭제 되는지 확인

    ```
    kubectl get node
    ```
25. Cluster Autoscaler 로그 확인

    ```
    kubectl -n kube-system logs deploy/cluster-autoscaler
    ```
26. Cluster Autoscaler 스케일 다운 동작방식 리뷰 - <https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#how-does-scale-down-work>
27. Cluster Autoscaler에 의해서 삭제 되지 않는 Node 확인 - <https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#what-types-of-pods-can-prevent-ca-from-removing-a-node>

## Exposing

### Service

1. Deployment 생성&#x20;

   ```
   cat <<'EOF' | kubectl apply -f -
   apiVersion: apps/v1
   kind: Deployment
   metadata:
     name: nginx
   spec:
     replicas: 3
     selector:
       matchLabels:
         app: nginx
     template:
       metadata:
         labels:
           app: nginx
       spec:
         containers:
         - name: nginx
           image: nginx
           ports:
           - containerPort: 80
           volumeMounts:
           - name: html
             mountPath: /usr/share/nginx/html
         initContainers:
         - name: index
           image: curlimages/curl
           command:
           - "sh"
           - "-c"
           - "echo 'hello from $(POD_NAME), my ip is $(POD_IP)' > /data/index.html"
           volumeMounts:
           - name: html
             mountPath: /data
           env:
           - name: POD_NAME
             valueFrom:
               fieldRef:
                 fieldPath: metadata.name
           - name: POD_IP
             valueFrom:
               fieldRef:
                 fieldPath: status.podIP
         volumes:
         - name: html
           emptyDir: {}
   EOF
   ```
2. Service 생성 &#x20;

   ```
   kubectl expose deployment nginx --port 80
   ```
3. 생성된 Service 확인&#x20;

   ```
   kubectl get svc nginx -o wide
   ```
4. 생성된 Endpoint 확인&#x20;

   ```
   kubectl get ep nginx
   ```
5. 생성된 Pod의 IP주소 확인 &#x20;

   ```
   kubectl get pod -l app=nginx -o wide
   ```
6. Endpoint에 타겟으로 등록되어 있는 Pod 목록 확인

   ```
   kubectl get ep nginx \
   -o jsonpath='{range .subsets[*].addresses[*]}{.targetRef.name}{"\t"}{.ip}{"\n"}{end}'
   ```
7. 생성된 Deployment의 Replica 갯수를 6개로 조정&#x20;

   ```
   kubectl scale deployment nginx --replicas=6
   ```
8. 생성된 Pod의 IP주소 확인 &#x20;

   ```
   kubectl get pod -l app=nginx -o wide
   ```
9. Endpoint에 타겟으로 등록되어 있는 Pod 목록 확인

   ```
   kubectl get ep nginx \
   -o jsonpath='{range .subsets[*].addresses[*]}{.targetRef.name}{"\t"}{.ip}{"\n"}{end}'
   ```
10. Pod를 생성하고 Pod에서 위에서 생성한 Service 호출&#x20;

    ```
    kubectl run curl --image=nginx -it --rm --restart=Never -- \
    bash -c "for i in {1..20};do curl -s $(kubectl get svc nginx -o=jsonpath='{.spec.clusterIP}');done"
    ```
11. 새로운 터미널을 열고 kube-proxy 로그 확인 - *아래의 명령어를 입력하고 엔터키를 몇번 입력해서 간격을 만들어두면 새로운 로그를 좀 더 쉽게 알아볼수 있음*

    ```
    kubectl -n kube-system logs ds/kube-proxy -f
    ```
12. 기존 터미널로 돌아와서 생성된 Deployment의 Replica 갯수를 3개로 조정&#x20;

    ```
    kubectl scale deployment nginx --replicas=3
    ```
13. 다른 터미널로 이동해서 kube-proxy 로그를 확인하고 Ctrl+C를 입력해서 프로세스 종료
14. 한개의 Node로 Session Manager 연결

    ```
    aws ssm start-session --target \
    $(kubectl get node -o jsonpath='{.items[0].spec.providerID}{"\n"}' | grep -oE "i-[a-z0-9]+")
    ```
15. Iptable의 모든 규칙 확인&#x20;

    ```
    sudo iptables-save
    ```
16. Iptable의 NAT 규칙 확인

    ```
    sudo iptables -L -t nat
    ```
17. KUBE-SERVICES 규칙 확인&#x20;

    ```
    sudo iptables -t nat -L KUBE-SERVICES -n  | column -t
    ```
18. 위에서 생성한 Service의 Cluster IP로 연결된 규칙의 상세내용 확인

    ```
    {
        export SERVICE_CHAIN=$(sudo iptables -t nat -L KUBE-SERVICES -n  | column -t | grep "default/nginx" | grep -oE "^KUBE-SVC-[A-Z0-9]+")
        
        sudo iptables -t nat -L $SERVICE_CHAIN -n  | column -t
    }
    ```
19. 위의 명령어로 나온 결과중의 한개의 Chain 규칙 확인&#x20;

    ```
    sudo iptables -t nat -L $(sudo iptables -t nat -L $SERVICE_CHAIN -n  | column -t | grep -oE "^KUBE-SEP-[A-Z0-9]+" | head -1) \
    -n  | column -t
    ```
20. 첫번째 터미널로 이동해서 생성된 Pod의 IP주소 확인 &#x20;

    ```
    kubectl get pod -o wide -l app=nginx
    ```
21. Deployment의 Replica 갯수를 6개로 조정&#x20;

    ```
    kubectl scale deployment nginx --replicas=6
    ```
22. 두번째 터미널로 이동해서 18번 명령어 재실행&#x20;
23. 첫번째 터미널로 이동해서 Service를 ClusterIP에서 *LoadBalancer*로 변경

    ```
    kubectl patch svc nginx -p '{"spec": {"type": "LoadBalancer"}}'
    ```
24. Service가 변경되었는지 확인

    ```
    kubectl get svc nginx -o wide
    ```
25. Service 객체에 발생한 Event 확인

    ```
    kubectl describe svc nginx
    ```
26. 웹 브라우저를 열고 Service의 External IP 주소로 접속 - *아래의 명령어로 주소 확인 가능*

    ```
    kubectl get svc nginx -o jsonpath='{.status.loadBalancer.ingress[0].hostname}{"\n"}' 
    ```
27. Service 상세 내용 확인 - 포트 정보 확인&#x20;

    ```
    kubectl get svc nginx -o=jsonpath='{.spec}' | jq 
    ```
28. Service를 통해서 생성된 ELB 상세 내용 확인 &#x20;

    ```
    aws elb describe-load-balancers --load-balancer-names \
    $(kubectl get svc nginx -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' | grep -o -E '^[a-z0-9]+' )
    ```
29. ELB의 Listener 설정 확인

    ```
    aws elb describe-load-balancers --load-balancer-names \
    $(kubectl get svc nginx -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' | grep -o -E '^[a-z0-9]+' ) \
    --query 'LoadBalancerDescriptions[*].ListenerDescriptions'
    ```
30. ELB의 보안그룹 확인

    ```
    aws elb describe-load-balancers --load-balancer-names \
    $(kubectl get svc nginx -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' | grep -o -E '^[a-z0-9]+' ) \
    --query 'LoadBalancerDescriptions[*].SecurityGroups'
    ```
31. 노드에 부여된 보안그룹에 ELB 보안그룹에 대한 새로운 인바운드 규칙이 추가 됐는지 확인&#x20;

    ```
    aws ec2 describe-security-groups --group-ids \
    $(aws ec2 describe-instances --instance-ids \
    $(kubectl get node -o jsonpath='{.items[0].spec.providerID}{"\n"}' | grep -oE "i-[a-z0-9]+") \
    --query 'Reservations[0].Instances[0].SecurityGroups[*].GroupId' --output text) \
    --query "SecurityGroups[*].IpPermissions"
    ```
32. 두번째 터미널로 이동해서 Iptable 규칙 확인

    ```
    sudo iptables-save | grep nginx
    ```
33. KUBE-SERVICES 규칙 확인&#x20;

    ```
    sudo iptables -t nat -L KUBE-SERVICES -n  | column -t
    ```
34. KUBE-NODEPORTS 규칙 확인&#x20;

    ```
    sudo iptables -t nat -L KUBE-NODEPORTS -n  | column -t
    ```
35. NodePort 호출&#x20;

    ```
    curl localhost:NODEPORT
    ```
36. 첫번째 터미널로 이동해서 Service를 *LoadBalancer*에서 *ClusterIP*로 변경

    ```
    kubectl patch svc nginx -p '{"spec": {"type": "ClusterIP"}}'
    ```
37. ELB가 삭제되었는지 확인

    ```
    aws elb describe-load-balancers
    ```
38. 데모 애플리케이션 삭제 &#x20;

    ```
    {
        kubectl delete deploy nginx
        kubectl delete svc nginx
    }
    ```

### Ingress

1. AWS Load Balancer Controller에 부여할 IAM 정책 생성&#x20;

   ```
   {
       export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
       export CLUSTER_NAME=$(kubectl get node \
       -o=jsonpath='{.items[0].metadata.labels.alpha\.eksctl\.io\/cluster-name}')
       
       eksctl utils associate-iam-oidc-provider \
       --region $AWS_REGION \
       --cluster $CLUSTER_NAME \
       --approve
       
       curl -o aws-loadbalancer-controller-policy.json \
       https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.13.2/docs/install/iam_policy.json
       
       aws iam create-policy \
       --policy-name AWSLoadBalancerControllerIAMPolicy \
       --policy-document file://aws-loadbalancer-controller-policy.json
   }
   ```
2. ServiceAccount 생성&#x20;

   ```
   eksctl create iamserviceaccount \
   --cluster $CLUSTER_NAME \
   --namespace=kube-system \
   --name=aws-load-balancer-controller \
   --attach-policy-arn=arn:aws:iam::${ACCOUNT_ID}:policy/AWSLoadBalancerControllerIAMPolicy \
   --override-existing-serviceaccounts \
   --approve \
   --region $AWS_REGION
   ```
3. EKS 리포지토리 추가&#x20;

   ```
   helm repo add eks https://aws.github.io/eks-charts && helm repo update
   ```
4. 위에서 추가한 리포지토리가 추가되었는지 확인

   ```
   helm repo list
   ```
5. 위에서 추가한 리포지토리에 있는 Helm 차트 목록 확인

   ```
   helm search repo eks
   ```
6. AWS Load Balancer Controller 설치

   ```
   helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
   --version 1.13.2 \
   --namespace kube-system \
   --set clusterName=$CLUSTER_NAME \
   --set replicaCount=1 \
   --set serviceAccount.create=false \
   --set serviceAccount.name=aws-load-balancer-controller
   ```
7. AWS Load Balancer Controller 로그 확인

   ```
   kubectl -n kube-system logs deploy/aws-load-balancer-controller
   ```
8. 데모 웹사이트 배포

   ```
   {
       kubectl create deployment nginx --image=nginx --port=80
       kubectl expose deploy nginx 
   }
   ```
9. 생성된 리소스 확인

   ```
   kubectl get all -l app=nginx
   ```
10. Ingress 생성

    ```
    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: nginx
    spec:
      ingressClassName: alb
      rules:
      - http:
          paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx
                port:
                  number: 80
    EOF
    ```
11. 생성된 Ingress 확인

    ```
    kubectl get ing nginx
    ```
12. 위에서 생성한 Ingress에 발생한 Event 확인

    ```
    kubectl describe ingress nginx
    ```
13. AWS Load Balancer Controller 로그 확인

    ```
    kubectl -n kube-system logs deploy/aws-load-balancer-controller
    ```
14. 에러 로그 발생원인 확인 - <https://github.com/kubernetes-sigs/aws-load-balancer-controller/issues/4014>
15. Ingress 수정

    ```
    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: nginx
      annotations:
        alb.ingress.kubernetes.io/target-type: ip
    spec:
      ingressClassName: alb
      rules:
      - http:
          paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx
                port:
                  number: 80
    EOF
    ```
16. Ingress 상태 확인

    ```
    kubectl get ing nginx
    ```
17. Ingress에 발생한 Event 확인

    ```
    kubectl describe ingress nginx
    ```
18. AWS Load Balancer Controller 로그 확인

    ```
    kubectl -n kube-system logs deploy/aws-load-balancer-controller
    ```
19. 아래의 명령어를 실행해서 생성된 ALB 엔드포인트를 확인하고 웹브라우저를 통해서 접근이 되는지 확인

    ```
    echo $(kubectl get ing nginx -o=jsonpath='{.status.loadBalancer.ingress[0].hostname}')
    ```
20. 생성된 ALB의 상세 내용 확인 - Scheme 확인

    ```
    {
        export LoadBalancerName=$(aws elbv2 describe-load-balancers | \
        jq -r '.LoadBalancers[] | 
        select(.DNSName == "'"$(kubectl get ing nginx -o=jsonpath='{.status.loadBalancer.ingress[0].hostname}')"'") | 
        .LoadBalancerName')

        aws elbv2 describe-load-balancers --names $LoadBalancerName
    }
    ```
21. ALB가 생성된 서브넷 확인

    ```
    aws elbv2 describe-load-balancers --names $LoadBalancerName \
    --query 'LoadBalancers[0].AvailabilityZones[*]'
    ```
22. ALB가 생성된 서브넷에 부여된 태그 확인

    ```
    aws ec2 describe-subnets --subnet-ids --query 'Subnets[*].Tags' \
    $(aws elbv2 describe-load-balancers --names $LoadBalancerName \
    --query 'LoadBalancers[0].AvailabilityZones[*].SubnetId' --output text)
    ```
23. AWS Load Balancer Controller가 ALB를 생성한 서브넷을 선택하는 방법 확인 - <https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.13/deploy/subnet_discovery/>
24. AWS Load Balancer Controller로 Ingress 생성할때 요구되는 파라미터 및 기본값 확인 - <https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.13/guide/ingress/annotations/>
25. Ingress 수정

    ```
    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: nginx
      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: nginx
                port:
                  number: 80
    EOF
    ```
26. Ingress에 발생한 Event 확인

    ```
    kubectl describe ingress nginx
    ```
27. AWS Load Balancer Controller 로그 확인

    ```
    kubectl -n kube-system logs deploy/aws-load-balancer-controller
    ```
28. 아래의 명령어를 실행해서 생성된 ALB 엔드포인트를 확인하고 웹브라우저를 통해서 접근이 되는지 확인

    ```
    echo $(kubectl get ing nginx -o=jsonpath='{.status.loadBalancer.ingress[0].hostname}')
    ```
29. 기존에 생성된 ALB가 존재하는지 확인

    ```
    aws elbv2 describe-load-balancers --names $LoadBalancerName
    ```
30. 새롭게 생성된 ALB의 ARN를 확인하고 환경변수로 저장

    ```
    {
        export LoadBalancerArn=$(aws elbv2 describe-load-balancers | \
        jq -r '.LoadBalancers[] | 
        select(.DNSName == "'"$(kubectl get ing nginx -o=jsonpath='{.status.loadBalancer.ingress[0].hostname}')"'") | 
        .LoadBalancerArn')

        echo $LoadBalancerArn
    }
    ```
31. 아파치 웹서버 배포

    ```
    {
        kubectl create deployment httpd --image=httpd
        kubectl expose deployment httpd --port=80
    }
    ```
32. Ingress 수정

    ```
    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: nginx
      annotations:
        alb.ingress.kubernetes.io/scheme: internet-facing
        alb.ingress.kubernetes.io/target-type: ip
    spec:
      ingressClassName: alb
      rules:
      - host: nginx.example.com
        http:
          paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx
                port:
                  number: 80
      - host: httpd.example.com
        http:
          paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: httpd
                port:
                  number: 80
    EOF
    ```
33. 아래의 명령어를 실행해서 생성된 ALB 엔드포인트를 확인하고 웹브라우저를 통해서 접근이 되는지 확인

    ```
    echo $(kubectl get ing nginx -o=jsonpath='{.status.loadBalancer.ingress[0].hostname}')
    ```
34. cURL 명령어로 **Host** 값을 *nginx.example.com*으로 명시하고 ALB 엔드포인트 호출

    ```
    curl -H "Host: nginx.example.com" \
    $(kubectl get ing nginx -o=jsonpath='{.status.loadBalancer.ingress[0].hostname}') 
    ```
35. cURL 명령어로 **Host** 값을 *httpd.example.com*으로 명시하고 ALB 엔드포인트 호출

    ```
    curl -H "Host: httpd.example.com" $(kubectl get ing nginx -o=jsonpath='{.status.loadBalancer.ingress[0].hostname}') 
    ```
36. ALB 리스너 확인&#x20;

    ```
    {
        export ListnerArn=$(aws elbv2 describe-listeners \
        --load-balancer-arn $LoadBalancerArn \
        --query "Listeners[0].ListenerArn" --output text)
        
        aws elbv2 describe-listeners --listener-arns $ListnerArn
    }
    ```
37. ALB 리스너 규칙 확인

    ```
    aws elbv2 describe-rules --listener-arn $ListnerArn
    ```
38. ALB에 연동된 대상그룹 확인&#x20;

    ```
    {
        export TG=$(aws elbv2 describe-target-groups \
        --load-balancer-arn $LoadBalancerArn \
        --query "TargetGroups[*].TargetGroupArn" \
        --output text)
        
        aws elbv2 describe-target-groups --target-group-arns $TG
    }
    ```
39. 대상그룹에 포함된 대상 목록 확인

    ```
    for target in $TG
    do 
        aws elbv2 describe-target-health --target-group-arn $target --no-cli-pager
    done
    ```
40. Pod 목록 확인

    ```
    kubectl get pod -o wide -l 'app in (nginx,httpd)'
    ```
41. 아파치 Pod 갯수를 3개로 조정

    ```
    kubectl scale deployment httpd --replicas=3
    ```
42. Pod 목록 확인

    ```
    kubectl get pod -o wide -l 'app in (nginx,httpd)'
    ```
43. 대상그룹에 포함된 대상 목록 확인

    ```
    for target in $TG
    do 
        aws elbv2 describe-target-health --target-group-arn $target --no-cli-pager
    done
    ```
44. Ingress 삭제

    ```
    kubectl delete ing nginx
    ```
45. AWS Load Balancer Controller 로그 확인

    ```
    kubectl -n kube-system logs deploy/aws-load-balancer-controller
    ```
46. ALB가 삭제되었는지 확인

    ```
    aws elbv2 describe-load-balancers --load-balancer-arns $LoadBalancerArn
    ```
47. 리소스 삭제

    ```
    {
        kubectl delete svc httpd nginx 
        kubectl delete deploy httpd nginx
    }
    ```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://aws.youngwjung.com/eks/lab.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
