hetzner-k3s 0.5.7 → 0.6.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +32 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +1 -1
- data/.ruby-version +1 -1
- data/Gemfile +13 -2
- data/Gemfile.lock +22 -53
- data/README.md +33 -15
- data/bin/build.sh +12 -6
- data/config/warble.rb +182 -0
- data/exe/hetzner-k3s +3 -0
- data/hetzner-k3s.gemspec +4 -4
- data/lib/hetzner/infra/client.rb +5 -5
- data/lib/hetzner/infra/firewall.rb +7 -9
- data/lib/hetzner/infra/load_balancer.rb +1 -1
- data/lib/hetzner/infra/network.rb +19 -9
- data/lib/hetzner/infra/server.rb +49 -37
- data/lib/hetzner/k3s/cli.rb +16 -393
- data/lib/hetzner/k3s/cluster.rb +71 -53
- data/lib/hetzner/k3s/configuration.rb +486 -0
- data/lib/hetzner/k3s/version.rb +1 -1
- data/lib/hetzner/utils.rb +19 -10
- metadata +26 -23
data/lib/hetzner/k3s/cluster.rb
CHANGED
@@ -4,7 +4,7 @@ require 'net/ssh'
|
|
4
4
|
require 'securerandom'
|
5
5
|
require 'base64'
|
6
6
|
require 'timeout'
|
7
|
-
require '
|
7
|
+
require 'fileutils'
|
8
8
|
|
9
9
|
require_relative '../infra/client'
|
10
10
|
require_relative '../infra/firewall'
|
@@ -19,13 +19,11 @@ require_relative '../utils'
|
|
19
19
|
class Cluster
|
20
20
|
include Utils
|
21
21
|
|
22
|
-
def initialize(
|
23
|
-
@
|
24
|
-
@hetzner_token = hetzner_token
|
22
|
+
def initialize(configuration:)
|
23
|
+
@configuration = configuration
|
25
24
|
end
|
26
25
|
|
27
|
-
def create
|
28
|
-
@configuration = configuration
|
26
|
+
def create
|
29
27
|
@cluster_name = configuration['cluster_name']
|
30
28
|
@kubeconfig_path = File.expand_path(configuration['kubeconfig_path'])
|
31
29
|
@public_ssh_key_path = File.expand_path(configuration['public_ssh_key_path'])
|
@@ -37,7 +35,8 @@ class Cluster
|
|
37
35
|
@masters_location = configuration['location']
|
38
36
|
@verify_host_key = configuration.fetch('verify_host_key', false)
|
39
37
|
@servers = []
|
40
|
-
@
|
38
|
+
@ssh_networks = configuration['ssh_allowed_networks']
|
39
|
+
@api_networks = configuration['api_allowed_networks']
|
41
40
|
@enable_encryption = configuration.fetch('enable_encryption', false)
|
42
41
|
@kube_api_server_args = configuration.fetch('kube_api_server_args', [])
|
43
42
|
@kube_scheduler_args = configuration.fetch('kube_scheduler_args', [])
|
@@ -57,8 +56,7 @@ class Cluster
|
|
57
56
|
deploy_system_upgrade_controller
|
58
57
|
end
|
59
58
|
|
60
|
-
def delete
|
61
|
-
@configuration = configuration
|
59
|
+
def delete
|
62
60
|
@cluster_name = configuration['cluster_name']
|
63
61
|
@kubeconfig_path = File.expand_path(configuration['kubeconfig_path'])
|
64
62
|
@public_ssh_key_path = File.expand_path(configuration['public_ssh_key_path'])
|
@@ -68,8 +66,7 @@ class Cluster
|
|
68
66
|
delete_resources
|
69
67
|
end
|
70
68
|
|
71
|
-
def upgrade(
|
72
|
-
@configuration = configuration
|
69
|
+
def upgrade(new_k3s_version:, config_file:)
|
73
70
|
@cluster_name = configuration['cluster_name']
|
74
71
|
@kubeconfig_path = File.expand_path(configuration['kubeconfig_path'])
|
75
72
|
@new_k3s_version = new_k3s_version
|
@@ -82,21 +79,21 @@ class Cluster
|
|
82
79
|
|
83
80
|
attr_accessor :servers
|
84
81
|
|
85
|
-
attr_reader :
|
82
|
+
attr_reader :configuration, :cluster_name, :kubeconfig_path, :k3s_version,
|
86
83
|
:masters_config, :worker_node_pools,
|
87
84
|
:masters_location, :public_ssh_key_path,
|
88
|
-
:hetzner_token, :new_k3s_version,
|
89
|
-
:config_file, :verify_host_key, :
|
85
|
+
:hetzner_token, :new_k3s_version,
|
86
|
+
:config_file, :verify_host_key, :ssh_networks, :private_ssh_key_path,
|
90
87
|
:enable_encryption, :kube_api_server_args, :kube_scheduler_args,
|
91
88
|
:kube_controller_manager_args, :kube_cloud_controller_manager_args,
|
92
|
-
:kubelet_args, :kube_proxy_args
|
89
|
+
:kubelet_args, :kube_proxy_args, :api_networks
|
93
90
|
|
94
91
|
def find_worker_node_pools(configuration)
|
95
92
|
configuration.fetch('worker_node_pools', [])
|
96
93
|
end
|
97
94
|
|
98
95
|
def latest_k3s_version
|
99
|
-
response =
|
96
|
+
response = HTTParty.get('https://api.github.com/repos/k3s-io/k3s/tags').body
|
100
97
|
JSON.parse(response).first['name']
|
101
98
|
end
|
102
99
|
|
@@ -106,22 +103,22 @@ class Cluster
|
|
106
103
|
end
|
107
104
|
|
108
105
|
def delete_placement_groups
|
109
|
-
Hetzner::PlacementGroup.new(hetzner_client
|
106
|
+
Hetzner::PlacementGroup.new(hetzner_client: hetzner_client, cluster_name: cluster_name).delete
|
110
107
|
|
111
108
|
worker_node_pools.each do |pool|
|
112
109
|
pool_name = pool['name']
|
113
|
-
Hetzner::PlacementGroup.new(hetzner_client
|
110
|
+
Hetzner::PlacementGroup.new(hetzner_client: hetzner_client, cluster_name: cluster_name, pool_name: pool_name).delete
|
114
111
|
end
|
115
112
|
end
|
116
113
|
|
117
114
|
def delete_resources
|
118
|
-
Hetzner::LoadBalancer.new(hetzner_client
|
115
|
+
Hetzner::LoadBalancer.new(hetzner_client: hetzner_client, cluster_name: cluster_name).delete(high_availability: (masters.size > 1))
|
119
116
|
|
120
|
-
Hetzner::Firewall.new(hetzner_client
|
117
|
+
Hetzner::Firewall.new(hetzner_client: hetzner_client, cluster_name: cluster_name).delete(all_servers)
|
121
118
|
|
122
|
-
Hetzner::Network.new(hetzner_client
|
119
|
+
Hetzner::Network.new(hetzner_client: hetzner_client, cluster_name: cluster_name, existing_network: existing_network).delete
|
123
120
|
|
124
|
-
Hetzner::SSHKey.new(hetzner_client
|
121
|
+
Hetzner::SSHKey.new(hetzner_client: hetzner_client, cluster_name: cluster_name).delete(public_ssh_key_path: public_ssh_key_path)
|
125
122
|
|
126
123
|
delete_placement_groups
|
127
124
|
delete_servers
|
@@ -190,15 +187,30 @@ class Cluster
|
|
190
187
|
puts 'Upgrade will now start. Run `watch kubectl get nodes` to see the nodes being upgraded. This should take a few minutes for a small cluster.'
|
191
188
|
puts 'The API server may be briefly unavailable during the upgrade of the controlplane.'
|
192
189
|
|
193
|
-
|
190
|
+
updated_configuration = configuration.raw
|
191
|
+
updated_configuration['k3s_version'] = new_k3s_version
|
194
192
|
|
195
|
-
File.write(config_file,
|
193
|
+
File.write(config_file, updated_configuration.to_yaml)
|
196
194
|
end
|
197
195
|
|
198
196
|
def master_script(master)
|
199
197
|
server = master == first_master ? ' --cluster-init ' : " --server https://#{api_server_ip}:6443 "
|
200
198
|
flannel_interface = find_flannel_interface(master)
|
201
|
-
|
199
|
+
|
200
|
+
available_k3s_releases = Hetzner::Configuration.available_releases
|
201
|
+
wireguard_native_min_version_index = available_k3s_releases.find_index('v1.23.6+k3s1')
|
202
|
+
selected_version_index = available_k3s_releases.find_index(k3s_version)
|
203
|
+
|
204
|
+
flannel_wireguard = if enable_encryption
|
205
|
+
if selected_version_index >= wireguard_native_min_version_index
|
206
|
+
' --flannel-backend=wireguard-native '
|
207
|
+
else
|
208
|
+
' --flannel-backend=wireguard '
|
209
|
+
end
|
210
|
+
else
|
211
|
+
' '
|
212
|
+
end
|
213
|
+
|
202
214
|
extra_args = "#{kube_api_server_args_list} #{kube_scheduler_args_list} #{kube_controller_manager_args_list} #{kube_cloud_controller_manager_args_list} #{kubelet_args_list} #{kube_proxy_args_list}"
|
203
215
|
taint = schedule_workloads_on_masters? ? ' ' : ' --node-taint CriticalAddonsOnly=true:NoExecute '
|
204
216
|
|
@@ -214,10 +226,8 @@ class Cluster
|
|
214
226
|
--cluster-cidr=10.244.0.0/16 \
|
215
227
|
--etcd-expose-metrics=true \
|
216
228
|
#{flannel_wireguard} \
|
217
|
-
--kube-controller-manager-arg="address=0.0.0.0" \
|
218
229
|
--kube-controller-manager-arg="bind-address=0.0.0.0" \
|
219
230
|
--kube-proxy-arg="metrics-bind-address=0.0.0.0" \
|
220
|
-
--kube-scheduler-arg="address=0.0.0.0" \
|
221
231
|
--kube-scheduler-arg="bind-address=0.0.0.0" \
|
222
232
|
#{taint} #{extra_args} \
|
223
233
|
--kubelet-arg="cloud-provider=external" \
|
@@ -298,8 +308,8 @@ class Cluster
|
|
298
308
|
namespace: 'kube-system'
|
299
309
|
name: 'hcloud'
|
300
310
|
stringData:
|
301
|
-
network: "#{cluster_name}"
|
302
|
-
token: "#{hetzner_token}"
|
311
|
+
network: "#{existing_network || cluster_name}"
|
312
|
+
token: "#{configuration.hetzner_token}"
|
303
313
|
EOF
|
304
314
|
BASH
|
305
315
|
|
@@ -318,7 +328,7 @@ class Cluster
|
|
318
328
|
puts
|
319
329
|
puts 'Deploying k3s System Upgrade Controller...'
|
320
330
|
|
321
|
-
cmd = 'kubectl apply -f https://github.com/rancher/system-upgrade-controller/releases/download/v0.
|
331
|
+
cmd = 'kubectl apply -f https://github.com/rancher/system-upgrade-controller/releases/download/v0.9.1/system-upgrade-controller.yaml'
|
322
332
|
|
323
333
|
run cmd, kubeconfig_path: kubeconfig_path
|
324
334
|
|
@@ -339,13 +349,13 @@ class Cluster
|
|
339
349
|
namespace: 'kube-system'
|
340
350
|
name: 'hcloud-csi'
|
341
351
|
stringData:
|
342
|
-
token: "#{hetzner_token}"
|
352
|
+
token: "#{configuration.hetzner_token}"
|
343
353
|
EOF
|
344
354
|
BASH
|
345
355
|
|
346
356
|
run cmd, kubeconfig_path: kubeconfig_path
|
347
357
|
|
348
|
-
cmd = 'kubectl apply -f https://raw.githubusercontent.com/hetznercloud/csi-driver/
|
358
|
+
cmd = 'kubectl apply -f https://raw.githubusercontent.com/hetznercloud/csi-driver/master/deploy/kubernetes/hcloud-csi.yml'
|
349
359
|
|
350
360
|
run cmd, kubeconfig_path: kubeconfig_path
|
351
361
|
|
@@ -413,7 +423,7 @@ class Cluster
|
|
413
423
|
|
414
424
|
masters.each do |master|
|
415
425
|
master_private_ip = master['private_net'][0]['ip']
|
416
|
-
sans
|
426
|
+
sans += " --tls-san=#{master_private_ip} "
|
417
427
|
end
|
418
428
|
|
419
429
|
sans
|
@@ -463,7 +473,7 @@ class Cluster
|
|
463
473
|
|
464
474
|
def placement_group_id(pool_name = nil)
|
465
475
|
@placement_groups ||= {}
|
466
|
-
@placement_groups[pool_name || '__masters__'] ||= Hetzner::PlacementGroup.new(hetzner_client
|
476
|
+
@placement_groups[pool_name || '__masters__'] ||= Hetzner::PlacementGroup.new(hetzner_client: hetzner_client, cluster_name: cluster_name, pool_name: pool_name).create
|
467
477
|
end
|
468
478
|
|
469
479
|
def master_instance_type
|
@@ -475,15 +485,15 @@ class Cluster
|
|
475
485
|
end
|
476
486
|
|
477
487
|
def firewall_id
|
478
|
-
@firewall_id ||= Hetzner::Firewall.new(hetzner_client
|
488
|
+
@firewall_id ||= Hetzner::Firewall.new(hetzner_client: hetzner_client, cluster_name: cluster_name).create(high_availability: (masters_count > 1), ssh_networks: ssh_networks, api_networks: api_networks)
|
479
489
|
end
|
480
490
|
|
481
491
|
def network_id
|
482
|
-
@network_id ||= Hetzner::Network.new(hetzner_client
|
492
|
+
@network_id ||= Hetzner::Network.new(hetzner_client: hetzner_client, cluster_name: cluster_name, existing_network: existing_network).create(location: masters_location)
|
483
493
|
end
|
484
494
|
|
485
495
|
def ssh_key_id
|
486
|
-
@ssh_key_id ||= Hetzner::SSHKey.new(hetzner_client
|
496
|
+
@ssh_key_id ||= Hetzner::SSHKey.new(hetzner_client: hetzner_client, cluster_name: cluster_name).create(public_ssh_key_path: public_ssh_key_path)
|
487
497
|
end
|
488
498
|
|
489
499
|
def master_definitions_for_create
|
@@ -494,13 +504,13 @@ class Cluster
|
|
494
504
|
instance_type: master_instance_type,
|
495
505
|
instance_id: "master#{i + 1}",
|
496
506
|
location: masters_location,
|
497
|
-
placement_group_id
|
498
|
-
firewall_id
|
499
|
-
network_id
|
500
|
-
ssh_key_id
|
501
|
-
image
|
502
|
-
additional_packages
|
503
|
-
additional_post_create_commands:
|
507
|
+
placement_group_id: placement_group_id,
|
508
|
+
firewall_id: firewall_id,
|
509
|
+
network_id: network_id,
|
510
|
+
ssh_key_id: ssh_key_id,
|
511
|
+
image: image,
|
512
|
+
additional_packages: additional_packages,
|
513
|
+
additional_post_create_commands: additional_post_create_commands
|
504
514
|
}
|
505
515
|
end
|
506
516
|
|
@@ -534,12 +544,12 @@ class Cluster
|
|
534
544
|
instance_id: "pool-#{worker_node_pool_name}-worker#{i + 1}",
|
535
545
|
placement_group_id: placement_group_id(worker_node_pool_name),
|
536
546
|
location: worker_location,
|
537
|
-
firewall_id
|
538
|
-
network_id
|
539
|
-
ssh_key_id
|
540
|
-
image
|
541
|
-
additional_packages
|
542
|
-
additional_post_create_commands:
|
547
|
+
firewall_id: firewall_id,
|
548
|
+
network_id: network_id,
|
549
|
+
ssh_key_id: ssh_key_id,
|
550
|
+
image: image,
|
551
|
+
additional_packages: additional_packages,
|
552
|
+
additional_post_create_commands: additional_post_create_commands
|
543
553
|
}
|
544
554
|
end
|
545
555
|
|
@@ -547,7 +557,7 @@ class Cluster
|
|
547
557
|
end
|
548
558
|
|
549
559
|
def create_load_balancer
|
550
|
-
Hetzner::LoadBalancer.new(hetzner_client
|
560
|
+
Hetzner::LoadBalancer.new(hetzner_client: hetzner_client, cluster_name: cluster_name).create(location: masters_location, network_id: network_id)
|
551
561
|
end
|
552
562
|
|
553
563
|
def server_configs
|
@@ -567,7 +577,7 @@ class Cluster
|
|
567
577
|
|
568
578
|
threads = server_configs.map do |server_config|
|
569
579
|
Thread.new do
|
570
|
-
servers << Hetzner::Server.new(hetzner_client
|
580
|
+
servers << Hetzner::Server.new(hetzner_client: hetzner_client, cluster_name: cluster_name).create(**server_config)
|
571
581
|
end
|
572
582
|
end
|
573
583
|
|
@@ -589,7 +599,7 @@ class Cluster
|
|
589
599
|
def delete_servers
|
590
600
|
threads = all_servers.map do |server|
|
591
601
|
Thread.new do
|
592
|
-
Hetzner::Server.new(hetzner_client
|
602
|
+
Hetzner::Server.new(hetzner_client: hetzner_client, cluster_name: cluster_name).delete(server_name: server['name'])
|
593
603
|
end
|
594
604
|
end
|
595
605
|
|
@@ -643,4 +653,12 @@ class Cluster
|
|
643
653
|
" --kube-proxy-arg=\"#{arg}\" "
|
644
654
|
end.join
|
645
655
|
end
|
656
|
+
|
657
|
+
def hetzner_client
|
658
|
+
configuration.hetzner_client
|
659
|
+
end
|
660
|
+
|
661
|
+
def existing_network
|
662
|
+
configuration['existing_network']
|
663
|
+
end
|
646
664
|
end
|