6
2

Kubernetesクラスターを「クラウド↔自宅ネットワーク」間で作る(前編)

Last updated at Posted at 2020-12-08

背景

Kubernetesの勉強用に、クラウド上で2つのVMを作りクラスター(Masterノード、Workerノード)の構築をしていた。が、勉強用でたまにしか使わないのに2つのVMぶんの料金が毎月掛かってもったいないので、Workerノードは家にあるLinuxマシンに作って、Masterノードのクラウドと繋げられるといいなあと思い始めた。
このようなモチベーションから試行錯誤した試みを本稿でまとめる。

実現したい構成

2022/12/20 追記
本記事は「クラスターの2ノードは別々のプライベートネットワークに属する前提」で書いており、かなり七面倒くさいネットワーク設定を行っている。
これはTailscaleというP2PのVPNサービスを使えばより簡単に解決できることを確認した。Tailscaleを用いたクラスター構築方法を以下のQiitaにまとめたため、特別な理由がなければそちらを見ていただくことをお勧めする。

下図が実現したい構成。
クラウド上のVMと自宅のLinuxマシンは公開IPでインターネットを経由しないとつながらないので、ここをどのように実現するかが一番のポイントとなる。

network1.png

k8sクラスターを構築するマシン、ネットワークの情報を念のため記載

  • クラウドサービス側
    • Masterノードにしたいマシン
      • ホスト名 : k8s-master
      • OS : Ubuntu 18.04
      • 公開IP : 20.30.40.50/24
      • プライベートIP : 10.0.0.4/24
  • 自宅ネットワーク側
    • ルーター(HG8045Q)

      • OS : 不明(Linux系?)
      • 公開IP : 60.70.80.90/24
      • プライベートIP : 192.168.1.1/24 (今後は登場しないがご参考まで)
    • Workerノードにしたいマシン

      • ホスト名 : k8s-worker
      • OS : Debian 9
      • 公開IP : なし(ルーター経由でしかインターネット接続できない)
        • ここに対処するための工夫が後々必要となる
      • プライベートIP : 192.168.1.4/24

Kubernetesのクラスター構築

今回は最も一般的な(?)kubeadmを使って構築する。基本的な手順は以下サイトに基づくが、幾つかカスタマイズが必要なので後ほど説明する。

(1) セキュリティ設定(ポート解放)

Tailscaleなどで構築したプライベートネットワークが使える場合は不要の手順

双方で、インターネットに対して以下のポートを開放する必要あり

23/03/03 追記
以下に示すポートは筆者の試行錯誤で記載したもの。 Kubernetes利用時に使うポートはkubernetes.ioの必須ポートの確認に記載されているので、素直にこちらの内容に従うほうが間違いないはず。

  • Masterノード(クラウド側)
    • 以下のポート(6443だけだが…)を、各クラウドサービスのセキュリティ設定で通信許可するようにする
    • 尚、kube-apiserverのポート番号6443はデフォルト値で別の値にカスタマイズも可能
通信許可するIP プロトコル ポート番号 通信の方向 用途
60.70.80.90
(自宅LANの公開IP)
TCP 6443 Inbound Workerノードがkube-apiserverを介して通信するのに利用
  • Workerノード(自宅LAN側)
    • 以下のポート(10250だけだが…)を、自宅ルーターのWeb管理ツールなどを使って通信許可するようにする。(他のルーターでもおそらく同じように設定できるはず?)
    • 筆者宅のルーターはHG8045Qを使っており、こちらのサイトなどを参考にするとインターネットからの通信を受け入れ可能にできる
    • 注意すべきは、実際にポート番号10250でサービス提供するのは「公開IPをもつルーターではなく、自宅LAN内にあるマシン」だということ。つまり、「インターネットから20.30.40.50:10250で受けたリクエスト」を「WorkerノードにするマシンのIP:ポート(=192.168.1.4:10250)」に転送する設定が必要となる。
通信許可するIP プロトコル ポート番号 通信の方向 用途
20.30.40.50
(クラウドサーバの公開IP)
TCP 10250 Inbound MasterノードがWorkerノード内のPodにアクセスする際に必要

(2) kubeletサービス起動時のノードIP指定

kubeadmを使ってクラスター構築する前に、各ノードがkubeadm initkubeadm joinコマンドでKubernetesクラスターに仲間入りする際に使うIPアドレス(ノードのIP)を明示的に指定する。ノードIPが他のノードから認識できないIPに設定されてしまうとクラスターが正常に構築できなくなってしまうので注意が必要である。
適切でない例として、例えば2つのノードが公開IPを介してしかアクセスできないのに、ノードIPがプライベートIPで設定されてしまう等。逆にプライベートネットワークでつながれるのに、公開IPがノードIPに設定されてしまうのも(一概には言えないが)よろしくないかも。

具体的には各マシンに/etc/default/kubeletを作成して、以下のように設定

Tailscaleなどで構築したプライベートネットワークが使える場合はそのIPを記載すればいいはず

  • Masterノード(クラウド側)
    • --node-ipにクラウドサーバの公開IPを設定する
/etc/default/kubelet
KUBELET_EXTRA_ARGS=--node-ip=20.30.40.50
  • Workerノード(自宅LAN側)
    • --node-ipに自宅LAN内のプライベートIPを指定する
/etc/default/kubelet
KUBELET_EXTRA_ARGS=--node-ip=192.168.1.4

Workerノード側のIPは、先ほど説明した内容と矛盾した値になっている。「公開IP経由でノードがつながるのだから、自宅LANルーターの公開IP(60.70.80.90)をWorkerの--node-ipに設定するべきではないか?」と思われただろう。ところが公開IPを指定しても、kubeadm joinコマンドでクラスターにWorkerノード追加後に、kube-apiserverでWorkerノードの公開IPが反映されない。具体的には、以下のようにWorkerノードのINTERNAL-IPが<none>となってしまう。

