Miroを自宅にも導入しようと思い立ち、自宅オンプレクラスタを持っているのでその上にデプロイしてみたので、その話をしてみる。

Excalidrawとは

OSSで手書き風スタイルのバーチャルホワイトボード。

https://github.com/excalidraw/excalidraw

セルフホストできる Miro オルタナティブを探してた時にたまたま見たサイトで1位になっていたので使ってみた。

AFFiNE でも良かったんだけど、そこまで多機能じゃなくてもいいかなと思った。

Namespace のマニフェストを用意する

Excalidraw用に専用の Namespace を作るマニフェスト。名前やラベルはそのまま Excalidraw にした。

apiVersion: v1
kind: Namespace
metadata:
  name: excalidraw
  labels:
    app.kubernetes.io/name: excalidraw

Deployment のマニフェストを用意する

Pod は何台必要なのかやどのイメージを利用するかを記載した Deployment マニフェストを用意した。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: excalidraw
  namespace: excalidraw
  labels:
    app.kubernetes.io/name: excalidraw
spec:
  replicas: 1
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app.kubernetes.io/name: excalidraw
  template:
    metadata:
      labels:
        app.kubernetes.io/name: excalidraw
    spec:
      automountServiceAccountToken: false
      containers:
        - name: excalidraw
          image: excalidraw/excalidraw:latest@sha256:f7ee194addd607bf831d2af0f0a34463dd4225e426cf35199ef0b12a803398e9
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 5
            periodSeconds: 15
          readinessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 3
            periodSeconds: 10
          resources:
            requests:
              cpu: 10m
              memory: 32Mi
            limits:
              memory: 128Mi

このマニフェストでは個人利用なので Pod は1台で、Docker Hub の excalidraw/excalidraw:latest をインデックスダイジェストをつけて利用している。

また Excalidraw は静的ファイルのみの SPA なので PVC は使用していない。 同じ理由で Kubernetes API も使用しないのでfalseにしてる。

クラスタ内通信を行うために Service マニフェストを用意する

excalidraw Namespace 内にある対象 Pod への接続先を記載した Service マニフェストを用意した。

apiVersion: v1
kind: Service
metadata:
  name: excalidraw
  namespace: excalidraw
  labels:
    app.kubernetes.io/name: excalidraw
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: excalidraw
  ports:
    - name: http
      port: 80
      targetPort: http
      protocol: TCP

Service はクラスタ内部で 80番ポートへの接続を受け付け、対象 Pod の http ポート(この場合はコンテナの80番)へ転送する。

厳密には以下のような流れになる。

クライアントPod → Service:80 → 対象Pod:80 → Excalidrawコンテナ

HTTPリクエストした際の振る舞いを設定するために HTTPRoute マニフェストを用意する

Gatewayリスナーからバックエンドのネットワークエンドポイントに対するHTTPリクエストのルーティングの振る舞いを記載した HTTPRoute マニフェストを用意した。

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: excalidraw
  namespace: excalidraw
spec:
  parentRefs:
    - name: shared-gateway
      namespace: gateway
      sectionName: https
  hostnames:
    - excalidraw.shooonng.com
  rules:
    - backendRefs:
        - name: excalidraw
          port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: excalidraw-redirect
  namespace: excalidraw
spec:
  parentRefs:
    - name: shared-gateway
      namespace: gateway
      sectionName: http
  hostnames:
    - excalidraw.shooonng.com
  rules:
    - filters:
        - type: RequestRedirect
          requestRedirect:
            scheme: https
            statusCode: 301

上段は https://excalidraw.shooonng.com へのリクエストを excalidraw Namespace の excalidraw Service の80番ポートへ転送する。

下段は http://excalidraw.shooonng.com とHTTPリスナーでリクエストが来た場合にHTTPSへリダイレクトする設定。

次のような通信構成になっている。

ブラウザ → shared-gateway の HTTPS リスナー → HTTPRoute → excalidraw Service:80 → Pod:80

構成を管理するために Kustomize マニフェストを用意する

複数のKubernetes マニフェストをまとめ、共通設定を適用して最終的なリソースを生成するために記載した Kustomization マニフェストを用意した。

kind: Kustomization
namespace: excalidraw
resources:
  - namespace.yaml
  - deployment.yaml
  - service.yaml
  - httproute.yaml

excalidraw Namespace に以下のリソースを生成している。

  • Namespace
  • Deployment
  • Service
  • HTTPRoute

マニフェストをデプロイする

GitOpsのためにArgoCDを用意してあるので、GitHubにpushすればそのままデプロイされる。

Podが立ち上がって、Curlが通ればデプロイできている。

# kubectl get pods -n excalidraw
NAME                          READY   STATUS    RESTARTS   AGE
excalidraw-5bc7659688-9txtc   1/1     Running   0          10h
# curl -I https://excalidraw.shooonng.com
HTTP/1.1 200 OK
server: envoy
date: Sat, 17 Oct 2026 15:07:45 GMT
content-type: text/html
content-length: 6843
last-modified: Wed, 06 May 2026 21:19:55 GMT
etag: "69fbb07b-1abb"
accept-ranges: bytes
x-envoy-upstream-service-time: 0