Infra, DevOps/Kubernetes, Docker

[k8s] 쿠버로 백엔드 환경 구축하기

philo0407 2024. 2. 19. 16:59

 

 

이 글을 작성한 시점의 저는 아직 k8s에 대해 깊게 알지 못합니다,
Backend 부분에 비해 틀린점이 있을 수 있습니다

또, 위 그림은 실제 진행한 설계의 일부분입니다.

특히 StatefulSet과 DB의 관계가 적절한지 아직 확실히 검증되지 않았습니다
(구상하다보니 애틋해져서 넣었습니다)

 

이 글을 보고 클론 코딩을 하며 배우겠다는 느낌보다는, (아마 그렇게 할 수 없을거에요...)
이런 기능이 있고 어떤 식으로 구성되어있구나란 것을 개략적으로 같이 느끼셨으면 좋겠습니다 :)

 

k8s는 과제를 준비하면서 급하게 공부하게 되었는데,
아주 좋은 기술이라 느껴졌다 (구글이 그 동안의 노하우를 담아서 만들었다는게 잘 느껴졌다)

 

쿠버를 사용하면 인프라 구성도를 코드로 쉽게 관리할 수 있다는 장점이 있다

또 코드(yaml)로 되어있어 재사용 가능하고 노하우를 쌓으면서 발전시킬 수 있다

 

Just 1 WAS

가장 간단한 경우부터 시작을 해 보면

 

Namespace는 작업을 할 수 있는 공간이다

 

이 공간에 Pod란 것을 배치할 수 있는데, 쿠버에서 관리하는 최소 단위이다

마치 도커의 컨테이너와 같다

컨테이너 내부에 여러 앱을 넣을 수 있듯이,

쿠버 또한 Pod내에 여러 컨테이너를 넣을 수 있다

 

도커와 마찬가지로 이 Pod는 단독으로 외부와 통신할 수 없는데,

쿠버에서는 서비스라는 객체로 외부와 통신을 중계한다 (우선은  포트포워딩이라고 봐도 된다)

 

이 서비스의 타입을 NodePort타입으로 만들어야 하는데... 이 부분은 넘어가도록 하자

(궁금하시다면)

더보기

서비스에는 크게 ClientIp, NodePort, LoadBalancer가 있는데


기본 값은 ClientIp로, 쿠버 클러스터 내부에서만 사용한다

 

NodePort는 클러스터 외부에서 접근할 수 있고 주로 내부망에서 개발 환경등에 사용한다
여기서의 Node의 뜻은 클러스터 노드의 노드이다

즉 노드의 포트

 

LoadBalancer는 운영환경에서 정식으로 배포를 할 때 사용하는 타입이다
쿠버를 배포한 환경인 클라우드 공급사에서 지원이 되어야 한다

 

아래의 yaml 명세를 통해서 배포를 하면되는데,

 

네임스페이스

apiVersion: v1
kind: Namespace
metadata:
 name: ns-sample

 

파드

apiVersion: v1
kind: Pod
metadata:
  name: pod-was
  namespace: ns-sample
  labels:
    pod-type: was
spec:
  containers:
  - name: container
    image: progress0407/app
    ports:
    - containerPort: 8000

 

서비스

apiVersion: v1
kind: Service
metadata:
  name: svc-nodeport
  namespace: ns-sample
spec:
  type: NodePort
  ports:
    - port: 8080
      targetPort: 8080
      nodePort: 30000
  selector:
    pod-type: was

 

배포 방법은

 

kubectl apply -f {파일 이름}
을 적용하면 할 수있다

 

혹은 미니쿠버를 사용한다면 아래와 같다

minikube kubectl apply -f {파일 이름}

 

그리고 

확인은 아래처럼 각각의 오브젝트를 조회하거나
(오브젝트는 pod, service, namespace와 같은 것이다)

kubectl get pod

 

아니면 대시보드에서 볼 수 있다 (대시보드 접근 법은 생략한다... ㅠ)

 

1 WAS 1 DB

 

이제 DB를 추가해보자

apiVersion: v1
kind: Pod
metadata:
  name: pod-db
  namespace: ns-sample
  spec:
    selector:
        matchLabels:
          pod-type: db
    containers:
        - name: container
          image: mysql:8.0.3
          ports:
          - containerPort: 3306

 

단, 여기서 우리는 추가만 하지 말고 Headless Service란 걸 넣도록 하자

 

어렵게 보일 수 있는데, 이해하기 쉽게 설명해보면...

쿠버네티스에서 파드(Pod)는 본디 어느때든 문제가 생길 수 있다는 것을 가정하고 설계되었다

