[K8S] CI/CD Pipeline, 모니터링, 로깅 시스템 구축 프로젝트 -GKE 환경에 배포하기

mDev_97·2023년 10월 26일
0

Kubernetes

목록 보기
5/10
post-thumbnail

📌 프로젝트 주제에 대한 자세한 설명은 [K8S] CI/CD Pipeline, 모니터링, 로깅 시스템 구축 프로젝트 - 프로젝트 소개 게시글을 확인해주세요.

이번 게시글에서 진행할 내용은 CI/CD Pipeline, 모니터링, 로깅 시스템 구축 프로젝트를 진행하기 전에
GKE 환경에 풀스택 어플리케이션을 배포해보는 과정입니다.

프로젝트를 시작하기 전에 GKE 환경에 직접 배포해보는 이유는
기존 [K8S] Ingress 도메인 라우팅 규칙 적용 (Feat. Mac M1 with Docker Desktop) 게시글에서 배포하였던 환경은 로컬 환경에서 진행했기 때문에 클라우드 환경에서도 정상적으로 작동하는지 확인해보기 위함입니다.

📌 소스코드 및 로컬 환경 메니페스트 GitHub

도커 이미지 준비하기

프로젝트를 진행하기 전에 배포하고자 하는 도커 이미지를 먼저 만들어주겠습니다.
프로젝트에 사용되는 소스코드는 GitHub 리포지토리를 클론하여 사용해주세요.

도커 이미지를 빌드하고 도커 허브에 푸시하는 과정은 지금 프로젝트에 중요한 단계는 아니기 때문에
따로 설명을 진행하지는 않겠습니다.

그럼 클론을 받은 소스코드의 디렉토리로 들어와서 아래의 명령어를 실행해주시면 됩니다.

docker build -t [DockerHub 리포명]/react:1 ./frontend
docker build -t [DockerHub 리포명]/nodejs:1 ./backend
docker build -t [DockerHub 리포명]/db:1 ./mysql

docker push moonsungkim/react:1
docker push moonsungkim/nodejs:1
docker push moonsungkim/db:1

📌 MAC M1 환경이신 분들은 아래의 명령어를 통해서 이미지 빌드와 푸시를 진행해야 합니다.

docker buildx build --platform=linux/amd64 -t [DockerHub 리포명]/react:1 ./frontend
docker buildx build --platform=linux/amd64 -t [DockerHub 리포명]/nodejs:1 ./backend
docker buildx build --platform=linux/amd64 -t [DockerHub 리포명]/db:1 ./mysql
docker push moonsungkim/react:1
docker push moonsungkim/nodejs:1
docker push moonsungkim/db:1

를 사용해주셔야 에러가 발생하지 않습니다.

환경 구축

이번에는 프로젝트 설명에서와 같이 GKE 클러스터 환경에서 진행을 합니다.
이를 위해서 GKE 클러스터는 만들기부터 시작하여 프로젝트를 진행해보겠습니다.

Google Cloud Platform에서 GKE 클러스터를 구축하는 방법은 아주 간단합니다.

이를 위해서 Google Cloud Platform 에서 콘솔로 접속하여서
좌측 메뉴를 통해 [Kubernetes Engine] -> [클러스터]로 들어가줍니다.

바로 [만들기] 버튼을 클릭하여 Kubernetes 클러스터를 만들어 보겠습니다.

우측 상단에 [STANDARD 클러스터로 전환] 클릭

아래의 화면에서 다른 설정들은 만지지 않고 바로 [만들기] 버튼을 통해 Kubernetes 클러스터를 만들겠습니다.
클러스터를 생성하는 과정은 5분 정도의 시간이 소요됩니다.

클러스터 생성이 완료되었으면 [연결] 버튼을 눌러 클러스터에 연결을 해줍니다.

클러스터에 연결할 때 kubectl 명령어 대한 액세스를 주기 위해서
아래의 명령어 밑에 있는 [CLOUD SHELL에서 실행] 버튼을 클릭하여
명령어를 Cloud 셸에서 실행시켜 주겠습니다.

클라우드 셸이 뜨면 엔터 후에 [승인] 클릭

이후에 아래의 명령어를 통해서 클러스터의 노드가 모두 정상 상태인지를 확인해줍니다.

kubectl get nodes



GKE에 애플리케이션 배포하기

이렇게 GKE 클러스터 환경이 구축되었으면 이제 풀스택 어플리케이션을 배포해보겠습니다.
배포 테스트에 대한 메니페스트는 fullstack-app 디렉토리에서 진행하겠습니다.

mkdir fullstack-app
cd fullstack-app

Ingress NGINX Controller 설치

로컬 환경에서와 같이 어플리케이션을 진행하기 전에 아래의 명렁어를 통해서 인그레스 컨트롤러를
설치해주어 인그레스 리소스가 정상적으로 동작할 수 있도록 합니다.

📌 GKE 환경에서는 따로 Ingress Controller를 설치해주지 않아도 되지만
저 같은 경우에는 기존 인그레스가 Nginx로 되어 있기 때문에 Ingress NGINX Controller를 설치해주었습니다.

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml

위의 명령어를 실행하였다면 Ingress Controller가 정상적으로 설치되었는지 확인해보겠습니다.

kubectl get all -n ingress-nginx

