Kubernetesを使ってCassandra環境を構築

Kubernetesを理解するために、まずは動かし感覚を掴む。どんな仕組みかは後でドキュメントを読でまとめる。

環境：VirtualBox
     CoreOS Alpha 845.0.0
     Kernel version:4.2.2
     docker version:1.8.3

CoreOSについては後日勉強。知らないことが今の時点では多いので。

# 1. kubernetes環境の確認
#### 1.1. etcdが正常に動作しているか確認する。
クラスタの状態をetcdctlコマンドを使用して確認する。

```
$ etcdctl cluster-health
member ce2a822cea30bfca is healthy: got healthy result from http://localhost:2379
cluster is healthy
```
※etcdはKVSで、CoreOSを使って構成するクラスタの全ノードがアクセス可能なデータを格納するために使用される。

topレベルのKeyを確認

```
$ etcdctl ls
/registry
/coreos.com
```

valueを得るにはgetでエンドポイントを指定。ディレクトリの場合は以下のようにディレクトリと返ってくる。

```
$ etcdctl get /coreos.com/              
/coreos.com: is a directory
```

以下のようにすれば全ての階層構造情報を出力できる。

```
etcdctl ls / --recursive
```
※zookeeperと似ている。

実際のデータがどのように見えるか、endpointを指定してgetする。

```
$ etcdctl get /coreos.com/network/config
{"Network":"10.2.0.0/16","Backend":{"Type":"vxlan"}}
```
#### 1.2. kubernetesのserviceが正常に動作しているか確認する。

```
$ systemctl status kubelet.service
● kubelet.service
   Loaded: loaded (/etc/systemd/system/kubelet.service; enabled; vendor preset: disabled)
   Active: active (running) since Thu 2015-10-29 05:52:17 UTC; 5h 21min ago
 Main PID: 601 (kubelet)
   Memory: 4.8M
      CPU: 592ms
   CGroup: /system.slice/kubelet.service
           ├─601 /usr/bin/kubelet --api_servers=http://127.0.0.1:8080 --register-node=true -...
           └─966 journalctl -f
・・・
```

```
$ docker ps |grep kube-api
c5a23f6f1e0b        gcr.io/google_containers/hyperkube:v1.0.6        "/hyperkube apiserver"   5 hours ago         Up 5 hours                              k8s_kube-apiserver.ae2b1f58_kube-apserver-172.17.4.99_kube-system_b5fd6f0b2e57f08b39d588afd25c9206_c57e302b
864e167abf14        gcr.io/google_containers/pause:0.8.0             "/pause"                 5 hours ago         Up 5 hours                              k8s_POD.e4cc795_kube-apiserver-172.17.4.99_kube-system_b5fd6f0b2e57f08b39d588afd25c9206_c1bedd4a
```

#### 1.3. kubernetesのstatusを確認する。
masterのアドレスやkubernetes.io/cluster-service=trueのラベルを持ったサービスの情報を表示する。

```
$ kubectl cluster-info
Kubernetes master is running at https://172.17.4.99:443
KubeDNS is running at https://172.17.4.99:443/api/v1/proxy/namespaces/kube-system/services/kube-dns
```

リソース情報（pods (po), replication controllers (rc), services (svc), nodes, events (ev), component statuses (cs), limit ranges (limits), nodes (no), persistent volumes (pv), persistent volume claims (pvc) or resource quotas）を表示する。

```
$ kubectl get nodes
NAME          LABELS                               STATUS
172.17.4.99   kubernetes.io/hostname=172.17.4.99   Ready

$ kubectl get pods        
NAME      READY     STATUS    RESTARTS   AGE
```

kubeconfigの設定を表示
```
$ kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority: /home/core/ssl/ca.pem
    server: https://172.17.4.99:443
  name: vagrant
contexts:
- context:
    cluster: vagrant
    user: vagrant-admin
  name: vagrant
current-context: vagrant
kind: Config
preferences: {}
users:
- name: vagrant-admin
  user:
    client-certificate: /home/core/ssl/admin.pem
    client-key: /home/core/ssl/admin-key.pem
```

# 2. Kubernetesを使って、cassandra環境の構築
http://kubernetes.io/v1.0/docs/user-guide/configuring-containers.html
http://kubernetes.io/v1.0/examples/cassandra/README.html

#### 2.1. Podの作成
KubernetesのAPI Resource Schemaを使用して設定ファイルを作成
Kubernetesではアプリケーションの最小単位はPodなので、Podの定義をまず行う。Podは同じホスト上にスケジューリングされる。Pod内の全てのコンテナはnetwork namespaceを共有し、volumeもシェア可能（オプション）。KubernetesはPod内でコンテナを生成する。

