# AWS Code Series와 Kustomize를 사용한 Gitops 자동화 구성
소프트웨어의 개발 단계는 크게 4가지로 나눌 수 있으며 이것들을 자동화 해 놓은 단계를 CI/CD라고 분류합니다.
CI/CD는 모던 어플리케이션에서 사용되는 중요한 요소중 하나 입니다. 또한 회사 또는 프로젝트의 규모와 상관없이 구성할 수 있으며 개발주기와 속도를 매우 높일 수 있습니다.
특히 쿠버네티스환경의 경우 k8s의 manifest로 인프라를 관리, 운영하기 때문에 이를 관리하기위한 방법이 필요합니다. 이런 경우 인프라 및 애플리케이션 구성관리를 별도의 git 레포지토리로 관리하는 방법사용하여 문제점을 해결 할 수 있습니다. 이것을 GitOps라고 합니다. Weaveworks가 처음 이 용어를 사용하기 시작했습니다. GitOps는 컨테이너 뿐만아니라 클라우드 네이티브한 환경에서는 모두 사용할수 있습니다.
## GitOps를 수행하기 위해선 네가지가 필요합니다.
1. 선언형 인프라(Declarative Model)가 필요합니다. 인프라를 기술한 문서가 필요하고 이것을 단일 레포지토리에서 관리합니다.
1. Git의 커밋버전이 시스템이 원하는 표준 상태입니다.
1. 선언형 인프라를 실제 운영환경과 상태차이를 일치시켜주는 자동화도구가 필요합니다. GitOps는 쿠버네티스를 위한 운영 모델로 사용하는 경우가 많으며 (물론 다른 모델도 사용가능합니다.) 대부분의 도구는 Kubernetes를 대상으로 릴리즈되고 있습니다. 대표적인 예로 FluxCD와 ArgoCD가 있습니다.
1. 정확성을 보장하고 코드와 환경이 일치하지 않을 경우 소프트웨어 에이전트가 치유 또는 경고를 보냅니다.
## GitOps를 사용하면 다음과 같은 이점이 있습니다.
1. 생산성을 향산시킬 수 있습니다.
1. 컨테이너가 아닌 Git과 같은 버전관리도구를 사용하기 때문에 개발자에게 더 친숙합니다.
1. Git 워크플로우를 사용하여 클러스터를 관리하면 모든 클러스터의 변경사항에 대한 로그를 남길 수 있습니다.
1. Git의 롤백, 브런치와 같은 기능으로 안정적이고 재현이 가능한 환경을 얻을 수 있습니다.
1. GitOps는 인프라 앱 및 Kubernetes 추가 기능 변경을 위한 하나의 일관정인 모델을 제공합니다.
1. 변경사항을 관리하고 추적하는데 필요한 Git의 강력한 보안기능을 사용하여 클러스터의 상태를 안전하게 정의할 수 있습니다.
AWS는 CI/CD를 위해 모든 스테이지에서 사용할 수 있는 서비스를 제공하고 있습니다. 그 중 각 단계를 시각화 해주는 CodePipeline과 컨테이너형 서버리스 빌드 서비스인 CodeBuild, 컨테이너 이미지를 저장하는 Elastic Container Registry, ArgoCD or FluxCD를 사용하여 GitOps를수행할 수 있습니다. 아래 보여드릴 예제에서는 github와 ArgoCD를 사용하여 진행하도록 하겠습니다.

### 컨테이너 이미지 빌드 및 ECR 푸쉬
Application Code가 들어있는 Git Repository에 새로운 Commit이 푸쉬 될 때마다 CodePipeline과 CodeBuild를 사용하여 빌드 후 Elastic Container Registry에 배포할 것 입니다. 이때 컨테이너 이미지의 태그는 commit의 태그와 동일하게 유지합니다. 이렇게 설정하면 어떤 코드의 빌드가 ECR의 어떤 이미지인지 확실하게 알 수 있습니다. CodeBuild와 CodePipeline을사용하여 Elastic Container Registry로 배포하는 내용에 대한 자세한 과정과 설명은 https://aws.amazon.com/blogs/devops/build-a-continuous-delivery-pipeline-for-your-container-images-with-amazon-ecr-as-source/ 이 블로그를 참조해주세요.

