project/resistance

Kubernetes 배포를 위한 클라우드 환경 구축

downfa11 2024. 11. 27. 11:52

 

resistance 게임 프로젝트의 MSA 서버도 k8s 환경에서 배포하고자 한다.

내가 개발한 서비스를 실제 운영 환경에서 직접 관리하고 유지보수할 능력은 있어야 한다고 생각했기에, kubernetes에 대해 공부해봤다.

정확한 기간은 가격 정책 모니터링을 해야 알겠지만 목표는 일단 일주일동안 풀가동해서 모니터링하고 분석해보기

​Terraform을 이용한 EKS 리소스 설치

IaC(Infrastructure as Code)는 말 그대로 개발자들에게 친숙한 코드로 Infrastructure를 다루겠다는 소리다.

수동적인 프로세스로 관리하던 시스템과 인프라를 소프트웨어 개발과 유사한 방식으로 정의해서 쉽게 관리할 수 있다.

이 IaC의 도구로, Terraform이 가장 널리 사용되고 있고 Terraform 기반으로 k8s cluster를 구성할 거다.

Terraform 기반의 클러스터 구성의 장점 :

  • 커스터마이징 자유도가 높다
  • 다른 인프라도 같이 구성 가능하다

Terraform 기반의 클러스터 구성의 단점 :

  • 복잡해서 러닝 커브가 크다
  • 직접 구성해야해서 정책에 맞게 구성하는거부터 난관임

Terraform 설치용 EC2 구성

aws의 Cloud9 환경에서 진행할거다. aws-cli도 기본적으로 설치되어 있어서 편함

사용하지 않으면 종료되므로 비용적 부담이 덜하다.

Cloud9 Volume이 작으면 다음과 같이 키울 수 있다.

 

https://docs.aws.amazon.com/ko_kr/cloud9/latest/user-guide/move-environment.html

 

Amazon EBS 볼륨에서 이동 AWS Cloud9 IDE - AWS Cloud9

현재 환경을 표시하는 웹 브라우저 탭을 모두 닫지 않으면 이 절차를 완료하는 데 방해가 될 AWS Cloud9 수 있습니다. 특히 이 절차 중에 잘못된 시간에 를 AWS Cloud9 시도하여 환경과 연결된 Amazon EC2

docs.aws.amazon.com

 

 

Terraform 설치

Cloud9의 os 종류에 맞춰서 선택해서 Terraform을 설치해준다

 

https://developer.hashicorp.com/terraform/install

 

Install | Terraform | HashiCorp Developer

Explore Terraform product documentation, tutorials, and examples.

developer.hashicorp.com

 

Cloud9 - Preferences - AWS Setting - Credentials - AWS managed temporary credentials 거부하자

cd ~/.aws → ls 했는데 credentials 파일이 있으면 rm credentials로 지워줘야한다.

데이터베이스는 VPC 내에서 Private Subnet을 만들고, RDS를 Provisioning 할 계획이다.

tf 코드로 작성해서 리소스를 생성하는게 매력적인 이유

여기서 k8s와 다른 VPC에 DB를 둬야한다면 VPC Peering을 염두해야함

k8s 안에서 MySQL을 구동하는 MySQL Operator에 대해서도 따로 작업해볼 생각이다.

 

 

IAM Policy 및 Role 생성

Cloud9에서 EC2에 대한 IAM을 추가해야한다.

EC2 콘솔 화면에서 Action → SECURITY → Modify IAM role 로 연결

aws sts get-caller-identity: Cloud9 환경에서 IAM이 잘 적용됐는지 확인해보자.

 

Terraform을 이용해서 k8s cluster 생성하기

AWS EKS는 노드들이 텅텅 비어있어서 편하게 클러스터 노드를 생성하기 위해서 eksctl를 제공해준다.

  • Weaveworks에서 개발한 오픈 소스 도구
  • AWS 및 EKS의 모범 사례를 따르고 있는 명령줄 도구
  • custom한 구성은 어렵다

Terraform 기반 클러스터는 앞서 설명했듯이 커스터마이징 자유도가 높아서, 오픈소스처럼 모듈을 쓸 수 있다. EKS module을 쓸거여

