TL;DR
構成を定義した後、次のコマンドで Kubernetes cluster が作成できる:
$ terraform init
$ terraform apply -auto-approve
$ docker run --rm -it \
--mount type=bind,source="$(pwd)"/inventory,dst=/inventory \
--mount type=bind,source="$(pwd)"/generate_inventory.py,dst=/kubespray/generate_inventory.py \
--mount type=bind,source="$(pwd)"/terraform.tfstate,dst=/kubespray/terraform.tfstate \
--mount type=bind,source="${HOME}"/.ssh/id_rsa,dst=/root/.ssh/id_rsa \
quay.io/kubespray/kubespray:v2.23.1 bash
# Inside a container
$ ansible-playbook -i generate_inventory.py cluster.yml
サンプルコード:
概要
Terraform + Kubespray で KVM 上に Kubernetes クラスタを構築する。大まかな手順は次の通り。
- Terraform で VM を作成
- 生成された
terraform.tfstate
から inventory 情報を抽出 - inventory 情報と Kuberspray の Ansible playbooks から Kubernetes クラスタを作成
ネットワーク構成は下図の通り:
クラスタの Public ネットワーク (eth0
側) と、Ansible が SSH ログインするためのネットワーク (eth1
側) を分けている。
前提条件
- terraform
- Container runtime (docker, podman, nerdctl, etc.)
- KVM packages
- qemu-kvm
- libvirt
- crdtools
- nmcli
- (Host OS: Ubuntu 22.04)
手順
Host の事前設定
ネットワーク設定
仮想ブリッジ br0
を作成し、既存のホストのネットワークインタフェース (例:enp1s0
) を br0
に接続する。このようにしておくと、後に作成する VM を br0
に接続させることにより、LAN 内にある任意のホストから VM に直接アクセスできるようになる。
図で説明すると、次のように Before から After へ設定を変更する。
次ようなスクリプトを作成し、パラメータを書き換え、ホスト上で実行する。
HOST_IP=192.168.8.10
CIDR_PREFIX=24
GATEWAY=192.168.8.1
DNS=192.168.8.1
NWIF=enp1s0
# ブリッジを作成
nmcli con add type bridge con-name br0 ifname br0
# ブリッジに既存の NIC 設定を与える
nmcli con modify br0 \
ipv4.method manual \
ipv4.addresses "$HOST_IP/$CIDR_PREFIX" \
ipv4.gateway "$GATEWAY" \
ipv4.dns $DNS
# enp1s0 のマスターを br0 にする
nmcli con add type bridge-slave ifname $NWIF master br0
# 既存の接続を無効化
nmcli con down $NWIF
# br0 を有効化
nmcli con up br0
libvirt の default pool の確認
libvirt が利用する様々なリソースは、 default pool と呼ばれるディレクトリ: /var/lib/libvirt/images
内で管理される。したがって、まずは default pool が存在することを確認する。
# virsh pool-list --all
Name State Autostart
-------------------------------
default active yes
default
pool がなければ次のコマンドで作成しておく:
# mkdir -p /var/lib/libvirt/images
# chmod 755 /var/lib/libvirt/images
# virsh pool-define /dev/stdin <<EOF
<pool type='dir'>
<name>default</name>
<target>
<path>/var/lib/libvirt/images</path>
</target>
</pool>
EOF
# virsh pool-start default
# virsh pool-autostart default
# virsh pool-list --all
Linux image の入手
本記事では VM の OS として、CentOS の後継である Rocky Linux を用いる。download.rockylinux.org より配布のイメージファイルを、libvirt のデフォルトプール /var/lib/libvirt/images/
にダウンロードする:
# curl -L -o /var/lib/libvirt/images/Rocky-9-GenericCloud.latest.x86_64.qcow2 https://download.rockylinux.org/pub/rocky/9.2/images/x86_64/Rocky-9-GenericCloud.latest.x86_64.qcow2
Terraform による VM のプロビジョニング
cloud-init の設定
cloud-init は、多くの Linux distribution で採用されている VM の初期設定を自動化するための仕組みである。設定は次の2つの設定ファイルを用意して与えることができる。
-
cloud_init.cfg
: ユーザ設定を記述 -
network_config.cfg
: ネットワーク設定を記述
#cloud-config
users:
- name: root
ssh-authorized-keys:
- "<YOUR_SSH_KEY>"
ネットワーク設定は network_config.cfg
から与える。
VM ごとに異なる値(例:静的 IP アドレスなど)は、${foo}
のようなプレースホルダーで記述したテンプレートを作成しておく。Terraform のテンプレート機能によって値が代入され、VM ごとの network_config.cfg を生成することができる。
version: 2
ethernets:
eth0:
dhcp4: no
addresses: [${ip}]
gateway4: ${gateway}
nameservers:
addresses: ${nameservers}
Terraform ファイルの用意
:::note
本節は、Kubespray 用の VM を構築するための Terraform コードの実装方法について解説します。実装するのではなく、import して使いたい場合は、本記事内 TIPS の 『本記事の-Terraform-コードを-module-として使う』 を参照してください。
ファイルは次の4つからなる:
- provider.tf
- main.tf
- variables.tf
- output.tf
provider.tf
Terraform Libvirt Provider の使用を宣言する。
terraform {
required_providers {
libvirt = {
source = "dmacvicar/libvirt"
version = "0.7.1"
}
}
}
provider "libvirt" {
uri = var.libvirt_uri
}
main.tf
主に VM の作成に関する処理を記述する。
locals {
cluster_cidr_splitted = split("/", var.cidr)
cluster_cidr_subnet = local.cluster_cidr_splitted[0]
cluster_cidr_prefix = local.cluster_cidr_splitted[1]
cluster_nameservers_string = "[\"${join("\", \"", var.nameservers)}\"]"
# Auto-calculate mac address from IP
cluster_ips_parts = [for vm in var.vms : split(".", vm.public_ip)]
cluster_mac_addrs = [
for ip_parts in local.cluster_ips_parts : format(
"52:54:00:%02X:%02X:%02X",
tonumber(ip_parts[1]),
tonumber(ip_parts[2]),
tonumber(ip_parts[3])
)
]
private_ips_parts = [for vm in var.vms : split(".", vm.private_ip)]
private_mac_addrs = [
for ip_parts in local.private_ips_parts : format(
"52:54:00:%02X:%02X:%02X",
tonumber(ip_parts[1]),
tonumber(ip_parts[2]),
tonumber(ip_parts[3])
)
]
}
data "template_file" "user_data" {
count = length(var.vms)
template = file(var.vms[count.index].cloudinit_file)
}
data "template_file" "network_config" {
count = length(var.vms)
template = file("${path.module}/network_config.cfg")
vars = {
ip = var.vms[count.index].public_ip
cidr_prefix = local.cluster_cidr_prefix
gateway = var.gateway
nameservers = local.cluster_nameservers_string
}
}
resource "libvirt_cloudinit_disk" "commoninit" {
count = length(var.vms)
name = "commoninit_${var.vms[count.index].name}.iso"
user_data = data.template_file.user_data[count.index].rendered
network_config = data.template_file.network_config[count.index].rendered
}
locals {
volume_list = { for vm in var.vms : "${vm.name}" => flatten([for volume in vm.volumes : volume]) }
volume_name_list = [for vm, volumes in local.volume_list : [for volume in volumes : { "name" : "${vm}_${volume.name}", "disk" : volume.disk }]]
volumes = flatten(local.volume_name_list)
volumes_indexed = { for index, volume in local.volumes : volume.name => index }
}
resource "libvirt_domain" "vm" {
count = length(var.vms)
name = var.vms[count.index].name
vcpu = var.vms[count.index].vcpu
memory = var.vms[count.index].memory
disk {
volume_id = libvirt_volume.system[count.index].id
}
cloudinit = libvirt_cloudinit_disk.commoninit[count.index].id
autostart = true
# Public network
network_interface {
bridge = var.bridge
addresses = [var.vms[count.index].public_ip]
mac = local.cluster_mac_addrs[count.index]
}
# Private network
network_interface {
network_name = "default"
addresses = [var.vms[count.index].private_ip]
mac = local.private_mac_addrs[count.index]
}
qemu_agent = true
cpu {
mode = "host-passthrough"
}
graphics {
type = "vnc"
listen_type = "address"
}
# Makes the tty0 available via `virsh console`
console {
type = "pty"
target_port = "0"
target_type = "serial"
}
}
resource "libvirt_volume" "system" {
count = length(var.vms)
name = "${var.vms[count.index].name}_system.qcow2"
pool = var.pool
format = "qcow2"
base_volume_id = var.vm_base_image_uri
size = var.vms[count.index].disk
}
variables.tf
変数を宣言する。
variable "libvirt_uri" {
type = string
}
variable "vm_base_image_uri" {
type = string
}
variable "bridge" {
type = string
}
variable "gateway" {
type = string
}
variable "cidr" {
type = string
}
variable "nameservers" {
type = list(string)
}
variable "pool" {
type = string
default = "default"
}
variable "vms" {
type = list(
object({
name = string
vcpu = number
memory = number
disk = number
public_ip = string
private_ip = string
cloudinit_file = string
kube_control_plane = bool
kube_node = bool
etcd = bool
})
)
}
output.tf
この後の工程で Ansible に渡すホストの情報を出力する。
locals {
kubespray_hosts_keys = ["name", "kube_control_plane", "kube_node", "etcd"]
kubespray_hosts = [for vm in var.vms :
merge(
{
for key, value in vm : key => value if contains(local.kubespray_hosts_keys, key)
},
{
ip = vm.public_ip
access_ip = vm.private_ip
})
]
}
output "kubespray_hosts" {
value = local.kubespray_hosts
}
Terraform の実行
Terraform を実行し、VM をプロビジョニングする。
$ terraform init
$ terraform apply -auto-approve
VM が作成され、稼働していることを確認する。
$ virsh list --all
Id Name State
------------------------------
1 k8s-master-1 running
2 k8s-worker-1 running
3 k8s-worker-2 running
次のコマンドで、作成した VM がbr0
に接続されていることが確認できる。
$ ip link show master br0
bridge name bridge id STP enabled interfaces
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master br0 state UP mode DEFAULT group default qlen 1000
link/ether 48:21:0b:57:b2:52 brd ff:ff:ff:ff:ff:ff
5: vnet4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br0 state UNKNOWN mode DEFAULT group default qlen 1000
link/ether fe:54:00:00:00:04 brd ff:ff:ff:ff:ff:ff
6: vnet5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br0 state UNKNOWN mode DEFAULT group default qlen 1000
link/ether fe:54:00:00:00:02 brd ff:ff:ff:ff:ff:ff
7: vnet6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br0 state UNKNOWN mode DEFAULT group default qlen 1000
link/ether fe:54:00:00:00:03 brd ff:ff:ff:ff:ff:ff
8: vnet7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br0 state UNKNOWN mode DEFAULT group default qlen 1000
link/ether fe:54:00:00:00:01 brd ff:ff:ff:ff:ff:ff
Kubespray による kubernetes cluster の作成
Kubespray は、Kubenetes クラスタの構築を自動化をする Ansible playbook を提供するオープンソースのプロジェクトである。手順は kubespray/docs/setting-up-your-first-cluster.md at master · kubernetes-sigs/kubespray に従う。
まず kubespray のリポジトリをクローンする。
$ git clone git@github.com:kubernetes-sigs/kubespray.git
$ cd kubespray
次に 既存のサンプルをコピーし、設定ファイルの土台を作成する。
$ git checkout release-2.23
$ cp -rfp inventory/sample inventory/mycluster
本記事ではコンテナランタイムに crio を使うことにする(デフォルトだと containerd になっている)。group_vars/k8s_cluster/k8s_cluster.yml
の container_manager
の値を crio に書き換える:
## Container runtime
## docker for docker, crio for cri-o and containerd for containerd.
## Default: containerd
container_manager: crio
より細かいオプションを設定する場合は、inventory/mycluster/group_vars/
内のファイルを修正する(*補足:プロキシの設定)。
Inventory の作成
次の2通りの方法を紹介するが、どちらか一方を選べば良い。
- Dynamic Inventory を用いる方法
- 手動で hosts.yaml を編集する方法
(方法1) Dynamic Inventory を用いる
terraform 実行後に生成される terraform.tfstate
から動的に inventory 情報を抽出する。(
これを行うには、JSON で記述された inventory 情報を出力だけのするスクリプト (実装例:./generate_inventory.py
) を用意すればよい。下記は、terraform.state
の .outputs
を読み込み、インベントリを作成するスクリプトの実装例である:
#!/usr/bin/env python3
import json
import re
def main():
output = get_outputs()
hosts = output['kubespray_hosts']['value']
libvirt_uri = output['libvirt_uri']['value']
hostvars = {}
kube_control_plane = []
kube_node = []
etcd = []
for host in hosts:
name = host['name']
ip = host['ip']
access_ip = host['access_ip']
hostvars.update({
name: {
"ansible_host": access_ip,
"ip": ip,
}
})
regex = r"^qemu(\+ssh)?://([^/]*)/.*"
res = re.match(regex, libvirt_uri)
if res:
hostname = res[2]
if hostname != "":
hostvars[name].update({
"ansible_ssh_common_args": f"-J {hostname}"
})
if host["kube_control_plane"]:
kube_control_plane.append(name)
if host["kube_node"]:
kube_node.append(name)
if host["etcd"]:
etcd.append(name)
inventory = {
"_meta": {
"hostvars": hostvars,
},
"kube_control_plane": kube_control_plane,
"kube_node": kube_node,
"etcd": etcd,
"k8s_cluster": {
"children": [
"kube_control_plane",
"kube_node",
]
}
}
print(json.dumps(inventory))
def get_outputs():
tfstate_path = './terraform.tfstate'
with open(tfstate_path) as f:
tfstate = json.load(f)
return tfstate['outputs']
main()
この ./generate_inventory.py
を実行すると次のような JSON が出力される:
{
"_meta": {
"hostvars": {
"storage1": {
"ansible_host": "192.168.122.201",
"ip": "192.168.8.201"
},
"storage2": {
"ansible_host": "192.168.122.202",
"ip": "192.168.8.202"
},
"storage3": {
"ansible_host": "192.168.122.203",
"ip": "192.168.8.203"
}
}
},
"kube_control_plane": [
"storage1"
],
"kube_node": [
"storage1",
"storage2",
"storage3"
],
"etcd": [
"storage1"
],
"k8s_cluster": {
"children": [
"kube_control_plane",
"kube_node"
]
}
}
これをインベントリとして指定し、Kubernetes クラスタを作成する:
$ docker pull quay.io/kubespray/kubespray:v2.23.1
$ docker run --rm -it \
--mount type=bind,source="$(pwd)"/inventory,dst=/inventory \
--mount type=bind,source="$(pwd)"/generate_inventory.py,dst=/kubespray/generate_inventory.py \
--mount type=bind,source="$(pwd)"/terraform.tfstate,dst=/kubespray/terraform.tfstate \
--mount type=bind,source="${HOME}"/.ssh/id_rsa,dst=/root/.ssh/id_rsa \
quay.io/kubespray/kubespray:v2.23.1 bash
# Inside a container
$ ansible-playbook -i generate_inventory.py cluster.yml
…
PLAY RECAP *****************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
k8s-master-1 : ok=765 changed=138 unreachable=0 failed=0 skipped=1263 rescued=0 ignored=8
k8s-worker-1 : ok=583 changed=104 unreachable=0 failed=0 skipped=795 rescued=0 ignored=2
k8s-worker-2 : ok=523 changed=82 unreachable=0 failed=0 skipped=763 rescued=0 ignored=1
Tuesday 01 August 2023 13:16:52 +0000 (0:00:00.041) 0:59:01.983 ******** ===============================================================================
なお、途中で失敗するなどしてインストールをやり直したくなった場合、次でリセットできる。
$ ansible-playbook -i ./generate_inventory.py reset.yml
Kubespray における Dynamic Inventory の詳細は、Kubespray 公式レポジトリの https://github.com/kubernetes-sigs/kubespray/blob/master/docs/aws.md#dynamic-inventory も参照。
(方法2) 手動で hosts.yaml を編集する
kubespray のコンテナを立ち上げる。
$ docker pull quay.io/kubespray/kubespray:v2.23.1
$ docker run --rm -it \
--mount type=bind,source="$(pwd)"/inventory/mycluster,dst=/inventory \
--mount type=bind,source="${HOME}"/.ssh/id_rsa,dst=/root/.ssh/id_rsa \
quay.io/kubespray/kubespray:v2.23.1 bash
コンテナの中に入るので、次のコマンドを実行する。
$ declare -a IPS=(192.168.8.201 192.168.8.202 192.168.8.203)
$ CONFIG_FILE=/inventory/hosts.yaml python3 contrib/inventory_builder/inventory.py ${IPS[@]}
inventory ファイル /inventory/mycluster/hosts.yaml
が生成されるので、適宜、ホスト情報を修正する。本記事では下記の設定を用いる:
all:
hosts:
storage1:
ansible_host: 192.168.122.201
ip: 192.168.8.201
storage2:
ansible_host: 192.168.122.202
ip: 192.168.8.202
storage3:
ansible_host: 192.168.122.203
ip: 192.168.8.203
children:
kube_control_plane:
hosts:
storage1:
kube_node:
hosts:
storage1:
storage2:
storage3:
etcd:
hosts:
storage1:
k8s_cluster:
children:
kube_control_plane:
kube_node:
$ docker run --rm -it \
--mount type=bind,source="$(pwd)"/inventory,dst=/inventory \
--mount type=bind,source="$(pwd)"/cluster.yml,dst=/kubespray/cluster.yml \
--mount type=bind,source="${HOME}"/.kube,dst=/root/.kube \
--mount type=bind,source="${HOME}"/.ssh/id_rsa,dst=/root/.ssh/id_rsa \
quay.io/kubespray/kubespray:v2.23.1 bash
# Inside a container
$ ansible-playbook -i /inventory/hosts.yaml cluster.yml
Kubernetes cluster へのアクセス
いずれかの master node から認証情報 admin.conf
を取ってくる。
$ mkdir -p ~/.kube
$ scp root@192.168.8.201:/etc/kubernetes/admin.conf ~/.kube
admin.conf 内の clusters.cluster.server の IP は、127.0.0.1
になっているので、Public IP (例:192.168.8.201
) に修正する。
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: <CERTIFICATE_AUTHORITY_DATA>
server: https://192.168.8.201:6443
name: cluster.local
...
.bashrc などで次の環境変数を設定する。
$ export KUBECONFIG=$HOME/.kube/admin.conf
以上で kubectl
でクラスタにアクセスできるようになる。
$ kubectl cluster-info
Kubernetes control plane is running at https://192.168.8.201:6443
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master-1 Ready control-plane 70m v1.26.5
k8s-worker-1 Ready <none> 68m v1.26.5
k8s-worker-2 Ready <none> 68m v1.26.5
TIPS
Kubespray で proxy を設定する
proxy を設定する場合は、inventory/mycluster/group_vars/all/all.yml
の次の項目を編集する。
http_proxy: 'http://your-proxy-server:8080'
https_proxy: 'http://your-proxy-server:8080'
no_proxy: 'localhost,127.0.0.1,.yourdomain.com'
Kubespray に独自の playbook を組み込む
例えば、cluster.yml
の適用後に独自の追加処理を実行させる場合について。
Kubespray の cluster.yml
の内容 を見ると、単に playbooks/cluster.yml
を import しているだけなので、追加のタスクを行う場合は cluster.yml
の末尾に独自の処理を追加した、次のような cusotmized_cluster.yml
を作成する。
---
# This role assumes to call Kubespray's `playbooks/cluster`.
- name: Install Kubernetes
ansible.builtin.import_playbook: playbooks/cluster.yml
- name: Registernqualified
hosts: all
tasks:
- ansible.builtin.file:
path: /etc/containers/registries.conf.d
state: directory
mode: '0755'
- ansible.builtin.copy:
dest: /etc/containers/registries.conf.d/01-unqualified.conf
content: |
unqualified-search-registries = ['docker.io', 'quay.io']
- name: Download amind.conf to localhost
hosts: kube_control_plane
run_once: true
tasks:
- ansible.builtin.fetch:
src: /etc/kubernetes/admin.conf
dest: ~/.kube/admin.conf
flat: yes
- delegate_to: localhost
ansible.builtin.replace:
path: ~/.kube/admin.conf
regexp: '127.0.0.1'
replace: "{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}"
上の例では、クラスタ作成後の次のタスク:
- Unqualified registries (
docker.io
とquay.io
) の追加 - admin.conf をローカルにダウンロードし、API サーバのアドレスを Public IP に置換
を自動化する。あとは Kubespray の cluster.yml
をこれに置き換え、通常通り playbook を実行すればよい。
$ docker run --rm -it \
--mount type=bind,source="$(pwd)"/inventory,dst=/inventory \
--mount type=bind,source="$(pwd)"/.terraform/modules/kubernetes/kubernetes/generate_inventory.py,dst=/kubespray/generate_inventory.py \
--mount type=bind,source="$(pwd)"/terraform.tfstate,dst=/kubespray/terraform.tfstate \
--mount type=bind,source="$(pwd)"/cluster.yml,dst=/kubespray/cluster.yml \
--mount type=bind,source="${HOME}"/.kube,dst=/root/.kube \
--mount type=bind,source="${HOME}"/.ssh/id_rsa,dst=/root/.ssh/id_rsa \
quay.io/kubespray/kubespray:v2.23.1 bash
# Inside a container
ansible-playbook -i generate_inventory.py cluster.yml
公式のガイド kubespray/docs/integration.md at master · kubernetes-sigs/kubespray · GitHub) も参照。
オフライン環境で Terraform を実行する
通常、Provider はインターネット経由で自動的にダウンロードされるが、オフライン環境の場合は、ホームディレクトリ下に ~/terraform.d/providers
を用意し、そこに Provider をおく。
例として、Terraform Libvirt Provider 使う場合、Releases · dmacvicar/terraform-provider-libvirt からバイナリをダウンロードし次のように配置する:
~/terraform.d/providers/
└── providers/
└── registry.terraform.io/
└── dmacvicar/
└── libvirt/
└── 0.7.1/
└── linux_amd64/
├── CHANGELOG.md
├── LICENSE
├── README.md
└── terraform-provider-libvirt_v0.7.4
リモートホストに対して Terraform を適用する
ローカルから Terraform を実行し、リモートホスト上に VM を作成することができる。
まずはリモートホスト上で、Host の事前設定 を完了させた上で、libvirtd が稼働していることを確認する:
# Remote side
$ systemctl status libvirtd
クライアント側から qemu+ssh://
プロトコルを用いて接続を確認する:
# Client side
$ virsh -c qemu+ssh://<user>@<remote_ip>/system list
Id Name State
--------------------
確認できたら、main.tf
で下記のようにリモートホストを指定し、クライアント側で terraform apply
を実行する。
locals {
user_home_directory = pathexpand("~")
}
provider "libvirt" {
uri = "qemu+ssh://<remote-user>@<remote_host>/system?keyfile=${local.user_home_directory}/.ssh/id_rsa&known_hosts_verify=ignore"
}
踏み台サーバを経由する場合
踏み台サーバを経由してリモートにアクセスする場合、リモートの22番ポートをローカルの適当なポート (例:50000) にフォワーディングする。
# Client side
$ ssh -C -N -f -L 50000:<remote-user>@<remote-host>:22 <bastion-host> -p <bastion-port>
$ virsh -c qemu+ssh://<remote-user>@localhost:50000/system list
Id Name State
--------------------
locals {
user_home_directory = pathexpand("~")
}
provider "libvirt" {
uri = "qemu+ssh://<remote-user>@localhost:50000/system?keyfile=${local.user_home_directory}/.ssh/id_rsa&known_hosts_verify=ignore"
}
本記事の Terraform コードを module として使う
下記ような main.tf
を用意する。例では1台の master node と 2台の worker nodes からなる合計3台のVMが作成されるが、所望の構成になるよう適宜パラメータを修正する。
output "kubespray_hosts" {
value = module.kubernetes.kubespray_hosts
}
output "libvirt_uri" {
value = module.kubernetes.libvirt_uri
}
locals {
user_home_directory = pathexpand("~")
}
module "kubernetes" {
source = "github.com/sawa2d2/k8s-on-kvm//kubernetes/"
## Localhost:
# libvirt_uri = "qemu:///system"
## Remote:
# libvirt_uri = "qemu+ssh://<user>@<remote-host>/system?keyfile=${local.user_home_directory}/.ssh/id_rsa&known_hosts_verify=ignore"
## Remote via bastion:
## Forward port in advance.
## $ ssh -C -N -f -L 50000:<remote-user>@<remote-host>:22 <bastion-host> -p <bastion-port>
# libvirt_uri = "qemu+ssh://<remote-user>@localhost:50000/system?keyfile=${local.user_home_directory}/.ssh/id_rsa&known_hosts_verify=ignore"
libvirt_uri = "qemu:///system"
# Download the image by:
# sudo curl -L -o /var/lib/libvirt/images/Rocky-9-GenericCloud.latest.x86_64.qcow2 https://download.rockylinux.org/pub/rocky/9.2/images/x86_64/Rocky-9-GenericCloud.latest.x86_64.qcow2
vm_base_image_uri = "/var/lib/libvirt/images/Rocky-9-GenericCloud.latest.x86_64.qcow2"
pool = "default"
# Cluster network
bridge = "br0"
cidr = "192.168.8.0/24"
gateway = "192.168.8.1"
nameservers = ["192.168.8.1"]
vms = [
{
name = "k8s-master-1"
vcpu = 4
memory = 16000 # in MiB
disk = 100 * 1024 * 1024 * 1024 # 100 GB
public_ip = "192.168.8.101"
private_ip = "192.168.122.201"
cloudinit_file = "cloud_init.cfg"
volumes = []
kube_control_plane = true
kube_node = true
etcd = true
},
{
name = "k8s-worker-1"
vcpu = 4
memory = 16000 # in MiB
disk = 100 * 1024 * 1024 * 1024 # 100 GB
public_ip = "192.168.8.102"
private_ip = "192.168.122.202"
cloudinit_file = "cloud_init.cfg"
volumes = []
kube_control_plane = false
kube_node = true
etcd = false
},
{
name = "k8s-worker-2"
vcpu = 2
memory = 8000 # in MiB
disk = 100 * 1024 * 1024 * 1024 # 100 GB
public_ip = "192.168.8.103"
private_ip = "192.168.122.203"
cloudinit_file = "cloud_init.cfg"
volumes = []
kube_control_plane = false
kube_node = true
etcd = false
},
]
}
参考資料
Terraform および Dynamic Inventory 関連
- End-to-End Application Provisioning with Ansible and Terraform - IBM Blog
- Integrating Ansible and Jenkins with Terraform to make a powerful infrastructure | by Ankush Chavan | Medium
libvirt の設定関連
- error when default storage pool is missing · Issue #8 · simon3z/virt-deploy ... default pool の作成方法など
Kuberspray 関連
ブリッジ設定関連
関連記事