CodePipeline과 CodeBuild를 사용하여 파이프라인을 작성하면 위와 같은 형태가 될 것입니다. Checkout한 코드를 빌드하고 ECR로 push하는 과정을 확인할 수 있습니다.
그리고 관리자는 배포할 tag를 k8s manifest파일에 지정하여 k8s code repository에 push하면 ArgoCD가 k8s manifest의 변화를 감지하여 새로운 배포버전을 쿠버네티스에 배포할 것입니다.
즉 코드의 ECR배포와 EKS배포 사이에 버전을 지정해야하는 수동작업이 추가로 들어가야합니다. 이 부분을 Codebuild와 Kustomize로 자동화 해보도록 하겠습니다.
# Kustomize
kustomize는 kustomization파일을 통해 쿠버네티스 오브젝트를 사용자가 원하는 대로 변경하는 독립형 도구 입니다. 자세한 내용은 이곳에서 확인하실 수 있습니다. 이 예제는 Kustomize의 명령형 구성변경 기능을 사용하여 CodeBuild에서 k8s manifest의 tag를 새 버전으로 변경하여 다시 k8s manifest가 있는 git repository로 push 할 것입니다. K8s manifest repository에 kustomization.yaml 파일을 생성하고 아래형태의 코드를 붙여넣습니다.
```yaml=
resources:
- deployment.yaml
- namespaces.yaml
- service.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
images:
- name: <12345678.dkr.ecr.us-west-2.amazonaws.com/projectname>
newTag: latest
```
위 내용은 예제일 뿐이지만 다음 CodePipeline의 Deploy 스테이지에서 kustomize edit 명령어를 사용하여 resources와 images의 name, newtag를 변경합니다. 잠시후 사용할 CodeBuild의 buildspec 코드를 사용하기 위해서는 해당 구조가 동일하게 있어야합니다.
<12345678.dkr.ecr.us-west-2.amazonaws.com/projectname>
위 값은 사용하는 ECR 주소로 변경해주세요.
# k8s manifest 자동 업데이트
## 1. 배포 스테이지 추가
기존의 ECR 배포 작업까지 구성되어있던 파이프라인에 k8s manifest를 업데이트하는 과정을 추가하겠습니다.
1. 먼저 기존의 CodePipeline에 새로운 스테이지를 추가하기위해 오른쪽 상단의 Edit버튼을 클릭합니다.
1. ECR에 push하는 작업인 Build스테이지 하단의 Add Stage 버튼을 클릭하고 Stage name에 Deploy를 입력하고 Add Stage를 클릭합니다.

## 2. K8s manifesta Deploy Action 추가하기
1. 새로운 스테이지가 추가된 것을 확인할 수 있습니다. 이제 Deploy 스테이지 카드의 Edit Stage를 클릭한후 Add action group를 클릭합니다.
1. Edit action의 Action name에 K8s manifesta Deploy Action을 입력합니다.
1. Action provier는 AWS CodeBuild를 선택합니다.
1. Region은 코드 파이프라인이 위치한 리전을 선택합니다.
1. Input artifacts는 코드아티팩트의 이름을 지정합니다. ( 기본 SourceArtifact )

## 3. CodeBuild Project 추가
1. Project name의 Create project 버튼을 클릭합니다.
1. CodeBuid의 Build project를 생성하는 창이 새로 뜹니다.
1. Project configuration의 Project name에 kubernetes-manifest-push를 입력합니다.
1. Description에 “for update k8s manifest” 라고 입력합니다.

1. 스크롤을 내려 Source의 Source provider를 No source를 선택합니다.

1. Environment카드에서 Environment image의 Managed image를 선택합니다.
1. Operating system은 Amazon linux2를 선택합니다.
2. Runtime은 Standard를 선택합니다.
3. Image는 aws/codebuild/amazonlinux2-x86-64-standard:3.0을 선택합니다.
4. Image version은 Always use the lastest image for this runtime version을 선택합니다.
5. Environment type은 Linux를 선택합니다.
6. Service role은 New service role을 선택합니다.

1. Buildspec 카드의 Insert build commands를 선택합니다. CodeBuild가 소스에서 시작하지 않기때문에 Buildspec.yaml을 코드에서 가져올 수 없습니다. 그래서 빌드 명령어를 프로젝트에 직접 입력시킬 것입니다. Switch to editor를 클릭하여 편집기를 활성화 시키겠습니다.
1. 아래 코드를 복사하여 편집창 안에 붙여넣습니다.
```yaml=
version: 0.2
phases:
install:
commands:
# - yum -y update
- curl --silent --location --remote-name "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v3.2.3/kustomize_kustomize.v3.2.3_linux_amd64" && \
- chmod a+x kustomize_kustomize.v3.2.3_linux_amd64 && \
- mv kustomize_kustomize.v3.2.3_linux_amd64 /usr/local/bin/kustomize
- kustomize version
pre_build:
commands:
- TAG="$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | head -c 8)"
- git config --global user.email "${GIT_ID}@amazon.com"
- git config --global user.name "Codebuild"
# - command
build:
commands:
- ls -a
- echo ${GIT_TOKEN}
- git clone https://${GIT_ID}:${GIT_TOKEN}@github.com/${GIT_TEAM}/${GIT_REPOSITORY}.git
- cd ${GIT_REPOSITORY}
- kustomize edit set image ${ECR_REPOSITORY}:${TAG}
- git add .
- git commit -m "Update Image Version :${TAG}"
- git push
```
위 코드는 지정한 Git repository를 clone한 후 kustomize를 사용하여 image tag를 업데이트 하여 push합니다.
코드는 총 다섯개의 변수를 가지고 있습니다. 이 매개변수들은 CodePipeline에서 Environment variables로 설정됩니다.
```yaml=
kustomize edit set image ${ECR_REPOSITORY}:${TAG}
```
이 부분이 실제 kustomize를 사용하여 ECR_Repository의 tag를 변경하는 부분입니다.
입력이 끝났다면 스크롤을 내려 가장 하단의 Create build project버튼을 클릭합니다.

