Contents

Kubernetes on Rapsberry Pi Part 3: クラスタ構築 (Kubernetes 1.23対応 + HA対応)

いよいよk8sクラスタ構築

前回の記事から随分と時間がたってしまいました・・・もはやブログと呼べなくなっている気がするので、今後はちょこちょこと小さめの記事をもう少し高頻度で投稿していきたいです。

ということで前回マシンの初期設定が完了したので、ここからk8sスペシフィックな設定を行っていきます。

ちなみにインストールにはkubeadmを利用します。
各コンポネントを一からインストールする方法もありますが、とりあえずラズパイ上にk8sをデプロイして色々動かしてみたかったので、手早くクラスタ環境を構築できるkubeadmを採用しました。

Kubernetesの構造をちゃんと理解するために一から構築してみたい!という方はKubernetes The Hard Wayが参考になると思います。(そのうち試したい)

またkubeadmの他にもkubesprayRancherなどのデプロイメントツールがあるので、気になる方は調べてみてください。

kubeadmインストール準備

ここからの手順は、指定のない限りすべてのホスト(HA構成の場合はロードバランサ以外)で行ってください。
(tmuxだったり自動化ツールだったりは適宜利用してください。)

公式導入手順日本語ページ)に詳しく書いてありますが、kubeadmインストールのために以下の条件を満たす必要があります。

この手順に限らず英語ページと日本語ページで微妙に内容の異なっていることがあるので、なにか調べるときはどちらも読んでみるといいかもしれません。

  1. Debian系またはRHEL系のLinuxマシン(日本語ページに詳しく載ってます)
  2. 2GB以上のメモリ/台
  3. 2コア以上のCPU
  4. ノード間通信可能なネットワーク(プライベートでもパブリックでも)
  5. ノードごとに固有のhostname、MACアドレス、product_uuid
  6. 特定のポート開放
  7. swapはオフ

このうち1~5,7についてはラズパイ4の4GB(2GBでも)以上かつPart2の通りにOSインストールを行っていればクリアできているはずです。ネットワークに関してもPart2で同じサブネットにしていれば大丈夫です。

一応swapがオフになっているかはswapon --show等のコマンドで確認できます。何も出てこなければ大丈夫です。

ちなみにコミュニティではswapをサポートする話も出ているようです。

UFW(Uncomplicated FireWall)のはなし

6に関して、今回はUbuntuを利用しているので、ファイアウォールの設定にはufwを利用しましょう。

ufwはUbuntuに標準搭載されているソフトウェアファイアウォールです。Uncomplicated Firewallという名の通り、お手軽にファイアウォールを設定できる便利ツールとなってます。RHEL系OSになじみのある方はfirewalld(firewall-cmd)のUbuntuバージョンだと思っていただければ大丈夫です。

ファイアウォールの設定といえばiptablesがありますが、設定の覚えられなさ複雑さが難点です。 その点ufwfirewall-cmdの半分はやさしさでできており、単純な設定なら簡単に行うことができます。

ufwfirewall-cmdはあくまでiptables(もしくはnftables)のフロントエンドであり、内部的にはそれら経由でカーネルのパケットフィルタであるnetfilterを呼んでいます。

だいぶ主旨からそれてしまいましたが、firewalldのサイトこのQ&Aに分かりやすい説明や図があるので興味があれば見てみてください。

設定

ということで設定を行っていきましょう。ufwはデフォルトで無効となっていると思います。

公式にもありますが、クラスタを組むうえで今回開放すべきポートは以下になります。そのほか必要に応じて開いてください。

ノード ポート 目的
Control plane (master) 6443/TCP Kubernetes API server
Control plane 2379-2380/TCP etcd server client API
Control plane 10250/TCP Kubelet API
Control plane 10259/TCP kube-scheduler
Control plane 10257/TCP kube-controller-manager
Worker 10250/TCP Kubelet API
Worker 30000-32767/TCP NodePort Services

私の場合クラスタ外部からsshで繫いでいるので、22も開けます。現実的にリモートでの操作が楽なのでとりあえず開けておくのが無難だと思います。Ubuntu ServerであればOpenSSHはデフォルトでサービスとして登録されているので、ポートではなくサービス指定で許可をすることもできます。

