Infrastructure as Code: Provisioning Secure Python Clusters on OVH Using Terraform
OVHcloud Provider Configuration for Terraform
To provision resources on OVHcloud, we’ll leverage the official Terraform OVHcloud provider. This provider abstracts the OVHcloud API, allowing us to define our infrastructure declaratively. First, ensure you have Terraform installed. Then, configure the provider in your Terraform root module. This typically involves setting up authentication credentials. For production environments, it’s highly recommended to use environment variables or a dedicated service principal rather than hardcoding credentials.
The OVHcloud provider requires your OVHcloud Application Key, Secret, and Consumer Key. These can be generated through the OVHcloud control panel under “Your personal data” > “Security” > “API credentials”.
Authentication Methods
The most secure method for authentication involves setting environment variables. Terraform will automatically pick these up.
Environment Variables
Set the following environment variables before running Terraform commands:
export OVH_APPLICATION_KEY="your_application_key" export OVH_APPLICATION_SECRET="your_application_secret" export OVH_CONSUMER_KEY="your_consumer_key" export OVH_ENDPOINT="ovh-eu" # or ovh-us, ovh-ca, runabove-eu, runabove-ca
Terraform Provider Block
In your main.tf file, define the OVHcloud provider block. The region parameter specifies the OVHcloud region where resources will be deployed. For this example, we’ll use the European region.
terraform {
required_providers {
ovh = {
source = "ovh/ovh"
version = "~> 1.0" # Specify a version constraint
}
}
}
provider "ovh" {
endpoint = var.ovh_endpoint
}
variable "ovh_endpoint" {
description = "The OVHcloud API endpoint to use."
type = string
default = "ovh-eu"
}
Provisioning a Public Cloud Instance
Let’s define a basic Public Cloud instance. This will be a virtual machine running a specified image. We’ll configure its name, region, flavor (instance type), and image ID. For Python development, a common choice is a Linux distribution like Ubuntu or Debian.
Instance Definition
The ovh_cloud_project_instance resource is used to create a new instance. You’ll need to know the service_name of your OVHcloud Public Cloud project. You can find this in your OVHcloud control panel under “Public Cloud” > “Project management”.
resource "ovh_cloud_project_instance" "python_app_server" {
service_name = var.ovh_public_cloud_project_id
name = "python-app-server-01"
region = "GRA1" # Example: Strasbourg, France
flavor_name = "vps-ssd-2" # Example: 2 vCPU, 4 GB RAM, 50 GB SSD
image_id = "ubuntu-2004" # Example: Ubuntu 20.04 LTS
ssh_key_name = "my-terraform-key" # Name of an SSH key already added to your OVH account
user_data = file("cloud-init.yaml") # Optional cloud-init script
}
variable "ovh_public_cloud_project_id" {
description = "The ID of your OVHcloud Public Cloud project."
type = string
# Sensitive value, should be set via TF_VAR_ovh_public_cloud_project_id or .tfvars
}
SSH Key Management
For secure access, it’s crucial to manage SSH keys. You can either pre-register an SSH public key in your OVHcloud account or use Terraform to manage it. If you pre-register, ensure the ssh_key_name in the resource matches the name in your OVHcloud console.
Cloud-Init for Initial Setup
The user_data parameter allows you to pass a cloud-init script. This is invaluable for automating the initial setup of your instance, such as installing Python, necessary packages, and configuring security settings.
# cloud-init.yaml #cloud-config package_update: true packages: - python3 - python3-pip - python3-venv - git - ufw runcmd: - ufw allow ssh - ufw allow 8000/tcp # Example for a Python web app - ufw enable -y - echo "Python environment setup complete." >> /var/log/cloud-init-output.log
Securing the Python Cluster
Security is paramount. Beyond SSH key management, we’ll implement firewall rules and consider network segmentation.
OVHcloud Firewall Rules
The ovh_cloud_project_instance_ip_firewall resource allows you to define firewall rules for your instance’s IP address. This provides an additional layer of network security managed by OVHcloud.
resource "ovh_cloud_project_instance_ip_firewall" "python_app_firewall" {
service_name = var.ovh_public_cloud_project_id
instance_id = ovh_cloud_project_instance.python_app_server.id
ip_address = ovh_cloud_project_instance.python_app_server.public_ip
rules {
action = "allow"
protocol = "tcp"
destination_port = "22" # Allow SSH
source = "*"
}
rules {
action = "allow"
protocol = "tcp"
destination_port = "8000" # Allow web traffic for Python app
source = "*"
}
rules {
action = "deny"
protocol = "any"
destination_port = "any"
source = "*"
}
}
Instance-Level Firewall (UFW)
As demonstrated in the cloud-init.yaml, using a host-based firewall like UFW (Uncomplicated Firewall) on the instance itself is a best practice. This provides granular control over network traffic entering and leaving the VM.
Deploying Python Applications
With the infrastructure provisioned and secured, the next step is deploying your Python application. This can be automated using various CI/CD tools or by leveraging instance user data for simpler deployments.
Automated Deployment with User Data
For a more robust deployment, you’d typically integrate with a CI/CD pipeline. However, for demonstration, we can extend the cloud-init.yaml to pull and run an application.
# cloud-init.yaml (Extended)
#cloud-config
package_update: true
packages:
- python3
- python3-pip
- python3-venv
- git
- ufw
- nginx # For serving static files or as a reverse proxy
runcmd:
- ufw allow ssh
- ufw allow 8000/tcp
- ufw allow 80/tcp # For Nginx
- ufw enable -y
- echo "Python environment setup complete." >> /var/log/cloud-init-output.log
# Application Deployment
- git clone https://github.com/your-username/your-python-app.git /opt/your-python-app
- cd /opt/your-python-app
- python3 -m venv venv
- . venv/bin/activate
- pip install -r requirements.txt
- echo "Application deployed." >> /var/log/cloud-init-output.log
# Example: Running a simple Flask app (for demonstration)
# In production, use a proper WSGI server like Gunicorn or uWSGI
- echo "from flask import Flask; app = Flask(__name__); @app.route('/')\ndef hello(): return 'Hello from Python App!'; if __name__ == '__main__': app.run(host='0.0.0.0', port=8000)" > /opt/your-python-app/app.py
- nohup python3 /opt/your-python-app/app.py & # Simple background execution
# Example: Nginx configuration for reverse proxy (optional)
- echo "server { listen 80; server_name your_domain.com; location / { proxy_pass http://127.0.0.1:8000; }}" > /etc/nginx/sites-available/your-python-app
- ln -sf /etc/nginx/sites-available/your-python-app /etc/nginx/sites-enabled/
- rm /etc/nginx/sites-enabled/default # Remove default Nginx config
- systemctl restart nginx
Considerations for Production Deployments
For production, avoid running applications directly with nohup. Instead, use a production-grade WSGI server like Gunicorn or uWSGI, managed by a process supervisor such as systemd. The Nginx configuration shown is a basic example; a full setup would involve SSL termination, caching, and more sophisticated load balancing if multiple instances are deployed.
Managing State and Destroying Resources
Terraform maintains a state file that tracks your provisioned infrastructure. This file is crucial for updates and deletions. Ensure this state file is stored securely, ideally in a remote backend like an S3 bucket or Terraform Cloud.
Initialization and Application
Before applying changes, initialize your Terraform working directory:
terraform init
To see what Terraform plans to do, run:
terraform plan
To apply the changes and provision the infrastructure:
terraform apply
Destroying Resources
When you no longer need the infrastructure, you can destroy all resources managed by Terraform:
terraform destroy
This command will prompt for confirmation before deleting all created OVHcloud resources. Always exercise caution when running terraform destroy.