Files
nginx-portal/README.md
qorgh529 b80c379310
Some checks failed
Build and Push Images / build-backend (push) Has been cancelled
docs: README 업데이트
2026-04-06 23:48:26 +09:00

12 KiB
Executable File

Web Portal - Kubernetes + GitOps 배포 가이드

📋 프로젝트 개요

조직 내부 웹페이지를 통합 관리하는 포털 시스템입니다. 로그인 후 본인에게 할당된 웹페이지 목록을 확인하고 접속할 수 있으며, 관리자는 페이지 및 사용자 권한을 관리할 수 있습니다.


🏗️ 전체 아키텍처

개발자 (git push)
    ↓
Gitea (192.168.10.101:30000)
  └── Container Registry (이미지 저장)
    ↓
ArgoCD 자동 감지 & 배포 (192.168.10.101:30080)
    ↓
Kubernetes - web-portal 네임스페이스
  ├── Nginx Frontend  (NodePort: 30090)
  ├── FastAPI Backend (ClusterIP: 8000)
  └── PostgreSQL DB   (ClusterIP: 5432)

🛠️ 기술 스택

구분 기술
Frontend Nginx + HTML/CSS/JS (SPA)
Backend Python FastAPI
Database PostgreSQL
Container Docker Desktop
Orchestration Kubernetes (Docker Desktop 내장)
GitOps Gitea + ArgoCD
Image Registry Gitea Container Registry

📁 프로젝트 구조

nginx-portal/
├── .gitea/
│   └── workflows/
│       └── build-and-push.yaml  # Gitea Actions CI (선택사항)
├── backend/
│   ├── main.py                  # FastAPI 전체 API 로직
│   ├── requirements.txt
│   └── Dockerfile
├── frontend/
│   ├── index.html               # 싱글 페이지 앱 (SPA)
│   ├── nginx.conf               # Nginx 설정 + /api/* 프록시
│   └── Dockerfile
├── k8s/
│   ├── 01-namespace.yaml        # web-portal 네임스페이스
│   ├── 02-postgres.yaml         # PostgreSQL + PVC + Service
│   ├── 03-secrets.yaml          # DB/JWT 시크릿
│   ├── 04-backend.yaml          # FastAPI Deployment + Service
│   └── 05-frontend.yaml         # Nginx Deployment + NodePort(30090)
├── 06-argocd-app.yaml           # ArgoCD Application 정의 (k8s 폴더 밖에 위치)
└── README.md

⚠️ 06-argocd-app.yaml 은 반드시 k8s/ 폴더 에 위치해야 합니다. ArgoCD가 k8s/ 폴더를 감시하므로 해당 파일이 안에 있으면 순환 참조 문제가 발생합니다.


🔑 기본 계정

구분 ID Password
관리자 admin admin1234
일반사용자 user1 user1234

기능 설명

일반 사용자

  • 로그인 후 본인에게 할당된 웹페이지 목록 카드 형태로 확인
  • 카드 클릭 시 새 탭에서 해당 URL로 이동

관리자

일반 사용자 기능 + 추가:

  • 페이지 관리: 웹페이지 추가 / 수정 / 삭제
  • 사용자 관리: 계정 생성 / 삭제
  • 권한 설정: 사용자별 접근 가능 페이지를 체크박스로 지정

🚀 최초 배포 순서

1단계. Docker Desktop insecure-registry 설정

Gitea Registry가 HTTP이므로 Docker Desktop에서 허용 설정 필요.

Docker Desktop → Settings → Docker Engine:

{
  "builder": {
    "gc": {
      "defaultKeepStorage": "20GB",
      "enabled": true
    }
  },
  "experimental": false,
  "insecure-registries": ["192.168.10.101:30000"]
}

Apply & Restart 클릭

2단계. Gitea Registry 로그인 및 이미지 빌드 & Push

# Registry 로그인
docker login 192.168.10.101:30000 -u <Gitea계정>

# 백엔드 이미지 빌드 & Push
docker build -t 192.168.10.101:30000/<계정>/portal-backend:latest ./backend/
docker push 192.168.10.101:30000/<계정>/portal-backend:latest

# 프론트엔드 이미지 빌드 & Push
docker build -t 192.168.10.101:30000/<계정>/portal-frontend:latest ./frontend/
docker push 192.168.10.101:30000/<계정>/portal-frontend:latest

3단계. K8s Registry Secret 생성

kubectl create namespace web-portal

