.kr

山とかトレランとか

Orange Pi Zero 2でおうちkubernetesクラスター


元ネタ

3日間クッキング【Kubernetes のラズペリーパイ包み “サイバーエージェント風”】
https://developers.cyberagent.co.jp/blog/archives/14721/

Raspberry PiでおうちKubernetes構築
https://qiita.com/go_vargo/items/d1271ab60f2bba375dcc
https://qiita.com/go_vargo/items/29f6d832ea0a289b4778

おうちKubernetesをOrange Piで構築する
https://www.tumblr.com/suzukiapple/182335667330/%E3%81%8A%E3%81%86%E3%81%A1kubernetes%E3%82%92orange-pi%E3%81%A7%E6%A7%8B%E7%AF%89%E3%81%99%E3%82%8B

元ネタはもはや6年前ですが、kubernetesの練習用に手頃なクラスタが欲しかったので、Orange Piを使っておうちクラスタを構築しました。

以前はRaspberry Piも安かったので複数枚購入して物理的に冗長なクラスタを構築することも容易でしたが、今は1万円もするので、その辺の適当な小型PCを買うより高くついてしまいます。ロマンの要素が大きいのである程度費用はかかってもいいのですが、あまり高すぎるのも嫌なので、取れる選択肢の中では比較的安いOrange Pi Zero 2 (購入時の価格が1枚当たり3412円) を使うことにしました。サイズも小さいのでおうちクラスタ向きですが、上の記事で紹介されているような積層型ケースはこのサイズに合うものがなかったので、ケースから自作です。

材料

  • 電子機器
    • Orange Pi Zero 2 x3 (10,236円+送料1,255円 Aliexpressで購入)
    • microSD 32GB x3 (2,040円)
    • TP-Link 5ポート スイッチングハブ (1,479円)
    • ヒートシンク 9mm×9mm×5mm (799円)
    • 電源アダプタ usb-c 3口ついてるやつ (5,597円)
  • ケース
    • ホームセンター購入
      • アクリル板 厚さ2mm
      • アクリルカッター
      • アクリル板に穴を開けるための専用ビット M3
      • なべ子ねじ M3x10mm
    • monotaro購入
      • 六角スペーサー M(mm): 3, L1(mm): 30, https://www.monotaro.com/p/8920/6381/ (1個72円x18)
      • スペーサー 内径(Φmm): 約3.2, 外径(Φmm): 約6, 長さL(mm): 約3 (10個189円x2)

到着編

orange pi 到着。一応ヒートシンクを貼っておく。microSDになんらかのOSをコピーしてスロットに嵌めて電源を入れると起動。

とりあえず電源と有線でスイッチに繋いでみる。電源はtype C。最初はtype Cが3股に分かれているヒュドラみたいなケーブルを使ったが、変換先がtype Aで家にしょぼいアダプタしかなく電圧不足で不安定だったので、オーバーだが最大100Wでtype Cが3口あるアダプタを購入した(上の写真では1枚だけ別の電源に繋いでいる)。

物理編

ホームセンターとmonotaroで必要な材料を買い揃えて工作開始。やることは、1) アクリル板を同じサイズに5枚切り出す、2) 穴を開ける、3) 基盤をはめていくだけだが、DIY素人なのでわからないことだらけ。設計図はノートに書いていたが紛失した。スイッチの方が大きいのでそのサイズに合わせてアクリル板のサイズを決める。

まずアクリル板は紙が貼ってあるのでそこに線を引いて、アクリルカッターで何度も傷をつけていく。ある程度傷をつけたら力を入れてバキッと割る。

アクリル板専用ビットというのがあるので、電動ドリルにセットして狙いをつけて穴を開ける(写真なし)。慣れてきたら複数枚貫通もできるようになった。とりあえず1枚作って、基盤をはめてみる。緊張の一瞬。

ネジはM3の10mm、スペーサーは内径3.2mm、長さ3mmのやつでピッタリハマった。

穴あけの精度が低いのでガタガタだが、ちゃんと固定されている。

1枚できたので板を量産。板を連結させる部品は六角スペーサーで、M3 30mmを使用。スイッチもギリギリ入った。

自分の中ではすごく良くできたので、ここで満足して10ヶ月ほど放置してた。

