본문으로 건너뛰기

Day 9 - K8s 배포 및 Ingress 구조 개선 (01/05)

GitLab Registry 연동, Ingress 기반 라우팅, GitHub OAuth 설정

목차

  1. 📋 개요
  2. 🏗️ 아키텍처
  3. 🐳 Registry 접근 테스트
  4. 🔄 CI/CD 파이프라인
  5. ☸️ K8s 배포
  6. 🌐 브라우저 접속
  7. 🔧 트러블슈팅
  8. 📚 참고

📋 개요

오늘 작업 내용

작업상태
GitLab Registry 연결 테스트
CI/CD 파이프라인 실행
containerd insecure registry 설정
외부 MySQL 연결 (Endpoints)
Ingress 기반 라우팅 구조 변경
ConfigMap으로 nginx 설정 분리
NodePort 30080 고정
GitHub OAuth 로그인 구현
브라우저 접속 확인
NFS 공유 저장소 (PVC) 구성
MySQL Master-Slave Replication 자동화
Branch Notes 기능 정상화

🏗️ 아키텍처

외부 인터넷 (gition.local)
│ SSH Tunnel (8080 -> 80)
▼ │
┌────────────────────────────────────────────────────────┐
│ Ingress Controller Service (NodePort) │
│ NodePort: 30080 │
│ ExternalIP: 172.100.100.20 │
└────────────────────────────────────────────────────────┘


