はじめに
Redhatが本気でやってそうなので企業内で使うならばProject Atomicが最有力なのではないかなと、Fedora Atomic Hostを使ってEC2上でDockerコンテナのクラスタリングを試してみました。
タイトルはKubernetesと書いていますが、Atomicに入っているCockpitとかも一通り試します。
Project Atomicが何者かは、このスライド「Docker on RHEL & Project Atomic 入門 – #Dockerjp 4」が大変わかりやすいです。
試した時点の各ソフトウェアのバージョン
だいぶ前ですが、試したのは2015年2月17日なので以下の情報はその日付時点の情報です。
Fedora-Cloud-Atomic-20141203-21.x86_64でrpm-ostree upgrade
で入った最新バージョンです。
- Kubernetes: 0.7.0
- Cockpit: 0.27
- etcd: 0.4.6
- docker: 1.4.1
- cAdvisor: 0.6.2
最新は以下なので、かなり古い。
まあ日々バージョンアップしているので仕方ないか。
- Kubernetes: 0.10.1
- Cockpit: 0.38
- etcd: 2.0.3
- docker: 1.5.0
- cAdvisor: 0.9.0
今rpm-ostree upgrade
すると上のより新しいバージョンが入りますし、最新のバージョンも変わっていますが、この記事の内容を試した時点ということでお許し下さい。
EC2インスタンスのローンチ
RHEL Atomic, CentOS Atomicもありますが、今回は一番開発が早そうなFedora Atomicを使ってみます。
Fedora Cloud のダウンロードの一番下の「Atomic Cloud HVM」から好きなリージョンのAMIを選択。

普通にローンチします。
あとでクラスタ組むときに使うので、セキュリティグループには適当な名前をつけておいてください。

SSHログインする際のユーザはfedoraです。
$ ssh -i ~/.ssh/key.pem fedora@1.2.3.4
Kubernetesなどのバージョンが古いので、パッケージのアップデートをします。
$ sudo rpm-ostree upgrade $ sudo systemctl reboot
Dockerコンテナを動かしてみる
Dockerもちゃんと動いています。
$ systemctl list-units | grep docker sys-devices-virtual-net-docker0.device loaded active plugged /sys/devices/virtual/net/docker0 sys-subsystem-net-devices-docker0.device loaded active plugged /sys/subsystem/net/devices/docker0 docker.service loaded active running Docker Application Container Engine
docker run
してみます。
$ sudo docker run centos /bin/echo "Hello Atomic" Unable to find image 'centos' locally Pulling repository centos dade6cb4530a: Download complete 511136ea3c5a: Download complete 5b12ef8fd570: Download complete Status: Downloaded newer image for centos:latest Hello Atomic
動きました。
Cockpitを動かしてみる
CockpitはサーバーおよびDockerコンテナを管理できるWebインターフェースです。
Atomicではデフォルトで動いています。
Cockpitは9090ポートで動くので、セキュリティグループで9090を開けます。

OSのユーザアカウントでログインするので、root
ユーザにパスワードを設定します。
$ sudo passwd root
http://1.2.3.4:9090
にアクセス

先ほどパスワードを使ってrootユーザでログインすると、クラスタ内のサーバー一覧が表示されます。

各リソースの利用状況を監視できます。

Dockerコンテナ・イメージの管理もできる

Dockerコンテナの管理はかなりいろいろできます。
コンテナの起動・停止
(惜しいことにdocker run
のオプションの指定はできない。)

コンテナ毎にシステムリソースの制限を設定

Docker Hubからイメージをpull