```cassandra.yaml
apiVersion: v1 #現在のバージョンはv1
kind: Pod # Podについて定義することを明示
metadata:
  labels:
    name: cassandra # label名
  name: cassandra #生成されるPodの名前。クラスタ内でuniqueでなければならない。Pod内のコンテナ名はcontainers[0].name
spec:
  containers:
  - args:
    - /run.sh
    resources:
      limits:
        cpu: "0.1" # cluster managerに0.1cpuを要求
    image: gcr.io/google_containers/cassandra:v5 #Docker Image名。イメージはデフォルトではDocker Hubから取得
    name: cassandra
    ports: #外部に公開する方法とポートを定義
    - name: cql #Cassandra Query Language
      containerPort: 9042
    - name: thrift
      containerPort: 9160
    volumeMounts:
    - name: data
      mountPath: /cassandra_data
    env: #Cassandraのパラメータ
    - name: MAX_HEAP_SIZE
      value: 512M
    - name: HEAP_NEWSIZE
      value: 100M
    - name: POD_NAMESPACE
      valueFrom:
        fieldRef:
          fieldPath: metadata.namespace
  volumes:
    - name: data
      emptyDir: {}
```

作った設定ファイルを指定してリソース（Pod)を作成する。

```
$ kubectl create -f cassandra.yaml 
pods/cassandra
```
Podが作成されたかどうか確認

```
$ kubectl get pods    
NAME        READY     STATUS    RESTARTS   AGE
cassandra   0/1       Running   0          8s
```

Resourceの詳細について確認。以下はPodだがpods (po)以外に, replicationcontrollers (rc), services (svc), nodes (no), events (ev), componentstatuses (cs), limitRanges (limits), persistentVolumes (pv), persistentVolumeClaims (pvc), resourceQuotas (quota) or secretsが指定可能。

```
$ kubectl describe pod cassandra
Name:				cassandra
Namespace:			default
Image(s):			gcr.io/google_containers/cassandra:v5
Node:				172.17.4.99/172.17.4.99
Labels:				name=cassandra
Status:				Running
Reason:				
Message:			
IP:				10.2.67.6
Replication Controllers:	<none>
Containers:
  cassandra:
    Image:	gcr.io/google_containers/cassandra:v5
    Limits:
      cpu:		100m
    State:		Running
      Started:		Fri, 30 Oct 2015 01:13:29 +0000
    Ready:		True
    Restart Count:	0
Conditions:
  Type		Status
  Ready 	True 
Events:
  FirstSeen				LastSeen			Count	From			SubobjectPath				Reason		Message
  Fri, 30 Oct 2015 01:13:29 +0000	Fri, 30 Oct 2015 01:13:29 +0000	1	{scheduler }							scheduled	Successfully assigned cassandra to 172.17.4.99
  Fri, 30 Oct 2015 01:13:29 +0000	Fri, 30 Oct 2015 01:13:29 +0000	1	{kubelet 172.17.4.99}	implicitly required container POD	pulled		Pod container image "gcr.io/google_containers/pause:0.8.0" already present on machine
  Fri, 30 Oct 2015 01:13:29 +0000	Fri, 30 Oct 2015 01:13:29 +0000	1	{kubelet 172.17.4.99}	implicitly required container POD	created		Created with docker id 2843e181382d
  Fri, 30 Oct 2015 01:13:29 +0000	Fri, 30 Oct 2015 01:13:29 +0000	1	{kubelet 172.17.4.99}	implicitly required container POD	started		Started with docker id 2843e181382d
  Fri, 30 Oct 2015 01:13:29 +0000	Fri, 30 Oct 2015 01:13:29 +0000	1	{kubelet 172.17.4.99}	spec.containers{cassandra}		created		Created with docker id d77752ffb587
  Fri, 30 Oct 2015 01:13:29 +0000	Fri, 30 Oct 2015 01:13:29 +0000	1	{kubelet 172.17.4.99}	spec.containers{cassandra}		started		Started with docker id d77752ffb587
```

#### 2.2. Serviceの作成
KubernetesではServiceは同じタスクを処理するPodの集まりとして定義される。

```cassandra-service.yaml
apiVersion: v1  #現在のバージョンはv1
kind: Service  #Serviceについて定義
metadata:
  labels:
    name: cassandra
  name: cassandra
spec:
  ports:
    - port: 9042
  selector: #query over labels(Podsのセット）. このサービスに所属するPodを選択。
    name: cassandra #この例ではcassandoraというlabelが付いたPodがこのserviceに所属
```
Serviceを作成。

```
$ kubectl create -f cassandra-service.yaml 
services/cassandra
```
Serviceが作成されているか確認

