Skip to content

[feat] k8s Deployment/Service 운영성 보강 및 selector 정합#25

Merged
eGovFrameSupport merged 9 commits into
eGovFramework:mainfrom
dasomel-eGovFramework:feat/k8s-ops-hardening-5.0.x
Jun 5, 2026
Merged

[feat] k8s Deployment/Service 운영성 보강 및 selector 정합#25
eGovFrameSupport merged 9 commits into
eGovFramework:mainfrom
dasomel-eGovFramework:feat/k8s-ops-hardening-5.0.x

Conversation

@dasomel
Copy link
Copy Markdown
Contributor

@dasomel dasomel commented May 20, 2026

변경 사유

기존 k8s/ 매니페스트에 두 가지 문제가 있습니다.

  1. Deployment가 최소한의 필드(selector, image, containerPort)만 정의되어 있어 livenessProbe/readinessProbe/resources/securityContext가 모두 부재 — 운영 가용성·보안 기본값 미충족.
  2. Service.spec.selector.app=egovframe-web-sampleDeployment.spec.template.metadata.labels.app=egovframe-web과 일치하지 않아 현재 Service가 어떤 Pod로도 트래픽을 라우팅하지 못함 (정합성 버그).

변경 내용

Deployment

  • 명시적 replicas=1, RollingUpdate 전략
  • pod-level securityContext (runAsNonRoot, runAsUser/Group/fsGroup=1000)
  • container securityContext (allowPrivilegeEscalation=false, drop ALL)
  • resources.requests/limits (CPU 250m–1000m, Memory 512Mi–1Gi)
  • readinessProbe/livenessProbe/ 경로의 named http 포트로 분리
  • /usr/local/tomcat/{work,temp,logs} emptyDir 분리 (추후 readOnlyRootFilesystem 적용 용이)
  • app.kubernetes.io/* 권장 라벨 추가

Service

  • selector를 app: egovframe-web로 정렬 (기존 egovframe-web-sample 오타 수정 — 핵심 버그픽스)
  • 이름도 egovframe-web로 일원화
  • type을 NodePort(nodePort: 30000 하드코딩)에서 ClusterIP로 변경 — 외부 노출은 Ingress/NodePort 오버레이로 운영자가 결정
  • targetPort를 named http 포트 참조로 변경
  • 권장 라벨 추가

영향 범위

  • Dockerfile, 애플리케이션 코드, war 결과물 미변경
  • NodePort → ClusterIP 변경으로 외부 직접 접근이 필요한 경우 별도 Ingress 또는 type override가 필요할 수 있음
  • pod-level runAsUser=1000tomcat:8.5-jre8 이미지에서 user 1000을 허용하지 않으면 부팅 실패 가능 — 별도로 진행 중인 Tomcat 베이스 이미지 업그레이드 PR(tomcat:10.1-jre17 기반)과 함께 적용 권장

체크리스트

  • 단일 주제(k8s 매니페스트 운영성 보강 + selector 정합)
  • 5.0.x 브랜치 대상
  • Dockerfile/애플리케이션 코드 미변경

The existing k8s/deployment.yaml had only the bare minimum (selector,
image, containerPort), and k8s/service.yaml advertised a selector
('egovframe-web-sample') that did not match the Deployment's labels
('egovframe-web'), so the Service routed to no pods.

Deployment changes:
- explicit replicas=1 and RollingUpdate strategy
- pod-level securityContext (runAsNonRoot, runAsUser/Group/fsGroup=1000)
- container securityContext (allowPrivilegeEscalation=false, drop ALL)
- resources.requests {cpu: 250m, memory: 512Mi} and
  resources.limits {cpu: 1000m, memory: 1Gi}
- readinessProbe and livenessProbe on / via the named 'http' port
- ephemeral /usr/local/tomcat/{work,temp,logs} volumes so the pod
  tolerates a readOnlyRootFilesystem profile if cluster policy adds it
- consistent app.kubernetes.io/* recommended labels

Service changes:
- selector aligned to 'app: egovframe-web' (matches the Deployment)
- name normalised to 'egovframe-web' (was 'egovframe-web-sample')
- type changed from NodePort (hard-coded nodePort 30000) to ClusterIP;
  cluster operators can layer an Ingress or NodePort overlay on top
- targetPort references the named 'http' port instead of a bare 8080
- recommended labels added
@eGovFrameSupport
Copy link
Copy Markdown
Contributor

컨트리뷰션 PR은 버전 브랜치가 아닌 main 대상으로 추가 바랍니다.

@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] k8s Deployment/Service 운영성 보강 및 selector 정합 [feat] k8s Deployment/Service 운영성 보강 및 selector 정합 May 26, 2026
Comment thread k8s/deployment.yaml Outdated
failureThreshold: 6
livenessProbe:
httpGet:
path: /
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dockerfile이 WAR를 webapps/app.war로 복사하도록 설정되어 있으므로 앱 컨텍스트가 /app입니다.
GET / 는 404의 가능성이 있으므로 재검토 바랍니다.

Comment thread k8s/deployment.yaml Outdated
app.kubernetes.io/name: egovframe-web
app.kubernetes.io/part-of: egovframe-sample
spec:
securityContext:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tomcat 이미지는 기본적으로 /usr/local/tomcat 하위 디렉터리가 root 소유일 가능성이 큽니다. 그런데 Deployment에서는 컨테이너를 UID 1000으로 실행하도록 강제하므로 실행을 보장할 수 없습니다

Comment thread k8s/deployment.yaml Outdated
mountPath: /usr/local/tomcat/logs
volumes:
- name: tomcat-work
emptyDir: {}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emptyDir: {}readOnlyRootFilesystem 설정이 없으면 효과가 없을 것으로 보입니다

@eGovFrameSupport
Copy link
Copy Markdown
Contributor

k8s 설정은 minikube 로컬 시연용 샘플이고, README 전체가 NodePort(30000) 접속(…:30000/app/, minikube service egovframe-web)을 전제로 하도록 기재되어 있는데 ClusterIP 설정과 충돌합니다. 설정 또는 README 의 수정이 필요하다고 판단됩니다.

Dockerfile이 WAR를 webapps/app.war로 복사하므로 Tomcat 컨텍스트 경로가 /app이다.
기존 path: / 는 404 응답할 가능성이 있어 컨테이너가 unhealthy 상태로 재시작될 수 있다.
실제 컨텍스트 경로에 맞춰 /app/으로 수정한다.

리뷰 의견 반영 (eGovFrameSupport).
@dasomel
Copy link
Copy Markdown
Contributor Author

dasomel commented May 27, 2026

리뷰 의견 반영 완료했습니다.

Dockerfile이 COPY ./target/web-example-1.0.0.war /usr/local/tomcat/webapps/app.war로 WAR를 복사하므로 Tomcat 컨텍스트 경로가 /app인 점 확인했습니다.

readinessProbe/livenessProbepath/에서 /app/로 변경하는 commit (0474766)을 추가했습니다.

감사합니다.

Tomcat 공식 이미지는 /usr/local/tomcat 하위 디렉터리가 root 소유 기반으로
설계되어 있어 UID 1000으로 강제 실행하면 webapps/work/logs 디렉터리 권한
부족으로 시작 실패 가능성이 있다. Pod-level runAsNonRoot/runAsUser/runAsGroup/
fsGroup 설정을 제거하고 컨테이너의 기본 사용자(root)로 실행되도록 둔다.

컨테이너 레벨 보안 설정(allowPrivilegeEscalation: false, capabilities.drop: ALL)
은 유지되어 권한 상승 및 추가 capability는 차단된다.

리뷰 의견 반영 (eGovFrameSupport).
@dasomel
Copy link
Copy Markdown
Contributor Author

dasomel commented May 27, 2026

리뷰 의견 반영 완료했습니다.

Tomcat 공식 이미지의 /usr/local/tomcat 하위 디렉터리가 root 소유 기반이라 UID 1000 강제 시 webapps/work/logs 권한 부족으로 시작 실패 가능성이 있는 점 확인했습니다.

Pod-level securityContextrunAsNonRoot/runAsUser/runAsGroup/fsGroup 설정을 제거하는 commit (b9e8dfd)을 추가했습니다. 컨테이너 레벨 allowPrivilegeEscalation: false/capabilities.drop: ALL은 유지되어 권한 상승은 차단됩니다.

감사합니다.

readOnlyRootFilesystem 설정 없이 work/temp/logs를 emptyDir로 마운트해도
Tomcat이 자체적으로 사용하는 디렉터리에 별도 영향 없이 정상 동작하므로
설정의 의미가 없어 제거한다.

컨테이너 레벨 보안(allowPrivilegeEscalation: false, capabilities.drop: ALL)은
유지되어 권한 상승 및 추가 capability 획득은 차단된다.

readOnlyRootFilesystem 기반의 더 강한 격리는 Tomcat의 webapps unpackWARs
동작 호환성을 확보한 별도 PR로 다루는 것이 적절하다고 판단했다.

리뷰 의견 반영 (eGovFrameSupport).
@dasomel
Copy link
Copy Markdown
Contributor Author

dasomel commented May 27, 2026

리뷰 의견 반영 완료했습니다.

말씀대로 readOnlyRootFilesystem 설정 없이 work/temp/logs만 emptyDir로 마운트해도 Tomcat이 어차피 자체적으로 그 디렉터리에 쓰는 동작에 영향을 주지 않아 의미가 없는 점 확인했습니다.

volumeMounts/volumes 블록을 제거하는 commit (2d1ed42)을 추가했습니다. 컨테이너 레벨 allowPrivilegeEscalation: false/capabilities.drop: ALL은 유지되어 권한 상승은 차단됩니다.

readOnlyRootFilesystem: true 기반의 더 강한 격리는 Tomcat webapps 의 WAR unpackWARs 동작 호환성(initContainer 또는 server.xml 설정) 확보가 함께 필요하여, 본 PR 범위 외 별도 후속 PR로 다루는 것이 안전하다고 판단했습니다.

감사합니다.

k8s/README.md가 minikube 로컬 시연용 NodePort(30000) 접속을 전제로 작성되어
있는데 service.yaml은 ClusterIP로 설정되어 두 설정이 충돌했다.
README의 접속 안내(http://<minikube-ip>:30000/app/)와 맞도록 Service 타입을
NodePort로 변경하고 nodePort를 30000으로 명시한다.

리뷰 의견 반영 (eGovFrameSupport).
@dasomel
Copy link
Copy Markdown
Contributor Author

dasomel commented May 27, 2026

리뷰 의견 반영 완료했습니다.

k8s/README.md가 minikube 로컬 시연용 NodePort(30000) 접속(http://<minikube-ip>:30000/app/)을 전제로 작성된 점 확인했습니다.

service.yamltypeNodePort로 변경하고 nodePort: 30000을 명시하는 commit (77a82bf)을 추가했습니다. 이제 README의 접속 안내와 Service 설정이 일관됩니다.

감사합니다.

@eGovFrameSupport
Copy link
Copy Markdown
Contributor

로컬 환경(Docker Desktop + Kubernetes, kubeadm provisioner)에서 테스트 진행 결과 매니페스트 자체는 정상 동작하나, 컨테이너 이미지가 main의 Dockerfile 결함으로 만들어지지 않아 PR을 있는 그대로는 검증할 수 없었습니다.

Dockerfile 이 다음과 같이 수정되어야 할 것으로 보입니다.

FROM tomcat:10.1-jdk17-temurin
COPY ./target/egovframe-web-5.0.0.war /usr/local/tomcat/webapps/app.war
EXPOSE 8080
ENTRYPOINT ["catalina.sh","run"]

추가로, 현재 PR #25 브랜치 base가 7ba8fc9 시점이라 main이 PR #26(Lombok refactor) / #28(unit tests) / #29(5.0.x merge) 등 4커밋 앞서 있습니다. 참고 부탁드립니다.

@dasomel
Copy link
Copy Markdown
Contributor Author

dasomel commented May 29, 2026

리뷰 감사합니다. 말씀하신 Dockerfile 결함은 k8s 매니페스트와 별개 주제이므로 PR #35(fix/dockerfile-war-context)로 분리해 별도 제출했습니다. 해당 PR에서 베이스 이미지를 tomcat:10.1-jdk17-temurin으로, COPY 경로를 egovframe-web-5.0.0.war로 정정했습니다.

본 브랜치(feat/k8s-ops-hardening-5.0.x)는 main보다 뒤처진 2개 커밋(PR #26, #28 머지 결과)을 git merge origin/main으로 반영하여 최신화했습니다.

tomcat:8.5-jre8(단종) → tomcat:10.1-jdk17-temurin으로 교체하고,
COPY 대상 파일명을 pom.xml finalName 기준 실제 빌드 산출물인
egovframe-web-5.0.0.war로 정정한다.

기존 web-example-1.0.0.war 경로는 존재하지 않아 이미지 빌드가
실패하던 문제를 해결한다.
@dasomel
Copy link
Copy Markdown
Contributor Author

dasomel commented Jun 1, 2026

지적해주신 사항 반영했습니다.

  • Dockerfile의 베이스 이미지 태그와 WAR 산출물 경로(egovframe-web-5.0.0.war)를 실제 빌드 결과와 일치하도록 수정했습니다.
  • service는 README의 NodePort 30000 안내와 일치하도록 맞췄습니다.

재검토 부탁드립니다.

@eGovFrameSupport
Copy link
Copy Markdown
Contributor

#35(Docker 빌드 복구)가 머지된 main 위에서 본 PR의 k8s 매니페스트를 로컬 Kubernetes
클러스터(Docker Desktop)에 올려 재검증했고, 정상 동작을 확인했습니다.

다만 병합 전에 k8s/README.md 두 가지 보강을 요청드립니다.

  1. 빌드 단계/이미지 이름 정합성. 현재 README는 egovframe-web:5.0.0 이미지가 이미
    존재한다고 전제하지만 빌드 방법이 없고, 루트 Dockerfile 주석은 egovframe-web-sample:5.0.0
    으로 빌드하도록 안내합니다. 이 둘이 어긋나 문서만 따라가면 deployment가 이미지를 찾지
    못합니다. README에 이미지 빌드 명령을 추가하거나 Dockerfile 주석의 태그를
    egovframe-web:5.0.0로 통일하여, 빌드→로드→배포 경로가 하나의 이름으로 이어지도록
    정리 부탁드립니다.

  2. 실행 환경 안내 보강. 현재 안내는 minikube 전제(minikube image load,
    minikube INTERNAL-IP 접속)인데, Docker Desktop/kind 등에서는 이미지 로드가 불필요하고
    접속이 http://localhost:30000/app/입니다. 대표 환경별 접속 방법을 한두 줄 덧붙여
    주시면 좋겠습니다.

위 두 가지가 반영되면 병합을 진행하도록 하겠습니다. 감사합니다.

Dockerfile 주석의 이미지 태그가 egovframe-web-sample:5.0.0으로
deployment.yaml 및 README와 불일치하는 문제를 수정.
이미지명을 egovframe-web:5.0.0 하나로 통일.

k8s/README.md에 docker build 명령을 명시하고,
minikube와 Docker Desktop/kind 환경별 이미지 적재 필요 여부 및
접속 URL을 표로 정리.
@dasomel
Copy link
Copy Markdown
Contributor Author

dasomel commented Jun 4, 2026

말씀해 주신 두 가지를 반영했습니다.

  1. Dockerfile 주석의 이미지 태그를 egovframe-web:5.0.0으로 수정해 deployment.yaml, README와 통일했습니다.
  2. k8s/README.md에 빌드 명령(docker build -t egovframe-web:5.0.0 .)을 추가하고, minikube와 Docker Desktop/kind 환경별 이미지 적재 필요 여부 및 접속 URL을 표로 정리했습니다.

확인 부탁드립니다. 감사합니다.

@eGovFrameSupport
Copy link
Copy Markdown
Contributor

표준프레임워크에 대한 지속적인 참여에
대단히 감사드립니다.

@eGovFrameSupport eGovFrameSupport merged commit a5d5a56 into eGovFramework:main Jun 5, 2026
1 check passed
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