actualized for kubespray version 2.28.0

This commit is contained in:
2025-06-15 20:25:45 +03:00
parent c819d4f51f
commit c2df2abc78
366 changed files with 9939 additions and 40388 deletions

View File

@@ -34,8 +34,6 @@ etcd_script_dir: "{{ bin_dir }}/etcd-scripts"
etcd_heartbeat_interval: "250"
etcd_election_timeout: "5000"
# etcd_snapshot_count: "10000"
etcd_metrics: "basic"
# Define in inventory to set a separate port for etcd to expose metrics on

View File

@@ -2,30 +2,29 @@
- name: Backup etcd
import_tasks: backup.yml
- name: Etcd | reload systemd
- name: Restart etcd
systemd_service:
daemon_reload: true
listen:
- Restart etcd
- Restart etcd-events
- name: Reload etcd
service:
name: etcd
state: restarted
when: is_etcd_master
listen: Restart etcd
daemon_reload: true
when: ('etcd' in group_names)
throttle: "{{ groups['etcd'] | length // 2 }}"
# Etcd cluster MUST have an odd number of members
# Truncated integer division by 2 will always return (majority - 1) which
# means the cluster will keep quorum and stay available
- name: Reload etcd-events
service:
- name: Restart etcd-events
systemd_service:
name: etcd-events
state: restarted
when: is_etcd_master
listen: Restart etcd-events
daemon_reload: true
# TODO: this seems odd. etcd-events should be a different group possibly ?
when: ('etcd' in group_names)
throttle: "{{ groups['etcd'] | length // 2 }}"
- name: Wait for etcd up
uri:
url: "https://{% if is_etcd_master %}{{ etcd_address }}{% else %}127.0.0.1{% endif %}:2379/health"
url: "https://{% if 'etcd' in group_names %}{{ etcd_address | ansible.utils.ipwrap }}{% else %}127.0.0.1{% endif %}:2379/health"
validate_certs: false
client_cert: "{{ etcd_cert_dir }}/member-{{ inventory_hostname }}.pem"
client_key: "{{ etcd_cert_dir }}/member-{{ inventory_hostname }}-key.pem"
@@ -40,7 +39,7 @@
- name: Wait for etcd-events up
uri:
url: "https://{% if is_etcd_master %}{{ etcd_address }}{% else %}127.0.0.1{% endif %}:2383/health"
url: "https://{% if 'etcd' in group_names %}{{ etcd_address | ansible.utils.ipwrap }}{% else %}127.0.0.1{% endif %}:2383/health"
validate_certs: false
client_cert: "{{ etcd_cert_dir }}/member-{{ inventory_hostname }}.pem"
client_key: "{{ etcd_cert_dir }}/member-{{ inventory_hostname }}-key.pem"

View File

@@ -21,7 +21,7 @@
get_checksum: true
get_mime: false
register: etcd_member_certs
when: inventory_hostname in groups['etcd']
when: ('etcd' in group_names)
with_items:
- ca.pem
- member-{{ inventory_hostname }}.pem
@@ -33,7 +33,7 @@
stat:
path: "{{ etcd_cert_dir }}/{{ item }}"
register: etcd_node_certs
when: inventory_hostname in groups['k8s_cluster']
when: ('k8s_cluster' in group_names)
with_items:
- ca.pem
- node-{{ inventory_hostname }}.pem
@@ -84,7 +84,7 @@
{% if not loop.last %}{{ ',' }}{% endif %}
{% endfor %}]
when:
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally | default(false) | bool
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally
- kube_network_plugin != "calico" or calico_datastore == "etcd"
- force_etcd_cert_refresh or not item in etcdcert_master.files | map(attribute='path') | list
@@ -99,7 +99,7 @@
set_fact:
etcd_member_requires_sync: true
when:
- inventory_hostname in groups['etcd']
- ('etcd' in group_names)
- (not etcd_member_certs.results[0].stat.exists | default(false)) or
(not etcd_member_certs.results[1].stat.exists | default(false)) or
(not etcd_member_certs.results[2].stat.exists | default(false)) or
@@ -115,7 +115,7 @@
set_fact:
kubernetes_host_requires_sync: true
when:
- inventory_hostname in groups['k8s_cluster'] and
- ('k8s_cluster' in group_names) and
inventory_hostname not in groups['etcd']
- (not etcd_node_certs.results[0].stat.exists | default(false)) or
(not etcd_node_certs.results[1].stat.exists | default(false)) or

View File

@@ -9,7 +9,7 @@
check_mode: false
run_once: true
when:
- is_etcd_master
- ('etcd' in group_names)
- etcd_cluster_setup
tags:
- facts
@@ -30,7 +30,7 @@
check_mode: false
run_once: true
when:
- is_etcd_master
- ('etcd' in group_names)
- etcd_events_cluster_setup
tags:
- facts
@@ -43,7 +43,7 @@
- name: Configure | Refresh etcd config
include_tasks: refresh_config.yml
when: is_etcd_master
when: ('etcd' in group_names)
- name: Configure | Copy etcd.service systemd file
template:
@@ -54,7 +54,9 @@
# FIXME: check that systemd version >= 250 (factory-reset.target was introduced in that release)
# Remove once we drop support for systemd < 250
validate: "sh -c '[ -f /usr/bin/systemd/system/factory-reset.target ] || exit 0 && systemd-analyze verify %s:etcd-{{ etcd_deployment_type }}.service'"
when: is_etcd_master and etcd_cluster_setup
when:
- ('etcd' in group_names)
- etcd_cluster_setup
- name: Configure | Copy etcd-events.service systemd file
template:
@@ -65,12 +67,14 @@
validate: "sh -c '[ -f /usr/bin/systemd/system/factory-reset.target ] || exit 0 && systemd-analyze verify %s:etcd-events-{{ etcd_deployment_type }}.service'"
# FIXME: check that systemd version >= 250 (factory-reset.target was introduced in that release)
# Remove once we drop support for systemd < 250
when: is_etcd_master and etcd_events_cluster_setup
when:
- ('etcd' in group_names)
- etcd_events_cluster_setup
- name: Configure | reload systemd
systemd_service:
daemon_reload: true
when: is_etcd_master
when: ('etcd' in group_names)
# when scaling new etcd will fail to start
- name: Configure | Ensure etcd is running
@@ -79,7 +83,9 @@
state: started
enabled: true
ignore_errors: "{{ etcd_cluster_is_healthy.rc == 0 }}" # noqa ignore-errors
when: is_etcd_master and etcd_cluster_setup
when:
- ('etcd' in group_names)
- etcd_cluster_setup
# when scaling new etcd will fail to start
- name: Configure | Ensure etcd-events is running
@@ -88,7 +94,9 @@
state: started
enabled: true
ignore_errors: "{{ etcd_events_cluster_is_healthy.rc != 0 }}" # noqa ignore-errors
when: is_etcd_master and etcd_events_cluster_setup
when:
- ('etcd' in group_names)
- etcd_events_cluster_setup
- name: Configure | Wait for etcd cluster to be healthy
shell: "set -o pipefail && {{ bin_dir }}/etcdctl endpoint --cluster status && {{ bin_dir }}/etcdctl endpoint --cluster health 2>&1 | grep -v 'Error: unhealthy cluster' >/dev/null"
@@ -102,7 +110,7 @@
check_mode: false
run_once: true
when:
- is_etcd_master
- ('etcd' in group_names)
- etcd_cluster_setup
tags:
- facts
@@ -125,7 +133,7 @@
check_mode: false
run_once: true
when:
- is_etcd_master
- ('etcd' in group_names)
- etcd_events_cluster_setup
tags:
- facts
@@ -137,12 +145,14 @@
ETCDCTL_ENDPOINTS: "{{ etcd_events_access_addresses }}"
- name: Configure | Check if member is in etcd cluster
shell: "{{ bin_dir }}/etcdctl member list | grep -w -q {{ etcd_access_address }}"
shell: "{{ bin_dir }}/etcdctl member list | grep -w -q {{ etcd_access_address | replace('[', '') | replace(']', '') }}"
register: etcd_member_in_cluster
ignore_errors: true # noqa ignore-errors
changed_when: false
check_mode: false
when: is_etcd_master and etcd_cluster_setup
when:
- ('etcd' in group_names)
- etcd_cluster_setup
tags:
- facts
environment:
@@ -153,12 +163,14 @@
ETCDCTL_ENDPOINTS: "{{ etcd_access_addresses }}"
- name: Configure | Check if member is in etcd-events cluster
shell: "{{ bin_dir }}/etcdctl member list | grep -w -q {{ etcd_access_address }}"
shell: "{{ bin_dir }}/etcdctl member list | grep -w -q {{ etcd_access_address | replace('[', '') | replace(']', '') }}"
register: etcd_events_member_in_cluster
ignore_errors: true # noqa ignore-errors
changed_when: false
check_mode: false
when: is_etcd_master and etcd_events_cluster_setup
when:
- ('etcd' in group_names)
- etcd_events_cluster_setup
tags:
- facts
environment:

