init: initial commit

This commit is contained in:
ITQ
2025-10-04 08:54:34 +03:00
commit 8351a2c667
37 changed files with 1408 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
# Terraform files
**/.terraform/*
*.tfstate
*.tfstate.*
crash.log
crash.*.log
*.tfvars
*.tfvars.json
override.tf
override.tf.json
*_override.tf
*_override.tf.json
.terraformrc
terraform.rc
# Env files
.env
# Yandex Cloud authorized key
key.json
+28
View File
@@ -0,0 +1,28 @@
# Terraform
## Prerequisites
Ensure you have the following installed on your system:
- [Terraform](https://developer.hashicorp.com/terraform) (latest version recommended)
## Main (`./main`)
Main Terraform module for infrastructure
## Service (`./service`)
Terraform module to create service account for [Main](#main-main) terraform module.
## CheatSheet
```bash
# Init Terraform module
../scripts/init
# Set required variables for terraform from dotenv file
export $(grep -v '^#' ./.env | xargs)
# Unset required variables for terraform from dotenv file
unset $(grep -v '^#' ./.env | sed 's/=.*//' | xargs)
```
+20
View File
@@ -0,0 +1,20 @@
# This is just a template file, create .env file when running init script
# Below all environment variables
YC_SERVICE_ACCOUNT_KEY_FILE=
YC_CLOUD_ID=
YC_FOLDER_ID=
# Must be the same as YC_FOLDER_ID and YC_CLOUD_ID
TF_VAR_cloud_id=
TF_VAR_folder_id=
TF_VAR_domain=
TF_VAR_ssh_public_key_path=
AWS_BUCKET=
AWS_BUCKET_KEY=
AWS_ENDPOINT=
AWS_USE_PATH_STYLE=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=
+23
View File
@@ -0,0 +1,23 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/random" {
version = "3.7.2"
hashes = [
"h1:356j/3XnXEKr9nyicLUufzoF4Yr6hRy481KIxRVpK0c=",
]
}
provider "registry.terraform.io/hashicorp/template" {
version = "2.2.0"
hashes = [
"h1:94qn780bi1qjrbC3uQtjJh3Wkfwd5+tTtJHOb7KTg9w=",
]
}
provider "registry.terraform.io/yandex-cloud/yandex" {
version = "0.148.0"
hashes = [
"h1:sih4+ex4J/T0hiVzE+TB0mJUUr2NAb7C9f+Ya5J+qec=",
]
}
+21
View File
@@ -0,0 +1,21 @@
resource "yandex_api_gateway" "frontend" {
name = "frontend"
description = "API Gateway for frontend"
execution_timeout = "300"
connectivity {
network_id = yandex_vpc_network.default.id
}
custom_domains {
fqdn = var.domain
certificate_id = yandex_cm_certificate.default.id
}
log_options {
log_group_id = yandex_logging_group.default.id
min_level = "INFO"
}
spec = data.template_file.api_gateway_spec.rendered
}
+13
View File
@@ -0,0 +1,13 @@
terraform {
backend "s3" {
use_lockfile = true
max_retries = 5
retry_mode = "adaptive"
skip_region_validation = true
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
skip_s3_checksum = true
}
}
+11
View File
@@ -0,0 +1,11 @@
resource "yandex_cm_certificate" "default" {
name = "default"
description = "Default certificate for all resources"
deletion_protection = true
domains = tolist([var.domain, "*.${var.domain}"])
managed {
challenge_type = "DNS_CNAME"
}
}
+141
View File
@@ -0,0 +1,141 @@
openapi: "3.0.0"
info:
version: 1.0.0
title: API Gateway
paths:
/health:
get:
summary: Health Check
description: Endpoint to confirm the API Gateway service is operational.
operationId: healthCheck
x-yc-apigateway-integration:
type: dummy
http_code: 200
http_headers:
"Content-Type": "application/json"
content:
"*": '{"status": "ok"}'
responses:
"200":
description: Service is healthy.
/config:
get:
summary: Config
description: Implementation of frontend dynamic configuration.
operationId: config
x-yc-apigateway-integration:
type: dummy
http_code: 200
http_headers:
"Content-Type": "application/json"
content:
"*": '{"backend_url": "https://backend.interview.yandex.itqdev.xyz"}'
responses:
"200":
description: Config returned.
/assets/{path+}:
get:
summary: Get Static File
description: Serves static files from the S3 bucket.
operationId: getAssetsFile
parameters:
- name: path
in: path
required: true
schema:
type: string
x-yc-apigateway-integration:
type: object_storage
bucket: "${bucket_name}"
object: "current/assets/{path}"
error_object: "old/assets/{path}"
responses:
"200":
description: Static file content.
"404":
description: Asset not found.
/img/{path+}:
get:
summary: Get Static File
description: Serves static files from the S3 bucket.
operationId: getImgFile
parameters:
- name: path
in: path
required: true
schema:
type: string
x-yc-apigateway-integration:
type: object_storage
bucket: "${bucket_name}"
object: "current/img/{path}"
error_object: "old/img/{path}"
responses:
"200":
description: Static file content.
"404":
description: Asset not found.
/favicon.svg:
get:
summary: Get Static File
description: Serves static files from the S3 bucket.
operationId: getFaviconFile
parameters:
- name: path
in: path
required: true
schema:
type: string
x-yc-apigateway-integration:
type: object_storage
bucket: "${bucket_name}"
object: "current/favicon.svg"
error_object: "old/favicon.svg"
responses:
"200":
description: Static file content.
"404":
description: Asset not found.
/:
get:
summary: Serve SPA Entrypoint (Root)
description: Serves the main 'index.html' file for requests to the root of the domain.
operationId: getSpaIndexRoot
x-yc-apigateway-integration:
type: object_storage
bucket: "${bucket_name}"
object: "current/index.html"
error_object: "old/index.html"
responses:
"200":
description: Main HTML page of the SPA.
"404":
description: "'index.html' not found in the bucket."
/{proxy+}:
get:
summary: Serve SPA Entrypoint (Client-Side Routes)
description: Catches all other application paths and serves 'index.html', enabling client-side routing.
operationId: getSpaIndexCatchAll
parameters:
- name: proxy
in: path
required: true
schema:
type: string
x-yc-apigateway-integration:
type: object_storage
bucket: "${bucket_name}"
object: "current/index.html"
error_object: "old/index.html"
responses:
"200":
description: Main HTML page of the SPA.
"404":
description: "'index.html' not found in the bucket."
+59
View File
@@ -0,0 +1,59 @@
#cloud-config
ssh_pwauth: false
package_update: true
package_upgrade: true
growpart:
mode: growpart
devices: ["/"]
ignore_growroot_disabled: true
users:
- name: ubuntu
sudo: ALL=(ALL) NOPASSWD:ALL
lock_passwd: true
shell: /bin/bash
write_files:
- path: /var/lib/rancher/credentialprovider/config.yaml
permissions: "0600"
encoding: b64
content: ${k3s_credential_provider_config}
- path: /var/lib/rancher/credentialprovider/bin/yc-credential-provider
permissions: "0700"
encoding: b64
content: ${k3s_credential_provider}
runcmd:
- |
export HOME=/root
curl https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash -s -- -i /tmp/yc -n
mv /tmp/yc/bin/yc /usr/bin/
yc config set instance-service-account true
yc config set cloud-id ${yc_cloud_id}
- |
DISK=/dev/disk/by-id/virtio-data
K3S_DIR=${k3s_dir}
mkdir -p $K3S_DIR
if ! blkid $DISK > /dev/null; then
mkfs.ext4 $DISK
fi
echo "$DISK $K3S_DIR ext4 defaults 0 0" >> /etc/fstab
mount -a
- |
curl -sfL https://get.k3s.io | INSTALL_K3S_CHANNEL=stable INSTALL_K3S_EXEC="server \
--cluster-init \
--token=${k3s_token} \
--node-taint CriticalAddonsOnly=true:NoExecute \
--write-kubeconfig ${k3s_dir}/kubeconfig/config \
--image-credential-provider-bin-dir /var/lib/rancher/credentialprovider/bin \
--image-credential-provider-config /var/lib/rancher/credentialprovider/config.yaml \
--data-dir=${k3s_dir} \
--default-local-storage-path=${k3s_dir}/storage" sh -
- |
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
- |
mkdir -p /home/ubuntu/.kube
cp ${k3s_dir}/kubeconfig/config /home/ubuntu/.kube/config
chown -R ubuntu:ubuntu /home/ubuntu/
+57
View File
@@ -0,0 +1,57 @@
#cloud-config
ssh_pwauth: false
package_update: true
package_upgrade: true
growpart:
mode: growpart
devices: ["/"]
ignore_growroot_disabled: true
users:
- name: ubuntu
sudo: ALL=(ALL) NOPASSWD:ALL
lock_passwd: true
shell: /bin/bash
write_files:
- path: /var/lib/rancher/credentialprovider/config.yaml
permissions: "0600"
encoding: b64
content: ${k3s_credential_provider_config}
- path: /var/lib/rancher/credentialprovider/bin/yc-credential-provider
permissions: "0700"
encoding: b64
content: ${k3s_credential_provider}
runcmd:
- |
export HOME=/root
curl https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash -s -- -i /tmp/yc -n
mv /tmp/yc/bin/yc /usr/bin/
yc config set instance-service-account true
yc config set cloud-id ${yc_cloud_id}
- |
DISK=/dev/disk/by-id/virtio-data
K3S_DIR=${k3s_dir}
mkdir -p $K3S_DIR
if ! blkid $DISK > /dev/null; then
mkfs.ext4 $DISK
fi
echo "$DISK $K3S_DIR ext4 defaults 0 0" >> /etc/fstab
mount -a
- |
curl -sfL https://get.k3s.io | INSTALL_K3S_CHANNEL=stable INSTALL_K3S_EXEC="server \
--server=https://${k3s_master_ip}:6443 \
--token=${k3s_token} \
--node-name=`hostname -f` \
--write-kubeconfig ${k3s_dir}/kubeconfig/config \
--image-credential-provider-bin-dir /var/lib/rancher/credentialprovider/bin \
--image-credential-provider-config /var/lib/rancher/credentialprovider/config.yaml \
--data-dir=${k3s_dir} \
--default-local-storage-path=${k3s_dir}/storage" sh -
- |
mkdir -p /home/ubuntu/.kube
cp ${k3s_dir}/kubeconfig/config /home/ubuntu/.kube/config
chown -R ubuntu:ubuntu /home/ubuntu/
+55
View File
@@ -0,0 +1,55 @@
#cloud-config
ssh_pwauth: false
package_update: true
package_upgrade: true
growpart:
mode: growpart
devices: ["/"]
ignore_growroot_disabled: true
users:
- name: ubuntu
sudo: ALL=(ALL) NOPASSWD:ALL
lock_passwd: true
shell: /bin/bash
write_files:
- path: /var/lib/rancher/credentialprovider/config.yaml
permissions: "0600"
encoding: b64
content: ${k3s_credential_provider_config}
- path: /var/lib/rancher/credentialprovider/bin/yc-credential-provider
permissions: "0700"
encoding: b64
content: ${k3s_credential_provider}
runcmd:
- |
export HOME=/root
curl https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash -s -- -i /tmp/yc -n
mv /tmp/yc/bin/yc /usr/bin/
yc config set instance-service-account true
yc config set cloud-id ${yc_cloud_id}
- |
DISK=/dev/disk/by-id/virtio-data
K3S_DIR=${k3s_dir}
mkdir -p $K3S_DIR
if ! blkid $DISK > /dev/null; then
mkfs.ext4 $DISK
fi
echo "$DISK $K3S_DIR ext4 defaults 0 0" >> /etc/fstab
mount -a
- |
curl -sfL https://get.k3s.io | INSTALL_K3S_CHANNEL=stable INSTALL_K3S_EXEC="agent \
--server=https://${k3s_master_ip}:6443 \
--token=${k3s_token} \
--node-name=`hostname -f` \
--image-credential-provider-bin-dir /var/lib/rancher/credentialprovider/bin \
--image-credential-provider-config /var/lib/rancher/credentialprovider/config.yaml \
--data-dir=${k3s_dir}" sh -
- |
mkdir -p /home/ubuntu/.kube
cp ${k3s_dir}/kubeconfig/config /home/ubuntu/.kube/config
chown -R ubuntu:ubuntu /home/ubuntu/
+10
View File
@@ -0,0 +1,10 @@
apiVersion: kubelet.config.k8s.io/v1beta1
kind: CredentialProviderConfig
providers:
- name: yc-credential-provider
apiVersion: credentialprovider.kubelet.k8s.io/v1
matchImages:
- "cr.yandex"
- "cr.cloud.yandex.net"
- "container-registry.cloud.yandex.net"
defaultCacheDuration: "1h"
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -euo pipefail
command -v jq >/dev/null || { echo "jq required" >&2; exit 1; }
command -v yc >/dev/null || { echo "yc required" >&2; exit 1; }
req=$(cat)
image=$(printf '%s' "$req" | jq -r '.image // empty')
if [[ -z "$image" ]]; then
echo "no image in request" >&2
exit 1
fi
registry=${image%%/*}
if [[ -z "$registry" ]]; then
registry="$image"
fi
creds_json=$(printf '%s' "$image" | yc container docker-credential get 2>/dev/null) || {
echo "yc docker-credential failed" >&2
exit 1
}
username=$(printf '%s' "$creds_json" | jq -r '.Username // empty')
secret=$(printf '%s' "$creds_json" | jq -r '.Secret // empty')
if [[ -z "$username" || -z "$secret" ]]; then
echo "failed to parse yc output" >&2
exit 1
fi
jq -n \
--arg api "credentialprovider.kubelet.k8s.io/v1" \
--arg kind "CredentialProviderResponse" \
--arg reg "$registry" \
--arg user "$username" \
--arg pass "$secret" \
--arg cache "6h" \
--arg keyType "Registry" \
'{
apiVersion: $api,
kind: $kind,
auth: { ($reg): { username: $user, password: $pass } },
cacheDuration: $cache,
cacheKeyType: $keyType
}'
+23
View File
@@ -0,0 +1,23 @@
resource "yandex_compute_disk" "master_disk" {
name = "master_disk"
description = "Disk for master persistent storage"
zone = "ru-central1-d"
size = 10
type = "network-hdd"
block_size = 4096
allow_recreate = false
}
resource "yandex_compute_disk" "worker_minecraft_disk" {
name = "worker_minecraft_disk"
description = "Disk for worker persistent storage"
zone = "ru-central1-d"
size = 20
type = "network-ssd"
block_size = 4096
allow_recreate = false
}
+85
View File
@@ -0,0 +1,85 @@
resource "yandex_iam_service_account" "instance_group_sa" {
name = "instance-group-sa"
description = "Service account for instance group"
}
resource "yandex_resourcemanager_folder_iam_member" "instance_group_sa_compute_editor" {
folder_id = var.folder_id
role = "compute.editor"
member = "serviceAccount:${yandex_iam_service_account.instance_group_sa.id}"
}
resource "yandex_resourcemanager_folder_iam_member" "instance_group_sa_lb_editor" {
folder_id = var.folder_id
role = "load-balancer.editor"
member = "serviceAccount:${yandex_iam_service_account.instance_group_sa.id}"
}
resource "yandex_iam_service_account" "instance_group_vm_sa" {
name = "instance-group-vm-sa"
description = "Service account for instance group vm"
}
resource "yandex_iam_service_account_iam_binding" "instance_group_vm_sa_admin" {
service_account_id = yandex_iam_service_account.instance_group_vm_sa.id
role = "admin"
members = [
"serviceAccount:${yandex_iam_service_account.instance_group_sa.id}",
]
}
resource "yandex_iam_service_account" "master_vm_sa" {
name = "master-vm-sa"
description = "Service account for master vm"
}
resource "yandex_compute_instance_iam_binding" "master_vm_sa_editor" {
instance_id = yandex_compute_instance.master.id
role = "editor"
members = [
"serviceAccount:${yandex_iam_service_account.master_vm_sa.id}"
]
}
resource "yandex_resourcemanager_folder_iam_member" "master_vm_sa_viewer" {
folder_id = var.folder_id
role = "compute.viewer"
member = "serviceAccount:${yandex_iam_service_account.master_vm_sa.id}"
}
resource "yandex_resourcemanager_folder_iam_member" "master_vm_sa_oslogin" {
folder_id = var.folder_id
role = "compute.osAdminLogin"
member = "serviceAccount:${yandex_iam_service_account.master_vm_sa.id}"
}
resource "yandex_iam_service_account" "registry_push_sa" {
name = "registry-push-sa"
description = "Service account for CI"
}
resource "yandex_iam_service_account_key" "registry_push_sa_key" {
service_account_id = yandex_iam_service_account.registry_push_sa.id
description = "Key for CI"
}
resource "yandex_iam_service_account" "registry_pull_sa" {
name = "registry-pull-sa"
description = "Service account for CD"
}
resource "yandex_iam_service_account_key" "registry_pull_sa_key" {
service_account_id = yandex_iam_service_account.registry_pull_sa.id
description = "Key for CD"
}
resource "yandex_iam_service_account" "frontend_push_sa" {
name = "frontend-push-sa"
description = "Service account for frontend CD"
}
resource "yandex_iam_service_account_key" "frontend_push_sa_key" {
service_account_id = yandex_iam_service_account.frontend_push_sa.id
description = "Key for frontend CD"
}
+143
View File
@@ -0,0 +1,143 @@
resource "yandex_compute_instance" "master" {
name = "master"
hostname = "master"
description = "Salt, ansible and kubernetes master, provisions other nodes"
platform_id = "standard-v3"
zone = "ru-central1-d"
service_account_id = yandex_iam_service_account.master_vm_sa.id
allow_recreate = true
allow_stopping_for_update = true
labels = {
salt = "master"
ansible = "master"
}
resources {
cores = 2
memory = 2
core_fraction = 20
gpus = 0
}
boot_disk {
initialize_params {
image_id = data.yandex_compute_image.ubuntu_2404.id
description = "Boot disk for master"
block_size = 4096
size = 10
type = "network-hdd"
}
auto_delete = true
mode = "READ_WRITE"
}
secondary_disk {
disk_id = yandex_compute_disk.master_disk.id
device_name = "data"
mode = "READ_WRITE"
auto_delete = false
}
network_interface {
subnet_id = yandex_vpc_subnet.default_ru_central1_d.id
ip_address = local.master_ip
ipv4 = true
nat = true
dns_record {
fqdn = local.master_fqdn
ptr = true
ttl = 10
}
}
network_acceleration_type = "standard"
metadata = {
enable-oslogin = true
serial-port-enable = 1
install-unified-agent = 0
user-data = data.template_file.main_master_cloud_init.rendered
}
metadata_options {
aws_v1_http_endpoint = 1
aws_v1_http_token = 2
gce_http_endpoint = 1
gce_http_token = 1
}
placement_policy {
host_affinity_rules = []
placement_group_partition = 0
}
}
resource "yandex_compute_instance" "worker-minecraft" {
name = "worker-minecraft"
hostname = "worker-minecraft.internal"
description = "Worker node for Kubernetes cluster"
labels = {
"kubernetes" = "worker"
"role" = "worker"
"salt" = "minion"
}
service_account_id = yandex_iam_service_account.instance_group_vm_sa.id
platform_id = "standard-v3"
zone = "ru-central1-d"
resources {
core_fraction = 100
cores = 2
gpus = 0
memory = 8
}
boot_disk {
initialize_params {
image_id = data.yandex_compute_image.ubuntu_2404.id
description = "Boot disk"
size = 10
type = "network-hdd"
}
mode = "READ_WRITE"
}
secondary_disk {
disk_id = yandex_compute_disk.worker_minecraft_disk.id
device_name = "data"
mode = "READ_WRITE"
auto_delete = false
}
network_interface {
subnet_id = yandex_vpc_subnet.default_ru_central1_d.id
ipv4 = true
nat = false
}
network_acceleration_type = "standard"
metadata = {
enable-oslogin = true
serial-port-enable = 1
install-unified-agent = 0
user-data = data.template_file.worker_cloud_init.rendered
}
metadata_options {
aws_v1_http_endpoint = 1
aws_v1_http_token = 2
gce_http_endpoint = 1
gce_http_token = 1
}
placement_policy {
host_affinity_rules = []
placement_group_partition = 0
}
depends_on = [
yandex_compute_instance.master,
yandex_iam_service_account.instance_group_vm_sa
]
}
+233
View File
@@ -0,0 +1,233 @@
resource "yandex_compute_instance_group" "master" {
name = "master"
description = "Masters instance group for Kubernetes cluster"
service_account_id = yandex_iam_service_account.instance_group_sa.id
deletion_protection = true
labels = {
"kubernetes" = "cluster"
"role" = "control-plane"
}
instance_template {
name = "master-{instance.index_in_zone}-{instance.zone_id}"
hostname = "master-{instance.index_in_zone}.{instance.zone_id}"
labels = {
"instance-group" = "master"
"kubernetes" = "control-plane"
"salt" = "minion"
}
service_account_id = yandex_iam_service_account.instance_group_vm_sa.id
platform_id = "standard-v3"
resources {
core_fraction = 50
cores = 2
gpus = 0
memory = 4
}
boot_disk {
initialize_params {
image_id = data.yandex_compute_image.ubuntu_2404.id
description = "Boot disk"
size = 10
type = "network-hdd"
}
mode = "READ_WRITE"
}
secondary_disk {
device_name = "data"
initialize_params {
description = "Data disk"
size = 20
type = "network-hdd"
}
mode = "READ_WRITE"
}
network_interface {
network_id = yandex_vpc_network.default.id
subnet_ids = [
yandex_vpc_subnet.default_ru_central1_a.id,
yandex_vpc_subnet.default_ru_central1_b.id,
yandex_vpc_subnet.default_ru_central1_d.id,
]
nat = false
}
network_settings {
type = "STANDARD"
}
metadata_options {
aws_v1_http_endpoint = 1
aws_v1_http_token = 2
gce_http_endpoint = 1
gce_http_token = 1
}
metadata = {
enable-oslogin = true
serial-port-enable = 1
install-unified-agent = 0
user-data = data.template_file.master_cloud_init.rendered
}
}
scale_policy {
fixed_scale {
size = 2
}
}
allocation_policy {
zones = ["ru-central1-a", "ru-central1-b"]
}
deploy_policy {
max_creating = 3
max_deleting = 3
max_expansion = 0
max_unavailable = 1
startup_duration = 300
strategy = "proactive"
}
load_balancer {
target_group_name = "master-instance-group"
target_group_description = "Load balancer target group for k8s masters"
max_opening_traffic_duration = 600
ignore_health_checks = true
}
health_check {
healthy_threshold = 2
unhealthy_threshold = 2
interval = 2
timeout = 1
tcp_options {
port = 6443
}
}
max_checking_health_duration = 600
depends_on = [
yandex_compute_instance.master,
yandex_iam_service_account.instance_group_sa,
yandex_resourcemanager_folder_iam_member.instance_group_sa_compute_editor,
yandex_resourcemanager_folder_iam_member.instance_group_sa_lb_editor
]
}
resource "yandex_compute_instance_group" "worker" {
name = "worker"
description = "Workers instance group for Kubernetes cluster"
service_account_id = yandex_iam_service_account.instance_group_sa.id
deletion_protection = true
labels = {
"kubernetes" = "cluster"
"role" = "worker"
}
instance_template {
name = "worker-{instance.index_in_zone}-{instance.zone_id}"
hostname = "worker-{instance.index_in_zone}.{instance.zone_id}.internal"
labels = {
"instance-group" = "worker"
"kubernetes" = "worker"
"salt" = "minion"
}
service_account_id = yandex_iam_service_account.instance_group_vm_sa.id
platform_id = "standard-v3"
resources {
core_fraction = 50
cores = 2
gpus = 0
memory = 2
}
boot_disk {
initialize_params {
image_id = data.yandex_compute_image.ubuntu_2404.id
description = "Boot disk"
size = 10
type = "network-hdd"
}
mode = "READ_WRITE"
}
secondary_disk {
device_name = "data"
initialize_params {
description = "Data disk"
size = 20
type = "network-hdd"
}
mode = "READ_WRITE"
}
network_interface {
network_id = yandex_vpc_network.default.id
subnet_ids = [
yandex_vpc_subnet.default_ru_central1_a.id,
yandex_vpc_subnet.default_ru_central1_b.id,
yandex_vpc_subnet.default_ru_central1_d.id,
]
nat = false
}
network_settings {
type = "STANDARD"
}
metadata_options {
aws_v1_http_endpoint = 1
aws_v1_http_token = 2
gce_http_endpoint = 1
gce_http_token = 1
}
metadata = {
enable-oslogin = true
serial-port-enable = 1
install-unified-agent = 0
user-data = data.template_file.worker_cloud_init.rendered
}
}
scale_policy {
fixed_scale {
size = 0
}
}
allocation_policy {
zones = ["ru-central1-a", "ru-central1-b", "ru-central1-d"]
}
deploy_policy {
max_creating = 3
max_deleting = 3
max_expansion = 0
max_unavailable = 1
startup_duration = 300
strategy = "proactive"
}
load_balancer {
target_group_name = "worker-instance-group"
target_group_description = "Load balancer target group for k8s workers"
max_opening_traffic_duration = 600
ignore_health_checks = true
}
health_check {
healthy_threshold = 2
unhealthy_threshold = 2
interval = 2
timeout = 1
tcp_options {
port = 80
}
}
max_checking_health_duration = 600
depends_on = [
yandex_compute_instance.master,
yandex_iam_service_account.instance_group_sa,
yandex_resourcemanager_folder_iam_member.instance_group_sa_compute_editor,
yandex_resourcemanager_folder_iam_member.instance_group_sa_lb_editor
]
}
+36
View File
@@ -0,0 +1,36 @@
# resource "yandex_lb_network_load_balancer" "instance_group_lb" {
# name = "instance-group-lb"
# allow_zonal_shift = true
# deletion_protection = true
# listener {
# name = "test"
# port = 8080
# external_address_spec {
# # address = yandex_vpc_address.default.external_ipv4_address[0].address
# ip_version = "ipv4"
# }
# }
# attached_target_group {
# target_group_id = yandex_compute_instance_group.default.load_balancer[0].target_group_id
# healthcheck {
# name = "http"
# http_options {
# port = 8080
# path = "/ping"
# }
# }
# }
# }
resource "yandex_lb_target_group" "main_master" {
name = "main-master"
region_id = "ru-central1"
target {
subnet_id = yandex_vpc_subnet.default_ru_central1_d.id
address = yandex_compute_instance.master.network_interface.0.ip_address
}
}
+7
View File
@@ -0,0 +1,7 @@
locals {
master_ip = "10.4.0.3"
master_fqdn = "master."
k3s_data_dir = "/mnt/k3s"
k3s_channel = "stable"
}
+5
View File
@@ -0,0 +1,5 @@
resource "yandex_logging_group" "default" {
name = "default"
retention_period = "336h0m0s"
}
+3
View File
@@ -0,0 +1,3 @@
data "yandex_compute_image" "ubuntu_2404" {
family = "ubuntu-2404-lts-oslogin"
}
+80
View File
@@ -0,0 +1,80 @@
resource "yandex_vpc_network" "default" {
name = "default"
description = "Default network"
labels = {
default = true
}
}
resource "yandex_vpc_default_security_group" "default" {
network_id = yandex_vpc_network.default.id
description = "Default security group for default network"
egress {
protocol = "ANY"
v4_cidr_blocks = ["0.0.0.0/0"]
}
ingress {
protocol = "ANY"
v4_cidr_blocks = ["0.0.0.0/0"]
}
}
resource "yandex_vpc_subnet" "default_ru_central1_a" {
name = "default-ru-central1-a"
description = "Default subnet for ru-central1-a"
network_id = yandex_vpc_network.default.id
route_table_id = yandex_vpc_route_table.default.id
v4_cidr_blocks = ["10.1.0.0/16"]
zone = "ru-central1-a"
}
resource "yandex_vpc_subnet" "default_ru_central1_b" {
name = "default-ru-central1-b"
description = "Default subnet for ru-central1-b"
network_id = yandex_vpc_network.default.id
route_table_id = yandex_vpc_route_table.default.id
v4_cidr_blocks = ["10.2.0.0/16"]
zone = "ru-central1-b"
}
resource "yandex_vpc_subnet" "default_ru_central1_d" {
name = "default-ru-central1-d"
description = "Default subnet for ru-central1-d"
network_id = yandex_vpc_network.default.id
route_table_id = yandex_vpc_route_table.default.id
v4_cidr_blocks = ["10.4.0.0/16"]
zone = "ru-central1-d"
}
resource "yandex_vpc_address" "nlb" {
name = "nlb"
description = "External IP address for network lb"
deletion_protection = true
external_ipv4_address {
zone_id = "ru-central1-d"
}
}
resource "yandex_vpc_gateway" "default" {
name = "default"
description = "Default Gateway for all instances"
labels = {}
shared_egress_gateway {}
}
resource "yandex_vpc_route_table" "default" {
name = "default"
network_id = yandex_vpc_network.default.id
static_route {
destination_prefix = "0.0.0.0/0"
gateway_id = yandex_vpc_gateway.default.id
}
}
+34
View File
@@ -0,0 +1,34 @@
resource "yandex_storage_bucket" "frontend" {
bucket = var.domain
default_storage_class = "STANDARD"
force_destroy = false
max_size = 104857600
anonymous_access_flags {
config_read = false
list = true
read = true
}
}
resource "yandex_storage_bucket" "frontend_develop" {
bucket = "dev.${var.domain}"
default_storage_class = "STANDARD"
force_destroy = false
max_size = 104857600
anonymous_access_flags {
config_read = false
list = true
read = true
}
}
resource "yandex_storage_bucket_iam_binding" "frontend_bucket_push" {
bucket = yandex_storage_bucket.frontend.bucket
role = "storage.editor"
members = [
"serviceAccount:${yandex_iam_service_account.frontend_push_sa.id}",
]
}
+51
View File
@@ -0,0 +1,51 @@
output "registry_data" {
value = {
url = "cr.yandex/${yandex_container_registry.default.id}"
}
}
output "api_gateway_data" {
value = {
domain = yandex_api_gateway.frontend.domain
}
}
output "registry_push_sa_key" {
value = jsonencode({
id = yandex_iam_service_account_key.registry_push_sa_key.id
service_account_id = yandex_iam_service_account_key.registry_push_sa_key.service_account_id
created_at = yandex_iam_service_account_key.registry_push_sa_key.created_at
key_algorithm = yandex_iam_service_account_key.registry_push_sa_key.key_algorithm
public_key = yandex_iam_service_account_key.registry_push_sa_key.public_key
private_key = yandex_iam_service_account_key.registry_push_sa_key.private_key
})
sensitive = true
}
output "registry_pull_sa_key" {
value = jsonencode({
id = yandex_iam_service_account_key.registry_pull_sa_key.id
service_account_id = yandex_iam_service_account_key.registry_pull_sa_key.service_account_id
created_at = yandex_iam_service_account_key.registry_pull_sa_key.created_at
key_algorithm = yandex_iam_service_account_key.registry_pull_sa_key.key_algorithm
public_key = yandex_iam_service_account_key.registry_pull_sa_key.public_key
private_key = yandex_iam_service_account_key.registry_pull_sa_key.private_key
})
sensitive = true
}
output "frontend_push_sa_key" {
value = jsonencode({
id = yandex_iam_service_account_key.frontend_push_sa_key.id
service_account_id = yandex_iam_service_account_key.frontend_push_sa_key.service_account_id
created_at = yandex_iam_service_account_key.frontend_push_sa_key.created_at
key_algorithm = yandex_iam_service_account_key.frontend_push_sa_key.key_algorithm
public_key = yandex_iam_service_account_key.frontend_push_sa_key.public_key
private_key = yandex_iam_service_account_key.frontend_push_sa_key.private_key
})
sensitive = true
}
output "lets_encrypt_challenges" {
value = yandex_cm_certificate.default.challenges
}
+4
View File
@@ -0,0 +1,4 @@
resource "random_password" "k3s_token" {
length = 32
special = false
}
+13
View File
@@ -0,0 +1,13 @@
terraform {
required_providers {
yandex = {
source = "yandex-cloud/yandex"
}
}
required_version = ">= 0.13"
}
provider "yandex" {
max_retries = 3
insecure = false
}
+32
View File
@@ -0,0 +1,32 @@
resource "yandex_container_registry" "default" {
name = "default"
labels = {}
}
resource "yandex_container_registry_iam_binding" "default_push" {
registry_id = yandex_container_registry.default.id
role = "container-registry.images.pusher"
members = [
"serviceAccount:${yandex_iam_service_account.registry_push_sa.id}",
]
}
resource "yandex_container_registry_iam_binding" "default_pull" {
registry_id = yandex_container_registry.default.id
role = "container-registry.images.puller"
members = [
"serviceAccount:${yandex_iam_service_account.registry_pull_sa.id}",
]
}
resource "yandex_container_registry_iam_binding" "instance_group_vm_sa_pull" {
registry_id = yandex_container_registry.default.id
role = "container-registry.images.puller"
members = [
"serviceAccount:${yandex_iam_service_account.instance_group_vm_sa.id}",
]
}
+41
View File
@@ -0,0 +1,41 @@
data "template_file" "main_master_cloud_init" {
template = file("${path.module}/configs/cloud_init/main_master.yaml")
vars = {
k3s_credential_provider_config = base64encode(file("${path.module}/configs/k3s/credentialprovider.yaml"))
k3s_credential_provider = base64encode(file("${path.module}/configs/k3s/yc-credential-provider"))
k3s_dir = local.k3s_data_dir
k3s_token = random_password.k3s_token.result
yc_cloud_id = var.cloud_id
}
}
data "template_file" "master_cloud_init" {
template = file("${path.module}/configs/cloud_init/master.yaml")
vars = {
k3s_credential_provider_config = base64encode(file("${path.module}/configs/k3s/credentialprovider.yaml"))
k3s_credential_provider = base64encode(file("${path.module}/configs/k3s/yc-credential-provider"))
k3s_dir = local.k3s_data_dir
k3s_token = random_password.k3s_token.result
k3s_master_ip = local.master_ip
yc_cloud_id = var.cloud_id
}
}
data "template_file" "worker_cloud_init" {
template = file("${path.module}/configs/cloud_init/worker.yaml")
vars = {
k3s_credential_provider_config = base64encode(file("${path.module}/configs/k3s/credentialprovider.yaml"))
k3s_credential_provider = base64encode(file("${path.module}/configs/k3s/yc-credential-provider"))
k3s_dir = local.k3s_data_dir
k3s_token = random_password.k3s_token.result
k3s_master_ip = local.master_ip
yc_cloud_id = var.cloud_id
}
}
data "template_file" "api_gateway_spec" {
template = file("${path.module}/configs/api_gateway/spec.yaml")
vars = {
bucket_name = yandex_storage_bucket.frontend.bucket
}
}
+18
View File
@@ -0,0 +1,18 @@
variable "folder_id" {}
variable "cloud_id" {}
variable "resources_prefix" {
type = string
default = ""
}
variable "domain" {
description = "Domain to issue TLS certificates for with Let's Encrypt"
type = string
}
variable "ssh_public_key_path" {
description = "This public key will be placed on all nodes"
type = string
}
Executable
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
if [ -f ./.env ]; then
export $(grep -v '^#' ./.env | xargs)
else
echo ".env file not found!"
exit 1
fi
terraform init \
-backend-config="endpoint=${AWS_ENDPOINT}" \
-backend-config="bucket=${AWS_BUCKET}" \
-backend-config="key=${AWS_BUCKET_KEY}" \
-backend-config="use_path_style=${AWS_USE_PATH_STYLE}" \
-migrate-state
+17
View File
@@ -0,0 +1,17 @@
# This is just a template file, create environment variables when applying
# Below all environment variables
YC_TOKEN=
YC_CLOUD_ID=
YC_FOLDER_ID=
# Must be the same as YC_FOLDER_ID
TF_VAR_folder_id=
AWS_BUCKET=
AWS_BUCKET_KEY=
AWS_ENDPOINT=
AWS_USE_PATH_STYLE=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=
+9
View File
@@ -0,0 +1,9 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/yandex-cloud/yandex" {
version = "0.147.0"
hashes = [
"h1:Lqw21/fIKjYUzc+tIcy8nSa8YAbpiYpDrjBMFwbjRYQ=",
]
}
+13
View File
@@ -0,0 +1,13 @@
terraform {
backend "s3" {
use_lockfile = true
max_retries = 5
retry_mode = "adaptive"
skip_region_validation = true
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
skip_s3_checksum = true
}
}
+15
View File
@@ -0,0 +1,15 @@
resource "yandex_iam_service_account" "sa" {
name = "terraform-provider"
description = "Service account for Terraform"
}
resource "yandex_resourcemanager_folder_iam_member" "sa_admin" {
folder_id = var.folder_id
role = "admin"
member = "serviceAccount:${yandex_iam_service_account.sa.id}"
}
resource "yandex_iam_service_account_key" "sa_key" {
service_account_id = yandex_iam_service_account.sa.id
description = "Key for Terraform"
}
+11
View File
@@ -0,0 +1,11 @@
output "service_account_key_json" {
value = jsonencode({
id = yandex_iam_service_account_key.sa_key.id
service_account_id = yandex_iam_service_account_key.sa_key.service_account_id
created_at = yandex_iam_service_account_key.sa_key.created_at
key_algorithm = yandex_iam_service_account_key.sa_key.key_algorithm
public_key = yandex_iam_service_account_key.sa_key.public_key
private_key = yandex_iam_service_account_key.sa_key.private_key
})
sensitive = true
}
+13
View File
@@ -0,0 +1,13 @@
terraform {
required_providers {
yandex = {
source = "yandex-cloud/yandex"
}
}
required_version = ">= 0.13"
}
provider "yandex" {
max_retries = 3
insecure = false
}
+1
View File
@@ -0,0 +1 @@
variable "folder_id" {}