실습

해당 실습을 AWS CloudShell에서 진행한다고 가정하고 작성되었습니다.

실습환경 구성

CLI 도구 설치

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

    {
        mkdir ~/bin
        echo export PATH="$HOME/bin:$PATH" >> ~/.bashrc
        export PATH="$HOME/bin:$PATH"
    }
  2. kubectl 설치

    {
        curl -sLO "https://dl.k8s.io/release/v1.30.0/bin/linux/amd64/kubectl"
        sudo install -o root -g root -m 0755 kubectl $HOME/bin/kubectl
        rm kubectl
        sudo dnf install bash-completion -y
        echo 'sudo dnf install bash-completion -y' >> ~/.bashrc
        echo 'source <(kubectl completion bash)' >> ~/.bashrc
        source <(kubectl completion bash)
    }
  3. eksctl 설치

    {
        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)
    }
  4. helm 설치

    curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | HELM_INSTALL_DIR=$HOME/bin bash
  5. yq 설치

    {
        sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O $HOME/bin/yq
        sudo chmod +x $HOME/bin/yq
    }
  6. Krew 설치

    {
        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.30"
    
    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
    EOF
  2. EKS 클러스터 생성

    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. Node에 부여된 Label을 통해서 EKS 클러스터 이름 확인하고 환경변수로 저장

    {
        export CLUSTER_NAME=$(kubectl get node \
        -o=jsonpath='{.items[0].metadata.labels.alpha\.eksctl\.io\/cluster-name}')
        echo $CLUSTER_NAME
    }
  9. 디코딩한 URL 호출 - HTTP 헤더에 클러스터 이름 추가

    curl -H "x-k8s-aws-id: $CLUSTER_NAME" $DECODED_URL
  10. 현재 설정된 AWS 자격증명을 확인

    aws sts get-caller-identity
  11. API 서버 주소를 확인하고 환경변수로 지정

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

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

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

    curl -k -X GET \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    $K8S_SERVER/api/v1/nodes
  15. IAM 유저 생성

    aws iam create-user --user-name john
  16. 위에서 생성한 IAM 유저의 Access Key 생성

    aws iam create-access-key --user-name john > key.txt
  17. Access Key가 정상적으로 생성되었는지 확인

    cat key.txt
  18. 위에서 생성한 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
    }
  19. 위에서 명시한 프로필을 통해서 AWS API 호출

    aws sts get-caller-identity --profile john
  20. kubeconfig 파일 삭제

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

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

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

    aws iam put-user-policy --user-name john --policy-name eks-admin --policy-document \
    '{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": [
                    "eks:DescribeCluster"
                ],
                "Resource": "*",
                "Effect": "Allow"
            }
        ]
    }'
  24. IAM 정책이 부여되었는지 확인

    {
        aws iam list-user-policies --user-name john --no-cli-pager
        aws iam get-user-policy --user-name john --policy-name eks-admin --no-cli-pager
    }
  25. 새로 생성한 IAM 유저의 자격증명으로 AWS CLI를 이용해서 kubeconfig 파일 생성 - 정책 적용까지 시간이 걸릴수 있음

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

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

    kubectl get pod -A
  28. 인증 토큰 검증

    {
        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
    }
  29. EKS 클러스터를 생성한 IAM 유저의 자격증명으로 AWS CLI를 이용해서 kubeconfig 파일 업데이트

    {
        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. 현재 kubeconfig에 설정된 유저의 자격증명으로 수행할수 있는 API 목록 확인

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

    aws eks list-access-entries --cluster-name $CLUSTER_NAME
  8. 한개의 Node의 인스턴스 ID를 확인하고 환경변수로 지정

    {
        export INSTANCE_ID=$(kubectl get node -o jsonpath='{.items[0].spec.providerID}' \
        | grep -oE "i-[a-z0-9]+")
        echo $INSTANCE_ID
    }
  9. Node에 부여된 IAM 인스턴스 프로필을 확인하고 환경변수로 지정

    {
        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-]+$")
        echo $INSTANCE_PROFILE
    }
  10. 위에서 확인한 인스턴스 프로필에 연동된 IAM 역할 확인

    {
        export INSTANCE_ROLE=$(aws iam get-instance-profile --instance-profile-name $INSTANCE_PROFILE \
        --query 'InstanceProfile.Roles[0].Arn' --output text)
        echo $INSTANCE_ROLE
    }
  11. Node에 부여된 IAM 역할의 Access Entry 확인

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

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

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

    kubectl rbac-tool lookup -e system:nodes
  15. ClusterRole 목록 확인

    kubectl get clusterrole
  16. eks:node-bootstrapper ClusterRole 리뷰

    kubectl get clusterrole eks:node-bootstrapper -o yaml
  17. ClusterRoleBinding 목록 확인

    kubectl get clusterrolebinding
  18. eks:node-bootstrapper ClusterRoleBinding 리뷰

    kubectl get clusterrolebinding eks:node-bootstrapper -o yaml
  19. EKS 클러스터가 생성되어 있는 AWS 계정번호 확인하고 환경변수로 저장

    {
        export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
        echo $ACCOUNT_ID
    }
  20. 위에서 새로 생성한 IAM 유저에 EKS Access Entry 생성

    aws eks create-access-entry --cluster-name $CLUSTER_NAME \
    --principal-arn arn:aws:iam::$ACCOUNT_ID:user/john
  21. 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
  22. IAM 유저의 자격증명으로 AWS CLI를 이용해서 kubeconfig 파일 업데이트

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

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

    kubectl get pod -n kube-system
  25. kube-system 네임스페이스에 Pod 생성 시도

    kubectl run nginx --image=nginx -n kube-system
  26. 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
    }
  27. 위에서 새로 생성한 IAM 역할에 EKS Access Entry 생성

    aws eks create-access-entry \
    --cluster-name $CLUSTER_NAME \
    --principal-arn arn:aws:iam::$ACCOUNT_ID:role/eks-admin-role
  28. 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
  29. IAM 유저의 자격증명으로 위에서 생성한 IAM 역할 전환하는 설정 추가해서 kubeconfig 파일 업데이트

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

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

    kubectl get pod -A
  32. 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"
            }
        ]
    }'
  33. IAM 정책이 부여되었는지 확인

    {
        aws iam list-user-policies --user-name john
        aws iam get-user-policy --user-name john --policy-name assume-role
    }
  34. 쿠버네티스 API 호출 시도 - 정책 적용까지 시간이 걸릴수 있음

    kubectl get pod -A
  35. user에 명시된 명령어 실행

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

    {
        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.).*')
        echo $TOKEN
    }
  37. 토큰값을 디코딩해서 환경변수로 지정

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

    curl -H "x-k8s-aws-id: $CLUSTER_NAME" $DECODED_URL
  39. AWS 관리콘솔에 로그인된 IAM 자격증명으로 AWS CLI를 이용해서 kubeconfig 파일 업데이트

    {
        rm -rf ~/.kube/config
        unset AWS_PROFILE
        aws eks update-kubeconfig --name $CLUSTER_NAME
    }
  40. 리소스 삭제

    {
        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-admin
        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 생성

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

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

    NAME         READY   STATUS    RESTARTS   AGE
    nginx        0/1     Pending   0          4s
  4. 1분 정도 시간이 지난후에 Pod 상태를 확인

    kubectl get pod nginx
  5. 노드 목록 확인

    kubectl get node
  6. spec.nodeName에 위의 명령어의 결과로 나온 첫번째 노드 이름을 넣고 Pod를 생성

    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
  7. Pod 상태 확인

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

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

    kubectl describe pod httpd
  10. spec.nodeName을 명시하고 생성한 Pod에 발생한 Event 확인

    kubectl describe pod nginx
  11. Pod 삭제

    kubectl delete pod nginx httpd

Node Selector

  1. Pod 생성

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

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

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

    kubectl describe pod nginx
  5. Node에 부여된 Label 확인

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

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

    kubectl get node --show-labels | grep -E 'env=dev|$'
  8. Pending 상태였던 Pod가 배포됐는지 확인

    kubectl get pod nginx
  9. Pod에 발생한 Event 확인

    kubectl describe pod nginx
  10. Node에 부여한 Label 삭제

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

    kubectl get node --show-labels | grep -E 'env=dev|$'
  12. Pod 상태 확인

    kubectl get pod nginx
  13. Pod 삭제

    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. 첫번째 Node의 가용영역 확인

    kubectl get node \
    -o=jsonpath='{.items[0].metadata.labels.topology\.kubernetes\.io/zone}{"\n"}'
  3. nodeSelector를 명시한 Pod 생성

    for s in web api queue cache 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
  4. 각 Pod가 배포된 Node 확인

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

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

    for i in {1..5}
    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
  7. 각 Pod가 배포된 Node 확인

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

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

    kubectl delete pod -l app=nginx
  10. 각 Pod가 배포된 Node 확인

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

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

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

    kubectl get pod -l app=nginx -o wide --sort-by=.spec.nodeName
  14. 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
  15. Deployment 삭제

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

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

    for i in {1..5}
    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
  18. 위에서 생성한 Pod들이 배포된 Node 확인

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

    kubectl delete pod -l app=nginx
  20. topologySpreadConstraints를 명시하고 Pod 생성

    for i in {1..5}
    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
  21. 위에서 생성한 Pod들이 배포된 Node 확인

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

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

Scaling

Manual Scaling

  1. Deployment 생성

    kubectl create deployment nginx --image=nginx --replicas=1
  2. Pod 갯수 확인

    kubectl get pod -l app=nginx
  3. Deployment의 replica를 3개로 수정

    kubectl scale deployment nginx --replicas=3 
  4. Pod 갯수 확인

    kubectl get pod -l app=nginx
  5. ReplicaSet 확인

    kubectl get rs -l app=nginx
  6. Deployment의 replica를 2개로 수정

    kubectl scale deployment nginx --replicas=2
  7. Deployment의 replica가 1개라면 replica를 3개로 변경

    kubectl scale deployment nginx --current-replicas=1 --replicas=3 
  8. 새로운 Deployment 생성

    kubectl create deployment httpd --image=httpd --replicas=1
  9. Deployment 중에서 replica가 1개인 Deployment가 있으면 replica를 3개로 변경

    kubectl scale deployment --current-replicas=1 --replicas=3 --all
  10. Pod 갯수 확인

    kubectl get pod -l 'app in (nginx,httpd)'
  11. 모든 Deployment의 replica를 5개로 변경

    kubectl scale deployment --replicas=5 --all
  12. Pod 갯수 확인

    kubectl get pod -l 'app in (nginx,httpd)'
  13. 모든 Deployment 삭제

    kubectl delete deployment -l 'app in (nginx,httpd)'

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

    kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
  4. API 서버에 등록된 API 목록 확인

    kubectl get apiservices.apiregistration.k8s.io
  5. v1beta1.metrics.k8s.io API의 상세 내용 확인

    kubectl get apiservices.apiregistration.k8s.io v1beta1.metrics.k8s.io -o yaml
  6. 모든 Pod의 리소스 사용량 확인

    kubectl top pod -A
  7. 모든 Node의 리소스 사용량 확인

    kubectl top node
  8. Metrics Server 로그 확인

    kubectl -n kube-system logs deploy/metrics-server
  9. Metrics Server 로그 레벨 변경

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

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

    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
  12. 위에서 생성한 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'
  13. 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'
  14. 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'
  15. API 서버를 통해서 Metrics Server가 지표를 수집할때 호출하는 Endpoint 호출

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

    kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=5
  17. 위에서 생성한 HPA 상태 확인

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

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

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

    kubectl get pod -l app=php-apache
  21. Pod의 리소스 사용량 확인

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

    kubectl delete pod load-generator
  23. HPA 상태 모니터링

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

    kubectl describe hpa php-apache
  25. Pod의 리소스 사용량 확인

    kubectl top pod -l app=php-apache 
  26. HPA 상태 확인

    kubectl get hpa php-apache
  27. Pod 갯수 확인

    kubectl get pod -l app=php-apache
  28. 데모 애플리케이션 및 리소스 삭제

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

Cluster Autoscaler

  1. Deployment 생성

    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 확인

    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 정책 JSON 파일 생성

    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
  5. IAM 정책 생성

    aws iam create-policy \
        --policy-name AmazonEKSClusterAutoscalerPolicy \
        --policy-document file://cluster-autoscaler-policy.json
  6. Node에 부여된 Label을 통해서 EKS 클러스터 이름 확인하고 환경변수로 저장

    {
        export CLUSTER_NAME=$(kubectl get node \
        -o=jsonpath='{.items[0].metadata.labels.alpha\.eksctl\.io\/cluster-name}')
        echo $CLUSTER_NAME
    }
  7. EKS 클러스터가 생성되어 있는 AWS 계정번호 확인하고 환경변수로 저장

    {
        export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
        echo $ACCOUNT_ID
    }
  8. IAM OIDC 제공자 활성화

    eksctl utils associate-iam-oidc-provider --region=$AWS_REGION \
    --cluster=$CLUSTER_NAME --approve
  9. ServiceAccount 생성

    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
  10. ServiceAccount가 생성되었는지 확인

    kubectl -n kube-system describe sa cluster-autoscaler
  11. ServiceAccount에 명시된 IAM 역할 이름을 확인하고 환경변수로 지정

    {
       IAM_ROLE_NAME=$(kubectl -n kube-system get sa cluster-autoscaler \
       -o=jsonpath='{.metadata.annotations.eks\.amazonaws\.com\/role-arn}' \
       | grep -oP '(?<=role.).*')
       echo $IAM_ROLE_NAME
    }
  12. ServiceAccount와 연동된 IAM 역할에 부여된 IAM 정책 확인

    aws iam list-attached-role-policies --role-name $IAM_ROLE_NAME
  13. ServiceAccount와 연동된 IAM 역할에 부여된 신뢰관계 정책 확인

    aws iam get-role --role-name $IAM_ROLE_NAME
  14. Cluster Autoscaler 설치

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

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

    kubectl -n kube-system get deploy cluster-autoscaler \
    -o=jsonpath='{.spec.template.spec.containers[*].command}' | jq
  17. EKS 노드그룹와 연동된 오토스케일링 그룹 이름을 확인하고 환경변수로 지정

    {
      export ASG_NAME=$(aws autoscaling describe-auto-scaling-groups --query \
      "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='$CLUSTER_NAME']].AutoScalingGroupName" --output text)
      echo $ASG_NAME
    }
  18. 오토스케일링 그룹의 인스턴스 현황 확인

    aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names $ASG_NAME \
    --query "AutoScalingGroups[0].{MinSize: MinSize, MaxSize: MaxSize, DesiredCapacity: DesiredCapacity}" 
  19. 오토스케일링 그룹에 부여된 태그 확인 - https://docs.aws.amazon.com/eks/latest/userguide/autoscaling.html

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

    kubectl get node
  21. Cluster Autoscaler 설정값 수정

    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"
    ]}]'
  22. Cluster Autoscaler 설정값 확인

    kubectl -n kube-system get deploy cluster-autoscaler \
    -o=jsonpath='{.spec.template.spec.containers[*].command}' | jq 
  23. Cluster Autoscaler 로그 확인 - ASG map 확인

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

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

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

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

    aws autoscaling describe-scaling-activities --auto-scaling-group-name $ASG_NAME
  28. Deployment 삭제

    kubectl delete deployment nginx
  29. Pod 목록 확인

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

    kubectl get node
  31. Cluster Autoscaler 로그 확인

    kubectl -n kube-system logs deploy/cluster-autoscaler
  32. 리소스 삭제

    {
        rm cluster-autoscaler-policy.json
        eksctl delete iamserviceaccount \
        --cluster=$CLUSTER_NAME \
        --namespace=kube-system \
        --name=cluster-autoscaler \
        --region $AWS_REGION
    }

