Raspberry Pi 4 で簡単にx86対応Kubernetesクラスタを作ろう

はじめに

サイバーエージェントの7days インフラ向け 開発型インターン ONLINEに参加しました。

色々と新しい学びがありましたが躓いたことなどもあったので、自分なりに簡単にクラスタを作れる手順をまとめました。

構築手順

OSセットアップ

今回利用したOSはubuntu20.04です。sshが接続できるような状態にします。

以下の作業をすべての端末で行ってください。

アップデート

以下のコマンドを実行しシステムを更新します。

sudo apt update
sudo apt upgrade -y
E: Unable to lock the administration directory /var/lib/dpkg/lock?

上記のようなメッセージが出る場合は自動更新が動いているのでしばらく放置しましょう。

ホストネームの設定

同じ端末が複数あるとどの端末を操作しているかわからなくなるので、名前をつけましょう。

とりあえず各端末を以下のようにします。

  • rpi4-node1
  • rpi4-node2
  • rpi4-node3
sudo hostnamectl set-hostname rpi4-node1

上記コマンド実行後に再度ログインすればホストネームは変更されています。

IPアドレスの固定

Ubuntu 20.04 LTSで固定IPアドレスの設定のようにすればアドレスを固定にできます。

本例では以下のようになっています。

  • rpi4-node1 192.168.1.21
  • rpi4-node2 192.168.1.22
  • rpi4-node3 192.168.1.23
  • デフォルトゲートウェイ 192.168.1.1

rpi4-node1であれば以下のようなファイルを「/etc/netplan/99_config.yaml」に作成します。

network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: false
      dhcp6: false
      addresses: [192.168.1.21/24]
      gateway4: 192.168.1.1
      nameservers:
        addresses: [192.168.1.1, 8.8.8.8, 8.8.4.4]

その後以下のコマンドで設定を適応します。

この際sshが切れる場合があるので、変更後のIPアドレスに再接続してください。

sudo netplan apply

魔法の設定

以下のコマンドを実行すると、memoryが0となっています。これを1にする必要があります。

ubuntu@rpi4-node1:~$ cat /proc/cgroups
#subsys_name    hierarchy       num_cgroups     enabled
cpuset  10      1       1
cpu     3       1       1
cpuacct 3       1       1
blkio   4       1       1
memory  0       48      0
devices 2       40      1
freezer 6       2       1
net_cls 5       1       1
perf_event      8       1       1
net_prio        5       1       1
pids    9       45      1
rdma    7       1       1

「/boot/firmware/cmdline.txt」を以下のように編集してください。

ビフォー

net.ifnames=0 dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc

アフター

net.ifnames=0 dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc cgroup_enable=memory

x86エミュレータの設定

以下のコマンドを貼り付け実行してください。

wget https://raw.githubusercontent.com/penM000/qemu-binfmt-setup/main/qemu-binfmt-setup.sh
chmod +x ./qemu-binfmt-setup.sh
./qemu-binfmt-setup.sh
rm ./qemu-binfmt-setup.sh

上記ではqemu 4.2が入りますが、割とコアダンプが多発するので以下のように無理やり新しいバージョンを入れることを推奨します。

qemu 5.0

wget http://ports.ubuntu.com/pool/universe/q/qemu/qemu-user-static_5.0-5ubuntu9_arm64.deb
sudo dpkg -i qemu-user-static_5.0-5ubuntu9_arm64.deb
rm qemu-user-static_5.0-5ubuntu9_arm64.deb

qemu 5.1

wget http://ftp.jp.debian.org/debian/pool/main/q/qemu/qemu-user-static_5.1+dfsg-4+b1_arm64.deb
sudo dpkg -i qemu-user-static_5.1+dfsg-4+b1_arm64.deb
rm qemu-user-static_5.1+dfsg-4+b1_arm64.deb

再起動

このタイミングで一旦再起動します。

sudo reboot

microk8sのインストール

以下のコマンドでインストールします。

sudo snap install microk8s --classic --channel=1.19/stable

kubectlの別名を設定します。

sudo snap alias microk8s.kubectl kubectl

