> 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
    }
    ```