간단하게 Cloud9 환경에 대해 소개하자면, 프로젝트의 경로는 최상위 디렉토리의 environment이다.

  • terraform init : terraform 실행을 위한 여러가지 설정이나 플러그인을 다운받는다.
  • terraform plan : 어떤 자원을 실행할건지 볼 수 있다.
  • terraform apply : yes 입력하면 자원(test-vpc와 EC2 instance들)이 생성된다
  • terraform destroy : yes 입력하면 생성했던 자원을 삭제한다.

끄기 전에 죄다 종료해두자

어떤 기능을 수정하느냐에 따라 기존 리소스를 교체하거나 삭제 후 생성하니, 변경사항을 저장할때 조심해야한다.

 

 

teffraform plan할때 terraform.tfstate 파일이 생성되는데, 궁금해서 찾아봤었다.

Terraform에서 만드는 리소스들에 대한 명세와 상태가 저장되는 파일로, plan 명령어시 해당 디렉토리에 자동으로 생성된다.

운영 환경이면 로컬이 두지 말고 안전한 장소에 두는걸 권장하고, 여러 사람이 terraform 코드를 수정하는 협업시 lock과 관련된 코드를 설정해줘야 한다고 함 공식문서 보쇼

Kubernetes에 접속하기 위한 kubectl 도구 설치

https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/

 

Install and Set Up kubectl on Linux

Before you begin You must use a kubectl version that is within one minor version difference of your cluster. For example, a v1.31 client can communicate with v1.30, v1.31, and v1.32 control planes. Using the latest compatible version of kubectl helps avoid

kubernetes.io

 

k8s에 접속하기 위한 config 설정을 마치면, 아래의 명령어로 잘 접속되는지 확인할 수 있다.

kubectl get node : 생성된 노드를 확인

kubectl get po -A : 파드 어떤거 설치됐는지 k8s의 상태 확인

잘된 모습

다음과 같이 도커 파일을 수정해줄거다.

FROM gradle:jdk17-alpine as builder
WORKDIR /workspace/app
COPY . /workspace/app/
RUN ./gradlew build -p ${MODULE}
FROM openjdk:17-ea-17-slim

RUN groupadd -r appuser && useradd -r -g appuser appuser

COPY --from=builder /workspace/app/${MODULE}/build/libs/${MODULE}.jar ./${MODULE}.jar
EXPOSE 8080

USER appuser

ENTRYPOINT ["java","-jar","${MODULE}.jar"]

DockerHub에서 본인의 jdk 버전에 맞는 녀석을 찾아서 넣어주면 된다.

최소한의 보안적 작업으로, appuser 사용자를 만들어서 root 사용자로 작업하지 않도록 막았다.

kubernetes 리소스의 시각화를 위한 k9s 도구 설치

아래의 공식 문서에서 설치를 도와준다.

 

https://k9scli.io/topics/install/

 

Install

Installation Overview K9s is available on Linux, macOS and Windows platforms. Binaries for Linux, Windows and Mac are available as tarballs in the release page. MacOS # Via Homebrew brew install derailed/k9s/k9s # Via MacPort sudo port install k9s Linux #

k9scli.io

 

 

마찬가지로 해당하는 녀석으로 골라 설치하기 바람

 

gz 압축파일 풀어주고 sudo cp k9s /usr/local/bin로 k9s 명령어를 어디서든지 가능하게 설정해두면 편하다

: 콜론으로 명령어를 입력할 수 있는 k9s 인터페이스가 작동한다

일단 종속성이 없는 microservice 하나만 배포해서 환경을 구축한 뒤에나 하게 되겠지만, DB와 Caching, Kafka같은 컴포넌트들을 k8s 배포 환경에서 어떻게 다룰지에 대한 고민중이다.

k8s 환경 안에서 mysql를 돌리는 MySQL Operator라는 도구가 있어서 그걸 시도해보고, 안되면 RDS, elasticaching, MSK로 때려박아서 지갑 탈탈 털어볼 생각이다.

결국 얘기한대로 VPC안에 프로비져닝할거같긴 한데, 요금 절감을 위한 시도도 다뤄보고 싶은 욕심

짠. CI/CD 구축과 AWS의 ECR 연동, Helm Chart 구성할거다.

 

커밋하면 ECR에 이미지를 등록하고 Helm Chart를 업데이트하는 구조로 CD를 구현함

Github Action과 Cloud9 연동을 위한 SSH키 생성

