Skip to content

[feat] Dockerfile, docker-compose, Kubernetes 매니페스트 추가 (nginx 정적 호스팅)#114

Open
dasomel wants to merge 2 commits into
eGovFramework:mainfrom
dasomel-eGovFramework:feat/docker-and-k8s-5.0.x
Open

[feat] Dockerfile, docker-compose, Kubernetes 매니페스트 추가 (nginx 정적 호스팅)#114
dasomel wants to merge 2 commits into
eGovFramework:mainfrom
dasomel-eGovFramework:feat/docker-and-k8s-5.0.x

Conversation

@dasomel
Copy link
Copy Markdown

@dasomel dasomel commented May 20, 2026

변경 사유

컨테이너 이미지/k8s 매니페스트가 없어 vite build 후 수동 배포만 가능합니다. nginxinc/nginx-unprivileged:1.27-alpine 기반의 운영 가능한 최소 산출물을 추가합니다.

변경 내용

  • Dockerfile — multi-stage node:22-alpinenginxinc/nginx-unprivileged:1.27-alpine
    • 빌드 단계: npm ci + npm run build
    • 런타임: nginx 설정을 SPA용으로 교체
      • 포트 8080 (unprivileged 이미지 기본)
      • try_files로 React Router HTML5 history mode 지원
      • /assets/*는 1년 immutable 캐시
      • /index.html은 no-cache
  • .dockerignorenode_modules/, dist/, coverage/, IDE/.env 등 제외
  • docker-compose.yml — 단일 서비스, ${APP_VERSION:-5.0.0} 변수화
  • k8s/deployment.yaml — 2 replica RollingUpdate, runAsNonRoot(uid 101 nginx-unprivileged 기본), readOnlyRootFilesystem, drop ALL, 정적 서빙에 맞춘 리소스(50m–300m CPU / 32Mi–128Mi), liveness/readiness probe, /var/cache/nginx//var/run//tmp emptyDir로 read-only root 호환
  • k8s/service.yaml — ClusterIP 8080

영향 범위

  • 애플리케이션 코드 변경 없음
  • package.json 변경 없음
  • image: 태그는 예시 — 운영 환경 레지스트리로 교체 필요
  • API 엔드포인트가 동일 origin이 아닌 경우 nginx 설정에 reverse proxy 블록을 별도로 추가 권장

체크리스트

  • 단일 주제(프론트엔드 컨테이너화 + k8s 매니페스트)
  • 5.0.x 브랜치 대상
  • 기존 코드 미변경

The repo has no container image definition or k8s manifests — the only
deployment path is 'vite dev'/'vite build' + manually copying dist to a
web server. Add a production-shaped set that serves the Vite bundle
from nginx-unprivileged.

- Dockerfile: multi-stage Node 22 -> nginx-unprivileged 1.27 Alpine.
  Build stage uses 'npm ci' for reproducibility and 'npm run build'.
  Runtime stage replaces the default nginx config with one that:
    - listens on 8080 (matches the unprivileged image's user)
    - serves the SPA with try_files fallback for HTML5 router
    - caches /assets/* aggressively (Vite emits hashed filenames)
    - bypasses cache for /index.html
- .dockerignore: keep build context small (node_modules/, dist/,
  build/, coverage/, IDE/.env files).
- docker-compose.yml: single-service compose for demo runs, image tag
  parameterised via ${APP_VERSION:-5.0.0}.
- k8s/deployment.yaml: replicas=2 RollingUpdate, runAsNonRoot (uid 101
  matches nginx-unprivileged), readOnlyRootFilesystem, drop ALL,
  resources sized for static serving (50m-300m CPU, 32Mi-128Mi),
  separate liveness/readiness probes, ephemeral
  /var/cache/nginx, /var/run, /tmp emptyDirs so the read-only root
  filesystem profile works.
- k8s/service.yaml: ClusterIP exposing port 8080.
@dasomel dasomel changed the base branch from 5.0.x to main May 26, 2026 15:53
@dasomel dasomel changed the title [feat][5.0.x] Dockerfile, docker-compose, Kubernetes 매니페스트 추가 (nginx 정적 호스팅) [feat] Dockerfile, docker-compose, Kubernetes 매니페스트 추가 (nginx 정적 호스팅) May 26, 2026
@eGovFrameSupport
Copy link
Copy Markdown
Contributor

eGovFrameSupport commented Jun 5, 2026

표준프레임워크에 대한 지속적인 참여에 대단히 감사드립니다. Dockerfile·docker-compose·k8s 매니페스트를 로컬에서 검증했습니다.
멀티스테이지 빌드(npm ci)와 단독 컨테이너 서빙(루트 200, SPA fallback 200, HEALTHCHECK healthy), k8s 배포(replicas 2 정상 기동)까지 확인했습니다.

  1. 배포 상태에서 백엔드 연동(로그인/CRUD)이 정상 동작할 것

본 프로젝트의 병합 기준은 "정상 동작"입니다. 기존 워크플로(백엔드·프론트엔드를 각각 로컬 구동 → 브라우저에서
로그인/CRUD가 백엔드 API를 타고 정상 동작)가 그 기준선입니다. 본 PR은 이 둘을 k8s에 올리는 배포 방식을 새로
제안하므로, 그 배포 산출물에서도 브라우저 로그인/CRUD가 동일하게 정상 동작해야 합니다.

현재는 그렇지 못합니다. 프론트 번들이 API base URL을 빌드 타임에 http://localhost:8080 으로 하드코딩하고(src/config.js), nginx에 백엔드로의 프록시가 없습니다.
그 결과 배포된 SPA는 정적 화면만 뜰 뿐, 클러스터 환경에서 localhost가 클라이언트 자신을 가리켜 API 호출이 전부 깨집니다.

이 end-to-end 동작의 최종 검증은 백엔드의 k8s 배포가 함께 떠야 가능합니다. 가능하면 백엔드까지 함께 올려
로그인이 도는 구성(예: compose 또는 k8s 매니페스트 묶음)을 제시해 주시면 한 번에 확인할 수 있습니다.

  1. 배포 문서(README) 보강

이번에 추가된 Docker/k8s 배포 경로에 대한 안내가 없습니다.

  • 이미지 빌드 명령(docker build -t egovframe-template-simple-react:5.0.0 .)과 BuildKit 필요(Dockerfile heredoc) 여부
  • k8s 배포 절차 및 Service가 ClusterIP이므로 접속은 kubectl port-forward 라는 점(예: kubectl port-forward svc/egov-simple-react 3000:8080 → http://localhost:3000/)
  • 1번과 연계하여 백엔드를 어떻게 띄우고 연결해야 로그인/기본 동작이 되는지(전제 backend, 연결 방식)

이하 사항 반영 확인 후 추가 검증을 진행하도록 하겠습니다.

정적 번들이 API base 를 빌드 시점에 http://localhost:8080 으로 고정해
클러스터/컨테이너에서 localhost 가 클라이언트 자신을 가리켜 로그인·CRUD 가
동작하지 않던 문제를 해결한다.

- src/config.js: SERVER_URL 을 동일 출처 상대경로(/api)로 변경.
  필요 시 VITE_APP_API_BASE_URL 로 절대 URL 지정 가능.
- Dockerfile: nginx 에 /api/ 리버스 프록시 location 추가. prefix 를 제거해
  백엔드(context-path=/)로 전달하고, 백엔드 주소는 BACKEND_URL 환경변수를
  기동 시 envsubst 로 치환(nginx 템플릿 사용).
- vite.config.js: 개발 서버에도 동일한 /api 프록시를 추가해 npm run dev 호환.
- docker-compose.yml: fullstack 프로파일로 백엔드+MySQL 동반 기동 구성 추가.
  브라우저 → nginx(/api) → 백엔드 → DB 경로로 로그인·CRUD 통합 동작.
- k8s/deployment.yaml: BACKEND_URL 환경변수 및 conf.d 쓰기 볼륨 추가
  (readOnlyRootFilesystem 환경에서 envsubst 출력 기록).
- README: 이미지 빌드(BuildKit), docker compose 단독/통합 실행,
  k8s 배포 및 port-forward 접속, 백엔드 연동 방법 문서화.
@dasomel
Copy link
Copy Markdown
Author

dasomel commented Jun 6, 2026

두 가지 모두 반영했습니다.

1. 배포 시 백엔드 연동
프론트가 API base를 http://localhost:8080으로 고정 빌드해 컨테이너/클러스터에서 깨지던 문제를, API base를 동일 출처 상대경로(/api)로 바꾸고 nginx에 /api/ 리버스 프록시를 추가해 해결했습니다. 백엔드 주소는 빌드가 아닌 런타임 BACKEND_URL 환경변수로 주입됩니다(nginx envsubst). 개발 서버(npm run dev)에도 동일한 /api 프록시를 넣어 동작을 맞췄습니다. docker-compose.yml에는 fullstack 프로파일로 백엔드+MySQL을 함께 올려 로그인/CRUD까지 한 번에 도는 구성을 추가했습니다(백엔드 이미지·DB 시드 전제는 README에 명시).

2. 문서 보강
README에 이미지 빌드(Dockerfile heredoc → BuildKit 필요), docker compose 단독/통합 실행, k8s 배포 및 kubectl port-forward svc/egov-simple-react 3000:8080(ClusterIP) 접속, 백엔드 연동 방법을 정리했습니다.

검증: npm run build·단위 테스트 통과, 번들에서 localhost:8080 제거 확인, 실제 이미지 빌드→컨테이너 기동→BACKEND_URL 치환 및 / 200 응답까지 확인했습니다. 다만 실제 백엔드+DB와의 E2E 로그인/CRUD, 그리고 k8s 클러스터 실배포는 이 환경에서 직접 검증하지 못해 매니페스트/구성 문법 검증 수준임을 밝힙니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants