From ccc4f52d56c0f51c92e97dda737d236eaa66de8e Mon Sep 17 00:00:00 2001 From: transcaffeine Date: Wed, 30 Oct 2024 12:21:51 +0100 Subject: [PATCH] feat(redis): add ansible role --- README.md | 4 ++ galaxy.yml | 1 + roles/redis/README.md | 47 +++++++++++++++++ roles/redis/defaults/main/config.yml | 41 +++++++++++++++ roles/redis/defaults/main/container.yml | 49 ++++++++++++++++++ roles/redis/defaults/main/main.yml | 15 ++++++ roles/redis/defaults/main/user.yml | 10 ++++ roles/redis/handlers/main.yml | 11 ++++ roles/redis/meta/main.yml | 10 ++++ roles/redis/tasks/deploy-docker.yml | 26 ++++++++++ roles/redis/tasks/main.yml | 68 +++++++++++++++++++++++++ roles/redis/vars/main.yml | 6 +++ 12 files changed, 288 insertions(+) create mode 100644 roles/redis/README.md create mode 100644 roles/redis/defaults/main/config.yml create mode 100644 roles/redis/defaults/main/container.yml create mode 100644 roles/redis/defaults/main/main.yml create mode 100644 roles/redis/defaults/main/user.yml create mode 100644 roles/redis/handlers/main.yml create mode 100644 roles/redis/meta/main.yml create mode 100644 roles/redis/tasks/deploy-docker.yml create mode 100644 roles/redis/tasks/main.yml create mode 100644 roles/redis/vars/main.yml diff --git a/README.md b/README.md index 7495d6c..138f1f5 100644 --- a/README.md +++ b/README.md @@ -4,5 +4,9 @@ - [`elasticsearch`](roles/elasticsearch/README.md): deploy elasticsearch (OSS) in a docker container + +- [`redis`](roles/redis/README.md): deploy and configure redis, + a fast cache, vector search and NoSQL database. + - [`mariadb`](roles/mariadb/README.md): deploy mariadb in a docker container diff --git a/galaxy.yml b/galaxy.yml index dc59cd1..6be2f77 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -14,5 +14,6 @@ repository: https://git.finally.coffee/finallycoffee/databases issues: https://codeberg.org/finallycoffee/ansible-collection-databases/issues tags: - elasticsearch + - redis - mariadb - docker diff --git a/roles/redis/README.md b/roles/redis/README.md new file mode 100644 index 0000000..cff9699 --- /dev/null +++ b/roles/redis/README.md @@ -0,0 +1,47 @@ +# `finallycoffee.databases.redis` ansible role + +Redis is the self-proclaimed world's fastest data platform for caching, +vector search and NoSQL databases. Since version 7.2.4, it is no longer +considered "Free and open source software" (FOSS), with redis switching +their license to the "Serverside public license" (SSPL). + +Setting the `redis_version` to higher than `7.2.4` means you will deploy +the SSPL-licensed version to redis. + +## Configuration + +All container-related options to the `docker_container` ansible module +are available under the `redis_container_*` namespace, for example use +`redis_container_ports: [ '127.0.0.1:6379:6370/tcp' ]` to map the +containers port 6379 to the docker host. + +Redis-related config options are either available in the `redis_config_*` +namespace or can be specified by setting them as a dictionary in +`redis_config` + +### Authentication and authorization + +Redis ACL can be specified as an array in the `redis_config_user` variable + - see [the redis documentation](https://github.com/redis/redis/blob/unstable/redis.conf#L869) +for the format. Per default, the `default` user is able to connect without +any password. To require a password and use a different user, override +the variable, for example `redis_config_user: [ 'username on +@all -DEBUG ~* >secret' ]`. + +## Redis on a unix socket + +To make redis available on a unix socket, a directory must be supplied in which the +socket lives: +```yaml +redis_container_socket: /var/run/redis.sock +redis_container_volumes: + - "/path/to/socket/on/host/redis.sock:{{ redis_container_socket }}:z" +redis_config_unixsocket: "{{ redis_container_socket }}" +``` + +## Container specific information + +Redis publishes their official container image in both a debian-based and an +alpine-based variant. Which image should be used can be configured in +`redis_container_image_flavour`, which defaults to `alpine`, which is smaller +in size but also includes less related / debugging tools. To use the debian- +based image, unset the flavour using `redis_container_image_flavour: ~`. diff --git a/roles/redis/defaults/main/config.yml b/roles/redis/defaults/main/config.yml new file mode 100644 index 0000000..375d11a --- /dev/null +++ b/roles/redis/defaults/main/config.yml @@ -0,0 +1,41 @@ +--- +redis_config_bind: + - "127.0.0.1" + - "-::1" +redis_config_protected_mode: true +redis_config_port: 6379 +redis_config_unixsocket: ~ +redis_config_unixsocketperm: '700' +redis_config_user: + - "default on +@all -DEBUG ~* nopass" +redis_config_databases: 16 +redis_config_supervised: false +redis_config_daemonize: false +redis_config_dbfilename: dump.rdb +redis_config_dir: "{{ redis_data_path }}" +redis_config_save: "3600 1 300 100 60 10000" +redis_config_appendfsync: everysec + +redis_base_config: + bind: "{{ redis_config_bind | join(' ') }}" + "protected-mode": "{{ redis_config_protected_mode | bool | ternary('yes', 'no') }}" + port: "{{ redis_config_port }}" + user: "{{ redis_config_user }}" + databases: "{{ redis_config_databases }}" + daemonize: "{{ redis_config_daemonize | bool | ternary('yes', 'no') }}" + supervised: "{{ redis_config_supervised | bool | ternary('yes', 'no') }}" + save: "{{ redis_config_save }}" + dbfilename: "{{ redis_config_dbfilename }}" + dir: "{{ redis_config_dir }}" + appendfsync: "{{ redis_config_appendfsync }}" + +redis_config: ~ +redis_merged_config: >-2 + {{ redis_base_config + | combine(({ + 'unixsocket': redis_config_unixsocket, + 'unixsocketperm': redis_config_unixsocketperm, + }) + if (redis_config_unixsocket | default(false, true)) else {}, + recursive=True) + | combine(redis_config | default({}, true), recursive=True) }} diff --git a/roles/redis/defaults/main/container.yml b/roles/redis/defaults/main/container.yml new file mode 100644 index 0000000..18872af --- /dev/null +++ b/roles/redis/defaults/main/container.yml @@ -0,0 +1,49 @@ +--- +redis_container_image_registry: docker.io +redis_container_image_namespace: ~ +redis_container_image_name: redis +redis_container_image_tag: ~ +redis_container_image_flavour: alpine +redis_container_image_source: pull +redis_container_image_force_source: >-2 + {{ redis_container_image_tag | default(false, true) | bool }} +redis_container_image: >-2 + {{ + ([ + redis_container_image_registry | default([], true), + redis_container_image_namespace | default([], true), + redis_container_image_name, + ] | flatten | join('/')) + + ':' + + (redis_container_image_tag | default( + redis_version + ( + ((redis_container_image_flavour is string) + and (redis_container_image_flavour | length > 0)) + | ternary('-' + (redis_container_image_flavour | default('')), '') + ), + true, + )) + }} + +redis_container_name: "redis{{ redis_instance_suffix }}" +redis_container_env: ~ +redis_container_user: >-2 + {{ redis_run_user_id }}:{{ redis_run_group_id }} +redis_container_ports: ~ +redis_container_labels: ~ +redis_container_volumes: ~ +redis_container_merged_volumes: >-2 + {{ redis_container_base_volumes + + redis_container_volumes | default([], true) }} +redis_container_command: + - "redis-server" + - "{{ redis_config_file }}" +redis_container_networks: ~ +redis_container_etc_hosts: ~ +redis_container_dns_servers: ~ +redis_container_restart_policy: "unless-stopped" +redis_container_state: >-2 + {{ (redis_state == 'present') | ternary('started', 'absent') }} +redis_container_base_volumes: + - "{{ redis_config_file }}:{{ redis_config_file }}:ro" + - "{{ redis_data_path }}:{{ redis_data_path }}:rw" diff --git a/roles/redis/defaults/main/main.yml b/roles/redis/defaults/main/main.yml new file mode 100644 index 0000000..56675b3 --- /dev/null +++ b/roles/redis/defaults/main/main.yml @@ -0,0 +1,15 @@ +--- +redis_version: "7.2.4" +redis_state: "present" +redis_instance: ~ +redis_instance_suffix: >-2 + {{ ((redis_instance is string) and (redis_instance | length > 0)) + | ternary('-' + (redis_instance | default('', true)), '') }} +redis_user: >-2 + redis{{ redis_instance_suffix }} + +redis_config_path: "/etc/redis" +redis_config_file: >-2 + {{ redis_config_path }}/redis{{ redis_instance_suffix }}.conf +redis_data_path: "/var/lib/redis{{ redis_instance_suffix }}" +redis_deployment_method: docker diff --git a/roles/redis/defaults/main/user.yml b/roles/redis/defaults/main/user.yml new file mode 100644 index 0000000..47e7ef5 --- /dev/null +++ b/roles/redis/defaults/main/user.yml @@ -0,0 +1,10 @@ +--- +redis_run_user_id: >-2 + {{ redis_user_info.uid | default(redis_user, true) }} +redis_run_group_id: >-2 + {{ redis_user_info.group | default(redis_user, true) }} +redis_user_system: true +redis_user_create_home: false +redis_user_groups: ~ +redis_user_append_groups: >-2 + {{ redis_user_groups | default(true, false) | bool }} diff --git a/roles/redis/handlers/main.yml b/roles/redis/handlers/main.yml new file mode 100644 index 0000000..97a7da4 --- /dev/null +++ b/roles/redis/handlers/main.yml @@ -0,0 +1,11 @@ +--- +- name: Ensure redis container '{{ redis_container_name }}' is restarted + community.docker.docker_container: + name: "{{ redis_container_name }}" + state: "{{ redis_container_state }}" + restart: true + listen: redis-restart + when: + - redis_deployment_method == 'docker' + - redis_state == 'present' + ignore_errors: "{{ ansible_check_mode }}" diff --git a/roles/redis/meta/main.yml b/roles/redis/meta/main.yml new file mode 100644 index 0000000..b2e6798 --- /dev/null +++ b/roles/redis/meta/main.yml @@ -0,0 +1,10 @@ +--- +allow_duplicates: true +dependencies: [] +galaxy_info: + role_name: redis + description: >-2 + Deploy and configure redis, a fast caching, vector-search and NoSQL database. + galaxy_tags: + - redis + - docker diff --git a/roles/redis/tasks/deploy-docker.yml b/roles/redis/tasks/deploy-docker.yml new file mode 100644 index 0000000..e0319a4 --- /dev/null +++ b/roles/redis/tasks/deploy-docker.yml @@ -0,0 +1,26 @@ +--- +- name: Ensure container image '{{ redis_container_image }}' is {{ redis_state }} + community.docker.docker_image: + name: "{{ redis_container_image }}" + state: "{{ redis_state }}" + source: "{{ redis_container_image_source }}" + force_source: "{{ redis_container_image_force_source }}" + register: redis_container_image_info + until: redis_container_image_info is success + retries: 5 + delay: 3 + +- name: Ensure container '{{ redis_container_name }}' is {{ redis_container_state }} + community.docker.docker_container: + name: "{{ redis_container_name }}" + image: "{{ redis_container_image }}" + env: "{{ redis_container_env | default(omit, true) }}" + user: "{{ redis_container_user }}" + ports: "{{ redis_container_ports | default(omit, true) }}" + labels: "{{ redis_container_labels | default(omit, true) }}" + command: "{{ redis_container_command }}" + volumes: "{{ redis_container_merged_volumes }}" + networks: "{{ redis_container_networks | default(omit, true) }}" + etc_hosts: "{{ redis_container_etc_hosts | default(omit, true) }}" + dns_servers: "{{ redis_container_dns_servers | default(omit, true) }}" + state: "{{ redis_container_state }}" diff --git a/roles/redis/tasks/main.yml b/roles/redis/tasks/main.yml new file mode 100644 index 0000000..67ed114 --- /dev/null +++ b/roles/redis/tasks/main.yml @@ -0,0 +1,68 @@ +--- +- name: Ensure state is valid + ansible.builtin.fail: + msg: >-2 + Unsupported state '{{ redis_state }}'. + Supported states are {{ redis_states | join(', ') }} + when: redis_state not in redis_states + +- name: Ensure deployment method is valid + ansible.builtin.fail: + msg: >-2 + Unsupported deployment method '{{ redis_deployment_method }}'! + Supported methods are {{ redis_deployment_method | join(', ') }} + when: redis_deployment_method not in redis_deployment_methods + +- name: Ensure redis user '{{ redis_user }}' is {{ redis_state }} + ansible.builtin.user: + name: "{{ redis_user }}" + state: "{{ redis_state }}" + system: "{{ redis_user_system }}" + create_home: "{{ redis_user_create_home }}" + groups: "{{ redis_user_groups | default(omit, true) }}" + append: "{{ redis_user_append_groups | default(omit, true) }}" + register: redis_user_info + +- name: Ensure redis config file '{{ redis_config_file }}' is {{ redis_state }} + ansible.builtin.file: + path: "{{ redis_config_file }}" + state: "{{ redis_state }}" + when: redis_state == 'absent' + +- name: Ensure redis host directories are {{ redis_state }} + ansible.builtin.file: + path: "{{ path.name }}" + state: >-2 + {{ (redis_state == 'present') | ternary('directory', 'absent') }} + owner: "{{ path.owner | default(redis_run_user_id) }}" + group: "{{ path.group | default(redis_run_group_id) }}" + mode: "{{ path.mode | default('0755') }}" + loop: + - name: "{{ redis_config_path }}" + - name: "{{ redis_data_path }}" + loop_control: + loop_var: "path" + label: "{{ path.name }}" + +- name: Ensure redis config file '{{ redis_config_file }}' is {{ redis_state }} + ansible.builtin.copy: + content: |+2 + {% for tuple in (redis_merged_config | dict2items) %} + {% if tuple.value is string or tuple.value is number %} + {{ tuple.key }} {{ tuple.value }} + {% else %} + {% for value in tuple.value %} + {{ tuple.key }} {{ value }} + {% endfor %} + {% endif %} + {% endfor %} + dest: "{{ redis_config_file }}" + owner: "{{ redis_run_user_id }}" + group: "{{ redis_run_group_id }}" + mode: "0640" + when: redis_state == 'present' + notify: redis-restart + +- name: Deploy redis using {{ redis_deployment_method }} + ansible.builtin.include_tasks: + file: "deploy-{{ redis_deployment_method }}.yml" diff --git a/roles/redis/vars/main.yml b/roles/redis/vars/main.yml new file mode 100644 index 0000000..ee16e40 --- /dev/null +++ b/roles/redis/vars/main.yml @@ -0,0 +1,6 @@ +--- +redis_states: + - present + - absent +redis_deployment_methods: + - docker