본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 22. · 3 Views
마이크로서비스 배포 완벽 가이드
Kubernetes를 활용한 마이크로서비스 배포의 핵심 개념부터 실전 운영까지, 초급 개발자도 쉽게 따라할 수 있는 완벽 가이드입니다. 실무에서 바로 적용 가능한 배포 전략과 노하우를 담았습니다.
목차
1. Deployment YAML 작성
어느 날 신입 개발자 김개발 씨가 처음으로 마이크로서비스를 배포하라는 미션을 받았습니다. "Deployment YAML을 작성해서 배포해보세요." 팀장님의 말씀이 떨어졌지만, 막상 어디서부터 시작해야 할지 막막하기만 합니다.
Deployment는 Kubernetes에서 애플리케이션을 배포하고 관리하는 핵심 리소스입니다. 마치 레시피처럼 "어떤 컨테이너를 몇 개나 실행할지, 어떻게 업데이트할지"를 정의합니다.
Deployment를 제대로 작성하면 무중단 배포와 자동 복구가 가능해집니다.
다음 코드를 살펴봅시다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
labels:
app: order-service
spec:
replicas: 3 # 3개의 Pod를 실행합니다
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: myregistry/order-service:v1.0.0
ports:
- containerPort: 8080 # 서비스 포트를 노출합니다
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
김개발 씨는 입사 2주 차 신입 개발자입니다. 오늘 처음으로 실제 서비스를 Kubernetes에 배포하라는 미션을 받았습니다.
선배 개발자 박시니어 씨가 옆자리에서 말합니다. "일단 Deployment YAML부터 작성해봐요." 그렇다면 Deployment란 정확히 무엇일까요?
쉽게 비유하자면, Deployment는 마치 공장의 생산 계획서와 같습니다. "이 제품을 몇 개 만들고, 어떤 재료를 사용하며, 문제가 생기면 어떻게 대응할지"를 상세히 적어놓은 문서입니다.
Kubernetes는 이 계획서를 보고 실제로 애플리케이션을 실행하고 관리합니다. Deployment가 없던 시절에는 어땠을까요?
개발자들은 서버에 직접 SSH로 접속해서 애플리케이션을 실행해야 했습니다. 서버가 다운되면?
직접 재시작해야 했습니다. 업데이트가 필요하면?
서비스를 중단하고 새 버전을 배포해야 했습니다. 더 큰 문제는 여러 서버에 동일한 작업을 반복해야 한다는 점이었습니다.
바로 이런 문제를 해결하기 위해 Deployment가 등장했습니다. Deployment를 사용하면 선언적 배포가 가능해집니다.
"현재 상태를 이렇게 만들어주세요"라고 요청하면, Kubernetes가 알아서 그 상태를 만들고 유지합니다. 또한 롤링 업데이트로 무중단 배포도 얻을 수 있습니다.
무엇보다 자동 복구라는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 apiVersion과 kind는 "이것이 Deployment 리소스입니다"라고 선언하는 부분입니다. metadata에서는 이름과 라벨을 지정합니다.
다음으로 spec.replicas에서는 "Pod를 3개 실행하라"고 명시합니다. selector는 "어떤 Pod를 관리할지" 선택하는 기준입니다.
template 안에는 실제 Pod의 스펙이 들어갑니다. containers 섹션이 가장 중요합니다.
여기서 실행할 컨테이너 이미지와 포트를 지정합니다. resources에서는 CPU와 메모리 제한을 설정합니다.
requests는 "최소한 이만큼은 보장해달라"는 의미이고, limits는 "이 이상은 사용하지 못하게 막아달라"는 의미입니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 쇼핑몰의 주문 서비스를 개발한다고 가정해봅시다. 평소에는 트래픽이 적지만, 특가 이벤트 때는 급증합니다.
Deployment로 배포하면 replicas 값만 조정해서 손쉽게 서버를 늘리거나 줄일 수 있습니다. 많은 기업에서 이런 패턴을 적극적으로 사용하고 있습니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 리소스 제한을 설정하지 않는 것입니다.
이렇게 하면 한 Pod가 메모리를 무한정 사용해서 다른 Pod까지 영향을 줄 수 있습니다. 따라서 항상 적절한 requests와 limits를 설정해야 합니다.
또 다른 실수는 이미지 태그를 latest로 지정하는 것입니다. 이렇게 하면 어떤 버전이 배포되었는지 추적하기 어렵습니다.
반드시 명확한 버전 태그를 사용하세요. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "아, Deployment가 이런 거였군요!" Deployment를 제대로 이해하면 안정적이고 확장 가능한 서비스를 배포할 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 이미지 태그는 항상 명확한 버전을 사용하세요 (latest 대신 v1.0.0)
- 리소스 제한을 반드시 설정하세요 (메모리 누수 방지)
- replicas는 최소 2개 이상 유지하세요 (고가용성 확보)
2. Service YAML 작성
Deployment로 Pod를 3개 띄웠는데, 이제 어떻게 접속해야 할까요? 김개발 씨가 박시니어 씨에게 물었습니다.
"Pod IP로 직접 접속하면 되나요?" 박시니어 씨가 미소를 지으며 답합니다. "그럼 Pod가 재시작될 때마다 IP가 바뀌는데 어떻게 하려고요?"
Service는 여러 Pod에 대한 안정적인 네트워크 엔드포인트를 제공합니다. 마치 회사 대표 전화번호처럼, 내부 직원이 바뀌어도 같은 번호로 연결됩니다.
Service를 사용하면 로드 밸런싱과 서비스 디스커버리가 자동으로 처리됩니다.
다음 코드를 살펴봅시다.
apiVersion: v1
kind: Service
metadata:
name: order-service
labels:
app: order-service
spec:
type: ClusterIP # 클러스터 내부에서만 접근 가능
selector:
app: order-service # 이 라벨을 가진 Pod로 트래픽 전달
ports:
- name: http
protocol: TCP
port: 80 # Service가 노출하는 포트
targetPort: 8080 # Pod의 실제 포트
sessionAffinity: ClientIP # 같은 클라이언트는 같은 Pod로 연결
김개발 씨는 Deployment로 주문 서비스 Pod 3개를 성공적으로 띄웠습니다. 신나서 다른 서비스에서 접속을 시도했는데, 문제가 생겼습니다.
Pod가 재시작될 때마다 IP 주소가 바뀌어서 연결이 끊어집니다. 어떻게 해야 할까요?
그렇다면 Service란 정확히 무엇일까요? 쉽게 비유하자면, Service는 마치 회사의 대표 전화번호와 같습니다.
고객은 항상 같은 번호로 전화를 걸지만, 실제로는 여러 직원 중 한 명에게 연결됩니다. 직원이 퇴사하거나 새로 입사해도 대표 번호는 바뀌지 않습니다.
이처럼 Service도 Pod가 재시작되어 IP가 바뀌어도 동일한 엔드포인트를 제공합니다. Service가 없던 시절에는 어땠을까요?
개발자들은 각 Pod의 IP 주소를 직접 관리해야 했습니다. 설정 파일에 IP 목록을 적어두고, Pod가 재시작되면 일일이 업데이트해야 했습니다.
로드 밸런싱도 직접 구현해야 했습니다. 더 큰 문제는 여러 마이크로서비스가 서로를 찾는 것이었습니다.
서비스가 많아질수록 관리는 악몽이 되었습니다. 바로 이런 문제를 해결하기 위해 Service가 등장했습니다.
Service를 사용하면 안정적인 DNS 이름으로 접근이 가능해집니다. 다른 서비스는 order-service라는 이름만 알면 됩니다.
또한 자동 로드 밸런싱도 얻을 수 있습니다. 무엇보다 서비스 디스커버리가 자동으로 처리된다는 큰 이점이 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 type: ClusterIP는 "클러스터 내부에서만 접근 가능한 Service"를 만들라는 의미입니다.
외부에 노출하려면 LoadBalancer나 NodePort를 사용합니다. selector가 핵심입니다.
여기 지정된 라벨과 일치하는 모든 Pod로 트래픽을 분산합니다. ports 섹션에서 port: 80은 "Service는 80번 포트로 요청을 받는다"는 의미입니다.
targetPort: 8080은 "실제 Pod의 8080번 포트로 전달한다"는 의미입니다. 마치 변환기처럼 포트 번호를 바꿀 수 있습니다.
sessionAffinity: ClientIP는 같은 클라이언트의 요청을 항상 같은 Pod로 보냅니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 마이크로서비스 아키텍처를 구축한다고 가정해봅시다. 주문 서비스, 결제 서비스, 재고 서비스가 서로 통신해야 합니다.
Service를 사용하면 각 서비스는 **http://payment-service**처럼 DNS 이름으로 다른 서비스를 호출할 수 있습니다. Pod가 몇 개든, IP가 어떻게 바뀌든 상관없습니다.
많은 기업에서 이런 패턴을 적극적으로 사용하고 있습니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 selector를 잘못 지정하는 것입니다. Deployment의 라벨과 Service의 selector가 일치하지 않으면 트래픽이 전달되지 않습니다.
이렇게 하면 "Service는 있는데 연결이 안 된다"는 문제가 발생합니다. 따라서 라벨을 정확히 일치시켜야 합니다.
또 다른 실수는 Service 타입을 잘못 선택하는 것입니다. 내부 통신에는 ClusterIP, 외부 노출에는 LoadBalancer를 사용해야 합니다.
보안을 고려하지 않고 모든 서비스를 LoadBalancer로 노출하면 위험합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 "아하!"를 외쳤습니다. "그래서 Deployment와 Service를 항상 같이 만드는 거군요!" Service를 제대로 이해하면 안정적인 마이크로서비스 네트워킹을 구축할 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - selector와 Deployment 라벨을 정확히 일치시키세요 (연결 실패 방지)
- 내부 통신에는 ClusterIP를 사용하세요 (보안 강화)
- 포트 이름을 명확하게 지정하세요 (http, grpc 등)
3. ConfigMap 연동
드디어 서비스가 잘 동작합니다! 하지만 김개발 씨에게 새로운 고민이 생겼습니다.
개발 환경과 운영 환경에서 데이터베이스 주소가 다른데, 매번 이미지를 다시 빌드해야 할까요? 박시니어 씨가 웃으며 말합니다.
"ConfigMap을 사용하면 코드 변경 없이 설정만 바꿀 수 있어요."
ConfigMap은 애플리케이션 설정 정보를 컨테이너 이미지와 분리해서 관리하는 리소스입니다. 마치 환경 변수 저장소처럼, 데이터베이스 주소나 API 키 같은 설정을 외부에서 주입합니다.
ConfigMap을 사용하면 동일한 이미지로 여러 환경을 운영할 수 있습니다.
다음 코드를 살펴봅시다.
apiVersion: v1
kind: ConfigMap
metadata:
name: order-service-config
data:
DATABASE_URL: "postgres://db.example.com:5432/orders"
LOG_LEVEL: "info"
MAX_CONNECTIONS: "100"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
template:
spec:
containers:
- name: order-service
image: myregistry/order-service:v1.0.0
envFrom:
- configMapRef:
name: order-service-config # ConfigMap 전체를 환경변수로 주입
env:
- name: SERVICE_NAME
valueFrom:
configMapKeyRef:
name: order-service-config
key: SERVICE_NAME # 특정 키만 가져오기
김개발 씨는 주문 서비스를 개발 환경에 성공적으로 배포했습니다. 이제 운영 환경에도 배포하려는데 문제가 생겼습니다.
데이터베이스 주소가 다릅니다. 코드에 하드코딩된 설정을 바꾸려면 이미지를 다시 빌드해야 할까요?
그렇다면 ConfigMap이란 정확히 무엇일까요? 쉽게 비유하자면, ConfigMap은 마치 설정 파일 보관함과 같습니다.
프로그램은 동일하지만, 설정 파일만 바꿔서 다른 환경에서 실행할 수 있습니다. 집에서는 와이파이 설정이 A이고, 회사에서는 B인 것처럼, 같은 노트북이지만 환경에 따라 다른 설정을 사용합니다.
이처럼 ConfigMap도 동일한 컨테이너 이미지에 다른 설정을 주입할 수 있습니다. ConfigMap이 없던 시절에는 어땠을까요?
개발자들은 환경별로 다른 이미지를 빌드해야 했습니다. order-service-dev, order-service-prod처럼 말이죠.
설정 하나 바꾸려면 전체 이미지를 다시 빌드하고 배포해야 했습니다. 더 큰 문제는 민감한 정보가 이미지에 포함되어 보안 위험이 있다는 점이었습니다.
환경이 많아질수록 관리는 복잡해졌습니다. 바로 이런 문제를 해결하기 위해 ConfigMap이 등장했습니다.
ConfigMap을 사용하면 설정과 코드의 분리가 가능해집니다. 동일한 이미지를 여러 환경에서 재사용할 수 있습니다.
또한 설정 변경이 간편해집니다. 무엇보다 배포 파이프라인이 단순해진다는 큰 이점이 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 ConfigMap 리소스를 정의합니다.
data 섹션에 키-값 쌍으로 설정을 작성합니다. 모두 문자열로 저장됩니다.
다음으로 Deployment에서 이 ConfigMap을 참조합니다. envFrom을 사용하면 ConfigMap의 모든 키가 환경변수로 주입됩니다.
예를 들어 DATABASE_URL은 컨테이너 안에서 process.env.DATABASE_URL로 접근할 수 있습니다. env 섹션에서는 특정 키만 선택적으로 가져올 수도 있습니다.
파일로도 마운트할 수 있습니다. volumeMounts와 volumes를 사용하면 ConfigMap을 파일 시스템에 마운트할 수 있습니다.
설정 파일이 필요한 애플리케이션에 유용합니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 글로벌 서비스를 운영한다고 가정해봅시다. 한국, 미국, 유럽 리전에 동일한 서비스를 배포하지만 각 리전의 데이터베이스 주소가 다릅니다.
ConfigMap을 리전별로 만들면 동일한 이미지를 모든 리전에 배포할 수 있습니다. 많은 기업에서 이런 패턴을 적극적으로 사용하고 있습니다.
또 다른 예시는 피처 플래그입니다. 신기능을 점진적으로 출시할 때 ConfigMap에 FEATURE_NEW_PAYMENT=true처럼 설정하고, 문제가 생기면 즉시 false로 바꿀 수 있습니다.
코드 배포 없이 기능을 제어할 수 있습니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 민감한 정보를 ConfigMap에 저장하는 것입니다. 비밀번호나 API 키는 ConfigMap이 아닌 Secret을 사용해야 합니다.
ConfigMap은 암호화되지 않기 때문입니다. 이렇게 하면 보안 취약점이 생길 수 있습니다.
또 다른 실수는 ConfigMap 변경 후 Pod를 재시작하지 않는 것입니다. 환경변수로 주입된 ConfigMap은 Pod 재시작 전까지 반영되지 않습니다.
따라서 설정 변경 후 kubectl rollout restart로 재시작해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 감탄했습니다. "와, 이제 환경별로 이미지를 따로 안 만들어도 되겠네요!" ConfigMap을 제대로 이해하면 유연하고 관리하기 쉬운 배포 시스템을 구축할 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 민감한 정보는 Secret을 사용하세요 (ConfigMap은 암호화되지 않음)
- ConfigMap 변경 후 Pod를 재시작하세요 (환경변수 반영)
- 네이밍 규칙을 정하세요 (예: {service-name}-config)
4. Secret 관리
ConfigMap으로 설정을 관리하던 김개발 씨가 문득 궁금해졌습니다. "데이터베이스 비밀번호도 ConfigMap에 넣으면 되나요?" 박시니어 씨가 놀라며 손을 내저었습니다.
"절대 안 됩니다! 민감한 정보는 Secret으로 관리해야 해요."
Secret은 비밀번호, API 키, 인증서 같은 민감한 정보를 안전하게 저장하는 리소스입니다. ConfigMap과 비슷하지만 base64로 인코딩되고 접근 권한이 제한됩니다.
Secret을 사용하면 민감한 정보를 코드나 이미지에서 완전히 분리할 수 있습니다.
다음 코드를 살펴봅시다.
apiVersion: v1
kind: Secret
metadata:
name: order-service-secret
type: Opaque
data:
# base64로 인코딩된 값 (echo -n 'mypassword' | base64)
DATABASE_PASSWORD: bXlwYXNzd29yZA==
API_KEY: c2VjcmV0LWFwaS1rZXktMTIzNDU2
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
template:
spec:
containers:
- name: order-service
image: myregistry/order-service:v1.0.0
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: order-service-secret
key: DATABASE_PASSWORD # Secret에서 특정 키만 가져오기
- name: API_KEY
valueFrom:
secretKeyRef:
name: order-service-secret
key: API_KEY
김개발 씨는 ConfigMap으로 설정을 잘 관리하고 있었습니다. 그런데 데이터베이스 비밀번호를 어디에 저장해야 할지 고민이 생겼습니다.
ConfigMap에 넣으면 편리할 것 같은데, 뭔가 찝찝합니다. 정말 안전할까요?
그렇다면 Secret이란 정확히 무엇일까요? 쉽게 비유하자면, Secret은 마치 금고와 같습니다.
ConfigMap이 일반 서랍이라면, Secret은 자물쇠가 달린 금고입니다. 누구나 서랍을 열 수 있지만, 금고는 권한이 있는 사람만 열 수 있습니다.
중요한 문서는 금고에 보관하듯이, 비밀번호 같은 민감한 정보는 Secret에 보관합니다. Secret이 없던 시절에는 어땠을까요?
개발자들은 비밀번호를 환경변수로 직접 설정하거나, 심지어 코드에 하드코딩했습니다. Git 저장소에 비밀번호가 올라가는 사고도 빈번했습니다.
누군가 코드를 훔쳐보면 모든 비밀번호가 노출되었습니다. 더 큰 문제는 여러 환경의 비밀번호를 관리하는 것이었습니다.
수백 개의 서비스에 각각 다른 비밀번호를 어떻게 관리할까요? 바로 이런 문제를 해결하기 위해 Secret이 등장했습니다.
Secret을 사용하면 민감한 정보의 중앙 관리가 가능해집니다. Git 저장소에 비밀번호를 올릴 필요가 없습니다.
또한 권한 기반 접근 제어도 얻을 수 있습니다. 무엇보다 비밀번호 변경이 간편해진다는 큰 이점이 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 Secret 리소스를 정의합니다.
type: Opaque는 "일반적인 키-값 Secret"을 의미합니다. data 섹션의 값은 반드시 base64로 인코딩해야 합니다.
터미널에서 echo -n 'mypassword' | base64 명령으로 인코딩할 수 있습니다. stringData를 사용하면 평문으로 작성할 수도 있습니다.
Kubernetes가 자동으로 base64로 변환합니다. 하지만 YAML 파일에 평문이 노출되므로 주의해야 합니다.
Deployment에서는 secretKeyRef로 Secret을 참조합니다. ConfigMap의 configMapKeyRef와 비슷합니다.
컨테이너 안에서는 일반 환경변수처럼 접근할 수 있습니다. 파일로도 마운트할 수 있습니다.
TLS 인증서처럼 파일 형태가 필요한 경우 volumes와 volumeMounts를 사용합니다. Secret이 파일 시스템에 마운트되고, 애플리케이션이 파일을 읽습니다.
실제 현업에서는 어떨게 활용할까요? 예를 들어 결제 서비스를 개발한다고 가정해봅시다.
PG사 API 키, 데이터베이스 비밀번호, JWT 서명 키 등 수많은 비밀 정보가 필요합니다. Secret으로 관리하면 개발자는 실제 값을 몰라도 됩니다.
운영팀만 Secret을 생성하고, 개발자는 Secret 이름만 참조합니다. 많은 기업에서 이런 패턴을 적극적으로 사용하고 있습니다.
또 다른 예시는 TLS 인증서 관리입니다. HTTPS를 위한 인증서와 개인키를 Secret에 저장하고, Ingress에서 참조합니다.
인증서가 만료되면 Secret만 업데이트하면 됩니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 Secret YAML을 Git에 커밋하는 것입니다. base64 인코딩은 암호화가 아닙니다.
누구나 디코딩할 수 있습니다. 이렇게 하면 Secret의 의미가 없어집니다.
따라서 Secret YAML은 절대 Git에 올리지 말고, CI/CD 파이프라인에서 생성해야 합니다. 또 다른 실수는 Secret을 너무 많이 만드는 것입니다.
관련된 비밀 정보는 하나의 Secret에 모아두는 것이 관리하기 쉽습니다. 하지만 서로 다른 권한이 필요하면 분리해야 합니다.
실무에서는 Sealed Secrets나 External Secrets Operator 같은 도구를 많이 사용합니다. 이들은 Secret을 암호화해서 Git에 안전하게 저장할 수 있게 해줍니다.
AWS Secrets Manager나 HashiCorp Vault 같은 외부 시스템과 연동할 수도 있습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 식은땀을 흘렸습니다. "위험할 뻔했네요.
거의 ConfigMap에 비밀번호를 넣을 뻔했어요!" Secret을 제대로 이해하면 안전한 마이크로서비스를 구축할 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - Secret YAML은 Git에 절대 커밋하지 마세요 (base64는 암호화가 아님)
- Sealed Secrets나 External Secrets를 고려하세요 (안전한 Secret 관리)
- 최소 권한 원칙을 적용하세요 (필요한 Pod만 Secret에 접근)
5. kubectl 명령어
Deployment, Service, ConfigMap, Secret까지 모든 YAML을 작성했습니다. 이제 실제로 배포할 차례입니다.
김개발 씨가 터미널을 열고 명령어를 입력하려는데, 손이 멈칫합니다. "어떤 명령어를 사용해야 하지?" 박시니어 씨가 옆에서 말합니다.
"kubectl의 핵심 명령어 몇 가지만 알면 충분해요."
kubectl은 Kubernetes 클러스터를 제어하는 CLI 도구입니다. 마치 리모컨처럼, 클러스터의 모든 리소스를 생성하고 조회하고 수정하고 삭제할 수 있습니다.
kubectl 명령어를 숙달하면 배포와 운영이 훨씬 수월해집니다.
다음 코드를 살펴봅시다.
# YAML 파일로 리소스 생성
kubectl apply -f deployment.yaml
# 여러 파일 한번에 적용
kubectl apply -f k8s/
# Deployment 조회
kubectl get deployments
# Pod 상세 정보 보기
kubectl describe pod order-service-abc123
# Pod 로그 확인 (실시간)
kubectl logs -f order-service-abc123
# 컨테이너 안으로 접속
kubectl exec -it order-service-abc123 -- /bin/sh
# ConfigMap 수정 후 재시작
kubectl rollout restart deployment/order-service
# 배포 히스토리 보기
kubectl rollout history deployment/order-service
# 이전 버전으로 롤백
kubectl rollout undo deployment/order-service
김개발 씨는 모든 YAML 파일을 준비했습니다. Deployment도 있고, Service도 있고, ConfigMap과 Secret도 만들었습니다.
이제 실제로 클러스터에 배포할 차례입니다. 하지만 명령어가 너무 많아 보입니다.
어디서부터 시작해야 할까요? 그렇다면 kubectl이란 정확히 무엇일까요?
쉽게 비유하자면, kubectl은 마치 TV 리모컨과 같습니다. TV의 모든 기능을 리모컨으로 제어합니다.
채널을 바꾸고, 볼륨을 조절하고, 전원을 끕니다. 이처럼 kubectl도 Kubernetes 클러스터의 모든 것을 제어합니다.
리소스를 만들고, 상태를 확인하고, 문제를 해결합니다. kubectl이 없던 시절에는 어땠을까요?
개발자들은 Kubernetes API를 직접 호출해야 했습니다. HTTP 요청을 만들고, JSON을 파싱하고, 인증 토큰을 관리했습니다.
간단한 작업도 복잡한 코드가 필요했습니다. 더 큰 문제는 실수하기 쉽다는 점이었습니다.
API 스펙을 정확히 알아야 했고, 한 글자만 틀려도 에러가 발생했습니다. 바로 이런 문제를 해결하기 위해 kubectl이 등장했습니다.
kubectl을 사용하면 직관적인 명령어로 모든 작업이 가능해집니다. 자동 완성과 도움말도 제공됩니다.
또한 YAML 파일로 선언적 관리도 얻을 수 있습니다. 무엇보다 실수를 줄이고 생산성을 높인다는 큰 이점이 있습니다.
위의 코드를 하나씩 살펴보겠습니다. 가장 중요한 명령어는 kubectl apply입니다.
YAML 파일을 읽어서 리소스를 생성하거나 업데이트합니다. 이미 존재하면 변경사항만 적용합니다.
-f 옵션으로 파일이나 디렉토리를 지정할 수 있습니다. kubectl get은 리소스 목록을 조회합니다.
get pods, get services, get deployments처럼 사용합니다. -o wide 옵션을 추가하면 더 많은 정보를 볼 수 있습니다.
-o yaml을 사용하면 전체 YAML 정의를 출력합니다. kubectl describe는 특정 리소스의 상세 정보를 보여줍니다.
Pod가 왜 시작되지 않는지, 어떤 이벤트가 발생했는지 확인할 때 유용합니다. 문제 해결의 첫 번째 단계입니다.
kubectl logs는 Pod의 로그를 출력합니다. -f 옵션을 추가하면 실시간으로 로그를 볼 수 있습니다.
마치 tail -f처럼 말이죠. 여러 컨테이너가 있으면 -c container-name으로 특정 컨테이너를 선택합니다.
kubectl exec는 실행 중인 컨테이너 안으로 접속합니다. 디버깅할 때 매우 유용합니다.
파일 시스템을 확인하거나, 네트워크를 테스트하거나, 임시로 명령어를 실행할 수 있습니다. kubectl rollout은 배포 관련 명령어입니다.
restart로 모든 Pod를 재시작하고, status로 배포 상태를 확인하고, undo로 이전 버전으로 롤백합니다. 무중단 배포의 핵심입니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 새로운 버전을 배포하는 상황을 가정해봅시다.
먼저 kubectl apply -f deployment.yaml로 새 버전을 배포합니다. kubectl rollout status로 배포 진행 상황을 실시간으로 확인합니다.
문제가 발생하면 kubectl logs로 에러를 확인하고, 심각하면 kubectl rollout undo로 즉시 롤백합니다. 많은 기업에서 이런 패턴을 적극적으로 사용하고 있습니다.
또 다른 예시는 긴급 상황 대응입니다. 운영 중 서비스에 문제가 생겼습니다.
kubectl get pods로 Pod 상태를 확인하고, kubectl describe pod로 문제 원인을 파악하고, kubectl logs로 에러 로그를 분석합니다. 필요하면 kubectl exec로 컨테이너에 접속해 직접 조사합니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 kubectl delete를 너무 쉽게 사용하는 것입니다.
운영 환경에서 실수로 중요한 리소스를 삭제하면 서비스 장애가 발생합니다. 이렇게 하면 큰 사고로 이어질 수 있습니다.
따라서 삭제 전에 항상 두 번 확인해야 합니다. 또 다른 실수는 네임스페이스를 지정하지 않는 것입니다.
-n namespace 옵션을 빠뜨리면 default 네임스페이스에서만 조회됩니다. 운영 환경은 보통 별도 네임스페이스를 사용하므로 주의해야 합니다.
실무 팁을 몇 가지 더 드리겠습니다. kubectl을 k로 alias 설정하면 타이핑이 줄어듭니다.
kubectl get pods -w로 Pod 상태를 실시간 모니터링할 수 있습니다. kubectl port-forward로 로컬에서 Pod에 직접 접속할 수 있습니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 자신감이 생겼습니다.
"생각보다 명령어가 많지 않네요. 이 정도면 충분히 할 수 있겠어요!" kubectl을 제대로 이해하면 Kubernetes를 자유자재로 다룰 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - kubectl을 k로 alias 설정하세요 (alias k=kubectl)
- 명령어 자동 완성을 설정하세요 (source <(kubectl completion bash))
- 네임스페이스를 항상 명시하세요 (-n 옵션 또는 context 설정)
6. 배포 상태 확인
드디어 kubectl apply로 배포를 완료했습니다! 김개발 씨가 기뻐하며 팀장님께 보고하려는 순간, 박시니어 씨가 말립니다.
"잠깐만요. 정말 잘 배포되었는지 확인부터 해야죠.
상태 확인은 배포의 절반입니다."
배포 상태 확인은 리소스가 정상적으로 작동하는지 검증하는 필수 과정입니다. Pod가 Running 상태인지, Readiness Probe가 성공하는지, 로그에 에러가 없는지 확인합니다.
제대로 확인하지 않으면 배포는 성공했지만 서비스는 동작하지 않는 상황이 발생할 수 있습니다.
다음 코드를 살펴봅시다.
# Pod 상태 확인 (STATUS가 Running이어야 함)
kubectl get pods -l app=order-service
# 배포 상태 실시간 확인
kubectl rollout status deployment/order-service
# Pod 상세 정보 (Events 섹션 확인)
kubectl describe pod order-service-abc123
# 로그 확인 (에러 메시지 찾기)
kubectl logs order-service-abc123 | grep -i error
# 여러 Pod의 로그 동시에 보기
kubectl logs -l app=order-service --tail=100
# Readiness/Liveness Probe 확인
kubectl get pods -o wide
# Service 엔드포인트 확인
kubectl get endpoints order-service
# 클러스터 내부에서 서비스 테스트
kubectl run curl-test --image=curlimages/curl -i --rm --restart=Never -- curl http://order-service/health
김개발 씨는 kubectl apply로 배포를 완료했습니다. 화면에 "deployment.apps/order-service configured"라는 메시지가 나타났습니다.
성공인 것 같습니다. 바로 팀장님께 보고해도 될까요?
그렇다면 배포 상태 확인이란 정확히 무엇일까요? 쉽게 비유하자면, 배포 상태 확인은 마치 요리 후 맛을 보는 것과 같습니다.
레시피대로 요리를 완성했다고 해서 끝이 아닙니다. 맛을 보고, 간을 확인하고, 온도를 체크해야 합니다.
이처럼 배포도 명령어를 실행했다고 끝이 아닙니다. 실제로 Pod가 실행되는지, 에러는 없는지, 트래픽을 받을 준비가 되었는지 확인해야 합니다.
배포 상태 확인이 없던 시절에는 어떨까요? 개발자들은 배포 후 사용자의 신고로 문제를 발견했습니다.
"서비스가 안 돼요!"라는 연락을 받고서야 Pod가 크래시 루프에 빠진 걸 알았습니다. 설정 오류로 데이터베이스에 연결되지 않았는데도 모르고 있었습니다.
더 큰 문제는 배포와 장애 사이의 시간 차였습니다. 몇 시간 후에 문제를 발견하면 원인 파악이 어려웠습니다.
바로 이런 문제를 해결하기 위해 체계적인 상태 확인이 중요해졌습니다. 상태 확인을 철저히 하면 조기 문제 발견이 가능해집니다.
사용자보다 먼저 문제를 인지할 수 있습니다. 또한 롤백 결정도 빠르게 내릴 수 있습니다.
무엇보다 서비스 안정성이 크게 향상된다는 이점이 있습니다. 위의 코드를 하나씩 살펴보겠습니다.
가장 먼저 할 일은 kubectl get pods입니다. STATUS 컬럼을 확인합니다.
Running이어야 정상입니다. Pending이면 리소스 부족이나 스케줄링 문제입니다.
CrashLoopBackOff는 Pod가 계속 재시작되는 상태입니다. ImagePullBackOff는 이미지를 다운로드할 수 없다는 의미입니다.
READY 컬럼도 중요합니다. 1/1이어야 정상입니다.
0/1이면 컨테이너는 실행 중이지만 Readiness Probe가 실패한 상태입니다. 이 경우 Service가 트래픽을 보내지 않습니다.
kubectl rollout status는 배포 진행 상황을 실시간으로 보여줍니다. 모든 Pod가 업데이트되고 Ready 상태가 될 때까지 기다립니다.
완료되면 "deployment successfully rolled out" 메시지가 나타납니다. 시간이 오래 걸리면 문제가 있다는 신호입니다.
kubectl describe pod는 가장 상세한 정보를 제공합니다. Events 섹션이 핵심입니다.
Pod 생성부터 현재까지의 모든 이벤트가 시간순으로 나열됩니다. 에러 메시지, 재시작 이유, 스케줄링 실패 원인 등을 여기서 찾을 수 있습니다.
kubectl logs는 애플리케이션 로그를 확인합니다. 시작 시 에러는 없는지, 데이터베이스 연결은 성공했는지, API 요청은 정상적으로 처리되는지 확인합니다.
grep -i error로 에러 메시지만 필터링할 수 있습니다. --tail=100으로 최근 100줄만 볼 수도 있습니다.
kubectl get endpoints는 Service와 Pod의 연결 상태를 확인합니다. Service의 selector와 일치하는 Ready 상태 Pod들이 엔드포인트로 등록됩니다.
여기에 Pod IP가 없으면 트래픽이 전달되지 않습니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 새 버전 배포 후 체크리스트를 만들어 봅시다. 첫째, Pod 상태가 모두 Running인지 확인합니다.
둘째, Readiness Probe가 성공해서 READY가 1/1인지 봅니다. 셋째, 로그에 에러나 경고가 없는지 체크합니다.
넷째, Endpoint에 Pod IP가 정상 등록되었는지 확인합니다. 다섯째, 실제 API를 호출해서 응답을 받는지 테스트합니다.
많은 기업에서 이런 패턴을 적극적으로 사용하고 있습니다. 또 다른 예시는 자동화입니다.
CI/CD 파이프라인에 상태 확인을 넣습니다. 배포 후 자동으로 kubectl wait로 모든 Pod가 Ready 될 때까지 기다립니다.
Health check 엔드포인트를 호출해서 200 응답이 오는지 검증합니다. 실패하면 자동으로 롤백합니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 Pod가 Running이면 성공이라고 생각하는 것입니다.
Running은 "컨테이너가 실행 중"이라는 의미일 뿐, "애플리케이션이 정상"을 의미하지 않습니다. 이렇게 하면 크래시 루프 상태에서도 Running으로 보일 수 있습니다.
따라서 Readiness Probe와 로그를 반드시 확인해야 합니다. 또 다른 실수는 첫 번째 Pod만 확인하는 것입니다.
여러 Pod 중 일부만 문제가 있을 수 있습니다. 모든 Pod를 확인해야 합니다.
kubectl get pods -l app=order-service로 라벨 선택자로 모든 관련 Pod를 조회하세요. 실무 팁을 더 드리겠습니다.
kubectl wait를 사용하면 특정 조건이 될 때까지 대기할 수 있습니다. 스크립트에서 유용합니다.
kubectl top pods로 CPU와 메모리 사용량을 모니터링할 수 있습니다. 급격히 증가하면 문제의 신호입니다.
Readiness Probe와 Liveness Probe를 반드시 설정하세요. Readiness Probe는 "트래픽을 받을 준비가 되었는지", Liveness Probe는 "살아있는지"를 확인합니다.
이 둘이 없으면 문제 있는 Pod에도 트래픽이 전달됩니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨가 재빨리 상태를 확인했습니다. 다행히 모든 Pod가 정상이었습니다.
"이제 진짜 배포 완료네요!" 배포 상태 확인을 제대로 하면 안정적인 서비스 운영이 가능합니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - Readiness Probe를 반드시 설정하세요 (트래픽 전달 제어)
- 배포 후 체크리스트를 만드세요 (일관된 검증 프로세스)
- 자동화된 Health Check를 추가하세요 (실제 API 호출 테스트)
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Istio 보안 완벽 가이드
마이크로서비스 환경에서 필수적인 Istio 보안 기능을 실무 중심으로 설명합니다. mTLS부터 인증, 인가까지 단계별로 학습하여 안전한 서비스 메시를 구축할 수 있습니다.
Istio 트래픽 관리 완벽 가이드
Istio의 트래픽 관리 기능을 마스터하는 완벽 가이드입니다. VirtualService와 DestinationRule을 활용한 라우팅부터 트래픽 분할, 헤더 기반 라우팅까지 실무에 필요한 모든 내용을 다룹니다.
Istio 설치와 구성 완벽 가이드
Kubernetes 환경에서 Istio 서비스 메시를 설치하고 구성하는 방법을 초급 개발자도 쉽게 이해할 수 있도록 실무 스토리와 비유로 풀어낸 가이드입니다. istioctl 설치부터 사이드카 주입까지 단계별로 학습합니다.
서비스 메시 완벽 가이드
마이크로서비스 간 통신을 안전하고 효율적으로 관리하는 서비스 메시의 핵심 개념부터 실전 도입까지, 초급 개발자를 위한 완벽한 입문서입니다. Istio와 Linkerd 비교, 사이드카 패턴, 실무 적용 노하우를 담았습니다.
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.