他にも「サーバーのターミナルをWebから操作」「全コンテナ合計のCPU・メモリ使用率の参照」「イメージからコンテナの起動」などができます。
cAdvisorを動かしてみる
cAdvisorはサーバー・コンテナ監視のツールです。デフォルトで起動はしないので、起動します。
$ sudo systemctl start cadvisor
動きました。
$ sudo journalctl -f -l -xn -u cadvisor -- Logs begin at Mon 2015-02-16 13:19:03 UTC. -- Feb 18 02:49:49 ip-172-30-0-246.ec2.internal cadvisor[1331]: I0218 02:49:49.443643 1331 manager.go:77] cAdvisor running in container: "/" Feb 18 02:49:49 ip-172-30-0-246.ec2.internal cadvisor[1331]: I0218 02:49:49.444400 1331 manager.go:91] Machine: {NumCores:1 MemoryCapacity:1040220160 Filesystems:[{Device:/dev/xvda1 Capacity:198902784}]} Feb 18 02:49:49 ip-172-30-0-246.ec2.internal cadvisor[1331]: I0218 02:49:49.444749 1331 manager.go:98] Version: {KernelVersion:3.18.6-200.fc21.x86_64 ContainerOsVersion:Fedora 21 (Twenty One) DockerVersion:Unknown CadvisorVersion:0.6.2} Feb 18 02:49:49 ip-172-30-0-246.ec2.internal cadvisor[1331]: E0218 02:49:49.444973 1331 cadvisor.go:62] Docker registration failed: unable to communicate with docker daemon: dial unix /var/run/docker.sock: no such file or directory. Feb 18 02:49:49 ip-172-30-0-246.ec2.internal cadvisor[1331]: I0218 02:49:49.445578 1331 factory.go:78] Registering Raw factory Feb 18 02:49:49 ip-172-30-0-246.ec2.internal cadvisor[1331]: I0218 02:49:49.446156 1331 manager.go:394] Added container: "/" (aliases: [], namespace: "") Feb 18 02:49:49 ip-172-30-0-246.ec2.internal cadvisor[1331]: I0218 02:49:49.446334 1331 manager.go:131] Starting recovery of all containers Feb 18 02:49:49 ip-172-30-0-246.ec2.internal cadvisor[1331]: I0218 02:49:49.446922 1331 manager.go:136] Recovery completed Feb 18 02:49:49 ip-172-30-0-246.ec2.internal cadvisor[1331]: I0218 02:49:49.447805 1331 cadvisor.go:103] Starting cAdvisor version: "0.6.2" on port 4194 Feb 18 02:49:49 ip-172-30-0-246.ec2.internal cadvisor[1331]: I0218 02:49:49.448019 1331 container.go:141] Start housekeeping for container "/"
4194
ポートで動いているので、4194ポートを開けて、http://<IPアドレス>:4194
にアクセスします。

