🕌

OpenFaaSをK3s上に構築する

2022/05/31に公開

はじめに

OpenFaaSを試してみたくなったので、K3sを利用してサクッと構築する。あわせてコンテナレジストリとしてローカルにPrivate Registryも一緒に構築してしまう。

検証環境
Ubuntu 22.04
Docker 20.10.16
K3s 1.23.6+k3s1
OpenFaaS 0.15.0

K3s Kubernetesクラスタを構築

なんとなくDocker環境は残しておきたかったので、コンテナランタイムをDockerに変更してK3sをインストールする。
https://zenn.dev/haccht/articles/6dd9549dd277bf

$ curl -sfL https://get.k3s.io | sh -s - --docker --kubelet-arg 'cgroup-driver=systemd' --write-kubeconfig-mode 644
[INFO]  Finding release for channel stable
[INFO]  Using v1.23.6+k3s1 as release
[INFO]  Downloading hash https://github.com/k3s-io/k3s/releases/download/v1.23.6+k3s1/sha256sum-amd64.txt
[INFO]  Skipping binary downloaded, installed k3s matches hash
[INFO]  Skipping installation of SELinux RPM
[INFO]  Skipping /usr/local/bin/kubectl symlink to k3s, already exists
[INFO]  Skipping /usr/local/bin/crictl symlink to k3s, already exists
[INFO]  Skipping /usr/local/bin/ctr symlink to k3s, command exists in PATH at /usr/bin/ctr
[INFO]  Creating killall script /usr/local/bin/k3s-killall.sh
[INFO]  Creating uninstall script /usr/local/bin/k3s-uninstall.sh
[INFO]  env: Creating environment file /etc/systemd/system/k3s.service.env
[INFO]  systemd: Creating service file /etc/systemd/system/k3s.service
[INFO]  systemd: Enabling k3s unit
Created symlink /etc/systemd/system/multi-user.target.wants/k3s.service → /etc/systemd/system/k3s.service.
[INFO]  systemd: Starting k3s

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.6+k3s1", GitCommit:"418c3fa858b69b12b9cefbcff0526f666a6236b9", GitTreeState:"clean", BuildDate:"2022-04-28T22:16:18Z", GoVersion:"go1.17.5", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.6+k3s1", GitCommit:"418c3fa858b69b12b9cefbcff0526f666a6236b9", GitTreeState:"clean", BuildDate:"2022-04-28T22:16:18Z", GoVersion:"go1.17.5", Compiler:"gc", Platform:"linux/amd64"}

$ kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:6443
CoreDNS is running at https://127.0.0.1:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Metrics-server is running at https://127.0.0.1:6443/api/v1/namespaces/kube-system/services/https:metrics-server:https/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

これだけでKubernetesクラスタ構築できるK3sほんと凄い。なおKubernetesもOpenFaaSもメモリをたくさん消費するので最低でもRAM 2GBくらいは確保しておいたほうが良い。[1]

以下でHelmを用いてOpenFaaSをインストールするが、HelmからKubernetesクラスタにアクセスできない旨のエラーが出ることがある。事前に~/.kube/configを作成しておくとよい。

$ kubectl config view --raw > ~/.kube/config
$ chmod o-r ~/.kube/config
$ chmod g-r ~/.kube/config

HelmチャートでOpenFaaSインストール

Helmをインストール。

$ curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
$ helm version
version.BuildInfo{Version:"v3.5.2", GitCommit:"167aac70832d3a384f65f9745335e9fb40169dc2", GitTreeState:"dirty", GoVersion:"go1.15.7"}

Kuberntets上でOpenFaaSそのものはopenfaasというnamespaceに、deployしたFunctionはopenfaas-fnというnamescaceに作成する。これらのnamespaceを事前に作成しておく。

$ kubectl apply -f https://raw.githubusercontent.com/openfaas/faas-netes/master/namespaces.yml

OpenFaaSをインストールする。

$ helm repo add openfaas https://openfaas.github.io/faas-netes/
$ helm repo update
$ helm install openfaas openfaas/openfaas \
     --namespace openfaas \
     --set functionNamespace=openfaas-fn \
     --set generateBasicAuth=true \
     --set serviceType=LoadBalancer

