EKS-workshop

EKS-workshop

EKS를 다뤄보고 GitOps까지 실습해봤습니다.

  • 실습 링크 : https://aws-eks-web-application.workshop.aws/ko/10-intro.html

    실습 영상 : https://www.youtube.com/watch?v=kb6s0Tmp2CA&ab_channel=AWS한국사용자모임-AWSKRUG

    AWS 워크샵에서는 다음 내용을 다룹니다.

    • AWS Cloud9을 통한 실습 환경 구축
    • 도커를 이용하여 컨테이너 이미지 생성
    • 컨테이너 이미지를 ECR에 업로드
    • Amazon EKS 클러스터 구축 및 서비스 배포
    • Container Insights 사용해보기
    • 파드 및 클러스터 오토 스케일링
    • AWS Fargate로 서비스 올리기



    Kubernetes(k8s)

    • 쿠버네티스는 컨테이너화된 워크로드와 서비스를 관리하기 위한 이식성이 있고, 확장가능한 오픈소스 플랫폼입니다. 쿠버네티스는 선언적 구성과 자동화를 모두 용이하게 해주는 컨테이너 오케스트레이션 툴입니다.

    🌐 쿠버네티스에 대해 더 자세히 알고 싶다면 여기 를 클릭하세요.

    쿠버네티스_아키텍쳐

    • 쿠버네티스를 배포하면 클러스터를 얻습니다. 그리고 이 클러스터는 노드들의 집합입니다. 노드들은 크게 두 가지 유형으로 나눠지는데, 각각이 컨트롤 플레인데이터 플레인입니다.
      • 컨트롤 플레인(Control Plane)은 워커 노드와 클러스터 내 파드를 관리하고 제어합니다.
      • 데이터 플레인(Data Plane)은 워커 노드들로 구성되어 있으며 컨테이너화된 애플리케이션의 구성 요소인 파드를 호스트합니다.



    Kubernetes Objects

    • 쿠버네티스의 오브젝트는 바라는 상태(desired state)를 담은 레코드입니다. 오브젝트를 생성하면 쿠버네티스의 컨트롤 플레인에서 오브젝트의 현재 상태(current state) 와 바라는 상태를 일치시키기 위해 끊임없이 관리합니다.
    • 쿠버네티스의 오브젝트에는 파드(pod), 서비스(service), 디플로이먼트(Deployment) 등이 있습니다.



