From 377f176501ca04ae9d48b9c9d86aa7e2f9d11dd1 Mon Sep 17 00:00:00 2001 From: Houzhong Xu Date: Sat, 20 Sep 2025 16:49:32 +0000 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E7=BB=93=E6=9E=84=E5=92=8C=E6=96=87=E6=A1=A3=EF=BC=8C?= =?UTF-8?q?=E7=A7=BB=E9=99=A4=E4=B8=8D=E5=86=8D=E4=BD=BF=E7=94=A8=E7=9A=84?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改README.md,更新项目特性和目录结构说明 - 重命名基础设施代码目录为tofu,并添加Docker Swarm配置目录 - 移除不再使用的Docker Compose和Traefik配置文件 - 更新Terraform配置,专注于Oracle Cloud支持,移除华为云相关配置 - 清理开发环境变量和示例文件 --- README.md | 64 ++- .../applications/gitea-runner-management.yml | 95 ++++ .../applications/gitea-runner-setup.yml | 157 ++++++ .../templates/gitea-runner-config.yml.j2 | 50 ++ .../templates/gitea-runner.env.j2 | 18 + .../templates/gitea-runner.logrotate.j2 | 12 + .../templates/gitea-runner.service.j2 | 39 ++ .../templates/register-runner.sh.j2 | 46 ++ .../applications/templates/start-runner.sh.j2 | 20 + .../templates/gitea-runner-config.yml.j2 | 50 ++ .../templates/gitea-runner.env.j2 | 18 + .../templates/gitea-runner.logrotate.j2 | 12 + .../templates/gitea-runner.service.j2 | 39 ++ .../templates/register-runner.sh.j2 | 46 ++ .../gitea-runner/templates/start-runner.sh.j2 | 20 + docs/setup/consul-terraform-integration.md | 198 +++++++ docs/setup/oci-credentials-setup.md | 86 +++ docs/setup/oracle-cloud-setup.md | 153 ++++++ infrastructure/environments/dev/main.tf | 47 +- key.md | 61 +++ mgmt.sh | 162 ++++++ scripts/setup/setup-opentofu.sh | 174 ++++++ scripts/utilities/consul-secrets-manager.sh | 228 ++++++++ .../utilities/terraform-consul-provider.sh | 311 +++++++++++ .../utilities/tofu-secrets-uploader-simple.sh | 128 +++++ scripts/utilities/tofu-secrets-uploader.sh | 495 ++++++++++++++++++ .../configs}/traefik-consul-setup.yml | 0 .../configs}/traefik.yml | 0 swarm/scripts/swarm-manager.sh | 184 +++++++ swarm/stacks/consul-ash3c-stack.yml | 41 ++ swarm/stacks/consul-cluster-stack.yml | 76 +++ swarm/stacks/consul-master-stack.yml | 40 ++ swarm/stacks/consul-simple-stack.yml | 39 ++ .../stacks}/demo-services-stack.yml | 0 .../stacks}/traefik-swarm-stack.yml | 0 tofu/environments/dev/main.tf | 109 ++++ .../environments/dev/terraform.tfvars.example | 0 .../environments/dev/variables.tf | 0 .../providers/huawei-cloud/main.tf | 9 +- tofu/providers/huawei-cloud/variables.tf | 54 ++ .../providers/oracle-cloud/main.tf | 11 +- tofu/providers/oracle-cloud/variables.tf | 55 ++ {infrastructure => tofu}/shared/outputs.tf | 0 {infrastructure => tofu}/shared/variables.tf | 0 {infrastructure => tofu}/shared/versions.tf | 0 45 files changed, 3278 insertions(+), 69 deletions(-) create mode 100644 configuration/playbooks/applications/gitea-runner-management.yml create mode 100644 configuration/playbooks/applications/gitea-runner-setup.yml create mode 100644 configuration/playbooks/applications/templates/gitea-runner-config.yml.j2 create mode 100644 configuration/playbooks/applications/templates/gitea-runner.env.j2 create mode 100644 configuration/playbooks/applications/templates/gitea-runner.logrotate.j2 create mode 100644 configuration/playbooks/applications/templates/gitea-runner.service.j2 create mode 100644 configuration/playbooks/applications/templates/register-runner.sh.j2 create mode 100644 configuration/playbooks/applications/templates/start-runner.sh.j2 create mode 100644 configuration/roles/gitea-runner/templates/gitea-runner-config.yml.j2 create mode 100644 configuration/roles/gitea-runner/templates/gitea-runner.env.j2 create mode 100644 configuration/roles/gitea-runner/templates/gitea-runner.logrotate.j2 create mode 100644 configuration/roles/gitea-runner/templates/gitea-runner.service.j2 create mode 100644 configuration/roles/gitea-runner/templates/register-runner.sh.j2 create mode 100644 configuration/roles/gitea-runner/templates/start-runner.sh.j2 create mode 100644 docs/setup/consul-terraform-integration.md create mode 100644 docs/setup/oci-credentials-setup.md create mode 100644 docs/setup/oracle-cloud-setup.md create mode 100644 key.md create mode 100755 mgmt.sh create mode 100755 scripts/setup/setup-opentofu.sh create mode 100755 scripts/utilities/consul-secrets-manager.sh create mode 100755 scripts/utilities/terraform-consul-provider.sh create mode 100755 scripts/utilities/tofu-secrets-uploader-simple.sh create mode 100755 scripts/utilities/tofu-secrets-uploader.sh rename {containers/infrastructure => swarm/configs}/traefik-consul-setup.yml (100%) rename {containers/infrastructure => swarm/configs}/traefik.yml (100%) create mode 100755 swarm/scripts/swarm-manager.sh create mode 100644 swarm/stacks/consul-ash3c-stack.yml create mode 100644 swarm/stacks/consul-cluster-stack.yml create mode 100644 swarm/stacks/consul-master-stack.yml create mode 100644 swarm/stacks/consul-simple-stack.yml rename {containers/compose/production => swarm/stacks}/demo-services-stack.yml (100%) rename {containers/compose/production => swarm/stacks}/traefik-swarm-stack.yml (100%) create mode 100644 tofu/environments/dev/main.tf rename {infrastructure => tofu}/environments/dev/terraform.tfvars.example (100%) rename {infrastructure => tofu}/environments/dev/variables.tf (100%) rename {infrastructure => tofu}/providers/huawei-cloud/main.tf (94%) create mode 100644 tofu/providers/huawei-cloud/variables.tf rename {infrastructure => tofu}/providers/oracle-cloud/main.tf (92%) create mode 100644 tofu/providers/oracle-cloud/variables.tf rename {infrastructure => tofu}/shared/outputs.tf (100%) rename {infrastructure => tofu}/shared/variables.tf (100%) rename {infrastructure => tofu}/shared/versions.tf (100%) diff --git a/README.md b/README.md index 87cf4da..83a8836 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # 🏗️ 基础设施管理项目 -这是一个现代化的多云基础设施管理平台,集成 OpenTofu、Ansible、Docker Swarm 和 Gitea CI/CD。 +这是一个现代化的多云基础设施管理平台,专注于 OpenTofu、Ansible 和 Docker Swarm 的集成管理。 ## 🎯 项目特性 - **🌩️ 多云支持**: Oracle Cloud, 华为云, Google Cloud, AWS, DigitalOcean - **🏗️ 基础设施即代码**: 使用 OpenTofu 管理云资源 - **⚙️ 配置管理**: 使用 Ansible 自动化配置和部署 -- **🐳 容器编排**: Docker Swarm 集群管理 +- **🐳 容器编排**: Docker Swarm 集群管理和服务编排 - **🔄 CI/CD**: Gitea Actions 自动化流水线 - **📊 监控**: Prometheus + Grafana 监控体系 - **🔐 安全**: 多层安全防护和合规性 @@ -17,23 +17,25 @@ ``` mgmt/ ├── .gitea/workflows/ # CI/CD 工作流 -├── infrastructure/ # OpenTofu 基础设施代码 +├── tofu/ # OpenTofu 基础设施代码 │ ├── environments/ # 环境配置 (dev/staging/prod) │ ├── modules/ # 可复用模块 │ ├── providers/ # 云服务商配置 │ └── shared/ # 共享配置 +├── swarm/ # Docker Swarm 配置 +│ ├── stacks/ # Docker Stack 配置文件 +│ ├── configs/ # Traefik 等基础设施配置 +│ └── scripts/ # Swarm 管理脚本 ├── configuration/ # Ansible 配置管理 │ ├── inventories/ # 主机清单 │ ├── playbooks/ # 剧本 │ ├── roles/ # 角色 │ └── group_vars/ # 组变量 -├── containers/ # 容器化应用 -│ ├── applications/ # 应用容器 -│ ├── infrastructure/ # 基础设施容器 -│ └── compose/ # Docker Compose 文件 ├── monitoring/ # 监控配置 ├── scripts/ # 自动化脚本 -├── docs/ # 文档 +│ ├── setup/ # 环境设置脚本 +│ └── utilities/ # 实用工具脚本 +├── mgmt.sh # 项目管理主脚本 └── Makefile # 项目管理命令 ``` @@ -46,56 +48,60 @@ mgmt/ git clone cd mgmt -# 设置开发环境 -make setup +# 检查环境状态 +./mgmt.sh status + +# 快速部署(适用于开发环境) +./mgmt.sh deploy ``` ### 2. 配置云服务商 ```bash # 复制配置模板 -cp infrastructure/environments/dev/terraform.tfvars.example infrastructure/environments/dev/terraform.tfvars +cp tofu/environments/dev/terraform.tfvars.example tofu/environments/dev/terraform.tfvars # 编辑配置文件,填入你的云服务商凭据 -vim infrastructure/environments/dev/terraform.tfvars +vim tofu/environments/dev/terraform.tfvars ``` ### 3. 初始化基础设施 ```bash # 初始化 OpenTofu -make init +./mgmt.sh tofu init # 查看执行计划 -make plan +./mgmt.sh tofu plan # 应用基础设施变更 -make apply +cd tofu/environments/dev && tofu apply ``` -### 4. 部署应用 +### 4. 部署 Docker Swarm 服务 ```bash -# 检查 Ansible 配置 -make ansible-check +# 初始化 Docker Swarm +./mgmt.sh swarm init -# 部署应用 -make ansible-deploy +# 部署 Traefik 反向代理 +./mgmt.sh swarm deploy traefik swarm/stacks/traefik-swarm-stack.yml + +# 部署示例服务 +./mgmt.sh swarm deploy demo swarm/stacks/demo-services-stack.yml ``` ## 🛠️ 常用命令 | 命令 | 描述 | |------|------| -| `make help` | 显示所有可用命令 | -| `make setup` | 设置开发环境 | -| `make init` | 初始化 OpenTofu | -| `make plan` | 生成基础设施执行计划 | -| `make apply` | 应用基础设施变更 | -| `make ansible-deploy` | 部署应用 | -| `make docker-up` | 启动开发环境 | -| `make test` | 运行测试 | -| `make clean` | 清理临时文件 | +| `./mgmt.sh status` | 显示项目状态总览 | +| `./mgmt.sh deploy` | 快速部署所有服务 | +| `./mgmt.sh cleanup` | 清理所有部署的服务 | +| `./mgmt.sh swarm ` | Docker Swarm 管理命令 | +| `./mgmt.sh tofu ` | OpenTofu 管理命令 | +| `swarm/scripts/swarm-manager.sh help` | Swarm 管理帮助 | +| `scripts/setup/setup-opentofu.sh help` | OpenTofu 设置帮助 | ## 🌩️ 支持的云服务商 diff --git a/configuration/playbooks/applications/gitea-runner-management.yml b/configuration/playbooks/applications/gitea-runner-management.yml new file mode 100644 index 0000000..842692f --- /dev/null +++ b/configuration/playbooks/applications/gitea-runner-management.yml @@ -0,0 +1,95 @@ +--- +- name: Gitea Runner Management + hosts: hcp + become: yes + vars: + gitea_runner_user: "gitea-runner" + gitea_runner_data_dir: "/var/lib/gitea-runner" + gitea_runner_log_dir: "/var/log/gitea-runner" + + tasks: + - name: Check gitea-runner service status + systemd: + name: gitea-runner + register: service_status + + - name: Display service status + debug: + msg: | + Service: {{ service_status.status.ActiveState }} + Enabled: {{ service_status.status.UnitFileState }} + Main PID: {{ service_status.status.MainPID | default('N/A') }} + + - name: Show recent logs + command: journalctl -u gitea-runner --no-pager -n 20 + register: recent_logs + changed_when: false + + - name: Display recent logs + debug: + var: recent_logs.stdout_lines + + - name: Check runner registration + stat: + path: "{{ gitea_runner_data_dir }}/.runner" + register: runner_registered + + - name: Display registration status + debug: + msg: "Runner registered: {{ runner_registered.stat.exists }}" + + - name: Show runner configuration (if registered) + command: cat {{ gitea_runner_data_dir }}/.runner + register: runner_config + become_user: "{{ gitea_runner_user }}" + when: runner_registered.stat.exists + changed_when: false + + - name: Display runner configuration + debug: + var: runner_config.stdout_lines + when: runner_registered.stat.exists + + - name: Check Docker access for runner user + command: docker ps + become_user: "{{ gitea_runner_user }}" + register: docker_access + changed_when: false + failed_when: false + + - name: Display Docker access status + debug: + msg: | + Docker access: {{ 'OK' if docker_access.rc == 0 else 'FAILED' }} + {% if docker_access.rc != 0 %} + Error: {{ docker_access.stderr }} + {% endif %} + +# 单独的任务用于管理服务 +- name: Service Management Tasks + hosts: hcp + become: yes + tasks: + - name: Start gitea-runner service + systemd: + name: gitea-runner + state: started + when: ansible_run_tags is defined and 'start' in ansible_run_tags + + - name: Stop gitea-runner service + systemd: + name: gitea-runner + state: stopped + when: ansible_run_tags is defined and 'stop' in ansible_run_tags + + - name: Restart gitea-runner service + systemd: + name: gitea-runner + state: restarted + when: ansible_run_tags is defined and 'restart' in ansible_run_tags + + - name: Reload gitea-runner service + systemd: + name: gitea-runner + state: reloaded + when: ansible_run_tags is defined and 'reload' in ansible_run_tags \ No newline at end of file diff --git a/configuration/playbooks/applications/gitea-runner-setup.yml b/configuration/playbooks/applications/gitea-runner-setup.yml new file mode 100644 index 0000000..fe8d2ac --- /dev/null +++ b/configuration/playbooks/applications/gitea-runner-setup.yml @@ -0,0 +1,157 @@ +--- +- name: Setup Gitea Runner on HCP nodes + hosts: hcp + become: yes + vars: + gitea_runner_token: "vOrrQda6Qiet9YOj4waZVU5QgLig2J3rKp2RfoN7" + gitea_server_url: "http://gitea:3000" + gitea_runner_user: "gitea-runner" + gitea_runner_home: "/home/{{ gitea_runner_user }}" + gitea_runner_config_dir: "/etc/gitea-runner" + gitea_runner_data_dir: "/var/lib/gitea-runner" + gitea_runner_log_dir: "/var/log/gitea-runner" + gitea_runner_binary: "/usr/bin/act_runner" + + tasks: + - name: Check if gitea-runner binary exists + stat: + path: "{{ gitea_runner_binary }}" + register: runner_binary + + - name: Fail if act_runner binary not found + fail: + msg: "Act runner binary not found at {{ gitea_runner_binary }}. Please install it first." + when: not runner_binary.stat.exists + + - name: Create gitea-runner user + user: + name: "{{ gitea_runner_user }}" + system: yes + shell: /bin/bash + home: "{{ gitea_runner_home }}" + create_home: yes + comment: "Gitea Runner Service User" + + - name: Create gitea-runner directories + file: + path: "{{ item }}" + state: directory + owner: "{{ gitea_runner_user }}" + group: "{{ gitea_runner_user }}" + mode: '0755' + loop: + - "{{ gitea_runner_config_dir }}" + - "{{ gitea_runner_data_dir }}" + - "{{ gitea_runner_log_dir }}" + + - name: Create gitea-runner configuration file + template: + src: gitea-runner-config.yml.j2 + dest: "{{ gitea_runner_config_dir }}/config.yml" + owner: "{{ gitea_runner_user }}" + group: "{{ gitea_runner_user }}" + mode: '0600' + notify: restart gitea-runner + + - name: Create gitea-runner systemd service file + template: + src: gitea-runner.service.j2 + dest: /etc/systemd/system/gitea-runner.service + owner: root + group: root + mode: '0644' + notify: + - reload systemd + - restart gitea-runner + + - name: Create gitea-runner environment file + template: + src: gitea-runner.env.j2 + dest: /etc/default/gitea-runner + owner: root + group: root + mode: '0600' + notify: restart gitea-runner + + - name: Create runner registration script + template: + src: register-runner.sh.j2 + dest: "{{ gitea_runner_home }}/register-runner.sh" + owner: "{{ gitea_runner_user }}" + group: "{{ gitea_runner_user }}" + mode: '0755' + + - name: Check if runner is already registered + stat: + path: "{{ gitea_runner_data_dir }}/.runner" + register: runner_registered + + - name: Register gitea runner + command: "{{ gitea_runner_home }}/register-runner.sh" + become_user: "{{ gitea_runner_user }}" + when: not runner_registered.stat.exists + register: registration_result + + - name: Display registration result + debug: + var: registration_result.stdout_lines + when: registration_result is defined and registration_result.stdout_lines is defined + + - name: Create runner startup script + template: + src: start-runner.sh.j2 + dest: "{{ gitea_runner_home }}/start-runner.sh" + owner: "{{ gitea_runner_user }}" + group: "{{ gitea_runner_user }}" + mode: '0755' + + - name: Create logrotate configuration for gitea-runner + template: + src: gitea-runner.logrotate.j2 + dest: /etc/logrotate.d/gitea-runner + owner: root + group: root + mode: '0644' + + - name: Install Docker (required for runner) + package: + name: docker.io + state: present + + - name: Add gitea-runner user to docker group + user: + name: "{{ gitea_runner_user }}" + groups: docker + append: yes + + - name: Start and enable Docker service + systemd: + name: docker + state: started + enabled: yes + + - name: Start and enable gitea-runner service + systemd: + name: gitea-runner + state: started + enabled: yes + daemon_reload: yes + + - name: Check gitea-runner service status + systemd: + name: gitea-runner + register: service_status + + - name: Display service status + debug: + msg: "Gitea Runner service is {{ service_status.status.ActiveState }}" + + handlers: + - name: reload systemd + systemd: + daemon_reload: yes + + - name: restart gitea-runner + systemd: + name: gitea-runner + state: restarted \ No newline at end of file diff --git a/configuration/playbooks/applications/templates/gitea-runner-config.yml.j2 b/configuration/playbooks/applications/templates/gitea-runner-config.yml.j2 new file mode 100644 index 0000000..283fee7 --- /dev/null +++ b/configuration/playbooks/applications/templates/gitea-runner-config.yml.j2 @@ -0,0 +1,50 @@ +# Gitea Runner Configuration +log: + level: info + file: {{ gitea_runner_log_dir }}/runner.log + +runner: + # Runner name (will be auto-generated if not specified) + name: "{{ inventory_hostname }}-runner" + + # Runner capacity (number of concurrent jobs) + capacity: 2 + + # Runner timeout + timeout: 3600 + + # Runner labels (for job targeting) + labels: + - "ubuntu-latest:docker://ubuntu:22.04" + - "ubuntu-20.04:docker://ubuntu:20.04" + - "ubuntu-18.04:docker://ubuntu:18.04" + - "node:docker://node:18" + - "python:docker://python:3.11" + - "ansible:docker://quay.io/ansible/ansible-runner:latest" + - "opentofu:docker://opentofu/opentofu:latest" + +cache: + enabled: true + dir: {{ gitea_runner_data_dir }}/cache + host: "" + port: 0 + +container: + # Docker network for runner containers + network: "gitea-runner" + + # Enable privileged containers (needed for Docker-in-Docker) + privileged: false + + # Container options + options: "--rm --pull=always" + + # Valid platforms + valid_volumes: + - "/tmp" + - "{{ gitea_runner_data_dir }}" + + docker_host: "unix:///var/run/docker.sock" + +host: + workdir_parent: {{ gitea_runner_data_dir }}/work \ No newline at end of file diff --git a/configuration/playbooks/applications/templates/gitea-runner.env.j2 b/configuration/playbooks/applications/templates/gitea-runner.env.j2 new file mode 100644 index 0000000..65b4ce8 --- /dev/null +++ b/configuration/playbooks/applications/templates/gitea-runner.env.j2 @@ -0,0 +1,18 @@ +# Gitea Runner Environment Variables + +# Gitea server configuration +GITEA_INSTANCE_URL={{ gitea_server_url }} +GITEA_RUNNER_REGISTRATION_TOKEN={{ gitea_runner_token }} + +# Runner configuration +GITEA_RUNNER_NAME={{ inventory_hostname }}-runner +GITEA_RUNNER_LABELS=ubuntu-latest,ubuntu-20.04,ubuntu-18.04,node,python,ansible,opentofu + +# Docker configuration +DOCKER_HOST=unix:///var/run/docker.sock + +# Logging +GITEA_RUNNER_LOG_LEVEL=info + +# Security +GITEA_RUNNER_SECURITY_PRIVILEGED=false \ No newline at end of file diff --git a/configuration/playbooks/applications/templates/gitea-runner.logrotate.j2 b/configuration/playbooks/applications/templates/gitea-runner.logrotate.j2 new file mode 100644 index 0000000..2d8049b --- /dev/null +++ b/configuration/playbooks/applications/templates/gitea-runner.logrotate.j2 @@ -0,0 +1,12 @@ +{{ gitea_runner_log_dir }}/*.log { + daily + missingok + rotate 30 + compress + delaycompress + notifempty + create 644 {{ gitea_runner_user }} {{ gitea_runner_user }} + postrotate + systemctl reload gitea-runner || true + endscript +} \ No newline at end of file diff --git a/configuration/playbooks/applications/templates/gitea-runner.service.j2 b/configuration/playbooks/applications/templates/gitea-runner.service.j2 new file mode 100644 index 0000000..1f1661f --- /dev/null +++ b/configuration/playbooks/applications/templates/gitea-runner.service.j2 @@ -0,0 +1,39 @@ +[Unit] +Description=Gitea Actions Runner +Documentation=https://docs.gitea.io/en-us/actions/ +After=network.target docker.service +Wants=docker.service + +[Service] +Type=simple +User={{ gitea_runner_user }} +Group={{ gitea_runner_user }} +WorkingDirectory={{ gitea_runner_data_dir }} +ExecStart={{ gitea_runner_binary }} daemon --config {{ gitea_runner_config_dir }}/config.yml +ExecReload=/bin/kill -HUP $MAINPID +KillMode=mixed +KillSignal=SIGINT +TimeoutStopSec=5 +Restart=always +RestartSec=10 +StartLimitInterval=0 + +# Security settings +NoNewPrivileges=yes +PrivateTmp=yes +ProtectSystem=strict +ProtectHome=yes +ReadWritePaths={{ gitea_runner_data_dir }} {{ gitea_runner_log_dir }} /var/run/docker.sock +ProtectKernelTunables=yes +ProtectKernelModules=yes +ProtectControlGroups=yes + +# Environment +EnvironmentFile=-/etc/default/gitea-runner + +# Logging +StandardOutput=append:{{ gitea_runner_log_dir }}/gitea-runner.log +StandardError=append:{{ gitea_runner_log_dir }}/gitea-runner-error.log + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/configuration/playbooks/applications/templates/register-runner.sh.j2 b/configuration/playbooks/applications/templates/register-runner.sh.j2 new file mode 100644 index 0000000..4944df0 --- /dev/null +++ b/configuration/playbooks/applications/templates/register-runner.sh.j2 @@ -0,0 +1,46 @@ +#!/bin/bash +# Gitea Runner Registration Script + +set -e + +echo "🚀 注册 Gitea Runner..." + +# 配置变量 +GITEA_URL="{{ gitea_server_url }}" +REGISTRATION_TOKEN="{{ gitea_runner_token }}" +RUNNER_NAME="{{ inventory_hostname }}-runner" +RUNNER_LABELS="ubuntu-latest,ubuntu-20.04,ubuntu-18.04,node,python,ansible,opentofu" + +# 切换到数据目录 +cd {{ gitea_runner_data_dir }} + +# 检查是否已经注册 +if [ -f ".runner" ]; then + echo "✅ Runner 已经注册" + exit 0 +fi + +echo "📝 注册 Runner: $RUNNER_NAME" +echo "🔗 Gitea URL: $GITEA_URL" +echo "🏷️ Labels: $RUNNER_LABELS" + +# 注册 Runner +{{ gitea_runner_binary }} register \ + --instance "$GITEA_URL" \ + --token "$REGISTRATION_TOKEN" \ + --name "$RUNNER_NAME" \ + --labels "$RUNNER_LABELS" + +if [ $? -eq 0 ]; then + echo "✅ Runner 注册成功!" + + # 设置文件权限 + chown {{ gitea_runner_user }}:{{ gitea_runner_user }} .runner .credentials + chmod 600 .runner .credentials + + echo "📋 Runner 信息:" + cat .runner +else + echo "❌ Runner 注册失败" + exit 1 +fi \ No newline at end of file diff --git a/configuration/playbooks/applications/templates/start-runner.sh.j2 b/configuration/playbooks/applications/templates/start-runner.sh.j2 new file mode 100644 index 0000000..85e94de --- /dev/null +++ b/configuration/playbooks/applications/templates/start-runner.sh.j2 @@ -0,0 +1,20 @@ +#!/bin/bash +# Gitea Runner Startup Script + +set -e + +echo "🚀 启动 Gitea Runner..." + +# 切换到数据目录 +cd {{ gitea_runner_data_dir }} + +# 检查注册状态 +if [ ! -f ".runner" ]; then + echo "❌ Runner 未注册,请先运行注册脚本" + exit 1 +fi + +echo "✅ Runner 已注册,启动守护进程..." + +# 启动 Runner +exec {{ gitea_runner_binary }} daemon --config {{ gitea_runner_config_dir }}/config.yml \ No newline at end of file diff --git a/configuration/roles/gitea-runner/templates/gitea-runner-config.yml.j2 b/configuration/roles/gitea-runner/templates/gitea-runner-config.yml.j2 new file mode 100644 index 0000000..283fee7 --- /dev/null +++ b/configuration/roles/gitea-runner/templates/gitea-runner-config.yml.j2 @@ -0,0 +1,50 @@ +# Gitea Runner Configuration +log: + level: info + file: {{ gitea_runner_log_dir }}/runner.log + +runner: + # Runner name (will be auto-generated if not specified) + name: "{{ inventory_hostname }}-runner" + + # Runner capacity (number of concurrent jobs) + capacity: 2 + + # Runner timeout + timeout: 3600 + + # Runner labels (for job targeting) + labels: + - "ubuntu-latest:docker://ubuntu:22.04" + - "ubuntu-20.04:docker://ubuntu:20.04" + - "ubuntu-18.04:docker://ubuntu:18.04" + - "node:docker://node:18" + - "python:docker://python:3.11" + - "ansible:docker://quay.io/ansible/ansible-runner:latest" + - "opentofu:docker://opentofu/opentofu:latest" + +cache: + enabled: true + dir: {{ gitea_runner_data_dir }}/cache + host: "" + port: 0 + +container: + # Docker network for runner containers + network: "gitea-runner" + + # Enable privileged containers (needed for Docker-in-Docker) + privileged: false + + # Container options + options: "--rm --pull=always" + + # Valid platforms + valid_volumes: + - "/tmp" + - "{{ gitea_runner_data_dir }}" + + docker_host: "unix:///var/run/docker.sock" + +host: + workdir_parent: {{ gitea_runner_data_dir }}/work \ No newline at end of file diff --git a/configuration/roles/gitea-runner/templates/gitea-runner.env.j2 b/configuration/roles/gitea-runner/templates/gitea-runner.env.j2 new file mode 100644 index 0000000..65b4ce8 --- /dev/null +++ b/configuration/roles/gitea-runner/templates/gitea-runner.env.j2 @@ -0,0 +1,18 @@ +# Gitea Runner Environment Variables + +# Gitea server configuration +GITEA_INSTANCE_URL={{ gitea_server_url }} +GITEA_RUNNER_REGISTRATION_TOKEN={{ gitea_runner_token }} + +# Runner configuration +GITEA_RUNNER_NAME={{ inventory_hostname }}-runner +GITEA_RUNNER_LABELS=ubuntu-latest,ubuntu-20.04,ubuntu-18.04,node,python,ansible,opentofu + +# Docker configuration +DOCKER_HOST=unix:///var/run/docker.sock + +# Logging +GITEA_RUNNER_LOG_LEVEL=info + +# Security +GITEA_RUNNER_SECURITY_PRIVILEGED=false \ No newline at end of file diff --git a/configuration/roles/gitea-runner/templates/gitea-runner.logrotate.j2 b/configuration/roles/gitea-runner/templates/gitea-runner.logrotate.j2 new file mode 100644 index 0000000..2d8049b --- /dev/null +++ b/configuration/roles/gitea-runner/templates/gitea-runner.logrotate.j2 @@ -0,0 +1,12 @@ +{{ gitea_runner_log_dir }}/*.log { + daily + missingok + rotate 30 + compress + delaycompress + notifempty + create 644 {{ gitea_runner_user }} {{ gitea_runner_user }} + postrotate + systemctl reload gitea-runner || true + endscript +} \ No newline at end of file diff --git a/configuration/roles/gitea-runner/templates/gitea-runner.service.j2 b/configuration/roles/gitea-runner/templates/gitea-runner.service.j2 new file mode 100644 index 0000000..1f1661f --- /dev/null +++ b/configuration/roles/gitea-runner/templates/gitea-runner.service.j2 @@ -0,0 +1,39 @@ +[Unit] +Description=Gitea Actions Runner +Documentation=https://docs.gitea.io/en-us/actions/ +After=network.target docker.service +Wants=docker.service + +[Service] +Type=simple +User={{ gitea_runner_user }} +Group={{ gitea_runner_user }} +WorkingDirectory={{ gitea_runner_data_dir }} +ExecStart={{ gitea_runner_binary }} daemon --config {{ gitea_runner_config_dir }}/config.yml +ExecReload=/bin/kill -HUP $MAINPID +KillMode=mixed +KillSignal=SIGINT +TimeoutStopSec=5 +Restart=always +RestartSec=10 +StartLimitInterval=0 + +# Security settings +NoNewPrivileges=yes +PrivateTmp=yes +ProtectSystem=strict +ProtectHome=yes +ReadWritePaths={{ gitea_runner_data_dir }} {{ gitea_runner_log_dir }} /var/run/docker.sock +ProtectKernelTunables=yes +ProtectKernelModules=yes +ProtectControlGroups=yes + +# Environment +EnvironmentFile=-/etc/default/gitea-runner + +# Logging +StandardOutput=append:{{ gitea_runner_log_dir }}/gitea-runner.log +StandardError=append:{{ gitea_runner_log_dir }}/gitea-runner-error.log + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/configuration/roles/gitea-runner/templates/register-runner.sh.j2 b/configuration/roles/gitea-runner/templates/register-runner.sh.j2 new file mode 100644 index 0000000..4944df0 --- /dev/null +++ b/configuration/roles/gitea-runner/templates/register-runner.sh.j2 @@ -0,0 +1,46 @@ +#!/bin/bash +# Gitea Runner Registration Script + +set -e + +echo "🚀 注册 Gitea Runner..." + +# 配置变量 +GITEA_URL="{{ gitea_server_url }}" +REGISTRATION_TOKEN="{{ gitea_runner_token }}" +RUNNER_NAME="{{ inventory_hostname }}-runner" +RUNNER_LABELS="ubuntu-latest,ubuntu-20.04,ubuntu-18.04,node,python,ansible,opentofu" + +# 切换到数据目录 +cd {{ gitea_runner_data_dir }} + +# 检查是否已经注册 +if [ -f ".runner" ]; then + echo "✅ Runner 已经注册" + exit 0 +fi + +echo "📝 注册 Runner: $RUNNER_NAME" +echo "🔗 Gitea URL: $GITEA_URL" +echo "🏷️ Labels: $RUNNER_LABELS" + +# 注册 Runner +{{ gitea_runner_binary }} register \ + --instance "$GITEA_URL" \ + --token "$REGISTRATION_TOKEN" \ + --name "$RUNNER_NAME" \ + --labels "$RUNNER_LABELS" + +if [ $? -eq 0 ]; then + echo "✅ Runner 注册成功!" + + # 设置文件权限 + chown {{ gitea_runner_user }}:{{ gitea_runner_user }} .runner .credentials + chmod 600 .runner .credentials + + echo "📋 Runner 信息:" + cat .runner +else + echo "❌ Runner 注册失败" + exit 1 +fi \ No newline at end of file diff --git a/configuration/roles/gitea-runner/templates/start-runner.sh.j2 b/configuration/roles/gitea-runner/templates/start-runner.sh.j2 new file mode 100644 index 0000000..85e94de --- /dev/null +++ b/configuration/roles/gitea-runner/templates/start-runner.sh.j2 @@ -0,0 +1,20 @@ +#!/bin/bash +# Gitea Runner Startup Script + +set -e + +echo "🚀 启动 Gitea Runner..." + +# 切换到数据目录 +cd {{ gitea_runner_data_dir }} + +# 检查注册状态 +if [ ! -f ".runner" ]; then + echo "❌ Runner 未注册,请先运行注册脚本" + exit 1 +fi + +echo "✅ Runner 已注册,启动守护进程..." + +# 启动 Runner +exec {{ gitea_runner_binary }} daemon --config {{ gitea_runner_config_dir }}/config.yml \ No newline at end of file diff --git a/docs/setup/consul-terraform-integration.md b/docs/setup/consul-terraform-integration.md new file mode 100644 index 0000000..c61434d --- /dev/null +++ b/docs/setup/consul-terraform-integration.md @@ -0,0 +1,198 @@ +# Consul + Terraform 集成指南 + +本指南介绍如何使用 Consul 安全地管理 Terraform 中的敏感配置信息,特别是 Oracle Cloud 的凭据。 + +## 概述 + +我们使用 Consul 作为安全的密钥存储,避免在 Terraform 配置文件中直接暴露敏感信息。 + +## 架构 + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ Terraform │───▶│ Consul │───▶│ Oracle Cloud │ +│ │ │ (密钥存储) │ │ │ +│ consul provider │ │ │ │ │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ +``` + +## 前提条件 + +1. Consul 集群正在运行 +2. 可以访问 Consul API (默认: http://localhost:8500) +3. 已安装 curl 和 Terraform + +## 快速开始 + +### 1. 启动 Consul 集群 + +```bash +# 进入 swarm 目录 +cd swarm + +# 启动 Consul 集群 +docker-compose -f configs/traefik-consul-setup.yml up -d +``` + +### 2. 设置 Oracle Cloud 配置 + +```bash +# 使用密钥管理脚本设置配置 +./scripts/utilities/consul-secrets-manager.sh set-oracle +``` + +脚本会提示你输入: +- 租户 OCID +- 用户 OCID +- API 密钥指纹 +- 私钥文件路径 +- 区间 OCID + +### 3. 配置 Terraform + +```bash +# 设置 Terraform Consul Provider +./scripts/utilities/terraform-consul-provider.sh setup +``` + +### 4. 验证配置 + +```bash +# 查看存储在 Consul 中的配置 +./scripts/utilities/consul-secrets-manager.sh get-oracle +``` + +### 5. 运行 Terraform + +```bash +cd infrastructure/environments/dev + +# 初始化 Terraform +terraform init + +# 规划部署 +terraform plan + +# 应用配置 +terraform apply +``` + +## 详细说明 + +### Consul 密钥存储结构 + +``` +config/ +└── dev/ + └── oracle/ + ├── tenancy_ocid + ├── user_ocid + ├── fingerprint + ├── private_key + └── compartment_ocid +``` + +### 脚本功能 + +#### consul-secrets-manager.sh + +- `set-oracle`: 设置 Oracle Cloud 配置到 Consul +- `get-oracle`: 从 Consul 获取配置信息 +- `delete-oracle`: 删除 Consul 中的配置 +- `generate-vars`: 生成临时 Terraform 变量文件 +- `cleanup`: 清理临时文件 + +#### terraform-consul-provider.sh + +- `setup`: 创建 Terraform Consul Provider 配置文件 + +### 安全特性 + +1. **敏感信息隔离**: 私钥等敏感信息只存储在 Consul 中 +2. **临时文件**: 私钥文件只在 Terraform 运行时临时创建 +3. **权限控制**: 临时私钥文件设置为 600 权限 +4. **自动清理**: 提供清理脚本删除临时文件 + +## 环境变量 + +```bash +# Consul 地址 +export CONSUL_ADDR="http://localhost:8500" + +# Consul ACL Token (如果启用了 ACL) +export CONSUL_TOKEN="your-token" + +# 环境名称 +export ENVIRONMENT="dev" +``` + +## 故障排除 + +### Consul 连接问题 + +```bash +# 检查 Consul 状态 +curl http://localhost:8500/v1/status/leader + +# 检查 Consul 服务 +docker ps | grep consul +``` + +### 配置验证 + +```bash +# 验证 Consul 中的配置 +curl http://localhost:8500/v1/kv/config/dev/oracle?recurse + +# 检查 Terraform 配置 +terraform validate +``` + +### 清理和重置 + +```bash +# 清理临时文件 +./scripts/utilities/consul-secrets-manager.sh cleanup + +# 删除 Consul 中的配置 +./scripts/utilities/consul-secrets-manager.sh delete-oracle +``` + +## 最佳实践 + +1. **定期轮换密钥**: 定期更新 Oracle Cloud API 密钥 +2. **备份配置**: 定期备份 Consul 数据 +3. **监控访问**: 监控 Consul 密钥访问日志 +4. **环境隔离**: 不同环境使用不同的 Consul 路径 + +## 扩展其他云服务商 + +可以类似地为其他云服务商添加 Consul 集成: + +```bash +# 华为云配置路径 +config/dev/huawei/access_key +config/dev/huawei/secret_key + +# AWS 配置路径 +config/dev/aws/access_key +config/dev/aws/secret_key + +# Google Cloud 配置路径 +config/dev/gcp/service_account_key +``` + +## 相关文件 + +- `infrastructure/environments/dev/terraform.tfvars` - Terraform 变量配置 +- `scripts/utilities/consul-secrets-manager.sh` - Consul 密钥管理脚本 +- `scripts/utilities/terraform-consul-provider.sh` - Terraform Consul Provider 配置脚本 +- `swarm/configs/traefik-consul-setup.yml` - Consul 集群配置 + +## 支持 + +如有问题,请检查: +1. Consul 集群是否正常运行 +2. 网络连接是否正常 +3. 权限设置是否正确 +4. 环境变量是否正确设置 \ No newline at end of file diff --git a/docs/setup/oci-credentials-setup.md b/docs/setup/oci-credentials-setup.md new file mode 100644 index 0000000..d75ab08 --- /dev/null +++ b/docs/setup/oci-credentials-setup.md @@ -0,0 +1,86 @@ +# Oracle Cloud 凭据配置指南 + +## 凭据文件位置 + +### 1. OpenTofu 配置文件 +**文件位置**: `infrastructure/environments/dev/terraform.tfvars` + +这是主要的配置文件,需要填入你的 OCI 凭据: + +```hcl +# Oracle Cloud 配置 +oci_config = { + tenancy_ocid = "ocid1.tenancy.oc1..aaaaaaaa_你的租户ID" + user_ocid = "ocid1.user.oc1..aaaaaaaa_你的用户ID" + fingerprint = "aa:bb:cc:dd:ee:ff:gg:hh:ii:jj:kk:ll:mm:nn:oo:pp" + private_key_path = "~/.oci/oci_api_key.pem" + region = "ap-seoul-1" + compartment_ocid = "ocid1.compartment.oc1..aaaaaaaa_你的区间ID" +} +``` + +### 2. OCI 私钥文件 +**文件位置**: `~/.oci/oci_api_key.pem` + +这是你的 API 私钥文件,内容类似: + +``` +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC... +你的私钥内容 +-----END PRIVATE KEY----- +``` + +### 3. OCI 配置文件 (可选) +**文件位置**: `~/.oci/config` + +这是 OCI CLI 的配置文件,可以作为备用: + +```ini +[DEFAULT] +user=ocid1.user.oc1..aaaaaaaa_你的用户ID +fingerprint=aa:bb:cc:dd:ee:ff:gg:hh:ii:jj:kk:ll:mm:nn:oo:pp +tenancy=ocid1.tenancy.oc1..aaaaaaaa_你的租户ID +region=ap-seoul-1 +key_file=~/.oci/oci_api_key.pem +``` + +## 设置步骤 + +### 步骤 1: 创建 .oci 目录 +```bash +mkdir -p ~/.oci +chmod 700 ~/.oci +``` + +### 步骤 2: 保存私钥文件 +```bash +# 将你的私钥内容保存到文件 +nano ~/.oci/oci_api_key.pem + +# 设置正确的权限 +chmod 400 ~/.oci/oci_api_key.pem +``` + +### 步骤 3: 编辑 terraform.tfvars +```bash +# 编辑配置文件 +nano infrastructure/environments/dev/terraform.tfvars +``` + +## 安全注意事项 + +1. **私钥文件权限**: 确保私钥文件权限为 400 (只有所有者可读) +2. **不要提交到 Git**: `.gitignore` 已经配置忽略 `*.tfvars` 文件 +3. **备份凭据**: 建议安全备份你的私钥和配置信息 + +## 验证配置 + +配置完成后,可以运行以下命令验证: + +```bash +# 检查配置 +./scripts/setup/setup-opentofu.sh check + +# 初始化 OpenTofu +./scripts/setup/setup-opentofu.sh init \ No newline at end of file diff --git a/docs/setup/oracle-cloud-setup.md b/docs/setup/oracle-cloud-setup.md new file mode 100644 index 0000000..adc5a5b --- /dev/null +++ b/docs/setup/oracle-cloud-setup.md @@ -0,0 +1,153 @@ +# Oracle Cloud 配置指南 + +## 概述 + +本指南将帮助你配置 Oracle Cloud Infrastructure (OCI) 以便与 OpenTofu 一起使用。 + +## 前提条件 + +1. Oracle Cloud 账户(可以使用免费层) +2. 已安装 OpenTofu +3. 已安装 OCI CLI(可选,但推荐) + +## 步骤 1: 创建 Oracle Cloud 账户 + +1. 访问 [Oracle Cloud](https://cloud.oracle.com/) +2. 点击 "Start for free" 创建免费账户 +3. 完成注册流程 + +## 步骤 2: 获取必要的 OCID + +### 获取 Tenancy OCID + +1. 登录 Oracle Cloud Console +2. 点击右上角的用户图标 +3. 选择 "Tenancy: " +4. 复制 OCID 值 + +### 获取 User OCID + +1. 在 Oracle Cloud Console 中 +2. 点击右上角的用户图标 +3. 选择 "User Settings" +4. 复制 OCID 值 + +### 获取 Compartment OCID + +1. 在导航菜单中选择 "Identity & Security" > "Compartments" +2. 选择你要使用的 compartment(通常是 root compartment) +3. 复制 OCID 值 + +## 步骤 3: 创建 API 密钥 + +### 生成密钥对 + +```bash +# 创建 .oci 目录 +mkdir -p ~/.oci + +# 生成私钥 +openssl genrsa -out ~/.oci/oci_api_key.pem 2048 + +# 生成公钥 +openssl rsa -pubout -in ~/.oci/oci_api_key.pem -out ~/.oci/oci_api_key_public.pem + +# 设置权限 +chmod 400 ~/.oci/oci_api_key.pem +chmod 400 ~/.oci/oci_api_key_public.pem +``` + +### 添加公钥到 Oracle Cloud + +1. 在 Oracle Cloud Console 中,进入 "User Settings" +2. 在左侧菜单中选择 "API Keys" +3. 点击 "Add API Key" +4. 选择 "Paste Public Key" +5. 复制 `~/.oci/oci_api_key_public.pem` 的内容并粘贴 +6. 点击 "Add" +7. 复制显示的 fingerprint + +## 步骤 4: 配置 terraform.tfvars + +编辑 `infrastructure/environments/dev/terraform.tfvars` 文件: + +```hcl +# Oracle Cloud 配置 +oci_config = { + tenancy_ocid = "ocid1.tenancy.oc1..aaaaaaaa_your_actual_tenancy_id" + user_ocid = "ocid1.user.oc1..aaaaaaaa_your_actual_user_id" + fingerprint = "aa:bb:cc:dd:ee:ff:gg:hh:ii:jj:kk:ll:mm:nn:oo:pp" + private_key_path = "~/.oci/oci_api_key.pem" + region = "ap-seoul-1" # 或你选择的区域 + compartment_ocid = "ocid1.compartment.oc1..aaaaaaaa_your_compartment_id" +} +``` + +## 步骤 5: 验证配置 + +```bash +# 检查配置 +./scripts/setup/setup-opentofu.sh check + +# 初始化 OpenTofu +./scripts/setup/setup-opentofu.sh init + +# 生成计划 +./scripts/setup/setup-opentofu.sh plan +``` + +## 可用区域 + +常用的 Oracle Cloud 区域: + +- `ap-seoul-1` - 韩国首尔 +- `ap-tokyo-1` - 日本东京 +- `us-ashburn-1` - 美国弗吉尼亚州 +- `us-phoenix-1` - 美国亚利桑那州 +- `eu-frankfurt-1` - 德国法兰克福 + +## 免费层资源 + +Oracle Cloud 免费层包括: + +- 2 个 AMD 计算实例(VM.Standard.E2.1.Micro) +- 4 个 Arm 计算实例(VM.Standard.A1.Flex) +- 200 GB 块存储 +- 10 GB 对象存储 +- 负载均衡器 +- 数据库等 + +## 故障排除 + +### 常见错误 + +1. **401 Unauthorized**: 检查 API 密钥配置 +2. **404 Not Found**: 检查 OCID 是否正确 +3. **权限错误**: 确保用户有足够的权限 + +### 验证连接 + +```bash +# 安装 OCI CLI(可选) +pip install oci-cli + +# 配置 OCI CLI +oci setup config + +# 测试连接 +oci iam compartment list +``` + +## 安全最佳实践 + +1. 定期轮换 API 密钥 +2. 使用最小权限原则 +3. 不要在代码中硬编码凭据 +4. 使用 compartment 隔离资源 +5. 启用审计日志 + +## 参考资料 + +- [Oracle Cloud Infrastructure 文档](https://docs.oracle.com/en-us/iaas/) +- [OCI Terraform Provider](https://registry.terraform.io/providers/oracle/oci/latest/docs) +- [Oracle Cloud 免费层](https://www.oracle.com/cloud/free/) \ No newline at end of file diff --git a/infrastructure/environments/dev/main.tf b/infrastructure/environments/dev/main.tf index ba916fc..c4d12bc 100644 --- a/infrastructure/environments/dev/main.tf +++ b/infrastructure/environments/dev/main.tf @@ -1,8 +1,30 @@ # 开发环境主配置文件 -# 引用共享配置 -module "shared" { - source = "../../shared" +# 版本和提供商配置 +terraform { + required_version = ">= 1.6" + + required_providers { + # Oracle Cloud Infrastructure + oci = { + source = "oracle/oci" + version = "~> 5.0" + } + } + + # 后端配置 + backend "local" { + path = "terraform.tfstate" + } +} + +# Oracle Cloud 提供商配置 +provider "oci" { + tenancy_ocid = var.oci_config.tenancy_ocid + user_ocid = var.oci_config.user_ocid + fingerprint = var.oci_config.fingerprint + private_key_path = var.oci_config.private_key_path + region = var.oci_config.region } # Oracle Cloud 基础设施 @@ -23,27 +45,8 @@ module "oracle_cloud" { instance_size = "VM.Standard.E2.1.Micro" # 免费层 } -# 华为云基础设施 (可选) -module "huawei_cloud" { - source = "../../providers/huawei-cloud" - count = contains(var.cloud_providers, "huawei") ? 1 : 0 - - environment = var.environment - project_name = var.project_name - owner = var.owner - vpc_cidr = "10.1.0.0/16" # 不同的 CIDR 避免冲突 - availability_zones = var.availability_zones - common_tags = var.common_tags - huawei_config = var.huawei_config -} - # 输出 output "oracle_cloud_outputs" { description = "Oracle Cloud 基础设施输出" value = module.oracle_cloud -} - -output "huawei_cloud_outputs" { - description = "华为云基础设施输出" - value = length(module.huawei_cloud) > 0 ? module.huawei_cloud[0] : null } \ No newline at end of file diff --git a/key.md b/key.md new file mode 100644 index 0000000..557173c --- /dev/null +++ b/key.md @@ -0,0 +1,61 @@ +oci usa +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDiDwCO56943GNv +rg2WYQCZTpdgA1YfdDcM6AXugzwGm6zQwuhuDdTABwJj0PfB7q5s0MZFqwpVW+MS +IkTQsk8r+8gBo9FBtH4nneBfXgqjiKhlHhcgqHWALXm8AzDq6MJ+lCDwjgi5PsID +5jbnBUBXRpVXlkEDMZj5yTNiRfLMlFZiqc4mv1j1RSIQeuptt7l2AnXhKSuW2FL3 +aQNWspUylIs9IDXuC490r04/fZmX0Iw7Crb4yWWFem1e1x0g6qDiAizwpE3FF0No +AqkH+p+y3Qe7Pew2/UUS4VNRyoGBstpbBJMQ8dXRER9M4KTEdLnSG2EzdM5IGupj +Gwo9PnPPAgMBAAECggEAChaP+HtPvLMJH6NtfnfXEQBi3P6zyd+OfV2gC3mBJO0E +P09ovqXmB/5ywDBD05G/6EyWLJG/ek5eycu3CnaKoJ8x2RuNwRg5m7GooQOPXKZC +mDtJiO7mSia9YgM6caFFh2SQ5mtQPwQVSxtA+U+mBBRlocJWJbsBj/7HSOwaM8BC +wl19kZiW0aOkoGidxvjlJfkPiNer/jTy5RMNKruDpaF8PsF7xIMLwuxT5VQ/gyYA +frXsWfQp+sve/XfUg9/RGP9jJQHNppL56YWYPa8XusC2nJCym9RLDlK56jF9jhYM +iQThksG3TzOXjdGM7MP5Q/SfNckQWy0KTOu7h+N98QKBgQD9l7SFypX5Mgn2PC1A +U3lwiLCvviaKSNbzNXc6pnijbGEEvNpUGRyGwmjXItGLiEoAky0eu42Ipult6PB+ +WsjCIGTGI0UBObrjbWfaj+vt6zCcI653tgvrQO97t+F6xTHiQyGRDqIlFQcE9ZKO +EJ+wuC+MbBFGPSc/Zw43/twpzQKBgQDkNGHiuoGvoSuRPpMIB82IsMmtOlzymk8B +ZZMNzHfxFyf7a/1NUhc3UvmZdE67MS4cmoY0LCY4HBW2zxxsnX0cA+vRNLFneJMC +oH2XgQs+mi1Dgem+N6EYO/5PJZqeyUJ5x5WFGrULvsL2GKcSeSp2o2nkFyKSHfhD +7zWSQyNICwKBgQD1lDZD4n3+BxFSndAMnUnbSuQgLPrRq9xNRpeh+piVWl1R4zlj +e7X+YsJ4pMVcZK2VhPGK84IKtekUgSJ0mqIULJ6qqnkmyKtNlyOdqwaFLt+yNXO9 +hlRgjE/e9aGr7M90GCKngQ5Q7t4PVWmJnlunHZceW4EXDh217qz8WRkIeQKBgGV0 +JFB4Ok+qh4P7HcLkNSwf7Ilm+QuiLp2gWtA3pts4QD42tFY7uLaP3Qer/ZSbOLTe +vetT9WnckorDaQ+gtI5P7/cCRhyKLlFsqGlCpY0fXiA1EYXPlX8ArP7i6OrO7w7U +/FRAm1ytYl+mdiBwXcCAxgLxhh0P1d/d6SMtVfIhAoGBALdQ9tPSkFsJiDJi+d1u +SwPauzmr8O0geaLuey9WxVAxm5DR7eTJTUswZJGqadAZlnDr/H+7YU6lEBRI224Q +0ApgIIeGpMsTZFetRsOq+TUoNQcGVCtspOckCbElW6NMSzuAlf7aI+VqKM3uwlAn +FiTDYcmZIE1yNjYi+uaD8vkU +-----END PRIVATE KEY----- +OCI_API_KEY + +oci kr +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCXpRaddq1RzS0J +cBAGHh94DJyqOBti+ASs6Abv1v3gyXu0ssNSe/g/ZBe+aDIIBisL/5nRLDZfeNZg +S5azKgPFPPlukTKVAfatQrgsaCC3uNm4D0NfGSINY90HcpZwX5GCZbUIFZuBZBac +33ze+RXoEo/QJFqniiUh/19A1R6mO2oJchgcXYv3+Fe8db35Fb9HDt6tg8+maO5R +8W149SpQUlU9JWCWuRV2cdKSXP1/z3JH3bq7hezD5qkZrcwt+4e+Pj9moYyA+O8i +n9Z9yQUeipp+HR3SdM++pzUhc1E/NNrgNTKQJi3OvrUx41jc00/5KwZUYyOtvJa3 +/GBzycKNAgMBAAECggEAFiooGw3cmWc+3PFHNk2y1c4qG+slfZq4vDkRwn6PDwsE +DM5QJD9AcquDmO4L2gZkxlUuu1cV/3BfDSYfOcK7WFnoL1QDq6nkz0BAQSVbGt9m +2zNH6p92zbQ5+zuxZ21gjEmnYy4dU5U4hOdZjhGkNQ55fLfDlFdpxAVae9RqrWsp +8/9SiuD1G+fgNKbe8x+ASd8Y4rP9gjItDUCqmi2rnJJsR8i1PHMZiLC0S3FbcUo6 +R7kwO0dGaXYFWtPUlMB/OPx172HM6yfE+lDmT6gKfWJ2dOKvu7mOtXTGSBIGMREV +MpC9ZhYBsk3jvZKlEju0sEATg3tdDBeX3xRNOkPFFQKBgQDVrmuAOS/Xrz5ZGzwl +nRzgtR02EjdQ+wJrfyJoufmJWZQlC6cAe3RyCBs8c7YiSyvTL773ZW/v9RoVln0R +WDaEdw7L+YLjdv51Dzy3KVY9A4HX1s+xhP5zG2C24Rp5ALwfS6Qf2JDGtAlm821J +z5kwEcY2sSOxGqL8MTA9gysmqwKBgQC1rXBNsW4BVhbqOXMC6ZpwQ/n6BEdHGl8g +1nNonznnZ6ECI/G/TBYA5Pb23FslpQAaFaYgg6pY1FisnIVUpjk23xKd193zY7P4 +8Ah7D/gcAtloXeJ6sknaWjkBBYRRyZhlG3M428kKoIgxQveokGZiLvF4xB9/NBM3 +oCepbq6bpwKBgQC8KWFEghcdCJYQhSkLvjQls5bLfHL1fnN9EXDNY6bXSehoTsB6 +bjv2BillrEcgH62xxAOXet19IgocJG5xjYpET0raVxbpEmmzzv0aFO55v9Lgq6os +mf4ugldB8ysKjpkZvdQCrwOd1f/JhmYgbwxoBd7TXl0doWUQSog+QnkHDQKBgDMd +jDZf0GqR1TqrVT+hiDFD/uYoJAHOWqt7itcJzZnc30Eh6dd/ycUQpqeIEiECTogI +RUhqoxgBDr3p/910My7MDonYfXsIN0+4ATrWoGEJMDAcEiehWAQWVGmEKtl0FeuE +kKOTuvnBdvAdPl7v2c6QFKJ807vPZATHi8ExAfGLAoGBALuFCz/9Xlw5legmGJxP +IgbhcmSFCw9OmGRA6KdRZUN+Zsb6FVj9eCcF1My13iw359xFaYdhBD6hnbPIe3XS +bzVMczdiuRAI9LijXhzGWmw5hlkumaVDqZI3+Sy5lohLhOsV4Erss1vqL3R40zjk +fk2tnbktORYd6/Q0i0FJdO/H +-----END PRIVATE KEY----- +OCI_API_KEY \ No newline at end of file diff --git a/mgmt.sh b/mgmt.sh new file mode 100755 index 0000000..a5e3701 --- /dev/null +++ b/mgmt.sh @@ -0,0 +1,162 @@ +#!/bin/bash + +# 项目管理主脚本 +set -euo pipefail + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# 项目根目录 +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# 显示项目状态 +show_status() { + log_info "=== 项目状态总览 ===" + echo "" + + # Docker Swarm 状态 + log_info "Docker Swarm 状态:" + if docker info --format '{{.Swarm.LocalNodeState}}' 2>/dev/null | grep -q "active"; then + log_success "✓ Docker Swarm 已激活" + docker node ls 2>/dev/null | head -n 5 + else + log_warning "✗ Docker Swarm 未激活" + fi + echo "" + + # OpenTofu 状态 + log_info "OpenTofu 状态:" + if command -v tofu &> /dev/null; then + local version=$(tofu version | head -n1) + log_success "✓ OpenTofu 已安装: $version" + else + log_warning "✗ OpenTofu 未安装" + fi + echo "" + + # 部署的堆栈 + log_info "已部署的 Docker Stack:" + if docker info --format '{{.Swarm.LocalNodeState}}' 2>/dev/null | grep -q "active"; then + docker stack ls 2>/dev/null || log_warning "无堆栈部署" + else + log_warning "Swarm 未激活,无法查看堆栈" + fi + echo "" +} + +# 快速部署 +quick_deploy() { + log_info "=== 快速部署 ===" + + # 检查 Swarm + if ! docker info --format '{{.Swarm.LocalNodeState}}' 2>/dev/null | grep -q "active"; then + log_info "初始化 Docker Swarm..." + "${PROJECT_ROOT}/swarm/scripts/swarm-manager.sh" init + fi + + # 部署 Traefik + log_info "部署 Traefik 反向代理..." + "${PROJECT_ROOT}/swarm/scripts/swarm-manager.sh" deploy traefik "${PROJECT_ROOT}/swarm/stacks/traefik-swarm-stack.yml" + + # 等待 Traefik 启动 + log_info "等待 Traefik 启动..." + sleep 10 + + # 部署示例服务 + log_info "部署示例服务..." + "${PROJECT_ROOT}/swarm/scripts/swarm-manager.sh" deploy demo "${PROJECT_ROOT}/swarm/stacks/demo-services-stack.yml" + + log_success "快速部署完成!" + echo "" + log_info "访问地址:" + echo " - Traefik Dashboard: http://localhost:8080" + echo " - 示例应用: 请查看 demo 堆栈的服务配置" +} + +# 清理环境 +cleanup() { + log_info "=== 清理环境 ===" + + # 停止所有堆栈 + log_info "停止所有 Docker Stack..." + docker stack ls --format "{{.Name}}" 2>/dev/null | while read -r stack; do + if [[ -n "$stack" ]]; then + log_info "删除堆栈: $stack" + docker stack rm "$stack" + fi + done + + # 等待服务清理 + log_info "等待服务清理..." + sleep 5 + + log_success "环境清理完成" +} + +# 显示帮助 +show_help() { + echo "项目管理脚本" + echo "" + echo "用法: $0 [命令]" + echo "" + echo "命令:" + echo " status - 显示项目状态总览" + echo " deploy - 快速部署所有服务" + echo " cleanup - 清理所有部署的服务" + echo " swarm - 打开 Swarm 管理工具" + echo " tofu - 打开 OpenTofu 管理工具" + echo " help - 显示此帮助信息" + echo "" + echo "子工具:" + echo " ./swarm/scripts/swarm-manager.sh - Docker Swarm 管理" + echo " ./scripts/setup/setup-opentofu.sh - OpenTofu 设置" + echo "" +} + +# 主函数 +main() { + cd "$PROJECT_ROOT" + + case "${1:-help}" in + "status") + show_status + ;; + "deploy") + quick_deploy + ;; + "cleanup") + cleanup + ;; + "swarm") + exec "${PROJECT_ROOT}/swarm/scripts/swarm-manager.sh" "${@:2}" + ;; + "tofu") + exec "${PROJECT_ROOT}/scripts/setup/setup-opentofu.sh" "${@:2}" + ;; + "help"|*) + show_help + ;; + esac +} + +main "$@" \ No newline at end of file diff --git a/scripts/setup/setup-opentofu.sh b/scripts/setup/setup-opentofu.sh new file mode 100755 index 0000000..b115ad3 --- /dev/null +++ b/scripts/setup/setup-opentofu.sh @@ -0,0 +1,174 @@ +#!/bin/bash + +# OpenTofu 设置脚本 +set -euo pipefail + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# 日志函数 +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# 检查 OpenTofu 是否已安装 +check_opentofu() { + log_info "检查 OpenTofu 安装状态..." + + if command -v tofu &> /dev/null; then + local version=$(tofu version | head -n1) + log_success "OpenTofu 已安装: $version" + return 0 + else + log_error "OpenTofu 未安装" + return 1 + fi +} + +# 检查配置文件 +check_config() { + log_info "检查配置文件..." + + local config_file="tofu/environments/dev/terraform.tfvars" + + if [[ ! -f "$config_file" ]]; then + log_error "配置文件不存在: $config_file" + log_info "请复制 terraform.tfvars.example 并填入实际配置" + return 1 + fi + + # 检查是否包含示例值 + if grep -q "your_tenancy_id_here\|your_user_id_here\|your:key:fingerprint:here" "$config_file"; then + log_warning "配置文件包含示例值,请填入实际的 Oracle Cloud 配置" + log_info "需要配置以下项目:" + echo " - tenancy_ocid: Oracle Cloud 租户 OCID" + echo " - user_ocid: 用户 OCID" + echo " - fingerprint: API 密钥指纹" + echo " - private_key_path: 私钥文件路径" + echo " - compartment_ocid: 区间 OCID" + return 1 + fi + + log_success "配置文件检查通过" + return 0 +} + +# 初始化 OpenTofu +init_opentofu() { + log_info "初始化 OpenTofu..." + + cd tofu/environments/dev + + # 清理旧的状态文件 + if [[ -d ".terraform" ]]; then + log_info "清理旧的 .terraform 目录..." + rm -rf .terraform + fi + + # 初始化 + if tofu init; then + log_success "OpenTofu 初始化成功" + else + log_error "OpenTofu 初始化失败" + return 1 + fi + + cd - > /dev/null +} + +# 验证配置 +validate_config() { + log_info "验证 OpenTofu 配置..." + + cd tofu/environments/dev + + if tofu validate; then + log_success "配置验证通过" + else + log_error "配置验证失败" + return 1 + fi + + cd - > /dev/null +} + +# 生成计划 +plan_infrastructure() { + log_info "生成基础设施计划..." + + cd tofu/environments/dev + + if tofu plan -var-file="terraform.tfvars" -out=tfplan; then + log_success "计划生成成功" + log_info "计划文件已保存为 tfplan" + else + log_error "计划生成失败" + return 1 + fi + + cd - > /dev/null +} + +# 显示帮助信息 +show_help() { + echo "OpenTofu 设置脚本" + echo "" + echo "用法: $0 [选项]" + echo "" + echo "选项:" + echo " init - 初始化 OpenTofu" + echo " validate - 验证配置" + echo " plan - 生成执行计划" + echo " check - 检查环境和配置" + echo " help - 显示此帮助信息" + echo "" + echo "示例:" + echo " $0 check # 检查环境" + echo " $0 init # 初始化项目" + echo " $0 plan # 生成计划" +} + +# 主函数 +main() { + case "${1:-help}" in + "check") + check_opentofu + check_config + ;; + "init") + check_opentofu || exit 1 + check_config || exit 1 + init_opentofu + ;; + "validate") + validate_config + ;; + "plan") + check_opentofu || exit 1 + check_config || exit 1 + plan_infrastructure + ;; + "help"|*) + show_help + ;; + esac +} + +# 运行主函数 +main "$@" \ No newline at end of file diff --git a/scripts/utilities/consul-secrets-manager.sh b/scripts/utilities/consul-secrets-manager.sh new file mode 100755 index 0000000..f317eec --- /dev/null +++ b/scripts/utilities/consul-secrets-manager.sh @@ -0,0 +1,228 @@ +#!/bin/bash + +# Consul 密钥管理脚本 +# 用于安全地管理 Oracle Cloud 和其他云服务商的敏感配置 + +set -euo pipefail + +# 配置 +CONSUL_ADDR="${CONSUL_ADDR:-http://localhost:8500}" +CONSUL_TOKEN="${CONSUL_TOKEN:-}" +ENVIRONMENT="${ENVIRONMENT:-dev}" + +# 颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# 日志函数 +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# 检查 Consul 连接 +check_consul() { + log_info "检查 Consul 连接..." + if ! curl -s "${CONSUL_ADDR}/v1/status/leader" > /dev/null; then + log_error "无法连接到 Consul: ${CONSUL_ADDR}" + exit 1 + fi + log_success "Consul 连接正常" +} + +# 设置 Oracle Cloud 配置 +set_oracle_config() { + log_info "设置 Oracle Cloud 配置..." + + echo "请输入 Oracle Cloud 配置信息:" + + read -p "租户 OCID: " tenancy_ocid + read -p "用户 OCID: " user_ocid + read -p "API 密钥指纹: " fingerprint + read -p "私钥文件路径: " private_key_path + read -p "区间 OCID: " compartment_ocid + + # 验证私钥文件是否存在 + if [[ ! -f "$private_key_path" ]]; then + log_error "私钥文件不存在: $private_key_path" + exit 1 + fi + + # 读取私钥内容 + private_key_content=$(cat "$private_key_path") + + # 存储到 Consul + local base_path="config/${ENVIRONMENT}/oracle" + + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/tenancy_ocid" -d "$tenancy_ocid" > /dev/null + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/user_ocid" -d "$user_ocid" > /dev/null + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/fingerprint" -d "$fingerprint" > /dev/null + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/private_key" -d "$private_key_content" > /dev/null + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/compartment_ocid" -d "$compartment_ocid" > /dev/null + + log_success "Oracle Cloud 配置已存储到 Consul" +} + +# 获取 Oracle Cloud 配置 +get_oracle_config() { + log_info "从 Consul 获取 Oracle Cloud 配置..." + + local base_path="config/${ENVIRONMENT}/oracle" + + echo "Oracle Cloud 配置:" + echo "租户 OCID: $(curl -s "${CONSUL_ADDR}/v1/kv/${base_path}/tenancy_ocid?raw" 2>/dev/null || echo "未设置")" + echo "用户 OCID: $(curl -s "${CONSUL_ADDR}/v1/kv/${base_path}/user_ocid?raw" 2>/dev/null || echo "未设置")" + echo "指纹: $(curl -s "${CONSUL_ADDR}/v1/kv/${base_path}/fingerprint?raw" 2>/dev/null || echo "未设置")" + echo "区间 OCID: $(curl -s "${CONSUL_ADDR}/v1/kv/${base_path}/compartment_ocid?raw" 2>/dev/null || echo "未设置")" + echo "私钥: $(curl -s "${CONSUL_ADDR}/v1/kv/${base_path}/private_key?raw" 2>/dev/null | head -1 || echo "未设置")" +} + +# 删除 Oracle Cloud 配置 +delete_oracle_config() { + log_warning "删除 Oracle Cloud 配置..." + + read -p "确定要删除所有 Oracle Cloud 配置吗?(y/N): " confirm + if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + log_info "操作已取消" + return + fi + + local base_path="config/${ENVIRONMENT}/oracle" + + curl -s -X DELETE "${CONSUL_ADDR}/v1/kv/${base_path}?recurse" > /dev/null + + log_success "Oracle Cloud 配置已删除" +} + +# 生成 Terraform 变量文件 +generate_terraform_vars() { + log_info "生成 Terraform 变量文件..." + + local base_path="config/${ENVIRONMENT}/oracle" + local output_file="infrastructure/environments/${ENVIRONMENT}/terraform.tfvars.consul" + + # 从 Consul 获取配置 + local tenancy_ocid=$(curl -s "${CONSUL_ADDR}/v1/kv/${base_path}/tenancy_ocid?raw" 2>/dev/null || echo "") + local user_ocid=$(curl -s "${CONSUL_ADDR}/v1/kv/${base_path}/user_ocid?raw" 2>/dev/null || echo "") + local fingerprint=$(curl -s "${CONSUL_ADDR}/v1/kv/${base_path}/fingerprint?raw" 2>/dev/null || echo "") + local compartment_ocid=$(curl -s "${CONSUL_ADDR}/v1/kv/${base_path}/compartment_ocid?raw" 2>/dev/null || echo "") + + if [[ -z "$tenancy_ocid" ]]; then + log_error "Consul 中没有找到 Oracle Cloud 配置" + exit 1 + fi + + # 创建临时私钥文件 + local temp_key_file="/tmp/oci_private_key_${ENVIRONMENT}.pem" + curl -s "${CONSUL_ADDR}/v1/kv/${base_path}/private_key?raw" > "$temp_key_file" + chmod 600 "$temp_key_file" + + # 生成 Terraform 变量文件 + cat > "$output_file" << EOF +# 从 Consul 生成的 Oracle Cloud 配置 +# 生成时间: $(date) +# 环境: ${ENVIRONMENT} + +oci_config = { + tenancy_ocid = "$tenancy_ocid" + user_ocid = "$user_ocid" + fingerprint = "$fingerprint" + private_key_path = "$temp_key_file" + region = "ap-seoul-1" + compartment_ocid = "$compartment_ocid" +} +EOF + + log_success "Terraform 变量文件已生成: $output_file" + log_warning "私钥文件位置: $temp_key_file" + log_warning "请在使用完毕后删除临时私钥文件" +} + +# 清理临时文件 +cleanup_temp_files() { + log_info "清理临时文件..." + + rm -f /tmp/oci_private_key_*.pem + rm -f infrastructure/environments/*/terraform.tfvars.consul + + log_success "临时文件已清理" +} + +# 显示帮助信息 +show_help() { + cat << EOF +Consul 密钥管理脚本 + +用法: $0 [选项] + +选项: + set-oracle 设置 Oracle Cloud 配置到 Consul + get-oracle 从 Consul 获取 Oracle Cloud 配置 + delete-oracle 从 Consul 删除 Oracle Cloud 配置 + generate-vars 从 Consul 生成 Terraform 变量文件 + cleanup 清理临时文件 + help 显示此帮助信息 + +环境变量: + CONSUL_ADDR Consul 地址 (默认: http://localhost:8500) + CONSUL_TOKEN Consul ACL Token (可选) + ENVIRONMENT 环境名称 (默认: dev) + +示例: + # 设置 Oracle Cloud 配置 + $0 set-oracle + + # 生成 Terraform 变量文件 + $0 generate-vars + + # 查看配置 + $0 get-oracle + + # 清理临时文件 + $0 cleanup +EOF +} + +# 主函数 +main() { + case "${1:-help}" in + "set-oracle") + check_consul + set_oracle_config + ;; + "get-oracle") + check_consul + get_oracle_config + ;; + "delete-oracle") + check_consul + delete_oracle_config + ;; + "generate-vars") + check_consul + generate_terraform_vars + ;; + "cleanup") + cleanup_temp_files + ;; + "help"|*) + show_help + ;; + esac +} + +main "$@" \ No newline at end of file diff --git a/scripts/utilities/terraform-consul-provider.sh b/scripts/utilities/terraform-consul-provider.sh new file mode 100755 index 0000000..511696a --- /dev/null +++ b/scripts/utilities/terraform-consul-provider.sh @@ -0,0 +1,311 @@ +#!/bin/bash + +# Terraform Consul Provider 配置脚本 +# 用于配置 Terraform 从 Consul 读取敏感配置 + +set -euo pipefail + +ENVIRONMENT="${ENVIRONMENT:-dev}" +CONSUL_ADDR="${CONSUL_ADDR:-http://localhost:8500}" + +# 颜色输出 +GREEN='\033[0;32m' +BLUE='\033[0;34m' +NC='\033[0m' + +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +# 创建 Terraform Consul Provider 配置 +create_consul_provider() { + local tf_dir="infrastructure/environments/${ENVIRONMENT}" + + log_info "创建 Terraform Consul Provider 配置..." + + cat > "${tf_dir}/consul-provider.tf" << 'EOF' +# Consul Provider 配置 +terraform { + required_providers { + consul = { + source = "hashicorp/consul" + version = "~> 2.18" + } + } +} + +provider "consul" { + address = var.consul_config.address + token = lookup(var.consul_config, "token", null) +} + +# 从 Consul 读取 Oracle Cloud 配置 +data "consul_keys" "oracle_config" { + key { + name = "tenancy_ocid" + path = "config/${var.environment}/oracle/tenancy_ocid" + } + + key { + name = "user_ocid" + path = "config/${var.environment}/oracle/user_ocid" + } + + key { + name = "fingerprint" + path = "config/${var.environment}/oracle/fingerprint" + } + + key { + name = "private_key" + path = "config/${var.environment}/oracle/private_key" + } + + key { + name = "compartment_ocid" + path = "config/${var.environment}/oracle/compartment_ocid" + } +} + +# 创建临时私钥文件 +resource "local_file" "oci_private_key" { + content = data.consul_keys.oracle_config.var.private_key + filename = "/tmp/oci_private_key_${var.environment}.pem" + file_permission = "0600" + + lifecycle { + ignore_changes = [content] + } +} + +# 本地变量,用于构建完整的 OCI 配置 +locals { + oci_config_from_consul = { + tenancy_ocid = data.consul_keys.oracle_config.var.tenancy_ocid + user_ocid = data.consul_keys.oracle_config.var.user_ocid + fingerprint = data.consul_keys.oracle_config.var.fingerprint + private_key_path = local_file.oci_private_key.filename + region = var.oci_config.region + compartment_ocid = data.consul_keys.oracle_config.var.compartment_ocid + } +} +EOF + + log_success "Consul Provider 配置已创建: ${tf_dir}/consul-provider.tf" +} + +# 创建变量定义文件 +create_variables() { + local tf_dir="infrastructure/environments/${ENVIRONMENT}" + + log_info "更新 Terraform 变量定义..." + + cat > "${tf_dir}/variables.tf" << 'EOF' +# 基本变量 +variable "environment" { + description = "环境名称" + type = string +} + +variable "project_name" { + description = "项目名称" + type = string +} + +variable "owner" { + description = "项目所有者" + type = string +} + +variable "cloud_providers" { + description = "要启用的云服务商" + type = list(string) + default = [] +} + +variable "vpc_cidr" { + description = "VPC CIDR 块" + type = string +} + +variable "availability_zones" { + description = "可用区列表" + type = list(string) +} + +variable "common_tags" { + description = "通用标签" + type = map(string) + default = {} +} + +# Consul 配置 +variable "consul_config" { + description = "Consul 配置" + type = object({ + address = string + token = optional(string) + }) +} + +# Oracle Cloud 配置(基本信息) +variable "oci_config" { + description = "Oracle Cloud 基本配置" + type = object({ + region = string + tenancy_ocid = optional(string, "FROM_CONSUL") + user_ocid = optional(string, "FROM_CONSUL") + fingerprint = optional(string, "FROM_CONSUL") + private_key_path = optional(string, "FROM_CONSUL") + compartment_ocid = optional(string, "FROM_CONSUL") + }) +} + +# 其他云服务商配置 +variable "huawei_config" { + description = "华为云配置" + type = object({ + access_key = string + secret_key = string + region = string + project_id = string + }) + default = { + access_key = "" + secret_key = "" + region = "cn-north-4" + project_id = "" + } +} + +variable "gcp_config" { + description = "Google Cloud 配置" + type = object({ + project_id = string + region = string + zone = string + credentials_file = string + }) + default = { + project_id = "" + region = "asia-northeast3" + zone = "asia-northeast3-a" + credentials_file = "" + } +} + +variable "aws_config" { + description = "AWS 配置" + type = object({ + region = string + access_key = string + secret_key = string + }) + default = { + region = "ap-northeast-2" + access_key = "" + secret_key = "" + } +} + +variable "do_config" { + description = "DigitalOcean 配置" + type = object({ + token = string + region = string + }) + default = { + token = "" + region = "sgp1" + } +} +EOF + + log_success "变量定义已更新: ${tf_dir}/variables.tf" +} + +# 创建示例 main.tf +create_main_tf() { + local tf_dir="infrastructure/environments/${ENVIRONMENT}" + + log_info "创建示例 main.tf..." + + cat > "${tf_dir}/main.tf" << 'EOF' +# 主要 Terraform 配置文件 + +terraform { + required_version = ">= 1.0" + + required_providers { + oci = { + source = "oracle/oci" + version = "~> 5.0" + } + } +} + +# Oracle Cloud Provider +provider "oci" { + tenancy_ocid = local.oci_config_from_consul.tenancy_ocid + user_ocid = local.oci_config_from_consul.user_ocid + fingerprint = local.oci_config_from_consul.fingerprint + private_key_path = local.oci_config_from_consul.private_key_path + region = local.oci_config_from_consul.region +} + +# 示例:创建 VCN +resource "oci_core_vcn" "main" { + count = contains(var.cloud_providers, "oracle") ? 1 : 0 + compartment_id = local.oci_config_from_consul.compartment_ocid + cidr_block = var.vpc_cidr + display_name = "${var.project_name}-${var.environment}-vcn" + + freeform_tags = var.common_tags +} + +# 输出 +output "vcn_id" { + description = "VCN ID" + value = try(oci_core_vcn.main[0].id, null) +} + +output "oci_config_source" { + description = "OCI 配置来源" + value = "consul" +} +EOF + + log_success "示例 main.tf 已创建: ${tf_dir}/main.tf" +} + +# 主函数 +main() { + case "${1:-help}" in + "setup") + create_consul_provider + create_variables + create_main_tf + ;; + "help"|*) + cat << EOF +Terraform Consul Provider 配置脚本 + +用法: $0 [选项] + +选项: + setup 创建 Terraform Consul Provider 配置 + help 显示此帮助信息 + +环境变量: + ENVIRONMENT 环境名称 (默认: dev) + CONSUL_ADDR Consul 地址 (默认: http://localhost:8500) +EOF + ;; + esac +} + +main "$@" \ No newline at end of file diff --git a/scripts/utilities/tofu-secrets-uploader-simple.sh b/scripts/utilities/tofu-secrets-uploader-simple.sh new file mode 100755 index 0000000..2c8f722 --- /dev/null +++ b/scripts/utilities/tofu-secrets-uploader-simple.sh @@ -0,0 +1,128 @@ +#!/bin/bash + +# 简化版 OpenTofu 密钥上传脚本 +set -euo pipefail + +# 配置 +CONSUL_ADDR="${CONSUL_ADDR:-http://master:8500}" +ENVIRONMENT="${ENVIRONMENT:-dev}" +TFVARS_FILE="tofu/environments/${ENVIRONMENT}/terraform.tfvars" + +# 颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +NC='\033[0m' + +log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } +log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +# 检查 Consul 连接 +check_consul() { + log_info "检查 Consul 连接..." + if ! curl -s "${CONSUL_ADDR}/v1/status/leader" > /dev/null; then + log_error "无法连接到 Consul: ${CONSUL_ADDR}" + exit 1 + fi + log_success "Consul 连接正常" +} + +# 上传配置 +upload_configs() { + local uploaded_count=0 + + log_info "开始解析并上传配置..." + + # 直接解析 tfvars 文件 + while IFS= read -r line; do + # 跳过注释和空行 + if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "${line// }" ]]; then + continue + fi + + # 匹配变量赋值 + if [[ "$line" =~ ^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*\"([^\"]*)\"|^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*([^[:space:]#]+) ]]; then + local var_name="${BASH_REMATCH[1]:-${BASH_REMATCH[3]}}" + local var_value="${BASH_REMATCH[2]:-${BASH_REMATCH[4]}}" + + # 跳过空值 + if [[ -z "$var_value" || "$var_value" == "null" ]]; then + continue + fi + + # 确定配置分类和路径 + local consul_path="" + if [[ "$var_name" =~ ^oci_ ]]; then + consul_path="config/${ENVIRONMENT}/oracle/${var_name#oci_}" + elif [[ "$var_name" =~ ^huawei_ ]]; then + consul_path="config/${ENVIRONMENT}/huawei/${var_name#huawei_}" + elif [[ "$var_name" =~ ^aws_ ]]; then + consul_path="config/${ENVIRONMENT}/aws/${var_name#aws_}" + elif [[ "$var_name" =~ ^do_ ]]; then + consul_path="config/${ENVIRONMENT}/digitalocean/${var_name#do_}" + elif [[ "$var_name" =~ ^gcp_ ]]; then + consul_path="config/${ENVIRONMENT}/gcp/${var_name#gcp_}" + else + consul_path="config/${ENVIRONMENT}/general/${var_name}" + fi + + # 上传到 Consul + if curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${consul_path}" -d "$var_value" > /dev/null; then + log_info "上传: ${consul_path}" + ((uploaded_count++)) + else + log_error "上传失败: ${consul_path}" + fi + fi + done < "$TFVARS_FILE" + + log_success "总共上传了 $uploaded_count 个配置项到 Consul" +} + +# 列出配置 +list_configs() { + log_info "列出 Consul 中的配置..." + + local keys=$(curl -s "${CONSUL_ADDR}/v1/kv/config/${ENVIRONMENT}/?keys" | jq -r '.[]' 2>/dev/null || echo "") + + if [[ -z "$keys" ]]; then + log_error "没有找到配置" + return + fi + + echo "=== 环境 ${ENVIRONMENT} 的配置 ===" + echo "$keys" | while read -r key; do + local value=$(curl -s "${CONSUL_ADDR}/v1/kv/${key}?raw" 2>/dev/null || echo "无法读取") + # 隐藏敏感信息 + if [[ "$key" =~ (secret|key|token|password|ocid) ]]; then + echo "$key: [已隐藏]" + else + echo "$key: $value" + fi + done +} + +# 主函数 +main() { + if [[ ! -f "$TFVARS_FILE" ]]; then + log_error "找不到配置文件: $TFVARS_FILE" + exit 1 + fi + + check_consul + + case "${1:-upload}" in + "upload") + upload_configs + ;; + "list") + list_configs + ;; + *) + echo "用法: $0 [upload|list]" + ;; + esac +} + +main "$@" \ No newline at end of file diff --git a/scripts/utilities/tofu-secrets-uploader.sh b/scripts/utilities/tofu-secrets-uploader.sh new file mode 100755 index 0000000..4d2a94d --- /dev/null +++ b/scripts/utilities/tofu-secrets-uploader.sh @@ -0,0 +1,495 @@ +#!/bin/bash + +# OpenTofu 密钥上传脚本 +# 用于将 terraform.tfvars 中的敏感配置批量上传到 Consul + +set -euo pipefail + +# 配置 +CONSUL_ADDR="${CONSUL_ADDR:-http://master:8500}" +CONSUL_TOKEN="${CONSUL_TOKEN:-}" +ENVIRONMENT="${ENVIRONMENT:-dev}" +TOFU_DIR="${TOFU_DIR:-tofu/environments/${ENVIRONMENT}}" +TFVARS_FILE="${TFVARS_FILE:-${TOFU_DIR}/terraform.tfvars}" + +# 颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# 日志函数 +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# 检查依赖 +check_dependencies() { + local deps=("curl" "jq") + for dep in "${deps[@]}"; do + if ! command -v "$dep" &> /dev/null; then + log_error "缺少依赖: $dep" + exit 1 + fi + done +} + +# 检查 Consul 连接 +check_consul() { + log_info "检查 Consul 连接..." + if ! curl -s "${CONSUL_ADDR}/v1/status/leader" > /dev/null; then + log_error "无法连接到 Consul: ${CONSUL_ADDR}" + exit 1 + fi + log_success "Consul 连接正常" +} + +# 检查 tfvars 文件 +check_tfvars_file() { + if [[ ! -f "$TFVARS_FILE" ]]; then + log_error "找不到 terraform.tfvars 文件: $TFVARS_FILE" + exit 1 + fi + log_info "找到配置文件: $TFVARS_FILE" +} + +# 解析 HCL 配置并转换为 JSON +parse_hcl_to_json() { + local tfvars_file="$1" + local temp_tf_file="/tmp/temp_config.tf" + local temp_json_file="/tmp/temp_config.json" + + # 创建临时 .tf 文件,将变量赋值转换为输出 + log_info "解析 HCL 配置..." + + # 读取 tfvars 文件并转换为 output 格式 + cat > "$temp_tf_file" << 'EOF' +# 临时配置文件,用于解析 tfvars +EOF + + # 解析每个配置块 + while IFS= read -r line; do + # 跳过注释和空行 + if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "${line// }" ]]; then + continue + fi + + # 提取变量名和值 + if [[ "$line" =~ ^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*(.+)$ ]]; then + local var_name="${BASH_REMATCH[1]}" + local var_value="${BASH_REMATCH[2]}" + + echo "output \"$var_name\" {" >> "$temp_tf_file" + echo " value = $var_value" >> "$temp_tf_file" + echo "}" >> "$temp_tf_file" + fi + done < "$tfvars_file" + + # 使用 terraform 解析配置 + if command -v terraform &> /dev/null; then + cd "$(dirname "$temp_tf_file")" + terraform init -backend=false > /dev/null 2>&1 || true + terraform output -json > "$temp_json_file" 2>/dev/null || { + log_warning "无法使用 terraform 解析,尝试手动解析..." + manual_parse_tfvars "$tfvars_file" "$temp_json_file" + } + else + log_warning "未找到 terraform,使用手动解析..." + manual_parse_tfvars "$tfvars_file" "$temp_json_file" + fi + + echo "$temp_json_file" +} + +# 手动解析 tfvars 文件 +manual_parse_tfvars() { + local tfvars_file="$1" + local output_file="$2" + + log_info "手动解析 tfvars 文件..." + + # 创建基础 JSON 结构 + echo "{" > "$output_file" + + local first_item=true + local in_block=false + local block_name="" + local block_content="" + + while IFS= read -r line; do + # 跳过注释和空行 + if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "${line// }" ]]; then + continue + fi + + # 检测配置块开始 + if [[ "$line" =~ ^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*\{[[:space:]]*$ ]]; then + block_name="${BASH_REMATCH[1]}" + in_block=true + block_content="" + continue + fi + + # 检测配置块结束 + if [[ "$in_block" == true && "$line" =~ ^[[:space:]]*\}[[:space:]]*$ ]]; then + if [[ "$first_item" == false ]]; then + echo "," >> "$output_file" + fi + echo " \"$block_name\": {" >> "$output_file" + echo "$block_content" >> "$output_file" + echo " }" >> "$output_file" + first_item=false + in_block=false + continue + fi + + # 处理块内容 + if [[ "$in_block" == true ]]; then + if [[ "$line" =~ ^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*\"([^\"]*)\"|^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*([^[:space:]]+) ]]; then + local key="${BASH_REMATCH[1]:-${BASH_REMATCH[3]}}" + local value="${BASH_REMATCH[2]:-${BASH_REMATCH[4]}}" + + if [[ -n "$block_content" ]]; then + block_content+="," + fi + block_content+="\n \"$key\": \"$value\"" + fi + continue + fi + + # 处理简单变量 + if [[ "$line" =~ ^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*\"([^\"]*)\"|^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*([^[:space:]]+) ]]; then + local var_name="${BASH_REMATCH[1]:-${BASH_REMATCH[3]}}" + local var_value="${BASH_REMATCH[2]:-${BASH_REMATCH[4]}}" + + if [[ "$first_item" == false ]]; then + echo "," >> "$output_file" + fi + echo " \"$var_name\": \"$var_value\"" >> "$output_file" + first_item=false + fi + done < "$tfvars_file" + + echo "}" >> "$output_file" +} + +# 上传配置到 Consul +upload_config_to_consul() { + local config_file="$1" + local uploaded_count=0 + + log_info "开始上传配置到 Consul..." + + # 读取 JSON 配置 + if [[ ! -f "$config_file" ]]; then + log_error "配置文件不存在: $config_file" + return 1 + fi + + # 上传 Oracle Cloud 配置 + local oci_tenancy=$(jq -r '.oci_tenancy_ocid // empty' "$config_file") + local oci_user=$(jq -r '.oci_user_ocid // empty' "$config_file") + local oci_fingerprint=$(jq -r '.oci_fingerprint // empty' "$config_file") + local oci_private_key_path=$(jq -r '.oci_private_key_path // empty' "$config_file") + local oci_compartment=$(jq -r '.oci_compartment_ocid // empty' "$config_file") + local oci_region=$(jq -r '.oci_region // empty' "$config_file") + + if [[ -n "$oci_tenancy" && "$oci_tenancy" != "null" && "$oci_tenancy" != "" ]]; then +======= +# 上传配置到 Consul +upload_config_to_consul() { + local config_file="$1" + local uploaded_count=0 + + log_info "开始上传配置到 Consul..." + + # 读取 JSON 配置 + if [[ ! -f "$config_file" ]]; then + log_error "配置文件不存在: $config_file" + return 1 + fi + + # 上传 Oracle Cloud 配置 + local oci_tenancy=$(jq -r '.oci_tenancy_ocid // empty' "$config_file") + local oci_user=$(jq -r '.oci_user_ocid // empty' "$config_file") + local oci_fingerprint=$(jq -r '.oci_fingerprint // empty' "$config_file") + local oci_private_key_path=$(jq -r '.oci_private_key_path // empty' "$config_file") + local oci_compartment=$(jq -r '.oci_compartment_ocid // empty' "$config_file") + local oci_region=$(jq -r '.oci_region // empty' "$config_file") + + if [[ -n "$oci_tenancy" && "$oci_tenancy" != "null" && "$oci_tenancy" != "" ]]; then + log_info "上传 Oracle Cloud 配置..." + local base_path="config/${ENVIRONMENT}/oracle" + + local tenancy_ocid=$(jq -r '.oci_config.tenancy_ocid // empty' "$config_file") + local user_ocid=$(jq -r '.oci_config.user_ocid // empty' "$config_file") + local fingerprint=$(jq -r '.oci_config.fingerprint // empty' "$config_file") + local private_key_path=$(jq -r '.oci_config.private_key_path // empty' "$config_file") + local compartment_ocid=$(jq -r '.oci_config.compartment_ocid // empty' "$config_file") + local region=$(jq -r '.oci_config.region // "ap-seoul-1"' "$config_file") + + # 上传非空配置 + [[ -n "$tenancy_ocid" && "$tenancy_ocid" != "null" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/tenancy_ocid" -d "$tenancy_ocid" > /dev/null + ((uploaded_count++)) + } + [[ -n "$user_ocid" && "$user_ocid" != "null" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/user_ocid" -d "$user_ocid" > /dev/null + ((uploaded_count++)) + } + [[ -n "$fingerprint" && "$fingerprint" != "null" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/fingerprint" -d "$fingerprint" > /dev/null + ((uploaded_count++)) + } + [[ -n "$compartment_ocid" && "$compartment_ocid" != "null" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/compartment_ocid" -d "$compartment_ocid" > /dev/null + ((uploaded_count++)) + } + [[ -n "$region" && "$region" != "null" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/region" -d "$region" > /dev/null + ((uploaded_count++)) + } + + # 上传私钥文件内容 + if [[ -n "$private_key_path" && "$private_key_path" != "null" && -f "$private_key_path" ]]; then + local private_key_content=$(cat "$private_key_path") + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/private_key" -d "$private_key_content" > /dev/null + ((uploaded_count++)) + fi + + log_success "Oracle Cloud 配置已上传" + fi + + # 上传华为云配置 + if jq -e '.huawei_config' "$config_file" > /dev/null 2>&1; then + log_info "上传华为云配置..." + local base_path="config/${ENVIRONMENT}/huawei" + + local access_key=$(jq -r '.huawei_config.access_key // empty' "$config_file") + local secret_key=$(jq -r '.huawei_config.secret_key // empty' "$config_file") + local region=$(jq -r '.huawei_config.region // "cn-north-4"' "$config_file") + local project_id=$(jq -r '.huawei_config.project_id // empty' "$config_file") + + [[ -n "$access_key" && "$access_key" != "null" && "$access_key" != "" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/access_key" -d "$access_key" > /dev/null + ((uploaded_count++)) + } + [[ -n "$secret_key" && "$secret_key" != "null" && "$secret_key" != "" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/secret_key" -d "$secret_key" > /dev/null + ((uploaded_count++)) + } + [[ -n "$region" && "$region" != "null" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/region" -d "$region" > /dev/null + ((uploaded_count++)) + } + [[ -n "$project_id" && "$project_id" != "null" && "$project_id" != "" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/project_id" -d "$project_id" > /dev/null + ((uploaded_count++)) + } + + log_success "华为云配置已上传" + fi + + # 上传 AWS 配置 + if jq -e '.aws_config' "$config_file" > /dev/null 2>&1; then + log_info "上传 AWS 配置..." + local base_path="config/${ENVIRONMENT}/aws" + + local access_key=$(jq -r '.aws_config.access_key // empty' "$config_file") + local secret_key=$(jq -r '.aws_config.secret_key // empty' "$config_file") + local region=$(jq -r '.aws_config.region // "ap-northeast-2"' "$config_file") + + [[ -n "$access_key" && "$access_key" != "null" && "$access_key" != "" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/access_key" -d "$access_key" > /dev/null + ((uploaded_count++)) + } + [[ -n "$secret_key" && "$secret_key" != "null" && "$secret_key" != "" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/secret_key" -d "$secret_key" > /dev/null + ((uploaded_count++)) + } + [[ -n "$region" && "$region" != "null" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/region" -d "$region" > /dev/null + ((uploaded_count++)) + } + + log_success "AWS 配置已上传" + fi + + # 上传 DigitalOcean 配置 + if jq -e '.do_config' "$config_file" > /dev/null 2>&1; then + log_info "上传 DigitalOcean 配置..." + local base_path="config/${ENVIRONMENT}/digitalocean" + + local token=$(jq -r '.do_config.token // empty' "$config_file") + local region=$(jq -r '.do_config.region // "sgp1"' "$config_file") + + [[ -n "$token" && "$token" != "null" && "$token" != "" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/token" -d "$token" > /dev/null + ((uploaded_count++)) + } + [[ -n "$region" && "$region" != "null" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/region" -d "$region" > /dev/null + ((uploaded_count++)) + } + + log_success "DigitalOcean 配置已上传" + fi + + # 上传 Google Cloud 配置 + if jq -e '.gcp_config' "$config_file" > /dev/null 2>&1; then + log_info "上传 Google Cloud 配置..." + local base_path="config/${ENVIRONMENT}/gcp" + + local project_id=$(jq -r '.gcp_config.project_id // empty' "$config_file") + local region=$(jq -r '.gcp_config.region // "asia-northeast3"' "$config_file") + local zone=$(jq -r '.gcp_config.zone // "asia-northeast3-a"' "$config_file") + local credentials_file=$(jq -r '.gcp_config.credentials_file // empty' "$config_file") + + [[ -n "$project_id" && "$project_id" != "null" && "$project_id" != "" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/project_id" -d "$project_id" > /dev/null + ((uploaded_count++)) + } + [[ -n "$region" && "$region" != "null" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/region" -d "$region" > /dev/null + ((uploaded_count++)) + } + [[ -n "$zone" && "$zone" != "null" ]] && { + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/zone" -d "$zone" > /dev/null + ((uploaded_count++)) + } + + # 上传凭证文件内容 + if [[ -n "$credentials_file" && "$credentials_file" != "null" && -f "$credentials_file" ]]; then + local credentials_content=$(cat "$credentials_file") + curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/credentials" -d "$credentials_content" > /dev/null + ((uploaded_count++)) + fi + + log_success "Google Cloud 配置已上传" + fi + + log_success "总共上传了 $uploaded_count 个配置项到 Consul" +} + +# 列出 Consul 中的配置 +list_consul_configs() { + log_info "列出 Consul 中的配置..." + + local base_path="config/${ENVIRONMENT}" + + echo "=== Consul 中的配置 ===" + + # 获取所有配置键 + local keys=$(curl -s "${CONSUL_ADDR}/v1/kv/${base_path}/?keys" | jq -r '.[]' 2>/dev/null || echo "") + + if [[ -z "$keys" ]]; then + log_warning "Consul 中没有找到配置" + return + fi + + echo "$keys" | while read -r key; do + local value=$(curl -s "${CONSUL_ADDR}/v1/kv/${key}?raw" 2>/dev/null || echo "无法读取") + # 隐藏敏感信息 + if [[ "$key" =~ (secret|key|token|password) ]]; then + echo "$key: [已隐藏]" + else + echo "$key: $value" + fi + done +} + +# 清理 Consul 配置 +cleanup_consul_configs() { + log_warning "清理 Consul 配置..." + + read -p "确定要删除环境 '$ENVIRONMENT' 的所有配置吗?(y/N): " confirm + if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + log_info "操作已取消" + return + fi + + local base_path="config/${ENVIRONMENT}" + curl -s -X DELETE "${CONSUL_ADDR}/v1/kv/${base_path}?recurse" > /dev/null + + log_success "环境 '$ENVIRONMENT' 的配置已清理" +} + +# 显示帮助信息 +show_help() { + cat << EOF +OpenTofu 密钥上传脚本 + +用法: $0 [选项] + +选项: + upload 上传 terraform.tfvars 中的配置到 Consul + list 列出 Consul 中的配置 + cleanup 清理 Consul 中的配置 + help 显示此帮助信息 + +环境变量: + CONSUL_ADDR Consul 地址 (默认: http://localhost:8500) + CONSUL_TOKEN Consul ACL Token (可选) + ENVIRONMENT 环境名称 (默认: dev) + TOFU_DIR OpenTofu 目录 (默认: tofu/environments/\${ENVIRONMENT}) + TFVARS_FILE 变量文件路径 (默认: \${TOFU_DIR}/terraform.tfvars) + +示例: + # 上传配置到 Consul + $0 upload + + # 列出 Consul 中的配置 + $0 list + + # 清理配置 + $0 cleanup + + # 指定不同环境 + ENVIRONMENT=production $0 upload +EOF +} + +# 主函数 +main() { + check_dependencies + + case "${1:-help}" in + "upload") + check_consul + check_tfvars_file + + log_info "解析配置文件: $TFVARS_FILE" + local config_json=$(manual_parse_tfvars "$TFVARS_FILE" "/tmp/parsed_config.json") + upload_config_to_consul "/tmp/parsed_config.json" + + # 清理临时文件 + rm -f /tmp/parsed_config.json /tmp/temp_config.tf + ;; + "list") + check_consul + list_consul_configs + ;; + "cleanup") + check_consul + cleanup_consul_configs + ;; + "help"|*) + show_help + ;; + esac +} + +main "$@" \ No newline at end of file diff --git a/containers/infrastructure/traefik-consul-setup.yml b/swarm/configs/traefik-consul-setup.yml similarity index 100% rename from containers/infrastructure/traefik-consul-setup.yml rename to swarm/configs/traefik-consul-setup.yml diff --git a/containers/infrastructure/traefik.yml b/swarm/configs/traefik.yml similarity index 100% rename from containers/infrastructure/traefik.yml rename to swarm/configs/traefik.yml diff --git a/swarm/scripts/swarm-manager.sh b/swarm/scripts/swarm-manager.sh new file mode 100755 index 0000000..573ba98 --- /dev/null +++ b/swarm/scripts/swarm-manager.sh @@ -0,0 +1,184 @@ +#!/bin/bash + +# Docker Swarm 管理脚本 +set -euo pipefail + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# 日志函数 +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# 检查是否在 Swarm 模式 +check_swarm_mode() { + if docker info --format '{{.Swarm.LocalNodeState}}' | grep -q "active"; then + log_success "Docker Swarm 模式已激活" + return 0 + else + log_error "Docker Swarm 模式未激活" + return 1 + fi +} + +# 初始化 Swarm +init_swarm() { + log_info "初始化 Docker Swarm..." + + if docker swarm init; then + log_success "Docker Swarm 初始化成功" + log_info "要添加工作节点,请在其他主机上运行:" + docker swarm join-token worker + else + log_error "Docker Swarm 初始化失败" + return 1 + fi +} + +# 部署堆栈 +deploy_stack() { + local stack_name="$1" + local compose_file="$2" + + log_info "部署堆栈: $stack_name" + + if [[ ! -f "$compose_file" ]]; then + log_error "Compose 文件不存在: $compose_file" + return 1 + fi + + if docker stack deploy -c "$compose_file" "$stack_name"; then + log_success "堆栈 $stack_name 部署成功" + else + log_error "堆栈 $stack_name 部署失败" + return 1 + fi +} + +# 列出堆栈 +list_stacks() { + log_info "当前部署的堆栈:" + docker stack ls +} + +# 查看堆栈服务 +show_stack_services() { + local stack_name="$1" + + log_info "堆栈 $stack_name 的服务:" + docker stack services "$stack_name" +} + +# 删除堆栈 +remove_stack() { + local stack_name="$1" + + log_info "删除堆栈: $stack_name" + + if docker stack rm "$stack_name"; then + log_success "堆栈 $stack_name 删除成功" + else + log_error "堆栈 $stack_name 删除失败" + return 1 + fi +} + +# 显示节点信息 +show_nodes() { + log_info "Swarm 节点信息:" + docker node ls +} + +# 显示帮助信息 +show_help() { + echo "Docker Swarm 管理脚本" + echo "" + echo "用法: $0 [命令] [参数]" + echo "" + echo "命令:" + echo " init - 初始化 Docker Swarm" + echo " deploy - 部署堆栈" + echo " list - 列出所有堆栈" + echo " services - 查看堆栈服务" + echo " remove - 删除堆栈" + echo " nodes - 显示节点信息" + echo " check - 检查 Swarm 状态" + echo " help - 显示此帮助信息" + echo "" + echo "示例:" + echo " $0 init # 初始化 Swarm" + echo " $0 deploy traefik stacks/traefik-swarm-stack.yml" + echo " $0 deploy demo stacks/demo-services-stack.yml" + echo " $0 list # 列出堆栈" + echo " $0 services traefik # 查看 traefik 堆栈服务" +} + +# 主函数 +main() { + case "${1:-help}" in + "init") + init_swarm + ;; + "deploy") + if [[ $# -lt 3 ]]; then + log_error "部署命令需要堆栈名称和 compose 文件" + echo "用法: $0 deploy " + exit 1 + fi + check_swarm_mode || exit 1 + deploy_stack "$2" "$3" + ;; + "list") + check_swarm_mode || exit 1 + list_stacks + ;; + "services") + if [[ $# -lt 2 ]]; then + log_error "需要指定堆栈名称" + echo "用法: $0 services " + exit 1 + fi + check_swarm_mode || exit 1 + show_stack_services "$2" + ;; + "remove") + if [[ $# -lt 2 ]]; then + log_error "需要指定堆栈名称" + echo "用法: $0 remove " + exit 1 + fi + check_swarm_mode || exit 1 + remove_stack "$2" + ;; + "nodes") + check_swarm_mode || exit 1 + show_nodes + ;; + "check") + check_swarm_mode + ;; + "help"|*) + show_help + ;; + esac +} + +# 运行主函数 +main "$@" \ No newline at end of file diff --git a/swarm/stacks/consul-ash3c-stack.yml b/swarm/stacks/consul-ash3c-stack.yml new file mode 100644 index 0000000..2ac88e3 --- /dev/null +++ b/swarm/stacks/consul-ash3c-stack.yml @@ -0,0 +1,41 @@ +version: '3.8' + +services: + consul: + image: consul:latest + hostname: consul-ash3c + command: > + sh -c " + IP=$$(hostname -i | awk '{print $$1}'); + consul agent -server -bootstrap-expect=2 + -datacenter=dc1 -data-dir=/consul/data + -node=consul-ash3c -bind=$$IP -advertise=100.116.80.94 -client=0.0.0.0 + -retry-join=100.117.106.136 + -ui + -log-level=INFO + " + ports: + - "8500:8500" + - "8600:8600/udp" + volumes: + - consul_data:/consul/data + networks: + - consul-net + deploy: + mode: replicated + replicas: 1 + placement: + constraints: + - node.hostname == ash3c + restart_policy: + condition: on-failure + delay: 5s + max_attempts: 3 + +volumes: + consul_data: + +networks: + consul-net: + driver: overlay + attachable: true \ No newline at end of file diff --git a/swarm/stacks/consul-cluster-stack.yml b/swarm/stacks/consul-cluster-stack.yml new file mode 100644 index 0000000..d34548f --- /dev/null +++ b/swarm/stacks/consul-cluster-stack.yml @@ -0,0 +1,76 @@ +version: '3.8' + +services: + consul-master: + image: consul:latest + hostname: consul-master + command: > + sh -c " + IP=$$(hostname -i | awk '{print $$1}'); + consul agent -server -bootstrap-expect=2 + -datacenter=dc1 -data-dir=/consul/data + -node=consul-master -bind=$$IP -advertise=$$IP -client=0.0.0.0 + -ui + -log-level=INFO + " + ports: + - "8500:8500" + - "8600:8600/udp" + volumes: + - consul_master_data:/consul/data + networks: + consul-net: + aliases: + - consul-master + deploy: + mode: replicated + replicas: 1 + placement: + constraints: + - node.hostname == master + restart_policy: + condition: on-failure + delay: 5s + max_attempts: 3 + + consul-ash3c: + image: consul:latest + hostname: consul-ash3c + command: > + sh -c " + IP=$$(hostname -i | awk '{print $$1}'); + consul agent -server -bootstrap-expect=2 + -datacenter=dc1 -data-dir=/consul/data + -node=consul-ash3c -bind=$$IP -advertise=$$IP -client=0.0.0.0 + -retry-join=consul-cluster_consul-master + -ui + -log-level=INFO + " + ports: + - "8501:8500" + - "8601:8600/udp" + volumes: + - consul_ash3c_data:/consul/data + networks: + consul-net: + aliases: + - consul-ash3c + deploy: + mode: replicated + replicas: 1 + placement: + constraints: + - node.hostname == ash3c + restart_policy: + condition: on-failure + delay: 5s + max_attempts: 3 + +volumes: + consul_master_data: + consul_ash3c_data: + +networks: + consul-net: + driver: overlay + attachable: true \ No newline at end of file diff --git a/swarm/stacks/consul-master-stack.yml b/swarm/stacks/consul-master-stack.yml new file mode 100644 index 0000000..eadfa92 --- /dev/null +++ b/swarm/stacks/consul-master-stack.yml @@ -0,0 +1,40 @@ +version: '3.8' + +services: + consul: + image: consul:latest + hostname: consul-master + command: > + sh -c " + IP=$$(hostname -i | awk '{print $$1}'); + consul agent -server -bootstrap-expect=2 + -datacenter=dc1 -data-dir=/consul/data + -node=consul-master -bind=$$IP -advertise=100.117.106.136 -client=0.0.0.0 + -ui + -log-level=INFO + " + ports: + - "8500:8500" + - "8600:8600/udp" + volumes: + - consul_data:/consul/data + networks: + - consul-net + deploy: + mode: replicated + replicas: 1 + placement: + constraints: + - node.hostname == master + restart_policy: + condition: on-failure + delay: 5s + max_attempts: 3 + +volumes: + consul_data: + +networks: + consul-net: + driver: overlay + attachable: true \ No newline at end of file diff --git a/swarm/stacks/consul-simple-stack.yml b/swarm/stacks/consul-simple-stack.yml new file mode 100644 index 0000000..cb09f5a --- /dev/null +++ b/swarm/stacks/consul-simple-stack.yml @@ -0,0 +1,39 @@ +version: '3.8' + +services: + consul: + image: consul:latest + hostname: consul + command: > + consul agent -server -bootstrap-expect=1 + -datacenter=dc1 -data-dir=/consul/data + -node=consul -client=0.0.0.0 + -ui + -log-level=INFO + ports: + - "8500:8500" + - "8600:8600/udp" + volumes: + - consul_data:/consul/data + networks: + - consul-net + deploy: + mode: replicated + replicas: 1 + restart_policy: + condition: on-failure + delay: 5s + max_attempts: 3 + labels: + - "traefik.enable=true" + - "traefik.http.routers.consul.rule=Host(`consul.local`)" + - "traefik.http.services.consul.loadbalancer.server.port=8500" + - "traefik.docker.network=consul-net" + +volumes: + consul_data: + +networks: + consul-net: + driver: overlay + attachable: true \ No newline at end of file diff --git a/containers/compose/production/demo-services-stack.yml b/swarm/stacks/demo-services-stack.yml similarity index 100% rename from containers/compose/production/demo-services-stack.yml rename to swarm/stacks/demo-services-stack.yml diff --git a/containers/compose/production/traefik-swarm-stack.yml b/swarm/stacks/traefik-swarm-stack.yml similarity index 100% rename from containers/compose/production/traefik-swarm-stack.yml rename to swarm/stacks/traefik-swarm-stack.yml diff --git a/tofu/environments/dev/main.tf b/tofu/environments/dev/main.tf new file mode 100644 index 0000000..ad7d76c --- /dev/null +++ b/tofu/environments/dev/main.tf @@ -0,0 +1,109 @@ +# 开发环境主配置文件 + +# 引入共享版本配置 +terraform { + required_version = ">= 1.6" + + required_providers { + # Oracle Cloud Infrastructure + oci = { + source = "oracle/oci" + version = "~> 5.0" + } + + # 华为云 + huaweicloud = { + source = "huaweicloud/huaweicloud" + version = "~> 1.60" + } + + # 其他常用提供商 + random = { + source = "hashicorp/random" + version = "~> 3.1" + } + + tls = { + source = "hashicorp/tls" + version = "~> 4.0" + } + + local = { + source = "hashicorp/local" + version = "~> 2.1" + } + } + + # 后端配置 + backend "local" { + path = "terraform.tfstate" + } +} + +# Oracle Cloud 提供商配置 +provider "oci" { + tenancy_ocid = var.oci_config.tenancy_ocid + user_ocid = var.oci_config.user_ocid + fingerprint = var.oci_config.fingerprint + private_key_path = var.oci_config.private_key_path + region = var.oci_config.region +} + +# 华为云提供商配置 (仅在需要时配置) +provider "huaweicloud" { + access_key = var.huawei_config.access_key + secret_key = var.huawei_config.secret_key + region = var.huawei_config.region + alias = "huawei" +} + +# Oracle Cloud 基础设施 +module "oracle_cloud" { + source = "../../providers/oracle-cloud" + + # 传递变量 + environment = var.environment + project_name = var.project_name + owner = var.owner + vpc_cidr = var.vpc_cidr + availability_zones = var.availability_zones + common_tags = var.common_tags + oci_config = var.oci_config + + # 开发环境特定配置 + instance_count = 1 + instance_size = "VM.Standard.E2.1.Micro" # 免费层 + + providers = { + oci = oci + } +} + +# 华为云基础设施 (可选) +module "huawei_cloud" { + source = "../../providers/huawei-cloud" + count = contains(var.cloud_providers, "huawei") ? 1 : 0 + + environment = var.environment + project_name = var.project_name + owner = var.owner + vpc_cidr = "10.1.0.0/16" # 不同的 CIDR 避免冲突 + availability_zones = var.availability_zones + common_tags = var.common_tags + huawei_config = var.huawei_config + + providers = { + huaweicloud = huaweicloud.huawei + } +} + +# 输出 +output "oracle_cloud_outputs" { + description = "Oracle Cloud 基础设施输出" + value = module.oracle_cloud +} + +output "huawei_cloud_outputs" { + description = "华为云基础设施输出" + value = length(module.huawei_cloud) > 0 ? module.huawei_cloud[0] : null +} \ No newline at end of file diff --git a/infrastructure/environments/dev/terraform.tfvars.example b/tofu/environments/dev/terraform.tfvars.example similarity index 100% rename from infrastructure/environments/dev/terraform.tfvars.example rename to tofu/environments/dev/terraform.tfvars.example diff --git a/infrastructure/environments/dev/variables.tf b/tofu/environments/dev/variables.tf similarity index 100% rename from infrastructure/environments/dev/variables.tf rename to tofu/environments/dev/variables.tf diff --git a/infrastructure/providers/huawei-cloud/main.tf b/tofu/providers/huawei-cloud/main.tf similarity index 94% rename from infrastructure/providers/huawei-cloud/main.tf rename to tofu/providers/huawei-cloud/main.tf index a719ef5..83446a5 100644 --- a/infrastructure/providers/huawei-cloud/main.tf +++ b/tofu/providers/huawei-cloud/main.tf @@ -1,4 +1,4 @@ -# 华为云提供商配置 +# 华为云模块 terraform { required_providers { @@ -9,13 +9,6 @@ terraform { } } -# 华为云提供商配置 -provider "huaweicloud" { - access_key = var.huawei_config.access_key - secret_key = var.huawei_config.secret_key - region = var.huawei_config.region -} - # 获取可用区 data "huaweicloud_availability_zones" "zones" {} diff --git a/tofu/providers/huawei-cloud/variables.tf b/tofu/providers/huawei-cloud/variables.tf new file mode 100644 index 0000000..ff866f6 --- /dev/null +++ b/tofu/providers/huawei-cloud/variables.tf @@ -0,0 +1,54 @@ +# 华为云提供商变量定义 + +variable "environment" { + description = "环境名称" + type = string +} + +variable "project_name" { + description = "项目名称" + type = string +} + +variable "owner" { + description = "项目所有者" + type = string +} + +variable "vpc_cidr" { + description = "VPC CIDR 块" + type = string +} + +variable "availability_zones" { + description = "可用区列表" + type = list(string) +} + +variable "common_tags" { + description = "通用标签" + type = map(string) +} + +variable "huawei_config" { + description = "华为云配置" + type = object({ + access_key = string + secret_key = string + region = string + project_id = string + }) + sensitive = true +} + +variable "instance_count" { + description = "实例数量" + type = number + default = 1 +} + +variable "instance_size" { + description = "实例规格" + type = string + default = "s6.small.1" +} \ No newline at end of file diff --git a/infrastructure/providers/oracle-cloud/main.tf b/tofu/providers/oracle-cloud/main.tf similarity index 92% rename from infrastructure/providers/oracle-cloud/main.tf rename to tofu/providers/oracle-cloud/main.tf index 55a644e..bc6d73b 100644 --- a/infrastructure/providers/oracle-cloud/main.tf +++ b/tofu/providers/oracle-cloud/main.tf @@ -1,4 +1,4 @@ -# Oracle Cloud Infrastructure 提供商配置 +# Oracle Cloud Infrastructure 模块 terraform { required_providers { @@ -9,15 +9,6 @@ terraform { } } -# OCI 提供商配置 -provider "oci" { - tenancy_ocid = var.oci_config.tenancy_ocid - user_ocid = var.oci_config.user_ocid - fingerprint = var.oci_config.fingerprint - private_key_path = var.oci_config.private_key_path - region = var.oci_config.region -} - # 获取可用域 data "oci_identity_availability_domains" "ads" { compartment_id = var.oci_config.tenancy_ocid diff --git a/tofu/providers/oracle-cloud/variables.tf b/tofu/providers/oracle-cloud/variables.tf new file mode 100644 index 0000000..5bf2b3f --- /dev/null +++ b/tofu/providers/oracle-cloud/variables.tf @@ -0,0 +1,55 @@ +# Oracle Cloud 提供商变量定义 + +variable "environment" { + description = "环境名称" + type = string +} + +variable "project_name" { + description = "项目名称" + type = string +} + +variable "owner" { + description = "项目所有者" + type = string +} + +variable "vpc_cidr" { + description = "VPC CIDR 块" + type = string +} + +variable "availability_zones" { + description = "可用区列表" + type = list(string) +} + +variable "common_tags" { + description = "通用标签" + type = map(string) +} + +variable "oci_config" { + description = "Oracle Cloud 配置" + type = object({ + tenancy_ocid = string + user_ocid = string + fingerprint = string + private_key_path = string + region = string + compartment_ocid = string + }) +} + +variable "instance_count" { + description = "实例数量" + type = number + default = 1 +} + +variable "instance_size" { + description = "实例规格" + type = string + default = "VM.Standard.E2.1.Micro" +} \ No newline at end of file diff --git a/infrastructure/shared/outputs.tf b/tofu/shared/outputs.tf similarity index 100% rename from infrastructure/shared/outputs.tf rename to tofu/shared/outputs.tf diff --git a/infrastructure/shared/variables.tf b/tofu/shared/variables.tf similarity index 100% rename from infrastructure/shared/variables.tf rename to tofu/shared/variables.tf diff --git a/infrastructure/shared/versions.tf b/tofu/shared/versions.tf similarity index 100% rename from infrastructure/shared/versions.tf rename to tofu/shared/versions.tf