こんにちは。CTOの馬場です。
このエントリは 1枚目 SoftLayer Advent Calendar 2014 の2日目です。
Google Container Engine (GKE) が花盛りですが、インフラエンジニアたるもの自分でも動かしてみたいですよね。とはいえ基盤と環境を用意するのは大変。
というわけで基盤と環境の部分はSoftLayerを使って代替することにして、SoftLayer上でKubernetes(k8s)を動かしてみます。
SoftLayer独自技術とかは使っていないので、これができればどんな環境でも大抵動かせると思います。
検証しながら書いているので、間違い・勘違いなどあれば指摘いただけると助かります。
構成とバージョン
- Kubernetes v0.5.3
- CentOS Linux release 7.0.1406 (Core)
- Docker 1.2.0 (extrasリポジトリ)
- golang 1.3.3 (extrasリポジトリ)
- etcd v0.4.6
- flannel v0.1.0
サーバを3台用意します。 それぞれのサーバのサーバ名(ホスト名)、役割、起動するプロセスは以下のとおりです。
- k8s-master1 : Master + Worker : kube-apiserver, kube-controller-manager, kubelet, kube-proxy, kube-scheduler, docker, etcd, flannel
- k8s-minion1 : Worker : kubelet, kube-proxy, docker, flannel
- k8s-minion2 : Worker : kubelet, kube-proxy, docker, flannel
SoftLayerの環境はeth0がLocal Network(Private IP)、eth1がPublic Network(Global IP)になります。 eth0側は10.x.x.xのうちのCIDRが割当られるので、k8sで使うアドレス帯は172.17.0.0/16、flannelで使うアドレス帯は172.18.0.0/16に変更しています。
動作概要
k8sに対するコンテナ作成等もろもろの操作はkube-apiserverに対して実施します。 設定はetcdに保存され、各サーバに伝搬します。
通信のendpointはServiceごとになります。
Serviceへの通信はkube-proxyが仲介しますが、kube-proxyに集約するためにkube-proxyが自サーバのiptablesをガリガリと書き換え、定期的にチェックしています。 コンテナ間通信がホストをまたぐ場合はkube-proxyとkube-proxyでやりとりをしたいところですが、うまくやるためにdockerをeth0にbridgeしたりすると大変なので、flannelを使ってオーバーレイネットワークを作ることで解決しています。
インストール
SoftLayerで仮想マシンを3台起動します。 そしてそれぞれにログインします。 諸事情によりプロビジョニングスクリプトは今回は利用しません。
なおiptablesをかなりガンガン使うので、インストール前の状態として全許可になっていたほうがよいです。
SoftLayerのCentOS7はデフォルトで全許可になっていたので特に問題なかったのですが、
もしiptables(firewalld)が有効なら firewall-cmd --set-default-zone=trusted
しておいてください。
まずは全台に対してrootでログインして必要なものをインストールし、master用とminion用のk8s起動用スクリプトを作成します。
厳密にはmasterにはminion用の起動スクリプトは不要だし、minionにはmaster用のetcdや起動スクリプトは不要ですが、たいした量ではないので一括でインストールしてしまいます。
yum -y clean all
yum -y update
yum -y install golang
yum -y install git
yum -y install kernel-headers
git clone https://github.com/coreos/flannel.git /opt/flannel
cd /opt/flannel
git checkout v0.1.0
./build
git clone https://github.com/coreos/etcd.git /opt/etcd
cd /opt/etcd
git checkout v0.4.6
./build
git clone https://github.com/coreos/etcdctl.git /opt/etcdctl
cd /opt/etcdctl
git checkout v0.4.5
./build
yum -y install docker
git clone https://github.com/GoogleCloudPlatform/kubernetes.git /opt/kubernetes
cd /opt/kubernetes
git checkout v0.5.3
cd /opt/kubernetes
sed -e '36,37s/^/#/' -e 's/--machines=".*"/--machines="${MACHINES}"/' -e '111,112s/".*"/"${MYADDRESS}"/' -e '145,146s/^/#/' -e 's@--portal_net=".*"@--portal_net="172.17.0.0/16"@' hack/local-up-cluster.sh > hack/local-up-master.sh
chmod a+x hack/local-up-master.sh
sed -e '36,37s/^/#/' -e '87,105s/^/# /' -e '122,126s/^/# /' -e '130,131s/^/# /' -e '134s/^/# /' -e '143s/^/# /' -e '139,140s/^/# /' -e '111,112s/".*"/"${MYADDRESS}"/' -e '145,146s/^/#/' -e 's/127.0.0.1:4001/${API_HOST:?}:4001/' hack/local-up-cluster.sh > hack/local-up-minion.sh
chmod a+x hack/local-up-minion.sh
ここまではProvisioning Scriptでやってしまってもいいかもしれませんね。
次に起動の準備をします。
以下のように全サーバの /etc/hosts
にPrivate IPと、k8s用のホスト名を登録します。
この登録はk8s的には必要ないのですが、私手順で取り扱い易くするために敢えて別のホスト名を設定しています。
echo '10.110.203.195 kube-master1' | tee -a /etc/hosts
echo '10.110.203.194 kube-minion1' | tee -a /etc/hosts
echo '10.110.203.196 kube-minion2' | tee -a /etc/hosts
これで準備は完了です。 まずはk8s-master1でetcd、flannel、docker、k8sを この順番で 起動します。
cd /opt/kubernetes
export MASTER_HOST=kube-master1
export LOCAL_IF=eth0
export API_HOST=$(getent hosts kube-master1 | awk '{print $1}')
export KUBERNETES_PROVIDER=local
export KUBERNETES_MASTER=http://${API_HOST:?}:8080
export MYADDRESS=$(ip a show ${LOCAL_IF:?} | grep -w inet | awk '{print $2}' | cut -d / -f 1)
export MACHINES=$(getent hosts | grep -w "kube" | awk '{print $1}' | xargs echo | sed 's/ /,/g')
/opt/etcd/bin/etcd -name `uname -n` -data-dir /tmp/etcd-data -addr ${MYADDRESS:?}:4001 -peer-addr ${MYADDRESS}:7001 >/tmp/etcd.log 2>&1 &
sleep 3
/opt/etcdctl/bin/etcdctl mk /coreos.com/network/config '{"Network":"172.18.0.0/16"}'
/opt/flannel/bin/flanneld -ip-masq=true -etcd-endpoint=http://${API_HOST:?}:4001 >/tmp/flanneld.log 2>&1 &
sleep 3
source /run/flannel/subnet.env
sleep 1
/usr/bin/docker -d --selinux-enabled --bip=${FLANNEL_SUBNET:?} --mtu=${FLANNEL_MTU:?} --host=unix:///var/run/docker.sock >/tmp/docker.log 2>&1 &
hack/local-up-master.sh
随所に配置された sleep
が涙ぐましいですね。
うまくいかない場合は /tmp
配下にログが色々出ているので確認してみてください。
次にk8s-minion1とk8s-minion2でもflannel、docker、k8sをこの順番で起動します。
cd /opt/kubernetes
export MASTER_HOST=kube-master1
export LOCAL_IF=eth0
export API_HOST=$(getent hosts kube-master1 | awk '{print $1}')
export KUBERNETES_PROVIDER=local
export KUBERNETES_MASTER=http://${API_HOST:?}:8080
export MYADDRESS=$(ip a show ${LOCAL_IF:?} | grep -w inet | awk '{print $2}' | cut -d / -f 1)
export MACHINES=$(getent hosts | grep -w "kube" | awk '{print $1}' | xargs echo | sed 's/ /,/g')
/opt/flannel/bin/flanneld -ip-masq=true -etcd-endpoint=http://${API_HOST:?}:4001 >/tmp/flanneld.log 2>&1 &
sleep 3
source /run/flannel/subnet.env
sleep 1
/usr/bin/docker -d --selinux-enabled --bip=${FLANNEL_SUBNET:?} --mtu=${FLANNEL_MTU:?} --host=unix:///var/run/docker.sock >/tmp/docker.log 2>&1 &
hack/local-up-minion.sh
ここまでくれば設定は完了です。
minion
が3つ(k8s-master, k8s-minion1, k8s-minion2)登録できていれば成功です。
/minions
の代わりに /pods
, /services
, /replicationControllers
も指定可能です。
よく使うので覚えておきましょう。
[root@k8s-master1 kubernetes]# cluster/kubecfg.sh list /minions
Minion identifier
----------
10.110.203.194
10.110.203.196
10.110.203.195
※v0.5.0からはkubecfgではなくkubectlを使うらしいのですが、ここではguestbookの例に従ってkubecfgを使っています
※kubecfgの出力はデフォルトではソートされないため、実行するたびに順番が変わるので注意してください
guestbook exampleを動かす
k8sには例がいくつか同梱されているので、その中でもお手軽なguestbookを動かしてみます。
cd /opt/kubernetes
export MASTER_HOST=kube-master1
export LOCAL_IF=eth0
export API_HOST=$(getent hosts kube-master1 | awk '{print $1}')
export KUBERNETES_PROVIDER=local
export KUBERNETES_MASTER=http://${API_HOST:?}:8080
export MYADDRESS=$(ip a show ${LOCAL_IF:?} | grep -w inet | awk '{print $2}' | cut -d / -f 1)
export MACHINES=$(getent hosts | grep -w "kube" | awk '{print $1}' | xargs echo | sed 's/ /,/g')
cluster/kubecfg.sh -c examples/guestbook/redis-master.json create pods
# ※しばし待つ1
cluster/kubecfg.sh -c examples/guestbook/redis-master-service.json create services
cluster/kubecfg.sh -c examples/guestbook/redis-slave-controller.json create replicationControllers
# ※しばし待つ2
cluster/kubecfg.sh -c examples/guestbook/redis-slave-service.json create services
cluster/kubecfg.sh -c examples/guestbook/frontend-controller.json create replicationControllers
# ※しばし待つ3
※しばし待つ1〜3
のところで必要なdocker imageのをpullするため、かなり時間がかかります。
裏で非同期実行してくれるのでコンソール操作が止まることはないのですが、最初は pods
のステータスを見て Pending
が Running
になってから次に進むのが無難かなと思います。
※ cluster/kubecfg.sh -c examples/guestbook/frontend-service.json create services
は不要でした。@2014.12.2訂正
cluster/kubecfg.sh list /pods
最終的に全て Running
になれば成功です。
[root@k8s-master1 kubernetes]# cluster/kubecfg.sh list /pods
Name Image(s) Host Labels Status
---------- ---------- ---------- ---------- ----------
63d5bfdf-7561-11e4-a0d4-0626f523798c brendanburns/php-redis 10.110.203.195/ name=frontend,uses=redisslave,redis-master Running
redis-master dockerfile/redis 10.110.203.194/ name=redis-master Running
314f5cec-7519-11e4-959f-0626f523798c brendanburns/redis-slave 10.110.203.196/ name=redisslave,uses=redis-master Running
314f9a36-7519-11e4-959f-0626f523798c brendanburns/redis-slave 10.110.203.195/ name=redisslave,uses=redis-master Running
631decf1-7519-11e4-959f-0626f523798c brendanburns/php-redis 10.110.203.194/ name=frontend,uses=redisslave,redis-master Running
63200b5b-7519-11e4-959f-0626f523798c brendanburns/php-redis 10.110.203.196/ name=frontend,uses=redisslave,redis-master Running
この状態でChromeで http://(Public IP):8000/ アクセスするとguestbookが表示されます。 Public IPはホストOSのeth1にアサインされているグローバルIPアドレスです。
なお、このguestbookは結構バギーで、同じ値を複数回投稿すると壊れたりします。 あまり細かいことは気にしないようにしましょう。
まとめ
SoftLayer+k8sはいかがだったでしょうか? dockerまわりなどpullでかなり帯域を食うので、このような検証でもぜひクラウドをご活用ください。
ではでは。