Exposing

Service

  1. Deployment 생성

    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 생성

    kubectl expose deployment nginx --port 80
  3. 생성된 Service 확인

    kubectl get svc nginx -o wide
  4. 생성된 Endpoint 확인

    kubectl get ep nginx
  5. 생성된 Pod의 IP주소 확인

    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개로 조정

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

    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 생성

    kubectl run curl --image=nginx -- sleep infinity
  11. 위에서 생성한 Pod에서 Service 호출

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

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

    kubectl scale deployment nginx --replicas=3
  14. 다른 터미널로 이동해서 kube-proxy 로그를 확인하고 Ctrl+C를 입력해서 프로세스 종료

  15. Session Manager 플러그인 설치

    {
        curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/linux_64bit/session-manager-plugin.rpm" -o "session-manager-plugin.rpm"
        sudo yum install -y session-manager-plugin.rpm
    }
  16. 한개의 Node로 Session Manager 연결

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

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

    sudo iptables -L -t nat
  19. KUBE-SERVICES 규칙 확인

    sudo iptables -t nat -L KUBE-SERVICES -n  | column -t
  20. 생성된 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]+")
        echo $SERVICE_CHAIN
    }
  21. Service의 Cluster IP로 연결된 규칙의 상세내용 확인

    sudo iptables -t nat -L $SERVICE_CHAIN -n  | column -t
  22. 위의 명령어로 나온 결과중의 한개의 Chain 규칙 확인

    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
  23. 첫번째 터미널로 이동해서 생성된 Pod의 IP주소 확인

    kubectl get pod -o wide -l app=nginx
  24. 생성된 Deployment의 Replica 갯수를 6개로 조정

    kubectl scale deployment nginx --replicas=6
  25. 두번째 터미널로 이동해서 21번 명령어 재실행

  26. 첫번째 터미널로 이동해서 Service를 ClusterIP에서 LoadBalancer로 변경

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

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

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

    kubectl get svc nginx -o jsonpath='{.status.loadBalancer.ingress[0].hostname}{"\n"}' 
  30. 생성된 Service 상세 내용 확인

    kubectl get svc nginx -o=jsonpath='{.spec}' | jq
  31. 생성된 ELB 이름 확인

    kubectl get svc nginx -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' | grep -o -E '^[a-z0-9]+' 
  32. 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]+' )
  33. 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'
  34. 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'
  35. 노드에 부여된 보안그룹에 ELB 보안그룹에 대한 새로운 인바운드 규칙이 추가 됐는지 확인

    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"
  36. 두번째 터미널로 이동해서 Iptable 규칙 확인

    sudo iptables-save | grep nginx
  37. KUBE-SERVICES 규칙 확인

    sudo iptables -t nat -L KUBE-SERVICES -n  | column -t
  38. KUBE-NODEPORTS 규칙 확인

    sudo iptables -t nat -L KUBE-NODEPORTS -n  | column -t
  39. NodePort 호출

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

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

    aws elb describe-load-balancers
  42. 데모 애플리케이션 삭제

    {
        kubectl delete deploy nginx
        kubectl delete svc nginx
    }

