Kubernetes 또는 OpenShift 로 PaaS 를 구축할 경우에, 사용자 환경에 대응하는 인프라 측의 2개 부문의 솔루션을 반드시 고민해야 하는데 그중 하나가 Persistent Storage 에 대한 솔루션이다(다른 하나는 Overlay Network 을 위한 터널링 솔루션/Fabric 이 되겠다).


대개는 NFS 와 같은 일반적인 스토리지 모델을 적용하여 Persistent Volume을 일정 갯수 이상 만들어 놓고 사용/반납하는 식으로 사용(Static Provisioning)할 것 같은데, GlusterFS를 스토리지로 사용하고 각 워커노드에서 Native GlusterFS Client 를 활용한다면, Auto-healing 과 분산 파일 스토리지의 장점을 살리면서 스토리지 공간도 동적으로 사용(Dynamic Provisioning)할 수 있겠다(Ceph 의 경우도 비슷한 장점을 가지고 있기는 하다).


본 포스팅에서는 제목에서 알 수 있듯이 Kubernetes 또는 OpenShift Cluster 외부에 존재하는 Gluster 클러스터를, Kubernetes를 Persistent Volume을 위한 스토리지로 Gluster 를 활용하는 내용을 다룰 것이다. 참고로 Kubernetes 내에 Gluster+Heketi Pod 를 DaemonSet 으로 Deploy한 다음 Hyper-Converged 방식으로 사용하는 경우도 존재하는데, 다음에 한 번 따로 다루어 보기로 하겠다.



구현 목표


기존에 설치/사용중인 Kubernetes(또는 OpenShift) 클러스터가 이미 존재한다고 가정하고, Persistent Storage로 일정 대수의 Gluster 서버(여기서는 2대-뒤에 3대 이상이 필요하다는 것이 함정)을 구성한 후 Heketi RestAPI 서버를 이용, Container(또는 Pod)에서 마운트 해서 사용하는 모델을 구현해 보기로 한다. Heketi를 활용한다면, Gluster 스토리지를 Rest 방식으로 OpenStack Manilla, OpenShift, Kubernetes 와 연동/통합하는 좋은 방안 중 하나일 듯하다.



* Reference 1: https://github.com/heketi/heketi

* Reference 2: https://keithtenzer.com/2017/03/24/storage-for-containers-using-gluster-part-ii/


사전 준비