論理編

OS

最初はArmbianをトライ

./compile.sh BOARD=orangepizero2 BRANCH=current RELEASE=jammy BUILD_MINIMAL=no BUILD_DESKTOP=no KERNEL_CONFIGURE=no COMPRESS_OUTPUTIMAGE=sha,img

こんな感じでビルドしてsdカードにコピーして転送して電源を入れてみたが起動せず。

次にOrange Pi OSを使ったら起動できた。この辺からダウンロードする。

http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/service-and-support/Orange-Pi-Zero-2.html

$ uname -a
Linux orangepi1 5.16.17-sun50iw9 #3.0.6 SMP Tue Aug 9 13:51:16 CST 2022 aarch64 aarch64 aarch64 GNU/Linux

最終的にk3sも問題なくインストールできたので、このまま。

hostnameなど諸々設定する。技適を通ってないらしいので無線はオフにしておく。

kubernetes

最初kubeadmでクラスタ構築したところスペックが足らずagentがまともに動いてくれない。

Orange Pi Zero 2 のスペックはCPU 4core、RAM 1GB
http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-Zero-2.html

kubeadmは1台あたり2GB以上のメモリ、2コア以上のCPU
https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#%E5%A7%8B%E3%82%81%E3%82%8B%E5%89%8D%E3%81%AB

kubeadmが要求するスペックには足らないので動くはずがなかった。

そこで、k3sに変更して構築。とはいえk3sでもメモリを50%使ってしまうので、その上でアプリを動かすのはちょっと厳しかったりする。

k3sの最小要求スペックはCPU 1core, RAM 512MB
https://docs.k3s.io/installation/requirements#hardware

https://docs.k3s.io/quick-start を見てインストール。

master

curl -sfL https://get.k3s.io | sh -`

トークンはmasterとしてインストールしたノードの/var/lib/rancher/k3s/server/node-tokenにあるのでそれを使ってworkerをクラスタに参加させる。

worker

curl -sfL https://get.k3s.io | K3S_URL=https://<master IP>:6443 K3S_TOKEN=<token> sh -`

masterはsystemctl status k3sでサービスがrunningであればOK。

root@orangepi0:~# systemctl status k3s
● k3s.service - Lightweight Kubernetes
     Loaded: loaded (/etc/systemd/system/k3s.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2024-02-16 04:57:51 UTC; 21h ago
       Docs: https://k3s.io
    Process: 1180 ExecStartPre=/bin/sh -xc ! /usr/bin/systemctl is-enabled --quiet nm-cloud-setup.service (code=exited, status=0/SUCCESS)
    Process: 1212 ExecStartPre=/sbin/modprobe br_netfilter (code=exited, status=0/SUCCESS)
    Process: 1227 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS)
   Main PID: 1242 (k3s-server)
      Tasks: 168
     Memory: 574.5M
        CPU: 4h 42min 36.180s
     CGroup: /system.slice/k3s.service
             ├─ 1242 "/usr/local/bin/k3s server"
             ├─ 1549 containerd -c /var/lib/rancher/k3s/agent/etc/containerd/config.toml -a /run/k3s/containerd/containerd.sock --state /run/k3s/containerd --root /var/lib/rancher/k3s/agent/containerd
             ├─ 3189 /var/lib/rancher/k3s/data/4b147cafa965066cd68e04b4e3acce221078156a3b9ba635a653517ce459aa4d/bin/containerd-shim-runc-v2 -namespace k8s.io -id 6a9b2179460499f83fccf0e3e752da80df73dc4fd2008246d9f05d474223a5cf -address /run/>
             ├─ 3191 /var/lib/rancher/k3s/data/4b147cafa965066cd68e04b4e3acce221078156a3b9ba635a653517ce459aa4d/bin/containerd-shim-runc-v2 -namespace k8s.io -id 4b72dac5a3580db534217d7ad77e623207283182e7a724980927ce2ed54efcc6 -address /run/>
             ├─ 3192 /var/lib/rancher/k3s/data/4b147cafa965066cd68e04b4e3acce221078156a3b9ba635a653517ce459aa4d/bin/containerd-shim-runc-v2 -namespace k8s.io -id 983bd04163555f91a88d8a6622c93126984f8444fd5b56a8364ec45f487cdc2e -address /run/>
             ├─ 3214 /var/lib/rancher/k3s/data/4b147cafa965066cd68e04b4e3acce221078156a3b9ba635a653517ce459aa4d/bin/containerd-shim-runc-v2 -namespace k8s.io -id d742755983a44dc20df14c80b60b062d9086c9061ce32a47e811caa1b8d864e5 -address /run/>
             ├─ 3220 /var/lib/rancher/k3s/data/4b147cafa965066cd68e04b4e3acce221078156a3b9ba635a653517ce459aa4d/bin/containerd-shim-runc-v2 -namespace k8s.io -id 18db82abea3ef6c042d374ac6f533472015017e4bc982296934946ca04e7f614 -address /run/>
             ├─ 3284 /var/lib/rancher/k3s/data/4b147cafa965066cd68e04b4e3acce221078156a3b9ba635a653517ce459aa4d/bin/containerd-shim-runc-v2 -namespace k8s.io -id 3218418fb35f9e1baf79238d75c0b0287198ef34c24fe43c4ce6471ef676099e -address /run/>
             ├─29887 /var/lib/rancher/k3s/data/4b147cafa965066cd68e04b4e3acce221078156a3b9ba635a653517ce459aa4d/bin/containerd-shim-runc-v2 -namespace k8s.io -id 2ee4d3c1b05ec228d382a334e5843407714b52057bd835f575d2e5679fb15261 -address /run/>
             └─36552 /var/lib/rancher/k3s/data/4b147cafa965066cd68e04b4e3acce221078156a3b9ba635a653517ce459aa4d/bin/containerd-shim-runc-v2 -namespace k8s.io -id 38009dff653bc3f9de5f75b14f5d34a27192ae1f92518f44efcb6aeb19da0e5f -address /run/>