# しばらく待つ
$ kubectl -n openfaas get deployments -l "release=openfaas, app=openfaas"
NAME                READY   UP-TO-DATE   AVAILABLE   AGE
prometheus          1/1     1            1           65s
nats                1/1     1            1           65s
alertmanager        1/1     1            1           65s
basic-auth-plugin   1/1     1            1           65s
queue-worker        1/1     1            1           65s
gateway             1/1     1            1           65s     

OpenFaaSはWebUIからも操作も可能だが、CLIで操作できる公式ツールが用意されているのでインストール。

$ curl -sSL https://cli.openfaas.com | sudo sh

OpenFaaSでFunctionのインストール

OpenFaaSにFunctionを登録・起動してみる。
まずはOpenFaaSにadminアカウントでログインする。

$ PASSWORD=$(kubectl get secret -n openfaas basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 --decode; echo)
$ echo -n $PASSWORD | faas-cli login --username admin --password-stdin
Calling the OpenFaaS server to validate the credentials...
credentials saved for admin http://127.0.0.1:8080

事前にOpenFaaS StoreにはいくつかFuncitonが登録されているので、ここから試しにfigletをデプロイしてみる。他にどのようなFunctionがあるかfaas-cli store listで確認できる。

$ faas-cli store deploy figlet

Deployed. 202 Accepted.
URL: http://127.0.0.1:8080/function/figlet

$ faas-cli list
Function                        Invocations     Replicas
figlet                          0               1

デプロイしたFunctionを呼び出す方法は2通り。
ひとつはfaas-cli invokeコマンドによるもの。デプロイしたFunctionのテストに使うことが多い。

$ echo openfaas | faas-cli invoke figlet
                         __
  ___  _ __   ___ _ __  / _| __ _  __ _ ___
 / _ \| '_ \ / _ \ '_ \| |_ / _` |/ _` / __|
