mirror of
https://github.com/dredx/prole.git
synced 2026-09-27 19:24:30 +00:00
- Update documentation to note `/external` may already be mounted before Ansible runs. - Avoid redundant ownership and permissions enforcement in `mariadb_primary` role.
299 lines
12 KiB
YAML
299 lines
12 KiB
YAML
---
|
|
|
|
- name: Install MariaDB and tooling
|
|
ansible.builtin.package:
|
|
name:
|
|
- mariadb-server
|
|
- mariadb-client
|
|
- rsync
|
|
state: present
|
|
|
|
- name: Temporary external storage for MariaDB (merlin)
|
|
block:
|
|
- name: Ensure exFAT support packages are installed (Debian/Ubuntu)
|
|
block:
|
|
- name: Install exfatprogs
|
|
ansible.builtin.package:
|
|
name: exfatprogs
|
|
state: present
|
|
rescue:
|
|
- name: Install legacy exFAT packages
|
|
ansible.builtin.package:
|
|
name:
|
|
- exfat-fuse
|
|
- exfat-utils
|
|
state: present
|
|
when: ansible_facts['os_family'] | default('') == 'Debian'
|
|
|
|
- name: Ensure external mountpoint directory exists
|
|
ansible.builtin.file:
|
|
path: "{{ mariadb_external_mountpoint | default('/external') }}"
|
|
state: directory
|
|
|
|
- name: Ensure external partition device exists
|
|
ansible.builtin.stat:
|
|
path: "{{ mariadb_external_device | default('/dev/sda1') }}"
|
|
register: _mariadb_external_device_stat
|
|
|
|
- name: Fail if external partition device is missing
|
|
ansible.builtin.assert:
|
|
that:
|
|
- _mariadb_external_device_stat.stat.exists
|
|
fail_msg: "External device {{ mariadb_external_device | default('/dev/sda1') }} does not exist on {{ inventory_hostname }}."
|
|
|
|
- name: Discover filesystem UUID for external partition
|
|
ansible.builtin.command: >-
|
|
blkid -s UUID -o value {{ mariadb_external_device | default('/dev/sda1') }}
|
|
register: _mariadb_external_uuid
|
|
changed_when: false
|
|
failed_when: _mariadb_external_uuid.rc != 0 or (_mariadb_external_uuid.stdout | trim | length) == 0
|
|
|
|
- name: Mount external filesystem at /external (persistent)
|
|
ansible.posix.mount:
|
|
src: "UUID={{ _mariadb_external_uuid.stdout | trim }}"
|
|
path: "{{ mariadb_external_mountpoint | default('/external') }}"
|
|
fstype: "{{ mariadb_external_fstype | default('exfat') }}"
|
|
opts: "{{ mariadb_external_mount_opts | default('defaults,nofail') }}"
|
|
state: mounted
|
|
|
|
- name: Fail if /external is not mounted
|
|
ansible.builtin.command: "mountpoint -q {{ mariadb_external_mountpoint | default('/external') }}"
|
|
changed_when: false
|
|
when: mariadb_external_enabled | default(false) | bool
|
|
|
|
- name: Ensure USB mountpoint directory exists
|
|
ansible.builtin.file:
|
|
path: "{{ mariadb_usb_mountpoint | default('/srv/mariadb') }}"
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
when: not (mariadb_external_enabled | default(false) | bool)
|
|
|
|
- name: Mount MariaDB USB filesystem
|
|
ansible.builtin.mount:
|
|
src: "LABEL={{ mariadb_usb_label | default('MARIADB') }}"
|
|
path: "{{ mariadb_usb_mountpoint | default('/srv/mariadb') }}"
|
|
fstype: "{{ mariadb_usb_fstype | default('ext4') }}"
|
|
opts: "{{ mariadb_usb_mount_opts | default('noatime,nofail') }}"
|
|
state: mounted
|
|
when: not (mariadb_external_enabled | default(false) | bool)
|
|
|
|
- name: Stop MariaDB before datadir changes
|
|
ansible.builtin.service:
|
|
name: mariadb
|
|
state: stopped
|
|
failed_when: false
|
|
|
|
- name: Migrate MariaDB datadir to /external (merlin temporary setup)
|
|
vars:
|
|
mariadb_external_src: "{{ mariadb_external_src_datadir | default('/srv/mariadb/mariadb') }}"
|
|
mariadb_external_dst: "{{ mariadb_external_dst_datadir | default('/external/mariadb') }}"
|
|
mariadb_external_backup: "{{ mariadb_external_backup_datadir | default('/srv/mariadb/mariadb.pre-external') }}"
|
|
mariadb_external_marker: "{{ mariadb_external_migration_marker | default('/external/mariadb/.prole-mariadb-external-migrated') }}"
|
|
block:
|
|
- name: Fail if external mount is not present
|
|
ansible.builtin.command: "mountpoint -q {{ mariadb_external_mountpoint | default('/external') }}"
|
|
changed_when: false
|
|
|
|
- name: Ensure destination directory exists on external disk
|
|
ansible.builtin.file:
|
|
path: "{{ mariadb_external_dst }}"
|
|
state: directory
|
|
|
|
- name: Check migration marker
|
|
ansible.builtin.stat:
|
|
path: "{{ mariadb_external_marker }}"
|
|
register: _mariadb_external_marker_stat
|
|
|
|
- name: Fail if source datadir does not exist
|
|
ansible.builtin.stat:
|
|
path: "{{ mariadb_external_src }}"
|
|
register: _mariadb_external_src_stat
|
|
|
|
- name: Assert source datadir exists
|
|
ansible.builtin.assert:
|
|
that:
|
|
- _mariadb_external_src_stat.stat.exists
|
|
- _mariadb_external_src_stat.stat.isdir
|
|
fail_msg: "Source MariaDB datadir {{ mariadb_external_src }} is missing or not a directory on {{ inventory_hostname }}."
|
|
|
|
- name: Fail clearly if rsync is not available
|
|
ansible.builtin.command: rsync --version
|
|
register: _mariadb_external_rsync_version
|
|
changed_when: false
|
|
|
|
- name: Check whether source datadir is already a mountpoint (bind-mounted)
|
|
ansible.builtin.command: "mountpoint -q {{ mariadb_external_src }}"
|
|
register: _mariadb_external_src_is_mountpoint
|
|
changed_when: false
|
|
failed_when: false
|
|
|
|
- name: Rsync MariaDB data to external disk (copy; non-destructive)
|
|
ansible.builtin.command: >-
|
|
rsync -aH --numeric-ids {{ mariadb_external_src }}/ {{ mariadb_external_dst }}/
|
|
register: _mariadb_external_rsync
|
|
changed_when: true
|
|
when: not _mariadb_external_marker_stat.stat.exists
|
|
|
|
- name: Write migration marker
|
|
ansible.builtin.copy:
|
|
dest: "{{ mariadb_external_marker }}"
|
|
content: |
|
|
migrated_from={{ mariadb_external_src }}
|
|
migrated_to={{ mariadb_external_dst }}
|
|
host={{ inventory_hostname }}
|
|
when: not _mariadb_external_marker_stat.stat.exists
|
|
|
|
- name: Check whether backup datadir already exists
|
|
ansible.builtin.stat:
|
|
path: "{{ mariadb_external_backup }}"
|
|
register: _mariadb_external_backup_stat
|
|
|
|
- name: Move original datadir aside (kept for rollback)
|
|
ansible.builtin.command: >-
|
|
mv {{ mariadb_external_src }} {{ mariadb_external_backup }}
|
|
when:
|
|
- _mariadb_external_marker_stat.stat.exists or (not _mariadb_external_marker_stat.stat.exists and (_mariadb_external_rsync is defined))
|
|
- _mariadb_external_src_is_mountpoint.rc != 0
|
|
- not _mariadb_external_backup_stat.stat.exists
|
|
|
|
- name: Re-create source datadir path as a mountpoint directory
|
|
ansible.builtin.file:
|
|
path: "{{ mariadb_external_src }}"
|
|
state: directory
|
|
when: _mariadb_external_src_is_mountpoint.rc != 0
|
|
|
|
- name: Bind-mount external MariaDB directory to the expected datadir path
|
|
ansible.posix.mount:
|
|
src: "{{ mariadb_external_dst }}"
|
|
path: "{{ mariadb_external_src }}"
|
|
fstype: none
|
|
opts: bind
|
|
state: mounted
|
|
when: _mariadb_external_src_is_mountpoint.rc != 0
|
|
|
|
- name: Fail if bind mount is not active
|
|
ansible.builtin.command: "mountpoint -q {{ mariadb_external_src }}"
|
|
changed_when: false
|
|
when: mariadb_external_enabled | default(false) | bool
|
|
|
|
- name: Ensure MariaDB datadir exists on USB
|
|
ansible.builtin.file:
|
|
path: "{{ mariadb_datadir | default((mariadb_usb_mountpoint | default('/srv/mariadb')) ~ '/mariadb') }}"
|
|
state: directory
|
|
owner: mysql
|
|
group: mysql
|
|
mode: "0700"
|
|
when: not (mariadb_external_enabled | default(false) | bool)
|
|
|
|
- name: Allow MariaDB datadir in AppArmor (Ubuntu/Debian)
|
|
ansible.builtin.copy:
|
|
dest: /etc/apparmor.d/local/usr.sbin.mysqld
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
content: |
|
|
# Managed by Ansible (mariadb_primary)
|
|
{{ mariadb_datadir | default((mariadb_usb_mountpoint | default('/srv/mariadb')) ~ '/mariadb') }}/ r,
|
|
{{ mariadb_datadir | default((mariadb_usb_mountpoint | default('/srv/mariadb')) ~ '/mariadb') }}/** rwk,
|
|
register: _mariadb_apparmor_local
|
|
when: ansible_facts['os_family'] | default('') == 'Debian'
|
|
|
|
- name: Reload AppArmor profile for mysqld
|
|
ansible.builtin.command: apparmor_parser -r /etc/apparmor.d/usr.sbin.mysqld
|
|
changed_when: false
|
|
when:
|
|
- ansible_facts['os_family'] | default('') == 'Debian'
|
|
- _mariadb_apparmor_local is changed
|
|
failed_when: false
|
|
|
|
- name: Configure MariaDB bind-address
|
|
ansible.builtin.lineinfile:
|
|
path: /etc/mysql/mariadb.conf.d/50-server.cnf
|
|
regexp: '^bind-address\s*='
|
|
line: "bind-address = {{ mariadb_bind_address | default('0.0.0.0') }}"
|
|
insertafter: '^\[mysqld\]'
|
|
register: _mariadb_cfg_bind
|
|
|
|
- name: Configure MariaDB datadir
|
|
ansible.builtin.lineinfile:
|
|
path: /etc/mysql/mariadb.conf.d/50-server.cnf
|
|
regexp: '^datadir\s*='
|
|
line: "datadir = {{ mariadb_datadir | default((mariadb_usb_mountpoint | default('/srv/mariadb')) ~ '/mariadb') }}"
|
|
insertafter: '^\[mysqld\]'
|
|
register: _mariadb_cfg_datadir
|
|
|
|
- name: Check if source MariaDB system tables exist
|
|
ansible.builtin.stat:
|
|
path: /var/lib/mysql/mysql
|
|
register: _mariadb_src_tables
|
|
|
|
- name: Check if destination MariaDB system tables exist
|
|
ansible.builtin.stat:
|
|
path: "{{ (mariadb_datadir | default((mariadb_usb_mountpoint | default('/srv/mariadb')) ~ '/mariadb')) ~ '/mysql' }}"
|
|
register: _mariadb_dst_tables
|
|
|
|
- name: Copy initial MariaDB data to USB datadir (first migration only)
|
|
ansible.builtin.command: >-
|
|
rsync -aHAX --numeric-ids /var/lib/mysql/ {{ mariadb_datadir | default((mariadb_usb_mountpoint | default('/srv/mariadb')) ~ '/mariadb') }}/
|
|
when:
|
|
- _mariadb_src_tables.stat.exists
|
|
- not _mariadb_dst_tables.stat.exists
|
|
- not (mariadb_external_enabled | default(false) | bool)
|
|
register: _mariadb_rsync
|
|
changed_when: true
|
|
|
|
- name: Ensure MariaDB datadir ownership after migration
|
|
ansible.builtin.command: >-
|
|
chown -R mysql:mysql {{ mariadb_datadir | default((mariadb_usb_mountpoint | default('/srv/mariadb')) ~ '/mariadb') }}
|
|
when:
|
|
- _mariadb_rsync is defined
|
|
- _mariadb_rsync is changed
|
|
- not (mariadb_external_enabled | default(false) | bool)
|
|
changed_when: true
|
|
|
|
- name: Ensure MariaDB service is started and enabled
|
|
ansible.builtin.service:
|
|
name: mariadb
|
|
state: started
|
|
enabled: true
|
|
|
|
- name: Require MariaDB admin credentials
|
|
ansible.builtin.assert:
|
|
that:
|
|
- k3s_mariadb_admin_user | default('') | length > 0
|
|
- k3s_mariadb_admin_password | default('') | length > 0
|
|
fail_msg: "Set k3s_mariadb_admin_user and vaulted k3s_mariadb_admin_password for MariaDB provisioning."
|
|
when: not ansible_check_mode
|
|
|
|
- name: Create/ensure MariaDB admin user for remote TCP management
|
|
ansible.builtin.command: >-
|
|
mysql --protocol=socket --user=root
|
|
--execute="
|
|
CREATE USER IF NOT EXISTS '{{ k3s_mariadb_admin_user }}'@'%' IDENTIFIED BY '{{ k3s_mariadb_admin_password }}';
|
|
ALTER USER '{{ k3s_mariadb_admin_user }}'@'%' IDENTIFIED BY '{{ k3s_mariadb_admin_password }}';
|
|
GRANT ALL PRIVILEGES ON *.* TO '{{ k3s_mariadb_admin_user }}'@'%' WITH GRANT OPTION;
|
|
FLUSH PRIVILEGES;"
|
|
when: not ansible_check_mode
|
|
no_log: true
|
|
|
|
- name: Require k3s MariaDB user password (vaulted)
|
|
ansible.builtin.assert:
|
|
that:
|
|
- k3s_datastore_mariadb_password | default('') | length > 0
|
|
fail_msg: "k3s_datastore_mariadb_password is empty; set it from Ansible Vault."
|
|
when: not ansible_check_mode
|
|
|
|
- name: Create k3s database/user/grants (local socket)
|
|
ansible.builtin.command: >-
|
|
mysql --protocol=socket --user=root
|
|
--execute="
|
|
CREATE DATABASE IF NOT EXISTS `{{ k3s_datastore_mariadb_db | default('k3s') }}`;
|
|
CREATE USER IF NOT EXISTS '{{ k3s_datastore_mariadb_user | default('prole_k3s') }}'@'%' IDENTIFIED BY '{{ k3s_datastore_mariadb_password }}';
|
|
ALTER USER '{{ k3s_datastore_mariadb_user | default('prole_k3s') }}'@'%' IDENTIFIED BY '{{ k3s_datastore_mariadb_password }}';
|
|
GRANT ALL PRIVILEGES ON `{{ k3s_datastore_mariadb_db | default('k3s') }}`.* TO '{{ k3s_datastore_mariadb_user | default('prole_k3s') }}'@'%';
|
|
FLUSH PRIVILEGES;"
|
|
when: not ansible_check_mode
|
|
no_log: true
|