├── .yamllint.yaml ├── .config └── ansible-lint.yml ├── test-inventory.ini ├── meta ├── requirements.yml └── main.yml ├── .gitignore ├── .github └── workflows │ └── build.yml ├── tasks ├── systemd.yml ├── pki.yml └── main.yml ├── platform-matrix-v1.json ├── test.yml ├── templates ├── etcd.service.j2 └── etcd.conf.j2 ├── handlers └── main.yml ├── LICENSE ├── defaults └── main.yml ├── README.md ├── vars └── main.yml └── test-pki ├── ca.pem ├── localhost.pem └── localhost-key.pem /.yamllint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | rules: 3 | line-length: disable 4 | -------------------------------------------------------------------------------- /.config/ansible-lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | skip_list: 3 | - 'name[template]' 4 | -------------------------------------------------------------------------------- /test-inventory.ini: -------------------------------------------------------------------------------- 1 | [etcd_master] 2 | localhost 3 | 4 | [etcd] 5 | localhost 6 | -------------------------------------------------------------------------------- /meta/requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: andrewrothstein.etcd 3 | version: v2.3.11 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **~*.retry 2 | Dockerfile.* 3 | requirements.yml 4 | !meta/requirements.yml 5 | **/*undo-tree* -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | --- 2 | jobs: 3 | bake-ansible-images-v1: 4 | uses: andrewrothstein/.github/.github/workflows/bake-ansible-images-v1.yml@develop 5 | 'on': push 6 | -------------------------------------------------------------------------------- /tasks/systemd.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install etcd.service systemd unit 3 | become: true 4 | become_user: root 5 | with_items: 6 | - f: etcd.service 7 | d: /etc/systemd/system 8 | ansible.builtin.template: 9 | src: '{{ item.f }}.j2' 10 | dest: '{{ item.d }}/{{ item.f }}' 11 | mode: '{{ item.m | default("0644") }}' 12 | notify: Systemctl daemon-reload 13 | 14 | - name: Launching etcd... 15 | when: etcd_launch | bool 16 | become: true 17 | become_user: root 18 | ansible.builtin.service: 19 | name: etcd 20 | enabled: true 21 | state: started 22 | -------------------------------------------------------------------------------- /tasks/pki.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install keys/certs 3 | become: true 4 | become_user: root 5 | with_items: 6 | - f: '{{ etcd_pki_key_src }}' 7 | d: '{{ etcd_pki_key_dest }}' 8 | m: '400' 9 | - f: '{{ etcd_pki_cert_src }}' 10 | d: '{{ etcd_pki_cert_dest }}' 11 | m: '600' 12 | - f: '{{ etcd_pki_ca_cert_src }}' 13 | d: '{{ etcd_pki_ca_cert_dest }}' 14 | m: '600' 15 | ansible.builtin.copy: 16 | src: '{{ item.f }}' 17 | dest: '{{ item.d }}' 18 | owner: '{{ etcd_user }}' 19 | group: '{{ etcd_group }}' 20 | mode: '{{ item.m }}' 21 | -------------------------------------------------------------------------------- /platform-matrix-v1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "OS": "archlinux", 4 | "OS_VER": "latest" 5 | }, 6 | { 7 | "OS": "debian", 8 | "OS_VER": "bookworm" 9 | }, 10 | { 11 | "OS": "debian", 12 | "OS_VER": "bullseye" 13 | }, 14 | { 15 | "OS": "fedora", 16 | "OS_VER": "39" 17 | }, 18 | { 19 | "OS": "fedora", 20 | "OS_VER": "40" 21 | }, 22 | { 23 | "OS": "rockylinux", 24 | "OS_VER": "8" 25 | }, 26 | { 27 | "OS": "rockylinux", 28 | "OS_VER": "9" 29 | }, 30 | { 31 | "OS": "ubuntu", 32 | "OS_VER": "jammy" 33 | }, 34 | { 35 | "OS": "ubuntu", 36 | "OS_VER": "noble" 37 | } 38 | ] -------------------------------------------------------------------------------- /test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Testing andrewrothstein.etcd_cluster 3 | hosts: all 4 | vars: 5 | etcd_use_ips: false 6 | etcd_pki_dir: '{{ playbook_dir }}/test-pki' 7 | etcd_launch: false 8 | etcd_cluster_snapshot_counter: 100 9 | etcd_cluster_heartbeat_interval: '5s' 10 | etcd_cluster_election_timeout: '5m' 11 | etcd_cluster_max_snapshots: 100 12 | etcd_cluster_max_wals: 5 13 | etcd_cluster_cors: foo-bar-no-idea-format 14 | etcd_cluster_debug: true 15 | etcd_cluster_log_package_levels: 'etcdserver=WARNING,security=DEBUG' 16 | etcd_additional_envvars: 17 | foo: bar 18 | baz: bing 19 | roles: 20 | - role: '{{ playbook_dir }}' 21 | -------------------------------------------------------------------------------- /templates/etcd.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Etcd Server 3 | After=network.target 4 | 5 | [Service] 6 | {% if etcd_service_start_timeout is defined %} 7 | TimeoutStartSec={{ etcd_service_start_timeout }} 8 | {% endif %} 9 | Type={{ etcd_systemd_service_type | default("notify") }} 10 | User={{ etcd_user }} 11 | WorkingDirectory={{ etcd_data_dir }}/ 12 | EnvironmentFile=-/etc/etcd/etcd.conf 13 | # set GOMAXPROCS to number of processors 14 | ExecStart=/bin/bash -c "GOMAXPROCS=$(nproc) {{ etcd_install_dir }}/etcd" 15 | Restart={{ etcd_service_restart | default("on-failure") }} 16 | LimitNOFILE={{ etcd_service_limitnofile | default(65536) }} 17 | 18 | [Install] 19 | WantedBy=multi-user.target 20 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Andrew Rothstein 4 | company: BlackRock 5 | description: configures an etcd cluster 6 | galaxy_tags: 7 | - etcd 8 | - kv 9 | - transactions 10 | license: MIT 11 | min_ansible_version: '2.0' 12 | namespace: andrewrothstein 13 | platforms: 14 | - name: ArchLinux 15 | versions: 16 | - all 17 | - name: Debian 18 | versions: 19 | - bookworm 20 | - bullseye 21 | - name: EL 22 | versions: 23 | - '8' 24 | - '9' 25 | - name: Fedora 26 | versions: 27 | - '39' 28 | - '40' 29 | - name: Ubuntu 30 | versions: 31 | - jammy 32 | - noble 33 | role_name: etcd_cluster 34 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for etcd_cluster 3 | 4 | # Restart masters one-by-one to keep quorum 5 | - name: Restart etcd masters 6 | become: true 7 | become_user: root 8 | when: 9 | - etcd_launch | bool 10 | ansible.builtin.service: 11 | name: etcd 12 | state: restarted 13 | run_once: true 14 | loop: '{{ groups[etcd_master_group_name] }}' 15 | delegate_to: '{{ item }}' 16 | 17 | # Restart non-voting members 18 | - name: Restart etcd members 19 | become: true 20 | become_user: root 21 | when: 22 | - etcd_launch | bool 23 | - not inventory_hostname in groups[etcd_master_group_name] 24 | ansible.builtin.service: 25 | name: etcd 26 | state: restarted 27 | 28 | - name: Systemctl daemon-reload 29 | become: true 30 | become_user: root 31 | when: 32 | - etcd_launch | bool 33 | ansible.builtin.systemd: 34 | daemon_reload: true 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Andrew Rothstein 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | etcd_user: etcd 3 | etcd_group: etcd 4 | etcd_install_dir: /usr/local/bin 5 | etcd_data_dir: /var/lib/etcd 6 | 7 | etcd_master_group_name: etcd_master 8 | 9 | etcd_secure: true 10 | etcd_client_cert_auth: true 11 | etcd_peer_client_cert_auth: true 12 | etcd_pki_dir: ~/pki-dir 13 | etcd_pki_key_suffix: -key.pem 14 | etcd_pki_cert_suffix: .pem 15 | 16 | etcd_use_ips: true 17 | etcd_iface_public: '{{ etcd_network_iface | default("all") }}' 18 | etcd_iface_cluster: '{{ etcd_network_iface | default("default") }}' 19 | etcd_port_client: 2379 20 | etcd_port_peer: 2380 21 | 22 | etcd_cluster_name: test-cluster-name 23 | etcd_initial_cluster_token: d8bf8cc6-5158-11e6-8f13-3b32f4935bde 24 | 25 | etcd_init_system: systemd 26 | etcd_launch: true 27 | 28 | # Accept etcd V2 client requests 29 | etcd_enable_v2: true 30 | 31 | # Additional environment vars for the service 32 | # etcd_additional_envvars: 33 | # foo: bar 34 | # baz: ding 35 | 36 | # systemd Service.TimeoutStartSec 37 | # etcd_service_start_timeout: '5min' 38 | 39 | # systemd Service.Restart 40 | # etcd_service_restart: on-failure 41 | 42 | # systemd Service.LimitNOFILE 43 | # etcd_service_limitnofile: 65536 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | andrewrothstein.etcd_cluster 2 | =========================== 3 | ![Build Status](https://github.com/andrewrothstein/ansible-etcd_cluster/actions/workflows/build.yml/badge.svg) 4 | 5 | Configures an etcd cluster. Runs a voting member on every host in the ```etcd-master``` group 6 | and a proxy on the rest of the hosts in the ```etcd``` group. 7 | 8 | Requirements 9 | ------------ 10 | 11 | See [meta/main.yml](meta/main.yml) 12 | 13 | Role Variables 14 | -------------- 15 | 16 | See [defaults/main.yml](defaults/main.yml) 17 | 18 | Dependencies 19 | ------------ 20 | 21 | See [meta/main.yml](meta/main.yml) 22 | 23 | Example Playbook 24 | ---------------- 25 | 26 | example inventory.ini 27 | ```ini 28 | # voting and non-voting members 29 | [etcd] 30 | host[1:n].test 31 | 32 | # voting members 33 | [etcd_master] 34 | host[1:3].test 35 | ``` 36 | 37 | in your playbook: 38 | ```yml 39 | - hosts: etcd 40 | roles: 41 | - role: andrewrothstein.etcd_cluster 42 | # if you don't want to secure your install then use 43 | # etcd_secure: False 44 | ``` 45 | 46 | Get details of etcd cluster 47 | ``` 48 | etcdctl endpoint status --cluster -w table 49 | ``` 50 | 51 | License 52 | ------- 53 | 54 | MIT 55 | 56 | Author Information 57 | ------------------ 58 | 59 | Andrew Rothstein 60 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Installing etcd... 3 | ansible.builtin.include_role: 4 | name: andrewrothstein.etcd 5 | 6 | - name: Collect facts 7 | ansible.builtin.set_fact: 8 | cacheable: true 9 | etcd_listen_public: '{{ etcd_listen }}' 10 | etcd_listen_cluster: '{{ etcd_listen }}' 11 | etcd_address_public: '{{ etcd_address }}' 12 | etcd_address_cluster: '{{ etcd_address }}' 13 | 14 | - name: Create etcd group 15 | become: true 16 | become_user: root 17 | ansible.builtin.group: 18 | name: '{{ etcd_group }}' 19 | state: present 20 | 21 | - name: Create etcd user 22 | become: true 23 | become_user: root 24 | ansible.builtin.user: 25 | name: '{{ etcd_user }}' 26 | group: '{{ etcd_group }}' 27 | home: '{{ etcd_data_dir }}' 28 | state: present 29 | 30 | - name: Create dirs... 31 | become: true 32 | become_user: root 33 | ansible.builtin.file: 34 | state: directory 35 | path: '{{ item }}' 36 | owner: '{{ etcd_user }}' 37 | group: '{{ etcd_group }}' 38 | mode: '755' 39 | with_items: 40 | - /etc/etcd 41 | - '{{ etcd_cluster_pki_dir }}' 42 | 43 | - name: Install PKI... 44 | ansible.builtin.include_tasks: pki.yml 45 | when: etcd_secure | bool 46 | 47 | - name: Templating /etc/etcd/etcd.conf 48 | become: true 49 | become_user: root 50 | with_items: 51 | - f: etcd.conf 52 | d: /etc/etcd 53 | ansible.builtin.template: 54 | src: '{{ item.f }}.j2' 55 | dest: '{{ item.d }}/{{ item.f }}' 56 | owner: '{{ etcd_user }}' 57 | group: '{{ etcd_group }}' 58 | mode: '644' 59 | notify: 60 | - Restart etcd masters 61 | - Restart etcd members 62 | 63 | - name: Configure init system 64 | ansible.builtin.include_tasks: '{{ etcd_init_system }}.yml' 65 | -------------------------------------------------------------------------------- /vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | etcd_scheme: "{% if etcd_secure %}https{% else %}http{% endif %}://" 4 | etcd_cluster: "{% for host in groups[etcd_master_group_name] %}{{ hostvars[host]['ansible_fqdn' if not etcd_use_ips | bool else 'inventory_hostname'] }}={{ etcd_scheme }}{{ hostvars[host]['etcd_address_cluster'] }}:{{ etcd_port_peer }}{% if not loop.last %},{% endif %}{% endfor %}" 5 | 6 | etcd_cluster_data_dir: '{{ etcd_data_dir }}/{{ etcd_cluster_name }}.etcd' 7 | etcd_cluster_pki_dir: '{{ etcd_data_dir }}/{{ etcd_cluster_name }}.pki' 8 | 9 | etcd_pki_key_file: '{{ inventory_hostname }}{{ etcd_pki_key_suffix }}' 10 | etcd_pki_key_src: '{{ etcd_pki_dir }}/{{ etcd_pki_key_file }}' 11 | etcd_pki_key_dest: '{{ etcd_cluster_pki_dir }}/{{ etcd_pki_key_file }}' 12 | 13 | etcd_pki_cert_file: '{{ inventory_hostname }}{{ etcd_pki_cert_suffix }}' 14 | etcd_pki_cert_src: '{{ etcd_pki_dir }}/{{ etcd_pki_cert_file }}' 15 | etcd_pki_cert_dest: '{{ etcd_cluster_pki_dir }}/{{ etcd_pki_cert_file }}' 16 | 17 | etcd_pki_ca_file: 'ca{{ etcd_pki_cert_suffix }}' 18 | etcd_pki_ca_cert_src: '{{ etcd_pki_dir }}/{{ etcd_pki_ca_file }}' 19 | etcd_pki_ca_cert_dest: '{{ etcd_cluster_pki_dir }}/{{ etcd_pki_ca_file }}' 20 | 21 | etcd_listen: |- 22 | {%- if etcd_iface_public == "all" -%} 23 | 0.0.0.0 24 | {%- elif etcd_iface_public == "default" -%} 25 | {{- ansible_default_ipv4.address | default("127.0.0.1") -}} 26 | {%- else -%} 27 | {{- hostvars[inventory_hostname]["ansible_" ~ etcd_iface_public]["ipv4"]["address"] -}} 28 | {%- endif -%} 29 | 30 | etcd_address: |- 31 | {%- if not (etcd_use_ips | bool) -%} 32 | {{- ansible_fqdn -}} 33 | {%- elif etcd_iface_public in [ "all", "default" ] -%} 34 | {{- ansible_default_ipv4.address | default("127.0.0.1") -}} 35 | {%- else -%} 36 | {{- hostvars[inventory_hostname]["ansible_" ~ etcd_iface_public]["ipv4"]["address"] -}} 37 | {%- endif -%} 38 | -------------------------------------------------------------------------------- /test-pki/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 33 | -----END CERTIFICATE----- 34 | -------------------------------------------------------------------------------- /test-pki/localhost.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 34 | Q7hZ 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /templates/etcd.conf.j2: -------------------------------------------------------------------------------- 1 | # [member] 2 | ETCD_NAME="{{ ansible_fqdn if not etcd_use_ips | bool else inventory_hostname }}" 3 | ETCD_DATA_DIR="{{ etcd_cluster_data_dir }}" 4 | {% if etcd_cluster_snapshot_counter is defined %} 5 | ETCD_SNAPSHOT_COUNTER="{{ etcd_cluster_snapshot_counter }}" 6 | {% endif %} 7 | {% if etcd_cluster_heartbeat_interval is defined %} 8 | ETCD_HEARTBEAT_INTERVAL="{{ etcd_cluster_heartbeat_interval }}" 9 | {% endif %} 10 | {% if etcd_cluster_election_timeout is defined %} 11 | ETCD_ELECTION_TIMEOUT="{{ etcd_cluster_election_timeout }}" 12 | {% endif %} 13 | {% if etcd_iface_public == 'all' %} 14 | {% set client_endpoints = [ etcd_listen_public ] %} 15 | {% else %} 16 | {% set client_endpoints = [ etcd_listen_public, '127.0.0.1' ] %} 17 | {% endif %} 18 | ETCD_LISTEN_CLIENT_URLS={{ client_endpoints | map('regex_replace', '^(.+)$', etcd_scheme ~ '\\1' ~ ':' ~ etcd_port_client) | join(',') | quote }} 19 | ETCD_ENABLE_V2="{{ etcd_enable_v2 | string | lower }}" 20 | {% if etcd_cluster_max_snapshots is defined %} 21 | ETCD_MAX_SNAPSHOTS="{{ etcd_cluster_max_snapshots }}" 22 | {% endif %} 23 | {% if etcd_cluster_max_wals is defined %} 24 | ETCD_MAX_WALS="{{ etcd_cluster_max_wals }}" 25 | {% endif %} 26 | {% if etcd_cluster_cors is defined %} 27 | ETCD_CORS="{{ etcd_cluster_cors }}" 28 | {% endif %} 29 | # 30 | # 31 | # [cluster] 32 | {% if inventory_hostname in groups[etcd_master_group_name] %} 33 | ETCD_LISTEN_PEER_URLS="{{ etcd_scheme }}{{ etcd_listen_cluster }}:{{ etcd_port_peer }}" 34 | ETCD_ADVERTISE_CLIENT_URLS="{{ etcd_scheme }}{{ etcd_address_public }}:{{ etcd_port_client }}" 35 | ETCD_INITIAL_ADVERTISE_PEER_URLS="{{ etcd_scheme }}{{ etcd_address_cluster }}:{{ etcd_port_peer }}" 36 | {% endif %} 37 | # 38 | ETCD_INITIAL_CLUSTER="{{ etcd_cluster }}" 39 | ETCD_INITIAL_CLUSTER_STATE="new" 40 | ETCD_INITIAL_CLUSTER_TOKEN="{{ etcd_initial_cluster_token }}" 41 | # 42 | # if you use different ETCD_NAME (e.g. test), set ETCD_INITIAL_CLUSTER value for this name, i.e. "test=http://..." 43 | #ETCD_DISCOVERY="" 44 | #ETCD_DISCOVERY_SRV="" 45 | #ETCD_DISCOVERY_FALLBACK="proxy" 46 | #ETCD_DISCOVERY_PROXY="" 47 | # 48 | #[proxy] 49 | {% if inventory_hostname not in groups[etcd_master_group_name] %} 50 | ETCD_PROXY="on" 51 | {% endif %} 52 | # 53 | #[security] 54 | {% if etcd_secure %} 55 | ETCD_CERT_FILE="{{ etcd_pki_cert_dest }}" 56 | ETCD_KEY_FILE="{{ etcd_pki_key_dest }}" 57 | ETCD_CLIENT_CERT_AUTH="{{ etcd_client_cert_auth | string | lower }}" 58 | ETCD_TRUSTED_CA_FILE="{{ etcd_pki_ca_cert_dest }}" 59 | ETCD_PEER_CERT_FILE="{{ etcd_pki_cert_dest }}" 60 | ETCD_PEER_KEY_FILE="{{ etcd_pki_key_dest }}" 61 | ETCD_PEER_CLIENT_CERT_AUTH="{{ etcd_peer_client_cert_auth | string | lower }}" 62 | ETCD_PEER_TRUSTED_CA_FILE="{{ etcd_pki_ca_cert_dest }}" 63 | {% endif %} 64 | # 65 | #[logging] 66 | {% if etcd_cluster_debug is defined and etcd_cluster_debug | bool %} 67 | ETCD_DEBUG="{{ etcd_cluster_debug | string | lower }}" 68 | {% endif %} 69 | {% if etcd_cluster_log_package_levels is defined %} 70 | ETCD_LOG_PACKAGE_LEVELS="{{ etcd_cluster_log_package_levels }}" 71 | {% else %} 72 | # examples for -log-package-levels etcdserver=WARNING,security=DEBUG 73 | #ETCD_LOG_PACKAGE_LEVELS="etcdserver=DEBUG" 74 | {% endif %} 75 | # 76 | # [custom_env_vars] 77 | {% if etcd_additional_envvars is defined %} 78 | {% for k, v in etcd_additional_envvars.items() %} 79 | {{ k }}="{{ v }}" 80 | {% endfor %} 81 | {% endif %} 82 | -------------------------------------------------------------------------------- /test-pki/localhost-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | --------------------------------------------------------------------------------