sudo 無しで利用できるようにします。次回ログインで適応されます。

sudo usermod -a -G microk8s ubuntu
sudo chown -f -R ubuntu ~/.kube

正しくインストールできれば以下のような表示になります。

sudo microk8s.status
microk8s is running
high-availability: no
  datastore master nodes: 127.0.0.1:19001
  datastore standby nodes: none
addons:
  enabled:
    ha-cluster           # Configure high availability on the current node
  disabled:
    dashboard            # The Kubernetes dashboard
    dns                  # CoreDNS
    helm                 # Helm 2 - the package manager for Kubernetes
    helm3                # Helm 3 - Kubernetes package manager
    host-access          # Allow Pods connecting to Host services smoothly
    ingress              # Ingress controller for external access
    metallb              # Loadbalancer for your Kubernetes cluster
    metrics-server       # K8s Metrics Server for API access to service metrics
    rbac                 # Role-Based Access Control for authorisation
    registry             # Private image registry exposed on localhost:32000
    storage              # Storage class; allocates storage from host directory

以下のような表示になる場合は「魔法の設定」を行っていない可能性があります。

また、起動直後は処理中の場合があります。

sudo microk8s.status
microk8s is not running. Use microk8s inspect for a deeper inspection.

クラスタの作成

下記作業は、すべてのノードでmicrok8sが利用可能なものとして進行します。

rpi4-node1で以下のコマンドを実行します。すると以下のような情報が出るのでこれをrpi4-node2で実行します。

joinにはsudoが必要です。

ubuntu@rpi4-node1:~$ microk8s add-node
From the node you wish to join to this cluster, run the following:
microk8s join 192.168.1.21:25000/1d40b640b06cf019f1f4b47bf2a8e42e
ubuntu@rpi4-node1:~$ sudo microk8s join 192.168.1.21:25000/1d40b640b06cf019f1f4b47bf2a8e42e
Contacting cluster at 192.168.1.21
Waiting for this node to finish joining the cluster. ..

戻ってきたら再度rpi4-node1でコマンドを実行し、同様にして、rpi4-node3で実行します。

ubuntu@rpi4-node1:~$ microk8s add-node
From the node you wish to join to this cluster, run the following:
microk8s join 192.168.1.21:25000/373ed6c9036180b5906348a8657e78c5
ubuntu@rpi4-node3:~$ sudo microk8s join 192.168.1.21:25000/373ed6c9036180b5906348a8657e78c5
Contacting cluster at 192.168.1.21
Waiting for this node to finish joining the cluster. ..

すべて参加できると以下のように3台がマスターノードで認識されます。

ubuntu@rpi4-node1:~$ microk8s.status
microk8s is running
high-availability: yes
  datastore master nodes: 192.168.1.21:19001 192.168.1.23:19001 192.168.1.22:19001
  datastore standby nodes: none

もし以下のように参加できない場合は参加できないノードで「sudo microk8s.leave」などをでリセットしてから再度参加しましょう。

ubuntu@rpi4-node1:~$ microk8s.status
microk8s is running
high-availability: no
  datastore master nodes: 192.168.1.21:19001 192.168.1.23:19001
  datastore standby nodes: none

Kubernetes設定

上記でKubernetesクラスターが完成しました。

ここからは実際に使えるようにKubernetesの設定を行います。

helm有効化

ubuntu@rpi4-node1:~$ sudo microk8s.enable helm3
sudo snap alias microk8s.helm3 helm
helm repo add stable https://kubernetes-charts.storage.googleapis.com/

DNS有効化

以下のコマンドでポッドがDNSを利用できるようになります

ubuntu@rpi4-node1:~$ sudo microk8s.enable dns
ubuntu@rpi4-node1:~$ kubectl run test --image busybox:1.28 --restart Never -it --rm -- nslookup google.com
Server:    10.152.183.10
Address 1: 10.152.183.10 kube-dns.kube-system.svc.cluster.local
Name:      google.com
Address 1: 2404:6800:4004:808::200e nrt20s08-in-x0e.1e100.net
Address 2: 172.217.175.238 nrt12s29-in-f14.1e100.net
pod "test" deleted