스토리지용 서버 2대와 Gluster Console/Heketi API 서버 1대를 준비한다. gluster 서버의 설치에 관해서는 따로 다루지 않는다(필요하면 기존 포스팅 중 http://bryan.wiki/270 를 참고하기를 권한다). 또한 모든 서버간의 시간동기화 설정은 꼭 해 두는 것이 좋다.


* Heketi 서버(Gluster Console/Client): CentOS 7, IP - 192.168.50.100

* Gluster-1: CentOS 7, 3 disks(/dev/sdb, /dev/sdc, /dev/sdd), IP - 192.168.50.101

* Gluster-2: CentOS 7, 3 disks(/dev/sdb, /dev/sdc, /dev/sdd), IP - 192.168.50.102

* Gluster-3: 뒤에 언급한다


2대의 Gluster 서버와 1대의 Heketi 서버에서 /etc/hosts 파일에 아래 호스트 정보를 추가해 둔다


# vi /etc/hosts

...

192.168.50.100 gfs-console

192.168.50.101 gfs-node01

192.168.50.102 gfs-node02


Gluster 서버의 환경 점검


Firewall을 사용중이라면 firewall-cmd 를 통해 포트를 개방해 둔다.


[root@gfs-node01 ~]# firewall-cmd --add-port=24007-24008/tcp --add-port=49152-49664/tcp --add-port=2222/tcp  --add-port=8080/tcp

[root@gfs-node01 ~]# firewall-cmd --runtime-to-permanent


기존에 Gluster Volume을 사용중인 서버들이었다면 모든 볼륨을 삭제하고, Gluster 용으로 사용중인 모든 디스크를 unmount해야 하며 /etc/fstab 도 그에 맞게 수정한 후(reboot), 각 디스크의 파티션을 삭제하고 Raw Device 상태로 만들어 둔다. lsblk 를 수행했을 때 디바이스(/dev/sdb 등) 아래에 파티션(sdb1 등)이 보인다면 반드시 삭제한다.


[root@gfs-node01 ~]# fdisk /dev/sdb

p

(... 파티션이 있다면 ...)

d

w


[root@gfs-node01 ~]# fdisk /dev/sdc

p

d

w

...

...

[root@gfs-node01 ~]# wipefs -a /dev/sdb

[root@gfs-node01 ~]# wipefs -a /dev/sdc

[root@gfs-node01 ~]# wipefs -a /dev/sdd



Heketi 서버 설정


* Heketi 서버 데몬과 클라이언트 모듈을 설치한다

[root@gfs-console ~]# yum install -y epel-release

[root@gfs-console ~]# yum install -y glusterfs-client heketi heketi-client


* Gluster 노드 1, 2에 Passwordless ssh 접속을 가능하도록 키를 교환한다

[root@gfs-console ~]# ssh-keygen -f /etc/heketi/heketi_key -t rsa -N ''

[root@gfs-console ~]# ssh-copy-id -i /etc/heketi/heketi_key.pub root@gfs-node01

[root@gfs-console ~]# ssh-copy-id -i /etc/heketi/heketi_key.pub root@gfs-node02

[root@gfs-console ~]# chown heketi:heketi /etc/heketi/heketi_key*


* Heketi 동작 방식 및 키 파일 위치 설정

[root@gfs-console ~]# vi /etc/heketi/heketi.json

...

31     "executor": "ssh",

32 

33     "_sshexec_comment": "SSH username and private key file information",

34     "sshexec": {

35       "keyfile": "/etc/heketi/heketi_key",

36       "user": "root",

37       "port": "22",

38       "fstab": "/etc/fstab"

39     },

...

[root@gfs-console ~]# systemctl enable heketi

[root@gfs-console ~]# systemctl restart heketi

[root@gfs-console ~]# curl http://localhost:8080/hello

Hello from Heketi


*Gluster 서버의 Cluster 설정 정보와 디스크 Brick 구성을 정의한다

[root@gfs-console ~]# vi heketi_topology.json

{

  "clusters": [

    {

      "nodes": [

        {

          "node": {

            "hostnames": {

              "manage": [

                "192.168.50.101"

              ],

              "storage": [

                "192.168.50.101"

              ]

            },

            "zone": 1

          },

          "devices": [

            "/dev/sdb",

            "/dev/sdc",

            "/dev/sdd"

          ]

        },

        {

          "node": {

            "hostnames": {

              "manage": [

                "192.168.50.102"

              ],

              "storage": [

                "192.168.50.102"

              ]

            },

            "zone": 1

          },

          "devices": [

            "/dev/sdb",

            "/dev/sdc",

            "/dev/sdd"

          ]

        }

      ]

    }

  ]

}

[root@gfs-console ~]# export HEKETI_CLI_SERVER=http://192.168.50.100:8080


[root@gfs-console ~]# heketi-cli topology load --json=heketi_topology.json

Creating cluster ... ID: fae7eb318f26475afbd552236670b979

    Creating node 192.168.50.101 ... ID: 325db3146610c1546c32159ce0ca5b2c 

        Adding device /dev/sdb ... OK 

        Adding device /dev/sdc ... OK         

        Adding device /dev/sdd ... OK 

    Creating node 192.168.50.102 ... ID: 8d59a92dc64963e58c650be192762784 

        Adding device /dev/sdb ... OK 

        Adding device /dev/sdc ... OK 

        Adding device /dev/sdd ... OK

[root@gfs-console ~]# heketi-cli volume create --size=1 --replica=2

[root@gluster-console ~]# heketi-cli volume delete 207ee2a905675bc7506cadb6cce4624f

Volume 207ee2a905675bc7506cadb6cce4624f deleted

* Gluster 노드 구성이 2대일 경우, replica=3(디폴트)로 볼륨을 생성하면 "No Space" 에러가 발생하게 됨에 유의



Kubernetes 노드(Minion) 설정


모든 k8s 노드에 GlusterFS Client 를 설치한다


[root@kubenode01 ~]# yum install -y glusterfs-client

[root@kubenode01 ~]# setsebool -P virt_sandbox_use_fusefs on

* SELinux 기본 설정에서는 Pod 등에서 remote Gluster 서버로의 write 가 불가능하게 되어 있다. 반드시 setsebool 로 fusefs 쓰기 가능 설정



[3-노드 구성의 필요성]


여기서, 현재 고려중인 2-노드 Gluster Server 구성으로는 Kubernetes 에서 Gluster Volume 생성이 불가능함이 확인되었다. 즉 Heketi-cli 로는 --replicas=2 로 명시적으로 replica 를 2로 지정할 수 있지만, Kubernetes 의 리소스 정의(yaml 파일)을 통한 PVC(Persistent Volume Claim) 방식에서는 replica 를 명시적으로 지정할 수 있는 옵션이 존재하지 않는다. 따라서 다음과 같은, 새로운 Gluster Server(node03)를 추가하는 과정이 별도로 필요하다.


* 3번째 Gluster Server 노드 셋업 및 노드 추가(1~6)


1, 2번 노드와 동일한 스펙과 디스크 구성의 새로운 서버 생성

 - Gluster-3: CentOS 7, 3 disks(/dev/sdb, /dev/sdc, /dev/sdd), IP - 192.168.50.103


2. Gluster Server 패키지 설치

# hostnamectl set-hostname gfs-node03

# yum install -y centos-release-gluster310.noarch #-> 2017/08 기준 Long Term Stable 버전

# yum install -y glusterfs-server ntp

# yum update -y

# systemctl enable glusterd

# systemctl start glusterd

# vi /etc/ntp.conf

....

server 192.168.x.y iburst # 다른 Gluster Server 노드와 동일하게 설정

...

# systemctl enable ntpd

# systemctl start ntpd

# ntpq -pn

    remote           refid      st t when poll reach   delay   offset  jitter

==============================================================================

*192.168.x.y    203.248.240.140  3 u    2   64    1    0.324    7.201   0.323


3. 각 Gluster 노드의 /etc/hosts 파일 수정

# vi /etc/hosts

...

192.168.50.100 gfs-console

192.168.50.101 gfs-node01

192.168.50.102 gfs-node02

192.168.50.103 gfs-node03


4. 새로운 노드(gfs-node03)의 방화벽 제거, 디스크 구성 등 최종 확인

[root@gfs-node03 ~]# systemctl stop firewalld

[root@gfs-node03 ~]# systemctl disable firewalld

Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.

Removed symlink /etc/systemd/system/basic.target.wants/firewalld.service.


[root@gfs-node03 ~]# systemctl status glusterd

● glusterd.service - GlusterFS, a clustered file-system server

   Loaded: loaded (/usr/lib/systemd/system/glusterd.service; enabled; vendor preset: disabled)

   Active: active (running) since Fri 2017-08-11 08:25:36 EDT; 22min ago

 Main PID: 6953 (glusterd)

   CGroup: /system.slice/glusterd.service

           └─6953 /usr/sbin/glusterd -p /var/run/glusterd.pid --log-level INFO


Aug 11 08:25:36 gfs-node03 systemd[1]: Starting GlusterFS, a clustered file-system server...

Aug 11 08:25:36 gfs-node03 systemd[1]: Started GlusterFS, a clustered file-system server.


[root@gfs-node03 ~]# lsblk

NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT

fd0           2:0    1    4K  0 disk 

sda           8:0    0   20G  0 disk 

├─sda1        8:1    0    1G  0 part /boot

└─sda2        8:2    0   19G  0 part 

  ├─cl-root 253:0    0   17G  0 lvm  /

  └─cl-swap 253:1    0    2G  0 lvm  [SWAP]

sdb           8:16   0   20G  0 disk 

sdc           8:32   0   20G  0 disk 

sdd           8:48   0   20G  0 disk 

sr0          11:0    1 1024M  0 rom

* 각 디스크는 포맷되거나 파티셔닝 되지 않은 Raw 상태 그대로 유지


5. 기존 Gluster Server 노드 중 아무 서버로 로그인, 새로운 peer 추가

[root@gfs-node02 ~]# gluster peer status

Number of Peers: 1


Hostname: gfs-node01

Uuid: 995a7229-fcde-4eeb-b699-4f5fd2829b20

State: Peer in Cluster (Connected)


[root@gfs-node02 ~]# gluster peer probe 192.168.50.103

peer probe: success.

* Gluster 클러스터 외부(Kubernetes 클러스터)에서 Gluster 클러스터로 접근하는 경우이므로, 되도록이면 IP로 probe 한다


6. Heketi 서버에서 새로운 노드 추가

[root@gluster-console ~]# ssh-copy-id -i /etc/heketi/heketi_key.pub root@gfs-node03 #-> 키 교환 필수


[root@gluster-console ~]# heketi-cli node list

Id:d0517df692a2ebf40c47c73925ad21c4 Cluster:33b4b4ce590ca914e56aa606159e82c6

Id:f5a68d69da2c9f9d14243392ead4367c Cluster:33b4b4ce590ca914e56aa606159e82c6


[root@gluster-console ~]# heketi-cli node add \

> --zone=1 \

> --cluster=33b4b4ce590ca914e56aa606159e82c6 \

> --management-host-name=192.168.50.103 \

> --storage-host-name=192.168.50.103

Node information:

Id: 98c20ae34697f261ca6f45c05ca0105c

State: online

Cluster Id: 33b4b4ce590ca914e56aa606159e82c6

Zone: 1

Management Hostname 192.168.50.103

Storage Hostname 192.168.50.103

[root@gluster-console ~]# heketi-cli device add --name=/dev/sdb --node=98c20ae34697f261ca6f45c05ca0105c

Device added successfully

[root@gluster-console ~]# heketi-cli device add --name=/dev/sdc --node=98c20ae34697f261ca6f45c05ca0105c

Device added successfully

[root@gluster-console ~]# heketi-cli device add --name=/dev/sdd --node=98c20ae34697f261ca6f45c05ca0105c

Device added successfully

[root@gluster-console ~]# heketi-cli topology info

...클러스터 전체 노드/디스크 등 구성 정보...

* 기존 위에서 작업했던 heketi_topology.json 의 마지막에 새로운 노드 정보를 추가하고 heketi-cli topology load ... 를 해도 되고, 위와 같이 cli 명령을 통해 대화식으로 처리해도 된다



Kubernetes/OpenShift 에서 Heketi-Gluster 볼륨 사용을 위한 Secret, StorageClass 생성


[root@kubemaster ~]# mkdir kube-storage-gluster-heketi; cd kube-storage-gluster-heketi

[root@kubemaster kube-storage-gluster-heketi]# vi 00-gluster-secret.yaml

apiVersion: v1

kind: Secret

metadata:

  name: heketi-secret

  namespace: default

type: "kubernetes.io/glusterfs"

data:

  # echo -n "PASSWORD" | base64

  # key: PASSWORD_BASE64_ENCODED

  key: "UEFTU1dPUkQ="

[root@kubemaster kube-storage-gluster-heketi]# kubectl create -f 00-gluster-secret.yaml

secret "heketi-secret" created


[root@kubemaster kube-storage-gluster-heketi]# vi 01-gluster-heketi-storage-class.yaml 

apiVersion: storage.k8s.io/v1beta1

kind: StorageClass

metadata:

  name: gluster-heketi-external

provisioner: kubernetes.io/glusterfs

parameters:

  resturl: "http://192.168.50.100:8080"

  restuser: "admin"

  secretName: "heketi-secret"

  secretNamespace: "default"

  gidMin: "40000"

  gidMax: "50000"

[root@kubemaster kube-storage-gluster-heketi]# kubectl create -f 01-gluster-heketi-storage-class.yaml 

storageclass "gluster-heketi-external" created


[root@kubemaster kube-storage-gluster-heketi]# kubectl get secret,storageclass

NAME                            TYPE                                  DATA      AGE

secrets/default-token-10xkd     kubernetes.io/service-account-token   3         57d

secrets/heketi-secret           kubernetes.io/glusterfs               1         17h

secrets/heketi-storage-secret   Opaque                                1         1h


NAME                      TYPE

gluster-heketi-external   kubernetes.io/glusterfs

* gidMin~gidMax 값은 Storage Class 내에서 각 볼륨에 대해 유일한 값으로 순차적으로 정해지는 값이며, 볼륨이 삭제되면 gid Pool에 반납되도록 되어 있는 supplemental Group ID 값이다(PV가 attach된 Pod 내의 root 계정으로 전달되어 PV에 write 가능하게 된다. id 명령으로 확인 가능 - 참고), 별도 지정하지 않으면 2000~2147483647 사이의 값이 임의로 할당되는데, Heketi 3.x 이후 버전에서는 이 범위를 Storage Class 정의 시 지정하게 되어 있다 - 참고.


Kubernetes/OpenShift 에서 Heketi endpoint, job, secret 생성


* Heketi 서버에서 다음과 같이 실행하면 heketi-storage.json 파일이 생성된다(Gluster 서버쪽에는 heketidbstorage 볼륨이 생성)

[root@gluster-console ~]# heketi-cli setup-openshift-heketi-storage

Saving heketi-storage.json


* Kubernetes/OpenShift Master 쪽으로 Heketi 서버에서 생성한 heketi-storage.json 파일을 복사하여 kubectl create -f 한다

[root@kubemaster kube-storage-gluster-heketi]# scp root@192.168.50.100:~/heketi-storage.json .

root@192.168.50.100's password: 

heketi-storage.json                                                                       100%   83KB  83.1KB/s   00:00    


[root@kubemaster kube-storage-gluster-heketi]# kubectl create -f heketi-storage.json 

secret "heketi-storage-secret" created

endpoints "heketi-storage-endpoints" created

service "heketi-storage-endpoints" created

job "heketi-storage-copy-job" created


[root@kubemaster kube-storage-gluster-heketi]# kubectl get ep,job,pod,svc

NAME                                   ENDPOINTS                                            AGE

ep/heketi-storage-endpoints            192.168.50.101:1,192.168.50.102:1,192.168.50.103:1   1h

ep/kubernetes                          192.168.60.160:6443                                  58d


NAME                           DESIRED   SUCCESSFUL   AGE

jobs/heketi-storage-copy-job   1         1            1h


NAME                     READY     STATUS    RESTARTS   AGE

po/busybox-for-dnstest   1/1       Running   58         2d


NAME                                    CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE

svc/heketi-storage-endpoints            10.199.188.129   <none>        1/TCP     1h

svc/kubernetes                          10.199.0.1       <none>        443/TCP   58d

heketi-storage-copy-job이 완료(SUCCESSFUL=1) 되면 Kubernetes 클러스터 내에서 Heketi 를 통한 동적 볼륨 사용이 가능하게 된 것이다. 


Kubernetes/OpenShift 에서 PV/PVC 생성 & 확인 


Yaml 파일을 통해서 Gluster Volume으로 된 pvc, pv 생성 확인


[root@kubemaster kube-storage-gluster-heketi]# cat gluster-pvc-example.yaml 

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

 name: gluster-dyn-pvc

 annotations:

   volume.beta.kubernetes.io/storage-class: gluster-heketi-external

spec:

 accessModes:

  - ReadWriteOnce

 resources:

   requests:

     storage: 1Gi


[root@kubemaster kube-storage-gluster-heketi]# kubectl create -f gluster-pvc-example.yaml 

persistentvolumeclaim "gluster-dyn-pvc" created


[root@kubemaster kube-storage-gluster-heketi]# kubectl get pvc

NAME              STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE

gluster-dyn-pvc   Bound     pvc-801691d7-7f04-11e7-9f70-005056a79d0a   1Gi        RWO           31m


[root@kubemaster kube-storage-gluster-heketi]# kubectl get pv

NAME                                       CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM                     REASON    AGE

pvc-801691d7-7f04-11e7-9f70-005056a79d0a   1Gi        RWO           Delete          Bound     default/gluster-dyn-pvc             27m

* Persistent Volume Claim 이 처리 되면, 그에 대응하는 Persistent Volume 이 할당된다



10. 생성된 PV를 사용하는 Pod를 생성하고, Volume 이 정상 작동하는지 확인


[root@kubemaster kube-storage-gluster-heketi]# vi 04-gluster-pod-pv-example.yaml

apiVersion: v1

kind: Pod

metadata:

  name: nginx-pv-pod

  labels:

    name: nginx-pv-pod

spec:

  containers:

  - name: nginx-pv-pod

    image: gcr.io/google_containers/nginx-slim:0.8

    ports:

    - name: web

      containerPort: 80

    volumeMounts:

    - name: gluster-vol1

      mountPath: /usr/share/nginx/html

  volumes:

  - name: gluster-vol1

    persistentVolumeClaim:

      claimName: gluster-dyn-pvc


[root@kubemaster kube-storage-gluster-heketi]# kubectl get pod -o wide

NAME                     READY     STATUS    RESTARTS   AGE       IP            NODE

po/busybox-for-dnstest   1/1       Running   116        4d        172.30.59.4   192.168.60.162

po/nginx-pv-pod          1/1       Running   0          2h        172.30.59.5   192.168.60.162


[root@kubemaster kube-storage-gluster-heketi]# kubectl describe pod nginx-pv-pod 

Name: nginx-pv-pod

Namespace: default

Node: 192.168.60.162/192.168.60.162

Start Time: Mon, 14 Aug 2017 05:52:21 -0400

Labels: name=nginx-pv-pod

Status: Running

IP: 172.30.59.5

Controllers: <none>

Containers:

  nginx-pv-pod:

    Container ID: docker://29e40373a1999612ed7ac50d18e2a933aa2adbb7a31d403a7dc239d4b8fbaa6d

    Image: gcr.io/google_containers/nginx-slim:0.8

    Image ID: docker-pullable://gcr.io/google_containers/nginx-slim@sha256:8b4501fe0fe221df663c22e16539f399e89594552f400408303c42f3dd8d0e52

    Port: 80/TCP

    State: Running

      Started: Mon, 14 Aug 2017 05:53:23 -0400

    Ready: True

    Restart Count: 0

    Volume Mounts:

      /usr/share/nginx/html from gluster-vol1 (rw)

      /var/run/secrets/kubernetes.io/serviceaccount from default-token-10xkd (ro)

    Environment Variables: <none>

Conditions:

  Type Status

  Initialized True 

  Ready True 

  PodScheduled True 

Volumes:

  gluster-vol1:

    Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)

    ClaimName: gluster-dyn-pvc

    ReadOnly: false

  default-token-10xkd:

    Type: Secret (a volume populated by a Secret)

    SecretName: default-token-10xkd

