Skip to main content

OpenStack Heat: Single and Multi VM Deployments with Heat Manifest and Cloud-Init, Assign Floating IP with Script, Create Network with Router

1880 words·
OpenStack OpenStack Heat Cloud-init
Table of Contents
OpenStack - This article is part of a series.
Part 3: This Article

Source Openrc / Login
#

# CD into "stack" home directory
cd ~/devstack

# Verify the openrc file
ls *openrc*

# Loads the environment variables set in openrc into current shell: Admin user & project
source openrc admin admin

SSH Key
#

I’m using the following SSH key for all the Heat manifest examples in this blog post.

Create SSH Key
#

# Create SSH key
openstack keypair create key_vm-1> id_rsa_vm-1

# Change permissions
chmod 600 id_rsa_vm-1

Verify the SSH Key
#

# Verify the key
openstack keypair list

# Shell output:
+----------+-------------------------------------------------+------+
| Name     | Fingerprint                                     | Type |
+----------+-------------------------------------------------+------+
| key_vm-1 | 20:f3:61:fb:9e:b9:e8:85:93:4f:e2:8f:46:4f:b5:57 | ssh  |
+----------+-------------------------------------------------+------+

Network
#

I’m using the following network for all the Heat manifest examples in this blog post.

Create Network & Subnet: CLI Version
#

# Create a new network "jkw_net-01"
openstack network create jkw_net-01
# Create a new subnet for the network "jkw_net-01"
openstack subnet create --network jkw_net-01 --subnet-range 10.8.5.0/24 \
--allocation-pool start=10.8.5.11,end=10.8.5.254 --dns-nameserver 1.1.1.1 \
--gateway 10.8.5.1 jkw_subnet-01

Create Network & Subnet: Heat Version
#

# Create Heat manifest configuration
vi network_setup.yaml
heat_template_version: 2016-10-14

description: Create network & subnet

resources:
  jkw_net_01:
    type: OS::Neutron::Net
    properties:
      name: jkw_net-01

  jkw_subnet_01:
    type: OS::Neutron::Subnet
    properties:
      name: jkw_subnet-01
      network_id: { get_resource: jkw_net_01 }
      cidr: 10.8.5.0/24
      gateway_ip: 10.8.5.1
      dns_nameservers:
        - 1.1.1.1
        - 8.8.8.8
      allocation_pools:
        - start: 10.8.5.200
          end: 10.8.5.254
# Deploy the networking resources
openstack stack create -t network_setup.yaml centos-network

Find External Network
#

# List networks: Copy public / external networkID
openstack network list --long -c ID -c Name -c "Router Type" -c Subnets -f table

# Shell output:
+--------------------------------------+----------+----------------------------------------------------------------------------+-------------+
| ID                                   | Name     | Subnets                                                                    | Router Type |
+--------------------------------------+----------+----------------------------------------------------------------------------+-------------+
| 2055b2ed-3f65-4174-b208-45bcf55cdafc | private  | 300c9671-793f-4f8c-9ff5-bc27e43f64ee, 68c5d11c-27ef-4076-ae2c-9f875e67232e | Internal    |
| 26911f76-db56-4aee-91a8-a17fb38204de | shared   | ef937ded-002a-41c4-b65f-dce94ed24a6b                                       | Internal    |
| 5e12176f-a048-460f-83cf-d2b9cd395b10 | jkw_net  | 54e303b7-f665-4b24-92b1-4d306676e3b9                                       | Internal    |
| b7c160e7-f189-4a8d-998b-860ea5e47a64 | heat-net | 6be302d4-6c25-470a-a86f-bc84973891ba                                       | Internal    |
| c68d5a39-e0bb-426d-8442-ffc60c1d2623 | public   | 6e8dfa7e-e901-43b3-8ab9-f8b8090e1715, e9108251-e10e-444f-90af-92edd4559181 | External    |
+--------------------------------------+----------+----------------------------------------------------------------------------+-------------+

Create Network Router
#

This step is neccessary to add a floating IP the to network port.

# Create a router
openstack router create jkw_router-01

# Set the router gateway to the external network: Define external network ID
openstack router set jkw_router-01 --external-gateway c68d5a39-e0bb-426d-8442-ffc60c1d2623

