[feat] k8s Deployment/Service 운영성 보강 및 selector 정합#25
Conversation
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
|
컨트리뷰션 PR은 버전 브랜치가 아닌 main 대상으로 추가 바랍니다. |
| failureThreshold: 6 | ||
| livenessProbe: | ||
| httpGet: | ||
| path: / |
There was a problem hiding this comment.
Dockerfile이 WAR를 webapps/app.war로 복사하도록 설정되어 있으므로 앱 컨텍스트가 /app입니다.
GET / 는 404의 가능성이 있으므로 재검토 바랍니다.
| app.kubernetes.io/name: egovframe-web | ||
| app.kubernetes.io/part-of: egovframe-sample | ||
| spec: | ||
| securityContext: |
There was a problem hiding this comment.
Tomcat 이미지는 기본적으로 /usr/local/tomcat 하위 디렉터리가 root 소유일 가능성이 큽니다. 그런데 Deployment에서는 컨테이너를 UID 1000으로 실행하도록 강제하므로 실행을 보장할 수 없습니다
| mountPath: /usr/local/tomcat/logs | ||
| volumes: | ||
| - name: tomcat-work | ||
| emptyDir: {} |
There was a problem hiding this comment.
emptyDir: {}은 readOnlyRootFilesystem 설정이 없으면 효과가 없을 것으로 보입니다
|
k8s 설정은 minikube 로컬 시연용 샘플이고, README 전체가 NodePort(30000) 접속( |
Dockerfile이 WAR를 webapps/app.war로 복사하므로 Tomcat 컨텍스트 경로가 /app이다. 기존 path: / 는 404 응답할 가능성이 있어 컨테이너가 unhealthy 상태로 재시작될 수 있다. 실제 컨텍스트 경로에 맞춰 /app/으로 수정한다. 리뷰 의견 반영 (eGovFrameSupport).
|
리뷰 의견 반영 완료했습니다. Dockerfile이
감사합니다. |
Tomcat 공식 이미지는 /usr/local/tomcat 하위 디렉터리가 root 소유 기반으로 설계되어 있어 UID 1000으로 강제 실행하면 webapps/work/logs 디렉터리 권한 부족으로 시작 실패 가능성이 있다. Pod-level runAsNonRoot/runAsUser/runAsGroup/ fsGroup 설정을 제거하고 컨테이너의 기본 사용자(root)로 실행되도록 둔다. 컨테이너 레벨 보안 설정(allowPrivilegeEscalation: false, capabilities.drop: ALL) 은 유지되어 권한 상승 및 추가 capability는 차단된다. 리뷰 의견 반영 (eGovFrameSupport).
|
리뷰 의견 반영 완료했습니다. Tomcat 공식 이미지의 Pod-level 감사합니다. |
readOnlyRootFilesystem 설정 없이 work/temp/logs를 emptyDir로 마운트해도 Tomcat이 자체적으로 사용하는 디렉터리에 별도 영향 없이 정상 동작하므로 설정의 의미가 없어 제거한다. 컨테이너 레벨 보안(allowPrivilegeEscalation: false, capabilities.drop: ALL)은 유지되어 권한 상승 및 추가 capability 획득은 차단된다. readOnlyRootFilesystem 기반의 더 강한 격리는 Tomcat의 webapps unpackWARs 동작 호환성을 확보한 별도 PR로 다루는 것이 적절하다고 판단했다. 리뷰 의견 반영 (eGovFrameSupport).
|
리뷰 의견 반영 완료했습니다. 말씀대로
감사합니다. |
k8s/README.md가 minikube 로컬 시연용 NodePort(30000) 접속을 전제로 작성되어 있는데 service.yaml은 ClusterIP로 설정되어 두 설정이 충돌했다. README의 접속 안내(http://<minikube-ip>:30000/app/)와 맞도록 Service 타입을 NodePort로 변경하고 nodePort를 30000으로 명시한다. 리뷰 의견 반영 (eGovFrameSupport).
|
리뷰 의견 반영 완료했습니다.
감사합니다. |
|
로컬 환경(Docker Desktop + Kubernetes, kubeadm provisioner)에서 테스트 진행 결과 매니페스트 자체는 정상 동작하나, 컨테이너 이미지가 main의 Dockerfile 결함으로 만들어지지 않아 PR을 있는 그대로는 검증할 수 없었습니다. Dockerfile 이 다음과 같이 수정되어야 할 것으로 보입니다. 추가로, 현재 PR #25 브랜치 base가 |
|
리뷰 감사합니다. 말씀하신 Dockerfile 결함은 k8s 매니페스트와 별개 주제이므로 PR #35(fix/dockerfile-war-context)로 분리해 별도 제출했습니다. 해당 PR에서 베이스 이미지를 본 브랜치(feat/k8s-ops-hardening-5.0.x)는 main보다 뒤처진 2개 커밋(PR #26, #28 머지 결과)을 |
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 경로는 존재하지 않아 이미지 빌드가 실패하던 문제를 해결한다.
|
지적해주신 사항 반영했습니다.
재검토 부탁드립니다. |
|
#35(Docker 빌드 복구)가 머지된 main 위에서 본 PR의 k8s 매니페스트를 로컬 Kubernetes 다만 병합 전에
위 두 가지가 반영되면 병합을 진행하도록 하겠습니다. 감사합니다. |
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을 표로 정리.
|
말씀해 주신 두 가지를 반영했습니다.
확인 부탁드립니다. 감사합니다. |
|
표준프레임워크에 대한 지속적인 참여에 |
변경 사유
기존
k8s/매니페스트에 두 가지 문제가 있습니다.Deployment가 최소한의 필드(selector, image, containerPort)만 정의되어 있어livenessProbe/readinessProbe/resources/securityContext가 모두 부재 — 운영 가용성·보안 기본값 미충족.Service.spec.selector.app=egovframe-web-sample이Deployment.spec.template.metadata.labels.app=egovframe-web과 일치하지 않아 현재 Service가 어떤 Pod로도 트래픽을 라우팅하지 못함 (정합성 버그).변경 내용
Deployment
replicas=1,RollingUpdate전략securityContext(runAsNonRoot, runAsUser/Group/fsGroup=1000)securityContext(allowPrivilegeEscalation=false, drop ALL)resources.requests/limits(CPU 250m–1000m, Memory 512Mi–1Gi)readinessProbe/livenessProbe를/경로의 namedhttp포트로 분리/usr/local/tomcat/{work,temp,logs}emptyDir 분리 (추후readOnlyRootFilesystem적용 용이)app.kubernetes.io/*권장 라벨 추가Service
app: egovframe-web로 정렬 (기존egovframe-web-sample오타 수정 — 핵심 버그픽스)egovframe-web로 일원화NodePort(nodePort: 30000하드코딩)에서ClusterIP로 변경 — 외부 노출은 Ingress/NodePort 오버레이로 운영자가 결정targetPort를 namedhttp포트 참조로 변경영향 범위
runAsUser=1000은tomcat:8.5-jre8이미지에서 user 1000을 허용하지 않으면 부팅 실패 가능 — 별도로 진행 중인 Tomcat 베이스 이미지 업그레이드 PR(tomcat:10.1-jre17기반)과 함께 적용 권장체크리스트