hetzner-k3s 0.4.2 → 0.4.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +6 -6
- data/README.md +18 -4
- data/bin/build.sh +3 -3
- data/lib/hetzner/infra/network.rb +4 -3
- data/lib/hetzner/infra/placement_group.rb +55 -0
- data/lib/hetzner/infra/server.rb +15 -4
- data/lib/hetzner/infra/ssh_key.rb +1 -1
- data/lib/hetzner/k3s/cli.rb +1 -1
- data/lib/hetzner/k3s/cluster.rb +49 -31
- data/lib/hetzner/k3s/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60167b5a135a2932bff53b3d030b22940e3a7a943584ab9645d420fdfa2f59f4
|
4
|
+
data.tar.gz: 6b36f71696ffb4f94d1dfde94b5215c46b9f92fc482c0da148ea84c0cfa5a56f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 73242160d8701cfb92332bf18aefcf7e2c15275493b2e4dedfc16d1e577367e91e338b2c57fc3d5731a5f68317751e1644b7d2be4d505371ee1543f04365685f
|
7
|
+
data.tar.gz: 2da1c1ef1bf32b3c403868d47bc94fbeee1ad8479aa1272db9141c749b695e70489c85c19f8e891fd332a91de6deda000d273e959f1d1d6a1e88edf9efe4310f
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
hetzner-k3s (0.4.
|
4
|
+
hetzner-k3s (0.4.5)
|
5
5
|
bcrypt_pbkdf
|
6
6
|
ed25519
|
7
7
|
http
|
@@ -20,12 +20,12 @@ GEM
|
|
20
20
|
diff-lcs (1.4.4)
|
21
21
|
domain_name (0.5.20190701)
|
22
22
|
unf (>= 0.0.5, < 1.0.0)
|
23
|
-
dry-configurable (0.
|
23
|
+
dry-configurable (0.13.0)
|
24
24
|
concurrent-ruby (~> 1.0)
|
25
|
-
dry-core (~> 0.
|
26
|
-
dry-container (0.
|
25
|
+
dry-core (~> 0.6)
|
26
|
+
dry-container (0.9.0)
|
27
27
|
concurrent-ruby (~> 1.0)
|
28
|
-
dry-configurable (~> 0.
|
28
|
+
dry-configurable (~> 0.13, >= 0.13.0)
|
29
29
|
dry-core (0.7.1)
|
30
30
|
concurrent-ruby (~> 1.0)
|
31
31
|
dry-equalizer (0.3.0)
|
@@ -99,7 +99,7 @@ GEM
|
|
99
99
|
to_regexp (0.2.1)
|
100
100
|
unf (0.1.4)
|
101
101
|
unf_ext
|
102
|
-
unf_ext (0.0.
|
102
|
+
unf_ext (0.0.8)
|
103
103
|
yajl-ruby (1.4.1)
|
104
104
|
yaml-safe_load_stream (0.1.1)
|
105
105
|
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
This is a CLI tool - based on a Ruby gem - to quickly create and manage Kubernetes clusters in [Hetzner Cloud](https://www.hetzner.com/cloud) using the lightweight Kubernetes distribution [k3s](https://k3s.io/) from [Rancher](https://rancher.com/).
|
4
4
|
|
5
|
-
Hetzner Cloud is an awesome cloud provider which offers a truly great service with the best performance/cost ratio in the market.
|
5
|
+
Hetzner Cloud is an awesome cloud provider which offers a truly great service with the best performance/cost ratio in the market. With Hetzner's Cloud Controller Manager and CSI driver you can provision load balancers and persistent volumes very easily.
|
6
6
|
|
7
7
|
k3s is my favorite Kubernetes distribution now because it uses much less memory and CPU, leaving more resources to workloads. It is also super quick to deploy because it's a single binary.
|
8
8
|
|
@@ -38,7 +38,7 @@ This will install the `hetzner-k3s` executable in your PATH.
|
|
38
38
|
Alternatively, if you don't want to set up a Ruby runtime but have Docker installed, you can use a container. Run the following from inside the directory where you have the config file for the cluster (described in the next section):
|
39
39
|
|
40
40
|
```bash
|
41
|
-
docker run --rm -it -v ${PWD}:/cluster -v ${HOME}/.ssh:/tmp/.ssh vitobotta/hetzner-k3s:v0.4.
|
41
|
+
docker run --rm -it -v ${PWD}:/cluster -v ${HOME}/.ssh:/tmp/.ssh vitobotta/hetzner-k3s:v0.4.6 create-cluster --config-file /cluster/test.yaml
|
42
42
|
```
|
43
43
|
|
44
44
|
Replace `test.yaml` with the name of your config file.
|
@@ -78,7 +78,7 @@ If you are using Docker, then set `kubeconfig_path` to `/cluster/kubeconfig` so
|
|
78
78
|
|
79
79
|
If you don't want to specify the Hetzner token in the config file (for example if you want to use the tool with CI), then you can use the `HCLOUD_TOKEN` environment variable instead, which has predecence.
|
80
80
|
|
81
|
-
**Important**: The tool assignes the label `cluster` to each server it creates, with the
|
81
|
+
**Important**: The tool assignes the label `cluster` to each server it creates, with the cluster name you specify in the config file, as the value. So please ensure you don't create unrelated servers in the same project having
|
82
82
|
the label `cluster=<cluster name>`, because otherwise they will be deleted if you delete the cluster. I recommend you create a separate Hetzner project for each cluster, see note at the end of this README for more details.
|
83
83
|
|
84
84
|
|
@@ -86,7 +86,8 @@ If you set `masters.instance_count` to 1 then the tool will create a non highly
|
|
86
86
|
|
87
87
|
You can specify any number of worker node pools for example to have mixed nodes with different specs for different workloads.
|
88
88
|
|
89
|
-
At the moment Hetzner Cloud has
|
89
|
+
At the moment Hetzner Cloud has four locations: two in Germany (`nbg1`, Nuremberg and `fsn1`, Falkensteing), one in Finland (`hel1`, Helsinki) and one in the USA (`ash`, Ashburn, Virginia). Please note that the Ashburn, Virginia location has just
|
90
|
+
been announced and it's limited to AMD instances for now.
|
90
91
|
|
91
92
|
For the available instance types and their specs, either check from inside a project when adding a server manually or run the following with your Hetzner token:
|
92
93
|
|
@@ -241,6 +242,19 @@ I recommend that you create a separate Hetzner project for each cluster, because
|
|
241
242
|
|
242
243
|
## changelog
|
243
244
|
|
245
|
+
- 0.4.5
|
246
|
+
- Added a check to abort gracefully when for some reason one or more servers are not created, for example due to temporary problems with the Hetzner API.
|
247
|
+
|
248
|
+
- 0.4.5
|
249
|
+
- Fix network creation (bug introduced in the previous version)
|
250
|
+
|
251
|
+
- 0.4.4
|
252
|
+
- Add support for the new Ashburn, Virginia (USA) location
|
253
|
+
- Automatically use a placement group so that the instances are all created on different physical hosts for high availability
|
254
|
+
|
255
|
+
- 0.4.3
|
256
|
+
- Fix an issue with SSH key creation
|
257
|
+
|
244
258
|
- 0.4.2
|
245
259
|
- Update Hetzner CSI driver to v1.6.0
|
246
260
|
- Update System Upgrade Controller to v0.8.0
|
data/bin/build.sh
CHANGED
@@ -6,9 +6,9 @@ set -e
|
|
6
6
|
|
7
7
|
IMAGE="vitobotta/hetzner-k3s"
|
8
8
|
|
9
|
-
docker build -t ${IMAGE}:v0.4.
|
9
|
+
docker build -t ${IMAGE}:v0.4.6 \
|
10
10
|
--platform=linux/amd64 \
|
11
|
-
--cache-from ${IMAGE}:v0.4.
|
11
|
+
--cache-from ${IMAGE}:v0.4.5 \
|
12
12
|
--build-arg BUILDKIT_INLINE_CACHE=1 .
|
13
13
|
|
14
|
-
docker push vitobotta/hetzner-k3s:v0.4.
|
14
|
+
docker push vitobotta/hetzner-k3s:v0.4.6
|
@@ -5,7 +5,8 @@ module Hetzner
|
|
5
5
|
@cluster_name = cluster_name
|
6
6
|
end
|
7
7
|
|
8
|
-
def create
|
8
|
+
def create(location:)
|
9
|
+
@location = location
|
9
10
|
puts
|
10
11
|
|
11
12
|
if network = find_network
|
@@ -38,7 +39,7 @@ module Hetzner
|
|
38
39
|
|
39
40
|
private
|
40
41
|
|
41
|
-
attr_reader :hetzner_client, :cluster_name
|
42
|
+
attr_reader :hetzner_client, :cluster_name, :location
|
42
43
|
|
43
44
|
def network_config
|
44
45
|
{
|
@@ -47,7 +48,7 @@ module Hetzner
|
|
47
48
|
subnets: [
|
48
49
|
{
|
49
50
|
ip_range: "10.0.0.0/16",
|
50
|
-
network_zone: "eu-central",
|
51
|
+
network_zone: (location == "ash" ? "us-east" : "eu-central"),
|
51
52
|
type: "cloud"
|
52
53
|
}
|
53
54
|
]
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Hetzner
|
2
|
+
class PlacementGroup
|
3
|
+
def initialize(hetzner_client:, cluster_name:)
|
4
|
+
@hetzner_client = hetzner_client
|
5
|
+
@cluster_name = cluster_name
|
6
|
+
end
|
7
|
+
|
8
|
+
def create
|
9
|
+
puts
|
10
|
+
|
11
|
+
if (placement_group = find_placement_group)
|
12
|
+
puts "Placement group already exists, skipping."
|
13
|
+
puts
|
14
|
+
return placement_group["id"]
|
15
|
+
end
|
16
|
+
|
17
|
+
puts "Creating placement group..."
|
18
|
+
|
19
|
+
response = hetzner_client.post("/placement_groups", placement_group_config).body
|
20
|
+
|
21
|
+
puts "...placement group created."
|
22
|
+
puts
|
23
|
+
|
24
|
+
JSON.parse(response)["placement_group"]["id"]
|
25
|
+
end
|
26
|
+
|
27
|
+
def delete
|
28
|
+
if (placement_group = find_placement_group)
|
29
|
+
puts "Deleting placement group..."
|
30
|
+
hetzner_client.delete("/placement_groups", placement_group["id"])
|
31
|
+
puts "...placement group deleted."
|
32
|
+
else
|
33
|
+
puts "Placement group no longer exists, skipping."
|
34
|
+
end
|
35
|
+
|
36
|
+
puts
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :hetzner_client, :cluster_name
|
42
|
+
|
43
|
+
def placement_group_config
|
44
|
+
{
|
45
|
+
name: cluster_name,
|
46
|
+
type: "spread"
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def find_placement_group
|
51
|
+
hetzner_client.get("/placement_groups")["placement_groups"].detect{ |placement_group| placement_group["name"] == cluster_name }
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
data/lib/hetzner/infra/server.rb
CHANGED
@@ -5,7 +5,7 @@ module Hetzner
|
|
5
5
|
@cluster_name = cluster_name
|
6
6
|
end
|
7
7
|
|
8
|
-
def create(location:, instance_type:, instance_id:, firewall_id:, network_id:, ssh_key_id:)
|
8
|
+
def create(location:, instance_type:, instance_id:, firewall_id:, network_id:, ssh_key_id:, placement_group_id:)
|
9
9
|
puts
|
10
10
|
|
11
11
|
server_name = "#{cluster_name}-#{instance_type}-#{instance_id}"
|
@@ -36,15 +36,26 @@ module Hetzner
|
|
36
36
|
labels: {
|
37
37
|
cluster: cluster_name,
|
38
38
|
role: (server_name =~ /master/ ? "master" : "worker")
|
39
|
-
}
|
39
|
+
},
|
40
|
+
placement_group: placement_group_id
|
40
41
|
}
|
41
42
|
|
42
|
-
response = hetzner_client.post("/servers", server_config)
|
43
|
+
response = hetzner_client.post("/servers", server_config)
|
44
|
+
response_body = response.body
|
45
|
+
|
46
|
+
server = JSON.parse(response_body)["server"]
|
47
|
+
|
48
|
+
unless server
|
49
|
+
puts "Error creating server #{server_name}. Response details below:"
|
50
|
+
puts
|
51
|
+
p response
|
52
|
+
return
|
53
|
+
end
|
43
54
|
|
44
55
|
puts "...server #{server_name} created."
|
45
56
|
puts
|
46
57
|
|
47
|
-
|
58
|
+
server
|
48
59
|
end
|
49
60
|
|
50
61
|
def delete(server_name:)
|
data/lib/hetzner/k3s/cli.rb
CHANGED
@@ -193,7 +193,7 @@ module Hetzner
|
|
193
193
|
|
194
194
|
def validate_location
|
195
195
|
return if locations.empty? && !valid_token?
|
196
|
-
errors << "Invalid location - available locations: nbg1 (Nuremberg, Germany), fsn1 (Falkenstein, Germany), hel1 (Helsinki, Finland)" unless locations.include? configuration.dig("location")
|
196
|
+
errors << "Invalid location - available locations: nbg1 (Nuremberg, Germany), fsn1 (Falkenstein, Germany), hel1 (Helsinki, Finland) or ash (Ashburn, Virginia, USA)" unless locations.include? configuration.dig("location")
|
197
197
|
end
|
198
198
|
|
199
199
|
def find_available_releases
|
data/lib/hetzner/k3s/cluster.rb
CHANGED
@@ -11,6 +11,7 @@ require_relative "../infra/network"
|
|
11
11
|
require_relative "../infra/ssh_key"
|
12
12
|
require_relative "../infra/server"
|
13
13
|
require_relative "../infra/load_balancer"
|
14
|
+
require_relative "../infra/placement_group"
|
14
15
|
|
15
16
|
require_relative "../k3s/client_patch"
|
16
17
|
|
@@ -89,6 +90,11 @@ class Cluster
|
|
89
90
|
master_instance_type = masters_config["instance_type"]
|
90
91
|
masters_count = masters_config["instance_count"]
|
91
92
|
|
93
|
+
placement_group_id = Hetzner::PlacementGroup.new(
|
94
|
+
hetzner_client: hetzner_client,
|
95
|
+
cluster_name: cluster_name
|
96
|
+
).create
|
97
|
+
|
92
98
|
firewall_id = Hetzner::Firewall.new(
|
93
99
|
hetzner_client: hetzner_client,
|
94
100
|
cluster_name: cluster_name
|
@@ -97,7 +103,7 @@ class Cluster
|
|
97
103
|
network_id = Hetzner::Network.new(
|
98
104
|
hetzner_client: hetzner_client,
|
99
105
|
cluster_name: cluster_name
|
100
|
-
).create
|
106
|
+
).create(location: location)
|
101
107
|
|
102
108
|
ssh_key_id = Hetzner::SSHKey.new(
|
103
109
|
hetzner_client: hetzner_client,
|
@@ -113,7 +119,8 @@ class Cluster
|
|
113
119
|
instance_id: "master#{i+1}",
|
114
120
|
firewall_id: firewall_id,
|
115
121
|
network_id: network_id,
|
116
|
-
ssh_key_id: ssh_key_id
|
122
|
+
ssh_key_id: ssh_key_id,
|
123
|
+
placement_group_id: placement_group_id
|
117
124
|
}
|
118
125
|
end
|
119
126
|
|
@@ -136,7 +143,8 @@ class Cluster
|
|
136
143
|
instance_id: "pool-#{worker_node_pool_name}-worker#{i+1}",
|
137
144
|
firewall_id: firewall_id,
|
138
145
|
network_id: network_id,
|
139
|
-
ssh_key_id: ssh_key_id
|
146
|
+
ssh_key_id: ssh_key_id,
|
147
|
+
placement_group_id: placement_group_id
|
140
148
|
}
|
141
149
|
end
|
142
150
|
end
|
@@ -149,6 +157,11 @@ class Cluster
|
|
149
157
|
|
150
158
|
threads.each(&:join) unless threads.empty?
|
151
159
|
|
160
|
+
if server_configs.size != servers.size
|
161
|
+
puts "Something went wrong while creating some servers, please try again."
|
162
|
+
exit 1
|
163
|
+
end
|
164
|
+
|
152
165
|
puts
|
153
166
|
threads = servers.map do |server|
|
154
167
|
Thread.new { wait_for_ssh server }
|
@@ -158,6 +171,11 @@ class Cluster
|
|
158
171
|
end
|
159
172
|
|
160
173
|
def delete_resources
|
174
|
+
Hetzner::PlacementGroup.new(
|
175
|
+
hetzner_client: hetzner_client,
|
176
|
+
cluster_name: cluster_name
|
177
|
+
).delete
|
178
|
+
|
161
179
|
Hetzner::LoadBalancer.new(
|
162
180
|
hetzner_client: hetzner_client,
|
163
181
|
cluster_name: cluster_name
|
@@ -217,28 +235,28 @@ class Cluster
|
|
217
235
|
taint = schedule_workloads_on_masters? ? " " : " --node-taint CriticalAddonsOnly=true:NoExecute "
|
218
236
|
|
219
237
|
<<~EOF
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
238
|
+
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="#{k3s_version}" K3S_TOKEN="#{k3s_token}" INSTALL_K3S_EXEC="server \
|
239
|
+
--disable-cloud-controller \
|
240
|
+
--disable servicelb \
|
241
|
+
--disable traefik \
|
242
|
+
--disable local-storage \
|
243
|
+
--disable metrics-server \
|
244
|
+
--write-kubeconfig-mode=644 \
|
245
|
+
--node-name="$(hostname -f)" \
|
246
|
+
--cluster-cidr=10.244.0.0/16 \
|
247
|
+
--etcd-expose-metrics=true \
|
248
|
+
--kube-controller-manager-arg="address=0.0.0.0" \
|
249
|
+
--kube-controller-manager-arg="bind-address=0.0.0.0" \
|
250
|
+
--kube-proxy-arg="metrics-bind-address=0.0.0.0" \
|
251
|
+
--kube-scheduler-arg="address=0.0.0.0" \
|
252
|
+
--kube-scheduler-arg="bind-address=0.0.0.0" \
|
253
|
+
#{taint} \
|
254
|
+
--kubelet-arg="cloud-provider=external" \
|
255
|
+
--advertise-address=$(hostname -I | awk '{print $2}') \
|
256
|
+
--node-ip=$(hostname -I | awk '{print $2}') \
|
257
|
+
--node-external-ip=$(hostname -I | awk '{print $1}') \
|
258
|
+
--flannel-iface=#{flannel_interface} \
|
259
|
+
#{server} #{tls_sans}" sh -
|
242
260
|
EOF
|
243
261
|
end
|
244
262
|
|
@@ -246,12 +264,12 @@ class Cluster
|
|
246
264
|
flannel_interface = find_flannel_interface(worker)
|
247
265
|
|
248
266
|
<<~EOF
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
267
|
+
curl -sfL https://get.k3s.io | K3S_TOKEN="#{k3s_token}" INSTALL_K3S_VERSION="#{k3s_version}" K3S_URL=https://#{first_master_private_ip}:6443 INSTALL_K3S_EXEC="agent \
|
268
|
+
--node-name="$(hostname -f)" \
|
269
|
+
--kubelet-arg="cloud-provider=external" \
|
270
|
+
--node-ip=$(hostname -I | awk '{print $2}') \
|
271
|
+
--node-external-ip=$(hostname -I | awk '{print $1}') \
|
272
|
+
--flannel-iface=#{flannel_interface}" sh -
|
255
273
|
EOF
|
256
274
|
end
|
257
275
|
|
data/lib/hetzner/k3s/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hetzner-k3s
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vito Botta
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -140,6 +140,7 @@ files:
|
|
140
140
|
- lib/hetzner/infra/firewall.rb
|
141
141
|
- lib/hetzner/infra/load_balancer.rb
|
142
142
|
- lib/hetzner/infra/network.rb
|
143
|
+
- lib/hetzner/infra/placement_group.rb
|
143
144
|
- lib/hetzner/infra/server.rb
|
144
145
|
- lib/hetzner/infra/ssh_key.rb
|
145
146
|
- lib/hetzner/k3s/cli.rb
|