EKS CICD 및 매니페스트 관리

EKS CICD 설정 및 매니페스트 관리 방법에 대해 알아봅니다. 지속적인 통합/지속적인 전달 방법은 여러가지가 있습니다. 아마존 EKS에서 관련 구현을 위한 도구와 설정하는 방법을 설명하도록 하겠습니다.

깃옵스와 깃옵스를 구현하기 위한 EKS CICD 도구 등장

  • CI/CD Continuous Integration/Continuous Delivery – 지속적 통합과 지속적 전달
  • 위브 클라우드, 스피네이커, 스캐폴드 등

GitOps what you need to know

CodePipeline을 이용해 깃옵스 구현

  • AWS CI/CD 구현 서비스 – 소스 작성, 빌드, 배포

CI/CD에 필요한 리소스 생성

  • CloudFormation 사용 → 2.3.2 데이터베이스 환경 구축을 참고 → cicd-environment-template.yaml 적용
  • cicd-environment-template.yaml
Parameters:

  # 실행 대상 리전
  Region:
    Type: String
    Default: "ap-northeast-2"
    Description: Which region do you want to exeute pipeline.

  # 파이프 라인 성과물을 저장하는 S3 버킷명
  CodePipelineArtifactStoreBucketName:
    Type: String
    Default: pipeline-manifest
    Description: Bucket name of code pipeline's artifact.

  # 디플로이 대상 EKS 클러스터명
  EKSClusterName:
    Type: String
    Default: eks-work-cluster
    Description: Target EKS Cluster name.
  
  # kubectl 버전
  KubectlVersion:
    Type: String
    Default: "1.14.0"
    Description: The version of the kubectl command used in the code build task.

  # CICD 테스트로 사용할 Code Commit 저장소
  CodeCommitRepositoryNameForSampleAP:
    Type: String
    Default: "eks-work-cicd-repo"
    Description: Code commit repo name used in this pipeline's source.
  # 트리거 대상 브랜치명
  Branch:
    Type: String
    Description: CodeCommit branch name to execte pipeline.
    Default: master

  # CICD 테스트로 사용할 ECR 레지스트리
  ECRNameForSampleAP:
    Type: String
    Default: "k8sbook/backend-app"
    Description: ECR name used in this pipeline.

Metadata: 
  AWS::CloudFormation::Interface: 
    ParameterGroups: 
      - 
        Label: 
          default: "Base Configuration"
        Parameters: 
          - Region
      - 
        Label: 
          default: "Pipeline Configuration"
        Parameters: 
          - CodeCommitRepositoryNameForSampleAP
          - Branch
          - ECRNameForSampleAP
          - CodePipelineArtifactStoreBucketName
      - 
        Label: 
          default: "EKS Cluster Configuration"
        Parameters: 
          - KubectlVersion
          - EKSClusterName

Resources:

  # S3 Bucket for Pipeline Arfifact store.
  CodePipelineArtifactStoreBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${CodePipelineArtifactStoreBucketName}-${AWS::AccountId}

  # Code Commit for SampleAP.
  CodeCommitRepository:
    Type: AWS::CodeCommit::Repository
    Properties: 
      RepositoryDescription: String
      RepositoryName: !Ref CodeCommitRepositoryNameForSampleAP

  # IAM Role for Code Pipeline service Role
  CodePipelineServiceRole: 
    Type: AWS::IAM::Role
    Properties:
      RoleName: EKS-SampleAP-CodePipelineServiceRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AWSCodePipelineFullAccess
      Path: /
      AssumeRolePolicyDocument: |
        {
          "Statement": [{
            "Effect": "Allow",
            "Principal": {"Service": "codepipeline.amazonaws.com"},
            "Action": "sts:AssumeRole"
          }]
        }
  CodePipelineServiceRolePolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: EKS-SampleAP-CodePipelineServiceRolePolicy
      Roles:
      - !Ref CodePipelineServiceRole
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Action:
            - s3:PutObject
            - s3:GetObject
            - s3:GetObjectVersion
          Resource: "*"
        - Effect: Allow
          Action:
            - s3:PutObject
          Resource:
            - arn:aws:s3:::codepipeline*
            - arn:aws:s3:::elasticbeanstalk*
        - Effect: Allow
          Action:
            - codecommit:CancelUploadArchive
            - codecommit:GetBranch
            - codecommit:GetCommit
            - codecommit:GetUploadArchiveStatus
            - codecommit:UploadArchive
          Resource: "*"
        - Effect: Allow
          Action:
            - codedeploy:CreateDeployment
            - codedeploy:GetApplicationRevision
            - codedeploy:GetDeployment
            - codedeploy:GetDeploymentConfig
            - codedeploy:RegisterApplicationRevision
          Resource: "*"
        - Effect: Allow
          Action:
            - elasticbeanstalk:*
            - ec2:*
            - elasticloadbalancing:*
            - autoscaling:*
            - cloudwatch:*
            - s3:*
            - sns:*
            - cloudformation:*
            - rds:*
            - sqs:*
            - ecs:*
            - iam:PassRole
          Resource: "*"
        - Effect: Allow
          Action:
            - lambda:InvokeFunction
            - lambda:ListFunctions
          Resource: "*"
        - Effect: Allow
          Action:
            - cloudformation:CreateStack
            - cloudformation:DeleteStack
            - cloudformation:DescribeStacks
            - cloudformation:UpdateStack
            - cloudformation:CreateChangeSet
            - cloudformation:DeleteChangeSet
            - cloudformation:DescribeChangeSet
            - cloudformation:ExecuteChangeSet
            - cloudformation:SetStackPolicy
            - cloudformation:ValidateTemplate
            - iam:PassRole
          Resource: "*"
        - Effect: Allow
          Action:
            - codebuild:BatchGetBuilds
            - codebuild:StartBuild
          Resource: "*"

  # IAM Role for Code Build Service role.
  CodeBuildServiceRole: 
    Type: AWS::IAM::Role
    Properties: 
      RoleName: EKS-SampleAP-CodeBuildServiceRole
      Path: /
      AssumeRolePolicyDocument: |
        {
          "Statement": [{
            "Effect": "Allow",
            "Principal": {"Service": "codebuild.amazonaws.com"},
            "Action": "sts:AssumeRole"
          }]
        }
  CodeBuildServiceRolePolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: EKS-SampleAP-CodeBuildServiceRolePolicy
      Roles:
      - !Ref CodeBuildServiceRole
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Action:
            - logs:CreateLogGroup
            - logs:CreateLogStream
            - logs:PutLogEvents
          Resource:
            - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${CodeBuildProjectBuildApplicationAndPushToECR}
            - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${CodeBuildProjectBuildApplicationAndPushToECR}:*
            - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${CodeBuildProjectApplyToEKS}
            - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${CodeBuildProjectApplyToEKS}:*
        - Effect: Allow
          Action:
            - s3:PutObject
            - s3:GetObject
            - s3:GetObjectVersion
          Resource:
            - !Sub arn:aws:s3:::${CodePipelineArtifactStoreBucket}*
        - Effect: Allow
          Action:
            - ssm:GetParameters
          Resource:
            - !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/CodeBuild/*
        - Effect: Allow
          Action:
            - eks:DescribeCluster
          Resource:
            - !Sub arn:aws:eks:${AWS::Region}:${AWS::AccountId}:cluster/${EKSClusterName}
        - Effect: Allow
          Action:
            - "ecr:BatchCheckLayerAvailability"
            - "ecr:BatchGetImage"
            - "ecr:GetDownloadUrlForLayer"
            - "ecr:PutImage"
            - "ecr:InitiateLayerUpload"
            - "ecr:UploadLayerPart"
            - "ecr:CompleteLayerUpload"
            - "ecr:GetAuthorizationToken"
          Resource:
            - "*"

  # Code Build Project.
  CodeBuildProjectBuildApplicationAndPushToECR:
    Description: Creating AWS CodeBuild project
    Type: AWS::CodeBuild::Project
    Properties:
      Artifacts:
        Type: CODEPIPELINE
      Description: !Sub Building stage for ${Branch}.
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        EnvironmentVariables:
          - Name: Branch
            Value: !Ref Branch
          - Name: EKS_CLUSTER_NAME
            Value: !Ref EKSClusterName
          - Name: REGION
            Value: !Ref Region
          - Name: KUBECTL_VERSION
            Value: !Ref KubectlVersion
          - Name: REGISTORY_NAME
            Value: !Ref ECRNameForSampleAP
          - Name: REGISTORY_URI
            Value: !Sub ${AWS::AccountId}.dkr.ecr.${Region}.amazonaws.com
        Image: aws/codebuild/standard:2.0
        Type: LINUX_CONTAINER
        PrivilegedMode: True
      Name: EKS-SampleAP-build
      ServiceRole: !GetAtt CodeBuildServiceRole.Arn
      Source:
        Type: CODEPIPELINE
        BuildSpec: cicd/cloudformation/buildspec-build.yaml
      TimeoutInMinutes: 5

  # Code Build Project.
  CodeBuildProjectApplyToEKS:
    Description: Creating AWS CodeBuild project
    Type: AWS::CodeBuild::Project
    Properties:
      Artifacts:
        Type: CODEPIPELINE
      Description: !Sub Building stage for ${Branch}.
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        EnvironmentVariables:
          - Name: Branch
            Value: !Ref Branch
          - Name: EKS_CLUSTER_NAME
            Value: !Ref EKSClusterName
          - Name: REGION
            Value: !Ref Region
          - Name: KUBECTL_VERSION
            Value: !Ref KubectlVersion
          - Name: REGISTORY_NAME
            Value: !Ref ECRNameForSampleAP
          - Name: REGISTORY_URI
            Value: !Sub ${AWS::AccountId}.dkr.ecr.${Region}.amazonaws.com
        Image: aws/codebuild/standard:2.0
        Type: LINUX_CONTAINER
        PrivilegedMode: True
      Name: EKS-SampleAP-apply
      ServiceRole: !GetAtt CodeBuildServiceRole.Arn
      Source:
        Type: CODEPIPELINE
        BuildSpec: cicd/cloudformation/buildspec-apply.yaml
      TimeoutInMinutes: 5

  # Main process of Code Pipeline.
  ProjectPipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      ArtifactStore:
        Location: !Ref CodePipelineArtifactStoreBucket
        Type: S3
      Name: EKS-SampleAP-Pipeline
      RestartExecutionOnUpdate: false
      RoleArn:
        !GetAtt CodePipelineServiceRole.Arn
      Stages:
        - 
          Name: Source
          Actions:
            -
              Name: SourceAction
              ActionTypeId:
                Category: Source
                Owner: AWS
                Provider: CodeCommit
                Version: 1
              OutputArtifacts:
                -
                  Name: SourceOutput
              Configuration:
                RepositoryName: !Ref CodeCommitRepositoryNameForSampleAP
                BranchName: !Ref Branch
              RunOrder: 1
        -
          Name: BuildApplicationAndPushToECR
          Actions:
            -
              Name: CodeBuild
              InputArtifacts:
                -
                  Name: SourceOutput
              ActionTypeId:
                Category: Build
                Owner: AWS
                Version: 1
                Provider: CodeBuild
              Configuration:
                ProjectName: !Ref CodeBuildProjectBuildApplicationAndPushToECR
              OutputArtifacts:
                -
                  Name: CodebuildOutputBuild
              RunOrder: 1
        -
          Name: ApplyApplicationToEKS
          Actions:
            -
              Name: CodeBuild
              InputArtifacts:
                -
                  Name: SourceOutput
              ActionTypeId:
                Category: Build
                Owner: AWS
                Version: 1
                Provider: CodeBuild
              Configuration:
                ProjectName: !Ref CodeBuildProjectApplyToEKS
              OutputArtifacts:
                -
                  Name: CodebuildOutputApply
              RunOrder: 1

Outputs:
  YourCodeCommitRepositoryUrl:
    Value: !GetAtt CodeCommitRepository.CloneUrlHttp
    Description: Set this string to your Git Settings.
  YourECRUri:
    Value: !Sub ${AWS::AccountId}.dkr.ecr.${Region}.amazonaws.com/${ECRNameForSampleAP}
    Description: Set this string to your /cicd/kustomiation/prod/kustomization.yaml
  YourCodeBuildServiceRoleArn:
    Value: !GetAtt CodeBuildServiceRole.Arn
    Description: Set this string to your EKS Cluster's RBAC.
  

  

EKS 클러스터 액세스 권한에 CodeBuild의 IAM 역할 추가

# CodeBuild가 EKS 클러스터를 조작하기 위한 인증 정보 설정
eksctl create iamidentitymapping \\
--region ap-northeast-2 \\
--username codebuild \\
--group system:masters \\
--cluster eks-work-cluster \\
--arn <CodeBuild가 사용하는 IAM 역할의 ARN> 
#CloudFormation의 eks-book-sample-ap-slack 스택 '출력' 탭에 표시된 ARN 설정

예제 소스 코드를 AWS CodeCommit에 푸시하도록 설정

  • 리포지토리 URL은 CloudFormation ‘출력’ 탭의 ‘YourCodeCommitRepositoryUrl’ 항목에서 확인
# 예제 리포지터리의 리모트 설정 변경
git remote rename origin upstream

# origin의 리모트 대상을 새로 생성한 CodeCommit의 리포지터리로 변경
git remote add origin <새로 생성한 CodeCommit 리포지터리의 YourCOdeCommitRepositoryUrl값>

애플리케이션 수정

ECR의 URI와 버전 번호를 업데이트하도록 매니페스트 수정

  • kustomization.yaml
# 버전 번허와 리포지터리 URL 설정 부분
images:
 - name: backend-app-image
   newTag: 1.0.1 # 애플리케이션 버전 번호
   newName: <ECR k8sbook/backend-app 항목의 URI>
  • kustomize를 이용한 실전 배포

코드 커밋에 푸쉬

# 변경 내용을 커밋
git add *
git commit -m 'Test Commit'

# 리포지터리로 푸시
git push origin master

# 인증 정보를 물어보면 코드 커밋용 깃 인증 정보를 입력한다.

자동으로 EKS에 배포된 것을 확인하기

  • EKS-SampleAp-Pipeline 파이프라인 확인
kubectl get pod

kubectl describe pod | grep Image:

kustomize를 이용한 실전 배포

  • 디렉토리 각각에 생성한 kustomization.yaml 내에는 기본 매니페스트가 어디에 있는지 등의 기본 설정을 작성 kustomization.yaml
# test 디렉토리 아래의 설정으로 생성되는 매니페스트 확인
kubectl kustomize test

Column 애플리케이션이나 환경마다 클러스터를 나눠야 하나?

  • ‘개발 환경에서 테스트하고 스테이징 환견에서 최종 점검하여 문제가 없으면 서비스 환경에 배포한다.’

Column 시크릿 등의 비밀 정보를 깃옵스로 관리하는 방법

  • 깃옵스란 모든 쿠버네티스의 매니페스트를 리포지터리로 구성 관리하는 것을 의미.
  • 실드시크릿 kubeseal : 비밀 정보를 암호화한 매니페스트를 생성해 적용하면 클러스터에 복호화된 시크릿을 등록해주는 도구
  • AWS Secrets Manager 활용한 aws-secret-operator 이용

버전 관리

쿠버네티스 버전 업데이트 계획과 지원 정책

  • 9개월 정도에 한번은 버전 업데이트 작업이 필요함

버젼 업데이트 방법

컨트롤 플레인과 시스템 컴포넌트 업데이트

# 클러스터 버전 업데이트 : 몇초 ~ 1분 미만
eksctl upgrade cluster --name eks-work-cluster --approve

# kube-proxy 버전 업데이트
eksctl utils update-kube-proxy --cluster eks-work-cluster --approve

# coredns 버전 업데이트
eksctl utils update-coredns --cluster eks-work-cluster --approve

데이타 플레인 업데이트

  • 새로운 노드 그룹으로 새로운 버전의 데이타 플레인을 생성하고 기존 데이타 플레인에서 전환하는 방법
# 현재 노드 그룹 설정 확인
eksctl get nodegroups --cluster=eks-work-cluster

# 결과 내용
2021 xxx eksctl version 0.44.0
# 노드 그룹 생성
# version은 클러스터의 새로운 버전에 맞춘다.
# 인스턴스 타입과 대수는 기존 설정에 맞춘다.
eksctl create nodegroup \\
--cluster eks-work-cluster \\
--version 1.19 \\
--name eks-work-nodegroup-2 \\
--node-type t2.small \\
--nodes 2 \\
--nodes-min 2 \\
--nodes-max 5 \\
--node-ami auto
# 새로운 노드 2개가 늘어남
kubectl get nodes
# 이전 노드 그룹 삭제
eksctl delete nodegroup --cluster eks-work-cluster --name eks-work-nodegroup

파드를 안전하게 재배치하는 방법

eksctl을 이용한 노드 변경 동작

모든 파드가 동시에 정지하지 않기 위한 방법

  • PodDisruptionBudget 리소스 준비 : ‘정상이 아닌 파드를 허용하는 수’를 결정
  • 레플리카 수가 3인 파드의 .maxUnavailable을 2로 설정했을 때 모든 파드가 드레인 상태의 노드에 스케줄링되었다고 해도 반드시 파드 하나는 정상적인 상태를 유지하면서 파드 재배치가 이루어짐.

Column 버전 업데이트 전략

  • 블루/그린 배포
    • 새로운 클러스터를 생성해 거기에 파드를 동작시킴 → 정상적으로 동작하는지 확인한 후 엔트포인트를 변경

기존 클러서터를 업데이트하는 경우의 장단점

  • 단점 : 컨트롤 플레인 업데이트 시 일시적 다운 현상, 데이터 플레인 업데이트 시 파드의 재배치 전략 고려, 업데이트 실패 시 복원 작업 복잡

새로운 클러스터를 생성하여 변경하는 경우의 장단점

  • 단점 : 엔드포인트가 신규로 생성되어 DNS 수준에서의 변경 필요, 모니터링 등 운영적 기능 설정을 다시 해야 함.

Back to top