Ingress

  1. EKS 클러스터가 생성되어 있는 AWS 계정번호 확인하고 환경변수로 저장

    {
        export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
        echo $ACCOUNT_ID
    }
  2. Node에 부여된 Label을 통해서 EKS 클러스터 이름 확인하고 환경변수로 저장

    {
        export CLUSTER_NAME=$(kubectl get node \
        -o=jsonpath='{.items[0].metadata.labels.alpha\.eksctl\.io\/cluster-name}')
        echo $CLUSTER_NAME
    }
  3. IAM OIDC 제공자 생성

    eksctl utils associate-iam-oidc-provider \
    --region $AWS_REGION \
    --cluster $CLUSTER_NAME \
    --approve
  4. AWS Load Balancer Controller에 부여할 IAM 권한이 명시된 JSON 파일 다운로드

    curl -o aws-loadbalancer-controller-policy.json \
    https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.8.2/docs/install/iam_policy.json
  5. 다운받은 IAM 정책 JSON 파일 리뷰

    cat aws-loadbalancer-controller-policy.json
  6. IAM 정책 생성

    aws iam create-policy \
    --policy-name AWSLoadBalancerControllerIAMPolicy \
    --policy-document file://aws-loadbalancer-controller-policy.json
  7. ServiceAccount 생성

    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
  8. EKS 리포지토리 추가

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

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

    helm search repo eks
  11. AWS Load Balancer Controller 설치

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

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

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

    kubectl get all -l app=nginx
  15. 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
  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. 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
  20. Ingress 상태 확인

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

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

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

    echo $(kubectl get ing nginx -o=jsonpath='{.status.loadBalancer.ingress[0].hostname}')
  24. 생성된 ALB 이름을 확인하고 환경변수로 저장

    {
        export LoadBalancerName=$(aws elbv2 describe-load-balancers | \
        jq -r '.LoadBalancers[] | 
        select(.DNSName == "'"$(kubectl get ing nginx -o=jsonpath='{.status.loadBalancer.ingress[0].hostname}')"'") | 
        .LoadBalancerName')
    
        echo $LoadBalancerName
    }
  25. ALB의 상세 내용 확인

    aws elbv2 describe-load-balancers --names $LoadBalancerName
  26. ALB의 Scheme 확인

    aws elbv2 describe-load-balancers --names $LoadBalancerName \
    --query 'LoadBalancers[0].Scheme' --output text
  27. ALB가 생성된 서브넷 확인

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

    aws ec2 describe-subnets --subnet-ids --query 'Subnets[*].Tags' \
    $(aws elbv2 describe-load-balancers --names $LoadBalancerName \
    --query 'LoadBalancers[0].AvailabilityZones[*].SubnetId' --output text)
  29. AWS Load Balancer Controller가 ALB를 생성한 서브넷을 선택하는 방법 확인 - https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.8/deploy/subnet_discovery

  30. AWS Load Balancer Controller로 Ingress 생성할때 요구되는 파라미터 및 기본값 확인 - https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.8/guide/ingress/annotations/

  31. 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
  32. Ingress에 발생한 Event 확인

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

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

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

    aws elbv2 describe-load-balancers --names $LoadBalancerName
  36. 새롭게 생성된 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
    }
  37. 아파치 웹서버 배포

    {
        kubectl create deployment httpd --image=httpd
        kubectl expose deployment httpd --port=80
    }
  38. 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
  39. 아래의 명령어를 실행해서 생성된 ALB 엔드포인트를 확인하고 웹브라우저를 통해서 접근이 되는지 확인

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

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

    curl -H "Host: httpd.example.com" $(kubectl get ing nginx -o=jsonpath='{.status.loadBalancer.ingress[0].hostname}') 
  42. ALB의 리스너 ARN를 확인하고 환경변수로 저장

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

    aws elbv2 describe-listeners --listener-arns $ListnerArn
  44. ALB 리스너 규칙 확인

    aws elbv2 describe-rules --listener-arn $ListnerArn
  45. ALB에 연동된 대상그룹들의 ARN을 확인하고 환경변수로 저장

    {
        export TG=$(aws elbv2 describe-target-groups \
        --load-balancer-arn $LoadBalancerArn \
        --query "TargetGroups[*].TargetGroupArn" \
        --output text)
        
        echo $TG
    }
  46. 대상그룹 확인

    aws elbv2 describe-target-groups --target-group-arns $TG
  47. 대상그룹에 포함된 대상 목록 확인

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

    kubectl get pod -o wide -l 'app in (nginx,httpd)'
  49. 아파치 Deployment의 Replica 갯수를 3개로 조정

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

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

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

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

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

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

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

Last updated