지난글에 이어 이번에는 네트워크 구성하는 방법과 vSphere Cluster에 추가적인 Kubernetes 클러스터를 배포하는 것에 대해 이야기 하고자 한다. 이번 글도 지난글과 동일하게 virtuallyGhetto 블로그 포스팅을 참고하여 그대로 진행한 내용이라고 보면 된다.
지난 포스팅은 다음 링크에서 확인할 수 있다.
지난글에 이어 이번에는 네트워크 구성하는 방법과 vSphere Cluster에 추가적인 Kubernetes 클러스터를 배포하는 것에 대해 이야기 하고자 한다. 이번 글도 지난글과 동일하게 virtuallyGhetto 블로그 포스팅을 참고하여 그대로 진행한 내용이라고 보면 된다.
지난 포스팅은 다음 링크에서 확인할 수 있다.
스타트업으로 이직후 정신없는 6개월을 보냈고 집에서 하는 사이드 프로젝트나 공부하는 것들이 소홀해지는것 같아 마음을 다잡고자 작성하는 포스팅이다. 최근 몇개월 간은 회사 블로그 글만 작성하다보니 개인 블로그를 거의 못하고 있어서 회사 제품과는 아직까지 거리가 있는 플랫폼 공부차 작성한다. 순수하게 정보공유차 작성하는 것이며, 특정회사의 회사의 제품이나 플랫폼을 홍보하고자 함은 아니다.
또 한해가 지나갔다. 2019년 한일 정리하면서 2020년 플랜 기록용으로 끄적거려본다.
Work
Main Tools
Side (Community포함)
Speak
2020 목표
이전 포스팅에서 EKS 클러스터와 어플리케이션 배포 및 테스트를 간단하게 해봤다. 이번에는 AWS ECS 또는 AWS Batch Application을 CircleCI를 통해 배포 및 업데이트 하는 것을 알아본다.
기본적으로 현재 업무 중에 IBM클라우드에서 AWS로 데이터를 가져와야 하는 업무가 생겨서 시작되었고 정기적으로 매일 새벽에 크롤링(Pull)방식으로 데이터를 복제해야하는 Use-Case를 구현해야 했다.
CI/CD Pipeline구성이 주 목적으로 Application구현은 포스팅 내용에서 제외하였다.
실제 프로젝트에 적용할 구성안이다.
기본적으로 모든 구성은 최대한 비용이 들지 않고 AWS 종속성을 제거한 방법으로 구성하였다.
이번 데모에서는 다음과 같은 시나리오로 진행하려고 한다.
기본적으로 Batch 컴퓨팅을 목적으로 하는 서비스로 컴퓨팅 타입을 Fargate 또는 EC2 중 하나를 선택하여 동적으로 프로비저닝하는 서비스이다.
초기 설정이 번거롭긴 하지만 한번 구성하고 나면 별도 관리가 필요없고 Batch작업이 발생하는것과 컨테이너 이미지 보관비용 이외에는 추가 비용이 발생하지 않기 때문에 비용측면에서 장점이 있는 서비스이다.
위에서 이야기한 정기적으로 새벽마다 크롤링(Pull)방식으로 데이터를 복제해야하는 Use-Case를 구현하는것이 적당한 워크로드 구현방법이라 생각했기 때문에 AWS Batch 서비스를 사용하게 되었다.
간단한 Batch demo는 https://github.com/awslabs/aws-batch-helpers/에서 확인이 가능하며 CircleCI연동을 위해 Fork후 진행하였다.
Demo Repository : https://github.com/ddiiwoong/aws-batch-helpers/
모든 Pipeline은 CircleCI기반으로 작성하였다. 혹자는 AWS Code Commit, Pipeline을 사용하지 않느냐고 문의하시는 분들도 있는데 다음과 같은 이유로 CircleCI를 선택하였다.
개인적으로 Registry는 구축형보다는 Managed서비스를 사용하는것이 좋다고 보기 때문에 Webhook을 Native하게 지원하는 DockerHub도 대체 가능하다고 생각한다.
https://www.terraform.io/docs/providers/aws/index.html
선언적 인프라스트럭처 관리 도구로 많이 사용하고 있는 도구이며 Docs와 블로그 자료가 많은 관계로 따로 설명하지 않겠다.
이번 포스트에서는 AWS Batch를 생성하는 영역을 Terraform이 담당한다.
version: 2.1
orbs:
aws-ecr: circleci/aws-ecr@6.1.0
aws-ecs: circleci/aws-ecs@0.0.8
aws-s3: circleci/aws-s3@1.0.11
aws-cli: circleci/aws-cli@0.1.13
jobs:
sh-s3-upload:
docker:
- image: 'circleci/python:2.7'
steps:
- checkout
- aws-s3/copy:
from: ./myjob.sh
to: 's3://batch-ecr-test/myjob.sh'
deploy-batch:
executor: aws-cli/default
steps:
- checkout
- aws-cli/install
- run:
name: Update AWS Batch Job
command: |
aws batch register-job-definition --job-definition-name fetch_and_run --type container --container-properties '{ "image": "823928750534.dkr.ecr.ap-northeast-2.amazonaws.com/fetch_and_run:v20190623", "vcpus": 1, "memory": 512}'
workflows:
build-and-deploy:
jobs:
- aws-ecr/build-and-push-image:
repo: $AWS_RESOURCE_NAME_PREFIX
tag: v20190623
- sh-s3-upload:
name: sh-s3-upload
requires:
- aws-ecr/build-and-push-image
- deploy-batch:
name: deploy-batch
requires:
- sh-s3-upload
단계별 설명을 위해 부분적으로 설명하도록 하겠다.
Terraform으로 Batch 배포
resource "aws_batch_compute_environment" "default"{
compute_environment_name = "env_fetch_and_run"
compute_resources {
instance_role = "arn:aws:iam::823928750534:instance-profile/ecsInstanceRole"
instance_type = [
"optimal",
]
desired_vcpus = 1
max_vcpus = 1
min_vcpus = 0
security_group_ids = [
"sg-0632cf81b5c4dff17"
]
subnets = [
"subnet-0c4f8135b536e8fab",
"subnet-0a261950d894cf27e"
]
type = "EC2"
}
service_role = "arn:aws:iam::823928750534:role/service-role/AWSBatchServiceRole"
type ="MANAGED"
}
Batch Computing 환경을 구성하게 되는데 "aws_batch_compute_environment"
를 사용한다.
resource "aws_batch_job_definition" "default" {
name = "fetch_and_run"
type = "container"
container_properties = <<CONTAINER_PROPERTIES
{
"command": [],
"image": "823928750534.dkr.ecr.ap-northeast-2.amazonaws.com/fetch_and_run:v20190623",
"vcpus": 1,
"memory": 512,
"volumes": [],
"environment": [],
"mountPoints": [],
"ulimits": []
}
CONTAINER_PROPERTIES
}
Job Definition 정의("aws_batch_job_definition")에서는 Resource spec 및 Command(Docker RUN), Container IMAGE 등을 선언하게 된다.
.circleci/config.yml에 CircleCI 버전 명시 및 관련 orb(ecr,ecs,s3,cli) 추가
기본적으로 2.1로 설정을 해야 ecs, eks orb 사용이 가능하다.
version: 2.1
orbs:
aws-ecr: circleci/aws-ecr@6.1.0
aws-ecs: circleci/aws-ecs@0.0.8
aws-s3: circleci/aws-s3@1.0.11
aws-cli: circleci/aws-cli@0.1.13
workflow 구성
build-and-push-image
-> sh-s3-upload
-> deploy-batch
순으로 순차적으로 pipeline구성을 하였다.
workflows:
build-and-deploy:
jobs:
- aws-ecr/build-and-push-image:
repo: $AWS_RESOURCE_NAME_PREFIX # data-crawler-test
tag: v20190623
# create-repo: true
# dockerfile: Dockerfile
- sh-s3-upload:
name: sh-s3-upload
requires:
- aws-ecr/build-and-push-image
- deploy-batch:
name: deploy-batch
requires:
- sh-s3-upload
기본적으로 CircleCI에서는 orb job을 [orb name]/[predefined-job] 형식으로 선언한다.
I. 첫번째 step에서는 container image를 build하고 push하는 Job을 수행하므로 aws-ecr/build-and-push-image
을 선언한다.
자세한 내용은 https://circleci.com/orbs/registry/orb/circleci/aws-ecr를 확인하자.
II. 두번째 step에서는 S3에 배치스크립트를 올리는 Custom Job을 수행한다. Job은 Step의 모음으로 선행되어야할 Job은 상위에 requires:
에서 선언하면 된다.
III. 세번째 step에서는 AWS Batch job definition의 container image를 교체(revision 변경)하는 Custom Job을 수행한다.
custom job 구성 Workflow에서 선언한 Custom Job의 상세 definition을 기재한다.
jobs:
sh-s3-upload:
docker:
- image: 'circleci/python:2.7'
steps:
- checkout
- aws-s3/copy:
from: ./myjob.sh
to: 's3://batch-ecr-test/myjob.sh'
deploy-batch:
executor: aws-cli/default
steps:
- aws-cli/install
- run:
name: Update AWS Batch Job
command: |
aws batch register-job-definition --job-definition-name fetch_and_run --type container --container-properties '{ "image": "823928750534.dkr.ecr.ap-northeast-2.amazonaws.com/fetch_and_run:v20190623", "vcpus": 1, "memory": 512}'
I. sh-s3-upload
에서는 aws-s3/copy
를 사용하여 원하는 S3버킷에 사용할 스크립트를 업로드 한다. checkout
에서는 현재 상태의 git repo를 clone하게 된다.
II. deploy-batch
에서는 aws-cli/install
를 사용하여 기존에 작성한 Batch Job definition을 awscli로 원하는 새로운 image로 업데이트 하게 된다.
빌드 및 배포과정을 간단하게 설명하면 코드가 업데이트되면 CircleCI는 해당 저장소의 .circleci/config.yml
을 기준으로 빌드 및 배포를 시작하게 된다.
https://circleci.com/docs/2.0/status-badges/
위 링크와 설정을 참고하여 아래 그림과 같이 Build Statud Badge를 달 수 있다.
이번에는 CircleCI를 가지고 AWS Batch job을 구성하는 데모를 진행하였다.
얼마전인가에도 어형부형님께서도 언급하신 선언적인 구성 기반에 최대한 serverless 환경에서의 배포를 추구하게 되다 보니 아래와 같은 CI/CD Pipeline 시나리오
를 구상하게 되었다.
새로운 기능의 브랜치(New Feature)가 Git에 push되면 빌드와 테스트가 트리거되어 자동으로 진행되고 동시에 개발자가 PR을 작성하면 동료 및 담당 상급자에게 코드 리뷰를 받게 된다.
리뷰가 통과되고 CircleCI에서 빌드가 성공했다면 승인단계를 통해 Merge를 하고 CircleCI 가 한 번 더 빌드를 시작하고 배포까지 수행하게 된다.
ECS나 Batch로 배포는 CircleCI를 통해 직접 배포를 하고 EC2나 EKS로의 배포는 Terraform 및 ArgoCD를 통해 배포를 진행하는 방식이다.
다음번 포스팅에는 위 그림 기반으로 CircleCI와 ArgoCD를 활용하여 EKS기반 배포과정을 정리할 예정이다.
CI/CD는 개발단계에서 지속적인 통합, 배포를 통해 효율성을 높여주는 도구라고 말할수 있다. 특히 GitOps가 중요시 되는 최근 트렌드에서 Public Git서비스와 통합은 필수적인 요소이다.
GitHub MarketPlace에서 CI
라고 검색하면 다음과 같은 결과를 얻을수 있다.
CircleCI, Travis CI, Google Cloud Build 등 최근 트렌드한 도구들을 확인할 수 있다.
이번 포스팅에서는 CircleCI
를 GitHub
과 연동해서 AWS ECR Push 및 EKS로 배포하는 간단한 Pipeline을 구성하는 방법을 적어보고자 한다.
circleci.com에 접속하여 Sign Up을 진행하면 GitHub과 BitBucket 계정을 연동할 수 있는데 일반적인 GitHub 3rd Party OAuth연동 진행을 하게된다.
위 그림처럼 모든 Repository가 확인되고 Follow할 Repository를 선택하면 초기 구성이 완료된다.
첫 연동이 되면 Build를 진행하게 되는데 위 그림과 같이 error를 보게 되는데 이는 기본적으로 circleci가 필요로 하는 기본 설정값(.circleci/config.yaml)이 없어서 발생하는 오류이다.
#!/bin/sh -eo pipefail
# No configuration was found in your project. Please refer to https://circleci.com/docs/2.0/ to get started with your configuration.
#
# -------
# Warning: This configuration was auto-generated to show you the message above.
# Don't rerun this job. Rerunning will have no effect.
false
위에서 보이는것 처럼 config를 체크하는것도 하나의 가상머신(컨테이너)이 진행하는데 CircleCI콘솔에서 Spin up Environment
로그를 보면 Docker(18.09.6)로 aws Linux기반으로 환경구성을 하는 것을 알 수 있다.
Build-agent version 1.0.11727-b0960fc9 (2019-05-23T02:12:54+0000)
Docker Engine Version: 18.09.6
Kernel Version: Linux 9a20a41aeae4 4.15.0-1035-aws #37-Ubuntu SMP Mon Mar 18 16:15:14 UTC 2019 x86_64 Linux
Starting container bash:4.4.19
using image bash@sha256:9f0a4aa3c9931bd5fdda51b1b2b74a0398a8eabeaf9519d807e010b9d9d41993
...
아래 링크는 CircleCI에서 자주 사용하는 용어들을 따로 정리한 페이지이다. 주로 컨테이너 기반으로 동작하기 때문에 용어들은 Docker에서 사용하는 용어과 겹치는 부분이 많다.
https://circleci.com/docs/2.0/glossary/
위 링크 내용을 확인하면 Orbs
라는 용어가 나오는데 이는 공유가능한 패키지로 Jenkins의 Plugin과 유사한 개념이라고 보면 된다. CircleCI에서 제공하는 자체 패키지 뿐만 아니라 3rd Party orbs를 제공하고 있다.
MacOS에서는 brew를 통해 cli를 설치하고 orb 리스트를 확인하거나 https://circleci.com/orbs/registry/에서 확인할 수 있다.
$ brew install circleci
$ circleci orb list
Orbs found: 43. Showing only certified orbs.
Add --uncertified for a list of all orbs.
circleci/android (0.1.0)
circleci/artifactory (1.0.0)
circleci/aws-cli (0.1.13)
circleci/aws-code-deploy (0.0.9)
circleci/aws-ecr (6.1.0)
circleci/aws-ecs (0.0.8)
circleci/aws-eks (0.1.0)
circleci/aws-s3 (1.0.11)
...
circleci/jira (1.0.5)
circleci/jq (1.9.0)
circleci/kubernetes (0.3.0)
circleci/lein-nvd (0.0.2)
circleci/maven (0.0.8)
circleci/node (1.0.1)
...
circleci/slack (3.2.0)
circleci/twilio (0.0.1)
circleci/welcome-orb (0.3.1)
In order to see more details about each orb, type: `circleci orb info orb-namespace/orb-name`
Search, filter, and view sources for all Orbs online at https://circleci.com/orbs/registry/
GitHub 또는 Bitbucket에서 관리하는 Repository가 CircleCI 프로젝트로 승인되면 최초에는 컨테이너나 가상머신환경(2core 4GB)이 프로비저닝 되고 자동으로 테스트가 진행된다.
테스트가 완료된 후 성공 또는 실패에 대한 Alert설정(Email, Slack)이 가능하고 각 단계(job, workflow)에 대한 결과는 각 단계별 세부 정보 페이지에서 확인할 수 있다.
또한 배포는 AWS CodeDeploy, AWS ECS, AWS S3, AWS EKS, Google Kubernetes Engine (GKE) 및 Heroku 등 다양한 환경에 코드를 배포하도록 구성 할 수 있다. 이외의 클라우드 서비스 배포는 SSH를 통해 직접 사용하거나 terraform과 같은 도구를 가지고 해당 클라우드 서비스의 API통해 자동화가 가능한 구조로 되어있다.
https://github.com/CircleCI-Public/circleci-demo-aws-eks
위 demo는 CircleCI를 이용하여 다음과 같은 workflow (상세내용 아래 config.yaml 참고)를 수행한다.
version: 2.1
orbs:
aws-eks: circleci/aws-eks@0.1.0
aws-ecr: circleci/aws-ecr@3.1.0
kubernetes: circleci/kubernetes@0.3.0
공통적으로 orb를 3가지를 추가하였기 때문에 각단계마다 관련 orb를 추가하는 단계를 거치게 된다.
workflows:
deployment:
jobs:
- aws-ecr/build_and_push_image:
name: build-and-push-image
account-url: AWS_ECR_URL
region: AWS_DEFAULT_REGION
repo: eks_orb_demo_app
dockerfile: ~/project/demo_app/Dockerfile
path: ~/project/demo_app
tag: ${CIRCLE_SHA1}
# repository가 없을 경우 생성하는 옵션
# create-repo: true
us-west-2
219547004475.dkr.ecr.us-west-2.amazonaws.com/eks_orb_demo_app
workflows:
deployment:
jobs:
- aws-eks/create-cluster:
cluster-name: eks-orb-demo-app-deployment
aws-region: $AWS_DEFAULT_REGION
requires:
- build-and-push-image
jobs:
deploy-application:
executor: aws-eks/python3
parameters:
...
steps:
- checkout
- run:
name: Create deployment manifest
command: |
BUILD_DATE=$(date '+%Y%m%d%H%M%S')
cat deployment/demo-app-deployment.yaml.template |\
sed "s|DOCKER_IMAGE_NAME|<< parameters.docker-image-name >>|\
g;s|BUILD_DATE_VALUE|$BUILD_DATE|g;s|VERSION_INFO_VALUE|\
<< parameters.version-info >>|g" > deployment/demo-app-deployment.yaml
- aws-eks/update-kubeconfig-with-authenticator:
cluster-name: << parameters.cluster-name >>
install-kubectl: true
aws-region: << parameters.aws-region >>
- kubernetes/create-or-update-resource:
resource-file-path: "deployment/demo-app-deployment.yaml"
get-rollout-status: true
resource-name: deployment/demoapp
- kubernetes/create-or-update-resource:
resource-file-path: "deployment/demo-app-service.yaml"
...
workflows:
deployment:
jobs:
- deploy-application:
cluster-name: eks-orb-demo-app-deployment
aws-region: $AWS_DEFAULT_REGION
docker-image-name: "${AWS_ECR_URL}/eks_orb_demo_app:${CIRCLE_SHA1}"
version-info: "${CIRCLE_SHA1}"
requires:
- aws-eks/create-cluster
...
workflows:
deployment:
jobs:
- test-application:
name: test-application
cluster-name: eks-orb-demo-app-deployment
aws-region: $AWS_DEFAULT_REGION
expected-version-info: "${CIRCLE_SHA1}"
requires:
- deploy-application
jobs:
test-application:
executor: aws-eks/python3
parameters:
...
steps:
- aws-eks/update-kubeconfig-with-authenticator:
cluster-name: << parameters.cluster-name >>
install-kubectl: true
aws-region: << parameters.aws-region >>
- run:
name: Wait for service to be ready
command: |
kubectl get pods
kubectl get services
sleep 30
for attempt in {1..20}; do
EXTERNAL_IP=$(kubectl get service demoapp | awk '{print $4}' | tail -n1)
echo "Checking external IP: ${EXTERNAL_IP}"
if [ -n "${EXTERNAL_IP}" ] && [ -z $(echo "${EXTERNAL_IP}" | grep "pending") ]; then
break
fi
echo "Waiting for external IP to be ready: ${EXTERNAL_IP}"
sleep 10
done
sleep 180
curl -s --retry 10 "http://$EXTERNAL_IP" | grep "<< parameters.expected-version-info >>"
jobs:
undeploy-application:
executor: aws-eks/python3
parameters:
...
steps:
- aws-eks/update-kubeconfig-with-authenticator:
cluster-name: << parameters.cluster-name >>
install-kubectl: true
aws-region: << parameters.aws-region >>
- kubernetes/delete-resource:
resource-types: "deployment,service"
label-selector: "app=demo"
wait: true
- run:
name: Check on pod status
command: |
kubectl get pods
workflows:
deployment:
jobs:
- undeploy-application:
cluster-name: eks-orb-demo-app-deployment
aws-region: $AWS_DEFAULT_REGION
requires:
- test-application
workflows:
deployment:
jobs:
- aws-eks/delete-cluster:
cluster-name: eks-orb-demo-app-deployment
aws-region: $AWS_DEFAULT_REGION
wait: true
requires:
- undeploy-application
상세 config는 다음 링크에서 확인할 수 있다.
https://github.com/ddiiwoong/circleci-demo-aws-eks/blob/master/.circleci/config.yml
위 Workflow를 수행하게 되면 마지막에 어플리케이션과 클러스터를 삭제하기 때문에 해당 workflow는 제외하고 수행한다. 해당 config를 commit하면 바로 해당 CI가 트리거 되어 시작되게 된다.
# - undeploy-application:
# cluster-name: eks-orb-demo-app-deployment
# aws-region: $AWS_DEFAULT_REGION
# requires:
# - test-application
# - aws-eks/delete-cluster:
# cluster-name: eks-orb-demo-app-deployment
# aws-region: $AWS_DEFAULT_REGION
# wait: true
# requires:
# - undeploy-application
클러스터 구성포함해서 총 20분정도 소요되었다. 실제 eksctl
로 프로비저닝하게 되면 CloudFormation으로 수행되기 때문에 약 15-20분 정도 소요되니 간단한 빌드,배포,테스트는 5분정도 소요된것을 알 수 있다.
삭제는 역순으로 진행하거나 위에 주석 처리된 영역만 수행하면 된다.
간단하게 CircleCI를 가지고 GitOps와 CI/CD를 구성하는 데모를 진행하였다.
CircleCI는 Jenkins와 유사하지만 Public서비스이고 Slave관리에 대해서 의존적이지 않기 때문에 기존에 Jenkins를 사용한 경험이 있다면 아주 쉽게 구성할 수 있다.
https://circleci.com/docs/2.0/migrating-from-jenkins를 참고하면 Jenkins와 차이점을 알 수 있는데 가장 큰 장점은 병렬로 테스트나 Job을 수행할수 있다는 점과 Lambda와 같은 서버리스 앱을 배포할때 정말 서버리스 환경으로 구성할수 있다는 점이다.
이는 Git Repository를 연동할때도 Java Plugin을 설치해야하는 번거로움을 덜 수 있다. 가장 중요한건 SaaS라는 장점도 무시할수 없다. Travis 무료 플랜과 비교해도 작은 프로젝트의 경우 별도의 Docker environment(2core, 4GB)를 제공받기 때문에 지연없이 바로 빌드가 가능하다는 점이다.
다음번 포스팅에는 terraform을 통해 ECR과 ECS로 배포하는 workflow를 살펴보려고 한다.