Harbor 이미지로 Kubernetes 배포하기: WildFly·Nginx 배포와 오류 해결
폐쇄망에서 전달받은 컨테이너 이미지 파일을 Harbor에 업로드하고, Kubernetes에 WildFly 백엔드와 Nginx 프런트엔드를 배포하는 과정을 정리한다. 최초 배포뿐 아니라 PVC Pending, DB 인증 실패, Service 접속, 인증서 교체와 재배포 과정에서 확인할 항목까지 함께 살펴본다.
본문의 주소, IP, 계정 경로와 리소스 이름은 공개용 예시다.
harbor.example.internal, demo,
192.0.2.34는 실제 환경에 맞게 변경한다.
예시 IP는 문서 설명용이므로 실제 접속 대상으로 사용할 수 없다.
이미지 버전은 작업 예시이며 최신 버전을 의미하지 않는다.
nfs-client도 Kubernetes 기본 내장 이름이 아니라
해당 환경에 설치된 StorageClass 이름이다.
1. 예제 환경과 리소스 역할
| 항목 | 예시 값 |
|---|---|
| Harbor 주소 | harbor.example.internal |
| Harbor 프로젝트 | demo |
| 저장소 경로 구분 | web/ |
| Kubernetes 네임스페이스 | demo |
| 백엔드 Deployment | demo-backend |
| 백엔드 컨테이너 | wildfly |
| 프런트 Deployment | demo-frontend |
| 프런트 컨테이너 | nginx |
| 애플리케이션 ConfigMap | demo-config |
| 애플리케이션 Secret | demo-secret |
| Harbor 인증 Secret | harbor-secret |
| HTTPS 인증서 Secret | demo-tls |
| 첨부파일 PVC | demo-attachments |
| StorageClass | nfs-client |
관리 서버의 작업 디렉터리는 다음과 같이 구성한 것으로 가정한다.
/opt/deploy/backend
├─ neoenv/
│ ├─ demo-config.env
│ └─ demo-secret.env
└─ k8s/
└─ backend-wildfly.yaml
/opt/deploy/frontend
├─ cert/
│ ├─ fullchain.pem
│ ├─ privkey.pem
│ └─ demo-ca.crt
└─ k8s/
└─ frontend.yaml
| Service | 용도 |
|---|---|
backend |
프런트의 Nginx가 backend:8081로 호출하는 내부 Service |
demo-backend |
백엔드 점검용 NodePort Service |
demo-frontend |
프런트 화면에 접근하는 NodePort Service |
backend와 demo-backend는 같은
app: demo-backend 라벨을 선택한다.
따라서 두 Service가 존재해도 백엔드 Pod가 중복 생성되는 것은 아니다.
Harbor 프로젝트와 Kubernetes 네임스페이스는 서로 다른 개념이다.
이름을 둘 다 demo로 사용하더라도 각각 별도로 생성해야 한다.
2. 이미지 파일 불러오기와 Harbor 업로드
전달받은 파일이 Docker 이미지 아카이브라면 직접 빌드하지 않고
docker load로 불러올 수 있다.
단순히 WildFly 설치 폴더를 압축한 파일은 이 방법의 대상이 아니다.
이미지 업로드 작업은 Docker가 설치되어 있고 Harbor와 통신할 수 있는 서버에서 실행한다. Kubernetes master에 Docker를 추가 설치할 필요는 없다. 이 예제에서는 Harbor 서버에 설치된 Docker 클라이언트를 사용한다.
2-1. 이미지 파일 불러오기
docker load -i backend-image.tar.gz
docker load -i frontend-image.tar.gz
docker images
불러온 이미지 이름과 태그를 확인한다. 이 글에서는 다음 이름을 가정한다.
backend/backend-wildfly:0.0.3
frontend/demo-frontend:0.0.4
2-2. Harbor 로그인
Harbor 웹 화면에 demo 프로젝트를 먼저 생성한 뒤 로그인한다.
docker login harbor.example.internal
2-3. 백엔드 이미지 업로드
docker tag backend/backend-wildfly:0.0.3 harbor.example.internal/demo/web/backend-wildfly:0.0.3
docker push harbor.example.internal/demo/web/backend-wildfly:0.0.3
2-4. 프런트 이미지 업로드
docker tag frontend/demo-frontend:0.0.4 harbor.example.internal/demo/web/demo-frontend:0.0.4
docker push harbor.example.internal/demo/web/demo-frontend:0.0.4
이미지 경로의 demo는 Harbor 프로젝트이며,
web/backend-wildfly는 저장소 이름이다.
web라는 하위 프로젝트를 별도로 생성하는 구조는 아니다.
작업이 끝나면 Harbor의 프로젝트 화면에서 저장소와 태그를 확인한다.
이후 YAML의 image에는 Push한 전체 경로를 사용한다.
3. 네임스페이스와 Harbor 인증정보 준비
이후 kubectl 명령어는 대상 클러스터의 kubeconfig와 권한이 준비된 관리 서버에서 실행한다.
3-1. 네임스페이스 생성
kubectl create namespace demo
kubectl get namespace demo
이미 존재하는 네임스페이스라면 생성 단계는 생략한다.
3-2. Harbor 인증 Secret 생성
kubectl create secret docker-registry harbor-secret \
--docker-server=harbor.example.internal \
--docker-username='HARBOR_USER' \
--docker-password='HARBOR_PASSWORD' \
-n demo
실제 계정에는 해당 프로젝트의 이미지 다운로드 권한이 있어야 한다. 운영 환경에서는 필요한 권한만 부여한 Robot Account 사용을 검토한다.
kubectl get secret harbor-secret -n demo
Deployment는 다음 위치에서 인증정보를 참조한다.
spec:
template:
spec:
imagePullSecrets:
- name: harbor-secret
imagePullSecrets가 참조하는 Secret은 Pod와 같은 네임스페이스에 있어야 한다.
인증 Secret과 별개로, 이미지를 내려받는 노드에서 Harbor 주소 확인과
네트워크 연결, 서버 인증서 신뢰가 가능해야 한다.
4. WildFly 백엔드 최초 배포
4-1. 작업 디렉터리 이동
cd /opt/deploy/backend
4-2. ConfigMap과 Secret 생성
일반 설정은 ConfigMap, 비밀번호 등 민감한 값은 Secret으로 구분한다. 환경변수 이름은 애플리케이션이 실제로 읽는 이름과 일치해야 한다.
kubectl create configmap demo-config \
--from-env-file=neoenv/demo-config.env \
-n demo
kubectl create secret generic demo-secret \
--from-env-file=neoenv/demo-secret.env \
-n demo
kubectl get configmap demo-config -n demo
kubectl get secret demo-secret harbor-secret -n demo
4-3. Backend YAML 확인
k8s/backend-wildfly.yaml의 이미지, 네임스페이스,
환경설정 참조, 포트와 볼륨을 확인한다.
아래 코드는 각 설정의 위치를 설명하는 발췌 예시이며,
전체 Deployment YAML을 대체하는 파일은 아니다.
image: harbor.example.internal/demo/web/backend-wildfly:0.0.3
ports:
- containerPort: 8081
envFrom:
- configMapRef:
name: demo-config
- secretRef:
name: demo-secret
첨부파일 저장용 PVC에는 실제 사용할 StorageClass를 지정한다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: demo-attachments
namespace: demo
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
NodePort Service는 다음과 같이 구성한다.
apiVersion: v1
kind: Service
metadata:
name: demo-backend
namespace: demo
spec:
type: NodePort
selector:
app: demo-backend
ports:
- port: 8081
targetPort: 8081
nodePort: 30275
30275는 예시 포트다.
클러스터의 NodePort 허용 범위와 기존 사용 여부를 확인한 뒤 지정한다.
4-4. 검증 후 적용
kubectl apply --dry-run=server -f k8s/backend-wildfly.yaml -n demo
검증에 오류가 없으면 실제 적용한다.
kubectl apply -f k8s/backend-wildfly.yaml -n demo
4-5. 상태와 로그 확인
kubectl get pods,pvc,svc -n demo
kubectl rollout status deployment/demo-backend -n demo --timeout=300s
kubectl logs -f deployment/demo-backend -c wildfly -n demo
Pod의 1/1 Running과 PVC의 Bound를 확인한다.
이 예제에서는 애플리케이션의 /actuator/health도 함께 검사한다.
5. 백엔드 배포 오류와 대응
5-1. apiVersion not set
error validating data: apiVersion not set
YAML의 apiVersion 누락이나 오탈자,
문서 구분 이후의 잘못된 내용을 먼저 확인한다.
cat -n k8s/backend-wildfly.yaml
리소스마다 올바른 apiVersion과 kind가 있어야 한다.
apiVersion: v1
kind: PersistentVolumeClaim
---
apiVersion: apps/v1
kind: Deployment
---
apiVersion: v1
kind: Service
위 예시는 문서 구분 설명용이다. 각 문서에는 실제 리소스에 필요한 metadata와 spec이 추가되어야 한다. 검증을 끄기보다 원본 YAML의 구조를 수정한다.
5-2. Pod와 PVC가 Pending
kubectl get pods,pvc -n demo
kubectl describe pod -l app=demo-backend -n demo
kubectl describe pvc demo-attachments -n demo
다음 메시지가 나오면 PVC 연결이 완료되지 않은 상태다.
pod has unbound immediate PersistentVolumeClaims
no persistent volumes available for this claim and no storage class is set
해당 사례에서는 기본 StorageClass가 지정되지 않았고 PVC에도 StorageClass가 없어 저장공간을 자동 할당받지 못했다.
kubectl get storageclass
kubectl get pv
사용 가능한 StorageClass를 확인한 뒤 PVC에 지정한다.
storageClassName: nfs-client
최초 구성 중 아직 볼륨과 연결되지 않은 PVC를 다시 만드는 작업과, 운영 중인 Bound PVC를 삭제하는 작업은 다르다. Bound PVC는 첨부파일 데이터와 연결될 수 있으므로 단순 재배포를 위해 삭제하지 않는다.
5-3. storageClassName 오탈자
unknown field "spec.storageClasssName"
정확한 필드 이름은 다음과 같다.
storageClassName: nfs-client
필드 이름은 대소문자와 철자가 정확해야 한다. 오류가 발생하면 새로 입력한 필드를 우선 확인한다.
5-4. YAML 파싱 오류
could not find expected ':'
콜론 누락, 잘못된 들여쓰기, 복사 과정에서 들어간 문자를 확인한다. 여러 문서를 포함한 파일은 오류 줄 번호가 문서 단위로 해석될 수 있으므로, 앞부분뿐 아니라 마지막 Service까지 확인한다.
cat -n k8s/backend-wildfly.yaml
tail -30 k8s/backend-wildfly.yaml
예를 들어 NodePort 부분은 다음 형태여야 한다.
ports:
- port: 8081
targetPort: 8081
nodePort: 30275
여러 리소스를 한 번에 적용하면 앞의 리소스는 반영되고 뒤의 리소스에서 오류가 날 수 있다. 일부 성공 메시지만 보고 전체 성공으로 판단하지 않는다.
5-5. PVC spec is immutable
spec: Forbidden: spec is immutable after creation
기존 PVC와 새 YAML의 변경 불가 필드가 다른 경우 발생한다.
이 사례에서는 새 YAML에서 storageClassName이 빠져
기존 nfs-client를 변경하는 요청이 생성되었다.
기존 PVC를 조회해 새 YAML과 비교한다.
kubectl get pvc demo-attachments -n demo -o yaml
기존 StorageClass를 유지하도록 수정한 뒤 다시 검증한다.
storageClassName: nfs-client
kubectl apply --dry-run=server -f k8s/backend-wildfly.yaml -n demo
5-6. Running이지만 0/1이고 DB 인증 실패
Running은 컨테이너가 실행 중이라는 뜻이며,
애플리케이션이 요청을 처리할 준비가 끝났다는 뜻은 아니다.
로그에 DB 인증 실패가 있으면 접속 대상과 인증정보를 확인한다.
- DB 주소, 포트, DB명과 사용자명이 올바른지 확인한다.
- 애플리케이션이 읽는 환경변수 이름을 확인한다.
- ConfigMap과 Secret을 업데이트한다.
- 새 Pod를 생성해 환경변수를 반영한다.
- DB 인증 오류와 readiness 상태를 다시 확인한다.
임의로 PASSWORD라는 변수를 추가해도
애플리케이션이 그 이름을 읽지 않으면 효과가 없다.
--from-env-file로 읽는 값에 넣은 따옴표도
실제 값에 포함될 수 있으므로 파일 형식을 확인한다.
5-7. ConfigMap을 실수로 Secret으로 생성
ConfigMap과 Secret은 서로 다른 리소스다. 동일한 이름으로 둘 다 존재할 수 있으므로 잘못 만든 종류와 이름을 확인한다.
kubectl get configmap demo-config -n demo
kubectl get secret demo-config -n demo
잘못 생성한 Secret이 확인된 경우 해당 리소스만 삭제한다.
kubectl delete secret demo-config -n demo --ignore-not-found
이후 ConfigMap을 올바르게 반영한다.
이미지 다운로드에 사용하는 harbor-secret과 혼동하지 않는다.
5-8. 파이프 명령어 입력 오류
| 메시지 | 확인할 부분 |
|---|---|
flag needs an argument: 'f' |
마지막을 kubectl apply -f -까지 입력했는지 확인 |
unknown flag |
--dry-run=client의 하이픈, 철자와 공백 확인 |
no objects passed to apply |
파이프 앞의 create 명령이 먼저 실패했는지 확인 |
missing ... last-applied-configuration |
뒤에 configured가 나오면 적용 성공 여부 확인. 관리용 annotation 추가 안내일 수 있음 |
정확한 ConfigMap 업데이트 명령은 다음과 같다.
kubectl create configmap demo-config --from-env-file=neoenv/demo-config.env -n demo --dry-run=client -o yaml | kubectl apply -f -
마지막 하이픈은 표준입력으로 전달된 YAML을 읽는다는 뜻이다.
6. NodePort 접속과 네트워크 확인
6-1. LoadBalancer Pending의 의미
Service가 LoadBalancer이면 외부 로드밸런서와 주소 할당을 요청한다.
이를 처리할 컨트롤러나 외부 연동이 없으면
EXTERNAL-IP가 계속 Pending으로 남을 수 있다.
TYPE EXTERNAL-IP PORT(S)
LoadBalancer <pending> 8081:30275/TCP
이것은 Pod의 Pending과 별개다. 외부 IP가 Pending이어도 할당된 NodePort의 통신은 별도로 확인해야 한다.
워커 IP와 포트로 접근할 계획이면 NodePort로 변경할 수 있다.
kubectl patch svc demo-backend -n demo -p '{"spec":{"type":"NodePort"}}'
원본 YAML에도 같은 타입을 반영한다.
spec:
type: NodePort
Service 변경만으로는 Pod를 재기동할 필요가 없다.
6-2. 접속 포트 확인
kubectl get svc demo-backend -n demo
8081:30275/TCP
8081: Service 포트30275: 노드 IP로 접근할 때 사용하는 NodePort
6-3. 워커 노드 선택
kubectl get svc demo-backend -n demo -o jsonpath='{.spec.externalTrafficPolicy}'
Cluster이면 일반적으로 다른 노드의 Ready Pod에도 전달된다.
다만 노드 주소 제한, 방화벽, 라우팅과 서비스 프록시 설정에 따라
실제 접근 가능 여부는 달라질 수 있다.
Local이면 해당 노드의 로컬 엔드포인트를 사용하는 방식이므로
Ready Pod가 배치된 노드인지 함께 확인해야 한다.
6-4. 관리 서버에서 HTTP 테스트
curl -i --connect-timeout 5 --max-time 10 \
http://192.0.2.34:30275/actuator/health
정상 응답 예시는 다음과 같다.
HTTP/1.1 200 OK
{"status":"UP"}
6-5. PC에서만 접속이 안 되는 경우
Windows PowerShell에서 TCP 연결을 확인한다.
Test-NetConnection 192.0.2.34 -Port 30275
| 결과 | 점검 방향 |
|---|---|
| 관리 서버 성공, PC TCP 실패 | PC에서 워커까지의 방화벽과 라우팅 확인 |
| PC TCP 성공, 브라우저 실패 | HTTP·HTTPS 구분, 프록시, URL 경로와 브라우저 오류 확인 |
| TCP 연결 후 HTTP 404 | 서버까지 도달했을 수 있으므로 실제 애플리케이션 경로 확인 |
| Pod 0/1, Ready 엔드포인트 없음 | 애플리케이션 기동과 readiness 오류부터 확인 |
백엔드 루트 경로에 화면이 없을 수 있다. 상태 확인 API의 성공과 프런트 화면 제공 여부는 구분해서 확인한다.
7. Nginx 프런트엔드 최초 배포
7-1. 작업 경로 이동
cd /opt/deploy/frontend
7-2. 인증서 파일 확인
ls -l cert/fullchain.pem cert/privkey.pem cert/demo-ca.crt
| 파일 | 용도 |
|---|---|
fullchain.pem | HTTPS 서버 인증서와 체인 |
privkey.pem | 서버 인증서에 대응하는 개인키 |
demo-ca.crt | 해당 인증서 발급자를 신뢰하기 위한 CA 인증서 |
7-3. TLS Secret 생성
kubectl create secret tls demo-tls \
--cert=cert/fullchain.pem \
--key=cert/privkey.pem \
-n demo
kubectl get secret demo-tls -n demo
7-4. Frontend YAML 확인
배포 파일은 k8s/frontend.yaml이다.
프런트 Deployment에는 다음 값을 반영한다.
| 항목 | 예시 값 |
|---|---|
| Deployment | demo-frontend |
| 네임스페이스 | demo |
| 이미지 | harbor.example.internal/demo/web/demo-frontend:0.0.4 |
| 컨테이너 | nginx |
| replicas | 2 |
| 이미지 인증 | harbor-secret |
| TLS Secret | demo-tls |
| 외부 접속 방식 | NodePort |
이 사례의 이미지에서는 Nginx 템플릿이 다음 경로에서 인증서를 읽도록 구성되어 있었다. 환경변수와 인증서 마운트 경로를 함께 확인한다.
env:
- name: SERVER_NAME
value: "127.0.0.1"
volumeMounts:
- name: tls
mountPath: /etc/letsencrypt/live/127.0.0.1
readOnly: true
volumes:
- name: tls
secret:
secretName: demo-tls
items:
- key: tls.crt
path: fullchain.pem
- key: tls.key
path: privkey.pem
위의 127.0.0.1은 해당 이미지의 설정 예시다.
실제 인증서 대상이나 외부 접속 주소를 자동으로 결정하지 않는다.
다른 이미지에서는 Nginx 템플릿과 서버 블록을 확인한 뒤 설정해야 한다.
7-5. backend 별칭 Service 확인
Nginx의 upstream이 backend:8081이면
같은 네임스페이스에 다음 내부 Service가 필요하다.
apiVersion: v1
kind: Service
metadata:
name: backend
namespace: demo
spec:
type: ClusterIP
selector:
app: demo-backend
ports:
- name: http
port: 8081
targetPort: 8081
7-6. 검증과 배포
kubectl apply --dry-run=server -f k8s/frontend.yaml -n demo
kubectl apply -f k8s/frontend.yaml -n demo
kubectl rollout status deployment/demo-frontend -n demo --timeout=300s
kubectl get pods -l app=demo-frontend -n demo
kubectl get svc demo-frontend -n demo
replicas: 2이면 프런트 Pod가 두 개 생성된다.
각 Pod의 1/1은 컨테이너 한 개 중 한 개가 Ready라는 뜻이다.
7-7. 프런트 접속 포트 확인
80:31080/TCP,443:31443/TCP
위와 같은 출력이라면 HTTPS 접속 주소는 다음과 같다.
https://192.0.2.34:31443
실제 환경에서 할당된 443:NodePort 값을 사용한다.
HTTP가 HTTPS 기본 포트로 리다이렉트될 수 있으므로
최초 점검은 HTTPS NodePort로 직접 수행하는 편이 명확하다.
앞단 프록시 서버를 사용하는 경우에는 프록시가 프런트 Service로 전달하도록 별도 설정해야 한다. 인증서를 등록하는 것만으로 프록시 연결이 생성되지는 않는다.
8. 인증서 등록 위치와 오류 대응
8-1. key and cert must be specified
error: key and cert must be specified
kubectl create secret tls에는 인증서와 개인키를 모두 지정해야 한다.
--cert만 입력하고 --key를 빠뜨리면 오류가 발생한다.
kubectl create secret tls demo-tls \
--cert=cert/fullchain.pem \
--key=cert/privkey.pem \
-n demo \
--dry-run=client -o yaml | kubectl apply -f -
8-2. 서버 인증서와 CA 신뢰 등록 구분
| 작업 | 목적 |
|---|---|
demo-tls Secret 마운트 |
Nginx가 사용자에게 HTTPS 서버 인증서를 제공 |
| Windows PC의 CA 등록 | 브라우저 등 클라이언트가 해당 CA를 신뢰 |
| Pod 내부 CA 등록 | Pod 내부 프로그램이 외부 HTTPS 서버의 발급자를 신뢰 |
세 작업은 적용 위치와 목적이 다르다. 프런트 Pod에 CA를 등록해도 사용자 PC 브라우저의 신뢰 저장소는 바뀌지 않는다.
8-3. Windows PC에 CA 등록
신뢰할 수 있는 업체나 조직에서 받은 CA 인증서인지 확인한 뒤, 접속할 Windows PC의 CMD에서 실행한다.
certutil -user -addstore Root "C:\cert\demo-ca.crt"
-user는 현재 사용자 저장소에 등록하는 옵션이다.
등록 후 브라우저를 완전히 종료했다가 다시 실행한다.
8-4. SAN에 접속 주소가 포함되어 있는지 확인
openssl x509 -in cert/fullchain.pem -noout -subject -ext subjectAltName
CA 등록은 발급자 신뢰를 설정하는 작업이다. 접속 주소 검증은 별도이므로 인증서의 SAN에 실제 접속 IP나 도메인이 포함되어야 한다. CN만 있고 SAN이 없는 인증서는 최신 브라우저에서 경고가 발생할 수 있다.
8-5. Alpine 기반 프런트 Pod에 CA 등록
컨테이너 환경과 업데이트 명령어를 먼저 확인한다.
kubectl exec -n demo deployment/demo-frontend -c nginx -- \
sh -c 'cat /etc/os-release; command -v update-ca-certificates; id'
이 사례에서는 Alpine Linux, root 사용자,
update-ca-certificates 사용 가능 상태를 확인했다.
cd /opt/deploy/frontend/cert
kubectl get pods -n demo -l app=demo-frontend
실제 Pod 이름을 넣어 인증서를 복사한다.
kubectl cp demo-ca.crt demo/POD_NAME:/usr/local/share/ca-certificates/demo-ca.crt -c nginx
CA 저장소를 업데이트한다.
kubectl exec -n demo POD_NAME -c nginx -- update-ca-certificates
프런트 Pod가 두 개이면 두 Pod 각각 적용한다.
kubectl cp는 컨테이너의 tar 명령 등에 의존하므로
복사 실패 시 오류 메시지를 확인해야 한다.
컨테이너 안에 수동으로 복사한 CA와 신뢰 저장소 변경은 컨테이너 또는 Pod 재생성 시 사라진다. 반복 운영에는 이미지에 CA를 포함하거나 마운트와 초기화 절차를 배포 구성에 반영해야 한다.
Nginx의 upstream 인증서 검증은
proxy_ssl_verify,
proxy_ssl_trusted_certificate 등
실제 Nginx 설정도 함께 확인해야 한다.
OS 신뢰 저장소 갱신만으로 Nginx의 모든 TLS 검증 설정이 바뀌는 것은 아니다.
9. 백엔드 재배포 순서
환경파일과 YAML을 수정한 뒤에는 기존 리소스를 지우기보다 업데이트한다. Bound PVC와 Harbor 인증정보는 유지한다.
9-1. ConfigMap과 Secret 업데이트
cd /opt/deploy/backend
kubectl create configmap demo-config --from-env-file=neoenv/demo-config.env -n demo --dry-run=client -o yaml | kubectl apply -f -
kubectl create secret generic demo-secret --from-env-file=neoenv/demo-secret.env -n demo --dry-run=client -o yaml | kubectl apply -f -
9-2. YAML 검증과 적용
kubectl apply --dry-run=server -f k8s/backend-wildfly.yaml -n demo
kubectl apply -f k8s/backend-wildfly.yaml -n demo
9-3. 환경변수 반영을 위한 Pod 교체
ConfigMap과 Secret을 환경변수로 읽는 컨테이너는 리소스 값만 바꿔도 실행 중인 프로세스 환경이 자동 변경되지 않는다. 새 Pod에 변경값을 반영한다.
kubectl rollout restart deployment/demo-backend -n demo
kubectl rollout status deployment/demo-backend -n demo --timeout=300s
kubectl get pods,pvc,svc -n demo
kubectl logs -f deployment/demo-backend -c wildfly -n demo
YAML의 Pod 템플릿 변경으로 이미 새 rollout이 시작되었다면, 변경한 ConfigMap과 Secret이 새 Pod 생성 전에 반영됐는지 확인한다. 해당 rollout으로 필요한 변경이 모두 적용되면 추가 restart는 생략할 수 있다.
9-4. Pod가 일시적으로 두 개 보이는 경우
RollingUpdate 중에는 기존 Pod와 새 Pod가 동시에 존재할 수 있다. 새 Pod가 Ready가 되면 기존 Pod가 종료된다. 다만 재시작 횟수가 증가하거나 새 Pod가 계속 0/1이면 로그와 Events를 확인한다.
9-5. 모든 백엔드 Pod를 내리고 다시 올리는 경우
서비스 중단을 허용하고 완전히 내린 뒤 설정을 교체해야 할 때만 사용한다. Pod를 단순 삭제하면 Deployment가 다시 생성하므로 replicas를 먼저 0으로 줄인다.
kubectl scale deployment demo-backend --replicas=0 -n demo
kubectl wait --for=delete pod -l app=demo-backend -n demo --timeout=60s
설정을 반영한 뒤 원하는 개수로 다시 올린다. 이 과정에서도 PVC를 삭제할 필요는 없다.
kubectl scale deployment demo-backend --replicas=1 -n demo
10. 프런트엔드 재배포 순서
10-1. 새 TLS 인증서 반영
cd /opt/deploy/frontend
kubectl create secret tls demo-tls --cert=cert/fullchain.pem --key=cert/privkey.pem -n demo --dry-run=client -o yaml | kubectl apply -f -
10-2. 수정한 YAML 적용
kubectl apply --dry-run=server -f k8s/frontend.yaml -n demo
kubectl apply -f k8s/frontend.yaml -n demo
10-3. 새 인증서로 Nginx 기동
kubectl rollout restart deployment/demo-frontend -n demo
kubectl rollout status deployment/demo-frontend -n demo --timeout=300s
kubectl get pods -l app=demo-frontend -n demo
kubectl get svc demo-frontend -n demo
kubectl logs deployment/demo-frontend -c nginx -n demo --tail=100
Secret 볼륨의 파일 갱신과 Nginx의 인증서 재읽기는 별도다. 이 절차는 새 Pod를 생성해 변경된 인증서를 읽도록 하는 방식이다. YAML 변경으로 이미 새 Pod가 생성됐다면 실제 인증서 반영 여부를 확인하고 불필요한 연속 재기동은 피한다.
10-4. CA 변경이 있는 경우
- 서버 인증서만 바뀌고 발급 CA가 같으면 클라이언트 CA 재등록이 필요하지 않을 수 있다.
- 발급 CA가 바뀌었다면 접속하는 PC에 새 CA를 등록한다.
- Pod의 외부 HTTPS 통신에도 해당 CA가 필요하면 새 프런트 Pod에 다시 반영한다.
Pod 내부 수동 CA 등록은 반드시 최종 재배포가 끝난 뒤 수행한다. 등록 직후 다시 rollout하면 수동 변경이 사라진다.
11. 증상별 점검 명령어
| 증상 | 명령어 |
|---|---|
| YAML 검증 오류 | kubectl apply --dry-run=server -f FILE.yaml -n demo |
| Pod Pending | kubectl describe pod POD_NAME -n demo |
| PVC Pending | kubectl describe pvc demo-attachments -n demo |
| 백엔드 기동 오류 | kubectl logs POD_NAME -c wildfly -n demo |
| 컨테이너 재시작 | kubectl logs POD_NAME -c wildfly -n demo --previous |
| Nginx 오류 | kubectl logs POD_NAME -c nginx -n demo |
| Service 엔드포인트 확인 | kubectl get endpointslice -n demo -l kubernetes.io/service-name=demo-backend |
| 외부 접속 포트 확인 | kubectl get svc -n demo |
| PC에서 TCP 연결 확인 | Test-NetConnection 192.0.2.34 -Port 30275 |
POD_NAME, FILE.yaml, 예시 IP와 포트는
실제 값으로 변경한다. 오류를 전달할 때는 비밀번호와 개인키,
Secret의 디코딩 값이 포함되지 않도록 확인한다.
12. 운영 시 확인할 사항과 복구 범위
12-1. 이미지는 새 태그 또는 digest로 배포
같은 태그에 이미지를 덮어쓰면 노드의 캐시와 imagePullPolicy에 따라 기대한 새 이미지가 실행되지 않을 수 있다. 릴리스마다 고유 태그를 부여하거나 digest를 지정해 배포 대상을 명확히 한다.
12-2. PVC는 일반 재배포에서 유지
Deployment 재배포와 PVC 재생성은 다른 작업이다. 스토리지 변경이 목적이 아니라면 기존 Bound PVC와 StorageClass 설정을 유지한다. ReadWriteOnce는 단일 Pod가 아니라 단일 노드의 읽기·쓰기 마운트를 의미한다.
여러 노드에서 첨부파일을 공유할 계획이면 드라이버와 스토리지가 지원하는 ReadWriteMany 등의 접근 모드를 검토한다. NFS PVC의 10Gi 요청이 실제 사용량 제한을 강제하는지는 provisioner와 NAS의 쿼터 설정을 별도로 확인한다.
12-3. Ready와 사용자 기능 검증 구분
TCP Probe의 성공은 해당 포트에 연결할 수 있다는 의미다. 프런트 화면 로딩, 로그인, 백엔드 API, DB 조회까지 모두 정상이라는 뜻은 아니다. 배포 후에는 실제 사용자 경로를 따라 기능을 점검한다.
12-4. Deployment 롤백 범위
kubectl rollout history deployment/demo-backend -n demo
kubectl rollout undo deployment/demo-backend -n demo
kubectl rollout status deployment/demo-backend -n demo --timeout=300s
위 명령은 Deployment의 Pod 템플릿 이력을 되돌리는 작업이다. ConfigMap, Secret, 인증서 파일, DB 변경과 PVC 데이터까지 복원하지 않는다. 설정과 인증서는 이전 버전을 별도로 보관해야 한다.
프런트엔드도 같은 방식으로 Deployment 이력을 확인할 수 있지만, TLS Secret이 교체된 상태라면 이미지 롤백과 인증서 복원 범위를 함께 판단한다.
공식 문서
'지식 공유 > Kubernetes' 카테고리의 다른 글
| Kubernetes NeuVector 사용법 2편: Network Activity·Groups·Security Events 읽기 (0) | 2026.10.06 |
|---|---|
| Kubernetes NeuVector 사용법 1편: 메뉴와 Dashboard 지표 이해하기 (0) | 2026.10.06 |
| NGINX Gateway Fabric 오프라인 설치: Harbor 등록부터 Gateway API 병행 검증 (0) | 2026.10.06 |
| 2026 Kubernetes 트렌드: Gateway API·AI GPU·리소스 조정·GitOps (0) | 2026.10.06 |
| Kubernetes NeuVector란? K8s 클라우드 네이티브 보안 솔루션 기능과 활용 범위 (0) | 2026.09.29 |