| (_) | |_) |  __/ | | |  _| (_| | (_| \__ \
 \___/| .__/ \___|_| |_|_|  \__,_|\__,_|___/
      |_|

もうひとつはOpenFaaSゲートウェイ( ここでは http://127.0.0.1:8080 )のエンドポイントからFunctionを起動するもの。

$ echo openfaas | curl -X POST -d @-  http://127.0.0.1:8080/function/figlet
                         __
  ___  _ __   ___ _ __  / _| __ _  __ _ ___
 / _ \| '_ \ / _ \ '_ \| |_ / _` |/ _` / __|
| (_) | |_) |  __/ | | |  _| (_| | (_| \__ \
 \___/| .__/ \___|_| |_|_|  \__,_|\__,_|___/
      |_|

楽しい🎉

自分でFunctionを作成する

さていよいよ自分でFunctionを登録・起動してみる。
OpenFaaSはコンテナ起動イメージからプロセスを起動するため、自前のFunctionも事前にコンテナレジストリに登録しておくことが必要。特に何も指定しなければ、OpenFaaSはDocker Hubをコンテナレジストリとする。

Docker Hubはダウンロード制限があったり無償プライベートレポジトリは1つだけとされていたりいくつかの制限があるため、ここでは自前でプライベートレジストリを構築しこれを利用したい。[2]

さっそくKubenetes上にプライベートレジストリを構築する。

$ kubectl create namespace docker-registry
$ cat << EOL | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: docker-registry
  namespace: docker-registry
  labels:
    app: docker-registry
spec:
  replicas: 1
  selector:
    matchLabels:
      app: docker-registry
  template:
    metadata:
      labels:
        app: docker-registry
        name: docker-registry
    spec:
      containers:
      - name: docker-registry
        image: registry
        ports:
        - containerPort: 5000
        volumeMounts:
        - name: storage
          mountPath: /var/lib/registry
      volumes:
      - name: storage
        emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: docker-registry-service
  namespace: docker-registry
spec:
  selector:
    app: docker-registry
  type: NodePort
  ports:
    - name: docker-registry-port
      protocol: TCP
      port: 5000
      targetPort: 5000
EOL

$ kubectl get service -n docker-registry
NAME                      TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
docker-registry-service   NodePort   10.43.242.177   <none>        5000:30848/TCP   17s

無事に10.43.242.177:5000でプライベートレジストリが起動している。
このレジストリを他のPodsから利用するためには、K3sにこのプライベートレジストリを登録しておく必要がある。

$ export REGISTRY_IP=$(kubectl get svc/docker-registry-service -n docker-registry -o jsonpath='{.spec.clusterIP}')
$ cat << EOF | sudo tee /etc/rancher/k3s/registries.yaml
mirrors:
  "${REGISTRY_IP}:5000":
    endpoint:
      - "http://${REGISTRY_IP}:5000"
EOF

$ sudo systemctl restart k3s

また今回構築したプライベートレジストリは通信が暗号化されていないためinsecure-registriesとしてDockerにも登録が必要。[3]

$ export REGISTRY_IP=$(kubectl get svc/docker-registry-service -n docker-registry -o jsonpath='{.spec.clusterIP}')
$ cat << EOL | sudo tee /etc/docker/daemon.json
{
  "insecure-registries": [ "${REGISTRY_IP}:5000" ]
}
EOL

$ sudo systemctl restart docker

準備完了。FunctionはOpenFaaSで用意されているTemplateから作成していく。[4]
まずは最新のTemplateをダウンロードしてくる。

$ faas-cli template pull
$ faas-cli new --list
Languages available as templates:
- csharp
- dockerfile
- go
- java11
- java11-vert-x
- node
- node12
- node12-debian
- node14
- node16
- node17
- php7
- python
- python3
- python3-debian
- ruby

それではpython3テンプレートを利用してFunctionを作成してみる。
Function名はhello-openfaas--prefixはコンテナイメージのprefixのことで、ビルドされたイメージの登録先レジストリを指す。

$ faas-cli new --lang python3 hello-openfaas --prefix ${REGISTRY_IP}:5000
$ ls
hello-openfaas/  hello-openfaas.yml  template/

hello-openfaas/handler.pyがFunctionの実体。これを書き換えて以下のように変更する。

$ cat <<EOL > ./hello-openfaas/handler.py
def handle(req):
    print("Hello! You said: " + req)
EOL

このFunctionをデプロイする。
faas-cli upコマンドひとつで以下を一括で実施してくれる。

  • コンテナイメージの作成: faas-cli build
  • コンテナイメージのレジストリ登録: faas-cli push
  • Functionのデプロイ: faas-cli deploy
$ faas-cli up -f hello-openfaas.yml
$ faas-cli list
Function                        Invocations     Replicas
figlet                          2               1
hello-openfaas                  0               1

無事登録されたFunctionを起動する。

$ echo Nice to meet you! | curl -X POST -d @- http://127.0.0.1:8080/function/hello-openfaas
Hello! You said: Nice to meet you!

できた!
追加でpipモジュールが必要な場合には ./hello-openfaas/requirements.txtに必要なモジュール名を書いておけばコンテナイメージ作成時に読み込んでくれる。
楽しい🎉[5]

脚注
  1. なおこの環境構築のためにVagrantでVMをたてて色々試行錯誤していたところ、VMデフォルトのメモリサイズが512MBのままであったのを忘れてうまく起動しないことを愚痴っていたらOpenFaaS FounderのAlexさんに「何やってんの?」と言われてしまった。https://twitter.com/alexellisuk/status/1528994343125655552 ↩︎

  2. GitHub Packages Container registryやGitlab Container Registryなども利用できるのかな?未検証。 ↩︎

  3. もしすでに/etc/docker/daemon.jsonがある場合は上書きしないように気を付けてね ↩︎

  4. もちろん自分でTemplateから作成することも可能 ↩︎

  5. OpenFaaS Workshopにより詳細なチュートリアルがある。なお本記事はLab.11まであるチュートリアルのうち、Lab.3程度までの内容。深い。。。 ↩︎

Discussion