使用Terraform + Kubespray在KVM上构建Kubernetes集群
太长不读 bù dú)
在定义了组成之后,可以使用以下命令创建 Kubernetes 集群:
$ 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 创建虚拟机
-
- 从生成的 terraform.tfstate 文件中提取存货信息
- 使用存货信息和 Kuberspray 的 Ansible playbooks 创建 Kubernetes 集群
网络配置如下图所示:
在集群中,将公共网络(eth0)与用于Ansible SSH登录的网络(eth1)进行了分隔。
前提条件 (Qian ti tiao jian)
-
- terraform
-
- Container runtime (docker, podman, nerdctl, etc.)
-
- KVM packages
qemu-kvm
libvirt
crdtools
nmcli
(Host OS: Ubuntu 22.04)
步骤
主持人的预先设置
网络设置
创建一个虚拟桥接口br0,并将现有主机的网络接口(如enp1s0)连接到br0。通过这样做,可以将后续创建的虚拟机连接到br0,使得可以直接从局域网中的任何主机访问虚拟机。
图示说明如下,将设置从 “Before” 更改为 “After”。
$ HOST_IP=192.168.8.10
$ CIDR=24
$ GATEWAY=192.168.8.1
$ DNS=192.168.8.1
$ NWIF=enp1s0
# br0 を作成
$ nmcli connection add type bridge ifname br0
$ nmcli connection show
NAME UUID TYPE DEVICE
bridge-br0 55bef68c-1232-46c3-adac-e40964c24d4d bridge br0
...
# 既存の enp1s0 と同じ設定を br0 に与える
$ nmcli connection modify bridge-br0 \
ipv4.method manual \
ipv4.addresses "$HOST_IP/$CIDR" \
ipv4.gateway "$GATEWAY" \
ipv4.dns $DNS
# enp1s0 を br0 に接続
$ nmcli connection add type bridge-slave ifname $NWIF master bridge-br0
# 既存の enp1s0 のネットワークインタフェースを削除
$ nmcli connection delete $NWIF
# br0 の設定を反映
$ nmcli connection up bridge-br0
检查libvirt的默认存储池。
首先,需要确认是否存在默认存储池,libvirt使用各种资源,这些资源在称为”default pool”的目录/var/lib/libvirt/images内进行管理。
$ virsh pool-list --all
Name State Autostart
-------------------------------
default active yes
如果没有默认池的话,在这里可以使用以下命令创建一个:
$ mkdir /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 镜像
在本文中,我们将使用Rocky Linux作为VM的操作系统,取代原先的CentOS。我们将从download.rockylinux.org网站下载映像文件,并将其保存到libvirt的默认存储池/var/lib/libvirt/images/中。
$ 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
使用 Terraform 进行虚拟机的配置
云初始化配置
cloud-init是一种用于自动化虚拟机的初始设置的机制,在许多Linux发行版中被采用。可以通过准备以下两个配置文件来提供设置。
cloud_init.cfg: ユーザ設定を記述
network_config.cfg: ネットワーク設定を記述
#cloud-config
users:
- name: root
ssh-authorized-keys:
- "<YOUR_SSH_KEY>" # 自分の SSH 公開鍵を書く
runcmd:
- dnf install -y python3
- dnf install -y libselinux-python
网络设置由 network_config.cfg 提供。
然而,如果每个虚拟机有不同的值(例如分配静态 IP 地址),可以创建一个包含参数部分类似于 ${foo} 的占位符的模板,如下所示。使用 Terraform 的模板功能,稍后可以将值分配给它,并生成每个虚拟机的 network_config.cfg。
version: 2
ethernets:
eth0:
dhcp4: no
addresses: [${ip}]
gateway4: ${gateway}
nameservers:
addresses: ${nameservers}
准备 Terraform 的 libvirt 提供程序
使用Terraform libvirt provider (dmacvicar/libvirt),可以通过Terraform控制libvirt。
由于libvirt默认限制外部应用程序对libvirt资源的访问,因此为了给libvirt提供者授予访问权限,需要编辑/etc/apparmor.d/libvirt/TEMPLATE.qemu文件如下。
# This profile is for the domain whose UUID matches this file.
#
#include <tunables/global>
profile LIBVIRT_TEMPLATE flags=(attach_disconnected) {
#include <abstractions/libvirt-qemu>
/var/lib/libvirt/images/** rwk,
/tmp/** rwk,
}
重新启动libvirt。
$ sudo systemctl restart libvirtd
准备Terraform文件
文件由以下四部分组成:
-
- provider.tf
-
- main.tf
-
- variables.tf
- output.tf
provider.tf (提供者.tf)
声明使用Terraform Libvirt Provider。
terraform {
required_providers {
libvirt = {
source = "dmacvicar/libvirt"
version = "0.7.1"
}
}
}
provider "libvirt" {
uri = var.libvirt_uri
}
主要.tf
主要描述与创建虚拟机相关的处理过程。
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的意思是tf是指Terraform,variables是指变量,.tf是指Terraform的文件扩展名。
声明变量。
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 的汉语翻译是输出.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并进行虚拟机配置。
$ terraform init
$ terraform apply -auto-approve
确认虚拟机已经被创建并正在运行。
$ virsh list --all
Id Name State
------------------------------
1 k8s-master-1 running
2 k8s-worker-1 running
3 k8s-worker-2 running
可以通过以下命令确认创建的虚拟机已连接到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 集群。
Kubespray是一个开源项目,提供了自动化构建Kubernetes集群的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/ 目录下的文件(附注:代理设置)。
创建库存清单
我将介绍以下两种方法,但你只需要选择其中一种就可以了。
-
- 使用动态库存的方法。
- 手动编辑hosts.yaml文件的方法。
使用动态库存方法
在执行terraform后,从生成的terraform.tfstate文件中动态地提取清单信息。
只需准备一个仅进行输出的脚本(示例实现:./generate_inventory.py),该脚本将输出以 JSON 描述的 inventory 信息。以下是一个读取 terraform.state 的 .outputs 并创建 inventory 的脚本实现示例:
#!/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官方仓库的 https://github.com/kubernetes-sigs/kubespray/blob/master/docs/aws.md#dynamic-inventory ,获取有关Kubespray动态清单的详细信息。
(选项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/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 集群
从任一主节点获取认证信息 admin.conf。
$ mkdir -p ~/.kube
$ scp root@192.168.8.201:/etc/kubernetes/admin.conf ~/.kube
请将 admin.conf 文件中 clusters.cluster.server 的 IP 地址修改为公共IP地址(例如:192.168.8.201),原地址为127.0.0.1。
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
技巧
使用Kubespray进行代理配置
如果要设置代理,请编辑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'
将自己的playbook集成到Kubespray中。
如果在应用cluster.yml之后需要执行自定义的附加处理,可以这样操作。
查看Kubespray的cluster.yml文件,可以看出只是简单地导入了playbooks/cluster.yml文件,所以如果需要执行额外的任务,需要在cluster.yml的末尾添加定制化的处理,创建一个名为customized_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为例,从dmacvicar/terraform-provider-libvirt的Releases页面下载二进制文件,并按照以下方式进行配置:
~/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,并在远程主机上创建虚拟机。
首先,在远程主机上完成主机的预配置后,确认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 代码作为模块使用
准备一个如下的 main.tf 文件。在这个例子中,将创建一个由1个主节点和2个工作节点组成的共计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的相关内容
-
- kubesprayを使ったKubernetesのインストール | Kubernetes
- An introduction to Kubespray | Enable Sysadmin
桥梁设置相关
- KVMでゲストOSをブリッジ接続する – Qiita
相关文章