feat: добавлены роли mdadm и k8s-user с полной оркестрацией SSH ключей
Роль mdadm: - автоопределение RAID массива через mdadm --detail --scan - монтирование в /storage через fstab (UUID-based, nofail) - автоопределение fstype через blkid - обновление mdadm.conf + initramfs - флаг mdadm_enabled для отключения на отдельных нодах Роль k8s-user: - создание пользователя k8s + группа + sudo без пароля (visudo validation) - генерация RSA 4096 ключевой пары на первом мастере (идемпотентно, creates:) - раскладка приватного и публичного ключа на все ноды кластера - добавление public key в authorized_keys — SSH с любой ноды на любую - обновление /etc/hosts блоками через blockinfile (k3s_cluster + lab_hosts) - поддержка lab_hosts: создание пользователя и деплой ключей через пароль из vault Плейбуки: - k8s-user.yml — полная оркестрация (5 plays: create → generate → distribute → hosts → lab) - mdadm.yml — запуск роли mdadm на k3s_cluster Инфраструктура: - inventory: добавлена группа [lab_hosts] с примерами - host_vars/nas01/vault.yml.example — шаблон credentials для лаб-серверов - group_vars/all/main.yml: переменные mdadm_enabled и k8s_service_user_* - Makefile: цели k8s-user и mdadm - docker/entrypoint.sh: команды k8s-user и mdadm
This commit is contained in:
24
roles/k8s-user/defaults/main.yml
Normal file
24
roles/k8s-user/defaults/main.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
# ─── k8s-user — сервисный пользователь для управления кластером ──────────────
|
||||
|
||||
# Имя пользователя
|
||||
k8s_service_user: k8s
|
||||
|
||||
# Shell
|
||||
k8s_service_user_shell: /bin/bash
|
||||
|
||||
# Комментарий
|
||||
k8s_service_user_comment: "K8S Service Account"
|
||||
|
||||
# Тип SSH ключа и длина
|
||||
k8s_service_user_key_type: rsa
|
||||
k8s_service_user_key_bits: 4096
|
||||
|
||||
# Комментарий в публичном ключе
|
||||
k8s_service_user_key_comment: "k8s@cluster"
|
||||
|
||||
# Путь к ключам на серверах (внутри home пользователя)
|
||||
k8s_service_user_ssh_dir: ".ssh"
|
||||
|
||||
# Разрешить sudo без пароля для k8s пользователя
|
||||
k8s_service_user_sudo: true
|
||||
36
roles/k8s-user/tasks/create_user.yml
Normal file
36
roles/k8s-user/tasks/create_user.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
# Создание пользователя k8s + sudo на текущем хосте
|
||||
|
||||
- name: Create k8s user group
|
||||
ansible.builtin.group:
|
||||
name: "{{ k8s_service_user }}"
|
||||
state: present
|
||||
become: true
|
||||
|
||||
- name: Create k8s service user
|
||||
ansible.builtin.user:
|
||||
name: "{{ k8s_service_user }}"
|
||||
comment: "{{ k8s_service_user_comment }}"
|
||||
shell: "{{ k8s_service_user_shell }}"
|
||||
group: "{{ k8s_service_user }}"
|
||||
create_home: true
|
||||
state: present
|
||||
become: true
|
||||
|
||||
- name: Ensure .ssh directory exists for k8s user
|
||||
ansible.builtin.file:
|
||||
path: "/home/{{ k8s_service_user }}/{{ k8s_service_user_ssh_dir }}"
|
||||
state: directory
|
||||
owner: "{{ k8s_service_user }}"
|
||||
group: "{{ k8s_service_user }}"
|
||||
mode: '0700'
|
||||
become: true
|
||||
|
||||
- name: Grant passwordless sudo to k8s user
|
||||
ansible.builtin.copy:
|
||||
dest: "/etc/sudoers.d/{{ k8s_service_user }}"
|
||||
content: "{{ k8s_service_user }} ALL=(ALL) NOPASSWD:ALL\n"
|
||||
mode: '0440'
|
||||
validate: visudo -cf %s
|
||||
become: true
|
||||
when: k8s_service_user_sudo | bool
|
||||
28
roles/k8s-user/tasks/distribute_keys.yml
Normal file
28
roles/k8s-user/tasks/distribute_keys.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
# Раскладывает приватный и публичный ключ k8s пользователя на текущий хост
|
||||
# Ключи берутся из hostvars первого мастера (сгенерированы там play'ем generate_keys)
|
||||
|
||||
- name: Deploy private key to k8s user
|
||||
ansible.builtin.copy:
|
||||
content: "{{ hostvars[groups['k3s_master'][0]]['k8s_ssh_private_key'] }}"
|
||||
dest: "/home/{{ k8s_service_user }}/{{ k8s_service_user_ssh_dir }}/id_rsa"
|
||||
owner: "{{ k8s_service_user }}"
|
||||
group: "{{ k8s_service_user }}"
|
||||
mode: '0600'
|
||||
become: true
|
||||
|
||||
- name: Deploy public key to k8s user
|
||||
ansible.builtin.copy:
|
||||
content: "{{ hostvars[groups['k3s_master'][0]]['k8s_ssh_public_key'] }}\n"
|
||||
dest: "/home/{{ k8s_service_user }}/{{ k8s_service_user_ssh_dir }}/id_rsa.pub"
|
||||
owner: "{{ k8s_service_user }}"
|
||||
group: "{{ k8s_service_user }}"
|
||||
mode: '0644'
|
||||
become: true
|
||||
|
||||
- name: Add k8s public key to authorized_keys
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ k8s_service_user }}"
|
||||
key: "{{ hostvars[groups['k3s_master'][0]]['k8s_ssh_public_key'] }}"
|
||||
state: present
|
||||
become: true
|
||||
50
roles/k8s-user/tasks/generate_keys.yml
Normal file
50
roles/k8s-user/tasks/generate_keys.yml
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
# Генерация RSA 4096 ключевой пары для k8s пользователя
|
||||
# Выполняется ОДИН РАЗ на первом мастере, ключи затем распространяются на все хосты
|
||||
|
||||
- name: Generate RSA {{ k8s_service_user_key_bits }} key pair for k8s user
|
||||
ansible.builtin.command:
|
||||
cmd: >
|
||||
ssh-keygen
|
||||
-t {{ k8s_service_user_key_type }}
|
||||
-b {{ k8s_service_user_key_bits }}
|
||||
-N ''
|
||||
-C "{{ k8s_service_user_key_comment }}"
|
||||
-f /home/{{ k8s_service_user }}/{{ k8s_service_user_ssh_dir }}/id_rsa
|
||||
creates: "/home/{{ k8s_service_user }}/{{ k8s_service_user_ssh_dir }}/id_rsa"
|
||||
become: true
|
||||
become_user: "{{ k8s_service_user }}"
|
||||
|
||||
- name: Set correct permissions on private key
|
||||
ansible.builtin.file:
|
||||
path: "/home/{{ k8s_service_user }}/{{ k8s_service_user_ssh_dir }}/id_rsa"
|
||||
owner: "{{ k8s_service_user }}"
|
||||
group: "{{ k8s_service_user }}"
|
||||
mode: '0600'
|
||||
become: true
|
||||
|
||||
- name: Set correct permissions on public key
|
||||
ansible.builtin.file:
|
||||
path: "/home/{{ k8s_service_user }}/{{ k8s_service_user_ssh_dir }}/id_rsa.pub"
|
||||
owner: "{{ k8s_service_user }}"
|
||||
group: "{{ k8s_service_user }}"
|
||||
mode: '0644'
|
||||
become: true
|
||||
|
||||
- name: Slurp private key from first master
|
||||
ansible.builtin.slurp:
|
||||
src: "/home/{{ k8s_service_user }}/{{ k8s_service_user_ssh_dir }}/id_rsa"
|
||||
register: k8s_private_key_raw
|
||||
become: true
|
||||
|
||||
- name: Slurp public key from first master
|
||||
ansible.builtin.slurp:
|
||||
src: "/home/{{ k8s_service_user }}/{{ k8s_service_user_ssh_dir }}/id_rsa.pub"
|
||||
register: k8s_public_key_raw
|
||||
become: true
|
||||
|
||||
- name: Store key content as persistent facts (доступны во всех последующих plays)
|
||||
ansible.builtin.set_fact:
|
||||
k8s_ssh_private_key: "{{ k8s_private_key_raw.content | b64decode }}"
|
||||
k8s_ssh_public_key: "{{ k8s_public_key_raw.content | b64decode | trim }}"
|
||||
cacheable: true
|
||||
3
roles/k8s-user/tasks/main.yml
Normal file
3
roles/k8s-user/tasks/main.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
- name: Create k8s service user
|
||||
ansible.builtin.include_tasks: create_user.yml
|
||||
23
roles/k8s-user/tasks/update_hosts.yml
Normal file
23
roles/k8s-user/tasks/update_hosts.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
# Обновляет /etc/hosts: добавляет все ноды кластера и лабораторные серверы
|
||||
|
||||
- name: Add k3s cluster nodes to /etc/hosts
|
||||
ansible.builtin.blockinfile:
|
||||
path: /etc/hosts
|
||||
marker: "# {mark} ANSIBLE MANAGED - K3S CLUSTER"
|
||||
block: |
|
||||
{% for host in groups['k3s_cluster'] %}
|
||||
{{ hostvars[host]['ansible_host'] }} {{ host }}
|
||||
{% endfor %}
|
||||
become: true
|
||||
|
||||
- name: Add lab hosts to /etc/hosts
|
||||
ansible.builtin.blockinfile:
|
||||
path: /etc/hosts
|
||||
marker: "# {mark} ANSIBLE MANAGED - LAB HOSTS"
|
||||
block: |
|
||||
{% for host in groups['lab_hosts'] %}
|
||||
{{ hostvars[host]['ansible_host'] }} {{ host }}
|
||||
{% endfor %}
|
||||
become: true
|
||||
when: groups['lab_hosts'] | default([]) | length > 0
|
||||
23
roles/mdadm/defaults/main.yml
Normal file
23
roles/mdadm/defaults/main.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
# ─── mdadm — поиск RAID и монтирование в /storage ────────────────────────────
|
||||
|
||||
# Включить установку и монтирование mdadm RAID
|
||||
mdadm_enabled: true
|
||||
|
||||
# Точка монтирования для найденного RAID массива
|
||||
mdadm_mount_point: /storage
|
||||
|
||||
# Опции монтирования в fstab
|
||||
# nofail — не останавливать загрузку если RAID не найден
|
||||
mdadm_mount_opts: "defaults,nofail,x-systemd.device-timeout=5"
|
||||
|
||||
# Файловая система на RAID (auto — определяется автоматически)
|
||||
mdadm_fstype: auto
|
||||
|
||||
# Права на точку монтирования
|
||||
mdadm_mount_point_mode: "0755"
|
||||
mdadm_mount_point_owner: root
|
||||
mdadm_mount_point_group: root
|
||||
|
||||
# Обновить конфиг mdadm.conf после обнаружения массива
|
||||
mdadm_update_config: true
|
||||
118
roles/mdadm/tasks/main.yml
Normal file
118
roles/mdadm/tasks/main.yml
Normal file
@@ -0,0 +1,118 @@
|
||||
---
|
||||
- name: Skip mdadm if not enabled
|
||||
ansible.builtin.meta: end_play
|
||||
when: not mdadm_enabled | default(true) | bool
|
||||
|
||||
- name: Install mdadm package
|
||||
ansible.builtin.apt:
|
||||
name: mdadm
|
||||
state: present
|
||||
update_cache: false
|
||||
become: true
|
||||
|
||||
- name: Scan for active RAID arrays
|
||||
ansible.builtin.command: mdadm --detail --scan
|
||||
register: mdadm_scan
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
become: true
|
||||
|
||||
- name: Show discovered RAID arrays
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ mdadm_scan.stdout_lines }}"
|
||||
when: mdadm_scan.stdout | length > 0
|
||||
|
||||
- name: Warn if no RAID arrays found
|
||||
ansible.builtin.debug:
|
||||
msg: "Внимание: RAID массивы не найдены на {{ inventory_hostname }}. Пропускаем монтирование."
|
||||
when: mdadm_scan.stdout | length == 0 or mdadm_scan.rc != 0
|
||||
|
||||
- name: Extract first RAID device name
|
||||
ansible.builtin.set_fact:
|
||||
mdadm_device: >-
|
||||
{{ mdadm_scan.stdout
|
||||
| regex_search('ARRAY\s+(/dev/md[^\s]+)', '\1')
|
||||
| first }}
|
||||
when:
|
||||
- mdadm_scan.rc == 0
|
||||
- mdadm_scan.stdout | regex_search('ARRAY\s+/dev/md')
|
||||
|
||||
- name: Check RAID device is accessible
|
||||
ansible.builtin.stat:
|
||||
path: "{{ mdadm_device }}"
|
||||
register: mdadm_device_stat
|
||||
become: true
|
||||
when: mdadm_device is defined
|
||||
|
||||
- name: Get UUID of RAID device (for stable fstab entry)
|
||||
ansible.builtin.command: blkid -s UUID -o value {{ mdadm_device }}
|
||||
register: mdadm_uuid
|
||||
changed_when: false
|
||||
become: true
|
||||
when:
|
||||
- mdadm_device is defined
|
||||
- mdadm_device_stat.stat.exists | default(false)
|
||||
|
||||
- name: Detect filesystem type on RAID device
|
||||
ansible.builtin.command: blkid -s TYPE -o value {{ mdadm_device }}
|
||||
register: mdadm_detected_fstype
|
||||
changed_when: false
|
||||
become: true
|
||||
when:
|
||||
- mdadm_device is defined
|
||||
- mdadm_device_stat.stat.exists | default(false)
|
||||
- mdadm_fstype == "auto"
|
||||
|
||||
- name: Set filesystem type fact
|
||||
ansible.builtin.set_fact:
|
||||
mdadm_real_fstype: >-
|
||||
{{ mdadm_detected_fstype.stdout | default('ext4')
|
||||
if mdadm_fstype == 'auto'
|
||||
else mdadm_fstype }}
|
||||
when: mdadm_device is defined
|
||||
|
||||
- name: Create mount point directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ mdadm_mount_point }}"
|
||||
state: directory
|
||||
mode: "{{ mdadm_mount_point_mode }}"
|
||||
owner: "{{ mdadm_mount_point_owner }}"
|
||||
group: "{{ mdadm_mount_point_group }}"
|
||||
become: true
|
||||
when: mdadm_device is defined
|
||||
|
||||
- name: Mount RAID array via fstab (UUID-based, persistent)
|
||||
ansible.posix.mount:
|
||||
path: "{{ mdadm_mount_point }}"
|
||||
src: "UUID={{ mdadm_uuid.stdout | trim }}"
|
||||
fstype: "{{ mdadm_real_fstype }}"
|
||||
opts: "{{ mdadm_mount_opts }}"
|
||||
state: mounted
|
||||
become: true
|
||||
when:
|
||||
- mdadm_device is defined
|
||||
- mdadm_uuid.stdout | default('') | length > 0
|
||||
|
||||
- name: Update mdadm.conf with discovered arrays
|
||||
ansible.builtin.shell: |
|
||||
mdadm --detail --scan >> /etc/mdadm/mdadm.conf
|
||||
update-initramfs -u 2>/dev/null || true
|
||||
args:
|
||||
executable: /bin/bash
|
||||
become: true
|
||||
changed_when: true
|
||||
when:
|
||||
- mdadm_update_config | bool
|
||||
- mdadm_device is defined
|
||||
|
||||
- name: Show mount status
|
||||
ansible.builtin.command: df -h {{ mdadm_mount_point }}
|
||||
register: mount_status
|
||||
changed_when: false
|
||||
become: true
|
||||
when: mdadm_device is defined
|
||||
|
||||
- name: Display RAID mount result
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ mount_status.stdout_lines }}"
|
||||
when: mdadm_device is defined
|
||||
Reference in New Issue
Block a user