View File

@@ -35,7 +35,6 @@
mode: "0700"
run_once: true
when:
- gen_certs | default(false)
- inventory_hostname == groups['etcd'][0]
- name: Gen_certs | run cert generation script for etcd and kube control plane nodes
@@ -55,7 +54,7 @@
run_once: true
delegate_to: "{{ groups['etcd'][0] }}"
when:
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally | default(false) | bool
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally
- kube_network_plugin != "calico" or calico_datastore == "etcd"
- gen_certs | default(false)
notify: Set etcd_secret_changed
@@ -79,7 +78,7 @@
{% endfor %}]"
delegate_to: "{{ groups['etcd'][0] }}"
when:
- inventory_hostname in groups['etcd']
- ('etcd' in group_names)
- sync_certs | default(false)
- inventory_hostname != groups['etcd'][0]
notify: Set etcd_secret_changed
@@ -93,7 +92,7 @@
mode: "0640"
with_items: "{{ etcd_master_certs.results }}"
when:
- inventory_hostname in groups['etcd']
- ('etcd' in group_names)
- sync_certs | default(false)
- inventory_hostname != groups['etcd'][0]
loop_control:
@@ -110,9 +109,9 @@
{% endfor %}]"
delegate_to: "{{ groups['etcd'][0] }}"
when:
- inventory_hostname in groups['etcd']
- ('etcd' in group_names)
- inventory_hostname != groups['etcd'][0]
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally | default(false) | bool
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally
- kube_network_plugin != "calico" or calico_datastore == "etcd"
notify: Set etcd_secret_changed
@@ -125,9 +124,9 @@
mode: "0640"
with_items: "{{ etcd_master_node_certs.results }}"
when:
- inventory_hostname in groups['etcd']
- ('etcd' in group_names)
- inventory_hostname != groups['etcd'][0]
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally | default(false) | bool
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally
- kube_network_plugin != "calico" or calico_datastore == "etcd"
loop_control:
label: "{{ item.item }}"
@@ -135,15 +134,15 @@
- name: Gen_certs | Generate etcd certs
include_tasks: gen_nodes_certs_script.yml
when:
- inventory_hostname in groups['kube_control_plane'] and
- ('kube_control_plane' in group_names) and
sync_certs | default(false) and inventory_hostname not in groups['etcd']
- name: Gen_certs | Generate etcd certs on nodes if needed
include_tasks: gen_nodes_certs_script.yml
when:
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally | default(false) | bool
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally
- kube_network_plugin != "calico" or calico_datastore == "etcd"
- inventory_hostname in groups['k8s_cluster'] and
- ('k8s_cluster' in group_names) and
sync_certs | default(false) and inventory_hostname not in groups['etcd']
- name: Gen_certs | check certificate permissions
@@ -154,3 +153,25 @@
owner: "{{ etcd_owner }}"
mode: "{{ etcd_cert_dir_mode }}"
recurse: true
# This is a hack around the fact kubeadm expect the same certs path on all kube_control_plane
# TODO: fix certs generation to have the same file everywhere
# OR work with kubeadm on node-specific config
- name: Gen_certs | Pretend all control plane have all certs (with symlinks)
file:
state: link
src: "{{ etcd_cert_dir }}/node-{{ inventory_hostname }}{{ item[0] }}.pem"
dest: "{{ etcd_cert_dir }}/node-{{ item[1] }}{{ item[0] }}.pem"
mode: "0640"
loop: "{{ suffixes | product(groups['kube_control_plane']) }}"
vars:
suffixes:
- ''
- '-key'
when:
- ('kube_control_plane' in group_names)
- item[1] != inventory_hostname
register: symlink_created
failed_when:
- symlink_created is failed
- ('refusing to convert from file to symlink' not in symlink_created.msg)

