Files
kubespray/roles/kubernetes/control-plane/tasks/kubeadm-setup.yml
Seena Fallah 44ab38baae control-plane: fix kubeadm ops when advertiseAddress cannot be derived from default gateway (#13156)
* download: add apiserver ip in InitConfiguration

In setups where the advertiseAddress cannot be derived from the
default gateway, setting localAPIEndpoint.advertiseAddress will
eliminate the error caused by that. this is like other kubeadm
configs that we have.

Signed-off-by: Seena Fallah <seenafallah@gmail.com>

* control-plane: pass kubeadm config when creating token

kubeadm token create subcommand will generate a default config when
nothing as --config is passed. therefore if we have setups that need
the config values to let kubeadm work (like if the ip cannot be derived
by the default gateway) it will fail.

Signed-off-by: Seena Fallah <seenafallah@gmail.com>

---------

Signed-off-by: Seena Fallah <seenafallah@gmail.com>
2026-09-10 07:42:37 +00:00

273 lines
11 KiB
YAML

---
- name: Install OIDC certificate
copy:
content: "{{ kube_oidc_ca_cert | b64decode }}"
dest: "{{ kube_oidc_ca_file }}"
owner: root
group: root
mode: "0644"
when:
- kube_oidc_auth
- kube_oidc_ca_cert is defined
- name: Kubeadm | Check if kubeadm has already run
stat:
path: "/var/lib/kubelet/config.yaml"
get_attributes: false
get_checksum: false
get_mime: false
register: kubeadm_already_run
- name: Kubeadm | Backup kubeadm certs / kubeconfig
import_tasks: kubeadm-backup.yml
when:
- kubeadm_already_run.stat.exists
- name: Kubeadm | aggregate all SANs
set_fact:
apiserver_sans: "{{ _apiserver_sans | flatten | select | unique }}"
vars:
_apiserver_sans:
- "kubernetes"
- "kubernetes.default"
- "kubernetes.default.svc"
- "kubernetes.default.svc.{{ dns_domain }}"
- "{{ kube_apiserver_ip }}"
- "localhost"
- "127.0.0.1"
- "::1"
- "{{ apiserver_loadbalancer_domain_name | d('') }}"
- "{{ loadbalancer_apiserver.address | d('') }}"
- "{{ supplementary_addresses_in_ssl_keys }}"
- "{{ groups['kube_control_plane'] | map('extract', hostvars, 'main_access_ip') }}"
- "{{ groups['kube_control_plane'] | map('extract', hostvars, 'main_ip') }}"
- "{{ groups['kube_control_plane'] | map('extract', hostvars, ['ansible_default_ipv4', 'address']) | select('defined') }}"
- "{{ groups['kube_control_plane'] | map('extract', hostvars, ['ansible_default_ipv6', 'address']) | select('defined') }}"
- "{{ groups['kube_control_plane'] | map('extract', hostvars, 'ansible_hostname') }}"
- "{{ groups['kube_control_plane'] | map('extract', hostvars, 'ansible_fqdn') }}"
- "{{ kube_override_hostname }}"
- "{{ kube_vip_address }}"
tags: facts
- name: Create audit-policy directory
file:
path: "{{ audit_policy_file | dirname }}"
state: directory
mode: "0640"
when: kubernetes_audit or kubernetes_audit_webhook
- name: Write api audit policy yaml
template:
src: apiserver-audit-policy.yaml.j2
dest: "{{ audit_policy_file }}"
mode: "0640"
when: kubernetes_audit or kubernetes_audit_webhook
notify: Control plane | Restart apiserver
- name: Write api audit webhook config yaml
template:
src: apiserver-audit-webhook-config.yaml.j2
dest: "{{ audit_webhook_config_file }}"
mode: "0640"
when: kubernetes_audit_webhook
notify: Control plane | Restart apiserver
- name: Create apiserver tracing config directory
file:
path: "{{ kube_config_dir }}/tracing"
state: directory
mode: "0640"
when: kube_apiserver_tracing
- name: Write apiserver tracing config yaml
template:
src: apiserver-tracing.yaml.j2
dest: "{{ kube_config_dir }}/tracing/apiserver-tracing.yaml"
mode: "0640"
when: kube_apiserver_tracing
notify: Control plane | Restart apiserver
# Nginx LB(default), If kubeadm_config_api_fqdn is defined, use other LB by kubeadm controlPlaneEndpoint.
- name: Set kubeadm_config_api_fqdn define
set_fact:
kubeadm_config_api_fqdn: "{{ apiserver_loadbalancer_domain_name }}"
when: loadbalancer_apiserver is defined
- name: Kubeadm | Create kubeadm config
template:
src: "kubeadm-config.v1beta4.yaml.j2"
dest: "{{ kube_config_dir }}/kubeadm-config.yaml"
mode: "0640"
validate: "{{ kubeadm_config_validate_enabled | ternary(bin_dir + '/kubeadm config validate --config %s', omit) }}"
- name: Kubeadm | Create directory to store admission control configurations
file:
path: "{{ kube_config_dir }}/admission-controls"
state: directory
mode: "0640"
when: kube_apiserver_admission_control_config_file
- name: Kubeadm | Push admission control config file
template:
src: "admission-controls.yaml.j2"
dest: "{{ kube_config_dir }}/admission-controls/admission-controls.yaml"
mode: "0640"
when: kube_apiserver_admission_control_config_file
notify: Control plane | Restart apiserver
- name: Kubeadm | Push admission control config files
template:
src: "{{ item | lower }}.yaml.j2"
dest: "{{ kube_config_dir }}/admission-controls/{{ item | lower }}.yaml"
mode: "0640"
when:
- kube_apiserver_admission_control_config_file
- item in kube_apiserver_admission_plugins_needs_configuration
loop: "{{ kube_apiserver_enable_admission_plugins }}"
notify: Control plane | Restart apiserver
- name: Kubeadm | Check apiserver.crt SANs
vars:
apiserver_ips: "{{ apiserver_sans | map('ansible.utils.ipaddr') | reject('equalto', False) | list }}"
apiserver_hosts: "{{ apiserver_sans | difference(apiserver_ips) }}"
when:
- kubeadm_already_run.stat.exists
- not kube_external_ca_mode
block:
- name: Kubeadm | Check apiserver.crt SAN IPs
command:
cmd: "openssl x509 -noout -in {{ kube_cert_dir }}/apiserver.crt -checkip {{ item }}"
loop: "{{ apiserver_ips }}"
register: apiserver_sans_ip_check
changed_when: apiserver_sans_ip_check.stdout is not search('does match certificate')
failed_when: apiserver_sans_ip_check.rc != 0 and apiserver_sans_ip_check.stdout is not search('does NOT match certificate')
- name: Kubeadm | Check apiserver.crt SAN hosts
command:
cmd: "openssl x509 -noout -in {{ kube_cert_dir }}/apiserver.crt -checkhost {{ item }}"
loop: "{{ apiserver_hosts }}"
register: apiserver_sans_host_check
changed_when: apiserver_sans_host_check.stdout is not search('does match certificate')
failed_when: apiserver_sans_host_check.rc != 0 and apiserver_sans_host_check.stdout is not search('does NOT match certificate')
- name: Kubeadm | regenerate apiserver cert 1/2
file:
state: absent
path: "{{ kube_cert_dir }}/{{ item }}"
with_items:
- apiserver.crt
- apiserver.key
when:
- kubeadm_already_run.stat.exists
- apiserver_sans_ip_check.changed or apiserver_sans_host_check.changed
- not kube_external_ca_mode
- name: Kubeadm | regenerate apiserver cert 2/2
command: >-
{{ bin_dir }}/kubeadm
init phase certs apiserver
--config={{ kube_config_dir }}/kubeadm-config.yaml
when:
- kubeadm_already_run.stat.exists
- apiserver_sans_ip_check.changed or apiserver_sans_host_check.changed
- not kube_external_ca_mode
# TODO: Remove --skip-phases from command when v1beta4 UpgradeConfiguration supports skipPhases
- name: Kubeadm | Initialize first control plane node
when: inventory_hostname == first_kube_control_plane and not kubeadm_already_run.stat.exists
vars:
kubeadm_init_first_control_plane_cmd: >-
timeout -k {{ kubeadm_init_timeout }} {{ kubeadm_init_timeout }}
{{ bin_dir }}/kubeadm init
--config={{ kube_config_dir }}/kubeadm-config.yaml
--ignore-preflight-errors={{ _ignore_errors | flatten | join(',') }}
--skip-phases={{ kubeadm_init_phases_skip | join(',') }}
{{ kube_external_ca_mode | ternary('', '--upload-certs') }}
_ignore_errors: "{{ kubeadm_ignore_preflight_errors }}"
environment:
PATH: "{{ bin_dir }}:{{ ansible_env.PATH }}"
notify: Control plane | restart kubelet
block:
- name: Kubeadm | Initialize first control plane node (1st try)
command: "{{ kubeadm_init_first_control_plane_cmd }}"
register: kubeadm_init
failed_when: kubeadm_init.rc != 0 and "field is immutable" not in kubeadm_init.stderr
rescue:
# Retry is because upload config sometimes fails
# This retry task is separated from 1st task to show log of failure of 1st task.
- name: Kubeadm | Initialize first control plane node (retry)
command: "{{ kubeadm_init_first_control_plane_cmd }}"
vars:
_errors_from_first_try:
- 'FileAvailable--etc-kubernetes-manifests-kube-controller-manager.yaml'
- 'FileAvailable--etc-kubernetes-manifests-kube-scheduler.yaml'
- 'FileAvailable--etc-kubernetes-manifests-kube-apiserver.yaml'
- 'Port-10250'
_ignore_errors:
- "{{ kubeadm_ignore_preflight_errors }}"
- "{{ _errors_from_first_try if 'all' not in kubeadm_ignore_preflight_errors else [] }}"
register: kubeadm_init
retries: 2
until: kubeadm_init is succeeded or "field is immutable" in kubeadm_init.stderr
failed_when: kubeadm_init.rc != 0 and "field is immutable" not in kubeadm_init.stderr
- name: Set kubeadm certificate key
set_fact:
kubeadm_certificate_key: "{{ item | regex_search('--certificate-key ([^ ]+)', '\\1') | first }}"
with_items: "{{ hostvars[groups['kube_control_plane'][0]]['kubeadm_init'].stdout_lines | default([]) }}"
when:
- kubeadm_certificate_key is not defined
- (item | trim) is match('.*--certificate-key.*')
- name: Create hardcoded kubeadm token for joining nodes with 24h expiration (if defined)
shell: >-
{{ bin_dir }}/kubeadm --kubeconfig {{ kube_config_dir }}/admin.conf token delete {{ kubeadm_token }} || :;
{{ bin_dir }}/kubeadm --kubeconfig {{ kube_config_dir }}/admin.conf token create {{ kubeadm_token }} --config={{ kube_config_dir }}/kubeadm-config.yaml
changed_when: false
when:
- inventory_hostname == first_kube_control_plane
- kubeadm_token is defined
- kubeadm_refresh_token
tags:
- kubeadm_token
- name: Remove binding to anonymous user
command: "{{ kubectl }} -n kube-public delete rolebinding kubeadm:bootstrap-signer-clusterinfo --ignore-not-found"
when: inventory_hostname == first_kube_control_plane and remove_anonymous_access
- name: Create kubeadm token for joining nodes with 24h expiration (default)
command: "{{ bin_dir }}/kubeadm --kubeconfig {{ kube_config_dir }}/admin.conf token create --config={{ kube_config_dir }}/kubeadm-config.yaml"
changed_when: false
register: temp_token
retries: 5
delay: 5
until: temp_token is succeeded
delegate_to: "{{ first_kube_control_plane }}"
when: kubeadm_token is not defined
tags:
- kubeadm_token
- name: Set kubeadm_token
set_fact:
kubeadm_token: "{{ temp_token.stdout }}"
when: temp_token.stdout is defined
tags:
- kubeadm_token
- name: Kubeadm | Join other control plane nodes
include_tasks: kubeadm-secondary.yml
- name: Kubeadm | upgrade kubernetes cluster to {{ kube_version }}
include_tasks: kubeadm-upgrade.yml
when:
- upgrade_cluster_setup
- kubeadm_already_run.stat.exists
# FIXME(mattymo): from docs: If you don't want to taint your control-plane node, set this field to an empty slice, i.e. `taints: {}` in the YAML file.
- name: Kubeadm | Remove taint for control plane node with node role
command: "{{ kubectl }} taint node {{ inventory_hostname }} {{ item }}"
delegate_to: "{{ first_kube_control_plane }}"
with_items:
- "node-role.kubernetes.io/control-plane:NoSchedule-"
when: ('kube_node' in group_names)
failed_when: false