For dev environments, we were writing the ip domain mapping in /etc/hosts which was fine for starting the project but now since setting up the DNS Server is a requirement for development, we will get rid of the dev helper logics.
227 lines
7.8 KiB
Ruby
227 lines
7.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Prog::Kubernetes::ProvisionKubernetesNode < Prog::Base
|
|
subject_is :kubernetes_cluster
|
|
|
|
def vm
|
|
@vm ||= Vm[frame["vm_id"]]
|
|
end
|
|
|
|
def kubernetes_nodepool
|
|
@kubernetes_nodepool ||= KubernetesNodepool[frame["nodepool_id"]]
|
|
end
|
|
|
|
# We need to create a random ula cidr for the cluster services subnet with
|
|
# a NetMask of /108
|
|
# For reference read here:
|
|
# https://github.com/kubernetes/kubernetes/blob/44c230bf5c321056e8bc89300b37c497f464f113/cmd/kubeadm/app/constants/constants.go#L251-L255
|
|
# This is an excerpt from the link:
|
|
# MaximumBitsForServiceSubnet defines maximum possible size of the service subnet in terms of bits.
|
|
# For example, if the value is 20, then the largest supported service subnet is /12 for IPv4 and /108 for IPv6.
|
|
# Note however that anything in between /108 and /112 will be clamped to /112 due to the limitations of the underlying allocation logic.
|
|
# MaximumBitsForServiceSubnet = 20
|
|
def random_ula_cidr
|
|
random_bytes = 0xfd.chr + SecureRandom.bytes(13)
|
|
high_bits = random_bytes[0...8].unpack1("Q>")
|
|
low_bits = (random_bytes[8...14] + "\x00\x00").unpack1("Q>")
|
|
network_address = NetAddr::IPv6.new((high_bits << 64) | low_bits)
|
|
NetAddr::IPv6Net.new(network_address, NetAddr::Mask128.new(108))
|
|
end
|
|
|
|
def before_run
|
|
if kubernetes_cluster.strand.label == "destroy" && strand.label != "destroy"
|
|
pop "provisioning canceled"
|
|
end
|
|
end
|
|
|
|
label def start
|
|
name, vm_size, storage_size_gib = if kubernetes_nodepool
|
|
["#{kubernetes_nodepool.ubid}-#{SecureRandom.alphanumeric(5).downcase}",
|
|
kubernetes_nodepool.target_node_size,
|
|
kubernetes_nodepool.target_node_storage_size_gib]
|
|
else
|
|
["#{kubernetes_cluster.ubid}-#{SecureRandom.alphanumeric(5).downcase}",
|
|
kubernetes_cluster.target_node_size,
|
|
kubernetes_cluster.target_node_storage_size_gib]
|
|
end
|
|
|
|
storage_volumes = [{encrypted: true, size_gib: storage_size_gib}] if storage_size_gib
|
|
|
|
boot_image = "kubernetes-#{kubernetes_cluster.version.tr(".", "_")}"
|
|
|
|
vm = Prog::Vm::Nexus.assemble_with_sshable(
|
|
Config.kubernetes_service_project_id,
|
|
sshable_unix_user: "ubi",
|
|
name: name,
|
|
location_id: kubernetes_cluster.location.id,
|
|
size: vm_size,
|
|
storage_volumes: storage_volumes,
|
|
boot_image: boot_image,
|
|
private_subnet_id: kubernetes_cluster.private_subnet_id,
|
|
enable_ip4: true
|
|
).subject
|
|
|
|
current_frame = strand.stack.first
|
|
current_frame["vm_id"] = vm.id
|
|
strand.modified!(:stack)
|
|
|
|
if kubernetes_nodepool
|
|
kubernetes_nodepool.add_vm(vm)
|
|
else
|
|
kubernetes_cluster.add_cp_vm(vm)
|
|
kubernetes_cluster.api_server_lb.add_vm(vm)
|
|
end
|
|
|
|
hop_bootstrap_rhizome
|
|
end
|
|
|
|
label def bootstrap_rhizome
|
|
nap 5 unless vm.strand.label == "wait"
|
|
|
|
vm.sshable.cmd "sudo iptables-nft -t nat -A POSTROUTING -s #{vm.nics.first.private_ipv4} -o ens3 -j MASQUERADE"
|
|
vm.sshable.cmd("sudo nft --file -", stdin: <<TEMPLATE)
|
|
table ip6 pod_access;
|
|
delete table ip6 pod_access;
|
|
table ip6 pod_access {
|
|
chain ingress_egress_control {
|
|
type filter hook forward priority filter; policy drop;
|
|
# allow access to the vm itself in order to not break the normal functionality of Clover and SSH
|
|
ip6 daddr #{vm.ephemeral_net6.nth(2)} ct state established,related,new counter accept
|
|
ip6 saddr #{vm.ephemeral_net6.nth(2)} ct state established,related,new counter accept
|
|
|
|
# not allow new connections from internet but allow new connections from inside
|
|
ip6 daddr #{vm.ephemeral_net6} ct state established,related counter accept
|
|
ip6 saddr #{vm.ephemeral_net6} ct state established,related,new counter accept
|
|
|
|
# used for internal private ipv6 communication between pods
|
|
ip6 saddr #{kubernetes_cluster.private_subnet.net6} ct state established,related,new counter accept
|
|
ip6 daddr #{kubernetes_cluster.private_subnet.net6} ct state established,related,new counter accept
|
|
}
|
|
}
|
|
TEMPLATE
|
|
vm.ephemeral_net6
|
|
vm.sshable.cmd "sudo systemctl enable --now kubelet"
|
|
|
|
bud Prog::BootstrapRhizome, {"target_folder" => "kubernetes", "subject_id" => vm.id, "user" => "ubi"}
|
|
|
|
hop_wait_bootstrap_rhizome
|
|
end
|
|
|
|
label def wait_bootstrap_rhizome
|
|
reap
|
|
hop_assign_role if leaf?
|
|
donate
|
|
end
|
|
|
|
label def assign_role
|
|
hop_join_worker if kubernetes_nodepool
|
|
|
|
hop_init_cluster if kubernetes_cluster.cp_vms.count == 1
|
|
|
|
hop_join_control_plane
|
|
end
|
|
|
|
label def init_cluster
|
|
case vm.sshable.d_check("init_kubernetes_cluster")
|
|
when "Succeeded"
|
|
hop_install_cni
|
|
when "NotStarted"
|
|
params = {
|
|
node_name: vm.name,
|
|
cluster_name: kubernetes_cluster.name,
|
|
lb_hostname: kubernetes_cluster.endpoint,
|
|
port: "443",
|
|
private_subnet_cidr4: kubernetes_cluster.private_subnet.net4,
|
|
private_subnet_cidr6: kubernetes_cluster.private_subnet.net6,
|
|
node_ipv4: vm.private_ipv4,
|
|
node_ipv6: vm.ephemeral_net6.nth(2),
|
|
service_subnet_cidr6: random_ula_cidr
|
|
}
|
|
vm.sshable.d_run("init_kubernetes_cluster", "/home/ubi/kubernetes/bin/init-cluster", stdin: JSON.generate(params), log: false)
|
|
nap 30
|
|
when "InProgress"
|
|
nap 10
|
|
when "Failed"
|
|
Clog.emit("INIT CLUSTER FAILED")
|
|
nap 65536
|
|
# TODO: register deadline
|
|
end
|
|
|
|
nap 65536
|
|
end
|
|
|
|
label def join_control_plane
|
|
case vm.sshable.d_check("join_control_plane")
|
|
when "Succeeded"
|
|
hop_install_cni
|
|
when "NotStarted"
|
|
cp_sshable = kubernetes_cluster.sshable
|
|
params = {
|
|
is_control_plane: true,
|
|
node_name: vm.name,
|
|
endpoint: "#{kubernetes_cluster.endpoint}:443",
|
|
join_token: cp_sshable.cmd("sudo kubeadm token create --ttl 24h --usages signing,authentication", log: false).chomp,
|
|
certificate_key: cp_sshable.cmd("sudo kubeadm init phase upload-certs --upload-certs", log: false)[/certificate key:\n(.*)/, 1],
|
|
discovery_token_ca_cert_hash: cp_sshable.cmd("sudo kubeadm token create --print-join-command", log: false)[/discovery-token-ca-cert-hash (\S+)/, 1],
|
|
node_ipv4: vm.private_ipv4,
|
|
node_ipv6: vm.ephemeral_net6.nth(2)
|
|
}
|
|
vm.sshable.d_run("join_control_plane", "kubernetes/bin/join-node", stdin: JSON.generate(params), log: false)
|
|
nap 15
|
|
when "InProgress"
|
|
nap 10
|
|
when "Failed"
|
|
# TODO: Create a page
|
|
Clog.emit("JOIN CP NODE TO CLUSTER FAILED")
|
|
nap 65536
|
|
end
|
|
|
|
nap 65536
|
|
end
|
|
|
|
label def join_worker
|
|
case vm.sshable.d_check("join_worker")
|
|
when "Succeeded"
|
|
hop_install_cni
|
|
when "NotStarted"
|
|
cp_sshable = kubernetes_cluster.sshable
|
|
params = {
|
|
is_control_plane: false,
|
|
node_name: vm.name,
|
|
endpoint: "#{kubernetes_cluster.endpoint}:443",
|
|
join_token: cp_sshable.cmd("sudo kubeadm token create --ttl 24h --usages signing,authentication", log: false).tr("\n", ""),
|
|
discovery_token_ca_cert_hash: cp_sshable.cmd("sudo kubeadm token create --print-join-command", log: false)[/discovery-token-ca-cert-hash (\S+)/, 1],
|
|
node_ipv4: vm.private_ipv4,
|
|
node_ipv6: vm.ephemeral_net6.nth(2)
|
|
}
|
|
vm.sshable.d_run("join_worker", "kubernetes/bin/join-node", stdin: JSON.generate(params), log: false)
|
|
nap 15
|
|
when "InProgress"
|
|
nap 10
|
|
when "Failed"
|
|
# TODO: Create a page
|
|
Clog.emit("JOIN WORKER NODE TO CLUSTER FAILED")
|
|
nap 65536
|
|
end
|
|
|
|
nap 65536
|
|
end
|
|
|
|
label def install_cni
|
|
cni_config = <<CONFIG
|
|
{
|
|
"cniVersion": "1.0.0",
|
|
"name": "ubicni-network",
|
|
"type": "ubicni",
|
|
"ranges":{
|
|
"subnet_ipv6": "#{NetAddr::IPv6Net.new(vm.ephemeral_net6.network, NetAddr::Mask128.new(80))}",
|
|
"subnet_ula_ipv6": "#{vm.nics.first.private_ipv6}",
|
|
"subnet_ipv4": "#{vm.nics.first.private_ipv4}"
|
|
}
|
|
}
|
|
CONFIG
|
|
vm.sshable.cmd("sudo tee /etc/cni/net.d/ubicni-config.json", stdin: cni_config)
|
|
pop vm_id: vm.id
|
|
end
|
|
end
|