ssh-keygen -t ed25519 -C "comment" 명령어를 통해 Cloud9 환경에서 SSH 키를 만든다.

  • -t ed25519 : 생성할 SSH 키의 유형을 지정

 

ed25519은 RSA보다 더 강력하고 효율적인 암호화 알고리즘 중 하나

  • -C "comment" : 주석(comment)은 키를 식별하거나 설명하는 데 사용

cat ~/.ss/id_ed25519.pub : 생성된 Key의 공개키를 확인한다. 이 공개키로 Github Setting에서 새로운 SSH Key를 생성해준다.

 

그리고 CI 구축을 위한 과정인데, 커밋의 오류가 없다면 AWS의 ECR에 이미지를 저장할거다

Github Action에서 aws ECR에 접속할려면 또 IAM 권한이 필요하다.

보안 자격 증명에서 액세스 키를 생성해야한다.

Actions Sevrets and variables에서 Repository Secrets로 등록하고 Github Action WorkFlow를 작성해줬다.

과정 :

  1. git checkout
  2. Docker Image 생성
  3. ECR(Elastic Countainer Registry) push

 

Private Elastic Container의 이미지를 보안적 요소 없이 가져올 수 있는 이유?

 

해당 작업을 하는 EC2 인스턴스의 IAM을 보니, AmazonEC2ContainerRegistryReadOnly 정책이 허용되어 있다.

Terraform할때 포함된 내용인데, eks에서 별도의 Docker 로그인 과정 없이 가져올 수 있었다.

Helm 설치

Helm은 k8s의 패키지 매니저로 생각하면 된다.

주요 특징

  • 복잡성 관리 : Kustomize의 Overlay가 아니라 yaml Manifest를 템플릿화하고 값 파일을 통해 구성 관리
  • 쉬운 업데이트 : 새로운 버전의 차트나 현재 차트의 값 파일 업데이트 쉬움(chart = app의 명세)
  • 간편한 공유 : 공개 Helm Repo가 있어서 공유가 쉬움
  • 롤백

HELM 실행을 위해선 Helm Chart와 values.yaml이 필요하다.

 

chart에는 k8s 리소스 자원들이 들어있어서 k8s cluster 내에 별도 패키지 설치할 필요 없음

앱 구동에 필요한 설정 파일인 values.yam

​​

https://helm.sh/ko/docs/helm/helm_install/

 

Helm Install

헬름 - 쿠버네티스 패키지 매니저

helm.sh

 

 

sudo cp linux-amd64/helm /usr/local/bin 로 편하게 넣어두자.

helm version로 설치를 확인할 수 있음

helm repo add eks https://aws.github.io/eks-charts

 “eks” has been added to your repositories

aws-load-balancer 설치

Ingress 리소스에 대한 로드밸런서로 AWS의 ALB를 사용할 거다.

alb Ingress Controller로 ALB 리소스를 제어하는 방식인데, ingress 생성 요청을 계속 관찰하면서 ALB와 TargetGroup을 만다는 구조

aws-load-balancer-controller를 설치하지 않으면 Ingress 리소스의 Address가 나오지 않음

 

 

https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.7/deploy/installation/#detailed-instructions

 

Installation Guide - AWS Load Balancer Controller

If you want to run the controller on Fargate, use the Helm chart, since it doesn't depend on the cert-manager. Detailed instructions Follow the instructions in the aws-load-balancer-controller Helm chart. Summary Add the EKS chart repo to Helm helm repo ad

kubernetes-sigs.github.io

 

 

Helm install command for clusters with IRSA

helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=<cluster-name> --set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller

 

 

 

IAM Role인 load-balancer-controller의 고유 식별 번호인 ARN을 다음 명령어에 넣어야함.

helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=<cluster name> --set serviceAccount.create=true --set serviceAccount.name=aws-load-balancer-controller --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=<ARN>
  • serviceAccount.create=true : 따로 생성안해서 이번에 생성해야함
  • serviceAccount.name=aws-load-balancer-controller (IAM Role)
  • serviceAccount.annotations … : IAM 정보(Role)와 연결

 

terrafrom 코드의 아래에 해당 내용을 추가하자

module "load_balancer_controller_irsa_role" {
  source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"

  role_name                              = "load-balancer-controller"
  attach_load_balancer_controller_policy = true