1
sudo ufw app list
1
2
3
4
5
sudo ufw allow 22/tcp または sudo ufw allow OpenSSH
sudo ufw allow 6443/tcp
sudo ufw allow ...  

sudo ufw enable 

ちなみにufwはデフォルトですべてのインバウンド通信を拒否するので、sshの許可を忘れたままufw enableしてしまうと面倒なことになるかもしれません。

コンテナランタイムのはなし

ここからいよいよKubernetesっぽい話をしていきます。

Kubernetesというのはご存じの通りコンテナオーケストレーションシステムと言われる、コンテナのデプロイだったり管理だったりを行ってくれるツールです。つまりコンテナを操作します。そしてコンテナを操作するのに必要なのがコンテナランタイムです。

コンテナランタイムはハイレベルランタイムとローレベルランタイムに区別できます。ここでは詳しく書きませんが

  • ハイレベルランタイム:CRI(Container Runtime Interface)というインターフェース通してDockerやKubernetesから受け取った指示を、ローレベルランタイムに伝える中継役的なやつ
  • ローレベルランタイム:OCIという標準に則った、ハイレベルランタイムからの指示に従いoverlayfs、namespaces、cgroupsといったカーネル機能を利用することでコンテナの作成、操作を行ってくれるやつ

といった感じです。他にもいくつかあると思います。

今回はハイレベルランタイムにcri-o、ローレベルランタイムにruncを利用します。

CRI-O導入

現在(1.23)、Kubernetesに利用できるハイレベルランタイムはdockerd(Docker Engine)containerdcri-oの三種類になります。 dockerdなんて上の表にないじゃんと思われた方はこの記事内のKubernetesがDockerサポートを終了した件についてを読んでいただくと幸せになれるかもしれません。

dockerdは後述の理由でそもそも採用しないと決めていました。なのでcontainerdcri-oのどちらを使うかで少し悩みましたが、結局「軽量」という言葉に惹かれcri-oを採用しました。(Ubuntuを使いながらRedHat主導のプロジェクトを採用とはいかに)

  • 導入手順
  1. 必要なコマンドのインストール
    これからの作業に必要なコマンドを一気にインストールしておきましょう。
    デフォルトでほとんど入っていると思いますが念のため・・

    1
    
    sudo apt install -y ca-certificates curl gnupg lsb-release apt-transport-https ca-certificates
    
  2. カーネルモジュールのロード
    overlaybr_netfilterと呼ばれるカーネルモジュールのロード及び起動時の自動ロード設定を行います。 overlayはOverlayFSと呼ばれるコンテナ周りに不可欠なファイルシステムモジュールで、br_netfilter は異なるホストのPod間通信に必要なモジュールです。

    1
    2
    3
    4
    5
    6
    7
    
    cat <<EOF | sudo tee /etc/modules-load.d/crio.conf
    overlay
    br_netfilter
    EOF
    
    sudo modprobe overlay
    sudo modprobe br_netfilter
    
  3. カーネルパラメータの設定
    次に必要なカーネルパラメータを設定、永続化します。
    この設定により、Kubernetesからiptablesでのブリッジのトラフィック制御が可能になります。

    1
    2
    3
    4
    5
    6
    7
    
    cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
    net.bridge.bridge-nf-call-iptables  = 1
    net.ipv4.ip_forward                 = 1
    net.bridge.bridge-nf-call-ip6tables = 1
    EOF
    
    sudo sysctl --system
    
  4. 環境変数$OS$VERSIONの設定
    cri-oのインストールのため、2つの環境変数を設定する必要があります。 設定方法は特に指定しませんが、例えば

    1
    
    echo -e "export OS=xUbuntu_20.04\nexport VERSION=1.23" | sudo tee /etc/profile.d/crio.sh
    

    といった感じで環境変数を設定するスクリプトを作成し、

    1
    
    . /etc/profile
    

    で反映させます。

  5. コンテナランタイムのインストール
    cri-oおよびcri-o推奨のruncをインストールします。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    sudo echo "deb [signed-by=/usr/share/keyrings/libcontainers-archive-keyring.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/ /" > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
    sudo echo "deb [signed-by=/usr/share/keyrings/libcontainers-crio-archive-keyring.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$VERSION/$OS/ /" > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:$VERSION.list
    
    sudo mkdir -p /usr/share/keyrings
    sudo curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/Release.key | gpg --dearmor -o /usr/share/keyrings/libcontainers-archive-keyring.gpg
    sudo curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$VERSION/$OS/Release.key | gpg --dearmor -o /usr/share/keyrings/libcontainers-crio-archive-keyring.gpg
    
    sudo apt-get update
    sudo apt-get install cri-o cri-o-runc
    

    ちなみにもし推奨されたrunc(cri-o-runc)ではなくUbuntu標準のruncを利用したい場合、

    1
    2
    3
    4
    5
    6
    
    cat <<EOF | sudo tee /etc/crio/crio.conf.d/oci.conf
    [crio.runtime.runtimes.runc]
    runtime_path = ""
    runtime_type = "oci"
    runtime_root = "/run/runc"
    EOF
    

    といった感じでデフォルトのruncを利用するためのコンフィグを作成してください。(この場合cri-o-runcのインストールは不要です。)

  6. cri-oの自動起動設定
    daemon-reloadは必要ないと思いますが一応

    1
    2
    3
    
    sudo systemctl daemon-reload
    sudo systemctl enable crio --now
    sudo systemctl status crio
    