サーバーのリソースやDockerコンテナのリソースを見ることができます。
APIもあるので監視の仕組みに使えると思います。
(Dockerコンテナを見ようとするとなぜかエラーになりましたが、とりあえず見なかったことにしました。)
最新バージョン(v0.9.0)だとネットワークやDiskIOの監視もできるようです。(Releases · google/cadvisor)
なんかCockpitと機能がかぶっていますが、cAdvisorはKubernetesが使っているのでしょうがないですね。
Kubernetesを動かしてみる
KubernetesはDockerコンテナのクラスタリングツールです。CoreOSのfleetに当たります。
Testing Kubernetes with an Atomic Host — Project Atomicを参考にKubernetesを動かします。
このバージョン(etcd 0.4.6)ではデフォルトの設定ではetcdが起動しないので設定を変更する。/etc/etcd/etcd.conf
のname
のコメントを外す。
$ sudo vi /etc/etcd/etcd.conf
name = "default-name"
Kubernetesはインストール時は動いていないので手動で動かします。
$ sudo su # for SERVICES in etcd kube-apiserver kube-controller-manager kube-scheduler docker kube-proxy.service kubelet.service; do systemctl restart $SERVICES systemctl enable $SERVICES systemctl status $SERVICES done
etcdが起動していることを確認します。
$ curl http://localhost:4001/version etcd 0.4.6
Kubernetesのapiserverが起動していることを確認します。
$ curl http://localhost:8080/version { "major": "0", "minor": "7+", "gitVersion": "v0.7.0-18-g52e165a4fd720d-dirty", "gitCommit": "52e165a4fd720d1703ebc31bd6660e01334227b8", "gitTreeState": "dirty" }
まだクラスタは組んでいないのでminionは自分だけです。
$ kubectl get minions NAME LABELS 127.0.0.1 <none>
Apacheのpodを起動してみます。
$ vi apache.json
{ "id": "fedoraapache", "kind": "Pod", "apiVersion": "v1beta1", "desiredState": { "manifest": { "version": "v1beta1", "id": "fedoraapache", "containers": [{ "name": "fedoraapache", "image": "fedora/apache", "ports": [{ "containerPort": 80, "hostPort": 80 }] }] } }, "labels": { "name": "fedoraapache" } }
$ kubectl create -f apache.json I0216 16:00:07.854098 6240 restclient.go:133] Waiting for completion of operation 2 fedoraapache
Apacheが動きました。
$ kubectl get pod fedoraapache NAME IMAGE(S) HOST LABELS STATUS fedoraapache fedora/apache 127.0.0.1/ name=fedoraapache Running
$ curl http://localhost/ Apache
Kubernetesでクラスタ環境を構築
kubernetes/fedora_manual_config.md at master · GoogleCloudPlatform/kubernetesを参考に手動で設定します。
インスタンスを一台増やして一台をmaster、残りをminionとしてクラスタを組みます。以下の構成とします。
fed-master = 172.30.0.246 fed-minion = 172.30.0.43
このとき追加したインスタンスは全部同じセキュリティグループに所属させてください。
追加したインスタンスもパッケージを更新します。
$ sudo rpm-ostree upgrade $ sudo systemctl reboot
上の記事ではFedora Atomicでは必要ないと書かれているのですが、同じエラーが出るのでバグ対応します。
$ sudo su # sed -e "s/docker\.socket/docker\.service/g" /usr/lib/systemd/system/kubelet.service > /etc/systemd/system/kubelet.service # systemctl daemon-reload
全ノードのhostsにサーバーのIPを書きます。(DNSサーバーにホスト名が登録されていれば必要はありません。)
$ sudo su # echo "172.30.0.246 fed-master 172.30.0.43 fed-minion" >> /etc/hosts
全ノードの/etc/kubernetes/config
を以下のように変更します。KUBE_ETCD_SERVERS
の設定のみ変更すれば大丈夫なはずです。
# Comma seperated list of nodes in the etcd cluster KUBE_ETCD_SERVERS="--etcd_servers=http://fed-master:4001" # logging to stderr means we get it in the systemd journal KUBE_LOGTOSTDERR="--logtostderr=true" # journal message level, 0 is debug KUBE_LOG_LEVEL="--v=0" # Should this cluster be allowed to run privleged docker containers KUBE_ALLOW_PRIV="--allow_privileged=false"
同じセキュリティグループ内で全てのTCP通信をOKに設定します。

