Helm을 통한 워크로드 배포
이번에는 Helm을 통하여 마이크로서비스 워크로드를 배포해 보겠습니다.
1. Helm이란?
Helm이란?
Helm은 Kubernetes 패키지 매니저입니다. 이를 사용하면 Kubernetes 어플리케이션을 정의, 저장, 관리하는데 도움이 됩니다. Helm 차트라는 패키지 형식을 사용하여, Kubernetes 어플리케이션의 배포를 간소화하고 재사용 가능하게 만듭니다.
Helm을 사용하면 얻을 수 있는 주요 장점은 다음과 같습니다:
재사용 가능한 패키지: Helm 차트를 사용하면, Kubernetes 어플리케이션을 패키지로 만들어 재사용할 수 있습니다. 이는 배포 과정을 간소화하고, 일관성을 유지하는데 도움이 됩니다.
버전 관리: Helm은 차트의 버전 관리를 지원합니다. 이를 통해 어플리케이션의 이전 버전으로 롤백하거나, 특정 버전을 배포하는 것이 가능합니다.
커뮤니티 지원: Helm은 강력한 커뮤니티 지원을 받고 있습니다. 이를 통해 다양한 오픈 소스 Helm 차트를 사용할 수 있습니다.
배포 자동화: Helm은 CI/CD 파이프라인에 통합되어, 어플리케이션의 배포를 자동화하는데 사용될 수 있습니다.
여기에서는 CI/CD 파이프라인을 사용하지 않고, Helm CLI을 통해 마이크로서비스 워크로드를 배포하는 방법을 살펴보겠습니다.
2. Outer Component 생성
Outer Component 생성
우리는 오늘 마이크로서비스 아키텍처 (Microservice Architecture; MSA)를 깊이 있게 다루지는 않을 것이지만, 아래 그림에서와 같이 마이크로서비스가 동작할 수 있도록 해주는 외부 아키텍처 (Outer Architecture) 컴포넌트들을 테라폼으로 먼저 생성해 보도록 하겠습니다.
여기에는 Persistence를 위한 Amazon RDS, Amazon DocumentDB (MongoDB 호환), MOM (Message Oriented Middleware)을 위한 Amazon MSK 등이 포함됩니다.

앞서 우리가 아마존 EKS 클러스터 생성하는 과정에서 이러한 외부 컴포넌트를 함께 생성할 수도 있었지만, 관심사를 분리하고 EKS 클러스터를 빠르게 생성하여 다음 실습을 진행하기 위해 이 과정은 생략했던 것입니다.
(참고) 오늘 워크샵에 포함된 테라폼 코드는 참조용 아키텍처를 구현하기 위하여
Production과Staging환경을 위해 위한Amazon EKS클러스터 각각 1개씩 총 2개가 생성되었습니다. 앞으로의 실습은Production을 대상으로 하지만 추후 필요할 경우Staging클러스터를 활용할 수도 있습니다.
아래 명령을 통해 외부 아키텍처 구성 요소를 상세화하는 변수를 terraform.tfvars 파일에 정의하고, 곧바로 테라폼 명령을 통해 자원을 생성합니다.
cd ~/environment/eks-workshop/infrastructure-terraform
cat <<EOF >> terraform.tfvars
create_msk = true
create_documentdb = true
create_rds = true
EOF
# Plan을 생성하지 않고 곧바로 자원 생성
terraform apply -auto-approve(참고) 이 작업은 컴퓨팅, 네트워킹 및 스토리지 자원을 확보하고 데이터베이스 (Amazon RDS Aurora MySQL, Amazon DocumentDB)와 카프카 브로커 (Amazon Managed Streaming for Apache Kafka)를 생성하는데, 전체 작업을 완료하는데 약 20 ~ 30분 정도 소요됩니다.
자원 생성이 마무리되면 아래와 같은 자원이 생성됩니다.
Amazon RDS Aurora데이터베이스 (MySQL 호환) 클러스터 1개
(참고) 함께 생성된
flightspecials-test-postgres-db데이터베이스는 마이크로서비스 관련 실습에 사용되는 것으로서 본 워크샵과는 관련이 없습니다.
위 1의
Amazon RDS Aurora데이터베이스의Root계정 비밀번호를 안전하게 저장하는Secrets ManagerSecretsAmazon DocumentDB(MongoDB 5.0 호환) 클러스터 1개
(참고) 실습 환경에 따라 권한 관련 오류가 AWS 콘솔에 표시될 수 있으나, 실습에 영향을 주지는 않으므로 무시해도 됩니다.
위 3의
Amazon DocumentDB클러스터의Admin비밀 번호를 안전하게 저장하는Secrets ManagerSecretsAmazon MSK (Managed Streaming for Apache Kafka)클러스터 1개
(참고)
Amazon MSK서비스는 업스트림Apache Kafka와 호환되는 서비스로서, 브로커와 클라이언트의 모든 기능을 그대로 사용할 수 있으며,AWS IAM에 기반한 인가 (Authorization) 기능이 추가되어 있습니다. 이 기능을 사용하면IAM역할을 통해 클러스터에 대한 액세스를 제어할 수 있으며,IRSA항목에서 접해볼 예정입니다.
앞서 생성한 Amazon EKS 클러스터와 함께 전체 아키텍처를 살펴보면 아래와 같습니다.

3. Helm을 통한 워크로드 배포
Helm을 통한 워크로드 배포
이제 Amazon EKS 클러스터와 외부 아키텍처 자원이 모두 준비되었으니, 이번에는 Helm을 통해 마이크로서비스 워크로드를 배포해 보겠습니다.
Helm 차트에 대한 상세한 내용은 배포가 완료된 이후에 같이 살펴 보도록 하겠습니다.
3.1. 컨테이너 이미지 빌드
아래 명령을 수행하여 컨테이너 이미지를 빌드합니다.
cd ~/environment/eks-workshop
# docker-compose 시에 사용할 환경 변수 파일 생성
# (참고) 이 파일은 실제로는 사용되는 환경 변수 파일이 아니라, docker-compose에서 사용할 환경 변수를 정의한 파일입니다 (Docker Build 시에 Warning 방지).
cat <<EOF > .env
RABBITMQ_USR=rabbit-user-prod
RABBITMQ_PWD=rabbit-pwd-prod
MONGODB_USR=mongodb-user-prod
MONGODB_PWD=mongodb-pwd-prod
MYSQL_ROOT_PWD=rootpwd
MYSQL_USR=mysql-user-prod
MYSQL_PWD=mysql-pwd-prod
GATEWAY_TLS_PWD=password
EOF
# 컨테이너 이미지 빌드
make clean build docker-build3.2. 컨테이너 이미지 레지스트리에 푸시
위에서 빌드된 컨테이너 이미지를 Amazon ECR (Amazon Container Registry)에 푸시합니다.
cd ~/environment/eks-workshop
# ECR에 로그인.
make docker-login
# 컨테이너 이미지 태깅.
make docker-tags ENV=prod
# 컨테이너 이미지 ECR 푸시.
make docker-push ENV=prod위 명령을 통해 Amazon ECR에 컨테이너 이미지가 푸시되었습니다.