KubernetesがDockerサポートを終了した件について

ここ最近、KubernetesがDockerのサポートを打ち切ったという話を耳にしたことがある思います。確かに間違ってはいないのですが、Dockerで作ったコンテナがk8sで管理できなくなるなんて話ではありません。これには上のほうで説明したコンテナランタイムが関わってきます。

もともとコンテナの作成といえばDocker Engine (dockerd)でした。そしてこのDocker Engineに内包されるコンテナランタイムこそ現在のcontainerdなのです。kubernetesもDocker Engine内のcontainerdを利用しポッドの作成を行っていました。

しかしコンテナランタイムであるcontainerdはあくまでDocker Engineのコンポネントの一つにすぎません。(とはいえ核部分なのは間違いありません)それに加えDocker Engine自体はCRIに対応していないという問題がありました。

なのでKubernetesはdockershimというモジュールを実装し、CRIをDocker APIに変換することで、Docker内部のコンテナランタイムを利用していました。コンテナランタイムのはなしにあるリストにdockerdを含まなかったのは、厳密にはdockerd自体がコンテナランタイムではないからです。

2017年頃、containerdが単体でCloud Native Computing Foundation(CNCF)に寄贈されました。そしてこのcontainerdはCRIに対応しています。つまり、KubernetesはDockershimを経由せずとも、コンテナランタイムであるcontainerdを利用することが可能になったわけです。