masterノードの設定
前に起動したApacheのPodを停止しておきます。
$ kubectl delete -f apache.json fedoraapache
masterノードに不必要なサービスを停止します。
$ sudo su # for SERVICES in kube-proxy kubelet docker; do systemctl stop $SERVICES systemctl disable $SERVICES systemctl status $SERVICES done
masterノードの/etc/kubernetes/apiserver
を以下のように変更します。
# The address on the local server to listen to. KUBE_API_ADDRESS="--address=0.0.0.0" # The port on the local server to listen on. KUBE_API_PORT="--port=8080" # How the replication controller and scheduler find the kube-apiserver KUBE_MASTER="--master=http://fed-master:8080" # Port minions listen on KUBELET_PORT="--kubelet_port=10250" # Address range to use for services KUBE_SERVICE_ADDRESSES="--portal_net=10.254.0.0/16" # Add you own! KUBE_API_ARGS=""
masterノードの/etc/kubernetes/controller-manager
を以下のように変更します。
# Comma seperated list of minions KUBELET_ADDRESSES="--machines=fed-minion"
masterノードで関連サービスを起動します。
$ sudo su # for SERVICES in etcd kube-apiserver kube-controller-manager kube-scheduler; do systemctl restart $SERVICES systemctl enable $SERVICES systemctl status $SERVICES done
minionノードの設定
/etc/kubernetes/kubelet
を以下のように設定します。
# The address for the info server to serve on KUBELET_ADDRESS="--address=0.0.0.0" # The port for the info server to serve on KUBELET_PORT="--port=10250" # You may leave this blank to use the actual hostname KUBELET_HOSTNAME="--hostname_override=fed-minion" # Add your own! KUBELET_ARGS=""
必要なサービスを起動します。
$ sudo su # for SERVICES in kube-proxy kubelet docker; do systemctl restart $SERVICES systemctl enable $SERVICES systemctl status $SERVICES done
masterノードで以下のコマンドを実行し、fed-minionがクラスタに追加されていることを確認します。
$ kubectl get minions NAME LABELS fed-minion <none>
Apache Podを作成してみます。
$ kubectl create -f apache.json fedoraapache
ちゃんとfed-minionノードで起動しました。
$ kubectl get pod fedoraapache NAME IMAGE(S) HOST LABELS STATUS fedoraapache fedora/apache fed-minion/ name=fedoraapache Running
$ curl http://fed-minion/ Apache
クラスタ構成が組めました。
しかし、ここでCockpitを見てもmasterノードしか表示されない。最新のCockpitではKubernetes連携もマルチホスト対応が書かれているので何か違うかもしれない。(Releases · cockpit-project/cockpit)
minionを2つにする
1インスタンス追加します。
上と同じ手順で1インスタンス追加してください。
fed-master = 172.30.0.246 fed-minion = 172.30.0.43 fed-minion-2 = 172.30.0.11
fed-minion-2の/etc/hosts
に各ノードのIPを設定します。
# echo "172.30.0.246 fed-master 172.30.0.43 fed-minion 172.30.0.11 fed-minion-2" >> /etc/hosts
fed-masterとfed-minionの/etc/hosts
にも以下の設定を追加します。
# echo "172.30.0.11 fed-minion-2" >> /etc/hosts
fed-masterの/etc/kubernetes/controller-manager
の以下の箇所にfed-minion-2を追加します。
KUBELET_ADDRESSES="--machines=fed-minion,fed-minion-2"
fed-minion-2の/etc/kubernetes/config
のKUBE_ETCD_SERVERS
を以下のように変更します。
KUBE_ETCD_SERVERS="--etcd_servers=http://fed-master:4001"
fed-minion-2の/etc/kubernetes/kubelet
の以下の2箇所を変更します。
KUBELET_ADDRESS="--address=0.0.0.0"
KUBELET_HOSTNAME="--hostname_override=fed-minion-2"
fed-minion-2の必要なサービスを起動します。
$ sudo su # for SERVICES in kube-proxy kubelet docker; do systemctl restart $SERVICES systemctl enable $SERVICES systemctl status $SERVICES done
fed-masterのサービスを再起動します。
$ sudo su # for SERVICES in etcd kube-apiserver kube-controller-manager kube-scheduler; do systemctl restart $SERVICES systemctl enable $SERVICES systemctl status $SERVICES done
クラスタにfed-minion-2が追加されました。
$ kubectl get minions NAME LABELS fed-minion-2 <none> fed-minion <none>
Guestbookサンプルを動かす
再び、Testing Kubernetes with an Atomic Host — Project Atomicを参考に進め、このクラスタ構成でKubernetesに付いているGuestbookサンプルを動かします。
GuestbookはPHPとRedisで構成されているデモアプリで、コメントを書き込むとRedisに保存されます。
フロントエンドのコンテナは3つ、Redisのマスターが1つ、読み込み専用のRedisのスレーブが2つという構成です。PHPのアプリは読み込みはスレーブから、書き込みはマスターに行います。
今回はminion2つで試すので、フロントエンドも2つに減らします。以下のような構成です。