QoS Class: BestEffort

Tolerations: <none>

No events.


[root@kubemaster kube-storage-gluster-heketi]# kubectl exec -it nginx-pv-pod -- bash

  root@nginx-pv-pod:/# cd /usr/share/nginx/html

  root@nginx-pv-pod:/# echo 'Hello World from GlusterFS!' > index.html

  root@nginx-pv-pod:/# exit


 [root@kubemaster kube-storage-gluster-heketi]# curl http://172.30.59.5

 Hello World from GlusterFS!



Kuberbetes 노드에서 mount 상태를, GlusterFS 서버에서 볼륨 내용을 확인해 보자

[root@kubenode02 ~]# mount -l

...

192.168.50.101:vol_f8a6a9f5ef82d805c25e5cd98a63e89d on /var/lib/kubelet/pods/43e560c9-80d6-11e7-9f70-005056a79d0a/volumes/kubernetes.io~glusterfs/pvc-801691d7-7f04-11e7-9f70-005056a79d0a type fuse.glusterfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other,max_read=131072)

...


[root@gfs-node01 ~]# gluster volume info vol_f8a6a9f5ef82d805c25e5cd98a63e89d

 

Volume Name: vol_f8a6a9f5ef82d805c25e5cd98a63e89d

Type: Replicate