workerはsystemctl status k3s-agent

root@orangepi1:~# systemctl status k3s-agent
● k3s-agent.service - Lightweight Kubernetes
     Loaded: loaded (/etc/systemd/system/k3s-agent.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2024-02-16 04:58:08 UTC; 21h ago
       Docs: https://k3s.io
    Process: 1121 ExecStartPre=/bin/sh -xc ! /usr/bin/systemctl is-enabled --quiet nm-cloud-setup.service 2>/dev/null (code=exited, status=0/SUCCESS)
    Process: 1171 ExecStartPre=/sbin/modprobe br_netfilter (code=exited, status=0/SUCCESS)
    Process: 1173 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS)
   Main PID: 1176 (k3s-agent)
      Tasks: 108
     Memory: 255.6M
        CPU: 2h 12min 59.043s
     CGroup: /system.slice/k3s-agent.service
             ├─ 1176 "/usr/local/bin/k3s agent"
             ├─ 1540 "containerd " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "">
             ├─ 2608 /var/lib/rancher/k3s/data/51f1454895fb5bca15f0e8fb5310ff90ba7fc1c3fc5a8c9d052f8570f3483527/bin/containerd-shim-runc-v2 -namespace k8s.io -id 972e5d21c2333155394ccdcc86c81e94175dc4ae4b625a0976acd938b51c7a6a -address /run/>
             ├─ 2610 /var/lib/rancher/k3s/data/51f1454895fb5bca15f0e8fb5310ff90ba7fc1c3fc5a8c9d052f8570f3483527/bin/containerd-shim-runc-v2 -namespace k8s.io -id a21f011d5021b372cfdfedc3b27bef9a726642e777f657ba2342df538f5e6a1d -address /run/>
             ├─ 2611 /var/lib/rancher/k3s/data/51f1454895fb5bca15f0e8fb5310ff90ba7fc1c3fc5a8c9d052f8570f3483527/bin/containerd-shim-runc-v2 -namespace k8s.io -id 8419a10fea3394caf90e961145507b4ea957f103e273109af9df4d3ef27140c5 -address /run/>
             ├─33392 /var/lib/rancher/k3s/data/51f1454895fb5bca15f0e8fb5310ff90ba7fc1c3fc5a8c9d052f8570f3483527/bin/containerd-shim-runc-v2 -namespace k8s.io -id 0d270807cbd8a1a8c534427a5629d6575f93bc1d0e9f3a46a1df787c300161a2 -address /run/>
             ├─33877 /var/lib/rancher/k3s/data/51f1454895fb5bca15f0e8fb5310ff90ba7fc1c3fc5a8c9d052f8570f3483527/bin/containerd-shim-runc-v2 -namespace k8s.io -id 19e1d88e105a164c04b5d0bf95330e95a8ab89d54f3d9a8bc5bffcbc58acef7c -address /run/>
             └─39262 /var/lib/rancher/k3s/data/51f1454895fb5bca15f0e8fb5310ff90ba7fc1c3fc5a8c9d052f8570f3483527/bin/containerd-shim-runc-v2 -namespace k8s.io -id 310059e2e512ecb45104a075ca5666c2deba423068006457305ffe3dfd6a6747 -address /run/>