Release v0.7.0 Release Candidate · GoogleCloudPlatform/kubernetesからkubernetesのGitリポジトリをダウンロードします。一応インストールされているバージョンと同じv0.7.0にしておきます。
なお、ここからの作業は全てfed-masterノードで行います。
$ curl -L -O https://github.com/GoogleCloudPlatform/kubernetes/releases/download/v0.7.0/kubernetes.tar.gz $ tar zxvf kubernetes.tar.gz $ cd kubernetes/examples/guestbook
RedisのマスターPod
を起動します。
$ kubectl create -f redis-master.json
get pod redis-master
がRuning
になるまで待ちます。
$ kubectl get pod redis-master NAME IMAGE(S) HOST LABELS STATUS redis-master dockerfile/redis fed-minion/ name=redis-master Running
redis-master
というラベルのついたPod
に通信するサービスを作成します。
KubernetesのService
はコンテナ間通信を仲介するロードバランサーです。コンテナからは環境変数を通してサービスを使うことができます。
$ kubectl create -f redis-master-service.json
Service
が作成されたことを確認します。
$ kubectl get service redis-master NAME LABELS SELECTOR IP PORT redis-master name=redis-master name=redis-master 10.254.104.212 6379
これで全てのPod
はredisマスタに10.254.104.212:6379
でアクセスできるようになります。
Redisマスタは単一のPod
でしたが、Redisスレーブは2つのPod
を維持するよう設定されています。これはreplicated pod
と呼ばれ、ReplicationController
が管理します。
以下のコマンドでRedisスレーブを起動します。
$ kubectl create -f redis-slave-controller.json
ReplicationContorlller
とPod
が2つ起動しています。
Redisスレーブはfed-minionとfed-minion-2の両方で1つずつ動きます。
$ kubectl get replicationController redisSlaveController NAME IMAGE(S) SELECTOR REPLICAS redisSlaveController brendanburns/redis-slave name=redisslave 2
$ kubectl get pods -l "name=redisslave" NAME IMAGE(S) HOST LABELS STATUS 36300455-b6a8-11e4-8444-12e8df01b920 brendanburns/redis-slave fed-minion-2/ name=redisslave,uses=redis-master Running 3630b03b-b6a8-11e4-8444-12e8df01b920 brendanburns/redis-slave fed-minion/ name=redisslave,uses=redis-master Running
RedisスレーブのDockerコンテナのコマンドは以下のように環境変数からRedisマスタのホスト名とポート番号を取得するようになっています。これはredis-masterサービス
で解決されます。
redis-server --slaveof ${REDIS_MASTER_SERVICE_HOST:-$SERVICE_HOST} $REDIS_MASTER_SERVICE_PORT
RedisスレーブPod
がRunning
になったら、RedisスレーブService
を作成します。
$ kubectl create -f redis-slave-service.json
$ kubectl get service redisslave NAME LABELS SELECTOR IP PORT redisslave name=redisslave name=redisslave 10.254.197.146 6379
RedisマスタService
と同じように、どのPod内からもRedisスレーブに10.254.197.146:6379
でアクセスできるようになります。Service
がロードバランシングし、2つのPodに透過的にアクセスできます。
最後にフロントエンドPod
を作成します。サンプルではフロントエンドPod
は3つのPod
で構成されているのですが、minion
が2つしかないので、frontend-controller.json
のreplicas
を3から2に変更します。
"replicas": 2,
最後にフロントエンドPod
を作成します。
$ kubectl create -f frontend-controller.json
$ kubectl get replicationController frontendController NAME IMAGE(S) SELECTOR REPLICAS frontendController kubernetes/example-guestbook-php-redis name=frontend 2
$ kubectl get pods -l "name=frontend" NAME IMAGE(S) HOST LABELS STATUS b8947bd1-b6ac-11e4-8444-12e8df01b920 kubernetes/example-guestbook-php-redis fed-minion-2/ name=frontend,uses=redisslave,redis-master Running b89561c0-b6ac-11e4-8444-12e8df01b920 kubernetes/example-guestbook-php-redis fed-minion/ name=frontend,uses=redisslave,redis-master Running
PHPのコードの中ではgetenv('REDIS_MASTER_SERVICE_PORT');
のように環境変数からRedisマスタとスレーブのホスト名、ポートを取得しています。詳しくはguestbook/php-redis/index.phpを見てください。
これで全てのPod, Service, Replication Controllerが起動しました。
$ kubectl get pods NAME IMAGE(S) HOST LABELS STATUS redis-master dockerfile/redis fed-minion/ name=redis-master Running 36300455-b6a8-11e4-8444-12e8df01b920 brendanburns/redis-slave fed-minion-2/ name=redisslave,uses=redis-master Running 3630b03b-b6a8-11e4-8444-12e8df01b920 brendanburns/redis-slave fed-minion/ name=redisslave,uses=redis-master Running b8947bd1-b6ac-11e4-8444-12e8df01b920 kubernetes/example-guestbook-php-redis fed-minion-2/ name=frontend,uses=redisslave,redis-master Running b89561c0-b6ac-11e4-8444-12e8df01b920 kubernetes/example-guestbook-php-redis fed-minion/ name=frontend,uses=redisslave,redis-master Running
frontendが起動しているfed-minionからfed-minion-2に8000ポートでアクセスするとGuestbookアプリケーションが動きます。(事前にEC2のセキュリティグループで8000を開けておいてください。)

