AWS
  • About
  • 수업준비
    • AWS 환경설정
  • 1주차 - EC2
    • 예습
    • 실습
    • AWS Skill Builder
    • 과제
  • 2주차 - ELB & Autoscaling
    • 예습
    • 실습
    • AWS Skill Builder
    • 과제
  • 3주차 - VPC
    • 예습
    • 실습
    • AWS Skill Builder
    • 과제
  • 4주차 - Database
    • 예습
    • 실습
    • AWS Skill Builder
    • 과제
  • 5주차 - Storage
    • 예습
    • 실습
    • AWS Skill Builder
    • 과제
  • 6주차 - IAM & Security
    • 예습
    • 실습
    • AWS Skill Builder
    • 과제
  • 7주차 - Monitoring & Ops
    • 예습
    • 실습
    • AWS Skill Builder
    • 과제
  • 8주차 - DevOps
    • 예습
    • 실습
    • 과제
  • EKS
    • 실습
    • 실습 (AWS Worshop Studio)
  • Serverless
    • 실습
  • FAQ
  • Archive
    • AWS CodeBuild로 애플리케이션 테스트 자동화 시스템 구축
    • AWS CodeDeploy로 애플리케이션 배포 자동화 시스템 구축
      • 배포 실패 수정 방법
Powered by GitBook
On this page
  • 실습환경 구성
  • CLI 도구 설치
  • EKS 클러스터 생성
  • Authentication
  • Authorization
  • Scheduling
  • Manual Scheduling
  • Node Selector
  • Spread Pod Across Cluster
  • Scaling
  • HPA (Horizontal Pod Autoscaler)
  • Cluster Autoscaler
  • Exposing
  • Service
  • Ingress

Was this helpful?

  1. EKS

실습

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

실습환경 구성

CLI 도구 설치

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

    {
        mkdir ~/bin
        echo export PATH="$HOME/bin:$PATH" >> ~/.bashrc
        export PATH="$HOME/bin:$PATH"
    }
  2. 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)
    }
  3. helm 설치

    curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | HELM_INSTALL_DIR=$HOME/bin bash
  4. 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.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 클러스터 생성

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

    kubectl get node

Authentication

  1. kubeconfig 파일 리뷰

    kubectl config view
  2. 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 자격증명을 확인

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

    {
        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. 인증 토큰 검증

    {
        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 파일 업데이트

    {
        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에 부여된 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
    }
  9. Node에 부여된 IAM 역할의 Access Entry 확인

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

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

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

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

    kubectl describe clusterrole eks:node-bootstrapper
  14. 위에서 새로 생성한 IAM 유저에 EKS Access Entry 생성

    {
        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
    }
  15. 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
  16. IAM 유저의 자격증명으로 AWS CLI를 이용해서 kubeconfig 파일 업데이트

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

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

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

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

    aws eks create-access-entry \
    --cluster-name $CLUSTER_NAME \
    --principal-arn arn:aws:iam::$ACCOUNT_ID:role/eks-admin-role
  22. 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
  23. 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
    }
  24. 새로 생성된 kubeconfig 파일 리뷰

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

    kubectl get pod -A
  26. 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"
            }
        ]
    }'
  27. 쿠버네티스 API 호출 시도 - 정책 적용까지 시간이 걸릴수 있음

    kubectl get pod -A
  28. EKS 인증 토큰 확인

    {
        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
    }
  29. AWS 관리콘솔에 로그인된 IAM 자격증명으로 AWS CLI를 이용해서 kubeconfig 파일 업데이트

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

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

    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. Pod 상태를 모니터링

    watch 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
        kubectl get node --show-labels | grep -E 'env=dev|$'
    }
  7. Pending 상태였던 Pod가 배포됐는지 확인

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

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

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

    kubectl get pod nginx
  12. 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. 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. Deployment 삭제

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

    kubectl describe node | grep -E "(^Name:|^Non-terminated)"
  15. 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
  16. 위에서 생성한 Pod들이 배포된 Node 확인

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

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

    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
  19. 위에서 생성한 Pod들이 배포된 Node 확인

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

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

Scaling

