Skip to content

Latest commit

 

History

History
213 lines (157 loc) · 12 KB

Creating_a_cluster.md

File metadata and controls

213 lines (157 loc) · 12 KB

Creating a cluster

The tool needs a basic configuration file, written in YAML format, to handle tasks like creating, upgrading, or deleting clusters. Below is an example where commented lines indicate optional settings:

---
hetzner_token: <your token>
cluster_name: test
kubeconfig_path: "./kubeconfig"
k3s_version: v1.30.3+k3s1

networking:
  ssh:
    port: 22
    use_agent: false # set to true if your key has a passphrase
    public_key_path: "~/.ssh/id_ed25519.pub"
    private_key_path: "~/.ssh/id_ed25519"
  allowed_networks:
    ssh:
      - 0.0.0.0/0
    api: # this will firewall port 6443 on the nodes
      - 0.0.0.0/0
  public_network:
    ipv4: true
    ipv6: true
  private_network:
    enabled: true
    subnet: 10.0.0.0/16
    existing_network_name: ""
  cni:
    enabled: true
    encryption: false
    mode: flannel

  # cluster_cidr: 10.244.0.0/16 # optional: a custom IPv4/IPv6 network CIDR to use for pod IPs
  # service_cidr: 10.43.0.0/16 # optional: a custom IPv4/IPv6 network CIDR to use for service IPs. Warning, if you change this, you should also change cluster_dns!
  # cluster_dns: 10.43.0.10 # optional: IPv4 Cluster IP for coredns service. Needs to be an address from the service_cidr range


# manifests:
#   cloud_controller_manager_manifest_url: "https://github.com/hetznercloud/hcloud-cloud-controller-manager/releases/download/v1.23.0/ccm-networks.yaml"
#   csi_driver_manifest_url: "https://raw.githubusercontent.com/hetznercloud/csi-driver/v2.12.0/deploy/kubernetes/hcloud-csi.yml"
#   system_upgrade_controller_deployment_manifest_url: "https://github.com/rancher/system-upgrade-controller/releases/download/v0.14.2/system-upgrade-controller.yaml"
#   system_upgrade_controller_crd_manifest_url: "https://github.com/rancher/system-upgrade-controller/releases/download/v0.14.2/crd.yaml"
#   cluster_autoscaler_manifest_url: "https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/hetzner/examples/cluster-autoscaler-run-on-master.yaml"
#   cluster_autoscaler_container_image_tag: "v1.32.0"

datastore:
  mode: etcd # etcd (default) or external
  external_datastore_endpoint: postgres://....

schedule_workloads_on_masters: false

# image: rocky-9 # optional: default is ubuntu-24.04
# autoscaling_image: 103908130 # optional, defaults to the `image` setting
# snapshot_os: microos # optional: specified the os type when using a custom snapshot

masters_pool:
  instance_type: cpx21
  instance_count: 3 # for HA; you can also create a single master cluster for dev and testing (not recommended for production)
  locations: # You can choose a single location for single master clusters or if you prefer to have all masters in the same location. For regional clusters (which are only available in the eu-central network zone), each master needs to be placed in a separate location.
    - fsn1
    - hel1
    - nbg1

worker_node_pools:
- name: small-static
  instance_type: cpx21
  instance_count: 4
  location: hel1
  # image: debian-11
  # labels:
  #   - key: purpose
  #     value: blah
  # taints:
  #   - key: something
  #     value: value1:NoSchedule
- name: medium-autoscaled
  instance_type: cpx31
  location: fsn1
  autoscaling:
    enabled: true
    min_instances: 0
    max_instances: 3

embedded_registry_mirror:
  enabled: false # Enables fast p2p distribution of container images between nodes for faster pod startup. Check if your k3s version is compatible before enabling this option. You can find more information at https://docs.k3s.io/installation/registry-mirror

protect_against_deletion: true

create_load_balancer_for_the_kubernetes_api: false # Just a heads up: right now, we can’t limit access to the load balancer by IP through the firewall. This feature hasn’t been added by Hetzner yet.

# additional_packages:
# - somepackage

# post_create_commands:
# - apt update
# - apt upgrade -y
# - apt autoremove -y

# kube_api_server_args:
# - arg1
# - ...
# kube_scheduler_args:
# - arg1
# - ...
# kube_controller_manager_args:
# - arg1
# - ...
# kube_cloud_controller_manager_args:
# - arg1
# - ...
# kubelet_args:
# - arg1
# - ...
# kube_proxy_args:
# - arg1
# - ...
# api_server_hostname: k8s.example.com # optional: DNS for the k8s API LoadBalancer. After the script has run, create a DNS record with the address of the API LoadBalancer.

Most settings are straightforward and easy to understand. To see a list of available k3s releases, you can run the command hetzner-k3s releases.

If you prefer not to include the Hetzner token directly in the config file—perhaps for use with CI or to safely commit the config to a repository—you can use the HCLOUD_TOKEN environment variable instead. This variable takes precedence over the config file.

When setting masters_pool.instance_count, keep in mind that if you set it to 1, the tool will create a control plane that is not highly available. For production clusters, it’s better to set this to a number greater than 1. To avoid split brain issues with etcd, this number should be odd, and 3 is the recommended value. Additionally, for production environments, it’s a good idea to configure masters in different locations using the masters_pool.locations setting.

