1. [실습] 각각의 프로브를 사용한 livenessProbe 구성해보기
쿠버네티스에서 livenessProbe는 컨테이너가 정상적으로 동작 중인지 여부를 주기적으로 확인하여 비정상일 경우 자동으로 재시작해주는 메커니즘이다. 이 실습에서는 exec 프로브를 활용하여 /tmp/healthy 파일 존재 여부를 기준으로 헬스 체크를 수행한다.
파라미터 종류
initialDelaySeconds : 컨테이너 시작 후 헬스체크까지 대기 시간
periodSeconds : 헬스 체크 주기
timeoutSeconds : 헬스체크 명령 실행 제한 시간
successThreshold : n회 이상 성공하면 정상으로 간주
failureThreshold : n회 연속 실패시 비정상으로 간주하고 컨테이너 재시작 트리거 됨
# vi exec-liveness.yaml
apiVersion: v1
kind: Pod
metadata:
name: exec-liveness
spec:
containers:
- name: liveness
image: docker.io/library/rockylinux:9.2
args:
- /bin/sh
- -c
- "dnf install -y coreutiles && touch /tmp/healthy && sleep 15 && rm -f /tmp/healthy && sleep 300"
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
periodSeconds: 3
initialDelaySeconds: 30
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
- 컨테이너 시작 시 coreutils 설치
- /tmp/healthy 파일 생성
- 15초 후 해당 파일 삭제
- 이후 300초간 sleep
- /tmp/healthy 파일이 존재하는지 주기적으로 확인 → 없으면 liveness 실패로 간주하고 컨테이너 재시작
동작 로그 예시
---- Thu Jul 3 07:18:40 PM KST 2025 ----
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 101s default-scheduler Successfully assigned default/exec-liveness to node1.example.com
Normal Killing 65s kubelet Container liveness failed liveness probe, will be restarted
Normal Pulled 35s (x2 over 101s) kubelet Container image "docker.io/library/rockylinux:9.2" already present on machine
Normal Created 35s (x2 over 101s) kubelet Created container: liveness
Normal Started 35s (x2 over 101s) kubelet Started container liveness
Warning Unhealthy 2s (x5 over 71s) kubelet Liveness probe failed: cat: /tmp/healthy: No such file or directory
2. [실습] httpGet 명령을 사용하는 livnessProbe 실습
httpGet 프로브는 컨테이너 내부에서 HTTP 요청을 보내 해당 애플리케이션이 응답 가능한 상태인지 확인한다.
가장 일반적으로 사용되는 헬스체크 방식이다.
# vi httpd-liveness.yaml
apiVersion: v1
kind: Pod
metadata:
name: http-liveness
spec:
containers:
- name: liveness
image: registry.k8s.io/e2e-test-images/agnhost:2.40
args:
- liveness
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: Custom-Header
value: Awesome
periodSeconds: 3
동작 로그 예시
---- Thu Jul 3 07:32:20 PM KST 2025 ----
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m6s default-scheduler Successfully assigned default/http-liveness to node1.example.com
Normal Pulled 71s (x4 over 2m5s) kubelet Container image "registry.k8s.io/e2e-test-images/agnhost:2.40" already present on machine
Normal Created 71s (x4 over 2m5s) kubelet Created container: liveness
Normal Started 71s (x4 over 2m5s) kubelet Started container liveness
Normal Killing 71s (x3 over 107s) kubelet Container liveness failed liveness probe, will be restarted
Warning Unhealthy 59s (x10 over 113s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 500
3. [실습] tcpSocket 명령을 사용하는 livenessProbe 실습
tcpSocket 방식은 HTTP 요청 없이, 단순히 TCP 연결이 가능한지만 확인하는 방식이다.
주로 간단한 포트 레벨의 헬스체크에 사용된다.
# vi tcp-liveness.yaml
apiVersion: v1
kind: Pod
metadata:
name: tcp-liveness
spec:
containers:
- name: goproxy
image: registry.k8s.io/goproxy:0.1
ports:
- containerPort: 8080
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8081
initialDelaySeconds: 15
timeoutSeconds: 1
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
동작 로그 예시
---- Thu Jul 3 07:46:40 PM KST 2025 ----
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 93s default-scheduler Successfully assigned default/tcp-liveness to node1.example.com
Normal Pulled 12s (x3 over 92s) kubelet Container image "registry.k8s.io/goproxy:0.1" already present on machine
Normal Created 12s (x3 over 92s) kubelet Created container: goproxy
Normal Started 12s (x3 over 92s) kubelet Started container goproxy
Warning Unhealthy 12s (x6 over 72s) kubelet Liveness probe failed: dial tcp 10.10.11.93:8081: connect: connection refused
Normal Killing 12s (x2 over 52s) kubelet Container goproxy failed liveness probe, will be restarted
4. 초기화 컨테이너 (Init Container)
- 앱 컨테이너 실행 전에 먼저 순차적으로 실행되는 컨테이너
- 일반 컨테이너와 동일한 spec을 사용하지만, 반드시 완료(완료 상태 종료)되어야 다음 단계로 진행
- 앱 컨테이너 실행 전 사전 작업 (예: 설정 파일 생성, 외부 의존성 체크 등)에 사용
- 실패 시 성공할 때까지 재시작 반복
- restartPolicy: Never일 경우 초기화 컨테이너 실패 시 파드 전체가 실패 상태로 종료
5. 정적 파드 (Static Pod)
- API 서버와 무관하게 kubelet이 직접 관리하는 파드
- 주로 노드에 고정적으로 동작해야 하는 시스템 컴포넌트나 관리 목적 파드에 사용
- Kubelet은 /etc/kubernetes/manifests/ 디렉토리를 주기적으로 감시하며, 여기에 YAML 파일이 생성되면 자동 실행
- 정적 파드의 이름에는 호스트 이름 앞에 하이픈(-)을 붙여 접미사로 추가됨
6. 자원 요청 & 제한
- requests: 파드가 최소한으로 보장받아야 할 리소스
- limits: 파드가 사용할 수 있는 최대 리소스 한도
- Pod에서 컨테이너에 대한 리소스 요청을 지정하면 kube-scheduler는 이 정보를 사용하여 Pod가 배치될 노드를 결정
1) CPU 자원 요청과 상한 제한
- Pod 스케줄링은 CPU 자원 요청을 충분히 만족하는 경우에만 노드에 스케줄링됨
- CPU는 탄력적인 자원으로, 제한을 초과해도 OOM처럼 종료되지는 않지만, 성능 저하가 발생할 수 있음
- requests를 만족하지 못하는 경우 파드는 Pending 상태에 머무름
2) MEM 자원 요청과 상한 제한
- Memory는 고정 자원으로 초과 사용이 불가하며, limit 초과 시 즉시 종료됨
- 종료된 컨테이너는 kubelet에 의해 자동 재시작
- requests를 만족하지 못하면 마찬가지로 파드는 Pending 상태로 남음
3) 예시
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod-resource
spec:
containers:
- name: nginx-container
image: nginx:1.14
ports:
- containerPort: 80
protocol: TCP
resources:
requests:
cpu: 200m
memory: 250Mi
limits:
cpu: 1
memory: 500Mi
# kubectl describe pod nginx-pod-resource

