티스토리 뷰

<출처>

https://kubernetes.io/docs/tutorials/stateful-application/zookeeper/





이번 튜토리얼에서는 StatefulSetsPodDisruptionBudgets,과 PodAntiAffinity을 이용하여 K8S 상에서 Apache Zookeeper을 동작시키는 것을 시연해본다.




Objectives


이 튜토리얼을 마친 후, 다음을 익히게 될 것이다.

  • StatefulSet을 이용하여 ZooKeeper 앙상블(ensemble)을 배포하는 법
  • ConfigMaps을 이용하여 일관되게 앙상블을 구성하는 법
  • 앙상블 내 ZooKeeper 서버에 대한 배포를 확산하는 법
  • 계획된 정비 기간 중 PodDisruptionBudgets를 이용하여 서비스 가용성을 보장하는 법 




Before you begin


이 튜토리얼을 시작하기 전에, 다음 K8S 개념에 대해 친숙해야만 한다.


최소 4개의 노드를 가진 클러스터가 필요할 것이다. 그리고 각 노드는 최소 2 CPUs 와 4 GiB 메모리가 요구된다. 이번 튜토리얼에서 여러분은 클러스터의 노드를 차단(cordon)하고 유출(drain) 해보게 될 것이다. 이 말의 의미는 클러스터가 노드상의 모든 Pod들을 종료시키고 축출시켜, 일시적으로 노드가 스케쥴 불가 상태에 이르게 될 것이라는 의미이다. 여러분은 이 튜토리얼을 위해 전용 클러스터를 사용해야만 한다. 전용 클러스터가 아닐 경우, 여러분이 유발한 (서비스) 중단이 다른 테넌트들에게 간섭을 주지 않을 것이라는 확신이 서야만 한다.

이 튜토리얼은 동적으로 PersistentVolumes를 프로비저닝 해주는 클러스터 구성이 되어 있음을 가정한다. 준비가 안되었다면, 튜토리얼을 시작하기 전에 손수 3개의 20 GiB 볼륨을 준비해야 한다.


이번 실습은 만만치 않다. 시도 결과, 원문의 결과와 다르게 나오는 경우가 상당부분 있었고 반복 시도하며 검증해 봤으나, 이러한 차이가 비정상이 아니라는 결론을 내렸다. 무조건 명령어를 복사/붙여넣기 하여 실습하게 되면 진행이 어려울 것이다. 특히 마지막 Maintenance 파트의 경우, 각자 클러스터 구성환경이 다를테니, 그 출력 결과도 달리 나타날 수 있다. 명령어와 그 결과에 대한 주요 포인트만 체크하면서 정상여부를 판단해 나가면 될 듯하다.(사실상 필자도 전체를 클리어 하게 이해하지 못한 상태인지라 반복해가며 더 체크하고 정리해 나가야 할 듯하다.)


K8S 클러스터에 연결하기 위해 다음 절차를 따른다.  원하는 작업 터미널 형태를 선택하여 연결한다.




1
2
3
4
5
6
7
8
9
10
11
[root@docker-registry ~]# kubectl get nodes
Unable to connect to the server: x509: certificate signed by unknown authority
[root@docker-registry ~]# gcloud container clusters get-credentials zookeeper-ensemble --zone asia-east1-c --project zerobig-k8s-tutorial
Fetching cluster endpoint and auth data.
kubeconfig entry generated for zookeeper-ensemble.
[root@docker-registry ~]# kubectl get nodes
NAME                                                STATUS    ROLES     AGE       VERSION
gke-zookeeper-ensemble-default-pool-71e68058-39tp   Ready     <none>    5m        v1.9.7-gke.6
gke-zookeeper-ensemble-default-pool-71e68058-45k6   Ready     <none>    5m        v1.9.7-gke.6
gke-zookeeper-ensemble-default-pool-71e68058-t7rd   Ready     <none>    5m        v1.9.7-gke.6
gke-zookeeper-ensemble-default-pool-71e68058-zf4t   Ready     <none>    5m        v1.9.7-gke.6
cs



ZooKeeper Basics

Apache ZooKeeper 은 분산형 애플리케이션을 위한 분산, 오픈 소스 코디네이션 서비스다. ZooKeeper는 데이터 읽기, 쓰기 그리고 업데이트에 대한 인지를 가능하게 해준다. 데이터는 계층처럼 파일 시스템에서 조직화되고 앙상블(하나의 ZooKeeper 서버 세트)내 모든 ZooKeeper 서버에 복제된다. 데이터에 대한 모든 조작은 미세하고 연속적으로 일관된다. ZooKeeper는 앙상블 내 모든 서버를 통해서 상태 머신(state machine)를 복제하는 Zab 만장일치 프로토콜을 이용함으로써 이를 보장해준다. 

앙상블은 리더 선발을 위해 Zab 프로토콜을 이용하고, 앙상블은 그 선출이 완료될 때까지 데이터 쓰기가 불가하다. 일단 (선출이) 완료되면, 앙상블은 이를 승인하기 전에, 정족수(quorum)에게 모든 기록들이 복사된 것을 보증하기 위해 Zab을 이용하고 클라이언트에게 보일 수 있도록 해준다. 가중치의 정족수 고려없이, 정족수는 현재 리더를 포함하는 앙상블 구성의 과반수를 나타낸다. 가령, 앙상블에 세 개의 서버가 있다면, 리더와 다른 하나의 서버를 포함하는 구성이면 정족수가 성립된다. 앙상블이 정족수를 이루지 못하면, 앙상블은 데이터에 쓰기가 불가하다.

ZooKeeper 서버는 메모리에 전체 상태 머신을 유지하고, 모든 변화를 스토리지 매체 상의 내구성 강한 WAL(Write Ahead Log)에 기록한다. 서버가 갑작스럽게 고장나면, WAL을 재생하여 이전 상태로 복구할 수 있다. WAL이 제한없이 증가하는 것을 방지하기 위해, ZooKeeper 서버는 정기적으로 스토리지 매체에 메모리 상태에 대한 스냅샷을 찍어 둘 것이다. 이러한 스냅샷들은 직접 메모리 속으로 적재될 수도 있고, 스냅샷 전에 선행된 모든  WAL 기입항목들은 폐기될지도 모른다.




Creating a ZooKeeper Ensemble


하기 manifest는 Headless ServiceServicePodDisruptionBudget, 그리고 StatefulSet을 포함고 있다.


application/zookeeper/zookeeper.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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
apiVersion: v1
kind: Service
metadata:
  name: zk-hs
  labels:
    app: zk
spec:
  ports:
  - port: 2888
    name: server
  - port: 3888
    name: leader-election
  clusterIP: None
  selector:
    app: zk
---
apiVersion: v1
kind: Service
metadata:
  name: zk-cs
  labels:
    app: zk
spec:
  ports:
  - port: 2181
    name: client
  selector:
    app: zk
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  selector:
    matchLabels:
      app: zk
  maxUnavailable: 1
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: zk
spec:
  selector:
    matchLabels:
      app: zk
  serviceName: zk-hs
  replicas: 3
  updateStrategy:
    type: RollingUpdate
  podManagementPolicy: Parallel
  template:
    metadata:
      labels:
        app: zk
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zk
              topologyKey: "kubernetes.io/hostname"
      containers:
      - name: kubernetes-zookeeper
        imagePullPolicy: Always
        image: "k8s.gcr.io/kubernetes-zookeeper:1.0-3.4.10"
        resources:
          requests:
            memory: "1Gi"
            cpu: "0.5"
        ports:
        - containerPort: 2181
          name: client
        - containerPort: 2888
          name: server
        - containerPort: 3888
          name: leader-election
        command:
        - sh
        - -c
        - "start-zookeeper \
          --servers=3 \
          --data_dir=/var/lib/zookeeper/data \
          --data_log_dir=/var/lib/zookeeper/data/log \
          --conf_dir=/opt/zookeeper/conf \
          --client_port=2181 \
          --election_port=3888 \
          --server_port=2888 \
          --tick_time=2000 \
          --init_limit=10 \
          --sync_limit=5 \
          --heap=512M \
          --max_client_cnxns=60 \
          --snap_retain_count=3 \
          --purge_interval=12 \
          --max_session_timeout=40000 \
          --min_session_timeout=4000 \
          --log_level=INFO"
        readinessProbe:
          exec:
            command:
            - sh
            - -c
            - "zookeeper-ready 2181"
          initialDelaySeconds: 10
          timeoutSeconds: 5
        livenessProbe:
          exec:
            command:
            - sh
            - -c
            - "zookeeper-ready 2181"
          initialDelaySeconds: 10
          timeoutSeconds: 5
        volumeMounts:
        - name: datadir
          mountPath: /var/lib/zookeeper
      securityContext:
        runAsUser: 1000
        fsGroup: 1000
  volumeClaimTemplates:
  - metadata:
      name: datadir
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 10Gi
cs



