kubernetes knative でサーバレス Vim
はじめに
半月ほど前に、ようやく自分の VPS 環境で動いているものすべてを kubernetes クラスタに移行しました。とても満足感が高くやって良かったと思っています。
ウェブサーバ、メールサーバ、Nostr のリレーサーバや Nostr/Bluesky/Twitter で動かしている各種 bot もすべて kubernetes です。
昨日は knative を導入したので、Go や Rust や Ruby や Python や、いろんな言語のクラウドネイティブアプリを簡単に実行できる様にしました。
knative 便利
残念ながら knative は helm パッケージとして提供されていません。ArtifactHub でそれっぽい物が公開されていますが、ほぼ手作業と変わりません。
おおよそ以下の手順でインストールできます。knative ではネットワークレイヤとして以下の3つが用意されています。
- net-kourier
- net-contour
- net-istio
よく分かってない場合は kourier を入れておくと良いと言われたので kourier を使いました。
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.14.0/serving-default-domain.yaml
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.14.0/serving-crds.yaml
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.14.0/serving-core.yaml
kubectl patch configmap/config-network \
--namespace knative-serving \
--type merge \
--patch '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}'
kubectl apply -f https://github.com/knative/net-kourier/releases/download/knative-v1.14.0/kourier.yaml
ここまで実行するとおおよそ実行環境が起動します。
DomainMapping
以下を設定してアプリが起動したらサブドメインからアクセスできる様に。
apiVersion: networking.internal.knative.dev/v1alpha1
kind: ClusterDomainClaim
metadata:
name: example.com # ドメイン名(サブドメイン抜き)
spec:
namespace: default # アプリを入れるネームスペース
apiVersion: v1
kind: ConfigMap
metadata:
name: config-network
namespace: knative-serving
data:
ingress.class: kourier.ingress.networking.knative.dev
autocreate-cluster-domain-claims: "true"
#domain-template: '{{.Name}}.{{.Domain}}'
http-protocol: "Enabled"
external-domain-tls: "Disabled"
cluster-local-tls: "Disabled"
auto-tls: "Enabled"
apiVersion: v1
kind: ConfigMap
metadata:
name: config-domain
namespace: knative-serving
data:
example.com: ""
さっそく Go でアプリを起動してみる
適当なコードを用意します。
package main
import (
"fmt"
"log"
"net/http"
"os"
)
func handler(w http.ResponseWriter, r *http.Request) {
log.Print("helloworld: received a request")
target := os.Getenv("TARGET")
if target == "" {
target = "World"
}
fmt.Fprintf(w, "Hello %s!\n", target)
}
func main() {
log.Print("helloworld: starting server...")
http.HandleFunc("/", handler)
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Printf("helloworld: listening on port %s", port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}
Go の場合は ko を使うとめちゃめちゃ便利なので入れておくといいです。
環境変数 KO_DOCKER_REPO を設定したあと以下を実行。
kn service create --force helloworld-go --image=$(ko build .) --env TARGET="Go Sample v1"
ghcr のプライベートレジストリから pull したい場合はクレデンシャル(GitHub PAT)をサービスアカウントに登録して以下の様に実行。
kn service create --force helloworld-go --image=$(ko build .) --env TARGET="Go Sample v1" --service-account ghcr
無事、サブドメインからアクセスできる様になります。
当然ですがサーバレスなので、しばらく放置しておくと勝手に眠ってくれます。
かっこいい!
サーバレス Vim
こうなれば Vim でサーバレスしたいですよね。わかります。ただ残念ながら Vim はウェブサーバになる為の機能を持っていません。そこで apache の助けをかります。
#!/bin/bash
TMPFILE=$(mktemp)
trap 'rm -f "$TMPFILE"' EXIT INT TERM HUP
/usr/bin/vim -nNes -c "let &verbose=1" -c "let &viminfo=''" -c "redir!>$TMPFILE" -c "source $1" -c "redir END" -c "qall!"
tail -n +1 $TMPFILE
これをランタイムとして Vim script を CGI として起動できる様にしました。
#!/usr/bin/vim-cgi
echon "Content-Type: text/plain\r\n\r\n"
func s:main() abort
for i in range(1,100)
echo [[i,"Buzz"],["Fizz","FizzBuzz"]][i%3<1][i%5<1]
endfor
endfunc
call s:main()
Dockerfile を書いてビルドし、適当な所に push します。
FROM httpd:2.4.46
RUN apt update && apt install -y vim && apt clean
COPY vim-cgi /usr/bin/vim-cgi
RUN chmod +x /usr/bin/vim-cgi
COPY hello.vim /usr/local/apache2/cgi-bin/hello.vim
RUN chmod +x /usr/local/apache2/cgi-bin/hello.vim
CMD httpd-foreground -c "LoadModule cgid_module modules/mod_cgid.so"
後は apache のポートを指定してサーバレスアプリを作れば OK
kn service create --port 80 --force helloworld-vim --image=$IMAGE --env TARGET="Vim Sample v1"
やった!
おわりに
Vim はサーバレスアプリだったんだよ。
ΩΩΩ<な、なんだってー!?
Discussion