7. 파드 환경변수
- 파드 내 컨테이너가 실행될 때 필요한 값을 환경변수(env) 형태로 선언
- 실행 시점에 앱 설정, 외부 정보, 비밀값 등을 주입하기 위해 사용
- 파드 필드 또는 컨테이너 필드 정보를 노출할 수 있음
- 환경 변수 선언 방법
- yaml 파일에 env:에 선언
- kubectl run 명령어에 --env=에 선언
8. 다중 컨테이너 Pod 디자인 패턴
Kubernetes는 여러 컨테이너를 하나의 파드에 묶어서 구성할 수 있으며, 이때 각 컨테이너의 역할에 따라 설계 패턴을 적용한다.
대표적으로 Sidecar, Adapter, Ambassador 패턴이 있다.
1) Sidecar 패턴
- 보조 역할 컨테이너가 메인 애플리케이션을 보완
- 로깅, 모니터링, 설정 동기화 등에 사용
예제 YAML
apiVersion: v1
kind: Pod
metadata:
name: sidecar
spec:
volumes:
- name: sharedlogs
emptyDir: {}
containers:
- name: app-container
image: alpine
command: ["/bin/sh"]
args: ["-c", "while true; do date >> /var/log/app.log; sleep 5; done"]
volumeMounts:
- name: sharedlogs
mountPath: /var/log
- name: sidecar-container
image: nginx:1.14
ports:
- containerPort: 80
volumeMounts:
- name: sharedlogs
mountPath: /usr/share/nginx/html

