EKSが発表された今、AWSでのKubernetesを見直したい。
AWSで運用するときの前提条件
KubernetesをAWSで運用しようというとき、比較的忘れがちなのが以下の条件です。
- m4インスタンスよりm3インスタンスを推奨
- Docker Storageでdevicemapper/ループバックデバイスは非推奨
m4よりm3というのは、EBSよりアクセスが安定していて高速なインスタンスストアが利用できるからですね。
インスタンス終了とともに消えてしまうインスタンスストアですが、コンテナは揮発性なので、それが乗っかるホストのディスクも揮発性でいいでしょという…1
また、Dockerのストレージドライバでdevicemapperを使用している際、loop-lvm
モードは本番環境で推奨されていません。
http://docs.docker.jp/engine/userguide/storagedriver/device-mapper-driver.html
ループバックデバイスを使っていると、コンテナの展開は遅くなり、コンテナ内のファイル読み書き速度も劣化するはずですが、RHEL系のOSだと多くの場合、デフォルトの設定がdevicemapper
でloop-lvm
です2。
そういうわけで、この記事では、
- インスタンスストアのある
m3.large
でKubernetesクラスタを構築し、 - インスタンスストアにDockerストレージを置き
devicemapper
、direct-lvm
モードを使う
という条件でKubernetesのクラスタを構築してみます。クラスタ構築にはkubeadm
を使用します。
デフォルトのストレージドライバ
とはいえ、よく使われている構成ツールの場合はDockerストレージをループバックデバイスに作ることはないようです。
kubeadm
や手作業でクラスタを構築する場合、dockerインストールは自分でやる必要があるので、RHEL系で普通にセットアップするとdocker info
の結果が以下のようになります。
# CentOSに普通にdockerをyumでインストールした場合
$ sudo docker info
Storage Driver: devicemapper
Pool Name: docker-202:1-4210412-pool
Pool Blocksize: 65.54 kB
...
Deferred Deleted Device Count: 0
Data loop file: /var/lib/docker/devicemapper/devicemapper/data
WARNING: Usage of loopback devices is strongly discouraged for production use. Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.
WARNINGで怒られてますね。これを正しくセットアップしようというのがこの記事の主旨になります。
一方、メジャーな構成ツールであるkops
でk8sクラスタを構築すると以下の通りです。
# kopsで構成した場合のDocker Storage
Server Version: 1.13.1
Storage Driver: overlay
Backing Filesystem: xfs
kube-aws
はContainer Linuxを使用していますが、以下の様にContainer LinuxはデフォルトでDockerストレージが正しく設定されているので大丈夫そうです。
# Container Linux
Server Version: 1.12.6
Storage Driver: overlay
Backing Filesystem: extfs
kubespray
も、インストールプロセスでエラーが出たので実際の挙動は見てませんが、コード上だとdocker-storge-setup
というDockerストレージのセットアップ実行コードがあったので大丈夫そうですね。
プロダクション運用を考えた際overlay2
やoverlay
を使用するのが本当は良さそうですが、overlay2
にカーネルv4.0以降が必要なことやCentOSでdevicemapper
が推奨されていること、Dockerの公式ページであまりoverlay
を推奨してないことから、今回はdevicemapper
でdirect-lvm
をセットアップします。
https://docs.docker.com/engine/userguide/storagedriver/overlayfs-driver/
https://docs.docker.com/engine/userguide/storagedriver/selectadriver/#docker-ce
(あと、別のディスクにDockerストレージをセットアップすることで、Dockerストレージの使用率が増えてきたときもOS、Kubernetesの動作に与える影響が少なく、安定することが期待できそう)
m3系のインスタンスを作成し、インスタンスストアをアタッチする
まず、AWSでインスタンスを作成して、インスタンスストアをアタッチします。
今回はmaster(&etcd)1台、worker1台で構成するので2つインスタンスを立ち上げます。
パブリックサブネットに、通常通りm3.large
のインスタンスを立ち上げますが、以下のようにインスタンスストアもアタッチしておきます。
セキュリティグループは、面倒だったので今回はインスタンス間全通しで行きます。
Dockerのセットアップ
kubeadm
は、実行前にクラスタのノードにdocker
やkubelet
をインストールしておく必要があります。
ここでdocker
をインストールした際に、Dockerストレージのセットアップも実行しておきます。
$ ssh centos@k8s-master
$ sudo su -
$ yum install -y docker
awsのインスタンスストアは以下のようにマウント済みなので、そのままDockerストレージとしてセットアップしようとすると失敗します。
そのため、前もってアンマウントします。
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 8G 0 disk
└─xvda1 202:1 0 8G 0 part /
xvdb 202:16 0 30G 0 disk /mnt
$ umount /mnt
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 8G 0 disk
└─xvda1 202:1 0 8G 0 part /
xvdb 202:16 0 30G 0 disk
インスタンスストアの/dev/xvdb
デバイスをDockerストレージとしてセットアップします。
# 設定ファイルの作成
$ cat << EOT > /etc/sysconfig/docker-storage-setup
DEVS=/dev/xvdb
VG=docker-vg
WIPE_SIGNATURES=true
EOT
# 以下コマンドでDockerストレージをセットアップ
$ docker-storage-setup
INFO: Volume group backing root filesystem could not be determined
INFO: Wipe Signatures is set to true. Any signatures on /dev/xvdb will be wiped.
/dev/xvdb: 2 bytes were erased at offset 0x00000438 (ext3): 53 ef
INFO: Device node /dev/xvdb1 exists.
Physical volume "/dev/xvdb1" successfully created.
Volume group "docker-vg" successfully created
Using default stripesize 64.00 KiB.
Rounding up size to full physical extent 32.00 MiB
Thin pool volume with chunk size 512.00 KiB can address at most 126.50 TiB of data.
Logical volume "docker-pool" created.
Logical volume docker-vg/docker-pool changed.
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 8G 0 disk
└─xvda1 202:1 0 8G 0 part /
xvdb 202:16 0 30G 0 disk
└─xvdb1 202:17 0 30G 0 part
├─docker--vg-docker--pool_tmeta 253:0 0 32M 0 lvm
│ └─docker--vg-docker--pool 253:2 0 12G 0 lvm
└─docker--vg-docker--pool_tdata 253:1 0 12G 0 lvm
└─docker--vg-docker--pool 253:2 0 12G 0 lvm
Dockerストレージがセットアップできました。docker
サービスを起動します。
$ systemctl enable docker && systemctl start docker
$ docker info
Storage Driver: devicemapper
Pool Name: docker--vg-docker--pool
Pool Blocksize: 524.3 kB
Base Device Size: 10.74 GB
Backing Filesystem: xfs
Data file:
Metadata file:
Data Space Used: 19.92 MB
Data Space Total: 12.81 GB
Data Space Available: 12.79 GB
Metadata Space Used: 45.06 kB
Metadata Space Total: 33.55 MB
Metadata Space Available: 33.51 MB
Thin Pool Minimum Free Space: 1.281 GB
Udev Sync Supported: true
Deferred Removal Enabled: true
Deferred Deletion Enabled: true
Deferred Deleted Device Count: 0
Library Version: 1.02.140-RHEL7 (2017-05-03)
このように、ループバックデバイスに関するWARNINGが消えていることが確認できました。
同様の処理をworkerに対しても実施します。
kubeadmによるクラスタの初期化
ドキュメントを参考に、kubeadm
でクラスタを初期化します。
kubeadm
はまだβ段階で、プロダクション運用にはまだ早いですが、簡単にクラスタを構築できるのでここではkubeadm
でクラスタを構築してみます。
kubernetesのmasterを初期化
kubeadm init
コマンドでmasterを初期化します。
なお、kubeadm init
コマンドが成功した際に表示される、
kubeadm join --token xxxx
というコマンドは後でWorkerを追加する際に必要になるので控えておきます。
# yumリポジトリの追加
$ cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
# k8s関連パッケージの追加
yum install -y kubelet kubeadm kubectl
# kubeletの開始
systemctl enable kubelet && systemctl start kubelet
# kubeadmのセットアップはselinuxを無効化する必要があるようです
$ setenforce 0
# kubeadmでmasterを初期化
$ kubeadm init --pod-network-cidr=10.244.0.0/16
# 以下コマンドが表示されるので控えておく(Worker追加の際に使用する)
# kubeadm join --token xxx
# kubectlをAPIサーバーへアクセスできるよう設定
$ mkdir -p $HOME/.kube
$ cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ chown $(id -u):$(id -g) $HOME/.kube/config
# podネットワーク用のadd-onを追加(今回はflannelを使用)
$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml
# masterがReadyになるのを待つ
watch kubectl get nodes
kubernetesのworkerを追加
worker用に作成したec2インスタンスにsshログインし、上記で初期化したクラスタにJOINさせます。
$ ssh centos@k8s-worker
$ sudo su -
# yumリポジトリの追加
$ cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
# kubeadmのセットアップはselinuxを無効化する必要があるようです
$ setenforce 0
# k8s関連パッケージの追加
yum install -y kubelet kubeadm kubectl
# kubeletの開始
systemctl enable kubelet && systemctl start kubelet
# kubeadmでWorkerを追加する。
# kubeadm initコマンドの完了時に表示されたコマンドをworkerインスタンスで実行する
$ kubeadm join --token xxxx 10.0.157.43:6443 --discovery-token-ca-cert-hash xxx
kubeadm join --token 2f80ec.3db07de32fde6f62 10.0.157.43:6443 --discovery-token-ca-cert-hash sha256:5db1f5a2fb1694bd9ad986a8367351632499d9668f30373797d1b74d85456b40
[kubeadm] WARNING: kubeadm is in beta, please do not use it for production clusters.
[preflight] Running pre-flight checks
[preflight] WARNING: kubelet service is not enabled, please run 'systemctl enable kubelet.service'
[preflight] Starting the kubelet service
[discovery] Trying to connect to API Server "10.0.157.43:6443"
[discovery] Created cluster-info discovery client, requesting info from "https://10.0.157.43:6443"
[discovery] Requesting info from "https://10.0.157.43:6443" again to validate TLS against the pinned public key
[discovery] Cluster info signature and contents are valid and TLS certificate validates against pinned roots, will use API Server "10.0.157.43:6443"
[discovery] Successfully established connection with API Server "10.0.157.43:6443"
[bootstrap] Detected server version: v1.8.5
[bootstrap] The server supports the Certificates API (certificates.k8s.io/v1beta1)
Node join complete:
* Certificate signing request sent to master and response
received.
* Kubelet informed of new secure connection details.
Run 'kubectl get nodes' on the master to see this machine join.
master側で、workerがjoinしたことを確認します。
$ ssh centos@k8s-master
$ sudo su -
$ watch kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-x.ap-northeast-1.compute.internal Ready <none> 45m v1.8.5
ip-x.ap-northeast-1.compute.internal Ready master 1h v1.8.5
nginxでも走らせてみますか。
# masterで
$ kubectl run my-nginx --image=nginx --replicas=2 --port=80
$ kubectl expose deployment my-nginx --type=NodePort
$ kubectl describe services my-nginx
Name: my-nginx
Namespace: default
Labels: run=my-nginx
Annotations: <none>
Selector: run=my-nginx
Type: NodePort
IP: 10.99.129.58
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 32017/TCP
Endpoints: 10.244.1.2:80,10.244.1.3:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
# NodePortでアクセスして表示されることを確認
$ curl http://$(curl -s http://169.254.169.254/latest/meta-data/local-ipv4):32017
というわけで、Dockerストレージをdirect-lvm
モードで動かしてkubernetesクラスタを動かすことができました。
まとめ
Kubernetesを使用していて、コンテナのデプロイが遅いとか、コンテナ内ファイルアクセスが遅いとか、そういった問題を感じるようでしたら一度Dockerストレージの設定を見直してみてはいかがでしょうか。
-
ただ、以前は上記のようなことがkubernetesのドキュメントに書いてましたが今は消えてるので、インスタンスストアにこだわる必要性はあまりないのかもしれません。既にm5インスタンスが利用可能になっている時代ですし… ↩
-
Docker v1.13.1からデフォルトのストレージドライバが
overlay
になっているので、最新のDockerをインストールしている場合はoverlay
ドライバを使用していると思います。普通にyum install
でDockerを入れた場合1.12.6がインストールされました。 ↩