Volume ID: 46c88aae-2521-4d0e-b3e4-a4c2afef850b

Status: Started

Snapshot Count: 0

Number of Bricks: 1 x 3 = 3

Transport-type: tcp

Bricks:

Brick1: 192.168.50.103:/var/lib/heketi/mounts/vg_a040256530d028869b78f30a614efe3b/brick_0bd04d87fcb584d14a5b605ecad0eb9d/brick

Brick2: 192.168.50.102:/var/lib/heketi/mounts/vg_3a0181f175a826880a02d03d16e8e908/brick_c9ee2b0d42e30788005ec3b73228563e/brick

Brick3: 192.168.50.101:/var/lib/heketi/mounts/vg_3a7109f386e745ce9f990657e9388a19/brick_84f16f03a4aaa388d6ceacf607b24443/brick

Options Reconfigured:

transport.address-family: inet

performance.readdir-ahead: on

nfs.disable: on


[root@gfs-node01 ~]# ls -l /var/lib/heketi/mounts/vg_3a7109f386e745ce9f990657e9388a19/brick_84f16f03a4aaa388d6ceacf607b24443/brick

total 4

-rw-r--r--. 2 root 40000 30 Aug 14 05:59 index.html



- Barracuda -



저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들

Gluster 파일시스템의 Geo-replication 환경을 구축하고 테스트 하는 과정을 정리해 보자. 작업 환경을 단순하게, 과정을 직관적으로 표현하기 위해 추가 디스크 없는 VM 2개만으로 모든 내용을 소화해 보기로 한다. 이를 위해 Linux 의 Sparse 파일을 가상 디스크로 매핑하여, 마치 추가 디스크가 장착된, 또는 별도 파티션 된 물리 디스크 볼륨이 있는 것처럼 흉내 내는 방법을 같이 다루어 보고자 한다(블록디바이스를 통한 비슷한 테스트를 진행해야 할 때 유용한 방법으로 써먹을 수 있을 것이다).