MetalLB有効化

以下のコマンドでMetalLBが利用できます

アドレス範囲は192.168.1.30~192.168.1.40とします。

ubuntu@rpi4-node1:~$ sudo microk8s.enable metallb
Enabling MetalLB
Enter each IP address range delimited by comma (e.g. '10.64.140.43-10.64.140.49,192.168.0.105-192.168.0.111'): 192.168.1.30-192.168.1.40

ダッシュボード有効化

以下のコマンドでダッシュボードが利用できます。

ubuntu@rpi4-node1:~$ sudo microk8s.enable dashboard

ダッシュボードを8443ポートで見れるようにします。

ubuntu@rpi4-node1:~$ kubectl expose deployment kubernetes-dashboard --name=kubernetes-dashboard-lb --port 8443 --target-port=8443 --type LoadBalancer -n kube-system

以下のコマンドでhttps://192.168.1.30:8443にアクセスすれば良いことがわかります。

ubuntu@rpi4-node1:~$ kubectl get svc -n kube-system
NAME                        TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)                  AGE
kube-dns                    ClusterIP      10.152.183.10    <none>         53/UDP,53/TCP,9153/TCP   9m24s
metrics-server              ClusterIP      10.152.183.225   <none>         443/TCP                  2m15s
kubernetes-dashboard        ClusterIP      10.152.183.105   <none>         443/TCP                  2m6s
dashboard-metrics-scraper   ClusterIP      10.152.183.178   <none>         8000/TCP                 2m5s
kubernetes-dashboard-lb     LoadBalancer   10.152.183.118   192.168.1.30   8443:31650/TCP           27s

アクセスするとトークンを求められます。以下のコマンドでトークンを取得する事ができます。

ubuntu@rpi4-node1:~$ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep default-token | a
wk '{print $1}')
Name:         default-token-gqfr7
Namespace:    kube-system
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: default
              kubernetes.io/service-account.uid: 650450a4-16f4-42e7-9092-11e77d6e91dd
Type:  kubernetes.io/service-account-token
Data
====
ca.crt:     1103 bytes
namespace:  11 bytes
token:      eyJhbGciOiJ

Argo CD

kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

以下のようにすべて起動するまで待ちましょう。

ubuntu@rpi4-node1:~$ kubectl get pods -n argocd
NAME                                             READY   STATUS    RESTARTS   AGE
argocd-redis-cccbb8f7-lz59z                      1/1     Running   0          65s
argocd-application-controller-5bb5bf665c-qnk8b   1/1     Running   0          65s
argocd-repo-server-8fbdb95bf-zm2g2               1/1     Running   0          65s
argocd-dex-server-5559ccbb9f-rgcw2               1/1     Running   1          65s
argocd-server-5bc896856-56sjp                    1/1     Running   0          64s

以下のようにpodが受付状態になっていますが、外からアクセスできないのでこれをできるようにします。

ubuntu@rpi4-node1:~$ kubectl get svc -n argocd
NAME                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
argocd-dex-server       ClusterIP   10.152.183.238   <none>        5556/TCP,5557/TCP,5558/TCP   3m16s
argocd-metrics          ClusterIP   10.152.183.169   <none>        8082/TCP                     3m16s
argocd-redis            ClusterIP   10.152.183.107   <none>        6379/TCP                     3m16s
argocd-repo-server      ClusterIP   10.152.183.193   <none>        8081/TCP,8084/TCP            3m16s
argocd-server-metrics   ClusterIP   10.152.183.83    <none>        8083/TCP                     3m16s
argocd-server           ClusterIP   10.152.183.114   <none>        80/TCP,443/TCP               3m16s

上記でロードバランサが使えるようになったので変更します。

