Docker Container로 띄운 서비스를 어떻게 관리해야 할까?
Traffic에 따라 Scaling up, down을 어떻게 자동화할까?
Container의 상태를 어떻게 추적, 관리할까?
...
Docker Container의 상태를 관리하는 도구가 여러가지가 있다.
Docker swarm: 간단한 설정, 다양한 기능 제공 X
MESOS: 어려운 설정, 다양한 기능 제공 O
Kubernetes: 어려운 설정, 다양한 기능 제공 O
그 중 Kubernetes
에 대해 알아보도록 하겠다.
Kubernetes(이하 k8s)
를 사용함으로써 다양한 이점을 얻을 수 있다.
k8s
의 주요 컨셉을 살펴보자
Worker Node
master node (아래에서 다룸)
만약 한 개의 Node만 운영하면 어떻게 될까? Node에 장애가 발생하면 서비스가 죽을것이다.
그렇기 때문에 Node는 2개 이상 유지해야 한다.
Cluster에 속한 Node들 중 하나가 죽더라도, 다른 Node들은 여전히 살아있으므로 서비스는 계속 작동할 것이다.
그리고 Cluster에 여러 Node를 유지함으로써 Load balancing을 할 수 있을 것이다.
Orchestration
Container Runtime
(Ex. Docker)을 가지고 있음Kubelet Agent
를 가지고 있음Kube API Server
를 가지고 있음etcd
를 가지고 있음Controller
, Scheduler
를 가지고 있음k8s는 컨테이너를 workder node에 직접적으로 배포하지 않는다.
대신, container를 pod
라 불리는 k8s object 단위로 encapsulate한뒤 배포한다.
Pod
로 캡슐화한뒤 Worker Node에 배포한다.
3. 파이썬 서버가 아닌, 다른 서비스 Container는 동일한 Pod에 배포할 수 있다.
파이썬 서버 컨테이너를 일일이 배포하는 방식으로 서비스를 운영해도 문제는 없다.
그런데, 서버 작동을 위한 helper container가 추가되거나, Scaling을 위해 서버 Container가 추가되는 경우에는 관리가 복잡해진다.
k8s는 Pod
Object 단위로 Container들을 관리하도록하여, 위의 불편함을 개선한다.
# 단순히 Docker container run 하기
docker run container_name
# pod를 생성하여 Docker Container run 하기
kubectl run pod-name --image IMAGE_NAME
# Pods 목록 확인
kubectl get pods
# Pods 목록 확인 + wide
kubectl get pods -o wide
# Pods 디테일 확인
kubectl describe pod pod-name
# Pods 제거
kubectl delete pod pod-name
# cluster 목록 확인
kubectl config get-clusters
# Pod 접속
kubectl -it exec POD_NAME -n NAMESPACE_NAME -- /bin/bash
기본적으로 Docker Image는 Docker hub (public image repository)에서 가져온다.
설정을 통해 이미지를 불러올 주소를 지정할 수 있다.
k8s은 Pod를 비롯한 Object를 생성하기 위해 YAML을 사용할 수 있다.
기본적으로 4개의 Required Top level Property를 가진다.
# pod-definition.yaml
apiVersion: v1
# Object를 생성하는 k8s API 버전을 지정하는 부분
# 생성하는 Object에 따라 적합한 API 버전을 사용해야 함
kind: Pod
# 생성하려는 Object의 종류를 지정하는 부분
metadata:
# 생성하는 Object에 대한 정보를 명시하는 부분
# k8s이 제공하는 child만 사용 가능
name: myapp-pod
# Object의 이름을 명시
labels:
# dictionary 형태로, 원하는 형태의 key, value label을 명시 가능
# label을 가지고 pod를 필터링, 검색할 때 사용할 수 있음
app: myapp
type: front-end
spec:
# 생성하는 Object에 따라, k8s에 추가로 제공해야 하는 정보를 명시하는 부분
containers:
# containers 속성은 list 형식
- name: nginx-container
image: nginx
위처럼 yaml
형식으로 정의한 내용를 가지고 pod를 생성할 수 있다.
kubectl craete -f pod-definition.yaml
# kubectl apply -f pod-definition.yaml
여러 개의 Pod을 운영함으로써 High Availability, Load Balancing, Scaling의 이점을 얻을 수 있다.
Replication Controller
는 Definition file에 명시한 내용에 따라 Pod을 관리한다.
Pod 개수를 몇 개 유지하라
...
Replication Controller
는 같은 Cluster에 위치한 여러 (Worker) Node에 걸쳐 사용된다.
# Replication Controller Definition file
# rc-definition.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: myapp-rc
labels:
app: myapp
type: front-end
spec:
# Replication Controller에서 생성할 Pod들을 정의하기 위한 Template 부분
template:
# 위에서 작성한 pod-definition.yaml과 동일한 형식, 내용을 사용한다
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
# 몇 개의 pod replica를 만들 것인가
replicas: 3
# Replication Controller, Pod를 생성
kubectl create -f rc-definition.yaml
# Replication Controller 목록 확인
kubectl get replicationcontroller
ReplicaSet
은 Replication Controller
와 동일한 목적을 가지나 차이점이 있다.
# ReplicaSet Definition file
# replicaset-definition.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myrs
labels:
app: rs
spec:
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
replicas: 3
selector:
matchLabels:
type: front-end
# ReplicaSet을 생성
kubectl create -f rs-definition.yaml
# ReplicaSet 목록 확인
kubectl get replicaset
ReplicaSet에 Pod definition이 들어있는데, 굳이
selector
써서 자기가 만든 Pod들을 구분하려고 하는건가?
Replication Controller
는 기본적으로, 자기에 정의된 Pod만을 관리(개수, 상태 등)하는 반면, ReplicaSet
은 자기 밖에서 만들어진 Pod도 관리할 수 있기 때문이다.
`Replication Controller`도 `selector`필드를 사용할 수는 있지만 필수는 아님
ReplicaSet
은 자기가 만들지는 않았지만, 관리해야할 Pod를 식별할 수 있어야하는데, 이때 selector
와 label
필드가 사용되는 것이다.
# ReplicaSet의 selector
spec:
...
# 나는 pod가 어디에서 생성되었는지 상관 없이, label이 'type: front-end'인 pod를 관리할거야
selector:
matchLabels:
type: front-end
# Pod의 label 정보
metadata:
name: mypod-fe
labels:
type: front-end
미리 만들어진 Pod가 3개가 있는데, ReplicaSet의 desired replica가 5라면, ReplicaSet은 2개의 Pod만 생성한다.
ReplicaSet을 통해 3개의 Pod를 운영하는 상황에서, Pod를 6개로 늘리고 싶으면 어떻게 할까
여러 방법이 있다.
ReplicaSet
의 definition file의 replicas
필드를 수정하고 replace한다.
kubectl replace -f replicaset-definition.yaml
kubectl scale --replicas=6 -f replicaset-definition.yaml
kubectl scale --replicas=6 -f replicaset my-replicaset-name
Deployment
는 말 그대로 배포와 관련한 편의성
을 제공하기 위한 k8s Object이다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: myrs
labels:
app: rs
spec:
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
replicas: 3
selector:
matchLabels:
type: front-end
Deployment
는 ReplicaSet
과 동일한 definition file구조를 가진다.
kubectl create 0f dp-definition.yaml
을 통해 Deployment
를 생성하면 자동으로 ReplicaSet
이 생성되고, ReplicaSet
이 생성됨에 따라 자동으로 Pod
가 생성된다.
Deployment
가ReplicaSet
보다 더 큰 개념인건 알겠는데, 무슨 장점이 있는것일까
kubectl create dp.yaml
을 수행하면 rollout
이 트리거된다. 그리고 rollout
은 새로운 deployment revision
을 생성한다.
새로운 App 버전이 나와서 Container가 변경되면, rollout
이 또 트리거되어 새로운 revision
이 생성된다.
revision
기록은 계속 누적되고, 이를 통한 버저닝이 가능하다.
# 특정 deployment의 rollout 상태 확인하기
kubectl rollout status deployment/my-deployment-name
# 특정 deployment의 rollout history 확인하기
kubeclt rollout history deployment/my-deployment-name
kube
Deployment에 속한 Pod들을 Update하려고 한다.
위와 같은 Recreate
전략은 '삭제-생성' 간격동안 서비스를 제공할 수 없다.
Deployment는 대신에 기본값으로 RollingUpdate
전략을 사용한다.
Recreate
전략을 수행할 경우 replicaset은 다음의 흐름으로 작동한다.
replicas
값을 0으로 설정 -> pod all deletereplicas
값을 5으로 설정 -> pod all createRollingUpdate
전략을 수행할 경우 replicaset은 다음의 흐름으로 작동한다.
kubectl rollout undo deployment/myapp-deployment
롤백 역시 RollingUpdate기반으로 작동한다.
deployment의 definition file을 변경하고 이를 반영하자
kubectl apply -f dp-definition.yaml
수행그러면 deployment는 기본적으로 rolling update 방식으로 업데이트를 수행할 것이다.
특정 필드의 값만 지정하여 업데이트 할 수 있다.
kubectl set image deployment/my-dp-name nginx-continaer=nginx:1.9.1
변경사항이 definition file에 영구적으로 반영되지 않는 다는 점을 주의해야 한다 (ReplicaSet도 마찬가지)
네트워킹과 관련한 기능을 지원하는 k8s object
세 종류의 타입이 있다.
Node에 속한 Pod와 Node의 port를 연결하여 Pod가 accessible하게 만드는 기능
Service
가 요청을 전달하려는 Pod의 포트Service
의 포트Service
는 마치 Node의 가상 서버이다. 클러스터 내부에서 서비스는 고유한 IP(ClusterIP)를 갖는다.
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:ㄹ
type: NodePort
ports: # 여러 port를 지정할 수 있다
- targetPort: 80 # optional: 따로 설정하지 않으면 port과 같은 번호를 사용
port: 80 # mandatory
nodePort: 30008 # optional: 따로 설정하지 않으면 30000 ~ 32767값으로 자동 설정
# 어떤 Pod와 포트를 매핑시킬지 selector를 통해 지정
selector:
app: myapp
type: front-end
## pod-definition.yaml
...
metadat:
labels:
app: myapp
type: front-end
...
# 생성
kubectl create -f service-definition.yaml
# 조회
kubectl get services
# Node의 IP, Port를 통해 Pod에 접근 가능함
curl http://NODE_IP_ADD:NODE_PORT
한 Node에 여러 Pod이 있는 경우는 어떻게 할까
Pod가 여러 개인 경우에도 별도로 설정 없이, Pod가 1개인 경우처럼 Service의 selector
로 Pod의 Label과 매핑하면 알아서 포트 작업을 진행한다.
별도의 설정 없이 Service
가 LoadBalancer 기능을 수행한다.
기본 LoadBalance 알고리즘은 'random'
Service definition의 'Algorithm'필드를 통해 방식을 변경할 수 있다.
이 경우에도 별다른 설정 없이 없어도, 클러스터 내부의 여러 Node에 걸쳐 작동하는 서비스가 생성되고, 동일한 NodePort
를 통해 접근할 수 있게 된다.
각 계층의 서비스를 여러 Pod들로 지원하는 상황
Pod들은 상태에 따라 계속 생성, 제거를 반복한다. 그런 상황에서 계층간의 연결을 Pod의 IP로 하면 문제가 발생할 것이다.
즉, static한 IP를 가진 클러스터 역할이 필요한데, clusterIP
타입의 서비스로 해결할 수 있다.
apiVersion: v1
kind: Service
metadata:
name: back-end
spec:
type: ClusterIP # Service의 기본 type은 ClusterIP
ports:
- targetPort: 80 # Pod들의 포트
port: 80 # Service의 포트
selector: # Back-end 서버 Pod들의 Label로 식별
app: myapp
type: back-end
1. 여러 Node에 분산된 Pod들로 구성된 서비스가 존재한다.
2. Node마다 IP 주소가 존재한다.
Q: 사용자는 어떤 IP(Node)로 요청을 보내야 하는가?
apiVersion: v1
kind: Service
metadata:
name: back-end
spec:
type: LoadBalancer
ports:
- targetPort: 80 # Pod들의 포트
port: 80 # Service의 포트
nodePort: 30008 # Node의 포트
클라우드 플랫폼 (Ex. AWS, GCP, ...)에서는 단순히 Service type을 LoadBalancer
로 설정하면 알아서 로드밸런싱을 해준다.
on-premise 환경을 직접 구축해야 함
참고