# Add the internal network subnet to the router
openstack router add subnet jkw_router-01 jkw_subnet-01

Delete Network Resources
#

Delete the router:

# Remove the Gateway from the Router
openstack router unset --external-gateway jkw_router-01

# Remove the Subnet Interface from the Router
openstack router remove subnet jkw_router-01 jkw_subnet-01

# Delete the Router
openstack router delete jkw_router-01

Delete the network: Heat version

# Delete the networking resources
openstack stack delete --yes centos-network

Delete the network: Manual version

# Delete the Subnet
openstack subnet delete jkw_subnet-01

# Delete the Network
openstack network delete jkw_net-01

CentOS Cloud Image
#

I’m using the following Image for all the Heat manifest examples in this blog post.

Download Cloud Image
#

# Download the CentOS Stream cloud image
curl -O https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-x86_64-9-latest.x86_64.qcow2

Upload Image to OpenStack
#

# Upload the ISO file to OpenStack
openstack image create \
    --container-format bare \
    --disk-format qcow2 \
    --property hw_disk_bus=scsi \
    --property hw_scsi_model=virtio-scsi \
    --property os_type=linux \
    --property os_distro=rhel \
    --property os_admin_user=root \
    --public \
    --file CentOS-Stream-GenericCloud-x86_64-9-latest.x86_64.qcow2 \
    CentOS-Cloud

Heat Manifest Examples
#

Create Heat Manifest
#

Single VM CentOS Stack
# Create manifest configuration
vi centos-stack.yaml
heat_template_version: 2013-05-23

description: CentOS VM Deployment

resources:
  vm_instance-1:
    type: OS::Nova::Server
    properties:
      name: "centos-vm1"
      image: "CentOS-Cloud"
      flavor: m1.small
      key_name: "key_vm-1"
      networks:
        - network: "jkw_net-01"
      security_groups:
        - { get_resource: vm_security-group }
      user_data_format: RAW
      user_data:
        get_resource: server_init
      tags: ["centos-stack"]

  vm_security-group:
    type: OS::Neutron::SecurityGroup
    properties:
      name: "centos-security-group"
      description: "Allow SSH and Ping"
      rules:
        - protocol: tcp
          port_range_min: 22
          port_range_max: 22
          remote_ip_prefix: 0.0.0.0/0
        - protocol: icmp
          remote_ip_prefix: 0.0.0.0/0


  server_init:
    type: OS::Heat::MultipartMime
    properties:
      parts:
      - config: {get_resource: one_init}

  one_init:
    type: OS::Heat::CloudConfig
    properties:
      cloud_config:
        package_update: true
        package_upgrade: true
        packages:
          - httpd
Multi VM RedHat Stack: Define Each VM
# Create manifest configuration
vi centos-stack.yaml
heat_template_version: 2013-05-23

description: CentOS VM Deployment

resources:
  vm_instance-1:
    type: OS::Nova::Server
    properties:
      name: "centos-vm1"
      image: "CentOS-Cloud"
      flavor: m1.small
      key_name: "key_vm-1"
      networks:
        - network: "jkw_net-01"
      security_groups:
        - { get_resource: vm_security-group }
      user_data_format: RAW
      user_data:
        get_resource: server_init
      tags: ["centos-stack"]

  vm_instance-2:
    type: OS::Nova::Server
    properties:
      name: "centos-vm2"
      image: "CentOS-Cloud"
      flavor: m1.small
      key_name: "key_vm-1"
      networks:
        - network: "jkw_net-01"
      security_groups:
        - { get_resource: vm_security-group }
      user_data_format: RAW
      user_data:
        get_resource: server_init
      tags: ["centos-stack"]

  vm_instance-3:
    type: OS::Nova::Server
    properties:
      name: "centos-vm3"
      image: "CentOS-Cloud"
      flavor: m1.small
      key_name: "key_vm-1"
      networks:
        - network: "jkw_net-01"
      security_groups:
        - { get_resource: vm_security-group }
      user_data_format: RAW
      user_data:
        get_resource: server_init
      tags: ["centos-stack"]


  vm_security-group:
    type: OS::Neutron::SecurityGroup
    properties:
      name: "centos-security-group"
      description: "Allow SSH and Ping"
      rules:
        - protocol: tcp
          port_range_min: 22
          port_range_max: 22
          remote_ip_prefix: 0.0.0.0/0
        - protocol: icmp
          remote_ip_prefix: 0.0.0.0/0


  server_init:
    type: OS::Heat::MultipartMime
    properties:
      parts:
      - config: {get_resource: one_init}

  one_init:
    type: OS::Heat::CloudConfig
    properties:
      cloud_config:
        package_update: true
        package_upgrade: true
        packages:
          - httpd
