diff --git a/.travis.yml b/.travis.yml index 29cb3690..57e65894 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,9 @@ language: python python: "2.7" env: - - ANSIBLE_VERSION=2.4.0 + - ANSIBLE_VERSION=2.5.14 + - ANSIBLE_VERSION=2.6.11 + - ANSIBLE_VERSION=2.7.5 - ANSIBLE_VERSION=latest before_install: diff --git a/README.md b/README.md index 2f45592f..52a76a9a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://travis-ci.org/DavidWittman/ansible-redis.svg?branch=master)](https://travis-ci.org/DavidWittman/ansible-redis) [![Ansible Galaxy](https://img.shields.io/badge/galaxy-DavidWittman.redis-blue.svg?style=flat)](https://galaxy.ansible.com/davidwittman/redis) - - Ansible 2.4+ + - Ansible 2.5+ - Compatible with most versions of Ubuntu/Debian and RHEL/CentOS 6.x ## Contents @@ -10,7 +10,7 @@ 1. [Installation](#installation) 2. [Getting Started](#getting-started) 1. [Single Redis node](#single-redis-node) - 2. [Master-Slave Replication](#master-slave-replication) + 2. [Master-Replica Replication](#master-replica-replication) 3. [Redis Sentinel](#redis-sentinel) 3. [Advanced Options](#advanced-options) 1. [Verifying checksums](#verifying-checksums) @@ -51,18 +51,18 @@ $ ansible-playbook -i redis01.example.com, redis.yml That's it! You'll now have a Redis server listening on 127.0.0.1 on redis01.example.com. By default, the Redis binaries are installed under /opt/redis, though this can be overridden by setting the `redis_install_dir` variable. -### Master-Slave replication +### Master-Replica replication -Configuring [replication](http://redis.io/topics/replication) in Redis is accomplished by deploying multiple nodes, and setting the `redis_slaveof` variable on the slave nodes, just as you would in the redis.conf. In the example that follows, we'll deploy a Redis master with three slaves. +Configuring [replication](http://redis.io/topics/replication) in Redis is accomplished by deploying multiple nodes, and setting the `redis_replicaof` variable on the replica nodes, just as you would in the redis.conf. In the example that follows, we'll deploy a Redis master with three replicas. -In this example, we're going to use groups to separate the master and slave nodes. Let's start with the inventory file: +In this example, we're going to use groups to separate the master and replica nodes. Let's start with the inventory file: ``` ini [redis-master] redis-master.example.com -[redis-slave] -redis-slave0[1:3].example.com +[redis-replica] +redis-replica0[1:3].example.com ``` And here's the playbook: @@ -74,10 +74,10 @@ And here's the playbook: roles: - davidwittman.redis -- name: configure redis slaves - hosts: redis-slave +- name: configure redis replicas + hosts: redis-replica vars: - - redis_slaveof: redis-master.example.com 6379 + - redis_replicaof: redis-master.example.com 6379 roles: - davidwittman.redis ``` @@ -85,29 +85,29 @@ And here's the playbook: In this case, I'm assuming you have DNS records set up for redis-master.example.com, but that's not always the case. You can pretty much go crazy with whatever you need this to be set to. In many cases, I tell Ansible to use the eth1 IP address for the master. Here's a more flexible value for the sake of posterity: ``` yml -redis_slaveof: "{{ hostvars['redis-master.example.com'].ansible_eth1.ipv4.address }} {{ redis_port }}" +redis_replicaof: "{{ hostvars['redis-master.example.com'].ansible_eth1.ipv4.address }} {{ redis_port }}" ``` -Now you're cooking with gas! Running this playbook should have you ready to go with a Redis master and three slaves. +Now you're cooking with gas! Running this playbook should have you ready to go with a Redis master and three replicas. ### Redis Sentinel #### Introduction -Using Master-Slave replication is great for durability and distributing reads and writes, but not so much for high availability. If the master node fails, a slave must be manually promoted to master, and connections will need to be redirected to the new master. The solution for this problem is [Redis Sentinel](http://redis.io/topics/sentinel), a distributed system which uses Redis itself to communicate and handle automatic failover in a Redis cluster. +Using Master-Replica replication is great for durability and distributing reads and writes, but not so much for high availability. If the master node fails, a replica must be manually promoted to master, and connections will need to be redirected to the new master. The solution for this problem is [Redis Sentinel](http://redis.io/topics/sentinel), a distributed system which uses Redis itself to communicate and handle automatic failover in a Redis cluster. Sentinel itself uses the same redis-server binary that Redis uses, but runs with the `--sentinel` flag and with a different configuration file. All of this, of course, is abstracted with this Ansible role, but it's still good to know. #### Configuration -To add a Sentinel node to an existing deployment, assign this same `redis` role to it, and set the variable `redis_sentinel` to True on that particular host. This can be done in any number of ways, and for the purposes of this example I'll extend on the inventory file used above in the Master/Slave configuration: +To add a Sentinel node to an existing deployment, assign this same `redis` role to it, and set the variable `redis_sentinel` to True on that particular host. This can be done in any number of ways, and for the purposes of this example I'll extend on the inventory file used above in the Master/Replica configuration: ``` ini [redis-master] redis-master.example.com -[redis-slave] -redis-slave0[1:3].example.com +[redis-replica] +redis-replica0[1:3].example.com [redis-sentinel] redis-sentinel0[1:3].example.com redis_sentinel=True @@ -123,10 +123,10 @@ Now, all we need to do is set the `redis_sentinel_monitors` variable to define t roles: - davidwittman.redis -- name: configure redis slaves - hosts: redis-slave +- name: configure redis replicas + hosts: redis-replica vars: - - redis_slaveof: redis-master.example.com 6379 + - redis_replicaof: redis-master.example.com 6379 roles: - davidwittman.redis @@ -149,6 +149,37 @@ Along with the variables listed above, Sentinel has a number of its own configur Should you need to execute the role several times, have a look at `test/test_all.yml` to see how to proceed. See [here](https://github.com/DavidWittman/ansible-redis/issues/133) and [here](https://github.com/DavidWittman/ansible-redis/issues/193) for context. +## Config file management, version changes and server restarts + +As redis (server and sentinel) may rewrite their configuration files we cannot simply overwrite an updated file with our static +view of the redis deployment. E.g. in case of a fail-over/switch-over the `replicaof` property of the server and the `sentinel monitor` property +for the respective instance will change and these changes may not be overwritten during the next run of this playbook. + +This playbook gets current runtime values where it is needed and dynamically updates the facts in the inventory. The configuration files +are only created/updated via `lineinfile` such that we do not interfere with additional options that redis may write/update. + +You need to keep an eye on unwanted changes. E.g. if we would set `pidfile /var/run/redis-6379/redis_6379.pid` in `redis.conf`, then redis would rewrite it to `pidfile "/var/run/redis-6379/redis_6379.pid"` (i.e. redis adds quotes around the path). When running the playbook the next time +we would remove the quotes and the playbook would register a change in the config file. So you should make sure that you define properties the same way as redis will rewrite them. + +All servers and sentinels where a configuration file change occurs will be restarted. Only one server/sentinel will be restarted at a time and between and after the restart we will wait 30 seconds before task finishes and the next server/sentinel will be restarted. + +Restarts will also be scheduled when a new version of redis is installed. + +## Adding or removing nodes + +As per https://redis.io/topics/sentinel adding a new sentinel only requires starting the process. Should you want to add multiple sentinels, you should wait at least 30 seconds after each sentinel before adding more. + +This playbook does not provide any additional support or safeguards for this. If you want to add additional nodes, just add one node at a time to your inventory and run this playbook. Wait at least 30 seconds before adding another node. + +When you want to permanently remove a node you need to inform each sentinel about that change. These are the steps you need to do: + +1. You need to stop the redis server/sentinel on that node manually, i.e. this playbook does not support uninstallations. +2. Remove that node from your inventory (or at least configure it such that neither the server nor the sentinel will be installed) +3. Run this playbook as follows which will send a `sentinel reset '*'` to all remaining sentinels and wait 30 seconds between each sentinel: + +``` +ansible-playbook -i hosts --tags sentinel_reset redis.yml +``` ## Advanced Options @@ -193,11 +224,13 @@ Here is a list of all the default variables for this role, which are also availa ``` yml --- ## Installation options -redis_version: 2.8.24 +redis_version: 6.0.6 redis_install_dir: /opt/redis redis_dir: /var/lib/redis/{{ redis_port }} -redis_config_file_name: "{{ redis_port }}.conf" +redis_config_file_name: "redis_{{ redis_port }}.conf" redis_download_url: "http://download.redis.io/releases/redis-{{ redis_version }}.tar.gz" + +redis_protected_mode: "yes" # Set this to true to validate redis tarball checksum against vars/main.yml redis_verify_checksum: false # Set this value to a local path of a tarball to use for installation instead of downloading @@ -210,8 +243,10 @@ redis_group: "{{ redis_user }}" # The open file limit for Redis/Sentinel redis_nofile_limit: 16384 +redis_oom_score_adjust: 0 ## Role options +redis_server: true # Configure Redis as a service # This creates the init scripts for Redis and ensures the process is running # Also applies for Redis Sentinel @@ -222,12 +257,12 @@ redis_local_facts: true redis_service_name: "redis_{{ redis_port }}" ## Networking/connection options -redis_bind: 0.0.0.0 +redis_bind: false redis_port: 6379 redis_password: false -# Slave replication options -redis_min_slaves_to_write: 0 -redis_min_slaves_max_lag: 10 +# replication options +redis_min_replicas_to_write: 0 +redis_min_replicas_max_lag: 10 redis_tcp_backlog: 511 redis_tcp_keepalive: 0 # Max connected clients at a time @@ -239,11 +274,14 @@ redis_socket_path: false redis_socket_perm: 755 ## Replication options -# Set slaveof just as you would in redis.conf. (e.g. "redis01 6379") -redis_slaveof: false -# Make slaves read-only. "yes" or "no" -redis_slave_read_only: "yes" -redis_slave_priority: 100 +# Set replicaof just as you would in redis.conf. (e.g. "redis01 6379") +redis_replicaof: false +# Make replicas read-only. "yes" or "no" +redis_replica_read_only: "yes" +# default priority for promotion to master in case of master failure +# - lower value means higher priority for promotion +# - nodes with a priority of 0 will never be promoted to master +redis_replica_priority: 100 redis_repl_backlog_size: false ## Logging @@ -252,11 +290,12 @@ redis_logfile: '""' redis_syslog_enabled: "yes" redis_syslog_ident: "{{ redis_service_name }}" # Syslog facility. Must be USER or LOCAL0-LOCAL7 -redis_syslog_facility: USER +redis_syslog_facility: user ## General configuration redis_daemonize: "yes" -redis_pidfile: /var/run/redis/{{ redis_port }}.pid +# DO NOT CHANGE redis_pidfile, otherwise the systemd unit file will not work properly +redis_pidfile: /var/run/redis-{{ redis_port }}/redis_{{ redis_port }}.pid # Number of databases to allow redis_databases: 16 redis_loglevel: notice @@ -268,7 +307,13 @@ redis_slowlog_max_len: 128 redis_maxmemory: false redis_maxmemory_policy: noeviction redis_rename_commands: [] -redis_db_filename: dump.rdb + +# Lua script time limit +redis_lua_time_limit: 5000 + +# the file name for the RDB Backup +redis_db_filename: "dump.rdb" + # How frequently to snapshot the database to disk # e.g. "900 1" => 900 seconds if at least 1 key changed redis_save: @@ -286,23 +331,37 @@ redis_auto_aof_rewrite_percentage: "100" redis_auto_aof_rewrite_min_size: "64mb" redis_notify_keyspace_events: '""' +redis_client_output_buffer_limit_normal: 0 0 0 +redis_client_output_buffer_limit_replica: 256mb 64mb 60 +redis_client_output_buffer_limit_pubsub: 32mb 8mb 60 + +redis_hz: 10 + ## Additional configuration options # leave empty if not required. Use a block style scalar to add options, e.g. # redis_config_additional: | -# io-threads 4 -# io-threads-do-reads yes +# io-threads: 4 +# io-threads-do-reads: yes +# NOTE: you need to add a colon after the option name for config file modification via lineinfile to work redis_config_additional: "" ## Redis sentinel configs # Set this to true on a host to configure it as a Sentinel redis_sentinel: false +redis_sentinel_protected_mode: "yes" redis_sentinel_dir: /var/lib/redis/sentinel_{{ redis_sentinel_port }} +redis_sentinel_config_file_name: "sentinel_{{ redis_sentinel_port }}.conf" redis_sentinel_bind: 0.0.0.0 redis_sentinel_port: 26379 +redis_sentinel_announce_ip: false redis_sentinel_password: false -redis_sentinel_pidfile: /var/run/redis/sentinel_{{ redis_sentinel_port }}.pid +# DO NOT CHANGE redis_sentinel_pidfile, otherwise the systemd unit file will not work properly +redis_sentinel_pidfile: /var/run/redis-sentinel-{{ redis_sentinel_port }}/sentinel_{{ redis_sentinel_port }}.pid redis_sentinel_logfile: '""' redis_sentinel_syslog_ident: sentinel_{{ redis_sentinel_port }} +redis_sentinel_oom_score_adjust: 0 +# NOTE: do not set options to its default values otherwise sentinel will delete these settings when flushing the config +# which would result in Ansible changing the config again redis_sentinel_monitors: - name: master01 host: localhost @@ -310,8 +369,8 @@ redis_sentinel_monitors: quorum: 2 auth_pass: ant1r3z down_after_milliseconds: 30000 - parallel_syncs: 1 - failover_timeout: 180000 +# parallel_syncs: 1 +# failover_timeout: 180000 notification_script: false client_reconfig_script: false rename_commands: [] @@ -328,3 +387,8 @@ The following facts are accessible in your inventory or tasks outside of this ro - `{{ ansible_local.redis.sentinel_monitors }}` To disable these facts, set `redis_local_facts` to a false value. + +Note that `{{ ansible_local.redis.sentinel_monitors }}` gets only updated with live values +on nodes where Redis Sentinel is running. On nodes where only the server is running this fact +will always contain the static values from the inventory which might be stale after fail-overs and +switch-overs. diff --git a/defaults/main.yml b/defaults/main.yml index 6efc37ec..bc065af9 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,9 +1,9 @@ --- ## Installation options -redis_version: 2.8.24 +redis_version: 6.0.6 redis_install_dir: /opt/redis redis_dir: /var/lib/redis/{{ redis_port }} -redis_config_file_name: "{{ redis_port }}.conf" +redis_config_file_name: "redis_{{ redis_port }}.conf" redis_download_url: "http://download.redis.io/releases/redis-{{ redis_version }}.tar.gz" redis_protected_mode: "yes" @@ -22,6 +22,7 @@ redis_nofile_limit: 16384 redis_oom_score_adjust: 0 ## Role options +redis_server: true # Configure Redis as a service # This creates the init scripts for Redis and ensures the process is running # Also applies for Redis Sentinel @@ -35,9 +36,9 @@ redis_service_name: "redis_{{ redis_port }}" redis_bind: false redis_port: 6379 redis_password: false -# Slave replication options -redis_min_slaves_to_write: 0 -redis_min_slaves_max_lag: 10 +# replication options +redis_min_replicas_to_write: 0 +redis_min_replicas_max_lag: 10 redis_tcp_backlog: 511 redis_tcp_keepalive: 0 # Max connected clients at a time @@ -49,11 +50,14 @@ redis_socket_path: false redis_socket_perm: 755 ## Replication options -# Set slaveof just as you would in redis.conf. (e.g. "redis01 6379") -redis_slaveof: false -# Make slaves read-only. "yes" or "no" -redis_slave_read_only: "yes" -redis_slave_priority: 100 +# Set replicaof just as you would in redis.conf. (e.g. "redis01 6379") +redis_replicaof: false +# Make replicas read-only. "yes" or "no" +redis_replica_read_only: "yes" +# default priority for promotion to master in case of master failure +# - lower value means higher priority for promotion +# - nodes with a priority of 0 will never be promoted to master +redis_replica_priority: 100 redis_repl_backlog_size: false ## Logging @@ -62,11 +66,12 @@ redis_logfile: '""' redis_syslog_enabled: "yes" redis_syslog_ident: "{{ redis_service_name }}" # Syslog facility. Must be USER or LOCAL0-LOCAL7 -redis_syslog_facility: USER +redis_syslog_facility: user ## General configuration redis_daemonize: "yes" -redis_pidfile: /var/run/redis/{{ redis_port }}.pid +# DO NOT CHANGE redis_pidfile, otherwise the systemd unit file will not work properly +redis_pidfile: /var/run/redis-{{ redis_port }}/redis_{{ redis_port }}.pid # Number of databases to allow redis_databases: 16 redis_loglevel: notice @@ -103,7 +108,7 @@ redis_auto_aof_rewrite_min_size: "64mb" redis_notify_keyspace_events: '""' redis_client_output_buffer_limit_normal: 0 0 0 -redis_client_output_buffer_limit_slave: 256mb 64mb 60 +redis_client_output_buffer_limit_replica: 256mb 64mb 60 redis_client_output_buffer_limit_pubsub: 32mb 8mb 60 redis_hz: 10 @@ -111,8 +116,9 @@ redis_hz: 10 ## Additional configuration options # leave empty if not required. Use a block style scalar to add options, e.g. # redis_config_additional: | -# io-threads 4 -# io-threads-do-reads yes +# io-threads: 4 +# io-threads-do-reads: yes +# NOTE: you need to add a colon after the option name for config file modification via lineinfile to work redis_config_additional: "" ## Redis sentinel configs @@ -120,13 +126,18 @@ redis_config_additional: "" redis_sentinel: false redis_sentinel_protected_mode: "yes" redis_sentinel_dir: /var/lib/redis/sentinel_{{ redis_sentinel_port }} +redis_sentinel_config_file_name: "sentinel_{{ redis_sentinel_port }}.conf" redis_sentinel_bind: 0.0.0.0 redis_sentinel_port: 26379 +redis_sentinel_announce_ip: false redis_sentinel_password: false -redis_sentinel_pidfile: /var/run/redis/sentinel_{{ redis_sentinel_port }}.pid +# DO NOT CHANGE redis_sentinel_pidfile, otherwise the systemd unit file will not work properly +redis_sentinel_pidfile: /var/run/redis-sentinel-{{ redis_sentinel_port }}/sentinel_{{ redis_sentinel_port }}.pid redis_sentinel_logfile: '""' redis_sentinel_syslog_ident: sentinel_{{ redis_sentinel_port }} redis_sentinel_oom_score_adjust: 0 +# NOTE: do not set options to its default values otherwise sentinel will delete these settings when flushing the config +# which would result in Ansible changing the config again redis_sentinel_monitors: - name: master01 host: localhost @@ -134,8 +145,8 @@ redis_sentinel_monitors: quorum: 2 auth_pass: ant1r3z down_after_milliseconds: 30000 - parallel_syncs: 1 - failover_timeout: 180000 +# parallel_syncs: 1 +# failover_timeout: 180000 notification_script: false client_reconfig_script: false rename_commands: [] diff --git a/files/redis-parse-field-value-pairs-array.sh b/files/redis-parse-field-value-pairs-array.sh new file mode 100755 index 00000000..c3498f47 --- /dev/null +++ b/files/redis-parse-field-value-pairs-array.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# parses redis/sentinel output given as pairs of keys and values on separate lines and returns a json object +# usage example: redis-cli --raw -p 26379 sentinel master | ./parse_field_value_pairs_array.sh + +set -euo pipefail + +printf '{\n' +first=1 +while read -r key +do + read -r value + + if [ $first -eq 1 ]; then + first=0 + else + printf ',\n' + fi + + printf ' "%s": "%s"' "$key" "$(echo "$value")" +done +unset first + +printf '\n}\n' diff --git a/files/redis-parse-info.sh b/files/redis-parse-info.sh new file mode 100755 index 00000000..1e7ccb00 --- /dev/null +++ b/files/redis-parse-info.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -euo pipefail + +printf '{\n' +first=1 +tr -d '\r' | while IFS=':' read key value; do + case "$key" in + ""|\#*) continue ;; + esac + + if [ $first -eq 1 ]; then + first=0 + else + printf ',\n' + fi + + printf ' "%s": "%s"' "$key" "$(echo "$value")" +done +unset first + +printf '\n}\n' + diff --git a/handlers/main.yml b/handlers/main.yml deleted file mode 100644 index 7719b322..00000000 --- a/handlers/main.yml +++ /dev/null @@ -1,12 +0,0 @@ ---- -- name: "restart redis" - service: - name: "{{ redis_service_name }}" - state: restarted - when: redis_as_service - -- name: "restart sentinel" - service: - name: sentinel_{{ redis_sentinel_port }} - state: restarted - when: redis_as_service diff --git a/meta/main.yml b/meta/main.yml index f322ae86..881af901 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -2,7 +2,7 @@ galaxy_info: author: David Wittman description: Highly configurable role to install Redis and Redis Sentinel from source - min_ansible_version: 2.4.0 + min_ansible_version: 2.5.0 license: MIT platforms: - name: Ubuntu diff --git a/tasks/check_vars.yml b/tasks/check_vars.yml index b20e0409..6b376692 100644 --- a/tasks/check_vars.yml +++ b/tasks/check_vars.yml @@ -11,7 +11,7 @@ - name: check for checksum fail: msg: > - There is no sha1 checksum defined for version {{ redis_version }} in + There is no checksum defined for version {{ redis_version }} in vars/main.yml. Set redis_checksum manually or submit a PR to add this version. when: diff --git a/tasks/dependencies.yml b/tasks/dependencies.yml index cbe2e429..dff43a4d 100644 --- a/tasks/dependencies.yml +++ b/tasks/dependencies.yml @@ -1,23 +1,27 @@ --- - name: install debian dependencies apt: - pkg: + name: "{{ packages }}" + update_cache: yes + cache_valid_time: 86400 + state: present + vars: + packages: - gcc - make - libc6-dev # This should be `else omit`, but that doesn't quite work, so duplicate gcc - "{{ 'libc6-dev-i386' if redis_make_32bit|bool else 'gcc' }}" - update_cache: yes - cache_valid_time: 86400 - state: present when: ansible_os_family == "Debian" - name: install redhat dependencies yum: - name: - - gcc - - make + name: "{{ packages }}" state: present + vars: + packages: + - gcc + - make when: ansible_os_family == "RedHat" # Conditionally install the i686 build of libgcc if we are building 32-bit @@ -33,21 +37,25 @@ - name: install redhat 32-bit dependencies yum: - name: + name: "{{ packages }}" + state: latest + vars: + packages: - libgcc.i686 - glibc-devel.i686 - state: latest when: ansible_os_family == "RedHat" and redis_make_32bit|bool tags: - skip_ansible_lint - name: install suse dependencies zypper: - name: + name: "{{ packages }}" + state: present + vars: + packages: - gcc - make # These should be `else omit`, but that doesn't quite work, so duplicate gcc - "{{ 'gcc-32bit' if redis_make_32bit|bool else 'gcc' }}" - "{{ 'libgcc_s1-32bit' if redis_make_32bit|bool else 'gcc' }}" - state: present when: ansible_os_family == 'Suse' diff --git a/tasks/install.yml b/tasks/install.yml index 62ae34a8..4e5ea466 100644 --- a/tasks/install.yml +++ b/tasks/install.yml @@ -26,43 +26,23 @@ state: directory mode: 0755 -- name: check if redis user exists (ignore errors) - command: id {{ redis_user }} - ignore_errors: yes - changed_when: false - register: user_exists - -- name: add redis group - group: - name: "{{ redis_group }}" - state: present - when: user_exists is failed - -- name: add redis user - user: - name: "{{ redis_user }}" - group: "{{ redis_group }}" - comment: "Redis" - home: "{{ redis_install_dir }}" - shell: /bin/false - system: yes - when: user_exists is failed - - name: create /var/run/redis file: path: /var/run/redis state: directory owner: "{{ redis_user }}" -- name: install redis +- name: install redis (if not installed or if different version) shell: umask 0022 && make PREFIX={{ redis_install_dir }} install args: chdir: /usr/local/src/redis-{{ redis_version }} - creates: "{{ redis_install_dir }}/bin/redis-server" + when: not redis_is_installed or (redis_runtime_version is defined and redis_version != redis_runtime_version) - name: list redis binaries to add to alternatives - command: ls -1 {{ redis_install_dir }}/bin + shell: "[ -d {{ redis_install_dir }}/bin ] && ls -1 {{ redis_install_dir }}/bin" + check_mode: no register: redis_binaries + failed_when: redis_binaries.rc != 0 and not ansible_check_mode changed_when: false - name: add redis binaries to alternatives @@ -70,5 +50,4 @@ name: "{{ item }}" path: "{{ redis_install_dir }}/bin/{{ item }}" link: "/usr/bin/{{ item }}" - with_items: "{{ redis_binaries.stdout_lines }}" - when: redis_binaries is succeeded + loop: "{{ redis_binaries.stdout_lines }}" diff --git a/tasks/local_facts.yml b/tasks/local_facts.yml index 0020d283..a20c819e 100644 --- a/tasks/local_facts.yml +++ b/tasks/local_facts.yml @@ -7,3 +7,5 @@ template: src: etc/ansible/facts.d/redis.fact.j2 dest: /etc/ansible/facts.d/redis.fact + changed_when: false + diff --git a/tasks/main.yml b/tasks/main.yml index 77327f71..d37b8388 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,27 +1,38 @@ --- -- include: check_vars.yml -- include: download.yml +# tags: [never,sentinel_reset] cannot be used when this playbook is included in another playbook and tags +# are used to run only this playbook within that larger playbook, as this tag will then be copied to all +# imported tasks by Ansible and as such this include_task would always run +- include_tasks: sentinel_reset.yml + when: ('sentinel_reset' in ansible_run_tags) + +- include_tasks: check_vars.yml + +- include_tasks: pre_install.yml + +- include_tasks: runtime_facts.yml + +- include_tasks: download.yml tags: - download -- include: dependencies.yml +- include_tasks: dependencies.yml tags: - install -- include: install.yml +- include_tasks: install.yml tags: - install -- include: server.yml - when: not redis_sentinel +- include_tasks: server.yml + when: redis_server tags: - config -- include: sentinel.yml +- include_tasks: sentinel.yml when: redis_sentinel tags: - config -- include: local_facts.yml +- include_tasks: local_facts.yml when: redis_local_facts|bool diff --git a/tasks/pre_install.yml b/tasks/pre_install.yml new file mode 100644 index 00000000..1848dd4c --- /dev/null +++ b/tasks/pre_install.yml @@ -0,0 +1,21 @@ +--- +- name: Pre-install | add redis group + group: + name: "{{ redis_group }}" + state: present + +- name: Pre-install | add redis user + user: + name: "{{ redis_user }}" + group: "{{ redis_group }}" + comment: "Redis" + home: "{{ redis_install_dir }}" + shell: /bin/false + system: yes + state: present + +- name: Pre-install | Create bin directory if it does not exist + file: + path: "{{ redis_install_dir }}/bin" + state: directory + mode: '0755' diff --git a/tasks/runtime_facts.yml b/tasks/runtime_facts.yml new file mode 100644 index 00000000..3d9e9cc4 --- /dev/null +++ b/tasks/runtime_facts.yml @@ -0,0 +1,90 @@ +--- + +- name: Runtime facts - Copy helper scripts + copy: + src: "files/{{ item }}" + dest: "{{ redis_install_dir }}/bin/{{ item }}" + owner: "{{ redis_user }}" + group: "{{ redis_group }}" + mode: '0755' + loop: + - redis-parse-field-value-pairs-array.sh + - redis-parse-info.sh + +- name: Runtime facts - get installed version + shell: + executable: /bin/bash + cmd: "set -o pipefail && [ -x {{ redis_install_dir }}/bin/redis-cli ] && {{ redis_install_dir }}/bin/redis-cli --version | cut -d' ' -f2" + check_mode: no + changed_when: false + failed_when: false + register: redis_runtime_version_result + +- name: Runtime facts - set_fact redis_is_installed + set_fact: + redis_is_installed: "{{ true if (redis_runtime_version_result.rc == 0) else false }}" + +- name: Runtime facts - set_fact 'redis_runtime_version' + set_fact: + redis_runtime_version: "{{ redis_runtime_version_result.stdout }}" + when: redis_is_installed + +- name: Runtime facts - check if running + shell: + executable: /bin/bash + cmd: "set -o pipefail && {{ redis_install_dir }}/bin/redis-cli -p {{ redis_port }} {% if redis_password %}-a '{{ redis_password }}'{% endif %} ping | grep -i pong" + check_mode: no + changed_when: false + failed_when: false + register: redis_runtime_status_result + when: redis_is_installed + +- name: Runtime facts - set_fact 'redis_is_running' + set_fact: + redis_is_running: "{{ true if (redis_is_installed and redis_runtime_status_result.rc == 0) else false }}" + +- name: Runtime facts - (server) get replicaof + shell: + executable: /bin/bash + cmd: "set -o pipefail && {{ redis_install_dir }}/bin/redis-cli --raw -p {{ redis_port }} {% if redis_password %}-a '{{ redis_password }}'{% endif %} info replication | {{ redis_install_dir }}/bin/redis-parse-info.sh" + check_mode: no + changed_when: false + register: redis_runtime_replication_info_result + when: redis_server and redis_is_running + +- name: Runtime facts - (server) get json info output + set_fact: + redis_runtime_replication_info: "{{ redis_runtime_replication_info_result.stdout | from_json }}" + when: redis_server and redis_is_running and redis_runtime_replication_info_result is success + +- name: Runtime facts - (server) set_fact 'redis_replicaof' for replicas + set_fact: + redis_replicaof: "{{ redis_runtime_replication_info.master_host }} {{ redis_runtime_replication_info.master_port }}" + when: redis_server and redis_is_running and redis_runtime_replication_info_result is success and redis_runtime_replication_info.role|lower != "master" + +- name: Runtime facts - (server) unset fact 'redis_replicaof' for masters + set_fact: + redis_replicaof: + when: redis_server and redis_is_running and redis_runtime_replication_info_result is success and redis_runtime_replication_info.role|lower == "master" + +- name: Runtime facts - (sentinel) get replicaof + shell: + executable: /bin/bash + cmd: "set -o pipefail && {{ redis_install_dir }}/bin/redis-cli --raw -p {{ redis_sentinel_port }} {% if redis_password %}-a '{{ redis_password }}'{% endif %} sentinel master {{ item.name }} | {{ redis_install_dir }}/bin/redis-parse-field-value-pairs-array.sh" + check_mode: no + changed_when: false + register: redis_runtime_sentinel_monitor_result + loop: "{{ redis_sentinel_monitors }}" + when: redis_sentinel and redis_is_running + +- name: Runtime facts - (sentinel) set_fact 'redis_sentinel_runtime_monitors' + set_fact: + redis_sentinel_runtime_monitors: "{{ {} | combine( {item.name: item} ) }}" + loop: "{{ redis_runtime_sentinel_monitor_result.results | map(attribute='stdout') | map('from_json') | list }}" + when: redis_sentinel and redis_is_running + +- name: Runtime facts - (sentinel) update fact 'redis_sentinel_monitors' + set_fact: + redis_sentinel_monitors: "{{ [] + [ item | combine( { 'host': redis_sentinel_runtime_monitors[item.name].ip, 'port': redis_sentinel_runtime_monitors[item.name].port, 'quorum': redis_sentinel_runtime_monitors[item.name].quorum }) ] }}" + loop: "{{ redis_sentinel_monitors }}" + when: redis_sentinel and redis_is_running diff --git a/tasks/sentinel.yml b/tasks/sentinel.yml index dc389d57..2aafec63 100644 --- a/tasks/sentinel.yml +++ b/tasks/sentinel.yml @@ -1,12 +1,12 @@ --- -- name: create sentinel working directory +- name: Sentinel - create sentinel working directory file: path: "{{ redis_sentinel_dir }}" state: directory recurse: yes owner: "{{ redis_user }}" -- name: create sentinel init script +- name: Sentinel - create sentinel init script template: src: "{{ item }}" dest: /etc/init.d/sentinel_{{ redis_sentinel_port }} @@ -21,7 +21,7 @@ - ../templates when: redis_as_service and ansible_service_mgr|default() != "systemd" -- name: create sentinel systemd service +- name: Sentinel - create sentinel systemd service template: src: "{{ item }}" dest: /etc/systemd/system/sentinel_{{ redis_sentinel_port }}.service @@ -35,7 +35,7 @@ register: sentinel_unit_file when: redis_as_service and ansible_service_mgr|default() == "systemd" -- name: create systemd tmpfiles configuration +- name: Sentinel - create systemd tmpfiles configuration template: src: etc/tmpfiles.d/redis.conf.j2 dest: /etc/tmpfiles.d/redis.conf @@ -45,7 +45,7 @@ - ansible_service_mgr|default() == "systemd" - (redis_sentinel_pidfile|dirname).startswith("/var/run") or (redis_sentinel_pidfile|dirname).startswith("/run") -- name: reload systemd daemon +- name: Sentinel - reload systemd daemon systemd: daemon_reload: true when: @@ -53,21 +53,21 @@ - ansible_service_mgr|default() == "systemd" - sentinel_unit_file is changed -- name: set sentinel to start at boot +- name: Sentinel - set sentinel to start at boot service: name: sentinel_{{ redis_sentinel_port }} enabled: yes when: redis_as_service # Check then create log dir to prevent aggressively overwriting permissions -- name: check if sentinel log directory exists +- name: Sentinel - check if sentinel log directory exists stat: path: "{{ redis_sentinel_logfile|dirname }}" register: sentinel_logdir changed_when: false when: redis_sentinel_logfile != '""' -- name: create sentinel log directory if it does not exist +- name: Sentinel - create sentinel log directory if it does not exist file: state: directory path: "{{ redis_sentinel_logfile|dirname }}" @@ -77,22 +77,24 @@ - redis_sentinel_logfile != '""' - not sentinel_logdir.stat.exists -- name: touch the sentinel log file - file: - state: touch - path: "{{ redis_sentinel_logfile }}" +# Create an empty log file only if destination file does not exist +- name: Sentinel - create the sentinel log file + copy: + content: "" + dest: "{{ redis_sentinel_logfile }}" + force: no owner: "{{ redis_user }}" group: "{{ redis_group }}" when: redis_sentinel_logfile != '""' -- name: check if sentinel pid directory exists +- name: Sentinel - check if sentinel pid directory exists stat: path: "{{ redis_sentinel_pidfile|dirname }}" register: sentinel_piddir changed_when: false - when: redis_sentinel_pidfile != '""' + when: redis_sentinel_pidfile != '""' and ansible_service_mgr|default() != "systemd" -- name: create sentinel pid directory if it does not exist +- name: Sentinel - create sentinel pid directory if it does not exist file: state: directory path: "{{ redis_sentinel_pidfile|dirname }}" @@ -100,37 +102,51 @@ group: "{{ redis_group }}" when: - redis_sentinel_pidfile != '""' + - ansible_service_mgr|default() != "systemd" - not sentinel_piddir.stat.exists -- name: create sentinel config file - template: - src: redis_sentinel.conf.j2 - dest: /etc/redis/sentinel_{{ redis_sentinel_port }}.conf +- name: Sentinel - create or update config file + lineinfile: + path: /etc/redis/{{ redis_sentinel_config_file_name }} + create: yes + regexp: "^{{ item.split(':')[0].strip() | regex_escape() }}$" + line: "{{ item.split(':')[0].strip() }} {{ item.split(':')[1].strip() }}" owner: "{{ redis_user }}" - mode: 0640 - notify: "restart sentinel" + group: "{{ redis_group }}" + mode: '0640' + loop: "{{ lookup('template', './templates/redis_sentinel.conf.j2').splitlines() | reject('match', '^$') | reject('match', '^#.*') | list }}" + loop_control: + label: "write line: {{ item }}" + register: redis_sentinel_conf_file -- name: add sentinel init config file +- name: Sentinel - add sentinel init config file template: dest: /etc/sysconfig/sentinel_{{ redis_sentinel_port }} src: redis.init.conf.j2 when: ansible_os_family == "RedHat" - notify: "restart sentinel" + register: redis_sentinel_init_config -- name: add sentinel init config file +- name: Sentinel - add sentinel init config file template: dest: /etc/default/sentinel_{{ redis_sentinel_port }} src: redis.init.conf.j2 when: ansible_os_family == "Debian" - notify: "restart sentinel" + register: redis_sentinel_init_config -# Flush handlers before ensuring the service is started to prevent -# a start and then restart -- name: flush handlers to apply config changes - meta: flush_handlers - -- name: ensure sentinel is running +- name: Sentinel - ensure sentinel is running service: name: sentinel_{{ redis_sentinel_port }} state: started when: redis_as_service + +- name: Sentinel - rolling restart when already running and config or version changed + shell: + executable: /bin/bash + cmd: "set -o pipefail && systemctl restart sentinel_{{ redis_sentinel_port }} && sleep 30 && {{ redis_install_dir }}/bin/redis-cli -p {{ redis_sentinel_port }} {% if redis_password %}-a '{{ redis_password }}'{% endif %} ping" + throttle: 1 + when: > + redis_is_running and ( + redis_sentinel_conf_file is changed or + redis_sentinel_init_config is changed or + (redis_runtime_version is defined and redis_version != redis_runtime_version) + ) diff --git a/tasks/sentinel_reset.yml b/tasks/sentinel_reset.yml new file mode 100644 index 00000000..aa40c4b5 --- /dev/null +++ b/tasks/sentinel_reset.yml @@ -0,0 +1,6 @@ +--- +- name: Sentinel reset - send 'sentinel reset *' to all sentinels waiting 30 seconds after each sentinel + shell: + executable: /bin/bash + cmd: "set -o pipefail && {{ redis_install_dir }}/bin/redis-cli -p {{ redis_sentinel_port }} {% if redis_password %}-a '{{ redis_password }}'{% endif %} sentinel reset '*' && sleep 30" + throttle: 1 diff --git a/tasks/server.yml b/tasks/server.yml index 7befe8a9..f4218089 100644 --- a/tasks/server.yml +++ b/tasks/server.yml @@ -1,12 +1,12 @@ --- -- name: create redis working directory +- name: Server - create redis working directory file: path: "{{ redis_dir }}" state: directory recurse: yes owner: "{{ redis_user }}" -- name: create redis init script +- name: Server - create redis init script template: src: "{{ item }}" dest: /etc/init.d/{{ redis_service_name }} @@ -21,7 +21,7 @@ - ../templates when: redis_as_service and ansible_service_mgr|default() != "systemd" -- name: create redis systemd service +- name: Server - create redis systemd service template: src: "{{ item }}" dest: /etc/systemd/system/{{ redis_service_name }}.service @@ -35,7 +35,7 @@ register: redis_unit_file when: redis_as_service and ansible_service_mgr|default() == "systemd" -- name: create systemd tmpfiles configuration +- name: Server - create systemd tmpfiles configuration template: src: etc/tmpfiles.d/redis.conf.j2 dest: /etc/tmpfiles.d/redis.conf @@ -43,9 +43,8 @@ when: - redis_as_service - ansible_service_mgr|default() == 'systemd' - - (redis_pidfile|dirname).startswith('/var/run') or (redis_pidfile|dirname).startswith('/run') -- name: reload systemd daemon +- name: Server - reload systemd daemon systemd: daemon_reload: true when: @@ -53,21 +52,21 @@ - ansible_service_mgr|default() == "systemd" - redis_unit_file is changed -- name: set redis to start at boot +- name: Server - set redis to start at boot service: name: "{{ redis_service_name }}" enabled: yes when: redis_as_service # Check then create log dir to prevent aggressively overwriting permissions -- name: check if log directory exists +- name: Server - check if log directory exists stat: path: "{{ redis_logfile|dirname }}" register: logdir changed_when: false when: redis_logfile != '""' -- name: create log directory if it does not exist +- name: Server - create log directory if it does not exist file: state: directory path: "{{ redis_logfile|dirname }}" @@ -77,31 +76,24 @@ - redis_logfile != '""' - not logdir.stat.exists -- name: create log file if it does not exist +# Create an empty log file only if destination file does not exist +- name: Server - create the log file copy: - dest: "{{ redis_logfile }}" content: "" - force: false # Don't override file contet if the file already exits - owner: "{{ redis_user }}" - group: "{{ redis_group }}" - when: redis_logfile != '""' - -- name: update permissions of log file if needed - file: - state: file - path: "{{ redis_logfile }}" + dest: "{{ redis_logfile }}" + force: no owner: "{{ redis_user }}" group: "{{ redis_group }}" when: redis_logfile != '""' -- name: check if pid directory exists +- name: Server - check if pid directory exists stat: path: "{{ redis_pidfile|dirname }}" register: piddir changed_when: false - when: redis_pidfile != '""' + when: redis_pidfile != '""' and ansible_service_mgr|default() != "systemd" -- name: create pid directory if it does not exist +- name: Server - create pid directory if it does not exist file: state: directory path: "{{ redis_pidfile|dirname }}" @@ -109,39 +101,53 @@ group: "{{ redis_group }}" when: - redis_pidfile != '""' + - ansible_service_mgr|default() != "systemd" - not piddir.stat.exists -- name: create redis config file - template: - src: redis.conf.j2 - dest: /etc/redis/{{ redis_config_file_name }} +- name: Server - create or update config file + lineinfile: + path: /etc/redis/{{ redis_config_file_name }} + create: yes + regexp: "^{{ item.split(':')[0].strip() | regex_escape() }}$" + line: "{{ item.split(':')[0].strip() }} {{ item.split(':')[1].strip() }}" owner: "{{ redis_user }}" - mode: 0640 - notify: "restart redis" + group: "{{ redis_group }}" + mode: '0640' + loop: "{{ lookup('template', './templates/redis.conf.j2').splitlines() | reject('match', '^$') | reject('match', '^#.*') | list }}" + loop_control: + label: "write line: {{ item }}" + register: redis_conf_file -- name: add redis init config file +- name: Server - add redis init config file (RedHat) template: dest: /etc/sysconfig/{{ redis_service_name }} src: redis.init.conf.j2 mode: 0600 when: ansible_os_family == "RedHat" - notify: "restart redis" + register: redis_init_config -- name: add redis init config file +- name: Server - add redis init config file (Debian) template: dest: /etc/default/{{ redis_service_name }} src: redis.init.conf.j2 mode: 0600 when: ansible_os_family == "Debian" - notify: "restart redis" + register: redis_init_config -# Flush handlers before ensuring the service is started to prevent -# a start and then restart -- name: flush handlers to apply config changes - meta: flush_handlers - -- name: ensure redis is running +- name: Server - ensure redis is running service: name: "{{ redis_service_name }}" state: started when: redis_as_service + +- name: Server - rolling restart when already running and config or version changed + shell: + executable: /bin/bash + cmd: "set -o pipefail && systemctl restart {{ redis_service_name }} && sleep 30 && {{ redis_install_dir }}/bin/redis-cli -p {{ redis_port }} {% if redis_password %}-a '{{ redis_password }}'{% endif %} ping" + throttle: 1 + when: > + redis_is_running and ( + redis_conf_file is changed or + redis_init_config is changed or + (redis_runtime_version is defined and redis_version != redis_runtime_version) + ) diff --git a/templates/default/redis.service.j2 b/templates/default/redis.service.j2 index 4bfb7b81..f66c329c 100644 --- a/templates/default/redis.service.j2 +++ b/templates/default/redis.service.j2 @@ -6,12 +6,14 @@ Documentation=http://redis.io/documentation, man:redis-server(1) [Service] Type={{ 'forking' if redis_daemonize == 'yes' else 'simple' }} ExecStart={{ redis_install_dir }}/bin/redis-server /etc/redis/{{ redis_config_file_name }} -EnvironmentFile=-/etc/default/{{ redis_service_name }} -PIDFile={{ redis_pidfile }} +#EnvironmentFile=-/etc/default/{{ redis_service_name }} +PIDFile=/run/redis-{{ redis_port }}/redis_{{ redis_port }}.pid TimeoutStopSec=0 Restart=always User={{ redis_user }} Group={{ redis_group }} +RuntimeDirectory=redis-{{ redis_port }} +RuntimeDirectoryMode=2755 {% if redis_oom_score_adjust != 0 %} OOMScoreAdjust={{ redis_oom_score_adjust }} @@ -23,15 +25,25 @@ LimitNOFILE={{ redis_nofile_limit }} PrivateDevices=yes ProtectHome=yes ReadOnlyDirectories=/ -ReadWriteDirectories=-{{ redis_dir }} +ReadWritePaths=-{{ redis_dir }} +ReadWritePaths=-/var/run/redis-{{ redis_port }} {% if redis_logfile != '""' %} -ReadWriteDirectories=-{{ redis_logfile|dirname }} +ReadWritePaths=-{{ redis_logfile|dirname }} {% endif %} -ReadWriteDirectories=-{{ redis_pidfile|dirname }} -CapabilityBoundingSet=~CAP_SYS_PTRACE -# redis-server writes its own config file when in cluster mode so we allow -# writing there (NB. ProtectSystem=true over ProtectSystem=full) +NoNewPrivileges=true +CapabilityBoundingSet=CAP_SETGID CAP_SETUID CAP_SYS_RESOURCE +MemoryDenyWriteExecute=true +ProtectKernelModules=true +ProtectKernelTunables=true +ProtectControlGroups=true +RestrictRealtime=true +RestrictNamespaces=true +RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX + +# redis-server can write to its own config file when in cluster mode so we +# permit writing there by default. If you are not using this feature, it is +# recommended that you replace the following lines with "ProtectSystem=full". ProtectSystem=true ReadWriteDirectories=-/etc/redis diff --git a/templates/default/redis_sentinel.service.j2 b/templates/default/redis_sentinel.service.j2 index a8593d52..c735430d 100644 --- a/templates/default/redis_sentinel.service.j2 +++ b/templates/default/redis_sentinel.service.j2 @@ -5,13 +5,15 @@ Documentation=http://redis.io/documentation, man:redis-sentinel(1) [Service] Type={{ 'forking' if redis_daemonize == 'yes' else 'simple' }} -ExecStart={{ redis_install_dir }}/bin/redis-server /etc/redis/sentinel_{{ redis_sentinel_port }}.conf --sentinel +ExecStart={{ redis_install_dir }}/bin/redis-server /etc/redis/{{ redis_sentinel_config_file_name }} --sentinel EnvironmentFile=-/etc/default/sentinel_{{ redis_sentinel_port }} -PIDFile={{ redis_sentinel_pidfile }} +PIDFile=/run/redis-sentinel-{{ redis_sentinel_port }}/sentinel_{{ redis_sentinel_port }}.pid TimeoutStopSec=0 Restart=always User={{ redis_user }} Group={{ redis_group }} +RuntimeDirectory=redis-sentinel-{{ redis_sentinel_port }} +RuntimeDirectoryMode=2755 {% if redis_sentinel_oom_score_adjust != 0 %} OOMScoreAdjust={{ redis_sentinel_oom_score_adjust }} @@ -23,15 +25,25 @@ LimitNOFILE={{ redis_nofile_limit }} PrivateDevices=yes ProtectHome=yes ReadOnlyDirectories=/ -ReadWriteDirectories=-{{ redis_sentinel_dir }} +ReadWritePaths=-{{ redis_sentinel_dir }} +ReadWritePaths=-/var/run/redis-sentinel-{{ redis_sentinel_port }} {% if redis_sentinel_logfile != '""' %} -ReadWriteDirectories=-{{ redis_sentinel_logfile|dirname }} +ReadWritePaths=-{{ redis_sentinel_logfile|dirname }} {% endif %} -ReadWriteDirectories=-{{ redis_sentinel_pidfile|dirname }} -CapabilityBoundingSet=~CAP_SYS_PTRACE -# redis-sentinel writes its own config file so we allow writing there (NB. -# ProtectSystem=true over ProtectSystem=full) +NoNewPrivileges=true +CapabilityBoundingSet=CAP_SETGID CAP_SETUID CAP_SYS_RESOURCE +MemoryDenyWriteExecute=true +ProtectKernelModules=true +ProtectKernelTunables=true +ProtectControlGroups=true +RestrictRealtime=true +RestrictNamespaces=true +RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX + +# redis-server can write to its own config file when in cluster mode so we +# permit writing there by default. If you are not using this feature, it is +# recommended that you replace the following lines with "ProtectSystem=full". ProtectSystem=true ReadWriteDirectories=-/etc/redis diff --git a/templates/redis.conf.j2 b/templates/redis.conf.j2 index 04e24a4f..223e688b 100644 --- a/templates/redis.conf.j2 +++ b/templates/redis.conf.j2 @@ -1,105 +1,106 @@ {{ ansible_managed | comment }} # General -daemonize {{ redis_daemonize }} -protected-mode {{ redis_protected_mode }} -pidfile {{ redis_pidfile }} -dir {{ redis_dir }} -port {{ redis_port }} +daemonize: {{ redis_daemonize }} +supervised: no +protected-mode: {{ redis_protected_mode }} +pidfile: "{{ redis_pidfile }}" +dir: "{{ redis_dir }}" +port: {{ redis_port }} {% if redis_bind -%} -bind {{ redis_bind }} +bind: {{ redis_bind }} {% endif -%} {% if redis_socket_path -%} -unixsocket {{ redis_socket_path }} -unixsocketperm {{ redis_socket_perm }} +unixsocket: {{ redis_socket_path }} +unixsocketperm: {{ redis_socket_perm }} {% endif -%} -timeout {{ redis_timeout }} -tcp-keepalive {{ redis_tcp_keepalive }} -tcp-backlog {{ redis_tcp_backlog }} -loglevel {{ redis_loglevel }} -logfile {{ redis_logfile }} -syslog-enabled {{ redis_syslog_enabled }} -syslog-ident {{ redis_syslog_ident }} -syslog-facility {{ redis_syslog_facility }} -databases {{ redis_databases }} - +timeout: {{ redis_timeout }} +tcp-keepalive: {{ redis_tcp_keepalive }} +tcp-backlog: {{ redis_tcp_backlog }} +loglevel: {{ redis_loglevel }} +logfile: {{ redis_logfile }} +syslog-enabled: {{ redis_syslog_enabled }} +syslog-ident: "{{ redis_syslog_ident }}" +syslog-facility: {{ redis_syslog_facility }} +databases: {{ redis_databases }} # Snapshotting {% for save in redis_save -%} -save {{ save }} +save: {{ save }} {% endfor -%} -stop-writes-on-bgsave-error {{ redis_stop_writes_on_bgsave_error|string }} -rdbcompression {{ redis_rdbcompression|string }} -rdbchecksum {{ redis_rdbchecksum|string }} -dbfilename {{ redis_db_filename|string }} +stop-writes-on-bgsave-error: {{ redis_stop_writes_on_bgsave_error|string }} +rdbcompression: {{ redis_rdbcompression|string }} +rdbchecksum: {{ redis_rdbchecksum|string }} +dbfilename: "{{ redis_db_filename|string }}" # Replication -{% if redis_slaveof -%} -slaveof {{ redis_slaveof }} +{% if redis_replicaof -%} +replicaof: {{ redis_replicaof }} {% endif -%} -slave-serve-stale-data yes -slave-read-only {{ redis_slave_read_only }} -repl-disable-tcp-nodelay no +replica-serve-stale-data: yes +replica-read-only: {{ redis_replica_read_only }} +repl-disable-tcp-nodelay: no {% if redis_repl_backlog_size -%} -repl-backlog-size {{ redis_repl_backlog_size }} +repl-backlog-size: {{ redis_repl_backlog_size }} {% endif -%} -slave-priority {{ redis_slave_priority }} -{% if redis_min_slaves_to_write -%} -min-slaves-to-write {{ redis_min_slaves_to_write }} +# priority for promotion to master (lower value equals higher priority; if 0 node will never be promoted to master) +replica-priority: {{ redis_replica_priority }} +{% if redis_min_replicas_to_write -%} +min-replicas-to-write: {{ redis_min_replicas_to_write }} {% endif -%} -{% if redis_min_slaves_max_lag -%} -min-slaves-max-lag {{ redis_min_slaves_max_lag }} +{% if redis_min_replicas_max_lag -%} +min-replicas-max-lag: {{ redis_min_replicas_max_lag }} {% endif -%} {% if redis_password -%} -masterauth {{ redis_password }} +masterauth: {{ redis_password }} {% endif -%} # Security {% if redis_password -%} -requirepass {{ redis_password }} +requirepass: {{ redis_password }} {% endif -%} {% for command in redis_rename_commands -%} -rename-command {{ command }} +rename-command: {{ command }} {% endfor -%} # Limits -maxclients {{ redis_maxclients }} +maxclients: {{ redis_maxclients }} {% if redis_maxmemory -%} -maxmemory {{ redis_maxmemory }} +maxmemory: {{ redis_maxmemory }} {% endif -%} -maxmemory-policy {{ redis_maxmemory_policy }} +maxmemory-policy: {{ redis_maxmemory_policy }} # Append Only Mode -appendonly {{ redis_appendonly }} -appendfilename "{{ redis_appendfilename }}" -appendfsync {{ redis_appendfsync|string }} -no-appendfsync-on-rewrite {{ redis_no_appendfsync_on_rewrite }} -auto-aof-rewrite-percentage {{ redis_auto_aof_rewrite_percentage }} -auto-aof-rewrite-min-size {{ redis_auto_aof_rewrite_min_size }} +appendonly: {{ redis_appendonly }} +appendfilename: "{{ redis_appendfilename }}" +appendfsync: {{ redis_appendfsync|string }} +no-appendfsync-on-rewrite: {{ redis_no_appendfsync_on_rewrite }} +auto-aof-rewrite-percentage: {{ redis_auto_aof_rewrite_percentage }} +auto-aof-rewrite-min-size: {{ redis_auto_aof_rewrite_min_size }} # Lua -lua-time-limit {{ redis_lua_time_limit }} +lua-time-limit: {{ redis_lua_time_limit }} # Slow Log -slowlog-log-slower-than {{ redis_slowlog_log_slower_than }} -slowlog-max-len {{ redis_slowlog_max_len }} +slowlog-log-slower-than: {{ redis_slowlog_log_slower_than }} +slowlog-max-len: {{ redis_slowlog_max_len }} # Event Notification -notify-keyspace-events {{ redis_notify_keyspace_events }} +notify-keyspace-events: {{ redis_notify_keyspace_events }} # Advanced -hash-max-ziplist-entries 512 -hash-max-ziplist-value 64 -list-max-ziplist-entries 512 -list-max-ziplist-value 64 -set-max-intset-entries 512 -zset-max-ziplist-entries 128 -zset-max-ziplist-value 64 -activerehashing yes -client-output-buffer-limit normal {{ redis_client_output_buffer_limit_normal }} -client-output-buffer-limit slave {{ redis_client_output_buffer_limit_slave }} -client-output-buffer-limit pubsub {{ redis_client_output_buffer_limit_pubsub }} -hz {{ redis_hz }} -aof-rewrite-incremental-fsync yes +hash-max-ziplist-entries: 512 +hash-max-ziplist-value: 64 +list-max-ziplist-entries: 512 +list-max-ziplist-value: 64 +set-max-intset-entries: 512 +zset-max-ziplist-entries: 128 +zset-max-ziplist-value: 64 +activerehashing: yes +client-output-buffer-limit normal: {{ redis_client_output_buffer_limit_normal }} +client-output-buffer-limit replica: {{ redis_client_output_buffer_limit_replica }} +client-output-buffer-limit pubsub: {{ redis_client_output_buffer_limit_pubsub }} +hz: {{ redis_hz }} +aof-rewrite-incremental-fsync: yes {% if redis_config_additional|length -%} # Additional diff --git a/templates/redis_sentinel.conf.j2 b/templates/redis_sentinel.conf.j2 index 2fc59390..56cec0a8 100644 --- a/templates/redis_sentinel.conf.j2 +++ b/templates/redis_sentinel.conf.j2 @@ -1,33 +1,36 @@ # redis-sentinel {{ redis_version }} configuration file # sentinel_{{ redis_sentinel_port }}.conf -daemonize {{ redis_daemonize }} -protected-mode {{ redis_sentinel_protected_mode }} -dir {{ redis_sentinel_dir }} -pidfile {{ redis_sentinel_pidfile }} -port {{ redis_sentinel_port }} -bind {{ redis_sentinel_bind }} +daemonize: {{ redis_daemonize }} +protected-mode: {{ redis_sentinel_protected_mode }} +dir: "{{ redis_sentinel_dir }}" +pidfile: "{{ redis_sentinel_pidfile }}" +port: {{ redis_sentinel_port }} +bind: {{ redis_sentinel_bind }} +{% if redis_sentinel_announce_ip|default -%} +sentinel announce-ip: "{{ ansible_default_ipv4.address }}" +{% endif %} # Security {% if redis_sentinel_password %} -requirepass {{ redis_sentinel_password }} +requirepass: {{ redis_sentinel_password }} {% endif %} {% for master in redis_sentinel_monitors -%} -sentinel monitor {{ master.name }} {{ master.host }} {{ master.port }} {{ master.quorum|d('2') }} +sentinel monitor {{ master.name }}: {{ master.host }} {{ master.port }} {{ master.quorum|d('2') }} {% for option in ('auth_pass', 'down_after_milliseconds', 'parallel_syncs', 'failover_timeout', 'notification_script', 'client_reconfig_script') -%} {% if master[option] is defined and master[option] -%} -sentinel {{ option|replace('_', '-') }} {{ master.name }} {{ master[option] }} +sentinel {{ option|replace('_', '-') }} {{ master.name }}: {{ master[option] }} {% endif %} {% endfor -%} {% if master['rename_commands'] is defined -%} {% for command in master['rename_commands'] -%} -sentinel rename-command {{ master.name }} {{ command }} +sentinel rename-command {{ master.name }}: {{ command }} {% endfor -%} {% endif -%} {% endfor -%} -logfile {{ redis_sentinel_logfile }} -syslog-enabled {{ redis_syslog_enabled }} -syslog-ident {{ redis_sentinel_syslog_ident }} -syslog-facility {{ redis_syslog_facility }} +logfile: {{ redis_sentinel_logfile }} +syslog-enabled: {{ redis_syslog_enabled }} +syslog-ident: "{{ redis_sentinel_syslog_ident }}" +syslog-facility: {{ redis_syslog_facility }} diff --git a/test/test_all.yml b/test/test_all.yml index f032959c..cc1cf2ed 100644 --- a/test/test_all.yml +++ b/test/test_all.yml @@ -20,8 +20,7 @@ name: ../../ansible-redis vars: redis_port: 8379 - redis_slaveof: 127.0.0.1 7379 - redis_local_facts: false + redis_replicaof: 127.0.0.1 7379 - meta: flush_handlers @@ -36,4 +35,3 @@ port: 7379 quorum: 1 auth_pass: "{{ redis_password }}" - redis_local_facts: false