ingress-nginx-controller 라는 LoadBalancer 타입의 서비스가 정상적으로 띄어진 것을 볼 수 있습니다.
LoadBalancer 타입의 서비스는 생성하고 대략 30초 정도의 시간이 지나면 EXTERNAL-IP가 부여됩니다.

Kustomization 생성

# kustomization.yaml

secretGenerator:
- name: mysql-pass
  literals:
  - password=1234
resources:
  - mysql-deployment.yaml
  - api-deployment.yaml
  - web-deployment.yaml
  - ingress.yaml

Ingress 생성

# ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080

Trouble Shooting

로컬 환경에서 배포하였던 인그레스 파일을 통해서 GKE 환경에 배포하는 과정에 접근했던 이슈는
서비스와 인그레스를 배포하는 과정에서 인그레스가 서비스의 엔드포인트를 인식하지 못하고
서비스의 백엔드가 UNHEALTH 하다는 오류를 뱉어내었습니다.

이는 GKE에서 흔하게 발생되는 에러로 보입니다.
저 같은 경우에는 NGINX Ingress Controller를 사용하였기 때문에
Ingress에 metadata.annotations: kubernetes.io/ingress.class: nginx 라는 어노테이션을 추가하여 이 문제를 해결하였습니다.

React 리소스 생성

# web-deployment.yaml

apiVersion: v1
kind: Service
metadata:
  name: web-service
  labels:
    app: ingress-project
spec:
  ports:
    - port: 80
      targetPort: 3000
  selector:
    app: ingress-project
    tier: web
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  labels:
    app: ingress-project
spec:
  replicas: 2
  selector:
    matchLabels:
      app: ingress-project
      tier: web
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: ingress-project
        tier: web
    spec:
      containers:
      - image: moonsungkim/react:1
        imagePullPolicy: Always
        name: web-app
        ports:
        - containerPort: 80
          name: web-app

NodeJS 리소스 생성

# api-deployment.yaml

apiVersion: v1
kind: Service
metadata:
  name: api-service
  labels:
    app: ingress-project
spec:
  ports:
    - port: 8080
      targetPort: 5000
  selector:
    app: ingress-project
    tier: api
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-app
  labels:
    app: ingress-project
spec:
  replicas: 2
  selector:
    matchLabels:
      app: ingress-project
      tier: api
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: ingress-project
        tier: api
    spec:
      containers:
      - image: moonsungkim/nodejs:1
        imagePullPolicy: Always
        name: api-app
        env:
          - name: DB_HOST
            value: mysql
          - name: DB_PASSWORD
            valueFrom:
              secretKeyRef:
                name: mysql-pass
                key: password
        ports:
        - containerPort: 5000
          name: api-app

MySQL 리소스 생성

# mysql-deployment.yaml

apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: ingress-project
spec:
  ports:
    - port: 3306
  selector:
    app: ingress-project
    tier: mysql

---

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: storage
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
  labels:
    app: ingress-project
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: storage

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-deploy
  labels:
    app: ingress-project
spec:
  selector:
    matchLabels:
      app: ingress-project
      tier: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: ingress-project
        tier: mysql
    spec:
      containers:
        - image: moonsungkim/db:1
          name: mysql
          env:
          - name: MYSQL_ROOT_PASSWORD
            valueFrom:
              secretKeyRef:
                name: mysql-pass
                key: password
          - name: MYSQL_DATABASE
            value: myapp
          ports:
          - containerPort: 3306
            name: mysql
          volumeMounts:
          - name: mysql-persistent-storage
            mountPath: /var/lib/mysql
          args:
           - "--ignore-db-dir=lost+found"
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim

Trouble Shooting

GKE 환경에서 로컬에서 실행한 MySQL 디플로이먼트 메니페스트를 사용하여 배포하는 과정에서
PV와 관련된 문제를 마주쳤습니다.

이유는 로컬에서 사용하였던 경우에는 PV를 hostpath 타입으로 잡아두었기 때문입니다.
hostpath 타입의 PV는 단일노드 또는 로컬 환경에서만 추천하는 방식입니다.

이를 해결하기 위해서 제가 사용한 방식은 동적 볼륨 프로비저닝을 활용한 persistentVolumeClaim 방식입니다.

동적 프로비저닝 기능을 사용하면 클러스터 관리자가 스토리지를 사전에 프로비저닝 할 필요가 없으며, 사용자가 스토리지를 요청하면 자동으로 프로비저닝 하는 방식입니다.

또한, 퍼시스턴트볼륨클레임은 사용자가 특정 클라우드 환경의 세부 내용을 몰라도 내구성이있는 Persistent Disk와 같은 스토리지를 클레임할 수 있는 방법입니다.



결과 확인

이제 작성한 메니페스트를 통해서 GKE 환경에서 애플리케이션이 배포되는지를 확인해보겠습니다.

kubectl apply -k ./

그럼 다음과 같이 모두 정상적으로 배포된 것을 볼 수 있습니다.

kubectl get ing

이제 브라우저 상에서 확인하기 위해서 ingress의 주소로 접근해보겠습니다.

그럼 아래와 같이 브라우저에서도 접근할 수 있도록 배포가 된 모습을 볼 수 있습니다.

profile
안녕하세요. 백엔드, 클라우드, 인프라에 관심과 열정이 있는 김문성입니다. 😊

0개의 댓글