다시 작성중이었던 Codepipeline 창으로 돌아와 Project name에 kubernetes-manifest-push를 선택합니다.(선택지에 나오지 않는 경우 오른쪽 새로고침 화살표를 클릭하세요.)

## 4.CodePipeline 환경변수 추가
1. Environment variables에서 Buildspec.yaml에서 사용하는 매개변수 값을 셋팅해줍니다. Add environment variable을 클릭하여 변수를 생성합니다. 이렇게 매개변수를 분리하여 구성하면 나중에 Pipeline만 복제하여 쉽게 변경 사용이 가능합니다.
1. GIT_REPOSITORY : k8s manifest repository 주소입니다.
1. GIT_TOKEN : github접근을 위한 token입니다.
1. GIT_ID : 사용하는 git의 id입니다.
1. ECR_REPOSITORY : 배포하려는 Container Image의 ECR 주소를 입력하세요.
1. GIT_TEAM : Github의 팀에 속해있다면 Team 이름을 입력해주세요. (개인 레포지토리라면 github id를 입력해주세요)
1. 아래 화면과 같이 입력이 됩니다.

1. github 개발자 토큰은 github settings에서 Developer setting탭으로 이동 후 Persnal access tokens를 클릭하여 새로 발급받으시면됩니다. 토큰이 유출되지 않게 유의하세요.

## 5.스테이지 설정 마무리
1. Output artifacts에 BuildArtifactinfra를 입력합니다.
1. 마지막 스크롤의 가장 아래 Done버튼을 클릭하여 Action 생성을 마무리합니다.

1. 스테이지 카드의 오른족 Done을 클릭하여 Stage 생성을 마무리합니다.
1. 오른쪽 상단의 Save버튼을 클릭하여 모든 설정을 끝냅니다.

1. 위와 같은 메세지가 나오면 Save를 클릭합니다.
## 6.재배포
1. 오른쪽 상단의 Release change버튼을 클릭해 변경사항을 배포합니다.

1. 이제 3개의 스테이지가 완성되었습니다.

# 배포 확인
1. 새로운 코드를 Push하면 코드를 빌드하고 컨테이너 이미지를 ECR에 Push하고 새로운 Tag를 k8s Repository에 Push합니다.
1. 코드빌드가 변경된 이미지 Tag를 업데이트한 Commit을 확인할 수 있습니다.

1. ArgoCD에서도 새로 push된 코드가 배포된 것을 확인할 수 있습니다. 마지막 동기화한 내용이 CodeBuild가 업데이트한 내용임을 확인할 수 있습니다.
# 정리
코드시리즈를 사용하여 App repository와 k8s repository간 변경사항 업데이트를 자동화 하였습니다. 이것을 사용하면 개발자는 자신의 코드가 빌드되어 ECR에 배포되고 K8s 레포지토리까지 반영되는 과정을 확인할 수 있습니다.
### 참고
CodeCommit 사용시 buildspec 예제
#### 환경변수
- CODECOMMIT_URL : 코드커밋 주소 (GRC)
- GIT_REPOSITORY : 프로젝트 이름
- GIT_ID : CODEBUILD
#### buildspec.yaml
```yaml!
version: 0.2
phases:
install:
commands:
# - yum -y update
- curl --silent --location --remote-name "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v3.2.3/kustomize_kustomize.v3.2.3_linux_amd64" && \
- chmod a+x kustomize_kustomize.v3.2.3_linux_amd64 && \
- mv kustomize_kustomize.v3.2.3_linux_amd64 /usr/local/bin/kustomize
- kustomize version
- pip --version
- curl -O https://bootstrap.pypa.io/get-pip.py
- python3 get-pip.py --user
- pip install git-remote-codecommit
pre_build:
commands:
- TAG="$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | head -c 8)"
- git config --global user.email "${GIT_ID}@amazon.com"
- git config --global user.name "Codebuild"
- git config --global --replace-all credential.helper '!aws codecommit credential-helper $@'
- git config --global credential.UseHttpPath true
# - command
build:
commands:
- ls -a
- echo ${CODECOMMIT_URL}
- echo ${GIT_REPOSITORY}
- git clone ${CODECOMMIT_URL}
- cd ${GIT_REPOSITORY}
- kustomize edit set image ${ECR_REPOSITORY}:${TAG}
- git add .
- git commit -m "Update Image Version :${TAG}"
- git push
```