ubuntu@rpi4-node1:~$ kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
service/argocd-server patched
ubuntu@rpi4-node1:~$ kubectl get svc -n argocd
NAME                    TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)                      AGE
argocd-dex-server       ClusterIP      10.152.183.238   <none>         5556/TCP,5557/TCP,5558/TCP   4m25s
argocd-metrics          ClusterIP      10.152.183.169   <none>         8082/TCP                     4m25s
argocd-redis            ClusterIP      10.152.183.107   <none>         6379/TCP                     4m25s
argocd-repo-server      ClusterIP      10.152.183.193   <none>         8081/TCP,8084/TCP            4m25s
argocd-server-metrics   ClusterIP      10.152.183.83    <none>         8083/TCP                     4m25s
argocd-server           LoadBalancer   10.152.183.114   192.168.1.31   80:31148/TCP,443:30112/TCP   4m25s

192.168.1.31にアクセスするとページが見れます。

ユーザー名はadminで、パスワードは以下のコマンドを実行することで得ることができます。

ubuntu@rpi4-node1:~$ kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-server -o name | cut -d'/' -f 2
argocd-server-5bc896856-56sjp
project: default
source:
  repoURL: 'https://github.com/argoproj/argocd-example-apps.git'
  path: guestbook
  targetRevision: HEAD
destination:
  server: 'https://kubernetes.default.svc'
  namespace: default

コマンドも以下のようにすれば動きます。

wget https://github.com/argoproj/argo-cd/releases/download/v1.7.8/argocd-linux-amd64
chmod +x argocd-linux-amd64
./argocd-linux-amd64
argocd controls a Argo CD server
Usage:
  argocd [flags]
  argocd [command]
Available Commands:
  account     Manage account settings
  app         Manage applications
  cert        Manage repository certificates and SSH known hosts entries
  cluster     Manage cluster credentials
  completion  output shell completion code for the specified shell (bash or zsh)
  context     Switch between contexts
  gpg         Manage GPG keys used for signature verification
  help        Help about any command
  login       Log in to Argo CD
  logout      Log out from Argo CD
  proj        Manage projects
  relogin     Refresh an expired authenticate token
  repo        Manage repository connection parameters
  repocreds   Manage repository connection parameters
  version     Print version information
Flags:
      --auth-token string               Authentication token
      --client-crt string               Client certificate file
      --client-crt-key string           Client certificate key file
      --config string                   Path to Argo CD config (default "/home/ubuntu/.argocd/config")
      --grpc-web                        Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2.
      --grpc-web-root-path string       Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root.
  -H, --header strings                  Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers)
  -h, --help                            help for argocd
      --insecure                        Skip server certificate and domain verification
      --logformat string                Set the logging format. One of: text|json (default "text")
      --loglevel string                 Set the logging level. One of: debug|info|warn|error (default "info")
      --plaintext                       Disable TLS
      --port-forward                    Connect to a random argocd-server port using port forwarding
      --port-forward-namespace string   Namespace name which should be used for port forwarding
      --server string                   Argo CD server address
      --server-crt string               Server certificate file
Use "argocd [command] --help" for more information about a command.

kube-ops-view

kubectl create namespace kube-ops-view
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: kube-ops-view
spec:
  destination:
    name: ''
    namespace: kube-ops-view
    server: 'https://kubernetes.default.svc'
  source:
    path: deploy
    repoURL: 'https://codeberg.org/hjacobs/kube-ops-view.git'
    targetRevision: HEAD
  project: default
kubectl port-forward service/kube-ops-view 8080:80 --address 0.0.0.0 -n kube-ops-view

Weave Scope

kubectl apply -f "https://cloud.weave.works/k8s/scope.yaml?k8s-version=$(kubectl version | base64 | tr -d '\n')"
kubectl port-forward -n weave "$(kubectl get -n weave pod --selector=weave-scope-component=app -o jsonpath='{.items..metadata.name}')" 4040 --address 0.0.0.0

prometheus

kubectl create namespace prometheus
helm install prometheus stable/prometheus-operator --namespace prometheus
kubectl port-forward service/prometheus-grafana 3000:80 --address 0.0.0.0 -n prometheus

ユーザ: admin
パスワード: prom-operator

まとめ

CPUがarmであるので、x86のイメージが動かないという問題から、イメージのビルドをしなければならないという問題がありましたが、これである程度はビルドしなくても動かせることがわかりました。でもやっぱり、コアダンプする時はするのでその場合はarm向けのビルドを行わなければいけなさそうです。