Kubernetesを試す方法はKubernetesリポジトリのGetting Started GuideにGCEやAWSなどのCloudを使った方法やVagrantを使った方法があります。
今回は手元で試したかったので、いくつか試したなかで一番構築が簡単でメンテナンスされているMulti-node cluster using cloud-config and Vagrantを使います。
前提としてVirtualBoxとVagrantがインストールされてある必要があります。
クラスタの構築
masterとnodeを2台構築します
$ git clone git://github.com/pires/kubernetes-vagrant-coreos-cluster.git
$ cd kubernetes-vagrant-coreos-cluster
$ vagrant up master node-01 node-02
必須ではないですが、今後の操作が簡単になるのでvagrant ssh-config
を設定しておくと良いでしょう。
$ vagrant ssh-config >> ~/.ssh/config
kubernetesを操作するためのCLIとしてkubectl
をインストールします。ちなみに以前存在していたkubecfg
はもう使われていません。
$ ./kubLocalSetup install
$ /usr/local/bin/kubectl -h
kubectlで操作するmasterの向き先を設定します。etcdとfleetの設定もしてくれます。
$ $(./kubLocalSetup shellinit)
$ env | egrep '(KUBE|FLEET|ETCD)'
ETCDCTL_PEERS=http://172.17.8.101:4001
FLEETCTL_ENDPOINT=http://172.17.8.101:4001
KUBERNETES_MASTER=http://172.17.8.101:8080
kubectl version
でクライアントとmaster(apiserver)のバージョンを確認。get nodes
でnode一覧が確認できれば完了。
$ kubectl version
Client Version: version.Info{Major:"0", Minor:"11+", GitVersion:"v0.11.0-dirty", GitCommit:"18ddff0eb6bc069b41832ce47d19b5b89cfb26e3", GitTreeState:"dirty"}
Server Version: version.Info{Major:"0", Minor:"11+", GitVersion:"v0.11.0-dirty", GitCommit:"18ddff0eb6bc069b41832ce47d19b5b89cfb26e3", GitTreeState:"dirty"}
$ kubectl get nodes
NAME LABELS STATUS
172.17.8.102 <none> Ready
172.17.8.103 <none> Ready
サンプルを試す
kubernetesリポジトリにあるサンプルを試します。一番標準的なguestbookを試しましょう。
基本的にはREADMEに沿ってコマンドを実行していくだけです。READMEに書いてあるコマンドでcluster/kubectl.sh
と書いてあるのはkubectl
と読み替えて下さい。cluster/kubectl.sh
は構築した環境ごとに向き先を切り替えてくれるkubectl
のラッパースクリプトです。
前準備
v0.11.0のタグで試さないとうまく動作しません。
この例ではHEADで試していたので一部Podの割り当てがおかしいです
$ git clone git://github.com/GoogleCloudPlatform/kubernetes.git
$ cd kubernetes
$ git checkout refs/tags/v0.11.0
step 1: Redis MasterのRCを作成
まずはredis masterを構築。replicaが1のReplicationControllerを作成します。
$ kubectl create -f examples/guestbook/redis-master-controller.json
$ kubectl get rc
CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS
redis-master-controller redis-master dockerfile/redis name=redis-master 1
同時にPodも作成されます。STATUSがRunning
になるまで待ちましょう。Peding
中はdocker pull中です。
docker ps
すると3つ動作していますが、それぞれredis、infra container, cadvisorです。
infra containerはPod毎に必ず1つ作られます。Pod内のコンテナはこれを通してネットワークがつながっています。cadvisorはkubeletと一緒に起動する(ようになった)もので、コンテナのmetrics情報を収集しています。
$ kubectl get pods
POD IP CONTAINER(S) IMAGE(S) HOST LABELS STATUS
redis-master-controller-fplln 10.244.45.3 redis-master dockerfile/redis 172.17.8.102/172.17.8.102 app=redis,name=redis-master Pending
Runningになったらdockerが動いているか確認しましょう。今回はIPが102なのでnode-01
になります。
$ ssh node-01 'docker images'
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
google/cadvisor 0.10.1 6a46ed29e869 13 hours ago 18.03 MB
google/cadvisor latest 6a46ed29e869 13 hours ago 18.03 MB
dockerfile/redis latest 8d898e6eef25 7 days ago 418.9 MB
kubernetes/pause go 6c4579af347b 7 months ago 239.8 kB
kubernetes/pause latest 6c4579af347b 7 months ago 239.8 kB
$ ssh node-01 'docker ps'
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7f74b7245c65 dockerfile/redis:latest "redis-server /etc/r 2 minutes ago Up 2 minutes k8s_redis-master.952cf9b6_redis-master-controller-fplln.default.api_f8f28d8a-bf17-11e4-b8b6-08002787f925_264d797a
5b4878091382 kubernetes/pause:go "/pause" 6 minutes ago Up 6 minutes k8s_POD.5efe987_redis-master-controller-fplln.default.api_f8f28d8a-bf17-11e4-b8b6-08002787f925_86f143e5
ffef368b3ed1 google/cadvisor:0.10.1 "/usr/bin/cadvisor - 24 minutes ago Up 24 minutes 8080/tcp, 0.0.0.0:4194->4194/tcp cadvisor
step 2: Redis MasterのServiceを作成
redis masterのServiceを作ります。Serviceを作るとServiceの配下にあるPodに対してのエンドポイントが提供されます。どのようなエンドポイントが提供されるかは設定によって変わります。
デフォルトではPod内の環境変数としてアクセス先のPodのIPが渡されるようになります(次のstepで確認)。
$ kubectl create -f examples/guestbook/redis-master-service.json
$ kubectl get se
NAME LABELS SELECTOR IP PORT
kubernetes component=apiserver,provider=kubernetes <none> 10.100.0.2 443
kubernetes-ro component=apiserver,provider=kubernetes <none> 10.100.0.1 80
redis-master name=redis-master name=redis-master 10.100.194.126 6379
ここで表示されるIPはPodのIPとなるので、ルーティングの設定をしていない限りKubernetesクラスタの外からはアクセスできません。クラスタ内ではflannelによってルーティングが設定されているので別のノードからPodのIPを指定してもアクセスできます。
step 3: Redis Slave RCを作成
redis-masterに(Serviceのエンドポイント経由で)接続するredis-slaveをreplica数2で作成します。
$ kubectl create -f examples/guestbook/redis-slave-controller.json
$ kubectl get rc
CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS
redis-master-controller redis-master dockerfile/redis name=redis-master 1
redis-slave-controller redis-slave brendanburns/redis-slave name=redis-slave 2
Podを確認すると2つのPodができています。
$ kubectl get pods
POD IP CONTAINER(S) IMAGE(S) HOST LABELS STATUS
redis-master-controller-fplln 10.244.45.3 redis-master dockerfile/redis 172.17.8.102/172.17.8.102 app=redis,name=redis-master Running
redis-slave-controller-gziey 10.244.59.3 redis-slave brendanburns/redis-slave 172.17.8.103/172.17.8.103 app=redis,name=redis-slave,uses=redis-master Pending
redis-slave-controller-oh43e 10.244.59.4 redis-slave brendanburns/redis-slave 172.17.8.103/172.17.8.103 app=redis,name=redis-slave,uses=redis-master Pending
Runningになったらnode-02に割り当てられたredis-slaveを’確認します。なぜか2台ともnode-02に割り当てられていますが。
$ ssh node-02 'docker ps' | grep redis-slave
5f2ccf61a68c brendanburns/redis-slave:latest "/bin/sh -c /run.sh" About a minute ago Up About a minute k8s_redis-slave.725fc7a_redis-slave-controller-oh43e.default.api_780ac5b4-bf1d-11e4-b8b6-08002787f925_afc7fab2
c9c404e5775c brendanburns/redis-slave:latest "/bin/sh -c /run.sh" About a minute ago Up About a minute k8s_redis-slave.725fc7a_redis-slave-controller-gziey.default.api_7807bcb8-bf1d-11e4-b8b6-08002787f925_dfb33229
5ba9a62ab028 kubernetes/pause:go "/pause" 5 minutes ago Up 5 minutes k8s_POD.5efe987_redis-slave-controller-oh43e.default.api_780ac5b4-bf1d-11e4-b8b6-08002787f925_e3841b8f
d4fc52a77b99 kubernetes/pause:go "/pause" 5 minutes ago Up 5 minutes k8s_POD.5efe987_redis-slave-controller-gziey.default.api_7807bcb8-bf1d-11e4-b8b6-08002787f925_d1a853f7
docker inspect
でコンテナ内の環境変数を確認するとREDIS_MASTER_*
という名前で先ほど設定したredis-masterのServiceのエンドポイントが提供されています。このようにServiceを作るとPodの中に環境変数として渡されます。
$ ssh node-02 'docker inspect 5f2ccf61a68c' | jq '.[].Config.Env'
[
"KUBERNETES_PORT_443_TCP=tcp://10.100.0.2:443",
"KUBERNETES_PORT_443_TCP_PORT=443",
"KUBERNETES_PORT_443_TCP_ADDR=10.100.0.2",
"KUBERNETES_RO_PORT_80_TCP_PROTO=tcp",
"REDIS_MASTER_SERVICE_PORT=6379",
"REDIS_MASTER_PORT=tcp://10.100.194.126:6379",
"REDIS_MASTER_PORT_6379_TCP=tcp://10.100.194.126:6379",
"REDIS_MASTER_PORT_6379_TCP_PORT=6379",
"REDIS_MASTER_PORT_6379_TCP_ADDR=10.100.194.126",
"KUBERNETES_SERVICE_HOST=10.100.0.2",
"KUBERNETES_RO_SERVICE_HOST=10.100.0.1",
"KUBERNETES_RO_PORT_80_TCP_PORT=80",
"KUBERNETES_RO_PORT_80_TCP_ADDR=10.100.0.1",
"KUBERNETES_SERVICE_PORT=443",
"KUBERNETES_PORT=tcp://10.100.0.2:443",
"KUBERNETES_RO_SERVICE_PORT=80",
"KUBERNETES_RO_PORT=tcp://10.100.0.1:80",
"KUBERNETES_PORT_443_TCP_PROTO=tcp",
"KUBERNETES_RO_PORT_80_TCP=tcp://10.100.0.1:80",
"REDIS_MASTER_SERVICE_HOST=10.100.194.126",
"REDIS_MASTER_PORT_6379_TCP_PROTO=tcp",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"HOME=/root"
]
step 4: Redis SlaveのServiceを作成
redis slaveも同様Serviceを作ります。
$ kubectl create -f examples/guestbook/redis-slave-service.json
$ kubectl get services
NAME LABELS SELECTOR IP PORT
kubernetes component=apiserver,provider=kubernetes <none> 10.100.0.2 443
kubernetes-ro component=apiserver,provider=kubernetes <none> 10.100.0.1 80
redis-master name=redis-master name=redis-master 10.100.194.126 6379
redisslave name=redis-slave name=redis-slave 10.100.159.170 6379
step 5: フロントエンドのRCを作成
$ kubectl create -f examples/guestbook/frontend-controller.json
frontend-controller
$ kubectl get rc
CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS
frontend-controller php-redis kubernetes/example-guestbook-php-redis name=frontend 3
redis-master-controller redis-master dockerfile/redis name=redis-master 1
redis-slave-controller redis-slave brendanburns/redis-slave name=redis-slave 2
$ kubectl get po --selector='app=frontend'
POD IP CONTAINER(S) IMAGE(S) HOST LABELS STATUS
frontend-controller-0133o 10.244.45.4 php-redis kubernetes/example-guestbook-php-redis 172.17.8.102/172.17.8.102 app=frontend,name=frontend,uses=redis-slave,redis-master Pending
frontend-controller-hh2gd 10.244.45.5 php-redis kubernetes/example-guestbook-php-redis 172.17.8.102/172.17.8.102 app=frontend,name=frontend,uses=redis-slave,redis-master Pending
frontend-controller-ls6k1 10.244.59.5 php-redis kubernetes/example-guestbook-php-redis 172.17.8.103/172.17.8.103 app=frontend,name=frontend,uses=redis-slave,redis-master Pending
フロントエンドのPodにはredis-masterとredis-slaveの両方のエンドポイントが設定されています。
$ ssh node-01 'docker inspect 0160f3a9ae28' | jq '.[].Config.Env'
[
"KUBERNETES_PORT_443_TCP_PROTO=tcp",
"REDIS_MASTER_SERVICE_HOST=10.100.194.126",
"REDIS_MASTER_PORT_6379_TCP_PROTO=tcp",
"KUBERNETES_PORT_443_TCP=tcp://10.100.0.2:443",
"KUBERNETES_PORT_443_TCP_PORT=443",
"REDIS_MASTER_SERVICE_PORT=6379",
"REDIS_MASTER_PORT=tcp://10.100.194.126:6379",
"REDIS_MASTER_PORT_6379_TCP=tcp://10.100.194.126:6379",
"REDIS_MASTER_PORT_6379_TCP_PORT=6379",
"REDISSLAVE_PORT_6379_TCP_ADDR=10.100.159.170",
"KUBERNETES_RO_PORT_80_TCP_PORT=80",
"KUBERNETES_PORT=tcp://10.100.0.2:443",
"KUBERNETES_RO_PORT=tcp://10.100.0.1:80",
"KUBERNETES_RO_PORT_80_TCP=tcp://10.100.0.1:80",
"REDISSLAVE_SERVICE_HOST=10.100.159.170",
"REDISSLAVE_PORT_6379_TCP_PROTO=tcp",
"KUBERNETES_PORT_443_TCP_ADDR=10.100.0.2",
"KUBERNETES_RO_PORT_80_TCP_PROTO=tcp",
"REDIS_MASTER_PORT_6379_TCP_ADDR=10.100.194.126",
"REDISSLAVE_SERVICE_PORT=6379",
"REDISSLAVE_PORT=tcp://10.100.159.170:6379",
"REDISSLAVE_PORT_6379_TCP=tcp://10.100.159.170:6379",
"REDISSLAVE_PORT_6379_TCP_PORT=6379",
"KUBERNETES_SERVICE_HOST=10.100.0.2",
"KUBERNETES_RO_SERVICE_HOST=10.100.0.1",
"KUBERNETES_RO_PORT_80_TCP_ADDR=10.100.0.1",
"KUBERNETES_SERVICE_PORT=443",
"KUBERNETES_RO_SERVICE_PORT=80",
"HOME=/",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
]
step 6: フロントエンドのServiceを作成
このステップは今回の環境では失敗します。このServiceではエンドポイントとしてロードバランサーを作成しようとしています。ロードバランサーはクラウドプロバイダー(GCE, AWS, OpenStackなど)との連携が必要なため今回は作成できません。ロードバランサーを作ることでKubernetesクラスタ外からPodにアクセスするためのエンドポイントを提供することができます。
動作確認
ロードバランサー経由でアクセスできないので直接アクセスして動作確認します。
エンドポイントがなくてもPodの設定でPortを外部公開(expose)するように設定していればNodeがそのポートでListenしているので、直接Nodeに対してアクセスすればPodにアクセスできます。
今回は環境ではVirtualBox内のサーバへ直接アクセスできないのでNodeのIPを指定してもそもそもアクセスできません。なのでポートフォワーディングしてアクセスすることにします。
ローカルホストのPort8080にアクセスしたらnode-02のPort80にアクセスするように設定します。
$ ssh -L 8080:localhost:80 node-02
この状態でブラウザから http://localhost:8080 にアクセスしてページが表示されたら大丈夫です。テキストボックスに文字を入れて送信して文字が表示されたらバックエンド(Redis)との通信に成功しています。
以下おまけ(v0.11.0ではなくHEADで試した場合)
HEADでは公開(expose)するPortに少し修正が入ったのでNode(ホストサーバ)でPortのListenが行われてないようです。step 2で同じNodeにredis serverが2つ割り当てられたのはこのせい。
この場合はnode-02(IP:172.17.8.103)のPort80にアクセスしてもフロントエンドにアクセスできませんが、PodのIPに対してPort80でアクセスは可能です。
ポートフォワーディングの設定をnode-02
経由でPodのIP(10.244.59.19)にアクセスするように変更します。この状態から先ほどと同様にブラウザから http://localhost:8080 にアクセスすればPodにアクセスできます。
$ ssh -L 8080:10.244.59.19:80 node-02