dockerdを利用する場合、Kubernetesはdockershimを利用しDocker内部のcontainerdを呼ぶので、CRIに対応したランタイムを利用するのに比べオーバーヘッドが発生します。それに加え、dockershimをサポートし続けるのはコミュニティにとって大変です。(公式でもa heavy burdenと表現されるほど

そんなこんなでKubernetesコミュニティは今後dockershimをサポートしないことを決めました。具体的には今回利用している1.23の次バージョンである1.24から、dockerdが利用できなくなります。

厳密にはMirantisというところがdockershimのサポートを引き継ぐ事になったので、彼らの提供するアダプタを利用することで引き続きdockerdの利用が可能です。ただKubernetes自体がdockerdをサポートすることはなくなります。

この辺りの話について更に詳しく知りたい方は、以下の記事を参考にしてください。

DockerからContainerdやCRI-Oへの移行

新規クラスタ作成者にはあまり関係ありませんが、CRIの移行を考えている方に役立ちそうなリンクを貼っておきます。

kubeadm,kubelet,kubectlのインストール

クラスタ作成にあまり関係のない話が続いてしまいましたが、いよいよkubeadmおよびKubernetesコンポネントをインストールする準備が整いました・・・!

早速導入していきましょう。

  • 手順
  1. Google Cloud public signing keyのダウンロード

    1
    
    sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
    
  2. Kubernetesリポジトリの追加

    1
    
    echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
    
  3. kubeadm,kubelet,kubectlのインストール
    kubeadm自体がkubeletkubectlのインストールおよび管理を行ってくれるわけではないので、個別にインストールする必要があります。この際、各コンポネントのバージョン(今回の場合1.23)を一致させることに注意してください。

    1
    2
    
    sudo apt update
    sudo apt install -y kubelet kubeadm kubectl
    
  4. バージョンの固定
    最後に各コンポネントのバージョンを固定しましょう。これで勝手にアップデートされたりバージョンがちぐはぐになってしまうことを防げます。ついでにcrio-oも固定しておきましょう。

    1
    
    sudo apt-mark hold kubelet kubeadm kubectl cri-o cri-o-runc
    

Q. kubeletってマスターノードに必要なの?
A. kubeadmを利用する場合は必須です。
kubeadmはコンテナ(ポッド)を使いkubelet経由でetcdAPI serverをデプロイするので、マスターにもkubeletが必要です。他のクラスタ作成ツールでは必要のない場合もあります。
(参考)Why kubelet is running on kubernetes master node?

Q. kubectlってワーカーノードに必要なの?
A. 必須ではありませんが、クラスタのどのノードからも操作できるとなにかと便利です。
(参考)is kubectl required on the worker nodes ?

(Option)HA用の設定

ここでの作業は2台のロードバランサ用マシン(前回の記事を参照)でのみ行います。 なのでシングルマスターで組む方は読み飛ばしてください。

当初はHAクラスタについて次回の記事で書こうと思っていたのですが、設定する場合kubeadm initによるデプロイより前に行わなければいけないのと、そもそもデプロイ後のHA化は推奨されていないようなので、まとめて書くことにしました。

Part 1でも軽く触れましたが、Control Planeを冗長化は大きく分けて2パターンあり、違いはetcdを個別ノードで冗長化させるかどうかです。また、どちらの方法でもAPI serverへのロードバランサを導入する必要があります。

  1. 内部etcd構成 ../part1/kubeadm-ha-topology-stacked-etcd.png

  2. 外部etcd構成 /posts/raspberry-pi-k8s-cluster/part3/kubeadm-ha-topology-external-etcd.png

今回は前者のetcdをControl Planeの一部として冗長化するアーキテクチャを採用しています。

ということで、今回のHA化ではロードバランサにKeepalivedHAProxyを利用します。パブリッククラウド上で構築される方は、サービスとして提供されているロードバランサ(AWSのELBだったり)を使ってもいいと思います。

それでは早速インストールしていきましょう。

1
sudo apt install -y keepalived haproxy

これでインストールは完了したので、個々の設定を行っていきます。

KeepalivedとHAProxyの設定はどちらからでもいいのですが、HAProxyへの設定を反映した時点から1つめのコントロールプレーンをデプロイするまでの間エラー出るので、反映はデプロイ直前にするのが無難だと思います。

Keepalivedの設定

ルータやロードバランサの冗長化を行うためのソフトウェアです。

今回のような構成の場合、上の図のようにロードバランサが一つではそこがSPOFとなってしまうため、複数のHAProxyを用意しKeepalivedと組み合わせることで、エンドポイントの冗長化を図ります。

VRRP(Virtual Router Redundancy Protocol)というプロトコルにより仮想IP(VIP)を用いるため、クライアントやワーカーノードはエンドポイントとなるHAProxyが何らかの原因で落ち、別ホストに切り替わった場合でも、物理IPの変化を意識する必要がなくなります。

インストールが完了すると/etc/keepalived/というディレクトリが生成されていると思うので(なければ作りましょう)、そこにkeepalived.confcheck_apiserver.shというファイルを作成し、それぞれ以下のように編集しましょう。

また、sudo chmod +x check_apiserver.sh等で実行権限の付与も忘れずに行ってください。

/etc/keepalived/keepalived.conf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
! /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
    router_id LVS_DEVEL
}
vrrp_script check_apiserver {
  script "/etc/keepalived/check_apiserver.sh"
  interval 3
  weight -2
  fall 10
  rise 2
}

