We are committed to provide service 24 X 7

Deals, Shopping, Training, Tools

24
1.422.853,15

Cart

Learn how to – How To Create LXC Containers using Terraform

Learn how to – How To Create LXC Containers using Terraform.

LXC, or Linux Containers is a userspace interface that provides containment features of the Linux kernel. LXC lets users create Linux containers which are as close as possible to a standard Linux installation but using the same Kernel as the host machine.

Terraform on the other hand is an Infrastructure as code tool used for automation of infrastructure deployment. Terraform is widely used with cloud providers to automate management of their infrastructure as it provides a wide range of mechanisms both for deployment and management of existing infrastructure.

Terraform uses configuration files which describes the components needed to run a single application or your entire datacenter, and desired state will be effected.

This guide we shall discuss how to use Terraform to deploy LXC containers on Ubuntu LTS. The pointers below highlight what we shall achieve at the end of this guide:

  • Install LXC on Ubuntu
  • Install Terraform on Ubuntu
  • Create LXC container using Terraform
  • Attach volume to a container using Terraform
  • Configure container network using Terraform

Let’s dive right in!

#1) Install LXC on Ubuntu / Debian

We need to install and initialize LXC on our Ubuntu host. Use the following steps:

  1. Update and upgrade your system:
sudo apt update && sudo apt -y full-upgrade
[ -f /var/run/reboot-required ] && sudo reboot -f

2. Install LXD / LXC

$ sudo snap install lxd
Setup snap "lxd" (23680) security profiles                                                                                                                                                           -
2022-10-13T20:02:46+03:00 INFO Waiting for conflicting change in progress: conflicting snap snapd with task "discard-snap"
lxd 5.6-794016a from Canonical✓ installed

Confirm installation:

$ which lxc
/snap/bin/lxc

$ which lxd
/snap/bin/lxd

3. Initialize LXC

Run the command below to initialize LXC and setup some defaults

sudo lxd init

You will have to answer some questions according to how you want to setup your LXC environment. e.g.

$ sudo lxd init
Would you like to use LXD clustering? (yes/no) [default=no]:  
Do you want to configure a new storage pool? (yes/no) [default=yes]:  
Name of the new storage pool [default=default]:  
Name of the storage backend to use (btrfs, dir, lvm, zfs, ceph) [default=zfs]:  
Create a new ZFS pool? (yes/no) [default=yes]:  
Would you like to use an existing empty disk or partition? (yes/no) [default=no]:  
Size in GB of the new loop device (1GB minimum) [default=5GB]:  
Would you like to connect to a MAAS server? (yes/no) [default=no]:  
Would you like to create a new local network bridge? (yes/no) [default=yes]:  
What should the new bridge be called? [default=lxdbr0]:  
What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:  
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:  
Would you like LXD to be available over the network? (yes/no) [default=no]:  
Would you like stale cached images to be updated automatically? (yes/no) [default=yes]  
Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]:

We now have an LXC/LXD infrastructure ready to start deployment of Linux Containers.

#2) Install Terraform on Ubuntu / Debian

Use the steps below to install Terraform on your host.

  1. Add the HashiCorp GPG key
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/hashicorp.gpg

2. Add HashiCorp repository

sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"

3. Install Terraform

sudo apt update
sudo apt install terraform

4. Enable tab completion

terraform -install-autocomplete
source ~/.bashrc||source ~/.zshrc

We have our terraform installed and ready to be used. You can verify installed version by using the command below:

$ terraform -version
Terraform v1.3.9
on linux_amd64

#3) Create LXC container using Terraform

The next step is to configure Terraform so we can use it to install LXC containers. We shall be using LXD Terraform provider to connect provision resources. Create a new terraform main.tf configuration file that will define the provider to be used.

tee main.tf<<EOF
terraform {
  required_providers {
    lxd = {
      source = "terraform-lxd/lxd"
    }
  }
}
EOF

Initialize to download provider and create necessary files.

$ terraform init
Initializing the backend...

Initializing provider plugins...
- Finding latest version of terraform-lxd/lxd...
- Installing terraform-lxd/lxd v1.9.1...
- Installed terraform-lxd/lxd v1.9.1 (self-signed, key ID 62BC1162214B5D1E)

Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

We have to update our main.tf file to add container resource definitions:

$ vim main.tf
terraform {
  required_providers {
    lxd = {
      source = "terraform-lxd/lxd"
    }
  }
}
provider "lxd" {
  generate_client_certificates = true
  accept_remote_certificate    = true
}

