K8s 환경에서 3-tier 구성하기! (feat. 아코디언 v2)

유형욱·2022년 3월 13일
1

KANS 스터디

목록 보기
9/9
post-thumbnail

여는 글

지난 8주간 진행했던 KANS 스터디의 최종 졸업과제로 "K8s 환경에서 3-tier 구성하기"라는 주제를 선정하였습니다.

해당 주제를 선택한 이유는 최근 참여한 프로젝트에서 비슷한 구조로 설계/도입했던 사례가 있어 공유 및 개선안을 함께 고민하기 위해 선정하였습니다.

필자는 아직까지 다양한 프로젝트 경험은 없지만, 최근 실제 도입된 사례나 요구사항을 보면 아직까지 국내에서는 3-tier 기반의 웹 서비스를 운영하는 고객이 많은 것 같습니다.

  • 정적 페이지와 동적 페이지의 분리 : 그림출처

최근 MSA 기반의 앱 현대화에 대한 이야기가 많이 나오고 있지만, "잘 동작하는 서비스를 굳이 MSA로 이관해야하나?"라는 질문에 대해서는 선뜻 "네" 라고 대답하기는 어려운 것 같습니다.

현실적으로 많은 업무들이 모놀리식(Monolithic)한 애플리케이션 형태를 가지고 있으며, 앱 리팩토링 과정은 생략하고 클라우드, 클라우드 네이티브 환경으로 넘어가는 경우도 많습니다.

📢 참고 : 결코 권장하는 방식은 NO!


마이그레이션 방법에는 다양한 형태가 있겠지만 Lift&Shift(Rehost) 또는 Replatform 방법이 일반적으로 쉽고 간편하기 떄문에 많이 사용되고 있습니다.

물론, 클라우드 네이티브 환경에 적합(fit)하게 이관하기 위해서는 Application Refactor가 필수적이지만, 컨테이너 환경으로 옮기는 것 만으로도 비용이나 효율성 측면에서 굉장히 큰 장점을 가져갈 수 있다고 생각됩니다.


0. 사전환경

쿠버네티스 환경에서 3-tier 형태의 애플리케이션을 배포하기 위한 사전 환경 구성입니다.

1) 환경

  • IaaS : vSphere
  • PaaS : Accordion v2.1.1 (Kubernetes v1.21.7)
  • Cluster : M3 + W2
  • Sample Apps : apache, wildfly

2) 구성도

본 글에서는 Node 2대로 클러스터를 구성하였습니다. 앞단에 Ingress를 통해 유입된된 후 정적 컨텐츠는 apache로 분기하고 동적 컨텐츠는 HA구성된 Wildfly로 전달되도록 구성해보겠습니다.


1. Wildfly HA

wildfly 소개 - 출처

  • Java를 기반으로 하는 오픈소스 미들웨어로써 레드햇에서 상용제품인 Jboss EAP와 혼동을막기위해 Jboss AS8 버전부터 WildFly라는 이름으로 변경
  • 대표적으로 Java EE스펙을 지원하며 40개 이상의 다양한 프로젝트가 있으며 Jboss 커뮤니티에의해 개발 및 운영

KUBE_PING 이란?

kube_ping을 알아보기 전에, jgroups에 대해 먼저 알아보자면, JGroups는 멀티캐스트 프로토콜을 사용하여 신뢰성 높은 통신을 할 수 있도록 구현된 통신 라이브러리 입니다. JGroups에서 사용되는 주요한 프로토콜은 다음과 같습니다.

  • 가입(JOIN)
  • 탈퇴(REMOVE)
  • 장애감지(FD)
  • 장애감지 소켓(FD_OCK)
  • 파티션 결합(MERGE2)
  • 핑(PING)

이 중에서 PING이라고 하는 프로토콜은 클러스터의 멤버를 Discovery 할 때 사용하는 프로토콜입니다.

일반적으로는 멀티캐스트 통신을 통해 MPING을 요청하여 리더(코디네이터)를 찾고 다른 멤버를 Discovery 합니다. 하지만 멀티캐스트를 사용할 수 없는 환경(클라우드, K8s의 특정 CNI 등)에서는 다음과 같은 다양한 방식을 사용할 수 있습니다.

  • TCPPING
  • GOSSIP
  • FILE_PING
  • JDBC_PING
  • S3_PING
  • SWIFT_PING
  • KUBE_PING

KUBE_PING은 이러한 PING 프로토콜 중 하나이며, Kubernetes에서 클러스터 노드 검색을 위한 프로토콜 입니다. 이러한 KUBE_PING 기능을 활용하여 Wildfly의 Session Cluster를 구성해볼 예정입니다.

💡 참고 : 보다 상세한 설명은 jgroups-kubernetes 문서를 참고

KUBE_PING 실습

