diff --git a/TODO.md b/TODO.md index 118eb25..1650455 100644 --- a/TODO.md +++ b/TODO.md @@ -7,7 +7,7 @@ Stuff that are planned to be added/changed. - [ ] /etc/security/access.conf - [ ] Filesystem snapshot: - [ ] sanoid / zrepl (rootfs=zfs) -- [ ] How should we figure out the underlying fstype if rootfs=tmpfs?? +- [ ] Root on tmpfs - [ ] Filesystem backup (I don't have spare hard drives -_- so not supported for now): - [ ] Local incremental backups (to spare disk) - [ ] Remote backups diff --git a/requirements/accepted_variables.yml b/requirements/accepted_variables.yml index 7b6e483..7d9227b 100644 --- a/requirements/accepted_variables.yml +++ b/requirements/accepted_variables.yml @@ -4,9 +4,11 @@ initramfs_generator: - dracut - booster -snapshot_tool: '{{ ["sanoid", "zrepl"] if rootfs == "zfs" - else ["snapper", "btrbk"] if rootfs == "btrfs" - else ["none"] }}' +snapshot_tool: + - btrbk + - sanoid + - snapper + - zrepl # NOTE: Keep this in sync with `shell_mappings` in roles/user/defaults/main.yml usershell: diff --git a/roles/cron/files/btrfs-scrub b/roles/cron/files/btrfs-scrub deleted file mode 100644 index 1160161..0000000 --- a/roles/cron/files/btrfs-scrub +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -# Use flock to ensure the cron job won't start a 2nd time -# Ref: https://wiki.archlinux.org/title/Cron#Ensuring_exclusivity -/usr/bin/flock -nx /tmp/btrfs_scrub.lock /sbin/btrfs scrub start / diff --git a/roles/cron/files/fstrim b/roles/cron/files/fstrim index cb4ab90..bb61d3e 100644 --- a/roles/cron/files/fstrim +++ b/roles/cron/files/fstrim @@ -1,5 +1,8 @@ #!/bin/sh -# This needs fstrim -# For busybox's fstrim, using multiple 'fstrim /mount_point' is more feasible -/sbin/fstrim -a +# NOTE: we only trim things inside /etc/fstab to avoid ZFS mount paths +# (they will be handled by `zpool trim` cron job) + +# This needs fstrim from util-linux package. +# For busybox's fstrim, use multiple 'fstrim /mount_point'. +exec /sbin/fstrim -A diff --git a/roles/cron/tasks/main.yml b/roles/cron/tasks/main.yml index cfeb7d2..88af57c 100644 --- a/roles/cron/tasks/main.yml +++ b/roles/cron/tasks/main.yml @@ -1,12 +1,4 @@ --- -- name: cron | Create fstrim daily job - copy: - src: fstrim - dest: /etc/periodic/daily/fstrim - owner: root - group: root - mode: '755' - # It is recomended to preserve /var/tmp across reboots - name: cron | Create empty /var/tmp/ weekly job copy: @@ -16,14 +8,11 @@ group: root mode: '755' -- name: cron | Create btrfs-scrub monthly job - copy: - src: btrfs-scrub - dest: /etc/periodic/monthly/btrfs-scrub - owner: root - group: root - mode: '755' - when: rootfs == 'btrfs' +- name: cron | Create trimming cron jobs + import_tasks: trim.yml + +- name: cron | Create scrubbing cron jobs for zfs/btrfs + import_tasks: scrub.yml - name: cron | Install logrotate and cpulimit community.general.apk: diff --git a/roles/cron/tasks/scrub.yml b/roles/cron/tasks/scrub.yml new file mode 100644 index 0000000..a3a87dd --- /dev/null +++ b/roles/cron/tasks/scrub.yml @@ -0,0 +1,23 @@ +--- +- name: cron | Create BTRFS scrub monthly job + copy: + content: | + #!/bin/sh + exec /sbin/btrfs scrub start -B {{ item }} + dest: /etc/periodic/monthly/scrub-btrfs-{{ item | regex_replace('/', '@') }} + owner: root + group: root + mode: '755' + # PERF: a shorter version but requires `py3-jmespath`: '{{ ansible_mounts | json_query("[?fstype == `btrfs`].mount") }}' + loop: '{{ ansible_mounts | selectattr("fstype", "equalto", "btrfs") | map(attribute="mount") | list }}' + +- name: cron | Create ZFS pool scrub monthly job + copy: + content: | + #!/bin/sh + exec /usr/sbin/zpool scrub -w {{ item }} + dest: /etc/periodic/monthly/scrub-zfs-{{ item }} + mode: '755' + owner: root + group: root + loop: '{{ ansible_zfs_pools | map(attribute="name") | list }}' diff --git a/roles/cron/tasks/trim.yml b/roles/cron/tasks/trim.yml new file mode 100644 index 0000000..99b0ad5 --- /dev/null +++ b/roles/cron/tasks/trim.yml @@ -0,0 +1,25 @@ +--- +# NOTE: dmcrypt discourages using `discard` mount option (as in BTRFS on LUKS), +# so running trim manually in a cron job +- name: cron | Create fstrim weekly job + copy: + src: fstrim + dest: /etc/periodic/weekly/fstrim + owner: root + group: root + mode: '755' + +- name: cron | Create ZFS pool trim weekly job + copy: + content: | + #!/bin/sh + if /usr/sbin/zpool status {{ item }} | grep -qF "trimming"; then + exec /usr/sbin/zpool wait -t trim {{ item }} + else + exec /usr/sbin/zpool trim -w {{ item }} + fi + dest: /etc/periodic/weekly/trim-zfs-{{ item }} + mode: '755' + owner: root + group: root + loop: '{{ ansible_zfs_pools | map(attribute="name") | list }}' diff --git a/setup.yml b/setup.yml index 9e22962..7245766 100644 --- a/setup.yml +++ b/setup.yml @@ -2,20 +2,7 @@ - name: Setup the system hosts: all gather_facts: true - # vars: - # # Determine the fstype of root filesystem - # # PERF: a shorter version but requires `py3-jmespath`: '{{ ansible_mounts | json_query("[?mount == `/`].fstype") | first }}' - # rootfs: '{{ ansible_mounts | selectattr("mount", "equalto", "/") | map(attribute="fstype") | first }}' pre_tasks: - # FIXME: ansible_mounts doesn't recognise ZFS dataset (yet???) - # Ref: https://github.com/ansible/ansible/issues/24644 - - name: Guess fstype of the root filesystem - shell: - cmd: /bin/findmnt -M / -o FSTYPE -f | tail -n 1 - register: findmnt_result - - name: Set rootfs variable - set_fact: - rootfs: findmnt_result.stdout - name: Sanity checks tags: always block: @@ -32,6 +19,9 @@ msg: 'Variable `{{ item }}` needs to be 1 of {{ accepted_values[item] }}' when: not vars[item] in accepted_values[item] loop: '{{ accepted_values | flatten }}' + - name: Get ZFS pool facts + community.general.zpool_facts: + tags: always roles: - role: essential tags: essential @@ -67,7 +57,6 @@ tags: zram - role: snapshot tags: snapshot - when: snapshot_tool != 'none' - role: earlyoom tags: earlyoom - role: user