Multi VM RedHat Stack: Define VMs Via Resource Group
# Create manifest configuration
vi centos-stack.yaml
heat_template_version: 2013-05-23

description: CentOS VM Deployment

resources:
  vm_instance_group:
    type: OS::Heat::ResourceGroup
    properties:
      count: 3  # Number of instances
      resource_def:
        type: OS::Nova::Server
        properties:
          name: "%index%-centos-vm"
          image: "CentOS-Cloud"
          flavor: "m1.small"
          key_name: "key_vm-1"
          networks:
            - network: "jkw_net-01"
          security_groups:
            - { get_resource: vm_security-group }
          user_data_format: RAW
          user_data:
            get_resource: server_init
          tags: ["centos-stack"]


  vm_security-group:
    type: OS::Neutron::SecurityGroup
    properties:
      name: "centos-security-group"
      description: "Allow SSH and Ping"
      rules:
        - protocol: tcp
          port_range_min: 22
          port_range_max: 22
          remote_ip_prefix: 0.0.0.0/0
        - protocol: icmp
          remote_ip_prefix: 0.0.0.0/0


  server_init:
    type: OS::Heat::MultipartMime
    properties:
      parts:
      - config: {get_resource: one_init}

  one_init:
    type: OS::Heat::CloudConfig
    properties:
      cloud_config:
        package_update: true
        package_upgrade: true
        packages:
          - httpd
  • "%index%-rhel-vm" The Heat variable auto-increments for each instance name: 0-rhel-vm, 1-rhel-vm, …

  • count Define the number of VMs


Deploy the Heat Manifest
#

# Deploy the manifest: Stack name "centos-stack"
openstack stack create -t centos-stack.yaml centos-stack

Verify the VM Deployment
#

# List the VMs via it's tags
openstack server list --tags centos-stack
Single VM CentOS Stack
# Shell output:
+--------------------------------------+------------+--------+-----------------------+--------------+----------+
| ID                                   | Name       | Status | Networks              | Image        | Flavor   |
+--------------------------------------+------------+--------+-----------------------+--------------+----------+
| a45e70b2-c368-45c7-a6c0-3c3a53e7d2a3 | centos-vm1 | ACTIVE | jkw_net-01=10.8.5.240 | CentOS-Cloud | m1.small |
+--------------------------------------+------------+--------+-----------------------+--------------+----------+
Multi VM CentOS Stack: Define Each VM
# Shell output:
+--------------------------------------+------------+--------+-----------------------+--------------+----------+
| ID                                   | Name       | Status | Networks              | Image        | Flavor   |
+--------------------------------------+------------+--------+-----------------------+--------------+----------+
| 12505464-134c-4fa6-bc95-e161f181b487 | centos-vm1 | ACTIVE | jkw_net-01=10.8.5.231 | CentOS-Cloud | m1.small |
| 43294664-9fe3-4c82-81e7-17e3349610b0 | centos-vm3 | ACTIVE | jkw_net-01=10.8.5.239 | CentOS-Cloud | m1.small |
| a95f8b92-24f4-4ebc-8eae-beae9a99441f | centos-vm2 | ACTIVE | jkw_net-01=10.8.5.234 | CentOS-Cloud | m1.small |
+--------------------------------------+------------+--------+-----------------------+--------------+----------+
Multi VM CentOS Stack: Define VM Via Resource Group
# Shell output:
+--------------------------------------+-------------+--------+-----------------------+--------------+----------+
| ID                                   | Name        | Status | Networks              | Image        | Flavor   |
+--------------------------------------+-------------+--------+-----------------------+--------------+----------+
| 6610fddb-5a02-457f-9e07-82d87b3a58d8 | 2-centos-vm | ACTIVE | jkw_net-01=10.8.5.248 | CentOS-Cloud | m1.small |
| 3287c0ad-213d-469c-acc9-f73a0c9ffd0a | 0-centos-vm | ACTIVE | jkw_net-01=10.8.5.239 | CentOS-Cloud | m1.small |
| 3fb622f9-636b-4152-b323-a3eec53f211b | 1-centos-vm | ACTIVE | jkw_net-01=10.8.5.242 | CentOS-Cloud | m1.small |
+--------------------------------------+-------------+--------+-----------------------+--------------+----------+

