From b49195d1e1dd20e0e00b707c628c153445b5f9e8 Mon Sep 17 00:00:00 2001 From: zwen Date: Sat, 13 Dec 2025 21:55:16 +0800 Subject: [PATCH] feat(dev): enable local debugging with Delve and Docker Compose Debugging the service while it's running inside a Docker container can be a complex and manual process, which slows down development and troubleshooting. This change introduces a dedicated Docker Compose setup that streamlines local debugging using Delve. By running a single command, developers can now start the service in debug mode and easily attach a debugger from their IDE (like VS Code or GoLand). This significantly improves the developer experience (DX) by providing a simple and repeatable debugging workflow. Detailed step by step explanation is provided both in the Chinese and English README file --- README.md | 43 +++ README.zh_CN.md | 43 +++ backend/DockerFile-local-debug | 81 +++++ docker/docker-compose-local-debug.yml | 441 ++++++++++++++++++++++++++ 4 files changed, 608 insertions(+) create mode 100644 backend/DockerFile-local-debug create mode 100644 docker/docker-compose-local-debug.yml diff --git a/README.md b/README.md index df67a0235d..367ea7c707 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,49 @@ Deployment steps: * [Code Development and Testing](https://github.com/coze-dev/coze-studio/wiki/7.-Development-Standards#code-development-and-testing): Learn how to perform secondary development and testing based on the open-source version of Coze Studio. * [Troubleshooting](https://github.com/coze-dev/coze-studio/wiki/7.-Development-Standards#troubleshooting): Learn how to view container states and system logs. +### Local Debugging with Delve + +This project supports remote debugging of the `coze-server` service using [Delve](https://github.com/go-delve/delve). This is achieved through a specific Dockerfile and a `docker-compose` configuration. + +#### Relevant Files + +- **`backend/DockerFile-local-debug`**: + This Dockerfile builds the Go application with debug symbols. It installs the `delve` debugger and ensures the application starts with `delve` attached. + +- **`docker/docker-compose-local-debug.yml`**: + This `docker-compose` file starts the local debugging environment for the entire application. It uses `backend/DockerFile-local-debug` to build the `coze-server` service and exposes the debugger port `40000`. + +#### Running the Debug Environment + +1. **Navigate to the `docker` directory**: + ```bash + cd docker + ``` + +2. **Start the services**: + Use the `docker-compose-local-debug.yml` file to build and start all services. + ```bash + docker-compose -f docker-compose-local-debug.yml up --build + ``` + The `coze-server` service will start inside the container, listening on port `40000` for a debugger to connect. + +#### Connecting the Debugger + +You can connect using any IDE that supports Delve remote debugging. Here are example for GoLand. + +1. Go to `Run -> Edit Configurations...`. +2. Click the `+` button to add a new configuration and select `Go Remote`. +3. Configure the settings: + * **Name**: `Connect to Delve (Docker)` (or any descriptive name) + * **Host**: `127.0.0.1` + * **Port**: `40000` + * **Go version**: Select your Go SDK. +4. Apply and Save the configuration. +5. With the `coze-server` running in debug mode (as described in "Running the Debug Environment"), select the newly created run configuration and click the debug icon (🐞). + +You should now be able to set breakpoints, inspect variables, and step through code in the `coze-server` service as if you were debugging a local application. + + ## Using the open-source version of Coze Studio > Regarding how to use Coze Studio, refer to the [Coze Development Platform Official Documentation Center](https://www.coze.cn/open/docs) for more information. Please note that certain features, such as tone customization, are limited to the commercial version. Differences between the open-source and commercial versions can be found in the **Feature List**. diff --git a/README.zh_CN.md b/README.zh_CN.md index a50262592a..2e9a173809 100644 --- a/README.zh_CN.md +++ b/README.zh_CN.md @@ -84,6 +84,49 @@ Coze Studio 的后端采用 Golang 开发,前端使用 React + TypeScript, * [代码开发与测试](https://github.com/coze-dev/coze-studio/wiki/7.-%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83#%E4%BB%A3%E7%A0%81%E5%BC%80%E5%8F%91%E4%B8%8E%E6%B5%8B%E8%AF%95):了解如何基于 Coze Studio 开源版进行二次开发与测试。 * [故障排查](https://github.com/coze-dev/coze-studio/wiki/7.-%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83#%E6%95%85%E9%9A%9C%E6%8E%92%E6%9F%A5):了解如何查看容器状态、系统日志。 +### 本地调试 (Local Debugging with Delve) + +本项目支持使用 [Delve](https://github.com/go-delve/delve) 对 `coze-server` 服务进行远程调试。 + +#### 相关文件 + +- **`backend/DockerFile-local-debug`**: + 这个 Dockerfile 用于构建一个包含调试信息的 Go 应用。它会安装 `delve` 调试器,并确保应用在启动时附加了 `delve`。 + +- **`docker/docker-compose-local-debug.yml`**: + 这个 `docker-compose` 文件用于启动整个应用的本地调试环境。它会使用 `backend/DockerFile-local-debug` 来构建 `coze-server` 服务,并暴露调试器端口 `40000`。 + +#### 运行调试环境 + +1. **切换到 `docker` 目录**: + ```bash + cd docker + ``` + +2. **启动服务**: + 使用 `docker-compose-local-debug.yml` 文件来构建并启动所有服务。 + ```bash + docker-compose -f docker-compose-local-debug.yml up --build + ``` + `coze-server` 服务将在容器内启动,并监听 `40000` 端口等待调试器连接。 + +#### 连接调试器 + +你可以使用任何支持 Delve 远程调试的 IDE 进行连接。以下为 GoLand 的配置示例。 + +1. 进入 `Run -> Edit Configurations...`。 +2. 点击 `+` 按钮添加新配置,选择 `Go Remote`。 +3. 配置以下设置: + * **Name**: `Connect to Delve (Docker)` (或任何有描述性的名称) + * **Host**: `127.0.0.1` + * **Port**: `40000` + * **Go version**: 选择你的 Go SDK。 +4. 应用并保存配置。 +5. 在 `coze-server` 以调试模式运行后(如“运行调试环境”所述),选择新创建的运行配置并点击调试图标 (🐞)。 + +现在你应该可以像调试本地应用一样,在 `coze-server` 的代码中设置断点、检查变量和执行代码了。 + + ## 使用 Coze Studio 开源版 > 关于如何使用 Coze Studio,可参考[扣子开发平台官方文档中心](https://www.coze.cn/open/docs)获取更多资料。需要注意的是,音色等部分功能限商业版本使用,开源版与商业版的功能差异可参考**功能清单**。 diff --git a/backend/DockerFile-local-debug b/backend/DockerFile-local-debug new file mode 100644 index 0000000000..95fdc6a7e5 --- /dev/null +++ b/backend/DockerFile-local-debug @@ -0,0 +1,81 @@ +# Stage 1: Builder for Go application +FROM golang:1.24-alpine AS builder + +WORKDIR /app + +# Install build dependencies for Go +RUN apk add --no-cache git gcc libc-dev + +# Copy go.mod and go.sum first to leverage Docker cache +COPY backend/go.mod backend/go.sum ./ +RUN go mod download + +RUN go install github.com/go-delve/delve/cmd/dlv@latest + +# Copy the entire backend source code +COPY backend/ ./ + +# Build the Go application +RUN go build -gcflags="all=-N -l" -o /app/opencoze main.go + + + +# Stage 2: Final image +FROM alpine:3.22.0 + +WORKDIR /app + +# Install runtime dependencies for Go app and base for Python +# pax-utils for scanelf, python3 for running Python, python3-dev for headers/shared libs +# bind-tools for nslookup etc., file for debugging file types +RUN apk add --no-cache pax-utils python3 python3-dev bind-tools file deno curl + +RUN deno run -A jsr:@langchain/pyodide-sandbox -c "print('Hello, World')" + +# Install Python build dependencies, create venv, install packages, then remove build deps +RUN apk add --no-cache --virtual .python-build-deps build-base py3-pip git && \ + python3 -m venv --copies --upgrade-deps /app/.venv && \ + # Activate venv and install packages + . /app/.venv/bin/activate && \ + # If you want to use other third-party libraries, you can install them here. + pip install urllib3==1.26.16 && \ + pip install --no-cache-dir h11==0.16.0 httpx==0.28.1 pillow==11.2.1 pdfplumber==0.11.7 python-docx==1.2.0 numpy==2.3.1 && \ + # Deactivate (optional, as RUN is a new shell) + # deactivate && \ + # Remove build dependencies + apk del .python-build-deps + + +# Copy the built Go binary from the builder stage +COPY --from=builder /app/opencoze /app/opencoze + +COPY --from=builder /go/bin/dlv /usr/local/bin/dlv + +# Copy Python application scripts +COPY backend/infra/document/parser/impl/builtin/parse_pdf.py /app/parse_pdf.py +COPY backend/infra/document/parser/impl/builtin/parse_docx.py /app/parse_docx.py +COPY backend/infra/coderunner/impl/script/sandbox.py /app/sandbox.py + + +# Copy static resources +# COPY backend/static /app/resources/static/ +COPY backend/conf /app/resources/conf/ +COPY docker/.env.example /app/.env +# COPY docker/.env.ve /app/.env +# COPY docker/cert.pem /app/cert.pem +# COPY docker/key.pem /app/key.pem + +# Set PATH to prioritize venv's binaries +ENV PATH="/app/.venv/bin:${PATH}" +# ENV LD_LIBRARY_PATH="/usr/lib:${LD_LIBRARY_PATH}" # Keep commented for now + +# Ensure python scripts and venv executables are executable +RUN chmod +x /app/parse_pdf.py /app/parse_docx.py && \ + find /app/.venv/bin -type f -exec chmod +x {} \; + + +EXPOSE 8888 40000 + +ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt + +CMD ["dlv", "exec", "/app/opencoze", "--listen=:40000", "--headless=true", "--api-version=2", "--accept-multiclient"] diff --git a/docker/docker-compose-local-debug.yml b/docker/docker-compose-local-debug.yml new file mode 100644 index 0000000000..ccbfe2b35b --- /dev/null +++ b/docker/docker-compose-local-debug.yml @@ -0,0 +1,441 @@ +name: coze-studio +x-env-file: &env_file + - .env + +services: + mysql: + image: mysql:8.4.5 + container_name: coze-mysql + restart: always + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root} + MYSQL_DATABASE: ${MYSQL_DATABASE:-opencoze} + MYSQL_USER: ${MYSQL_USER:-coze} + MYSQL_PASSWORD: ${MYSQL_PASSWORD:-coze123} + env_file: *env_file + # ports: + # - '3306' + volumes: + - ./data/mysql:/var/lib/mysql + - ./volumes/mysql/schema.sql:/docker-entrypoint-initdb.d/init.sql + - ./atlas/opencoze_latest_schema.hcl:/opencoze_latest_schema.hcl:ro + entrypoint: + - bash + - -c + - | + /usr/local/bin/docker-entrypoint.sh mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci & + MYSQL_PID=$$! + + echo 'Waiting for MySQL to start...' + until mysqladmin ping -h localhost -u root -p$${MYSQL_ROOT_PASSWORD} --silent 2>/dev/null; do + echo 'MySQL is starting...' + sleep 2 + done + + echo 'Waiting for workflow_version table to exist...' + while true; do + if mysql -h localhost -u root -p$${MYSQL_ROOT_PASSWORD} $${MYSQL_DATABASE} -e "SHOW TABLES LIKE 'workflow_version';" 2>/dev/null | grep -q "workflow_version"; then + echo 'Found workflow_version table, continuing...' + break + else + echo 'workflow_version table not found, retrying in 2 seconds...' + sleep 2 + fi + done + + echo 'MySQL is ready, installing Atlas CLI...' + + if ! command -v atlas >/dev/null 2>&1; then + echo 'Installing Atlas CLI...' + curl -sSf https://atlasgo.sh | sh -s -- -y --community + export PATH=$$PATH:/root/.local/bin + else + echo 'Atlas CLI already installed' + fi + + if [ -f '/opencoze_latest_schema.hcl' ]; then + echo 'Running Atlas migrations...' + ATLAS_URL="mysql://$${MYSQL_USER}:$${MYSQL_PASSWORD}@localhost:3306/$${MYSQL_DATABASE}" + atlas schema apply -u "$ATLAS_URL" --to "file:///opencoze_latest_schema.hcl" --exclude "atlas_schema_revisions,table_*" --auto-approve + echo 'Atlas migrations completed successfully' + else + echo 'No migrations found' + fi + wait $$MYSQL_PID + healthcheck: + test: + [ + 'CMD', + 'mysqladmin', + 'ping', + '-h', + 'localhost', + '-u$${MYSQL_USER}', + '-p$${MYSQL_PASSWORD}', + ] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + networks: + - coze-network + + redis: + image: bitnamilegacy/redis:8.0 + container_name: coze-redis + restart: always + user: root + privileged: true + env_file: *env_file + environment: + - REDIS_AOF_ENABLED=${REDIS_AOF_ENABLED:-no} + - REDIS_PORT_NUMBER=${REDIS_PORT_NUMBER:-6379} + - REDIS_IO_THREADS=${REDIS_IO_THREADS:-4} + - ALLOW_EMPTY_PASSWORD=${ALLOW_EMPTY_PASSWORD:-yes} + # ports: + # - '6379' + volumes: + - ./data/bitnami/redis:/bitnami/redis/data:rw,Z + command: > + bash -c " + /opt/bitnami/scripts/redis/setup.sh + # Set proper permissions for data directories + chown -R redis:redis /bitnami/redis/data + chmod g+s /bitnami/redis/data + + exec /opt/bitnami/scripts/redis/entrypoint.sh /opt/bitnami/scripts/redis/run.sh + " + healthcheck: + test: ['CMD', 'redis-cli', 'ping'] + interval: 5s + timeout: 10s + retries: 10 + start_period: 10s + networks: + - coze-network + elasticsearch: + image: bitnamilegacy/elasticsearch:8.18.0 + container_name: coze-elasticsearch + restart: always + user: root + privileged: true + env_file: *env_file + environment: + - TEST=1 + # Add Java certificate trust configuration + # - ES_JAVA_OPTS=-Djdk.tls.client.protocols=TLSv1.2 -Dhttps.protocols=TLSv1.2 -Djavax.net.ssl.trustAll=true -Xms4096m -Xmx4096m + # ports: + # - '9200' + volumes: + - ./data/bitnami/elasticsearch:/bitnami/elasticsearch/data + - ./volumes/elasticsearch/elasticsearch.yml:/opt/bitnami/elasticsearch/config/my_elasticsearch.yml + - ./volumes/elasticsearch/analysis-smartcn.zip:/opt/bitnami/elasticsearch/analysis-smartcn.zip:rw,Z + - ./volumes/elasticsearch/setup_es.sh:/setup_es.sh + - ./volumes/elasticsearch/es_index_schema:/es_index_schema + healthcheck: + test: + [ + 'CMD-SHELL', + 'curl -f http://localhost:9200 && [ -f /tmp/es_plugins_ready ] && [ -f /tmp/es_init_complete ]', + ] + interval: 5s + timeout: 10s + retries: 10 + start_period: 10s + networks: + - coze-network + # Install smartcn analyzer plugin and initialize ES + command: > + bash -c " + /opt/bitnami/scripts/elasticsearch/setup.sh + # Set proper permissions for data directories + chown -R elasticsearch:elasticsearch /bitnami/elasticsearch/data + chmod g+s /bitnami/elasticsearch/data + + # Create plugin directory + mkdir -p /bitnami/elasticsearch/plugins; + + # Unzip plugin to plugin directory and set correct permissions + echo 'Installing smartcn plugin...'; + if [ ! -d /opt/bitnami/elasticsearch/plugins/analysis-smartcn ]; then + + # Download plugin package locally + echo 'Copying smartcn plugin...'; + cp /opt/bitnami/elasticsearch/analysis-smartcn.zip /tmp/analysis-smartcn.zip + + elasticsearch-plugin install file:///tmp/analysis-smartcn.zip + if [[ "$$?" != "0" ]]; then + echo 'Plugin installation failed, exiting operation'; + rm -rf /opt/bitnami/elasticsearch/plugins/analysis-smartcn + exit 1; + fi; + rm -f /tmp/analysis-smartcn.zip; + fi; + + # Create marker file indicating plugin installation success + touch /tmp/es_plugins_ready; + echo 'Plugin installation successful, marker file created'; + + # Start initialization script in background + ( + echo 'Waiting for Elasticsearch to be ready...' + until curl -s -f http://localhost:9200/_cat/health >/dev/null 2>&1; do + echo 'Elasticsearch not ready, waiting...' + sleep 2 + done + echo 'Elasticsearch is ready!' + + # Run ES initialization script + echo 'Running Elasticsearch initialization...' + sed 's/\r$$//' /setup_es.sh > /setup_es_fixed.sh + chmod +x /setup_es_fixed.sh + /setup_es_fixed.sh --index-dir /es_index_schema + # Create marker file indicating initialization completion + touch /tmp/es_init_complete + echo 'Elasticsearch initialization completed successfully!' + ) & + + # Start Elasticsearch + exec /opt/bitnami/scripts/elasticsearch/entrypoint.sh /opt/bitnami/scripts/elasticsearch/run.sh + echo -e "⏳ Adjusting Elasticsearch disk watermark settings..." + " + + minio: + image: minio/minio:RELEASE.2025-06-13T11-33-47Z-cpuv1 + container_name: coze-minio + user: root + privileged: true + restart: always + env_file: *env_file + # ports: + # - '9000' + # - '9001' + volumes: + - ./data/minio:/data + - ./volumes/minio/default_icon/:/default_icon + - ./volumes/minio/official_plugin_icon/:/official_plugin_icon + environment: + MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin} + MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin123} + MINIO_DEFAULT_BUCKETS: ${STORAGE_BUCKET:-opencoze},${MINIO_DEFAULT_BUCKETS:-milvus} + entrypoint: + - /bin/sh + - -c + - | + # Run initialization in background + ( + # Wait for MinIO to be ready + until (/usr/bin/mc alias set localminio http://localhost:9000 $${MINIO_ROOT_USER} $${MINIO_ROOT_PASSWORD}) do + echo "Waiting for MinIO to be ready..." + sleep 1 + done + + # Create bucket and copy files + /usr/bin/mc mb --ignore-existing localminio/$${STORAGE_BUCKET} + /usr/bin/mc cp --recursive /default_icon/ localminio/$${STORAGE_BUCKET}/default_icon/ + /usr/bin/mc cp --recursive /official_plugin_icon/ localminio/$${STORAGE_BUCKET}/official_plugin_icon/ + + echo "MinIO initialization complete." + ) & + + # Start minio server in foreground + exec minio server /data --console-address ":9001" + healthcheck: + test: + [ + 'CMD-SHELL', + '/usr/bin/mc alias set health_check http://localhost:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD} && /usr/bin/mc ready health_check', + ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + networks: + - coze-network + + etcd: + image: bitnamilegacy/etcd:3.5 + container_name: coze-etcd + user: root + restart: always + privileged: true + env_file: *env_file + environment: + - ETCD_AUTO_COMPACTION_MODE=revision + - ETCD_AUTO_COMPACTION_RETENTION=1000 + - ETCD_QUOTA_BACKEND_BYTES=4294967296 + - ALLOW_NONE_AUTHENTICATION=yes + # ports: + # - '2379' + # - '2380' + volumes: + - ./data/bitnami/etcd:/bitnami/etcd:rw,Z + - ./volumes/etcd/etcd.conf.yml:/opt/bitnami/etcd/conf/etcd.conf.yml:ro,Z + command: > + bash -c " + /opt/bitnami/scripts/etcd/setup.sh + # Set proper permissions for data and config directories + chown -R etcd:etcd /bitnami/etcd + chmod g+s /bitnami/etcd + + exec /opt/bitnami/scripts/etcd/entrypoint.sh /opt/bitnami/scripts/etcd/run.sh + " + healthcheck: + test: ['CMD', 'etcdctl', 'endpoint', 'health'] + interval: 5s + timeout: 10s + retries: 10 + start_period: 10s + networks: + - coze-network + + milvus: + container_name: coze-milvus + image: milvusdb/milvus:v2.5.10 + user: root + privileged: true + restart: always + env_file: *env_file + command: > + bash -c " + # Set proper permissions for data directories + chown -R root:root /var/lib/milvus + chmod g+s /var/lib/milvus + + exec milvus run standalone + " + security_opt: + - seccomp:unconfined + environment: + ETCD_ENDPOINTS: etcd:2379 + MINIO_ADDRESS: minio:9000 + MINIO_BUCKET_NAME: ${MINIO_DEFAULT_BUCKETS:-milvus} + MINIO_ACCESS_KEY_ID: ${MINIO_ROOT_USER:-minioadmin} + MINIO_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD:-minioadmin123} + MINIO_USE_SSL: false + LOG_LEVEL: debug + volumes: + - ./data/milvus:/var/lib/milvus:rw,Z + healthcheck: + test: ['CMD', 'curl', '-f', 'http://localhost:9091/healthz'] + interval: 5s + timeout: 10s + retries: 10 + start_period: 10s + # ports: + # - '19530' + # - '9091' + depends_on: + etcd: + condition: service_healthy + minio: + condition: service_healthy + networks: + - coze-network + nsqlookupd: + image: nsqio/nsq:v1.2.1 + container_name: coze-nsqlookupd + command: /nsqlookupd + restart: always + # ports: + # - '4160' + # - '4161' + networks: + - coze-network + healthcheck: + test: ['CMD-SHELL', 'nsqlookupd --version'] + interval: 5s + timeout: 10s + retries: 10 + start_period: 10s + + nsqd: + image: nsqio/nsq:v1.2.1 + container_name: coze-nsqd + command: /nsqd --lookupd-tcp-address=nsqlookupd:4160 --broadcast-address=nsqd + restart: always + # ports: + # - '4150' + # - '4151' + depends_on: + nsqlookupd: + condition: service_healthy + networks: + - coze-network + healthcheck: + test: ['CMD-SHELL', '/nsqd --version'] + interval: 5s + timeout: 10s + retries: 10 + start_period: 10s + + nsqadmin: + image: nsqio/nsq:v1.2.1 + container_name: coze-nsqadmin + command: /nsqadmin --lookupd-http-address=nsqlookupd:4161 + restart: always + # ports: + # - '4171' + depends_on: + nsqlookupd: + condition: service_healthy + networks: + - coze-network + + coze-server: + build: + context: ../ + dockerfile: backend/DockerFile-local-debug + image: cozedev/coze-studio-server:latest + restart: always + container_name: coze-server + env_file: *env_file + # environment: + # LISTEN_ADDR: 0.0.0.0:8888 + networks: + - coze-network + ports: + - "40000:40000" # <--- 映射 Delve 的调试端口 + security_opt: + - "seccomp:unconfined" + cap_add: + - SYS_PTRACE + volumes: + - .env:/app/.env + - ../backend/conf:/app/resources/conf + # - ../backend/static:/app/resources/static + depends_on: + mysql: + condition: service_healthy + redis: + condition: service_healthy + elasticsearch: + condition: service_healthy + minio: + condition: service_healthy + milvus: + condition: service_healthy + command: ["dlv", "exec", "/app/opencoze", "--listen=:40000", "--headless=true", "--api-version=2", "--accept-multiclient"] + + coze-web: + build: + context: .. + dockerfile: frontend/Dockerfile + image: cozedev/coze-studio-web:latest + container_name: coze-web + restart: always + ports: + - "${WEB_LISTEN_ADDR:-8888}:80" + # - "443:443" # SSL port (uncomment if using SSL) + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro # Main nginx config + - ./nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf:ro # Proxy config + # - ./nginx/ssl:/etc/nginx/ssl:ro # SSL certificates (uncomment if using SSL) + depends_on: + - coze-server + networks: + - coze-network + +networks: + coze-network: + driver: bridge