Skip to content

Commit 160af5b

Browse files
author
Julien Girardin
committed
Refactor join of workers nodes
1 parent 48a765e commit 160af5b

File tree

23 files changed

+249
-161
lines changed

23 files changed

+249
-161
lines changed

README.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,31 @@ What ansible-kubeadm expect to be done and will not do:
1717
- remove unattented-upgrade
1818
- configure CNI
1919

20+
2021
## Quickstart
2122

2223
see [Quickstart](docs/quickstart.md)
2324

25+
2426
## Configuration
2527

2628
If you want a customized (ansible-)kubeadm experience there is a number of variables you can use:
2729

2830
[Variables reference](docs/variables.md)
2931

32+
33+
## Guides
34+
35+
Some operation has their own guided page:
36+
37+
- [join nodes](docs/guides/join_nodes.md)
38+
39+
3040
## Flow
3141

3242
If you're looking for what ansible-kubeadm is doing step-by-step, [hooks && plugins](docs/hooks_and_plugins.md) is a good way to start.
3343

44+
3445
## Migration planning
3546

3647
Long term migration plan, [*] to indicate current phase
@@ -42,4 +53,4 @@ Long term migration plan, [*] to indicate current phase
4253

4354
## Tips and Tricks
4455

45-
[Tips&Tricks](tips_tricks.md)
56+
[Tips&Tricks](docs/tips_tricks.md)

