From a0bed6c97c7eed7db1bdb1e792559ca8e189e20d Mon Sep 17 00:00:00 2001 From: Radamanthus Batnag Date: Fri, 19 Jan 2018 16:31:58 +0800 Subject: [PATCH] Adds the Redis Sentinel recipe --- cookbooks/redis-sentinel/README.md | 7 + .../redis-sentinel/attributes/default.rb | 19 ++ .../libraries/is_redis_sentinel_instance.rb | 23 ++ .../libraries/redis_sentinel_instances.rb | 33 +++ cookbooks/redis-sentinel/metadata.rb | 10 + cookbooks/redis-sentinel/recipes/configure.rb | 60 ++++++ cookbooks/redis-sentinel/recipes/default.rb | 6 + cookbooks/redis-sentinel/recipes/install.rb | 81 +++++++ .../recipes/install_from_package.rb | 13 ++ .../recipes/install_from_source.rb | 21 ++ .../templates/default/redis-sentinel.conf.erb | 200 ++++++++++++++++++ .../templates/default/redis-sentinel.erb | 2 + .../default/redis-sentinel.monitrc.erb | 5 + .../templates/default/redis-sentinel.yml.erb | 5 + .../templates/default/redis.yml.erb | 7 + custom-cookbooks/redis-sentinel/README.md | 6 + .../cookbooks/custom-redis-sentinel/README.md | 89 ++++++++ .../attributes/default.rb | 19 ++ .../custom-redis-sentinel/metadata.rb | 10 + .../custom-redis-sentinel/recipes/default.rb | 1 + .../cookbooks/ey-custom/metadata.rb | 3 + .../cookbooks/ey-custom/recipes/after-main.rb | 1 + 22 files changed, 621 insertions(+) create mode 100644 cookbooks/redis-sentinel/README.md create mode 100644 cookbooks/redis-sentinel/attributes/default.rb create mode 100644 cookbooks/redis-sentinel/libraries/is_redis_sentinel_instance.rb create mode 100644 cookbooks/redis-sentinel/libraries/redis_sentinel_instances.rb create mode 100644 cookbooks/redis-sentinel/metadata.rb create mode 100644 cookbooks/redis-sentinel/recipes/configure.rb create mode 100644 cookbooks/redis-sentinel/recipes/default.rb create mode 100644 cookbooks/redis-sentinel/recipes/install.rb create mode 100644 cookbooks/redis-sentinel/recipes/install_from_package.rb create mode 100644 cookbooks/redis-sentinel/recipes/install_from_source.rb create mode 100644 cookbooks/redis-sentinel/templates/default/redis-sentinel.conf.erb create mode 100644 cookbooks/redis-sentinel/templates/default/redis-sentinel.erb create mode 100644 cookbooks/redis-sentinel/templates/default/redis-sentinel.monitrc.erb create mode 100644 cookbooks/redis-sentinel/templates/default/redis-sentinel.yml.erb create mode 100644 cookbooks/redis-sentinel/templates/default/redis.yml.erb create mode 100644 custom-cookbooks/redis-sentinel/README.md create mode 100644 custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/README.md create mode 100644 custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/attributes/default.rb create mode 100644 custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/metadata.rb create mode 100644 custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/recipes/default.rb create mode 100644 custom-cookbooks/redis-sentinel/cookbooks/ey-custom/metadata.rb create mode 100644 custom-cookbooks/redis-sentinel/cookbooks/ey-custom/recipes/after-main.rb diff --git a/cookbooks/redis-sentinel/README.md b/cookbooks/redis-sentinel/README.md new file mode 100644 index 00000000..5915ae59 --- /dev/null +++ b/cookbooks/redis-sentinel/README.md @@ -0,0 +1,7 @@ +# Redis Sentinel + +This recipe is used to run on Redis Sentinel the stable-v5 stack. + +The `redis-sentinel` recipe is managed by Engine Yard. You should not copy this recipe to your repository but instead copy custom-redis-sentinel. Please check the [custom-redis-sentinel readme](../../custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/README.md) for the complete instructions. + +We accept contributions for changes that can be used by all customers. diff --git a/cookbooks/redis-sentinel/attributes/default.rb b/cookbooks/redis-sentinel/attributes/default.rb new file mode 100644 index 00000000..7741ca44 --- /dev/null +++ b/cookbooks/redis-sentinel/attributes/default.rb @@ -0,0 +1,19 @@ +default['redis-sentinel'].tap do |sentinel| + sentinel['port'] = '26379' + + # Install redis-sentinel on all app instances + # sentinel['install_type'] = 'ALL_APP_INSTANCES' + # Install redis-sentinel on all app and util instances + # sentinel['install_type'] = 'ALL_APP_AND_UTIL_INSTANCES' + + # Install redis-sentinel on utility instances named 'sidekiq' + #sentinel['utility_name'] = 'sidekiq' + #sentinel['install_type'] = 'NAMED_UTILS' + + # Install redis-sentinel on all app instances, plus utility instances named 'sidekiq' + sentinel['utility_name'] = 'sidekiq' + sentinel['install_type'] = 'ALL_APP_AND_NAMED_UTIL_INSTANCES' + + # Timeout + sentinel['timeout'] = 300_000 +end diff --git a/cookbooks/redis-sentinel/libraries/is_redis_sentinel_instance.rb b/cookbooks/redis-sentinel/libraries/is_redis_sentinel_instance.rb new file mode 100644 index 00000000..e831a2f0 --- /dev/null +++ b/cookbooks/redis-sentinel/libraries/is_redis_sentinel_instance.rb @@ -0,0 +1,23 @@ +class Chef::Recipe + # Return true if the we should install redis-sentinel on the current instance + # Based on the settings in redis-sentinel/attributes/default.rb + def is_redis_sentinel_instance(node_name, node_instance_role) + case node['redis-sentinel']['install_type'] + when 'ALL_APP_INSTANCES' + %w(solo app_master app).include?(node_instance_role) + when 'ALL_APP_AND_UTIL_INSTANCES' + %w(solo app_master app util).include?(node_instance_role) + when 'ALL_APP_AND_NAMED_UTIL_INSTANCES' + %w(solo app_master app).include?(node_instance_role) || + ( + (node_instance_role == 'util') && + (/^#{node['redis-sentinel']['utility_name']}/.match(node_name)) + ) + when 'NAMED_UTILS' + (node_instance_role == 'util') && + (/^#{node['redis-sentinel']['utility_name']}/.match(node_name)) + else + false + end + end +end diff --git a/cookbooks/redis-sentinel/libraries/redis_sentinel_instances.rb b/cookbooks/redis-sentinel/libraries/redis_sentinel_instances.rb new file mode 100644 index 00000000..b17fd823 --- /dev/null +++ b/cookbooks/redis-sentinel/libraries/redis_sentinel_instances.rb @@ -0,0 +1,33 @@ +class Chef::Recipe + # Returns the list of redis-sentinel instances + # Based on the settings in redis-sentinel/attributes/default.rb + def redis_sentinel_instances + all_app_instances = node['dna']['engineyard']['environment']['instances'].map do |i| + i['private_hostname'] if ['app_master', 'app', 'solo'].include?(i['role']) + end.compact + + all_util_instances = node['dna']['engineyard']['environment']['instances'].map do |i| + i['private_hostname'] if i['role'] == 'util' + end.compact + + named_utility_instances = node['dna']['utility_instances'].map do |i| + i['hostname'] if i['name'] == node['redis-sentinel']['utility_name'] + end.compact + + case node['redis-sentinel']['install_type'] + when 'ALL_APP_INSTANCES' + all_app_instances + when 'ALL_APP_AND_UTIL_INSTANCES' + all_app_instances + all_util_instances + when 'ALL_APP_AND_NAMED_UTIL_INSTANCES' + all_app_instances + named_utility_instances + when 'NAMED_UTILS' + named_utility_instances + else + # We should never get to this case + # If we do, we return an empty array + # to help in debugging why node['redis-sentinel']['install_type'] wasn't properly set + [] + end + end +end diff --git a/cookbooks/redis-sentinel/metadata.rb b/cookbooks/redis-sentinel/metadata.rb new file mode 100644 index 00000000..da8f425b --- /dev/null +++ b/cookbooks/redis-sentinel/metadata.rb @@ -0,0 +1,10 @@ +name 'redis-sentinel' +description 'Configuration & deployment of Redis Sentinel on Engine Yard' +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +maintainer 'Engine Yard' +maintainer_email 'support@engineyard.com' +version '5.0.0' +source_url 'https://github.com/engineyard/ey-cookbooks-stable-v5' +issues_url 'https://github.com/engineyard/ey-cookbooks-stable-v5/issues' + +depends 'redis' diff --git a/cookbooks/redis-sentinel/recipes/configure.rb b/cookbooks/redis-sentinel/recipes/configure.rb new file mode 100644 index 00000000..3602d5f4 --- /dev/null +++ b/cookbooks/redis-sentinel/recipes/configure.rb @@ -0,0 +1,60 @@ +# +# Cookbook Name:: redis-sentinel +# Recipe:: configure +# + +redis_master = node['dna']['utility_instances'].select{ |i| i[:name] == 'redis'}.first + +# Skip everything if there is no redis instance +if redis_master + redis_master_hostname = redis_master[:hostname] + + sentinel_instances = redis_sentinel_instances + sentinels_for_redis_yml = sentinel_instances.map do |i| + { 'host' => i, 'port' => node['redis-sentinel']['port'] } + end + + if is_redis_sentinel_instance(node['dna']['name'], node['dna']['instance_role']) + # Generate a redis-sentinel.yml in /data/appname/shared/config + # so that the application can know which redis-sentinel instances to talk to + if %w(solo app app_master util).include?(node['dna']['instance_role']) + node['dna']['applications'].each do |app, _data| + template "/data/#{app}/shared/config/redis-sentinel.yml" do + source 'redis-sentinel.yml.erb' + owner node['owner_name'] + group node['owner_name'] + mode 0655 + backup 0 + variables('sentinel_instances' => sentinel_instances, + 'port' => node['redis-sentinel']['port']) + end + end + end + + # Override the redis.yml in /data/appname/shared/config + # Redis clients should connect to the sentinels, not directly to redis master + if %w(solo app app_master util).include?(node['dna']['instance_role']) + node['dna']['applications'].each do |app, _data| + template "/data/#{app}/shared/config/redis.yml" do + source 'redis.yml.erb' + owner node['owner_name'] + group node['owner_name'] + mode 0655 + backup 0 + variables({ + 'environment' => node['dna']['environment']['framework_env'], + 'sentinels' => sentinels_for_redis_yml, + 'redis_url' => "redis://#{redis_master_hostname}" + }) + end + end + end + + # Reload monit and restart redis-sentinel + execute 'restart-redis-sentinel' do + command 'monit reload && sleep 10 && /usr/bin/redis-cli -p 26379 shutdown' + not_if { `ps aux | grep [r]edis-server` == '' } + end + + end +end diff --git a/cookbooks/redis-sentinel/recipes/default.rb b/cookbooks/redis-sentinel/recipes/default.rb new file mode 100644 index 00000000..7734a315 --- /dev/null +++ b/cookbooks/redis-sentinel/recipes/default.rb @@ -0,0 +1,6 @@ +# +# Cookbook Name:: redis-sentinel +# + +include_recipe 'redis-sentinel::install' +include_recipe 'redis-sentinel::configure' diff --git a/cookbooks/redis-sentinel/recipes/install.rb b/cookbooks/redis-sentinel/recipes/install.rb new file mode 100644 index 00000000..da1cfc5f --- /dev/null +++ b/cookbooks/redis-sentinel/recipes/install.rb @@ -0,0 +1,81 @@ +# +# Cookbook Name:: redis-sentinel +# Recipe:: install +# +# This installs redis-sentinel on selected instances in the environment +# +# redis-sentinel is essentially redis-server running in sentinel mode +# so the code here looks very much like redis/recipes/install.rb +# + +redis_version = node['redis']['version'] +redis_config_file_version = redis_version[0..2] +redis_download_url = node['redis']['download_url'] +redis_base_directory = node['redis']['basedir'] + +run_installer = !FileTest.exists?(redis_base_directory) || node['redis']['force_upgrade'] + +if is_redis_sentinel_instance(node['dna']['name'], node['dna']['instance_role']) + + sysctl 'Enable Overcommit Memory' do + variables 'vm.overcommit_memory' => 1 + end + + if run_installer + if node['redis']['install'] + include_recipe 'redis-sentinel::install_from_source' + else + include_recipe 'redis-sentinel::install_from_package' + end + + directory redis_base_directory do + owner 'redis' + group 'redis' + mode 0o755 + recursive true + action :create + end + end + + # Determine the redis master instance private IP + redis_master = node['dna']['utility_instances'].select{ |i| i['name'] == 'redis'}.first + redis_master_hostname = redis_master[:hostname] if redis_master + redis_sentinel_config_variables = { + 'port' => node['redis-sentinel']['port'], + 'redis_name' => 'redis', + 'redis_master_hostname' => redis_master_hostname + } + template '/etc/redis-sentinel.conf' do + owner 'root' + group 'root' + mode 0o644 + source 'redis-sentinel.conf.erb' + variables redis_sentinel_config_variables + end + + bin_path = if node['redis']['install_from_source'] + '/usr/local/bin' + else + '/usr/sbin' + end + template '/engineyard/bin/redis-sentinel' do + owner 'root' + group 'root' + mode 0o755 + source 'redis-sentinel.erb' + variables('configfile' => '/etc/redis-sentinel.conf', + 'bin_path' => bin_path) + end + + template '/data/monit.d/redis-sentinel.monitrc' do + owner 'root' + group 'root' + mode 0o644 + source 'redis-sentinel.monitrc.erb' + variables('port' => node['redis-sentinel']['port']) + end + + execute 'monit reload' do + action :run + end +end diff --git a/cookbooks/redis-sentinel/recipes/install_from_package.rb b/cookbooks/redis-sentinel/recipes/install_from_package.rb new file mode 100644 index 00000000..32a8d508 --- /dev/null +++ b/cookbooks/redis-sentinel/recipes/install_from_package.rb @@ -0,0 +1,13 @@ +redis_package = 'dev-db/redis' +redis_version = node['redis']['version'] + +enable_package redis_package do + version redis_version + override_hardmask true + unmask true +end + +package redis_package do + version redis_version + action :install +end diff --git a/cookbooks/redis-sentinel/recipes/install_from_source.rb b/cookbooks/redis-sentinel/recipes/install_from_source.rb new file mode 100644 index 00000000..442544a7 --- /dev/null +++ b/cookbooks/redis-sentinel/recipes/install_from_source.rb @@ -0,0 +1,21 @@ +redis_version = node['redis']['version'] +redis_download_url = node['redis']['download_url'] +redis_installer_directory = "/opt/redis-#{redis_version}" + +remote_file "/opt/redis-#{redis_version}.tar.gz" do + source redis_download_url.to_s + owner node['owner_name'] + group node['owner_name'] + mode 0o644 + backup 0 +end + +execute 'unarchive Redis installer' do + cwd '/opt' + command "tar zxf redis-#{redis_version}.tar.gz && sync" +end + +execute 'run redis-source/make install' do + cwd redis_installer_directory + command 'make install' +end diff --git a/cookbooks/redis-sentinel/templates/default/redis-sentinel.conf.erb b/cookbooks/redis-sentinel/templates/default/redis-sentinel.conf.erb new file mode 100644 index 00000000..28228829 --- /dev/null +++ b/cookbooks/redis-sentinel/templates/default/redis-sentinel.conf.erb @@ -0,0 +1,200 @@ +# Example sentinel.conf + +# *** IMPORTANT *** +# +# By default Sentinel will not be reachable from interfaces different than +# localhost, either use the 'bind' directive to bind to a list of network +# interfaces, or disable protected mode with "protected-mode no" by +# adding it to this configuration file. +# +# Before doing that MAKE SURE the instance is protected from the outside +# world via firewalling or other means. +# +# For example you may use one of the following: +# +# bind 127.0.0.1 192.168.1.1 +# +# NOTE: protected-mode is only available on 3.x +# Do not disable the line below unless you really want to disable protected mode, +# AND you are running version 2.8.x +# protected-mode no + +# port +# The port that this sentinel instance will run on +port <%= @port %> +pidfile /var/run/redis-sentinel.pid + +# sentinel announce-ip +# sentinel announce-port +# +# The above two configuration directives are useful in environments where, +# because of NAT, Sentinel is reachable from outside via a non-local address. +# +# When announce-ip is provided, the Sentinel will claim the specified IP address +# in HELLO messages used to gossip its presence, instead of auto-detecting the +# local address as it usually does. +# +# Similarly when announce-port is provided and is valid and non-zero, Sentinel +# will announce the specified TCP port. +# +# The two options don't need to be used together, if only announce-ip is +# provided, the Sentinel will announce the specified IP and the server port +# as specified by the "port" option. If only announce-port is provided, the +# Sentinel will announce the auto-detected local IP and the specified port. +# +# Example: +# +# sentinel announce-ip 1.2.3.4 + +# dir +# Every long running process should have a well-defined working directory. +# For Redis Sentinel to chdir to /tmp at startup is the simplest thing +# for the process to don't interfere with administrative tasks such as +# unmounting filesystems. +dir /tmp + +# sentinel monitor +# +# Tells Sentinel to monitor this master, and to consider it in O_DOWN +# (Objectively Down) state only if at least sentinels agree. +# +# Note that whatever is the ODOWN quorum, a Sentinel will require to +# be elected by the majority of the known Sentinels in order to +# start a failover, so no failover can be performed in minority. +# +# Slaves are auto-discovered, so you don't need to specify slaves in +# any way. Sentinel itself will rewrite this configuration file adding +# the slaves using additional configuration options. +# Also note that the configuration file is rewritten when a +# slave is promoted to master. +# +# Note: master name should not include special characters or spaces. +# The valid charset is A-z 0-9 and the three characters ".-_". +sentinel monitor <%= @redis_name %> <%= @redis_master_hostname %> 6379 2 + +# sentinel auth-pass +# +# Set the password to use to authenticate with the master and slaves. +# Useful if there is a password set in the Redis instances to monitor. +# +# Note that the master password is also used for slaves, so it is not +# possible to set a different password in masters and slaves instances +# if you want to be able to monitor these instances with Sentinel. +# +# However you can have Redis instances without the authentication enabled +# mixed with Redis instances requiring the authentication (as long as the +# password set is the same for all the instances requiring the password) as +# the AUTH command will have no effect in Redis instances with authentication +# switched off. +# +# Example: +# +# sentinel auth-pass mymaster MySUPER--secret-0123passw0rd + +# sentinel down-after-milliseconds +# +# Number of milliseconds the master (or any attached slave or sentinel) should +# be unreachable (as in, not acceptable reply to PING, continuously, for the +# specified period) in order to consider it in S_DOWN state (Subjectively +# Down). +# +# Default is 30 seconds. +sentinel down-after-milliseconds <%= @redis_name %> 30000 + +# sentinel parallel-syncs +# +# How many slaves we can reconfigure to point to the new slave simultaneously +# during the failover. Use a low number if you use the slaves to serve query +# to avoid that all the slaves will be unreachable at about the same +# time while performing the synchronization with the master. +sentinel parallel-syncs <%= @redis_name %> 1 + +# sentinel failover-timeout +# +# Specifies the failover timeout in milliseconds. It is used in many ways: +# +# - The time needed to re-start a failover after a previous failover was +# already tried against the same master by a given Sentinel, is two +# times the failover timeout. +# +# - The time needed for a slave replicating to a wrong master according +# to a Sentinel current configuration, to be forced to replicate +# with the right master, is exactly the failover timeout (counting since +# the moment a Sentinel detected the misconfiguration). +# +# - The time needed to cancel a failover that is already in progress but +# did not produced any configuration change (SLAVEOF NO ONE yet not +# acknowledged by the promoted slave). +# +# - The maximum time a failover in progress waits for all the slaves to be +# reconfigured as slaves of the new master. However even after this time +# the slaves will be reconfigured by the Sentinels anyway, but not with +# the exact parallel-syncs progression as specified. +# +# Default is 3 minutes. +sentinel failover-timeout <%= @redis_name %> 180000 + +# SCRIPTS EXECUTION +# +# sentinel notification-script and sentinel reconfig-script are used in order +# to configure scripts that are called to notify the system administrator +# or to reconfigure clients after a failover. The scripts are executed +# with the following rules for error handling: +# +# If script exits with "1" the execution is retried later (up to a maximum +# number of times currently set to 10). +# +# If script exits with "2" (or an higher value) the script execution is +# not retried. +# +# If script terminates because it receives a signal the behavior is the same +# as exit code 1. +# +# A script has a maximum running time of 60 seconds. After this limit is +# reached the script is terminated with a SIGKILL and the execution retried. + +# NOTIFICATION SCRIPT +# +# sentinel notification-script +# +# Call the specified notification script for any sentinel event that is +# generated in the WARNING level (for instance -sdown, -odown, and so forth). +# This script should notify the system administrator via email, SMS, or any +# other messaging system, that there is something wrong with the monitored +# Redis systems. +# +# The script is called with just two arguments: the first is the event type +# and the second the event description. +# +# The script must exist and be executable in order for sentinel to start if +# this option is provided. +# +# Example: +# +# sentinel notification-script mymaster /var/redis/notify.sh + +# CLIENTS RECONFIGURATION SCRIPT +# +# sentinel client-reconfig-script +# +# When the master changed because of a failover a script can be called in +# order to perform application-specific tasks to notify the clients that the +# configuration has changed and the master is at a different address. +# +# The following arguments are passed to the script: +# +# +# +# is currently always "failover" +# is either "leader" or "observer" +# +# The arguments from-ip, from-port, to-ip, to-port are used to communicate +# the old address of the master and the new address of the elected slave +# (now a master). +# +# This script should be resistant to multiple invocations. +# +# Example: +# +# sentinel client-reconfig-script mymaster /var/redis/reconfig.sh + diff --git a/cookbooks/redis-sentinel/templates/default/redis-sentinel.erb b/cookbooks/redis-sentinel/templates/default/redis-sentinel.erb new file mode 100644 index 00000000..1b5337ac --- /dev/null +++ b/cookbooks/redis-sentinel/templates/default/redis-sentinel.erb @@ -0,0 +1,2 @@ +#!/bin/bash +start-stop-daemon --start -b -m -p /var/run/redis-sentinel.pid --exec /bin/bash -- -c "<%= @bin_path %>/redis-server <%= @configfile %> --sentinel" diff --git a/cookbooks/redis-sentinel/templates/default/redis-sentinel.monitrc.erb b/cookbooks/redis-sentinel/templates/default/redis-sentinel.monitrc.erb new file mode 100644 index 00000000..9d47b7f6 --- /dev/null +++ b/cookbooks/redis-sentinel/templates/default/redis-sentinel.monitrc.erb @@ -0,0 +1,5 @@ +check process redis-sentinel +with pidfile /var/run/redis-sentinel.pid + start program = "/engineyard/bin/redis-sentinel" + stop program = "/usr/bin/redis-cli -p <%= @port %> shutdown" + group redis-util diff --git a/cookbooks/redis-sentinel/templates/default/redis-sentinel.yml.erb b/cookbooks/redis-sentinel/templates/default/redis-sentinel.yml.erb new file mode 100644 index 00000000..0a4835cb --- /dev/null +++ b/cookbooks/redis-sentinel/templates/default/redis-sentinel.yml.erb @@ -0,0 +1,5 @@ +sentinels: +<% @sentinel_instances.each do |hostname| -%> +- <%= hostname %> +<% end -%> +port: <%= @port %> diff --git a/cookbooks/redis-sentinel/templates/default/redis.yml.erb b/cookbooks/redis-sentinel/templates/default/redis.yml.erb new file mode 100644 index 00000000..bf6754a2 --- /dev/null +++ b/cookbooks/redis-sentinel/templates/default/redis.yml.erb @@ -0,0 +1,7 @@ +<%= @environment %>: + url: <%= @redis_url %> + sentinels: + <% @sentinels.each do |sentinel| -%> + - <%= sentinel['host'] %> + <%= sentinel['port'] %> + <% end -%> diff --git a/custom-cookbooks/redis-sentinel/README.md b/custom-cookbooks/redis-sentinel/README.md new file mode 100644 index 00000000..75e96f87 --- /dev/null +++ b/custom-cookbooks/redis-sentinel/README.md @@ -0,0 +1,6 @@ +# Redis Sentinel + +This example contains a complete cookbooks/ that you can use on the stable-v5 stack. + +See https://github.com/engineyard/ey-cookbooks-stable-v5/tree/next-release/custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel for complete instructions. + diff --git a/custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/README.md b/custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/README.md new file mode 100644 index 00000000..fa0c13f0 --- /dev/null +++ b/custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/README.md @@ -0,0 +1,89 @@ +# Redis Sentinel + +This recipe installs version Redis Sentinel 2.8 or later using either the package from the Engine Yard portage tree or the Redis installer from redis.io. Redis Sentinel is actually Redis running in sentinel mode, so you might find some similarities between this recipe and the Redis recipe. + +## Installation + +1. Edit `cookbooks/main/recipes/default.rb` and add + + ``` + include_recipe 'redis-sentinel' + ``` + +2. Boot a redis slave instance. Please refer to the Redis recipe for detailed instructions. + + +3. Edit `cookbooks/redis-sentinel/attributes.rb` to specify where to install Redis Sentinel. You can choose to install Redis Sentinel on all app instances, or on all utility instances that match a given name. For environments running Sidekiq, we recommend installing Redis Sentinel on all Sidekiq instances. + + NOTE: You need at least 3 Redis Sentinel instances to have a proper quorum. + +4. Download the ey-core gem on your local machine and upload the recipes + + ``` + gem install ey-core + ey-core recipes upload --environment= --file= --apply + ``` + +5. After the chef run, verify that the Redis Sentinels are properly monitoring the Redis master instance. SSH into the Redis Sentinel instances and run this command: + + ``` + redis-cli -p 26379 info + ``` + + NOTE: If you specified a different Redis Sentinel port then you should use that in the redis-cli command. + + In the last section of the output you should see information about the Redis master instance, and the number of connected sentinel instances: + + ``` + # Sentinel + sentinel_masters:1 + sentinel_tilt:0 + sentinel_running_scripts:0 + sentinel_scripts_queue_length:0 + sentinel_simulate_failure_flags:0 + master0:name=redis,status=ok,address=172.31.20.21:6379,slaves=1,sentinels=2 + ``` + +## Customizations + +All customizations go to `cookbooks/redis-sentinel/attributes/default.rb`. + +### Choose the instances that run the recipe + +By default, the Redis Sentinel recipe runs on a utility instance named "sidekiq". You can change this by modifying `attributes/default.rb`. + +#### A. Run Redis Sentinel on a utility instances with a custom name + +* Ensure that these lines are not commented out: + + ``` + sentinel['utility_name'] = 'sidekiq' + sentinel['install_type'] = 'NAMED_UTILS' + ``` + +* Specify the Redis Sentinel instance name. If the instances are not yet running, boot instances with that name. + +* Make sure this line is commented out: + + ``` + # sentinel['install_type'] = 'ALL_APP_INSTANCES' + ``` + +#### B. Run Redis Sentinel on all application instances + +* Ensure that these lines are not commented out: + + ``` + sentinel['install_type'] = 'ALL_APP_INSTANCES' + ``` + +* Make sure these lines are commented out: + + ``` + # sentinel['utility_name'] = 'sidekiq' + # sentinel['install_type'] = 'NAMED_UTILS' + ``` + +## Dependencies + +You need to install the appropriate Redis client library for your application. See https://redis.io/clients \ No newline at end of file diff --git a/custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/attributes/default.rb b/custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/attributes/default.rb new file mode 100644 index 00000000..7741ca44 --- /dev/null +++ b/custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/attributes/default.rb @@ -0,0 +1,19 @@ +default['redis-sentinel'].tap do |sentinel| + sentinel['port'] = '26379' + + # Install redis-sentinel on all app instances + # sentinel['install_type'] = 'ALL_APP_INSTANCES' + # Install redis-sentinel on all app and util instances + # sentinel['install_type'] = 'ALL_APP_AND_UTIL_INSTANCES' + + # Install redis-sentinel on utility instances named 'sidekiq' + #sentinel['utility_name'] = 'sidekiq' + #sentinel['install_type'] = 'NAMED_UTILS' + + # Install redis-sentinel on all app instances, plus utility instances named 'sidekiq' + sentinel['utility_name'] = 'sidekiq' + sentinel['install_type'] = 'ALL_APP_AND_NAMED_UTIL_INSTANCES' + + # Timeout + sentinel['timeout'] = 300_000 +end diff --git a/custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/metadata.rb b/custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/metadata.rb new file mode 100644 index 00000000..4fd5a05c --- /dev/null +++ b/custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/metadata.rb @@ -0,0 +1,10 @@ +name 'custom-redis-sentinel' +description 'Configuration & deployment of Redis Sentinel on Engine Yard' +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +maintainer 'Engine Yard' +maintainer_email 'support@engineyard.com' +version '5.0.1' +issues_url 'https://github.com/engineyard/ey-cookbooks-stable-v5/issues' +source_url 'https://github.com/engineyard/ey-cookbooks-stable-v5' + +depends 'redis-sentinel' diff --git a/custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/recipes/default.rb b/custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/recipes/default.rb new file mode 100644 index 00000000..591bf696 --- /dev/null +++ b/custom-cookbooks/redis-sentinel/cookbooks/custom-redis-sentinel/recipes/default.rb @@ -0,0 +1 @@ +include_recipe 'redis-sentinel' diff --git a/custom-cookbooks/redis-sentinel/cookbooks/ey-custom/metadata.rb b/custom-cookbooks/redis-sentinel/cookbooks/ey-custom/metadata.rb new file mode 100644 index 00000000..186d1ac6 --- /dev/null +++ b/custom-cookbooks/redis-sentinel/cookbooks/ey-custom/metadata.rb @@ -0,0 +1,3 @@ +name "ey-custom" + +depends "custom-redis-sentinel" diff --git a/custom-cookbooks/redis-sentinel/cookbooks/ey-custom/recipes/after-main.rb b/custom-cookbooks/redis-sentinel/cookbooks/ey-custom/recipes/after-main.rb new file mode 100644 index 00000000..d83d2fed --- /dev/null +++ b/custom-cookbooks/redis-sentinel/cookbooks/ey-custom/recipes/after-main.rb @@ -0,0 +1 @@ +include_recipe "custom-redis-sentinel"