Kubernetes 는 다양한 컴포넌트들로 인해서 작동된다. 이러한 컴포넌트들은 중요성에 있어서 약간의 차이가 있다. 예를들어 Worker Node 에서 docker 프로세스가 정지되거나 문제가 되었을때에 어떻게 될까? 혹은 Worker Node 에 kubelet 프로세스가 문제가 된다면?
이 문서는 Kubernetes 프로세스별 상태 에 대한 글이다.
환경
여기서 환경은 Kubernetes 의 객체를 말한다. 객체라함은 Pods, Deployments, Services, StatefulSet 으로 했다. 그밖에 다양한 객체가 있지만 이 정도 생성해서 진행해보기로 했다.
docker 정지
이것은 Work Node 에 docker 를 정지 시키는 것이다. 이렇게 되었을때에 Kubernetes 의 각종 컴포넌트들은 어떤 상태를 보일지 알아보자.
먼저, Nodes 상태는 ‘Notready’ 로 변경된다.
Node 상태
ZSH
1
2
3
4
kubectl get nodes
NAME STATUS ROLES AGE VERSION
kmaster Ready master16dv1.18.6
knode NotReady<none>15dv1.18.6
Node 의 자세한 상태를 describe 보면 다음과같이 몇가지 상태가 나온다.
Warning ContainerGCFailed 38s (x2 over 98s) kubelet, knode rpc error: code = Unknown desc = Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
Conditions 는 다음과 같이 된다.
Node Condition 상태
Default
1
2
3
4
5
6
7
8
Conditions:
Type Status LastHeartbeatTime LastTransitionTime Reason Message
이 문서는 StatefulSet 에서 로컬 디스크 사용 에 대한 글이다. Kubernetes 에서 디스크 사용은 어려운감이 있다. PersistentVolume 과 PersistentVolumeClaim 이라는 것을 알아야하고 이를 알고나서도 여러가지 속성들때문에 헷깔리는 경우가 많다.
PersistentVolume 관련해서 문서를 보면 대부분 클라우드가 제공하는 스토리지를 이용하는 예제가 많다. 그것이 아니라면 nfs 를 이용하는 경우가 많아서 나처럼 집에 컴퓨터를 이용하는 경우에 실습해 보기가 쉽지 않은게 사실이다.
StatefulSet 의 경우가 바로 이런 경우였다. StatefulSet 을 연구하기 위해서 여러가지 예제를 찾아봤고 다음과 같은 파일을 찾아냈다.
StatefulSet 생성을 위한 yaml 파일 내용
YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
---
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: perf-poc
labels:
app: nginx
spec:
ports:
-port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
namespace: perf-poc
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
terminationGracePeriodSeconds: 10
containers:
-name: nginx
image: k8s.gcr.io/nginx-slim:0.8
ports:
-containerPort: 80
name: web
volumeMounts:
-name: www
mountPath: /usr/share/nginx/html
nodeSelector:
kubernetes.io/hostname: knode
volumeClaimTemplates:
-metadata:
name: www
namespace: perf-poc
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
StatefulSet 은 비상태(stateless) 자원을 생성하는게 아니라 상태를 가지는 자원을 생성하게 해주는 Kubernetes 의 객체다. 대부분 Persistent Data 를 위한 것이기에 PersistentVolume 을 대부분 필요로 한다.
Pod 생성이 Pending 상태를 보이고 있다. 왜 이럴까? 앞에서 객체 생성을 정의한 yaml 파일을 자세히 보면 Persistent Volume 을 요청하고 있다. volumeClaimTemplates 이 바로 그것이다. 이것은 PersistentVolumeClaim 을 요청하는 것으로 나타난다. 확인해 보자.
PersistentVolumeClaim 확인
ZSH
1
2
3
]$kubectl get po,pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/www-web-0Pending5m29s
persistentvolumeclaim 이 Pending 상태이다. 결국에는 이것으로 인해서 Pod 생성도 Pending 이 된 것이다.
해결방안
PersistentVolumeClaim 은 PersistentVolume 이 있어야 한다. 그리고 이것을 묶어줘야하는데 이것을 Bound 라고 한다. 문제는 web.yaml 을 실행했을때에 생성되는 PersistentVolumeClaim 과 PersistentVolume 을 어떻게 연결할 것인가?
좀더 정확하게 말하면 StatefulSet 이 자동으로 생성하는 PersistentVolumeClaim 과 PersistentVolume 을 어떻게 연결할 것인가?
StatefulSet 의 특징은 생성하는 객체의 이름이 임의대로 정해지지 않는다. 정확하게 사람이 인식하기 쉬운 이름으로 결정된다. 앞에서보면 Pod 의 이름이 web-0 이다. 이는 Pod 이름에 Replicas 의 첫번째인 0 을 붙여서 만든다.
StatefulSet 에서 PersistentVolumeClaim 이름은 예측 가능한 패턴을 따른다. volumeclaimtemplates-name–statefulset-name–replica-index. 그래서 생성된 PersistentVolumeClaim 의 이름이 www-web-0 가 된 것이다.
PersistentVolumeClaim 은 PersistentVolume 을 필요로 한다. PersistentVolume 을 생성할때에 claimRef.name 을 PersistentVolumeClaim 이름으로 지정한다. 이렇게 하면 PersistentVolumeClaim 이 생성될때에 PersistentVolume 이 이름과 연결되어 자동으로 Bound 되어진다. 다음과 같이 PersistentVolume 을 만든다.
PersistentVolume 생성
YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-statefulset-demo-0
namespace: perf-poc
spec:
storageClassName: "example-storageclass"
capacity:
storage: 2Gi
volumeMode: Filesystem
accessModes:
-ReadWriteOnce
claimRef:
namespace: perf-poc
name: www-web-0
persistentVolumeReclaimPolicy: Delete
hostPath:
path: /tmp/k8s-pv-statefulset-demo
type: DirectoryOrCreate
nodeAffinity:
required:
nodeSelectorTerms:
-matchExpressions:
-key: "kubernetes.io/hostname"
operator: "In"
values:
-knode
이것은 Kubernetes Worker 호스트에 파일시스템에 특정 디렉토리를 PersistentVolume 으로 생성한다. 만일 디렉토리가 존재하지 않을 경우에는 생성하도록 한다. 그리고 PersistentVolumeClaim 이 삭제되면 이 볼륨도 함께 삭제되도록 했다.
여기서 중요한 것이 claimRef 필드이다. name 속성에 PersistentVolumeClaim 이름을 지정해준다.
또, nodeAffinity 필드를 이용해서 특정한 Worker 에서 생성되도록 했다.
StatefulSet 에서 replicas 를 2 로 했기 때문에 위 PersistentVolume 생성은 claimRef.name 이 www-web-0, www-web-1 로 두개가 필요하다.
두개의 PersistentVolume 을 생성하고 시간을 갖고 기다리면 다음과 같은 결과를 얻게 된다.
객체 확인
ZSH
1
2
3
4
5
6
7
8
9
10
11
12
]$kubectl get po,pv,pvc
NAME READY STATUS RESTARTS AGE
pod/web-01/1Running021m
pod/web-11/1Running033s
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
CentOS 8.2 에서 Docker 를 설치하면 필요한 서비스가 자동으로 시작되지 않는다. 이를 위해서 다음과 같이 systemd 를 설정해주고 시작해준다.
Docker 서비스 시작
ZSH
1
2
3
4
]# systemctl enable containerd
]# systemctl start containerd
]# systemctl enable docker
]# systemctl start docker
CGroup Driver 설정
Ubuntu, CentOS 모두 공통으로 Docker 를 설정해 주는 부분이 존재한다. 바로 Driver 를 systemd 로 바꿔줘야 한다.
Kubernetes 를 설치할때에 Cgroup driver 를 systemd 로 추천하고 있다. 그래서 Kubernetes 만 systemd 로 드라이버를 교체하면 docker 와 통신이 되지 않는다. 이것을 위해서 Docker 에서도 드라이버를 systemd 로 교체해 준다.
CGoup Driver 설정
ZSH
1
2
3
4
5
6
7
8
9
10
11
12
13
]# cat > /etc/docker/daemon.json <<EOF
{
"exec-opts":["native.cgroupdriver=systemd"],
"log-driver":"json-file",
"log-opts":{
"max-size":"100m"
},
"storage-driver":"overlay2",
"dns":["8.8.8.8","8.8.4.4"]
}
EOF
]# systemctl daemon-reload
]# systemctl restart docker
다음과 같이 확인이 가능하다.
CGroup Driver 확인
ZSH
1
2
3
4
]$docker info
Logging Driver:json-file
Cgroup Driver:systemd
Plugins:
Kubernetes 설치
Ubuntu 20.04
Master 로 사용될 Ubuntu 20.04 에서 다음과 같이 Kubernetes 패키지 저장소를 추가 한다.
Metric Server 를 설치할때에 주의해야 할 것은 Kube API 서버와의 통신에서 사용할 TLS 를 수정하는 것이다. Metric Server 는 Public TLS 를 기본으로 하지만 Kube API 는 Kube 자체의 TLS 를 사용하기 때문에 그냥 설치하면 문제가 된다.
2020.04.19 현 시점에서 v0.3.7 이 있지만 ErrorImagePull 에러가 발생하면서 설치가 진행되지 않는다. 따라서 v0.3.6 으로 설치한다.
TLS 수정
Metric Server 를 설치할때에 주의해야 할 것은 Kube API 서버와의 통신에서 사용할 TLS 를 수정하는 것이다. Metric Server 는 Public TLS 를 기본으로 하지만 Kube API 는 Kube 자체의 TLS 를 사용하기 때문에 그냥 설치하면 문제가 된다.
쿠버네티스(Kubernetes) 처음 접하면 개념 잡기를 해야 한다. 쿠버네티스를 잘 사용하기 위한 사용법을 익히는것도 중요하지만 개념을 이해하고 나면 외워서 문제해결을 하는 것이 아닌 응용력이 생겨 예기치 않은 장애를 겪을때에 힘을 발휘한다.
개인적으로 쿠버네티스의 개념을 이해하는데 약간의 혼란이 있었다. 개념을 설명하는 용어와 실제 작업을 할때에 사용하는 단어, 명령어들이 다 틀리게 사용되고 있는데서 오는 것이였다.
Master/Worker
다들 알다시피 쿠버네티스는 Master Node와 Work Node 로 구성된다. 대부분 Work Node 가 Master Node 보다 훨씬 많고 대부분의 서비스들이 여기에 배포되어진다.
Node 라는 단어를 빼버리고 단순히 Master/Worker 라고도 하지만 그냥 Master/Node 라고 하기도 하는 모양이다. 하지만 정확하게는 Master Node, Worker Node 라고 불린다.
또, Master Node 를 Controller 라고도 한다. Master 의 역활이 Worker Node 를 모니터링, 감시를 하고 각종 명령어을 Master 가 받아서 처리한다. 이 Master Node 는 거대한 Controller 서버 역할이라고 보면 된다.
kubectl – ResTful API
Master 가 거대한 Controller 서버라면 클라이언트를 이용해서 이 서버에 명령을 보내면 될 것이다. 이 클라이언트가 바로 kubectl 이라는 커맨드가 수행한다. 서버-클라이언트 구조상 서버는 1대지만 클라이언트는 여러대 일 수 있다. 실제로 kubectl 은 단일한 명령어로서 도커(Docker), 컨테이너(Container) 등도 필요없이 동작한다. 그래서 클라이언트는 어느 머신이든지 다 설치/사용이 가능하다.
kubectl 은 클라이언트, Master Node 는 Controller 서버다. 이 둘이 명령어를 주고 받는 방법이 바로 RESTFul API 통신 방법이다.
ResTful API 는 HTTP 를 이요해 URI 에 자원(Resources) 을 명시하고 메소드(Method) 를 이용해 CRUD 연산을 수행한다.
쿠버네티스(Kubernetes) 에서 중요한 것이 바로 자원이다. ResTful API 를 이용해 어떤 작업을 명령할때에 반드시 대상이 필요한데, 이것이 바로 자원이다.
Object, Resource, Kind
쿠버네티스(Kubernetes) 에서 개념을 설명할때에 오브젝트(Object) 라는 말을 쓴다. 오브젝트라고하면 쿠버네티스를 구성요소쯤으로 이해하면 된다. 예를들어 Pods, Service, Volume, Namespace 등은 쿠버네티스의 기본 오브젝트라고 불린다.
문제는 이러한 오브젝트들이 때로는 자원(Resource), 때로는 종류(Kind) 로 불린다.
자원 ResTful API 관점에서 실체적인 존재로서 호출하는 오브젝트들이다. 실제로 kubectl 커맨드를 사용할때에는 ‘오브젝트를 명시한다’ 하지 않고 ‘자원을 명시한다’라고 한다. ResTful API 개념을 차용했기 때문에 ‘자원을 명시한다’라고 해야 맞다.
kubectl 은 Kubernetes Client 이다. 이 명령어는 HTTP 통신을 기반으로 Kubernetes Controller 와 RestFul API 통신을 요청하고 결과를 받아 사용자에게 출력하게 하는 역할을 한다.
HTTP RESTFUL API 통신을 한다는 말을 듣는 순간 직감했겠지만 인터넷만 된다면 kubectl 은 어느 컴퓨터에서든 실행이 가능하다. 처음 Kubernetes 설치문서들을 보면 대부분 Master Node 에서 실행되도록 설정을 하는데, 여기서는 다른 컴퓨터에서 kubectl 만 설치해서 Kubernetes Controller 와 연결하는 방법에 대해서 살펴보도록 할 것이다.
설치환경
설치 환경은 Mint Linux 19.2 – XFCE4 환경에서 진행했다. kubectl 를 실행하는 환경는 다양하겠지만 제일 편한 것으로는 Unix 환경일 것이다. Mac OS X, Linux 가 가장 적합한데, 필자는 Mint Linux 19.2 – XFCE4 데스크탑을 사용하고 있음으로해서 이 환경에서 진행하게 됐다.
Mint Linux 19.2 는 Ubuntu 기반이기 때문에 Ubuntu 에서 설치, 설정 모두 동일하다고 생각하면 된다.
kubectl 설치하기
Mint Linux 19/2 에서 설치하는 방법은 Ubuntu 에서 설치하기와 동일하다. 단, 여기서는 kubectl 패키지만 설치하면 된다. root 계정으로 다음과 같이 한다.
Kubernetes 를 설치할때에 Cgroup driver 를 systemd 로 추천하고 있다. 그래서 Kubernetes 만 systemd 로 드라이버를 교체하면 docker 와 통신이 되지 않는다. 이것을 위해서 Docker 에서도 드라이버를 systemd 로 교체해 준다.
Default
1
2
3
4
5
6
7
8
9
10
11
12
]# cat > /etc/docker/daemon.json <<EOF
{
"exec-opts":["native.cgroupdriver=systemd"],
"log-driver":"json-file",
"log-opts":{
"max-size":"100m"
},
"storage-driver":"overlay2"
}
EOF
]# systemctl daemon-reload
]# systemctl restart docker
다음과 같이 확인 가능하다.
Default
1
2
3
4
]$docker info
Logging Driver:json-file
Cgroup Driver:systemd
Plugins:
Kubernetes 설치
Ubuntu 18.04
Ubuntu 18.04 에서는 다음과 같이 Kubernetes 패키지 저장소를 추가 한다.