Assign Floating IP with Script
#

Use the following script to assign a floating IP to the vm rhel-vm1:

# Create a file for the script
vi floating-ip.sh

# Change permission
chmod +x floating-ip.sh
#!/bin/bash

# Set VM name
VM_NAME="centos-vm1"

# Set network name
NETWORK_NAME="public"

# Get VM ID
VM_ID=$(openstack server list --name $VM_NAME -f value -c ID)

# Get port ID
PORT_ID=$(openstack port list --device-id $VM_ID -f value -c ID)

# Create floating IP and assign it to the VM
FLOATING_IP=$(openstack floating ip create $NETWORK_NAME -f value -c floating_ip_address --port $PORT_ID)

echo "Floating IP $FLOATING_IP has been assigned to VM $VM_NAME"
# Assign floating IP
./floating-ip.sh

# Shell output:
Floating IP 192.168.30.247 has been assigned to VM centos-vm1

Access the VM
#

# SSH into VM
ssh -i id_rsa_vm-1 cloud-user@192.168.30.247

Verify the cloud-init script has run:

# Switch to root user
sudo su

# Check cloud-init logs: grep for httpd
grep -C 1 "httpd" /var/log/cloud-init.log
# Remove the host key if necessary (useful in playground scenarios)
ssh-keygen -f "/opt/stack/.ssh/known_hosts" -R "192.168.30.246"

Delete the Stack
#

# Delete the Deployment
openstack stack delete --yes centos-stack



Cloud Image Examples
#

CirrOS
#

CirrOS is a minimal Linux distribution that is included by default with DevStack. It’s great for testing and debugging.

Heat Manifest
#

CirrOS Deployment: Default Security Group
# Create manifest configuration
vi heat-cirros-vm.yaml
heat_template_version: 2013-05-23

description: CirrOS VM Deployment

resources:
  vm_instance-1:
    type: OS::Nova::Server
    properties:
      name: "cirros-vm1"
      image: "cirros-0.6.2-x86_64-disk"
      flavor: m1.tiny
      key_name: "key_vm-1"
      networks:
        - network: "jkw_net-01"
      security_groups:
        - default
      tags: ["cirros-stack"]
CirrOS Deployment: Dedicated Security Group
# Create manifest configuration
vi heat-cirros-vm.yaml
heat_template_version: 2013-05-23

description: CirrOS VM Deployment

resources:
  vm_security-group-1:
    type: OS::Neutron::SecurityGroup
    properties:
      name: "vm-security-group"
      description: "Allow SSH and Ping"
      rules:
        - protocol: tcp
          port_range_min: 22
          port_range_max: 22
          remote_ip_prefix: 0.0.0.0/0
        - protocol: icmp
          remote_ip_prefix: 0.0.0.0/0

  vm_instance-1:
    type: OS::Nova::Server
    properties:
      name: "cirros-vm1"
      image: "cirros-0.6.2-x86_64-disk"
      flavor: m1.tiny
      key_name: "key_vm-1"
      networks:
        - network: "jkw_net-01"
      security_groups:
        - { get_resource: vm_security-group-1 }
      tags: ["cirros-stack"]

Deploy, Update the Stack
#

# Deploy the manifest: Stack name "cirros-stack"
openstack stack create -t heat-cirros-vm.yaml cirros-stack

# Update the sack
openstack stack update -t heat-cirros-vm.yaml cirros-stack

Verify the VM Deployment
#

# List VM details
openstack server list --name cirros-vm1

# Shell output:
+--------------------------------------+------------+--------+-----------------------+--------------------------+---------+
| ID                                   | Name       | Status | Networks              | Image                    | Flavor  |
+--------------------------------------+------------+--------+-----------------------+--------------------------+---------+
| 169b3f85-9f2e-4a8e-9bfe-b9edd8c0d297 | cirros-vm1 | ACTIVE | jkw_net-01=10.8.5.230 | cirros-0.6.2-x86_64-disk | m1.tiny |
+--------------------------------------+------------+--------+-----------------------+--------------------------+---------+

