[파이널 프로젝트] PodScheduling & EKS에서 젠킨스 사용

신현식·2023년 4월 3일
0

구름_Kubernetes

목록 보기
19/25
post-thumbnail

PodScheduling

두개의 인스턴스(=노드)가 있고 각 인스턴스에는 서비스를 구현하기 위한 리소스들이 똑같이 배포되어 있다. 즉, 이중화 구현을 한것이고, 만약 인스턴스가 포함된 AZ OR 서브넷에 장애가 발생해도 로드밸런싱을 통해 다른 AZ OR 서브넷에 있는 인스턴스에 트래픽이 전달되기 때문에 클라이언트는 다운타임없이 서비스를 이용할 수 있다.

  • 만약 서비스를 구현하기위한 리소스들을 파드로 배포하는데 어떤 기능을 담당하는 파드가 각 인스턴스에 replica로 복제되어 동일하게 배치되어야하는데 한쪽 인스턴스에 몰려서 배치되게 된다면 다른 쪽 인스턴스는 해당 파드가 담당하는 기능을 갖지 못하게 된다.

쉽게말해, 백엔드 파드와 프론트 파드가 있고, 두개의 인스턴스에 각각 한 세트로 배치되어서 각 인스턴스가 동일한 서비스를 가능하게 해야한다. 하지만 백엔드 파드 두개가 모두 하나의 인스턴스에 스케줄링된다면, 다른쪽 인스턴스는 서비스를 배포할 수 없다.

백엔드 어피니티 설정

  • backend파드에 PodAntiAffinity 설정으로 label이 tier: backend인 파드를 서로 배척하도록 스케줄링하였다.
  • baceknd파드가 아무리 늘어나도 파드들의 레이블은 모두 동일하기 때문에 backend파드끼리 같은 노드에 배치되는 일은 존재하지 않는다.
vi back-deployment.yaml

spec:
. . . 
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: tier
            operator: In
            values:
            - backend
        topologyKey: "kubernetes.io/hostname"

프론트 어피니티 설정

  • frontend파드에 마찬가지로 PodAntiAffinity 설정으로 label이 tier: frontend인 파드를 배척해서 스케줄링(= 배치) 되게 하였다.
  • frontend파드가 아무리 늘어나도 파드들의 레이블은 동일하므로 frontend파드끼리 같은 노드에 배치되는 일은 존재하지 않는다.
  • PodAffinity 설정으로 label이 tier: backend인 파드에 대해서 해당 파드와 묶여서 동일한 노드에 배치되도록 하였다. 즉, 서비스를 구성하는 backend파드와 frontend파드가 한쌍이 되어서 각 노드(= EC2 인스턴스) 에 배치된다. 단, 동일한 기능을 하는 파드가 묶여서 배치되는 일은 없다.
vi front-deployment.yaml

spec:
. . .
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: tier
            operator: In
            values:
            - frontend
        topologyKey: "kubernetes.io/hostname"
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: tier
            operator: In
            values:
            - backend
        topologyKey: "kubernetes.io/hostname"

Deploy Jenkins on EKS with Helm

📢 젠킨스 공식 사이트: 헬름으로 설치하는 방법

# 네임스페이스 생성 및 레포 생성
kubectl create namespace jenkins
helm repo add jenkinsci https://charts.jenkins.io
helm repo update


# 작업할 디렉터리 생성
mkdir jenkins_with_eks
cd jenkins_with_eks

# 파일 작성, 계정 및 플러그인 자동 구성
vi jenkins-values.yaml

controller:
  tag: "lts-jdk11"
  serviceType: LoadBalancer
  installPlugins:   
  - kubernetes
  - workflow-aggregator
  - git
  - configuration-as-code
  - pipeline-stage-view
  adminPassword: "패스워드 입력"
persistence:
  storageClass: "efs-sc"  # EFS 스토리지 사용


# 설치 진행
helm install jenkins jenkinsci/jenkins -n jenkins -f jenkins-values.yaml