View File

@@ -3,6 +3,7 @@
command: "{{ bin_dir }}/etcd --version"
register: etcd_current_host_version
# There's a chance this play could run before etcd is installed at all
# TODO: figure out whether this happens. "A chance" is not enough information
ignore_errors: true
when: etcd_cluster_setup
@@ -11,18 +12,18 @@
notify: Restart etcd
when:
- etcd_cluster_setup
- etcd_version.lstrip('v') not in etcd_current_host_version.stdout | default('')
- etcd_version not in etcd_current_host_version.stdout | default('')
- name: Restart etcd-events if necessary
command: /bin/true
notify: Restart etcd-events
when:
- etcd_events_cluster_setup
- etcd_version.lstrip('v') not in etcd_current_host_version.stdout | default('')
- etcd_version not in etcd_current_host_version.stdout | default('')
- name: Install | Copy etcd binary from download dir
copy:
src: "{{ local_release_dir }}/etcd-{{ etcd_version }}-linux-{{ host_architecture }}/{{ item }}"
src: "{{ local_release_dir }}/etcd-v{{ etcd_version }}-linux-{{ host_architecture }}/{{ item }}"
dest: "{{ bin_dir }}/{{ item }}"
mode: "0755"
remote_src: true

View File

@@ -19,7 +19,7 @@
etcd_events_peer_addresses: >-
{% for host in groups['etcd'] -%}
{%- if hostvars[host]['etcd_events_member_in_cluster'].rc == 0 -%}
{{ "etcd" + loop.index | string }}=https://{{ hostvars[host].etcd_events_access_address | default(hostvars[host].ip | default(fallback_ips[host])) }}:2382,
{{ "etcd" + loop.index | string }}="https://{{ hostvars[host].etcd_events_access_address | default(hostvars[host]['main_ip']) | ansible.utils.ipwrap }}:2382",
{%- endif -%}
{%- if loop.last -%}
{{ etcd_member_name }}={{ etcd_events_peer_url }}

View File

@@ -20,7 +20,7 @@
etcd_peer_addresses: >-
{% for host in groups['etcd'] -%}
{%- if hostvars[host]['etcd_member_in_cluster'].rc == 0 -%}
{{ "etcd" + loop.index | string }}=https://{{ hostvars[host].etcd_access_address | default(hostvars[host].ip | default(fallback_ips[host])) }}:2380,
{{ "etcd" + loop.index | string }}="https://{{ hostvars[host].etcd_access_address | default(hostvars[host]['main_ip']) | ansible.utils.ipwrap }}:2380",
{%- endif -%}
{%- if loop.last -%}
{{ etcd_member_name }}={{ etcd_peer_url }}

View File

