EKS CICD 설정 및 매니페스트 관리 방법에 대해 알아봅니다. 지속적인 통합/지속적인 전달 방법은 여러가지가 있습니다. 아마존 EKS에서 관련 구현을 위한 도구와 설정하는 방법을 설명하도록 하겠습니다.
목차
Toggle깃옵스와 깃옵스를 구현하기 위한 EKS CICD 도구 등장
- CI/CD Continuous Integration/Continuous Delivery – 지속적 통합과 지속적 전달
- 위브 클라우드, 스피네이커, 스캐폴드 등
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 수준에서의 변경 필요, 모니터링 등 운영적 기능 설정을 다시 해야 함.