3.3. Helm 차트 의존성 설치
Helm 차트 의존성 설치
Helm 차트에는 의존성이 있을 수 있습니다. 이번에는 Helm 차트의 의존성을 설치합니다.
cd ~/environment/eks-workshop
# Production 환경 확인
kcp
# Helm 의존성 설치
make helm-dep-up-all ENV=prod3.4. Helm 차트 설치 - 워크로드 배포
Helm 차트 설치 - 워크로드 배포
이제 Helm 차트를 통해 마이크로서비스 워크로드를 배포합니다.
cd ~/environment/eks-workshop
# Production 환경 확인
kcp
# Helm 차트 설치
make helm-install-env ENV=prod
# 모든 Pod가 Running 상태가 될 때까지 대기
#kubectl wait --timeout=600s --for=condition=ready pod --all -n hands-on
# 모든 Pod가 실행되면 Pod 들의 이미지를 확인
kubectl get pods -n hands-on -o json | jq .items[].spec.containers[].image위 과정을 통해 모든 마이크로서비스 워크로드가 배포된 상태는 아래 그림과 같습니다.

그런데 무엇인가 잘못된 것 같습니다.
review-service가 계속 Restart되고 있으며 궁극적으로 CrashLoopBackOff 상태에 빠질 것입니다.
review-service의 로그를 확인해 보겠습니다.

또한 정상적으로 실행되고 있는 것처럼 보이는 product-service, product-composite-service, reeommendation-servier의 로그를 살펴보면 카프카 브로커 접속에 문제가 있는 것으로 보입니다.

무엇이 잘못된 것일까요?
우리는 Amazon EKS 클러스터 내에서 비즈니스 로직을 수행하는 마이크로서비스 애플리케이션들을 배보하였습니다. 그리고 이들은 Amazon RDS, Amazon DocumentDB, Amazon MSK와 같은 외부 서비스들과 연동되어 있습니다. 이러한 외부 서비스들과의 연동을 위해서는 AWS IAM을 통한 인증 및 권한 부여가 필요합니다. 그리고 이러한 인증 및 권한 부여를 위해서는 IRSA (IAM Role for Service Account)를 사용하여 Kubernetes Service Account에 IAM Role을 연결해야 합니다.
다음 섹션에서는 이러한 IRSA를 통해 Amazon RDS, Amazon DocumentDB, Amazon MSK와 같은 외부 서비스들과의 연동을 위한 IAM Role for Service Account (IRSA)에 대해 살펴보도록 하겠습니다.
(참고)
Pod의CrashLoopBackOff상태
실패한 Kubernetes 파드가
CrashLoopBackoff상태가 되기까지의 기본 시간은 파드의 재시작 정책과 실패의 원인에 따라 달라집니다.파드가 자동으로 재시작하도록 설정된 경우 (재시작 정책이 "Always" 또는 "OnFailure"로 설정된 경우), Kubernetes는 파드가 실패할 때마다 재시작을 시도합니다.
일정 횟수의 재시작 시도가 실패하면, 파드는
CrashLoopBackoff상태에 들어갑니다.파드가
CrashLoopBackoff상태에 들어가기 전의 시간은 지수 백오프 지연(10초, 20초, 40초, ...)에 의해 결정되며, 이는 5분으로 제한되고, 성공적인 실행 후 10분이 지나면 재설정됩니다.이는 Kubernetes가 처음에는 재시작을 시도하기 전에 10초 동안 기다린다는 것을 의미합니다. 만약 재시작이 다시 실패하면, 20초, 그 다음 40초를 기다리고, 이런 식으로 지연이 5분에 도달할 때까지 계속됩니다.
만약 파드가 적어도 10분 동안 성공적으로 실행되면, 지연은 초기 값으로 재설정됩니다.
재시작 정책은 아래 3가지의 값이 있습니다.
Always (Default): Container들이 정상적으로 종료(Zero Exit Code)되었더라도 재시작하는 정책입니다. Container가 어떻게 종료되든 간에 무조건 구동 중이여야한다면 해당 옵션을 사용하는 것이 유용합니다.
OnFailure : Container가 비정상적으로 종료(Non-Zero Exit Code)하는 경우에만 재시작하는 정책입니다. Pod가 특정 태스크를 수행하도록 설게되었고, 그 태스크가 완료 (성공)되면 재시작하지 않는게 정상인 경우라면 해당 옵션을 사용하는 것이 유용합니다.
Never: Container의 종료 코드와 관계없이 재시작하지 않는 정책입니다.
재시작 정책에 따른 재시작은 노드의
kubelet이 수행합니다.
Probe중livenessProbe,startupProbe의 경우 실패 (Failure) 시, 재시작하지만readinessProbe는 실패해도 재시작하지 않습니다.
4. Helm 차트 상세 설명
Helm 차트 상세 설명
4.1. Helm 소개
Helm 소개
잘 알려진 대로, Kubernetes에 마이크로서비스를 배포하려면 Deployment와 Service 객체의 원하는 상태를 선언하는 매니페스트 파일을 작성해야 합니다. 마이크로서비스에 추가 구성이 필요한 경우, ConfigMaps과 Secrets에 대한 매니페스트도 추가해야 할 수 있습니다. 원하는 상태를 선언하고 Kubernetes에 책임을 넘기는 접근법은 실제 상태가 항상 원하는 상태에 최대한 가깝게 유지되도록 하는데 매우 유용합니다.
그러나 이러한 매니페스트 파일을 작성하고 관리하는 것은 상당한 유지 관리 부담이 될 수 있습니다. 파일에는 많은 보일러플레이트 코드가 포함되어 있을 것이며, 이는 모든 마이크로서비스에 대해 동일하게 보일 수 있는 중복된 매니페스트를 의미합니다. 환경별 설정을 복제하지 않고 처리하는 것도 번거롭습니다. 콘텐츠의 일부만 업데이트해야 하는 경우에도 매니페스트 파일 전체 세트를 복제해야 합니다.
테스트, QA, 프로덕션 환경과 같은 소수의 환경에만 배포될 소수의 마이크로서비스의 경우, 이것이 큰 문제가 아닐 수 있습니다. 그러나 마이크로서비스의 수가 수십 개, 수백 개로 늘어나고, 다양한 테스트, QA, 프로덕션 환경으로 빠르게 전환해야 하는 다양한 마이크로서비스 그룹을 관리하는 것에은 유지 관리 문제가 대두될 수 있습니다.
이러한 단점을 해결하기 위해 오픈소스 기반 패키지인 Helm을 사용한 것입니다. Helm에는 마이크로서비스 또는 환경에 특정한 Kubernetes의 다양한 일반적인 정의에서 설정을 추출하는 데 사용할 수 있는 템플릿 언어가 제공됩니다.
(참고) 시스템 랜드스케이프가 작고 몇 개의 배포 객체만 있으면 더 간단한 템플릿 도구가 충분할 수 있습니다. 예를 들어, Ansible과 Jinja2 템플릿에 익숙하다면, 그것들을 대신 사용할 수 있습니다. 또한,
kubectl자체도Kustomize을 기본적으로 지원하므로,Kubernetes매니페스트 파일을 커스터마이즈하기 위해 템플릿이 필요 없는 대안을 제공합니다.
Helm에서 패키지는 차트라고 알려져 있습니다. 차트는 템플릿, 템플릿의 기본값, 그리고 다른 차트의 정의에 대한 선택적인 의존성을 포함합니다. 배포해야 하는 각 컴포넌트, 즉 마이크로서비스와 그들이 의존하는 데이터베이스와 큐 매니저와 같은 리소스 매니저는, 그것을 어떻게 배포할지를 설명하는 자체 차트를 가질 것입니다. 컴포넌트의 차트에서 보일러플레이트 정의를 추출하기 위해, 라이브러리 차트 라는 특별한 유형의 차트를 사용할 것입니다. 라이브러리 차트는 배포 가능한 정의를 포함하지 않지만, 다른 차트가 Kubernetes 매니페스트를 위해 사용할 것으로 예상되는 템플릿만 포함합니다 - 우리의 경우, Deployment, Service, Ingress, ConfigMap, 그리고 Secret 객체에 라이브러리 차트를 사용합니다. 마지막으로, 모든 컴포넌트를 다양한 유형의 환경에 배포하는 방법을 설명할 수 있도록, 예를 들어 개발 및 테스트 또는 스테이징 및 프로덕션을 위해, 부모 차트 (Parent Chart)와 하위 차트 (Child 혹은 Subchart)의 개념을 사용할 것입니다. 우리는 dev-env와 prod-env라는 두 가지 유형의 환경을 정의하였는데 오늘 실습은 단순함을 위해 prod-env라는 환경만을 사용합니다 (더 복잡하므로). 각 환경은 부모 차트로 구현되며, 마이크로서비스 차트와 같은 다른 하위 차트 세트에 의존합니다. 환경 차트는 또한 요청된 파드의 수, Docker 이미지 버전, 자격 증명, 리소스 요청 및 제한과 같은 환경별 기본값을 제공합니다.
요약하면, 우리는 다은과 같은 차트를 구성하였습니다.
하나의 재사용 가능한 라이브러리 차트인
common차트컴포넌트폴더에 위치한 마이크로서비스 차트의 세트(참고)
컴포넌트폴더에는 로컬에서의 작업을 위한 리소스 매니저 정의 (예: 몽고DB, MySQL, RabbitMQ, Kafka)가 포함되어 있지만 이번 워크샵에서는 사용하지 않을 예정입니다.
그리고 환경 폴더에 위치한 두 개의
환경별 부모 차트
파일 구조는 다음과 같으며 내려받은 소스 경로의 ~/environment/eks-workshop/kubernetes/helm에서 다음 명령을 실행하여 확인할 수 있습니다.
sudo yum install -y tree
cd ~/environment/eks-workshop/kubernetes/helm
tree .