EKS

  • Amazon EKS는 Kubernetes를 쉽게 실행할 수 있는 관리형 서비스입니다. Amazon EKS를 사용하시면 AWS 환경에서 Kubernetes 컨트롤 플레인 또는 노드를 직접 설치, 운영 및 유지할 필요가 없습니다.

  • Amazon EKS는 여러 가용 영역에서 Kubernetes 컨트롤 플레인 인스턴스를 실행하여 고가용성을 보장합니다. 또한, 비정상 컨트롤 플레인 인스턴스를 자동으로 감지하고 교체하며 자동화된 버전 업그레이드 및 패치를 제공합니다.

  • Amazon EKS는 다양한 AWS 서비스들과 연동하여 애플리케이션에 대한 확장성 및 보안을 제공하는 서비스를 제공합니다.

    • 컨테이너 이미지 저장소인 Amazon ECR(Elastic Container Registry)
    • 로드 분산을 위한 AWS ELB(Elastic Load Balancing)
    • 인증을 위한 AWS IAM
    • 격리된 Amazon VPC
  • Amazon EKS는 오픈 소스 Kubernetes 소프트웨어의 최신 버전을 실행하므로 Kubernetes 커뮤니티에서 사용되는 플러그인과 툴을 모두 사용할 수 있습니다. 온프레미스 데이터 센터에서 실행 중인지 퍼블릭 클라우드에서 실행 중인지에 상관없이, Amazon EKS에서 실행 중인 애플리케이션은 표준 Kubernetes 환경에서 실행 중인 애플리케이션과 완벽하게 호환됩니다. 즉, 코드를 수정하지 않고 표준 Kubernetes 애플리케이션을 Amazon EKS로 손쉽게 마이그레이션할 수 있습니다.

  • 이 워크샵을 위해서는 administrator IAM 권한이 필요합니다.



    Cloud9

  • AWS Cloud9은 브라우저만으로도 코드를 작성, 실행 및 디버깅할 수 있는 IDE입니다. 코드 편집기, 디버거 및 터미널이 포함되어 있으며 많이 사용되는 프로그래밍 언어를 위한 필수 도구가 사전에 패키징되어 제공되므로, 새로운 프로젝트를 시작하기 위해 파일을 설치하거나 개발 머신을 구성할 필요가 없다는 특징을 가지고 있습니다.



    kubectl

  • 쿠버네티스 클러스터에 명령을 내리는 CLI입니다.

  • 쿠버네티스는 오브젝트 생성, 수정 혹은 삭제와 관련한 동작을 수행하기 위해 쿠버네티스 API를 사용합니다. 이때, kubectl CLI를 사용하면 해당 명령어가 쿠버네티스 API를 호출해 관련 동작을 수행합니다.



    eksctl

  • eksctl이란 EKS 클러스터를 쉽게 생성 및 관리하는 CLI 툴입니다. Go 언어로 쓰여 있으며 CloudFormation 형태로 배포됩니다.

    eksctl로 클러스터 생성하기

  • eksctl을 사용하여 아무 설정 값을 주지 않고 이 명령어(eksctl create cluster)를 실행하면 default parameter로 클러스터가 배포됩니다.

  • 원하는 설정이 있다면 yaml파일을 만들어서 배포하면 됩니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    cat << EOF > eks-demo-cluster.yaml
    ---
    apiVersion: eksctl.io/v1alpha5
    kind: ClusterConfig

    metadata:
    name: eks-demo # 생성할 EKS 클러스터명
    region: ${AWS_REGION} # 클러스터를 생성할 리젼
    version: "1.21"

    vpc:
    cidr: "192.168.0.0/16" # 클러스터에서 사용할 VPC의 CIDR

    managedNodeGroups:
    - name: node-group # 클러스터의 노드 그룹명
    instanceType: m5.large # 클러스터 워커 노드의 인스턴스 타입
    desiredCapacity: 3 # 클러스터 워커 노드의 갯수
    volumeSize: 10 # 클러스터 워커 노드의 EBS 용량 (단위: GiB)
    ssh:
    enableSsm: true
    iam:
    withAddonPolicies:
    imageBuilder: true # AWS ECR에 대한 권한 추가
    # albIngress: true # albIngress에 대한 권한 추가
    cloudWatch: true # cloudWatch에 대한 권한 추가
    autoScaler: true # auto scaling에 대한 권한 추가

    cloudWatch:
    clusterLogging:
    enableTypes: ["*"]
    EOF
  • eksctl create cluster -f eks-demo-cluster.yaml 로 배포

  • 노드 확인

    • kubectl get nodes

      EKS클러스터 구성

      • 기본적인 EKS 클러스터 구성은 끝났고, 본격적으로 서비스를 배포하고, 유저가 들어오는 통로인 인그레스를 설정해 봅시다.
  • 이 상태에서는 클러스터의 노드를 UI에서 확인할 수 없습니다. Console Crediential을 더해줘야 합니다.

  • Cloud9의 IAM credential을 통해, 클러스터를 생성하였기 때문에 Amazon EKS 콘솔창에서 해당 클러스터 정보를 확인하기 위해서는 실제 콘솔에 접근할 IAM entity(사용자 또는 역할)의 AWS Console credential을 클러스터에 추가하는 작업이 필요합니다.

    • rolearn=$(aws cloud9 describe-environment-memberships --environment-id=$C9_PID | jq -r '.memberships[].userArn')
    • assumedrolename=$(echo ${rolearn} | awk -F/ '{print $(NF-1)}') rolearn=$(aws iam get-role --role-name ${assumedrolename} --query Role.Arn --output text) rolearn에 자신의 arn을 넣어주도록 합시다.
    • echo ${rolearn}
    • eksctl create iamidentitymapping --cluster eks-demo --arn ${rolearn} --group system:masters --username admin identity 맵핑
    • kubectl describe configmap -n kube-system aws-auth 적용 확인



    Ingress Controller

  • AWS Load Balancer Controller구 AWS ALB Ingress Controller에서 리브랜드된 개념입니다.

  • 인그레스(Ingress) 는 주로 클러스터 외부에서 쿠버네티스 내부로 접근할 때, 요청들을 어떻게 처리할지 정의해놓은 규칙이자 리소스 오브젝트입니다. 한마디로 외부의 요청이 내부로 접근하기 위한 관문의 역할을 하는 것이죠. 외부 요청에 대한 로드 밸런싱, TLS/SSL 인증서 처리, HTTP 경로에 대한 라우팅 등을 설정할 수 있습니다. 인그레스는 L7 영역의 요청을 처리합니다.

  • 쿠버네티스에서 서비스 타입 중, NodePort 혹은 LoadBalancer로도 외부로 노출할 수 있지만 인그레스 없이 서비스를 사용할 경우, 모든 서비스에게 라우팅 규칙 및 TLS/SSL 등의 상세한 옵션들을 적용해야 되죠. 그래서 인그레스가 필요합니다.

    Ingress

  • 인그레스는 외부 요청 처리에 대한 규칙들을 설정해놓은 것을 의미하며, 이런 설정이 동작하기 위해서 필요한 것이 인그레스 컨트롤러입니다. kube-controller-manager의 일부로 실행되는 다른 컨트롤러와 달리 인그레스 컨트롤러는 클러스터와 함께 생성되진 않습니다. 따라서 직접 구현해야 합니다.

    AWS Load Balancer 만들기

  • Amazon EKS의 Application Load Balancing이란 클러스터에 인그레스 자원이 생성될 때에 ALB(Application Load Balancer) 및 필요한 자원이 생성되도록 트리거하는 컨트롤러입니다. 인그레스 자원들은 ALB를 구성하여 HTTP 또는 HTTPS 트래픽을 클러스터 내 파드로 라우팅합니다.

    • 쿠버네티스의 Ingress의 경우, Application Load Balancers으로 프로비저닝됩니다.
    • 쿠버네티스의 Service의 경우, Network Load Balancers으로 프로비저닝됩니다.
  • AWS Load Balancer 컨트롤러에서 지원하는 트래픽 모드는 아래의 두 가지입니다.

    • Instance(default): 클러스터 내 노드를 ALB의 대상으로 등록합니다. ALB에 도달하는 트래픽은 NodePort로 라우팅된 다음 파드로 프록시됩니다.
    • IP: 파드를 ALB 대상으로 등록합니다. ALB에 도달하는 트래픽은 파드로 직접 라우팅됩니다. 해당 트래픽 모드를 사용하기 위해선 ingress.yaml 파일에 주석을 사용하여 명시적으로 지정해야 합니다.

    ![Instance Mode, IP Mode](/images/eks-workshop/Untitled 1.png)

    • 각종 배포들에 사용할 manifests 디렉토리를 만들어놓고 관리하는 것을 추천합니다.
  • AWS Load Balancer 컨트롤러를 배포하기 전, 우리는 몇 가지 작업을 수행해야 합니다. controller가 워커 노드 위에서 동작되기 때문에 IAM permissions를 통해, AWS ALB/NLB 리소스에 접근할 수 있도록 만들어야 합니다. IAM permissions는 ServiceAccount를 위한 IAM roles를 설치하거나 워커 노드의 IAM roles에 직접적으로 붙일 수 있습니다.

  • 먼저, 클러스터에 대한 IAM OIDC(OpenID Connect) identity Provider를 생성합니다. Pod와 같은 클러스터 내 쿠버네티스가 생성한 항목이 API Server 또는 외부 서비스에 인증하는데 사용되는 service account에 IAM role을 사용하기 위해, 생성한 클러스터(현재 실습에서의 eks-demo)에 IAM OIDC provider가 존재해야 합니다.

    • eksctl utils associate-iam-oidc-provider
      –region ${AWS_REGION}
      –cluster eks-demo
      –approve

    • OIDC provider URL

      • aws eks describe-cluster –name eks-demo –query “cluster.identity.oidc.issuer” –output text
      • output: https://oidc.eks.ap-northeast-2.amazonaws.com/id/8A6E78112D7F1C4DC352B1B511DD13CF
        • 뒤에 id 부분을 복사해서 다음과 같이 입력합니다.
        • aws iam list-open-id-connect-providers | grep 8A6E78112D7F1C4DC352B1B511DD13CF
        • 결과 값이 출력되면 IAM OIDC identity provider가 클러스터에 생성이 된 것이고, 아무 값도 나타나지 않으면 생성 작업을 수행해야 합니다.
    • AWS Load Balancer Controller에 부여할 IAM Policy를 생성하는 작업을 수행합니다.

    • AWS Load Balancer Controller를 위한 ServiceAccount를 생성합니다.

      • eksctl create iamserviceaccount
        –cluster eks-demo
        –namespace kube-system
        –name aws-load-balancer-controller
        –attach-policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy
        –override-existing-serviceaccounts
        –approve
    • AWS Load Balancer controller를 클러스터에 추가하는 작업을 수행합니다. 먼저, 인증서 구성을 웹훅에 삽입할 수 있도록 cert-manager를 설치합니다. Cert-manager는 쿠버네티스 클러스터 내에서 TLS인증서를 자동으로 프로비저닝 및 관리하는 오픈 소스입니다.

    • load balancer controller yaml

    • yaml 파일에서 클러스터의 cluster-name을 편집합니다. 본 실습에서는 eks-demo로 설정합니다.

      • spec:
        containers:
      • args:
      • –cluster-name=eks-demo # 생성한 클러스터 이름을 입력
      • –ingress-class=alb
        image: amazon/aws-alb-ingress-controller:v2.2.0
    • ServiceAccount yaml spec 삭제


  • 배포

    • kubectl apply -f v2_2_1_full.yaml
    • 확인 kubectl get deployment -n kube-system aws-load-balancer-controller
    • 서비스 어카운드 확인 kubectl get sa aws-load-balancer-controller -n kube-system -o yaml
  • 클러스터 내부에서 필요한 기능들을 위해 실행되는 파드들을 애드온(Addon) 이라고 합니다. 애드온에 사용되는 파드들은 디플로이먼트, 리플리케이션 컨트롤러 등에 의해 관리됩니다. 그리고 이 애드온이 사용하는 네임스페이스가 kube-system입니다. Yaml 파일에서 네임스페이스를 kube-system으로 명시했기에 위의 명령어로 파드 이름이 도출되면 정상적으로 배포된 것입니다. 또한, 아래의 명령어로 관련 로그를 확인할 수 있습니다.

    • kubectl logs -n kube-system $(kubectl get po -n kube-system | egrep -o “aws-load-balancer[a-zA-Z0-9-]+”)
    • ALBPOD=$(kubectl get pod -n kube-system | egrep -o “aws-load-balancer[a-zA-Z0-9-]+”)
    • kubectl describe pod -n kube-system ${ALBPOD}



    서비스 배포

  • 서비스 배포하는 순서는 다음과 같습니다.

    ![서비스 배포 순서](/images/eks-workshop/Untitled 2.png)

  • 소스 코드 다운로드

  • Amazon ECR에 각 서비스에 대한 리포지토리 생성

  • Dockerfile을 포함한 소스 코드 위치에서 컨테이너 이미지 빌드 후, 리포지토리에 푸시

  • 각 서비스에 대한 Deployment, Service, Ingress 매니페스트 파일 생성 및 배포

  • 사용자가 실제 서비스를 접근하는 순서

    ![사용자가 접근하는 순서](/images/eks-workshop/Untitled 3.png)

  • 우리의 서비스는 두 개의 백엔드가 존재합니다.

    • Flask
    • Node.js
  • 각 백엔드의 API가 잘 동작하는지 확인하고 프론트엔드 배포로 넘어가봅시다.