CentOS 7.2에 Gluster Filesystem 설치


먼저 위의 그림과 같이 네트워크가 연결된 2개의 가상머신을 준비한다(CentOS 7.2 또는 RHEL 7.2는 이미 설치되었다고 가정하자). Redhat 이나 CentOS의 경우 대개 EPEL 을 통해 Gluster 를 설치하게 된다.


* 주의: 2개의 서버 노드를 peer 로 연결하여 Cluster 에 투입하는 것은 아니며, 서로 독립된 Gluster 세트가 원격지에 각각 존재하는 경우에 대한 내용을 다룬다. 여러 peer 를 통하여 Brick을 구성하고 데이터가 분산(distributed) 또는 중복(replica)되는 Volume 구성에 대해서는 여기서 논외로 하자.


* 2개의 서버 각각에 다음과 같이 gluster 를 설치한다.

# yum install -y wget

# wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-8.noarch.rpm

# rpm -ivh epel-release-7-8.noarch.rpm

# yum install -y centos-release-gluster38.noarch

# yum install -y glusterfs-server

yum install -y glusterfs-geo-replication

# systemctl start glusterd

# systemctl enable glusterd


* RHEL 7 에서는 약간 과정이 다르다. 다음과 같이 해 보자.

# yum install -y wget

# wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-8.noarch.rpm

