外出先でも快適なお家k8sクラスタを構築する
Table of Contents
- 1 なぜお家Kubernetesクラスタか
- 2 Workerのハードウェア構成と価格
- 3 マシンにUbuntuサーバをインストールする
- 3.1 Live USBの作成
- 3.2 インストーラの進化
- 3.3 LVMのディスク容量を増やす
- 4 Kubernetesの構成検討と構築
- 4.1 cri-oのセットアップ
- 4.2 kubeadmによるクラスタ構築
- 4.2.1 事前準備
- 4.2.2 Master/Workerの構築
- 4.3 ciliumのCNIプラグインを適用する
- 4.4 MetalLBの構築
- 5 CloudflareのArgo経由でSSHする
- 5.1 Server設定
- 5.2 Client設定
- 5.3 Cloudflare Accessでアクセスユーザを制限する
- 6 kubeadmによるクラスタアップグレード
- 6.1 CNIプラグインのアップグレード
1 なぜお家Kubernetesクラスタか
なんでもクラウドサービスな時代だが、やはりそれなりのスペックを求めるとそれなりのお値段になってしまう。また、サービスによっては下回りの構成変更の柔軟性や新機能が利用できるようになるまでラグもサービスに依存する。そんな状況を鑑みると、実験場として請求書に怯えることなく使えるお家クラスタが欲しくなってくるものだ。
2 Workerのハードウェア構成と価格
Master用のNUCは自宅のものを再利用したため、ここには含まれていない。こちらのスペックは数世代前のi5 NUCで32GBメモリ、2TBのディスクを積んでいる。メモリが市場に溢れてる今が買い時ということで8GBと悩んだ結果16GBにした。ツクモで全て買おうとしていたが購入制限に引っかかったので1台だけソフマップとなっている。また、スイッチのポートも足りなくなってきたので一緒に購入した(最近のNETGEARは謎にスマホアプリから設定変更や状態確認ができて面白い。
Shop | Item | Price(JPY) | Number | Total(JPY) |
---|---|---|---|---|
TSUKUMO | NUC8I3BEH (BOXNUC8I3BEH) | 35,366 | 2 | 70,732 |
CT2K8G4SFS824A (Crucial 16GB:8GBx2) | 11,858 | 3 | 35,574 | |
WDS500G2B0B (WD 500GB SSD) | 6,980 | 3 | 20,940 | |
Sofmap | NUC8I3BEH (BOXNUC8I3BEH) | 35,366 | 1 | 35,366 |
Amazon | GS105E-200JPS (NETGEAR Switching Hub 5port) | 3,570 | 1 | 3,570 |
BSACC0802BKA (BUFFALO power cable 3pin/2pin plug) | 482 | 3 | 1,446 | |
Yodobashi | LA-SL6-005BK (SANWA SUPPLY CAT6 LAN cable 0.5m Black) | 230 | 5 | 1,150 |
168,778 |
新世代のNUCは電源ボタンが写真右の底面にある。この向きでそのまま置いてしまうと電源ボタンが押されて切れてしまうため、割り箸を下に引いてスペーサにするという微妙な配置を行なっている。
3 マシンにUbuntuサーバをインストールする
3.1 Live USBの作成
- UbuntuサーバのISOイメージを公式ページからダウンロードする
lsusb
で操作するUSBデバイスが認識されていることを確認する- USBを抜き差しして
ls /dev/sd*
から該当デバイスを特定する sudo umount /dev/sd*
でデバイスをアンマウントするsudo mkfs.ext4 /dev/sd*
でext4にフォーマットするsudo dd if=/path/to/ubuntu-server.iso of=/dev/sda
でISOイメージをUSBに書き込む1
3.2 インストーラの進化
BIOSは Intel
ロゴが表示されたタイミングで F10
を押すと起動する2。最近はOSのインストールを自動化してしまってなかなかインストーラの画面を見ることがないが、久しぶりに使ったらGitHubのユーザ名でSSH鍵指定できるようになっていて便利だった 👏
3.3 LVMのディスク容量を増やす
Ubuntuのインストール時に無心3でぽちぽちしてしまった結果、数百メガしかないrootができあがった。結果EphemeralStorageが足りなくてPodがEvictionされてるということに。LVMの意味という感じだが、論理ボリュームを100%まで拡張することにした。
# 指定するLG(logical group)とLV(logical volume)名は =df= で調べることができる
root@worker1:/home/ladicle# df |grep vg
/dev/mapper/ubuntu--vg-ubuntu--lv 4062912 2609448 1227368 69% /
# 100%まで拡張する
root@worker1:/home/ladicle# lvextend -l +100%FREE /dev/mapper/ubuntu--vg-ubuntu--lv
Size of logical volume ubuntu-vg/ubuntu-lv changed from 4.00 GiB (1024 extents) to <464.26 GiB (118850 extents).
Logical volume ubuntu-vg/ubuntu-lv successfully resized.
# 最後にリサイズしたら完了 (容量を明示的に指定しなければ、自動的にサイズを合わせてくれる)
root@worker1:/home/ladicle# resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv
resize2fs 1.44.1 (24-Mar-2018)
Filesystem at /dev/mapper/ubuntu--vg-ubuntu--lv is mounted on /; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 59
# dfで確認すると増えていることが分かる
root@worker1:/home/ladicle# df -h|grep vg
/dev/mapper/ubuntu--vg-ubuntu--lv 457G 2.6G 436G 1% /
4 Kubernetesの構成検討と構築
せっかくのお家クラスタなので普段使わない構成で組むことにしたが、それでも躓くくことなく構築が完了する。Kubernetesの安定感が実感できた。4
4.1 cri-oのセットアップ
cri-o
を手順に沿ってセットアップする。以下の手順には含まれていないが、k8sから利用するためには /etc/default/kubelet
の KUBELET_EXTRA_ARGSに --cgroup-driver=systemd
フラグを追加する必要がある。
# 必要なモジュールの追加と設定
$ modprobe overlay
$ modprobe br_netfilter
$ cat > /etc/sysctl.d/99-kubernetes-cri.conf <<EOF
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
$ sysctl --system
# CRI-Oと依存パッケージのインストール
$ apt-get update -y
$ apt-get install software-properties-common -y
$ add-apt-repository ppa:projectatomic/ppa
$ apt-get update -y
$ apt-get install cri-o-1.13 -y
# CRI-Oの有効化と起動
systemctl enable crio
systemctl start crio
dockerに慣れすぎて、コンテナ周りで何か困るとしまってついつい docker
コマンドを叩いてしまう。しかし、慣れ以外はランタイムをcri-oに変えた事による悩み事は発生していない。
4.2 kubeadmによるクラスタ構築
4.2.1 事前準備
事前準備は、 イメージレジストリの指定 / Swapの無効化 / kubelet, kubeadm, kubectlのインストール の3つ。イメージレジストリは、 /etc/containers/registries.conf
にイメージをPullするレジストリを指定しないと no registries configured
エラーになる。また、Swapが有効になっていると kubeadm init
でエラーになる。そのため、 swapoff -a
でオフにする必要がある。ツール群のインストール時は、 apt update
したときに勝手にアップデートされると困るのでついでに apt-mark hold
しておくと良いだろう。
4.2.2 Master/Workerの構築
cri-o用のフラグを忘れないようにしつつMasterは kubeadm init
で構築していく。最初にバリデーションが走るので何か問題があれば事前に教えてくれる。また、中途半端に構築されてしまうと面倒なのでdry-runオプションで動作は事前に確認しておくと安心。
Workerのセットアップはmasterセットアップ時にgetしたtokenを使ってセットアップしていく。cri-oの設定も必要なのでcri-socketのオプションをMasterと同様に追加する。
# Masterノードで以下を実行
$ kubeadm init --cri-socket "/var/run/crio/crio.sock"
# Workerノードで、Master構築時に取得したトークンを利用して以下を実行
$ kubeadm join <master>:6443 --token <token> \
--discovery-token-ca-cert-hash <cert> \
--cri-socket "/var/run/crio/crio.sock"
4.3 ciliumのCNIプラグインを適用する
cri-o用のプラグインも提供されているのでこれを適用する。
$ kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.5.0/examples/kubernetes/1.14/cilium-crio.yaml
4.4 MetalLBの構築
MetalLBのマニフェストからControllerとSpeakerがインストールできる。設定はConfigMapで管理できる。構築後に作成しても、Speakerが変更を検出して再読み込みしてくれる。ここでは、BGPルータ無いので、L2な設定を選択。DHCPの範囲外を指定している。
$ kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.7.3/manifests/metallb.yaml
# 全部立ち上がっていたらok
$ kubectl get pods -n metallb-system -w
NAME READY STATUS RESTARTS AGE
controller-cd8657667-hz47m 1/1 Running 0 32s
speaker-f9wb4 1/1 Running 0 32s
speaker-jllwx 1/1 Running 0 32s
speaker-tvl59 1/1 Running 0 32s
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: my-ip-space
protocol: layer2
addresses:
- 10.0.1.200-10.0.1.250
5 CloudflareのArgo経由でSSHする
今までddnsで自宅サーバを直接晒していたが、流石にそろそろ不安なのでCloudflare Argoに移行することにした。このArgoというのは、CloudflareのCDN経由で、トンネルを貼った先にアクセスできるサービス。直接サーバを晒さないで良いので安全 & CDN通せるので早いというメリットがある。金額も月額$5 + $0.10/GBのため個人利用しやすい。トンネルを貼る先はHTTPでもSSHでもよい。HTTPの方はよく記事があるので、ここではSSHの設定をのせていく。
5.1 Server設定
CloudflareのDomain Dashboardで、 Traffic > Argo
を有効にすると、Tunnelも利用できるようになる。利用にはcloudflaredが必要なので、該当バイナリをココからダウンロードする。バイナリのインストールが完了した後、 tunnel login
を実行するとブラウザからどのサービスとtunnelするかを確認できる。認証に成功すると、証明書が保存される。
# cloudflareコマンドのインストール
$ wget https://bin.equinox.io/c/VdrWdbjqyF/cloudflared-stable-linux-amd64.tgz
$ tar -zxvf cloudflared-stable-linux-amd64.tgz
$ sudo mv cloudflared /usr/local/bin
# バージョンを確認する
$ cloudflared --version
cloudflared version 2019.4.1 (built 2019-04-19-2149 UTC)
# 実際にサーバからCloudflareにトンネルが貼れること確認する
$ cloudflared tunnel login
Please open the following URL and log in with your Cloudflare acchostname: ssh.example.com
url: ssh://localhost:22ount:hostname: ssh.example.com
url: ssh://localhost:22
https://dash.cloudflare.com/argotunnel?callback=<loginurl>
Leave cloudflared running to download the cert automatically.
You have successfully logged in.
If you wish to copy your credentials to a server, they have been saved to:
~/.cloudflared/cert.pem
常に手動でトンネルを貼ることはできないので、Systemdで起動できるようにServiceを作成する。cloudflareコマンドにはサービスを作成するための service install
コマンドがあるのでこれを使っていく。このコマンドは、実行ディレクトりにある conf.yaml
を /etc/cloudflared
にコピーし、この設定ファイルを読み込むService作成する。
$ cat <<EOF > conf.yaml
hostname: ssh.example.com
url: ssh://localhost:22
EOF
$ sudo cloudflared service install
5.2 Client設定
Tunnel張るために必要なので、Server側と同じように cloudflared
コマンドをインストールする。コマンド直打ちでも問題ないが、毎回プロキシ設定を入力するのは面倒なので ~/.ssh/config
に以下のような設定を追加する。
Host home-k8s
User ladicle
HostName ssh.example.com
ProxyCommand cloudflared access ssh --hostname %h
5.3 Cloudflare Accessでアクセスユーザを制限する
Cloudflare Accessを使うとアクセス制限を設定することができる。5ユーザまでは$3/monthなのでご家庭用には3ドルで十分。色々な設定ができるが、今回は先ほどのGifアニメーションの通りで、SSHの前段にGitHub認証を挟んだ。設定はGUIからAccessPolicyをぽちぽちするだけ。注意点としては、サブドメインにWildcardは使えないことくらい。
また、一人ずつアカウント設定するのは面倒だったため、家庭用GitHub Organizationを作成してそのOrganizationに対してアクセスを許可するようにした。GitHub側ではAuthAppを作成してCloudflareを設定する形になる。
6 kubeadmによるクラスタアップグレード
4月に構築したときはk8sの最新バージョンがv1.14だったが、8月5日にはv1.15.2もリリースされた。Kubernetesのドキュメントには親切にもアップデート方法も書かれているのでこの通りに進めていく。kubeadmによるk8s本体のアップグレードは今回特にハマらなかった。どちらかというと上にのっているリソースを確認してバージョンに合わせて修正する方に時間がかかる。
# バージョン固定しているので一時的に解除する
$ sudo apt update -y
$ sudo apt-cache policy kubeadm
$ sudo apt-mark unhold kubeadm && \
apt-get update && apt-get install -y kubeadm=1.15.2-00 && \
apt-mark hold kubeadm
# upgrade planコマンドでサポートバージョンを確認した後、適用する
$ sudo kubeadm upgrade plan
$ sudo kubeadm upgrade apply v1.15.2
# Masterは1台構成なのでupgrade nodeする(HAのばあい2台目以降はapply)
$ sudo kubeadm upgrade node
# kubeletを更新する
$ sudo apt-mark unhold kubelet kubectl && \
sudo apt-get update && sudo apt-get install -y kubelet=1.15.2-00 kubectl=1.15.2-00 && \
sudo apt-mark hold kubelet kubectl && sudo systemctl restart kubelet
$ sudo systemctl restart kubelet
# 一台ずつdrainして上のアプリケーションを別のノードに退避させる
$ kubectl drain worker1 --ignore-daemonsets
# 同じようにkubeadmを更新する
$ sudo apt update -y
$ sudo apt-cache policy kubeadm
$ sudo apt-mark unhold kubeadm && \
apt-get update && apt-get install -y kubeadm=1.15.2-00 && \
apt-mark hold kubeadm
# 同じようにkubeletを更新する
$ sudo apt-mark unhold kubelet kubectl && \
sudo apt-get update && sudo apt-get install -y kubelet=1.15.2-00 kubectl=1.15.2-00 && \
sudo apt-mark hold kubelet kubectl && sudo systemctl restart kubelte
$ sudo systemctl restart kubelet
# メンテモードを終了する
$ kubectl uncordon worker1
クラスタ上にのっているリソースはChangeLogに合わせて事前に修正しておく
6.1 CNIプラグインのアップグレード
Ciliumの公式サイトにはアップグレードガイドがあり、アップグレード前にすべきことも書かれている。が、読み落として壊しかけてしまった。今回は設定ファイルに変更があるため、事前にConfigMapの修正が必要だった。
.