Flask 배포

  • cd ~/environment/manifests/

  • deploy manifest

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    cat <<EOF> flask-deployment.yaml
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: demo-flask-backend
    namespace: default
    spec:
    replicas: 3
    selector:
    matchLabels:
    app: demo-flask-backend
    template:
    metadata:
    labels:
    app: demo-flask-backend
    spec:
    containers:
    - name: demo-flask-backend
    image: $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/demo-flask-backend:latest
    imagePullPolicy: Always
    ports:
    - containerPort: 8080
    EOF
  • service manifest

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    cat <<EOF> flask-service.yaml
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: demo-flask-backend
    annotations:
    alb.ingress.kubernetes.io/healthcheck-path: "/contents/aws"
    spec:
    selector:
    app: demo-flask-backend
    type: NodePort
    ports:
    - port: 8080
    targetPort: 8080
    protocol: TCP
    EOF
  • Ingress manifest

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    cat <<EOF> ingress.yaml
    ---
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    name: "backend-ingress"
    namespace: default
    annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    spec:
    rules:
    - http:
    paths:
    - path: /contents
    pathType: Prefix
    backend:
    service:
    name: "demo-flask-backend"
    port:
    number: 8080
    EOF
  • 순서대로 배포

  • kubectl apply -f flask-deployment.yaml kubectl apply -f flask-service.yaml kubectl apply -f ingress.yaml

  • echo http://$(kubectl get ingress/backend-ingress -o jsonpath=’{.status.loadBalancer.ingress[*].hostname}’)/contents/aws 여기서 나온 주소로 확인

    • ingress object가 배포되는 동안 기다리고 주소로 접속

    ![flask 백엔드 배포](/images/eks-workshop/Untitled 4.png)



    node.js 배포

  • deploy manifest

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    cat <<EOF> nodejs-deployment.yaml
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: demo-nodejs-backend
    namespace: default
    spec:
    replicas: 3
    selector:
    matchLabels:
    app: demo-nodejs-backend
    template:
    metadata:
    labels:
    app: demo-nodejs-backend
    spec:
    containers:
    - name: demo-nodejs-backend
    image: public.ecr.aws/y7c9e1d2/joozero-repo:latest
    imagePullPolicy: Always
    ports:
    - containerPort: 3000
    EOF
  • service manifest

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    cat <<EOF> nodejs-service.yaml
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: demo-nodejs-backend
    annotations:
    alb.ingress.kubernetes.io/healthcheck-path: "/services/all"
    spec:
    selector:
    app: demo-nodejs-backend
    type: NodePort
    ports:
    - port: 8080
    targetPort: 3000
    protocol: TCP
    EOF
  • ingress manifest

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    cat <<EOF> ingress.yaml
    ---
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    name: "backend-ingress"
    namespace: default
    annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    spec:
    rules:
    - http:
    paths:
    - path: /contents
    pathType: Prefix
    backend:
    service:
    name: "demo-flask-backend"
    port:
    number: 8080
    # 추가된 부분 확인
    - path: /services
    pathType: Prefix
    backend:
    service:
    name: "demo-nodejs-backend"
    port:
    number: 8080
    EOF
  • 순서대로 배포

    • kubectl apply -f nodejs-deployment.yaml kubectl apply -f nodejs-service.yaml kubectl apply -f ingress.yaml

    • echo http://$(kubectl get ingress/backend-ingress -o jsonpath=’{.status.loadBalancer.ingress[*].hostname}’)/services/all

      ![node.js 백엔드 배포](/images/eks-workshop/Untitled 5.png)



    프론트엔드 배포

  • 프론트엔드 소스 다운

    cd /home/ec2-user/environment
    git clone https://github.com/joozero/amazon-eks-frontend.git

  • ecr repository 생성

    aws ecr create-repository
    –repository-name demo-frontend
    –image-scanning-configuration scanOnPush=true
    –region ${AWS_REGION}

  • 프론트엔드 소스에서 url을 백엔드의 ingress 주소로 변경\

    • App.js
      • echo http://$(kubectl get ingress/backend-ingress -o jsonpath=’{.status.loadBalancer.ingress[*].hostname}’)/contents/‘${search}’
    • page/UpperPage.js
      • echo http://$(kubectl get ingress/backend-ingress -o jsonpath=’{.status.loadBalancer.ingress[*].hostname}’)/services/all
  • cd /home/ec2-user/environment/amazon-eks-frontend
    npm install
    npm run build

  • npm audit fix를 해도 안된다면

    • export NODE_OPTIONS=--openssl-legacy-provider
  • ECR demo-fronted 로 네이밍 한 뒤, 푸쉬

    • docker build -t demo-frontend .
    • docker tag demo-frontend:latest$ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/demo-frontend:latest
    • docker push $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/demo-frontend:latest
  • fronted배포 manifest

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    cd /home/ec2-user/environment/manifests

    cat <<EOF> frontend-deployment.yaml
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: demo-frontend
    namespace: default
    spec:
    replicas: 3
    selector:
    matchLabels:
    app: demo-frontend
    template:
    metadata:
    labels:
    app: demo-frontend
    spec:
    containers:
    - name: demo-frontend
    image: $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/demo-frontend:latest
    imagePullPolicy: Always
    ports:
    - containerPort: 80
    EOF
  • service

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    cat <<EOF> frontend-service.yaml
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: demo-frontend
    annotations:
    alb.ingress.kubernetes.io/healthcheck-path: "/"
    spec:
    selector:
    app: demo-frontend
    type: NodePort
    ports:
    - protocol: TCP
    port: 80
    targetPort: 80
    EOF
  • ingress 배포

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    cat <<EOF> ingress.yaml
    ---
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    name: "backend-ingress"
    namespace: default
    annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    spec:
    rules:
    - http:
    paths:
    - path: /contents
    pathType: Prefix
    backend:
    service:
    name: "demo-flask-backend"
    port:
    number: 8080
    - path: /services
    pathType: Prefix
    backend:
    service:
    name: "demo-nodejs-backend"
    port:
    number: 8080
    - path: /
    pathType: Prefix
    backend:
    service:
    name: "demo-frontend"
    port:
    number: 80
    EOF
  • 배포

    • kubectl apply -f frontend-deployment.yaml kubectl apply -f frontend-service.yaml kubectl apply -f ingress.yaml

    ![프론트까지 배포 완료](/images/eks-workshop/Untitled 6.png)

    모든 배포가 완료된 모습



    Fargate

