feat: add Docker Compose deployment support (#38)

- Add Docker Compose configuration for one-click deployment
- Add Dockerfiles for server, agent, and web services
- Add deployment script (deploy.sh) with automatic setup
- Add comprehensive deployment documentation (DEPLOYMENT.md)
- Use host Docker socket instead of DinD for better performance
- Add Nginx configuration for web frontend
- Add Makefile for common operations
- Update README with Docker deployment quick start

Features:
- One-command deployment with ./deploy.sh
- Automatic JWT secret generation
- Health checks for all services
- Data persistence with Docker volumes
- Support for Bot container management via host Docker
- Production-ready configuration examples

Co-authored-by: root <root@DESKTOP-OU6H3GS.localdomain>
This commit is contained in:
zenhouke
2026-02-11 22:58:05 +08:00
committed by GitHub
parent f8cd244d25
commit d45487433c
11 changed files with 930 additions and 3 deletions
+60
View File
@@ -0,0 +1,60 @@
# Git
.git
.gitignore
.gitattributes
# Documentation
*.md
docs/
assets/
# IDE
.vscode/
.cursor/
.idea/
# Dependencies
node_modules/
**/node_modules/
# Build outputs
dist/
**/dist/
build/
**/build/
out/
# Logs
*.log
logs/
# Environment
.env
.env.local
.env.*.local
# OS
.DS_Store
Thumbs.db
# Temporary files
tmp/
temp/
*.tmp
*.swp
*.swo
# Test
coverage/
.nyc_output/
# Mise
.mise.toml.local
# Data
data/
*.db
*.sqlite
# Docker
docker-compose.override.yml
+401
View File
@@ -0,0 +1,401 @@
# Memoh Docker Deployment Guide
Deploy Memoh AI Agent System with Docker Compose in one command.
## Quick Start
### 1. Clone the Repository
```bash
git clone https://github.com/memohai/Memoh.git
cd Memoh
```
### 2. One-Click Deployment
```bash
./deploy.sh
```
The script will automatically:
- Check Docker and Docker Compose installation
- Create `.env` configuration file (if not exists)
- Generate random JWT secret
- Create `config.toml` configuration file
- Build MCP image
- Start all services
### 3. Access the Application
- Web UI: http://localhost
- API Service: http://localhost:8080
- Agent Gateway: http://localhost:8081
Default admin credentials:
- Username: `admin`
- Password: `admin123` (change in `.env`)
## Manual Deployment
If you prefer not to use the automated script:
```bash
# 1. Create configuration files
cp .env.example .env
cp config.docker.toml config.toml
# 2. Edit configuration (Important!)
nano .env
# 3. Generate JWT secret
openssl rand -base64 32
# 4. Build MCP image
docker build -f cmd/mcp/Dockerfile -t memoh-mcp:latest .
# 5. Start services
docker compose up -d
# 6. View logs
docker compose logs -f
```
## Architecture
This deployment uses the host's Docker daemon to manage Bot containers:
```
Host Docker
├── memoh-postgres (PostgreSQL)
├── memoh-qdrant (Qdrant)
├── memoh-server (Main Service) ← Manages Bot containers via /var/run/docker.sock
├── memoh-agent (Agent Gateway)
├── memoh-web (Web Frontend)
└── memoh-bot-* (Bot containers, dynamically created by main service)
```
Advantages:
- ✅ Lightweight, no additional Docker daemon needed
- ✅ Better performance, uses host container runtime directly
- ✅ Easier to manage and debug
- ✅ Lower resource consumption
## Common Commands
### Using Make (Recommended)
```bash
make help # Show all commands
make deploy # One-click deployment
make logs # View logs
make restart # Restart services
make ps # View status
make backup # Backup data
make bots # View Bot containers
```
### Using Docker Compose
```bash
docker compose up -d # Start services
docker compose down # Stop services
docker compose logs -f # View logs
docker compose ps # View status
docker compose restart # Restart services
```
## Configuration
### Environment Variables (.env)
Key configuration items:
```bash
# PostgreSQL password (must change)
POSTGRES_PASSWORD=your_secure_password
# JWT secret (must change)
JWT_SECRET=your_random_jwt_secret
# Admin account
ADMIN_USERNAME=admin
ADMIN_PASSWORD=your_admin_password
ADMIN_EMAIL=admin@yourdomain.com
```
### Application Configuration (config.toml)
Main configuration items:
```toml
[postgres]
host = "postgres"
password = "your_secure_password" # Must match POSTGRES_PASSWORD in .env
[containerd]
socket_path = "unix:///var/run/docker.sock" # Use host Docker
[qdrant]
base_url = "http://qdrant:6334"
```
## Service Overview
| Service | Container Name | Ports | Description |
|---------|---------------|-------|-------------|
| postgres | memoh-postgres | - | PostgreSQL database (internal only) |
| qdrant | memoh-qdrant | - | Qdrant vector database (internal only) |
| docker-cli | memoh-docker-cli | - | Docker CLI (uses host Docker) |
| server | memoh-server | 8080 | Main service (Go) |
| agent | memoh-agent | 8081 | Agent Gateway (Bun) |
| web | memoh-web | 80 | Web frontend (Nginx) |
## Data Persistence
Data is stored in Docker volumes:
```bash
# View volumes
docker volume ls | grep memoh
# Backup database
docker compose exec postgres pg_dump -U memoh memoh > backup.sql
```
### Bot Container Management
Bot containers are dynamically created by the main service and run directly on the host:
```bash
# View all Bot containers
make bots
# or
docker ps -a | grep memoh-bot
# View Bot logs
docker logs <bot-container-id>
# Enter Bot container
docker exec -it <bot-container-id> sh
# Stop Bot container
docker stop <bot-container-id>
```
## Backup and Restore
### Backup
```bash
# Create backup directory
mkdir -p backups
# Backup database
docker compose exec postgres pg_dump -U memoh memoh > backups/postgres_$(date +%Y%m%d).sql
# Backup Bot data
docker run --rm -v memoh_memoh_bot_data:/data -v $(pwd)/backups:/backup alpine \
tar czf /backup/bot_data_$(date +%Y%m%d).tar.gz -C /data .
# Backup configuration files
tar czf backups/config_$(date +%Y%m%d).tar.gz config.toml .env
```
### Restore
```bash
# Restore database
docker compose exec -T postgres psql -U memoh memoh < backups/postgres_20240101.sql
# Restore Bot data
docker run --rm -v memoh_memoh_bot_data:/data -v $(pwd)/backups:/backup alpine \
tar xzf /backup/bot_data_20240101.tar.gz -C /data
```
## Troubleshooting
### Services Won't Start
```bash
# View detailed logs
docker compose logs server
# Check configuration
docker compose config
# Rebuild
docker compose build --no-cache
docker compose up -d
```
### Database Connection Failed
```bash
# Check if database is ready
docker compose exec postgres pg_isready -U memoh
# Test connection
docker compose exec postgres psql -U memoh -d memoh
# View database logs
docker compose logs postgres
```
### Port Conflicts
```bash
# Check port usage
sudo netstat -tlnp | grep :8080
sudo netstat -tlnp | grep :80
# Modify port mapping in docker-compose.yml
# Example: change "80:80" to "8000:80"
```
### Docker Socket Permission Issues
```bash
# Add user to docker group
sudo usermod -aG docker $USER
newgrp docker
# Check permissions
ls -la /var/run/docker.sock
```
## Production Deployment
### 1. Use HTTPS
Create `docker-compose.override.yml`:
```yaml
services:
web:
ports:
- "443:443"
volumes:
- ./ssl:/etc/nginx/ssl:ro
- ./nginx-https.conf:/etc/nginx/conf.d/default.conf:ro
```
Create `nginx-https.conf`:
```nginx
server {
listen 80;
server_name your-domain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name your-domain.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Other configurations same as nginx.conf
# ...
}
```
### 2. Resource Limits
Edit `docker-compose.yml` to add resource limits:
```yaml
services:
server:
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '1'
memory: 1G
```
### 3. Security Recommendations
Production environment recommendations:
- Use separate `.env` file
- Change all default passwords
- Use strong JWT secret
- Configure firewall rules
- Use HTTPS
- Regular data backups
- Limit containerd socket access permissions
- Run services as non-root user
- Configure log rotation
## Performance Optimization
### PostgreSQL Optimization
Create `postgres-custom.conf`:
```
shared_buffers = 2GB
effective_cache_size = 6GB
maintenance_work_mem = 512MB
checkpoint_completion_target = 0.9
wal_buffers = 16MB
```
Mount in `docker-compose.yml`:
```yaml
postgres:
volumes:
- ./postgres-custom.conf:/etc/postgresql/postgresql.conf:ro
command: postgres -c config_file=/etc/postgresql/postgresql.conf
```
### Network Optimization
```yaml
networks:
memoh-network:
driver: bridge
driver_opts:
com.docker.network.driver.mtu: 1500
```
## Update Application
```bash
# Pull latest code
git pull
# Rebuild and restart
docker compose up -d --build
# Or use Make
make update
```
## Complete Uninstall
```bash
# Stop and remove all containers
docker compose down
# Remove data volumes (Warning! This deletes all data)
docker compose down -v
# Remove images
docker rmi memoh-mcp:latest
docker rmi $(docker images | grep memoh | awk '{print $3}')
```
## Security Considerations
⚠️ Important Security Notes:
1. **Docker Socket Access**: The main service container has access to the host Docker socket, which means the application can manage other containers on the host. Only run in trusted environments.
2. **Change Default Passwords**: Must change all default passwords in `.env`
3. **Strong JWT Secret**: Use a strong random JWT secret
4. **Firewall**: Configure firewall to only open necessary ports
5. **HTTPS**: Use HTTPS in production
6. **Regular Backups**: Regularly backup data
7. **Updates**: Regularly update images and dependencies
## Get Help
- Detailed Documentation: [DOCKER_DEPLOYMENT_CN.md](DOCKER_DEPLOYMENT_CN.md) (Chinese)
- GitHub Issues: https://github.com/memohai/Memoh/issues
- Telegram Group: https://t.me/memohai
- Email: business@memoh.net
---
**That's it! Deploy Memoh in minutes!**
+28
View File
@@ -0,0 +1,28 @@
FROM oven/bun:1 AS builder
WORKDIR /build
COPY agent/package.json agent/bun.lock* ./
RUN bun install
COPY agent/ ./
RUN bun run build
FROM oven/bun:1-alpine
WORKDIR /app
RUN apk add --no-cache ca-certificates wget
COPY --from=builder /build/dist /app/dist
COPY --from=builder /build/node_modules /app/node_modules
COPY --from=builder /build/package.json /app/package.json
EXPOSE 8081
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8081/health || exit 1
CMD ["bun", "run", "dist/index.js"]
+33
View File
@@ -0,0 +1,33 @@
FROM golang:1.25-alpine AS builder
WORKDIR /build
RUN apk add --no-cache git make
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -trimpath -ldflags "-s -w" \
-o memoh-server ./cmd/agent/main.go
FROM alpine:latest
WORKDIR /app
RUN apk add --no-cache ca-certificates tzdata wget
COPY --from=builder /build/memoh-server /app/memoh-server
COPY config.toml.example /app/config.toml.example
RUN mkdir -p /var/lib/memoh/data
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
CMD ["/app/memoh-server"]
+33
View File
@@ -0,0 +1,33 @@
FROM node:25-alpine AS builder
WORKDIR /build
RUN npm install -g pnpm@10
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
COPY packages ./packages
RUN pnpm install
ARG VITE_API_URL=http://localhost:8080
ARG VITE_AGENT_URL=http://localhost:8081
ENV VITE_API_URL=$VITE_API_URL
ENV VITE_AGENT_URL=$VITE_AGENT_URL
WORKDIR /build/packages/web
RUN pnpm build
FROM nginx:alpine
COPY --from=builder /build/packages/web/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1
CMD ["nginx", "-g", "daemon off;"]
+16 -2
View File
@@ -43,9 +43,23 @@ Memoh Bot can distinguish and remember requests from multiple humans and bots, w
Please refer to the [Roadmap Version 0.1](https://github.com/memohai/Memoh/issues/2) for more details. Please refer to the [Roadmap Version 0.1](https://github.com/memohai/Memoh/issues/2) for more details.
## Development ## Quick Start
Refer to [CONTRIBUTING.md](CONTRIBUTING.md) for more details. ### Docker Deployment (Recommended)
The fastest way to deploy Memoh:
```bash
git clone https://github.com/memohai/Memoh.git
cd Memoh
./deploy.sh
```
Visit http://localhost after deployment. See [Docker Deployment Guide](README_DOCKER.md) for details.
### Development
Refer to [CONTRIBUTING.md](CONTRIBUTING.md) for development setup.
## Star History ## Star History
+15 -1
View File
@@ -43,7 +43,21 @@ Memoh Bot 能够区分并记忆来自多个人类/Bot 的请求,可在任意
详情请参阅 [Roadmap Version 0.1](https://github.com/memohai/Memoh/issues/2)。 详情请参阅 [Roadmap Version 0.1](https://github.com/memohai/Memoh/issues/2)。
## 开发 ## 快速开始
### Docker 部署(推荐)
最快的部署方式:
```bash
git clone https://github.com/memohai/Memoh.git
cd Memoh
./deploy.sh
```
部署完成后访问 http://localhost。详见 [Docker 部署指南](README_DOCKER.md)。
### 开发环境
详见 [CONTRIBUTING.md](CONTRIBUTING.md)。 详见 [CONTRIBUTING.md](CONTRIBUTING.md)。
+54
View File
@@ -0,0 +1,54 @@
## Service configuration
[log]
level = "info"
format = "text"
[server]
addr = ":8080"
## Admin
[admin]
username = "admin"
password = "admin123"
email = "admin@memoh.local"
## Auth configuration
[auth]
jwt_secret = "YZq8kXrW5dFpNt9mLxQvHbRjKsMnOePw"
jwt_expires_in = "168h"
## Docker configuration (使用宿主机 Docker socket)
[containerd]
socket_path = "unix:///var/run/docker.sock"
namespace = "default"
[mcp]
busybox_image = "memoh-mcp:latest"
snapshotter = "overlayfs"
data_root = "/var/lib/memoh/data"
data_mount = "/data"
## Postgres configuration
[postgres]
host = "postgres"
port = 5432
user = "memoh"
password = "memoh123"
database = "memoh"
sslmode = "disable"
## Qdrant configuration
[qdrant]
base_url = "http://qdrant:6334"
api_key = ""
collection = "memory"
timeout_seconds = 10
## Agent Gateway
[agent_gateway]
host = "agent"
port = 8081
[brave]
api_key = ""
base_url = "https://api.search.brave.com/res/v1/"
Executable
+89
View File
@@ -0,0 +1,89 @@
#!/bin/bash
set -e
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN} Memoh Docker Compose Deployment${NC}"
echo -e "${GREEN}========================================${NC}"
echo ""
# Check Docker
if ! command -v docker &> /dev/null; then
echo -e "${RED}Error: Docker is not installed${NC}"
echo "Please install Docker first: https://docs.docker.com/get-docker/"
exit 1
fi
# Check Docker Compose
if ! docker compose version &> /dev/null; then
echo -e "${RED}Error: Docker Compose is not installed or version is too old${NC}"
echo "Please install Docker Compose v2.0+: https://docs.docker.com/compose/install/"
exit 1
fi
echo -e "${GREEN}✓ Docker and Docker Compose are installed${NC}"
echo ""
# Check .env file
if [ ! -f .env ]; then
echo -e "${YELLOW}⚠ .env file does not exist, creating...${NC}"
cp .env.example .env
# Generate random JWT secret
JWT_SECRET=$(openssl rand -base64 32 2>/dev/null || head -c 32 /dev/urandom | base64)
sed -i.bak "s|JWT_SECRET=.*|JWT_SECRET=$JWT_SECRET|g" .env
rm -f .env.bak
echo -e "${GREEN}✓ .env file created${NC}"
echo -e "${YELLOW}⚠ Please edit .env file to change default passwords and configuration${NC}"
echo ""
fi
# Check config.toml
if [ ! -f config.toml ]; then
echo -e "${YELLOW}⚠ config.toml does not exist, creating...${NC}"
cp config.docker.toml config.toml
echo -e "${GREEN}✓ config.toml created${NC}"
echo ""
fi
# Build MCP image
echo -e "${GREEN}Building MCP image...${NC}"
if docker build -f cmd/mcp/Dockerfile -t memoh-mcp:latest . > /dev/null 2>&1; then
echo -e "${GREEN}✓ MCP image built successfully${NC}"
else
echo -e "${YELLOW}⚠ MCP image build failed, will try to pull at runtime${NC}"
fi
echo ""
# Start services
echo -e "${GREEN}Starting services...${NC}"
docker compose up -d
echo ""
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN} Deployment Complete!${NC}"
echo -e "${GREEN}========================================${NC}"
echo ""
echo "Service URLs:"
echo " - Web UI: http://localhost"
echo " - API Service: http://localhost:8080"
echo " - Agent Gateway: http://localhost:8081"
echo ""
echo "View service status:"
echo " docker compose ps"
echo ""
echo "View logs:"
echo " docker compose logs -f"
echo ""
echo "Stop services:"
echo " docker compose down"
echo ""
echo -e "${YELLOW}⚠ First startup may take 1-2 minutes, please be patient${NC}"
echo ""
echo "View detailed documentation: DEPLOYMENT.md"
+137
View File
@@ -0,0 +1,137 @@
services:
postgres:
image: postgres:16-alpine
container_name: memoh-postgres
environment:
POSTGRES_DB: memoh
POSTGRES_USER: memoh
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-memoh123}
volumes:
- postgres_data:/var/lib/postgresql/data
- ./db/migrations:/docker-entrypoint-initdb.d:ro
expose:
- "5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U memoh"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- memoh-network
qdrant:
image: qdrant/qdrant:latest
container_name: memoh-qdrant
volumes:
- qdrant_data:/qdrant/storage
expose:
- "6333"
- "6334"
healthcheck:
test: ["CMD-SHELL", "timeout 10s bash -c ':> /dev/tcp/127.0.0.1/6333' || exit 1"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- memoh-network
docker-cli:
image: docker:27-cli
container_name: memoh-docker-cli
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- memoh_bot_data:/var/lib/memoh/data
command: ["tail", "-f", "/dev/null"]
restart: unless-stopped
networks:
- memoh-network
server:
build:
context: .
dockerfile: Dockerfile.server
container_name: memoh-server
environment:
- LOG_LEVEL=${LOG_LEVEL:-info}
- SERVER_ADDR=:8080
- POSTGRES_HOST=postgres
- POSTGRES_PORT=5432
- POSTGRES_USER=memoh
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-memoh123}
- POSTGRES_DB=memoh
- POSTGRES_SSLMODE=disable
- QDRANT_BASE_URL=http://qdrant:6334
- QDRANT_COLLECTION=memory
- CONTAINERD_SOCKET=unix:///var/run/docker.sock
- AGENT_GATEWAY_HOST=agent
- AGENT_GATEWAY_PORT=8081
- JWT_SECRET=${JWT_SECRET:-YZq8kXrW5dFpNt9mLxQvHbRjKsMnOePw}
- JWT_EXPIRES_IN=168h
- ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin123}
- ADMIN_EMAIL=${ADMIN_EMAIL:-admin@memoh.local}
- MCP_BUSYBOX_IMAGE=memoh-mcp:latest
- MCP_DATA_ROOT=/var/lib/memoh/data
- MCP_DATA_MOUNT=/data
volumes:
- ./config.toml:/app/config.toml:ro
- /var/run/docker.sock:/var/run/docker.sock
- memoh_bot_data:/var/lib/memoh/data
ports:
- "8080:8080"
depends_on:
postgres:
condition: service_healthy
qdrant:
condition: service_healthy
restart: unless-stopped
networks:
- memoh-network
agent:
build:
context: .
dockerfile: Dockerfile.agent
container_name: memoh-agent
environment:
- NODE_ENV=production
- PORT=8081
ports:
- "8081:8081"
depends_on:
- server
restart: unless-stopped
networks:
- memoh-network
web:
build:
context: .
dockerfile: Dockerfile.web
args:
- VITE_API_URL=${VITE_API_URL:-http://localhost:8080}
- VITE_AGENT_URL=${VITE_AGENT_URL:-http://localhost:8081}
container_name: memoh-web
ports:
- "8090:80"
depends_on:
- server
- agent
restart: unless-stopped
networks:
- memoh-network
volumes:
postgres_data:
driver: local
qdrant_data:
driver: local
memoh_bot_data:
driver: local
networks:
memoh-network:
driver: bridge
+64
View File
@@ -0,0 +1,64 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json application/javascript;
# 前端路由
location / {
try_files $uri $uri/ /index.html;
}
# API 代理
location /api/ {
proxy_pass http://memoh-server:8080/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
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 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Agent Gateway 代理
location /agent/ {
proxy_pass http://memoh-agent:8081/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
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 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# 静态资源缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}