nodeの確認。kubeconfig は https://docs.k3s.io/cluster-access をみる

kubectl get nodes
NAME        STATUS   ROLES                  AGE    VERSION
orangepi1   Ready    <none>                 19d    v1.28.5+k3s1
orangepi2   Ready    <none>                 19d    v1.28.5+k3s1
orangepi0   Ready    control-plane,master   279d   v1.26.4+k3s1

用途

squidで広告ブロックしてみようかなーと思って、squidをデプロイ。

Dockerfile

FROM ubuntu:22.04

RUN apt-get update && apt-get install -y squid

COPY entrypoint.sh /entrypoint.sh

RUN chmod 755 /entrypoint.sh

RUN mkdir -p /etc/squid/list
COPY adaway.txt /etc/squid/list/adaway.txt

ENTRYPOINT [ "/entrypoint.sh" ]

adaway.txtは適当に拾ってきたリスト。

entrypoint.sh

#!/bin/bash -e

chown proxy:proxy /dev/stdout

exec $(which squid) -NYCd 1
  • 標準出力にログを出力するために、/dev/stdoutのownerの変更をしている
  • imageはローカルでビルドしてdocker.ioにpush

squid.yaml

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: squid
  labels:
    app: squid
spec:
  replicas: 2
  selector:
    matchLabels:
      app: squid
  template:
    metadata:
      labels:
        app: squid
    spec:
      nodeSelector:
        role: worker
      containers:
        - name: squid
          image: <repo>
          imagePullPolicy: Always
          ports:
            - containerPort: 3128
              protocol: TCP
              name: squid
          volumeMounts:
            - name: data
              mountPath: /var/spool/squid
            - name: squid-config
              mountPath: /etc/squid/squid.conf
              subPath: squid.conf
      volumes:
        - name: data
          emptyDir: {}
        - name: squid-config
          configMap:
            name: squid-config  
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: squid-config
data:
  squid.conf: |
    acl block_domain dstdomain -n "/etc/squid/list/adaway.txt"
    http_access deny block_domain
    http_access allow all
    http_port 3128
    logfile_rotate 0
    cache_log stdio:/dev/stdout
    access_log stdio:/dev/stdout
---
apiVersion: v1
kind: Service
metadata:
  name: squid
spec:
  type: LoadBalancer
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 3128
      targetPort: squid
  selector:
    app: squid

k3sはServiceLBというロードバランサーを使ってノードの外部にサービスをexposeできる https://docs.k3s.io/networking#how-the-service-lb-works

ついでにfluent bitでログをS3に転送する。fluent bitは以下のvaluesを使ってhelmでインストールする。

env:
  - name: AWS_ACCESS_KEY_ID
    valueFrom:
      secretKeyRef:
        name: aws-key-secret
        key: aws-access-key-id
  - name: AWS_SECRET_ACCESS_KEY
    valueFrom:
      secretKeyRef:
        name: aws-key-secret
        key: aws-secret-access-key

config:
  outputs: |
    [OUTPUT]
        Name s3
        Match kube.var.log.containers.squid*
        region <region>
        bucket <bucket>
        s3_key_format /squid-logs/%Y/%m/%d/%Y-%m-%dT%H:%M:%SZ-$UUID.log

環境変数access keyの設定と(secretは別途必要)、outputの設定だけ。<ノードのIP>:3128をproxyに指定して、広告ブロッカーの動作とログの転送が確認できた。