# rpm -ivh epel-release-7-8.noarch.rpm

# vi /etc/yum.repos.d/Gluster.repo

[gluster38]

name=Gluster 3.8

baseurl=http://mirror.centos.org/centos/7/storage/$basearch/gluster-3.8/

gpgcheck=0

enabled=1

# yum install -y glusterfs-server

# yum install -y glusterfs-geo-replication

# systemctl start glusterd

# systemctl enable glusterd


* 커맨드를 단순하게 하기 위해 /etc/hosts 파일에 다음의 2 라인을 추가

10.10.10.10 gnode1

10.10.10.11 gnode2


* 호스트 명을 각각 gnode1, gnode 로 변경하고 재접속

hostnamectl set-hostname gnode1



준비1: 데이터를 동기화할 가상 블록디바이스 장착

서두에서 말한 것처럼 Sparse 파일(1 GByte 짜리 ^^;;)을 만들어 가상디스크로 붙이고, 이를 데이터 동기화용 블록디바이스로 활용할 것이다. VM 2개에 각각 다음과 같이 작업한다.


* losetup -f 로 Sparse 이미지 파일을 LO 디바이스에 매핑하면, 자동으로 다음의 빈 LO 디바이스(아래의 경우, /dev/loop2)가 찾아져서 매핑됨

* 블록디바이스가 준비되었으므로 xfs 로 파일시스템을 포맷하고 마운트포인트를 /mnt/gnode_disk1 디렉토리로 하여 시스템에 마운트하면 준비 끝

[root@gnode1 ~]# mkdir gluster_disk

[root@gnode1 ~]# cd gluster_disk/

[root@gnode1 ~]# dd if=/dev/zero of=./disk1.img bs=1M count=1024

[root@gnode1 ~]# losetup -f /root/gluster_disk/disk1.img

[root@gnode1 ~]# losetup

NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE

...

/dev/loop2         0      0         0  0 /root/gluster_disk/disk1.img 

[root@gnode1 ~]# mkfs.xfs /dev/loop2

[root@gnode1 ~]# mkdir /mnt/gnode_disk1

[root@gnode1 ~]# mount /dev/loop2 /mnt/gnode_disk1/



준비2: Password-less 로그인 설정