kubectl create secret docker-registry gitea-registry-secret \
  --namespace=web-portal \
  --docker-server=gitea-http.gitea.svc.cluster.local:3000 \
  --docker-username=<Gitea계정> \
  --docker-password=<Gitea패스워드>

4단계. ArgoCD에 Gitea 저장소 인증 등록

kubectl create secret generic gitea-repo-secret \
  --namespace=argocd \
  --from-literal=type=git \
  --from-literal=url=http://192.168.10.101:30000/<계정>/nginx-portal.git \
  --from-literal=username=<Gitea계정> \
  --from-literal=password=<Gitea패스워드>

kubectl label secret gitea-repo-secret \
  -n argocd \
  argocd.argoproj.io/secret-type=repository

5단계. ArgoCD Application 등록

kubectl apply -f 06-argocd-app.yaml

6단계. 접속 확인

http://192.168.10.101:30090

🔄 이후 배포 방법 (코드 수정 시)

yaml만 변경한 경우 (설정 변경)

git add .
git commit -m "fix: 변경내용"
git push origin main
# → ArgoCD가 자동으로 감지해서 재배포

이미지도 변경한 경우 (코드 변경)

# 이미지 재빌드 & Push
docker build -t 192.168.10.101:30000/<계정>/portal-backend:latest ./backend/
docker push 192.168.10.101:30000/<계정>/portal-backend:latest

# Pod 재시작
kubectl rollout restart deployment/backend -n web-portal

# yaml도 변경했다면
git add .
git commit -m "feat: 변경내용"
git push origin main

🔧 운영 명령어

# 전체 리소스 상태 확인
kubectl get all -n web-portal

# 백엔드 로그 확인
kubectl logs -n web-portal deployment/backend -f

# 프론트엔드 로그 확인
kubectl logs -n web-portal deployment/frontend -f

# Pod 재시작
kubectl rollout restart deployment/backend -n web-portal
kubectl rollout restart deployment/frontend -n web-portal

# 전체 삭제
kubectl delete namespace web-portal

🔐 운영 시 보안 설정

k8s/03-secrets.yaml 에서 반드시 변경:

stringData:
  db-password: "강력한패스워드로변경"
  jwt-secret: "64자이상의랜덤문자열로변경"

트러블슈팅

1. NodePort 포트 충돌

증상

Service "frontend-service" is invalid: spec.ports[0].nodePort:
Invalid value: 30080: provided port is already allocated

원인 해당 NodePort가 다른 서비스에서 이미 사용 중.

해결 k8s/05-frontend.yaml 에서 nodePort를 다른 번호로 변경 (30000~32767 범위):

nodePort: 30090

변경 후 기존 서비스 삭제 및 재적용:

kubectl delete service frontend-service -n web-portal
kubectl apply -f k8s/05-frontend.yaml

2. Backend Liveness Probe 실패로 인한 CrashLoopBackOff

증상

Liveness probe failed: HTTP probe failed with statuscode: 404
Back-off restarting failed container

원인 Backend가 DB 연결을 기다리는 동안 liveness probe가 먼저 실패해서 K8s가 강제 재시작.

해결 k8s/04-backend.yaml 의 probe 대기 시간 증가:

readinessProbe:
  initialDelaySeconds: 20
  periodSeconds: 5
  failureThreshold: 6
livenessProbe:
  initialDelaySeconds: 60
  periodSeconds: 15
  failureThreshold: 5

3. Nginx가 API 요청을 백엔드로 프록시 못함

증상 로그인 시 Unexpected token '<', "<html>..." is not valid JSON 에러.

원인 Nginx가 /api/ 요청을 백엔드로 전달하지 못하고 HTML을 반환.

해결 frontend/nginx.conf 에서 백엔드 주소를 FQDN으로 변경:

location /api/ {
    proxy_pass http://backend-service.web-portal.svc.cluster.local:8000/api/;
}

4. ArgoCD - authentication required: Unauthorized

증상

failed to list refs: authentication required: Unauthorized

원인 ArgoCD가 Gitea 저장소에 접근할 인증 정보가 없음.

해결 kubectl로 직접 인증 Secret 생성:

kubectl create secret generic gitea-repo-secret \
  --namespace=argocd \
  --from-literal=type=git \
  --from-literal=url=http://192.168.10.101:30000/<계정>/nginx-portal.git \
  --from-literal=username=<계정> \
  --from-literal=password=<패스워드>