@@ -9,23 +9,23 @@
- name: Generate etcd certs
include_tasks: "gen_certs_script.yml"
when:
- cert_management | d('script') == "script"
- cert_management == "script"
tags:
- etcd-secrets
- name: Trust etcd CA
include_tasks: upd_ca_trust.yml
when:
- inventory_hostname in groups['etcd'] | union(groups['kube_control_plane']) | unique | sort
- ('etcd' in group_names) or ('kube_control_plane' in group_names)
tags:
- etcd-secrets
- name: Trust etcd CA on nodes if needed
include_tasks: upd_ca_trust.yml
when:
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally | default(false) | bool
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally
- kube_network_plugin != "calico" or calico_datastore == "etcd"
- inventory_hostname in groups['k8s_cluster']
- ('k8s_cluster' in group_names)
tags:
- etcd-secrets
@@ -35,22 +35,24 @@
changed_when: false
check_mode: false
when:
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally | default(false) | bool
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally
- kube_network_plugin != "calico" or calico_datastore == "etcd"
- inventory_hostname in groups['k8s_cluster']
- ('k8s_cluster' in group_names)
tags:
- master
- master # master tag is deprecated and replaced by control-plane
- control-plane
- network
- name: Set etcd_client_cert_serial
set_fact:
etcd_client_cert_serial: "{{ etcd_client_cert_serial_result.stdout.split('=')[1] }}"
when:
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally | default(false) | bool
- kube_network_plugin in ["calico", "flannel", "cilium"] or cilium_deploy_additionally
- kube_network_plugin != "calico" or calico_datastore == "etcd"
- inventory_hostname in groups['k8s_cluster']
- ('k8s_cluster' in group_names)
tags:
- master
- master # master tag is deprecated and replaced by control-plane
- control-plane
- network
- name: Install etcdctl and etcdutl binary
@@ -61,36 +63,42 @@
- etcdutl
- upgrade
when:
- inventory_hostname in groups['etcd']
- ('etcd' in group_names)
- etcd_cluster_setup
- name: Install etcd
include_tasks: "install_{{ etcd_deployment_type }}.yml"
when: is_etcd_master
when: ('etcd' in group_names)
tags:
- upgrade
- name: Configure etcd
include_tasks: configure.yml
when: is_etcd_master
when: ('etcd' in group_names)
- name: Refresh etcd config
include_tasks: refresh_config.yml
when: is_etcd_master
when: ('etcd' in group_names)
- name: Restart etcd if certs changed
command: /bin/true
notify: Restart etcd
when: is_etcd_master and etcd_cluster_setup and etcd_secret_changed | default(false)
when:
- ('etcd' in group_names)
- etcd_cluster_setup
- etcd_secret_changed | default(false)
- name: Restart etcd-events if certs changed
command: /bin/true
notify: Restart etcd
when: is_etcd_master and etcd_events_cluster_setup and etcd_secret_changed | default(false)
when:
- ('etcd' in group_names)
- etcd_events_cluster_setup
- etcd_secret_changed | default(false)
# After etcd cluster is assembled, make sure that
# initial state of the cluster is in `existing`
# state instead of `new`.
- name: Refresh etcd config again for idempotency
include_tasks: refresh_config.yml
when: is_etcd_master
when: ('etcd' in group_names)

View File

@@ -5,7 +5,9 @@
dest: /etc/etcd.env
mode: "0640"
notify: Restart etcd
when: is_etcd_master and etcd_cluster_setup
when:
- ('etcd' in group_names)
- etcd_cluster_setup
- name: Refresh config | Create etcd-events config file
template:
@@ -13,4 +15,6 @@
dest: /etc/etcd-events.env
mode: "0640"
notify: Restart etcd-events
when: is_etcd_master and etcd_events_cluster_setup
when:
- ('etcd' in group_names)
- etcd_events_cluster_setup

View File

@@ -4,11 +4,11 @@ ETCD_INITIAL_ADVERTISE_PEER_URLS={{ etcd_events_peer_url }}
ETCD_INITIAL_CLUSTER_STATE={% if etcd_events_cluster_is_healthy.rc == 0 | bool %}existing{% else %}new{% endif %}
ETCD_METRICS={{ etcd_metrics }}
ETCD_LISTEN_CLIENT_URLS=https://{{ etcd_address }}:2383,https://127.0.0.1:2383
ETCD_LISTEN_CLIENT_URLS=https://{{ etcd_address | ansible.utils.ipwrap }}:2383,https://127.0.0.1:2383
ETCD_ELECTION_TIMEOUT={{ etcd_election_timeout }}
ETCD_HEARTBEAT_INTERVAL={{ etcd_heartbeat_interval }}
ETCD_INITIAL_CLUSTER_TOKEN=k8s_events_etcd
ETCD_LISTEN_PEER_URLS=https://{{ etcd_address }}:2382
ETCD_LISTEN_PEER_URLS=https://{{ etcd_address | ansible.utils.ipwrap }}:2382
ETCD_NAME={{ etcd_member_name }}-events
ETCD_PROXY=off
ETCD_INITIAL_CLUSTER={{ etcd_events_peer_addresses }}