┌────────────────────────────────────────────────────────┐
│ Ingress (gition-ingress) │
└────────────────────────────────────────────────────────┘
│ │ │ /api/* │ │ │ /* (catch-all)
/auth/github │ │ │ /auth/callback
▼ │ ▼ ┌───────────────────────┐
┌───────────────────────┐ │ frontend-svc │
│ api-svc │ │ :80 │
│ :3001 │ └───────────────────────┘
└───────────────────────┘ │ │ │
│ │ │ │ │ │
┌───────────────────────┐ │ Frontend │
│ API (FastAPI) │ │(nginx+React)│
│ │ └───────────────────────┘
└───────────────────────┘


┌───────────────────────────────┐
│ mysql-master (외부 VM) │
│ 172.100.100.11:3306 │
│ (ClusterIP + Endpoints) │
└───────────────────────────────┘

🐳 Registry 접근 테스트

docker login 172.100.100.8:5050
docker pull 172.100.100.8:5050/root/gition/backend:latest
docker pull 172.100.100.8:5050/root/gition/frontend:latest

🔄 CI/CD 파이프라인

GitLab Runner 설정

# /etc/gitlab-runner/config.toml
[[runners]]
name = "docker-runner"
url = "http://172.100.100.8"
clone_url = "http://172.100.100.8"

[runners.docker]
network_mode = "host"
privileged = true
volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]

BuildKit Provenance 비활성화

build-backend:
script:
- docker build --provenance=false -t $CI_REGISTRY_IMAGE/backend:latest ./backend

☸️ K8s 배포

배포 파일 구조

파일역할
frontend.yamlFrontend + ConfigMap + Service
fastapi-deployment.yamlBackend API + Service + OAuth 환경변수
ingress.yaml라우팅 규칙 (/auth/github 등 API, /* 등 Frontend)
ingress-nginx-svc.yamlNodePort 30080 고정
mysql-master-svc.yaml외부 MySQL 연결

Secret 생성

# GitHub OAuth
kubectl create secret generic github-secret \
--from-literal=client-id='<GITHUB_CLIENT_ID>' \
--from-literal=client-secret='<GITHUB_CLIENT_SECRET>' \
-n gition

# MySQL
kubectl create secret generic mysql-secret \
--from-literal=user-password='pista' \
-n gition

배포 순서

# 1. Ingress Controller NodePort 고정
kubectl apply -f ingress-nginx-svc.yaml

# 2. 앱 배포
kubectl apply -f frontend.yaml
kubectl apply -f fastapi-deployment.yaml
kubectl apply -f mysql-master-svc.yaml

# 3. Ingress 배포
kubectl apply -f ingress.yaml

# 4. 확인
kubectl get pods,svc -n gition
kubectl get ingress -n gition

🌐 브라우저 접속

1. Windows hosts 파일 수정 (관리자 권한)

# C:\Windows\System32\drivers\etc\hosts
127.0.0.1 gition.local

2. SSH 터널 (Bastion 경유)

ssh -L 8080:172.100.100.20:80 -J pista@192.168.5.9 pista@172.100.100.12

3. 브라우저 접속

http://gition.local:8080

GitHub OAuth 설정

GitHub Developer Settings에서 OAuth App 설정:

  • Homepage URL: http://gition.local:8080
  • Callback URL: http://gition.local:8080/auth/github/callback

🔧 트러블슈팅

1. Runner에서 GitLab 접근 불가

에러: Could not connect to server

해결: config.tomlclone_url, network_mode = "host" 추가


2. K8s에서 HTTP Registry 접근 불가

에러: http: server gave HTTP response to HTTPS client

해결: containerd 설정

sudo mkdir -p /etc/containerd/certs.d/172.100.100.8:5050
sudo tee /etc/containerd/certs.d/172.100.100.8:5050/hosts.toml > /dev/null <<EOF
server = "http://172.100.100.8:5050"

[host."http://172.100.100.8:5050"]
capabilities = ["pull", "resolve", "push"]
skip_verify = true
EOF
sudo systemctl restart containerd

3. ExternalName 서비스 DNS 해석 불가

원인: ExternalName은 IP 주소를 지원하지 않음 해결: ClusterIP + Endpoints 사용 (mysql-master-svc.yaml)


4. nginx upstream "auth-backend" not found

원인: 기존 nginx가 backend 목록을 설정 못함

해결:

  • Ingress가 라우팅 담당
  • ConfigMap으로 nginx 설정 오버라이드 (정적 파일만 서빙)

5. Pod에서 외부 MySQL 연결 Timeout

에러: Can't connect to MySQL server on 'mysql-master'

원인: Docker 컨테이너와 iptables 규칙 꼬임 (UFW와 충돌)

해결:

# MySQL VM (172.100.100.11)에서
sudo ufw disable
sudo systemctl restart docker

6. GitHub OAuth redirect_uri 불일치

에러: The redirect_uri is not associated with this application

원인: SSH 터널 포트(8080)와 GitHub App 설정(80) 불일치 해결: GitHub OAuth App 설정에서 Callback URL을 SSH 터널 포트와 일치시킴

  • http://gition.local:8080/auth/github/callback

7. OAuth 콜백 후 Not Found (404)

에러: http://gition.local/auth/callback?user=... 에서 404

원인: Ingress가 /auth/*를 모두 백엔드로 라우팅 해결: ingress.yaml에서 /auth 대신 /auth/github로 변경

  • /auth/github/* 은 백엔드 (OAuth 처리)
  • /auth/callback 은 프론트엔드 (catch-all로 전달)

📚 참고


8. Git History Secret Leakage (Secret Scrubbing)

문제: .env.example 및 K8s Manifest 파일에 하드코딩된 비밀번호(***REMOVED***)가 Git History에 남아있음 (GitGuardian 경고).

해결: BFG Repo-Cleaner를 사용하여 히스토리 전체에서 비밀번호 제거.

# 1. BFG로 히스토리 정제 (***REMOVED*** -> <REDACTED_PASSWORD>)
java -jar bfg.jar --replace-text replacements.txt .

# 2. 잔여 객체 정리 및 강제 푸시
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push --force

9. File Encoding Issues (Mojibake)

문제: Windows(EUC-KR)와 Linux/Git(UTF-8) 환경 차이로 인해 한글 주석이 깨짐 (??? 등으로 표시).

해결: 깨진 파일을 식별하고 UTF-8 인코딩으로 내용을 복원하여 재작성.

  • 대상 파일: k8s/frontend.yaml, k8s/ingress.yaml, k8s/nfs-provisioner.yaml 및 Markdown 문서들.
  • 조치: 원래의 한글 주석 내용을 유추하여 복구 완료.

10. Repository not cloned (저장소 불일치)

에러:

Repository not cloned

원인: replicas: 3 설정으로 백엔드 Pod 3개가 각자 로컬 볼륨 사용 → Clone된 저장소가 공유되지 않음

해결: NFS 기반 PVC 생성 및 볼륨 마운트 추가

# fastapi-deployment.yaml
spec:
template:
spec:
containers:
- name: api
volumeMounts:
- name: repos-volume
mountPath: /repos
volumes:
- name: repos-volume
persistentVolumeClaim:
claimName: repos-pvc
kubectl apply -f fastapi-deployment.yaml

11. 🐢 Failed to load page (DB 복제 실패)

에러:

Replica_IO_Running: Connecting
Last_IO_Error: error connecting to master 'repl_pista@172.100.100.11:3306'

원인: Master DB에 복제용 계정(repl_pista)이 존재하지 않음

해결:

  1. Master에 복제 계정 생성:
-- docker/mysql-master/initdb.d/replication.sql
CREATE USER IF NOT EXISTS 'repl_pista'@'%' IDENTIFIED BY '<REPL_PASSWORD>';
GRANT REPLICATION SLAVE ON *.* TO 'repl_pista'@'%';
FLUSH PRIVILEGES;
  1. Slave에 자동 복제 Sidecar 추가 (mysql-slave.yaml)
# 적용
docker exec mysql-master bash -c "mysql -uroot -p\$MYSQL_ROOT_PASSWORD < /docker-entrypoint-initdb.d/replication.sql"
kubectl delete pod -n gition -l app=mysql,role=slave

12. 🕳️ Schema Missing (텅 빈 DB)

에러:

mysql> SHOW TABLES;
-- Empty set (테이블 없음)

원인: GTID 복제는 연결 시점 이후의 변경만 동기화 → 기존 테이블/데이터 미전송

해결: mysqldump로 Master 스키마를 Slave에 수동 적용

kubectl exec -it mysql-slave-0 -n gition -c mysql -- bash -c \
"mysqldump -h 172.100.100.11 -uroot -p<PASSWORD> \
--single-transaction --set-gtid-purged=OFF gition \
| mysql -uroot -p<PASSWORD> gition"

검증:

kubectl exec mysql-slave-0 -n gition -c mysql -- \
mysql -uroot -p<PASSWORD> -e "USE gition; SHOW TABLES;"
# 결과: users, repositories, sessions, documents, branch_pages, pipelines

13. 🌐 DNS Crash (이름 해석 불가)

에러:

RuntimeError: Read database pool not initialized. Call init_pool() first.
mysqldump: Got error: 2005: Unknown MySQL server host 'mysql-master' (-2)

원인: K8s 내부 DNS가 mysql-master 서비스명을 IP로 변환 실패 → 앱 크래시

해결: fastapi-deployment.yaml에서 호스트명 대신 IP 직접 지정

# 변경 전
- name: MYSQL_READ_HOST
value: "mysql-read"
- name: MYSQL_WRITE_HOST
value: "mysql-master"

# 변경 후
- name: MYSQL_READ_HOST
value: "172.100.100.11"
- name: MYSQL_WRITE_HOST
value: "172.100.100.11"
kubectl apply -f fastapi-deployment.yaml
kubectl delete pod -n gition -l app=api

✅ 최종 상태

항목상태
Branch Notes 로딩✅ 정상
MySQL ReplicationReplica_IO_Running: Yes
공유 저장소 (PVC)✅ 3개 Pod 동일 데이터
DB 연결✅ IP 직접 연결

관련 문서

그래프 뷰

그래프 데이터 로딩 중…
현재: Day 9 - K8s 배포 및 Ingress 구조 개선 (01/05)
노드 클릭 → 이동 · 드래그 → 위치 조정