Merge branch 'main' of https://g.wace.me/chpark/vexplor
Some checks failed
Deploy vexplor / build-and-deploy (push) Failing after 14m18s
Some checks failed
Deploy vexplor / build-and-deploy (push) Failing after 14m18s
This commit is contained in:
327
.gitea/workflows/deploy.yml
Normal file
327
.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
# Gitea Actions Workflow - vexplor 자동 배포
|
||||||
|
#
|
||||||
|
# 환경 변수:
|
||||||
|
# - GITEA_DOMAIN: g.wace.me
|
||||||
|
# - HARBOR_REGISTRY: harbor.wace.me
|
||||||
|
# - K8S_NAMESPACE: vexplor
|
||||||
|
#
|
||||||
|
# 필수 Secrets (Repository Settings > Secrets):
|
||||||
|
# - HARBOR_USERNAME: Harbor 사용자명
|
||||||
|
# - HARBOR_PASSWORD: Harbor 비밀번호
|
||||||
|
# - KUBECONFIG: base64로 인코딩된 Kubernetes config
|
||||||
|
#
|
||||||
|
# Application Secrets:
|
||||||
|
# - k8s/vexplor-secret.yaml 파일에서 관리
|
||||||
|
|
||||||
|
name: Deploy vexplor
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- "backend-node/**"
|
||||||
|
- "frontend/**"
|
||||||
|
- "docker/**"
|
||||||
|
- "k8s/**"
|
||||||
|
- ".gitea/workflows/deploy.yml"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
GITEA_DOMAIN: g.wace.me
|
||||||
|
HARBOR_REGISTRY: localhost:5001
|
||||||
|
HARBOR_REGISTRY_K8S: 192.168.1.100:5001
|
||||||
|
HARBOR_REGISTRY_EXTERNAL: harbor.wace.me
|
||||||
|
HARBOR_PROJECT: speefox_vexplor
|
||||||
|
K8S_NAMESPACE: vexplor
|
||||||
|
|
||||||
|
# Frontend 빌드 환경 변수
|
||||||
|
NEXT_PUBLIC_API_URL: "https://api.vexplor.com/api"
|
||||||
|
NEXT_PUBLIC_ENV: "production"
|
||||||
|
INTERNAL_API_URL: "http://vexplor-backend-service:3001"
|
||||||
|
|
||||||
|
# Frontend 설정
|
||||||
|
FRONTEND_IMAGE_NAME: vexplor-frontend
|
||||||
|
FRONTEND_DEPLOYMENT_NAME: vexplor-frontend
|
||||||
|
FRONTEND_CONTAINER_NAME: vexplor-frontend
|
||||||
|
FRONTEND_BUILD_CONTEXT: frontend
|
||||||
|
FRONTEND_DOCKERFILE_PATH: docker/deploy/frontend.Dockerfile
|
||||||
|
|
||||||
|
# Backend 설정
|
||||||
|
BACKEND_IMAGE_NAME: vexplor-backend
|
||||||
|
BACKEND_DEPLOYMENT_NAME: vexplor-backend
|
||||||
|
BACKEND_CONTAINER_NAME: vexplor-backend
|
||||||
|
BACKEND_BUILD_CONTEXT: backend-node
|
||||||
|
BACKEND_DOCKERFILE_PATH: docker/deploy/backend.Dockerfile
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
# 작업 디렉토리 정리
|
||||||
|
- name: Clean workspace
|
||||||
|
run: |
|
||||||
|
echo "작업 디렉토리 정리..."
|
||||||
|
cd /workspace
|
||||||
|
rm -rf source
|
||||||
|
mkdir -p source
|
||||||
|
echo "정리 완료"
|
||||||
|
|
||||||
|
# 필수 도구 설치
|
||||||
|
- name: Install required tools
|
||||||
|
run: |
|
||||||
|
echo "필수 도구 설치 중..."
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y git curl ca-certificates gnupg
|
||||||
|
|
||||||
|
# kubectl 설치
|
||||||
|
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
|
||||||
|
chmod +x kubectl
|
||||||
|
mv kubectl /usr/local/bin/
|
||||||
|
|
||||||
|
# Docker 클라이언트 설치
|
||||||
|
install -m 0755 -d /etc/apt/keyrings
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
|
||||||
|
chmod a+r /etc/apt/keyrings/docker.asc
|
||||||
|
|
||||||
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
|
||||||
|
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||||
|
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y docker-ce-cli
|
||||||
|
|
||||||
|
echo "설치 완료:"
|
||||||
|
git --version
|
||||||
|
kubectl version --client
|
||||||
|
docker --version
|
||||||
|
|
||||||
|
export DOCKER_HOST=unix:///var/run/docker.sock
|
||||||
|
docker version || echo "소켓 연결 대기 중..."
|
||||||
|
|
||||||
|
# 저장소 체크아웃
|
||||||
|
- name: Checkout code
|
||||||
|
run: |
|
||||||
|
echo "저장소 체크아웃..."
|
||||||
|
cd /workspace/source
|
||||||
|
|
||||||
|
git clone --depth 1 --branch ${GITHUB_REF_NAME} \
|
||||||
|
https://oauth2:${{ github.token }}@${GITEA_DOMAIN}/${GITHUB_REPOSITORY}.git .
|
||||||
|
|
||||||
|
echo "체크아웃 완료"
|
||||||
|
git log -1 --oneline
|
||||||
|
|
||||||
|
# 빌드 환경 설정
|
||||||
|
- name: Set up build environment
|
||||||
|
run: |
|
||||||
|
IMAGE_TAG="v$(date +%Y%m%d-%H%M%S)-${GITHUB_SHA::7}"
|
||||||
|
echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# Frontend 이미지
|
||||||
|
echo "FRONTEND_FULL_IMAGE=${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${FRONTEND_IMAGE_NAME}" >> $GITHUB_ENV
|
||||||
|
echo "FRONTEND_FULL_IMAGE_K8S=${HARBOR_REGISTRY_K8S}/${HARBOR_PROJECT}/${FRONTEND_IMAGE_NAME}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# Backend 이미지
|
||||||
|
echo "BACKEND_FULL_IMAGE=${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${BACKEND_IMAGE_NAME}" >> $GITHUB_ENV
|
||||||
|
echo "BACKEND_FULL_IMAGE_K8S=${HARBOR_REGISTRY_K8S}/${HARBOR_PROJECT}/${BACKEND_IMAGE_NAME}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
echo "빌드 태그: ${IMAGE_TAG}"
|
||||||
|
|
||||||
|
# Harbor 로그인
|
||||||
|
- name: Login to Harbor
|
||||||
|
env:
|
||||||
|
HARBOR_USER: ${{ secrets.HARBOR_USERNAME }}
|
||||||
|
HARBOR_PASS: ${{ secrets.HARBOR_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
echo "Harbor 로그인..."
|
||||||
|
export DOCKER_HOST=unix:///var/run/docker.sock
|
||||||
|
echo "${HARBOR_PASS}" | docker login ${HARBOR_REGISTRY} \
|
||||||
|
--username ${HARBOR_USER} \
|
||||||
|
--password-stdin
|
||||||
|
echo "Harbor 로그인 완료"
|
||||||
|
|
||||||
|
# Backend 빌드 및 푸시
|
||||||
|
- name: Build and Push Backend image
|
||||||
|
run: |
|
||||||
|
echo "Backend 이미지 빌드 및 푸시..."
|
||||||
|
export DOCKER_HOST=unix:///var/run/docker.sock
|
||||||
|
cd /workspace/source
|
||||||
|
|
||||||
|
docker build \
|
||||||
|
-t ${BACKEND_FULL_IMAGE}:${IMAGE_TAG} \
|
||||||
|
-t ${BACKEND_FULL_IMAGE}:latest \
|
||||||
|
-f ${BACKEND_DOCKERFILE_PATH} \
|
||||||
|
${BACKEND_BUILD_CONTEXT}
|
||||||
|
|
||||||
|
docker push ${BACKEND_FULL_IMAGE}:${IMAGE_TAG}
|
||||||
|
docker push ${BACKEND_FULL_IMAGE}:latest
|
||||||
|
echo "Backend 푸시 완료"
|
||||||
|
|
||||||
|
# Frontend 빌드 및 푸시
|
||||||
|
- name: Build and Push Frontend image
|
||||||
|
run: |
|
||||||
|
echo "Frontend 이미지 빌드 및 푸시..."
|
||||||
|
export DOCKER_HOST=unix:///var/run/docker.sock
|
||||||
|
cd /workspace/source
|
||||||
|
|
||||||
|
echo "빌드 환경 변수:"
|
||||||
|
echo " NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}"
|
||||||
|
echo " NEXT_PUBLIC_ENV=${NEXT_PUBLIC_ENV}"
|
||||||
|
|
||||||
|
docker build \
|
||||||
|
-t ${FRONTEND_FULL_IMAGE}:${IMAGE_TAG} \
|
||||||
|
-t ${FRONTEND_FULL_IMAGE}:latest \
|
||||||
|
-f ${FRONTEND_DOCKERFILE_PATH} \
|
||||||
|
--build-arg NEXT_PUBLIC_API_URL="${NEXT_PUBLIC_API_URL}" \
|
||||||
|
${FRONTEND_BUILD_CONTEXT}
|
||||||
|
|
||||||
|
docker push ${FRONTEND_FULL_IMAGE}:${IMAGE_TAG}
|
||||||
|
docker push ${FRONTEND_FULL_IMAGE}:latest
|
||||||
|
echo "Frontend 푸시 완료"
|
||||||
|
|
||||||
|
# Kubernetes 설정
|
||||||
|
- name: Setup Kubernetes config
|
||||||
|
env:
|
||||||
|
KUBECONFIG_CONTENT: ${{ secrets.KUBECONFIG }}
|
||||||
|
run: |
|
||||||
|
echo "Kubernetes 설정..."
|
||||||
|
|
||||||
|
if [ -z "${KUBECONFIG_CONTENT}" ]; then
|
||||||
|
echo "KUBECONFIG secret이 설정되지 않았습니다!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p ~/.kube
|
||||||
|
echo "${KUBECONFIG_CONTENT}" | base64 -d > ~/.kube/config
|
||||||
|
chmod 600 ~/.kube/config
|
||||||
|
|
||||||
|
if [ ! -s ~/.kube/config ]; then
|
||||||
|
echo "kubeconfig 파일이 비어있습니다"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "kubeconfig 파일 생성 완료"
|
||||||
|
kubectl cluster-info > /dev/null 2>&1 && echo "Kubernetes 클러스터 연결 성공"
|
||||||
|
|
||||||
|
# Kubernetes 배포
|
||||||
|
- name: Deploy to Kubernetes
|
||||||
|
run: |
|
||||||
|
echo "Kubernetes 배포 시작..."
|
||||||
|
|
||||||
|
cd /workspace/source
|
||||||
|
|
||||||
|
# 네임스페이스 생성 (없을 때만)
|
||||||
|
echo "네임스페이스 확인..."
|
||||||
|
kubectl apply -f k8s/namespace.yaml
|
||||||
|
|
||||||
|
# ConfigMap 적용
|
||||||
|
echo "ConfigMap 적용..."
|
||||||
|
kubectl apply -f k8s/vexplor-config.yaml -n ${K8S_NAMESPACE}
|
||||||
|
|
||||||
|
# Secret 적용 (존재하는 경우에만)
|
||||||
|
if [ -f k8s/vexplor-secret.yaml ]; then
|
||||||
|
echo "Secret 적용..."
|
||||||
|
kubectl apply -f k8s/vexplor-secret.yaml -n ${K8S_NAMESPACE}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Harbor Registry Secret 생성 (없을 때만)
|
||||||
|
echo "Harbor Registry Secret 확인..."
|
||||||
|
if ! kubectl get secret harbor-registry -n ${K8S_NAMESPACE} > /dev/null 2>&1; then
|
||||||
|
echo "Harbor Registry Secret 생성 중..."
|
||||||
|
kubectl create secret docker-registry harbor-registry \
|
||||||
|
--docker-server=${HARBOR_REGISTRY_K8S} \
|
||||||
|
--docker-username=${{ secrets.HARBOR_USERNAME }} \
|
||||||
|
--docker-password=${{ secrets.HARBOR_PASSWORD }} \
|
||||||
|
-n ${K8S_NAMESPACE}
|
||||||
|
echo "Harbor Registry Secret 생성 완료"
|
||||||
|
else
|
||||||
|
echo "Harbor Registry Secret 이미 존재"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backend 배포
|
||||||
|
echo "Backend 배포..."
|
||||||
|
kubectl apply -f k8s/vexplor-backend-deployment.yaml -n ${K8S_NAMESPACE}
|
||||||
|
|
||||||
|
if kubectl get deployment ${BACKEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} > /dev/null 2>&1; then
|
||||||
|
echo "Backend 이미지 업데이트..."
|
||||||
|
kubectl set image deployment/${BACKEND_DEPLOYMENT_NAME} \
|
||||||
|
${BACKEND_CONTAINER_NAME}=${BACKEND_FULL_IMAGE_K8S}:latest \
|
||||||
|
-n ${K8S_NAMESPACE}
|
||||||
|
kubectl rollout restart deployment/${BACKEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE}
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Backend Rolling Update 진행 중..."
|
||||||
|
kubectl rollout status deployment/${BACKEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} --timeout=5m
|
||||||
|
echo "Backend 배포 완료"
|
||||||
|
|
||||||
|
# Frontend 배포
|
||||||
|
echo "Frontend 배포..."
|
||||||
|
kubectl apply -f k8s/vexplor-frontend-deployment.yaml -n ${K8S_NAMESPACE}
|
||||||
|
|
||||||
|
if kubectl get deployment ${FRONTEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} > /dev/null 2>&1; then
|
||||||
|
echo "Frontend 이미지 업데이트..."
|
||||||
|
kubectl set image deployment/${FRONTEND_DEPLOYMENT_NAME} \
|
||||||
|
${FRONTEND_CONTAINER_NAME}=${FRONTEND_FULL_IMAGE_K8S}:latest \
|
||||||
|
-n ${K8S_NAMESPACE}
|
||||||
|
kubectl rollout restart deployment/${FRONTEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE}
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Frontend Rolling Update 진행 중..."
|
||||||
|
kubectl rollout status deployment/${FRONTEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} --timeout=5m
|
||||||
|
echo "Frontend 배포 완료"
|
||||||
|
|
||||||
|
# Ingress 배포
|
||||||
|
echo "Ingress 배포..."
|
||||||
|
kubectl apply -f k8s/vexplor-ingress.yaml -n ${K8S_NAMESPACE}
|
||||||
|
|
||||||
|
echo "전체 배포 완료!"
|
||||||
|
|
||||||
|
# 배포 검증
|
||||||
|
- name: Verify deployment
|
||||||
|
run: |
|
||||||
|
echo "배포 검증..."
|
||||||
|
echo ""
|
||||||
|
echo "Backend 상태:"
|
||||||
|
kubectl get deployment ${BACKEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE}
|
||||||
|
kubectl get pods -l app=${BACKEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE}
|
||||||
|
echo ""
|
||||||
|
echo "Frontend 상태:"
|
||||||
|
kubectl get deployment ${FRONTEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE}
|
||||||
|
kubectl get pods -l app=${FRONTEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE}
|
||||||
|
echo ""
|
||||||
|
echo "Services:"
|
||||||
|
kubectl get svc -n ${K8S_NAMESPACE}
|
||||||
|
echo ""
|
||||||
|
echo "Ingress:"
|
||||||
|
kubectl get ingress -n ${K8S_NAMESPACE}
|
||||||
|
echo ""
|
||||||
|
echo "검증 완료"
|
||||||
|
|
||||||
|
# 배포 요약
|
||||||
|
- name: Deployment summary
|
||||||
|
if: success()
|
||||||
|
run: |
|
||||||
|
echo "=========================================="
|
||||||
|
echo "배포가 성공적으로 완료되었습니다!"
|
||||||
|
echo "=========================================="
|
||||||
|
echo "빌드 버전: ${IMAGE_TAG}"
|
||||||
|
echo "Frontend: https://v1.vexplor.com"
|
||||||
|
echo "Backend API: https://api.vexplor.com"
|
||||||
|
echo "=========================================="
|
||||||
|
|
||||||
|
# 실패 시 롤백
|
||||||
|
- name: Rollback on failure
|
||||||
|
if: failure()
|
||||||
|
run: |
|
||||||
|
echo "배포 실패! 이전 버전으로 롤백..."
|
||||||
|
kubectl rollout undo deployment/${BACKEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} || true
|
||||||
|
kubectl rollout undo deployment/${FRONTEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} || true
|
||||||
|
|
||||||
|
# Harbor 로그아웃
|
||||||
|
- name: Logout from Harbor
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
export DOCKER_HOST=unix:///var/run/docker.sock
|
||||||
|
docker logout ${HARBOR_REGISTRY} || true
|
||||||
|
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -225,6 +225,11 @@ secrets.yml
|
|||||||
api-keys.json
|
api-keys.json
|
||||||
tokens.json
|
tokens.json
|
||||||
|
|
||||||
|
# Kubernetes Secrets (절대 커밋하지 않음!)
|
||||||
|
k8s/vexplor-secret.yaml
|
||||||
|
k8s/*-secret.yaml
|
||||||
|
!k8s/*-secret.yaml.template
|
||||||
|
|
||||||
# 데이터베이스 덤프 파일
|
# 데이터베이스 덤프 파일
|
||||||
*.sql
|
*.sql
|
||||||
*.dump
|
*.dump
|
||||||
|
|||||||
375
docs/KUBERNETES_DEPLOYMENT_GUIDE.md
Normal file
375
docs/KUBERNETES_DEPLOYMENT_GUIDE.md
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
# vexplor 쿠버네티스 자동 배포 가이드
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
|
||||||
|
이 문서는 vexplor 프로젝트를 Gitea Actions를 통해 쿠버네티스 클러스터에 자동 배포하는 방법을 설명합니다.
|
||||||
|
|
||||||
|
**작성일**: 2024년 12월 22일
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 아키텍처
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Gitea Repository │
|
||||||
|
│ g.wace.me/chpark/vexplor │
|
||||||
|
└─────────────────────┬───────────────────────────────────────────┘
|
||||||
|
│ push to main
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Gitea Actions Runner │
|
||||||
|
│ 1. Checkout code │
|
||||||
|
│ 2. Build Docker images (frontend, backend) │
|
||||||
|
│ 3. Push to Harbor Registry │
|
||||||
|
│ 4. Deploy to Kubernetes │
|
||||||
|
└─────────────────────┬───────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌──────────┴──────────┐
|
||||||
|
▼ ▼
|
||||||
|
┌──────────────────┐ ┌──────────────────┐
|
||||||
|
│ Harbor Registry │ │ Kubernetes (K8s) │
|
||||||
|
│ harbor.wace.me │ │ 112.168.212.142 │
|
||||||
|
└──────────────────┘ └──────────────────┘
|
||||||
|
│
|
||||||
|
┌────────────────┼────────────────┐
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||||
|
│ Frontend │ │ Backend │ │ Ingress │
|
||||||
|
│ :3000 │ │ :3001 │ │ Nginx │
|
||||||
|
└──────────┘ └──────────┘ └──────────┘
|
||||||
|
│ │ │
|
||||||
|
└────────────────┴────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────┐
|
||||||
|
│ External Access │
|
||||||
|
│ v1.vexplor.com │
|
||||||
|
│ api.vexplor.com │
|
||||||
|
└─────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 사전 요구사항
|
||||||
|
|
||||||
|
### 1. 쿠버네티스 클러스터
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 서버 정보
|
||||||
|
IP: 112.168.212.142
|
||||||
|
SSH: ssh -p 22 wace@112.168.212.142
|
||||||
|
K8s 버전: v1.28.15
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Harbor 레지스트리 접근 권한
|
||||||
|
|
||||||
|
Harbor에 `vexplor` 프로젝트가 생성되어 있어야 합니다.
|
||||||
|
|
||||||
|
### 3. Gitea Repository Secrets
|
||||||
|
|
||||||
|
Gitea 저장소에 다음 Secrets를 설정해야 합니다:
|
||||||
|
|
||||||
|
| Secret 이름 | 설명 |
|
||||||
|
|------------|------|
|
||||||
|
| `HARBOR_USERNAME` | Harbor 사용자명 |
|
||||||
|
| `HARBOR_PASSWORD` | Harbor 비밀번호 |
|
||||||
|
| `KUBECONFIG` | base64 인코딩된 Kubernetes config |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 초기 설정
|
||||||
|
|
||||||
|
### 1단계: 쿠버네티스 클러스터 접속
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -p 22 wace@112.168.212.142
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2단계: Nginx Ingress Controller 설치
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Nginx Ingress Controller 설치 (baremetal용)
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.5/deploy/static/provider/baremetal/deploy.yaml
|
||||||
|
|
||||||
|
# 설치 확인
|
||||||
|
kubectl get pods -n ingress-nginx
|
||||||
|
kubectl get svc -n ingress-nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3단계: Local Path Provisioner 설치 (PVC용)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Local Path Provisioner 설치
|
||||||
|
kubectl apply -f k8s/local-path-provisioner.yaml
|
||||||
|
|
||||||
|
# 설치 확인
|
||||||
|
kubectl get pods -n local-path-storage
|
||||||
|
kubectl get storageclass
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4단계: Cert-Manager 설치 (SSL 인증서용)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Cert-Manager 설치
|
||||||
|
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.3/cert-manager.yaml
|
||||||
|
|
||||||
|
# 설치 확인
|
||||||
|
kubectl get pods -n cert-manager
|
||||||
|
|
||||||
|
# ClusterIssuer 생성 (Let's Encrypt)
|
||||||
|
cat <<EOF | kubectl apply -f -
|
||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: ClusterIssuer
|
||||||
|
metadata:
|
||||||
|
name: letsencrypt-prod
|
||||||
|
spec:
|
||||||
|
acme:
|
||||||
|
server: https://acme-v02.api.letsencrypt.org/directory
|
||||||
|
email: admin@vexplor.com
|
||||||
|
privateKeySecretRef:
|
||||||
|
name: letsencrypt-prod
|
||||||
|
solvers:
|
||||||
|
- http01:
|
||||||
|
ingress:
|
||||||
|
class: nginx
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5단계: vexplor Secret 생성
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Secret 템플릿을 복사하여 실제 값으로 수정
|
||||||
|
cp k8s/vexplor-secret.yaml.template k8s/vexplor-secret.yaml
|
||||||
|
|
||||||
|
# 값 수정 후 적용
|
||||||
|
kubectl apply -f k8s/vexplor-secret.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6단계: Gitea Secrets 설정
|
||||||
|
|
||||||
|
1. Gitea 저장소로 이동: https://g.wace.me/chpark/vexplor
|
||||||
|
2. Settings > Secrets > Actions 메뉴로 이동
|
||||||
|
3. 다음 Secrets 추가:
|
||||||
|
|
||||||
|
#### HARBOR_USERNAME
|
||||||
|
Harbor 로그인 사용자명
|
||||||
|
|
||||||
|
#### HARBOR_PASSWORD
|
||||||
|
Harbor 로그인 비밀번호
|
||||||
|
|
||||||
|
#### KUBECONFIG
|
||||||
|
```bash
|
||||||
|
# 쿠버네티스 서버에서 실행
|
||||||
|
cat ~/.kube/config | base64 -w 0
|
||||||
|
```
|
||||||
|
출력된 값을 KUBECONFIG secret으로 등록
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 배포 트리거
|
||||||
|
|
||||||
|
### 자동 배포 (Push)
|
||||||
|
|
||||||
|
다음 경로의 파일이 변경되어 `main` 브랜치에 push되면 자동으로 배포됩니다:
|
||||||
|
|
||||||
|
- `backend-node/**`
|
||||||
|
- `frontend/**`
|
||||||
|
- `docker/**`
|
||||||
|
- `k8s/**`
|
||||||
|
- `.gitea/workflows/deploy.yml`
|
||||||
|
|
||||||
|
### 수동 배포
|
||||||
|
|
||||||
|
1. Gitea 저장소 > Actions 탭으로 이동
|
||||||
|
2. "Deploy vexplor" 워크플로우 선택
|
||||||
|
3. "Run workflow" 버튼 클릭
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 파일 구조
|
||||||
|
|
||||||
|
```
|
||||||
|
vexplor/
|
||||||
|
├── .gitea/
|
||||||
|
│ └── workflows/
|
||||||
|
│ └── deploy.yml # Gitea Actions 워크플로우
|
||||||
|
├── docker/
|
||||||
|
│ └── deploy/
|
||||||
|
│ ├── backend.Dockerfile # 백엔드 배포용 Dockerfile
|
||||||
|
│ └── frontend.Dockerfile # 프론트엔드 배포용 Dockerfile
|
||||||
|
├── k8s/
|
||||||
|
│ ├── namespace.yaml # 네임스페이스 정의
|
||||||
|
│ ├── vexplor-config.yaml # ConfigMap
|
||||||
|
│ ├── vexplor-secret.yaml.template # Secret 템플릿
|
||||||
|
│ ├── vexplor-backend-deployment.yaml # 백엔드 Deployment/Service/PVC
|
||||||
|
│ ├── vexplor-frontend-deployment.yaml # 프론트엔드 Deployment/Service
|
||||||
|
│ ├── vexplor-ingress.yaml # Ingress 설정
|
||||||
|
│ ├── local-path-provisioner.yaml # 스토리지 프로비저너
|
||||||
|
│ └── ingress-nginx.yaml # Ingress 컨트롤러 패치
|
||||||
|
└── docs/
|
||||||
|
└── KUBERNETES_DEPLOYMENT_GUIDE.md # 이 문서
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 운영 명령어
|
||||||
|
|
||||||
|
### 상태 확인
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 전체 리소스 확인
|
||||||
|
kubectl get all -n vexplor
|
||||||
|
|
||||||
|
# Pod 상태 확인
|
||||||
|
kubectl get pods -n vexplor -o wide
|
||||||
|
|
||||||
|
# 로그 확인
|
||||||
|
kubectl logs -f deployment/vexplor-backend -n vexplor
|
||||||
|
kubectl logs -f deployment/vexplor-frontend -n vexplor
|
||||||
|
|
||||||
|
# Pod 상세 정보
|
||||||
|
kubectl describe pod <pod-name> -n vexplor
|
||||||
|
```
|
||||||
|
|
||||||
|
### 수동 배포/롤백
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 이미지 업데이트
|
||||||
|
kubectl set image deployment/vexplor-backend \
|
||||||
|
vexplor-backend=harbor.wace.me/vexplor/vexplor-backend:v20241222-120000-abc1234 \
|
||||||
|
-n vexplor
|
||||||
|
|
||||||
|
# 롤아웃 상태 확인
|
||||||
|
kubectl rollout status deployment/vexplor-backend -n vexplor
|
||||||
|
|
||||||
|
# 롤백
|
||||||
|
kubectl rollout undo deployment/vexplor-backend -n vexplor
|
||||||
|
kubectl rollout undo deployment/vexplor-frontend -n vexplor
|
||||||
|
|
||||||
|
# 히스토리 확인
|
||||||
|
kubectl rollout history deployment/vexplor-backend -n vexplor
|
||||||
|
```
|
||||||
|
|
||||||
|
### 스케일링
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 레플리카 수 조정
|
||||||
|
kubectl scale deployment/vexplor-backend --replicas=3 -n vexplor
|
||||||
|
kubectl scale deployment/vexplor-frontend --replicas=3 -n vexplor
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pod 재시작
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Deployment 재시작 (롤링 업데이트)
|
||||||
|
kubectl rollout restart deployment/vexplor-backend -n vexplor
|
||||||
|
kubectl rollout restart deployment/vexplor-frontend -n vexplor
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 문제 해결
|
||||||
|
|
||||||
|
### Pod이 Pending 상태일 때
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pod 이벤트 확인
|
||||||
|
kubectl describe pod <pod-name> -n vexplor
|
||||||
|
|
||||||
|
# 노드 리소스 확인
|
||||||
|
kubectl describe node
|
||||||
|
kubectl top nodes
|
||||||
|
```
|
||||||
|
|
||||||
|
### ImagePullBackOff 오류
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Harbor Secret 확인
|
||||||
|
kubectl get secret harbor-registry -n vexplor -o yaml
|
||||||
|
|
||||||
|
# Secret 재생성
|
||||||
|
kubectl delete secret harbor-registry -n vexplor
|
||||||
|
kubectl create secret docker-registry harbor-registry \
|
||||||
|
--docker-server=192.168.1.100:5001 \
|
||||||
|
--docker-username=<username> \
|
||||||
|
--docker-password=<password> \
|
||||||
|
-n vexplor
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ingress가 작동하지 않을 때
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ingress 상태 확인
|
||||||
|
kubectl get ingress -n vexplor
|
||||||
|
kubectl describe ingress vexplor-ingress -n vexplor
|
||||||
|
|
||||||
|
# Ingress Controller 로그
|
||||||
|
kubectl logs -f deployment/ingress-nginx-controller -n ingress-nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### SSL 인증서 문제
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Certificate 상태 확인
|
||||||
|
kubectl get certificate -n vexplor
|
||||||
|
kubectl describe certificate vexplor-tls -n vexplor
|
||||||
|
|
||||||
|
# Cert-Manager 로그
|
||||||
|
kubectl logs -f deployment/cert-manager -n cert-manager
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 네트워크 설정
|
||||||
|
|
||||||
|
### 방화벽 포트 개방
|
||||||
|
|
||||||
|
쿠버네티스 서버에서 다음 포트가 개방되어야 합니다:
|
||||||
|
|
||||||
|
| 포트 | 용도 |
|
||||||
|
|-----|------|
|
||||||
|
| 30080 | HTTP (Ingress NodePort) |
|
||||||
|
| 30443 | HTTPS (Ingress NodePort) |
|
||||||
|
| 6443 | Kubernetes API |
|
||||||
|
|
||||||
|
### DNS 설정
|
||||||
|
|
||||||
|
다음 도메인이 쿠버네티스 서버 IP를 가리키도록 설정:
|
||||||
|
|
||||||
|
- `v1.vexplor.com` → 112.168.212.142
|
||||||
|
- `api.vexplor.com` → 112.168.212.142
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 환경 변수
|
||||||
|
|
||||||
|
### Backend 환경 변수
|
||||||
|
|
||||||
|
| 변수 | 설명 | 소스 |
|
||||||
|
|-----|------|-----|
|
||||||
|
| `NODE_ENV` | 환경 (production) | ConfigMap |
|
||||||
|
| `PORT` | 서버 포트 (3001) | ConfigMap |
|
||||||
|
| `DATABASE_URL` | PostgreSQL 연결 문자열 | Secret |
|
||||||
|
| `JWT_SECRET` | JWT 서명 키 | Secret |
|
||||||
|
| `JWT_EXPIRES_IN` | JWT 만료 시간 | ConfigMap |
|
||||||
|
| `CORS_ORIGIN` | CORS 허용 도메인 | ConfigMap |
|
||||||
|
|
||||||
|
### Frontend 환경 변수
|
||||||
|
|
||||||
|
| 변수 | 설명 | 소스 |
|
||||||
|
|-----|------|-----|
|
||||||
|
| `NODE_ENV` | 환경 (production) | ConfigMap |
|
||||||
|
| `NEXT_PUBLIC_API_URL` | 클라이언트 API URL | ConfigMap |
|
||||||
|
| `SERVER_API_URL` | SSR용 내부 API URL | Deployment |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 참고 자료
|
||||||
|
|
||||||
|
- [Kubernetes 공식 문서](https://kubernetes.io/docs/)
|
||||||
|
- [Gitea Actions 문서](https://docs.gitea.com/usage/actions/overview)
|
||||||
|
- [Nginx Ingress Controller](https://kubernetes.github.io/ingress-nginx/)
|
||||||
|
- [Cert-Manager](https://cert-manager.io/docs/)
|
||||||
|
- [Harbor Registry](https://goharbor.io/docs/)
|
||||||
|
|
||||||
41
k8s/ingress-nginx.yaml
Normal file
41
k8s/ingress-nginx.yaml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Nginx Ingress Controller 설치
|
||||||
|
# 단일 노드 클러스터용 설정 (NodePort 사용)
|
||||||
|
#
|
||||||
|
# 설치 명령어:
|
||||||
|
# kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.5/deploy/static/provider/baremetal/deploy.yaml
|
||||||
|
#
|
||||||
|
# 또는 이 파일로 커스텀 설치:
|
||||||
|
# kubectl apply -f k8s/ingress-nginx.yaml
|
||||||
|
|
||||||
|
# NodePort를 80, 443으로 고정하는 패치용 설정
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: ingress-nginx-controller
|
||||||
|
namespace: ingress-nginx
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
spec:
|
||||||
|
type: NodePort
|
||||||
|
externalTrafficPolicy: Local
|
||||||
|
ipFamilyPolicy: SingleStack
|
||||||
|
ipFamilies:
|
||||||
|
- IPv4
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
protocol: TCP
|
||||||
|
targetPort: http
|
||||||
|
nodePort: 30080
|
||||||
|
- name: https
|
||||||
|
port: 443
|
||||||
|
protocol: TCP
|
||||||
|
targetPort: https
|
||||||
|
nodePort: 30443
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
|
||||||
135
k8s/local-path-provisioner.yaml
Normal file
135
k8s/local-path-provisioner.yaml
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
# Local Path Provisioner - 단일 노드 클러스터용 스토리지
|
||||||
|
# Rancher의 Local Path Provisioner 사용
|
||||||
|
# 참고: https://github.com/rancher/local-path-provisioner
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: local-path-storage
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner-service-account
|
||||||
|
namespace: local-path-storage
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner-role
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["nodes", "persistentvolumeclaims", "configmaps"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["endpoints", "persistentvolumes", "pods"]
|
||||||
|
verbs: ["*"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["events"]
|
||||||
|
verbs: ["create", "patch"]
|
||||||
|
- apiGroups: ["storage.k8s.io"]
|
||||||
|
resources: ["storageclasses"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner-bind
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: local-path-provisioner-role
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: local-path-provisioner-service-account
|
||||||
|
namespace: local-path-storage
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner
|
||||||
|
namespace: local-path-storage
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: local-path-provisioner
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: local-path-provisioner
|
||||||
|
spec:
|
||||||
|
serviceAccountName: local-path-provisioner-service-account
|
||||||
|
containers:
|
||||||
|
- name: local-path-provisioner
|
||||||
|
image: rancher/local-path-provisioner:v0.0.26
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command:
|
||||||
|
- local-path-provisioner
|
||||||
|
- --debug
|
||||||
|
- start
|
||||||
|
- --config
|
||||||
|
- /etc/config/config.json
|
||||||
|
volumeMounts:
|
||||||
|
- name: config-volume
|
||||||
|
mountPath: /etc/config/
|
||||||
|
env:
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
volumes:
|
||||||
|
- name: config-volume
|
||||||
|
configMap:
|
||||||
|
name: local-path-config
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: StorageClass
|
||||||
|
metadata:
|
||||||
|
name: local-path
|
||||||
|
annotations:
|
||||||
|
storageclass.kubernetes.io/is-default-class: "true"
|
||||||
|
provisioner: rancher.io/local-path
|
||||||
|
volumeBindingMode: WaitForFirstConsumer
|
||||||
|
reclaimPolicy: Delete
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: local-path-config
|
||||||
|
namespace: local-path-storage
|
||||||
|
data:
|
||||||
|
config.json: |-
|
||||||
|
{
|
||||||
|
"nodePathMap": [
|
||||||
|
{
|
||||||
|
"node": "DEFAULT_PATH_FOR_NON_LISTED_NODES",
|
||||||
|
"paths": ["/opt/local-path-provisioner"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
setup: |-
|
||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
mkdir -m 0777 -p "$VOL_DIR"
|
||||||
|
teardown: |-
|
||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
rm -rf "$VOL_DIR"
|
||||||
|
helperPod.yaml: |-
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: helper-pod
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: helper-pod
|
||||||
|
image: busybox:latest
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
|
||||||
9
k8s/namespace.yaml
Normal file
9
k8s/namespace.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# vexplor 네임스페이스
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: vexplor
|
||||||
|
labels:
|
||||||
|
name: vexplor
|
||||||
|
project: vexplor
|
||||||
|
|
||||||
133
k8s/vexplor-backend-deployment.yaml
Normal file
133
k8s/vexplor-backend-deployment.yaml
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# vexplor Backend Deployment
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: vexplor-backend
|
||||||
|
namespace: vexplor
|
||||||
|
labels:
|
||||||
|
app: vexplor-backend
|
||||||
|
component: backend
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: vexplor-backend
|
||||||
|
strategy:
|
||||||
|
type: RollingUpdate
|
||||||
|
rollingUpdate:
|
||||||
|
maxSurge: 1
|
||||||
|
maxUnavailable: 0
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: vexplor-backend
|
||||||
|
component: backend
|
||||||
|
spec:
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: harbor-registry
|
||||||
|
containers:
|
||||||
|
- name: vexplor-backend
|
||||||
|
image: harbor.wace.me/vexplor/vexplor-backend:latest
|
||||||
|
imagePullPolicy: Always
|
||||||
|
ports:
|
||||||
|
- containerPort: 3001
|
||||||
|
protocol: TCP
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: vexplor-config
|
||||||
|
- secretRef:
|
||||||
|
name: vexplor-secret
|
||||||
|
env:
|
||||||
|
- name: PORT
|
||||||
|
value: "3001"
|
||||||
|
- name: HOST
|
||||||
|
value: "0.0.0.0"
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
limits:
|
||||||
|
memory: "1Gi"
|
||||||
|
cpu: "500m"
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /api/health
|
||||||
|
port: 3001
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /api/health
|
||||||
|
port: 3001
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 3
|
||||||
|
volumeMounts:
|
||||||
|
- name: uploads
|
||||||
|
mountPath: /app/uploads
|
||||||
|
- name: data
|
||||||
|
mountPath: /app/data
|
||||||
|
- name: logs
|
||||||
|
mountPath: /app/logs
|
||||||
|
volumes:
|
||||||
|
- name: uploads
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: vexplor-backend-uploads-pvc
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: vexplor-backend-data-pvc
|
||||||
|
- name: logs
|
||||||
|
emptyDir: {}
|
||||||
|
|
||||||
|
---
|
||||||
|
# Backend Service
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: vexplor-backend-service
|
||||||
|
namespace: vexplor
|
||||||
|
labels:
|
||||||
|
app: vexplor-backend
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app: vexplor-backend
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 3001
|
||||||
|
targetPort: 3001
|
||||||
|
protocol: TCP
|
||||||
|
|
||||||
|
---
|
||||||
|
# Backend PVC - Uploads
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: vexplor-backend-uploads-pvc
|
||||||
|
namespace: vexplor
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Gi
|
||||||
|
storageClassName: local-path
|
||||||
|
|
||||||
|
---
|
||||||
|
# Backend PVC - Data
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: vexplor-backend-data-pvc
|
||||||
|
namespace: vexplor
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 5Gi
|
||||||
|
storageClassName: local-path
|
||||||
|
|
||||||
32
k8s/vexplor-config.yaml
Normal file
32
k8s/vexplor-config.yaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# vexplor ConfigMap - 환경 설정
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: vexplor-config
|
||||||
|
namespace: vexplor
|
||||||
|
labels:
|
||||||
|
app: vexplor
|
||||||
|
data:
|
||||||
|
# 공통 설정
|
||||||
|
NODE_ENV: "production"
|
||||||
|
TZ: "Asia/Seoul"
|
||||||
|
|
||||||
|
# Backend 설정
|
||||||
|
BACKEND_PORT: "3001"
|
||||||
|
BACKEND_HOST: "0.0.0.0"
|
||||||
|
JWT_EXPIRES_IN: "24h"
|
||||||
|
LOG_LEVEL: "info"
|
||||||
|
CORS_CREDENTIALS: "true"
|
||||||
|
|
||||||
|
# Frontend 설정
|
||||||
|
FRONTEND_PORT: "3000"
|
||||||
|
FRONTEND_HOSTNAME: "0.0.0.0"
|
||||||
|
NEXT_TELEMETRY_DISABLED: "1"
|
||||||
|
|
||||||
|
# 내부 서비스 URL (클러스터 내부 통신)
|
||||||
|
INTERNAL_BACKEND_URL: "http://vexplor-backend-service:3001"
|
||||||
|
|
||||||
|
# 외부 URL (클라이언트 접근용)
|
||||||
|
NEXT_PUBLIC_API_URL: "https://api.vexplor.com/api"
|
||||||
|
CORS_ORIGIN: "https://v1.vexplor.com,https://api.vexplor.com"
|
||||||
|
|
||||||
92
k8s/vexplor-frontend-deployment.yaml
Normal file
92
k8s/vexplor-frontend-deployment.yaml
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
# vexplor Frontend Deployment
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: vexplor-frontend
|
||||||
|
namespace: vexplor
|
||||||
|
labels:
|
||||||
|
app: vexplor-frontend
|
||||||
|
component: frontend
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: vexplor-frontend
|
||||||
|
strategy:
|
||||||
|
type: RollingUpdate
|
||||||
|
rollingUpdate:
|
||||||
|
maxSurge: 1
|
||||||
|
maxUnavailable: 0
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: vexplor-frontend
|
||||||
|
component: frontend
|
||||||
|
spec:
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: harbor-registry
|
||||||
|
containers:
|
||||||
|
- name: vexplor-frontend
|
||||||
|
image: harbor.wace.me/vexplor/vexplor-frontend:latest
|
||||||
|
imagePullPolicy: Always
|
||||||
|
ports:
|
||||||
|
- containerPort: 3000
|
||||||
|
protocol: TCP
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: vexplor-config
|
||||||
|
env:
|
||||||
|
- name: PORT
|
||||||
|
value: "3000"
|
||||||
|
- name: HOSTNAME
|
||||||
|
value: "0.0.0.0"
|
||||||
|
- name: NODE_ENV
|
||||||
|
value: "production"
|
||||||
|
- name: NEXT_PUBLIC_API_URL
|
||||||
|
value: "https://api.vexplor.com/api"
|
||||||
|
# 서버사이드 렌더링시 내부 백엔드 호출용
|
||||||
|
- name: SERVER_API_URL
|
||||||
|
value: "http://vexplor-backend-service:3001"
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
limits:
|
||||||
|
memory: "1Gi"
|
||||||
|
cpu: "500m"
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 3000
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 3000
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 3
|
||||||
|
|
||||||
|
---
|
||||||
|
# Frontend Service
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: vexplor-frontend-service
|
||||||
|
namespace: vexplor
|
||||||
|
labels:
|
||||||
|
app: vexplor-frontend
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app: vexplor-frontend
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 3000
|
||||||
|
targetPort: 3000
|
||||||
|
protocol: TCP
|
||||||
|
|
||||||
58
k8s/vexplor-ingress.yaml
Normal file
58
k8s/vexplor-ingress.yaml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# vexplor Ingress - Nginx Ingress Controller 기반
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: vexplor-ingress
|
||||||
|
namespace: vexplor
|
||||||
|
labels:
|
||||||
|
app: vexplor
|
||||||
|
annotations:
|
||||||
|
# Nginx Ingress Controller 설정
|
||||||
|
kubernetes.io/ingress.class: nginx
|
||||||
|
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
|
||||||
|
|
||||||
|
# WebSocket 지원
|
||||||
|
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
|
||||||
|
nginx.ingress.kubernetes.io/upstream-hash-by: "$remote_addr"
|
||||||
|
|
||||||
|
# SSL Redirect
|
||||||
|
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||||
|
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
|
||||||
|
|
||||||
|
# Cert-Manager (Let's Encrypt)
|
||||||
|
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||||
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- v1.vexplor.com
|
||||||
|
- api.vexplor.com
|
||||||
|
secretName: vexplor-tls
|
||||||
|
rules:
|
||||||
|
# Frontend 도메인
|
||||||
|
- host: v1.vexplor.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: vexplor-frontend-service
|
||||||
|
port:
|
||||||
|
number: 3000
|
||||||
|
|
||||||
|
# Backend API 도메인
|
||||||
|
- host: api.vexplor.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: vexplor-backend-service
|
||||||
|
port:
|
||||||
|
number: 3001
|
||||||
|
|
||||||
38
k8s/vexplor-secret.yaml.template
Normal file
38
k8s/vexplor-secret.yaml.template
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# vexplor Secret 템플릿
|
||||||
|
# 이 파일은 템플릿입니다. 실제 값으로 채운 후 vexplor-secret.yaml로 저장하세요.
|
||||||
|
# 주의: vexplor-secret.yaml은 .gitignore에 추가되어야 합니다!
|
||||||
|
#
|
||||||
|
# Secret 값은 base64로 인코딩해야 합니다:
|
||||||
|
# echo -n "your-value" | base64
|
||||||
|
#
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: vexplor-secret
|
||||||
|
namespace: vexplor
|
||||||
|
labels:
|
||||||
|
app: vexplor
|
||||||
|
type: Opaque
|
||||||
|
data:
|
||||||
|
# 데이터베이스 연결 정보 (base64 인코딩 필요)
|
||||||
|
# echo -n "postgresql://postgres:password@211.115.91.141:11134/plm" | base64
|
||||||
|
DATABASE_URL: "cG9zdGdyZXNxbDovL3Bvc3RncmVzOnZleHBsb3IwOTA5ISFAMjExLjExNS45MS4xNDE6MTExMzQvcGxt"
|
||||||
|
|
||||||
|
# JWT 시크릿
|
||||||
|
# echo -n "your-jwt-secret" | base64
|
||||||
|
JWT_SECRET: "aWxzaGluLXBsbS1zdXBlci1zZWNyZXQtand0LWtleS0yMDI0"
|
||||||
|
|
||||||
|
# 메일 암호화 키
|
||||||
|
# echo -n "your-encryption-key" | base64
|
||||||
|
ENCRYPTION_KEY: "aWxzaGluLXBsbS1tYWlsLWVuY3J5cHRpb24ta2V5LTMyY2hhcmFjdGVycy0yMDI0LXNlY3VyZQ=="
|
||||||
|
|
||||||
|
# API 키들
|
||||||
|
# echo -n "your-kma-api-key" | base64
|
||||||
|
KMA_API_KEY: "b2dkWHIyZTlUNGlIVjY5bnZWLUl3QQ=="
|
||||||
|
|
||||||
|
# echo -n "your-its-api-key" | base64
|
||||||
|
ITS_API_KEY: "ZDZiOWJlZmVjMzExNGQ2NDgyODQ2NzRiOGZkZGNjMzI="
|
||||||
|
|
||||||
|
# echo -n "your-expressway-api-key" | base64
|
||||||
|
EXPRESSWAY_API_KEY: ""
|
||||||
|
|
||||||
304
kubernetes-setup-guide.md
Normal file
304
kubernetes-setup-guide.md
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
# 쿠버네티스 클러스터 구축 가이드
|
||||||
|
|
||||||
|
## 📋 개요
|
||||||
|
|
||||||
|
이 문서는 Digital Twin 프로젝트의 쿠버네티스 클러스터 구축 과정을 정리한 가이드입니다.
|
||||||
|
|
||||||
|
**작성일**: 2024년 12월 22일
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🖥️ 서버 정보
|
||||||
|
|
||||||
|
### 기존 서버 (참조용)
|
||||||
|
|
||||||
|
| 항목 | 값 |
|
||||||
|
|------|-----|
|
||||||
|
| IP | 211.115.91.170 |
|
||||||
|
| SSH 포트 | 12991 |
|
||||||
|
| 사용자 | geonhee |
|
||||||
|
| OS | Ubuntu 24.04.3 LTS |
|
||||||
|
| K8s 버전 | v1.28.0 |
|
||||||
|
| 컨테이너 런타임 | containerd 1.7.28 |
|
||||||
|
|
||||||
|
### 새 서버 (구축 완료)
|
||||||
|
|
||||||
|
| 항목 | 값 |
|
||||||
|
|------|-----|
|
||||||
|
| IP | 112.168.212.142 |
|
||||||
|
| SSH 포트 | 22 |
|
||||||
|
| 사용자 | wace |
|
||||||
|
| 호스트명 | waceserver |
|
||||||
|
| OS | Ubuntu 24.04.3 LTS |
|
||||||
|
| K8s 버전 | v1.28.15 |
|
||||||
|
| 컨테이너 런타임 | containerd 1.7.28 |
|
||||||
|
| 내부 IP | 10.10.0.74 |
|
||||||
|
| CPU | 20코어 |
|
||||||
|
| 메모리 | 31GB |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 SSH 접속 설정
|
||||||
|
|
||||||
|
### SSH 키 기반 인증 설정
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 로컬에서 SSH 키 확인
|
||||||
|
ls -la ~/.ssh/
|
||||||
|
|
||||||
|
# 2. 공개키를 서버에 복사
|
||||||
|
ssh-copy-id -p 12991 geonhee@211.115.91.170 # 기존 서버
|
||||||
|
ssh-copy-id -p 22 wace@112.168.212.142 # 새 서버
|
||||||
|
|
||||||
|
# 3. 비밀번호 없이 접속 테스트
|
||||||
|
ssh -p 12991 geonhee@211.115.91.170
|
||||||
|
ssh -p 22 wace@112.168.212.142
|
||||||
|
```
|
||||||
|
|
||||||
|
### SSH Config 설정 (선택사항)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ~/.ssh/config 파일에 추가
|
||||||
|
Host wace-old
|
||||||
|
HostName 211.115.91.170
|
||||||
|
Port 12991
|
||||||
|
User geonhee
|
||||||
|
|
||||||
|
Host wace-new
|
||||||
|
HostName 112.168.212.142
|
||||||
|
Port 22
|
||||||
|
User wace
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 쿠버네티스 클러스터 구축 과정
|
||||||
|
|
||||||
|
### 1단계: Swap 비활성화
|
||||||
|
|
||||||
|
쿠버네티스는 swap이 활성화되어 있으면 제대로 동작하지 않습니다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# swap 비활성화
|
||||||
|
sudo swapoff -a
|
||||||
|
|
||||||
|
# 영구적으로 비활성화 (재부팅 후에도 유지)
|
||||||
|
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
|
||||||
|
|
||||||
|
# 확인 (아무것도 출력되지 않으면 성공)
|
||||||
|
swapon --show
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2단계: containerd 설정
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# containerd 기본 설정 생성
|
||||||
|
sudo containerd config default | sudo tee /etc/containerd/config.toml
|
||||||
|
|
||||||
|
# SystemdCgroup 활성화 (중요!)
|
||||||
|
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
|
||||||
|
|
||||||
|
# containerd 재시작
|
||||||
|
sudo systemctl restart containerd
|
||||||
|
|
||||||
|
# 상태 확인
|
||||||
|
sudo systemctl is-active containerd
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3단계: kubeadm init (클러스터 초기화)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
|
||||||
|
```
|
||||||
|
|
||||||
|
**출력 결과 (중요 정보)**:
|
||||||
|
- 클러스터 초기화 성공
|
||||||
|
- API 서버: https://10.10.0.74:6443
|
||||||
|
- 워커 노드 조인 토큰 생성됨
|
||||||
|
|
||||||
|
### 4단계: kubectl 설정
|
||||||
|
|
||||||
|
일반 사용자가 kubectl을 사용할 수 있도록 설정합니다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p $HOME/.kube
|
||||||
|
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
|
||||||
|
sudo chown $(id -u):$(id -g) $HOME/.kube/config
|
||||||
|
|
||||||
|
# 확인
|
||||||
|
kubectl cluster-info
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5단계: 네트워크 플러그인 설치 (Flannel)
|
||||||
|
|
||||||
|
Pod 간 통신을 위한 네트워크 플러그인을 설치합니다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6단계: 단일 노드 설정
|
||||||
|
|
||||||
|
마스터 노드에서도 워크로드를 실행할 수 있도록 taint를 제거합니다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 구축 결과
|
||||||
|
|
||||||
|
### 클러스터 상태
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get nodes -o wide
|
||||||
|
```
|
||||||
|
|
||||||
|
| NAME | STATUS | ROLES | VERSION | INTERNAL-IP | OS-IMAGE | CONTAINER-RUNTIME |
|
||||||
|
|------|--------|-------|---------|-------------|----------|-------------------|
|
||||||
|
| waceserver | Ready | control-plane | v1.28.15 | 10.10.0.74 | Ubuntu 24.04.3 LTS | containerd://1.7.28 |
|
||||||
|
|
||||||
|
### 시스템 Pod 상태
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get pods -n kube-system
|
||||||
|
kubectl get pods -n kube-flannel
|
||||||
|
```
|
||||||
|
|
||||||
|
| 컴포넌트 | 상태 |
|
||||||
|
|---------|------|
|
||||||
|
| etcd | ✅ Running |
|
||||||
|
| kube-apiserver | ✅ Running |
|
||||||
|
| kube-controller-manager | ✅ Running |
|
||||||
|
| kube-scheduler | ✅ Running |
|
||||||
|
| kube-proxy | ✅ Running |
|
||||||
|
| coredns (x2) | ✅ Running |
|
||||||
|
| kube-flannel | ✅ Running |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 워커 노드 추가 (필요 시)
|
||||||
|
|
||||||
|
다른 서버를 워커 노드로 추가하려면:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubeadm join 10.10.0.74:6443 --token 4lfga6.luad9f367uxh0rlq \
|
||||||
|
--discovery-token-ca-cert-hash sha256:9bea59b6fd34115c3f893a4b10bacc0a5409192b288564dc055251210081c86e
|
||||||
|
```
|
||||||
|
|
||||||
|
**토큰 만료 시 새 토큰 생성**:
|
||||||
|
```bash
|
||||||
|
kubeadm token create --print-join-command
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 유용한 명령어
|
||||||
|
|
||||||
|
### 클러스터 정보 확인
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 노드 상태
|
||||||
|
kubectl get nodes -o wide
|
||||||
|
|
||||||
|
# 모든 Pod 상태
|
||||||
|
kubectl get pods -A
|
||||||
|
|
||||||
|
# 클러스터 정보
|
||||||
|
kubectl cluster-info
|
||||||
|
|
||||||
|
# 컴포넌트 상태
|
||||||
|
kubectl get componentstatuses
|
||||||
|
```
|
||||||
|
|
||||||
|
### 문제 해결
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# kubelet 로그 확인
|
||||||
|
sudo journalctl -u kubelet -f
|
||||||
|
|
||||||
|
# containerd 로그 확인
|
||||||
|
sudo journalctl -u containerd -f
|
||||||
|
|
||||||
|
# Pod 상세 정보
|
||||||
|
kubectl describe pod <pod-name> -n <namespace>
|
||||||
|
|
||||||
|
# Pod 로그 확인
|
||||||
|
kubectl logs <pod-name> -n <namespace>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 클러스터 리셋 (초기화 실패 시)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo kubeadm reset
|
||||||
|
sudo rm -rf /etc/cni/net.d
|
||||||
|
sudo rm -rf $HOME/.kube
|
||||||
|
sudo iptables -F && sudo iptables -t nat -F && sudo iptables -t mangle -F && sudo iptables -X
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📂 다음 단계: 자동 배포 설정
|
||||||
|
|
||||||
|
쿠버네티스 클러스터 구축이 완료되었습니다. 다음 단계로 진행할 사항:
|
||||||
|
|
||||||
|
1. **Ingress Controller 설치** (외부 트래픽 라우팅) ✅ 완료
|
||||||
|
2. **Cert-Manager 설치** (SSL 인증서 자동 관리)
|
||||||
|
3. **Harbor/Registry 연동** (컨테이너 이미지 저장소)
|
||||||
|
4. **CI/CD 파이프라인 구성** (Gitea Actions) ✅ 완료
|
||||||
|
5. **Helm 설치** (패키지 관리)
|
||||||
|
6. **애플리케이션 배포** (Deployment, Service, Ingress) ✅ 완료
|
||||||
|
|
||||||
|
### Gitea Actions 자동 배포 설정 완료
|
||||||
|
|
||||||
|
자세한 설정 방법은 [KUBERNETES_DEPLOYMENT_GUIDE.md](docs/KUBERNETES_DEPLOYMENT_GUIDE.md) 참조
|
||||||
|
|
||||||
|
#### 생성된 파일 목록
|
||||||
|
|
||||||
|
```
|
||||||
|
.gitea/workflows/deploy.yml # Gitea Actions 워크플로우
|
||||||
|
k8s/
|
||||||
|
├── namespace.yaml # 네임스페이스 정의
|
||||||
|
├── vexplor-config.yaml # ConfigMap
|
||||||
|
├── vexplor-secret.yaml.template # Secret 템플릿
|
||||||
|
├── vexplor-backend-deployment.yaml # 백엔드 Deployment/Service/PVC
|
||||||
|
├── vexplor-frontend-deployment.yaml# 프론트엔드 Deployment/Service
|
||||||
|
├── vexplor-ingress.yaml # Ingress 설정
|
||||||
|
├── local-path-provisioner.yaml # 스토리지 프로비저너
|
||||||
|
└── ingress-nginx.yaml # Ingress 컨트롤러 패치
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Gitea Repository Secrets 설정 필요
|
||||||
|
|
||||||
|
| Secret 이름 | 설명 |
|
||||||
|
|------------|------|
|
||||||
|
| `HARBOR_USERNAME` | Harbor 사용자명 |
|
||||||
|
| `HARBOR_PASSWORD` | Harbor 비밀번호 |
|
||||||
|
| `KUBECONFIG` | base64 인코딩된 Kubernetes config |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# KUBECONFIG 생성 방법 (K8s 서버에서 실행)
|
||||||
|
cat ~/.kube/config | base64 -w 0
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 참고 정보
|
||||||
|
|
||||||
|
### 서버 접속
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 새 서버 (쿠버네티스 클러스터)
|
||||||
|
ssh -p 22 wace@112.168.212.142
|
||||||
|
|
||||||
|
# 기존 서버 (참조용)
|
||||||
|
ssh -p 12991 geonhee@211.115.91.170
|
||||||
|
```
|
||||||
|
|
||||||
|
### 관련 문서
|
||||||
|
|
||||||
|
- [Kubernetes 공식 문서](https://kubernetes.io/docs/)
|
||||||
|
- [kubeadm 설치 가이드](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/)
|
||||||
|
- [Flannel 네트워크 플러그인](https://github.com/flannel-io/flannel)
|
||||||
|
|
||||||
Reference in New Issue
Block a user