2) Adapter 패턴
- 출력 데이터를 표준 포맷으로 가공하는 중간 컨테이너
- 모니터링, 수집기 시스템과 연동 시 주로 사용
예시 YAML
apiVersion: v1
kind: Pod
metadata:
name: adapter
spec:
volumes:
- name: sharedlogs
emptyDir: {}
containers:
- name: app-container
image: alpine
command: ["/bin/sh"]
args: ["-c", "while true; do date > /var/log/top.txt && top -n 1 -b >> /var/log/top.txt; sleep 5; done"]
volumeMounts:
- name: sharedlogs
mountPath: /var/log
- name: adapter-container
image: alpine
command: ["/bin/sh"]
args: ["-c", "while true; do (echo '<pre>' > /var/log/status.html) && \
(cat /var/log/top.txt | head -1 >> /var/log/status.html) && \
(cat /var/log/top.txt | grep '^Mem:' | awk -F, '{print $1}' >> /var/log/status.html) && \
(cat /var/log/top.txt | grep '^CPU:' | awk '{print $1, $8, $9}' >> /var/log/status.html) && \
(echo '</pre>' >> /var/log/status.html); sleep 5; done"]
volumeMounts:
- name: sharedlogs
mountPath: /var/log
- name: sidecar-container
image: nginx:1.14
ports:
- containerPort: 80
volumeMounts:
- name: sharedlogs
mountPath: /usr/share/nginx/html
- app-container
- 생산자 역할
- 5초마다 시스템의 현재 시간과 top 명령어 결과를 /var/log/top.txt에 저장
- top -n 1 -b 명령어로 현재 CPU, 메모리 등 자원 사용 상태 출력
- adapter-container
- 어댑터 역할
- top.txt 파일을 읽고 원하는 정보만 추출하며 status.html 파일로 가공
- 포함되는 정보: 현재 시간, 메모리 사용량, CPU 사용량
- 출력 형식은 HTML -> 웹 브라우저에서 보기 좋게 출력
- sidecar-container
- 웹 서버 역할
- nginx가 실행되며 /usr/share/nginx/html 디렉토리를 서비스
- adapter-container가 생성한 status.html 파일을 웹 페이지로 제공
=> app-container가 데이터를 만들고, adapter-container가 가공하며, sidecar-container가 사용자에게 제공하는
Producer-Adapter-Viewer 구조
3) Ambassador 패턴
- 애플리케이션과 외부 세계 사이에서 프록시 역할 수행
- 외부 API 요청시 Ambassador 컨테이너가 API 게이트웨이처럼 중계
- 인증, 보안, 트래픽 조정 등을 담당하는 프록시/브로커 컨테이너
✍️ 하루 회고
오늘은 어제 개념만 훑어봤던 프로브 실습을 시작으로 환경 변수, 초기화 컨테이너, 정적 파드, 자원 요청과 제한, 그리고 다중 컨테이너 파드 디자인 패턴까지 정말 다양한 쿠버네티스 개념들을 학습하고 실습해보았다.
단순히 문서로 개념만 보는 것과 실제로 손으로 YAML을 작성하고 파드 상태를 확인하는 경험은 확실히 다르다.
직접 실습을 통해 눈으로 확인하고 손으로 쳐보는 과정이 반복되니, 처음엔 낯설게 느껴졌던 쿠버네티스 코드와 구조도 점점 익숙해지는 것 같다.
아직 복습이 부족해서 개념을 바로바로 설명하긴 어렵지만, 부트캠프에서 제공된 서적과 함께 자격증 준비를 병행하면서 더 탄탄히 다져나갈 계획이다.
이런 과정을 통해 더 능숙하게 쿠버네티스를 다룰 수 있을 거라는 확신이 생긴 하루였다. 💪
