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
이 문서는 GitLab-Runner 설치 에 대한 것이다. GitLab 은 CI/CD 를 위해 외부 프로그램을 사용하는데, 이것이 바로 GitLab-Runner 이다. 외부 프로그램을 사용하기 때문에 반드시 GitLab 과 함께 있어야 하는것도 아니고 독립적으로 다양한 플랫폼에 설치해도 된다.
설치
설치는 아주 간단하다. 각 배포판, 플랫폼마다 패키지를 제공한다. 나는 Ubuntu 20.04 에 그것도 GitLab 서버에 설치할 예정이다. 다른 서버에 설치를 해도 되지만 테스트 삼아 설치하는 것이여서 이렇게 진행했다.
Please enter the gitlab-ci coordinator URL(e.g.https://gitlab.com/):
http://gitlab.systemv.local:8001/
Please enter the gitlab-ci token forthisrunner:
HHjQMREKED4vxoazrDBv
Please enter the gitlab-ci description forthisrunner:
[gitlab]:Forcompile java
Please enter the gitlab-ci tags forthisrunner(comma separated):
java,java runner
Registering runner...succeeded runner=HHjQMREK
Please enter the executor:docker,shell,ssh,docker-ssh+machine,docker+machine,kubernetes,custom,docker-ssh,parallels,virtualbox:
shell
Runner registered successfully.Feel free tostart it,but ifit'srunning already the config should be automatically reloaded!
등록할때에 URL, Token 값을 입력해 준다. 그리고 무엇보다 중요한 executor 를 선택해줘야 한다. 나의 경우에 Java 컴파일을 해줘야 하기 때문에 executor 를 shell 로 선택해줬다. 이렇게 설정된 내용은 /etc/gitlab-runner/config.toml 파일에 기록된다.
정상적으로 완료됐다면 서비스를 시작해 준다. 그리고 GitLab 에서 다음과 같이 runner 가 보인다.
등록된 gitlab-runner
여기서 중요한 것이, config.toml 파일을 수동으로 조작한다고 해서 GitLab 서버에 등록이 되지 않는다. 반드시 register 를 이용해야만 등록이 가능하다.
Java 컴파일 환경 구축.
번외로 Java 컴파일 환경을 구축해 보겠다. gitlab-runner 의 프로세스를 보면 다음과 같다.
gitlab-runner 를 executor 로 작동하도록 했기 때문에 gitlab-runner 의 기본 쉘인 bash 를 기반이다. gitlab-runner 계정에 .bashrc 파일에 java 1.8 환경 변수를 등록해준다. gitlab-runner 는 서비스에 등록되어 동작은 root 계정으로 동작한다. 하지만 gitlab-runner 가 실행될때에 su 명령어로 gitlab-runner 계정으로 전환된다.
문제는 이때에 .bashrc 를 읽어들이지 않는다. 대신에 .profile 을 읽어들이게 된다. 따라서 각종 환경 변수들은 .profile 에 작성해야 한다.
GitLab 설치 하기. GitLab 은 무료로 사용가능한 git 저장소, ticket 시스템이다. github 의 오픈소스 버전으로 생각할 수 있지만 그것보다 많은 기능을 제공한다. 이 글에서는 gitlab 을 Ubuntu 20.04 LTS 에 설치와 설정에 대해서 알아보도록 하겠다.
준비
Gitlab 을 설치하기 전에 필요한 의존성 패키지들을 먼저 설치해 준다.
Gitlab 설치를 위한 의존성 패키지 설치
ZSH
1
]$sudo apt install-yca-certificates curl
메일을 사용할 경우에는 메일 서버를 설치해줘야 하지만 여기서는 제외한다.
Gitlab 설치를 위한 저장소 추가
gitlab 홈페이지에서는 각 리눅스 배포판에 맞춰 스크립트를 제공한다. Ubuntu 20.04 를 위해 제공해주는 스크립트를 실행해 준다.
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 를 사용하기 때문에 그냥 설치하면 문제가 된다.
/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc:Noto Sans CJK SC:style=Regular
‘:’ 를 기준으로 필터를 줄 수 있다. 필터는 ‘:’ 를 기준으로 연달아 적어주면 되는데 위의 예제는 언어가 ko 인 것만을 보여주도록 옵션을 줬다. 하지만 결과를 보면 중국어, 홍콩, 일본등도 함께 출련된다. 이것은 CJK 폰트의 특성이다. CJK 는 중국어,일본어,한국어만을 따로 모아 놓은 폰트를 말한다.
다음과 같은 필터를 사용할 수 있다.
fc-list : family
fc-list : lang=ko family
fc-list : family style
fc-list –format=”%{family[0]}\n” | sort | uniq
위 보기에서 2번째 명령어를 통해서 한글을 지원하는 폰트를 알아낸다. 나는 “Noto Sans Mono CJK KR” 를 선택했다.
VSCode 폰트 변경
리눅스용 VSCode 는 설정을 열기위한 단축키를 제공하는데, Shift + Ctl + P 를 누르면 된다. 여기서 ‘설정열기(JSON)’ 선택해 자신만의 설정을 커스터마이징 할 수 있다. 폰트 변경은 다음과 같이 했다.
Change font for vscode
Default
1
2
3
4
{
"editor.fontFamily":"'Noto Sans Mono CJK KR', 'monospace', monospace, 'Droid Sans Fallback'",
"editor.fontSize":16,
}
폰트 이름에 공백(Space) 가 들어간 경우에 홑따옴표(‘) 로 감싸준다. 새로운 폰트 이름을 적자마자 바로 적용된다.
Oracle Linux 는 UEK(Unbreakable Enterprise Kernel) 라고 해서 커널을 제공 한다. Oracle 은 이를통해 최신의 커널을 제공하고 자신들만의 퍼포먼스 패치를 더해서 고성능을 낼수 있도록 했다. Oracle Linux 8 에서 UEK 커널을 설치해 보자.
Yum Repository 확인
Oracle Linux 8 에는 uek 를 위한 yum repository 가 추가되어 있다. 다음과 같이 확인이 가능하다.
이 글은 Spring5 에 Spring-Security 에 대한 기초 템플릿이다. Session 유지를 위해서 Redis 를 필요로 한다. 또, ElasticSearch 의 RestHighLevelClient 로 ElasticSearch를 연결 한다. 데이터베이스는 JNDI 설정으로 연결 된다.
다음과 같은 내용을 담았다.
JDK 11
Tomcat 서버 9 를 이용.
spring-session.xml 을 작성해 Redis 를 세션으로 사용하도록 했다.
JNDI 를 이용해 MySQL 에 연결된다.
Redis 설정한 이유는 Spring Security 에 인증을 InMemory 를 요구 한다. 이는 다음과 같은 설정 때문이다.
spring-security.xml 에 bcrypt 패스워드 설정
XHTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<authentication-manager>
<authentication-provider>
<user-service>
<!-- This is required a InMemory DB. So I setup Redis and config to a spring-session.xml for connecting the redis
if you set spring-session.xml, you should config also web.xml for springSessionRepositoryFilter