Delete the Stack
#

# Delete the deployment / stack
openstack stack delete --yes cirros-stack

Ubuntu Cloud Image
#

Download Image
#

# Download Ubuntu 22 cloud image
curl -O https://cloud-images.ubuntu.com/releases/jammy/release/ubuntu-22.04-server-cloudimg-amd64.img

Upload Image to OpenStack
#

# Upload the ISO file to OpenStack
openstack image create \
    --container-format bare \
    --disk-format qcow2 \
    --property hw_disk_bus=scsi \
    --property hw_scsi_model=virtio-scsi \
    --property os_type=linux \
    --property os_distro=ubuntu \
    --property os_admin_user=ubuntu \
    --public \
    --file ubuntu-22.04-server-cloudimg-amd64.img \
    ubuntu-22-cloud

Heat Manifest
#

# Create manifest configuration
vi heat-ubuntu-vm.yaml
heat_template_version: 2013-05-23

description: Ubuntu VM Deployment

resources:
  vm_security-group-1:
    type: OS::Neutron::SecurityGroup
    properties:
      name: "vm-security-group"
      description: "Allow SSH and Ping"
      rules:
        - protocol: tcp
          port_range_min: 22
          port_range_max: 22
          remote_ip_prefix: 0.0.0.0/0
        - protocol: icmp
          remote_ip_prefix: 0.0.0.0/0

  vm_instance-1:
    type: OS::Nova::Server
    properties:
      name: "ubuntu-vm1"
      image: "ubuntu-22-cloud"
      flavor: m1.small
      key_name: "key_vm-1"
      networks:
        - network: "jkw_net-01"
      security_groups:
        - { get_resource: vm_security-group-1 }
      user_data_format: RAW
      user_data:
        get_resource: server_init

Deploy Stack
#

# Deploy the manifest: Stack name "ubuntu-stack"
openstack stack create -t heat-ubuntu-vm.yaml ubuntu-stack

Verify the VM Deployment
#

# List VM details: Wait till Status jumps to "ACTIVE"
openstack server list --name ubuntu-vm1

Delete the Stack
#

# Delete the Deployment
openstack stack delete --yes ubuntu-stack

RedHat Cloud image
#

Download Image
#

Download the Image from the RedHat website:
https://access.redhat.com/downloads/content/rhel

Upload Image to OpenStack
#

# Upload the ISO file to OpenStack
openstack image create \
    --container-format bare \
    --disk-format qcow2 \
    --property hw_disk_bus=scsi \
    --property hw_scsi_model=virtio-scsi \
    --property os_type=linux \
    --property os_distro=rhel \
    --property os_admin_user=root \
    --public \
    --file rhel-9.4-x86_64-kvm.qcow2 \
    rhel-9.4-cloud

Heat Manifest
#

# Create manifest configuration
vi heat-redhat-stack.yaml
heat_template_version: 2013-05-23

description: RHEL VM Deployment

resources:
  vm_instance-1:
    type: OS::Nova::Server
    properties:
      name: "rhel-vm1"
      image: "rhel-9.4-cloud"
      flavor: m1.small
      key_name: "key_vm-1"
      networks:
        - network: "jkw_net-01"
      security_groups:
        - { get_resource: vm_security-group }
      user_data_format: RAW
      user_data:
        get_resource: server_init
      tags: ["rhel-stack"]

  vm_security-group:
    type: OS::Neutron::SecurityGroup
    properties:
      name: "rhel-security-group"
      description: "Allow SSH and Ping"
      rules:
        - protocol: tcp
          port_range_min: 22
          port_range_max: 22
          remote_ip_prefix: 0.0.0.0/0
        - protocol: icmp
          remote_ip_prefix: 0.0.0.0/0

Deploy Stack
#

# Deploy the manifest: Stack name "redhat-stack"
openstack stack create -t heat-redhat-stack.yaml redhat-stack

Verify the VM Deployment
#

# List the VMs via it's tags
openstack server list --tags rhel-stack

Delete the Stack
#

# Delete the Deployment
openstack stack delete --yes redhat-stack
OpenStack - This article is part of a series.
Part 3: This Article