# 젠킨스 배포 확인
kubectl get all -n jenkins
  • yaml파일에 스토리지클래스 항목에 gp2 스토리지클래스를 지정했다.

  • 클러스터 배포과정에서 OIDC를 이용해서 EBS CSI Driver를 사용할 수 있는 EBS CSI Controller를 가진 서비스 계정을 생성했었다. 따라서 스토리지 클래스에 EBS타입인 gp2를 사용해도 EBS를 사용할 수 있는 권한이 있는 서비스 계정이 있기 때문에 문제없다고 생각했다.

  • 하지만 eks 설치과정에서 문제가 있었는지 확인해보니 EBS-CSI-Controller가 존재하지 않았다. 따라서 gp2를 사용하지 않고 그냥 현재 이용중인 EFS스토리지를 이용해도 되겠다고 생각하여 이전에 생성한 EFS스토리지 클래스를 지정했다.

젠킨스 에러 발생

Jenkins를 배포하고나서 하루가 지난뒤 갑자기 접속이 안되는 현상이 발생했다. Jenkins를 띄우는 파드의 로그를 확인해보니 볼륨 마운트에 문제가 발생했다.
내 생각으로는 원래 gp2볼륨을 스토리지로 사용했는데 ebs-csi-driver가 설치되있지 않아서 그냥 EFS를 스토리지로 사용해서 발생한 문제 같았다. 왜냐하면 로그에서 Mount지점이 공유되있어서 문제가 발생했다는 것을 확인했기 때문이다.
따라서 Jenkins의 스토리지를 다시 gp2볼륨으로 설정할 것이다.

vi jenkins-values.yaml

. . . 
persistence:
  storageClass: "gp2" # EFS -> gp2로 수정 
. . .

gp2스토리지 클래스를 사용하려면 ebs-csi-driver가 있어야 하는데 현재 없기 때문에 애드온으로 추가하여야 한다.

  • 애드온으로 ebs-csi-driver를 설치하려면 ebs-csi-controller가 필요하기 때문에 ebs-csi-controller를 사용할 수 있는 IAM정책이 부여된 IAM-role을 가진 서비스 계정이 있어야 한다.
    이러한 서비스 계정은 클러스터 배포과정에서 OIDC를 이용해 IAM-role을 붙여 생성했었다.
#서비스계정의 role-arn확인
eksctl get iamserviceaccount --cluster finalproject --name ebs-csi-controller-sa

#확인한 role-arn을 사용, 자신의 클러스터 이름 사용하여 애드온 생성
eksctl create addon --name aws-ebs-csi-driver --cluster finalproject --service-account-role-arn < role-arn > --force

#생성한 애드온 확인 
eksctl get addon --cluster finalproject

배포과정에서 OIDC를 통해 생성된 IAM서비스 계정인 ebs-csi-controller-sa에는 IAM 롤이 붙어있고 IAM롤에는 IAM정책을 통해 ebs-csi-controller에 대한 권한을 가지고 있다. 따라서 해당 IAM서비스 계정은 ebs-csi-controller를 통해 ebs-csi-driver를 설치할 수 있다.

  • 서비스계정에 붙어있는 IAM 롤의 arn을 확인한다. 이후 확인한 arn을 이용하여 ebs-csi-controller가 ebs-csi-driver라는 애드온을 설치한다.
helm install jenkins jenkinsci/jenkins -n jenkins -f jenkins-values.yaml

위의 과정으로 ebs-csi-driver가 설치되었다면, 기존에 생성되어있던 gp2스토리지 클래스를 다시 이용할 수 있게 되기 때문에 jenkins-values.yaml파일을 helm으로 다시 설치해보면 jenkins가 정상적으로 배포되는 것을 확인가능하다.

AWS콘솔에 가보면 기존에 EFS에서 Jenkins에 마운트되어있는 볼륨의 엑세스포인트가 하나 사라지고, gp2볼륨이 새로 생성되어있는 것을 확인

gp2에서 gp3로 교체

gp3의 최고 성능은 gp2 볼륨의 최대 처리량보다 4배 빠르지만 gp3 볼륨은 범용 SSD(gp2) 볼륨보다 GiB당 20% 저렴하다. gp3 볼륨은 한 자릿수 밀리초의 대기 시간과 99.8% ~ 99.9%의 볼륨 내구성을 0.2% 이하의 연간 고장률(AFR)로 제공한다.
gp3가 gp2보다 효율도 좋고 가격도 저렴하기때문에 Jenkins에 마운트한 gp2볼륨을 gp3볼륨으로 교체하려 한다.