# Create LXC Container
resource "lxd_container" "server1" {
  name      = "server1"
  image     = "ubuntu:22.04"
  ephemeral = false
  profiles = ["default"]
}

Check what will be deployed buy running the command below:

$ terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # lxd_container.server1 will be created
  + resource "lxd_container" "server1" {
      + ephemeral        = false
      + id               = (known after apply)
      + image            = "ubuntu:22.04"
      + ip_address       = (known after apply)
      + ipv4_address     = (known after apply)
      + ipv6_address     = (known after apply)
      + mac_address      = (known after apply)
      + name             = "server1"
      + privileged       = false
      + profiles         = [
          + "default",
        ]
      + start_container  = true
      + status           = (known after apply)
      + target           = (known after apply)
      + type             = (known after apply)
      + wait_for_network = true
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Run the terraform apply command to apply the changes. You will be prompted to confirm if the script should proceed making changes.

$ terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # lxd_container.server1 will be created
  + resource "lxd_container" "server1" {
      + ephemeral        = false
      + id               = (known after apply)
      + image            = "ubuntu:22.04"
      + ip_address       = (known after apply)
      + ipv4_address     = (known after apply)
      + ipv6_address     = (known after apply)
      + mac_address      = (known after apply)
      + name             = "server1"
      + privileged       = false
      + profiles         = [
          + "default",
        ]
      + start_container  = true
      + status           = (known after apply)
      + target           = (known after apply)
      + type             = (known after apply)
      + wait_for_network = true
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes
lxd_container.server1: Creating...
lxd_container.server1: Still creating... [10s elapsed]
lxd_container.server1: Still creating... [20s elapsed]
lxd_container.server1: Still creating... [30s elapsed]
lxd_container.server1: Still creating... [40s elapsed]
lxd_container.server1: Still creating... [50s elapsed]
lxd_container.server1: Creation complete after 52s [id=server1]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

You can now check and see if your instance is created:

$ lxc list

Sample output:

You can access the created server using the command below:

$ lxc exec server1 /bin/bash
root@server1:~# ip ad
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
4: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:f3:29:d9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.21.25.225/24 metric 100 brd 10.21.25.255 scope global dynamic eth0
       valid_lft 3443sec preferred_lft 3443sec
    inet6 fd42:bf39:6882:ccb3:216:3eff:fef3:29d9/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 3508sec preferred_lft 3508sec
    inet6 fe80::216:3eff:fef3:29d9/64 scope link
       valid_lft forever preferred_lft forever

Remember to use a name of your choice for server1 during container creation and accessing the container.

#4) Attaching Volume to a container using Terraform

You can also create a volume and attach it to your container. We will first create a storage pool of type=dir and assign a path on the server where the for the pool.

We then will create a volume then attach the volume to our container. We then will define where the new volume shall be mounted on the container.

Update your main.tf file to have all the components discussed above as shown below:

terraform {
  required_providers {
    lxd = {
      source = "terraform-lxd/lxd"
    }
  }
}

provider "lxd" {
  generate_client_certificates = true
  accept_remote_certificate    = true
}

resource "lxd_storage_pool" "pool1" {
  name = "mypool"
  driver = "dir"
  config = {
    source = "/var/lib/lxd/storage-pools/mypool"
  }
}

resource "lxd_volume" "volume1" {
  name = "myvolume"
  pool = "${lxd_storage_pool.pool1.name}"
}

resource "lxd_container" "server1" {
  name      = "server1"
  image     = "ubuntu:22.04"
  ephemeral = false
  profiles = ["default"]

device {
    name = "volume1"
    type = "disk"
    properties = {
      path = "/opt/"
      source = "${lxd_volume.volume1.name}"
      pool = "${lxd_storage_pool.pool1.name}"
    }
   }                                                                                                                            
  }

Run the terraform plan command to see what changes will be effected when you apply.

$ terraform apply

lxd_container.server1: Refreshing state... [id=server1]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create
  ~ update in-place

Terraform will perform the following actions:

  # lxd_container.server1 will be updated in-place
  ~ resource "lxd_container" "server1" {
        id               = "server1"
        name             = "server1"
        # (14 unchanged attributes hidden)

      + device {
          + name       = "volume1"
          + properties = {
              + "path"   = "/opt/"
              + "pool"   = "mypool"
              + "source" = "myvolume"
            }
          + type       = "disk"
        }
    }

  # lxd_storage_pool.pool1 will be created
  + resource "lxd_storage_pool" "pool1" {
      + config = {
          + "source" = "/var/lib/lxd/storage-pools/mypool"
        }
      + driver = "dir"
      + id     = (known after apply)
      + name   = "mypool"
    }

  # lxd_volume.volume1 will be created
  + resource "lxd_volume" "volume1" {
      + expanded_config = (known after apply)
      + id              = (known after apply)
      + name            = "myvolume"
      + pool            = "mypool"
      + type            = "custom"
    }

Plan: 2 to add, 1 to change, 0 to destroy.

Then apply the changes.

$ terraform apply
....
Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

lxd_storage_pool.pool1: Creating...
lxd_storage_pool.pool1: Creation complete after 0s [id=mypool]
lxd_volume.volume1: Creating...
lxd_volume.volume1: Creation complete after 0s [id=mypool/myvolume/custom]
lxd_container.server1: Modifying... [id=server1]
lxd_container.server1: Modifications complete after 0s [id=server1]

Apply complete! Resources: 2 added, 1 changed, 0 destroyed.

In the screenshot below, we can see that the volume has been attached to /opt of the container.

#5) Configure container network using Terraform

The last part of this article is about how to setup container network and use it to provision conainers.

This part involved creating a profile, LXC uses namespaces(profiles) do define the containers. We will create a new profile configure the attributes including the creation of a new network bridge and DHCP server. We shall then provision our containers in the new namespace/profile to make use of the new changes we shall have made.

Modify your main.tf file and add the following:

resource "lxd_network" "new_default" {
  name = "new_default"

  config = {
    "ipv4.address" = "10.150.19.1/24"
    "ipv4.nat"     = "true"
    "ipv6.address" = "fd42:474b:622d:259d::1/64"
    "ipv6.nat"     = "true"
  }
}

resource "lxd_profile" "profile1" {
  name = "profile1"

  device {
    name = "eth0"
    type = "nic"

    properties = {
      nictype = "bridged"
      parent  = "${lxd_network.new_default.name}"
    }
  }
 device {
    type = "disk"
    name = "root"

    properties = {
      pool = "default"
      path = "/"
    }
  }
}

We have named our profile as profile1. A network bridge has also been created.

We will need to modify the existing container state to use the new profile if we wish to use the new network subnet. The new main.tf configuration file should be as below:

terraform {
  required_providers {
    lxd = {
      source = "terraform-lxd/lxd"
      version = "1.5.0"
    }
  }
}


resource "lxd_storage_pool" "pool1" {
  name = "mypool"
  driver = "dir"
  config = {
    source = "/var/lib/lxd/storage-pools/mypool"
  }
}

resource "lxd_volume" "volume1" {
  name = "myvolume"
  pool = "${lxd_storage_pool.pool1.name}"
}

resource "lxd_network" "new_default" {
  name = "new_default"

  config = {
    "ipv4.address" = "10.150.19.1/24"
    "ipv4.nat"     = "true"
    "ipv6.address" = "fd42:474b:622d:259d::1/64"
    "ipv6.nat"     = "true"
  }
}

resource "lxd_profile" "profile1" {
  name = "profile1"
  device {
    name = "eth0"
    type = "nic"

    properties = {
      nictype = "bridged"
      parent  = "${lxd_network.new_default.name}"
    }
  }
device {
    type = "disk"
    name = "root"

    properties = {
      pool = "default"
      path = "/"
    }
  }
}

resource "lxd_container" "server1" {
  name      = "server1"
  image     = "ubuntu:18.04"
  ephemeral = false
  profiles = ["profile1"]

device {
    name = "volume1"
    type = "disk"
    properties = {
      path = "/opt/"
      source = "${lxd_volume.volume1.name}"
      pool = "${lxd_storage_pool.pool1.name}"
    }
  }
}

Apply the changes then check if your container network has changed.

root@host:~/terraform# lxc list
+---------+---------+---------------------+-----------------------------------------------+------------+-----------+
|  NAME   |  STATE  |        IPV4         |                     IPV6                      |    TYPE    | SNAPSHOTS |
+---------+---------+---------------------+-----------------------------------------------+------------+-----------+
| server1 | RUNNING | 10.150.19.13 (eth0) | fd42:474b:622d:259d:216:3eff:fe8c:e44e (eth0) | PERSISTENT | 0         |
+---------+---------+---------------------+-----------------------------------------------+------------+-----------+

We have achieved all the objectives for this article as stated in the beginning. I hope you were able to follow keenly and deploy your first container using Terraform. Check out these interesting articles about Terraform:

24
1.422.853,15

Cart