문제는 문제가 생긴 이 파드가 재기동되면 IP가 변경된다

만일 그 파드가 DB라면, 이 DB를 참조하는 WAS는 장애가 난다

 

이를 해결하는 방법 중 하나가 Headless Service이다

IP가 아니라 파드를 label(라벨)로 간접적으로 식별하기 때문에 DNS처럼 타겟 파드에 라벨로 접근할 수 있다

즉, 느슨히 결합할 수 있게 도와준다

 

헤드리스 서비스

apiVersion: v1
kind: Service
metadata:
  name: svc-db-router
  namespace: ns-sample
spec:
  clusterIP: None
  selector:
    pod-type: db
  ports:
  - protocol: TCP
    port: 3306
    targetPort: 3306

 

위 속성에서 selector.pod-type과 DB 파드의 matchesLabels의 라벨이 매칭이 되면,

해당 서비스는 파드의 위치를 중계한다

 

이때 WAS는 이 헤드리스 서비스를 바라보게 하면 된다

apiVersion: v1
kind: Pod
metadata:
  name: pod-was
  namespace: ns-sample
  spec:
    selector:
      matchLabels:
        pod-type: was
    containers:
      - name: container
        image: progress0407/app
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_DATASOURCE_URL
          value: 'jdbc:mysql://svc-db-router:3306/sample_app?useSSL=false&allowPublicKeyRetrieval=true'
        - name: SPRING_DATASOURCE_USERNAME
          value: root
        - name: SPRING_DATASOURCE_PASSWORD
          value: 1234

 

이때 `SPRING_DATASOURCE_URL`값에 `svc-db-router`가 헤드리스 서비스를 가리키면 된다!

그리고 해당 부분은 스프링의 yaml처럼 config과 secret 으로 뺄 수 있다 (생략)

 

또한 DB의 경우 영구 저장이 되어야 하는데,

이 부분 또한 도커의 볼륨처럼 마운트 설정을 할 수 있다

 

다만 쿠버의 경우 이 볼륨이 PVC와 PV 두 부분으로 나뉜다

PV가 실제로 물리적인 볼륨을 설정하는 부분이고

PVC는 PV를 가리키는데, 인터페이스와 구현체의 관계라고 보면 된다 (이런 예시를 본 적은 없는데 현재로써는 이렇게 해석해도 오해의 소지가 없는 걸로 보인다)

 

 

2 WAS 1 DB, Rolling Deploy + Auto Scailing

 

WAS의 경우 배포를 할 때

쿠버의 롤링 배포로 쉽게 무중단 배포를 구현할 수 있다

 

아래에서 롤링 배포을 참고하면 된다!

 

[k8s] 쿠버로 무중단 배포 3종 모두 해보자 (argo없는 Blue Green)

이 글은 아주 짧습니다 :) 실습 스크립트 및 코드는 이곳에 있습니다. 현재 블루 그린의 경우 무중단 배포가 되지 않습니다! 편의상 쿠버네티스를 k8s 혹은 쿠버라고 표시하겠습니다 :) 눈물 젖은

progress0407.tistory.com

 

다음은 HPA를 추가해보자

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: hpa-was
  namespace: ns-sample
spec:
  maxReplicas: 6
  minReplicas: 2
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: deploy-was
    namespace: ns-sample
  metrics:
  - type: Resource 
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50

 

위와 같이 설정하면 처음에는 2개의 pod로 시작해서 부하 정도에 따라 따라 6개까지 늘어난다

위에서 설정한 부하의 기준은 평균CPU 사용량이다 (보통의 경우 해당 설정으로 한다고 한다)

 

현재 pod들의 평균 CPU가 50%를 기준으로 동작한다

늘어나는 pod의 갯수는 현재 pod 갯수  * (평균 CPU / HPA의 기준 CPU ) 이다

 

예를들어

2개의 pod의 평균 CPU가 75%일 경우 3개로 늘어난다 // 2개 * (75% / 50%)

 

만일 Deployment의 replicas 갯수가 HPA의 min 갯수와 다를 경우 HPA의 설정이 덮어쓴다고 한다

 

작성 후기

사실 이 지식은 이미 1월 중순에 습득을 했고 2월 5일에 거의 작성을 다했었다

 

 

근데 일들이 겹쳐서 끝끝내 퍼블리시는 현재한다 ㅠ
PVC, PV 설명은 다소 아쉬운 점이 많은 것 같다...

이 글은 이쯤에서 마무리한다 ㅎㅎ,,