터미널을 열어, manifest 생성을 위해 kubectl apply 이용한다.

1
[root@docker-registry ~]# kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml
cs


이 명령은 zk-hs Headless Service, zk-cs Service, zk-pdb PodDisruptionBudget, 그리고 zk StatefulSet을 생성한다.

1
2
3
4
5
[root@docker-registry ~]# kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml
service/zk-hs created
service/zk-cs created
poddisruptionbudget.policy/zk-pdb created
statefulset.apps/zk created
cs


StatefulSet 컨트롤러가 StatefulSet의 Pods를 생성하는 것을 지켜보기위해 kubectl get 명령을 이용한다.

1
[root@docker-registry ~]# kubectl get pods -w -l app=zk
cs



Once the zk-2 Pod가 동작 대기 상태가 되면, CTRL-C 를 눌러 kubectl을 종료시킨다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@docker-registry ~]# kubectl get pods -w -l app=zk
NAME      READY     STATUS        RESTARTS   AGE
zk-0      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-0      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-1      0/1       ContainerCreating   0         0s
zk-1      0/1       Running   0         4s
zk-0      0/1       Running   0         7s
zk-2      0/1       Terminating   0         2m
zk-2      0/1       Terminating   0         2m
zk-1      1/1       Running   0         22s
zk-2      0/1       Pending   0         0s
zk-2      0/1       Pending   0         0s
zk-2      0/1       ContainerCreating   0         0s
zk-0      1/1       Running   0         24s
zk-2      0/1       Running   0         18s
^C[root@docker-registry ~]
cs



StatefulSet 컨트롤러는 3개의 Pod를 생성한다. 각 Pod는 ZooKeeper 서버를 담고 있는 컨테이너다.

Facilitating Leader Election

익명의 네트워크에서 리더를 선출하는 경우, 종료처리 알고리즘이 없기 때문에, Zab은 리더 선출 수행을 위한 명시적인 맴버쉽 구성을 요구한다. 앙상블 내 각 서버는 고유의 식별자가 필요하며, 모든 서버는 전체적인 식별자 세트를 파악하고 있어야 한다. 그리고 각 식별자는 네트워크 주소와 함께 연계되어질 필요가 있다.

zk StatefulSet 내 Pod들의 호스트네임을 취하기 위해 kubectl exec 를 이용한다.

1
[root@docker-registry ~]# for i in 0 1 2; do kubectl exec zk-$i -- hostname; done
cs


StatefulSet 컨트롤러는 순서 인덱스를 근거로 고유의 호스트네임을 갖는 개별 Pod를 제공해준다. 호스트네임은 "<statefulset name>-<ordinal index>" 형식을 갖는다.  zk StatefulSet 필드의 "replicas" 가 3으로 설정되어 있으므로, Set 컨트롤러는 zk-0zk-1, 그리고 zk-2로 설정된 호스트네임을 갖는 3개의 Pod를 생성한다.

1
2
3
4
[root@docker-registry ~]# for i in 0 1 2; do kubectl exec zk-$i -- hostname; done
zk-0
zk-1
zk-2
cs


ZooKeeper 앙상블은 고유의 식별자로서 자연수를 이용하고 서버의 데이터 디렉토리내 "myid"라 칭하는 파일에 각 서버의 식별자를 저장한다.

각 서버에 대한 "myid" 컨텐츠를 조사하기 위해 다음 명령을 이용한다.

1
2
3
4
5
6
7
[root@docker-registry ~]# for i in 0 1 2; do echo "myid zk-$i";kubectl exec zk-$i -- cat /var/lib/zookeeper/data/myid; done
myid zk-0
1
myid zk-1
2
myid zk-2
3
cs


zk StatefulSet내 각 Pod의 전체 주소 도메인 이름(Fully Qualified Domain Name)을 확인하기 위해 다음 명령을 이용힌다.

1
[root@docker-registry ~]# for i in 0 1 2; do kubectl exec zk-$i -- hostname -f; done
cs


zk-hs Service는 모든 Pod들에 대해 도메인을 생성한다. 

1
2
3
4
[root@docker-registry ~]# for i in 0 1 2; do kubectl exec zk-$i -- hostname -f; done
zk-0.zk-hs.default.svc.cluster.local
zk-1.zk-hs.default.svc.cluster.local
zk-2.zk-hs.default.svc.cluster.local
cs


A 레코드는 Pod의 IP 주소에 대한 FQDN을 분석해 준다. K8S가 Pod를 재 스케쥴하면, Pod의 새로운 IP 주소로 A 레코드를 업데이트 할 것이다. 그러나 A 레코드 이름은 변경되지 않을 것이다.

ZooKeeper는 zoo.cfg. Use kubectl" 이름의 파일 내 애플리케이션 구성정보를 저장한다. "zk-0" Pod의 "zoo.cfg" 파일 내용을 확인하기 위해 "kubectl exec"를 이용한다.

1
[root@docker-registry ~]# kubectl exec zk-0 -- cat /opt/zookeeper/conf/zoo.cfg
cs

파일 맨 하단에 "server.1", "server.2", 그리고 "server.3" 속성은 ZooKeeper server의 "myid" 파일 내 식별자와 일치한다. "zk" StatefulSet 내 Pod에 대한 FQDN으로 설정 되어진다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@docker-registry ~]# for i in 0 1 2; do kubectl exec zk-$i -- hostname -f; done
zk-0.zk-hs.default.svc.cluster.local
zk-1.zk-hs.default.svc.cluster.local
zk-2.zk-hs.default.svc.cluster.local
[root@docker-registry ~]# for i in 0 1 2; do kubectl exec zk-$i -- hostname -f; done
zk-0.zk-hs.default.svc.cluster.local
zk-1.zk-hs.default.svc.cluster.local
zk-2.zk-hs.default.svc.cluster.local
[root@docker-registry ~]# kubectl exec zk-0 -- cat /opt/zookeeper/conf/zoo.cfg
#This file was autogenerated DO NOT EDIT
clientPort=2181
dataDir=/var/lib/zookeeper/data
dataLogDir=/var/lib/zookeeper/data/log
tickTime=2000
initLimit=10
syncLimit=5
maxClientCnxns=60
minSessionTimeout=4000
maxSessionTimeout=40000
autopurge.snapRetainCount=3
autopurge.purgeInteval=12
server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888
server.2=zk-1.zk-hs.default.svc.cluster.local:2888:3888
server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888
cs



Achieving Consensus

만장일치 프로토콜에서는 각 참각자의 식별자가 고유하도록 요구한다. Zab 프로토콜에서 두 참가자는 동일한 고유 식별자를 요청해서는 안된다. 어느 프로세스가 어느 데이터에 커밋하였는지에 대해 시스템 내 프로세스가 동의하도록 허용해줄 필요가 있다. 만약 두 개의 Pod가 동일 순서값으로 구동되어지면, 두 ZooKeeper 서버는 그것들을 동일 서버로 인식하게 된다.

1
2
3
4
5
[root@docker-registry ~]# kubectl get pods -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   0          6m
zk-1      1/1       Running   0          6m
zk-2      1/1       Running   0          6m
cs


각 Pod에 대한 A 레코드는 Pod가 준비상태가 될 때 입력된다. 그러므로, ZooKeeper 서버의 FQDN은 단일 엔드포인트로 분석될 것이다. 그리고 그 엔드포인트는 "myid" 파일 내 구성된 ID를 요청하는 유일한 ZooKeeper 서버가 될 것이다.

1
2
3
zk-0.zk-hs.default.svc.cluster.local
zk-1.zk-hs.default.svc.cluster.local
zk-2.zk-hs.default.svc.cluster.local
cs

이는 ZooKeeper의 "zoo.cfg" 파일 내 "Servers" 속성이 구성된 앙상블을 정확하게 나타낸다는 것을 보증한다.

1
2
3
server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888
server.2=zk-1.zk-hs.default.svc.cluster.local:2888:3888
server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888
cs