vrrp_instance VI_1 {
    state ${ステイト}
    interface ${インターフェース}
    virtual_router_id ${ルーターID}
    priority ${優先度}
    authentication {
        auth_type PASS
        auth_pass ${認証用パスワード}
    }
    virtual_ipaddress {
        ${仮想IP}}
    }
    track_script {
        check_apiserver
    }
}
  • ステイト
    一台目にMASTER、二台目(以降)にBACKUPと設定しましょう。仮想IPは最初MASTERに振り分けられます。

  • インターフェース
    利用するNICを指定しましょう。ラズパイなら変更していない限りeth0で大丈夫です。

  • ルーターID
    複数のkeepalivedを同じNICで実行させている時の識別に利用されます。なんでもかまいませんが各ホストで同じ数値にしてください。(51がよく使われるよう?)

  • 優先度
    MASTERを100、BACKUPをそれ以下の数値で設定しましょう。他の数でもかまいませんがMASTERが最大となるよう気を付けてください。

  • 認証用パスワード
    フェイルオーバーの際の同期に利用されるパスワードです。なんでもかまいませんが各ホストで同じ数値にしてください。

  • 仮想IP
    使用したい仮想IPを指定してください。今回の場合ホストやk8sクラスタと同じサブネットで指定します。

/etc/keepalived/check_apiserver.sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#!/bin/sh

errorExit() {
    echo "*** $*" 1>&2
    exit 1
}

curl --silent --max-time 2 --insecure https://localhost:${エンドポイントのポート}/ -o /dev/null || errorExit "Error GET https://localhost:${エンドポイントのポート}/"
if ip addr | grep -q ${APISERVER_VIP}; then
    curl --silent --max-time 2 --insecure https://${仮想IP}:${エンドポイントのポート}/ -o /dev/null || errorExit "Error GET https://${仮想IP}:${エンドポイントのポート}/"
fi
  • 仮想IP
    上記で設定した仮想IPを指定してください。

  • エンドポイントのポート
    APIサーバへのエンドポイントとして利用するポートです。HAProxyはここへのアクセスを各コントロールプレーンに振り分けます。
    k8s APIサーバのデフォルトである6443を使ってもいいですが、私はロードバランサを使っていることを分かりやすくするため別ポートの8443を採用しました。

HAProxyの設定

高性能なソフトウェアリバースプロキシサーバです。 複数のマスター(Control Plane)へのロードバランシングを行ってくれます。

インストールが完了すると/etc/haproxy/haproxy.cfgというファイルが生成されていると思うので(なければ作りましょう)、ファイルを以下のように編集します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# /etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
    log /dev/log local0
    log /dev/log local1 notice
    daemon

#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 1
    timeout http-request    10s
    timeout queue           20s
    timeout connect         5s
    timeout client          20s
    timeout server          20s
    timeout http-keep-alive 10s
    timeout check           10s

#---------------------------------------------------------------------
# apiserver frontend which proxys to the control plane nodes
#---------------------------------------------------------------------
frontend apiserver
    bind *:${APIサーバのエンドポイントとして利用するポート}
    mode tcp
    option tcplog
    default_backend apiserver

#---------------------------------------------------------------------
# round robin balancing for apiserver
#---------------------------------------------------------------------
backend apiserver
    option httpchk GET /healthz
    http-check expect status 200
    mode tcp
    option ssl-hello-chk
    balance     roundrobin
        server k8s-master00 ${Control Plane 1のIP}:6443 check
        server k8s-master01 ${Control Plane 2のIP}:6443 check
        server k8s-master02 ${Control Plane 3のIP}:6443 check
        # [...]
  • エンドポイントのポート
    上記で設定したポート(私の場合8443)を指定してください。

  • Control Plane XのIP
    各コントロールプレーンのIPです。5台,7台とコントロールプレーンを追加していくたびに、ここへ追記・リロードをしてください。

ファイアウォールの設定

ufwで必要なポートの開放を行いましょう。

1
2
3
4
5
sudo ufw allow 22/tcp または sudo ufw allow OpenSSH
sudo ufw allow ${エンドポイントのポート}/tcp comment 'API Server Endpoint'
sudo ufw allow to 224.0.0.18 comment 'VRRP Broadcast'

sudo ufw enable 
  • エンドポイントのポート
    上記で設定したポート(私の場合8443)を指定してください。

KeepalivedとHAProxyの実行、設定の反映

以下のコマンドで設定の反映、実行を行います。 私の環境だとすでに実行されていたためにリロードが必要でした。

1
2
3
4
5
6
実行する場合
sudo systemctl start haproxy --now && sudo systemctl start keepalived --now
sudo systemctl enable haproxy --now && sudo systemctl enable keepalived --now