Geo-replication 이 가능하게 하기 위해서는, 2개의 서버에서 각각 상대방 서버로 패스워드 없이 root 계정으로 접속할 수 있도록 ssh 키를 교환해 두어야 한다. 다음과 같이 진행하자.


* Password-less ssh 접속을 위한 키 교환. 상대방 서버에 ssh 접속하고 암호를 입력해 두면 다음에는 암호 없이 로그인 가능

[root@gnode1 ~]# ssh-keygen 

[root@gnode1 ~]# ssh-copy-id -i /root/.ssh/id_rsa.pub root@10.10.10.11

[root@gnode1 ~]# ssh gnode2



Gluster Volume 생성 및 기동

각각의 Gluster 서버에서 다음과 같이 Volume 을 생성하고 기동한다. 


* 주의: 마운트포인트 아래에 서브디렉토리를 생성하여 볼륨(여기서는 distvol)에 할당해야 함

[root@gnode1 ~]# mkdir -p /mnt/gnode_disk1/brick

[root@gnode1 ~]# gluster volume create distvol gnode1:/mnt/gnode_disk1/brick

[root@gnode1 ~]# gluster volume start distvol

[root@gnode1 ~]# gluster volume info

Volume Name: distvol

Type: Distribute

Volume ID: 9754b79a-d0e7-4823-9e85-38340d99e732

Status: Started

Snapshot Count: 0

Number of Bricks: 1

Transport-type: tcp

Bricks:

Brick1: gnode1:/mnt/gnode_disk1/brick

Options Reconfigured:

nfs.disable: on

performance.readdir-ahead: on

transport.address-family: inet


[root@gnode2 ~]# mkdir -p /mnt/gnode_disk1/brick

[root@gnode2 ~]# gluster volume create distvol gnode2:/mnt/gnode_disk1/brick

[root@gnode2 ~]# gluster volume start distvol

[root@gnode2 ~]# gluster volume info

Volume Name: distvol

Type: Distribute

Volume ID: e12db607-2e19-4a45-bc3b-eb6e922f59e5

Status: Started

Snapshot Count: 0

Number of Bricks: 1

Transport-type: tcp

Bricks:

Brick1: gnode2:/mnt/gnode_disk1/brick

Options Reconfigured:

nfs.disable: on

performance.readdir-ahead: on

transport.address-family: inet



Geo-replication 설정 및 테스트

* Geo-replication 설정은 Master(여기서는 gnode1) 에서만 수행

[root@gnode1 ~]# gluster system:: execute gsec_create

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol create push-pem

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol start

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol status

MASTER NODE    MASTER VOL    MASTER BRICK              SLAVE USER    SLAVE              SLAVE NODE    STATUS    CRAWL STATUS       LAST_SYNCED                  

-----------------------------------------------------------------------------------------------------------------------------------------------------

gnode1         distvol       /mnt/gnode_disk1/brick    root          gnode2::distvol    gnode2        Active    Changelog Crawl    2016-10-10 01:10:04

[root@gnode1 ~]# gluster volume info distvol

Volume Name: distvol

Type: Distribute

Volume ID: 9754b79a-d0e7-4823-9e85-38340d99e732

Status: Started

Snapshot Count: 0

Number of Bricks: 1

Transport-type: tcp

Bricks:

Brick1: gnode1:/mnt/gnode_disk1/brick

Options Reconfigured:

changelog.changelog: on

geo-replication.ignore-pid-check: on

geo-replication.indexing: on

nfs.disable: on

performance.readdir-ahead: on

transport.address-family: inet



동기화 테스트

동기화 테스트를 수행하기 위해 별도의 Gluster Client 를 사용해야 하지만, Gluster 서버 자신을 클라이언트로 사용해도 된다. 다시 말해 Gluster 서버에서 자신의 Volume 을 리모트 마운트하는 것이다. 다음과 같이 진행해 보자.


* gnode1: Gluster Fuse Client 로 distvol 볼륨을 로컬의 /mnt/gnode1_distvol/ 디렉토리로 마운트하고 해당 디렉토리로 이동

[root@gnode1 ~]# mkdir /mnt/gnode1_distvol

[root@gnode1 ~]# mount.glusterfs gnode1:/distvol /mnt/gnode1_distvol/

[root@gnode1 ~]# cd /mnt/gnode1_distvol/


* gnode2: Gluster Fuse Client 로 distvol 볼륨을 로컬의 /mnt/gnode2_distvol/ 디렉토리로 마운트하고 해당 디렉토리로 이동

[root@gnode2 ~]# mkdir /mnt/gnode2_distvol

[root@gnode2 ~]# mount.glusterfs gnode1:/distvol /mnt/gnode2_distvol/

[root@gnode2 ~]# cd /mnt/gnode2_distvol/


* gnode1: 파일 생성, 수정 및 삭제

[root@gnode1 ~]# echo abcdef----xyzxyz----xxxx > test.txt

[root@gnode1 ~]# cp test.txt s

