Terraformを使ってUbuntu 20.04上にVMを作成する

概要

前回の記事ではUbuntu 20.04にKVMをインストールし、virt-installコマンドでVMを作成するところまで行いました。

しかし、VMの構築を都度virt-installコマンドで実施するのは面倒なため、今回の内容でterraformのlibvirt providerである dmacvicar/terraform-provider-libvirtを使ってVMの構築をできるようにします。

なお、terraformについての説明などは省くので、ご了承ください。

terraformをインストールする

バージョン管理が面倒なので、asdfを使ってterraformをインストールしてterraformのバージョン管理を手軽に行えるようにしておく。

$ sudo apt install curl git
$ git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.9.0 # branchは適宜最新のものを指定してください。
# 下記の内容を .bashrc に追加します。
echo '. $HOME/.asdf/asdf.sh' >> ~/.bashrc
echo '. $HOME/.asdf/completions/asdf.bash' >> ~/.bashrc
$ asdf --version
v0.9.0-9ee24a3
$ asdf plugin-list-all # インストール可能なプラグインのリストを確認できる
$ asdf plugin add terraform
$ asdf install terraform latest # とりあえず最新を入れる
$ asdf global terraform 1.1.5
$ terraofmr version
Terraform v1.1.5
on linux_amd64

terraformでvmを作成するための下準備

もし、pool関連でファイル権限エラーが出た場合は、以下のURLの対応を試してみてください。

dmacvicar/terraform-provider-libvirt を使って Ubuntu 20.04 のVMを作成する

terraformのlibvirt-providerを通してKVM上にVMを作成し、コンソール接続でVMにログインするまで実施します。

プロジェクト用のディレクトリを作成します。

$ mkdir -p ~/project/terraform-libvirt-proj && cd ~/project/terraform-libvirt-proj

terraformのファイルを用意します。ファイル名はmain.tfとします。各リソースの詳細はlibvirt-providerドキュメントを参照してください。

terraform {
  required_version = "~> 1.1.0"
  required_providers {
      libvirt = {
          source = "dmacvicar/libvirt"
          version = "0.6.14"
      }
  }
}

provider "libvirt" {
    uri = "qemu:///system"
}

resource "libvirt_volume" "ubuntu-qcow2" {
    name = "ubuntu-qcow2"
    source = "https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img"
    format = "qcow2"
}

resource "libvirt_volume" "ubuntu-node" {
    name = "ubuntu-base"
    base_volume_id = libvirt_volume.ubuntu-qcow2.id
    size = 536870912000
}

data "template_file" "user_data" {
    template = file("${path.module}/cloud_init.cfg")
}

resource "libvirt_cloudinit_disk" "commoninit" {
    name = "common.iso"
    user_data = data.template_file.user_data.rendered
}

resource "libvirt_domain" "k8s-node" {
    name = "worker01"
    memory = "8096"
    vcpu = 4

    cloudinit = libvirt_cloudinit_disk.commoninit.id

    network_interface {
        bridge = "br0"
    }

    console {
        type = "pty"
        target_port = "0"
        target_type = "serial"
    }

    console {
        type = "pty"
        target_type = "virtio"
        target_port = "1"
    }

    disk {
        volume_id = libvirt_volume.ubuntu-node.id
    }

    graphics {
        type = "spice"
        listen_type = "address"
        autoport = true

    }
}

続いて、こちらのファイルはcloud-initと呼ばれるクラウドサービスなどでOS起動時に各種設定を自動で行えるようにする仕組みです。今回はこの仕組みを使ってログイン時のユーザ設定を行います。

まず、cloud_init.cfgファイルを用意します。\<username>および\<password>は適宜お好みの文字列に置き換えてください。

#cloud-config
system_info:
  default_user:
    name: <username>
    home: /home/<username>
password: <password>
chpasswd: { expire: False }
hostname: local

# configure sshd to allow users logging in using password
# rather than just keys
ssh_pwauth: True

下記コマンドでterraformプロジェクトを初期化します

$ terraform init

上記コマンド実行後、作成したファイルの構成情報をもとにVMを作成していきます。

$ terraform apply -auto-approve

正常に完了すると、VMが作成されていることが確認できます。

$ virsh list --all
 Id   Name       State
--------------------------
1    worker01   running

$ virsh console worker01

ネットワークの設定を行う

VMの作成まできるようになりましたが、ホストのネットワーク設定についてはまだ何も行われていません。 実際に、ホストで ip addressコマンドでホストのIPアドレス情報を表示してもアドレスが割り当てられていることは確認できません。

前の章でmain.tfに記載したlibvirt_cloudinit_diskリソースにnetwork_configパラメータがあるので、こちらに作成するネットワーク設定のcloud-init設定ファイルを指定します。

設定ファイルを修正する前に、一度VMを削除しておきましょう。

$ terraform destroy -auto-approve

では、network_config.cfgファイルを作成し、下記の内容で保存しましょう。IPアドレスなどはお使いの環境に併せて適宜修正してください。

version: 2
ethernets:
  ens3:
    dhcp4: false
    addresses:
      - 192.168.11.249/24
    gateway4: 192.168.11.1
    nameservers:
      addresses: [192.168.11.1, 8.8.8.8]

続いて、main.tfの内容を修正します。ネットワーク設定用のtemplate_filelibvirt_cloudinit_disknetwork_configが指定されています。

terraform {
  required_version = "~> 1.1.0"
  required_providers {
      libvirt = {
          source = "dmacvicar/libvirt"
          version = "0.6.14"
      }
  }
}

provider "libvirt" {
    uri = "qemu:///system"
}

resource "libvirt_volume" "ubuntu-qcow2" {
    name = "ubuntu-qcow2"
    source = "https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img"
    format = "qcow2"
}

resource "libvirt_volume" "ubuntu-node" {
    name = "ubuntu-base"
    base_volume_id = libvirt_volume.ubuntu-qcow2.id
    size = 536870912000
}

data "template_file" "user_data" {
    template = file("${path.module}/cloud_init.cfg")
}

data "template_file" "network_config" {
    template = file("${path.module}/network_config.cfg")
}

resource "libvirt_cloudinit_disk" "commoninit" {
    name = "common.iso"
    user_data = data.template_file.user_data.rendered
    network_config = data.template_file.network_config.rendered
}

resource "libvirt_domain" "k8s-node" {
    name = "worker01"
    memory = "8096"
    vcpu = 4

    cloudinit = libvirt_cloudinit_disk.commoninit.id

    network_interface {
        bridge = "br0"
    }

    console {
        type = "pty"
        target_port = "0"
        target_type = "serial"
    }

    console {
        type = "pty"
        target_type = "virtio"
        target_port = "1"
    }

    disk {
        volume_id = libvirt_volume.ubuntu-node.id
    }

    graphics {
        type = "spice"
        listen_type = "address"
        autoport = true

    }
}

設定ファイルを修正し終わったら前回同様にterraform applyコマンドを使ってVMを作成しましょう。

$ terraform apply -auto-approve

ログイン後、ip addressコマンドでネットワークインターフェースの割り当てを確認すると、network_config.cfgファイルで指定したIPアドレスが割り当てられていることが確認できると思います。

参考資料