設定を反映する場合
sudo systemctl reload haproxy --now && sudo systemctl reload keepalived --now

確認

この時点でVRRPがノード間で通っているかを確認しましょう。 簡単な確認方法として、ip aコマンドでMASTERのNICのみに仮想IPが振り分けられているかをチェックします。

(私の場合ファイアウォールの設定を失念しており、仮想IPが両方のホストにふりわけられていました。)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
ip a

...
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 192.168.20.20/24 brd 192.168.3.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet 192.168.20.100/32 scope global eth0
       valid_lft forever preferred_lft forever
...

仮想IPが振り分けられているホストを再起動することで、一時的にバックアップのほうに振り分けられることも確認することができます。

Control Planeのデプロイ

必要コンポネントが全て揃ったので、デプロイしていきましょう!
ここでの手順はControl Plane用のマシン1台でのみ行います。

以下のコマンドで最初のControl Planeをデプロイします。

1
sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --control-plane-endpoint=${IP:Port} --upload-certs

kubeadm initはKubernetesコンポネントをデプロイするためのコマンドです。

  • --pod-network-cidr
    Container Network Interface(CNI)が利用する仮想ネットワークです。Pod間通信で使います。
    10.244.0.0/16は導入予定のFlannelというネットワークプラグインがデフォルトで指定しているネットワークになります。
    他のネットワークを使いたい場合、flannel導入時に利用するマニフェスト(例:Flannel)を書き換える必要があるので注意してください。
    flannel以外のプラグインを利用したい場合、プラグインデフォルトのネットワークを指定、もしくはマニフェストの書き換えを適宜行ってください。

  • --control-plane-endpoint
    IPとポートを指定します。シングルマスターの場合は自らのIPと任意のポート(デフォルトで6443)、HAの場合上記で設定した仮想IPとエンドポイントのポートを指定します。

  • --upload-certs(オプション)
    コントロールプレーンの追加を行う場合、デプロイ時に生成される証明書を追加するコントロールプレーンに共有する必要があります。このパラメータを設定することにより、2台目以降のコントロールプレーンへの手動での証明書受け渡しが不要になります。シングルマスターの場合は不要です。

cgroup問題

ラズパイでデプロイする場合、以下のようにpreflight段階でエラーが発生する場合があります。
(発生しない場合もあるようですが、私の環境では発生しました。)