kubectl label secret gitea-repo-secret \
  -n argocd \
  argocd.argoproj.io/secret-type=repository

5. RepeatedResourceWarning - 리소스 중복

증상

Resource apps/Deployment/web-portal/backend appeared 2 times among application resources

원인 k8s/ 폴더 안에 동일한 리소스를 정의하는 yaml 파일이 중복 존재 (portal.yaml 등).

해결 중복 파일 삭제 후 push:

rm k8s/portal.yaml
git add .
git commit -m "fix: 중복 yaml 파일 제거"
git push origin main

6. ImagePullBackOff - Gitea Registry HTTP 접근 오류

증상

Failed to pull image: server gave HTTP response to HTTPS client

원인 Gitea Registry가 HTTP인데 Docker가 HTTPS로 접근 시도.

해결 Docker Desktop → Settings → Docker Engine에 insecure-registry 추가:

{
  "insecure-registries": ["192.168.10.101:30000"]
}

Apply & Restart 후 이미지 재빌드 & Push.


7. Gitea Container Registry 로그인 실패 (context deadline exceeded)

증상

Error response from daemon: Get "http://192.168.10.101:30000/v2/":
context deadline exceeded

원인 Gitea의 ROOT_URL이 localhost 로 설정되어 있어 token 요청이 외부로 나가지 못함. 또한 Packages(Container Registry) 기능이 비활성화된 상태.

해결 Helm upgrade로 Gitea 설정 영구 변경:

helm repo add gitea https://dl.gitea.com/charts/
helm repo update

helm upgrade gitea gitea/gitea -n gitea \
  --set gitea.config.server.DOMAIN=192.168.10.101 \
  --set gitea.config.server.ROOT_URL=http://192.168.10.101:30000 \
  --set gitea.config.server.HTTP_PORT=3000 \
  --set gitea.config.packages.ENABLED=true \
  --set service.http.type=NodePort \
  --set service.http.nodePort=30000 \
  --reuse-values

8. K8s 내부에서 Gitea Registry 이미지 Pull 실패

증상 Pod가 ImagePullBackOff 상태. 외부에서는 push가 되지만 K8s Pod는 이미지를 못 가져옴.

원인 K8s Pod는 외부 IP(192.168.10.101:30000)로 접근이 불안정하므로 내부 서비스명으로 접근해야 함.

해결 yaml의 image 주소를 K8s 내부 서비스명으로 변경:

sed -i "s|192.168.10.101:30000|gitea-http.gitea.svc.cluster.local:3000|g" k8s/04-backend.yaml
sed -i "s|192.168.10.101:30000|gitea-http.gitea.svc.cluster.local:3000|g" k8s/05-frontend.yaml

Registry Secret도 내부 주소로 재생성:

kubectl delete secret gitea-registry-secret -n web-portal

kubectl create secret docker-registry gitea-registry-secret \
  --namespace=web-portal \
  --docker-server=gitea-http.gitea.svc.cluster.local:3000 \
  --docker-username=<계정> \
  --docker-password=<패스워드>

9. 로그인 실패 - 비밀번호 해시 오염

증상 올바른 계정으로 로그인해도 아이디 또는 비밀번호가 올바르지 않습니다 출력.

원인 터미널 색상 코드(\x1B[0m 등)가 비밀번호 해시에 섞여 DB에 저장됨.

해결 Backend Pod에서 직접 Python으로 해시 재생성 후 DB 업데이트:

kubectl exec -n web-portal deployment/backend -- python3 -c "
import bcrypt, psycopg2
conn = psycopg2.connect(host='postgres-service', database='portaldb', user='portaluser', password='portalpass')
cur = conn.cursor()
h1 = bcrypt.hashpw('admin1234'.encode(), bcrypt.gensalt()).decode()
h2 = bcrypt.hashpw('user1234'.encode(), bcrypt.gensalt()).decode()
cur.execute('UPDATE users SET password_hash=%s WHERE username=%s', (h1, 'admin'))
cur.execute('UPDATE users SET password_hash=%s WHERE username=%s', (h2, 'user1'))
conn.commit()
print('완료')
"

10. git push 거절 (non-fast-forward)

증상

error: failed to push some refs
hint: Updates were rejected because the tip of your current branch is behind

원인 Gitea UI에서 직접 파일을 수정해서 로컬과 원격 브랜치가 diverge된 상태.

해결

git pull origin main --rebase
git push origin main