이제 간단하게 KUBE_PING을 사용하여 wildfly의 HA기능을 테스트해보도록 하겠습니다.

참고 : 샘플소스

샘플 소스를 분석해보면 우선 SA(ServiceAccount), CR(ClusterRole), CRB(ClusterRoleBinding) 이 필요합니다.
jgroups-kubeping이 배포될 네임스페이스의 SA에는 pods의 목록을 조회할 수 있는 CR이 필요하기 때문에 해당 권한을 부여해줍니다.

💡 참고 : 네임스페이스 설정시에는 export TARGET_NAMESPACE=hwyu 와 같이 추가할 수 있습니다.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: jgroups-kubeping-service-account
  namespace: $TARGET_NAMESPACE
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: jgroups-kubeping-pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: jgroups-kubeping-api-access
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: jgroups-kubeping-pod-reader
subjects:
- kind: ServiceAccount
  name: jgroups-kubeping-service-account
  namespace: $TARGET_NAMESPACE

다음으로 wildfly 앱을 배포할 때 --server-configstandalone-ha.xml을 참조하고 $(POD_IP)를 변수로 처리하여 실행합니다. 그리고 앞서 생성한 SA를 참조하기 serviceAccountName: jgroups-kubeping-service-account를 기입합니다.

참고 : 해당 SA를 참조하여야 Cluster Member를 Discovery 할 수 있습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wildfly
  labels:
    app: wildfly
spec:
  selector:
    matchLabels:
      app: wildfly
  replicas: 2
  template:
    metadata:
      labels:
        app: wildfly
    spec:
      serviceAccountName: jgroups-kubeping-service-account
      containers:
        - name: wildfly-kube-ping
          image: yuhyungwook/wildfly:25.0.0.Final
          command: ["/opt/jboss/wildfly/bin/standalone.sh"]
          args: ["--server-config", "standalone-ha.xml", "-b", $(POD_IP), "-bmanagement", $(POD_IP) ,"-bprivate", $(POD_IP) ]
          resources:
            requests:
              memory: 256Mi
          imagePullPolicy: Always
          ports:
            - containerPort: 8080
            - containerPort: 7600
          env:
            - name: POD_IP
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: status.podIP
            - name: KUBERNETES_NAMESPACE
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
            - name: KUBERNETES_LABELS
              value: app=wildfly
  • Dockerfile 내용 분석
    베이스 이미지로 wildfly:25.0.0.Final이미지를 채택하였으며, KUBE_PING기능을 설정을 위해 config-widlfly.cli 스크립트 파일을 작성하여 추가하였습니다.
FROM jboss/wildfly:25.0.0.Final
LABEL MAINTAINER hwyu <hwyu@mantech.co.kr>

ADD config-wildfly.cli /opt/jboss/
ADD cluster.war /opt/jboss/wildfly/standalone/deployments/

