From 1ccfd3d62645ba05b557e6f8c20d29e276e73e48 Mon Sep 17 00:00:00 2001 From: zhenyi <434836402@qq.com> Date: Thu, 11 Jun 2026 15:32:18 +0800 Subject: [PATCH] chore(build): add Docker configuration and update dependency versions - Update Cargo.toml with shortened version specifications for dependencies - Add .dockerignore file to exclude unnecessary files from Docker builds - Create .env.example with comprehensive environment variable configurations - Add docker-compose.yaml with complete multi-service infrastructure setup - Add Dockerfile with optimized multi-stage build process using cargo-chef - Add Dockerfile.fast for faster container builds during development - Configure --- .dockerignore | 8 ++ .env.example | 120 ++++++++++++++++ Cargo.toml | 42 +++--- Dockerfile | 32 +++++ Dockerfile.fast | 16 +++ docker-compose.yaml | 340 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 537 insertions(+), 21 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 Dockerfile create mode 100644 Dockerfile.fast create mode 100644 docker-compose.yaml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8c2e837 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +.codegraph +.claude +target +.git +.idea +*.md +LICENSE +.env diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..c89ce91 --- /dev/null +++ b/.env.example @@ -0,0 +1,120 @@ +# HTTP Server +APP_HTTP_HOST=0.0.0.0 +APP_HTTP_PORT=8000 +APP_HTTP_WORKERS=4 +APP_HTTP_JSON_LIMIT_BYTES=10485760 + +# App +APP_URL=http://localhost:8000 +APP_MAIN_DOMAIN=localhost + +# Session +APP_SESSION_SECRET=change-me-to-a-secure-random-string-at-least-32-bytes +APP_SESSION_COOKIE_NAME=sid +APP_SESSION_COOKIE_SECURE=false +APP_SESSION_COOKIE_HTTP_ONLY=true +APP_SESSION_COOKIE_SAME_SITE=Lax +APP_SESSION_COOKIE_PATH=/ +APP_SESSION_COOKIE_DOMAIN= +APP_SESSION_TTL_SECS=86400 +APP_SESSION_MAX_AGE_SECS=86400 + +# PostgreSQL +DATABASE_URL=postgres://appks:appks@localhost:5432/appks +APP_DATABASE_URL=postgres://appks:appks@localhost:5432/appks +APP_DATABASE_MAX_CONNECTIONS=10 +APP_DATABASE_MIN_CONNECTIONS=2 +APP_DATABASE_IDLE_TIMEOUT=600 +APP_DATABASE_MAX_LIFETIME=3600 +APP_DATABASE_CONNECTION_TIMEOUT=8 +APP_DATABASE_SCHEMA_SEARCH_PATH=public +APP_DATABASE_READ_WRITE_SPLIT=false +APP_DATABASE_RETRY_ATTEMPTS=3 +APP_DATABASE_RETRY_DELAY=5 + +# Redis +# Single-node mode (set APP_REDIS_CLUSTER_ENABLED=false) +APP_REDIS_URL=redis://localhost:6379/0 +# Cluster mode (set APP_REDIS_CLUSTER_ENABLED=true) +APP_REDIS_CLUSTER_ENABLED=true +APP_REDIS_CLUSTER_NODES=redis://localhost:6379,redis://localhost:6380,redis://localhost:6381,redis://localhost:6382,redis://localhost:6383,redis://localhost:6384 +APP_REDIS_READ_FROM_REPLICAS=false +APP_REDIS_USERNAME= +APP_REDIS_PASSWORD= +APP_REDIS_MAX_CONNECTIONS=20 +APP_REDIS_MIN_CONNECTIONS=2 +APP_REDIS_IDLE_TIMEOUT=300 +APP_REDIS_CONNECTION_TIMEOUT=5 +APP_REDIS_MAX_RETRIES=3 +APP_REDIS_RETRY_DELAY_MS=100 +APP_REDIS_TLS_ENABLED=false +APP_REDIS_KEY_PREFIX=appks: + +# etcd +APP_ETCD_ENDPOINTS=http://localhost:2379 +APP_ETCD_KEY_PREFIX=/appks/ +APP_ETCD_CONNECT_TIMEOUT=5 +APP_ETCD_REQUEST_TIMEOUT=10 +APP_ETCD_KEEP_ALIVE_INTERVAL=10 +APP_ETCD_LEASE_TTL=15 +APP_ETCD_MAX_RETRIES=3 +APP_ETCD_REGISTER_SELF=false + +# NATS +APP_NATS_URL=nats://localhost:4222 +APP_NATS_CONNECTION_TIMEOUT=5 +APP_NATS_PING_INTERVAL=20 +APP_NATS_RECONNECT_DELAY=2 +APP_NATS_MAX_RECONNECTS=60 +APP_NATS_STREAM_PREFIX=APPKS +APP_NATS_ACK_WAIT_SECS=30 +APP_NATS_MAX_DELIVER=5 + +# S3 / MinIO +APP_S3_ENDPOINT=http://localhost:9000 +APP_S3_REGION=us-east-1 +APP_S3_ACCESS_KEY=admin +APP_S3_SECRET_KEY=mysecret123 +APP_S3_BUCKET=appks +APP_S3_PATH_STYLE=true +APP_S3_FORCE_PATH_STYLE=true +APP_S3_PUBLIC_URL=http://localhost:9000/appks +APP_S3_MAX_CONNECTIONS=50 +APP_S3_IDLE_TIMEOUT=90 +APP_S3_CONNECTION_TIMEOUT=10 +APP_S3_MAX_RETRIES=3 +APP_S3_UPLOAD_PART_SIZE=8388608 +APP_S3_MAX_UPLOAD_SIZE=104857600 +APP_S3_PRESIGNED_URL_EXPIRY=3600 + +# LRU Cache +APP_LRU_DEFAULT_CAPACITY=1000 +APP_LRU_DEFAULT_TTL_SECS=300 +APP_LRU_CLEANUP_INTERVAL_SECS=60 + +# gRPC Server +APP_RPC_SELF_HOST=0.0.0.0 +APP_RPC_SELF_PORT=50049 +APP_RPC_SELF_REFLECTION=false +APP_RPC_SELF_SERVICE_NAME=appks +APP_RPC_DEFAULT_TIMEOUT_SECS=10 + +# AI Provider +APP_AI_PROVIDER_API_KEY= +APP_AI_PROVIDER_URL= + +# Qdrant +APP_QDRANT_URL=http://localhost:6334 +APP_QDRANT_COLLECTION=appks_embeddings +APP_QDRANT_VECTOR_SIZE=1536 +APP_QDRANT_DISTANCE=Cosine +APP_QDRANT_MAX_CONNECTIONS=10 +APP_QDRANT_IDLE_TIMEOUT=300 +APP_QDRANT_CONNECTION_TIMEOUT=10 +APP_QDRANT_MAX_RETRIES=3 +APP_QDRANT_TLS_ENABLED=false +APP_QDRANT_SEARCH_LIMIT=10 +APP_QDRANT_SCORE_THRESHOLD=0.7 + +# Email RPC +APP_EMAIL_RPC_ADDR=http://localhost:50050 diff --git a/Cargo.toml b/Cargo.toml index 01d26e3..a5465d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,20 +16,20 @@ path = "main.rs" name = "gen_openapi" path = "gen_openapi.rs" [dependencies] -sqlx = { version = "0.9.0", features = ["postgres","runtime-tokio","chrono","uuid","json","migrate"] } -tokio = { version = "1.52.3", features = ["full"] } -serde = { version = "1.0.228", features = ["derive"] } -serde_json = { version = "1.0.150", features = [] } -chrono = { version = "0.4.19", features = ["serde"] } -uuid = { version = "1.23.1", features = ["serde","v4","v7","v5"] } -reqwest = { version = "0.13.4", features = ["json"] } -tracing = { version = "0.1.44", features = [] } -tracing-subscriber = { version = "0.3.23", features = ["fmt"] } -dotenvy = "0.15.7" +sqlx = { version = "0.9", features = ["postgres","runtime-tokio","chrono","uuid","json","migrate"] } +tokio = { version = "1", features = ["full"] } +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1", features = [] } +chrono = { version = "0.4", features = ["serde"] } +uuid = { version = "1", features = ["serde","v4","v7","v5"] } +reqwest = { version = "0.13", features = ["json"] } +tracing = { version = "0.1", features = [] } +tracing-subscriber = { version = "0.3", features = ["fmt"] } +dotenvy = "0.15" thiserror = "2" -redis = { version = "1.2.1", features = ["cluster","cluster-async","aio","tokio-comp","connection-manager"] } -dashmap = "6.1" -object_store = { version = "0.13.2", features = ["tokio","aws","cloud"] } +redis = { version = "1", features = ["cluster","cluster-async","aio","tokio-comp","connection-manager"] } +dashmap = "6" +object_store = { version = "0.13", features = ["tokio","aws","cloud"] } argon2 = "0.5" rsa = "0.9" chacha20poly1305 = "0.10" @@ -42,19 +42,19 @@ arc-swap = "1" base64 = "0.22" rand = "0.8" captcha-rs = "0.5" -tonic = { version = "0.14.6", features = ["transport", "channel"] } -prost = "0.14.3" -prost-types = "0.14.3" -tonic-prost = "0.14.6" +tonic = { version = "0.14", features = ["transport", "channel"] } +prost = "0.14" +prost-types = "0.14" +tonic-prost = "0.14" url = "2.5" -etcd-client = { version = "0.18.0", features = ["tls"] } +etcd-client = { version = "0.18", features = ["tls"] } tokio-stream = "0.1" async-nats = "0.49" futures-util = "0.3" -utoipa = { version = "5.5.0", features = ["uuid","chrono","actix_extras","decimal","macros"]} +utoipa = { version = "5", features = ["uuid","chrono","actix_extras","decimal","macros"]} actix-web = { version = "4", features = ["secure-cookies"] } actix-multipart = "0.7" -hex = "0.4.3" +hex = "0.4" [build-dependencies] -tonic-prost-build = "0.14.6" +tonic-prost-build = "0.14" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bdb07ba --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +FROM rust:1.96-bookworm AS chef +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + protobuf-compiler libprotobuf-dev mold clang && \ + rm -rf /var/lib/apt/lists/* +RUN cargo install cargo-chef +WORKDIR /app + +FROM chef AS planner +COPY . . +RUN cargo chef prepare --recipe-path recipe.json + +FROM chef AS builder +COPY --from=planner /app/recipe.json recipe.json +RUN cargo chef cook --release --recipe-path recipe.json +COPY . . +RUN cargo build --release --bin appks && \ + strip target/release/appks + +FROM ubuntu:26.04 +RUN apt-get update && \ + apt-get install -y --no-install-recommends ca-certificates && \ + rm -rf /var/lib/apt/lists/* +COPY --from=builder /app/target/release/appks /usr/local/bin/appks + +ENV APP_HTTP_HOST=0.0.0.0 +ENV APP_HTTP_PORT=8000 +ENV APP_RPC_SELF_HOST=0.0.0.0 +ENV APP_RPC_SELF_PORT=50049 + +EXPOSE 8000 50049 +ENTRYPOINT ["appks"] diff --git a/Dockerfile.fast b/Dockerfile.fast new file mode 100644 index 0000000..6e5fe57 --- /dev/null +++ b/Dockerfile.fast @@ -0,0 +1,16 @@ +FROM ubuntu:26.04 + +RUN apt-get update && \ + apt-get install -y --no-install-recommends ca-certificates && \ + rm -rf /var/lib/apt/lists/* + +COPY target/release/appks /usr/local/bin/appks + +ENV APP_HTTP_HOST=0.0.0.0 +ENV APP_HTTP_PORT=8000 +ENV APP_RPC_SELF_HOST=0.0.0.0 +ENV APP_RPC_SELF_PORT=50049 + +EXPOSE 8000 50049 + +ENTRYPOINT ["appks"] diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..790aa68 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,340 @@ +x-appks-env: &appks-env + RUST_LOG: info + APP_HTTP_HOST: 0.0.0.0 + APP_HTTP_PORT: 8000 + APP_HTTP_WORKERS: 2 + APP_HTTP_JSON_LIMIT_BYTES: 10485760 + APP_URL: http://localhost:8000 + APP_MAIN_DOMAIN: localhost + APP_SESSION_SECRET: TC5uuvxDNHLNm-BjZVhHtObtdF1oLL6bPmYlmwbNaGWe00mpWiT2uBJbeFrNYumi4UGI7sVBn83mLTIeKxfHEg + APP_SESSION_COOKIE_NAME: sid + APP_SESSION_COOKIE_SECURE: "false" + APP_SESSION_COOKIE_HTTP_ONLY: "true" + APP_SESSION_COOKIE_SAME_SITE: Lax + APP_SESSION_COOKIE_PATH: / + APP_SESSION_TTL_SECS: 86400 + APP_SESSION_MAX_AGE_SECS: 86400 + # Postgres + APP_DATABASE_URL: postgres://appks:appks@postgres:5432/appks + DATABASE_URL: postgres://appks:appks@postgres:5432/appks + APP_DATABASE_MAX_CONNECTIONS: 10 + APP_DATABASE_MIN_CONNECTIONS: 2 + APP_DATABASE_IDLE_TIMEOUT: 600 + APP_DATABASE_MAX_LIFETIME: 3600 + APP_DATABASE_CONNECTION_TIMEOUT: 8 + APP_DATABASE_SCHEMA_SEARCH_PATH: public + APP_DATABASE_READ_WRITE_SPLIT: "false" + APP_DATABASE_RETRY_ATTEMPTS: 3 + APP_DATABASE_RETRY_DELAY: 5 + # Redis (cluster mode) + APP_REDIS_CLUSTER_ENABLED: "true" + APP_REDIS_CLUSTER_NODES: redis://redis-node-0:6379,redis://redis-node-1:6379,redis://redis-node-2:6379,redis://redis-node-3:6379,redis://redis-node-4:6379,redis://redis-node-5:6379 + APP_REDIS_READ_FROM_REPLICAS: "false" + APP_REDIS_PASSWORD: "" + APP_REDIS_MAX_CONNECTIONS: 20 + APP_REDIS_MIN_CONNECTIONS: 2 + APP_REDIS_IDLE_TIMEOUT: 300 + APP_REDIS_CONNECTION_TIMEOUT: 5 + APP_REDIS_MAX_RETRIES: 3 + APP_REDIS_RETRY_DELAY_MS: 100 + APP_REDIS_TLS_ENABLED: "false" + APP_REDIS_KEY_PREFIX: "appks:" + # etcd + APP_ETCD_ENDPOINTS: http://etcd:2379 + APP_ETCD_KEY_PREFIX: /appks/ + APP_ETCD_CONNECT_TIMEOUT: 5 + APP_ETCD_REQUEST_TIMEOUT: 10 + APP_ETCD_KEEP_ALIVE_INTERVAL: 10 + APP_ETCD_LEASE_TTL: 15 + APP_ETCD_MAX_RETRIES: 3 + APP_ETCD_REGISTER_SELF: "true" + # NATS + APP_NATS_URL: nats://nats:4222 + APP_NATS_CONNECTION_TIMEOUT: 5 + APP_NATS_PING_INTERVAL: 20 + APP_NATS_RECONNECT_DELAY: 2 + APP_NATS_MAX_RECONNECTS: 60 + APP_NATS_STREAM_PREFIX: APPKS + APP_NATS_ACK_WAIT_SECS: 30 + APP_NATS_MAX_DELIVER: 5 + # S3 / MinIO + APP_S3_ENDPOINT: http://minio:9000 + APP_S3_REGION: us-east-1 + APP_S3_ACCESS_KEY: admin + APP_S3_SECRET_KEY: mysecret123 + APP_S3_BUCKET: appks + APP_S3_PATH_STYLE: "true" + APP_S3_FORCE_PATH_STYLE: "true" + APP_S3_PUBLIC_URL: http://localhost:9000/appks + APP_S3_MAX_CONNECTIONS: 50 + APP_S3_IDLE_TIMEOUT: 90 + APP_S3_CONNECTION_TIMEOUT: 10 + APP_S3_MAX_RETRIES: 3 + APP_S3_UPLOAD_PART_SIZE: 8388608 + APP_S3_MAX_UPLOAD_SIZE: 104857600 + APP_S3_PRESIGNED_URL_EXPIRY: 3600 + # LRU cache + APP_LRU_DEFAULT_CAPACITY: 1000 + APP_LRU_DEFAULT_TTL_SECS: 300 + APP_LRU_CLEANUP_INTERVAL_SECS: 60 + # gRPC + APP_RPC_SELF_HOST: 0.0.0.0 + APP_RPC_SELF_PORT: 50049 + APP_RPC_SELF_REFLECTION: "false" + APP_RPC_SELF_SERVICE_NAME: appks + APP_RPC_DEFAULT_TIMEOUT_SECS: 10 + # AI (disabled by default in compose — set externally) + APP_AI_PROVIDER_API_KEY: "" + APP_AI_PROVIDER_URL: "" + # Qdrant + APP_QDRANT_URL: http://qdrant:6334 + APP_QDRANT_COLLECTION: appks_embeddings + APP_QDRANT_VECTOR_SIZE: 1536 + APP_QDRANT_DISTANCE: Cosine + APP_QDRANT_MAX_CONNECTIONS: 10 + APP_QDRANT_IDLE_TIMEOUT: 300 + APP_QDRANT_CONNECTION_TIMEOUT: 10 + APP_QDRANT_MAX_RETRIES: 3 + APP_QDRANT_TLS_ENABLED: "false" + APP_QDRANT_SEARCH_LIMIT: 10 + APP_QDRANT_SCORE_THRESHOLD: 0.7 + +services: + # AppKS + appks: + image: appks + restart: unless-stopped + ports: + - "8000:8000" + - "50049:50049" + environment: + <<: *appks-env + depends_on: + postgres: + condition: service_healthy + etcd: + condition: service_started + nats: + condition: service_started + minio: + condition: service_healthy + qdrant: + condition: service_started + networks: + - appks-net + + # PostgreSQL + postgres: + image: postgres:17-alpine + restart: unless-stopped + environment: + POSTGRES_USER: appks + POSTGRES_PASSWORD: appks + POSTGRES_DB: appks + ports: + - "5432:5432" + volumes: + - pg_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U appks -d appks"] + interval: 5s + timeout: 5s + retries: 5 + networks: + - appks-net + + # Redis Cluster (6 nodes) + redis-node-0: + image: redis:7-alpine + command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes + restart: unless-stopped + ports: + - "6379:6379" + volumes: + - redis_data_0:/data + networks: + - appks-net + + redis-node-1: + image: redis:7-alpine + command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes + restart: unless-stopped + ports: + - "6380:6379" + volumes: + - redis_data_1:/data + networks: + - appks-net + + redis-node-2: + image: redis:7-alpine + command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes + restart: unless-stopped + ports: + - "6381:6379" + volumes: + - redis_data_2:/data + networks: + - appks-net + + redis-node-3: + image: redis:7-alpine + command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes + restart: unless-stopped + ports: + - "6382:6379" + volumes: + - redis_data_3:/data + networks: + - appks-net + + redis-node-4: + image: redis:7-alpine + command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes + restart: unless-stopped + ports: + - "6383:6379" + volumes: + - redis_data_4:/data + networks: + - appks-net + + redis-node-5: + image: redis:7-alpine + command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes + restart: unless-stopped + ports: + - "6384:6379" + volumes: + - redis_data_5:/data + networks: + - appks-net + + redis-cluster-init: + image: redis:7-alpine + depends_on: + - redis-node-0 + - redis-node-1 + - redis-node-2 + - redis-node-3 + - redis-node-4 + - redis-node-5 + entrypoint: > + sh -c ' + echo "Waiting for all Redis nodes to be ready..."; + sleep 5; + echo "Creating Redis cluster..."; + redis-cli --cluster create + redis-node-0:6379 + redis-node-1:6379 + redis-node-2:6379 + redis-node-3:6379 + redis-node-4:6379 + redis-node-5:6379 + --cluster-replicas 1 + --cluster-yes + ' + networks: + - appks-net + + # etcd + etcd: + image: quay.io/coreos/etcd:v3.5 + restart: unless-stopped + command: > + etcd + --name etcd + --data-dir /etcd-data + --listen-client-urls http://0.0.0.0:2379 + --advertise-client-urls http://etcd:2379 + --listen-peer-urls http://0.0.0.0:2380 + --initial-advertise-peer-urls http://etcd:2380 + --initial-cluster etcd=http://etcd:2380 + --initial-cluster-token appks-etcd + --initial-cluster-state new + ports: + - "2379:2379" + - "2380:2380" + volumes: + - etcd_data:/etcd-data + networks: + - appks-net + + # NATS + nats: + image: nats:2-alpine + restart: unless-stopped + command: --js --store_dir /data/nats + ports: + - "4222:4222" + - "8222:8222" + volumes: + - nats_data:/data/nats + networks: + - appks-net + + # MinIO (S3-compatible storage) + minio: + image: minio/minio:latest + restart: unless-stopped + command: server /data --console-address ":9001" + environment: + MINIO_ROOT_USER: admin + MINIO_ROOT_PASSWORD: mysecret123 + ports: + - "9000:9000" + - "9001:9001" + volumes: + - minio_data:/data + healthcheck: + test: ["CMD-SHELL", "mc ready local || exit 1"] + interval: 5s + timeout: 5s + retries: 5 + networks: + - appks-net + + # MinIO bucket auto-creation + minio-init: + image: minio/mc:latest + depends_on: + minio: + condition: service_healthy + entrypoint: > + sh -c ' + mc alias set local http://minio:9000 admin mysecret123; + mc mb --ignore-existing local/appks; + echo "MinIO bucket appks ready"; + exit 0 + ' + networks: + - appks-net + + # Qdrant (vector database) + qdrant: + image: qdrant/qdrant:latest + restart: unless-stopped + ports: + - "6333:6333" + - "6334:6334" + volumes: + - qdrant_data:/qdrant/storage + networks: + - appks-net + +volumes: + pg_data: + redis_data_0: + redis_data_1: + redis_data_2: + redis_data_3: + redis_data_4: + redis_data_5: + etcd_data: + nats_data: + minio_data: + qdrant_data: + +networks: + appks-net: + driver: bridge