-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore(openstack): add OVHcloud example #27
Merged
tormath1
merged 1 commit into
flatcar:main
from
spnngl:chore/openstack/ovhcloud_example
Sep 27, 2024
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# Flatcar Provisioning Automation for OVHcloud | ||
|
||
This repository provides tools to automate Flatcar provisioning on [OVHcloud][ovhcloud] using [Terraform][terraform]. | ||
|
||
## Features | ||
|
||
- Minimal configuration required (demo deployment works with default settings w/o any customisation, just run `terraform apply`!). | ||
- Deploy one or multiple servers. | ||
- Per-server custom configuration via separate [container linux config][container-linux-config] files. | ||
|
||
## Prerequisites | ||
|
||
1. OVHcloud credentials: username, tenant name, password, authentication URL, region. | ||
|
||
## Disclaimer | ||
|
||
This Terraform code is tested against a [DevStack][devstack] instance, for actual OVHcloud deployment the "network" attribute might need to be modified. | ||
|
||
## HowTo | ||
|
||
This will create a server in 'RegionOne' using a medium instance size in the 'public' network with 'default' security group. | ||
See "Customisation" below for advanced settings. | ||
|
||
1. Clone the repo. | ||
2. Add credentials in a `terraform.tfvars` file, expected credentials name can be found in `provider.tf` | ||
3. Run | ||
```shell | ||
terraform init | ||
``` | ||
4. Edit [`server1.yaml`][server-1] and provide your own custom provisioning configuration in [container linux config][container-linux-config] syntax. | ||
5. Plan and apply. | ||
This will also auto-generate an SSH key pair to log in to the server after provisioning. | ||
The public key of that key pair will be registered with OpenStack. <br /> | ||
Invoke Terraform: | ||
```shell | ||
terraform plan | ||
terraform apply | ||
``` | ||
|
||
Terraform will print server information (name, ipv4 and v6) after deployment concluded. | ||
The deployment will create an SSH key pair in `.ssh/`. | ||
|
||
After provisioning concluded the private key of the key pair generated during provisioning can be used to connect to node. As mentioned in the disclaimer, the tested setup relies on DevStack so we need to proxy jump on the devstack instance before reaching the deployed instance: | ||
```shell | ||
ssh -J user@[DEVSTACK-IP] -i ./.ssh/provisioning_private_key.pem -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null core@[SERVER-IP] | ||
``` | ||
|
||
_NOTE_: | ||
* Server IP address can be found at any moment after deployment by running `terraform output` | ||
* If you update server configuration(s) in `server-configs` and re-run `terraform apply`, the instance will be **replaced**. | ||
Consider adding [`create_before_destroy`](https://www.terraform.io/docs/configuration/meta-arguments/lifecycle.html#syntax-and-arguments) to the `openstack_compute_instance_v2` resource in [`compute.tf`](compute.tf) to avoid services becoming unavailable during reprovisioning. | ||
* SSH key is passed twice: in the Butane configuration and in the `key_pair` argument of the instance - while the SSH keys in the Butane configuration are added by Ignition during the first boot of the instance, `key_pair` are processed by `[email protected]` (a.k.a [`afterburn`][afterburn]) from the OpenStack [metadata service][metadata-service]. It means that you can install SSH keys without Ignition and change them as wanted. | ||
|
||
### Customisation | ||
|
||
The provisioning automation can be customised via settings in [`terraform.tfvars`][terraform.tfvars]: | ||
- `cluster_name`: Descriptive name of your cluster, will be used to generate server names. | ||
`flatcar-terraform` by default. | ||
- `machines`: Add more machines to your deployment. | ||
Each machine name must be unique and requires a respective `[NAME].yaml` server configuration in [`server-configs`](server-configs). | ||
An example / default configuration for machine `server1` is provided with [`server1.yaml`](server-configs/server1.yaml). | ||
During provisioning, server names are generated by concatenating the cluster name and the machine name - the defaults above will create a single server named `flatcar-terraform-server1`. | ||
- `ssh_keys`: Additional SSH public keys to add to core user's `authorized_keys`. | ||
Note that during provisioning, a provisioning RSA key pair will be generated and stored in the local directory `.ssh/provisioning_private_key.pem` and `.ssh/provisioning_key.pub`, respectively. | ||
The private key can be used for ssh connections (`core` user) to all provisioned servers. | ||
- `release_channel`: Select one of "lts", "stable", "beta", or "alpha". | ||
Read more about channels [here](https://www.flatcar.org/releases). | ||
- `flatcar_version`: Select the desired Flatcar version for the given channel (default to "current", which is the latest). | ||
- `flavor_name`: The spec of the machine, it default to ds1G (1vCPU, 10GB of disk and 1GB of memory) | ||
- `ssh`: A boolean to create and attach a security group to the instances to allow SSH connections. (_NOTE_: At this moment, when creating an instance with this variable enabled then turning it off does not work as the security group is not firstly detached from the instance so the security group can't be deleted) | ||
|
||
[afterburn]: https://coreos.github.io/afterburn/ | ||
[container-linux-config]: https://www.flatcar.org/docs/latest/provisioning/config-transpiler/configuration/ | ||
[devstack]: https://opendev.org/openstack/devstack | ||
[metadata-service]: https://docs.openstack.org/nova/zed/user/metadata.html | ||
[ovhcloud]: https://www.ovhcloud.com/ | ||
[server-1]: server-configs/server1.yaml | ||
[terraform]: https://www.terraform.io/ | ||
[terraform-tfvars]: terraform.tfvars |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
# we let 'Nova' generate a new keypair. | ||
resource "openstack_compute_keypair_v2" "provisioning_keypair" { | ||
name = "Provisioning key for Flatcar cluster ${var.cluster_name}" | ||
} | ||
|
||
# keypair is saved locally for later SSH connections. | ||
resource "local_file" "provisioning_key" { | ||
filename = "${path.module}/.ssh/provisioning_private_key.pem" | ||
content = openstack_compute_keypair_v2.provisioning_keypair.private_key | ||
directory_permission = "0700" | ||
file_permission = "0400" | ||
} | ||
|
||
resource "local_file" "provisioning_key_pub" { | ||
filename = "${path.module}/.ssh/provisioning_key.pub" | ||
content = openstack_compute_keypair_v2.provisioning_keypair.public_key | ||
directory_permission = "0700" | ||
file_permission = "0440" | ||
} | ||
|
||
# Get the flavor ID | ||
data "openstack_compute_flavor_v2" "flatcar" { | ||
name = var.flavor_name | ||
} | ||
|
||
# We create the OVHcloud image | ||
# | ||
# XXX not supporting web_download + specify properties (notably | ||
# for block-storage to avoid using virtio-blk instead of SCSI) | ||
resource "openstack_images_image_v2" "flatcar" { | ||
name = "${var.cluster_name}-${var.release_channel}.${var.flatcar_version}" | ||
image_source_url = "https://${var.release_channel}.release.flatcar-linux.net/amd64-usr/${var.flatcar_version}/flatcar_production_openstack_image.img.gz" | ||
# XXX do not use it, OVH openstack seems to not handle this well :( | ||
# web_download = false | ||
verify_checksum = true | ||
decompress = true | ||
container_format = "bare" | ||
disk_format = "qcow2" | ||
protected = false | ||
hidden = false | ||
visibility = "private" | ||
|
||
# See: https://docs.openstack.org/glance/stein/admin/useful-image-properties.html | ||
# See: https://wiki.openstack.org/wiki/VirtDriverImageProperties | ||
properties = { | ||
architecture = "x86_64" | ||
image_original_user = "core" | ||
distro_family = "gentoo" | ||
os_distro = "gentoo" | ||
os_version = var.flatcar_version | ||
os_release_channel = var.release_channel | ||
os_arch = "amd64" | ||
os_type = "linux" | ||
# XXX OVHcloud supports 256 volumes by VM, use SCSI to be able to use this | ||
# feature. If you do not specify this it will fallback to virtio-blk and | ||
# you'll only be able to use 26 volumes (including the root one). | ||
hw_disk_bus = "scsi" | ||
hw_scsi_model = "virtio-scsi" | ||
hypervisor_type = "qemu" | ||
hw_qemu_guest_agent = true | ||
hw_vif_model = "virtio" | ||
hw_vif_multiqueue_enabled = true | ||
hw_time_hpet = true | ||
} | ||
|
||
timeouts { | ||
create = "5m" | ||
} | ||
} | ||
|
||
# 'instance' are the OpenStack instances created from the 'flatcar' image | ||
# using user data. | ||
resource "openstack_compute_instance_v2" "instance" { | ||
for_each = toset(var.machines) | ||
name = "${var.cluster_name}-${each.key}" | ||
image_id = openstack_images_image_v2.flatcar.id | ||
flavor_id = data.openstack_compute_flavor_v2.flatcar.id | ||
key_pair = openstack_compute_keypair_v2.provisioning_keypair.name | ||
|
||
network { | ||
name = "public" | ||
} | ||
|
||
user_data = data.ct_config.machine-ignitions[each.key].rendered | ||
|
||
security_groups = flatten([["default"], var.ssh ? [openstack_networking_secgroup_v2.ssh[0].name] : []]) | ||
} | ||
|
||
data "ct_config" "machine-ignitions" { | ||
for_each = toset(var.machines) | ||
strict = true | ||
content = file("${path.module}/server-configs/${each.key}.yaml") | ||
snippets = [ | ||
data.template_file.core_user.rendered | ||
] | ||
} | ||
|
||
data "template_file" "core_user" { | ||
template = file("${path.module}/core-user.yaml.tmpl") | ||
vars = { | ||
ssh_keys = jsonencode(concat(var.ssh_keys, [openstack_compute_keypair_v2.provisioning_keypair.public_key])) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
variant: flatcar | ||
version: 1.0.0 | ||
|
||
passwd: | ||
users: | ||
- name: core | ||
ssh_authorized_keys: ${ssh_keys} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
resource "openstack_networking_secgroup_v2" "ssh" { | ||
name = "ssh-terraform" | ||
description = "Allow SSH from the outside - Managed by Terraform" | ||
count = var.ssh ? 1 : 0 | ||
} | ||
|
||
resource "openstack_networking_secgroup_rule_v2" "ssh-ipv4" { | ||
direction = "ingress" | ||
ethertype = "IPv4" | ||
protocol = "tcp" | ||
port_range_min = 22 | ||
port_range_max = 22 | ||
remote_ip_prefix = "0.0.0.0/0" | ||
security_group_id = openstack_networking_secgroup_v2.ssh[0].id | ||
count = var.ssh ? 1 : 0 | ||
} | ||
|
||
resource "openstack_networking_secgroup_rule_v2" "ssh-ipv6" { | ||
direction = "ingress" | ||
ethertype = "IPv6" | ||
protocol = "tcp" | ||
port_range_min = 22 | ||
port_range_max = 22 | ||
remote_ip_prefix = "::/0" | ||
security_group_id = openstack_networking_secgroup_v2.ssh[0].id | ||
count = var.ssh ? 1 : 0 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
output "provisioning_public_key_file" { | ||
value = local_file.provisioning_key_pub.filename | ||
} | ||
|
||
output "provisioning_private_key_file" { | ||
value = local_file.provisioning_key.filename | ||
} | ||
|
||
output "ipv4" { | ||
value = { | ||
for key in var.machines : | ||
"${var.cluster_name}-${key}" => openstack_compute_instance_v2.instance[key].access_ip_v4 | ||
} | ||
} | ||
|
||
output "ipv6" { | ||
value = { | ||
for key in var.machines : | ||
"${var.cluster_name}-${key}" => openstack_compute_instance_v2.instance[key].access_ip_v6 | ||
} | ||
} | ||
|
||
output "name" { | ||
value = { | ||
for key in var.machines : | ||
"${var.cluster_name}-${key}" => openstack_compute_instance_v2.instance[key].name | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
terraform { | ||
required_version = ">= 0.14.0" | ||
required_providers { | ||
openstack = { | ||
source = "terraform-provider-openstack/openstack" | ||
version = "~> 1.48.0" | ||
} | ||
ct = { | ||
source = "poseidon/ct" | ||
version = "0.11.0" | ||
} | ||
template = { | ||
source = "hashicorp/template" | ||
version = "~> 2.2.0" | ||
} | ||
} | ||
} | ||
|
||
# Configure the OpenStack Provider | ||
# See: https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs | ||
# See: https://help.ovhcloud.com/csm/en-public-cloud-compute-terraform?id=kb_article_view&sysparm_article=KB0050797 | ||
provider "openstack" { | ||
user_name = var.user_name | ||
tenant_name = var.tenant_name | ||
password = var.password | ||
auth_url = var.auth_url | ||
region = var.region | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
variant: flatcar | ||
version: 1.0.0 | ||
|
||
# This is a simple NGINX example. | ||
# Replace the below with your own config. | ||
# Refer to https://www.flatcar.org/docs/latest/provisioning/config-transpiler/configuration/ for more information. | ||
|
||
systemd: | ||
units: | ||
- name: nginx.service | ||
enabled: true | ||
contents: | | ||
[Unit] | ||
Description=NGINX example | ||
After=docker.service | ||
Requires=docker.service | ||
[Service] | ||
TimeoutStartSec=0 | ||
ExecStartPre=-/usr/bin/docker rm --force nginx1 | ||
ExecStart=/usr/bin/docker run --name nginx1 --pull always --net host docker.io/nginx:1 | ||
ExecStop=/usr/bin/docker stop nginx1 | ||
Restart=always | ||
RestartSec=5s | ||
[Install] | ||
WantedBy=multi-user.target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
variable "machines" { | ||
type = list(string) | ||
description = "Machine names, corresponding to machine-NAME.yaml.tmpl files" | ||
default = [] | ||
} | ||
|
||
variable "cluster_name" { | ||
type = string | ||
description = "Cluster name used as prefix for the machine names" | ||
default = "terraform-flatcar" | ||
} | ||
|
||
variable "ssh_keys" { | ||
type = list(string) | ||
default = [] | ||
description = "Additional SSH public keys for user 'core'." | ||
} | ||
|
||
variable "release_channel" { | ||
type = string | ||
description = "Release channel" | ||
default = "stable" | ||
|
||
validation { | ||
condition = contains(["lts", "stable", "beta", "alpha"], var.release_channel) | ||
error_message = "release_channel must be lts, stable, beta, or alpha." | ||
} | ||
} | ||
|
||
variable "flavor_name" { | ||
type = string | ||
description = "The OpenStack flavor to use (a.k.a the spec of the instance)" | ||
default = "b2-7" | ||
} | ||
|
||
variable "flatcar_version" { | ||
type = string | ||
description = "The Flatcar version associated to the release channel" | ||
default = "current" | ||
} | ||
|
||
variable "user_name" { | ||
type = string | ||
description = "OpenStack username" | ||
} | ||
|
||
variable "tenant_name" { | ||
type = string | ||
description = "OpenStack tenant name" | ||
} | ||
|
||
variable "password" { | ||
type = string | ||
description = "OpenStack password" | ||
} | ||
|
||
variable "auth_url" { | ||
type = string | ||
description = "OpenStack authentication URL" | ||
} | ||
|
||
variable "region" { | ||
type = string | ||
description = "OpenStack region" | ||
default = "RegionOne" | ||
} | ||
|
||
variable "ssh" { | ||
type = bool | ||
description = "Allow SSH connection from the outside" | ||
default = false | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can just add a note to the OVH documentation to find those information?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some links to documentation but did not find a lot of useful things on OVHcloud :|