[yongggg's] Kubernetes(쿠버네티스) 생성 및 배포 테스트
안녕하세요 이번 장에서는 kubernetes의 기본적인 배포 testing을 위한 글을 작성하려고 합니다!
현재 제가 커스텀하고 만든 모델을 서빙하는 코드를 FastAPI로 서빙할 수 있는 상태로 만들었고, 이후 배포가 잘되는지 테스팅하기 위한 과정을 담아보았습니다.
지금부터 그 과정을 설명하겠습니다.
1. Kubernetes CLUSTER 생성
** CLUSTER 란?
- 쿠버네티스 클러스터는 여러 대의 서버(노드)를 하나의 거대한 컴퓨터처럼 묶어서 컨테이너(=서버 프로그램)를 자동으로 배포/운영/관리해주는 시스템이다.
- 클러스터 안에는 여러 노드(서버)가 있고,쿠버네티스가 이 노드들에 컨테이너를 자동으로 분산시켜준다.
위 까지 docker image가 잘 build 되었다면, 사용할 CLUSTER의 namespace에서 배포를 테스팅 해야한다.
kubectl config get-contexts
해당 명령어로 현재 CLUSTER가 무엇인지 확인할 수 있으며, 만약 현재 CLUSTER 말고 다른 접속 가능한 CLUSTER가 있다면, 다음과 같은 명령어로 그 CLUSTER에 접근 가능하다.
kubectl config use-contexts <context name>
하지만 간혹가다 모두 접근이 불가능할 수 있고, 내가 CLUSTER를 새로 생성하고자 할때가 있다. 먼저 해당 CLUSTER에 정상적으로 접근이 가능한지를 다음의 명령어로 알아볼 수 있다.
kubectl cluster-info
이 명령어를 사용했을때, 오류가 나오지 않고 그 클러스터를 이용하고자하면, 바로 다음 단계로 넘어가면 되지만,
필자는 실제로 내부망에서 띄운 CLUSTER에 접근 권한이 없기 때문에 CLUSTER를 새로 생성하기로 했다.
CLUSTER를 생성하기 위해서는 kubectl 명령어로 생성할 수 없다. kubectl은 이미 만들어진 쿠버네티스 클러스터에 "접속해서 명령을 내리는 도구"이기 때문이다. 따라서 minikube를 다음과 같은 명령어로 설치해준다.
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
설치가 완료 되었다면, 다음과 같은 명령어로 CLUSTER를 생성할 수 있다.
minikube start -p <원하는_이름>
이렇게 CLUSTER를 생성했다면, 위에서 CLUSTER 조회 및 접근 가능 정보 명령어를 수행하여 확인해보자.
(kubectl config use-context <context이름>으로 접근한 뒤, kubectl cluster-info로 확인.)
2. minikube 도커 환경으로 전환
eval $(minikube -p CLUSTER NAME docker-env)
이 작업을 해주는 이유는, minikube는 자체적으로 “가상머신 내부”에 쿠버네티스 클러스터를 띄운다.
내가 로컬에서 docker build로 이미지를 만들었더라도, minikube 내부에서는 그 이미지를 볼 수 없다.
- minikube가 이미지를 찾는 순서:
- minikube 내부 도커에 있는 이미지
- 도커허브 등 외부 레지스트리
이 작업이 잘 되었는지 확인하기 위해,
docker images
명령어를 사용해보면, 현재 이 cluster에서 볼 수 있는 Images만이 나올 것이다.
3. Dockerfile 구성
FROM python:3.11.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY layout_server.py .
COPY model ./model
COPY ./core_docling ./core_docling
COPY ./datamodel ./datamodel
COPY ./ibm_docling_models ./ibm_docling_models
ENV PYTHONPATH="/app"
EXPOSE 8001
CMD ["uvicorn", "layout_server:app", "--host", "0.0.0.0", "--port", "8001", "--workers", "1"]
이건 필자가 커스텀한 모델은 아니지만, layout_server.py에 기능들을 추가해서 재 작성한 파일을 Dockerfile로 구동하고자 했다.
여기서 WORKDIR을 정해준다는 것은 띄울 서버의 작업 경로를 지정한다는 것이다.
그리고 "COPY requirements.txt ." "COPY layout_server.py ." 등의 명령어가 있는데,
COPY <호스트(로컬) 경로> <컨테이너(이미지 내부) 경로>
이렇게 외워 두자.
이렇게 필요한 파일과 폴더 등을 layout_server.py 에서 Import 했을 때, 잘 찾을 수 있도록 알맞게 구성한다.
4. Docker build
이제 저런 폴더를 build를 하여 Image를 만들어야 한다. 내가 사용할 커스텀 모델은 총 세개로, 다음과 같이 docker를 빌드했다.
# 각 서버의 폴더 경로에서
# ocr_server/
docker build -t ocr_server:latest .
# layout_server/
docker build -t layout_server:latest .
# tableformer_server/
docker build -t tableformer_server:latest .
5. namspace 생성 및 yaml file작성
** 네임스페이스(Namespace)
- 네임스페이스는 클러스터 안에서 리소스(서버, 서비스 등)를 논리적으로 분리하는 공간이다.
예를 들어, dev(개발), test(테스트), prod(운영) 사용자별, 팀별로 나눠서 리소스를 격리할 수 있다. - 네임스페이스를 사용하면, 서로 다른 프로젝트/팀/환경이 같은 클러스터를 안전하게 공유할 수 있다.
kubectl create namespace <namespace 이름>
# namespace 조회
kubectl get ns
namespace를 생성하는 명령어는 다음과 같다.
그리고 yaml 파일을 구성해야 하는데, 필자는 세 개의 서버를 한번에 띄워서 테스팅 하고 싶어서, 다음과 같이 작성을 했다. container port를 8000으로 지정하지만, nodePort를 각각 다르게 지정해주면, 다른 포트로 접근할 수 있다고 한다.
또, 여기서 metadata의 namespace에 방금 내가 정의한 namespace를 알맞게 입력해주어야 한다.
# ocr_server
apiVersion: apps/v1
kind: Deployment
metadata:
name: ocr-server
namespace: docling-test
spec:
replicas: 1
selector:
matchLabels:
app: ocr-server
template:
metadata:
labels:
app: ocr-server
spec:
containers:
- name: ocr-server
image: ocr_server:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: ocr-server-service
spec:
type: NodePort
selector:
app: ocr-server
ports:
- protocol: TCP
port: 8000
targetPort: 8000
nodePort: 30080
---
# layout_server
apiVersion: apps/v1
kind: Deployment
metadata:
name: layout-server
namespace: docling-test
spec:
replicas: 1
selector:
matchLabels:
app: layout-server
template:
metadata:
labels:
app: layout-server
spec:
containers:
- name: layout-server
image: layout_server:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: layout-server-service
spec:
type: NodePort
selector:
app: layout-server
ports:
- protocol: TCP
port: 8000
targetPort: 8000
nodePort: 30081
---
# tableformer_server
apiVersion: apps/v1
kind: Deployment
metadata:
name: tableformer-server
namespace: docling-test
spec:
replicas: 1
selector:
matchLabels:
app: tableformer-server
template:
metadata:
labels:
app: tableformer-server
spec:
containers:
- name: tableformer-server
image: tableformer_server:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: tableformer-server-service
spec:
type: NodePort
selector:
app: tableformer-server
ports:
- protocol: TCP
port: 8000
targetPort: 8000
nodePort: 30082
ㅜ** imagePullPolicy란?
쿠버네티스에서 파드를 생성할 때컨테이너 이미지를 어디서, 어떻게 가져올지(=pull할지) 결정하는 정책이다.
- 주요 옵션
- Always
: 파드가 생성될 때마다 항상(무조건) 외부 레지스트리(도커허브 등)에서 이미지를 가져옵니다.
- IfNotPresent
: 노드(서버)에 해당 이미지가 이미 있으면 그걸 사용하고,없으면 외부 레지스트리에서 이미지를 가져옵니다.
- Never
: 절대 외부에서 이미지를 가져오지 않고,노드(서버)에 있는 이미지만 사용합니다.(없으면 에러)
6. 서버 배포
서버 배포는 다음과 같은 명령어로 배포할 수 있다.
kubectl apply -f all_servers.yaml --namespace=docling-test
## 배포 삭제
kubectl delete -f all_servers.yaml --namespace=docling-test
그리고 다음과 같은 명령어로 서버가 잘 배포되었는지를 확인할 수 있다.
kubectl get pods -n docling-test
다음과 같은 명령어로 docling-test 네임스페이스에 있는 모든 파드(Pod)의 목록과 상태를 확인한다.
- 확인할 수 있는 것:
- 각 서버(ocr_server, layout_server, tableformer_server 등)가 실제로 파드로 잘 배포되었는지
- 파드의 현재 상태(Running, Pending, CrashLoopBackOff 등)
- 파드 이름, 재시작 횟수, 생성 시간 등
kubectl get svc -n docling-test
다음과 같은 명령어로 docling-test 네임스페이스에 있는 모든 서비스(Service)의 목록과 상태를 확인한다.
- 확인할 수 있는 것:
- 각 서버에 대한 서비스(ocr-server-service, layout-server-service, tableformer-server-service 등)가 잘 생성되었는지
- 서비스의 타입(NodePort, ClusterIP 등)
- 서비스의 클러스터 내부 IP, 외부에서 접근 가능한 포트(nodePort)
위에서 뜬 podname을 검색해서 어떤 상황인지, 로그를 확인할 수 있는 명령어는 다음과 같다.
# 이미지 풀 실패, 인증 오류, 네트워크 문제 등 상세 원인을 확인할 수 있다.
kubectl describe pod <pod name> -n <namespace name>
# 컨테이너가 실행되었을 때, 단, ImagePullBackOff 상태에서는 로그가 거의 없거나 아예 없을 수 있다.
kubectl logs <pod name> -n <namespace name>
7. Server 찌르기
여기서 띄워진 써버는 minikube에서 Ip를 할당해 주기 때문에, minikube의 ip를 알아야한다. 다음의 명령어로 minikube의 Ip를 알 수 있다.
minikube ip -p docling-server
또 위에서 yaml에서 정의한 targetport는 Dockerfile 혹은 python code에서 서버를 띄울때, 지정한 Port와 "반드시!!!" 같아야 하며, 이 port가 같다면, nodePort를 통해 서버를 찌를 수 있다.
즉, 위에서 나온 IP와 nodePort를 다음과 같은 명령어로 실행해보자.
curl <minikube IP>:<nodePort>
여기서 정상적으로 응답이 온다면, 자신이 설정한 "Get" or "Post" 등으로 요청을 찌르면 된다.