  oidc_providers = {
    ex = {
      provider_arn               = module.eks.oidc_provider_arn
      namespace_service_accounts = ["kube-system:aws-load-balancer-controller"]
    }
  }

  tags = local.tags
}

 

다시 init하고 apply하는 과정을 거치면 손쉽게 ELB controller를 설치할 수 있다.

CD를 위해서 workflow를 수정해서 Helm Chart도 변경해야한다.

Helm chart는 helm create 명령어로 생성할 수 있으며, metedata를 포함하는 chart.yaml이 포함된 폴더가 생성된다.

template 디렉토리에 들어가면 기본적인 리소스가 들어있음

 

Cloud9 환경에서 key를 보관한다. 이전과 동일하게 ED25519로 만들면 됨

Helm chart를 관리하는 레포지토리를 private으로 만들고, Setting - Deploy Key에 공개키(*.pub)를 넣는다.

본 프로젝트의 레포지토리의 Security - Secrets and variables - Actions의 Secret Key를 만들어서 개인키를 넣는다.

 

이때 개인키의 형식 맞춰서 그대로 전부 붙여넣기 해야한다

-----BEGIN OPENSSH PRIVATE KEY-----

...

-----END OPENSSH PRIVATE KEY-----

 

 

Argo 도구까지 설치해서 Ingress의 Address가 잘 나오는 것을 확인했다.

로드밸런서의 프로비져닝이 끝나면 해당 주소로 ArgoCD에 접속할 수 있다

초기 아이디는 admin, 패스워드는 k9s의 secrets 리소스에 있는 argocd-initial-admin-secret에 있다.

(secret 리소스는 엔터가 아니라 x키를 눌러서 확인할 수 있다)

모든 설정은 끝났고, 다음 시간에는 Canary 방식의 무중단 배포로 찾아오겠다

 

뭘 까먹었나 했는데 정작 Helm Chart 코드 작성에 대한 내용을 넣지 않았었다,,,,

CI 과정에서 생긴 Github Action '/workspace/app/{MODULE}' does not exist. 오류

 

어디서부터 잘못된걸까..

환경 변수 module로 따로 jar 파일의 이름을 떼서 저장해봤는데 갑자기 안된다.

 

#15 [builder 5/6] RUN ls -la /workspace/app
#15 0.218 total 732
#15 0.218 drwxr-xr-x    1 root     root          4096 May 27 16:13 .
#15 0.218 drwxr-xr-x    1 root     root          4096 May 27 16:14 ..
#15 0.219 drwxr-xr-x    3 root     root          4096 May 27 16:13 membership-service
#15 0.219 -rw-r--r--    1 root     root         92596 May 27 16:13 resistance_architecture.png
#15 0.219 -rw-r--r--    1 root     root           207 May 27 16:13 settings.gradle
#15 0.219 -rw-r--r--    1 root     root        147128 May 27 16:13 스크린샷 2024-03-16 013118.png
#15 DONE 0.2s
#16 [builder 6/6] RUN ./gradlew build -p membership_service
#16 0.270 Downloading https://services.gradle.org/distributions/gradle-8.6-bin.zip
#16 0.787 ............10%.............20%............30%.............40%.............50%............60%.............70%.............80%............90%.............100%
#16 3.025 
#16 3.026 Welcome to Gradle 8.6!
#16 3.026 
#16 3.026 Here are the highlights of this release:
#16 3.026  - Configurable encryption key for configuration cache
#16 3.026  - Build init improvements
#16 3.026  - Build authoring improvements
#16 3.027 
#16 3.027 For more details see https://docs.gradle.org/8.6/release-notes.html
#16 3.027 
#16 3.125 Starting a Gradle Daemon (subsequent builds will be faster)
#16 4.219 
#16 4.220 FAILURE: Build failed with an exception.
#16 4.220 
#16 4.220 * What went wrong:
#16 4.220 The specified project directory '/workspace/app/membership_service' does not exist.

 

삽질하면서 Dockerfile에 ls 명령어를 찍어서 다음과 같이 디버깅해봤다. 분명 해당 서비스가 디렉토리 존재하는데 왜 없대?

화가난다. membership-service인데 membership_service로 한거였다.

 

오타때문에 하루종일 삽질한거였다!!!