Init commit
This commit is contained in:
51
.gitignore
vendored
Normal file
51
.gitignore
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
# OpenTofu / Terraform
|
||||
**/.terraform/
|
||||
**/.terraform.lock.hcl
|
||||
|
||||
# State
|
||||
**/*.tfstate
|
||||
**/*.tfstate.*
|
||||
**/crash.log
|
||||
**/crash.*.log
|
||||
|
||||
# Variable files with secrets
|
||||
**/*.tfvars
|
||||
**/*.tfvars.json
|
||||
!**/*.tfvars.example
|
||||
|
||||
# Optional local override files
|
||||
**/override.tf
|
||||
**/override.tf.json
|
||||
**/*_override.tf
|
||||
**/*_override.tf.json
|
||||
|
||||
# CLI config / credentials
|
||||
.terraformrc
|
||||
terraform.rc
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Secrets / keys
|
||||
*.pem
|
||||
*.key
|
||||
*.p12
|
||||
*.pfx
|
||||
id_rsa
|
||||
id_rsa.pub
|
||||
id_ed25519
|
||||
id_ed25519.pub
|
||||
|
||||
# Editors / OS
|
||||
.DS_Store
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
notes.md
|
||||
823
README.md
823
README.md
@@ -1,93 +1,800 @@
|
||||
# opentofu-standart-vm
|
||||
# OpenTofu + Proxmox 9.1.7 + Ubuntu Cloud-Init
|
||||
|
||||
---
|
||||
|
||||
## Обучение
|
||||
|
||||
Если хотите разобраться глубже, как это всё работает:
|
||||
|
||||
- Курс по OpenTofu / Terraform:
|
||||
https://stepik.org/a/238385
|
||||
|
||||
---
|
||||
|
||||
## Контакты
|
||||
|
||||
Автор: Юрий Анфиногенов
|
||||
Telegram: https://t.me/uanfinogenov
|
||||
|
||||
---
|
||||
Подробное описание проекта для создания и управления виртуальными машинами в Proxmox через OpenTofu.
|
||||
|
||||
|
||||
Этот вариант документации рассчитан на структуру, где используется одна рабочая директория `cluster/` и один общий модуль `modules/node`.
|
||||
|
||||
## Getting started
|
||||
Документ описывает:
|
||||
|
||||
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
|
||||
* установку OpenTofu
|
||||
* оффлайн установку провайдеров
|
||||
* настройку `~/.tofurc`
|
||||
* подготовку Ubuntu cloud image / template в Proxmox
|
||||
* настройку доступа к Proxmox API
|
||||
* структуру проекта
|
||||
* работу `cloud-init`
|
||||
* fallback логику `default.yml`
|
||||
* использование разных `cloud-init` файлов для разных VM
|
||||
* работу VLAN
|
||||
* практические замечания по отладке
|
||||
|
||||
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
|
||||
---
|
||||
|
||||
## Add your files
|
||||
## Совместимость
|
||||
|
||||
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
|
||||
- [ ] [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command:
|
||||
Проверено в окружении:
|
||||
|
||||
```
|
||||
cd existing_repo
|
||||
git remote add origin http://git.yur811.ru/devtools/opentofu-standart-vm.git
|
||||
git branch -M main
|
||||
git push -uf origin main
|
||||
* Proxmox VE 9.1.7
|
||||
* Ubuntu cloud image (noble / 24.04)
|
||||
* OpenTofu
|
||||
|
||||
Если в вашей среде используются другие версии Proxmox, Ubuntu image или провайдеров, поведение может отличаться.
|
||||
|
||||
---
|
||||
## Важно:
|
||||
- перед использованием необходимо проверить и при необходимости изменить переменные под свою среду
|
||||
- чаще всего отличаются:
|
||||
- datastore (local, local-lvm, ssd и т.д.)
|
||||
- image_datastore и путь к образу
|
||||
- сетевой bridge (например vmbr0)
|
||||
- network_base и gateway
|
||||
- VLAN (если используется)
|
||||
- значения в примере не универсальны и зависят от конкретного Proxmox окружения
|
||||
|
||||
---
|
||||
## Идея проекта
|
||||
|
||||
OpenTofu отвечает за инфраструктуру:
|
||||
|
||||
* создание VM
|
||||
* сеть
|
||||
* диски
|
||||
* cloud-init disk
|
||||
* загрузку cloud-init user-data в Proxmox
|
||||
|
||||
Cloud-init отвечает за конфигурацию ОС внутри VM:
|
||||
|
||||
* пользователи
|
||||
* SSH ключи
|
||||
* hostname
|
||||
* пакеты
|
||||
* systemd сервисы
|
||||
* базовая bootstrap-настройка
|
||||
|
||||
Это ключевой принцип проекта:
|
||||
|
||||
* OpenTofu не должен подробно конфигурировать ОС
|
||||
* cloud-init не должен управлять инфраструктурой Proxmox
|
||||
|
||||
---
|
||||
|
||||
## Структура проекта
|
||||
|
||||
Ожидаемая структура:
|
||||
|
||||
```text
|
||||
.
|
||||
├── README.md
|
||||
├── terraform.tfvars.example
|
||||
├── cluster/
|
||||
│ ├── cloud-config/
|
||||
│ │ └── default.yml
|
||||
│ ├── locals.tf
|
||||
│ ├── main.tf
|
||||
│ ├── outputs.tf
|
||||
│ ├── providers.tf
|
||||
│ ├── terraform.tfvars
|
||||
│ └── variables.tf
|
||||
└── modules/
|
||||
└── node/
|
||||
├── cloud-config/
|
||||
│ └── default.yml
|
||||
├── main.tf
|
||||
├── outputs.tf
|
||||
└── variables.tf
|
||||
```
|
||||
|
||||
## Integrate with your tools
|
||||
Где:
|
||||
|
||||
- [ ] [Set up project integrations](http://git.yur811.ru/devtools/opentofu-standart-vm/-/settings/integrations)
|
||||
* `cluster/` - рабочее окружение
|
||||
* `cluster/cloud-config/` - project-specific cloud-init файлы
|
||||
* `modules/node/` - общий модуль для VM
|
||||
* `modules/node/cloud-config/default.yml` - модульный fallback cloud-init
|
||||
* `terraform.tfvars.example` - пример переменных без секретов
|
||||
|
||||
## Collaborate with your team
|
||||
---
|
||||
|
||||
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
|
||||
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
||||
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
||||
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
||||
- [ ] [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
|
||||
## Как работает cloud-init в этом проекте
|
||||
|
||||
## Test and Deploy
|
||||
Для каждой VM модуль выбирает cloud-init файл по следующему правилу:
|
||||
|
||||
Use the built-in continuous integration in GitLab.
|
||||
1. если у ноды задан параметр `cloudinit`, модуль ищет файл в `cluster/cloud-config/<имя файла>`
|
||||
2. если параметр `cloudinit` не задан, модуль пытается использовать `cluster/cloud-config/default.yml`
|
||||
3. если файла в `cluster/cloud-config/` нет, используется fallback из модуля: `modules/node/cloud-config/default.yml`
|
||||
|
||||
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/)
|
||||
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
||||
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
||||
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
||||
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
||||
Это даёт три уровня конфигурации:
|
||||
|
||||
***
|
||||
* per-VM cloud-init
|
||||
* project default cloud-init
|
||||
* module fallback cloud-init
|
||||
|
||||
# Editing this README
|
||||
---
|
||||
|
||||
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
|
||||
## Пример логики выбора cloud-init
|
||||
|
||||
## Suggestions for a good README
|
||||
Пример в `locals.tf`:
|
||||
|
||||
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
|
||||
```hcl
|
||||
locals {
|
||||
nodes = {
|
||||
worker-1 = {
|
||||
index = 1
|
||||
cpu = 2
|
||||
memory = 2048
|
||||
disk = 20
|
||||
datastore = "ssd2"
|
||||
ip = "192.168.20.101"
|
||||
cloudinit = "worker.yml"
|
||||
}
|
||||
|
||||
## Name
|
||||
Choose a self-explaining name for your project.
|
||||
worker-2 = {
|
||||
index = 2
|
||||
cpu = 2
|
||||
memory = 2048
|
||||
disk = 20
|
||||
datastore = "ssd2"
|
||||
ip = "192.168.20.102"
|
||||
cloudinit = "worker.yml"
|
||||
}
|
||||
|
||||
## Description
|
||||
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
||||
master-1 = {
|
||||
index = 3
|
||||
cpu = 4
|
||||
memory = 4096
|
||||
disk = 40
|
||||
datastore = "ssd2"
|
||||
ip = "192.168.20.110"
|
||||
cloudinit = "master.yml"
|
||||
}
|
||||
# ip:
|
||||
# - опциональный параметр
|
||||
# - если НЕ задан → вычисляется автоматически
|
||||
# - формула:
|
||||
# ${network_base}.${cluster_ip_start + index}
|
||||
test = {
|
||||
index = 5
|
||||
cpu = 1
|
||||
memory = 1024
|
||||
disk = 10
|
||||
datastore = "ssd2"
|
||||
# ip = "192.168.20.130" -> ip не задан, вычисляется автоматически
|
||||
# cloudinit не задан -> будет использован default.yml
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Badges
|
||||
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
|
||||
Поведение:
|
||||
|
||||
## Visuals
|
||||
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
||||
* `worker-1`, `worker-2` -> `cluster/cloud-config/worker.yml`
|
||||
* `master-1` -> `cluster/cloud-config/master.yml`
|
||||
* `test` -> `cluster/cloud-config/default.yml`, а если его нет -> `modules/node/cloud-config/default.yml`
|
||||
|
||||
## Installation
|
||||
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
||||
---
|
||||
|
||||
## Usage
|
||||
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
|
||||
## Установка OpenTofu
|
||||
|
||||
## Support
|
||||
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg
|
||||
sudo install -m 0755 -d /etc/apt/keyrings
|
||||
|
||||
## Roadmap
|
||||
If you have ideas for releases in the future, it is a good idea to list them in the README.
|
||||
curl -fsSL https://get.opentofu.org/opentofu.gpg \
|
||||
| sudo tee /etc/apt/keyrings/opentofu.gpg >/dev/null
|
||||
|
||||
## Contributing
|
||||
State if you are open to contributions and what your requirements are for accepting them.
|
||||
curl -fsSL https://packages.opentofu.org/opentofu/tofu/gpgkey \
|
||||
| sudo gpg --no-tty --batch --dearmor -o /etc/apt/keyrings/opentofu-repo.gpg >/dev/null
|
||||
|
||||
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
|
||||
sudo chmod a+r /etc/apt/keyrings/opentofu.gpg /etc/apt/keyrings/opentofu-repo.gpg
|
||||
|
||||
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
|
||||
echo "deb [signed-by=/etc/apt/keyrings/opentofu.gpg,/etc/apt/keyrings/opentofu-repo.gpg] \
|
||||
https://packages.opentofu.org/opentofu/tofu/any/ any main" \
|
||||
| sudo tee /etc/apt/sources.list.d/opentofu.list >/dev/null
|
||||
|
||||
## Authors and acknowledgment
|
||||
Show your appreciation to those who have contributed to the project.
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y tofu
|
||||
```
|
||||
|
||||
## License
|
||||
For open source projects, say how it is licensed.
|
||||
Проверка:
|
||||
|
||||
## Project status
|
||||
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
||||
```bash
|
||||
tofu version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Установка Golang
|
||||
|
||||
Для части оффлайн-провайдеров требуется Go.
|
||||
|
||||
Установите Golang удобным для вас способом.
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
go version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Настройка оффлайн-провайдеров
|
||||
|
||||
Если OpenTofu должен работать без выхода в интернет, провайдеры нужно положить в локальное зеркало:
|
||||
|
||||
```text
|
||||
~/.terraform.d/plugins/
|
||||
```
|
||||
|
||||
Создание каталогов:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/bpg/proxmox
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/hashicorp/{local,random,tls}
|
||||
```
|
||||
|
||||
Ожидаемая структура:
|
||||
|
||||
```text
|
||||
/home/user/.terraform.d/
|
||||
└── plugins
|
||||
└── registry.opentofu.org
|
||||
├── bpg
|
||||
│ └── proxmox
|
||||
│ ├── 0.101.1
|
||||
│ │ └── linux_amd64
|
||||
│ │ └── terraform-provider-proxmox_v0.101.1
|
||||
│ ├── 0.86.0
|
||||
│ │ └── linux_amd64
|
||||
│ │ └── terraform-provider-proxmox_v0.86.0
|
||||
│ └── 0.87.0
|
||||
│ └── linux_amd64
|
||||
│ └── terraform-provider-proxmox_v0.87.0
|
||||
└── hashicorp
|
||||
├── local
|
||||
│ └── 2.6.1
|
||||
│ └── linux_amd64
|
||||
│ └── terraform-provider-local_v2.6.1
|
||||
├── random
|
||||
│ └── 3.7.2
|
||||
│ └── linux_amd64
|
||||
│ └── terraform-provider-random_v3.7.2
|
||||
└── tls
|
||||
└── 4.1.0
|
||||
└── linux_amd64
|
||||
└── terraform-provider-tls_v4.1.0
|
||||
```
|
||||
|
||||
### Провайдер bpg/proxmox
|
||||
|
||||
Релизы:
|
||||
|
||||
```text
|
||||
https://github.com/bpg/terraform-provider-proxmox/releases
|
||||
```
|
||||
|
||||
Пример для версии `0.86.0`:
|
||||
|
||||
```bash
|
||||
wget https://github.com/bpg/terraform-provider-proxmox/releases/download/v0.86.0/terraform-provider-proxmox_0.86.0_linux_amd64.zip
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/bpg/proxmox/0.86.0/linux_amd64
|
||||
unzip terraform-provider-proxmox_0.86.0_linux_amd64.zip -d /tmp
|
||||
mv /tmp/terraform-provider-proxmox_v0.86.0 \
|
||||
~/.terraform.d/plugins/registry.opentofu.org/bpg/proxmox/0.86.0/linux_amd64/
|
||||
chmod +x ~/.terraform.d/plugins/registry.opentofu.org/bpg/proxmox/0.86.0/linux_amd64/terraform-provider-proxmox_v0.86.0
|
||||
```
|
||||
|
||||
Пояснение:
|
||||
|
||||
* `bpg/proxmox` распространяется как готовый бинарник
|
||||
* его не нужно собирать через `go build`
|
||||
* достаточно скачать ZIP, распаковать и положить бинарник в локальное зеркало
|
||||
|
||||
### Провайдер hashicorp/local
|
||||
|
||||
```bash
|
||||
wget https://github.com/hashicorp/terraform-provider-local/archive/refs/tags/v2.6.1.zip
|
||||
unzip v2.6.1.zip
|
||||
cd terraform-provider-local-2.6.1/
|
||||
go build -o terraform-provider-local .
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/hashicorp/local/2.6.1/linux_amd64
|
||||
mv terraform-provider-local \
|
||||
~/.terraform.d/plugins/registry.opentofu.org/hashicorp/local/2.6.1/linux_amd64/terraform-provider-local_v2.6.1
|
||||
```
|
||||
|
||||
### Провайдер hashicorp/random
|
||||
|
||||
```bash
|
||||
wget https://github.com/hashicorp/terraform-provider-random/archive/refs/tags/v3.7.2.zip
|
||||
unzip v3.7.2.zip
|
||||
cd terraform-provider-random-3.7.2/
|
||||
go build -o terraform-provider-random .
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/hashicorp/random/3.7.2/linux_amd64
|
||||
mv terraform-provider-random \
|
||||
~/.terraform.d/plugins/registry.opentofu.org/hashicorp/random/3.7.2/linux_amd64/terraform-provider-random_v3.7.2
|
||||
```
|
||||
|
||||
### Провайдер hashicorp/tls
|
||||
|
||||
```bash
|
||||
wget https://github.com/hashicorp/terraform-provider-tls/archive/refs/tags/v4.1.0.zip
|
||||
unzip v4.1.0.zip
|
||||
cd terraform-provider-tls-4.1.0/
|
||||
go build -o terraform-provider-tls .
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/hashicorp/tls/4.1.0/linux_amd64
|
||||
mv terraform-provider-tls \
|
||||
~/.terraform.d/plugins/registry.opentofu.org/hashicorp/tls/4.1.0/linux_amd64/terraform-provider-tls_v4.1.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Настройка `~/.tofurc`
|
||||
|
||||
Файл `~/.tofurc` говорит OpenTofu использовать только локальные провайдеры и не пытаться скачивать их из интернета.
|
||||
|
||||
Пример:
|
||||
|
||||
```hcl
|
||||
provider_installation {
|
||||
filesystem_mirror {
|
||||
path = "/home/$USER/.terraform.d/plugins"
|
||||
include = [
|
||||
"registry.opentofu.org/bpg/proxmox",
|
||||
"registry.opentofu.org/hashicorp/local",
|
||||
"registry.opentofu.org/hashicorp/random",
|
||||
"registry.opentofu.org/hashicorp/tls"
|
||||
]
|
||||
}
|
||||
|
||||
direct {
|
||||
exclude = [
|
||||
"registry.opentofu.org/bpg/proxmox",
|
||||
"registry.opentofu.org/hashicorp/local",
|
||||
"registry.opentofu.org/hashicorp/random",
|
||||
"registry.opentofu.org/hashicorp/tls"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
tofu init -reconfigure
|
||||
```
|
||||
|
||||
Ожидаемое поведение:
|
||||
|
||||
* OpenTofu берёт провайдеры из local filesystem mirror
|
||||
* в интернет за ними не выходит
|
||||
|
||||
---
|
||||
|
||||
## Доступ к Proxmox API
|
||||
|
||||
Минимальные права для API token:
|
||||
|
||||
* `Datastore.AllocateSpace`
|
||||
* `VM.Allocate`
|
||||
* `VM.Audit`
|
||||
* `VM.Config.*`
|
||||
|
||||
Пример отдельного файла с credentials:
|
||||
|
||||
```bash
|
||||
vim ~/.pve-creds
|
||||
```
|
||||
|
||||
```bash
|
||||
export PVE_TOKEN_ID="root@pam!tofu"
|
||||
export PVE_TOKEN_SECRET="YOUR_SECRET"
|
||||
export PVE_HOST="192.168.22.5"
|
||||
```
|
||||
|
||||
Загрузка в shell:
|
||||
|
||||
```bash
|
||||
set -a
|
||||
source ~/.pve-creds
|
||||
set +a
|
||||
```
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
curl -k -H "Authorization: PVEAPIToken=${PVE_TOKEN_ID}=${PVE_TOKEN_SECRET}" \
|
||||
https://$PVE_HOST:8006/api2/json/version
|
||||
```
|
||||
В example также показано как можно хранить ключи в файле. Если будете использовать, не забудьте убедится, что файл с переменными в .gitignore.
|
||||
---
|
||||
|
||||
## Подготовка Ubuntu Cloud-Init template в Proxmox
|
||||
|
||||
Нужен именно cloud image и корректно подготовленный template.
|
||||
|
||||
### Скачать cloud image
|
||||
|
||||
```bash
|
||||
wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img -O ubuntu.img
|
||||
```
|
||||
|
||||
### Создать VM под template
|
||||
|
||||
```bash
|
||||
qm create 9001 --name ubuntu-template --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0
|
||||
qm importdisk 9001 ubuntu.img local-lvm
|
||||
qm set 9001 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9001-disk-0
|
||||
qm set 9001 --ide2 local-lvm:cloudinit
|
||||
qm set 9001 --boot c --bootdisk scsi0
|
||||
qm set 9001 --serial0 socket --vga serial0
|
||||
```
|
||||
|
||||
### Превратить VM в template
|
||||
|
||||
```bash
|
||||
qm template 9001
|
||||
```
|
||||
|
||||
Важно:
|
||||
|
||||
* нужен cloud image
|
||||
* нужен cloud-init disk
|
||||
* template должен быть корректно подготовлен
|
||||
|
||||
Если этого нет, cloud-init может не применяться внутри VM.
|
||||
|
||||
---
|
||||
|
||||
## Переменные проекта
|
||||
|
||||
Пример `terraform.tfvars.example`:
|
||||
|
||||
```hcl
|
||||
# Proxmox API endpoint (формат: https://host:port/api2/json)
|
||||
proxmox_endpoint = "https://<IP>:<PORT>/api2/json"
|
||||
|
||||
# ID API token (формат: user@realm!token_name)
|
||||
proxmox_token_id = "terraform@ve!user"
|
||||
|
||||
# Secret API token
|
||||
proxmox_token_secret = "<PROXMOX_TOKEN>"
|
||||
|
||||
# Стартовый VMID
|
||||
worker_vmid_start = 1000
|
||||
|
||||
# Дефолтные ресурсы worker VM
|
||||
worker_cpu = 2
|
||||
worker_memory = 2048
|
||||
worker_disk = 20
|
||||
worker_datastore = "ssd2"
|
||||
|
||||
# Datastore, где лежит cloud image
|
||||
image_datastore = "local"
|
||||
|
||||
# Путь к образу
|
||||
image_file = "import/ubuntu-24.qcow2"
|
||||
|
||||
# Сеть
|
||||
cluster_gateway = "192.168.20.1"
|
||||
network_base = "192.168.20"
|
||||
network_cidr = "24"
|
||||
cluster_ip_start = 0
|
||||
|
||||
# Datastore для дополнительных дисков
|
||||
data_datastore = "data1"
|
||||
```
|
||||
|
||||
Практика:
|
||||
|
||||
* реальный `terraform.tfvars` не коммитить
|
||||
* в git хранить только `terraform.tfvars.example`
|
||||
|
||||
---
|
||||
|
||||
## Пример описания `nodes`
|
||||
|
||||
```hcl
|
||||
# nodes — описание виртуальных машин
|
||||
#
|
||||
# vlan_id:
|
||||
# - опциональный параметр
|
||||
# - если НЕ указан → VM будет в обычной сети (untagged, vmbr0)
|
||||
# - если указан → VM попадет в соответствующий VLAN
|
||||
#
|
||||
# cloudinit:
|
||||
# - опциональный параметр
|
||||
# - указывает имя cloud-init файла для конкретной VM
|
||||
# - файл должен находиться в cluster/cloud-config/<имя>.yml
|
||||
# - если НЕ указан → используется default.yml
|
||||
# - если файл НЕ найден в cluster/cloud-config → используется fallback из модуля
|
||||
|
||||
variable "nodes" {
|
||||
type = map(object({
|
||||
index = number
|
||||
cpu = number
|
||||
memory = number
|
||||
disk = number
|
||||
datastore = string
|
||||
ip_offset = optional(number)
|
||||
ip = optional(string)
|
||||
vmid = optional(number)
|
||||
vlan_id = optional(number)
|
||||
data_disk = optional(number)
|
||||
cloudinit = optional(string)
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## VLAN
|
||||
|
||||
Параметр:
|
||||
|
||||
```hcl
|
||||
vlan_id = 20
|
||||
```
|
||||
|
||||
Поведение:
|
||||
|
||||
* если `vlan_id` не указан -> обычная сеть без тегирования
|
||||
* если `vlan_id` указан -> VM подключается в соответствующий VLAN
|
||||
|
||||
Пример:
|
||||
|
||||
```hcl
|
||||
nodes = {
|
||||
worker-1 = {
|
||||
index = 1
|
||||
cpu = 2
|
||||
memory = 2048
|
||||
disk = 20
|
||||
datastore = "ssd2"
|
||||
ip = "192.168.20.101"
|
||||
vlan_id = 20
|
||||
cloudinit = "worker.yml"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Модульный `default.yml`
|
||||
|
||||
Модульный `default.yml` нужен как безопасный fallback, если проект не передал отдельный cloud-init файл.
|
||||
|
||||
Пример содержимого:
|
||||
|
||||
```yaml
|
||||
#cloud-config
|
||||
|
||||
timezone: Europe/Moscow
|
||||
|
||||
users:
|
||||
- default
|
||||
- name: ubuntu
|
||||
groups: [sudo]
|
||||
shell: /bin/bash
|
||||
lock_passwd: true
|
||||
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
|
||||
ssh_authorized_keys:
|
||||
- ${ssh_key}
|
||||
|
||||
ssh_pwauth: false
|
||||
|
||||
package_update: true
|
||||
|
||||
packages:
|
||||
- qemu-guest-agent
|
||||
|
||||
write_files:
|
||||
- path: /etc/motd
|
||||
content: |
|
||||
Managed by OpenTofu
|
||||
|
||||
runcmd:
|
||||
- systemctl enable --now qemu-guest-agent
|
||||
- systemctl disable --now packagekit
|
||||
- systemctl disable --now ModemManager
|
||||
- systemctl disable --now multipathd
|
||||
- hostnamectl set-hostname ${hostname}
|
||||
|
||||
final_message: "cloud-init finished"
|
||||
```
|
||||
|
||||
Почему именно так:
|
||||
|
||||
* пользователь `ubuntu` доступен по SSH ключу
|
||||
* пароль отключён
|
||||
* `qemu-guest-agent` включается для работы с Proxmox
|
||||
* отключаются лишние сервисы, которые обычно не нужны на серверной VM:
|
||||
|
||||
* `packagekit` - GUI / D-Bus пакетный сервис
|
||||
* `ModemManager` - менеджер USB/LTE модемов
|
||||
* `multipathd` - multipath storage daemon
|
||||
|
||||
---
|
||||
|
||||
## Пример project-specific cloud-init
|
||||
|
||||
Например `cluster/cloud-config/vpn.yml`:
|
||||
|
||||
```yaml
|
||||
#cloud-config
|
||||
|
||||
timezone: Europe/Moscow
|
||||
|
||||
users:
|
||||
- name: user
|
||||
groups: [sudo]
|
||||
shell: /bin/bash
|
||||
lock_passwd: true
|
||||
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
|
||||
ssh_authorized_keys:
|
||||
- ${ssh_key}
|
||||
|
||||
ssh_pwauth: false
|
||||
|
||||
package_update: true
|
||||
|
||||
packages:
|
||||
- qemu-guest-agent
|
||||
- wireguard
|
||||
- curl
|
||||
|
||||
write_files:
|
||||
- path: /etc/motd
|
||||
content: |
|
||||
VPN node managed by OpenTofu
|
||||
|
||||
runcmd:
|
||||
- systemctl enable --now qemu-guest-agent
|
||||
- systemctl disable --now packagekit
|
||||
- systemctl disable --now ModemManager
|
||||
- systemctl disable --now multipathd
|
||||
- hostnamectl set-hostname ${hostname}
|
||||
|
||||
final_message: "cloud-init finished"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Быстрый запуск
|
||||
|
||||
Создать рабочий файл переменных:
|
||||
|
||||
```bash
|
||||
cp ../terraform.tfvars.example terraform.tfvars
|
||||
```
|
||||
|
||||
Инициализация:
|
||||
|
||||
```bash
|
||||
cd cluster
|
||||
tofu init
|
||||
```
|
||||
|
||||
Проверка плана:
|
||||
|
||||
```bash
|
||||
tofu plan
|
||||
```
|
||||
|
||||
Применение:
|
||||
|
||||
```bash
|
||||
tofu apply
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `.gitignore`
|
||||
|
||||
Минимально рекомендуется игнорировать:
|
||||
|
||||
```gitignore
|
||||
**/.terraform/
|
||||
**/.terraform.lock.hcl
|
||||
**/*.tfstate
|
||||
**/*.tfstate.*
|
||||
**/*.tfvars
|
||||
!**/*.tfvars.example
|
||||
.env
|
||||
.env.*
|
||||
*.pem
|
||||
*.key
|
||||
*.log
|
||||
.vscode/
|
||||
.idea/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Что хранить в git, а что нет
|
||||
|
||||
Хранить в git можно:
|
||||
|
||||
* `README.md`
|
||||
* `terraform.tfvars.example`
|
||||
* `cluster/*.tf`
|
||||
* `cluster/cloud-config/*.yml`
|
||||
* `modules/node/*.tf`
|
||||
* `modules/node/cloud-config/default.yml`
|
||||
|
||||
Не хранить в git:
|
||||
|
||||
* `terraform.tfvars`
|
||||
* `*.tfstate`
|
||||
* `.terraform/`
|
||||
* токены Proxmox
|
||||
* приватные SSH ключи
|
||||
* `.env`
|
||||
|
||||
---
|
||||
|
||||
## Отладка
|
||||
|
||||
Если cloud-init "не применился", проверять в первую очередь:
|
||||
|
||||
1. первая строка файла должна быть `#cloud-config`
|
||||
2. cloud-init файл должен реально попасть в Proxmox
|
||||
3. VM должна быть создана заново, если проверяется first boot логика
|
||||
4. cloud image и template должны быть подготовлены корректно
|
||||
5. если включён `qemu-guest-agent`, `tofu apply` может висеть в ожидании `guest-ping`, если агент не стартовал
|
||||
|
||||
Типовые причины проблем:
|
||||
|
||||
* отсутствует `#cloud-config`
|
||||
* битый YAML
|
||||
* неправильный override файл
|
||||
* не тот cloud image / плохо подготовленный template
|
||||
* ожидание `qemu-guest-agent` при неуспешном cloud-init
|
||||
|
||||
---
|
||||
|
||||
## Практические замечания
|
||||
|
||||
* если нельзя гарантировать интернет, лучше сразу готовить оффлайн провайдеры
|
||||
* если не нужен парольный вход, лучше использовать только SSH ключи
|
||||
* если нужен проектный `default.yml`, его можно положить в `cluster/cloud-config/default.yml`
|
||||
* если нужен отдельный cloud-init для группы машин, можно указывать один и тот же файл в `cloudinit` у нескольких VM
|
||||
* если нужна полная изоляция, можно задавать отдельный cloud-init на каждую VM
|
||||
|
||||
---
|
||||
|
||||
## Что обязательно должно быть
|
||||
|
||||
Если хотите воспроизвести этот проект в своей среде, вам в любом случае понадобятся:
|
||||
|
||||
* Ubuntu cloud image
|
||||
* корректно подготовленный Proxmox template с cloud-init disk
|
||||
* установленные OpenTofu провайдеры
|
||||
|
||||
Если этих компонентов нет, проект в полном виде воспроизвести нельзя.
|
||||
|
||||
34
cluster/cloud-config/default.yml
Normal file
34
cluster/cloud-config/default.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
#cloud-config
|
||||
# Создать passwd hash: openssl passwd -6
|
||||
|
||||
timezone: Europe/Moscow
|
||||
|
||||
users:
|
||||
- name: ubuntu
|
||||
groups: [sudo]
|
||||
shell: /bin/bash
|
||||
lock_passwd: false
|
||||
passwd: "$6$M8xzsYT0lwWjlcdH$HJRhw0rBb2WowCbqg03/WvMUlte0j5SLs9tAVCxCukEqWAt7XG0ceWYc5GRUBmRxFRTbcEDrdCVTXu5jIxN2f0"
|
||||
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
|
||||
ssh_authorized_keys:
|
||||
- ${ssh_key}
|
||||
|
||||
package_update: true
|
||||
|
||||
packages:
|
||||
- qemu-guest-agent
|
||||
|
||||
runcmd:
|
||||
- systemctl enable --now qemu-guest-agent
|
||||
- hostnamectl set-hostname ${hostname}
|
||||
- systemctl disable --now packagekit
|
||||
- systemctl disable --now ModemManager
|
||||
- systemctl disable --now multipathd
|
||||
|
||||
write_files:
|
||||
- path: /etc/motd
|
||||
content: |
|
||||
Managed by OpenTofu
|
||||
|
||||
|
||||
final_message: "cloud-init finished"
|
||||
34
cluster/cloud-config/master.yml
Normal file
34
cluster/cloud-config/master.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
#cloud-config
|
||||
# Создать passwd hash: openssl passwd -6
|
||||
|
||||
timezone: Europe/Moscow
|
||||
|
||||
users:
|
||||
- name: iurii
|
||||
groups: [sudo]
|
||||
shell: /bin/bash
|
||||
lock_passwd: false
|
||||
passwd: "$6$Zc8nwvtw0Kns5.sD$FpQ4aBSeGogefqjM4we4U5QQd4YBtC98tuG3rR4j9ZmbtC1kyFf2sY/IodYW3wG.U81aEntlZrtOTOqw3ZcOc0"
|
||||
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
|
||||
ssh_authorized_keys:
|
||||
- ${ssh_key}
|
||||
|
||||
package_update: true
|
||||
|
||||
packages:
|
||||
- qemu-guest-agent
|
||||
|
||||
runcmd:
|
||||
- systemctl enable --now qemu-guest-agent
|
||||
- hostnamectl set-hostname ${hostname}
|
||||
- systemctl disable --now packagekit
|
||||
- systemctl disable --now ModemManager
|
||||
- systemctl disable --now multipathd
|
||||
|
||||
write_files:
|
||||
- path: /etc/motd
|
||||
content: |
|
||||
Managed by OpenTofu
|
||||
|
||||
|
||||
final_message: "cloud-init finished"
|
||||
34
cluster/cloud-config/worker.yml
Normal file
34
cluster/cloud-config/worker.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
#cloud-config
|
||||
# Создать passwd hash: openssl passwd -6
|
||||
|
||||
timezone: Europe/Moscow
|
||||
|
||||
users:
|
||||
- name: iurii
|
||||
groups: [sudo]
|
||||
shell: /bin/bash
|
||||
lock_passwd: false
|
||||
passwd: "$6$Zc8nwvtw0Kns5.sD$FpQ4aBSeGogefqjM4we4U5QQd4YBtC98tuG3rR4j9ZmbtC1kyFf2sY/IodYW3wG.U81aEntlZrtOTOqw3ZcOc0"
|
||||
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
|
||||
ssh_authorized_keys:
|
||||
- ${ssh_key}
|
||||
|
||||
package_update: true
|
||||
|
||||
packages:
|
||||
- qemu-guest-agent
|
||||
|
||||
runcmd:
|
||||
- systemctl enable --now qemu-guest-agent
|
||||
- hostnamectl set-hostname ${hostname}
|
||||
- systemctl disable --now packagekit
|
||||
- systemctl disable --now ModemManager
|
||||
- systemctl disable --now multipathd
|
||||
|
||||
write_files:
|
||||
- path: /etc/motd
|
||||
content: |
|
||||
Managed by OpenTofu
|
||||
|
||||
|
||||
final_message: "cloud-init finished"
|
||||
52
cluster/locals.tf
Normal file
52
cluster/locals.tf
Normal file
@@ -0,0 +1,52 @@
|
||||
# nodes — описание виртуальных машин
|
||||
#
|
||||
# vlan_id:
|
||||
# - опциональный параметр
|
||||
# - если НЕ указан → VM будет в обычной сети (untagged, vmbr0)
|
||||
# - если указан → VM попадет в соответствующий VLAN (например 20 → 192.168.20.0/24)
|
||||
# cloudinit:
|
||||
# - опциональный параметр
|
||||
# - указывает имя cloud-init файла для конкретной VM
|
||||
# - файл должен находиться в root: cloud-config/<имя>.yml
|
||||
# - если НЕ указан → используется "default.yml"
|
||||
# - если файл НЕ найден в root → используется fallback из модуля (modules/node/cloud-config/default.yml)
|
||||
#
|
||||
# пример:
|
||||
# - cloudinit = "worker.yml" → будет использован cloud-config/worker.yml
|
||||
# - cloudinit не задан → будет использован default.yml
|
||||
|
||||
locals {
|
||||
nodes = {
|
||||
k8s-master-1 = {
|
||||
cloudinit = "master.yml"
|
||||
index = 1
|
||||
cpu = var.worker_cpu
|
||||
memory = 8192
|
||||
disk = var.worker_disk
|
||||
datastore = var.worker_datastore
|
||||
ip_offset = 10
|
||||
vlan_id = 20
|
||||
}
|
||||
|
||||
k8s-worker-1 = {
|
||||
cloudinit = "worker.yml"
|
||||
index = 2
|
||||
cpu = 4
|
||||
memory = 8192
|
||||
disk = var.worker_disk
|
||||
datastore = var.worker_datastore
|
||||
ip_offset = 20
|
||||
vlan_id = 20
|
||||
}
|
||||
k8s-worker-2 = {
|
||||
cloudinit = "worker.yml"
|
||||
index = 3
|
||||
cpu = 4
|
||||
memory = 8192
|
||||
disk = var.worker_disk
|
||||
datastore = var.worker_datastore
|
||||
ip_offset = 20
|
||||
vlan_id = 20
|
||||
}
|
||||
}
|
||||
}
|
||||
27
cluster/main.tf
Normal file
27
cluster/main.tf
Normal file
@@ -0,0 +1,27 @@
|
||||
data "local_file" "ssh_key" {
|
||||
filename = pathexpand("~/.ssh/id_rsa.pub")
|
||||
}
|
||||
|
||||
module "cluster" {
|
||||
source = "../modules/node"
|
||||
|
||||
nodes = local.nodes
|
||||
ssh_key = trimspace(data.local_file.ssh_key.content)
|
||||
|
||||
cluster_ip_start = var.cluster_ip_start
|
||||
worker_vmid_start = var.worker_vmid_start
|
||||
|
||||
cloudinit_datastore = var.cloudinit_datastore
|
||||
proxmox_node = var.proxmox_node
|
||||
|
||||
node_bridge = var.node_bridge
|
||||
image_datastore = var.image_datastore
|
||||
image_file = var.image_file
|
||||
disk_interface = var.disk_interface
|
||||
|
||||
network_base = var.network_base
|
||||
network_cidr = var.network_cidr
|
||||
cluster_gateway = var.cluster_gateway
|
||||
|
||||
data_datastore = var.data_datastore
|
||||
}
|
||||
13
cluster/outputs.tf
Normal file
13
cluster/outputs.tf
Normal file
@@ -0,0 +1,13 @@
|
||||
output "nodes_ipv4" {
|
||||
value = module.cluster.ip_addresses
|
||||
}
|
||||
|
||||
output "nodes_hostnames" {
|
||||
value = module.cluster.hostnames
|
||||
}
|
||||
|
||||
output "nodes_vmid" {
|
||||
value = module.cluster.vmids
|
||||
}
|
||||
|
||||
|
||||
20
cluster/providers.tf
Normal file
20
cluster/providers.tf
Normal file
@@ -0,0 +1,20 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
proxmox = {
|
||||
source = "registry.opentofu.org/bpg/proxmox"
|
||||
version = "= 0.101.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "proxmox" {
|
||||
endpoint = var.proxmox_endpoint
|
||||
api_token = "${var.proxmox_token_id}=${var.proxmox_token_secret}"
|
||||
insecure = true
|
||||
|
||||
ssh {
|
||||
username = "root"
|
||||
agent = true
|
||||
private_key = file(pathexpand("~/.ssh/id_rsa"))
|
||||
}
|
||||
}
|
||||
86
cluster/variables.tf
Normal file
86
cluster/variables.tf
Normal file
@@ -0,0 +1,86 @@
|
||||
variable "proxmox_endpoint" {}
|
||||
variable "proxmox_token_id" {}
|
||||
variable "proxmox_token_secret" {}
|
||||
|
||||
variable "proxmox_node" {
|
||||
type = string
|
||||
default = "ve"
|
||||
}
|
||||
|
||||
variable "cloudinit_datastore" {
|
||||
type = string
|
||||
default = "local"
|
||||
}
|
||||
|
||||
variable "disk_interface" {
|
||||
type = string
|
||||
default = "virtio0"
|
||||
}
|
||||
|
||||
variable "image_datastore" {
|
||||
type = string
|
||||
default = "local"
|
||||
}
|
||||
|
||||
variable "image_file" {
|
||||
type = string
|
||||
default = "import/ubuntu-24.qcow2"
|
||||
}
|
||||
|
||||
variable "worker_cpu" {
|
||||
default = 2
|
||||
}
|
||||
|
||||
|
||||
variable "worker_memory" {
|
||||
default = 2048
|
||||
}
|
||||
|
||||
|
||||
variable "worker_disk" {
|
||||
default = 20
|
||||
}
|
||||
|
||||
variable "network_base" {
|
||||
default = "192.168.22"
|
||||
}
|
||||
|
||||
variable "network_cidr" {
|
||||
default = "24"
|
||||
}
|
||||
|
||||
variable "cluster_gateway" {
|
||||
default = "192.168.22.1"
|
||||
}
|
||||
|
||||
variable "cluster_ip_start" {
|
||||
default = 10
|
||||
}
|
||||
|
||||
|
||||
variable "worker_ip_offset" {
|
||||
default = 5
|
||||
}
|
||||
|
||||
variable "node_bridge" {
|
||||
default = "vmbr0"
|
||||
}
|
||||
|
||||
variable "worker_datastore" {
|
||||
type = string
|
||||
default = "local-lvm"
|
||||
}
|
||||
|
||||
|
||||
|
||||
variable "worker_vmid_start" {
|
||||
type = number
|
||||
default = 3000
|
||||
}
|
||||
|
||||
variable "data_datastore" {
|
||||
type = string
|
||||
default = "data1"
|
||||
description = "Datastore for VM data disks"
|
||||
}
|
||||
|
||||
34
infra/cloud-config/default.yml
Normal file
34
infra/cloud-config/default.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
#cloud-config
|
||||
# Создать passwd hash: openssl passwd -6
|
||||
|
||||
timezone: Europe/Moscow
|
||||
|
||||
users:
|
||||
- name: ubuntu
|
||||
groups: [sudo]
|
||||
shell: /bin/bash
|
||||
lock_passwd: false
|
||||
passwd: "$6$M8xzsYT0lwWjlcdH$HJRhw0rBb2WowCbqg03/WvMUlte0j5SLs9tAVCxCukEqWAt7XG0ceWYc5GRUBmRxFRTbcEDrdCVTXu5jIxN2f0"
|
||||
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
|
||||
ssh_authorized_keys:
|
||||
- ${ssh_key}
|
||||
|
||||
package_update: true
|
||||
|
||||
packages:
|
||||
- qemu-guest-agent
|
||||
|
||||
runcmd:
|
||||
- systemctl enable --now qemu-guest-agent
|
||||
- hostnamectl set-hostname ${hostname}
|
||||
- systemctl disable --now packagekit
|
||||
- systemctl disable --now ModemManager
|
||||
- systemctl disable --now multipathd
|
||||
|
||||
write_files:
|
||||
- path: /etc/motd
|
||||
content: |
|
||||
Managed by OpenTofu
|
||||
|
||||
|
||||
final_message: "cloud-init finished"
|
||||
34
infra/cloud-config/vm.yml
Normal file
34
infra/cloud-config/vm.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
#cloud-config
|
||||
# vpn
|
||||
|
||||
timezone: Europe/Moscow
|
||||
|
||||
users:
|
||||
- name: iurii
|
||||
groups: [sudo]
|
||||
shell: /bin/bash
|
||||
lock_passwd: false
|
||||
passwd: "$6$Zc8nwvtw0Kns5.sD$FpQ4aBSeGogefqjM4we4U5QQd4YBtC98tuG3rR4j9ZmbtC1kyFf2sY/IodYW3wG.U81aEntlZrtOTOqw3ZcOc0"
|
||||
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
|
||||
ssh_authorized_keys:
|
||||
- ${ssh_key}
|
||||
|
||||
package_update: true
|
||||
|
||||
packages:
|
||||
- qemu-guest-agent
|
||||
|
||||
runcmd:
|
||||
- systemctl enable --now qemu-guest-agent
|
||||
- hostnamectl set-hostname ${hostname}
|
||||
- systemctl disable --now packagekit
|
||||
- systemctl disable --now ModemManager
|
||||
- systemctl disable --now multipathd
|
||||
|
||||
write_files:
|
||||
- path: /etc/motd
|
||||
content: |
|
||||
Managed by OpenTofu
|
||||
|
||||
|
||||
final_message: "cloud-init finished"
|
||||
31
infra/locals.tf
Normal file
31
infra/locals.tf
Normal file
@@ -0,0 +1,31 @@
|
||||
# nodes — описание виртуальных машин
|
||||
#
|
||||
# vlan_id:
|
||||
# - опциональный параметр
|
||||
# - если НЕ указан → VM будет в обычной сети (untagged, vmbr0)
|
||||
# - если указан → VM попадет в соответствующий VLAN (например 20 → 192.168.20.0/24)
|
||||
# cloudinit:
|
||||
# - опциональный параметр
|
||||
# - указывает имя cloud-init файла для конкретной VM
|
||||
# - файл должен находиться в root: cloud-config/<имя>.yml
|
||||
# - если НЕ указан → используется "default.yml"
|
||||
# - если файл НЕ найден в root → используется fallback из модуля (modules/node/cloud-config/default.yml)
|
||||
#
|
||||
# пример:
|
||||
# - cloudinit = "worker.yml" → будет использован cloud-config/worker.yml
|
||||
# - cloudinit не задан → будет использован default.yml
|
||||
|
||||
locals {
|
||||
nodes = {
|
||||
sing-box-tun= {
|
||||
cloudinit = "vm.yml"
|
||||
index = 2
|
||||
cpu = 1
|
||||
memory = 1024
|
||||
disk = 7
|
||||
datastore = "local-lvm"
|
||||
ip_offset = 0
|
||||
ip = "192.168.22.50"
|
||||
}
|
||||
}
|
||||
}
|
||||
27
infra/main.tf
Normal file
27
infra/main.tf
Normal file
@@ -0,0 +1,27 @@
|
||||
data "local_file" "ssh_key" {
|
||||
filename = pathexpand("~/.ssh/id_rsa.pub")
|
||||
}
|
||||
|
||||
module "cluster" {
|
||||
source = "../modules/node"
|
||||
|
||||
nodes = local.nodes
|
||||
ssh_key = trimspace(data.local_file.ssh_key.content)
|
||||
|
||||
cluster_ip_start = var.cluster_ip_start
|
||||
worker_vmid_start = var.worker_vmid_start
|
||||
|
||||
cloudinit_datastore = var.cloudinit_datastore
|
||||
proxmox_node = var.proxmox_node
|
||||
|
||||
node_bridge = var.node_bridge
|
||||
image_datastore = var.image_datastore
|
||||
image_file = var.image_file
|
||||
disk_interface = var.disk_interface
|
||||
|
||||
network_base = var.network_base
|
||||
network_cidr = var.network_cidr
|
||||
cluster_gateway = var.cluster_gateway
|
||||
|
||||
data_datastore = var.data_datastore
|
||||
}
|
||||
13
infra/outputs.tf
Normal file
13
infra/outputs.tf
Normal file
@@ -0,0 +1,13 @@
|
||||
output "nodes_ipv4" {
|
||||
value = module.cluster.ip_addresses
|
||||
}
|
||||
|
||||
output "nodes_hostnames" {
|
||||
value = module.cluster.hostnames
|
||||
}
|
||||
|
||||
output "nodes_vmid" {
|
||||
value = module.cluster.vmids
|
||||
}
|
||||
|
||||
|
||||
20
infra/providers.tf
Normal file
20
infra/providers.tf
Normal file
@@ -0,0 +1,20 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
proxmox = {
|
||||
source = "registry.opentofu.org/bpg/proxmox"
|
||||
version = "= 0.101.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "proxmox" {
|
||||
endpoint = var.proxmox_endpoint
|
||||
api_token = "${var.proxmox_token_id}=${var.proxmox_token_secret}"
|
||||
insecure = true
|
||||
|
||||
ssh {
|
||||
username = "root"
|
||||
agent = true
|
||||
private_key = file(pathexpand("~/.ssh/id_rsa"))
|
||||
}
|
||||
}
|
||||
86
infra/variables.tf
Normal file
86
infra/variables.tf
Normal file
@@ -0,0 +1,86 @@
|
||||
variable "proxmox_endpoint" {}
|
||||
variable "proxmox_token_id" {}
|
||||
variable "proxmox_token_secret" {}
|
||||
|
||||
variable "proxmox_node" {
|
||||
type = string
|
||||
default = "ve"
|
||||
}
|
||||
|
||||
variable "cloudinit_datastore" {
|
||||
type = string
|
||||
default = "local"
|
||||
}
|
||||
|
||||
variable "disk_interface" {
|
||||
type = string
|
||||
default = "virtio0"
|
||||
}
|
||||
|
||||
variable "image_datastore" {
|
||||
type = string
|
||||
default = "local"
|
||||
}
|
||||
|
||||
variable "image_file" {
|
||||
type = string
|
||||
default = "import/ubuntu-24.qcow2"
|
||||
}
|
||||
|
||||
variable "worker_cpu" {
|
||||
default = 2
|
||||
}
|
||||
|
||||
|
||||
variable "worker_memory" {
|
||||
default = 2048
|
||||
}
|
||||
|
||||
|
||||
variable "worker_disk" {
|
||||
default = 20
|
||||
}
|
||||
|
||||
variable "network_base" {
|
||||
default = "192.168.22"
|
||||
}
|
||||
|
||||
variable "network_cidr" {
|
||||
default = "24"
|
||||
}
|
||||
|
||||
variable "cluster_gateway" {
|
||||
default = "192.168.22.1"
|
||||
}
|
||||
|
||||
variable "cluster_ip_start" {
|
||||
default = 10
|
||||
}
|
||||
|
||||
|
||||
variable "worker_ip_offset" {
|
||||
default = 5
|
||||
}
|
||||
|
||||
variable "node_bridge" {
|
||||
default = "vmbr0"
|
||||
}
|
||||
|
||||
variable "worker_datastore" {
|
||||
type = string
|
||||
default = "local-lvm"
|
||||
}
|
||||
|
||||
|
||||
|
||||
variable "worker_vmid_start" {
|
||||
type = number
|
||||
default = 3000
|
||||
}
|
||||
|
||||
variable "data_datastore" {
|
||||
type = string
|
||||
default = "data1"
|
||||
description = "Datastore for VM data disks"
|
||||
}
|
||||
|
||||
34
lab/cloud-config/default.yml
Normal file
34
lab/cloud-config/default.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
#cloud-config
|
||||
# Создать passwd hash: openssl passwd -6
|
||||
|
||||
timezone: Europe/Moscow
|
||||
|
||||
users:
|
||||
- name: ubuntu
|
||||
groups: [sudo]
|
||||
shell: /bin/bash
|
||||
lock_passwd: false
|
||||
passwd: "$6$M8xzsYT0lwWjlcdH$HJRhw0rBb2WowCbqg03/WvMUlte0j5SLs9tAVCxCukEqWAt7XG0ceWYc5GRUBmRxFRTbcEDrdCVTXu5jIxN2f0"
|
||||
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
|
||||
ssh_authorized_keys:
|
||||
- ${ssh_key}
|
||||
|
||||
package_update: true
|
||||
|
||||
packages:
|
||||
- qemu-guest-agent
|
||||
|
||||
runcmd:
|
||||
- systemctl enable --now qemu-guest-agent
|
||||
- hostnamectl set-hostname ${hostname}
|
||||
- systemctl disable --now packagekit
|
||||
- systemctl disable --now ModemManager
|
||||
- systemctl disable --now multipathd
|
||||
|
||||
write_files:
|
||||
- path: /etc/motd
|
||||
content: |
|
||||
Managed by OpenTofu
|
||||
|
||||
|
||||
final_message: "cloud-init finished"
|
||||
34
lab/cloud-config/vm.yml
Normal file
34
lab/cloud-config/vm.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
#cloud-config
|
||||
# vpn
|
||||
|
||||
timezone: Europe/Moscow
|
||||
|
||||
users:
|
||||
- name: iurii
|
||||
groups: [sudo]
|
||||
shell: /bin/bash
|
||||
lock_passwd: false
|
||||
passwd: "$6$Zc8nwvtw0Kns5.sD$FpQ4aBSeGogefqjM4we4U5QQd4YBtC98tuG3rR4j9ZmbtC1kyFf2sY/IodYW3wG.U81aEntlZrtOTOqw3ZcOc0"
|
||||
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
|
||||
ssh_authorized_keys:
|
||||
- ${ssh_key}
|
||||
|
||||
package_update: true
|
||||
|
||||
packages:
|
||||
- qemu-guest-agent
|
||||
|
||||
runcmd:
|
||||
- systemctl enable --now qemu-guest-agent
|
||||
- hostnamectl set-hostname ${hostname}
|
||||
- systemctl disable --now packagekit
|
||||
- systemctl disable --now ModemManager
|
||||
- systemctl disable --now multipathd
|
||||
|
||||
write_files:
|
||||
- path: /etc/motd
|
||||
content: |
|
||||
Managed by OpenTofu
|
||||
|
||||
|
||||
final_message: "cloud-init finished"
|
||||
31
lab/locals.tf
Normal file
31
lab/locals.tf
Normal file
@@ -0,0 +1,31 @@
|
||||
# nodes — описание виртуальных машин
|
||||
#
|
||||
# vlan_id:
|
||||
# - опциональный параметр
|
||||
# - если НЕ указан → VM будет в обычной сети (untagged, vmbr0)
|
||||
# - если указан → VM попадет в соответствующий VLAN (например 20 → 192.168.20.0/24)
|
||||
# cloudinit:
|
||||
# - опциональный параметр
|
||||
# - указывает имя cloud-init файла для конкретной VM
|
||||
# - файл должен находиться в root: cloud-config/<имя>.yml
|
||||
# - если НЕ указан → используется "default.yml"
|
||||
# - если файл НЕ найден в root → используется fallback из модуля (modules/node/cloud-config/default.yml)
|
||||
#
|
||||
# пример:
|
||||
# - cloudinit = "worker.yml" → будет использован cloud-config/worker.yml
|
||||
# - cloudinit не задан → будет использован default.yml
|
||||
|
||||
locals {
|
||||
nodes = {
|
||||
vm1 = {
|
||||
cloudinit = "vm.yml"
|
||||
index = 1
|
||||
cpu = 1
|
||||
memory = 1024
|
||||
disk = var.worker_disk
|
||||
datastore = var.worker_datastore
|
||||
ip_offset = 0
|
||||
vlan_id = 20
|
||||
}
|
||||
}
|
||||
}
|
||||
27
lab/main.tf
Normal file
27
lab/main.tf
Normal file
@@ -0,0 +1,27 @@
|
||||
data "local_file" "ssh_key" {
|
||||
filename = pathexpand("~/.ssh/id_rsa.pub")
|
||||
}
|
||||
|
||||
module "cluster" {
|
||||
source = "../modules/node"
|
||||
|
||||
nodes = local.nodes
|
||||
ssh_key = trimspace(data.local_file.ssh_key.content)
|
||||
|
||||
cluster_ip_start = var.cluster_ip_start
|
||||
worker_vmid_start = var.worker_vmid_start
|
||||
|
||||
cloudinit_datastore = var.cloudinit_datastore
|
||||
proxmox_node = var.proxmox_node
|
||||
|
||||
node_bridge = var.node_bridge
|
||||
image_datastore = var.image_datastore
|
||||
image_file = var.image_file
|
||||
disk_interface = var.disk_interface
|
||||
|
||||
network_base = var.network_base
|
||||
network_cidr = var.network_cidr
|
||||
cluster_gateway = var.cluster_gateway
|
||||
|
||||
data_datastore = var.data_datastore
|
||||
}
|
||||
13
lab/outputs.tf
Normal file
13
lab/outputs.tf
Normal file
@@ -0,0 +1,13 @@
|
||||
output "nodes_ipv4" {
|
||||
value = module.cluster.ip_addresses
|
||||
}
|
||||
|
||||
output "nodes_hostnames" {
|
||||
value = module.cluster.hostnames
|
||||
}
|
||||
|
||||
output "nodes_vmid" {
|
||||
value = module.cluster.vmids
|
||||
}
|
||||
|
||||
|
||||
20
lab/providers.tf
Normal file
20
lab/providers.tf
Normal file
@@ -0,0 +1,20 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
proxmox = {
|
||||
source = "registry.opentofu.org/bpg/proxmox"
|
||||
version = "= 0.101.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "proxmox" {
|
||||
endpoint = var.proxmox_endpoint
|
||||
api_token = "${var.proxmox_token_id}=${var.proxmox_token_secret}"
|
||||
insecure = true
|
||||
|
||||
ssh {
|
||||
username = "root"
|
||||
agent = true
|
||||
private_key = file(pathexpand("~/.ssh/id_rsa"))
|
||||
}
|
||||
}
|
||||
86
lab/variables.tf
Normal file
86
lab/variables.tf
Normal file
@@ -0,0 +1,86 @@
|
||||
variable "proxmox_endpoint" {}
|
||||
variable "proxmox_token_id" {}
|
||||
variable "proxmox_token_secret" {}
|
||||
|
||||
variable "proxmox_node" {
|
||||
type = string
|
||||
default = "ve"
|
||||
}
|
||||
|
||||
variable "cloudinit_datastore" {
|
||||
type = string
|
||||
default = "local"
|
||||
}
|
||||
|
||||
variable "disk_interface" {
|
||||
type = string
|
||||
default = "virtio0"
|
||||
}
|
||||
|
||||
variable "image_datastore" {
|
||||
type = string
|
||||
default = "local"
|
||||
}
|
||||
|
||||
variable "image_file" {
|
||||
type = string
|
||||
default = "import/ubuntu-24.qcow2"
|
||||
}
|
||||
|
||||
variable "worker_cpu" {
|
||||
default = 2
|
||||
}
|
||||
|
||||
|
||||
variable "worker_memory" {
|
||||
default = 2048
|
||||
}
|
||||
|
||||
|
||||
variable "worker_disk" {
|
||||
default = 20
|
||||
}
|
||||
|
||||
variable "network_base" {
|
||||
default = "192.168.22"
|
||||
}
|
||||
|
||||
variable "network_cidr" {
|
||||
default = "24"
|
||||
}
|
||||
|
||||
variable "cluster_gateway" {
|
||||
default = "192.168.22.1"
|
||||
}
|
||||
|
||||
variable "cluster_ip_start" {
|
||||
default = 10
|
||||
}
|
||||
|
||||
|
||||
variable "worker_ip_offset" {
|
||||
default = 5
|
||||
}
|
||||
|
||||
variable "node_bridge" {
|
||||
default = "vmbr0"
|
||||
}
|
||||
|
||||
variable "worker_datastore" {
|
||||
type = string
|
||||
default = "local-lvm"
|
||||
}
|
||||
|
||||
|
||||
|
||||
variable "worker_vmid_start" {
|
||||
type = number
|
||||
default = 3000
|
||||
}
|
||||
|
||||
variable "data_datastore" {
|
||||
type = string
|
||||
default = "data1"
|
||||
description = "Datastore for VM data disks"
|
||||
}
|
||||
|
||||
35
modules/node/cloud-config/default.yml
Normal file
35
modules/node/cloud-config/default.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
#cloud-config
|
||||
|
||||
timezone: Europe/Moscow
|
||||
|
||||
users:
|
||||
- default
|
||||
- name: ubuntu
|
||||
groups: [sudo]
|
||||
shell: /bin/bash
|
||||
lock_passwd: true
|
||||
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
|
||||
ssh_authorized_keys:
|
||||
- ${ssh_key}
|
||||
|
||||
ssh_pwauth: false
|
||||
|
||||
package_update: true
|
||||
|
||||
packages:
|
||||
- qemu-guest-agent
|
||||
|
||||
runcmd:
|
||||
- systemctl enable --now qemu-guest-agent
|
||||
- hostnamectl set-hostname ${hostname}
|
||||
- systemctl disable --now packagekit
|
||||
- systemctl disable --now ModemManager
|
||||
- systemctl disable --now multipathd
|
||||
|
||||
write_files:
|
||||
- path: /etc/motd
|
||||
content: |
|
||||
Managed by OpenTofu
|
||||
|
||||
|
||||
final_message: "cloud-init finished"
|
||||
29
modules/node/locals.tf
Normal file
29
modules/node/locals.tf
Normal file
@@ -0,0 +1,29 @@
|
||||
locals {
|
||||
ssh_public_key = var.ssh_key
|
||||
|
||||
nodes = {
|
||||
for name, node in var.nodes :
|
||||
name => node
|
||||
}
|
||||
|
||||
ip_map = {
|
||||
for name, node in local.nodes :
|
||||
name => coalesce(
|
||||
lookup(node, "ip", null),
|
||||
"${var.network_base}.${var.cluster_ip_start + node.ip_offset + node.index}"
|
||||
)
|
||||
}
|
||||
|
||||
vmid_map = {
|
||||
for name, node in local.nodes :
|
||||
name => coalesce(
|
||||
lookup(node, "vmid", null),
|
||||
var.worker_vmid_start + node.index
|
||||
)
|
||||
}
|
||||
|
||||
hostname_map = {
|
||||
for name, node in local.nodes :
|
||||
name => "${name}"
|
||||
}
|
||||
}
|
||||
92
modules/node/main.tf
Normal file
92
modules/node/main.tf
Normal file
@@ -0,0 +1,92 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
proxmox = {
|
||||
source = "registry.opentofu.org/bpg/proxmox"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "proxmox_virtual_environment_file" "cloudinit" {
|
||||
for_each = local.nodes
|
||||
content_type = "snippets"
|
||||
datastore_id = var.cloudinit_datastore
|
||||
node_name = var.proxmox_node
|
||||
|
||||
source_raw {
|
||||
file_name = "${each.key}.yml"
|
||||
data = fileexists("${path.root}/cloud-config/${coalesce(each.value.cloudinit, "default.yml")}") ? templatefile(
|
||||
"${path.root}/cloud-config/${coalesce(each.value.cloudinit, "default.yml")}",
|
||||
{
|
||||
hostname = local.hostname_map[each.key]
|
||||
ssh_key = local.ssh_public_key
|
||||
}
|
||||
) : templatefile(
|
||||
"${path.module}/cloud-config/default.yml",
|
||||
{
|
||||
hostname = local.hostname_map[each.key]
|
||||
ssh_key = local.ssh_public_key
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
resource "proxmox_virtual_environment_vm" "nodes" {
|
||||
for_each = local.nodes
|
||||
|
||||
name = local.hostname_map[each.key]
|
||||
node_name = var.proxmox_node
|
||||
|
||||
# allow vmid override
|
||||
vm_id = coalesce(
|
||||
lookup(local.nodes[each.key], "vmid", null),
|
||||
local.vmid_map[each.key]
|
||||
)
|
||||
|
||||
agent {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
cpu {
|
||||
cores = each.value.cpu
|
||||
}
|
||||
|
||||
memory {
|
||||
dedicated = each.value.memory
|
||||
}
|
||||
|
||||
network_device {
|
||||
bridge = var.node_bridge
|
||||
# vlan_id = try(each.value.vlan_id, null)
|
||||
vlan_id = each.value.vlan_id
|
||||
|
||||
}
|
||||
|
||||
disk {
|
||||
datastore_id = each.value.datastore
|
||||
import_from = "${var.image_datastore}:${var.image_file}"
|
||||
interface = var.disk_interface
|
||||
size = each.value.disk
|
||||
}
|
||||
|
||||
dynamic "disk" {
|
||||
for_each = try([each.value.data_disk], [])
|
||||
|
||||
content {
|
||||
datastore_id = var.data_datastore
|
||||
interface = "scsi1"
|
||||
size = disk.value
|
||||
}
|
||||
}
|
||||
|
||||
initialization {
|
||||
datastore_id = each.value.datastore
|
||||
user_data_file_id = proxmox_virtual_environment_file.cloudinit[each.key].id
|
||||
|
||||
ip_config {
|
||||
ipv4 {
|
||||
address = "${local.ip_map[each.key]}/${var.network_cidr}"
|
||||
gateway = var.cluster_gateway
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
modules/node/outputs.tf
Normal file
17
modules/node/outputs.tf
Normal file
@@ -0,0 +1,17 @@
|
||||
output "ip_addresses" {
|
||||
description = "IP addresses of all created nodes"
|
||||
value = {
|
||||
for name, _ in local.nodes :
|
||||
name => proxmox_virtual_environment_vm.nodes[name].ipv4_addresses[1][0]
|
||||
}
|
||||
}
|
||||
|
||||
output "hostnames" {
|
||||
description = "Hostnames of all created nodes"
|
||||
value = local.hostname_map
|
||||
}
|
||||
|
||||
output "vmids" {
|
||||
description = "VMIDs of all created nodes"
|
||||
value = local.vmid_map
|
||||
}
|
||||
71
modules/node/variables.tf
Normal file
71
modules/node/variables.tf
Normal file
@@ -0,0 +1,71 @@
|
||||
variable "ssh_key" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "nodes" {
|
||||
type = map(object({
|
||||
# role = string
|
||||
index = number
|
||||
cpu = number
|
||||
memory = number
|
||||
disk = number
|
||||
datastore = string
|
||||
ip_offset = optional(number)
|
||||
ip = optional(string)
|
||||
vmid = optional(number)
|
||||
vlan_id = optional(number)
|
||||
data_disk = optional(number)
|
||||
cloudinit = optional(string)
|
||||
}))
|
||||
}
|
||||
|
||||
variable "cluster_ip_start" {
|
||||
type = number
|
||||
}
|
||||
|
||||
variable "worker_vmid_start" {
|
||||
type = number
|
||||
}
|
||||
|
||||
variable "cloudinit_datastore" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "proxmox_node" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "node_bridge" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "image_datastore" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "image_file" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "disk_interface" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "network_base" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "network_cidr" {
|
||||
type = number
|
||||
}
|
||||
|
||||
variable "cluster_gateway" {
|
||||
type = string
|
||||
}
|
||||
|
||||
|
||||
|
||||
variable "data_datastore" {
|
||||
type = string
|
||||
description = "Datastore for data disk"
|
||||
}
|
||||
292
start.md
Normal file
292
start.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# OpenTofu + Proxmox + Ubuntu Cloud-Init
|
||||
|
||||
### Полностью воспроизводимая установка (оффлайн провайдеры)
|
||||
|
||||
Этот документ позволяет развернуть инфраструктурную машину (Ubuntu), установить OpenTofu, настроить оффлайн-провайдеры, подготовить шаблон Ubuntu Cloud-Init в Proxmox и выполнить первый `tofu plan`.
|
||||
|
||||
|
||||
## 2. Установка OpenTofu
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg
|
||||
sudo install -m 0755 -d /etc/apt/keyrings
|
||||
|
||||
curl -fsSL https://get.opentofu.org/opentofu.gpg \
|
||||
| sudo tee /etc/apt/keyrings/opentofu.gpg >/dev/null
|
||||
|
||||
curl -fsSL https://packages.opentofu.org/opentofu/tofu/gpgkey \
|
||||
| sudo gpg --no-tty --batch --dearmor -o /etc/apt/keyrings/opentofu-repo.gpg >/dev/null
|
||||
|
||||
sudo chmod a+r /etc/apt/keyrings/opentofu.gpg /etc/apt/keyrings/opentofu-repo.gpg
|
||||
|
||||
echo "deb [signed-by=/etc/apt/keyrings/opentofu.gpg,/etc/apt/keyrings/opentofu-repo.gpg] \
|
||||
https://packages.opentofu.org/opentofu/tofu/any/ any main" \
|
||||
| sudo tee /etc/apt/sources.list.d/opentofu.list >/dev/null
|
||||
```
|
||||
```sh
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y tofu
|
||||
```
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
tofu version
|
||||
```
|
||||
## Установка Golang
|
||||
Для компиляции должен быть установлен [Golang](https://go.dev/learn/)
|
||||
|
||||
|
||||
---
|
||||
## 3. Настройка оффлайн‑провайдеров
|
||||
|
||||
Для работы OpenTofu без доступа к интернету провайдеры можно разместить локально в каталоге `~/.terraform.d/plugins/`.
|
||||
### 3.1. Подготовка каталогов
|
||||
|
||||
Создаём директории для оффлайн-провайдеров:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/bpg/proxmox
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/hashicorp/{local,random,tls}
|
||||
```
|
||||
|
||||
Ожидаемая структура после установки:
|
||||
|
||||
```sh
|
||||
/home/user/.terraform.d/
|
||||
└── plugins
|
||||
└── registry.opentofu.org
|
||||
├── bpg
|
||||
│ └── proxmox
|
||||
│ ├── 0.86.0
|
||||
│ │ └── linux_amd64
|
||||
│ │ └── terraform-provider-proxmox_v0.86.0
|
||||
│ ├── 0.87.0
|
||||
│ │ └── linux_amd64
|
||||
│ │ └── terraform-provider-proxmox_v0.87.0
|
||||
│ └── terraform-provider-proxmox_0.86.0_linux_amd64.zip
|
||||
└── hashicorp
|
||||
├── local
|
||||
│ └── 2.6.1
|
||||
│ └── linux_amd64
|
||||
│ └── terraform-provider-local_v2.6.1
|
||||
├── random
|
||||
│ └── 3.7.2
|
||||
│ └── linux_amd64
|
||||
│ └── terraform-provider-random_v3.7.2
|
||||
└── tls
|
||||
└── 4.1.0
|
||||
└── linux_amd64
|
||||
└── terraform-provider-tls_v4.1.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Провайдер **bpg/proxmox**
|
||||
|
||||
Релизы: [https://github.com/bpg/terraform-provider-proxmox/releases](https://github.com/bpg/terraform-provider-proxmox/releases)
|
||||
|
||||
Пример загрузки версии `0.86.0`:
|
||||
>>Пояснение: провайдер bpg/proxmox распространяется уже в собранном виде. Его не нужно компилировать - достаточно скачать ZIP‑файл с готовым бинарником распаковать и положить его в каталог зеркала. Это отличается от провайдеров hashicorp/local, random, tls, которые распространяются как исходники и требуют сборки через go build.
|
||||
|
||||
Загрузить:
|
||||
```bash
|
||||
wget https://github.com/bpg/terraform-provider-proxmox/releases/download/v0.86.0/terraform-provider-proxmox_0.86.0_linux_amd64.zip
|
||||
|
||||
```
|
||||
Создать директорию:
|
||||
```sh
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/bpg/proxmox/0.86.0/linux_amd64
|
||||
```
|
||||
Распаковать и переместить:
|
||||
```sh
|
||||
unzip terraform-provider-proxmox_0.86.0_linux_amd64.zip -d /tmp
|
||||
mv /tmp/terraform-provider-proxmox_v0.86.0 \
|
||||
~/.terraform.d/plugins/registry.opentofu.org/bpg/proxmox/0.86.0/linux_amd64/
|
||||
# Делаем исполняемым
|
||||
chmod +x ~/.terraform.d/plugins/registry.opentofu.org/bpg/proxmox/0.86.0/linux_amd64/terraform-provider-proxmox_v0.86.0
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
### 📌 Правильное размещение бинарников Hashicorp после компиляции
|
||||
|
||||
После выполнения go build бинарники провайдеров необходимо разместить в строгой структуре, которую ожидает OpenTofu:
|
||||
```sh
|
||||
~/.terraform.d/plugins/registry.opentofu.org/hashicorp/<provider>/<version>/linux_amd64/
|
||||
```
|
||||
|
||||
### Провайдер **hashicorp/local**
|
||||
|
||||
Релизы: [https://github.com/hashicorp/terraform-provider-local/releases/tag/v2.6.1](https://github.com/hashicorp/terraform-provider-local/releases/tag/v2.6.1)
|
||||
|
||||
Загрузка и компиляция:
|
||||
|
||||
```bash
|
||||
wget https://github.com/hashicorp/terraform-provider-local/archive/refs/tags/v2.6.1.zip
|
||||
unzip v2.6.1.zip
|
||||
cd terraform-provider-local-2.6.1/
|
||||
go build -o terraform-provider-local .
|
||||
```
|
||||
|
||||
Переместить бинарник:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/hashicorp/local/2.6.1/linux_amd64
|
||||
mv terraform-provider-local \
|
||||
~/.terraform.d/plugins/registry.opentofu.org/hashicorp/local/2.6.1/linux_amd64/terraform-provider-local_v2.6.1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Провайдер **hashicorp/random**
|
||||
|
||||
Релизы: [https://github.com/hashicorp/terraform-provider-random/releases/tag/v3.7.2](https://github.com/hashicorp/terraform-provider-random/releases/tag/v3.7.2)
|
||||
|
||||
```bash
|
||||
wget https://github.com/hashicorp/terraform-provider-random/archive/refs/tags/v3.7.2.zip
|
||||
unzip v3.7.2.zip
|
||||
cd terraform-provider-random-3.7.2/
|
||||
go build -o terraform-provider-random .
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/hashicorp/random/3.7.2/linux_amd64
|
||||
mv terraform-provider-random \
|
||||
~/.terraform.d/plugins/registry.opentofu.org/hashicorp/random/3.7.2/linux_amd64/terraform-provider-random_v3.7.2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Провайдер **hashicorp/tls**
|
||||
|
||||
Релизы: [https://github.com/hashicorp/terraform-provider-tls/releases](https://github.com/hashicorp/terraform-provider-tls/releases)
|
||||
|
||||
```bash
|
||||
wget https://github.com/hashicorp/terraform-provider-tls/archive/refs/tags/v4.1.0.zip
|
||||
unzip v4.1.0.zip
|
||||
cd terraform-provider-tls-4.1.0/
|
||||
go build -o terraform-provider-tls .
|
||||
```
|
||||
|
||||
Бинарный файл переместить в каталог `tls`:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/hashicorp/tls/4.1.0/linux_amd64
|
||||
mv terraform-provider-tls \
|
||||
~/.terraform.d/plugins/registry.opentofu.org/hashicorp/tls/4.1.0/linux_amd64/terraform-provider-tls_v4.1.0
|
||||
```
|
||||
|
||||
Исходники после сборки можно удалить.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 4. Настройка `~/.tofurc`
|
||||
|
||||
Файл конфигурации указывает OpenTofu использовать локальные провайдеры и не пытаться тянуть их из сети.
|
||||
**$USER** Обязательно замените, на ваш реальный путь.
|
||||
|
||||
```bash
|
||||
provider_installation {
|
||||
filesystem_mirror {
|
||||
path = "/home/$USER/.terraform.d/plugins"
|
||||
include = [
|
||||
"registry.opentofu.org/bpg/proxmox",
|
||||
"registry.opentofu.org/hashicorp/local",
|
||||
"registry.opentofu.org/hashicorp/random",
|
||||
"registry.opentofu.org/hashicorp/tls"
|
||||
]
|
||||
}
|
||||
|
||||
direct {
|
||||
exclude = [
|
||||
"registry.opentofu.org/bpg/proxmox",
|
||||
"registry.opentofu.org/hashicorp/local",
|
||||
"registry.opentofu.org/hashicorp/random",
|
||||
"registry.opentofu.org/hashicorp/tls"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
tofu init -reconfigure
|
||||
```
|
||||
|
||||
Ожидаемое: `Installing provider ... from local filesystem`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Создание API-токена Proxmox
|
||||
|
||||
Минимальные права:
|
||||
|
||||
* Datastore.AllocateSpace
|
||||
* VM.Allocate
|
||||
* VM.Audit
|
||||
* VM.Config.*
|
||||
|
||||
---
|
||||
|
||||
## 6. Конфиг токена на Ubuntu
|
||||
|
||||
Файл:
|
||||
|
||||
```bash
|
||||
vim ~/.pve-creds
|
||||
```
|
||||
|
||||
Содержимое:
|
||||
|
||||
```
|
||||
export PVE_TOKEN_ID="root@pam!tofu"
|
||||
export PVE_TOKEN_SECRET="YOUR_SECRET"
|
||||
export PVE_HOST="192.168.22.5"
|
||||
```
|
||||
|
||||
Загрузить:
|
||||
|
||||
```bash
|
||||
set -a
|
||||
source ~/.pve-creds
|
||||
set +a
|
||||
```
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
curl -k -H "Authorization: PVEAPIToken=${PVE_TOKEN_ID}=${PVE_TOKEN_SECRET}" \
|
||||
https://$PVE_HOST:8006/api2/json/version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Подготовка шаблона Ubuntu Cloud-Init в Proxmox
|
||||
|
||||
### 7.1. Скачать cloud image
|
||||
|
||||
```bash
|
||||
wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img -O ubuntu.img
|
||||
```
|
||||
|
||||
### 7.2. Создать VM под шаблон
|
||||
|
||||
```bash
|
||||
qm create 9001 --name ubuntu-template --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0
|
||||
qm importdisk 9001 ubuntu.img local-lvm
|
||||
qm set 9001 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9001-disk-0
|
||||
qm set 9001 --ide2 local-lvm:cloudinit
|
||||
qm set 9001 --boot c --bootdisk scsi0
|
||||
qm set 9001 --serial0 socket --vga serial0
|
||||
```
|
||||
|
||||
### 7.3. Превратить VM в шаблон
|
||||
|
||||
```bash
|
||||
qm template 9001
|
||||
```
|
||||
|
||||
---
|
||||
292
stat2.md
Normal file
292
stat2.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# OpenTofu + Proxmox + Ubuntu Cloud-Init
|
||||
|
||||
### Полностью воспроизводимая установка (оффлайн провайдеры)
|
||||
|
||||
Этот документ позволяет развернуть инфраструктурную машину (Ubuntu), установить OpenTofu, настроить оффлайн-провайдеры, подготовить шаблон Ubuntu Cloud-Init в Proxmox и выполнить первый `tofu plan`.
|
||||
|
||||
|
||||
## 2. Установка OpenTofu
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg
|
||||
sudo install -m 0755 -d /etc/apt/keyrings
|
||||
|
||||
curl -fsSL https://get.opentofu.org/opentofu.gpg \
|
||||
| sudo tee /etc/apt/keyrings/opentofu.gpg >/dev/null
|
||||
|
||||
curl -fsSL https://packages.opentofu.org/opentofu/tofu/gpgkey \
|
||||
| sudo gpg --no-tty --batch --dearmor -o /etc/apt/keyrings/opentofu-repo.gpg >/dev/null
|
||||
|
||||
sudo chmod a+r /etc/apt/keyrings/opentofu.gpg /etc/apt/keyrings/opentofu-repo.gpg
|
||||
|
||||
echo "deb [signed-by=/etc/apt/keyrings/opentofu.gpg,/etc/apt/keyrings/opentofu-repo.gpg] \
|
||||
https://packages.opentofu.org/opentofu/tofu/any/ any main" \
|
||||
| sudo tee /etc/apt/sources.list.d/opentofu.list >/dev/null
|
||||
```
|
||||
```sh
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y tofu
|
||||
```
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
tofu version
|
||||
```
|
||||
## Установка Golang
|
||||
Для компиляции должен быть установлен [Golang](https://go.dev/learn/)
|
||||
|
||||
|
||||
---
|
||||
## 3. Настройка оффлайн‑провайдеров
|
||||
|
||||
Для работы OpenTofu без доступа к интернету провайдеры можно разместить локально в каталоге `~/.terraform.d/plugins/`.
|
||||
### 3.1. Подготовка каталогов
|
||||
|
||||
Создаём директории для оффлайн-провайдеров:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/bpg/proxmox
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/hashicorp/{local,random,tls}
|
||||
```
|
||||
|
||||
Ожидаемая структура после установки:
|
||||
|
||||
```sh
|
||||
/home/user/.terraform.d/
|
||||
└── plugins
|
||||
└── registry.opentofu.org
|
||||
├── bpg
|
||||
│ └── proxmox
|
||||
│ ├── 0.86.0
|
||||
│ │ └── linux_amd64
|
||||
│ │ └── terraform-provider-proxmox_v0.86.0
|
||||
│ ├── 0.87.0
|
||||
│ │ └── linux_amd64
|
||||
│ │ └── terraform-provider-proxmox_v0.87.0
|
||||
│ └── terraform-provider-proxmox_0.86.0_linux_amd64.zip
|
||||
└── hashicorp
|
||||
├── local
|
||||
│ └── 2.6.1
|
||||
│ └── linux_amd64
|
||||
│ └── terraform-provider-local_v2.6.1
|
||||
├── random
|
||||
│ └── 3.7.2
|
||||
│ └── linux_amd64
|
||||
│ └── terraform-provider-random_v3.7.2
|
||||
└── tls
|
||||
└── 4.1.0
|
||||
└── linux_amd64
|
||||
└── terraform-provider-tls_v4.1.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Провайдер **bpg/proxmox**
|
||||
|
||||
Релизы: [https://github.com/bpg/terraform-provider-proxmox/releases](https://github.com/bpg/terraform-provider-proxmox/releases)
|
||||
|
||||
Пример загрузки версии `0.86.0`:
|
||||
>>Пояснение: провайдер bpg/proxmox распространяется уже в собранном виде. Его не нужно компилировать - достаточно скачать ZIP‑файл с готовым бинарником распаковать и положить его в каталог зеркала. Это отличается от провайдеров hashicorp/local, random, tls, которые распространяются как исходники и требуют сборки через go build.
|
||||
|
||||
Загрузить:
|
||||
```bash
|
||||
wget https://github.com/bpg/terraform-provider-proxmox/releases/download/v0.86.0/terraform-provider-proxmox_0.86.0_linux_amd64.zip
|
||||
|
||||
```
|
||||
Создать директорию:
|
||||
```sh
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/bpg/proxmox/0.86.0/linux_amd64
|
||||
```
|
||||
Распаковать и переместить:
|
||||
```sh
|
||||
unzip terraform-provider-proxmox_0.86.0_linux_amd64.zip -d /tmp
|
||||
mv /tmp/terraform-provider-proxmox_v0.86.0 \
|
||||
~/.terraform.d/plugins/registry.opentofu.org/bpg/proxmox/0.86.0/linux_amd64/
|
||||
# Делаем исполняемым
|
||||
chmod +x ~/.terraform.d/plugins/registry.opentofu.org/bpg/proxmox/0.86.0/linux_amd64/terraform-provider-proxmox_v0.86.0
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
### 📌 Правильное размещение бинарников Hashicorp после компиляции
|
||||
|
||||
После выполнения go build бинарники провайдеров необходимо разместить в строгой структуре, которую ожидает OpenTofu:
|
||||
```sh
|
||||
~/.terraform.d/plugins/registry.opentofu.org/hashicorp/<provider>/<version>/linux_amd64/
|
||||
```
|
||||
|
||||
### Провайдер **hashicorp/local**
|
||||
|
||||
Релизы: [https://github.com/hashicorp/terraform-provider-local/releases/tag/v2.6.1](https://github.com/hashicorp/terraform-provider-local/releases/tag/v2.6.1)
|
||||
|
||||
Загрузка и компиляция:
|
||||
|
||||
```bash
|
||||
wget https://github.com/hashicorp/terraform-provider-local/archive/refs/tags/v2.6.1.zip
|
||||
unzip v2.6.1.zip
|
||||
cd terraform-provider-local-2.6.1/
|
||||
go build -o terraform-provider-local .
|
||||
```
|
||||
|
||||
Переместить бинарник:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/hashicorp/local/2.6.1/linux_amd64
|
||||
mv terraform-provider-local \
|
||||
~/.terraform.d/plugins/registry.opentofu.org/hashicorp/local/2.6.1/linux_amd64/terraform-provider-local_v2.6.1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Провайдер **hashicorp/random**
|
||||
|
||||
Релизы: [https://github.com/hashicorp/terraform-provider-random/releases/tag/v3.7.2](https://github.com/hashicorp/terraform-provider-random/releases/tag/v3.7.2)
|
||||
|
||||
```bash
|
||||
wget https://github.com/hashicorp/terraform-provider-random/archive/refs/tags/v3.7.2.zip
|
||||
unzip v3.7.2.zip
|
||||
cd terraform-provider-random-3.7.2/
|
||||
go build -o terraform-provider-random .
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/hashicorp/random/3.7.2/linux_amd64
|
||||
mv terraform-provider-random \
|
||||
~/.terraform.d/plugins/registry.opentofu.org/hashicorp/random/3.7.2/linux_amd64/terraform-provider-random_v3.7.2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Провайдер **hashicorp/tls**
|
||||
|
||||
Релизы: [https://github.com/hashicorp/terraform-provider-tls/releases](https://github.com/hashicorp/terraform-provider-tls/releases)
|
||||
|
||||
```bash
|
||||
wget https://github.com/hashicorp/terraform-provider-tls/archive/refs/tags/v4.1.0.zip
|
||||
unzip v4.1.0.zip
|
||||
cd terraform-provider-tls-4.1.0/
|
||||
go build -o terraform-provider-tls .
|
||||
```
|
||||
|
||||
Бинарный файл переместить в каталог `tls`:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.terraform.d/plugins/registry.opentofu.org/hashicorp/tls/4.1.0/linux_amd64
|
||||
mv terraform-provider-tls \
|
||||
~/.terraform.d/plugins/registry.opentofu.org/hashicorp/tls/4.1.0/linux_amd64/terraform-provider-tls_v4.1.0
|
||||
```
|
||||
|
||||
Исходники после сборки можно удалить.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 4. Настройка `~/.tofurc`
|
||||
|
||||
Файл конфигурации указывает OpenTofu использовать локальные провайдеры и не пытаться тянуть их из сети.
|
||||
**$USER** Обязательно замените, на ваш реальный путь.
|
||||
|
||||
```bash
|
||||
provider_installation {
|
||||
filesystem_mirror {
|
||||
path = "/home/$USER/.terraform.d/plugins"
|
||||
include = [
|
||||
"registry.opentofu.org/bpg/proxmox",
|
||||
"registry.opentofu.org/hashicorp/local",
|
||||
"registry.opentofu.org/hashicorp/random",
|
||||
"registry.opentofu.org/hashicorp/tls"
|
||||
]
|
||||
}
|
||||
|
||||
direct {
|
||||
exclude = [
|
||||
"registry.opentofu.org/bpg/proxmox",
|
||||
"registry.opentofu.org/hashicorp/local",
|
||||
"registry.opentofu.org/hashicorp/random",
|
||||
"registry.opentofu.org/hashicorp/tls"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
tofu init -reconfigure
|
||||
```
|
||||
|
||||
Ожидаемое: `Installing provider ... from local filesystem`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Создание API-токена Proxmox
|
||||
|
||||
Минимальные права:
|
||||
|
||||
* Datastore.AllocateSpace
|
||||
* VM.Allocate
|
||||
* VM.Audit
|
||||
* VM.Config.*
|
||||
|
||||
---
|
||||
|
||||
## 6. Конфиг токена на Ubuntu
|
||||
|
||||
Файл:
|
||||
|
||||
```bash
|
||||
vim ~/.pve-creds
|
||||
```
|
||||
|
||||
Содержимое:
|
||||
|
||||
```
|
||||
export PVE_TOKEN_ID="root@pam!tofu"
|
||||
export PVE_TOKEN_SECRET="YOUR_SECRET"
|
||||
export PVE_HOST="192.168.22.5"
|
||||
```
|
||||
|
||||
Загрузить:
|
||||
|
||||
```bash
|
||||
set -a
|
||||
source ~/.pve-creds
|
||||
set +a
|
||||
```
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
curl -k -H "Authorization: PVEAPIToken=${PVE_TOKEN_ID}=${PVE_TOKEN_SECRET}" \
|
||||
https://$PVE_HOST:8006/api2/json/version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Подготовка шаблона Ubuntu Cloud-Init в Proxmox
|
||||
|
||||
### 7.1. Скачать cloud image
|
||||
|
||||
```bash
|
||||
wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img -O ubuntu.img
|
||||
```
|
||||
|
||||
### 7.2. Создать VM под шаблон
|
||||
|
||||
```bash
|
||||
qm create 9001 --name ubuntu-template --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0
|
||||
qm importdisk 9001 ubuntu.img local-lvm
|
||||
qm set 9001 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9001-disk-0
|
||||
qm set 9001 --ide2 local-lvm:cloudinit
|
||||
qm set 9001 --boot c --bootdisk scsi0
|
||||
qm set 9001 --serial0 socket --vga serial0
|
||||
```
|
||||
|
||||
### 7.3. Превратить VM в шаблон
|
||||
|
||||
```bash
|
||||
qm template 9001
|
||||
```
|
||||
|
||||
---
|
||||
42
terraform.tfvars.example
Normal file
42
terraform.tfvars.example
Normal file
@@ -0,0 +1,42 @@
|
||||
# Proxmox API endpoint (формат обязателен: https://host:port/api2/json)
|
||||
proxmox_endpoint = "https://<IP>:<PORT>/api2/json"
|
||||
|
||||
# ID API токена (формат: user@realm!token_name)
|
||||
proxmox_token_id = "terraform@ve!user"
|
||||
|
||||
# Секрет API токена (не коммитить в git)
|
||||
proxmox_token_secret = "<PROXMOX_TOKEN>"
|
||||
|
||||
|
||||
# Стартовый VMID для worker-нод (будут увеличиваться автоматически)
|
||||
worker_vmid_start = 1000
|
||||
|
||||
|
||||
# Дефолтные ресурсы для worker VM
|
||||
worker_cpu = 1 # количество CPU ядер
|
||||
worker_memory = 1024 # RAM в MB
|
||||
worker_disk = 15 # размер системного диска в GB
|
||||
|
||||
# Datastore для системного диска VM
|
||||
worker_datastore = "local-lvm"
|
||||
|
||||
|
||||
# Datastore, где лежит cloud image (qcow2)
|
||||
image_datastore = "local"
|
||||
|
||||
# Путь к образу внутри datastore (без "local:" если провайдер сам добавляет)
|
||||
image_file = "import/ubuntu-24.qcow2"
|
||||
|
||||
|
||||
# Сетевые настройки
|
||||
cluster_gateway = "192.168.0.1" # шлюз сети
|
||||
|
||||
network_base = "192.168.0" # база сети (используется для генерации IP)
|
||||
network_cidr = "24" # маска сети (лучше как number, не string)
|
||||
|
||||
# Смещение IP относительно base (например 0 → .0, 10 → .10)
|
||||
cluster_ip_start = 0
|
||||
|
||||
|
||||
# Datastore для дополнительных дисков (data disk)
|
||||
data_datastore = "data1"
|
||||
Reference in New Issue
Block a user