user@k8s-master$ kubectl get node -o wide
NAME         STATUS   ROLES    AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE ...
k8s-master   Ready    master   8d    v1.18.8   20.30.40.50   <none>        Ubuntu 18.04.3 LTS             ...
k8s-worker   Ready    <none>   23h   v1.18.8   <none>        <none>        Debian GNU/Linux 9 (stretch)   ...

推測だが、--node-ipに指定して有効となるIPは、そのマシンに各ネットワークインターフェースで付与されているIPのみの模様。例えば筆者のWorkerノードとなる自宅LAN内のマシンの付与IPを調べると、有効なIPは以下のように192.168.1.4172.17.0.1であり、自宅LANのルーターに付与されている公開IPは出てこない。

user@k8s-worker$ ip a | egrep -e ^[1-9] -e "inet "
...
4: wlp2s0: ...
    inet 192.168.1.4/24 brd 192.168.1.255 scope global dynamic wlp2s0
5: docker0: ...
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0

そこで既に述べたように、自宅LAN内のマシンでの設定ファイル/etc/default/kubeletには--node-ip=192.168.1.4を指定する。

これらの設定を行った後、Masterノードとなるクラウドサーバではkubeadm init、Workerノードとなる自宅LAN内のマシンではkubeadm joinで始まるコマンドを実行する。

  • クラウドサーバでのクラスター追加&Masterノード生成コマンド
user@k8s-master$ sudo kubeadm init \
  --pod-network-cidr=10.128.0.0/16 \        # Pod内ネットワーク範囲(好きなように設定可能)
  --apiserver-advertise-address=20.30.40.50 # クラウドサーバの公開IP
  • 自宅LAN内のマシンでのWorkerノード追加コマンド
    • tokenとhashはkubeadm initで最後に出力されるものをコピペすればOK
user@k8s-worker$ sudo kubeadm join 20.30.40.50:6443 --token xxxxxxxx... \
    --discovery-token-ca-cert-hash sha256:yyyyyyyy...

ここまでやると、2つのマシンでKubernetesクラスターが一応、出来上がる。

user@k8s-master$ kubectl get node -o wide
NAME         STATUS   ROLES    AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE ...
k8s-master   Ready    master   8d    v1.18.8   20.30.40.50   <none>        Ubuntu 18.04.3 LTS             ...
k8s-worker   Ready    <none>   23h   v1.18.8   192.168.1.4   <none>        Debian GNU/Linux 9 (stretch)   ...

だがまだ問題が残っている。Kubernetesクラスターは「WorkerノードのIPアドレスは192.168.1.4だ」と思っており、Workerノード内のPodにkubectl execコマンド等でアクセスする際に、このIPを使おうとしてしまう。192.168.1.4はWorkerノードの自宅LAN内でしか使えないアドレスなので、当然アクセスできない。

クラウドサーバ(Masterノード)のNAT設定

Tailscaleなどで構築したプライベートネットワークが使える場合は不要

MasterノードからWorkerノードにアクセス可能にするために、宛先IP192.168.1.4へのリクエストをWorkerノードの自宅LANの公開IP60.70.80.90に変換する。これをNAT(Network Address Transform)という。(実際は自宅LAN内でまた192.168.1.4に変換されるので、あまりスマートなやり方ではないのだが…)
NATを実現する方法はいくつかあるが、ここではiptablesコマンドによる設定を以下に紹介する。

user@k8s-master$ sudo iptables -t nat -A OUTPUT \
                               -p tcp -d 192.168.1.4 -j DNAT \
                               --to-destination 60.70.80.90

ちなみに、上記の設定を消したい場合は-Aの部分を -D に変換してコマンド実行すればOK。(24/06/04 間違って -Xと書いていたので修正)

NATが上手くできて、Workerノード内のPodにアクセスできるかどうかを確認する。まずは、kube-systemのNamespaceにできているPodを確認。


user@k8s-master$ kubectl get pod -n kube-system -o wide
NAME                                   READY   STATUS    RESTARTS   AGE     IP             NODE         NOMINATED NODE   READINESS GATES
coredns-66bff467f8-djn4w               1/1     Running   0          9d      10.128.117.2   k8s-master   <none>           <none>
coredns-66bff467f8-gf842               1/1     Running   0          9d      10.128.117.3   k8s-master   <none>           <none>
etcd-k8s-master                        1/1     Running   0          9d      20.30.40.50    k8s-master   <none>           <none>
kube-apiserver-k8s-master              1/1     Running   0          9d      20.30.40.50    k8s-master   <none>           <none>
kube-controller-manager-k8s-master     1/1     Running   5          9d      20.30.40.50    k8s-master   <none>           <none>
kube-proxy-p6bwq                       1/1     Running   1          2d      192.168.1.4    k8s-worker   <none>           <none>
kube-proxy-xdtzc                       1/1     Running   0          9d      20.30.40.50    k8s-master   <none>           <none>
kube-scheduler-k8s-master              1/1     Running   5          9d      20.30.40.50    k8s-master   <none>           <none>

確認すると、kube-proxy-p6bwqはWorkerノードに出来ていることがわかる。上手く行っていれば、以下のようにWorkerノードのPod(コンテナ)にアクセス可能。

user@k8s-master$ kubectl exec kube-proxy-p6bwq -n kube-system -it -- /bin/sh
# date
Tue Dec  8 14:06:51 UTC 2020
# 

残る課題

(くどいですが)Tailscaleなどで構築したプライベートネットワークが使える場合は課題にはならない

実はもう一つ課題が残っている。現状だとKubernetesのPod同士でノードをまたいだ通信ができない。その方法については後編に記載。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2