なになれ

IT系のことを記録していきます

Raspberry Piを使ってKubernetesクラスタを構築した

Raspberry Piを使って自宅環境でKubernetesクラスタを構築しました。
いわゆる、おうちKubernetesをやってみたかったのでやりました。
その内容を紹介します。

物理構築

自宅で未使用だったRaspberry Pi 3と買い足したRaspberry Pi 4を使用しました。

物品一覧

注意
Raspberry Piの電源にモバイルバッテリーを使用しています。
ただRaspberry Piで推奨されている電源のスペックはRaspberry Pi 3では2.5A、Raspberry Pi 4では3.0Aとなっています。
今回使用しているモバイルバッテリーはこのスペックを満たしていません。
今のところ、問題なく起動していますが、使用は自己責任でお願いします。

配置について

1段目から3段目までにRaspberry Piを配置し、4段目にスイッチングハブを配置しました。
バッテリーは横に置くようにしました。
f:id:hi1280:20200809160536j:plain:w300

Raspberry Piのセットアップ

OSのインストール

microSDカードにRaspbian OSをインストールします。
SDカードリーダーが必要になります。
公式で提供されているRaspberry Pi Imagerを利用してRaspbianをインストールします。

Setting up your Raspberry Pi - Set up your SD card | Raspberry Pi Projects

Raspberry Piの設定変更

それぞれのRaspberry Piに対して下記手順を行います。

Raspberry Piの起動

OSの設定変更

SSHの設定

これ以降はRaspberry Piスイッチングハブ経由でルータに接続し、操作用PCからSSHで操作します。

  • SSHの設定変更
    • 操作用PCからSSHRaspberry Piに接続する
      • 初期設定時はuser = pi、password = raspberryになっています
    • SSHの公開鍵認証を設定する
      • 操作用PCのSSH公開鍵をRaspberry Pi~/.ssh/authorized_keysに追記する
    • SSHのパスワード認証を無効化する
      • /etc/ssh/sshd_configPasswordAuthentication noを記載する

Kubernetesクラスタのセットアップ

Ansibleでセットアップしました。
Ansibleのファイルは下記にあります。

github.com

Ansibleで使用するhostsファイルの対象IPアドレスなどを設定内容に合わせて適宜変更します。
その後、以下のコマンドを実行すればKubernetesクラスタを構築できます。

$ ansible-playbook -i hosts control-plane-playbook.yml
$ ansible-playbook -i hosts node-playbook.yml

playbookの内容解説

Control Planeのセットアップ

Dockerをインストールします。

control-plane-playbook.yml

...
    // リポジトリの追加
    - name: Add an apt signing key for Docker
      apt_key:
        url: https://download.docker.com/linux/raspbian/gpg
        state: present

    - name: Add apt repository for stable version
      apt_repository:
        repo: deb [arch=armhf] https://download.docker.com/linux/raspbian {{ ansible_distribution_release }} stable
        state: present

    // インストール
    - name: Install docker and its dependecies
      apt:
        name: "{{ packages }}"
        state: present
        update_cache: yes
      vars:
        packages:
        - docker-ce
        - docker-ce-cli
        - containerd.io
      notify:
        - docker status
...

リポジトリの追加では、raspbian用のURLの指定が必要です。

kubeletを起動可能にするため、スワップを無効にします。

control-plane-playbook.yml

...
    - name: Remove swapfile from /etc/fstab
      mount:
        name: "{{ item }}"
        fstype: swap
        state: absent
      with_items:
        - swap
        - none

    - name: Disable swap
      command: swapoff -a
      when: ansible_swaptotal_mb > 0
...

Kubernetesをkubeadmでセットアップします。

control-plane-playbook.yml

...
    - name: Add an apt signing key for Kubernetes
      apt_key:
        url: https://packages.cloud.google.com/apt/doc/apt-key.gpg
        state: present

    - name: Adding apt repository for Kubernetes
      apt_repository:
        repo: deb https://apt.kubernetes.io/ kubernetes-xenial main
        state: present
        filename: kubernetes.list

    - name: Install Kubernetes binaries
      apt:
        name: "{{ packages }}"
        state: present
      vars:
        packages:
          - kubelet
          - kubeadm
          - kubectl

    - name: Configure node ip
      lineinfile:
        create: yes
        path: /etc/default/kubelet
        line: KUBELET_EXTRA_ARGS=--node-ip={{ ansible_host }}

    - name: Check if kubeadm has already run
      stat:
        path: "/etc/kubernetes/pki/ca.key"
      register: kubeadm_ca

    - name: Initialize the Kubernetes cluster using kubeadm
      command: kubeadm init --apiserver-advertise-address="{{ ansible_host }}" --apiserver-cert-extra-sans="{{ ansible_host }}"  --node-name k8s-control-plane --pod-network-cidr=10.244.0.0/16
      when: not kubeadm_ca.stat.exists

    - name: Restart kubelet
      service:
        name: kubelet
        daemon_reload: yes
        state: restarted
...

kubeadm initは一度実行すると2回目以降はエラーになるので、2回目以降は実行されないように条件を付けてます。

Flannelをデプロイします。

control-plane-playbook.yml

...
    - name: Setup kubeconfig for user
      command: "{{ item }}"
      with_items:
      - mkdir -p /home/pi/.kube
      - cp /etc/kubernetes/admin.conf /home/pi/.kube/config
      - chown pi:pi /home/pi/.kube/config

    - name: Install flannel pod network
      become: false
      command: kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
...

kubeadm joinの内容をローカルにコピーします。
後ほど、NodeをKubernetesクラスタに参加させるのに使用します。

control-plane-playbook.yml

    - name: Generate join command
      command: kubeadm token create --print-join-command
      register: join_command

    - name: Copy join command to local file
      become: false
      local_action: copy content="{{ join_command.stdout_lines[0] }}" dest="./join-command"

Nodeのセットアップ

ほぼcontrol planeの時と内容は一緒です。

control planeが出力したkubeadm joinのコマンドを実行します。

node-playbook.yml

...
    - name: Copy the join command to server location
      copy: src=join-command dest=/tmp/join-command.sh mode=0777

    - name: Reset Kubernetes component
      command: kubeadm reset --force
      register: reset_cluster

    - name: Join the node to cluster
      command: sh /tmp/join-command.sh
      when: reset_cluster is succeeded
...

kubeadm joinは一度実行すると2回目以降はエラーになるので、毎回kubeadm resetをして実行できるようにしています。

結果

control-planeの.kube/configの内容を操作用PCの.kube/configに追記すれば、操作用PCからkubectlを実行できます。

$ kubectl get node
NAME                STATUS   ROLES    AGE    VERSION
k8s-control-plane   Ready    master   16h    v1.18.6
raspberrypi-2       Ready    <none>   143m   v1.18.6
raspberrypi-3       Ready    <none>   143m   v1.18.6

まとめ

Kubernetesクラスタを構築する中でkubeadmを試行錯誤したのでそのあたりが勉強になりました。
あとはAnsibleを普段使わないのでハマりながらクラスタ構築用のスクリプトを書きました。

参考