自動フェイルオーバーを試してみる
fed-minionインスタンスを停止してみる。
$ kubectl get minions NAME LABELS fed-minion-2 <none>
minion
は減った。
$ kubectl get pods F0217 15:08:54.544116 1605 get.go:75] The requested resource does not exist.
あれ。。。
これかな。kubectl/apiserver problems if minion down · Issue #2951 · GoogleCloudPlatform/kubernetes
CoreOSとの比較
ChromeOS autoupdate vs rpm-ostree
シンプルさと柔軟性のトレードオフという印象
参考:OSTree: OSイメージとパッケージシステムの間にGitのアプローチを
fleet vs Kubernetes
Atomic同梱のバージョンだとfleetの方が安定している。Kubernetesはバギー。
日々リリースされているので、最新バージョンだともっといいんだろうけど。
けっこう大きな違いだと思うのはmasterサーバーの要不要。
Kubernetesはmasterサーバーが落ちたらコンテナは動いているけど、クラスタ管理機能が使えなくなる。
CoreOSの場合は全サーバーが同じ立場なので、どれが落ちても何も変わらない。
と理解しているが、合っているだろうか。
でも、Kubernetesの方が機能が豊富
- コンテナ間通信はKubernetesのServiceで実現
- Heapsterでクラスタ内コンテナのリソース監視
- fluentd+Elasticsearch+Kibanaによるログ収集・分析
Cockipit
Cockpit相当の機能はCoreOSにはない。
1ノード完結でDockerを動かす需要がどれほどあるかわからないが、それであればCockpitは十分使えそう。バギーでもなかった。
Kubernetes対応については、Cockpitの0.37でKubernetesプラグインが追加されていたり、さらに開発も進めているようなので期待したい。(今回はAtomicを試しただけなので、最新のCockpitは試していないです。)
Atomicの感想
デフォルトでKubernetes, etcd, cAdviserが起動せず、DockerとCockpitしか起動していないあたりからも、今のところAtomicは1ノードでのDockerコンテナ管理のためのホストOSとして作られている印象。
Dockerコンテナのクラスタリングは完全にKubernetes任せで、今のところはインストールしといたから後はKubernetesで好きにやってという感じに見える。
まとめると、1ノードなら使える(まあDockerにCockpit付いただけ)、クラスタ構成はまだまだこれからという感想です。