Add deploy and uninstall flows for process and docker modes

This commit is contained in:
Haitao Pan 2026-03-15 16:56:55 +08:00
parent d59a8a0415
commit 7a5e9fda08
8 changed files with 570 additions and 33 deletions

View File

@ -21,9 +21,25 @@ Cloud Neutral Toolkit 的账号与身份服务 (Account Service).
```bash
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
| bash -s -- accounts.svc.plus
| bash -s -- accounts.svc.plus --mode process --deploy
```
Docker 部署模式:
```bash
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
| bash -s -- accounts.svc.plus --mode docker --deploy
```
Cloud Run 部署模式:
```bash
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
| bash -s -- accounts.svc.plus --mode cloudrun
```
单机 `process` / `docker` 模式默认会写入 Caddy 站点配置到 `/etc/caddy/conf.d/accounts.svc.plus.conf`,并反向代理到本机 `127.0.0.1:8080`
### 本地运行 (Local Dev)
```bash
@ -54,4 +70,3 @@ make dev
- API 参考:`docs/api/overview.md`
- 运维:`docs/operations/monitoring.md`, `docs/operations/troubleshooting.md`
- Runbooks`docs/Runbook/README.md`

View File

@ -2,6 +2,22 @@
本项目支持本地编译运行、Docker 容器运行,以及 Cloud Run 部署。以下步骤基于仓库现有脚本与配置。
一键初始化脚本支持按部署模式选择:
```bash
# 进程部署模式
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
| bash -s -- accounts.svc.plus --mode process
# Docker 部署模式
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
| bash -s -- accounts.svc.plus --mode docker
# Cloud Run 部署模式
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
| bash -s -- accounts.svc.plus --mode cloudrun
```
## 本地安装Go
前置条件:
@ -41,7 +57,8 @@ docker run --rm -p 8080:8080 \
## Cloud Run
参考文件:
- `deploy/gcp/cloud-run/service.yaml`
- `deploy/gcp/cloud-run/prod-service.yaml`
- `deploy/gcp/cloud-run/preview-service.yaml`
- `deploy/gcp/cloud-run/stunnel.conf`
- `config/account.cloudrun.yaml`

View File

@ -5,23 +5,42 @@
推荐通过 Makefile 与脚本执行:
```bash
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
| bash -s -- accounts.svc.plus --mode process --deploy
make init-db
make build
make start
```
默认启动脚本 `scripts/start.sh` 使用 `config/account.yaml`
单机部署脚本 `scripts/install-process.sh` 默认写入 Caddy 配置到 `/etc/caddy/conf.d/accounts.svc.plus.conf`
## Docker
详见 `getting-started/installation.md`
初始化命令:
```bash
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
| bash -s -- accounts.svc.plus --mode docker --deploy
```
## Cloud Run
仓库内的 Cloud Run 配置:
- `deploy/gcp/cloud-run/service.yaml`
- `deploy/gcp/cloud-run/prod-service.yaml`
- `deploy/gcp/cloud-run/preview-service.yaml`
- `config/account.cloudrun.yaml`
初始化命令:
```bash
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
| bash -s -- accounts.svc.plus --mode cloudrun
```
特点:
- 通过 `entrypoint.sh` + `CONFIG_TEMPLATE` 注入配置
- 附带 stunnel sidecar用于安全连接数据库

155
scripts/install-docker.sh Normal file
View File

@ -0,0 +1,155 @@
#!/usr/bin/env bash
set -euo pipefail
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_common.sh"
if [[ "$(uname -s)" != "Linux" ]]; then
echo "docker deployment is only supported on Linux" >&2
exit 1
fi
if [[ "${EUID}" -ne 0 ]]; then
echo "docker deployment must run as root" >&2
exit 1
fi
need_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "missing required command: $1" >&2
exit 1
fi
}
need_cmd docker
need_cmd systemctl
need_cmd install
need_cmd curl
SERVICE_NAME="${SERVICE_NAME:-accounts-svc-plus-docker}"
CONTAINER_NAME="${CONTAINER_NAME:-accounts-svc-plus}"
IMAGE_TAG="${IMAGE_TAG:-accounts-svc-plus:local}"
DOMAIN="${ACCOUNT_DOMAIN:-accounts.svc.plus}"
LISTEN_ADDR="${ACCOUNT_LISTEN_ADDR:-127.0.0.1:8080}"
PUBLIC_URL="${ACCOUNT_PUBLIC_URL:-https://${DOMAIN}}"
CONFIG_DIR="${CONFIG_DIR:-/etc/accounts.svc.plus}"
CONFIG_PATH="${CONFIG_PATH:-${CONFIG_DIR}/account.standalone.yaml}"
SYSTEMD_UNIT_PATH="${SYSTEMD_UNIT_PATH:-/etc/systemd/system/${SERVICE_NAME}.service}"
CADDY_CONF_DIR="${CADDY_CONF_DIR:-/etc/caddy/conf.d}"
CADDY_CONF_PATH="${CADDY_CONF_PATH:-${CADDY_CONF_DIR}/${DOMAIN}.conf}"
DOCKER_BIN="$(command -v docker)"
install -d -m 0755 "${CONFIG_DIR}" "${CADDY_CONF_DIR}"
cat > "${CONFIG_PATH}" <<EOF
mode: "server-agent"
log:
level: info
auth:
enable: true
token:
publicToken: "standalone-public-token"
refreshSecret: "standalone-refresh-secret"
accessSecret: "standalone-access-secret"
accessExpiry: "1h"
refreshExpiry: "168h"
server:
addr: ":8080"
readTimeout: 15s
writeTimeout: 15s
publicUrl: "${PUBLIC_URL}"
allowedOrigins:
- "${PUBLIC_URL}"
- "http://localhost:8080"
- "http://127.0.0.1:8080"
store:
driver: "memory"
dsn: ""
session:
ttl: 24h
smtp:
host: ""
port: 587
username: ""
password: ""
from: ""
replyTo: ""
timeout: 10s
tls:
mode: "auto"
insecureSkipVerify: false
xray:
sync:
enabled: false
interval: 5m
outputPath: ""
templatePath: ""
validateCommand: []
restartCommand: []
agent:
id: "account-primary"
controllerUrl: "http://127.0.0.1:8080"
apiToken: "standalone-agent-token"
httpTimeout: 15s
statusInterval: 1m
syncInterval: 5m
tls:
insecureSkipVerify: false
agents:
credentials:
- id: "account-primary"
name: "Account Server (docker)"
token: "standalone-agent-token"
groups:
- "default"
EOF
cat > "${SYSTEMD_UNIT_PATH}" <<EOF
[Unit]
Description=Accounts Service Plus (docker mode)
After=docker.service network-online.target
Requires=docker.service
Wants=network-online.target
[Service]
Type=simple
Restart=always
RestartSec=3
ExecStartPre=-${DOCKER_BIN} rm -f ${CONTAINER_NAME}
ExecStart=${DOCKER_BIN} run --rm --name ${CONTAINER_NAME} -p ${LISTEN_ADDR}:8080 -v ${CONFIG_PATH}:/etc/xcontrol/account.yaml:ro ${IMAGE_TAG}
ExecStop=${DOCKER_BIN} stop ${CONTAINER_NAME}
[Install]
WantedBy=multi-user.target
EOF
cat > "${CADDY_CONF_PATH}" <<EOF
${DOMAIN} {
encode zstd gzip
reverse_proxy ${LISTEN_ADDR}
}
EOF
docker build -t "${IMAGE_TAG}" .
systemctl daemon-reload
systemctl enable --now "${SERVICE_NAME}.service"
if systemctl is-active --quiet caddy; then
systemctl reload caddy || systemctl restart caddy
fi
curl -fsS "http://${LISTEN_ADDR}/healthz" >/dev/null
echo "docker deployment complete"
echo "service: ${SERVICE_NAME}.service"
echo "image: ${IMAGE_TAG}"
echo "caddy: ${CADDY_CONF_PATH}"

163
scripts/install-process.sh Normal file
View File

@ -0,0 +1,163 @@
#!/usr/bin/env bash
set -euo pipefail
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_common.sh"
if [[ "$(uname -s)" != "Linux" ]]; then
echo "process deployment is only supported on Linux" >&2
exit 1
fi
if [[ "${EUID}" -ne 0 ]]; then
echo "process deployment must run as root" >&2
exit 1
fi
need_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "missing required command: $1" >&2
exit 1
fi
}
need_cmd systemctl
need_cmd install
need_cmd curl
if ! command -v go >/dev/null 2>&1; then
bash scripts/init-go.sh
fi
need_cmd go
SERVICE_NAME="${SERVICE_NAME:-accounts-svc-plus}"
DOMAIN="${ACCOUNT_DOMAIN:-accounts.svc.plus}"
LISTEN_ADDR="${ACCOUNT_LISTEN_ADDR:-127.0.0.1:8080}"
PUBLIC_URL="${ACCOUNT_PUBLIC_URL:-https://${DOMAIN}}"
CONFIG_DIR="${CONFIG_DIR:-/etc/accounts.svc.plus}"
CONFIG_PATH="${CONFIG_PATH:-${CONFIG_DIR}/account.standalone.yaml}"
BINARY_PATH="${BINARY_PATH:-/usr/local/bin/accounts-svc-plus}"
SYSTEMD_UNIT_PATH="${SYSTEMD_UNIT_PATH:-/etc/systemd/system/${SERVICE_NAME}.service}"
CADDY_CONF_DIR="${CADDY_CONF_DIR:-/etc/caddy/conf.d}"
CADDY_CONF_PATH="${CADDY_CONF_PATH:-${CADDY_CONF_DIR}/${DOMAIN}.conf}"
tmp_binary="$(mktemp)"
trap 'rm -f "${tmp_binary}"' EXIT
go build -o "${tmp_binary}" ./cmd/accountsvc/main.go
install -d -m 0755 "$(dirname "${BINARY_PATH}")" "${CONFIG_DIR}" "${CADDY_CONF_DIR}"
install -m 0755 "${tmp_binary}" "${BINARY_PATH}"
cat > "${CONFIG_PATH}" <<EOF
mode: "server-agent"
log:
level: info
auth:
enable: true
token:
publicToken: "standalone-public-token"
refreshSecret: "standalone-refresh-secret"
accessSecret: "standalone-access-secret"
accessExpiry: "1h"
refreshExpiry: "168h"
server:
addr: "${LISTEN_ADDR}"
readTimeout: 15s
writeTimeout: 15s
publicUrl: "${PUBLIC_URL}"
allowedOrigins:
- "${PUBLIC_URL}"
- "http://localhost:8080"
- "http://127.0.0.1:8080"
store:
driver: "memory"
dsn: ""
session:
ttl: 24h
smtp:
host: ""
port: 587
username: ""
password: ""
from: ""
replyTo: ""
timeout: 10s
tls:
mode: "auto"
insecureSkipVerify: false
xray:
sync:
enabled: false
interval: 5m
outputPath: ""
templatePath: ""
validateCommand: []
restartCommand: []
agent:
id: "account-primary"
controllerUrl: "http://${LISTEN_ADDR}"
apiToken: "standalone-agent-token"
httpTimeout: 15s
statusInterval: 1m
syncInterval: 5m
tls:
insecureSkipVerify: false
agents:
credentials:
- id: "account-primary"
name: "Account Server (standalone)"
token: "standalone-agent-token"
groups:
- "default"
EOF
cat > "${SYSTEMD_UNIT_PATH}" <<EOF
[Unit]
Description=Accounts Service Plus (process mode)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
DynamicUser=yes
StateDirectory=${SERVICE_NAME}
WorkingDirectory=/var/lib/${SERVICE_NAME}
Environment=HOME=/var/lib/${SERVICE_NAME}
ExecStart=${BINARY_PATH} --config ${CONFIG_PATH}
Restart=on-failure
RestartSec=3
NoNewPrivileges=yes
[Install]
WantedBy=multi-user.target
EOF
cat > "${CADDY_CONF_PATH}" <<EOF
${DOMAIN} {
encode zstd gzip
reverse_proxy ${LISTEN_ADDR}
}
EOF
systemctl daemon-reload
systemctl enable --now "${SERVICE_NAME}.service"
if systemctl is-active --quiet caddy; then
systemctl reload caddy || systemctl restart caddy
fi
curl -fsS "http://${LISTEN_ADDR}/healthz" >/dev/null
echo "process deployment complete"
echo "service: ${SERVICE_NAME}.service"
echo "config: ${CONFIG_PATH}"
echo "caddy: ${CADDY_CONF_PATH}"

View File

@ -4,14 +4,23 @@ set -euo pipefail
usage() {
cat <<'EOF'
Usage:
setup.sh <repo_name_or_dir> [--repo <git_url>] [--ref <git_ref>] [--dir <path>]
setup.sh <repo_name_or_dir> [--mode <process|docker|cloudrun>] [--deploy|--uninstall] [--repo <git_url>] [--ref <git_ref>] [--dir <path>]
Examples:
# Remote install:
# curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/<repo>/main/scripts/setup.sh?$(date +%s)" | bash -s -- <repo>
# Remote install (process mode):
# curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/<repo>/main/scripts/setup.sh?$(date +%s)" | bash -s -- <repo> --mode process --deploy
#
# Remote install (docker mode):
# curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/<repo>/main/scripts/setup.sh?$(date +%s)" | bash -s -- <repo> --mode docker --deploy
#
# Remote install (Cloud Run mode):
# curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/<repo>/main/scripts/setup.sh?$(date +%s)" | bash -s -- <repo> --mode cloudrun
#
# Local:
# bash scripts/setup.sh <repo>
# bash scripts/setup.sh <repo> --mode process
#
# Uninstall:
# bash scripts/setup.sh <repo> --mode process --uninstall
Notes:
- Safe: no secrets written; no destructive actions.
@ -21,6 +30,19 @@ EOF
log() { printf '[setup] %s\n' "$*"; }
normalize_mode() {
case "${1:-process}" in
process|proc|local|binary) printf 'process\n' ;;
docker|container) printf 'docker\n' ;;
cloudrun|cloud-run|gcp) printf 'cloudrun\n' ;;
*)
log "unsupported mode: ${1:-}"
usage
exit 2
;;
esac
}
need_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
log "missing required command: $1"
@ -39,9 +61,14 @@ shift
REPO_URL=""
REF="main"
DIR="$NAME"
MODE="process"
ACTION="setup"
while [[ $# -gt 0 ]]; do
case "$1" in
--mode|--deploy-mode) MODE="${2:-}"; shift 2 ;;
--deploy) ACTION="deploy"; shift ;;
--uninstall) ACTION="uninstall"; shift ;;
--repo) REPO_URL="${2:-}"; shift 2 ;;
--ref) REF="${2:-}"; shift 2 ;;
--dir) DIR="${2:-}"; shift 2 ;;
@ -49,6 +76,8 @@ while [[ $# -gt 0 ]]; do
esac
done
MODE="$(normalize_mode "$MODE")"
if [[ -z "${REPO_URL}" ]]; then
REPO_URL="https://github.com/cloud-neutral-toolkit/${NAME}.git"
fi
@ -74,29 +103,56 @@ fi
cd "${DIR}"
if [[ "${ACTION}" == "uninstall" ]]; then
case "$MODE" in
process) bash scripts/uninstall-process.sh ;;
docker) bash scripts/uninstall-docker.sh ;;
cloudrun)
log "cloudrun uninstall is not supported by setup.sh; use gcloud directly"
exit 2
;;
esac
exit 0
fi
did_any=false
if [[ -f "package.json" ]]; then
need_cmd node
if command -v corepack >/dev/null 2>&1; then
corepack enable >/dev/null 2>&1 || true
fi
if command -v yarn >/dev/null 2>&1; then
log "installing JS dependencies (yarn install)"
yarn install
did_any=true
else
log "yarn not found; install yarn (or enable corepack) then re-run"
exit 1
fi
fi
case "$MODE" in
process)
if [[ -f "package.json" ]]; then
need_cmd node
if command -v corepack >/dev/null 2>&1; then
corepack enable >/dev/null 2>&1 || true
fi
if command -v yarn >/dev/null 2>&1; then
log "installing JS dependencies (yarn install)"
yarn install
did_any=true
else
log "yarn not found; install yarn (or enable corepack) then re-run"
exit 1
fi
fi
if [[ -f "go.mod" ]]; then
need_cmd go
log "downloading Go dependencies (go mod download)"
go mod download
did_any=true
fi
if [[ -f "go.mod" ]]; then
need_cmd go
log "downloading Go dependencies (go mod download)"
go mod download
did_any=true
fi
;;
docker)
need_cmd docker
log "docker mode selected; skipping host-level dependency installation"
did_any=true
;;
cloudrun)
need_cmd docker
need_cmd gcloud
log "cloudrun mode selected; skipping host-level dependency installation"
did_any=true
;;
esac
if [[ "${did_any}" == "false" ]]; then
log "no supported project type detected (package.json/go.mod)."
@ -113,10 +169,44 @@ if [[ -f "scripts/post-setup.sh" ]]; then
bash scripts/post-setup.sh
fi
log "setup complete"
log "next steps:"
if [[ -f "go.mod" ]]; then
log " cp .env.example .env"
log " make dev"
if [[ "${ACTION}" == "deploy" ]]; then
case "$MODE" in
process) bash scripts/install-process.sh ;;
docker) bash scripts/install-docker.sh ;;
cloudrun)
if [[ -z "${GCP_PROJECT:-}" ]]; then
log "cloudrun deploy requires GCP_PROJECT in the environment"
exit 2
fi
make cloudrun-build
make cloudrun-deploy
;;
esac
fi
log "setup complete (mode=${MODE})"
log "next steps:"
case "$MODE" in
process)
if [[ -f ".env.example" ]]; then
log " cp .env.example .env"
fi
if [[ -f "Makefile" ]]; then
log " make init-db"
log " make start"
log " sudo bash scripts/install-process.sh"
elif [[ -f "go.mod" ]]; then
log " go run ./cmd/accountsvc/main.go --config config/account.yaml"
fi
;;
docker)
log " docker build -t accounts-svc-plus ."
log " docker run --rm -p 8080:8080 -e CONFIG_TEMPLATE=/app/config/account.yaml -e CONFIG_PATH=/etc/xcontrol/account.yaml accounts-svc-plus"
log " sudo bash scripts/install-docker.sh"
;;
cloudrun)
log " export GCP_PROJECT=<your-project>"
log " GCP_PROJECT=<your-project> make cloudrun-build"
log " GCP_PROJECT=<your-project> make cloudrun-deploy CLOUD_RUN_SERVICE_YAML=deploy/gcp/cloud-run/prod-service.yaml"
;;
esac

View File

@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ "$(uname -s)" != "Linux" ]]; then
echo "docker uninstall is only supported on Linux" >&2
exit 1
fi
if [[ "${EUID}" -ne 0 ]]; then
echo "docker uninstall must run as root" >&2
exit 1
fi
SERVICE_NAME="${SERVICE_NAME:-accounts-svc-plus-docker}"
CONTAINER_NAME="${CONTAINER_NAME:-accounts-svc-plus}"
IMAGE_TAG="${IMAGE_TAG:-accounts-svc-plus:local}"
DOMAIN="${ACCOUNT_DOMAIN:-accounts.svc.plus}"
CONFIG_DIR="${CONFIG_DIR:-/etc/accounts.svc.plus}"
SYSTEMD_UNIT_PATH="${SYSTEMD_UNIT_PATH:-/etc/systemd/system/${SERVICE_NAME}.service}"
CADDY_CONF_DIR="${CADDY_CONF_DIR:-/etc/caddy/conf.d}"
CADDY_CONF_PATH="${CADDY_CONF_PATH:-${CADDY_CONF_DIR}/${DOMAIN}.conf}"
if command -v systemctl >/dev/null 2>&1; then
systemctl disable --now "${SERVICE_NAME}.service" >/dev/null 2>&1 || true
fi
if command -v docker >/dev/null 2>&1; then
docker rm -f "${CONTAINER_NAME}" >/dev/null 2>&1 || true
docker image rm "${IMAGE_TAG}" >/dev/null 2>&1 || true
fi
rm -f "${SYSTEMD_UNIT_PATH}" "${CADDY_CONF_PATH}"
rm -rf "${CONFIG_DIR}"
if command -v systemctl >/dev/null 2>&1; then
systemctl daemon-reload
if systemctl is-active --quiet caddy; then
systemctl reload caddy || systemctl restart caddy
fi
fi
echo "docker deployment removed"

View File

@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ "$(uname -s)" != "Linux" ]]; then
echo "process uninstall is only supported on Linux" >&2
exit 1
fi
if [[ "${EUID}" -ne 0 ]]; then
echo "process uninstall must run as root" >&2
exit 1
fi
SERVICE_NAME="${SERVICE_NAME:-accounts-svc-plus}"
DOMAIN="${ACCOUNT_DOMAIN:-accounts.svc.plus}"
CONFIG_DIR="${CONFIG_DIR:-/etc/accounts.svc.plus}"
BINARY_PATH="${BINARY_PATH:-/usr/local/bin/accounts-svc-plus}"
SYSTEMD_UNIT_PATH="${SYSTEMD_UNIT_PATH:-/etc/systemd/system/${SERVICE_NAME}.service}"
CADDY_CONF_DIR="${CADDY_CONF_DIR:-/etc/caddy/conf.d}"
CADDY_CONF_PATH="${CADDY_CONF_PATH:-${CADDY_CONF_DIR}/${DOMAIN}.conf}"
if command -v systemctl >/dev/null 2>&1; then
systemctl disable --now "${SERVICE_NAME}.service" >/dev/null 2>&1 || true
fi
rm -f "${SYSTEMD_UNIT_PATH}" "${BINARY_PATH}" "${CADDY_CONF_PATH}"
rm -rf "${CONFIG_DIR}"
if command -v systemctl >/dev/null 2>&1; then
systemctl daemon-reload
if systemctl is-active --quiet caddy; then
systemctl reload caddy || systemctl restart caddy
fi
fi
echo "process deployment removed"