HPA (Horizontal Pod Autoscaler)

  1. 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. {
        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 설치 확인

    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의 상세 내용 확인

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

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

    kubectl top node
  9. Metrics Server 로그 확인

    kubectl -n kube-system logs deploy/metrics-server
  10. 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"}]'
  11. Metrics Server 로그 확인 - 새로운 Pod가 뜬 다음에 확인

    kubectl -n kube-system logs deploy/metrics-server
  12. 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
  13. 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)설정

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

    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가 생겼는지 확인

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

    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의 리소스 사용량 확인

    kubectl top pod -l app=php-apache
  27. stabilizationWindowSeconds의 기본값 확인

    kubectl explain hpa.spec.behavior.scaleDown.stabilizationWindowSeconds
  28. stabilizationWindowSeconds의 값을 60으로 조정

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

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

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

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

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

    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 제공자 활성화

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

    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 설치

    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 설정값 수정

    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 삭제

    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

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를 생성하고 Pod에서 위에서 생성한 Service 호출

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

    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의 모든 규칙 확인

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

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

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

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

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

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

  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 상세 내용 확인 - 포트 정보 확인

    kubectl get svc nginx -o=jsonpath='{.spec}' | jq 
  28. Service를 통해서 생성된 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]+' )
  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 보안그룹에 대한 새로운 인바운드 규칙이 추가 됐는지 확인

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

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

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

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

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

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

    {
        kubectl delete deploy nginx
        kubectl delete svc nginx
    }

Ingress

  1. AWS Load Balancer Controller에 부여할 IAM 정책 생성

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

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

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

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

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

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

    echo $(kubectl get ing nginx -o=jsonpath='{.status.loadBalancer.ingress[0].hostname}')
  19. 생성된 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
    }
  20. ALB가 생성된 서브넷 확인

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

    aws ec2 describe-subnets --subnet-ids --query 'Subnets[*].Tags' \
    $(aws elbv2 describe-load-balancers --names $LoadBalancerName \
    --query 'LoadBalancers[0].AvailabilityZones[*].SubnetId' --output text)
  22. 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
  23. Ingress에 발생한 Event 확인

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

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

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

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

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

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

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

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

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

    aws elbv2 describe-rules --listener-arn $ListnerArn
  35. ALB에 연동된 대상그룹 확인

    {
        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
    }
  36. 대상그룹에 포함된 대상 목록 확인

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

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

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

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

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

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

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

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

    {
        kubectl delete svc httpd nginx 
        kubectl delete deploy httpd nginx
    }
PreviousEKSNext실습 (AWS Worshop Studio)

Last updated 21 days ago

Was this helpful?

kubeconfig에 명시된 users 확인 -

EKS Access Policy 목록 확인 -

Kubernetes 공식문서 리뷰 -

Pod Garbage Collector 코드 리뷰 -

kube-scheduler 코드 리뷰 -

리뷰

데모 애플리케이션 배포 -

Metrics Server 설치 -

위에서 생성한 Pod에서 Metrics Server가 지표를 수집할때 호출하는 Endpoint 호출 -

HPA 스케일링 동작방식 확인 -

Cluster Autoscaler 스케일 다운 동작방식 리뷰 -

Cluster Autoscaler에 의해서 삭제 되지 않는 Node 확인 -

에러 로그 발생원인 확인 -

AWS Load Balancer Controller가 ALB를 생성한 서브넷을 선택하는 방법 확인 -

AWS Load Balancer Controller로 Ingress 생성할때 요구되는 파라미터 및 기본값 확인 -

https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins
https://docs.aws.amazon.com/eks/latest/userguide/access-policy-permissions.html
https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename
https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/podgc/gc_controller.go#L223
https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/schedule_one.go#L64
Pod Topology Spread Constraints
https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#run-and-expose-php-apache-server
https://github.com/kubernetes-sigs/metrics-server#kubernetes-metrics-server
https://github.com/kubernetes-sigs/metrics-server/blob/4436807eec6b07ea649444529eb3b46ddbbd8914/pkg/scraper/client/resource/client.go#L77
https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior
https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#how-does-scale-down-work
https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#what-types-of-pods-can-prevent-ca-from-removing-a-node
https://github.com/kubernetes-sigs/aws-load-balancer-controller/issues/4014
https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.13/deploy/subnet_discovery/
https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.13/guide/ingress/annotations/