1 gp3 스토리지 클래스 생성

기존의 ebs-csi-driver로 설치한 스토리지 클래스는 gp2이기때문에 ebs-csi-driver를 이용하여 새로 gp3스토리지 클래스를 설치한다.

vi gp3.yaml

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp3
provisioner: ebs.csi.aws.com # ebs-csi-driver이용
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
parameters:
  csi.storage.k8s.io/fstype: ext4
  type: gp3   # gp3로 스토리지 클래스 생성 
  
kubectl create -f gp3.yaml
  • 이전에 gp2스토리지 클래스를 생성하는 과정에서 설치된 ebs-csi-driver를 이용해서 gp3스토리지 클래스 또한 생성한다.

2 Jenkins-values.yaml 파일 수정

vi jenkins-values.yaml

. . .
persistence:
  storageClass: "gp3"   # gp2 -> gp3로 교체 
. . .

# helm으로 Jenkins 설치 
helm install jenkins jenkinsci/jenkins -n jenkins -f jenkins-values.yaml 
  • AWS콘솔에 새로운 로드밸런서가 추가되었고 해당 로드밸런서의 DNS Name:8080을 브라우저에 입력하면 Jenkins에 접속할 수 있다.

  • AWS 콘솔에서 Jenkins용 파드에 마운트된 gp3볼륨이 생성되어있는 것을 확인

젠킨스 추가설정

Jenkins에 접속하여 Jenkins관리에 들어가보니 아래와 같은 에러 메세지가 뜨는 것을 확인했다.

  • 이것을 해결하기 위해 아래와같이 Jenkins 시스템 설정에 들어가 Jenkins Location부분에서 이 브라우저에 접속할때 입력한 URL을 등록하니 해결되었다.

역방향 프록시란

쿠버네티스에서 사용하는 서비스 리소스 (Cluster IP 타입, NodePort타입 , LoadBalancer 타입)가 프록시 역할을 하는 것을 이미 알고 있다.
즉, 아래의 설명처럼 외부에서 클라이언트가 서버에 요청(ex.파드)을 보낼때 다이렉트로 서버에 도달하는 것이 아니라 중간에 프록시 역할을 하는 서비스가 존재하는 것이다.
따라서 우리가 지금까지 쿠버네티스 리소스들을 외부에 노출시키기 위해서 사용했던 서비스들이 프록시 역할을 하기때문에 리버스 프록시의 구조를 사용하고 있었던 것이다.

예를들어 우리가 생성한 파드를 외부에 노출시키기 위해 로드밸런서 타입의 서비스 리소스를 사용했다고 하자.

  • 클라이언트는 외부용 IP를 통해 요청을 보낸다. 이 요청에 대해서 바로 파드에 접근하는 것이 아니라, 포트포워딩을 통해 노드에 뚫린 NodePort에 들어온다. 이때 ClusterIP가 내부에서 서비스를 찾아준다. 바로 이 서비스가 프록시 역할을 하는 서비스이고, 자신과 느슨하게 연결된 파드들의 IP정보를 저장하고 있는 엔드포인트 정보를 보고 포트포워딩과 로드밸런싱을 통해 파드에 연결한다.
    = 이러한 구조가 리버스 프록시 구조이다.

  • 또한 프록시 역할을 하는 서비스가 존재하는 리버스 프록시 구조이기 때문에 마지막에 파드에 대해 로드밸런싱이 가능한 것이다.

  • 이러한 리버스 프록시 구조는 클라이언트가 서버에 다이렉트로 접근할 수 없기 때문에 보안에도 좋다.

Jenkins 선택 이유

본격적인 파이프라이닝 작업에 앞서 왜 Jenkins를 선택했는지 간단하게 설명할 것이다.
대표적인 CI/CD 툴로는 Jenkins와 GitHub Action이 있다. 하지만 요즘 개발자들 사이에서 GitHub Action이 유행이다. 그렇다면 왜 GitHub Action이 유행할까?