[root@gnode1 ~]# cat test.txt >> s

[root@gnode1 ~]# cp s x

[root@gnode1 ~]# cat s >> x

[root@gnode1 ~]# vi x

[root@gnode1 ~]# rm tttt

...

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol status

MASTER NODE    MASTER VOL    MASTER BRICK              SLAVE USER    SLAVE              SLAVE NODE    STATUS    CRAWL STATUS       LAST_SYNCED                  

-----------------------------------------------------------------------------------------------------------------------------------------------------

gnode1         distvol       /mnt/gnode_disk1/brick    root          gnode2::distvol    gnode2        Active    Changelog Crawl    2016-10-10 01:15:22


* gnode2 디렉토리 내의 파일 변화 관찰, 확인

[root@gnode2 gnode2_distvol]# ls

s  test.txt  x

[root@gnode2 gnode2_distvol]# cat test.txt

abcdef----xyzxyz----xxxx

[root@gnode2 gnode2_distvol]#


몇 가지 팁들 ... port 변경, 작은 파일에 유리한 option 등

Gluster 의 geo-replication은 내부적으로 ssh를 통한 rsync 방식으로 동작한다. 일반적으로 ssh는 TCP 22 포트를 사용하는데, 임의의 포트번호로 설정하려면 다음과 같이 진행하면 된다(사실, "How to replicate to slave via non-standard ssh port..." 에 대한 답이다).


우선 양쪽 Gluster 서버에서 ssh port 를 변경한다


# vi /etc/ssh/sshd_config

...

Port 22222

...


# systemctl restart sshd

# netstat -anp | grep 22222

tcp   0   0   0.0.0.0:22222    0.0.0.0:*    LISTEN    8425/sshd

...


만약 netstat 결과에 해당 포트번호로 바인딩되어 있지 않고 ssh 접속이 안된다면 SELINUX가 ssh의 기본 Port인 22번으로 바인딩하게 고정해 둔 것일 게다. 그러면 다음과 같은 semanage 명령을 수행하고 조금 시간이 지난 뒤에 다시 접속해 본다.


# semanage port -a -t ssh_port_t -p tcp 22222


Linux 서버에서 파이어월(firewalld)이 사용되고 있다면 다음과 같이 해당 포트를 허용해 주는 것도 잊지 않도록 하자.


# firewall-cmd --permanent --zone=public --add-port=22222/tcp

# firewall-cmd --reload


성공적으로 ssh 포트 번호를 변경하였다면, 이번에는 Gluster 에서 새로운 포트로 Geo-replication 이 되도록 설정하면 된다.


[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol stop

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol delete

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol create ssh-port 22222 push-pem

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol config ssh-port 22222

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol start



Rsync는 Block 방식의 전송이기 때문에 파일의 크기가 큰 경우에 좀 더 유리하다. 반대로 말하면 크기가 작은(대략 수 KB~수백KB 이내) 파일의 경우 전송 지연이 발생할 수 있게 되는데, 이 때에는 다음과 같은 옵션을 적용하여 테스트해 보기를 권한다. Geo-replication 이 작동되는 도중에도 실행해 볼 수 있다.


[root@gnode1 ~] gluster volume geo-replication distvol gnode2::distvol config use-tarssh true


참고로, 간단히 테스트 해 보니 50KB 크기의 몇 백 개 파일 전송에 1분 26초 정도 걸리던 것이, 옵션 변경 후에 약 10초 가량 전송 시간이 줄어든 것을 볼 수 있었다.


원래의 기본 전송방식인 Rsync 방식으로 돌아가려면 다음과 같이 하면 된다.

[root@gnode1 ~] gluster volume geo-replication distvol gnode2::distvol config \!use-tarssh



Good Luck !


- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


(참고)Opensuse 에서는 본 과정이 필요하지 않았음.
CentOS에서 별도로 ctype의 설정이 필요한 것을 보면 RHEL 등의 redhad 계열에서 모두 필요한 과정으로 보임.

Dependancy: fuse, fuse-devel, flex, bison, python-devel, ctype

위의 의존성 있는 패키지들을 모두 설치하여야 한다.
단, ctype은 기본 centOS repository 에 포함되어 있지 않으므로 Source로 다운로드해서 빌드한다.

 # wget wget  http://downloads.sourceforge.net/project/ctypes/ctypes/1.0.2/ctypes-1.0.2.tar.gz
# tar xvzf  ctypes-1.0.2.tar.gz
# cd  ctypes-1.0.2
# python setup.py build
# python setup.py install

이제 Gluster Source를 다운로드하고 설치한다.

# wget http://download.gluster.com/pub/gluster/glusterfs/3.2/3.2.2/glusterfs-3.2.2.tar.gz
# tar xvzf glusterfs-3.2.2.tar.gz
# cd  glusterfs-3.2.2
# ./configure --enable-fusermount
# make && make install
# ldconfig

Gluster volume 을 마운트하고 테스트해본다
# mount -t glusterfs w.x.y.z:/volname /local_mount_point


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들