개요
동아리방 서버에 OpenStack과 같은 가상머신이 아닌, 소프트웨어 단위, 즉 컨테이너 단위로 시스템을 구성하고자 한다. Kubernetes는 컨테이너 오케스트레이터 이기도 하며, 여러대의 컴퓨터를 이용하여 무 중단 시스템을 구축을 하며, 여러개의 컨테이너를 동시에 돌리기 때문에 하나의 저장공간에서 개별적으로 돌리는것에는 많은 어려움이 있다. 그래서 k8s에서 여러개의 컴퓨터에 동시에 컨테이너를 돌리기 위해서는 하나의 공유 저장 공간이 필요했다. 그래서 NFS를 이용하여 하나의 공유 시스템을 구축하고, 해당 컴퓨터에 시스템을 구축하는 방법을 설명 하고자 한다.
Synology-CSI 를 선택하지 않은 이유
Synology를 이용해서 k8s에 공유 시스템을 구축하고자 한다면 가장 먼저 나오는 자료는
- https://github.com/jparklab/synology-csi
- https://github.com/SynologyOpenSource/synology-csi
총 2가지의 자료가 나온다. 2번째의 자료는 시놀로지에서 직접 만든 자료이긴 하지만, 자료가 유지 보수가 정상적으로 안하고 있는것 같다. 최신버전이 1.19 로 구현이 되어 있다 보니, 현재 서버 컴퓨터에 설치 된 1.23 버전과 호환에 문제가 있는것인지 정상적으로 동작하고 있지 않다. 그래서 직접 StorageClass 또는 PV, PVC를 직접 구성 해야 했다.
네트워크 볼륨
쿠버네티스에서 공유 데이터를 만들기 위해서 k8s에서 지원하는 볼륨을 찾아보았다. 지원하는 볼륨 목록
그렇지만 실제로 적용할 네트워크 볼륨은 크게 iSCSI와 NFS이다. 그 외에는 클라우드에 종속적인 볼륨이 많기 때문에 프라이빗 클라우드 성향을 가지는 개인 서버에는 실제로 적용하는것은 불가능했다.
그래서 iSCSI와 NFS 였는데 iSCSI는 AWS의 EBS처럼 SCSI의 프로토콜을 모두 네트워크로 통신으로 주고 받는 방식인 반면 NFS는 살짝 FTP와 비슷한 방식이다. 그래서 iSCSI는 작은 파일을 빠르게 많이 읽을 때 매우 빠른 성능을 보여주는 반면, NFS는 비교적 큰 파일을 읽을 때 빠른 성능을 보여준다.
하지만 실제 개발 할 때는 소스코드 파일 자체의 용량이 작으므로 iSCSI를 적용했을 때 더 좋은 퍼포먼스나, 성능을 발휘 하겠지만 iSCSI의 가장 큰 단점은 iSCSI는 1:1 연결만 지원한다는 것이다. 즉 1개의 Pod은 1개의 iSCSI 서버가 필요하다는 의미이다.
그리고 iSCSI의 가장 큰 단점이 하나 더 있었다.
iSCSI는 1:1 마운트만을 제공한다는 점, 그리고 노드 Fail 시 다른 노드로의 Migration이 발생하지 않는다는 단점을 가진다. 출처
그래서 NFS를 이용하여 구축하기로 하였다.
시스템 내부 구성
시스템 내부 구성은 1개의 스토리지 서버와 3개의 컴퓨터로 구성을 하였다. 스토리지 서버는 Synology의 DS218+ 를 선택하게 되었고, 컴퓨터의 경우는 가상 머신이 아닌 물리적인 컴퓨터 3대로 하게 되었다.
1개의 외부망에 L2 스위치 허브를 연결하여 각각의 컴퓨터에 고정아이피를 할당 하였다. 하지만 NFS나 iSCSI의 경우에는 오직 내부망을 통하여 통신 하고 있을 때 가능했다. 그래서 모든 컴퓨터에 외부망과 내부망을 따로 연결 한 뒤, 라우팅 테이블을 수정하여 외부와 통신을 할 때는 외부망과 192.168.0.0/24 로 통신이 진행이 될 때 내부망으로 통신하도록 라우팅 설정 하였다.
내부망의 정의는 L3에 있는 NAT 프로토콜을 통하여 통신이 이뤄지고 있는지로 정의가 된다.
eth0과 eth1에서 내부망과 외부망이 분리가 된다.
이로 써 NFS 시스템을 통해 내부망 끼리 스토리지를 공유 하며, 외부와의 접속이 이뤄질 때 외부망으로 통신을 하도록 하였다.
시스템 구축 명령어
PV와 PVC를 구축 할 때 StorageClass가 아닌 임의적으로 PV, PVC를 만들어서 직접 마운트 시켜보는 쪽으로 하였다.
우선 PV는 아래와 같이 구성을 하였다.
NFS의 서버는 192.168.0.40 으로 하였으며, Path는 NFS 서버에서 마운트를 할 폴더를 하였다. 그리고, accessModes는 여러 Pod이 동시에 읽고, 쓰기 작업이 일어날 수 있으므로, 여러개의 Pod이 처리할 수 있도록 하였다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv
spec:
capacity:
storage: 20Gi
volumeMode: Filesystem
persistentVolumeReclaimPolicy: Recycle
mountOptions:
- hard
accessModes:
- ReadWriteMany
nfs:
server: 192.168.0.40
path: /volume1/kubernetes/test2
PVC는 PV와 맞춰야 하므로 용량은 20GB로 잡았다.
1
2
3
4
5
6
7
8
9
10
11
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc
spec:
accessModes:
- ReadWriteMany
volumeMode: Filesystem
resources:
requests:
storage: 20Gi
그리고 테스트를 하기 위한 Pod과 매번 재생성이 일어나는지 테스트를 하기 위해서 Deployment와 Pod, 그리고 외부 접속을 하기 위해서 Service를 작성 하였다.
Deployment는 replicas를 통해 여러개의 pod이 균일하게 생성이 되도록 만들어 주는 기능이며, Pod은 Docker로 표현하자면 하나의 Docker-Compose와 같은 컨테이너 이다. Service는 NodePort를 사용하였는데 NodePort는 외부망에서 k8s로 접근을 할 때 사용되는 서비스 이다.
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
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 5
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes:
- name: pvs
persistentVolumeClaim:
claimName: pvc
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: pvs
---
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80
targetPort: 80
nodePort: 30007
위의 이미지 처럼 총 모두 5개의 Pod이 모두 정상적으로 동작하는것을 확인이 가능하다.
이 처럼 매번 서버에 접근이 이뤄질 때 마다 발생하는 pod이 다름에도 불구하고 정상적으로 동작하는것을 볼 수 있다.