RUN /opt/jboss/wildfly/bin/add-user.sh admin accordion --silent \
    && /opt/jboss/wildfly/bin/jboss-cli.sh --file=config-wildfly.cli \
    && rm -Rf /opt/jboss/wildfly/standalone/configuration/standalone_xml_history/*

EXPOSE 8080 9990 7600 8888
  • 이미지 빌드
sudo docker build -t yuhyungwook/wildfly:25.0.0.Final .
Sending build context to Docker daemon  109.6kB
Step 1/6 : FROM jboss/wildfly:25.0.0.Final
25.0.0.Final: Pulling from jboss/wildfly
f87ff222252e: Pull complete
13776e8da872: Pull complete
0b43aea4eeb1: Pull complete
8116b2f7ca5a: Pull complete
f26d32e28c29: Pull complete
Digest: sha256:35320abafdec6d360559b411aff466514d5741c3c527221445f48246350fdfe5
Status: Downloaded newer image for jboss/wildfly:25.0.0.Final
 ---> 856694040847
Step 2/6 : LABEL MAINTAINER hwyu <hwyu@mantech.co.kr>
 ---> Running in 3befa8fb40d7
...(중략)
Successfully built b46ce24831f9
Successfully tagged yuhyungwook/wildfly:25.0.0.Final
  • 이미지 푸쉬
sudo docker push yuhyungwook/wildfly:25.0.0.Final
The push refers to repository [docker.io/yuhyungwook/wildfly]
692b28cab699: Pushed
353731747448: Pushed
8255f5f8095c: Pushed
7f40a58d5146: Mounted from jboss/wildfly
869989761eb2: Mounted from jboss/wildfly
3fbe1e874b0d: Mounted from jboss/wildfly
115463be137a: Mounted from jboss/wildfly
613be09ab3c0: Mounted from jboss/wildfly
25.0.0.Final: digest: sha256:e9b2fae230b811795eaa406620adbe185b3f1e5db499f35ab96eae168efb27fa size: 1997
  • 푸쉬된 이미지 확인

  • 앱 배포
export TARGET_NAMESPACE=hwyu
k apply -f wildfly-sa-role.yaml -n $TARGET_NAMESPACE

k apply -f wildfly-deploy.yaml -n hwyu
deployment.apps/wildfly created
service/wildfly created
  • SVC 타입 변경
kubectl patch svc wildfly -n hwyu -p '{"spec": {"type": "LoadBalancer"}}'
service/wildfly patched

k get svc -n hwyu
NAME      TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)          AGE
wildfly   LoadBalancer   10.107.77.162   10.20.200.227   8080:32185/TCP   4m23s

클러스터링 확인

이제 HA로 구성된 Wildfly가 정상적으로 클러스터링 되어있는지 확인해봅니다.

  • 배포된 파드 확인

두 대의 파드가 정상적으로 Running 인 것으로 확인이 됩니다.

k get pods -o wide -n hwyu
NAME                       READY   STATUS    RESTARTS   AGE     IP               NODE        NOMINATED NODE   READINESS GATES
wildfly-6878c54b8f-4rfvj   1/1     Running   0          3m50s   172.32.139.178   acc-node1   <none>           <none>
wildfly-6878c54b8f-q42xh   1/1     Running   0          3m15s   172.32.24.47     acc-node2   <none>           <none>

이제 실제로 클러스터링 된지 확인을 해볼까요?

  • 컨테이너 로그 확인 : wildfly-6878c54b8f-q42xh

wildfly 파드의 로그를 확인행보니 정상적으로 Cluster의 Member List가 조회된 것을 확인할 수 있습니다.
(이미지가 실수로 삭제되어 추후 다시 업로드 예정!)

  • 실제 Session ID, Node ID 확인

  • 배포된 앱 및 세션 클러스터 테스트

wildfly-6878c54b8f-rdrkp 파드를 삭제하고 세션 정보가 유지되는지 확인해보겠습니다. 기존 배포된 파드를 삭제하고 로그에서 확인해보면 SIGTERM이 수신된 것을 확인할 수 있습니다.

widlfly의 로그를 모니터링 해보니 정상적으로 새로운 POD가 Cluster Member로 추가된 것으로 확인됩니다.

실제 브라우저에서 확인해보니 Session ID는 유지가 되고 Node ID(POD)만 변경된 것을 확인할 수 있습니다.

참고 : 🤦‍♂️ 그림 속 POD IP -> POD ID 오타입니다!

이렇게 세션 클러스터 기능을 통해 이중화 구성된 Wildfly에서 서로 세션 정보를 공유하고 있는 것이 확인되었습니다!🔥


2. CI/CD 파이프라인 연계

이번에는 CI/CD 파이프라인을 연계하여 소스/이미지를 빌드(build)하고 배포(deploy)하는 방법을 소개합니다.

📢 참고 : 본 실습에서는 아코디언 v2에서 제공하는 카탈로그 기능을 연계하여 테스트해볼 예정입니다.

CI/CD 파이프라인을 사용하는 이유?

1. Wildfly HA 에서는 이미지 빌드시에 사전에 제작된 cluster.war 파일을 ADD하여 작업하였습니다.
하지만 실제 서비스 환경에서는 빈번하게 소스 변경이 발생하기 때문에 소스의 버전관리와 이에따른 빌드/배포 자동화가 필요합니다.

그렇기 때문에 소스를 빌드하는 CI 단계에서 .war, .jar와 같은 패키지 파일을 제작하고 이미지 빌드시에 WAS(wildfly, tomcat 등) 베이스 이미지 통합하는 과정을 활용할 수 있습니다.

CI/CD 파이프라인 구성도

테스트한 CI/CD 파이프라인 구성도는 다음 두 가지 형태가 있으며, 본 글에서는 두 번째 방식을 사용하여 배포합니다.

방식1. VCS에서 소스 받아서 배포
vcs get -> maven build -> image build -> deploy 플로우 형태로 배포하는 방법

방식2. WAR 패키징된 소스 업로드 방식으로 배포 ✔
war upload -> image build -> deploy 플로우 형태로 배포하는 방법

(참고) 아코디언 카탈로그 배포

필자는 편의상 별도 YAML 작성이나 Helm Chart 배포를 사용하지 않고, 아코디언에서 제공하는 Wildfly HA 카탈로그 템플릿을 사용하여 배포하였습니다.

💡 참고 : YAML 작성 내용은 위에서 소개한 샘플과 거의 동일하기 때문에 생략

📢 참고 : 아코디언 카탈로그 기능 소개 영상


3. Ingress 구성방안

쿠버네티스에서는 서비스를 외부에 노출하기 위해 NodePort, LoadBalancer 등을 사용할 수 있지만 Layer 7 동작(Web Proxy)을 컨트롤 하기위해서 Ingress를 별도로 제공하고 있습니다.

위 그림에서 처럼 Ingress를 통해 인입된 트래픽을 경로(Path) 기반으로 분기하여 정적 컨텐츠는 apache로 동적 컨텐츠는 wildfly로 분기하는 환경을 만들어보겠습니다.

Ingress설정은 다음과 같습니다.

  • 정규식을 사용하여 정적 컨텐츠는 백엔드 서비스 hw-apache로 분기
  • 이외의 요청은 hw-wildfly-ha로 분기 이때, /backend로 호출되도록 path 지정

📢 참고 : 이 외의 annotations 설정은 추후 별도로 분석한 글을 작성할 예정!

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hw-ing
  namespace: hwyu
  annotations:
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/session-cookie-hash: "sha1"
    nginx.ingress.kubernetes.io/session-cookie-name: "route"
spec:
  ingressClassName: nginx
  rules:
  - host: www.hwyu.com
    http:
      paths:
      - pathType: Prefix
        path: '/(.+\.(htm|html|bin|zip|cab|exe|js|css|jpeg|jpg|jpe|jfif|pjpeg|pjp|png|bmp|txt|mpeg|mpg|mpe|mpv|vbs|mpegv|avi|shtml|bat|gif|ppt|pdf|swf|ocx)$)'
        backend:
          service:
            name: hw-apache
            port:
              number: 80
      - pathType: Prefix
        path: /backend
        backend:
          service:
            name: hw-wildfly-ha
            port:
              number: 8080

4. 정적 / 동적페이지 접속 확인

1) 정적 페이지

html, css, js 등 각종 정적 컨텐츠는 apache 서버에서 처리할 수 있도록 분기처리 하였습니다. 실제로 apache에 배포한 소스로 접속이 되는지 확인해보겠습니다.

💡 참고 : 실제 운영시에는 구매한 도메인으로 호출가능!

  • 로컬 PC의 브라우저에서 hosts 등록 후 www.hwyu.com 를 호출 화면

💡 참고 : 정적 페이지 소스 출처

2) 동적 페이지

동적 페이지는 ingress에서 /backed path로 접속하면 분기되도록 설정하였습니다.

  • 로컬 PC의 브라우저에서 hosts 등록 후 www.hwyu.com/backend 호출한 화면

💡 참고 : 이미지를 넣지는 않았지만 HA 구성된 Wildfly 이기 때문에 하나의 wildfly가 내려가더라도 session id는 유지됨!


5. 마무리

이렇게 쿠버네티스 환경에서 3-tier 구성하는 방법에 대해서 간략하게 알아보았습니다.
이미지 빌드부터 배포까지 모든것을 다 설명하려고 하다보니 부가적인 설명이나 예시가 부족했던 것 같습니다.

이번 글에서는 전반적인 Overview를 소개하였다면 다음 글에서는 사용된 오픈소스(apache, wildfly 등)와 쿠버네티스에서 사용한 각종 설정 및 ingress 설정 등을 상세하게 파헤쳐보려 합니다.

긴글 읽어주셔서 감사드립니다🙏

부족한 내용이나 잘못된 부분에 대한 피드백은 언제나 환영합니다!🔥🔥


6. (번외 홍보글) 아코디언

소개

아코디언은 맨텍에서 개발한 관리형 쿠버네티스 또는 PaaS 플랫폼으로 소개드릴 수 있을 것 같습니다.

최근에는 많은 기업, 고객들이 컨테이너 가상화, 쿠버네티스에 대한 도입을 고려하고 있습니다. 하지만 결코 쉽지만은 않기 때문에 관리형 쿠버네티스, 플랫폼 형태의 서비스를 많이 사용하고 있는 추세입니다.

실제로 CNCF 2021 연간 서베이 결과에서 "응답자의 약 79%가 관리형 쿠버네티스를 사용하고 있다"라고 조사될 만큼 관리형 쿠버네티스의 비중이 높습니다.
출처 : CNCF Annual Survey 2021

국내에서도 많은 기업 및 제품들이 CNCF 인증받은 관리형 쿠버네티스 제품을 제공하고 있습니다. (국내는 아코디언이 최고) 참고

혹여나 쿠버네티스를 업무에 도입하려고 고민하시는 분들은 글로벌 CSP에서 제공하는 관리형 쿠버네티스 외에도 다양한 관리형 쿠버네티스에 대한 특/장점을 비교하고 도입해보는 것도 좋을 것 같습니다!

아코디언에 대한 소개자료 및 데모영상, 데모실습을 위해서는 아코디언 공식 홈페이지/블로그를 참고하세요! 👍

profile
DevOps를 꿈꾸는 엔지니어 입니다.

0개의 댓글