GitHub Action

  • Jenkins처럼 서버를 따로 설치할 필요가 없이 클라우드가 있으므로 GitHub과 통합되어 사용할 수 있다.
  • 또한 Jenkins에 비해 CI과정에서 설정할 것이 적기 때문에 훨씬 간편하게 사용할 수 있다. 따라서 설정에 자신이 없어 Jenkins를 사용하기 꺼려지는 사람들에게 좋은 선택이다.
  • Github에서 제공하는 완전 관리형 서비스이므로 실행하기 위한 인프라를 확장하고 운영하기 위한 방법을 알 필요가 없다.
  • Jenkins에 비해 문서가 적다.
  • 규모가 작은 프로젝트에 적합하다.

Jenkins

  • 설정 프로세스가 GitHub Action보다 복잡하다.
  • 호스팅을 하나부터 열까지 모두 관리해야하기 때문에 모든 문서를 관리하기 위한 비용이 든다.
  • 따로 Jenkins용 서버를 배포해야 한다.
  • 전세계에 많은 기업들이 사용하는 만큼 문서도 매우 많다.
  • 다양한 IDE를 지원하고 다양한 커스터마이징이 가능하다.
  • 규모가 큰 프로젝트에 적합하다

Jenkins 선택 이유

위의 차이점을 봤을때 뭔가 한눈에 보면 Jenkins가 더 안좋아 보인다. 또한 우리가 하는 프로젝트는 규모가 작은 프로젝트이기 때문에 GitHub Action이 더 적합하다. 하지만 그럼에도 선택한 이유는 아래와 같다.

  • 쿠버네티스 전문가 양성과정에서 배운 CI/CD툴이 Jenkins이기 때문에 실무와 비슷한 프로젝트에서 배운것을 한번 써보고싶다.

  • GitHub Action은 설정 프로세스가 간단하고, 따로 서버를 배포할 일도 없으며, 인프라에 대한 개념이 없어도 배포를 할 수 있다. 그렇기에 개발자에게도 인기가 많은 것 같다. 하지만 Jenkins는 인프라에 대한 이해도가 있어야 하며, 설정이 복잡한 만큼 커스터마이징도 다양하다. 따라서 Jenkins는 개발자보다는 좀 더 인프라엔지니어, Devops엔지니어등...에게 특화된 툴이라고 생각한다.
    즉 GitHub Action은 개발자 포함 누구나 다룰 수 있는 툴이지만, Jenkins는 인프라 관련 엔지니어에게 특화되어 있기에 나같은 인프라 엔지니어를 희망하는 사람들에게는 Jenkins를 다룰줄 아는 것은 차별점이라고 생각한다.

📢 Jenkins VS Github Action 참조

인프라 구축 작업에서 느낀점

인프라 구축을 하면서 느낀점은 주로 AWS EKS, EC2, RDS 등을 적극 활용하여 클러스터를 배포하고 인프라를 구축하였다. 그러다보니 비용이 만만치않게 청구되는 것을 확인할 수 있었다. 불과 2주만에 200달러 이상이 찍히고, 1년 예상비용이 3만8천달러가 측정되어있었다. 심지어 대규모 프로젝트도 아닌 소규모 프로젝트인데 말이다. 이것이 기업같은 대규모 인프라라면 상상을 초월하는 금액일 것이다. 따라서 비용을 고려하지 않고 단지 고가용성을 위해 인프라를 구축한다면 아무리 장애대응이 신속해도, 효율적인 인프라가 아닐 것이다.

이번 프로젝트에서 비용이 생각보다 많이 든 이유는 서버가 실행되지 않는 시간에도 인스턴스와 클러스터가 실행되고 있었고, 그 외에도 같은 인스턴스인데도 비용이 저렴한 스팟인스턴스와 같은 AWS에서 제공하는 다양한 서비스를 적극 활용하지 못했다. 지금은 처음 AWS를 사용하여 프로젝트를 진행하다보니 AWS의 정석이자 대표적인 서비스들만 이용하여 인프라를 구축하였지만 추후 프로젝트에서는 고가용성과 비용을 모두 고려하여 인프라를 설계해보도록 해야겠다.

profile
전공 소개

0개의 댓글