서버가 값을 커밋하기 위해 Zab 프로토콜을 이용할 때, 그것들은 만장일치를 이루어 값을 커밋하거나 (리더 선출에 성공하였고 최소 2개의 Pod가 동작 대기 상태라면), 또는 그렇게 하는 것을 실패한다 (조건 중 어느 쪽이던 맞지 않는다면) . 한 서버가 또 다른 서버를 대신하여 기록을 인지하게 되는 상태는 생기지 않을 것이다.


Sanity Testing the Ensemble

가장 기본적인 적합성 테스트(sanity test)는 하나의 ZooKeeper 서버에 데이터를 기록하고 다른 서버에서 그 데이터를 읽어 보는 것이다.

아래 명령은 앙상블의 "zk-0" Pod 에 "/hello" 경로에 "world" 를 기록하는 "zkCli.sh" 스크립트를 실행한다.

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
[root@docker-registry ~]# kubectl exec zk-0 zkCli.sh create /hello world
Connecting to localhost:2181
2018-09-05 23:46:20,988 [myid:] - INFO  [main:Environment@100- Client environment:zookeeper.version=3.4.10-39d3a4f269333c922ed3db283be479f9deacaa0f, built on 03/23/2017 10:13 GMT
2018-09-05 23:46:20,992 [myid:] - INFO  [main:Environment@100- Client environment:host.name=zk-0.zk-hs.default.svc.cluster.local
2018-09-05 23:46:20,992 [myid:] - INFO  [main:Environment@100- Client environment:java.version=1.8.0_131
2018-09-05 23:46:20,994 [myid:] - INFO  [main:Environment@100- Client environment:java.vendor=Oracle Corporation
2018-09-05 23:46:20,995 [myid:] - INFO  [main:Environment@100- Client environment:java.home=/usr/lib/jvm/java-8-openjdk-amd64/jre
2018-09-05 23:46:20,995 [myid:] - INFO  [main:Environment@100- Client environment:java.class.path=/usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.10.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper:
2018-09-05 23:46:20,995 [myid:] - INFO  [main:Environment@100- Client environment:java.library.path=/usr/java/packages/lib/amd64:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib
2018-09-05 23:46:20,995 [myid:] - INFO  [main:Environment@100- Client environment:java.io.tmpdir=/tmp
2018-09-05 23:46:20,995 [myid:] - INFO  [main:Environment@100- Client environment:java.compiler=<NA>
2018-09-05 23:46:20,995 [myid:] - INFO  [main:Environment@100- Client environment:os.name=Linux
2018-09-05 23:46:20,995 [myid:] - INFO  [main:Environment@100- Client environment:os.arch=amd64
2018-09-05 23:46:20,995 [myid:] - INFO  [main:Environment@100- Client environment:os.version=4.4.111+
2018-09-05 23:46:20,995 [myid:] - INFO  [main:Environment@100- Client environment:user.name=zookeeper
2018-09-05 23:46:20,996 [myid:] - INFO  [main:Environment@100- Client environment:user.home=/home/zookeeper
2018-09-05 23:46:20,996 [myid:] - INFO  [main:Environment@100- Client environment:user.dir=/
2018-09-05 23:46:20,997 [myid:] - INFO  [main:ZooKeeper@438- Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@22d8cfe0
2018-09-05 23:46:21,019 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1032- Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
2018-09-05 23:46:21,129 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@876- Socket connection established to localhost/127.0.0.1:2181, initiating session
2018-09-05 23:46:21,209 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1299- Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x165ac1a75d30000, negotiated timeout = 30000
 
WATCHER::
 
WatchedEvent state:SyncConnected type:None path:null
Created /hello
cs


zk-1 Pod의 데이터를 확인하기 위해 다음 명령을 사용한다.

1
[root@docker-registry ~]# kubectl exec zk-1 zkCli.sh get /hello
cs


"zk-0 "에 생성했던 데이터는 앙상블의 모든 서버에서 이용가능하다.

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
[root@docker-registry ~]# kubectl exec zk-1 zkCli.sh get /hello
Connecting to localhost:2181
2018-09-05 23:47:17,359 [myid:] - INFO  [main:Environment@100- Client environment:zookeeper.version=3.4.10-39d3a4f269333c922ed3db283be479f9deacaa0f, built on 03/23/2017 10:13 GMT
2018-09-05 23:47:17,363 [myid:] - INFO  [main:Environment@100- Client environment:host.name=zk-1.zk-hs.default.svc.cluster.local
2018-09-05 23:47:17,363 [myid:] - INFO  [main:Environment@100- Client environment:java.version=1.8.0_131
2018-09-05 23:47:17,365 [myid:] - INFO  [main:Environment@100- Client environment:java.vendor=Oracle Corporation
2018-09-05 23:47:17,366 [myid:] - INFO  [main:Environment@100- Client environment:java.home=/usr/lib/jvm/java-8-openjdk-amd64/jre
2018-09-05 23:47:17,366 [myid:] - INFO  [main:Environment@100- Client environment:java.class.path=/usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.10.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper:
2018-09-05 23:47:17,366 [myid:] - INFO  [main:Environment@100- Client environment:java.library.path=/usr/java/packages/lib/amd64:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib
2018-09-05 23:47:17,366 [myid:] - INFO  [main:Environment@100- Client environment:java.io.tmpdir=/tmp
2018-09-05 23:47:17,366 [myid:] - INFO  [main:Environment@100- Client environment:java.compiler=<NA>
2018-09-05 23:47:17,366 [myid:] - INFO  [main:Environment@100- Client environment:os.name=Linux
2018-09-05 23:47:17,366 [myid:] - INFO  [main:Environment@100- Client environment:os.arch=amd64
2018-09-05 23:47:17,367 [myid:] - INFO  [main:Environment@100- Client environment:os.version=4.4.111+
2018-09-05 23:47:17,367 [myid:] - INFO  [main:Environment@100- Client environment:user.name=zookeeper
2018-09-05 23:47:17,367 [myid:] - INFO  [main:Environment@100- Client environment:user.home=/home/zookeeper
2018-09-05 23:47:17,367 [myid:] - INFO  [main:Environment@100- Client environment:user.dir=/
2018-09-05 23:47:17,368 [myid:] - INFO  [main:ZooKeeper@438- Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@22d8cfe0
2018-09-05 23:47:17,390 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1032- Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
2018-09-05 23:47:17,458 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@876- Socket connection established to localhost/127.0.0.1:2181, initiating session
2018-09-05 23:47:17,476 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1299- Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x265ac1a66620000, negotiated timeout = 30000
 
WATCHER::
 
WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x200000002
ctime = Wed Sep 05 23:46:21 UTC 2018
mZxid = 0x200000002
mtime = Wed Sep 05 23:46:21 UTC 2018
pZxid = 0x200000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
cs


Providing Durable Storage

ZooKeeper Basics 섹션에서 언급한 바와 같이, ZooKeeper는 영속성있는 WAL에 모든 기입내역들을 커밋하고, 주기적으로 매체 스토리지에 메모리 상태 내 스냅샷을 기록한다. 영속성을 제공하기 위해 WALs을 이용하는 것은 복제된 상태 머신을 획득하기 위해 만장일치 프로토콜을 이용하는 애플리케이션에서는 보편적인 기술이다.

"zk" StatefulSet을 지우기 위해  "kubectl delete" 명령을 이용한다.

1
2
[root@docker-registry ~]# kubectl delete statefulset zk
statefulset.apps "zk" deleted
cs

StatefulSet 내 Pod의 종료를 지켜본다.

1
[root@docker-registry ~]# kubectl get pods -w -l app=zk
cs


"zk-0"가 완전히 종료되면, kubectl을 종료하기 위해 "CTRL-C"를 이용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@docker-registry ~]# kubectl get pods -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   0          6m
zk-1      1/1       Running   0          6m
zk-2      1/1       Running   0          6m
zk-0      1/1       Terminating   0         10m
zk-1      1/1       Terminating   0         10m
zk-2      1/1       Terminating   0         10m
zk-1      0/1       Terminating   0         11m
zk-0      0/1       Terminating   0         11m
zk-2      0/1       Terminating   0         10m
zk-2      0/1       Terminating   0         10m
zk-1      0/1       Terminating   0         11m
zk-1      0/1       Terminating   0         11m
zk-0      0/1       Terminating   0         11m
zk-0      0/1       Terminating   0         11m
zk-2      0/1       Terminating   0         11m
zk-2      0/1       Terminating   0         11m
^C[root@docker-registry ~]
cs


"zookeeper.yaml" 에서 manifest를 재적용한다.

1
2
3
4
5
[root@docker-registry ~]# kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml
service/zk-hs unchanged
service/zk-cs unchanged
poddisruptionbudget.policy/zk-pdb unchanged
statefulset.apps/zk created
cs


이는 zk StatefulSet 객체를 생성하지만, manifest 내 다른 API 객체는 이미 존재하므로 수정되지 않는다.

StatefulSet 컨트롤러에서 StatefulSet의 Pods를 재생성하는 것을 지켜본다.

1
[root@docker-registry ~]# kubectl get pods -w -l app=zk
cs


zk-2 Pod가 동작대기 상태에 이르면, kubectl을 종료하기 위해 "CTRL-C"를 이한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@docker-registry ~]# kubectl get pods -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Pending   0          0s
zk-0      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-2      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-2      0/1       Pending   0         0s
zk-1      0/1       ContainerCreating   0         0s
zk-2      0/1       ContainerCreating   0         0s
zk-1      0/1       Running   0         18s
zk-0      0/1       Running   0         18s
zk-2      0/1       Running   0         18s
zk-1      1/1       Running   0         28s
zk-0      1/1       Running   0         29s
zk-2      1/1       Running   0         29s
cs


"zk-2 "Pod로 부터 적합성 테스트 동안 여러분이 입력했던 값들을 얻어 오기 위해 다음 명령을 이용한다.

1
[root@docker-registry ~]# kubectl exec zk-2 zkCli.sh get /hello
cs


"zk" StatefulSet에서 모든 Pod를 종료시키고 재생성했다 하더라도, 앙상블은 여전히 원래 값을 제공하고 있다.

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
[root@docker-registry ~]# kubectl exec zk-2 zkCli.sh get /hello
Connecting to localhost:2181
2018-09-05 23:53:19,250 [myid:] - INFO  [main:Environment@100- Client environment:zookeeper.version=3.4.10-39d3a4f269333c922ed3db283be479f9deacaa0f, built on 03/23/2017 10:13 GMT
2018-09-05 23:53:19,254 [myid:] - INFO  [main:Environment@100- Client environment:host.name=zk-2.zk-hs.default.svc.cluster.local
2018-09-05 23:53:19,254 [myid:] - INFO  [main:Environment@100- Client environment:java.version=1.8.0_131
2018-09-05 23:53:19,257 [myid:] - INFO  [main:Environment@100- Client environment:java.vendor=Oracle Corporation
2018-09-05 23:53:19,257 [myid:] - INFO  [main:Environment@100- Client environment:java.home=/usr/lib/jvm/java-8-openjdk-amd64/jre
2018-09-05 23:53:19,257 [myid:] - INFO  [main:Environment@100- Client environment:java.class.path=/usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.10.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper:
2018-09-05 23:53:19,257 [myid:] - INFO  [main:Environment@100- Client environment:java.library.path=/usr/java/packages/lib/amd64:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib
2018-09-05 23:53:19,257 [myid:] - INFO  [main:Environment@100- Client environment:java.io.tmpdir=/tmp
2018-09-05 23:53:19,257 [myid:] - INFO  [main:Environment@100- Client environment:java.compiler=<NA>
2018-09-05 23:53:19,258 [myid:] - INFO  [main:Environment@100- Client environment:os.name=Linux
2018-09-05 23:53:19,258 [myid:] - INFO  [main:Environment@100- Client environment:os.arch=amd64
2018-09-05 23:53:19,258 [myid:] - INFO  [main:Environment@100- Client environment:os.version=4.4.111+
2018-09-05 23:53:19,258 [myid:] - INFO  [main:Environment@100- Client environment:user.name=zookeeper
2018-09-05 23:53:19,258 [myid:] - INFO  [main:Environment@100- Client environment:user.home=/home/zookeeper
2018-09-05 23:53:19,258 [myid:] - INFO  [main:Environment@100- Client environment:user.dir=/
2018-09-05 23:53:19,260 [myid:] - INFO  [main:ZooKeeper@438- Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@22d8cfe0
2018-09-05 23:53:19,282 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1032- Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
2018-09-05 23:53:19,340 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@876- Socket connection established to localhost/127.0.0.1:2181, initiating session
2018-09-05 23:53:19,416 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1299- Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x365ac26851f0000, negotiated timeout = 30000
 
WATCHER::
 
WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x200000002
ctime = Wed Sep 05 23:46:21 UTC 2018
mZxid = 0x200000002
mtime = Wed Sep 05 23:46:21 UTC 2018
pZxid = 0x200000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
cs


"zk" StatefulSet의 "spec"의 "volumeClaimTemplates" 필드는 각 Pod를 위해 제공한 PersistentVolume을 명시한다.

1
2
3
4
5
6
7
8
9
10
11
volumeClaimTemplates:
  - metadata:
      name: datadir
      annotations:
        volume.alpha.kubernetes.io/storage-class: anything
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 20Gi
 
cs


"StatefulSet" 컨트롤러는 "StatefulSet" 내 각 Pod를 위해 "PersistentVolumeClaim"를 생성한다. 

"StatefulSet"의 "PersistentVolumeClaims"를 확인하기 위해 다음 명령을 이용한다.

1
[root@docker-registry ~]# kubectl get pvc -l app=zk
cs

"StatefulSet"가 Pod를 재생성할 때, Pod의 PersistentVolumes를 다시 마운트한다.

1
2
3
4
5
[root@docker-registry ~]# kubectl get pvc -l app=zk
NAME           STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
datadir-zk-0   Bound     pvc-568be3bd-b164-11e8-8f46-42010af000df   10Gi       RWO            standard       20m
datadir-zk-1   Bound     pvc-568f1b89-b164-11e8-8f46-42010af000df   10Gi       RWO            standard       20m
datadir-zk-2   Bound     pvc-56965c34-b164-11e8-8f46-42010af000df   10Gi       RWO            standard       20m
cs


"StatefulSet"의 컨테이너 "template" 에 대한 "volumeMounts" 섹션은 ZooKeeper server들의 데이터 디렉토리 내 PersistentVolumes 을 마운트한다.

1
2
3
volumeMounts:
        - name: datadir
          mountPath: /var/lib/zookeeper
cs


"zk" "StatefulSet" 내 Pod가 (재) 스케쥴 될 때, 항상 ZooKeeper 서버의 데이터 디렉토리에 마운트 된 동일한 "PersistentVolume"을 가질 것이다. 심지어 Pod가 재 스케쥴될 때도, 모든 기록들은 ZooKeeper 서버의 WAL에서 이루어지고, 모든 스냇샷은 영속성을 유지한다.





Ensuring Consistent Configuration


Facilitating Leader Election 와 Achieving Consensus 섹션에서 언급했 듯이,  ZooKeeper 앙상블 내 서버는 리더를 선출하고 정족수를 형성하기 위해 일관성 있는 구성을 요구한다.  또한 프로토콜이 네트워크 상에서 올바르게 동작하도록 Zab 프로토콜의 일관성 있는 구성도 요구한다. 예제에서 우리는 manifest 속에 직접 구성을 끼워 넣음으로서 일관성 있는 구성을 이룬다.


"zk" StatefulSet을 확인한다.

1
2
3
4
5
6
7
8
9
10
[root@docker-registry ~]# kubectl get sts zk -o yaml
...
      - command:
        - sh
        - -c
        - start-zookeeper --servers=3 --data_dir=/var/lib/zookeeper/data --data_log_dir=/var/lib/zookeeper/data/log
          --conf_dir=/opt/zookeeper/conf --client_port=2181 --election_port=3888 --server_port=2888
          --tick_time=2000 --init_limit=10 --sync_limit=5 --heap=512M --max_client_cnxns=60
          --snap_retain_count=3 --purge_interval=12 --max_session_timeout=40000 --min_session_timeout=4000
          --log_level=INFO
cs


ZooKeeper server를 구동시키기 위해 사용한 명령어는 명령어 라인 파라미터로서 구성을 건제줬다. 여러분은 앙상블에 구성을 전달하기 위한 환경변수를 이용할 수도 있다.


Configuring Logging

"zkGenConfig.sh" 스크립트에 의해 생성된 파일들 중 하나는 ZooKeeper의 로깅을 제어한다. ZooKeeper는 기본적으로 Log4j를 사용한다. 로깅 구성을 위해 시간과 크기 기반 file appender 롤링을 이용한다.

"zk StatefulSet" 내 Pod 중 하나로 부터 로깅 구성을 확인하기 위해 아래 명령을 이용한다.

1
[root@docker-registry ~]# kubectl exec zk-0 cat /usr/etc/zookeeper/log4j.properties
cs


아래 로깅 구성은 ZooKeeper 프로세스가 표준 출력 파일 스트림에 모든 로그를 기록하도록 초래할 것이다.

1
2
3
4
5
6
7
8
[root@docker-registry ~]# kubectl exec zk-0 cat /usr/etc/zookeeper/log4j.properties
zookeeper.root.logger=CONSOLE
zookeeper.console.threshold=INFO
log4j.rootLogger=${zookeeper.root.logger}
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold}
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n
cs

이는 컨테이너 내부에 안전하게 로그를 기록할 수 가장 간단한 적절한 방법이다. 애플리케이션이 표준 출력으로 로그를 기록하기 때문에, K8S는 여러분을 위해 로그 로테이션을 처리해 줄 것이다. 또한 K8S는 애플리케이션 로그가 표준 출력으로 기록되도록 보증하고 표준 에러가 로컬 저장 매체를 고갈 시키지 않도록 보증하는 합리적인 보존 정책을 이행한다. 

Pod 중 하나로부터 마지막 20줄의 로그를 불러오기 위해 "kubectl logs" 를 이용한다.

1
[root@docker-registry ~]# kubectl logs zk-0 --tail 20
cs

여러분은 "kubectl logs"와 K8S 대쉬보드를 이용하여 표준 출력 또는 표준 에러에 기록된 애플리케이션 로그를 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@docker-registry ~]# kubectl logs zk-0 --tail 20
2018-09-05 23:58:27,173 [myid:1- INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@883- Processing ruok command from /127.0.0.1:39154
2018-09-05 23:58:27,174 [myid:1- INFO  [Thread-80:NIOServerCnxn@1044- Closed socket connection for client /127.0.0.1:39154 (no session established for client)
2018-09-05 23:58:35,099 [myid:1- INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192- Accepted socket connection from /127.0.0.1:39166
2018-09-05 23:58:35,100 [myid:1- INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@883- Processing ruok command from /127.0.0.1:39166
2018-09-05 23:58:35,101 [myid:1- INFO  [Thread-81:NIOServerCnxn@1044- Closed socket connection for client /127.0.0.1:39166 (no session established for client)
2018-09-05 23:58:37,175 [myid:1- INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192- Accepted socket connection from /127.0.0.1:39172
2018-09-05 23:58:37,175 [myid:1- INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@883- Processing ruok command from /127.0.0.1:39172
2018-09-05 23:58:37,177 [myid:1- INFO  [Thread-82:NIOServerCnxn@1044- Closed socket connection for client /127.0.0.1:39172 (no session established for client)
2018-09-05 23:58:45,095 [myid:1- INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192- Accepted socket connection from /127.0.0.1:39178
2018-09-05 23:58:45,096 [myid:1- INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@883- Processing ruok command from /127.0.0.1:39178
2018-09-05 23:58:45,097 [myid:1- INFO  [Thread-83:NIOServerCnxn@1044- Closed socket connection for client /127.0.0.1:39178 (no session established for client)
2018-09-05 23:58:47,172 [myid:1- INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192- Accepted socket connection from /127.0.0.1:39182
2018-09-05 23:58:47,173 [myid:1- INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@883- Processing ruok command from /127.0.0.1:39182
2018-09-05 23:58:47,174 [myid:1- INFO  [Thread-84:NIOServerCnxn@1044- Closed socket connection for client /127.0.0.1:39182 (no session established for client)
2018-09-05 23:58:55,098 [myid:1- INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192- Accepted socket connection from /127.0.0.1:39186
2018-09-05 23:58:55,099 [myid:1- INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@883- Processing ruok command from /127.0.0.1:39186
2018-09-05 23:58:55,101 [myid:1- INFO  [Thread-85:NIOServerCnxn@1044- Closed socket connection for client /127.0.0.1:39186 (no session established for client)
2018-09-05 23:58:57,175 [myid:1- INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192- Accepted socket connection from /127.0.0.1:39190
2018-09-05 23:58:57,176 [myid:1- INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@883- Processing ruok command from /127.0.0.1:39190
2018-09-05 23:58:57,179 [myid:1- INFO  [Thread-86:NIOServerCnxn@1044- Closed socket connection for client /127.0.0.1:39190 (no session established for client)
cs


K8S는 Stackdriver 와 Elasticsearch 그리고 Kibana와 통합하여 보다 복잡하지만 더 막강한 로깅을 지원한다. 클러스터 레벨의 로그 운송과 집합을 위해, 여러분의 로그를 순환시키고 나르기 위해 sidecar 컨테이너를 배포하는것을 고려한다.


Configuring a Non-Privileged User

컨테이너 내부에서 권한이 있는 사용자로서 애플리케이션을 구동할 수 있도록 허용하는 모범사례는 논란이 될 만한 문제다. 여러분의 조직에서 권한이 없는 사용자가 애플리케이션을 구동하는 것이 요구된다면, 진입점을 구동하는 사용자를 제어하기 위해  SecurityContext를 이용할 수 있다.

"zk" "StatefulSet"의 Pod "template"은 "SecurityContext"을 담고 있다.

1
2
3
securityContext:
  runAsUser: 1000
  fsGroup: 1000
cs

Pods의 컨테이너에서, UID 1000은 zookeeper 그룹에서 GID 1000에 상응한다.

"zk-0" Pod로부터 ZooKeeper 프로세스 정보를 확인한다.

1
[root@docker-registry ~]# kubectl exec zk-0 -- ps -elf
cs


"securityContext" 객체의 "runAsUser" 필드가 1000으로 설정되어 있으므로, 루트로 구동하는 대신, zookeeper 사용자로서 ZooKeeper 프로세스를 구동한다.

1
2
3
4
5
[root@docker-registry ~]# kubectl exec zk-0 -- ps -elf
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S zookeep+     1     0  0  80   0 -  1127 -      Sep05 ?        00:00:00 sh -c start-zookeeper --servers=3 --data_dir=/var/lib/zookeeper/data --data_log_dir=/var/lib/zookeeper/data/log --conf_dir=/opt/zookeeper/conf --client_port=2181 --election_port=3888 --server_port=2888 --tick_time=2000 --init_limit=10 --sync_limit=5 --heap=512M --max_client_cnxns=60 --snap_retain_count=3 --purge_interval=12 --max_session_timeout=40000 --min_session_timeout=4000 --log_level=INFO
4 S zookeep+     5     1  0  80   0 - 747007 -     Sep05 ?        00:00:01 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Dzookeeper.log.dir=/var/log/zookeeper -Dzookeeper.root.logger=INFO,CONSOLE -cp /usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.10.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper: -Xmx512M -Xms512M -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /usr/bin/../etc/zookeeper/zoo.cfg
4 R zookeep+  1173     0  0  80   0 -  8606 -      00:00 ?        00:00:00 ps -elf
cs


기본적으로, Pod’의 PersistentVolumes가 ZooKeeper 서버의 데이터 디렉토리에 마운트 될 때, 오직 루트 사용자만이 접근 가눙하다. 이 구성은 ZooKeeper 프로세스가 WAL 에 기록하고 스냅샷을 저장하는 것을 막는다.

"zk-0" Pod 상에 ZooKeeper 데이터 디렉토리의 파일 퍼미션을 확인하기 위해 다음 명령을 이용한다.

1
[root@docker-registry ~]# kubectl exec -ti zk-0 -- ls -ld /var/lib/zookeeper/data
cs


"securityContext" 객체의 "fsGroup" 필드가 1000으로 설정되어 있으므로, Pods의 PersistentVolumes에 대한 소유권이 zookeeper 그룹으로 설정되어 있고, ZooKeeper 프로세스는 데이터에 읽기와 쓰기가 가능하다.

1
2
[root@docker-registry ~]# kubectl exec -ti zk-0 -- ls -ld /var/lib/zookeeper/data
drwxrwsr-4 zookeeper zookeeper 4096 Sep  4 23:39 /var/lib/zookeeper/data
cs





Managing the ZooKeeper Process


ZooKeeper documentation 은 다음과 같이 언급하고 있다. "여러분은 개별 ZooKeeper 서버 프로세스(JVM)를 관리하는 감시 프로세스를 갖고 싶어할 것이다." 분산 시스템에서 실패된 프로세스를 재시작 해주는 워치독(감시 프로세스) 을 활용하는 것은 보편적인 패턴이다. K8S에서 애플리케이션을 배포할 때, 외부 유틸리티를 감시 프로세스로 이용하기 보다는, 여러분의 애플리케이션에 대한 워치독으로 K8S를 이용해야만 한다.


Updating the Ensemble

"zk" "StatefulSet"은 "RollingUpdate" 업데이트 전략을 사용하기 의해 구성되어 진다.

서버에 할당된 "cpus"의 수를 업데이트 하기 위해 "kubectl patch"를 이용할 수 있다.

1
2
[root@docker-registry ~]# kubectl patch sts zk --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/cpu", "value":"0.3"}]'
statefulset.apps/zk patched
cs


업데이트 상태를 지켜보기 위해 "kubectl rollout status"를 이용한다.

1
2
3
4
5
6
7
8
9
10
11
[root@docker-registry ~]# kubectl rollout status sts/zk
waiting for statefulset rolling update to complete 0 pods at revision zk-6ccdf4ddb5...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 1 pods at revision zk-6ccdf4ddb5...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 2 pods at revision zk-6ccdf4ddb5...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
statefulset rolling update complete 3 pods at revision zk-6ccdf4ddb5...
cs


이는, 순서값의 역순으로, 한번에 하나 씩 Pod를 종료시키고 새로운 구성으로 Pod를 재생성한다. 이는 롤링 업데이트 동안 정족수가 유지되는 것을 보장해 준다. 

이력과 이전 구성정보를 보기 위해 "kubectl rollout history" 명령을 이용한다.

1
2
3
4
5
6
7
[root@docker-registry ~]# kubectl rollout history sts/zk
statefulsets "zk"
REVISION
0
0
1
2
cs


modification을 롤백하기 하기 위해 "kubectl rollout undo" 명령을 이용한다. 

1
2
[root@docker-registry ~]# kubectl rollout undo sts/zk
statefulset.apps/zk rolled back
cs


Handling Process Failure

Restart Policies는 하나의 Pod 내 컨테이너의 진입점에 대한 프로세스 실패를 어떻게 K8S가 처리해 줄지에 대한 방법을 제어한다. 하나의 "StatefulSet" 내 Pod들에 대하여, 유일하게 적절한 "RestartPolicy"는 "Always" 이며 기본값이기도 하다. stateful 애플리케이션에서 여러분은 기본 정책을 절대 무효화해서는 안된다.

U"zk-0" Pod에서 동작하는 ZooKeeper 서버에 대한 프로세스 트리를 조사하기 위해 다음 명령을 이용한다.

1
[root@docker-registry ~]# kubectl exec zk-0 -- ps -ef
cs


컨테이너의 진입점으로 사용된 명령은 PID 1을 갖는다. 그리고 진입점의 자식 프로세스인 ZooKeeper 프로세스는 PID 11을 갖는다. (ZooKeeper 프로세스 ID는 각자 상황에 따라 다를 수 있다.)

1
2
3
4
5
[root@docker-registry ~]# kubectl exec zk-0 -- ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
zookeep+     1     0  0 00:06 ?        00:00:00 sh -c start-zookeeper --servers=3 --data_dir=/var/lib/zookeeper/data --data_log_dir=/var/lib/zookeeper/data/log --conf_dir=/opt/zookeeper/conf --client_port=2181 --election_port=3888 --server_port=2888 --tick_time=2000 --init_limit=10 --sync_limit=5 --heap=512M --max_client_cnxns=60 --snap_retain_count=3 --purge_interval=12 --max_session_timeout=40000 --min_session_timeout=4000 --log_level=INFO
zookeep+    11     1  0 00:06 ?        00:00:01 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Dzookeeper.log.dir=/var/log/zookeeper -Dzookeeper.root.logger=INFO,CONSOLE -cp /usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.10.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper: -Xmx512M -Xms512M -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /usr/bin/../etc/zookeeper/zoo.cfg
zookeep+   352     0  0 00:08 ?        00:00:00 ps -ef
cs


다음 명령으로 다른 창에서 "zk" "StatefulSet" 내 Pod를 지켜본다.

1
[root@docker-registry ~]# kubectl get pod -w -l app=zk
cs


또 다른 창에서, 다음 명령으로 Pod "zk-0" 내 ZooKeeper 프로세스를 종료시킨다.  

1
[root@docker-registry ~]# kubectl exec zk-0 -- pkill java
cs


ZooKeeper 프로세스의 종료는 부모 프로세스를 종료시키는 결과를 가져왔다.  컨테이너의 "RestartPolicy"가 "Always" 이므로, 부모 프로세스가 재구동 된 것이다.

1
2
3
4
5
6
7
8
[root@docker-registry ~]# kubectl get pod -w -l app=zk
NAME      READY     STATUS        RESTARTS   AGE
zk-0      1/1       Running       0          4m
zk-1      1/1       Running       0          39s
zk-2      1/1       Running       0          1m
zk-0      0/1       Error         0          1m
zk-0      0/1       Running       1          1m
zk-0      1/1       Running       1          1m
cs


여러분의 애플리케이션이 애플리케이션의 비지니스 로직을 이행하는 프로세스를 구동시키기 위해 스크립트 ("zkServer.sh" 와 같은)를 이용한다면, 그 스크립트는 자식 프로세스를 종료시켜야만 한다. 이는 프로세스가 애플리케이션의 비지니스 로직 이행에 실패 했을 때, K8S가 애플리케이션의 컨테이너를 재구동 시키는 것을 보장해 준다.


Testing for Liveness

여러분의 애플리케이션이 실패된 프로세스를 재구동 시키도록 구성하는 것만으로는 분산 시스템을 양호하게 유지하는데 불충분 하다. 시스템의 프로세스가 살아 있지만 응답이 없거나, 그렇지 않고 양호하지 않은 경우의 시나리오가 있다. 여러분은 K8S에게 애플리케이션이 양호하지 않으며 재구동 을 해야한다는 것을 알리기 위해 "liveness probe"를 이용해야만 한다.

 "zk" "StatefulSet" 에 대한 Pod "template"은 "liveness probe"를 명시한다.

1
2
3
4
5
6
7
8
livenessProbe:
          exec:
            command:
            - sh
            - -c
            - "zookeeper-ready 2181"
          initialDelaySeconds: 15
          timeoutSeconds: 5
cs


점검조사(probe)는 서버의 건강을 테스트하기 위해 ZooKeeper "ruok" 네 단어를 사용하는 bash 스크립트를 호출한다.

1
2
3
4
5
6
OK=$(echo ruok | nc 127.0.0.1 $1)
if [ "$OK" == "imok" ]; then
    exit 0
else
    exit 1
fi
cs


하나의 터미널 창에서, "zk" StatefulSet 내 Pod를 지켜보기 위해 다음 명령을 이용한다.

1
[root@docker-registry ~]# kubectl get pod -w -l app=zk
cs


또 다른 창에서, "zk-0" Pod의 파일시스템으로부터 "zkOk.sh" 스크립트를 삭제하기 위해 다음 명령을 이용한다.

1
2
3
[root@docker-registry ~]# kubectl exec zk-0 -- rm /usr/bin/zookeeper-ready
rm: cannot remove '/usr/bin/zookeeper-ready': Permission denied
command terminated with exit code 1
cs


When the liveness probe for the ZooKeeper 프로세스에 대한 "liveness prove" 가 실패날 때, 앙상블 내 양호하지 않은 프로세스가 재구동 되도록 보장하기 위해, K8S는 자동으로 여러분을 위해 프로세스를 재구동해 줄 것이다. 

1
2
3
4
5
[root@docker-registry ~]# kubectl get pod -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   1          5m
zk-1      1/1       Running   0          6m
zk-2      1/1       Running   0          7m
cs


Testing for Readiness

"Readiness"는 "liveness"와는 다르다. 하나의 프로세스가 살아있으면, 스케쥴 되어지고 건강한 상태가 된다. 하나의 프로세스가 준비가 되면, 프로세스 입력이 가능하다. "Liveness"는 "readiness" 에 대한 조건으로 필요하지만 불충분하다. 하나의 프로세스가 살아있지만 준비가 되지 않았을 때, 특히 초기화와 종료 도중인 경우가 있다.

여러분이 "readiness probe"를 명시했다면, K8S는 여러분의 애플리케이션 프로세스가 "readiness" 체크에 통과할 때까지 네트워크 트래픽을 수신받지 않도록 보장해 줄 것이다.

For a ZooKeeper 서버의 경우, "liveness"는 "readiness"를 암시한다. 그러므로, "zookeeper.yaml" manifest의 "readiness probe"는 "liveness probe"와 같다.

1
2
3
4
5
6
7
8
readinessProbe:
  exec:
    command:
    - sh
    - -c
    - "zookeeper-ready 2181"
  initialDelaySeconds: 15
  timeoutSeconds: 5
cs


비록 "liveness"와 "readiness probes"이 같다고 할지라도, 두 가지 모두 명시하는 것이 중요하다. 이는 ZooKeeper 앙상블 내 상태 양호한 서버들에게만 네트워크 트래픽이 수신되도록 보장해준다.


Tolerating Node Failure

ZooKeeper는 데이터에 변화를 성공적으로 커밋하기 위해서 서버의 정족수가 필요하다. 3개의 서버 앙상블의 경우, 2개의 서버는 기록에 성공하기 위해 양호 상태여야만 한다. 정족수 기반 시스템에서, 맴버들은 가용성을 보장하기 위해 실패 도메인을 거쳐서 배포되어진다. 개별 머신에 대한 유실로 기인하는 사용불능(outage) 상태를 피하기 위해, 모범사례는 동일 머신 상에 여러 개의 애플리케이션 인스턴스가 공존되는 것을 방지하는 것이다.

기본적으로, K8S는 동일 노드 상에서 "StatefulSet" 내 Pod들이 공존할 수 있다. 여러분이 생성한 3개 서버 앙상블의 경우, 2개의 서버가 동일 노드 상에 있는데, ZooKeeper 서비스의 클라이언트는 최소한 Pod들 중 하나가 재 스케쥴되어 질수 있을 때까지 사용불능을 경험하게 될 것이다.

여러분은 항상 노드 실패라는 이벤트 상황 속에서 중요한 시스템 프로세스들이 재 스케쥴 될 수 있도록 허용하기 위하여 추가적인 용량을 준비해둬야만 한다. 그렇게 한다면, 사용불능은 오직 K8S 스케쥴러가 ZooKeeper 서버 중 하나를 재 스케쥴하는 동안만 지속될 것이다. 그러나, 여러분의 서비스가 중단 없이 노드실패를 견뎌내길 원한다면, 여러분은 "podAntiAffinity"를 설정해야만 한다.

"zk" "StatefulSet" 내 Pod에 대한 노드를 확인하기 위해 아래 명령을 이용한다.

1
[root@docker-registry ~]# for i in 0 1 2; do kubectl get pod zk-$i --template {{.spec.nodeName}}; echo ""; done
cs


"zk" "StatefulSet" 내 Pod 내 모든 노드들은 상이한 노드들에 배포되어 있다.

1
2
3
4
[root@docker-registry ~]# for i in 0 1 2; do kubectl get pod zk-$i --template {{.spec.nodeName}}; echo ""; done
gke-zookeeper-ensemble-default-pool-71e68058-zf4t
gke-zookeeper-ensemble-default-pool-71e68058-45k6
gke-zookeeper-ensemble-default-pool-71e68058-t7rd
cs


이는 "zk" "StatefulSet" 내 Pod들이 "PodAntiAffinity" 로 명시되어져 있기 때문이다.

1
2
3
4
5
6
7
8
9
10
affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
            - key: "app"
              operator: In
              values:
              - zk
        topologyKey: "kubernetes.io/hostname"
cs


"requiredDuringSchedulingIgnoredDuringExecution" 필드는 K8S 스케쥴러에게 두 개의 Pod가 절대 공존하지 않아야 함을 일러준다. 이 Pod들은 "topologyKey"에 의해 정의된 도메인에서 "app"을 "zk"로 분류되었다. "topologyKey" "kubernetes.io/hostname"는 도메인이 개별 노드라는 것을 나탄내 준다. 다른 규칙, 레이블 그리고 셀렉터를 이용함으로써,여러분은 물리, 네트워크 그리고 정전 영역에 걸쳐 여러분의 앙상블을 넓혀서 이 기법을 확장시킬 수 있다.


Surviving Maintenance

이번 섹션에서 여러분은 노드를 차단(cordon)하고 유출(drain)할 것이다. 공용 클러스터 상에서 이 튜토리얼을 이용하고 있다면, 이로 인해 역으로 다른 테넌트가 영향받지 않을 것을 분명하게 확실히 한다.

이전 섹션에서 계획치 않은 노드 고장을 견뎌내기 위해 노드에 걸쳐 여러분의 Pod를 어떻게 확산하는지에 대해 보였다. 그러나 계획된 고장정비에 기인하여 발생한 일시적인 노드 고장에 대해 계획하는 것 또한 필요하다.

여러분의 클러스터 내에 노드들을 확인하기 위해 이 명령을 이용한다.

1
2
3
4
5
6
[root@docker-registry ~]# kubectl get nodes
NAME                                                STATUS    ROLES     AGE       VERSION
gke-zookeeper-ensemble-default-pool-71e68058-39tp   Ready     <none>    51m       v1.9.7-gke.6
gke-zookeeper-ensemble-default-pool-71e68058-45k6   Ready     <none>    51m       v1.9.7-gke.6
gke-zookeeper-ensemble-default-pool-71e68058-t7rd   Ready     <none>    51m       v1.9.7-gke.6
gke-zookeeper-ensemble-default-pool-71e68058-zf4t   Ready     <none>    51m       v1.9.7-gke.6
cs


클러스터 내 노드 5개를 제외하고 차단시키기 위해 "kubectl cordon"을 이용한다. 

(클러스터 내 추가 노드가 없으니 이 부분에서 할 것은 없다. 혹시 추가 노드가 있다면 차단한다.)

kubectl cordon <node-name>


"zk-pdb" "PodDisruptionBudget"를 확인하기 위해 이 명령을 사용한다.

1
[root@docker-registry ~]# kubectl get pdb zk-pdb
cs


"max-unavailable" 필드는 "zk" "StatefulSet"으로부터 최대 하나의 Pod가 언제라도 이용불가가 될 수 있다는 것을 K8S에게 지시해 준다. 

1
2
3
[root@docker-registry ~]# kubectl get pdb zk-pdb
NAME      MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
zk-pdb    N/A             1                 1                     44m
cs


하나의 터미널에서, "zk" "StatefulSet" 내 Pod를 지켜보기 위해 이 명령을 이용한다.

1
[root@docker-registry ~]# kubectl get pod -w -l app=zk
cs


또 다른 창에서, Pod가 현재 스케쥴 되어진 노드들의 정보를 확인하기 위해 이 명령을 이용한다.

1
2
3
4
[root@docker-registry ~]# for i in 0 1 2; do kubectl get pod zk-$i --template {{.spec.nodeName}}; echo ""; done
gke-zookeeper-ensemble-default-pool-71e68058-zf4t
gke-zookeeper-ensemble-default-pool-71e68058-45k6
gke-zookeeper-ensemble-default-pool-71e68058-t7rd
cs


"zk-0" Pod가 스케쥴 되어진 노드를 차단하고 유출하기 위해 "kubectl drain"을 이용한다.

1
2
3
4
5
6
[root@docker-registry ~]# kubectl drain $(kubectl get pod zk-0 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-local-data
node/gke-zookeeper-ensemble-default-pool-71e68058-zf4t cordoned
WARNING: Ignoring DaemonSet-managed pods: fluentd-gcp-v2.0.17-2xrjf
pod/kube-dns-autoscaler-69c5cbdcdd-rslqt evicted
pod/l7-default-backend-57856c5f55-slcwj evicted
pod/zk-0 evicted
cs


클러스터에 4개의 노드가 있으므로, "kubectl drain", 성공하고 "zk-0"이 다른 노드에 재 스케쥴 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@docker-registry ~]# kubectl get pod -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   1          12m
zk-1      1/1       Running   0          13m
zk-2      1/1       Running   0          14m
zk-0      1/1       Terminating   1         13m
zk-0      0/1       Terminating   1         13m
zk-0      0/1       Terminating   1         14m
zk-0      0/1       Terminating   1         14m
zk-0      0/1       Pending   0         0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         44s
zk-0      1/1       Running   0         53s
cs


첫 번째 창에서 계속 " StatefulSet"의 Pods를 지켜보며 "zk-1"이 스케쥴된 노드를 유출한다.

1
2
3
4
5
6
[root@docker-registry ~]# kubectl drain $(kubectl get pod zk-1 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-local-data
node/gke-zookeeper-ensemble-default-pool-71e68058-45k6 cordoned
WARNING: Ignoring DaemonSet-managed pods: fluentd-gcp-v2.0.17-pt4cv; Deleting pods with local storage: kubernetes-dashboard-64bbdbcbfc-t7lh9
pod/kubernetes-dashboard-64bbdbcbfc-t7lh9 evicted
pod/heapster-v1.5.2-69c97d4877-pbnt9 evicted
pod/zk-1 evicted
cs


"zk" "StatefulSet"이 Pod들의 공존을 방지하는 "PodAntiAffinity" 규칙을 포함하고 있으므로 "zk-1" Pod는  스케쥴 될 수 없다. 그리고, 오직 두개의 노드들만 스케쥴이 가능하기 때문에 Pod는 Pending 상태로 남게 될 것이다.

1
2
3
4
5
6
7
8
9
10
11
[root@docker-registry ~]# kubectl get pod -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   0          2m
zk-1      1/1       Running   0          17m
zk-2      1/1       Running   0          19m
zk-1      1/1       Terminating   0         20m
zk-1      0/1       Terminating   0         21m
zk-1      0/1       Terminating   0         21m
zk-1      0/1       Terminating   0         21m
zk-1      0/1       Pending   0         1s
zk-1      0/1       Pending   0         1s
cs


stateful 세트의 Pod를 계속 지켜보고, "zk-2"가 스케쥴 된 노드를 유출한다. 

1
2
3
4
5
6
7
8
[root@docker-registry ~]# kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-local-data
node/gke-zookeeper-ensemble-default-pool-71e68058-t7rd cordoned
WARNING: Ignoring DaemonSet-managed pods: fluentd-gcp-v2.0.17-2svsf; Deleting pods with local storage: kubernetes-dashboard-64bbdbcbfc-lrgxt
pod/metrics-server-v0.2.1-7f8dd98c8f-j4ctr evicted
pod/kubernetes-dashboard-64bbdbcbfc-lrgxt evicted
pod/heapster-v1.5.2-69c97d4877-nflgl evicted
^C
[root@docker-registry ~]#
cs


kubectl을 종료하기 위해 CTRL-C를 이용한다.

"zk-2"를 축출하면 "zk-budget"을 방해할 것이기 때문에 세번 째 노드를 유출할 수 없다. 그러나, 그 노드는 차단 상태로 남게 될 것이다.

"zk-0"으로부터 적합성 테스트 도중에 여러분이 입력했던 값을 불러 오기 위해 "zkCli.sh"을 이용한다.

1
[root@docker-registry ~]# kubectl exec zk-0 zkCli.sh get /hello
cs


"PodDisruptionBudget" 이 지켜졌기 때문에 여전히 서비스가 이용가능하다.

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
[root@docker-registry ~]# kubectl exec zk-0 zkCli.sh get /hello
Connecting to localhost:2181
2018-09-06 00:34:45,643 [myid:] - INFO  [main:Environment@100- Client environment:zookeeper.version=3.4.10-39d3a4f269333c922ed3db283be479f9deacaa0f, built on 03/23/2017 10:13 GMT
2018-09-06 00:34:45,647 [myid:] - INFO  [main:Environment@100- Client environment:host.name=zk-0.zk-hs.default.svc.cluster.local
2018-09-06 00:34:45,647 [myid:] - INFO  [main:Environment@100- Client environment:java.version=1.8.0_131
2018-09-06 00:34:45,649 [myid:] - INFO  [main:Environment@100- Client environment:java.vendor=Oracle Corporation
2018-09-06 00:34:45,649 [myid:] - INFO  [main:Environment@100- Client environment:java.home=/usr/lib/jvm/java-8-openjdk-amd64/jre
2018-09-06 00:34:45,649 [myid:] - INFO  [main:Environment@100- Client environment:java.class.path=/usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.10.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper:
2018-09-06 00:34:45,649 [myid:] - INFO  [main:Environment@100- Client environment:java.library.path=/usr/java/packages/lib/amd64:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib
2018-09-06 00:34:45,649 [myid:] - INFO  [main:Environment@100- Client environment:java.io.tmpdir=/tmp
2018-09-06 00:34:45,650 [myid:] - INFO  [main:Environment@100- Client environment:java.compiler=<NA>
2018-09-06 00:34:45,650 [myid:] - INFO  [main:Environment@100- Client environment:os.name=Linux
2018-09-06 00:34:45,650 [myid:] - INFO  [main:Environment@100- Client environment:os.arch=amd64
2018-09-06 00:34:45,650 [myid:] - INFO  [main:Environment@100- Client environment:os.version=4.4.111+
2018-09-06 00:34:45,650 [myid:] - INFO  [main:Environment@100- Client environment:user.name=zookeeper
2018-09-06 00:34:45,650 [myid:] - INFO  [main:Environment@100- Client environment:user.home=/home/zookeeper
2018-09-06 00:34:45,650 [myid:] - INFO  [main:Environment@100- Client environment:user.dir=/
2018-09-06 00:34:45,651 [myid:] - INFO  [main:ZooKeeper@438- Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@22d8cfe0
2018-09-06 00:34:45,670 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1032- Opening socket connection to server localhost/0:0:0:0:0:0:0:1:2181. Will not attempt to authenticate using SASL (unknown error)
2018-09-06 00:34:45,745 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@876- Socket connection established to localhost/0:0:0:0:0:0:0:1:2181, initiating session
2018-09-06 00:34:45,816 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1299- Session establishment complete on server localhost/0:0:0:0:0:0:0:1:2181, sessionid = 0x165ac44fa8a0000, negotiated timeout = 30000
 
WATCHER::
 
WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x200000002
ctime = Wed Sep 05 23:46:21 UTC 2018
mZxid = 0x200000002
mtime = Wed Sep 05 23:46:21 UTC 2018
pZxid = 0x200000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
cs


첫 번째 노드를 차단해제하기 위해 "kubectl uncordon"를 이용한다.

1
2
[root@docker-registry ~]# kubectl uncordon gke-zookeeper-ensemble-default-pool-71e68058-zf4t
node/gke-zookeeper-ensemble-default-pool-71e68058-zf4t uncordoned
cs


"zk-1"가 이 노드에서 재 스케쥴된다. "zk-1"이 동작 준비에 이를 때가지 기다린다.

1
2
3
4
5
6
7
8
9
[root@docker-registry ~]# kubectl get pod -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   0          12m
zk-1      0/1       Pending   0          5m
zk-2      1/1       Running   0          28m
zk-1      0/1       Pending   0         6m
zk-1      0/1       ContainerCreating   0         6m
zk-1      0/1       Running   0         7m
zk-1      1/1       Running   0         7m
cs


"zk-2"가 스케쥴 된 노드를 차단하기 위해 시도한다.

1
[root@docker-registry ~]# kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-local-data
cs


출력:

1
2
3
4
[root@docker-registry ~]# kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-local-data
node/gke-zookeeper-ensemble-default-pool-71e68058-t7rd already cordoned
WARNING: Ignoring DaemonSet-managed pods: fluentd-gcp-v2.0.17-2svsf
pod/zk-2 evicted
cs


이번에는 "kubectl drain" 이 성공한다.

"zk-2"가 다시 스케쥴 되어지도록 두번 째 노드를 차단해제한다.


여러분의 서비스가 고장정비 동안 이용가능한 상태로 보장하기 위해 "PodDisruptionBudgets"와 함께 연결하여 "kubectl drain"을 이용할 수 있다. 고장정비를 위해 노드를 오프라인으로 처리하기에 앞서 노드를  차단하고 Pod를 축출하는데 이용되면, "disruption budget(서비스 중단에 대한 예산)" 으로 표시한 서비스는 그 budget을 침범하지 않을 것이다. 여러분은 언제나 Pod가 즉시 재 스케쥴 될 수 있도록 중요 서비스들에 대해서 추가적인 용량을 할당해줘야만 한다.

1
2
[root@docker-registry ~]# kubectl uncordon gke-zookeeper-ensemble-default-pool-71e68058-45k6
node/gke-zookeeper-ensemble-default-pool-71e68058-45k6 uncordoned
cs





Cleaning up


  • 클러스터의 모든 노드들을 차단해제하기 위해 "kubectl uncordon"을 사용한다.
  • 이 튜토리얼에서 이용했던 PersistentVolumes에 대한 영구 스토리지 매체를 삭제해야 할 것이다. 여러분의 환경, 스토리지 구성, 그리고 프로비저닝 방식을 기준으로, 모든 스토리지가 반환되었음을 보장하기 위한 필요한 조치를 취한다. 


댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/11   »
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
글 보관함