1
2
3
4
5
...
error execution phase preflight: [preflight] Some fatal errors occurred:
[ERROR SystemVerification]: missing required cgroups: memory
[preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...`
To see the stack trace of this error execute with --v=5 or higher

この場合、/boot/firmware/cmdline.txtに以下の設定を追記し、再起動を行うことで解決します。

1
cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1

参考リンク

デプロイが成功すると、以下のように表示されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
k8s@k8s-master00:~$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --control-plane-endpoint "192.168.3.200:9443" --upload-certs
[init] Using Kubernetes version: v1.23.5

...

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of the control-plane node running the following command on each as root:

  kubeadm join 192.168.20.100:8443 --token xxxxxxxxxxxxxxxxxxxxxxx \
        --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
        --control-plane --certificate-key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.20.100:8443 --token xxxxxxxxxxxxxxxxxxxxxxx \
        --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

ここには

  1. kubectlを一般ユーザが利用する方法
  2. ネットワークプラグインの設定方法
  3. コントロールプレーンの追加方法
  4. ワーカーの追加方法

が記されています。この後の作業のために、以下のコマンドでkubectlを一般ユーザから使えるようにしておきましょう。

1
2
3
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

kubectl get pods --all-namespaces等でkubectlを使えることが確認出来たら、ネットワークプラグインを導入していきましょう。

Flannelの導入

CNI準拠のネットワークプラグインは多々ありますが、その中でおそらく最もシンプルかつ導入の容易なものがFlannelになります。デプロイ時に--pod-network-cidr=10.244.0.0/16と指定しておけば、以下のコマンドから用意されているマニフェストをそのまま導入することで、ポッド間通信を実現できます。

1
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

flannel用のポッドが起動しているか確認します。

1
2
3
4
5
6
7
kubectl get pods --all-namespaces

NAMESPACE     NAME                                   READY   STATUS    RESTARTS   AGE
......
kube-system   kube-flannel-ds-asge7                  1/1     Running   0          28s
kube-system   kube-proxy-wo3r4                       1/1     Running   0          8m45s
kube-system   kube-scheduler-k8s-master00            1/1     Running   0          8m52s

こんな感じでkube-flannel-xx-xxxxxというポッドのステータスがRunningとなっていれば成功です。 今後はノードを追加していくたびに自動でポッドが生成され、ネットワークの設定が行われるため安心ください。

それではいよいよノードを追加していきましょう。

ノードの追加

ここからノードの追加を行いますが、基本的にはkubeadm init時に表示されたコマンドをたたくだけです。まずはコントロールプレーンから追加していきましょう。

kubeadm init成功時に各環境で表示されたコマンドを、追加したいホスト上でroot権限にて実行してください。

1
2
3
sudo kubeadm join 192.168.20.100:8443 --token xxxxxxxxxxxxxxxxxxxxxxx \
        --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
        --control-plane --certificate-key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

kubeadm init時にも表示されますが、--upload-certsオプションによる証明書の自動共有機能は2時間で消失するのでご注意ください。(厳密には、一時的にSecretにアップロードされた証明書が2時間で無効になります。)

後日ノードの追加などで再アップロードが必要な場合、以下のコマンドを1台目のコントロールプレーンで実行してください。

1
kubeadm init phase upload-certs --upload-certs

それでは次にワーカーノードを追加していきます。コントロールプレーンと同じく、kubeadm init成功時に各環境で表示されたコマンドを、追加したいホスト上でroot権限にて実行してください。

1
2
sudo kubeadm join 192.168.20.100:8443 --token xxxxxxxxxxxxxxxxxxxxxxx \
        --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

最後にkubectl get nodeskubectl get pods --all-namespacesでノードやポッドが正常に起動していることを確認しましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
kubectl get nodes
NAME           STATUS   ROLES                  AGE     VERSION
k8s-master00   Ready    control-plane,master   8m      v1.23.4
k8s-master01   Ready    control-plane,master   5m38s   v1.23.4
k8s-master02   Ready    control-plane,master   4m3s    v1.23.4
k8s-worker00   Ready    <none>                 107s    v1.23.4
k8s-worker01   Ready    <none>                 102s    v1.23.4
k8s-worker02   Ready    <none>                 58s     v1.23.4
k8s-worker03   Ready    <none>                 46s     v1.23.4
k8s-worker04   Ready    <none>                 31s     v1.23.4

これにてKubernetesクラスタの完成です!お疲れさまでした!!!

おまけ:PCからクラスタへアクセス

普段利用しているPCからssh接続せず、直接クラスタ管理をしたくなると思います。

そんなときはそのPCにkubectlをインストールしましょう。Debian系のマシンであれば上記の手順でインストールできます。 その他OSの手順は公式ページを参考にしてください。

kubectlのインストールが完了したら、kubeadm initを行ったノードで以下のコマンドを叩き、出力を手元のマシンの~/.kube/configとして保存しましょう。

1
kubectl config view --raw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 出力結果例
# これを手元のマシンの~/.kube/configとして保存
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: xxxxxxxxxxxxxxxxxxxxxxxxxx
    server: https://192.168.20.100:8443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    client-key-data: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

kubectl get nodes等のコマンドでノードが確認できたら成功です。

ユーザや認証周りについて詳しくは触れませんが、とりあえず管理者として作成したクラスタにアクセスしたいのであれば、この方法がおそらく最も楽だと思います。(そもそもAPIサーバにアクセスできるNW環境かどうかは確認してください。)

そのあたりについて詳しく知りたい方は、さくらインターネットさんの記事がとても参考になると思います。

おわりに

今回はHA化も含めてんこ盛りな内容となりました。ものすごく疲れました。

前回からだいぶ経ってしまいましたが、最新バージョン対応だったり、一部ですが日本語ではあまりまとまってないようなことも書けた気がするので、参考にしていただけると嬉しいです。

ネタがたまっていく一方なので、次回からはもう少し細かいネタで書いていけたらと思います。

k8s上でのアプリケーション関係だったりRustやGoの勉強進捗だったりを書いていくかもしれません。

画像引用元・参考URL

その他参考にしたページのリンクです。

クラスタ構築関連

ランタイム関連

HA関連

ロードバランサ関連