```
$ kubectl get services
NAME         LABELS                                    SELECTOR         IP(S)       PORT(S)
cassandra    name=cassandra                            name=cassandra   10.3.0.80   9042/TCP
kubernetes   component=apiserver,provider=kubernetes   <none>           10.3.0.1    443/TCP
```

```
$ kubectl describe service cassandra
Name:			cassandra
Namespace:		default
Labels:			name=cassandra
Selector:		name=cassandra
Type:			ClusterIP
IP:			10.3.0.80
Port:			<unnamed>	9042/TCP
Endpoints:		10.2.67.6:9042
Session Affinity:	None
No events.
```

Pod内のContainerのログを出力

```
$ kubectl logs cassandra
```

#### 2.3. Replication Controllerの作成
Kubernetesにより、Cassandraクラスタを容易に構築、スケールすることができる。
Replication Controllerは同質のPodのセットをレプリケートする。指定するPodの数になるようPodをadd,removeする。

```cassandra-controller.yaml
apiVersion: v1 #現在のバージョンはv1
kind: ReplicationController #Replication Controllerを定義
metadata:
  labels:
    name: cassandra
  name: cassandra
spec:
  replicas: 1 #replicaの数
  selector: #controllerのselector query
    name: cassandra
  template:
    metadata:
      labels:
        name: cassandra
    spec:
      containers:
        - command:
            - /run.sh
          resources:
            limits:
              cpu: 0.1
          env:
            - name: MAX_HEAP_SIZE
              value: 512M
            - name: HEAP_NEWSIZE
              value: 100M
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          image: gcr.io/google_containers/cassandra:v5
          name: cassandra
          ports:
            - containerPort: 9042
              name: cql
            - containerPort: 9160
              name: thrift
          volumeMounts:
            - mountPath: /cassandra_data
              name: data
      volumes:
        - name: data
          emptyDir: {}
```

Controllerを作成する

```
$ kubectl create -f cassandra-controller.yaml 
replicationcontrollers/cassandra
```
Controllerが作成されているか確認する

```
$ kubectl get rc 
CONTROLLER   CONTAINER(S)   IMAGE(S)                                SELECTOR         REPLICAS
cassandra    cassandra      gcr.io/google_containers/cassandra:v5   name=cassandra   1
```

```
$ kubectl describe rc cassandra
Name:		cassandra
Namespace:	default
Image(s):	gcr.io/google_containers/cassandra:v5
Selector:	name=cassandra
Labels:		name=cassandra
Replicas:	1 current / 1 desired
Pods Status:	1 Running / 0 Waiting / 0 Succeeded / 0 Failed
No events.
```

#### 2.4. クラスタをスケールアウト、スケールイン
pod数を2へスケールアウトする。

```
$ kubectl scale rc cassandra --replicas=2
scaled
```
クラスタ内のpod一覧を出力し、label name=cassandraでフィルタリングする。2つのcassandra podを確認することができる。 

```
$ kubectl get pods -l="name=cassandra"
NAME              READY     STATUS    RESTARTS   AGE
cassandra         1/1       Running   0          10h
cassandra-vb3mp   1/1       Running   0          1m
```

片方はreplication controllerによって生成されたランダムな文字列が付加されている。
nodetoolを使用して、正しく動作しているかクラスタの状態を調べる。kubectl execを使用し、コンテナの中でコマンドを実行。(kubectl exec POD -c CONTAINER -- COMMAND [args...])
-tでstdinをtty、-iでコンテナに対してstdinをpassする。

```
core@localhost ~ $ kubectl exec -ti cassandra -- nodetool status
Datacenter: datacenter1
=======================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
--  Address    Load       Tokens  Owns (effective)  Host ID                               Rack
DN  10.2.67.7  ?          256     100.0%            186ce662-cc93-4e6c-90f2-1c58ffd5834a  rack1
UN  10.2.64.3  199.82 KB  256     100.0%            b5a7fb71-58f3-4870-8cf4-c8218feb46e6  rack1
```

cqlshを実行。このと指定するのはServiceのIPアドレスもしくはサービス名。

```
$ kubectl get service cassandra
NAME        LABELS           SELECTOR         IP(S)       PORT(S)
cassandra   name=cassandra   name=cassandra   10.3.0.80   9042/TCP

$ kubectl exec -ti cassandra -- cqlsh 10.3.0.80    
Connected to Test Cluster at 10.3.0.80:9042.
[cqlsh 5.0.1 | Cassandra 2.1.7 | CQL spec 3.2.0 | Native protocol v3]
Use HELP for help.
cqlsh> 

$ kubectl exec -ti cassandra -- cqlsh cassandra.default.cluster.local
```

環境の構築、スケールアウトなどは非常に速く、しかも容易であることを実感。次からはドキュメントベースでどういう仕組みなのかを調べてみる。。