AWS Fargate는 컨테이너에 적합한 서버리스 컴퓨팅 엔진으로 Amazon Elastic Container Service(ECS) 및 Amazon Elastic Kubernetes Service(EKS)에서 모두 작동합니다. Fargate는 애플리케이션을 빌드하는 데 보다 쉽게 초점을 맞출 수 있도록 해줍니다. Fargate에서는 서버를 프로비저닝하고 관리할 필요가 없어 애플리케이션별로 리소스를 지정하고 관련 비용을 지불할 수 있으며, 계획적으로 애플리케이션을 격리함으로써 보안 성능을 향상시킬 수 있습니다.

  • 클러스터에 Fargate로 pod를 배포하기 위해서는 pod가 실행될 때 사용하는 하나 이상의 fargate profile을 정의해야 합니다. 즉, fargate profile이란 fargate로 pod를 생성하기 위한 조건을 명시해놓은 프로파일이라고 보시면 됩니다.

    1
    cd /home/ec2-user/environment/manifestscat <<EOF> eks-demo-fargate-profile.yaml---apiVersion: eksctl.io/v1alpha5kind: ClusterConfigmetadata:  name: eks-demo  region: ${AWS_REGION}fargateProfiles:  - name: frontend-fargate-profile    selectors:      - namespace: default        labels:          app: frontend-fargateEOF
    • yaml 파일에서 selectors에 기재된 조건에 부합하는 pod의 경우, fargate로 배포됩니다.
  • 아래의 명령어를 통해, fargate profile을 프로비저닝합니다.

    • eksctl create fargateprofile -f eks-demo-fargate-profile.yaml
    • 정상 동작 확인
      • eksctl get fargateprofile –cluster eks-demo -o json
  • 배포한 3개의 pod 중, 프론트앤드 pod를 fargate로 프로비저닝하는 작업을 수행하겠습니다. 먼저, 기존의 pod를 삭제하는 작업을 수행합니다. 아래의 명령어를 yaml 파일이 위치한 폴더에서 작업합니다.

    • kubectl delete -f frontend-deployment.yaml
  • 그리고 frontend-deployment.yaml 파일을 수정합니다. 이 yaml파일에서 미리 등록한 fargate profile을 넣어주겠습니다. label값을 frontend-fargate로 변경해야 합니다. 1번에서 key 값이 app이고 value 값이 frontend-fargate이며 namespace가 default일 때, pod를 fargate로 배포하겠다는 조건을 맞추기 위해 값을 변경하였습니다.

    1
    cd /home/ec2-user/environment/manifestscat <<EOF> frontend-deployment.yaml---apiVersion: apps/v1kind: Deploymentmetadata:  name: demo-frontend  namespace: defaultspec:  replicas: 3  selector:    matchLabels:      app: frontend-fargate  template:    metadata:      labels:        app: frontend-fargate    spec:      containers:        - name: demo-frontend          image: $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/demo-frontend:latest          imagePullPolicy: Always          ports:            - containerPort: 80EOF
  • frontend-service.yaml

    1
    cat <<EOF> frontend-service.yaml---apiVersion: v1kind: Servicemetadata:  name: demo-frontend  annotations:    alb.ingress.kubernetes.io/healthcheck-path: "/"spec:  selector:    app: frontend-fargate  type: NodePort  ports:    - protocol: TCP      port: 80      targetPort: 80EOF
  • 매니페스트 배포

    kubectl apply -f frontend-deployment.yaml kubectl apply -f frontend-service.yaml

  • 배포 상태 확인

    • kubectl get po -o wide
  • 이전과 같은 상태인지 웹 확인

    • echo http://$(kubectl get ingress/backend-ingress -o jsonpath=’{.status.loadBalancer.ingress[*].hostname}’)



    GitOps?

  • 쿠버네티스 환경에서 CI/CD를 위해서는 Git과 워크플로우 툴의 연결이 필요하고, 이를 통해서 자동화 하는 방안이 필요합니다.

  • 일일이 수정하는 것은 너무나 번거롭습니다.

  • 그래서 등장한 것이 GitOps입니다.

  • Github Action, Kustomize, Helm Chart, Argo CD를 활용해 GitOps를 체험해보겠습니다.



    GitOps 전에 알아야 할 개념들

  • 앞서 소개한 Kustomize나 Argo CD등은 조금 생소해 보입니다.

  • 이해를 위해 미리 한번 정리를 하고 넘어가보겠습니다.

    Kustomize

  • Kubernetes에서 app을 배포를 할때 manifest 파일을 작성한다

  • staging 환경에 배포하는 요구사항이 발생하고, 수정사항이 계속 발생한다면, 작업을 반복해야 하는 이슈가 생긴다

  • 관리해야할 manifest파일이 3배가 되어버림

  • 겹치는 내용이 대부분인데, 이것을 base라고 하고, 환경마다 차이가 나는 부분을 overlay로 관리해보자

  • kustomize는 이렇게, 공통 부분과 차이가 나는 부분을 분리하는 것에서 시작한다.

  • base와 overlay를 머지하면서 환경마다 다른 spec의 리소스를 생성하게 된다.

  • 개발 환경마다 소스 구분하여 configmap, secret생성이 가능하다

  • resource이름에 prefix를 추가하는 기능도 제공한다

  • Kustomize를 적용해서 직접 수동으로 배포할 수도 있다

    • GitOps로 ArgoCD나 Flux등을 사용해서 배포과정을 자동화 한다.
    • Flux 는 쿠버네티스 클러스터가 설정 소스(git..)와 동기화된 상태를 유지하고 새 코드가 추가되면 자동으로 업데이트 해주는 도구



    Helm

  • 쿠버네티스 패키지 관리를 도와줌

    • npm, pip와 비슷한 역할
  • Chart: 헬름 패키지

    • kubernetes를 설명하는 파일들의 집합
    • kubernetes에서 app이 동작하기 위한 모든 리소스들이 포함되어 있음
  • Repository

    • 차트 저장소, 차트르 모아두고 공유함
  • Release

    • kubernetes 클러스터에서 구동되는 차트 인스턴스
    • 동일한 차트를 여러번 설치할 수 있고 이는 새 릴리즈로 관리됩니다.
    • 릴리즈 될 때 패키지된 차트와 config가 결합되어 정상 실행 됩니다
  • 작업 순서

    • Helm 차트를 원하는 레포에서 검색 후 설치 → 각 설치에 따른 새로운 릴리즈 생성



    Argo CD

  • Continuous Delivery

    • 지속적 통합을 통해 테스트 되고 빌드된 코드를 지속적으로 전달하여 제품의 질적 향상을 향하는 것
  • 쿠버네티스 운영과 관련된 manifest 파일 관리하고 있는 원격 레포지토리를 조회

  • 변경 내역이 감지되면 이를 반영하여 배포함(Auto Sync 옵션)

  • 히스토리를 저장하고, 롤백이 가능함

    Dev, Ops Pipeline

  • argo의 장점

    • 실행 단위가 컨테이너기 때문에 고립성이 높다
      • 개별 작업마다 실행환경이 다양한 경우 실행환경이 섞이지 않고 단독적인 환경을 제공할 수 있다.
      • 하나의 역할을 담당하는 Job을 단일하게 개발할 수 있어 재사용성을 높일 수 있다.
        • 데이터 입출만 잘 맞춰 놓으면 단일 역할을 하는 Job을 여러개 만들고, 블록처럼 쌓을 수 있다.
  • argo의 단점

    • Pod를 생성하고 삭제하는 비용이 크다.
      • 간단한 작업이라면 프로세스 또는 스레드 레벨에서 처리하는 게 효율적일 때가 있다
    • 각 스텝마다 개별적인 컨테이너를 실행해서 Job간에 데이터를 빠르게 공유하는 것이 힘들다.
      • Pod 내부 컨테이너 간에만 volume공유가 가능하다
  • 데이터 파이프라인 및 기계학습 모델 훈련에 활용 가능

    • 데이터 타입에 따라 추출하는 소스에 따라 상이한 실행환경
      • 데이터 추출 - Apache sqoop(Java runtime, hadoop 라이브러리 필요)
        • S3 - aws cli, boto3…
      • ML
        • python
        • R
    • 고립성이 굉장히 좋기 때문에 ML워크플로우 툴로도 활용이 가능함



    GitOps 실습

  • 목표하는 CI/CD 파이프라인은 다음과 같습니다.

    CI/CD
  • 개발에서 작업한 내용은 Github Action을 통해 ECR에 업로드 되고, 이것을 클러스터 Ops쪽에서 Pull을 받아 k8s에 argo CD를 사용해 배포하는 방식입니다.

  • 필요한 레포는 두 가지 입니다. application용과 k8s 메니페스트 관리용 레포입니다.

    • front-app-repo: Frontend 소스가 위치한 레파지토리
    • k8s-manifest-repo: K8S 관련 메니페스트가 위치한 레파지토리
  • git remote를 위해서 cloud9에 있는 amazon-eks-frontend 디렉토리 git을 초기화 합니다.

    • cd ~/environment/amazon-eks-frontend
      rm -rf .git
    • 소스 파일들을 푸시합니다.
  • CI/CD 파이프라인을 위해서는 권한이 필요합니다.

    • front app을 빌드하고 docker image로 만들어지면, 이것을 ECR로 푸시해야 합니다.

    • 이 과정은 github Action으로 이루어지기 때문에 최소한의 권한을 넣어줍니다.

      • IAM user 생성

        • aws iam create-user –user-name github-action
      • ECR policy 생성

        1
        cd ~/environmentcat <<EOF> ecr-policy.json{    "Version": "2012-10-17",    "Statement": [        {            "Sid": "AllowPush",            "Effect": "Allow",            "Action": [                "ecr:GetDownloadUrlForLayer",                "ecr:BatchGetImage",                "ecr:BatchCheckLayerAvailability",                "ecr:PutImage",                "ecr:InitiateLayerUpload",                "ecr:UploadLayerPart",                "ecr:CompleteLayerUpload"            ],            "Resource": "arn:aws:ecr:ap-northeast-2:${ACCOUNT_ID}:repository/demo-frontend"        },        {            "Sid": "GetAuthorizationToken",            "Effect": "Allow",            "Action": [                "ecr:GetAuthorizationToken"            ],            "Resource": "*"        }    ]}EOF
    • 이 파일을 사용해 IAM 정책을 생성합니다. 이름은 ecr-policy 입니다.

      • aws iam create-policy –policy-name ecr-policy –policy-document file://ecr-policy.json
    • 만든 정책을 IAM 유저에게 넣어줍니다.

      • aws iam attach-user-policy –user-name github-action –policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/ecr-policy
  • Github Secret 생성

    • 이제 github action에서 사용할 AWS credential, github token을 설정해줍니다.
    • AWS credential 생성
      • 이 또한 최소한의 권한을 갖는 유저를 만들어 줍니다. 이름은 github-action입니다.
      • aws iam create-access-key –user-name github-action
      • 생성된 accesskey와 secretkey를 저장해놓습니다.
    • Github으로 들어가서 PAT를 생성해주고 저장해놓습니다.
    • front-app-repo로 들어가 Settings에 Secret을 선택합니다.
      • 이 레포의 secret을 넣어줍니다.
      • Name은 ACTION_TOKEN
      • Value는 PAT를 넣어줍니다.
      • 마찬가지로 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY 도 넣어줍니다.
  • Github Action을 위한 workflow 스크립트를 생성합니다.

    • github action을 생성하면 .github/workflows에 만들어지기 때문에 같은 디렉토리를 먼저 만들어줍니다.

      • cd ~/environment/amazon-eks-frontend
        mkdir -p ./.github/workflows
    • 실제 build 코드를 작성합니다. build.yml

      1
      cd ~/environment/amazon-eks-frontend/.github/workflowscat > build.yaml <<EOFname: Build Fronton:  push:    branches: [ main ]jobs:  build:    runs-on: ubuntu-latest    steps:      - name: Checkout source code        uses: actions/checkout@v2      - name: Check Node v        run: node -v      - name: Build front        run: |          npm install          npm run build      - name: Configure AWS credentials        uses: aws-actions/configure-aws-credentials@v1        with:          aws-access-key-id: \${{ secrets.AWS_ACCESS_KEY_ID }}          aws-secret-access-key: \${{ secrets.AWS_SECRET_ACCESS_KEY }}          aws-region: $AWS_REGION      - name: Login to Amazon ECR        id: login-ecr        uses: aws-actions/amazon-ecr-login@v1      - name: Get image tag(verion)        id: image        run: |          VERSION=\$(echo \${{ github.sha }} | cut -c1-8)          echo VERSION=\$VERSION          echo "::set-output name=version::\$VERSION"      - name: Build, tag, and push image to Amazon ECR        id: image-info        env:          ECR_REGISTRY: \${{ steps.login-ecr.outputs.registry }}          ECR_REPOSITORY: demo-frontend          IMAGE_TAG: \${{ steps.image.outputs.version }}        run: |          echo "::set-output name=ecr_repository::\$ECR_REPOSITORY"          echo "::set-output name=image_tag::\$IMAGE_TAG"          docker build -t \$ECR_REGISTRY/\$ECR_REPOSITORY:\$IMAGE_TAG .          docker push \$ECR_REGISTRY/\$ECR_REPOSITORY:\$IMAGE_TAGEOF
      • IMAGE_TAG값은 랜덤으로 생성되어 ECR로 Push됩니다.
        • $
    • github action이 잘 동작하는지 테스트 합니다.

      • cd ~/environment/amazon-eks-frontend
        git add .
        git commit -m “Add github action build script”
        git push origin main
  • Kustomize 사용을 위한 k8s manifest 구조화

    • 앞서 설명한 대로, kustomize는 manifest를 base와 overlays로 나뉘어 관리합니다.

    • 이 구조를 만들어줍니다.

      1
      cd ~/environmentmkdir -p ./k8s-manifest-repo/basemkdir -p ./k8s-manifest-repo/overlays/devcd ~/environment/manifestscp *.yaml ../k8s-manifest-repo/basecd ../k8s-manifest-repo/basels -rlt
    • base : kubernetes manifest 원본이 위치한 디렉토리 입니다. 이 안에 위치한 manifest 들은 overlays 아래에 위치한 kustomize.yaml 파일에 담긴 사용자 지정 설정 내용에 따라 변경됩니다.

    • overlays : 사용자 입맛에 맞는 설정 값이 위치한 디렉토리 입니다. 이 설정은 kustomize.yaml 에 담습니다. 이 하위에 있는 dev 디렉토리는 실습을 위해 만든 것으로, 개발 환경에 적용할 설정 파일을 모아 두기 위함 입니다.

  • Kustomize manifest 생성

    • frontend app에 대한 배포 구성을 할 것이기 때문에 frontend부분만 작업 하겠습니다.

      • frontend-deployment.yaml 과 frontend-service.yaml 파일을 kustomize 를 통해 배포 시점에 의도한 값(e.g. Image Tag)을 반영 할겁니다
      • 반영될 값
        • metadata.labels: "env: dev"을 frontend-deployment.yaml, frontend-service.yaml 에 일괄 반영 합니다.
        • spec.selector : "select.app: frontend-fargate" 를 frontend-deployment.yaml, frontend-service.yaml 에 일괄 반영 합니다.
        • spec.template.spec.containers.image : "image: " 값을 새롭게 변경된 Image Tag 정보로 업데이트 합니다.
    • kustomize.yaml 파일을 만들어서 관리 변경할 manifest대상을 정의합니다.

      1
      2
      3
      4
      5
      6
      7
      8
      cd ~/environment/k8s-manifest-repo/base
      cat <<EOF> kustomization.yaml
      apiVersion: kustomize.config.k8s.io/v1beta1
      kind: Kustomization
      resources:
      - frontend-deployment.yaml
      - frontend-service.yaml
      EOF
    • overlays/dev 부분에 바꿀 부분을 정의합니다.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      cd ~/environment/k8s-manifest-repo/overlays/dev
      cat <<EOF> front-deployment-patch.yaml
      apiVersion: apps/v1
      kind: Deployment
      metadata:
      name: demo-frontend
      namespace: default
      labels:
      env: dev
      spec:
      selector:
      matchLabels:
      app: frontend-fargate
      template:
      metadata:
      labels:
      app: frontend-fargate
      EOF
      1
      cd ~/environment/k8s-manifest-repo/overlays/devcat <<EOF> front-service-patch.yamlapiVersion: v1kind: Servicemetadata:  name: demo-frontend  annotations:    alb.ingress.kubernetes.io/healthcheck-path: "/"  labels:    env: devspec:  selector:    app: frontend-fargateEOF
  • 마지막으로 위에서 설정 한 파일들(값)을 사용하고 frontend app 빌드에 따라 만들어진 새로운 Image Tag 를 사용 하겠다고 정의 하겠습니다. 구체적으로는, name 에 지정된 image는 newName의 image와 newTag의 값으로 사용 하겠다는 의미 입니다.

  • 이를 활용해 newTag 값을 변경해 새로운 배포가 이루어질 때 마다 이를 kubernetes 클러스터까지 변경 할 수 있습니다.

    1
    cd ~/environment/k8s-manifest-repo/overlays/devcat <<EOF> kustomization.yamlapiVersion: kustomize.config.k8s.io/v1beta1kind: Kustomizationimages:- name: ${ACCOUNT_ID}.dkr.ecr.ap-northeast-2.amazonaws.com/demo-frontend  newName: ${ACCOUNT_ID}.dkr.ecr.ap-northeast-2.amazonaws.com/demo-frontend  newTag: abcdefgresources:- ../../basepatchesStrategicMerge:- front-deployment-patch.yaml- front-service-patch.yamlEOF
    • 이상 -patch.yaml 파일에 정의한 내용들은 배포 과정에서 kustomize 에 의해 자동으로 kubernetes manifest 에 반영 됩니다.
    • 이미지의 태그명이 abcdefg로 나오면 성공입니다.
  • kubernetes manifest 용 github repo를 만들어줍니다.

    • k8s-manifefst-repo **생성
    1
    cd ~/environment/k8s-manifest-repo/git initgit add .git commit -m "first commit"git branch -M maingit remote add origin https://github.com/jinseo-jang/k8s-manifest-repo.gitgit push -u origin main
  • Argo CD 설치

  • Argo CD cli 설치

  • Argo CD는 퍼블릭하게 노출되지 않지만, ELB를 통해 접속 가능하도록 만들겠습니다.

    • kubectl patch svc argocd-server -n argocd -p ‘{“spec”: {“type”: “LoadBalancer”}}’
  • 접속할 uri를 얻습니다.

    • export ARGOCD_SERVER=kubectl get svc argocd-server -n argocd -o json | jq --raw-output .status.loadBalancer.ingress[0].hostname
      echo $ARGOCD_SERVER
  • 기본 username은 admin입니다. password는 다음과 같습니다.

    • ARGO_PWD=kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
      echo $ARGO_PWD
  • argo login

    login화면
  • 로그인 후 좌 상단 애플리케이션 설정 메뉴를 클릭하고 새 application을 만들어줍니다.

  • Application Nameeksworkshop-cd-pipeline, Projectdefault를 입력 합니다.

  • SOURCE 섹션의 Repository URL 에는 앞서 생성한 k8s-manifest-repo의 git 주소, Revision 에는 main, Path 에는 overlays/dev를 입력 합니다.

  • DESTINATION 섹션의 Cluster URL에는 https://kubernetes.default.svc, Namespace 에는 default를 입력 하고 상단의 Create 를 클릭 합니다.

  • eksworkshop-cd-pipeline이 만들어졌습니다.

  • Kustomize 빌드 단계 추가

    • github action에서 kustomize를 이용하여 image tag를 업데이트 한 후 k8s-manifest-repo에 커밋 푸시하는 단계를 추가 해줘야 합니다.
    • 이 단계가 동작하면, argo CD가 k8s-manifest-repo를 센싱 하다가 새로운 변경사항이 감지되면 Kustomize build 작업을 수행해 새로운 kubernetes manifest를 eks클러스터에 배포합니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    cd ~/environment/amazon-eks-frontend/.github/workflows
    cat <<EOF>> build.yaml

    - name: Setup Kustomize
    uses: imranismail/setup-kustomize@v1

    - name: Checkout kustomize repository
    uses: actions/checkout@v2
    with:
    repository: jinseo-jang/k8s-manifest-repo
    ref: main
    token: \${{ secrets.ACTION_TOKEN }}
    path: k8s-manifest-repo

    - name: Update Kubernetes resources
    run: |
    echo \${{ steps.login-ecr.outputs.registry }}
    echo \${{ steps.image-info.outputs.ecr_repository }}
    echo \${{ steps.image-info.outputs.image_tag }}
    cd k8s-manifest-repo/overlays/dev/
    kustomize edit set image \${{ steps.login-ecr.outputs.registry}}/\${{ steps.image-info.outputs.ecr_repository }}=\${{ steps.login-ecr.outputs.registry}}/\${{ steps.image-info.outputs.ecr_repository }}:\${{ steps.image-info.outputs.image_tag }}
    cat kustomization.yaml

    - name: Commit files
    run: |
    cd k8s-manifest-repo
    git config --global user.email "github-actions@github.com"
    git config --global user.name "github-actions"
    git commit -am "Update image tag"
    git push -u origin main

    EOF
  • 소스를 만들었으면, 커밋 푸시 해줍니다.

    • cd ~/environment/amazon-eks-frontend
      git add .
      git commit -m “Add kustomize image edit”
      git push -u origin main
  • github action이 잘 동작하는지를 확인하고, k8s-manifest-repo에 새 manifest가 커밋 되는지 확인합니다.

  • 이렇게 새 manifest가 배포되면, argo CD에서 감지하여, Sync Status가 업데이트 됩니다.

    • 아무 설정을 하지 않았다면, CURRENT SYNC STATUS의 값이 Out of Synced 입니다.
    • git repository 가 변경되면 자동으로 sync 작업이 수행 하도록 하려면 Auto-Sync 를 활성화 해야 합니다. 이를 위해 APP DETAILS 로 이동 하여 ENABLE AUTO-SYNC 버튼을 눌러 활성화 합니다.
    • 활성화 되었다면, ArgoCD에 의해 k8s-manifest-repo의 커밋 내용이 ArgoCD에 의해 eks클러스터에 반영됩니다.
    • 마지막으로 정상적으로 새 manifest가 배포되었는지를 확인하기 위해 k8s-manifest-repo의 커밋 히스토리를 통해 image tag를 살펴봅니다.
    • abcdefg가 아니라 새로운 태그값으로 들어갔다면 성공입니다.
  • frontend application에 코드를 변경해서, GitOps 파이프라인이 정상 동작하는지를 최종 점검합니다.

  • amazon-eks-frontend/src/ 로 이동하여 App.js 더블 클릭하여 파일을 오픈 합니다.

    • line 67의 값을 EKS DEMO Blog version Hyuby 로 변경 하고 저장 합니다. 저장은 ctrl+s 를 누릅니다.

    • 변경된 소스를 커밋 푸시 합니다.

      • cd ~/environment/amazon-eks-frontend
        git add .
        git commit -m “Add new blog version”
        git push -u origin main
    • Sync작업이 모두 끝나면, url로 접속하여 변경사항을 확인합니다.

      • echo http://$(kubectl get ingress/backend-ingress -o jsonpath=’{.status.loadBalancer.ingress[*].hostname}’)
      • 짠!

      정상동작 확인!




Reference

Author

SangHyub Lee, Jose

Posted on

2021-11-03

Updated on

2023-12-08

Licensed under

Comments