summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CLAUDE.md30
-rw-r--r--Deploy.md2
-rw-r--r--caddy/Caddyfile5
-rwxr-xr-xgit/hooks/hetzner-infra/post-receive1
-rw-r--r--hetzner-admin/app.py67
-rwxr-xr-xhetzner-admin/run-command.sh3
-rwxr-xr-xmail/dns/setup-route53.py1
-rw-r--r--status/targets.txt2
-rwxr-xr-xtests/check.sh4
-rwxr-xr-xuptime-kuma/deploy.sh5
-rw-r--r--uptime-kuma/docker-compose.yml17
11 files changed, 13 insertions, 124 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index 557fcfb..6861c6a 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -14,13 +14,12 @@ mail/ MTA(Postfix + OpenDKIM, docker-compose.yml, install.sh, dns/se
vpn/ strongSwan IKEv2 VPN(Dockerfile, docker-compose.yml, install.sh, renew-cert.sh)
status/ 軽量ステータスページ(app.py, targets.txt, Dockerfile, docker-compose.yml, deploy.sh)
ntfy/ ntfy 通知サーバー(server.yml, docker-compose.yml, deploy.sh, install.sh)
-uptime-kuma/ Uptime Kuma 監視(docker-compose.yml, deploy.sh)
server/ サーバー共通設定(authorized_keys, requirements.md)
```
## デプロイの仕組み
-- `git push origin master` → post-receive フック → `caddy/deploy.sh` / `cgit/deploy.sh` / `mail/deploy.sh` / `hetzner-admin/deploy.sh` / `vpn/deploy.sh` / `ntfy/deploy.sh` / `status/deploy.sh` / `uptime-kuma/deploy.sh` を実行
+- `git push origin master` → post-receive フック → `caddy/deploy.sh` / `cgit/deploy.sh` / `mail/deploy.sh` / `hetzner-admin/deploy.sh` / `vpn/deploy.sh` / `ntfy/deploy.sh` / `status/deploy.sh` を実行
- cgit の `lighttpd.conf` / `Dockerfile` 変更はイメージ再ビルドが必要(`deploy.sh` で `--build` するため push だけで反映される)
- cgit の `cgitrc` はボリュームマウントのため push だけで反映される
- フック・リポジトリの変更は push 後に `bash /app/infra/git/install.sh` を手動実行
@@ -128,32 +127,21 @@ docker compose -f /app/infra/vpn/docker-compose.yml restart
vi caddy/docker-compose.yml # PROXY_NO_AUTH=1 を削除(VPN と同じ users.conf の Basic 認証に戻る)
git push origin master
-# ステータスページ(3 種類)
-# - status.yyamashita.com : 公開ステータスページ(status/)。監視対象は status/targets.txt
-# JSON は /api/status。targets.txt は push だけで反映(次回チェック時に再読込)
-# - uptime.yyamashita.com : Uptime Kuma(uptime-kuma/)。監視設定・通知は Web UI で行う(データは volume に永続化)
-# - hetzner-admin /status : 認証付き。status-app の結果 + docker ps -a のコンテナ状態
+# ステータスページ(status.yyamashita.com, status/)
+# 監視対象は status/targets.txt(push だけで反映、次回チェック時に再読込)。JSON は /api/status
+# ntfy(ntfy.yyamashita.com, 認証必須)のトピック "status" に通知する:
+# - 連続 2 回失敗で DOWN、復旧で recovered
+# - 毎朝 8:00 JST に稼働率サマリー(status/.env に SUMMARY_HOUR=-1 で無効)
vi status/targets.txt && git push origin master
-# ステータスページ初回セットアップ
+# 初回セットアップ(済み。再構築時の手順)
git push origin master
ssh root@localhost 'bash /app/infra/git/install.sh' # post-receive フック更新
-ssh root@localhost 'bash /app/infra/status/deploy.sh && bash /app/infra/uptime-kuma/deploy.sh'
-python3 /app/infra/mail/dns/setup-route53.py # status / uptime の CNAME 追加
-# → 直後に https://uptime.yyamashita.com を開いて管理者アカウントを作成する
-# (初回アクセスした人が管理者になるため、放置しないこと)
-
-# ntfy 通知(ntfy.yyamashita.com)初回セットアップ
-# 認証必須(deny-all)。status-app が連続 2 回失敗で DOWN、復旧で recovered をトピック "status" に通知
-# 毎朝 8:00 JST に前日分の稼働率サマリーも送る(status/docker-compose.yml で SUMMARY_HOUR=-1 にすると無効)
-git push origin master
-ssh root@localhost 'bash /app/infra/git/install.sh' # post-receive フック更新
-python3 /app/infra/mail/dns/setup-route53.py # ntfy の CNAME 追加
+ssh root@localhost 'bash /app/infra/ntfy/deploy.sh && bash /app/infra/status/deploy.sh'
+# DNS(status / ntfy の CNAME): 下の「DNS 登録」参照
ssh -t root@localhost 'bash /app/infra/ntfy/install.sh' # 管理ユーザー作成(パスワード入力)
# → トークンを発行して status/.env に NTFY_URL / NTFY_TOKEN を書き込み、status-app を再起動
# スマホ: ntfy アプリでサーバー https://ntfy.yyamashita.com を追加し、トピック status を購読(admin でログイン)
-# Uptime Kuma: Settings → Notifications → ntfy
-# Server URL: http://ntfy / Topic: status / Access Token: ntfy token add admin で発行したもの
# ntfy テスト送信
ssh root@localhost 'docker exec ntfy ntfy publish --token "$(grep NTFY_TOKEN /app/infra/status/.env | cut -d= -f2)" http://localhost/status "test"'
diff --git a/Deploy.md b/Deploy.md
index a709a25..91dc319 100644
--- a/Deploy.md
+++ b/Deploy.md
@@ -17,7 +17,7 @@ ssh root@<server-ip> 'bash /app/infra/git/install.sh'
## 通常デプロイ
```bash
-git push origin master # caddy / cgit / mail / hetzner-admin / vpn / ntfy / status / uptime-kuma を再デプロイ(自動再ビルド含む)
+git push origin master # caddy / cgit / mail / hetzner-admin / vpn / ntfy / status を再デプロイ(自動再ビルド含む)
```
push 前に `bash tests/check.sh` で整合性チェックを実行できる。
diff --git a/caddy/Caddyfile b/caddy/Caddyfile
index d90d804..682df2d 100644
--- a/caddy/Caddyfile
+++ b/caddy/Caddyfile
@@ -54,11 +54,6 @@ status.yyamashita.com {
reverse_proxy status-app:8080
}
-uptime.yyamashita.com {
- import noindex
- reverse_proxy uptime-kuma:3001
-}
-
ntfy.yyamashita.com {
import noindex
reverse_proxy ntfy:80
diff --git a/git/hooks/hetzner-infra/post-receive b/git/hooks/hetzner-infra/post-receive
index bbc792a..892008b 100755
--- a/git/hooks/hetzner-infra/post-receive
+++ b/git/hooks/hetzner-infra/post-receive
@@ -8,5 +8,4 @@ bash /app/infra/hetzner-admin/deploy.sh
bash /app/infra/vpn/deploy.sh
bash /app/infra/ntfy/deploy.sh
bash /app/infra/status/deploy.sh
-bash /app/infra/uptime-kuma/deploy.sh
echo "Deploy complete: hetzner-infra"
diff --git a/hetzner-admin/app.py b/hetzner-admin/app.py
index 274364e..abef6a9 100644
--- a/hetzner-admin/app.py
+++ b/hetzner-admin/app.py
@@ -1,7 +1,5 @@
import functools
-import json
import os
-import urllib.request
import paramiko
from flask import Flask, Response, render_template_string, request
@@ -14,10 +12,6 @@ SSH_HOST = os.environ.get("SSH_HOST", "host.docker.internal")
SSH_PORT = int(os.environ.get("SSH_PORT", "22"))
SSH_USER = os.environ.get("SSH_USER", "root")
SSH_KEY_PATH = os.environ.get("SSH_KEY_PATH", "/app/id_rsa")
-STATUS_API = os.environ.get("STATUS_API", "http://status-app:8080/api/status")
-
-# /status ページでコンテナ一覧を取るコマンド(run-command.sh のホワイトリストに要登録)
-STATUS_COMMAND = "docker ps -a --format '{{.Names}}|{{.Status}}'"
COMMANDS = [
("Docker Status", "docker ps"),
@@ -36,7 +30,7 @@ HTML = """<!DOCTYPE html>
<head><title>Server Admin</title></head>
<body>
<h3>Server Admin</h3>
-<p><a href="/status">Status</a></p>
+<p><a href="https://status.yyamashita.com">Status</a></p>
{% for label, _ in commands %}
<form method="post" action="/run" style="display:block;margin-bottom:8px">
<input type="hidden" name="cmd" value="{{ label }}">
@@ -54,40 +48,6 @@ HTML = """<!DOCTYPE html>
</body>
</html>"""
-STATUS_HTML = """<!DOCTYPE html>
-<html>
-<head><title>Server Status</title><meta http-equiv="refresh" content="60"></head>
-<body>
-<h3>Server Status</h3>
-<p><a href="/">&larr; Admin</a></p>
-<h4>Services</h4>
-{% if services is none %}
-<pre>{{ services_error }}</pre>
-{% else %}
-<table border="1" cellpadding="4" style="border-collapse:collapse">
-<tr><th>Service</th><th>State</th><th>Detail</th><th>Latency</th><th>Uptime</th></tr>
-{% for s in services %}
-<tr><td>{{ s.name }}</td>
-<td style="color:{{ 'green' if s.up else 'red' }}">{{ 'UP' if s.up else 'DOWN' }}</td>
-<td>{{ s.detail }}</td><td>{{ s.ms }} ms</td><td>{{ s.uptime }}%</td></tr>
-{% endfor %}
-</table>
-{% endif %}
-<h4>Containers</h4>
-{% if containers is none %}
-<pre>{{ containers_error }}</pre>
-{% else %}
-<table border="1" cellpadding="4" style="border-collapse:collapse">
-<tr><th>Container</th><th>Status</th></tr>
-{% for name, st in containers %}
-<tr><td>{{ name }}</td>
-<td style="color:{{ 'green' if st.startswith('Up') and '(unhealthy)' not in st else 'red' }}">{{ st }}</td></tr>
-{% endfor %}
-</table>
-{% endif %}
-</body>
-</html>"""
-
def requires_auth(f):
@functools.wraps(f)
@@ -135,28 +95,3 @@ def run():
except Exception as e:
output = f"Error: {e}"
return render_template_string(HTML, commands=COMMANDS, output=output)
-
-
-@app.route("/status")
-@requires_auth
-def status():
- services = services_error = containers = containers_error = None
- try:
- with urllib.request.urlopen(STATUS_API, timeout=5) as r:
- services = json.load(r)["services"]
- except Exception as e:
- services_error = f"status-app に接続できない: {e}"
- try:
- out = run_ssh(STATUS_COMMAND)
- containers = sorted(
- tuple(line.split("|", 1)) for line in out.splitlines() if "|" in line
- )
- except Exception as e:
- containers_error = f"Error: {e}"
- return render_template_string(
- STATUS_HTML,
- services=services,
- services_error=services_error,
- containers=containers,
- containers_error=containers_error,
- )
diff --git a/hetzner-admin/run-command.sh b/hetzner-admin/run-command.sh
index 12f96bf..0ed9fe3 100755
--- a/hetzner-admin/run-command.sh
+++ b/hetzner-admin/run-command.sh
@@ -6,9 +6,6 @@ case "$SSH_ORIGINAL_COMMAND" in
"docker ps")
exec docker ps
;;
- "docker ps -a --format '{{.Names}}|{{.Status}}'")
- exec docker ps -a --format '{{.Names}}|{{.Status}}'
- ;;
"docker compose -f /app/infra/caddy/docker-compose.yml restart")
exec docker compose -f /app/infra/caddy/docker-compose.yml restart
;;
diff --git a/mail/dns/setup-route53.py b/mail/dns/setup-route53.py
index 0b5d569..54eadb2 100755
--- a/mail/dns/setup-route53.py
+++ b/mail/dns/setup-route53.py
@@ -39,7 +39,6 @@ WEB_SUBDOMAINS = [
"blog",
"todo",
"status",
- "uptime",
"ntfy",
"vpn",
"proxy",
diff --git a/status/targets.txt b/status/targets.txt
index 5ee1dcc..b9631c9 100644
--- a/status/targets.txt
+++ b/status/targets.txt
@@ -9,7 +9,7 @@ todo https://todo.yyamashita.com
git https://git.yyamashita.com
code https://code.yyamashita.com
hetzner-admin https://hetzner-admin.yyamashita.com
-uptime https://uptime.yyamashita.com
+status https://status.yyamashita.com
vpn https://vpn.yyamashita.com
ntfy http://ntfy/v1/health
mail tcp://mail:25
diff --git a/tests/check.sh b/tests/check.sh
index 85d19e5..a19b55e 100755
--- a/tests/check.sh
+++ b/tests/check.sh
@@ -67,7 +67,7 @@ while IFS=: read -r user repo; do
done < claude/sessions.txt
echo ""
-echo "=== hetzner-admin: app.py の COMMANDS / STATUS_COMMAND が run-command.sh で許可されているか ==="
+echo "=== hetzner-admin: app.py の COMMANDS が run-command.sh で許可されているか ==="
while IFS= read -r cmd; do
if grep -qF "\"$cmd\")" hetzner-admin/run-command.sh; then
ok "$cmd"
@@ -81,8 +81,6 @@ for node in ast.walk(tree):
if isinstance(node, ast.Assign) and getattr(node.targets[0], "id", "") == "COMMANDS":
for elt in node.value.elts:
print(elt.elts[1].value)
- if isinstance(node, ast.Assign) and getattr(node.targets[0], "id", "") == "STATUS_COMMAND":
- print(node.value.value)
EOF
)
diff --git a/uptime-kuma/deploy.sh b/uptime-kuma/deploy.sh
deleted file mode 100755
index c07b9e8..0000000
--- a/uptime-kuma/deploy.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-cd "$(dirname "$0")"
-docker compose pull -q
-docker compose up -d
diff --git a/uptime-kuma/docker-compose.yml b/uptime-kuma/docker-compose.yml
deleted file mode 100644
index d166ce2..0000000
--- a/uptime-kuma/docker-compose.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-services:
- uptime-kuma:
- image: louislam/uptime-kuma:2
- container_name: uptime-kuma
- restart: unless-stopped
- volumes:
- - uptime_kuma_data:/app/data
- networks:
- - web
-
-networks:
- web:
- external: true
- name: web
-
-volumes:
- uptime_kuma_data: