diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml index 5209438..17ba591 100644 --- a/.gitea/workflows/deploy.yaml +++ b/.gitea/workflows/deploy.yaml @@ -30,7 +30,7 @@ jobs: shell: sh run: | echo "🚀 开始构建 JDK 26 镜像: ${IMAGE_NAME}:${{ gitea.sha }}" - docker build -t ${IMAGE_NAME}:${{ gitea.sha }} . + DOCKER_BUILDKIT=0 docker build --memory=700m -t ${IMAGE_NAME}:${{ gitea.sha }} . docker tag ${IMAGE_NAME}:${{ gitea.sha }} ${IMAGE_NAME}:latest - name: 3. 旧容器清理与新容器平滑重启 @@ -43,10 +43,15 @@ jobs: echo "🔄 正在启动全新 JDK 26 容器环境..." docker run -d \ --name ${CONTAINER_NAME} \ - -p ${APP_PORT}:${APP_PORT} \ - --restart no \ - -m 512m \ - -e JAVA_TOOL_OPTIONS="-Xms128m -Xmx384m -XX:+UseG1GC" \ + --network tracecd-net \ + -p 127.0.0.1:${APP_PORT}:${APP_PORT} \ + --restart unless-stopped \ + --memory 384m \ + --memory-swap 768m \ + --cpus 0.75 \ + --pids-limit 256 \ + --env-file /opt/tracecd/app.env \ + -e JAVA_TOOL_OPTIONS="-Xms64m -Xmx256m -XX:+UseSerialGC" \ ${IMAGE_NAME}:latest - name: 4. 服务健康检查 (HTTP 探活) diff --git a/.gitignore b/.gitignore index 480bdf5..47d67f8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,10 @@ target/ +deploy/.env +deploy/stack.env +deploy/app.env +deploy/runner-token +deploy/data/ +.migration-private/ !.mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ !**/src/test/**/target/ @@ -36,4 +42,4 @@ build/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store diff --git a/Dockerfile b/Dockerfile index 901236a..fc75ef3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,9 @@ FROM maven:3.9.16-eclipse-temurin-26 AS builder WORKDIR /app +# 1C/1G 部署机上限制 Maven 峰值内存,避免构建挤占运行中的服务。 +ENV MAVEN_OPTS="-Xms64m -Xmx384m -XX:+UseSerialGC" + # 1. 配置阿里云 Maven 镜像加速下载(可选,国内服务器强烈推荐) RUN mkdir -p /root/.m2 RUN echo 'aliyunmaven*阿里云公共仓库https://maven.aliyun.com/repository/public' > /root/.m2/settings.xml diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..3af7715 --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,39 @@ +# TraceCD deployment on HK-1 + +The production stack lives at `/opt/tracecd` on `156.238.122.140` and is sized for a 1-core, 1-GiB host. + +## Services + +- MySQL 8.0.40: Docker-only `tracecd-net`; no host port is published. +- Gitea 1.22.6: host ports 3000 and 2222 are mapped, but remain blocked by the host firewall unless explicitly opened. +- Gitea Actions runner: capacity 1, label `centos-env`, Docker socket access. +- TraceCD: built by Gitea Actions, bound to `127.0.0.1:8080` and served by host Nginx. +- Nginx: `t.1633292.cn` on port 80. Port 443 remains owned by Xray. + +## Bootstrap + +Copy `compose.yaml`, `runner-config.yaml`, `nginx-tracecd.conf`, and `bootstrap-env.sh` to `/opt/tracecd`. Then run: + +```bash +cd /opt/tracecd +chmod 700 bootstrap-env.sh +./bootstrap-env.sh +install -m 644 nginx-tracecd.conf /etc/nginx/conf.d/tracecd.conf +nginx -t && systemctl reload nginx +docker compose pull +docker compose up -d +``` + +`bootstrap-env.sh` creates random database and bootstrap passwords in `stack.env` and `app.env`, both mode 0600. Add `DEEPSEEK_API_KEY` and `MIMO_API_KEY` to `app.env` on the server; never commit either file. + +## Operations + +```bash +cd /opt/tracecd +docker compose ps +docker compose logs --tail 100 mysql gitea runner +docker stats --no-stream +curl -I -H 'Host: t.1633292.cn' http://127.0.0.1/ +``` + +The workflow in `.gitea/workflows/deploy.yaml` builds on pushes to `dev`, joins the application to `tracecd-net`, loads `/opt/tracecd/app.env`, and replaces only `trace-container`. diff --git a/deploy/app.env.example b/deploy/app.env.example new file mode 100644 index 0000000..6089c8e --- /dev/null +++ b/deploy/app.env.example @@ -0,0 +1,6 @@ +SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/tracecd?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&createDatabaseIfNotExist=true +SPRING_DATASOURCE_USERNAME=tracecd +SPRING_DATASOURCE_PASSWORD=replace-with-stack-env-mysql-password +DEEPSEEK_API_KEY=replace-with-secret +MIMO_API_KEY=replace-with-secret +APP_DEFAULT_PASSWORD=replace-with-secret diff --git a/deploy/bootstrap-env.sh b/deploy/bootstrap-env.sh new file mode 100644 index 0000000..879a784 --- /dev/null +++ b/deploy/bootstrap-env.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd /opt/tracecd +umask 077 + +if [[ -e stack.env || -e app.env ]]; then + echo "Refusing to overwrite existing secret environment files." >&2 + exit 1 +fi + +mysql_root_password="$(openssl rand -hex 24)" +mysql_app_password="$(openssl rand -hex 24)" +app_default_password="$(openssl rand -hex 16)" + +{ + printf 'MYSQL_ROOT_PASSWORD=%s\n' "$mysql_root_password" + printf 'MYSQL_PASSWORD=%s\n' "$mysql_app_password" + printf 'GITEA_HOST=156.238.122.140\n' +} > stack.env + +{ + printf '%s\n' 'SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/tracecd?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&createDatabaseIfNotExist=true' + printf 'SPRING_DATASOURCE_USERNAME=tracecd\n' + printf 'SPRING_DATASOURCE_PASSWORD=%s\n' "$mysql_app_password" + printf 'DEEPSEEK_API_KEY=\n' + printf 'MIMO_API_KEY=\n' + printf 'APP_DEFAULT_PASSWORD=%s\n' "$app_default_password" +} > app.env + +chmod 600 stack.env app.env +ln -s stack.env .env +echo "Created protected environment files." diff --git a/deploy/compose.yaml b/deploy/compose.yaml new file mode 100644 index 0000000..88e4efa --- /dev/null +++ b/deploy/compose.yaml @@ -0,0 +1,95 @@ +services: + mysql: + image: mysql:8.0.40 + container_name: tracecd-mysql + restart: unless-stopped + env_file: stack.env + environment: + MYSQL_DATABASE: tracecd + MYSQL_USER: tracecd + command: + - --character-set-server=utf8mb4 + - --collation-server=utf8mb4_0900_ai_ci + - --performance-schema=OFF + - --innodb-buffer-pool-size=128M + - --innodb-log-buffer-size=8M + - --max-connections=20 + - --table-open-cache=200 + - --skip-log-bin + volumes: + - ./data/mysql:/var/lib/mysql + networks: + - tracecd-net + healthcheck: + test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p$$MYSQL_ROOT_PASSWORD --silent"] + interval: 10s + timeout: 5s + retries: 12 + mem_limit: 320m + memswap_limit: 640m + cpus: 0.55 + pids_limit: 256 + + gitea: + image: gitea/gitea:1.22.6 + container_name: gitea + restart: unless-stopped + depends_on: + mysql: + condition: service_healthy + env_file: stack.env + environment: + USER_UID: 1000 + USER_GID: 1000 + GITEA__database__DB_TYPE: mysql + GITEA__database__HOST: mysql:3306 + GITEA__database__NAME: gitea + GITEA__database__USER: root + GITEA__database__PASSWD: ${MYSQL_ROOT_PASSWORD} + GITEA__server__DOMAIN: ${GITEA_HOST} + GITEA__server__SSH_DOMAIN: ${GITEA_HOST} + GITEA__server__ROOT_URL: http://${GITEA_HOST}:3000/ + GITEA__server__SSH_PORT: 2222 + GITEA__service__DISABLE_REGISTRATION: "true" + GITEA__actions__ENABLED: "true" + volumes: + - ./data/gitea:/data + - /etc/localtime:/etc/localtime:ro + ports: + - "3000:3000" + - "2222:22" + networks: + - tracecd-net + mem_limit: 256m + memswap_limit: 512m + cpus: 0.35 + pids_limit: 256 + + runner: + image: gitea/act_runner:latest + container_name: gitea-runner + restart: unless-stopped + depends_on: + - gitea + env_file: stack.env + environment: + GITEA_INSTANCE_URL: http://gitea:3000 + GITEA_RUNNER_NAME: hk1-low-memory-runner + GITEA_RUNNER_LABELS: centos-env:docker://docker:cli + GITEA_RUNNER_REGISTRATION_TOKEN_FILE: /run/secrets/runner_token + CONFIG_FILE: /data/config.yaml + volumes: + - ./data/runner:/data + - ./runner-config.yaml:/data/config.yaml:ro + - ./runner-token:/run/secrets/runner_token:ro + - /var/run/docker.sock:/var/run/docker.sock + networks: + - tracecd-net + mem_limit: 128m + memswap_limit: 256m + cpus: 0.25 + pids_limit: 128 + +networks: + tracecd-net: + name: tracecd-net diff --git a/deploy/nginx-tracecd.conf b/deploy/nginx-tracecd.conf new file mode 100644 index 0000000..8d1f002 --- /dev/null +++ b/deploy/nginx-tracecd.conf @@ -0,0 +1,19 @@ +server { + listen 80; + listen [::]:80; + server_name t.1633292.cn; + + client_max_body_size 10m; + + location / { + proxy_pass http://127.0.0.1:8080; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 10s; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + } +} diff --git a/deploy/runner-config.yaml b/deploy/runner-config.yaml new file mode 100644 index 0000000..30a54c6 --- /dev/null +++ b/deploy/runner-config.yaml @@ -0,0 +1,34 @@ +log: + level: info + +runner: + file: .runner + capacity: 1 + timeout: 3h + shutdown_timeout: 30s + fetch_timeout: 5s + fetch_interval: 2s + fetch_interval_max: 10s + labels: + - "centos-env:docker://docker:cli" + +cache: + enabled: false + +container: + network: bridge + privileged: false + options: "-v /opt/tracecd/app.env:/opt/tracecd/app.env:ro" + valid_volumes: [] + docker_host: "" + force_pull: false + force_rebuild: false + require_docker: true + docker_timeout: 30s + bind_workdir: false + +host: + workdir_parent: "" + +metrics: + enabled: false diff --git a/deploy/stack.env.example b/deploy/stack.env.example new file mode 100644 index 0000000..5953de7 --- /dev/null +++ b/deploy/stack.env.example @@ -0,0 +1,4 @@ +# Copy to stack.env on the server and keep mode 0600. +MYSQL_ROOT_PASSWORD=replace-with-a-random-value +MYSQL_PASSWORD=replace-with-a-different-random-value +GITEA_HOST=156.238.122.140 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ccff5c0..dd5a3fd 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -24,9 +24,9 @@ spring: # MySQL 数据源 datasource: - url: jdbc:mysql://192.168.2.5:3306/tracecd?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&createDatabaseIfNotExist=true - username: root - password: root + url: ${SPRING_DATASOURCE_URL:jdbc:mysql://mysql:3306/tracecd?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&createDatabaseIfNotExist=true} + username: ${SPRING_DATASOURCE_USERNAME:tracecd} + password: ${SPRING_DATASOURCE_PASSWORD} driver-class-name: com.mysql.cj.jdbc.Driver # Session 存储方式 @@ -61,13 +61,13 @@ logging: # DeepSeek API deepseek: - api-key: ${DEEPSEEK_API_KEY:sk-732e5f5f1f07454492022401f0a2bf40} + api-key: ${DEEPSEEK_API_KEY} base-url: https://api.deepseek.com/chat/completions model: deepseek-v4-flash # MIMO ASR API mimo: - api-key: ${MIMO_API_KEY:sk-cqttr117tzllfolonxztu4qa5208liota2llfut458ixx2m1} + api-key: ${MIMO_API_KEY} base-url: https://api.xiaomimimo.com/v1/chat/completions model: mimo-v2.5-asr @@ -75,4 +75,4 @@ mimo: app: sql-max-retries: 5 # SQL 生成最大重试次数 default-username: admin # 默认用户名 - default-password: admin123 # 默认密码(首次启动自动创建) + default-password: ${APP_DEFAULT_PASSWORD}