This Terraform project demonstrates deploying infrastructure on Virtuozzo Hybrid Infrastructure (OpenStack-compatible) including:
- Kubernetes Cluster: Managed HA Kubernetes cluster (3 masters, 3 workers)
- PostgreSQL Cluster: 3 VMs for PostgreSQL cluster (infrastructure only)
- Jumphost: Bastion host with public IP for secure access
- Private Network: Internal network for secure inter-VM communication
Internet
|
| (Floating IP)
v
Jumphost (Ubuntu 24.04)
|
| SSH Tunnel / Private Network
v
Private Network (172.16.10.0/24)
|
+-- PostgreSQL-1 (172.16.10.x)
+-- PostgreSQL-2 (172.16.10.x)
+-- PostgreSQL-3 (172.16.10.x)
+-- Kubernetes Cluster (HA)
+-- 3 Master Nodes
+-- 3 Worker Nodes
This project uses GitLab CI/CD with GitLab-managed Terraform state for automation and remote state storage.
State files are stored in GitLab's infrastructure using HTTP backend:
- Secure state storage with encryption at rest and in transit
- Automatic state locking via HTTP POST/DELETE
- State versioning and history
- Built-in integration with GitLab CI/CD
- No external dependencies or S3 compatibility issues
- Free unlimited state storage
Automated pipeline with three stages:
-
Validate Stage (on MR and main branch)
terraform fmt -check: Check formattingterraform validate: Validate configuration
-
Plan Stage (on MR and main branch)
terraform plan: Generate execution plan- Saves plan artifact for apply stage
- Plan output visible in pipeline logs
-
Apply Stage (on main branch only)
terraform apply: Execute changes- Requires manual approval in GitLab UI
- Uses saved plan from plan stage
-
Push Repository to GitLab
- Create new project on GitLab
- Add GitLab as remote:
git remote add gitlab <gitlab-url> - Push code:
git push gitlab main
-
Configure CI/CD Variables
- In GitLab project, go to Settings > CI/CD > Variables
- Add OpenStack credentials (mark as Protected and Masked):
TF_VAR_auth_urlTF_VAR_user_nameTF_VAR_passwordTF_VAR_domain_nameTF_VAR_tenant_nameTF_VAR_region
- Add SSH public key:
TF_VAR_public_key(content of~/.ssh/id_rsa.pub)
- Add GitLab token:
GITLAB_ACCESS_TOKEN(Personal Access Token withapiscope)
- See Configure Terraform Variables and
GITLAB_SETUP.mdfor details
-
Enable Terraform State in GitLab
- GitLab automatically manages Terraform state
- No additional configuration required
- State visible at: Infrastructure > Terraform states
-
Local Development Setup
- Get your GitLab project ID (visible in project overview)
- Create GitLab Personal Access Token (Scopes: api, read_api)
- Initialize with backend config (see CLAUDE.md)
-
Virtuozzo Cloud Management Platform Access
- Account with appropriate permissions
- OpenStack API credentials
-
Tools
- Terraform >= 1.0.0
- OpenStack CLI (optional, for verification)
- SSH key pair
- Git
-
GitLab Account (for CI/CD and remote state)
- Free tier includes unlimited state storage
- 400 CI/CD minutes per month
- See Remote State & CI/CD above
-
Knowledge Required
- Basic Terraform understanding
- SSH and networking concepts
- Kubernetes basics (for cluster usage)
- Git and GitLab CI/CD basics
- Log in to Virtuozzo Cloud Management Platform
- Navigate to My Account > OpenStack Credentials tab
- Click Generate Application Credentials
- Save the following values:
- Auth URL (Keystone endpoint)
- Application Credential ID
- Application Credential Secret
Before deploying, check what resources are available in your Virtuozzo environment:
# Set environment variables (optional, for OpenStack CLI)
export OS_AUTH_URL="https://your-endpoint:5000/v3"
export OS_APPLICATION_CREDENTIAL_ID="your-id"
export OS_APPLICATION_CREDENTIAL_SECRET="your-secret"
# Check available flavors (VM sizes)
openstack flavor list
# Check available images
openstack image list
# Check Kubernetes cluster templates
openstack coe cluster template list
# Check external network name
openstack network list --externalCopy the example configuration and update with your values:
cp terraform.tfvars.example terraform.tfvarsEdit terraform.tfvars with your actual values:
# Required: OpenStack credentials
auth_url = "https://your-virtuozzo-endpoint:5000/v3"
application_credential_id = "your-application-credential-id"
application_credential_secret = "your-application-credential-secret"
# Update these based on your environment
k8s_template_name = "kubernetes-v1.28.2" # From: openstack coe cluster template list
external_network_name = "public" # From: openstack network list --external
image_name = "Ubuntu 24.04" # From: openstack image list# Initialize Terraform
terraform init
# Review the execution plan
terraform plan
# Apply the configuration
terraform apply
# Confirm with 'yes' when promptedThe deployment will take approximately 15-30 minutes, primarily for the Kubernetes cluster creation.
After successful deployment, Terraform will output connection information:
# View outputs
terraform output
# Get jumphost public IP
terraform output jumphost_public_ip
# Get summary
terraform output deployment_summary# Get the public IP
JUMPHOST_IP=$(terraform output -raw jumphost_public_ip)
# SSH to jumphost
ssh ubuntu@$JUMPHOST_IP# From your local machine (via jumphost)
JUMPHOST_IP=$(terraform output -raw jumphost_public_ip)
POSTGRES_IP="172.16.10.10" # Replace with actual IP from outputs
ssh -J ubuntu@$JUMPHOST_IP ubuntu@$POSTGRES_IP
# Or use ProxyJump in one command
ssh -o ProxyJump=ubuntu@$JUMPHOST_IP ubuntu@$POSTGRES_IP# Download kubeconfig (requires OpenStack CLI)
openstack coe cluster config demo-k8s-cluster
# Set KUBECONFIG environment variable
export KUBECONFIG=./config
# Verify cluster access
kubectl get nodes
# Check cluster info
kubectl cluster-infoAdd to ~/.ssh/config:
Host virtuozzo-jumphost
HostName <jumphost-public-ip>
User ubuntu
IdentityFile ~/.ssh/id_rsa
Host postgres-*
User ubuntu
ProxyJump virtuozzo-jumphost
IdentityFile ~/.ssh/id_rsa
Host postgres-1
HostName 172.16.10.10
Host postgres-2
HostName 172.16.10.11
Host postgres-3
HostName 172.16.10.12
Then simply use:
ssh virtuozzo-jumphost
ssh postgres-1- Private Network: 172.16.10.0/24
- DNS Nameservers: 8.8.8.8, 8.8.4.4
- Router: Connected to external network for internet access
- Security Groups:
- Jumphost: SSH (22) from anywhere (configurable)
- PostgreSQL: SSH (22) from private network, PostgreSQL (5432) within cluster
| Resource | Flavor | Count | Purpose |
|---|---|---|---|
| Jumphost | m1.small | 1 | Bastion host with public access |
| PostgreSQL | m1.medium | 3 | PostgreSQL cluster nodes |
| K8s Masters | m1.medium | 3 | Kubernetes control plane (HA) |
| K8s Workers | m1.medium | 3 | Kubernetes worker nodes |
Jumphost Security Group:
- Inbound: SSH (22/tcp) from allowed CIDR
- Inbound: ICMP (ping)
- Outbound: All traffic
PostgreSQL Security Group:
- Inbound: SSH (22/tcp) from private network
- Inbound: PostgreSQL (5432/tcp) from same security group
- Inbound: ICMP (ping)
- Outbound: All traffic
Edit terraform.tfvars:
# PostgreSQL cluster (must be odd number >= 3)
postgres_count = 5
# Kubernetes cluster
k8s_master_count = 3 # Odd number for HA
k8s_node_count = 5 # Any number >= 1jumphost_flavor = "m1.small"
postgres_flavor = "m1.large"
k8s_master_flavor = "m1.large"
k8s_worker_flavor = "m1.medium"For better security, limit SSH access to your IP:
allowed_ssh_cidr = "203.0.113.100/32" # Your public IPprivate_subnet_cidr = "10.0.1.0/24"Instead of terraform.tfvars, you can use environment variables:
export TF_VAR_auth_url="https://your-endpoint:5000/v3"
export TF_VAR_application_credential_id="your-id"
export TF_VAR_application_credential_secret="your-secret"For team collaboration, configure remote state:
# backend.tf
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "virtuozzo/demo/terraform.tfstate"
region = "us-east-1"
}
}Add to main.tf:
resource "openstack_blockstorage_volume_v3" "postgres_data" {
count = var.postgres_count
name = "${var.project_name}-postgres-${count.index + 1}-data"
size = 100 # GB
}
resource "openstack_compute_volume_attach_v2" "postgres_data" {
count = var.postgres_count
instance_id = openstack_compute_instance_v2.postgres[count.index].id
volume_id = openstack_blockstorage_volume_v3.postgres_data[count.index].id
}Kubernetes cluster creation can take 15-30 minutes. Monitor progress:
# Watch Terraform output
terraform apply
# Or check via OpenStack CLI
openstack coe cluster list
openstack coe cluster show demo-k8s-cluster# Verify jumphost is running
terraform output jumphost_public_ip
# Test connectivity
ping $(terraform output -raw jumphost_public_ip)
# Check security group rules
openstack security group list
openstack security group rule list <jumphost-sg-id>
# Verify SSH key
ssh-add -l# List available images
openstack image list | grep -i ubuntu
# List available flavors
openstack flavor list
# Update terraform.tfvars with correct names# List available templates
openstack coe cluster template list
# Update k8s_template_name in terraform.tfvarsAfter deployment, you can view various outputs:
# All outputs
terraform output
# Specific outputs
terraform output jumphost_public_ip
terraform output postgres_private_ips
terraform output k8s_api_address
terraform output deployment_summary
# JSON format
terraform output -jsonTo destroy all created resources:
# Preview what will be destroyed
terraform plan -destroy
# Destroy infrastructure
terraform destroy
# Confirm with 'yes' when promptedWarning: This will permanently delete:
- All VMs (jumphost, PostgreSQL nodes)
- Kubernetes cluster
- Networks and routers
- Security groups
- Floating IPs
.
├── main.tf # Root module - orchestrates all modules
├── providers.tf # OpenStack provider configuration
├── variables.tf # Root module variable definitions
├── outputs.tf # Root module outputs
├── versions.tf # Terraform version constraints
├── terraform.tfvars.example # Example configuration
├── .gitignore # Git ignore rules
├── README.md # This file
│
└── modules/ # Reusable Terraform modules
├── network/ # Network infrastructure module
│ ├── main.tf # Networks, subnets, routers, security groups
│ ├── variables.tf # Network module variables
│ └── outputs.tf # Network module outputs
│
├── compute/ # Compute resources module
│ ├── main.tf # VMs, keypairs, floating IPs
│ ├── variables.tf # Compute module variables
│ └── outputs.tf # Compute module outputs
│
└── kubernetes/ # Kubernetes cluster module
├── main.tf # K8s cluster via Magnum
├── variables.tf # Kubernetes module variables
└── outputs.tf # Kubernetes module outputs
The project is organized into three main modules:
-
Network Module (
modules/network/)- Creates private network and subnet
- Configures router with external gateway
- Sets up security groups for jumphost and PostgreSQL
- Manages network isolation and connectivity
-
Compute Module (
modules/compute/)- Creates and manages SSH keypairs
- Deploys jumphost VM with floating IP
- Deploys PostgreSQL cluster VMs
- Handles instance configuration and metadata
-
Kubernetes Module (
modules/kubernetes/)- Creates managed Kubernetes cluster via Magnum
- Configures HA setup with multiple masters and workers
- Manages cluster lifecycle and configuration
- Reusability: Modules can be reused across different environments or projects
- Maintainability: Each module has a clear, focused responsibility
- Testability: Modules can be tested independently
- Scalability: Easy to add, remove, or modify specific components
- Separation of Concerns: Network, compute, and Kubernetes logic are isolated
- Team Collaboration: Different teams can work on different modules
You can use these modules in other Terraform projects:
# Example: Use just the network module in another project
module "my_network" {
source = "git::https://your-repo.com/terraform-virtuozzo.git//modules/network"
project_name = "my-project"
environment = "production"
external_network_name = "public"
private_subnet_cidr = "10.0.1.0/24"
dns_nameservers = ["8.8.8.8"]
allowed_ssh_cidr = "203.0.113.0/24"
}After deploying the infrastructure:
-
Setup PostgreSQL Cluster:
- Install PostgreSQL on the 3 VMs
- Configure streaming replication
- Setup connection pooling (pgBouncer)
- Configure failover (Patroni/etcd)
-
Kubernetes Workloads:
- Deploy applications to K8s cluster
- Setup ingress controller
- Configure persistent volumes
- Deploy monitoring (Prometheus/Grafana)
-
Security Hardening:
- Rotate SSH keys regularly
- Enable firewall on VMs
- Setup VPN for jumphost access
- Configure audit logging
-
Monitoring & Backup:
- Setup monitoring (Zabbix, Prometheus)
- Configure automated backups
- Setup alerting
- Document disaster recovery procedures
- Virtuozzo Hybrid Infrastructure Documentation
- OpenStack Terraform Provider
- Terraform Documentation
- OpenStack CLI Guide
This demo project is provided as-is for educational and demonstration purposes.
For issues related to:
- Virtuozzo Platform: Contact Virtuozzo support
- This Terraform Code: Open an issue in the repository
- OpenStack Provider: Check provider documentation