📌 프로젝트 주제에 대한 자세한 설명은 [K8S] CI/CD Pipeline, 모니터링, 로깅 시스템 구축 프로젝트 - 프로젝트 소개 게시글을 확인해주세요.
이번 게시글에서 진행할 내용은 CI/CD Pipeline, 모니터링, 로깅 시스템 구축 프로젝트를 진행하기 전에
GKE 환경에 풀스택 어플리케이션을 배포해보는 과정입니다.
프로젝트를 시작하기 전에 GKE 환경에 직접 배포해보는 이유는
기존 [K8S] Ingress 도메인 라우팅 규칙 적용 (Feat. Mac M1 with Docker Desktop) 게시글에서 배포하였던 환경은 로컬 환경에서 진행했기 때문에 클라우드 환경에서도 정상적으로 작동하는지 확인해보기 위함입니다.
프로젝트를 진행하기 전에 배포하고자 하는 도커 이미지를 먼저 만들어주겠습니다.
프로젝트에 사용되는 소스코드는 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 클러스터 환경이 구축되었으면 이제 풀스택 어플리케이션을 배포해보겠습니다.
배포 테스트에 대한 메니페스트는 fullstack-app 디렉토리에서 진행하겠습니다.
mkdir fullstack-app
cd fullstack-app
로컬 환경에서와 같이 어플리케이션을 진행하기 전에 아래의 명렁어를 통해서 인그레스 컨트롤러를
설치해주어 인그레스 리소스가 정상적으로 동작할 수 있도록 합니다.
📌 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.yaml
secretGenerator:
- name: mysql-pass
literals:
- password=1234
resources:
- mysql-deployment.yaml
- api-deployment.yaml
- web-deployment.yaml
- ingress.yaml
# 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
로컬 환경에서 배포하였던 인그레스 파일을 통해서 GKE 환경에 배포하는 과정에 접근했던 이슈는
서비스와 인그레스를 배포하는 과정에서 인그레스가 서비스의 엔드포인트를 인식하지 못하고
서비스의 백엔드가 UNHEALTH 하다는 오류를 뱉어내었습니다.
이는 GKE에서 흔하게 발생되는 에러로 보입니다.
저 같은 경우에는 NGINX Ingress Controller를 사용하였기 때문에
Ingress에 metadata.annotations: kubernetes.io/ingress.class: nginx
라는 어노테이션을 추가하여 이 문제를 해결하였습니다.
# 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
# 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-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
GKE 환경에서 로컬에서 실행한 MySQL 디플로이먼트 메니페스트를 사용하여 배포하는 과정에서
PV와 관련된 문제를 마주쳤습니다.
이유는 로컬에서 사용하였던 경우에는 PV를 hostpath 타입으로 잡아두었기 때문입니다.
hostpath 타입의 PV는 단일노드 또는 로컬 환경에서만 추천하는 방식입니다.
이를 해결하기 위해서 제가 사용한 방식은 동적 볼륨 프로비저닝을 활용한 persistentVolumeClaim 방식입니다.
동적 프로비저닝 기능을 사용하면 클러스터 관리자가 스토리지를 사전에 프로비저닝 할 필요가 없으며, 사용자가 스토리지를 요청하면 자동으로 프로비저닝 하는 방식입니다.
또한, 퍼시스턴트볼륨클레임은 사용자가 특정 클라우드 환경의 세부 내용을 몰라도 내구성이있는 Persistent Disk와 같은 스토리지를 클레임할 수 있는 방법입니다.
이제 작성한 메니페스트를 통해서 GKE 환경에서 애플리케이션이 배포되는지를 확인해보겠습니다.
kubectl apply -k ./
그럼 다음과 같이 모두 정상적으로 배포된 것을 볼 수 있습니다.
kubectl get ing
이제 브라우저 상에서 확인하기 위해서 ingress의 주소로 접근해보겠습니다.
그럼 아래와 같이 브라우저에서도 접근할 수 있도록 배포가 된 모습을 볼 수 있습니다.