Автоматизации развёртывания Kubernetes-кластера
This commit is contained in:
commit
533f678ab7
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ssh
|
||||||
|
|
||||||
16
.terraform.lock.hcl
generated
Normal file
16
.terraform.lock.hcl
generated
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# This file is maintained automatically by "tofu init".
|
||||||
|
# Manual edits may be lost in future updates.
|
||||||
|
|
||||||
|
provider "registry.opentofu.org/bpg/proxmox" {
|
||||||
|
version = "0.98.1"
|
||||||
|
hashes = [
|
||||||
|
"h1:/3n9NevrIwRAI/0HOEbeO4uQfCCivjspG8pDgzhqOSU=",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "registry.opentofu.org/hashicorp/local" {
|
||||||
|
version = "2.7.0"
|
||||||
|
hashes = [
|
||||||
|
"h1:uWL9nhlxLY2xW3GMh8IFQv0S1UP3HMlN/B+2Nr7fsZE=",
|
||||||
|
]
|
||||||
|
}
|
||||||
1
.terraform/modules/modules.json
Normal file
1
.terraform/modules/modules.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"Modules":[{"Key":"","Source":"","Dir":"."},{"Key":"cluster","Source":"./modules/k8s-node","Dir":"modules/k8s-node"}]}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
/home/andy/.terraform.d/plugins/registry.opentofu.org/bpg/proxmox/0.98.1/linux_amd64
|
||||||
@ -0,0 +1 @@
|
|||||||
|
/home/andy/.terraform.d/plugins/registry.opentofu.org/hashicorp/local/2.7.0/linux_amd64
|
||||||
51
README.md
Normal file
51
README.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
Что содержит проект
|
||||||
|
|
||||||
|
Модуль OpenTofu для создания Kubernetes-нод в Proxmox.
|
||||||
|
Генерацию cloud-init конфигураций.
|
||||||
|
Параметризованную выдачу VMID, IP, hostname.
|
||||||
|
Готовый root-конфиг, использующий модуль k8s-node.
|
||||||
|
Возможность добавлять и удалять конкретные ноды без count.
|
||||||
|
Подготовку виртуальных машин к установке Kubernetes.
|
||||||
|
|
||||||
|
Структура репозитория
|
||||||
|
├── README.md
|
||||||
|
├── locals.tf
|
||||||
|
├── main.tf
|
||||||
|
├── modules
|
||||||
|
│ └── k8s-node
|
||||||
|
│ ├── cloud-config
|
||||||
|
│ ├── locals.tf
|
||||||
|
│ ├── main.tf
|
||||||
|
│ ├── outputs.tf
|
||||||
|
│ └── variables.tf
|
||||||
|
├── outputs.tf
|
||||||
|
├── providers.tf
|
||||||
|
├── terraform.tfstate
|
||||||
|
├── terraform.tfstate.backup
|
||||||
|
├── terraform.tfvars
|
||||||
|
└── variables.tf
|
||||||
|
|
||||||
|
Быстрый старт
|
||||||
|
1. Инициализация проекта
|
||||||
|
tofu init
|
||||||
|
2. Проверка плана
|
||||||
|
tofu plan
|
||||||
|
3. Создание инфраструктуры
|
||||||
|
tofu apply
|
||||||
|
|
||||||
|
После выполнения этой команды Proxmox создаст виртуальные машины, сгенерирует userdata для cloud-init и развернёт требуемые ноды.
|
||||||
|
|
||||||
|
Управление нодами
|
||||||
|
Модуль принимает объект вида:
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
master1 = { role = "master", cpu = 2, memory = 4096 }
|
||||||
|
worker1 = { role = "worker", cpu = 2, memory = 4096 }
|
||||||
|
worker2 = { role = "worker", cpu = 2, memory = 4096 }
|
||||||
|
}
|
||||||
|
|
||||||
|
Вы можете:
|
||||||
|
|
||||||
|
Добавить новую ноду, просто вписав её в map.
|
||||||
|
Удалить ноду, удалив её ключ из map.
|
||||||
|
Иметь несколько кластеров, копируя модуль в разные окружения.
|
||||||
43
locals.tf
Normal file
43
locals.tf
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
locals {
|
||||||
|
ssh_public_key = trimspace(file("./ssh/id_terraform.pub"))
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
master1 = {
|
||||||
|
role = "master"
|
||||||
|
cpu = var.master_cpu
|
||||||
|
memory = var.master_memory
|
||||||
|
disk = var.master_disk
|
||||||
|
datastore = var.master_datastore
|
||||||
|
ip_offset = var.master_ip_offset
|
||||||
|
}
|
||||||
|
|
||||||
|
worker1 = {
|
||||||
|
role = "worker"
|
||||||
|
cpu = var.worker_cpu
|
||||||
|
memory = var.worker_memory
|
||||||
|
disk = var.worker_disk
|
||||||
|
datastore = var.worker_datastore
|
||||||
|
ip_offset = var.worker_ip_offset
|
||||||
|
}
|
||||||
|
|
||||||
|
# worker2 = {
|
||||||
|
# role = "worker"
|
||||||
|
# cpu = var.worker_cpu
|
||||||
|
# memory = var.worker_memory
|
||||||
|
# disk = var.worker_disk
|
||||||
|
# datastore = var.worker_datastore
|
||||||
|
# ip_offset = var.worker_ip_offset
|
||||||
|
# }
|
||||||
|
|
||||||
|
worker3 = {
|
||||||
|
role = "worker"
|
||||||
|
cpu = var.worker_cpu
|
||||||
|
memory = var.worker_memory
|
||||||
|
disk = var.worker_disk
|
||||||
|
datastore = var.worker_datastore
|
||||||
|
ip_offset = var.worker_ip_offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
27
main.tf
Normal file
27
main.tf
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
data "local_file" "ssh_key" {
|
||||||
|
filename = pathexpand("./ssh/id_terraform.pub")
|
||||||
|
}
|
||||||
|
|
||||||
|
module "cluster" {
|
||||||
|
source = "./modules/k8s-node"
|
||||||
|
|
||||||
|
nodes = local.nodes
|
||||||
|
ssh_key = trimspace(data.local_file.ssh_key.content)
|
||||||
|
|
||||||
|
hostname_prefix = var.hostname_prefix
|
||||||
|
cluster_ip_start = var.cluster_ip_start
|
||||||
|
master_vmid_start = var.master_vmid_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
|
||||||
|
}
|
||||||
20
modules/k8s-node/cloud-config/node-base.yml
Normal file
20
modules/k8s-node/cloud-config/node-base.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#cloud-config
|
||||||
|
|
||||||
|
timezone: Europe/Moscow
|
||||||
|
|
||||||
|
users:
|
||||||
|
- default
|
||||||
|
- name: ubuntu
|
||||||
|
groups: [sudo]
|
||||||
|
shell: /bin/bash
|
||||||
|
ssh_authorized_keys:
|
||||||
|
- ${ssh_key}
|
||||||
|
|
||||||
|
package_update: true
|
||||||
|
|
||||||
|
packages:
|
||||||
|
- qemu-guest-agent
|
||||||
|
|
||||||
|
runcmd:
|
||||||
|
- systemctl enable --now qemu-guest-agent
|
||||||
|
- hostnamectl set-hostname ${hostname}
|
||||||
58
modules/k8s-node/locals.tf
Normal file
58
modules/k8s-node/locals.tf
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
locals {
|
||||||
|
# ssh-ключ приходит снаружи, файл не читаем
|
||||||
|
ssh_public_key = var.ssh_key
|
||||||
|
|
||||||
|
# Разделяем ноды по ролям
|
||||||
|
masters = {
|
||||||
|
for name, node in var.nodes :
|
||||||
|
name => node if node.role == "master"
|
||||||
|
}
|
||||||
|
|
||||||
|
workers = {
|
||||||
|
for name, node in var.nodes :
|
||||||
|
name => node if node.role == "worker"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Даём каждой ноде индекс внутри своей роли (master1, master2, worker1...)
|
||||||
|
# Индекс определяется по отсортированным именам, чтобы был стабильным.
|
||||||
|
indexed_masters = {
|
||||||
|
for name, node in local.masters :
|
||||||
|
name => merge(node, {
|
||||||
|
index = index(sort(keys(local.masters)), name) + 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
indexed_workers = {
|
||||||
|
for name, node in local.workers :
|
||||||
|
name => merge(node, {
|
||||||
|
index = index(sort(keys(local.workers)), name) + 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
# Общая карта нод
|
||||||
|
nodes = merge(local.indexed_masters, local.indexed_workers)
|
||||||
|
|
||||||
|
# IP-адреса
|
||||||
|
ip_map = {
|
||||||
|
for name, node in local.nodes :
|
||||||
|
name => var.cluster_ip_start + node.ip_offset + node.index
|
||||||
|
}
|
||||||
|
|
||||||
|
# VMID: разные диапазоны для master/worker
|
||||||
|
vmid_map = {
|
||||||
|
for name, node in local.nodes :
|
||||||
|
name => (
|
||||||
|
node.role == "master"
|
||||||
|
? var.master_vmid_start + node.index
|
||||||
|
: var.worker_vmid_start + node.index
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# hostname: prefix-role-index (k8s-master-1, k8s-worker-2 и т.п.)
|
||||||
|
hostname_map = {
|
||||||
|
for name, node in local.nodes :
|
||||||
|
name => "${var.hostname_prefix}-${node.role}-${node.index}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
71
modules/k8s-node/main.tf
Normal file
71
modules/k8s-node/main.tf
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
proxmox = {
|
||||||
|
source = "registry.opentofu.org/bpg/proxmox"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#Создание cloud-init для каждой ноды
|
||||||
|
|
||||||
|
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 = templatefile(
|
||||||
|
"${path.module}/cloud-config/node-base.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
|
||||||
|
vm_id = local.vmid_map[each.key]
|
||||||
|
|
||||||
|
agent {
|
||||||
|
enabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu {
|
||||||
|
cores = each.value.cpu
|
||||||
|
}
|
||||||
|
|
||||||
|
memory {
|
||||||
|
dedicated = each.value.memory
|
||||||
|
}
|
||||||
|
|
||||||
|
network_device {
|
||||||
|
bridge = var.node_bridge
|
||||||
|
}
|
||||||
|
|
||||||
|
disk {
|
||||||
|
datastore_id = each.value.datastore
|
||||||
|
import_from = "${var.image_datastore}:${var.image_file}"
|
||||||
|
interface = var.disk_interface
|
||||||
|
size = each.value.disk
|
||||||
|
}
|
||||||
|
|
||||||
|
initialization {
|
||||||
|
datastore_id = each.value.datastore
|
||||||
|
user_data_file_id = proxmox_virtual_environment_file.cloudinit[each.key].id
|
||||||
|
|
||||||
|
ip_config {
|
||||||
|
ipv4 {
|
||||||
|
address = "${var.network_base}.${local.ip_map[each.key]}/${var.network_cidr}"
|
||||||
|
gateway = var.cluster_gateway
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
modules/k8s-node/outputs.tf
Normal file
17
modules/k8s-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
|
||||||
|
}
|
||||||
66
modules/k8s-node/variables.tf
Normal file
66
modules/k8s-node/variables.tf
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
variable "ssh_key" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "nodes" {
|
||||||
|
type = map(object({
|
||||||
|
role = string
|
||||||
|
cpu = number
|
||||||
|
memory = number
|
||||||
|
disk = number
|
||||||
|
datastore = string
|
||||||
|
ip_offset = number
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "hostname_prefix" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cluster_ip_start" {
|
||||||
|
type = number
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "master_vmid_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
|
||||||
|
}
|
||||||
11
outputs.tf
Normal file
11
outputs.tf
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
output "nodes_ipv4" {
|
||||||
|
value = module.cluster.ip_addresses
|
||||||
|
}
|
||||||
|
|
||||||
|
output "nodes_hostnames" {
|
||||||
|
value = module.cluster.hostnames
|
||||||
|
}
|
||||||
|
|
||||||
|
output "nodes_vmid" {
|
||||||
|
value = module.cluster.vmids
|
||||||
|
}
|
||||||
20
providers.tf
Normal file
20
providers.tf
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
proxmox = {
|
||||||
|
source = "registry.opentofu.org/bpg/proxmox"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "proxmox" {
|
||||||
|
endpoint = var.proxmox_endpoint
|
||||||
|
api_token = "${var.proxmox_token_id}=${var.proxmox_token_secret}"
|
||||||
|
insecure = true
|
||||||
|
|
||||||
|
ssh {
|
||||||
|
agent = true
|
||||||
|
username = "root"
|
||||||
|
private_key = file("./ssh/id_terraform")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
24
terraform.tfvars
Normal file
24
terraform.tfvars
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
proxmox_endpoint = "https://185.78.29.7:8006/api2/json"
|
||||||
|
proxmox_token_id = "terraform@pve!tf"
|
||||||
|
proxmox_token_secret = "API_TOKEN"
|
||||||
|
|
||||||
|
master_vmid_start = 4000
|
||||||
|
worker_vmid_start = 4010
|
||||||
|
|
||||||
|
|
||||||
|
master_cpu = 2
|
||||||
|
master_memory = 2048
|
||||||
|
master_disk = 20
|
||||||
|
master_datastore = "local"
|
||||||
|
|
||||||
|
worker_cpu = 2
|
||||||
|
worker_memory = 4096
|
||||||
|
worker_disk = 30
|
||||||
|
worker_datastore = "local"
|
||||||
|
|
||||||
|
image_datastore = "local"
|
||||||
|
image_file = "import/ubuntu-24.qcow2"
|
||||||
|
cluster_gateway = "10.10.10.1"
|
||||||
|
network_base = "10.10.10"
|
||||||
|
network_cidr = "24"
|
||||||
|
cluster_ip_start = 40
|
||||||
103
variables.tf
Normal file
103
variables.tf
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
variable "proxmox_endpoint" {}
|
||||||
|
variable "proxmox_token_id" {}
|
||||||
|
variable "proxmox_token_secret" {}
|
||||||
|
|
||||||
|
variable "proxmox_node" {
|
||||||
|
type = string
|
||||||
|
default = "px"
|
||||||
|
}
|
||||||
|
|
||||||
|
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 "hostname_prefix" {
|
||||||
|
type = string
|
||||||
|
default = "k8s"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "master_cpu" {
|
||||||
|
default = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "worker_cpu" {
|
||||||
|
default = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "master_memory" {
|
||||||
|
default = 4096
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "worker_memory" {
|
||||||
|
default = 4096
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "master_disk" {
|
||||||
|
default = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "worker_disk" {
|
||||||
|
default = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "network_base" {
|
||||||
|
default = "10.10.10"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "network_cidr" {
|
||||||
|
default = "24"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cluster_gateway" {
|
||||||
|
default = "10.10.10.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cluster_ip_start" {
|
||||||
|
default = 40
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "master_ip_offset" {
|
||||||
|
default = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "worker_ip_offset" {
|
||||||
|
default = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "node_bridge" {
|
||||||
|
default = "vmbr1"
|
||||||
|
}
|
||||||
|
variable "master_datastore" {
|
||||||
|
type = string
|
||||||
|
default = "local"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "worker_datastore" {
|
||||||
|
type = string
|
||||||
|
default = "local"
|
||||||
|
}
|
||||||
|
variable "master_vmid_start" {
|
||||||
|
type = number
|
||||||
|
default = 2000
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "worker_vmid_start" {
|
||||||
|
type = number
|
||||||
|
default = 2010
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user