도커의 컨테이너
도커에서 컨테이너란 무엇이었나?
독립적인 환경을 가지는 일종의 미니 컴퓨터였다.
사실 이는 다르게 말하면 하나의 프로그램을 실행시키는 단위를 의미하기도 한다.
쿠버네티스의 파드
쿠버네티스의 파드(Pod)
라는 개념은 도커의 컨테이너처럼 하나의 프로그램을 실행시키는 단위를 뜻한다.
파드
는 쿠버네티스에서 가장 작은 단위에 해당한다.
이 파드
안에서는 일반적으로는 하나의 파드가 하나의 컨테이너를 가진다.
예외적으로 하나의 파드가 여러 개의 컨테이너를 가지는 경우도 있다. 참고로 파드
가 컨테이너를 가진다는 뜻은 파드도 도커처럼 이미지를 기반으로 실행된다는 뜻이다.
파드를 생성하는 방법
파드 생성하는 것은 CLI를 활용하는 방법과 yaml 파일을 활용하는 방법이 있다.
현업에서는 관리의 용이성 등을 이유로 yaml 파일을 활용하는 방법을 많이 사용한다.
yaml 파일 작성하기
우선 파드를 생성하기 위한 yaml 파일을 만들면 된다.
어차피 CLI를 통해서 yaml 파일을 실행할 것이니 파일명은 업무 규칙에 맞춰서 자유롭게 작성하면 된다.
일반적으로 프로젝트 루트 경로에 생성한다.
그런 다음에 아래와 같은 내용을 작성해보자.
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
containers:
- name: nginx-container
image: nginx
ports:
- containerPort: 80
각 속성은 아래와 같은 정보를 가진다.
apiVersion
- 리소스 생성 시 사용할 API의 버전
- 리소스 종류에 따라 공식문서를 확인하고 작성하면 된다.
- 파드의 경우에는 v1을 명시하면 된다.
kind
- 쿠버네티스에서 사용하는 리소스의 종류
metadata.name
- 파드의 이름
spec.containers
- 파드 내에서 사용되는 컨테이너 목록을 정의한다.
- 일반적인 경우에는 하나의 파드 내에서는 하나의 컨테이너만 사용한다.
spec.containers.name
- 컨테이너의 이름
spec.containers.image
- 파드 및 컨테이너에서 사용할 이미지
이미지명:태그명
처럼 명시한다.- yaml 문법 상 태그명까지 작성할 때는 쌍따옴표로 묶어주자.
spec.containers.ports.containerPort
- 컨테이너가 사용하는 포트 번호를 정의한다.
- 파드의 동작에는 영향이 없는 정보성 속성이다.
- DockerFile의 EXPOSE같은 역할이라고 보면 된다.
참고로 도커의 DockerFile
이나 compose.yml
, 쿠버네티스의 yaml
파일처럼
리소스를 관리하기 위한 파일을 매니피스트 파일(Manifest File)
이라고 부른다.
yaml 파일을 통한 파드 생성하기
만약에 프로젝트 루트 경로에 nginx-pod.yaml
이라는 파일을 만들었다고 가정해보자.
그러면 kubectl apply -f nginx-pod.yaml
명령문을 실행하면
해당 yaml 파일을 바탕으로 파드를 생성할 수 있다.
명령문을 실행하게 되면 pod/[파드명] created
같은 메시지가 나올 것이다.
파드를 생성하고 나면 잘 생성됬는지 확인해보도록 하자.
파드 조회하기
kubectl get pods
명령문을 실행해서 파드에 대한 정보를 확인해보자.
명령문을 실행하면 아래와 같은 정보를 알 수 있다.
NAME
- Pod의 이름
READY
- [파드 내 준비 완료된 컨테이너 수] / [파드 내 총 컨테이너 수]
STATUS
- 파드의 상태
Running
은 정상적으로 실행 중이라는 것을 의미한다.
RESTARTS
- 해당 파드의 컨테이너가 재시작된 횟수를 의미한다.
- 쿠버네티스는 파드에 장애가 발생하면 해당 파드를 재시작시키는데 그 횟수를 의미한다.
AGE
- 파드가 생성되어 실행된 시간
분리된 네트워크 환경
사실 파드를 띄우는 것까지는 성공했지만 실제로 localhost:80으로 접속해보면
분명 파드가 실행되있는 것을 확인했는데 접근이 안 되는 것을 확인할 수 있다.
그 이유는 파드도 도커처럼 호스트와 다른 독립적인 네트워크 환경을 가지고 있기 떄문에 그렇다.
파드는 컨테이너와 함께 독립적인 네트환경을 가지고 있다.
파드가 컨테이너를 감싸고 있는 형태라고 생각하면 된다.
그래서 파드는 컨테이너와 네트워크 환경을 같이 공유해서 사용한다.
Nginx가 파드 내부에 있기 때문에 로컬 컴퓨터의 네트워크랑은 독립적인 네트워크 환경을 사용하니
아무리 접속을 시도해봤자 Nginx의 페이지에는 접근할 수 없었던 것이다.
포트 포워딩 (Port Forwarding)
그렇다면 로컬 컴퓨터에서 파드 내부 컨테이너에 접근할 수 있는 방법은 뭘까?
바로 포트 포워딩(Port Forwarding)
기법을 사용하면 된다.
우선 아까 만든 Nginx에 대한 파드가 실행되고 있다는 가정하에
kubectl port-forward pod/nginx-pod 80:80
를 실행해보고
다시 localhost:80에 접속해보면 드디어 웹 페이지 접근에 성공한 것을 확인할 수 있다.
이는 아래와 같은 규칙을 통해서 포트 포워딩을 해주는 것이다.
kubectl port-forward pod/[파드명] [로컬에서의 포트]/[파드에서의 포트]
docker run
명령문에서 사용하던 -p
옵션같은 거라고 생각하면 된다.
파드 삭제하기
파드를 삭제할 때는 kubectl delete pod [파드명]
처럼 실행하면 된다.
파드를 삭제하고 나면 kubectl get pods
명령문을 통해 파드가 잘 삭제됬는지 다시 확인해보자.
참고로 파드가 삭제될 때는 컨테이너도 함께 삭제된다.
DockerFile과 파드
DockerFile을 통해서 이미지를 만들 수 있다.
만약에 kubernetes-test-spring
라는 임의의 스프링 부트 프로젝트를 만들었다고 가정해보자.
간단하게 DockerFile을 만들어보면 아래와 같이 만들어질 것이다.
FROM openjdk:17-jdk
COPY build/libs/*SNAPSHOT.jar app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
이번에는 ./gradlew clean build
명령문을 통해 프로젝트를 빌드하고
docker build -t kubernetes-spring .
명령문을 통해 이미지를 빌드해보자.
그런 다음에 docker image ls
명령문을 통해 이미지가 잘 만들어졌는지 확인해보자.
이번에는 이 이미지를 통해 파드를 생성해보자.
우선 yaml 파일을 만들어 보자.
apiVersion: v1
kind: Pod
metadata:
name: spring-pod
spec:
containers:
- name: spring-container
image: kubernetes-spring
ports:
- containerPort: 8080
해당 ymal 파일의 이름이 spring-pod.yaml
이라는 가정 하에 아래 명령어를 실행해보자.
kubectl apply -f spring-pod.yaml
실행하면 pod/spring-pod created
라는 메시지가 나온다.
파드가 생성되었으니 kubectl get pods
명령문을 통해 잘 생성되었는지 확인해보자.
그런데 kubectl get pods
를 실행해보니 READY
에는 0/1이라고 나오고,
STATUS
에는 ImagePullBackOff라고 출력된다.
그렇다면 파드가 생성되기만 하고 실행에는 실패했다는 뜻인데 그 이유가 뭘까?
이미지 풀 정책 (Image Pull Policy)
DockerFile을 통해서 이미지를 만들어서 파드를 생성하고 실행했더니
ImagePullBackOff
라는 에러가 발생했다.
이 에러는 쿠버네티스의 이미지 풀 정책(Image Pull Policy)
때문에 발생한 에러다.
이미지 풀 정책(Image Pull Policy)
이란 쿠버네티스가 yaml 파일을 읽어서 파드를 생성할 때,
이미지를 어떻게 받아올지(Pull)에 대한 정책이다.
정책에는 3가지 종류가 있다.
Always
- 로컬에서 이미지를 가져오지 않고, 무조건 레지스트리에서 가져온다.
- 레지스트리는 Dockerhub나 ECR같은 원격 이미지 저장소를 뜻한다.
- 이미지 태그가 latest거나 명시되지 않은 경우에 기본값으로 설정된다.
IfNotPresent
- 로컬에서 이미지를 먼저 가져온다.
- 만약 로컬에 이미지가 없는 경우에만 레지스트리에서 가져온다.
- 이미지 태그가 존재하며 그 태그가 latest가 아닌 경우 기본값으로 설정된다.
Never
- 로컬에서만 이미지를 가져온다.
그러면 이제 아까 왜 ImagePullBackOff라는 에러가 발생했는지 알 수 있다.
아까 작성했던 spring-pod.yaml
에서는 이미지명만 있고 태그명은 없었다.
그래서 이미지 풀 정책에 대한 기본값이 Always
가 되면서
실제 사용해야 하는 이미지는 로컬에 있는데
이미지를 레지스트리에서 가져오려고 하다 보니 문제가 된 것이다.
이제 yaml 파일에서 해당 파드에 대해서 spec.containers.imagePullPolicy
를 IfNotPresent
로 바꿔주고,
아까 만든 파드를 삭제하고 다시 생성 및 실행한 다음에
마지막으로 포트 포워딩까지 해주자.
이제 localhost:8080으로 접속해보면 정상적으로 웹 페이지에 접근 가능한 것을 확인할 수 있다.
파드 삭제하기
파드를 삭제할 때는 kubectl delete pod [파드명...]
명령문을 실행하면 된다.
만약에 서버를 3개 띄우고 싶다면 어떻게 해야 할까?
실제 서비스를 운영하다 보면 트래픽이 증가해서 서버가 버벅이는 경우가 있다.
그럴 때는 서버의 개수를 늘리는 수평적 확장
방식을 통해 해결한다.
수평적 확장을 하는 방법은 매우 간단하다.
파드를 생성할 때 ---
로 구분해서 여러 개의 파드를 정의하면 된다.
아래의 예시를 살펴보자.
apiVersion: v1
kind: Pod
metadata:
name: spring-pod-1
spec:
containers:
- name: spring-container
image: kubernetes-spring
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Pod
metadata:
name: spring-pod-2
spec:
containers:
- name: spring-container
image: kubernetes-spring
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Pod
metadata:
name: spring-pod-3
spec:
containers:
- name: spring-container
image: kubernetes-spring
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
파드를 생성하고 kubectl get pods
를 실행해보면
서버 3개를 띄우는 데 성공한 것을 확인할 수 있다.
그런데 3개의 서버를 띄우는 데도 꽤 많은 양의 설정을 작성해야 한다.
또한 이 경우에는 특정한 개수의 서버를 직접 작성했다.
트래픽에 맞게 서버의 수를 동적으로는 변경할 수는 없다는 뜻이다.
만약 트래픽에 의해 서버의 수를 동적으로 변경하고 싶다면
쿠버네티스의 기능은 디폴로이먼트(Deployment)
를 활용해야 한다.
파드 디버깅하기
파드의 에러 메시지 확인하기
kubectl describe pods [파드명]
처럼 실행하면 해당 파드에 대한 세부 정보를 조회할 수 있다.
파드의 로그 확인하기
kubectl logs [파드명]
처럼 실행하면 해당 파드의 로그를 확인할 수 있다.
파드에 접속하기
kubectl exec -it [파드명] -- bash
처럼 실행하면 해당 파드의 bash에 접근할 수 있다.
kubectl exec -it [파드명] -- sh
처럼 실행하면 해당 파드의 sh에 접근할 수 있다.
컨테이너의 종류에 따라 컨테이너 내부에 bash가 설치됬을 수도 있고 sh가 설치됬을 수도 있다.
bash가 설치되어 있지 않은데 bash에 접속하려고 하면 에러가 뜨면서 컨테이너로 접속이 되지 않는다.
그럴 때는 sh로 접속을 시도해보자.