├── .cursor └── rules │ └── docker-rules.mdc ├── .gitignore ├── README.md ├── docker-compose-rollout ├── README.md ├── build-example.sh ├── caddy │ └── Caddyfile ├── compose.yaml ├── rolling-update.sh └── web-example │ ├── Dockerfile │ └── src │ └── main.go ├── gitlab-ci-runner ├── README.md └── init.sh ├── kafka-replica ├── README.md └── init.sh ├── kafka-single ├── README.md └── init.sh ├── mongo-shard-replica ├── README.md └── init.sh ├── mongo-single-replica ├── README.md └── init.sh └── redis-shard-replica ├── README.md └── init.sh /.cursor/rules/docker-rules.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Docker 和 Docker Compose 使用规范 3 | globs: **/*.{yml,yaml}, **/Dockerfile 4 | --- 5 | 6 | # Docker使用规范 7 | 8 | ## 1. Docker Compose 版本规范 9 | 10 | - Compose 文件不需要指定 `version` 字段(现代 Docker Compose 自动使用最新格式) 11 | - 文件命名统一使用 `compose.yaml` 或 `compose.yml`(不再使用 docker-compose.yml) 12 | 13 | ## 2. Dockerfile 规范 14 | 15 | - 基础镜像应当使用官方镜像,并指定具体版本号 16 | - 使用多阶段构建(Multi-stage builds)减小最终镜像大小 17 | - 合并 RUN 指令,减少镜像层数 18 | - 使用 .dockerignore 文件排除不需要的文件 19 | - 遵循最小权限原则,避免使用 root 用户运行应用 20 | 21 | ## 3. Docker Compose 配置规范 22 | 23 | ### 3.1 服务定义 24 | ```yaml 25 | services: 26 | web: 27 | image: nginx:latest 28 | container_name: web_app # 指定容器名称 29 | restart: unless-stopped # 定义重启策略 30 | environment: 31 | - NODE_ENV=production 32 | ports: 33 | - "80:80" 34 | volumes: 35 | - ./nginx.conf:/etc/nginx/nginx.conf:ro 36 | ``` 37 | 38 | ### 3.2 网络配置 39 | ```yaml 40 | networks: 41 | backend: 42 | driver: bridge 43 | name: backend_network 44 | ``` 45 | 46 | ### 3.3 数据卷配置 47 | ```yaml 48 | volumes: 49 | db_data: 50 | driver: local 51 | ``` 52 | 53 | ## 4. 安全规范 54 | 55 | - 避免在镜像中存储敏感信息 56 | - 使用 `.env` 文件管理环境变量 57 | - 定期更新基础镜像以修复安全漏洞 58 | - 使用 secrets 管理敏感数据 59 | - 限制容器资源使用 60 | 61 | ## 5. 最佳实践 62 | 63 | - 使用 healthcheck 确保服务健康状态 64 | - 合理设置日志轮转策略 65 | - 使用 docker-compose.yml 配置文件的 `depends_on` 管理服务依赖 66 | - 为容器设置资源限制(CPU、内存) 67 | - 使用 `docker compose down` 清理停止的容器和网络 68 | 69 | ## 6. 开发环境配置 70 | 71 | - 使用 docker-compose.override.yml 覆盖生产环境配置 72 | - 开发环境启用调试端口和调试工具 73 | - 使用 volume 挂载源代码目录实现热重载 74 | 75 | ## 7. 生产环境部署 76 | 77 | - 使用 Docker Compose 进行服务管理和部署 78 | - 实施监控和日志收集 79 | - 配置备份策略 80 | - 合理规划服务扩容方案 81 | - 使用 watchtower 等工具自动更新容器 82 | 83 | ## 8. 命名规范 84 | 85 | - 容器名称:使用有意义的名称,遵循 `项目名_服务名` 格式 86 | - 镜像标签:使用语义化版本号 87 | - 网络名称:使用描述性名称,表明用途 88 | 89 | ## 9. 文档要求 90 | 91 | - 每个项目必须包含 README.md,说明如何构建和运行 92 | - 记录所有环境变量的用途和默认值 93 | - 提供示例配置文件 94 | - 说明调试和故障排除方法 95 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | mongo-shard-replica/mongo-cluster1 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-example 2 | 3 | ## [MongoDB 单节点副本集](./mongo-single-replica/README.md) 4 | 5 | ## [MongoDB 分片副本集集群](./mongo-shard-replica/README.md) 6 | 7 | ## [Docker GitLab CI Runner](./gitlab-ci-runner/README.md) 8 | 9 | ## [Docker Compose 滚动更新脚本](./docker-compose-rollout/README.md) 10 | 11 | ## [Redis 分片集群](./redis-shard-replica/README.md) 12 | 13 | ## [Kafka 集群](./kafka-replica/README.md) 14 | 15 | ## [Kafka 单节点](./kafka-single/README.md) -------------------------------------------------------------------------------- /docker-compose-rollout/README.md: -------------------------------------------------------------------------------- 1 | # Docker Compose 滚动更新脚本 2 | 3 | 一个通用的基于 Docker Compose 的容器滚动更新方案,支持零停机更新任何配置了健康检查的 Docker 服务。 4 | 5 | ## 核心功能 6 | 7 | 1. **零停机滚动更新** 8 | - 自动拉取最新镜像 9 | - 逐个替换现有容器,确保服务持续可用 10 | - 基于 docker compose scale 实现容器动态伸缩 11 | - 自动维持服务实例数量 12 | 13 | 2. **健康状态保障** 14 | - 自动检查新容器健康状态 15 | - 健康检查失败自动回滚 16 | - 确保服务可用性 17 | 18 | 3. **简单易用** 19 | - 无需特殊配置,开箱即用 20 | - 支持任何带健康检查的服务 21 | - 详细的状态日志输出 22 | 23 | ## 使用方法 24 | 25 | ### 1. 前置条件 26 | 27 | - Docker 和 Docker Compose 环境 28 | - 目标服务已在 docker-compose.yml 中定义 29 | - 服务镜像已配置健康检查 (HEALTHCHECK) 30 | 31 | ### 2. 执行更新 32 | 33 | ```bash 34 | ./rolling-update.sh <服务名称> 35 | ``` 36 | 37 | 更新流程: 38 | 1. 获取服务当前运行的容器数量 39 | 2. 拉取服务的最新镜像 40 | 3. 逐个替换容器: 41 | - 记录当前所有容器 ID 42 | - 增加一个新容器并强制使用新镜像 43 | - 识别新创建的容器 44 | - 等待新容器健康检查通过 45 | - 移除最旧的容器 46 | - 确保服务实例数量正确 47 | 4. 如遇异常自动回滚: 48 | - 移除未通过健康检查的新容器 49 | - 保持原有容器继续运行 50 | - 恢复原始实例数量 51 | 52 | ## 项目结构 53 | 54 | ``` 55 | . 56 | ├── rolling-update.sh # 核心功能:滚动更新脚本 57 | └── 示例环境(用于测试和演示): 58 | ├── build-example.sh # 示例服务构建脚本 59 | ├── docker-compose.yml # 示例环境配置 60 | ├── caddy/ # 示例代理配置 61 | └── web-example/ # 示例Web服务 62 | ``` 63 | 64 | ## 示例环境 65 | 66 | 为了演示和测试滚动更新效果,项目包含了一个简单的测试环境: 67 | 68 | 1. **示例 Web 服务** 69 | - 返回版本号的 HTTP 服务 70 | - 内置健康检查接口 71 | - 支持优雅关闭 72 | 73 | 2. **示例环境使用方法** 74 | ```bash 75 | # 构建示例服务 76 | ./build-example.sh 77 | 78 | # 启动测试环境 79 | docker compose up -d 80 | 81 | # 执行滚动更新测试 82 | ./rolling-update.sh web-example 83 | ``` 84 | 85 | ## 最佳实践 86 | 87 | 1. **健康检查配置** 88 | - 设置合适的检查间隔和超时时间 89 | - 使用可靠的健康检查方式 90 | - 避免过重的健康检查逻辑 91 | 92 | 2. **更新策略** 93 | - 确保服务支持多实例并行 94 | - 正确配置服务依赖关系 95 | - 合理设置更新超时时间 96 | 97 | 1. **镜像管理** 98 | - 确保镜像仓库有最新版本 99 | - 正确设置镜像标签 100 | - 保持镜像构建的一致性 101 | 102 | ## 扩展建议 103 | 104 | 1. **功能增强** 105 | - 支持批量服务更新 106 | - 添加更新计划功能 107 | - 支持自定义更新策略 108 | 109 | 2. **监控优化** 110 | - 集成告警机制 111 | - 添加更新日志记录 112 | - 提供更新状态 API -------------------------------------------------------------------------------- /docker-compose-rollout/build-example.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 示例服务构建脚本 4 | # ============== 5 | # 6 | # 这是一个用于构建测试服务的示例脚本,用于演示滚动更新效果 7 | # 脚本会: 8 | # 1. 生成一个新的版本号 9 | # 2. 通过构建参数传递版本号 10 | # 3. 构建新的 Docker 镜像 11 | # 12 | # 注意:这个脚本仅用于演示目的,实际使用时应该替换为你自己的构建流程 13 | 14 | # 确保脚本在发生错误时退出 15 | set -e 16 | 17 | # 配置信息 18 | IMAGE_NAME="web-example" 19 | IMAGE_TAG=$(date +%Y%m%d-%H%M%S) # 使用时间戳作为tag 20 | VERSION="v1.0.${IMAGE_TAG}" # 使用时间戳作为版本号的一部分 21 | 22 | # 构建镜像 23 | echo "正在构建镜像 ${IMAGE_NAME}:${IMAGE_TAG}..." 24 | cd web-example 25 | docker build --build-arg VERSION=${VERSION} -t ${IMAGE_NAME}:${IMAGE_TAG} . 26 | docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:latest 27 | 28 | echo "镜像构建成功:${IMAGE_NAME}:${IMAGE_TAG} (latest)" 29 | echo "版本号:$VERSION" -------------------------------------------------------------------------------- /docker-compose-rollout/caddy/Caddyfile: -------------------------------------------------------------------------------- 1 | :80 { 2 | # Reverse proxy to web-example service 3 | handle /* { 4 | reverse_proxy web-example:8080 5 | } 6 | } -------------------------------------------------------------------------------- /docker-compose-rollout/compose.yaml: -------------------------------------------------------------------------------- 1 | # 这是一个用于测试滚动更新脚本的示例环境配置 2 | # 实际使用时,将你自己的服务配置替换到这里即可 3 | 4 | services: 5 | # Caddy 作为前端代理,用于验证服务可用性 6 | caddy: 7 | image: caddy:alpine 8 | ports: 9 | - "80:80" 10 | volumes: 11 | - ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro 12 | depends_on: 13 | - web-example 14 | networks: 15 | - app-network 16 | restart: unless-stopped 17 | 18 | # 示例Web服务,用于演示滚动更新效果 19 | # 你可以将此服务替换为你自己的应用 20 | web-example: 21 | image: web-example:latest 22 | expose: 23 | - "8080" 24 | networks: 25 | - app-network 26 | restart: unless-stopped 27 | 28 | networks: 29 | app-network: 30 | driver: bridge -------------------------------------------------------------------------------- /docker-compose-rollout/rolling-update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Docker Compose 服务滚动更新脚本 4 | # =============================== 5 | # 6 | # 这是一个通用的基于 Docker Compose 的服务滚动更新脚本,可用于任何配置了健康检查的服务。 7 | # 脚本会自动处理以下流程: 8 | # 1. 逐个替换现有容器 9 | # 2. 确保新容器健康后再移除旧容器 10 | # 3. 出现异常时自动回滚 11 | # 12 | # 使用要求: 13 | # - 目标服务必须在 compose.yaml 中定义 14 | # - 服务的镜像必须配置了 HEALTHCHECK 15 | # - Docker Compose 需要支持 scale 功能 16 | # 17 | # 使用方法: 18 | # ./rolling-update.sh <服务名称> 19 | # 20 | # 示例: 21 | # ./rolling-update.sh myapp 22 | 23 | # 确保脚本在发生错误时退出 24 | set -e 25 | 26 | # 检查必要的参数 27 | if [ -z "$1" ]; then 28 | echo "用法: $0 <服务名称>" 29 | echo "示例: $0 web-example" 30 | echo "注意: 目标服务必须在 compose.yaml 中定义,且配置了健康检查" 31 | exit 1 32 | fi 33 | 34 | SERVICE_NAME=$1 35 | 36 | # 健康检查函数 37 | check_container_health() { 38 | local container_id=$1 39 | local max_attempts=30 40 | local attempt=1 41 | local wait_time=2 42 | 43 | while [ $attempt -le $max_attempts ]; do 44 | # 检查容器是否存在 45 | if ! docker inspect $container_id >/dev/null 2>&1; then 46 | echo "错误:容器 $container_id 不存在" 47 | return 1 48 | fi 49 | 50 | # 检查容器健康状态 51 | local health_status=$(docker inspect --format='{{.State.Health.Status}}' $container_id) 52 | 53 | case "$health_status" in 54 | "healthy") 55 | echo "容器 $container_id 健康状态正常" 56 | return 0 57 | ;; 58 | "unhealthy") 59 | echo "容器 $container_id 健康检查失败" 60 | return 1 61 | ;; 62 | "starting") 63 | echo "等待容器健康状态... 第 $attempt 次检查" 64 | ;; 65 | *) 66 | echo "警告:容器 $container_id 状态未知 ($health_status)" 67 | ;; 68 | esac 69 | 70 | sleep $wait_time 71 | attempt=$((attempt + 1)) 72 | done 73 | 74 | echo "健康检查超时: 容器未能在规定时间内变为健康状态" 75 | return 1 76 | } 77 | 78 | # 获取服务的容器数量 79 | REPLICAS=$(docker compose ps -q $SERVICE_NAME | wc -l) 80 | 81 | if [ $REPLICAS -eq 0 ]; then 82 | echo "错误:服务 $SERVICE_NAME 未运行" 83 | exit 1 84 | fi 85 | 86 | echo "服务 $SERVICE_NAME 当前副本数量: $REPLICAS" 87 | 88 | # 逐个更新容器 89 | for (( i=1; i<=$REPLICAS; i++ )) 90 | do 91 | echo "正在更新第 $i 个容器,共 $REPLICAS 个" 92 | 93 | # 扩展服务,添加一个新容器(docker compose 会创建新容器) 94 | docker compose up -d --scale $SERVICE_NAME=$((REPLICAS + 1)) --no-recreate $SERVICE_NAME 95 | 96 | # 获取最新创建的容器ID(由于 docker compose ps 按创建时间排序,最后一个就是最新的) 97 | NEW_CONTAINER=$(docker compose ps -q $SERVICE_NAME | tail -n 1) 98 | 99 | # 等待新容器健康检查通过 100 | if ! check_container_health $NEW_CONTAINER; then 101 | echo "错误:新容器健康检查未通过,执行回滚" 102 | docker compose up -d --scale $SERVICE_NAME=$REPLICAS --no-recreate $SERVICE_NAME 103 | exit 1 104 | fi 105 | 106 | # 获取最旧的容器ID 107 | OLD_CONTAINER=$(docker compose ps -q $SERVICE_NAME | head -n 1) 108 | 109 | # 停止并移除最旧的容器 110 | echo "停止并移除最旧的容器: $OLD_CONTAINER" 111 | docker stop $OLD_CONTAINER 112 | docker rm -f $OLD_CONTAINER 113 | 114 | # 因为已经手动移除了一个容器,所以需要更新当前的副本数 115 | docker compose up -d --scale $SERVICE_NAME=$REPLICAS --no-recreate $SERVICE_NAME 116 | 117 | echo "第 $i 个容器更新成功" 118 | done 119 | 120 | echo "服务 $SERVICE_NAME 的滚动更新已完成" -------------------------------------------------------------------------------- /docker-compose-rollout/web-example/Dockerfile: -------------------------------------------------------------------------------- 1 | # 这是一个用于测试滚动更新脚本的示例服务 2 | # 实际使用时可以替换为你自己的应用镜像 3 | # 关键点是需要配置 HEALTHCHECK 指令 4 | 5 | FROM golang:1.23-alpine AS builder 6 | 7 | # 设置构建参数 8 | ARG VERSION=v1.0.0 9 | 10 | WORKDIR /app 11 | COPY src/ . 12 | # 使用 ldflags 在构建时注入版本号,禁用 CGO 以获得静态二进制文件 13 | ENV CGO_ENABLED=0 14 | RUN go build -ldflags "-X main.VERSION=${VERSION}" -o main main.go 15 | 16 | FROM alpine/curl:latest 17 | WORKDIR /app 18 | COPY --from=builder /app/main . 19 | 20 | EXPOSE 8080 21 | 22 | # 健康检查配置示例 23 | # 实际使用时根据你的应用调整检查命令和参数 24 | HEALTHCHECK --interval=5s --timeout=3s --retries=3 \ 25 | CMD curl -f http://localhost:8080/ || exit 1 26 | 27 | # 使用exec格式的CMD,确保信号能正确传递给应用 28 | CMD ["./main"] 29 | 30 | # 确保容器接收到SIGTERM信号时能优雅退出 31 | STOPSIGNAL SIGTERM -------------------------------------------------------------------------------- /docker-compose-rollout/web-example/src/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | "os/signal" 10 | "syscall" 11 | "time" 12 | ) 13 | 14 | // VERSION 可以在构建时通过 -ldflags 修改 15 | var VERSION = "v1.0.0" 16 | 17 | func main() { 18 | // 创建 HTTP server 19 | server := &http.Server{ 20 | Addr: ":8080", 21 | Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 | fmt.Fprintf(w, VERSION) 23 | }), 24 | } 25 | 26 | // 创建一个通道来接收操作系统的信号 27 | done := make(chan os.Signal, 1) 28 | signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) 29 | 30 | go func() { 31 | if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { 32 | log.Fatalf("监听失败: %v\n", err) 33 | } 34 | }() 35 | 36 | log.Printf("服务启动成功,监听端口 :8080") 37 | 38 | // 等待中断信号 39 | <-done 40 | log.Print("服务关闭中...") 41 | 42 | // 创建一个5秒超时的上下文 43 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 44 | defer cancel() 45 | 46 | // 优雅关闭服务 47 | if err := server.Shutdown(ctx); err != nil { 48 | log.Fatalf("服务关闭出错: %v\n", err) 49 | } 50 | 51 | log.Print("服务已关闭") 52 | } -------------------------------------------------------------------------------- /gitlab-ci-runner/README.md: -------------------------------------------------------------------------------- 1 | # Docker GitLab CI Runner 安装指南 2 | 3 | 这个项目提供了一个使用 Docker 快速部署 GitLab CI Runner 的解决方案。 4 | 5 | ## 前置要求 6 | 7 | - Docker 8 | - Docker Compose 9 | - 可以访问 GitLab 服务器 10 | - GitLab Runner 注册令牌 11 | 12 | ## 快速开始 13 | 14 | 1. 克隆此仓库: 15 | ```bash 16 | git clone 17 | cd gitlab-ci-runner 18 | ``` 19 | 20 | 2. 修改配置信息: 21 | 编辑 `init.sh` 文件,更新以下变量: 22 | - `GITLAB_URL`: 你的 GitLab 服务器地址 23 | - `REGISTRATION_TOKEN`: 你的 Runner 注册令牌 24 | - `RUNNER_DESCRIPTION`: Runner 描述 25 | - `RUNNER_TAGS`: Runner 标签 26 | 27 | 3. 运行安装脚本: 28 | ```bash 29 | chmod +x init.sh 30 | ./init.sh 31 | ``` 32 | 33 | ## 配置说明 34 | 35 | 本项目使用 Docker Compose 来管理容器,主要配置包括: 36 | 37 | - 使用最新版本的 gitlab/gitlab-runner 镜像 38 | - 自动重启功能 39 | - 挂载 Docker socket 以支持 Docker executor 40 | - 持久化 Runner 配置 41 | 42 | ## 常用命令 43 | 44 | ```bash 45 | # 启动服务 46 | docker compose up -d 47 | 48 | # 查看日志 49 | docker compose logs gitlab-runner 50 | 51 | # 停止服务 52 | docker compose down 53 | 54 | # 查看 Runner 状态 55 | docker compose exec gitlab-runner gitlab-runner list 56 | ``` 57 | 58 | ## 故障排查 59 | 60 | 1. 如果 Runner 无法注册,检查: 61 | - GitLab URL 是否正确 62 | - 注册令牌是否有效 63 | - 网络连接是否正常 64 | 65 | 2. 如果容器无法启动,检查: 66 | - Docker 服务是否运行 67 | - 端口是否被占用 68 | - 系统权限是否足够 69 | 70 | ## 参考资料 71 | 72 | - [GitLab Runner 官方文档](https://docs.gitlab.com/runner/) 73 | - [Docker Hub - gitlab/gitlab-runner](https://hub.docker.com/r/gitlab/gitlab-runner) 74 | -------------------------------------------------------------------------------- /gitlab-ci-runner/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 预设 GitLab Runner 配置信息 4 | GITLAB_URL="https://gitlab.com/" 5 | REGISTRATION_TOKEN="YOUR_REGISTRATION_TOKEN" 6 | RUNNER_DESCRIPTION="docker-runner" 7 | RUNNER_TAGS="docker" 8 | 9 | # 创建必要的目录 10 | mkdir -p ./gitlab-runner/config 11 | 12 | # 创建 docker compose 配置文件 13 | cat > ./compose.yaml << 'EOF' 14 | version: '3.7' 15 | services: 16 | gitlab-runner: 17 | image: gitlab/gitlab-runner:latest 18 | container_name: gitlab-runner 19 | restart: always 20 | volumes: 21 | - ./gitlab-runner/config:/etc/gitlab-runner 22 | - /var/run/docker.sock:/var/run/docker.sock 23 | EOF 24 | 25 | # 启动 GitLab Runner 26 | docker compose up -d 27 | 28 | # 检测容器是否就绪 29 | max_attempts=30 30 | attempt=1 31 | echo "等待 GitLab Runner 容器就绪..." 32 | 33 | while [ $attempt -le $max_attempts ]; do 34 | if docker compose ps gitlab-runner | grep -q "Up"; then 35 | echo "GitLab Runner 容器已就绪!" 36 | break 37 | fi 38 | echo "等待中... ($attempt/$max_attempts)" 39 | attempt=$((attempt + 1)) 40 | sleep 2 41 | done 42 | 43 | if [ $attempt -gt $max_attempts ]; then 44 | echo "错误: GitLab Runner 容器启动超时" 45 | exit 1 46 | fi 47 | 48 | # 注册 Runner 49 | docker compose exec gitlab-runner gitlab-runner register \ 50 | --non-interactive \ 51 | --url "$GITLAB_URL" \ 52 | --registration-token "$REGISTRATION_TOKEN" \ 53 | --description "$RUNNER_DESCRIPTION" \ 54 | --tag-list "$RUNNER_TAGS" \ 55 | --executor "docker" \ 56 | --docker-image alpine:latest \ 57 | --docker-volumes /var/run/docker.sock:/var/run/docker.sock 58 | 59 | echo "GitLab Runner 安装和注册完成!" 60 | -------------------------------------------------------------------------------- /kafka-replica/README.md: -------------------------------------------------------------------------------- 1 | # Kafka KRaft 集群 2 | 3 | ## 简介 4 | 该项目使用 Docker 和 Docker Compose 部署一个基于 KRaft 模式的 Kafka 集群(无需 Zookeeper)。支持自定义 broker 数量、端口和版本等配置,并默认启用 SASL/PLAIN 认证。 5 | 6 | ## 使用说明 7 | 8 | ### 1. 克隆项目 9 | ```bash 10 | git clone 11 | cd kafka 12 | ``` 13 | 14 | ### 2. 运行初始化脚本 15 | ```bash 16 | chmod +x init.sh 17 | ./init.sh [选项] 18 | ``` 19 | 20 | ### 可用的命令行选项 21 | ``` 22 | 选项: 23 | -h, --help 显示帮助信息 24 | -n, --name NAME 设置集群名称 (默认: kafka-cluster1) 25 | -p, --port PORT 设置基础端口号 (默认: 9092) 26 | 将会使用连续的端口: 27 | - PORT: 第一个 broker 的对外端口 28 | - PORT+1: 第二个 broker 的对外端口 29 | - PORT+2: 第三个 broker 的对外端口(如果配置了3个broker) 30 | - 以此类推... 31 | -v, --version VERSION 设置 Kafka 版本 (默认: 3.9.0) 32 | -b, --brokers BROKERS 设置 broker 数量 (默认: 3) 33 | --host-ip IP 手动指定主机IP (可选) 34 | -u, --username USERNAME 设置SASL用户名 (默认: root) 35 | -w, --password PASSWORD 设置SASL密码 (默认: 123456) 36 | --heap-memory SIZE 设置JVM堆内存大小 (默认: 512M) 37 | --container-memory SIZE 设置容器内存限制 (默认: 1G) 38 | --cpu-limit CORES 设置CPU核心数上限 (默认: 1.0) 39 | --cpu-request CORES 设置CPU核心数下限 (默认: 0.5) 40 | ``` 41 | 42 | ### 使用示例 43 | ```bash 44 | # 使用默认配置 45 | ./init.sh 46 | 47 | # 指定集群名称和端口 48 | ./init.sh -n my-kafka -p 9092 49 | 50 | # 指定 Kafka 版本和 broker 数量 51 | ./init.sh -v 3.9.0 -b 5 52 | 53 | # 手动指定IP地址 54 | ./init.sh --host-ip 192.168.1.100 55 | 56 | # 指定认证信息 57 | ./init.sh -u admin -w secret123 58 | 59 | # 指定资源限制 60 | ./init.sh --heap-memory 1G --container-memory 2G --cpu-limit 2.0 --cpu-request 1.0 61 | 62 | # 组合使用多个参数 63 | ./init.sh -n my-kafka -p 9092 -v 3.9.0 -b 5 --host-ip 192.168.1.100 -u admin -w secret123 64 | ``` 65 | 66 | ### 3. 目录结构 67 | 初始化完成后,会在指定的集群名称目录下创建以下文件和目录: 68 | ``` 69 | / 70 | ├── compose.yaml # Docker Compose 配置文件 71 | ├── README.md # 集群信息和使用说明 72 | ├── kafka_jaas.conf # SASL 认证配置文件 73 | ├── client.properties # 客户端配置文件 74 | ├── kafka-0/ # 第一个 broker 的数据和配置目录 75 | │ ├── data/ # 数据目录 76 | │ └── config/ # 配置目录 77 | ├── kafka-1/ # 第二个 broker 的数据和配置目录 78 | └── kafka-2/ # 第三个 broker 的数据和配置目录(如果配置了3个broker) 79 | ``` 80 | 81 | ### 4. 客户端配置 82 | 集群默认启用了 SASL/PLAIN 认证。客户端连接时需要使用以下配置: 83 | 84 | ```properties 85 | bootstrap.servers=:9092,:9093,:9094 86 | security.protocol=SASL_PLAINTEXT 87 | sasl.mechanism=PLAIN 88 | sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username="root" password="123456"; 89 | ``` 90 | 91 | 注意:请根据实际设置的用户名和密码修改上述配置。 92 | 93 | ### 5. 常用命令示例 94 | 95 | #### 创建主题 96 | ```bash 97 | docker exec _kafka_0 kafka-topics.sh \ 98 | --create \ 99 | --topic test \ 100 | --partitions 3 \ 101 | --replication-factor 2 \ 102 | --bootstrap-server localhost:9092 \ 103 | --command-config /bitnami/kafka/config/client.properties 104 | ``` 105 | 106 | #### 查看主题列表 107 | ```bash 108 | docker exec _kafka_0 kafka-topics.sh \ 109 | --list \ 110 | --bootstrap-server localhost:9092 \ 111 | --command-config /bitnami/kafka/config/client.properties 112 | ``` 113 | 114 | #### 查看主题详情 115 | ```bash 116 | docker exec _kafka_0 kafka-topics.sh \ 117 | --describe \ 118 | --topic test \ 119 | --bootstrap-server localhost:9092 \ 120 | --command-config /bitnami/kafka/config/client.properties 121 | ``` 122 | 123 | #### 生产消息 124 | ```bash 125 | docker exec -it _kafka_0 kafka-console-producer.sh \ 126 | --topic test \ 127 | --bootstrap-server localhost:9092 \ 128 | --producer.config /bitnami/kafka/config/client.properties 129 | ``` 130 | 131 | #### 消费消息 132 | ```bash 133 | docker exec -it _kafka_0 kafka-console-consumer.sh \ 134 | --topic test \ 135 | --from-beginning \ 136 | --bootstrap-server localhost:9092 \ 137 | --consumer.config /bitnami/kafka/config/client.properties 138 | ``` 139 | 140 | ### 6. 集群管理 141 | 142 | #### 启动集群 143 | ```bash 144 | cd 145 | docker compose up -d 146 | ``` 147 | 148 | #### 停止集群 149 | ```bash 150 | cd 151 | docker compose down 152 | ``` 153 | 154 | #### 查看日志 155 | ```bash 156 | cd 157 | docker compose logs -f 158 | ``` 159 | 160 | ### 注意事项 161 | 1. 如果从其他机器连接,请将 `` 替换为服务器的实际可访问IP地址 162 | 2. 确保防火墙已开放所需端口(默认为 9092 及后续端口) 163 | 3. 所有客户端连接都需要配置正确的 SASL 认证信息 164 | 4. 建议根据实际需求调整内存和CPU限制 165 | 166 | ### 常见问题 167 | 1. 端口被占用 168 | - 脚本会自动检查端口占用情况 169 | - 如果端口被占用,可以使用 `-p` 参数指定其他起始端口 170 | 171 | 2. 无法获取主机IP 172 | - 脚本会自动尝试多种方法获取主机IP 173 | - 如果自动获取失败,可以使用 `--host-ip` 参数手动指定IP地址 174 | 175 | 3. 认证失败 176 | - 检查客户端配置中的用户名和密码是否正确 177 | - 确保使用了正确的 SASL 配置 178 | 179 | 4. 集群启动失败 180 | - 检查 Docker 日志:`docker compose logs -f` 181 | - 确保分配了足够的系统资源(内存、CPU) 182 | - 检查端口是否被占用 183 | - 检查数据目录权限 184 | 185 | ### 集群管理命令 186 | 187 | #### 启动集群 188 | ```bash 189 | cd 190 | docker-compose up -d 191 | ``` 192 | 193 | #### 停止集群 194 | ```bash 195 | cd 196 | docker-compose down 197 | ``` 198 | 199 | #### 查看日志 200 | ```bash 201 | cd 202 | docker-compose logs -f 203 | ``` 204 | 205 | #### 查看集群状态 206 | ```bash 207 | cd 208 | docker-compose ps 209 | ``` 210 | 211 | ### 性能调优建议 212 | 1. 根据实际需求调整分区数和副本数 213 | 2. 合理设置消息大小和批处理参数 214 | 3. 监控并及时清理过期数据 215 | 4. 定期检查和维护磁盘空间 216 | 5. 根据业务需求调整 retention 策略 217 | -------------------------------------------------------------------------------- /kafka-replica/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 默认配置 4 | DEFAULT_CLUSTER_NAME="kafka-cluster1" 5 | DEFAULT_BASE_PORT=9092 6 | DEFAULT_KAFKA_VERSION="3.9.0" 7 | DEFAULT_BROKERS=3 8 | DEFAULT_CONTROLLER_PORT=9093 9 | DEFAULT_USERNAME="root" 10 | DEFAULT_PASSWORD="123456" 11 | DEFAULT_HEAP_MEMORY="512M" # 默认堆内存大小 12 | DEFAULT_CONTAINER_MEMORY="1G" # 默认容器内存限制 13 | DEFAULT_CPU_LIMIT="1.0" # 默认 CPU 限制(核心数) 14 | DEFAULT_CPU_REQUEST="0.5" # 默认 CPU 请求(核心数) 15 | 16 | # 帮助信息 17 | show_usage() { 18 | echo "用法: $0 [选项]" 19 | echo "选项:" 20 | echo " -h, --help 显示此帮助信息" 21 | echo " -n, --name NAME 设置集群名称 (默认: ${DEFAULT_CLUSTER_NAME})" 22 | echo " -p, --port PORT 设置基础端口号 (默认: ${DEFAULT_BASE_PORT})" 23 | echo " -v, --version VERSION 设置Kafka版本 (默认: ${DEFAULT_KAFKA_VERSION})" 24 | echo " -b, --brokers BROKERS 设置broker数量 (默认: ${DEFAULT_BROKERS})" 25 | echo " --host-ip IP 手动指定主机IP (可选)" 26 | echo " -u, --username USERNAME 设置SASL用户名 (默认: ${DEFAULT_USERNAME})" 27 | echo " -w, --password PASSWORD 设置SASL密码 (默认: ${DEFAULT_PASSWORD})" 28 | echo " --heap-memory SIZE 设置JVM堆内存大小 (默认: ${DEFAULT_HEAP_MEMORY})" 29 | echo " --container-memory SIZE 设置容器内存限制 (默认: ${DEFAULT_CONTAINER_MEMORY})" 30 | echo " --cpu-limit CORES 设置CPU核心数上限 (默认: ${DEFAULT_CPU_LIMIT})" 31 | echo " --cpu-request CORES 设置CPU核心数下限 (默认: ${DEFAULT_CPU_REQUEST})" 32 | exit 1 33 | } 34 | 35 | # 参数解析 36 | while [[ $# -gt 0 ]]; do 37 | case $1 in 38 | -h|--help) 39 | show_usage 40 | ;; 41 | -n|--name) 42 | CLUSTER_NAME="$2" 43 | shift 2 44 | ;; 45 | -p|--port) 46 | BASE_PORT="$2" 47 | shift 2 48 | ;; 49 | -v|--version) 50 | KAFKA_VERSION="$2" 51 | shift 2 52 | ;; 53 | -b|--brokers) 54 | BROKERS="$2" 55 | shift 2 56 | ;; 57 | --host-ip) 58 | MANUAL_HOST_IP="$2" 59 | shift 2 60 | ;; 61 | -u|--username) 62 | USERNAME="$2" 63 | shift 2 64 | ;; 65 | -w|--password) 66 | PASSWORD="$2" 67 | shift 2 68 | ;; 69 | --heap-memory) 70 | HEAP_MEMORY="$2" 71 | shift 2 72 | ;; 73 | --container-memory) 74 | CONTAINER_MEMORY="$2" 75 | shift 2 76 | ;; 77 | --cpu-limit) 78 | CPU_LIMIT="$2" 79 | shift 2 80 | ;; 81 | --cpu-request) 82 | CPU_REQUEST="$2" 83 | shift 2 84 | ;; 85 | *) 86 | echo "错误: 未知参数 $1" 87 | show_usage 88 | ;; 89 | esac 90 | done 91 | 92 | # 设置默认值 93 | CLUSTER_NAME=${CLUSTER_NAME:-$DEFAULT_CLUSTER_NAME} 94 | BASE_PORT=${BASE_PORT:-$DEFAULT_BASE_PORT} 95 | KAFKA_VERSION=${KAFKA_VERSION:-$DEFAULT_KAFKA_VERSION} 96 | BROKERS=${BROKERS:-$DEFAULT_BROKERS} 97 | USERNAME=${USERNAME:-$DEFAULT_USERNAME} 98 | PASSWORD=${PASSWORD:-$DEFAULT_PASSWORD} 99 | HEAP_MEMORY=${HEAP_MEMORY:-$DEFAULT_HEAP_MEMORY} 100 | CONTAINER_MEMORY=${CONTAINER_MEMORY:-$DEFAULT_CONTAINER_MEMORY} 101 | CPU_LIMIT=${CPU_LIMIT:-$DEFAULT_CPU_LIMIT} 102 | CPU_REQUEST=${CPU_REQUEST:-$DEFAULT_CPU_REQUEST} 103 | 104 | # 验证必要参数 105 | if ! [[ "$BASE_PORT" =~ ^[0-9]+$ ]] || [ "$BASE_PORT" -lt 1024 ] || [ "$BASE_PORT" -gt 65535 ]; then 106 | echo "错误: 端口号必须是1024-65535之间的数字" 107 | exit 1 108 | fi 109 | 110 | # 检查端口占用 111 | check_port() { 112 | local port=$1 113 | if command -v nc >/dev/null 2>&1; then 114 | if nc -z 0.0.0.0 $port >/dev/null 2>&1; then 115 | echo "错误: 端口 $port 已被占用" 116 | exit 1 117 | fi 118 | elif command -v lsof >/dev/null 2>&1; then 119 | if lsof -i :$port >/dev/null 2>&1; then 120 | echo "错误: 端口 $port 已被占用" 121 | exit 1 122 | fi 123 | fi 124 | } 125 | 126 | # 检查所需端口 127 | echo "检查端口占用情况..." 128 | for ((i=0; i&2 141 | exit 1 142 | fi 143 | fi 144 | 145 | case "$(uname -s)" in 146 | Darwin) 147 | for interface in en0 en1 en2 en3; do 148 | host_ip=$(ipconfig getifaddr $interface 2>/dev/null) 149 | if [ ! -z "$host_ip" ]; then 150 | echo $host_ip 151 | return 0 152 | fi 153 | done 154 | host_ip=$(ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -n 1) 155 | ;; 156 | Linux) 157 | if command -v hostname >/dev/null 2>&1; then 158 | host_ip=$(hostname -I | awk '{print $1}') 159 | fi 160 | 161 | if [ -z "$host_ip" ]; then 162 | host_ip=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v 127.0.0.1 | head -n 1) 163 | fi 164 | 165 | if [ -z "$host_ip" ]; then 166 | host_ip=$(ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -n 1) 167 | fi 168 | ;; 169 | *) 170 | echo "不支持的操作系统" >&2 171 | exit 1 172 | ;; 173 | esac 174 | 175 | if [ -z "$host_ip" ]; then 176 | echo "错误: 无法获取主机IP地址。请使用 --host-ip 参数手动指定IP地址。" >&2 177 | exit 1 178 | fi 179 | 180 | echo $host_ip 181 | } 182 | 183 | # 获取主机IP 184 | HOST_IP=$(get_host_ip) 185 | 186 | # 创建集群目录 187 | mkdir -p ./${CLUSTER_NAME} 188 | 189 | # 生成集群ID 190 | CLUSTER_ID=$(od -x -N 4 /dev/urandom | head -1 | awk '{print $2}') 191 | 192 | # 生成JAAS配置文件 193 | cat > ./${CLUSTER_NAME}/kafka_jaas.conf < ./${CLUSTER_NAME}/client.properties < ./${CLUSTER_NAME}/compose.yaml <> ./${CLUSTER_NAME}/compose.yaml </dev/null) 298 | 299 | if [ "$container_status" != "running" ]; then 300 | echo -n "." 301 | sleep 2 302 | attempt=$((attempt + 1)) 303 | if [ $attempt -gt $max_attempts ]; then 304 | echo "\nKafka broker ${i} 启动超时!" 305 | docker logs $container 306 | exit 1 307 | fi 308 | continue 309 | fi 310 | 311 | # 检查是否包含最终的启动成功标志 312 | if docker logs $container 2>&1 | grep -q "Kafka Server started"; then 313 | echo " 就绪!" 314 | READY_COUNT=$((READY_COUNT + 1)) 315 | break 316 | fi 317 | 318 | echo -n "." 319 | sleep 2 320 | attempt=$((attempt + 1)) 321 | if [ $attempt -gt $max_attempts ]; then 322 | echo "\nKafka broker ${i} 启动超时!" 323 | docker logs $container 324 | exit 1 325 | fi 326 | done 327 | 328 | attempt=1 329 | done 330 | 331 | # 最后验证集群整体状态 332 | if [ $READY_COUNT -eq $BROKERS ]; then 333 | echo "验证集群整体状态..." 334 | sleep 5 335 | 336 | # 生成使用说明文件 337 | cat > README.md <&2 106 | exit 1 107 | fi 108 | fi 109 | 110 | case "$(uname -s)" in 111 | Darwin) 112 | # MacOS - 尝试多个常见网络接口 113 | for interface in en0 en1 en2 en3; do 114 | host_ip=$(ipconfig getifaddr $interface 2>/dev/null) 115 | if [ ! -z "$host_ip" ]; then 116 | echo $host_ip 117 | return 0 118 | fi 119 | done 120 | # 如果上面都失败了,尝试使用 ifconfig 121 | host_ip=$(ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -n 1) 122 | ;; 123 | Linux) 124 | # Linux - 尝试多种方法 125 | if command -v hostname >/dev/null 2>&1; then 126 | host_ip=$(hostname -I | awk '{print $1}') 127 | fi 128 | 129 | if [ -z "$host_ip" ]; then 130 | host_ip=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v 127.0.0.1 | head -n 1) 131 | fi 132 | 133 | if [ -z "$host_ip" ]; then 134 | host_ip=$(ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -n 1) 135 | fi 136 | ;; 137 | *) 138 | echo "不支持的操作系统" >&2 139 | exit 1 140 | ;; 141 | esac 142 | 143 | if [ -z "$host_ip" ]; then 144 | echo "错误: 无法获取主机IP地址。请使用 --host-ip 参数手动指定IP地址。" >&2 145 | exit 1 146 | fi 147 | 148 | echo $host_ip 149 | } 150 | 151 | # 获取主机IP 152 | HOST_IP=$(get_host_ip) 153 | 154 | echo "当前主机IP: $HOST_IP" 155 | echo "Kafka端口: $KAFKA_PORT" 156 | echo "Controller端口: $CONTROLLER_PORT" 157 | echo "服务名称: $KAFKA_NAME" 158 | 159 | # 创建必要的目录 160 | mkdir -p ./${KAFKA_NAME}/{data,config} 161 | 162 | # 设置目录权限 163 | chmod 777 ./${KAFKA_NAME}/data 164 | chmod 777 ./${KAFKA_NAME}/config 165 | 166 | # 生成集群ID 167 | CLUSTER_ID=$(od -x -N 4 /dev/urandom | head -1 | awk '{print $2}') 168 | 169 | # 创建 JAAS 配置文件 170 | cat > ./${KAFKA_NAME}/config/kafka_jaas.conf << EOL 171 | KafkaServer { 172 | org.apache.kafka.common.security.plain.PlainLoginModule required 173 | username="${KAFKA_USERNAME}" 174 | password="${KAFKA_PASSWORD}" 175 | user_${KAFKA_USERNAME}="${KAFKA_PASSWORD}"; 176 | }; 177 | 178 | Client { 179 | org.apache.kafka.common.security.plain.PlainLoginModule required 180 | username="${KAFKA_USERNAME}" 181 | password="${KAFKA_PASSWORD}"; 182 | }; 183 | EOL 184 | 185 | # 创建docker-compose.yml文件 186 | cat > ./${KAFKA_NAME}/docker-compose.yml << EOL 187 | services: 188 | kafka: 189 | image: 'bitnami/kafka:3.9.0' 190 | container_name: ${KAFKA_NAME}-kafka 191 | ports: 192 | - '${KAFKA_PORT}:9092' 193 | - '${CONTROLLER_PORT}:9093' 194 | environment: 195 | - KAFKA_CFG_NODE_ID=1 196 | - KAFKA_CFG_PROCESS_ROLES=controller,broker 197 | - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=1@kafka:9093 198 | - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER 199 | - KAFKA_CFG_LISTENERS=SASL_PLAINTEXT://:9092,CONTROLLER://:9093 200 | - KAFKA_CFG_ADVERTISED_LISTENERS=SASL_PLAINTEXT://${HOST_IP}:${KAFKA_PORT} 201 | - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,SASL_PLAINTEXT:SASL_PLAINTEXT 202 | - KAFKA_CFG_INTER_BROKER_LISTENER_NAME=SASL_PLAINTEXT 203 | - KAFKA_KRAFT_CLUSTER_ID=${CLUSTER_ID} 204 | - KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true 205 | - KAFKA_ENABLE_KRAFT=yes 206 | - KAFKA_CLIENT_USERS=${KAFKA_USERNAME} 207 | - KAFKA_CLIENT_PASSWORDS=${KAFKA_PASSWORD} 208 | - KAFKA_CFG_SASL_ENABLED_MECHANISMS=PLAIN 209 | - KAFKA_CFG_SASL_MECHANISM_INTER_BROKER_PROTOCOL=PLAIN 210 | - ALLOW_PLAINTEXT_LISTENER=yes 211 | - KAFKA_HEAP_OPTS=-Xmx${HEAP_MEMORY} -Xms${HEAP_MEMORY} 212 | - KAFKA_OPTS=-Djava.security.auth.login.config=/bitnami/kafka/config/kafka_jaas.conf 213 | volumes: 214 | - ./data:/bitnami/kafka/data 215 | - ./config:/bitnami/kafka/config 216 | deploy: 217 | resources: 218 | limits: 219 | memory: ${CONTAINER_MEMORY} 220 | cpus: ${CPU_LIMIT} 221 | reservations: 222 | memory: ${HEAP_MEMORY} 223 | cpus: ${CPU_REQUEST} 224 | EOL 225 | 226 | # 启动docker compose 227 | echo "启动docker compose..." 228 | cd ./${KAFKA_NAME} 229 | docker compose up -d 230 | 231 | # 等待Kafka启动 232 | echo "等待Kafka启动..." 233 | max_attempts=30 234 | attempt=1 235 | 236 | while [ $attempt -le $max_attempts ]; do 237 | # 检查容器状态 238 | container_status=$(docker inspect -f '{{.State.Status}}' ${KAFKA_NAME}-kafka 2>/dev/null) 239 | 240 | if [ "$container_status" != "running" ]; then 241 | echo "等待容器启动..." 242 | sleep 2 243 | attempt=$((attempt + 1)) 244 | if [ $attempt -gt $max_attempts ]; then 245 | echo "Kafka启动超时!" 246 | exit 1 247 | fi 248 | continue 249 | fi 250 | 251 | # 检查日志中是否包含启动成功的标志 252 | if docker logs ${KAFKA_NAME}-kafka 2>&1 | grep -q "Kafka Server started"; then 253 | echo "Kafka已就绪!" 254 | break 255 | fi 256 | 257 | echo "尝试 $attempt/$max_attempts ..." 258 | attempt=$((attempt + 1)) 259 | if [ $attempt -gt $max_attempts ]; then 260 | echo "Kafka启动超时!" 261 | exit 1 262 | fi 263 | sleep 2 264 | done 265 | 266 | echo "Kafka集群已成功启动!" 267 | echo "Kafka地址: ${HOST_IP}:${KAFKA_PORT}" 268 | 269 | # 生成说明文档 270 | generate_readme() { 271 | cat << EOT 272 | # Kafka 单节点部署说明 273 | 274 | ## 部署信息 275 | - 部署模式: KRaft (无需 ZooKeeper) 276 | - Kafka 版本: 3.9.0 277 | - 部署时间: $(date '+%Y-%m-%d %H:%M:%S') 278 | 279 | ## 连接信息 280 | - Kafka 地址: \`${HOST_IP}:${KAFKA_PORT}\` 281 | - Controller 地址: \`${HOST_IP}:${CONTROLLER_PORT}\` 282 | - 认证信息: 283 | - 用户名: \`${KAFKA_USERNAME}\` 284 | - 密码: \`${KAFKA_PASSWORD}\` 285 | - 认证机制: SASL/PLAIN 286 | 287 | ## 客户端连接示例 288 | ### Java 289 | \`\`\`java 290 | Properties props = new Properties(); 291 | props.put("bootstrap.servers", "${HOST_IP}:${KAFKA_PORT}"); 292 | props.put("security.protocol", "SASL_PLAINTEXT"); 293 | props.put("sasl.mechanism", "PLAIN"); 294 | props.put("sasl.jaas.config", "org.apache.kafka.common.security.plain.PlainLoginModule required username=\\"${KAFKA_USERNAME}\\" password=\\"${KAFKA_PASSWORD}\\";"); 295 | \`\`\` 296 | 297 | ### Python (kafka-python) 298 | \`\`\`python 299 | from kafka import KafkaConsumer, KafkaProducer 300 | 301 | conf = { 302 | 'bootstrap_servers': ['${HOST_IP}:${KAFKA_PORT}'], 303 | 'security_protocol': 'SASL_PLAINTEXT', 304 | 'sasl_mechanism': 'PLAIN', 305 | 'sasl_plain_username': '${KAFKA_USERNAME}', 306 | 'sasl_plain_password': '${KAFKA_PASSWORD}' 307 | } 308 | 309 | producer = KafkaProducer(**conf) 310 | consumer = KafkaConsumer('my-topic', **conf) 311 | \`\`\` 312 | 313 | ### Spring Boot 314 | \`\`\`yaml 315 | spring: 316 | kafka: 317 | bootstrap-servers: ${HOST_IP}:${KAFKA_PORT} 318 | properties: 319 | security.protocol: SASL_PLAINTEXT 320 | sasl.mechanism: PLAIN 321 | sasl.jaas.config: org.apache.kafka.common.security.plain.PlainLoginModule required username="${KAFKA_USERNAME}" password="${KAFKA_PASSWORD}"; 322 | \`\`\` 323 | 324 | ### Go (Sarama) 325 | \`\`\`go 326 | package main 327 | 328 | import ( 329 | "github.com/Shopify/sarama" 330 | "log" 331 | ) 332 | 333 | func main() { 334 | config := sarama.NewConfig() 335 | 336 | // 配置 SASL/PLAIN 认证 337 | config.Net.SASL.Enable = true 338 | config.Net.SASL.Mechanism = sarama.SASLTypePlaintext 339 | config.Net.SASL.User = "${KAFKA_USERNAME}" 340 | config.Net.SASL.Password = "${KAFKA_PASSWORD}" 341 | config.Net.SASL.Handshake = true 342 | 343 | // 设置安全协议 344 | config.Net.TLS.Enable = false 345 | config.Version = sarama.V3_9_0_0 // 使用与服务器相同的版本 346 | 347 | // 创建生产者 348 | producer, err := sarama.NewSyncProducer([]string{"${HOST_IP}:${KAFKA_PORT}"}, config) 349 | if err != nil { 350 | log.Fatalf("Failed to create producer: %s", err) 351 | } 352 | defer producer.Close() 353 | 354 | // 创建消费者 355 | consumer, err := sarama.NewConsumer([]string{"${HOST_IP}:${KAFKA_PORT}"}, config) 356 | if err != nil { 357 | log.Fatalf("Failed to create consumer: %s", err) 358 | } 359 | defer consumer.Close() 360 | } 361 | 362 | // 生产者示例 363 | func produceMessage(producer sarama.SyncProducer) { 364 | msg := &sarama.ProducerMessage{ 365 | Topic: "my-topic", 366 | Value: sarama.StringEncoder("test message"), 367 | } 368 | 369 | partition, offset, err := producer.SendMessage(msg) 370 | if err != nil { 371 | log.Printf("Failed to send message: %s", err) 372 | return 373 | } 374 | log.Printf("Message sent to partition %d at offset %d", partition, offset) 375 | } 376 | 377 | // 消费者示例 378 | func consumeMessages(consumer sarama.Consumer) { 379 | // 获取指定主题的分区列表 380 | partitions, err := consumer.Partitions("my-topic") 381 | if err != nil { 382 | log.Printf("Failed to get partitions: %s", err) 383 | return 384 | } 385 | 386 | // 为每个分区创建消费者 387 | for _, partition := range partitions { 388 | pc, err := consumer.ConsumePartition("my-topic", partition, sarama.OffsetNewest) 389 | if err != nil { 390 | log.Printf("Failed to create partition consumer: %s", err) 391 | continue 392 | } 393 | defer pc.Close() 394 | 395 | // 在 goroutine 中处理消息 396 | go func(pc sarama.PartitionConsumer) { 397 | for msg := range pc.Messages() { 398 | log.Printf("Message received: %s", string(msg.Value)) 399 | } 400 | }(pc) 401 | } 402 | } 403 | \`\`\` 404 | 405 | ## 目录结构 406 | \`\`\` 407 | ./${KAFKA_NAME}/ 408 | ├── docker-compose.yml # Docker Compose 配置文件 409 | ├── data/ # Kafka 数据目录 410 | └── config/ # Kafka 配置目录 411 | \`\`\` 412 | 413 | ## 常用命令示例 414 | 415 | ### 主题管理 416 | 1. 创建主题 417 | \`\`\`bash 418 | docker exec ${KAFKA_NAME}-kafka kafka-topics.sh \\ 419 | --create \\ 420 | --topic my-topic \\ 421 | --bootstrap-server localhost:9092 \\ 422 | --partitions 3 \\ 423 | --replication-factor 1 \\ 424 | --command-config /bitnami/kafka/config/producer.properties 425 | \`\`\` 426 | 427 | 2. 查看主题列表 428 | \`\`\`bash 429 | docker exec ${KAFKA_NAME}-kafka kafka-topics.sh \\ 430 | --list \\ 431 | --bootstrap-server localhost:9092 \\ 432 | --command-config /bitnami/kafka/config/producer.properties 433 | \`\`\` 434 | 435 | 3. 查看主题详情 436 | \`\`\`bash 437 | docker exec ${KAFKA_NAME}-kafka kafka-topics.sh \\ 438 | --describe \\ 439 | --topic my-topic \\ 440 | --bootstrap-server localhost:9092 \\ 441 | --command-config /bitnami/kafka/config/producer.properties 442 | \`\`\` 443 | 444 | ### 消息操作 445 | 1. 生产消息 446 | \`\`\`bash 447 | docker exec -it ${KAFKA_NAME}-kafka kafka-console-producer.sh \\ 448 | --topic my-topic \\ 449 | --bootstrap-server localhost:9092 \\ 450 | --producer.config /bitnami/kafka/config/producer.properties 451 | \`\`\` 452 | 453 | 2. 消费消息 454 | \`\`\`bash 455 | docker exec -it ${KAFKA_NAME}-kafka kafka-console-consumer.sh \\ 456 | --topic my-topic \\ 457 | --from-beginning \\ 458 | --bootstrap-server localhost:9092 \\ 459 | --consumer.config /bitnami/kafka/config/consumer.properties 460 | \`\`\` 461 | 462 | ### 服务管理 463 | 1. 停止服务 464 | \`\`\`bash 465 | cd ./${KAFKA_NAME} && docker compose down 466 | \`\`\` 467 | 468 | 2. 重启服务 469 | \`\`\`bash 470 | cd ./${KAFKA_NAME} && docker compose restart 471 | \`\`\` 472 | 473 | 3. 查看日志 474 | \`\`\`bash 475 | cd ./${KAFKA_NAME} && docker compose logs -f 476 | \`\`\` 477 | 478 | ## 注意事项 479 | 1. 本部署使用 KRaft 模式,不需要 ZooKeeper 480 | 2. 外部客户端连接请使用 \`${HOST_IP}:${KAFKA_PORT}\` 481 | 3. 容器内部连接请使用 \`localhost:9092\` 482 | 4. 数据持久化在 \`./${KAFKA_NAME}/data\` 目录 483 | 484 | ## 常见问题 485 | 1. 如果遇到连接问题,请检查: 486 | - 防火墙是否开放了 ${KAFKA_PORT} 端口 487 | - 是否使用了正确的连接地址 488 | 2. 如果需要重置数据: 489 | - 停止服务:\`docker compose down\` 490 | - 删除数据目录:\`rm -rf data\` 491 | - 重新启动:\`docker compose up -d\` 492 | ## 参考资料 493 | - [Kafka 官方文档](https://kafka.apache.org/documentation/) 494 | - [Bitnami Kafka Docker 镜像文档](https://hub.docker.com/r/bitnami/kafka) 495 | 496 | ## 资源限制配置 497 | ### 内存配置 498 | - JVM 堆内存: ${HEAP_MEMORY} 499 | - 容器内存限制: ${CONTAINER_MEMORY} 500 | 501 | ### CPU 配置 502 | - CPU 核心数上限: ${CPU_LIMIT} 503 | - CPU 核心数下限: ${CPU_REQUEST} 504 | 505 | 要调整资源限制,可以使用以下参数: 506 | \`\`\`bash 507 | # 设置内存 508 | ./init.sh --heap-memory 2G --container-memory 4G 509 | 510 | # 设置 CPU(支持小数点,如 0.5 表示半个核心) 511 | ./init.sh --cpu-limit 2 --cpu-request 1 512 | 513 | # 同时设置内存和 CPU 514 | ./init.sh --heap-memory 2G --container-memory 4G --cpu-limit 2 --cpu-request 1 515 | \`\`\` 516 | 517 | 注意事项: 518 | 1. 内存配置: 519 | - 容器内存限制(--container-memory)应该总是大于 JVM 堆内存(--heap-memory) 520 | - 建议容器内存限制至少比堆内存大 1GB,以给系统和其他开销留出空间 521 | 522 | 2. CPU 配置: 523 | - CPU 限制(--cpu-limit)定义了容器可以使用的最大 CPU 核心数 524 | - CPU 请求(--cpu-request)定义了容器需要的最小 CPU 核心数 525 | - CPU 值可以使用小数,如 0.5 表示半个核心 526 | - 建议 CPU 限制值至少是请求值的 1.5 倍,以处理流量突发 527 | EOT 528 | } 529 | 530 | # 生成说明文档并同时输出到终端和文件 531 | README_CONTENT=$(generate_readme) 532 | echo "$README_CONTENT" | tee README.md 533 | 534 | echo "==========================================================" 535 | echo "说明文档已生成到 README.md" 536 | echo "==========================================================" 537 | 538 | -------------------------------------------------------------------------------- /mongo-shard-replica/README.md: -------------------------------------------------------------------------------- 1 | # MongoDB分片副本集集群 2 | 3 | ## 架构说明 4 | 该项目使用 Docker 和 Docker Compose 部署一个完整的 MongoDB 分片副本集集群,包括: 5 | 6 | 1. **Mongos 路由服务** 7 | - 负责路由客户端请求到适当的分片 8 | - 提供统一的访问入口 9 | - 实现透明的分片访问 10 | 11 | 2. **Config Server** 12 | - 存储集群的元数据和配置信息 13 | - 以副本集模式运行 14 | - 管理分片的数据分布 15 | 16 | 3. **Shard 服务器** 17 | - Shard1 和 Shard2 两个分片服务器 18 | - 每个分片以副本集模式运行 19 | - 存储实际的数据 20 | 21 | 4. **安全特性** 22 | - 启用认证机制(SCRAM-SHA-1) 23 | - 使用 keyFile 进行内部认证 24 | - 支持用户名密码访问控制 25 | 26 | ## 系统要求 27 | - Docker Engine 20.10.0+ 28 | - Docker Compose v2.0.0+ 29 | - 至少 4GB 可用内存 30 | - 至少 10GB 可用磁盘空间 31 | - 支持的操作系统: 32 | - Linux(推荐) 33 | - macOS 34 | - Windows(需要 WSL2) 35 | 36 | ## 使用说明 37 | 38 | ### 1. 克隆项目 39 | ```bash 40 | git clone 41 | cd mongo-shard-replica 42 | ``` 43 | 44 | ### 2. 运行初始化脚本 45 | 46 | #### 准备工作 47 | 1. 确保 Docker 和 Docker Compose 已正确安装: 48 | ```bash 49 | docker --version 50 | docker compose version 51 | ``` 52 | 53 | 2. 确保当前用户有足够权限: 54 | ```bash 55 | # 如果不在 docker 组,添加当前用户到 docker 组 56 | sudo usermod -aG docker $USER 57 | # 重新登录以使权限生效 58 | ``` 59 | 60 | 3. 确保目标端口未被占用: 61 | ```bash 62 | # 检查默认端口 63 | nc -zv localhost 27017-27020 2>&1 64 | ``` 65 | 66 | #### 运行脚本 67 | ```bash 68 | chmod +x init.sh 69 | ./init.sh [选项] 70 | ``` 71 | 72 | ### 可用的命令行选项 73 | ``` 74 | 选项: 75 | -h, --help 显示帮助信息 76 | -n, --name NAME 设置集群名称 (默认: mongo-cluster1) 77 | -p, --port PORT 设置基础端口号 (默认: 27017) 78 | 将会使用连续的4个端口: 79 | - PORT: mongos路由服务 80 | - PORT+1: config配置服务 81 | - PORT+2: shard1分片服务 82 | - PORT+3: shard2分片服务 83 | -v, --version VERSION 设置MongoDB版本 (默认: 5.0) 84 | -u, --username USERNAME 设置MongoDB用户名 (默认: root) 85 | -w, --password PASSWORD 设置MongoDB密码 (默认: 123456) 86 | --host-ip IP 手动指定主机IP (可选) 87 | ``` 88 | 89 | ### 使用示例 90 | ```bash 91 | # 使用默认配置 92 | sudo ./init.sh 93 | 94 | # 指定集群名称和端口 95 | sudo ./init.sh -n my-cluster -p 27020 96 | 97 | # 指定MongoDB版本和认证信息 98 | sudo ./init.sh -v 6.0 -u admin -w mypassword 99 | 100 | # 手动指定IP地址 101 | sudo ./init.sh --host-ip 192.168.1.100 102 | 103 | # 组合使用多个参数 104 | sudo ./init.sh -n my-cluster -p 27020 -v 6.0 -u admin -w mypassword --host-ip 192.168.1.100 105 | ``` 106 | 107 | ### 3. 查看集群连接信息 108 | 109 | #### 自动生成的信息 110 | 脚本执行完成后会自动生成以下内容: 111 | 112 | 1. **终端输出** 113 | - 集群初始化过程的详细日志 114 | - 连接信息和示例命令 115 | - 可能的错误和解决方案 116 | 117 | 2. **README.md 文件** 118 | - 完整的集群配置信息 119 | - 多语言连接示例 120 | - 管理指南和最佳实践 121 | 122 | 3. **配置文件** 123 | - Docker Compose 配置 124 | - MongoDB 密钥文件 125 | - 各服务的数据目录 126 | 127 | 生成的连接信息包括: 128 | 129 | 1. mongosh命令行连接串(带认证机制): 130 | ```bash 131 | mongodb://:@:/admin?authSource=admin&authMechanism=SCRAM-SHA-1 132 | ``` 133 | 134 | 2. MongoDB Compass连接串(带高可用配置): 135 | ```bash 136 | mongodb://:@:/admin?authSource=admin&authMechanism=SCRAM-SHA-1&readPreference=primary&retryWrites=true&w=majority 137 | ``` 138 | 139 | 3. 应用程序连接串(带完整参数): 140 | ```bash 141 | mongodb://:@:/admin?authSource=admin&authMechanism=SCRAM-SHA-1&readPreference=primary&retryWrites=true&w=majority&maxPoolSize=50&minPoolSize=10&maxIdleTimeMS=30000&connectTimeoutMS=10000&serverSelectionTimeoutMS=5000&socketTimeoutMS=45000&waitQueueTimeoutMS=5000&heartbeatFrequencyMS=10000 142 | ``` 143 | 144 | ### 4. 目录结构 145 | 初始化完成后的完整目录结构: 146 | ``` 147 | / 148 | ├── compose.yaml # Docker Compose 配置文件 149 | ├── mongodb.key # MongoDB 认证密钥文件(权限:400) 150 | ├── README.md # 集群信息和使用说明 151 | ├── configsvr/ # Config Server 数据目录 152 | │ └── ... # Config Server 数据文件 153 | ├── shard1/ # Shard1 数据目录 154 | │ └── ... # Shard1 数据文件 155 | └── shard2/ # Shard2 数据目录 156 | └── ... # Shard2 数据文件 157 | ``` 158 | 159 | ### 5. 资源配置说明 160 | 脚本为各服务配置了以下资源限制: 161 | 162 | 1. **Mongos 路由器** 163 | - CPU: 0.5 核心(最小:0.2) 164 | - 内存: 1GB(最小:512MB) 165 | 166 | 2. **Config Server** 167 | - CPU: 0.5 核心(最小:0.2) 168 | - 内存: 1GB(最小:512MB) 169 | 170 | 3. **分片服务器** 171 | - CPU: 1 核心(最小:0.5) 172 | - 内存: 2GB(最小:1GB) 173 | 174 | ### 6. 安全配置 175 | 1. **认证机制** 176 | - 使用 SCRAM-SHA-1 认证 177 | - keyFile 用于内部认证 178 | - 强制启用授权验证 179 | 180 | 2. **网络安全** 181 | - 使用 Docker 网络隔离 182 | - 仅必要端口对外暴露 183 | - 支持 TLS/SSL 配置(可选) 184 | 185 | 3. **访问控制** 186 | - 创建管理员用户 187 | - 角色基础访问控制 188 | - 支持细粒度权限配置 189 | 190 | ### 常见问题 191 | 192 | #### 1. 端口被占用 193 | - **症状**:启动失败,提示端口已被使用 194 | - **解决方案**: 195 | ```bash 196 | # 查看端口占用 197 | sudo lsof -i : 198 | # 使用其他端口启动 199 | ./init.sh -p 200 | ``` 201 | 202 | #### 2. 无法获取主机IP 203 | - **症状**:自动IP检测失败 204 | - **解决方案**: 205 | ```bash 206 | # 手动查看IP 207 | ifconfig | grep "inet " 208 | # 指定IP启动 209 | ./init.sh --host-ip 210 | ``` 211 | 212 | #### 3. 权限问题 213 | - **症状**:mongodb.key 权限设置失败 214 | - **解决方案**: 215 | ```bash 216 | # 检查文件权限 217 | ls -l mongodb.key 218 | # 正确设置权限 219 | sudo chmod 400 mongodb.key 220 | sudo chown 999:999 mongodb.key 221 | ``` 222 | 223 | #### 4. 内存不足 224 | - **症状**:容器启动失败或不稳定 225 | - **解决方案**: 226 | - 增加系统可用内存 227 | - 调整容器内存限制: 228 | ```bash 229 | # 编辑 compose.yaml 230 | # 修改 deploy.resources.limits.memory 值 231 | ``` 232 | 233 | #### 5. 数据持久化 234 | - **症状**:重启后数据丢失 235 | - **解决方案**: 236 | - 确保数据目录正确挂载 237 | - 检查目录权限: 238 | ```bash 239 | sudo chown -R 999:999 configsvr/ shard1/ shard2/ 240 | ``` 241 | 242 | ### 性能优化建议 243 | 244 | 1. **系统层面** 245 | - 禁用透明大页面 246 | - 调整系统限制(ulimit) 247 | - 使用 XFS 文件系统 248 | 249 | 2. **MongoDB 配置** 250 | - 优化 WiredTiger 缓存大小 251 | - 调整连接池参数 252 | - 配置适当的写关注 253 | 254 | 3. **容器配置** 255 | - 使用主机网络模式 256 | - 调整 CPU 份额 257 | - 配置内存限制 258 | 259 | ### 监控指标 260 | 261 | 1. **基础指标** 262 | - CPU 使用率 263 | - 内存使用 264 | - 磁盘 IOPS 265 | - 网络流量 266 | 267 | 2. **MongoDB 指标** 268 | - 操作延迟 269 | - 连接数 270 | - 队列长度 271 | - 复制延迟 272 | 273 | 3. **分片特定指标** 274 | - 块分布 275 | - 平衡器状态 276 | - 分片大小 277 | - 跨分片查询 278 | 279 | -------------------------------------------------------------------------------- /mongo-shard-replica/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 默认配置 4 | DEFAULT_CLUSTER_NAME="mongo-cluster1" 5 | DEFAULT_BASE_PORT=27017 6 | DEFAULT_MONGO_VERSION="5.0" 7 | DEFAULT_MONGO_USERNAME="root" 8 | DEFAULT_MONGO_PASSWORD="123456" 9 | 10 | # 帮助信息 11 | show_usage() { 12 | echo "用法: $0 [选项]" 13 | echo "选项:" 14 | echo " -h, --help 显示此帮助信息" 15 | echo " -n, --name NAME 设置集群名称 (默认: ${DEFAULT_CLUSTER_NAME})" 16 | echo " -p, --port PORT 设置基础端口号 (默认: ${DEFAULT_BASE_PORT})" 17 | echo " 将会使用连续的4个端口:" 18 | echo " - PORT: mongos路由服务" 19 | echo " - PORT+1: config配置服务" 20 | echo " - PORT+2: shard1分片服务" 21 | echo " - PORT+3: shard2分片服务" 22 | echo " -v, --version VERSION 设置MongoDB版本 (默认: ${DEFAULT_MONGO_VERSION})" 23 | echo " -u, --username USERNAME 设置MongoDB用户名 (默认: ${DEFAULT_MONGO_USERNAME})" 24 | echo " -w, --password PASSWORD 设置MongoDB密码 (默认: ${DEFAULT_MONGO_PASSWORD})" 25 | echo " --host-ip IP 手动指定主机IP (可选)" 26 | exit 1 27 | } 28 | 29 | # 参数解析 30 | while [[ $# -gt 0 ]]; do 31 | case $1 in 32 | -h|--help) 33 | show_usage 34 | ;; 35 | -n|--name) 36 | CLUSTER_NAME="$2" 37 | shift 2 38 | ;; 39 | -p|--port) 40 | BASE_PORT="$2" 41 | shift 2 42 | ;; 43 | -v|--version) 44 | MONGO_VERSION="$2" 45 | shift 2 46 | ;; 47 | -u|--username) 48 | MONGO_USERNAME="$2" 49 | shift 2 50 | ;; 51 | -w|--password) 52 | MONGO_PASSWORD="$2" 53 | shift 2 54 | ;; 55 | --host-ip) 56 | MANUAL_HOST_IP="$2" 57 | shift 2 58 | ;; 59 | *) 60 | echo "错误: 未知参数 $1" 61 | show_usage 62 | ;; 63 | esac 64 | done 65 | 66 | # 设置默认值(如果未通过参数指定) 67 | CLUSTER_NAME=${CLUSTER_NAME:-$DEFAULT_CLUSTER_NAME} 68 | BASE_PORT=${BASE_PORT:-$DEFAULT_BASE_PORT} 69 | MONGO_VERSION=${MONGO_VERSION:-$DEFAULT_MONGO_VERSION} 70 | MONGO_USERNAME=${MONGO_USERNAME:-$DEFAULT_MONGO_USERNAME} 71 | MONGO_PASSWORD=${MONGO_PASSWORD:-$DEFAULT_MONGO_PASSWORD} 72 | 73 | # 验证必要参数 74 | if ! [[ "$BASE_PORT" =~ ^[0-9]+$ ]] || [ "$BASE_PORT" -lt 1024 ] || [ "$BASE_PORT" -gt 65535 ]; then 75 | echo "错误: 端口号必须是1024-65535之间的数字" 76 | exit 1 77 | fi 78 | 79 | # 验证端口范围 80 | if [ $((BASE_PORT + 3)) -gt 65535 ]; then 81 | echo "错误: 基础端口号过大,无法分配连续的4个端口" 82 | echo "当前配置将会使用以下端口:" 83 | echo "- $BASE_PORT: mongos路由服务" 84 | echo "- $((BASE_PORT + 1)): config配置服务" 85 | echo "- $((BASE_PORT + 2)): shard1分片服务" 86 | echo "- $((BASE_PORT + 3)): shard2分片服务" 87 | exit 1 88 | fi 89 | 90 | if [ -z "$MONGO_USERNAME" ] || [ -z "$MONGO_PASSWORD" ]; then 91 | echo "错误: 用户名和密码不能为空" 92 | exit 1 93 | fi 94 | 95 | # 端口配置 96 | MONGOS_PORT=$BASE_PORT 97 | CONFIG_PORT=$((BASE_PORT + 1)) 98 | SHARD1_PORT=$((BASE_PORT + 2)) 99 | SHARD2_PORT=$((BASE_PORT + 3)) 100 | 101 | # 显示端口使用信息 102 | echo "将使用以下端口:" 103 | echo "- $MONGOS_PORT: mongos路由服务" 104 | echo "- $CONFIG_PORT: config配置服务" 105 | echo "- $SHARD1_PORT: shard1分片服务" 106 | echo "- $SHARD2_PORT: shard2分片服务" 107 | echo 108 | 109 | # 检查端口占用 110 | check_port() { 111 | local port=$1 112 | if command -v nc >/dev/null 2>&1; then 113 | if nc -z localhost $port >/dev/null 2>&1; then 114 | echo "错误: 端口 $port 已被占用" 115 | exit 1 116 | fi 117 | elif command -v lsof >/dev/null 2>&1; then 118 | if lsof -i :$port >/dev/null 2>&1; then 119 | echo "错误: 端口 $port 已被占用" 120 | exit 1 121 | fi 122 | fi 123 | } 124 | 125 | # 检查所需端口 126 | for port in $MONGOS_PORT $CONFIG_PORT $SHARD1_PORT $SHARD2_PORT; do 127 | check_port $port 128 | done 129 | 130 | # 添加IP获取函数 131 | get_host_ip() { 132 | # 如果手动指定了IP,直接使用 133 | if [ ! -z "$MANUAL_HOST_IP" ]; then 134 | # 验证IP格式 135 | if [[ $MANUAL_HOST_IP =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then 136 | echo "$MANUAL_HOST_IP" 137 | return 0 138 | else 139 | echo "错误: 无效的IP地址格式: $MANUAL_HOST_IP" >&2 140 | exit 1 141 | fi 142 | fi 143 | 144 | case "$(uname -s)" in 145 | Darwin) 146 | # MacOS - 尝试多个常见网络接口 147 | for interface in en0 en1 en2 en3; do 148 | host_ip=$(ipconfig getifaddr $interface 2>/dev/null) 149 | if [ ! -z "$host_ip" ]; then 150 | echo $host_ip 151 | return 0 152 | fi 153 | done 154 | # 如果上面都失败了,尝试使用 ifconfig 155 | host_ip=$(ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -n 1) 156 | ;; 157 | Linux) 158 | # Linux - 尝试多种方法 159 | if command -v hostname >/dev/null 2>&1; then 160 | host_ip=$(hostname -I | awk '{print $1}') 161 | fi 162 | 163 | if [ -z "$host_ip" ]; then 164 | host_ip=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v 127.0.0.1 | head -n 1) 165 | fi 166 | 167 | if [ -z "$host_ip" ]; then 168 | host_ip=$(ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -n 1) 169 | fi 170 | ;; 171 | *) 172 | echo "不支持的操作系统" >&2 173 | exit 1 174 | ;; 175 | esac 176 | 177 | if [ -z "$host_ip" ]; then 178 | echo "错误: 无法获取主机IP地址。请使用 --host-ip 参数手动指定IP地址。" >&2 179 | exit 1 180 | fi 181 | 182 | echo $host_ip 183 | } 184 | 185 | # 获取主机IP 186 | HOST_IP=$(get_host_ip) 187 | 188 | # 创建集群目录 189 | mkdir -p ./${CLUSTER_NAME} 190 | 191 | # 创建和设置keyFile 192 | openssl rand -base64 756 > ./${CLUSTER_NAME}/mongodb.key 193 | chmod 400 ./${CLUSTER_NAME}/mongodb.key 194 | chown 999:999 ./${CLUSTER_NAME}/mongodb.key 195 | 196 | # 生成docker-compose.yml 197 | cat > ./${CLUSTER_NAME}/compose.yaml < README.md < 678 | builder.maxSize(50) 679 | .minSize(10) 680 | .maxConnectionIdleTime(30000, TimeUnit.MILLISECONDS) 681 | ) 682 | .applyToSocketSettings(builder -> 683 | builder.connectTimeout(10000, TimeUnit.MILLISECONDS) 684 | .readTimeout(45000, TimeUnit.MILLISECONDS) 685 | ) 686 | .applyToServerSettings(builder -> 687 | builder.heartbeatFrequency(10000, TimeUnit.MILLISECONDS) 688 | ) 689 | .build(); 690 | MongoClient mongoClient = MongoClients.create(settings); 691 | MongoDatabase database = mongoClient.getDatabase("your_database"); 692 | \`\`\` 693 | 694 | ### Go (mongo-driver) 695 | \`\`\`go 696 | import ( 697 | "context" 698 | "time" 699 | "go.mongodb.org/mongo-driver/mongo" 700 | "go.mongodb.org/mongo-driver/mongo/options" 701 | ) 702 | 703 | uri := "mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${HOST_IP}:${MONGOS_PORT}/admin?authSource=admin&authMechanism=SCRAM-SHA-1&readPreference=primary&retryWrites=true&w=majority" 704 | opts := options.Client(). 705 | ApplyURI(uri). 706 | SetMaxPoolSize(50). 707 | SetMinPoolSize(10). 708 | SetMaxConnIdleTime(30 * time.Second). 709 | SetConnectTimeout(10 * time.Second). 710 | SetServerSelectionTimeout(5 * time.Second). 711 | SetSocketTimeout(45 * time.Second) 712 | 713 | client, err := mongo.Connect(context.TODO(), opts) 714 | db := client.Database("your_database") 715 | \`\`\` 716 | 717 | ## 常用管理命令 718 | 719 | ### 1. 查看集群状态 720 | \`\`\`bash 721 | mongosh "mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${HOST_IP}:${MONGOS_PORT}/admin?authSource=admin" --eval "sh.status()" 722 | \`\`\` 723 | 724 | ### 2. 查看数据库列表 725 | \`\`\`bash 726 | mongosh "mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${HOST_IP}:${MONGOS_PORT}/admin?authSource=admin" --eval "show dbs" 727 | \`\`\` 728 | 729 | ### 3. 为集合启用分片 730 | \`\`\`bash 731 | # 首先对数据库启用分片 732 | mongosh "mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${HOST_IP}:${MONGOS_PORT}/admin?authSource=admin" --eval 'sh.enableSharding("your_database")' 733 | 734 | # 然后对集合启用分片(示例使用_id作为片键) 735 | mongosh "mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${HOST_IP}:${MONGOS_PORT}/admin?authSource=admin" --eval 'sh.shardCollection("your_database.your_collection", {_id: "hashed"})' 736 | \`\`\` 737 | 738 | ### 4. 查看集群配置 739 | \`\`\`bash 740 | mongosh "mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${HOST_IP}:${MONGOS_PORT}/admin?authSource=admin" --eval "db.getSiblingDB('config').settings.find()" 741 | \`\`\` 742 | 743 | ## 注意事项 744 | 1. 如果从其他机器连接,请将 ${HOST_IP} 替换为服务器的实际可访问IP地址 745 | 2. 确保防火墙已开放所需端口(${MONGOS_PORT}-$((MONGOS_PORT+3))) 746 | 3. 建议在生产环境中修改默认密码 747 | 4. 分片集群的所有操作都应该通过mongos路由进行 748 | 5. 建议使用支持MongoDB分片集群的驱动程序版本 749 | 750 | ## 管理命令 751 | 752 | ### 启动集群 753 | \`\`\`bash 754 | cd ${CLUSTER_NAME} 755 | docker compose up -d 756 | \`\`\` 757 | 758 | ### 停止集群 759 | \`\`\`bash 760 | cd ${CLUSTER_NAME} 761 | docker compose down 762 | \`\`\` 763 | 764 | ### 查看日志 765 | \`\`\`bash 766 | cd ${CLUSTER_NAME} 767 | docker compose logs -f 768 | \`\`\` 769 | 770 | ### 重启集群 771 | \`\`\`bash 772 | cd ${CLUSTER_NAME} 773 | docker compose restart 774 | \`\`\` 775 | 776 | ## 故障排查 777 | 1. 如果连接失败,请检查: 778 | - 用户名密码是否正确 779 | - IP地址和端口是否可访问 780 | - 防火墙是否开放端口 781 | - 集群服务是否正常运行 782 | 783 | 2. 如果分片不均衡,可以: 784 | - 检查片键选择是否合适 785 | - 运行手动平衡命令 786 | - 调整平衡器配置 787 | 788 | 3. 如果性能问题,建议: 789 | - 检查索引使用情况 790 | - 优化查询语句 791 | - 监控各分片负载 792 | - 考虑添加更多分片 793 | 794 | ## 备份和恢复 795 | 1. 备份整个集群: 796 | \`\`\`bash 797 | mongodump --uri="mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${HOST_IP}:${MONGOS_PORT}/admin?authSource=admin" --out=/backup/$(date +%Y%m%d) 798 | \`\`\` 799 | 800 | 2. 恢复备份: 801 | \`\`\`bash 802 | mongorestore --uri="mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${HOST_IP}:${MONGOS_PORT}/admin?authSource=admin" /backup/20240101 803 | \`\`\` 804 | 805 | ## 监控建议 806 | 1. 定期检查集群状态 807 | 2. 监控各节点资源使用 808 | 3. 设置适当的告警阈值 809 | 4. 保持日志定期归档 810 | 5. 建立备份恢复机制 811 | 812 | 祝您使用愉快! 813 | EOF 814 | 815 | echo "集群信息和使用说明已保存到 README.md" -------------------------------------------------------------------------------- /mongo-single-replica/README.md: -------------------------------------------------------------------------------- 1 | # mongo单节点副本集 2 | 3 | ## 功能 4 | 5 | * 通过docker compose启动一个mongo单节点副本集 6 | 7 | ## 原因 8 | 9 | * 本地开发环境中,需要一个mongo单节点副本集,有些功能只有在集群模式下可用,比如oplog(watch依赖于集群oplog) 10 | 11 | ## 使用方式 12 | 13 | ### 配置选项 14 | 15 | init.sh 支持以下参数: 16 | 17 | * `-p, --port PORT`: 设置端口号(默认:27017) 18 | * `-r, --replset NAME`: 设置副本集名称(默认:single-rs0) 19 | * `-u, --username USERNAME`: 设置MongoDB用户名(默认:root) 20 | * `-w, --password PASSWORD`: 设置MongoDB密码(默认:123456) 21 | * `--host-ip IP`: 手动指定主机IP(可选) 22 | * `-h, --help`: 显示帮助信息 23 | 24 | ### 启动 25 | 26 | * 运行 init.sh,可以使用上述参数自定义配置,例如: 27 | ```bash 28 | ./init.sh --port 27018 --replset my-rs0 29 | ``` 30 | 31 | ## 说明 32 | 33 | * init.sh 脚本会: 34 | - 自动创建副本集所需的目录结构 35 | - 创建mongo的集群key文件(如果不存在) 36 | - 生成 docker-compose.yml 配置文件(如果不存在) 37 | - 创建并启动docker compose集群 38 | - 自动初始化副本集 39 | 40 | ## 技术细节 41 | 42 | * 使用 MongoDB 5.0 及以上版本 43 | * 默认时区设置为 Asia/Shanghai 44 | * 自动配置副本集认证 45 | * 数据持久化存储在 ./mongo 目录 -------------------------------------------------------------------------------- /mongo-single-replica/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 默认配置 4 | DEFAULT_PORT=27017 5 | DEFAULT_REPLSET_NAME="single-rs0" 6 | DEFAULT_MONGO_USERNAME="root" 7 | DEFAULT_MONGO_PASSWORD="123456" 8 | 9 | # 帮助信息 10 | show_usage() { 11 | echo "用法: $0 [选项]" 12 | echo "选项:" 13 | echo " -h, --help 显示此帮助信息" 14 | echo " -p, --port PORT 设置端口号 (默认: ${DEFAULT_PORT})" 15 | echo " -r, --replset NAME 设置副本集名称 (默认: ${DEFAULT_REPLSET_NAME})" 16 | echo " -u, --username USERNAME 设置MongoDB用户名 (默认: ${DEFAULT_MONGO_USERNAME})" 17 | echo " -w, --password PASSWORD 设置MongoDB密码 (默认: ${DEFAULT_MONGO_PASSWORD})" 18 | echo " --host-ip IP 手动指定主机IP (可选)" 19 | exit 1 20 | } 21 | 22 | # 参数解析 23 | while [[ $# -gt 0 ]]; do 24 | case $1 in 25 | -h|--help) 26 | show_usage 27 | ;; 28 | -p|--port) 29 | PORT="$2" 30 | shift 2 31 | ;; 32 | -r|--replset) 33 | REPLSET_NAME="$2" 34 | shift 2 35 | ;; 36 | -u|--username) 37 | MONGO_USERNAME="$2" 38 | shift 2 39 | ;; 40 | -w|--password) 41 | MONGO_PASSWORD="$2" 42 | shift 2 43 | ;; 44 | --host-ip) 45 | MANUAL_HOST_IP="$2" 46 | shift 2 47 | ;; 48 | *) 49 | echo "错误: 未知参数 $1" 50 | show_usage 51 | ;; 52 | esac 53 | done 54 | 55 | # 设置默认值(如果未通过参数指定) 56 | PORT=${PORT:-$DEFAULT_PORT} 57 | REPLSET_NAME=${REPLSET_NAME:-$DEFAULT_REPLSET_NAME} 58 | MONGO_USERNAME=${MONGO_USERNAME:-$DEFAULT_MONGO_USERNAME} 59 | MONGO_PASSWORD=${MONGO_PASSWORD:-$DEFAULT_MONGO_PASSWORD} 60 | 61 | # 添加IP获取函数 62 | get_host_ip() { 63 | # 如果手动指定了IP,直接使用 64 | if [ ! -z "$MANUAL_HOST_IP" ]; then 65 | # 验证IP格式 66 | if [[ $MANUAL_HOST_IP =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then 67 | echo "$MANUAL_HOST_IP" 68 | return 0 69 | else 70 | echo "错误: 无效的IP地址格式: $MANUAL_HOST_IP" >&2 71 | exit 1 72 | fi 73 | fi 74 | 75 | case "$(uname -s)" in 76 | Darwin) 77 | # MacOS - 尝试多个常见网络接口 78 | for interface in en0 en1 en2 en3; do 79 | host_ip=$(ipconfig getifaddr $interface 2>/dev/null) 80 | if [ ! -z "$host_ip" ]; then 81 | echo $host_ip 82 | return 0 83 | fi 84 | done 85 | # 如果上面都失败了,尝试使用 ifconfig 86 | host_ip=$(ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -n 1) 87 | ;; 88 | Linux) 89 | # Linux - 尝试多种方法 90 | if command -v hostname >/dev/null 2>&1; then 91 | host_ip=$(hostname -I | awk '{print $1}') 92 | fi 93 | 94 | if [ -z "$host_ip" ]; then 95 | host_ip=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v 127.0.0.1 | head -n 1) 96 | fi 97 | 98 | if [ -z "$host_ip" ]; then 99 | host_ip=$(ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -n 1) 100 | fi 101 | ;; 102 | *) 103 | echo "不支持的操作系统" >&2 104 | exit 1 105 | ;; 106 | esac 107 | 108 | if [ -z "$host_ip" ]; then 109 | echo "错误: 无法获取主机IP地址。请使用 --host-ip 参数手动指定IP地址。" >&2 110 | exit 1 111 | fi 112 | 113 | echo $host_ip 114 | } 115 | 116 | # 获取主机IP 117 | HOST_IP=$(get_host_ip) 118 | 119 | echo "当前主机IP: $HOST_IP" 120 | echo "当前端口: $PORT" 121 | echo "当前副本集名称: $REPLSET_NAME" 122 | 123 | # 创建集群目录 124 | mkdir -p ./${REPLSET_NAME} 125 | 126 | #如果不存在mongo-keyfile文件,则创建 127 | if [ ! -f ./${REPLSET_NAME}/mongo-keyfile ]; then 128 | echo "找不到mongo-keyfile文件,创建中..." 129 | openssl rand -base64 756 > ./${REPLSET_NAME}/mongo-keyfile 130 | chmod 400 ./${REPLSET_NAME}/mongo-keyfile 131 | chown 999:999 ./${REPLSET_NAME}/mongo-keyfile 132 | fi 133 | 134 | #如果不存在docker-compose.yml文件,则创建 135 | if [ ! -f ./${REPLSET_NAME}/docker-compose.yml ]; then 136 | echo "找不到docker-compose.yml文件,创建中..." 137 | cat > ./${REPLSET_NAME}/docker-compose.yml << EOL 138 | version: '3' 139 | services: 140 | mongo: 141 | image: mongo:5.0 #需要MongoDB 5.0以上版本 142 | container_name: mongo-${REPLSET_NAME} 143 | ports: 144 | - "${PORT}:27017" 145 | expose: 146 | - "27017" 147 | restart: always 148 | environment: 149 | MONGO_INITDB_ROOT_USERNAME: "${MONGO_USERNAME}" 150 | MONGO_INITDB_ROOT_PASSWORD: "${MONGO_PASSWORD}" 151 | TZ: "Asia/Shanghai" 152 | volumes: 153 | - "./mongo:/data/db" 154 | - "./mongo-keyfile:/etc/mongo-keyfile:ro" 155 | command: [ "mongod", "--replSet", "${REPLSET_NAME}", "--keyFile", "/etc/mongo-keyfile" ] 156 | EOL 157 | fi 158 | 159 | #启动docker compose 160 | echo "启动docker compose" 161 | cd ./${REPLSET_NAME} 162 | docker compose up -d 163 | 164 | # 添加JSON解析函数 165 | parse_mongo_result() { 166 | local result=$1 167 | if [[ $result == *"ok"*"1"* ]]; then 168 | return 0 169 | else 170 | return 1 171 | fi 172 | } 173 | 174 | # 等待mongo启动 175 | echo "等待mongo启动" 176 | max_attempts=30 177 | attempt=1 178 | while [ $attempt -le $max_attempts ]; do 179 | result=$(docker exec -it mongo-${REPLSET_NAME} mongosh --quiet --eval "JSON.stringify(db.adminCommand('ping'))" 2>/dev/null) 180 | if parse_mongo_result "$result"; then 181 | echo "MongoDB已就绪!" 182 | break 183 | fi 184 | echo "尝试 $attempt/$max_attempts ..." 185 | attempt=$((attempt + 1)) 186 | sleep 2 187 | 188 | if [ $attempt -gt $max_attempts ]; then 189 | echo "MongoDB启动超时!" 190 | exit 1 191 | fi 192 | done 193 | 194 | #初始化mongo 195 | echo "初始化mongo" 196 | INIT_CMD="rs.initiate({_id: '${REPLSET_NAME}', members: [{_id: 0, host: '${HOST_IP}:${PORT}'}]})" 197 | echo "执行脚本: $INIT_CMD" 198 | docker exec -it mongo-${REPLSET_NAME} mongosh --authenticationDatabase admin -u ${MONGO_USERNAME} -p ${MONGO_PASSWORD} --host ${HOST_IP} --eval "$INIT_CMD" -------------------------------------------------------------------------------- /redis-shard-replica/README.md: -------------------------------------------------------------------------------- 1 | # Redis分片集群 2 | 3 | 基于 Docker 的 Redis 分片集群部署脚本,支持自定义分片数和副本数。 4 | 5 | ## 功能特点 6 | 7 | - 支持自定义分片数量和每个分片的副本数 8 | - 自动配置和部署 Redis 集群 9 | - 基于 Docker 容器,易于部署和管理 10 | - 自动检测端口占用和系统兼容性 11 | - 支持自定义 Redis 版本和密码 12 | - 支持自定义 CPU 和内存限制 13 | - 提供完整的连接信息和示例代码 14 | 15 | ## 使用方法 16 | 17 | ### 1. 基本用法 18 | 19 | ```bash 20 | ./init.sh 21 | ``` 22 | 23 | 这将使用默认配置创建一个 Redis 集群: 24 | - 3个分片(主节点,Redis集群要求至少3个主节点) 25 | - 无副本(仅用于开发测试) 26 | - 基础端口 6379 27 | - Redis 版本 7.2 28 | - 默认密码 123456 29 | - CPU限制 0.5核/节点 30 | - 内存限制 512MB/节点 31 | 32 | ### 2. 自定义配置 33 | 34 | ```bash 35 | ./init.sh [选项] 36 | ``` 37 | 38 | 可用选项: 39 | - `-h, --help` 显示帮助信息 40 | - `-n, --name NAME` 设置集群名称 (默认: redis-cluster1) 41 | - `-p, --port PORT` 设置基础端口号 (默认: 6379) 42 | - `-v, --version VERSION` 设置Redis版本 (默认: 7.2) 43 | - `-w, --password PASSWORD` 设置Redis密码 (默认: 123456) 44 | - `-s, --shards SHARDS` 设置分片数量 (默认: 3,最少3个) 45 | - `-r, --replicas REPLICAS` 设置每个分片的副本数 (默认: 0) 46 | - `--host-ip IP` 手动指定主机IP (可选) 47 | - `--cpu CPU` 设置CPU限制 (默认: 0.5) 48 | - `--memory MEM` 设置内存限制 (默认: 512M) 49 | 50 | ### 3. 示例 51 | 52 | 创建一个生产环境配置(3个分片、每个分片1个副本): 53 | ```bash 54 | ./init.sh -r 1 55 | ``` 56 | 57 | 创建一个高可用集群(5个分片、每个分片2个副本,更多资源): 58 | ```bash 59 | ./init.sh -s 5 -r 2 --cpu 1.0 --memory 2G 60 | ``` 61 | 62 | 创建一个开发测试集群(3个分片、无副本、自定义端口和密码): 63 | ```bash 64 | ./init.sh -p 7000 -w mypassword 65 | ``` 66 | 67 | ## 端口说明 68 | 69 | - 每个节点需要两个端口: 70 | - Redis服务端口:从基础端口开始递增 71 | - 集群总线端口:服务端口 + 10000 72 | 73 | 例如,使用默认配置(3分片,无副本)的端口分配: 74 | - 主节点:6379, 6380, 6381 (总线端口:16379, 16380, 16381) 75 | 76 | 如果添加副本(例如 -r 1),则端口分配: 77 | - 主节点:6379, 6380, 6381 (总线端口:16379, 16380, 16381) 78 | - 副本节点:6382, 6383, 6384 (总线端口:16382, 16383, 16384) 79 | 80 | ## 目录结构 81 | 82 | 部署完成后会创建如下目录结构(以默认配置为例): 83 | ``` 84 | redis-cluster1/ 85 | ├── compose.yaml # Docker Compose 配置文件 86 | ├── README.md # 集群信息和连接说明 87 | ├── redis-node-0/ # 主节点0数据目录 88 | ├── redis-node-1/ # 主节点1数据目录 89 | └── redis-node-2/ # 主节点2数据目录 90 | ``` 91 | 92 | 如果配置了副本,则还会包含对应的副本目录。 93 | 94 | ## 注意事项 95 | 96 | 1. 确保系统已安装 Docker 和 Docker Compose 97 | 2. 所需端口未被占用 98 | 3. 主机防火墙允许集群端口访问 99 | 4. Redis集群要求至少3个主节点 100 | 5. 建议在生产环境中: 101 | - 修改默认密码 102 | - 配置至少1个副本(-r 1) 103 | - 根据实际需求调整 CPU 和内存限制 104 | 6. 默认配置(无副本模式)适合开发和测试环境 105 | 7. 集群创建后会自动生成详细的连接信息到 README.md 文件 106 | 107 | ## 故障排除 108 | 109 | 1. 如果端口被占用: 110 | - 使用 `-p` 参数指定其他可用端口 111 | - 或先停止占用端口的服务 112 | 113 | 2. 如果无法获取主机IP: 114 | - 使用 `--host-ip` 参数手动指定IP 115 | 116 | 3. 如果容器启动失败: 117 | - 检查 Docker 服务状态 118 | - 检查资源限制是否合理 119 | - 查看容器日志:`docker logs 容器ID` 120 | 121 | 4. 如果集群创建失败: 122 | - 确保所有容器都在运行 123 | - 检查网络连接是否正常 124 | - 检查内存限制是否足够 125 | - 查看 Redis 日志是否有错误信息 126 | - 确保配置了至少3个主节点 -------------------------------------------------------------------------------- /redis-shard-replica/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 默认配置 4 | DEFAULT_CLUSTER_NAME="redis-cluster1" 5 | DEFAULT_BASE_PORT=6379 6 | DEFAULT_REDIS_VERSION="7.2" 7 | DEFAULT_REDIS_PASSWORD="123456" 8 | DEFAULT_SHARDS=3 9 | DEFAULT_REPLICAS=0 10 | DEFAULT_CPU_LIMIT="0.5" 11 | DEFAULT_MEMORY_LIMIT="512M" 12 | 13 | # 帮助信息 14 | show_usage() { 15 | echo "用法: $0 [选项]" 16 | echo "选项:" 17 | echo " -h, --help 显示此帮助信息" 18 | echo " -n, --name NAME 设置集群名称 (默认: ${DEFAULT_CLUSTER_NAME})" 19 | echo " -p, --port PORT 设置基础端口号 (默认: ${DEFAULT_BASE_PORT})" 20 | echo " -v, --version VERSION 设置Redis版本 (默认: ${DEFAULT_REDIS_VERSION})" 21 | echo " -w, --password PASSWORD 设置Redis密码 (默认: ${DEFAULT_REDIS_PASSWORD})" 22 | echo " -s, --shards SHARDS 设置分片数量 (默认: ${DEFAULT_SHARDS},最少3个)" 23 | echo " -r, --replicas REPLICAS 设置每个分片的副本数 (默认: ${DEFAULT_REPLICAS},0表示无副本)" 24 | echo " --host-ip IP 手动指定主机IP (可选)" 25 | echo " --cpu CPU 设置CPU限制 (默认: ${DEFAULT_CPU_LIMIT})" 26 | echo " --memory MEM 设置内存限制 (默认: ${DEFAULT_MEMORY_LIMIT})" 27 | exit 1 28 | } 29 | 30 | # 参数解析 31 | while [[ $# -gt 0 ]]; do 32 | case $1 in 33 | -h|--help) 34 | show_usage 35 | ;; 36 | -n|--name) 37 | CLUSTER_NAME="$2" 38 | shift 2 39 | ;; 40 | -p|--port) 41 | BASE_PORT="$2" 42 | shift 2 43 | ;; 44 | -v|--version) 45 | REDIS_VERSION="$2" 46 | shift 2 47 | ;; 48 | -w|--password) 49 | REDIS_PASSWORD="$2" 50 | shift 2 51 | ;; 52 | -s|--shards) 53 | SHARDS="$2" 54 | shift 2 55 | ;; 56 | -r|--replicas) 57 | REPLICAS="$2" 58 | shift 2 59 | ;; 60 | --host-ip) 61 | MANUAL_HOST_IP="$2" 62 | shift 2 63 | ;; 64 | --cpu) 65 | CPU_LIMIT="$2" 66 | shift 2 67 | ;; 68 | --memory) 69 | MEMORY_LIMIT="$2" 70 | shift 2 71 | ;; 72 | *) 73 | echo "错误: 未知参数 $1" 74 | show_usage 75 | ;; 76 | esac 77 | done 78 | 79 | # 设置默认值 80 | CLUSTER_NAME=${CLUSTER_NAME:-$DEFAULT_CLUSTER_NAME} 81 | BASE_PORT=${BASE_PORT:-$DEFAULT_BASE_PORT} 82 | REDIS_VERSION=${REDIS_VERSION:-$DEFAULT_REDIS_VERSION} 83 | REDIS_PASSWORD=${REDIS_PASSWORD:-$DEFAULT_REDIS_PASSWORD} 84 | SHARDS=${SHARDS:-$DEFAULT_SHARDS} 85 | REPLICAS=${REPLICAS:-$DEFAULT_REPLICAS} 86 | CPU_LIMIT=${CPU_LIMIT:-$DEFAULT_CPU_LIMIT} 87 | MEMORY_LIMIT=${MEMORY_LIMIT:-$DEFAULT_MEMORY_LIMIT} 88 | 89 | # 验证必要参数 90 | if ! [[ "$BASE_PORT" =~ ^[0-9]+$ ]] || [ "$BASE_PORT" -lt 1024 ] || [ "$BASE_PORT" -gt 65535 ]; then 91 | echo "错误: 端口号必须是1024-65535之间的数字" 92 | exit 1 93 | fi 94 | 95 | # 验证分片数量 96 | if [ "$SHARDS" -lt 3 ]; then 97 | echo "错误: Redis集群至少需要3个主节点(分片)" 98 | echo "提示: 请使用 --shards 参数指定至少3个分片,例如:" 99 | echo " ./init.sh --external-ip --shards 3" 100 | exit 1 101 | fi 102 | 103 | # 计算需要的总端口数 104 | TOTAL_NODES=$((SHARDS * (REPLICAS + 1))) 105 | if [ $((BASE_PORT + TOTAL_NODES - 1)) -gt 65535 ]; then 106 | echo "错误: 基础端口号过大,无法为所有节点分配端口" 107 | exit 1 108 | fi 109 | 110 | # 检查端口占用 111 | check_port() { 112 | local port=$1 113 | if command -v nc >/dev/null 2>&1; then 114 | if nc -z localhost $port >/dev/null 2>&1; then 115 | echo "错误: 端口 $port 已被占用" 116 | exit 1 117 | fi 118 | elif command -v lsof >/dev/null 2>&1; then 119 | if lsof -i :$port >/dev/null 2>&1; then 120 | echo "错误: 端口 $port 已被占用" 121 | exit 1 122 | fi 123 | fi 124 | } 125 | 126 | # 检查所需端口 127 | for ((i=0; i&2 139 | exit 1 140 | fi 141 | fi 142 | 143 | case "$(uname -s)" in 144 | Darwin) 145 | for interface in en0 en1 en2 en3; do 146 | host_ip=$(ipconfig getifaddr $interface 2>/dev/null) 147 | if [ ! -z "$host_ip" ]; then 148 | echo $host_ip 149 | return 0 150 | fi 151 | done 152 | host_ip=$(ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -n 1) 153 | ;; 154 | Linux) 155 | if command -v hostname >/dev/null 2>&1; then 156 | host_ip=$(hostname -I | awk '{print $1}') 157 | fi 158 | 159 | if [ -z "$host_ip" ]; then 160 | host_ip=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v 127.0.0.1 | head -n 1) 161 | fi 162 | 163 | if [ -z "$host_ip" ]; then 164 | host_ip=$(ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -n 1) 165 | fi 166 | ;; 167 | *) 168 | echo "不支持的操作系统" >&2 169 | exit 1 170 | ;; 171 | esac 172 | 173 | if [ -z "$host_ip" ]; then 174 | echo "错误: 无法获取主机IP地址。请使用 --host-ip 参数手动指定IP地址。" >&2 175 | exit 1 176 | fi 177 | 178 | echo $host_ip 179 | } 180 | 181 | # 获取主机IP 182 | HOST_IP=$(get_host_ip) 183 | 184 | # 创建集群目录 185 | mkdir -p ./${CLUSTER_NAME} 186 | 187 | # 生成compose.yml 188 | cat > ./${CLUSTER_NAME}/compose.yaml <> ./${CLUSTER_NAME}/compose.yaml <> ./${CLUSTER_NAME}/compose.yaml <= 1" 438 | else 439 | echo "- 当前配置了 ${REPLICAS} 个副本,支持故障转移和读写分离 440 | - 副本节点可以分担读取压力,提供数据冗余" 441 | fi) 442 | EOF 443 | } 444 | 445 | # 同时输出到终端和文件 446 | generate_info | tee README.md 447 | echo "集群信息已保存到 README.md" 448 | --------------------------------------------------------------------------------