구현된 Helm 차트를 Helm Chart Repository에 게시하여 차트를 공유할 수 있습니다. 혹은 Chart Museum과 같은 오픈 소스 차트 저장소를 사용하여 내부적으로 차트를 공유할 수도 있습니다.
4.2. Helm 명령어
Helm 명령어
Helm을 사용하여 작업을 수행하려면, Helm의 CLI 도구인 helm을 사용합니다.
가장 자주 사용되는 Helm 명령어는 다음과 같습니다:
create: 새 차트를 생성하는 데 사용됩니다.dependency update(dep up으로 줄여서 표현): 다른 차트에 대한 의존성을 해소합니다. 의존성 차트는charts폴더에 위치하고,Chart.lock파일에 해당 정보가 업데이트됩니다.dependency build:Chart.lock파일의 내용에 기반하여 의존성을 다시 빌드합니다.template: 템플릿에 의해 생성된 정의 파일을 렌더링합니다.install: 차트를 설치합니다. 이 명령은 차트가 제공하는 값을 재정의할 수 있는데,--set플래그를 사용하여 단일 값을 재정의하거나--values플래그를 사용하여 자체 yaml 파일에 값을 제공할 수 있습니다.install --Dry-run: 배포를 시뮬레이션하지만 수행하지는 않습니다. 이는 배포를 실행하기 전에 검증하는 데 유용합니다.list: 현재 네임스페이스의 설치 목록을 나열합니다.upgrade: 기존 설치를 업데이트합니다.uninstall: 설치를 제거합니다.
Helm이 제공하는 명령어에 대한 전체 문서는 https://helm.sh/docs/helm/에서 확인할 수 있습니다. 이제 이 Helm 명령어들을 적용해 보고 차트가 어떤 파일로 구성되는지 살펴보겠습니다.
4.3. Helm 차트 구조
Helm 차트 구조
Helm 차트는 미리 정의된 파일 구조를 가지고 있는데 다음 파일들을 사용합니다:
Chart.yaml: 차트에 대한 일반 정보와 이 차트가 의존할 수 있는 다른 차트 목록이 포함되어 있습니다.templates: 차트를 배포하는 데 사용될 템플릿이 포함된 폴더입니다.values.yaml: 템플릿에서 사용하는 변수의 기본값이 포함되어 있습니다.Chart.lock:Helm이Chart.yaml파일에 설명된 의존성을 해결할 때 생성하는 파일입니다. 이 정보는 실제로 어떤 의존성이 사용되는지에 대한 자세한 정보를 설명합니다. 이는Helm이 전체 의존성 트리를 추적하고, 차트가 마지막으로 작동했을 때의 의존성 트리를 정확하게 재생성할 수 있게 합니다.charts: Helm이 의존성을 해결한 후에 이 차트가 의존하는 차트가 위치하는 폴더입니다..helmignore:.gitignore와 비슷한 용도로 사용되는 특정 파일이나 폴더를 무시하는데 사용하는 파일입니다. 차트를 빌드할 때 제외해야 하는 파일을 나열하는 데 사용할 수 있습니다.
이제 Helm 차트 내부의 구조를 이해했으니, Helm의 핵심 기능 중 하나인 템플릿 메커니즘과 값을 전달하는 방법에 대해 알아보겠습니다.
4.4. Helm 템플릿과 값 (Values)
Helm 템플릿과 값 (Values)
Helm 템플릿은 Kubernetes 매니페스트 파일을 매개변수화하는 데 사용됩니다. 템플릿을 사용하면, 각 마이크로서비스에 대한 장황한 Deployment 매니페스트를 유지 관리할 필요가 없습니다. 대신, 특정 마이크로서비스에 대한 매니페스트가 렌더링될 때 템플릿에 마이크로서비스 특정 값이 배치될 위치에 대한 플레이스홀더를 포함하는 공통 템플릿을 정의할 수 있습니다. kubernetes/helm/common/templates/_deployment.yaml에서 추출한 예를 살펴봅시다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "common.fullname" . }}
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: {{ .Chart.Name }}이것은 일반적으로 자주 접하는 Deployment 매니페스트와 매우 유사해 보입니다. 다만, {{ ... }} 구조를 사용하여 템플릿에 마이크로서비스 특정 값들을 삽입하는 것이 다른 점입니다. {{ include "common.fullname" . }} 구조는 뒤에 설명하겠지만 다른 템플릿을 호출하는 데 사용됩니다. 나머지 두 개의 구조는 Helm의 내장 객체 중 하나를 사용하여 값을 삽입하는 데 사용됩니다.
가장 자주 사용되는 Helm 내장 객체는 다음과 같습니다:
Values: 차트의values.yaml파일 또는install과 같은Helm명령을 실행할 때 제공된 값들을 참조하는 데 사용됩니다.Release: 현재 설치된 릴리즈에 대한 메타데이터를 제공하는 데 사용됩니다. 이에는 다음과 같은 필드가 포함됩니다:Name: 릴리즈의 이름Namespace: 설치가 수행되는 네임스페이스의 이름Service: 설치 서비스의 이름, 항상Helm을 반환합니다
Chart:Chart.yaml파일에서 정보를 접근하는 데 사용됩니다. 유용한 메타데이터 필드의 예는 다음과 같습니다:Name: 차트의 이름Version: 차트의 버전 번호
File: 차트의 특정 파일에 접근하기 위한 함수를 포함합니다. 이 장에서는 파일 객체에서 다음 두 함수를 사용합니다:Glob:글로브 패턴을 기반으로 차트 내의 파일을 반환합니다. 예를 들어, "config-repo/*" 패턴은config-repo폴더에서 찾은 모든 파일을 반환합니다.AsConfig: 파일의 내용을ConfigMap에서 값을 선언하는 데 적합한 YAML 맵으로 반환합니다.
Capabilities: 설치가 수행되는 쿠버네티스 클러스터의 기능에 대한 정보를 찾는 데 사용할 수 있습니다. 예를 들어, 템플릿은 이 객체의 정보를 사용하여 실제 쿠버네티스 클러스터가 지원하는 API 버전에 따라 매니페스트를 채택할 수 있습니다. 이 장에서는 이 객체를 사용하지 않지만, 보다 고급 사용 사례를 위해 이를 염두에 두고 있으면 좋습니다.
내장 객체에 대한 자세한 내용은 https://helm.sh/docs/chart_template_guide/builtin_objects에서 확인할 수 있습니다.
모든 객체는 대부분의 경우 마침표(.)로 표현되는 현재 범위가 Root Context 경로인 트리에서 접근 가능합니다. 위의 예에서 .Values.replicaCount와 .Chart.Name에서 볼 수 있듯이, 내장 객체인 Values와 Chart는 현재 범위 바로 아래에서 접근 가능합니다. 위의 include 지시문에서는 common.fullname이라는 템플릿에 전달된 매개변수로서의 점(.)을 볼 수 있습니다. 이는 전체 트리가 템플릿에 전송된다는 것을 의미합니다. 템플릿에 전체 트리를 전송하는 대신 하위 트리를 전달할 수 있습니다.
Helm 함수 중 일부는 현재 범위가 변경되어 루트 컨텍스트를 가리키지 않게 됩니다. 대표적인 예로서 값의 컬렉션을 반복하는 데 사용할 수 있는 range 함수가 그것입니다. range 함수의 범위 내에서 루트 컨텍스트에 접근해야 하는 경우, 미리 정의된 변수 $를 사용할 수 있습니다.
Helm 템플릿은 다른 객체를 참조하기 위한 변수 선언도 지원합니다. 예를 들어:
$name := .Release.Name이 예에서는 변수 name이 선언되어 현재 처리 중인 Helm 릴리즈의 값을 저장하고 있습니다. 이 변수는 이후에 사용될 수 있습니다.
템플릿 메커니즘에 대해 간단하게 알아보았고, 이제는 세 가지 유형의 차트가 어떻게 구성되는지 알아봅시다.
가장 중요한 차트인 common 차트부터 시작하여, 그 후에 컴포넌트와 환경 차트를 설명하겠습니다.
4.5. common 라이브러리 차트
common 라이브러리 차트
이 차트는 재사용 가능한 템플릿, 즉 명명된 템플릿을 포함하고 있으며, 이는 전체 실습에서 사용되는 네 가지 유형의 쿠버네티스 매니페스트인 Deployment, Service, Ingress, ConfigMap, Secret에 대한 것입니다 (Issuer도 정의되어 있으나 이번 워크샵에서는 Amazon PCA (Private Certificate Authority) 기능을 대산 사용합니다). common 차트의 구조와 내용은 helm create 명령의 출력을 기반으로 합니다. 특히, _helpers.tpl 템플릿 파일은 명명 규칙에 대한 모범 사례를 재사용하기 위해 유지되었습니다. 다음 템플릿을 선언하여 명명 규칙을 캡슐화합니다:
common.name: 차트 이름을 기반으로 합니다.common.fullname: 릴리즈 이름과 차트의 조합을 기반으로 합니다. 이번 워크샵에서는 이 명명 규칙은 사용되지 않고 단순히 차트의 이름을 사용할 것입니다.common.chart: 차트 이름과 버전을 기반으로 합니다.
자세한 내용은 _helpers.tpl 파일의 구현을 참조하십시오.
파일 이름에서 특기할 만한 사항은, 다른 템플릿에서만 사용되고 매니페스트를 생성하는 데 사용되지 않는 명명된 템플릿은 밑줄, _로 시작하는 이름을 가져야 합니다. 이는 Helm이 그것들을 단독으로 사용하여 매니페스트를 생성하려는 시도를 방지하기 위한 것입니다.
앞서 언급한 쿠버네티스 매니페스트에 대한 명명된 템플릿은 주요한 부분의 로직을 포함하고 있으므로, Helm 차트의 대부분의 복잡성을 담당하고 있습니다. 하나씩 살펴보면 다음과 같습니다.
4.5.1. ConfigMap 템플릿
ConfigMap 템플릿
이 템플릿은 config-repo 폴더 내의 파일들로부터 ConfigMaps를 생성하도록 설계되었습니다. 각 ConfigMap은 특정 Deployment에 필요한 모든 비민감 정보 설정을 포함하게 됩니다. Deployment 매니페스트는 Pod 템플릿에서 ConfigMap의 내용을 볼륨으로 매핑합니다. 이로 인해 Deployment에 의해 생성된 Pod들은 로컬 파일 시스템의 파일로서 설정에 접근할 수 있게 됩니다. 자세한 내용은 이후의 Deployment 템플릿 섹션을 참조하십시오.
config-repo 폴더는 common 차트를 사용하는 차트 내에 위치해야 하며, 이 워크샵에서는 각 마이크로서비스 Helm 템플릿 경로에 Symbolic Link를 사용하여 중복을 피하면서 공통 설정을 재사용합니다.
해당 템플릿 파일은 _configmap_from_file.yaml로 명명되어 있으며, 다음과 같이 보입니다:
{{- define "common.configmap_from_file" -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "common.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "common.name" . }}
helm.sh/chart: {{ include "common.chart" . }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
data:
{{ (.Files.Glob "config-repo/*").AsConfig | indent 2 }}
{{- end -}}템플릿에 대한 설명은 다음과 같습니다:
첫 번째 줄인
{{- define "common.configmap_from_file " -}}는 재사용 가능한 템플릿의 이름을 선언하는 데 사용됩니다. 템플릿의 범위는 일치하는{{- end -}}로 끝나며, 이 예제에서는 마지막 줄입니다.ConfigMap의 이름을 설정하기 위해,_helpers.tpl파일의common.fullname템플릿이 사용됩니다.다음으로, 나중에
ConfigMap을 쉽게 식별할 수 있도록 여러 레이블이 정의됩니다. 다시_helpers.tpl파일의 템플릿이 사용되어 이름을 설정하고 사용된 차트를 지정합니다. 이 서비스가Helm을 사용하여 생성되었음을 표시하기 위해, 레이블app.kubernetes.io/managed-by는 필드.Release.Service의 값으로 설정됩니다. 앞서Release객체에 대한 설명에서, 이것이 항상Helm값을 반환한다는 것을 알 수 있습니다.다음은
ConfigMap의 핵심 부분인data섹션입니다.ConfigMap의 실제 구성을 지정하기 위해,Files객체의Glob함수가 사용되어config-repo폴더의 모든 파일을 가져옵니다. 다음으로,AsConfig함수가 파일의 내용에 적용되어 적절한YAML 맵을 형성합니다. 결과는indent 함수로 파이프되어 적절한 들여쓰기가 렌더링되도록 합니다. 이 경우에는 두 개의 공백 문자가 사용합니다.
{{- 및 -}}의 하이픈은 중괄호 안의 지시문 처리 후 남은 선행 및 후행 공백을 제거하는 데 사용됩니다.
아래는 ConfigMap 템플릿의 예입니다.
cd ~/environment/eks-workshop/kubernetes/helm/common
cp values-aws-template.yaml values.yaml
cd ~/environment/eks-workshop/kubernetes/helm/components/product
helm dependency update .
helm template . -s templates/configmap_from_file.yaml
cd ~/environment/eks-workshop/kubernetes/helm/common
rm -rf values.yaml---
# Source: product/templates/configmap_from_file.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: product
labels:
app.kubernetes.io/name: product
helm.sh/chart: product-1.0.0
app.kubernetes.io/managed-by: Helm
data:
application.yml: |
app:
auth-server: localhost
spring.rabbitmq:
host: 127.0.0.1
port: 5672
spring.cloud.stream.kafka.binder:
brokers: 127.0.0.1
defaultBrokerPort: 9092
spring.cloud.stream.defaultBinder: rabbit
# WARNING: Exposing all management endpoints over http should only be used during development, must be locked down in production!
management.endpoint.health.show-details: "ALWAYS"
management.endpoints.web.exposure.include: "*"
management.endpoint.health.probes.enabled: true
management.endpoint.health.group.readiness.include: readinessState, rabbit, db, mongo
management.health.rabbit.enabled: true
management.health.kafka.enabled: false
...위에서 보듯이 data 필드는 해당 마이크로서비스의 config-repo 폴더 내에서 Symbolic Link된 공통 config-repot 파일의 내용을 포함하고 있습니다.
4.5.2. Secrets 템플릿
Secrets 템플릿
이 템플릿은 dev-env와 prod-env 환경에서 필요로 하는 자격 증명 값을 위해 Secrets를 생성하도록 설계되었습니다. 이 Secrets는 Pods에서 환경 변수로 매핑됩니다. 자세한 내용은 아래의 Deployment 템플릿 섹션에서 다시 한번 설명할 예정입니다. 환경이 여러 Secrets를 정의할 수 있어야 하므로, 이 템플릿은 Helm의 range 함수를 사용하여 여러 Secret 매니페스트를 생성하도록 설계되었습니다. 템플릿 파일의 이름은 _secrets.yaml이며, 이는 다음과 같습니다:
{{- define "common.secrets" -}}
{{- range $secretName, $secretMap := .Values.secrets }}
apiVersion: v1
kind: Secret
metadata:
name: {{ $secretName }}
labels:
app.kubernetes.io/name: {{ $secretName }}
helm.sh/chart: {{ include "common.chart" $ }}
app.kubernetes.io/managed-by: {{ $.Release.Service }}
type: Opaque
data:
{{- range $key, $val := $secretMap }}
{{ $key }}: {{ $val | b64enc }}
{{- end }}
---
{{- end -}}
{{- end -}}템플릿에 대한 설명은 다음과 같습니다:
1행의 템플릿 선언 다음 2행에서
range함수를 사용합니다. 이 함수는.Values.secrets필드가Secret이름과Secret의 키/값 쌍의 맵을 포함하고 있다고 가정합니다. 각 환경 부모 차트의values.yaml파일 중 하나에서Secrets필드의 선언은 다음과 같이 보일 것입니다:secrets: a-secret: key-1: secret-value-1 key-2: secret-value-2 another-secret: key-3: secret-value-3이 정의는
a-secret과another-secret이라는 이름의 두 개의Secrets를 렌더링합니다.range함수는 현재Secret이름과 그 맵을 변수$secretName과$secretMap에 할당합니다.range함수는 현재 범위를 변경하기 때문에, 루트 컨텍스트를common.chart템플릿에 전달하기 위해 점 표기법을 더 이상 사용할 수 없습니다. 대신 변수$를 사용해야 합니다.매니페스트의
data섹션에서는 현재Secret의 키/값 쌍을 순회하기 위해 두 번째range함수가 사용됩니다. 각 키/값 쌍은range함수에 의해 변수$key와$val에 할당됩니다.마지막으로,
Secret의 키/값 쌍은data섹션의 맵 항목으로 정의됩니다.$val변수의 값은Secret매니페스트에 필요한 적절한Base64인코딩을 얻기 위해b64enc함수에 파이프됩니다.
---는 렌더링된 Secret 매니페스트를 서로 분리하여 별도의 YAML 문서로 처리되도록 사용됩니다.
다음은 Secrets 템플릿의 예입니다.
cd ~/environment/eks-workshop
make helm-dep-up-all ENV=prod
helm template kubernetes/helm/environments/prod-env -s templates/secrets.yaml---
# Source: prod-env/templates/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: mongodb-secrets
labels:
app.kubernetes.io/name: mongodb-secrets
helm.sh/chart: prod-env-1.0.0
app.kubernetes.io/managed-by: Helm
type: Opaque
data:
MONGODB_PASSWORD: alo4dEJYZ3FuQ0hFOXNocA==4.5.3. Service 템플릿
Service 템플릿
Service 템플릿은 공통 차트의 기본값을 각 컴포넌트 차트의 (공통 차트를 사용하는) 특정 값으로 덮어쓰는 기능을 포함하고 있습니다. 예를 들어, 공통 차트는 서비스 유형과 서비스가 노출할 포트에 대한 기본값을 제공할 것입니다. 이는 대부분의 마이크로서비스에 유용하지만, 일부는 자체 values.yaml 파일에서 이러한 기본값을 덮어써야 할 필요가 있을 것입니다.
템플릿 파일의 이름은 _service.yaml이며, 다른 명명된 템플릿처럼 이름 선언으로 시작하고, 덮어쓰기 메커니즘의 구현이 이어집니다. 이는 다음과 같습니다:
{{- define "common.service" -}}
{{- $common := dict "Values" .Values.common -}}
{{- $noCommon := omit .Values "common" -}}
{{- $overrides := dict "Values" $noCommon -}}
{{- $noValues := omit . "Values" -}}
{{- with merge $noValues $overrides $common -}}이 구조는 다음과 같이 설명할 수 있습니다:
_service.yaml템플릿이 마이크로서비스에 의해 서비스 매니페스트를 렌더링하는 데 사용될 때, 마이크로서비스의values.yaml파일의 값은.Values객체에서 사용할 수 있고, 공통 차트의 값은.Values.common필드에서 사용할 수 있습니다.따라서,
$common변수는dict함수에 의해 생성된 딕셔너리를 참조하며, 이 딕셔너리는 키로는Values와 그 값은 공통 차트의 기본값을 가집니다. 이 값들은.Values객체의common 키에서 가져옵니다.$noCommon변수는common키 아래의 값을 제외한마이크로서비스의 모든 값을 가집니다. 이는omit함수를 사용하여 지정됩니다.$overrides변수는 딕셔너리를 참조하며, 이 딕셔너리도 하나의 키인Values를 가지지만, 그 값은 공통 값을 제외한 마이크로서비스의 값입니다. 이는 이전 줄에서 선언된 $noCommon 변수에서 값을 가져옵니다.$noValues변수는Values객체를 제외한 모든 다른 내장 객체를 가집니다.이제 여기서 덮어쓰기가 발생합니다.
merge함수는$noValues, $overrides, $common변수가 참조하는 딕셔너리를 기반으로 하나의 딕셔너리를 생성합니다. 이 경우,$overrides딕셔너리에서 찾은 값이$common딕셔너리의 값보다 우선하여 그 값을 덮어씁니다.마지막으로,
with함수는{{- end -}}정의에 도달할 때까지 이후의 템플릿 코드에 대한 범위를 변경합니다. 따라서 현재 범위인.은 이제병합된 딕셔너리를 참조하게 됩니다.
이것이 어떻게 작동하는지 예를 들어 보겠습니다. 공통 차트의 values.yaml 파일에는 서비스 유형과 노출된 포트에 대한 다음과 같은 기본 설정이 포함되어 있습니다:
Service:
type: ClusterIP
ports:
- port: 80
targetPort: http
protocol: TCP
name: http이 설정은 ClusterIP 유형의 서비스 객체를 렌더링합니다. 서비스 객체는 포트 80을 노출하고 요청을 그들의 http라는 포트로 포워드합니다.
만약 컴포넌트 서비스 중 하나가 NodePort를 노출하고 다른 포트 설정을 사용해야 한다면, 위의 기본값을 덮어쓰기 위해, 차트의 values.yaml 파일에 다음을 선언합니다:
service:
type: NodePort
ports:
- port: 443
targetPort: 8443
nodePort: 30443서비스 템플릿 파일의 나머지 부분은 다음과 같습니다:
apiVersion: v1
kind: Service
metadata:
name: {{ include "common.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "common.name" . }}
helm.sh/chart: {{ include "common.chart" . }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
{{ toYaml .Values.service.ports | indent 4 }}
selector:
app.kubernetes.io/name: {{ include "common.name" . }}
{{- end -}}
{{- end -}}템플릿에 대한 설명은 다음과 같습니다:
이름과 레이블에 대한 메타데이터 필드는 이전 템플릿에서 이미 본 것과 같은 방식으로 정의됩니다.
서비스의 유형은
.Values.service.type필드로 설정됩니다.서비스가 노출하는 포트는
.Values.service.ports필드를 사용하여 지정됩니다. 내장 함수인toYaml은 그 값을 yaml로 포맷팅하는 데 사용되며, 결과는 indent 함수로 파이프되어 적절한 들여쓰기가 렌더링되도록 합니다. 이 경우에는 4개의 공백 문자입니다.마지막으로,
Pod Selector가 정의됩니다. 이는app.kubernetes.io/name레이블을 기반으로 하며, 이름은common.name템플릿을 사용하여 지정됩니다.
Service 템플릿의 예는 다음과 같습니다.
cd ~/environment/eks-workshop/kubernetes/helm/common
cp values-aws-template.yaml values.yaml
cd ~/environment/eks-workshop/kubernetes/helm/components/product
helm dependency update .
helm template . -s templates/service.yaml
cd ~/environment/eks-workshop/kubernetes/helm/common
rm -rf values.yaml---
# Source: product/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: product
labels:
app.kubernetes.io/name: product
helm.sh/chart: product-1.0.0
app.kubernetes.io/managed-by: Helm
spec:
type: ClusterIP
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
selector:
app.kubernetes.io/name: product4.5.4. Ingress 템플릿
Ingress 템플릿
{{- define "common.ingress" -}}
{{- $common := dict "Values" .Values.common -}}
{{- $noCommon := omit .Values "common" -}}
{{- $overrides := dict "Values" $noCommon -}}
{{- $noValues := omit . "Values" -}}
{{- with merge $noValues $overrides $common -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "common.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "common.name" . }}
helm.sh/chart: {{ include "common.chart" . }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
{{- with .Values.ingress.spec }}
spec:
{{ toYaml . | indent 2 }}
{{- end}}
{{- end }}
{{- end -}}Ingress 템플릿의 전체 내용은 위와 같으며 기본적인 구조는 Service 템플릿과 유사합니다.
하지만 가장 큰 차이점이자 주목해야 할 부분은 annotation 부분과 spec 부분이며 이는 Ingress 리소스의 특성을 정의합니다.
annotation:Ingress객체의 생성이 감지되면AWS Load Balancer Controller는Ingress리소스의annotation을 읽어AWS Application Load Balancer에 대한 설정을 구성합니다. 여기에는 리스닝 포트, HTTPS 인증서,Health Check 경로,타겟 그룹 유형 (IP 또는 인스턴스 ID)등이 포함됩니다.spec:AWS Load Balancer Controller의 경로 규칙과 함께 타겟 그룹 생성의 기반 정보인Service객체를 참조합니다.
Ingress 객체의 자세한 사항은 이후 과정에서 다시 한번 자세히 다루어질 예정입니다.
4.5.5. Deployment 템플릿
Deployment 템플릿
마지막으로, Deployment 매니페스트를 렌더링하기 위한 템플릿이 있습니다. 이 템플릿은 Deployment 매니페스트의 많은 부분을 선택적으로 처리해야 하므로 가장 복잡합니다. 다른 컴포넌트들은 Deployment 매니페스트의 다른 부분을 사용할 것입니다. 공통 차트의 values.yaml 파일에는 대부분의 컴포넌트에 적용 가능한 이러한 설정에 대한 기본값이 포함되어 있어, 각 컴포넌트의 자체 차트의 values.yaml 파일에서 이러한 설정을 덮어쓸 필요를 최소화합니다. Deployment 매니페스트의 다음 부분은 컴포넌트에 의해 선택적으로 사용될 수 있습니다:
컨테이너가 시작될 때 입력되는 매개변수
환경 변수
Secrets로부터의 환경 변수Liveness프로브Readiness프로브ConfigMap및 해당 볼륨템플릿 파일의 이름은
_deployment.yaml이며, 첫 번째 줄은Service템플릿과 매우 유사하게 보이며, 동일한 유형의 덮어쓰기 메커니즘을 사용합니다.
{{- define "common.deployment" -}}
{{- $common := dict "Values" .Values.common -}}
{{- $noCommon := omit .Values "common" -}}
{{- $overrides := dict "Values" $noCommon -}}
{{- $noValues := omit . "Values" -}}
{{- with merge $noValues $overrides $common -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "common.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "common.name" . }}
helm.sh/chart: {{ include "common.chart" . }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "common.name" . }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "common.name" . }}
spec:
# [2024-03-16] KSH: Add service account
{{- if .Values.serviceAccount }}
serviceAccountName: {{ .Values.serviceAccount.name }}
{{- end }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}/{{ .Values.image.name }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
...여기에서는 spec의 핵심 부분이 어떻게 정의되는지 볼 수 있습니다: 요청된 복제본의 수, Pod를 선택하기 위한 선택자 (Selector), 그리고 새로운 Pod를 생성하는 데 사용되는 템플릿입니다. 템플릿은 선택자와 일치하는 레이블과 컨테이너를 시작할 때 사용할 이름, Docker 이미지, 그리고 imagePullPolicy를 정의합니다. 다음으로는 위에서 설명한 매니페스트의 다양한 선택적 부분이 이어집니다.
{{- with .Values.image.args }}
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if .Values.env }}
env:
{{- range $key, $val := .Values.env }}
- name: {{ $key }}
value: {{ $val | quote }}
{{- end }}
{{- end }}
{{- if .Values.envFromSecretRefs }}
envFrom:
{{- range .Values.envFromSecretRefs }}
- secretRef:
name: {{ . }}
{{- end }}
{{- end }}
{{- if .Values.livenessProbe_enabled }}
livenessProbe:
{{ toYaml .Values.livenessProbe | indent 12 }}
{{- end }}
{{- if .Values.readinessProbe_enabled }}
readinessProbe:
{{ toYaml .Values.readinessProbe | indent 12 }}
{{- end }}환경 변수와 Secrets가 환경 변수로 매핑되는 경우, range 함수는 Secrets 템플릿이 사용하는 것과 동일한 방식으로 사용됩니다. 환경 변수는 사용 사례에 따라 컴포넌트 또는 환경 수준에서 지정될 수 있습니다. Secrets는 항상 환경 차트에 의해 지정됩니다. 컴포넌트와 환경 차트에 대한 다음 섹션을 참조하십시오. 매니페스트는 컨테이너가 노출하는 포트, 리소스 요청 및 제한을 선언하여 마무리되며, 마지막으로 선택적으로 ConfigMap과 해당 볼륨을 선언하여 ConfigMap의 파일을 매핑합니다.
ports:
{{ toYaml .Values.ports | indent 12 }}
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- if .Values.configmap.enabled }}
volumeMounts:
- name: {{ include "common.fullname" . }}
mountPath: {{ .Values.configmap.volumeMounts.mountPath }}
volumes:
- name: {{ include "common.fullname" . }}
configMap:
name: {{ include "common.fullname" . }}
{{- end }}
{{- end -}}
{{- end -}}공통 차트의 values.yaml 파일에서는 관심이 갈 만한 몇 가지 기본값을 찾을 수 있습니다. 예를 들어, liveness와 readiness 프로브에 대한 기본값이 어떻게 정의되는지를 볼 수 있습니다.
livenessProbe_enabled: false
livenessProbe:
httpGet:
scheme: HTTP
path: /actuator/health/liveness
port: 80
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 20
successThreshold: 1
readinessProbe_enabled: false
readinessProbe:
httpGet:
scheme: HTTP
path: /actuator/health/readiness
port: 80
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
successThreshold: 1이러한 값들을 선언하면 다음과 같은 내용이 정의됩니다:
프로브는 기본적으로 비활성화되어 있습니다. 왜냐하면 모든
Deployment가 프로브를 사용하지 않기 때문입니다.프로브는
Spring Boot의 Graceful Shutdown (우아한 종료) 및 프로브에 대한 지원 사용과 관련하여,Spring Boot이 노출하는 엔드포인트로 보내는 HTTP GET 요청에 기반합니다.엔드포인트가
2xx또는3xx응답 코드로 응답하는 한, 프로브는 성공한 것으로 간주됩니다.
프로브는 다음 매개변수를 사용하여 구성할 수 있습니다:
initialDelaySeconds는 컨테이너가 시작된 후Kubernetes가 프로브를 얼마나 오래 기다릴지를 지정합니다.periodSeconds는Kubernetes가 프로브 요청을 보내는 시간 간격을 지정합니다.
timeoutSeconds는Kubernetes가 프로브를 실패로 간주하기 전에 응답을 얼마나 오래 기다릴지를 지정합니다.failureThreshold는Kubernetes가 포기하기 전에 실패한 시도를 얼마나 많이 하는지를 지정합니다.liveness프로브의 경우, 이는Pod를 재시작하는 것을 의미합니다.readiness프로브의 경우,Kubernetes는readiness프로브가 다시 성공할 때까지 컨테이너에 더 이상 요청을 보내지 않습니다.successThreshold는 실패 후 프로브가 다시 성공적으로 간주되기 위해 필요한 성공적인 시도의 횟수를 지정합니다. 이는readiness프로브에만 적용되며,liveness프로브에 지정된 경우 1로 설정해야 합니다.
(참고)
프로브의 최적 설정을 찾는 것은 도전적일 수 있습니다. 즉, Pod의 가용성이 변경될 때
Kubernetes에서 신속한 반응을 얻고, 동시에 Pod에 프로브 요청으로 인한 과부하를 주지 않는 적절한 균형을 찾는 것입니다.특히, 너무 낮은 값으로
liveness프로브를 구성하면Kubernetes가 재시작할 필요가 없는Pod를 재시작하게 될 수 있습니다. 단지 약간의 추가 시간이 필요할 뿐임에도 불구하고 말이죠.많은 수의 Pod를 동시에 시작하면, 이로 인해 시작 시간이 길어질 수 있으며, 이는 또한 많은 불필요한 재시작을 초래할 수 있습니다.
프로브의 구성 값이 너무 높게 설정되면 (successThreshold 값 제외),
Kubernetes의 반응이 느려지게 되어, 개발 환경에서는 불편할 수 있습니다. 적절한 값은 또한 사용 가능한 하드웨어에 따라 달라지며, 이는 Pod의 시작 시간에 영향을 미칩니다. 이번 워크샵에서는 불필요한 재시작을 피하기 위해 liveness 프로브의failureThreshold를 높은 값인 20으로 설정하였습니다.
다음은 Deployment 템플릿의 예입니다.
cd ~/environment/eks-workshop/kubernetes/helm/common
cp values-aws-template.yaml values.yaml
cd ~/environment/eks-workshop/kubernetes/helm/components/product
helm dependency update .
helm template . -s templates/deployment.yaml
cd ~/environment/eks-workshop/kubernetes/helm/common
rm -rf values.yaml---
# Source: product/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: product
labels:
app.kubernetes.io/name: product
helm.sh/chart: product-1.0.0
app.kubernetes.io/managed-by: Helm
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: product
template:
metadata:
labels:
app.kubernetes.io/name: product
spec:
# [2024-03-16] KSH: Add service account
containers:
- name: product
image: "${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-2.amazonaws.com/product-service:latest"
imagePullPolicy: Always
env:
- name: SPRING_CLOUD_CONFIG_ENABLED
value: "false"
- name: SPRING_CONFIG_LOCATION
value: "file:/config-repo/application.yml,file:/config-repo/product.yml"
- name: SPRING_PROFILES_ACTIVE
value: "docker"
livenessProbe:
failureThreshold: 20
httpGet:
path: /actuator/health/liveness
port: 80
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 2
readinessProbe:
failureThreshold: 3
httpGet:
path: /actuator/health/readiness
port: 80
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 2
ports:
- containerPort: 80
name: http
protocol: TCP
resources:
limits:
memory: 350Mi
volumeMounts:
- name: product
mountPath: /config-repo
volumes:
- name: product
configMap:
name: product4.6. `Components' 차트
마이크로서비스를 위한 차트들은 components 폴더에 저장되어 있으며, 모두 동일한 파일 구조를 공유합니다:
Chart.yaml은 공통 라이브러리 차트에 대한 의존성을 표현합니다.
템플릿 폴더에는
deployment.yaml과service.yaml두 개의 템플릿이 포함되어 있습니다. 두 템플릿 모두 공통 차트에서 해당 명명된 템플릿을 적용합니다. 예를 들어, Service.yaml 템플릿은 다음과 같습니다:{{- template "common.service" . -}}values.yaml파일에는 마이크로서비스에 특정한 설정이 포함되어 있습니다. 예를 들어,auth-server차트의values파일은 다음과 같습니다:fullnameOverride: auth-server image: name: auth-server env: SPRING_PROFILES_ACTIVE: "docker" livenessProbe_enabled: true readinessProbe_enabled: trueauth-server는이름,Docker 이미지,Spring 프로파일을 선언하고,liveness와readiness프로브의 기본 설정을 사용하려고 합니다.
또한 횡단 관심사 마이크로서비스인 auth-server를 제외하고는 템플릿 폴더에서 우리가 이미 소개한 공통 차트의 명명된 템플릿을 기반으로 ConfigMap을 위한 템플릿을 정의합니다:
{{- template "common.configmap_from_file" . -}}이 템플릿은 차트 폴더, config-repo에서 속성 파일을 찾을 것으로 예상합니다. ~/environment/eks-workshop/config-repo에서 config-repo를 복제하는 것을 피하기 위해, 다음 명령으로 소프트 링크, 또는 심볼릭 링크가 생성되었습니다:
cd $BOOK_HOME/Chapter16/kubernetes/helm/components/config-server
ln -s ../../../../../config-repo/application.yml application.yml
ln -s ../../../../../config-repo/<마이크로서비스 이름>.yml <마이크로서비스 이름>.yml(참고) Git은 소프트 링크를 보존하므로, 소프트 링크를 다시 생성할 필요가 없습니다 -
git clone명령이 그것을 대신해줍니다!
4.7. 환경 (Environment) 차트
환경 (Environment) 차트
마지막으로, environments 폴더의 dev-env prod-env 차트는 모든 것을 결합하여 일반적인 개발/테스트 또는 스테이징/프로덕션 환경에 대한 설치 패키지를 완성합니다. 그들의 Charts.yaml 파일은 공통 차트와 components 폴더의 차트에 대한 의존성을 포함하고 있으며, 템플릿 폴더에는 환경별 자격 증명을 Secrets로 생성하는 secrets.yaml 템플릿이 포함되어 있습니다. 이는 공통 차트의 Secrets에 대한 명명된 템플릿을 기반으로 하며, 다음과 같이 보입니다:
{{- template "common.secrets" . -}}(참고) 위에서 자격 증명을
Secrets에 저장한다고 설명되었지만, 이번 실습에서는 서비스별 특성에 따라 보다 안전한AWS Secrets Manager에 저장하는 것으로 구성되어 있습니다.
Amazon RDS Aurora MySQL:Amazon Secrets Manager에 데이터베이스 암호 저장, 또한 이를 지원하는JDBC 드라이버도 관련 어플리케이션에 적용되어 있습니다.
Amazon DocumentDB (MongoDB 호환):Amazon Secrets Manager와KubernetesSecrets에 동시 저장
Amazon MSK (Managed Streaming for Apache Kafka): 암호를 사용하지 않고IAM역할을 사용하여 인증
prod-env 차트는 또한 운영 환경에서 중요한 컨테이너 이미지 태깅에 모범 사례를 준수하는 방법으로 특정 이미지 태그를 Pod에 사용하고 있습니다 (커밋 태그, 혹은 v1, v2, v3, ...). 또한 운영 환경에 맞는 Outer Architecture 요소들의 정보를 가지는 Spring Profile 정보도 정의하고 있습니다 (마이크로서비스 새시 (Chassis)).
product-composite:
image:
tag: v1
env:
SPRING_PROFILES_ACTIVE: "docker,streaming_partitioned,kafka,prod"
KAFKA_BOOTSTRAP_BROKERS: ${KAFKA_BOOTSTRAP_BROKERS}
resources:
requests:
memory: 400Mi
limits:
memory: 1024Mi다양한 유형의 차트가 포함하는 내용에 대한 이러한 소개를 바탕으로, 이제 Helm 명령어를 함께 사용하여 Kubernetes에서 마이크로서비스를 배포해봅시다 (혹은 위에서 이미 배포하였고 여기에서 자세히 설명하였습니다)!
5. Makefile 둘러보기
Makefile 둘러보기
우리가 이미 마이크로서비스를 배포하는데 사용한 Makefile은 Docker와 Helm 명령을 통합적으로 관리하고 있습니다.
파일을 열어 Makefile 내용을 살펴봅니다.
Last updated