docs/guides/join_nodes.md

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# To join worker-only nodes
2+
3+
**Note** : For control plane nodes, see dedicated [section](join_nodes.md#to-join-control-plane-nodes)
4+
5+
Let's assume that you have a cluster with two nodes and that you want to add a third node `node-3`
6+
You can join multiple worker node at once with this procedure,
7+
8+
### Add node to the inventory
9+
10+
First, add the node to the inventory like the following inventory:
11+
12+
```
13+
[kube_control_plane]
14+
cp-1
15+
cp-2
16+
cp-3
17+
18+
[kube_workers]
19+
node-1
20+
node-2
21+
node-3
22+
```
23+
24+
25+
### [optional] Deploy local apiserver proxy
26+
27+
If you don't have provision a load-balancer and require the local haproxy to be deployed:
28+
29+
```
30+
ansible-playbook -i inventory enix.kubeadm.00_apiserver_proxy -l kube_control_plane:nodes-3
31+
```
32+
You can skip the `-l` argument, if you're cluster doesn't have pending change you want to preserve on other nodes.
33+
Don't forget to put all control_plane or it will fail to provision the apiserver proxy
34+
35+
36+
### Create bootstrap-token
37+
38+
Then create a bootstrap token by adding using the `bootstrap_token` tag.
39+
Don't use a limit that skip control plane nodes.
40+
41+
```
42+
ansible-playbook -i inventory.cfg enix.kubeadm.01_site -t bootstrap_token
43+
```
44+
45+
No need to retrieve it by yourself, it will be discovered when joining the node
46+
The token has a validity of 1H, so you don't need to repeat this step each time you try to join nodes
47+
48+
### Joining nodes
49+
50+
You can join a node and skip other changes to the cluster by using the `join` tag.
51+
With the tag, you can limit to hosts you want to join.
52+
53+
```
54+
ansible-play -i inventory.cfg enix.kubeadm.01_site -t join -l nodes-3
55+
```
56+
57+
## Alternative method
58+
59+
You can merge the creation of the boostrap token with the joining of the action of join:
60+
61+
```
62+
ansible-playbook -i inventory.cfg enix.kubeadm.01_site -t bootstap_token,join -l kube_control_plane:node-3
63+
```
64+
65+
Please note that you need to include a least one control plane node in the limit host pattern,
66+
You can also skip the limit host pattern to apply to all nodes as those step are indempotent on their own: it will not mess with the current nodes.
67+
68+
# To join control-plane nodes
69+
70+
There is no tag for this operation, you need to apply the entire playbook for this

playbooks/00_apiserver_proxy.yml

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
- hosts: '{{ kube_cp_group|default("kube_control_plane") }}'
99
vars:
1010
_control_plane: true
11+
pre_tasks:
12+
- name: 'Fail if not all master in the specified limit'
13+
fail:
14+
msg: 'Not all control_plane provided, ajust --limit to provid all control_plane'
15+
when: groups[kube_worker_group|default("kube_workers")]|difference(ansible_play_hosts)|length > 0
1116
roles:
1217
- role: find_ip
1318

playbooks/01_site.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
name: hooks_call
5353
vars:
5454
kubeadm_hook_list: ['pre_init']
55-
5655
roles:
5756
- role: packages
5857
- role: init_cp
@@ -71,6 +70,8 @@
7170
kubeadm_hook_list: ['pre_config_update']
7271
roles:
7372
- role: bootstrap_token
73+
tags: ['bootstrap_token']
74+
- role: upload_certs
7475
- role: kubeadm_configs_update
7576
tasks:
7677
- include_role:
@@ -183,6 +184,7 @@
183184
- name: 'Join new workers nodes'
184185
hosts: '{{ kube_worker_group|default("kube_workers") }}'
185186
gather_facts: false
187+
tags: ['join']
186188
pre_tasks:
187189
- include_role:
188190
name: hooks_call
+1-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,2 @@
11
---
2-
sensitive_debug: false
3-
cluster_config: {}
4-
5-
kubeadm_config_yaml: '/tmp/kubeadm-config-{{ansible_date_time.iso8601 }}.yaml'
6-
7-
python2_openssl: python-openssl
8-
python3_openssl: python3-openssl
2+
_valid_bootstrap_tokens: []

roles/bootstrap_token/meta/main.yml

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
dependencies:
3+
- role: common_vars
34
- role: kubectl_module
45
galaxy_info:
56
author: Julien Girardin

roles/bootstrap_token/tasks/bootstrap_token.yml

-41
This file was deleted.

roles/bootstrap_token/tasks/main.yaml

+25-79
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,27 @@
11
---
2-
- name: 'Select candidate host to run init'
2+
- name: 'Find nodes to join'
33
set_fact:
4-
kubeadm_host: '{{ groups.cp_running|default(ansible_play_hosts, true)|first }}'
5-
6-
- name: 'Retrieve a valid bootstrap token'
7-
import_tasks: bootstrap_token.yml
8-
9-
- name: 'Create bootstrap token if no valid found'
10-
command: kubeadm token create
11-
run_once: true
12-
delegate_to: '{{ kubeadm_host }}'
13-
when: valid_bootstrap_tokens|length == 0
14-
15-
- name: 'Retrieve a valid bootstrap token'
16-
import_tasks: bootstrap_token.yml
17-
when: valid_bootstrap_tokens|length == 0
18-
19-
# TODO: fix two following tasks to be more platform dependent
20-
- name: 'Install python-openssl'
21-
package:
22-
name: >-
23-
{%- if ansible_python.version.major > 2 -%}
24-
{{ python3_openssl }}
25-
{%- else -%}
26-
{{ python2_openssl }}
27-
{%- endif -%}
28-
state: present
29-
run_once: true
30-
delegate_to: '{{ kubeadm_host }}'
31-
32-
- name: 'Get info from ca'
33-
openssl_certificate_info:
34-
path: /etc/kubernetes/pki/ca.crt
35-
run_once: true
36-
delegate_to: '{{ kubeadm_host }}'
37-
register: ca_info
38-
when: not(groups.cp_init is defined and ansible_check_mode)
39-
40-
- name: 'Display Kubernetes CA(cert) properties'
41-
debug:
42-
var: ca_info
43-
verbosity: 1
44-
run_once: true
45-
46-
- name: 'List current nodes'
47-
kubectl:
48-
state: get
49-
resource_type: nodes
50-
kubeconfig: /etc/kubernetes/admin.conf
51-
run_once: true
52-
delegate_to: '{{ kubeadm_host }}'
53-
register: current_nodes
54-
when:
55-
- not(found_kubectl.rc == 1 and ansible_check_mode)
56-
57-
- name: 'Compute list of "to-join" nodes'
58-
set_fact:
59-
# "items" cannot be defaulted easily as jinja fallback on using method instead
60-
to_join_cp: >-
61-
{{ ansible_play_hosts|difference(
62-
({"items": []}|combine(current_nodes))["items"]|map(attribute="metadata.name")) }}
63-
cert_encryption_key: >-
64-
{{ lookup('password', '/dev/null length=64 chars=hexdigits') }}
65-
run_once: true
66-
67-
- name: 'Display list of node that need to be joined'
68-
debug:
69-
var: to_join_cp
70-
verbosity: 1
71-
run_once: true
72-
73-
- name: 'Upload certificates if control-plane node need to be joined'
74-
command: >-
75-
kubeadm init phase upload-certs
76-
--upload-certs
77-
--certificate-key {{ cert_encryption_key }}
78-
no_log: '{{ sensitive_debug|bool }}'
79-
run_once: true
80-
delegate_to: '{{ kubeadm_host }}'
81-
when: to_join_cp|length > 0
4+
nodes_to_join: >-
5+
{{ q('inventory_hostnames', kube_cp_group ~ ':' ~ kube_worker_group)
6+
|map('extract', hostvars)
7+
|rejectattr('_kubelet_config_stat.stat.exists')
8+
|map(attribute='inventory_hostname')|list }}
9+
run_once: true
10+
11+
- name: 'Create bootstrap token'
12+
when: nodes_to_join|length > 0
13+
block:
14+
- name: 'Retrieve a valid bootstrap token'
15+
import_role:
16+
name: bootstrap_token_get
17+
18+
- name: 'Create bootstrap token if no valid found'
19+
command: kubeadm token create
20+
run_once: true
21+
delegate_to: '{{ cp_node }}'
22+
when: _valid_bootstrap_tokens|length == 0
23+
24+
- name: 'Retrieve a valid bootstrap token'
25+
import_role:
26+
name: bootstrap_token_get
27+
when: _valid_bootstrap_tokens|length == 0
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
dependencies:
3+
- role: common_vars
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
- name: 'Fetch bootstrap token'
3+
run_once: true
4+
delegate_to: '{{ cp_node }}'
5+
block:
6+
- name: 'Get list of bootstrap token'
7+
kubectl:
8+
state: get
9+
resource_type: secret
10+
namespace: kube-system
11+
extra_args: '--field-selector=type=bootstrap.kubernetes.io/token'
12+
kubeconfig: /etc/kubernetes/admin.conf
13+
register: _bootstrap_tokens
14+
when:
15+
- not(groups.cp_running|default([])|length == 0 and ansible_check_mode)
16+
17+
- name: 'Display all bootstrap tokens'
18+
debug:
19+
var: _bootstrap_tokens
20+
verbosity: 1
21+
22+
- name: 'Filter expire token'
23+
set_fact:
24+
_valid_bootstrap_tokens: >-
25+
{%- if ansible_collection_name is defined and ansible_collection_name is not none -%}
26+
{%- set filter_name = "enix.kubeadm.bootstrap_token_valid" -%}
27+
{%- else -%}
28+
{%- set filter_name = "bootstrap_token_valid" -%}
29+
{%- endif -%}
30+
{{ [bootstrap_tokens_dry_run["items"]
31+
|selectattr('data.usage-bootstrap-authentication', 'defined')|list]
32+
|map(filter_name)|first }}
33+
vars:
34+
# "items" cannot be defaulted easily as jinja fallback on using method instead
35+
bootstrap_tokens_dry_run: "{{ {'items': []}|combine(_bootstrap_tokens) }}"
36+
37+
- name: 'Display valid bootstrap tokens'
38+
debug:
39+
var: _valid_bootstrap_tokens
40+
verbosity: 1

roles/common_vars/defaults/main.yml

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
---
2+
_kubelet_config_path: /var/lib/kubelet/config.yaml
23
enable_kubeadm_patches: true
34
kubeadm_patch_dir: /etc/kubernetes/patches
45
kube_cp_group: kube_control_plane

roles/join_nodes/defaults/main.yml

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
---
22
_control_plane: false
3+
_kubernetes_ca_cert: /etc/kubernetes/pki/ca.crt

roles/join_nodes/meta/main.yml

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
dependencies:
33
- role: kubectl_module
44
- role: common_vars
5+
- role: bootstrap_token_get
6+
delegate_to: '{{ cp_node }}'
7+
run_once: true
58
galaxy_info:
69
author: Julien Girardin
710
description: Join node to a kubernetes cluster

roles/join_nodes/tasks/ca_info.yml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
- name: "Extract public key from kubernetes CA"
3+
command: openssl x509 -noout -pubkey -in {{ _kubernetes_ca_cert }}
4+
check_mode: false
5+
changed_when: false
6+
delegate_to: '{{ cp_node }}'
7+
run_once: true
8+
register: _kubernetes_ca_fingerprint
9+
10+
- name: "Compute sha256 of fingertprint"
11+
set_fact:
12+
ca_cert_hash: >-
13+
{{ _kubernetes_ca_fingerprint.stdout|regex_replace('[- A-Z]+\n([+/\w\n]+)\n[- A-Z]+', '\g<1>')|b64decode|hash('sha256') }}
14+
run_once: true

0 commit comments

Comments
 (0)