View File

@@ -8,13 +8,13 @@ ETCD_METRICS={{ etcd_metrics }}
{% if etcd_listen_metrics_urls is defined %}
ETCD_LISTEN_METRICS_URLS={{ etcd_listen_metrics_urls }}
{% elif etcd_metrics_port is defined %}
ETCD_LISTEN_METRICS_URLS=http://{{ etcd_address }}:{{ etcd_metrics_port }},http://127.0.0.1:{{ etcd_metrics_port }}
ETCD_LISTEN_METRICS_URLS=http://{{ etcd_address | ansible.utils.ipwrap }}:{{ etcd_metrics_port }},http://127.0.0.1:{{ etcd_metrics_port }}
{% endif %}
ETCD_LISTEN_CLIENT_URLS=https://{{ etcd_address }}:2379,https://127.0.0.1:2379
ETCD_LISTEN_CLIENT_URLS=https://{{ etcd_address | ansible.utils.ipwrap }}:2379,https://127.0.0.1:2379
ETCD_ELECTION_TIMEOUT={{ etcd_election_timeout }}
ETCD_HEARTBEAT_INTERVAL={{ etcd_heartbeat_interval }}
ETCD_INITIAL_CLUSTER_TOKEN=k8s_etcd
ETCD_LISTEN_PEER_URLS=https://{{ etcd_address }}:2380
ETCD_LISTEN_PEER_URLS=https://{{ etcd_address | ansible.utils.ipwrap }}:2380
ETCD_NAME={{ etcd_member_name }}
ETCD_PROXY=off
ETCD_INITIAL_CLUSTER={{ etcd_peer_addresses }}

View File

@@ -42,9 +42,16 @@ DNS.{{ counter["dns"] }} = {{ etcd_alt_name }}{{ increment(counter, 'dns') }}
{% if hostvars[host]['access_ip'] is defined %}
IP.{{ counter["ip"] }} = {{ hostvars[host]['access_ip'] }}{{ increment(counter, 'ip') }}
{% endif %}
IP.{{ counter["ip"] }} = {{ hostvars[host]['ip'] | default(fallback_ips[host]) }}{{ increment(counter, 'ip') }}
{% if hostvars[host]['access_ip6'] is defined %}
IP.{{ counter["ip"] }} = {{ hostvars[host]['access_ip6'] }}{{ increment(counter, 'ip') }}
{% endif %}
{% if ipv6_stack %}
IP.{{ counter["ip"] }} = {{ hostvars[host]['ip6'] | default(hostvars[host]['fallback_ip6']) }}{{ increment(counter, 'ip') }}
{% endif %}
IP.{{ counter["ip"] }} = {{ hostvars[host]['main_ip'] }}{{ increment(counter, 'ip') }}
{% endfor %}
{% for cert_alt_ip in etcd_cert_alt_ips %}
IP.{{ counter["ip"] }} = {{ cert_alt_ip }}{{ increment(counter, 'ip') }}
{% endfor %}
IP.{{ counter["ip"] }} = 127.0.0.1
IP.{{ counter["ip"] }} = 127.0.0.1{{ increment(counter, 'ip') }}
IP.{{ counter["ip"] }} = ::1

View File

@@ -6,5 +6,5 @@ cert_files:
- "{{ etcd_cert_dir }}/admin-{{ inventory_hostname }}.pem"
- "{{ etcd_cert_dir }}/admin-{{ inventory_hostname }}-key.pem"
node:
- "{{ etcd_cert_dir}}/node-{{ inventory_hostname }}.pem"
- "{{ etcd_cert_dir}}/node-{{ inventory_hostname }}-key.pem"
- "{{ etcd_cert_dir }}/node-{{ inventory_hostname }}.pem"
- "{{ etcd_cert_dir }}/node-{{ inventory_hostname }}-key.pem"