TiltとTelepresenceによるKubernetes開発ループの高速化
前回の記事「k3sでリモートアクセス可能なKubernetesホームラボを構築」では、インターネット経由でアクセス可能なk3sベースのホームラボを構築しました。今回は、その環境の上に高速開発ループを構築します。
前提条件
- Kubernetes環境 (前回記事を参照)
- ホームサーバーへのSSHアクセス
- kubectlとHelmの基本知識
開発用コンテナレジストリ
まずコンテナイメージをビルドしてプッシュする環境を整えます。
自宅サーバー上のk3sクラスター内にプライベートレジストリがある前提です。
自宅ネットワークの外からでもCloudflare Tunnelで多くのサービスにアクセスできますが、レジストリには向きません。Cloudflare Tunnel越しに大きなデータをpushすることはできないからです。イメージを公開する場合はパブリックレジストリが必要です。一方、開発用途ではSSH経由のリモートDocker(DOCKER_HOST
)を使えば、クラスター内のプライベートレジストリへプッシュできます。
前回記事でホームネットワークへのSSHアクセスを用意しました。これを使ってホームサーバー上のDockerデーモンに接続します。~/.ssh/config
の例 (buun.dev
は自分のホスト名に置き換え):
Host buun.dev
Hostname ssh.buun.dev
ProxyCommand /opt/homebrew/bin/cloudflared access ssh --hostname %h
内部的にはbuun.dev
へのSSHセッションが開かれ、Docker Engine APIがトンネルされます。CLIの視点ではdocker build
, docker push
, docker run
はすべてリモートエンジンに対する操作です。ビルドをオフロードできるのでノートPCのバッテリー節約にもなります。
イメージはlocalhost:30500/
プレフィックスでタグ付けします。これはホームサーバーにとってクラスターの内外から到達可能なアドレスです。docker push
はリモートホストのネットワーク視点で実行されるため、そのホストからレジストリに到達できれば、ノートPCから直接レジストリに届かなくてもプッシュは成功します。
サンプルアプリのビルド、プッシュ、デプロイ
sample-web-appを使います。これはNext.js + Drizzle ORM + PostgreSQLのシンプルなアプリです。
git clone https://github.com/buun-ch/sample-web-app
cd sample-web-app
export DOCKER_HOST=ssh://buun.dev
docker build -t localhost:30500/buun-ch/sample-web-app:latest .
docker push localhost:30500/buun-ch/sample-web-app:latest
これでイメージをビルドし、プライベートレジストリへプッシュできます。先述の通り、localhost:30500/...
タグはクラスターの内外から到達できます。
次にDBとユーザーを作成します。buun-stackを使っている場合は次のコマンドを実行します。
cd /path/to/buun-stack
just postgres::create-user-and-db
開発用のvalues.yaml
を作成します。
image:
imageRegistry: localhost:30500/buun-ch
repository: sample-web-app
tag: latest
pullPolicy: Always
env:
- name: DATABASE_URL
value: postgresql://todo:todopass@postgres-cluster-rw.postgres:5432/todo
migration:
enabled: true
env:
- name: DATABASE_URL
value: postgresql://todo:todopass@postgres-cluster-rw.postgres:5432/todo
Helmチャートをデプロイします。
kubectl create namespace sample
helm upgrade --install sample-web-app ./charts/sample-web-app -n sample --wait -f values.yaml
リリースの状態を確認します。
kubectl get all -n sample
Telepresence: ポートフォワードなしでクラスター内DNS/ルーティング
毎回kubectl port-forward
してブラウザを開くのは手間で、DNSも統合されません。TelepresenceはローカルプロセスをKubernetes内で動いているかのようにクラスターのネットワークへ参加させます。接続後はノートPCがサービス名を解決し、Telepresence経由でトラフィックをルーティングできるため、DNS名で直接アクセスできます。
- 初回のみ: Traffic Managerをインストール
telepresence helm install
- 接続:
telepresence connect
これで<service>.<namespace>
のようなDNS名でサービスに直接アクセスできます。DbGateなどのGUIツールもそのまま動作します。作業終了時は切断します:
telepresence quit
開発オーケストレーター: Skaffold, Tilt, DevSpace
TelepresenceでネットワークとDNSが整ったら、次のボトルネックは開発ループです。すなわち、複数サービスに対するbuild, tag, push, デプロイを何度も繰り返すワークフローです。
理想的な開発ループ:
- インクリメンタルビルド(変更箇所だけをビルド)
- ライブ同期が即時(ローカルの編集がコンテナに即反映)
- フィードバックの統合(ログやフォワードを1か所に集約)
これらを満たすツールとして、Skaffold, Tilt, DevSpaceがあります。以下は最小構成の例(概念的なサンプル)です。
Skaffold
apiVersion: skaffold/v4beta6
kind: Config
metadata:
name: sample-web-app
build:
artifacts:
- image: localhost:30500/sample-web-app
context: ./apps/sample-web-app
deploy:
helm:
releases:
- name: sample-web-app
chartPath: charts/sample-web-app
valuesFiles:
- values.dev.yaml
Tilt
# Tiltfile (Starlark)
docker_build('localhost:30500/sample-web-app', './apps/sample-web-app')
# 速い: 編集を同期し、必要に応じてコンテナ内でコマンド実行
live_update(
'localhost:30500/sample-web-app',
[
sync('./apps/sample-web-app', '/app'),
run('npm install', trigger=['package.json', 'package-lock.json'])
],
)
# マニフェストのレンダリングと適用(HelmまたはYAML)
k8s_yaml(local('helm template sample-web-app charts/sample-web-app -f values.dev.yaml'))
DevSpace
version: v2beta1
name: sample-web-app
images:
app:
image: localhost:30500/sample-web-app
context: ./apps/sample-web-app
deployments:
app:
helm:
chart:
path: charts/sample-web-app
values:
image:
repository: localhost:30500/sample-web-app
tag: dev
dev:
app:
imageSelector: localhost:30500/sample-web-app
sync:
- path: ./apps/sample-web-app:/app
ports:
- port: 3000
forward: 3000
注: これらは実行可能な完全設定ではなく、概念を示す例です。
以下は各ツールのGitHub Starの推移です。
それぞれ特長がありますが、柔軟性と使いやすさから、ここではTiltを推奨します。
Tiltfileの概要
sample-web-appのTiltfileは次のような構成です。
allow_k8s_contexts(k8s_context())
config.define_string('registry')
config.define_bool('port-forward')
config.define_string('extra-values-file')
config.define_bool('enable-health-logs')
cfg = config.parse()
registry = cfg.get('registry', 'localhost:30500')
default_registry(registry)
docker_build(
'sample-web-app-dev',
'.',
dockerfile='Dockerfile.dev',
live_update=[
sync('.', '/app'),
run('pnpm install', trigger=['./package.json', './pnpm-lock.yaml']),
]
)
values_files = ['./charts/sample-web-app/values-dev.yaml']
extra_values_file = cfg.get('extra-values-file', '')
if extra_values_file:
values_files.append(extra_values_file)
print("📝 Using extra values file: " + extra_values_file)
helm_set_values = []
enable_health_logs = cfg.get('enable-health-logs', False)
if enable_health_logs:
helm_set_values.append('logging.health_request=true')
print("📵 Health check request logs enabled")
helm_release = helm(
'./charts/sample-web-app',
name='sample-web-app',
values=values_files,
set=helm_set_values,
)
k8s_yaml(helm_release)
enable_port_forwards = cfg.get('port-forward', False)
k8s_resource(
'sample-web-app',
port_forwards='13000:3000' if enable_port_forwards else [],
)
if enable_port_forwards:
print("🚀 Access your application at: http://localhost:13000")
- いくつかのコマンドライン引数(レジストリなど)を定義
- イメージのビルド(
.dockerignore
を尊重、アプリ編集はlive_updateの同期で高速反映) - Helmでデプロイ(環境に応じてvaluesを切替)
Tiltの動作例
tilt up
を実行してみましょう。CLIがブラウザのTilt UIを開くよう促し、Kubernetesリソースの状態を一覧できます。
-
sample-web-app
リソースを選択すると、docker build
/docker push
/ Helmリリースのログがまとまって見られます。 - このデモでは前回からコード変更が無いため
docker build
は即完了。 - アプリがデプロイされたらブラウザで到達性を確認。
- Live Update: タイトルを「ToDo App Test」に変更して保存すると、手動操作無しで即反映されます。
- Dockerfileを変更すると、差分レイヤーのみ再ビルド。push完了後に自動で再デプロイされます。
- Helmのvaluesを更新すると、レンダリング・適用が自動で行われ、ロールアウト完了後に新しい設定が有効になります。
Kubernetes CLI生産性向上
日々の作業でキー入力を減らし、見やすく、すばやく状態を把握するための設定です。~/.zshrc
に追記して使えます。
オートコンプリート
リソース種別・名前の打ち間違いを減らし、操作を高速化します。zshを使用している場合は次を追加します。
autoload -Uz compinit
compinit
eval "$(kubectl completion zsh)"
使い方: kubectl get pod <TAB>
のようにTabで補完できます。
kubecolor + エイリアス
色付き出力でテーブルが読みやすくなります。kubectl
をkubecolor
にエイリアスし、補完の紐付けも維持します。
alias kubectl=kubecolor
compdef kubecolor=kubectl
alias k=kubecolor
compdef kubecolor=kubectl
短いエイリアスk
は頻繁な利用で有効です。
zshグローバルエイリアス
zshのグローバルエイリアスは行のどこでも展開できます。kubectlの可読性を高めます。
# YAMLをbatで強調表示
alias -g Y='-o yaml | bat -l yaml'
# ワイド表示
alias -g W='-o wide'
# Neovimの読み取り専用バッファでYAMLを開く
alias -g YE='-o yaml | nvim -c ":set ft=yaml" -R'
使用例:
# 通常(エイリアス無し)
kubectl get deploy sample-web-app -o yaml | bat -l yaml
# 強調表示付きでYAMLを表示
kubectl get deploy sample-web-app Y
# ワイド表示
kubectl get pods W
W
は-o wide
の短縮です。YE
はY
と同様ですが、ページャーの代わりにNeovimで開きます。
viddyで監視
KDashやk9sのようなダッシュボードも便利ですが、素早い確認にはkubectl + viddyをよく使います。viddy
はコマンドを再実行し、出力の変化をハイライト表示、過去のタイムスタンプの出力も遡って閲覧できます。
kubectlと組み合わせるためのエイリアス:
alias vk='viddy -dtw kubectl'
コンテキスト/ネームスペース切替
kubectxとkubensでコンテキストやネームスペースの切替を素早く行えます。
sternで複数Podのログを一括確認
sternは複数のPod(およびコンテナー)のログを同時にストリームできるKubernetes向けログテーラーです。ラベルセレクターや正規表現、ネームスペース横断、色分け・グルーピングに対応し、デプロイや障害対応の追跡に役立ちます。
# ラベルで絞り込み(名前空間を指定)
stern -n default -l app=sample-web-app
# 複数アプリを正規表現で、タイムスタンプ付き
stern -n default 'web|api' -t
# 直近5分のみ、素の行で
stern -n default -l app=sample-web-app --since 5m -o raw
# Pod 内の特定コンテナーにフォーカス
stern -n default -l app=sample-web-app -c web
まとめ
本記事では、Kubernetes上での開発ループの高速化をおこないました。
- SSH経由のリモートDockerでプライベートレジストリへpush
- TelepresenceによるDNS/ルーティングの統合(ポートフォワード不要)
- Tiltによる監視・増分ビルド・ライブ同期・自動デプロイ
これらにより、Kubernetes開発はより速く、よりミスに強くなります。
Discussion