You can define any number of worker node pools, either static or autoscaled, and create pools with nodes of different specifications to handle various workloads.

Hetzner Cloud init settings, such as additional_packages and post_create_commands, can be specified at the root level of the configuration file or for each individual pool if different settings are needed. If these settings are configured at the pool level, they will override any settings defined at the root level.

Currently, Hetzner Cloud offers six locations: two in Germany (nbg1 in Nuremberg and fsn1 in Falkenstein), one in Finland (hel1 in Helsinki), two in the USA (ash in Ashburn, Virginia and hil in Hillsboro, Oregon), and one in Singapore (sin). Be aware that not all instance types are available in every location, so it’s a good idea to check the Hetzner site and their status page for details.

To explore the available instance types and their specifications, you can either check them manually when adding an instance within a project or run the following command with your Hetzner token:

curl -H "Authorization: Bearer $API_TOKEN" 'https://api.hetzner.cloud/v1/server_types'

To create the cluster run:

hetzner-k3s create --config cluster_config.yaml | tee create.log

This process will take a few minutes, depending on how many master and worker nodes you have.

Disabling public IPs (IPv4 or IPv6 or both) on nodes

To improve security and save on IPv4 address costs, you can disable the public interface for all nodes by setting enable_public_net_ipv4: false and enable_public_net_ipv6: false. These settings are global and will apply to all master and worker nodes. If you disable public IPs, make sure to run hetzner-k3s from a machine that has access to the same private network as the nodes, either directly or through a VPN.

Additional networking setup is required via cloud-init, so it’s important that the machine you use to run hetzner-k3s has internet access and DNS configured correctly. Otherwise, the cluster creation process will get stuck after creating the nodes. For more details and instructions, you can refer to this discussion.

Using alternative OS images

By default, the image used for all nodes is ubuntu-24.04, but you can specify a different default image by using the root-level image config option. You can also set different images for different static node pools by using the image config option within each node pool. For example, if you have node pools with ARM instances, you can specify the correct OS image for ARM. To do this, set image to 103908130 with the specific image ID.

However, for autoscaling, there’s a current limitation in the Cluster Autoscaler for Hetzner. You can’t specify different images for each autoscaled pool yet. For now, if you want to use a different image for all autoscaling pools, you can set the autoscaling_image option to override the default image setting.

To see the list of available images, run the following:

export API_TOKEN=...

curl -H "Authorization: Bearer $API_TOKEN" 'https://api.hetzner.cloud/v1/images?per_page=100'

Besides the default OS images, you can also use a snapshot created from an existing instance. When using custom snapshots, make sure to specify the ID of the snapshot or image, not the description you assigned when creating the template instance.

I’ve tested snapshots with openSUSE MicroOS, but other options might work as well. You can easily create a MicroOS snapshot using this Terraform-based tool. The process only takes a few minutes. Once the snapshot is ready, you can use it with hetzner-k3s by setting the image configuration option to the ID of the snapshot and snapshot_os to microos.


Keeping a Project per Cluster

If you plan to create multiple clusters within the same project, refer to the section on Configuring Cluster-CIDR and Service-CIDR. Ensure that each cluster has its own unique Cluster-CIDR and Service-CIDR. Overlapping ranges will cause issues. However, I still recommend separating clusters into different projects. This makes it easier to clean up resources—if you want to delete a cluster, simply delete the entire project.


Configuring Cluster-CIDR and Service-CIDR

Cluster-CIDR and Service-CIDR define the IP ranges used for pods and services, respectively. In most cases, you won’t need to change these values. However, advanced setups might require adjustments to avoid network conflicts.

Changing the Cluster-CIDR (Pod IP Range): To modify the Cluster-CIDR, uncomment or add the cluster_cidr option in your cluster configuration file and specify a valid CIDR notation for the network. Make sure this network is not a subnet of your private network.

Changing the Service-CIDR (Service IP Range): To adjust the Service-CIDR, uncomment or add the service_cidr option in your configuration file and provide a valid CIDR notation. Again, ensure this network is not a subnet of your private network. Also, uncomment the cluster_dns option and provide a single IP address from the service_cidr range. This sets the IP address for the coredns service.

Sizing the Networks: The networks you choose should have enough space for your expected number of pods and services. By default, /16 networks are used. Select an appropriate size, as changing the CIDR later is not supported.


Idempotency

The create command can be run multiple times with the same configuration without causing issues, as the process is idempotent. If the process gets stuck or encounters errors (e.g., due to Hetzner API unavailability or timeouts), you can stop the command and rerun it with the same configuration to continue where it left off. Note that the kubeconfig will be overwritten each time you rerun the command.


Limitations:

  • Using a snapshot instead of a default image will take longer to create instances compared to regular images.
  • The networking.allowed_networks.api setting specifies which networks can access the Kubernetes API, but this currently only works with single-master clusters. Multi-master HA clusters can optionally use a load balancer for the API, but Hetzner’s firewalls do not yet support load balancers.
  • If you enable autoscaling for a nodepool, avoid changing this setting later, as it can cause issues with the autoscaler.
  • Autoscaling is only supported with Ubuntu or other default images, not snapshots.
  • SSH keys with passphrases can only be used if you set networking.ssh.use_ssh_agent to true and use an SSH agent to access your key. For example, on macOS, you can start an agent like this:
eval "$(ssh-agent -s)"
ssh-add --apple-use-keychain ~/.ssh/<private key>