Redis master-slave setup in docker-compose - read-only slave - redis

how can I make Redis slave writeable in docker-compose.yml?
I have a Python script running, which cannot transfer to slave in case of failure due to the following error:
File "app.py", line 22, in <module>
r.set(timestamp, num)
File "/usr/local/lib/python2.7/site-packages/redis/client.py", line 1519, in set
return self.execute_command('SET', *pieces)
File "/usr/local/lib/python2.7/site-packages/redis/client.py", line 839, in execute_command
return self.parse_response(conn, command_name, **options)
File "/usr/local/lib/python2.7/site-packages/redis/client.py", line 853, in parse_response
response = connection.read_response()
File "/usr/local/lib/python2.7/site-packages/redis/connection.py", line 717, in read_response
raise response
redis.exceptions.ReadOnlyError: You can't write against a read only replica.
The following is docker-compose.yml being used:
services:
redis-master:
container_name: redis-master
image: redis:latest
command: redis-server --port 6379
ports:
- "6379:6379"
volumes:
- .:/app
redis-slave:
container_name: redis-slave
image: redis:latest
command: redis-server --slaveof redis-master 6379
volumes:
- .:/app
Output from slave:
root#29b9b3919c4a:/data# redis-cli -p 6379 info replication
# Replication
role:slave
master_host:192.168.48.7
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:257149
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:2705ce53eb2c7778f207f7626280ca0964dc87b1
master_replid2:8acb89aa40f8d7edc254eaed3ac197d08b808e82
master_repl_offset:257149
second_repl_offset:60757
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:60757
repl_backlog_histlen:196393
Is there an option to add to docker-compose.yml to make slave writeable?
Thank you very much!

Looking at redis.conf file there is this option: replica-read-only yes.
You need to change that value to allows writings on slaves.
You can configure a replica instance to accept writes or not. Writing
against a replica instance may be useful to store some ephemeral data
(because data written on a replica will be easily deleted after resync
with the master) but may also cause problems if clients are writing to
it because of a misconfiguration. Since Redis 2.6 by default
replicas are read-only. Note: read only replicas are not designed
to be exposed to untrusted clients on the internet. It's just a
protection layer against misuse of the instance. Still a read only
replica exports by default all the administrative commands such as
CONFIG, DEBUG, and so forth. To a limited extent you can improve
security of read only replicas using 'rename-command' to shadow all
the administrative / dangerous commands.

Related

How can I extend redis database by redisgraph.so module?

Unable to import redisgraph module redisgraph.so indo redis database.
I successfully compiled redisgraph.so from sources.
redisgraph.so execution rights are set for everyone.
I tried:
$ redis-cli
> shutdown ((stop redis-server))
$ redis-server --loadmodule pathto/redisgraph.so
((System replies:))
# oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
# Redis version=4.0.9, bits=64, commit=00000000, modified=0, pid=2407, just started
# Configuration loaded
* Increased maximum number of open files to 10032 (it was originally set to 1024).
# Creating Server TCP listening socket *:6379: bind: Address already in use
$ redis-cli
> module list
(empty list or set)
> module load pathto/redisgraph.so
(error) ERR Error loading the extension. Please check the server logs.
((log file says: *no permission*))
redis database works fine as key-value database.
But I fail to extend it by graph functionality.
So far I am unable to drop commands like "GRAPH.QUERY" (redis replies: "unknown command").
I have no idea why redis-server seems to ignore the import command or redis-cli complains about permission rights.
The error indicates that you already have a running process bound to the same port (probably another redis-server).
Also, you'd be better off using redisgraph with the latest Redis version (i.e. v5).
It's better to have redis managed by systemd and you could configure it as follow:
Inside
update the supervised directive in /etc/redis/redis.conf to use systemd by setting supervised systemd
Creating a redis systemd file /etc/systemd/system/redis.service and set unit, service and install directive:
[Unit]
Description=Redis In-Memory Data Store
After=network.target
[Service]
User=redis
Group=redis
ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf
ExecStop=/usr/local/bin/redis-cli shutdown
Restart=always
[Install]
WantedBy=multi-user.target
Then start redis
sudo systemctl start redis
sudo systemctl status redis
If you want redis to automatically restart when your server starts then:
Assuming all of these tests worked and that you would like to start Redis automatically when your server boots, enable the systemd service:
sudo systemctl enable redis

Restoring redis running on docker from dump.rdb

What I want to do is to use a dump.rdb that I've taken from a production server, and use it in my development environment, that is defined by a very simple compose file.
For simplicity, assume that my app is the same as this compose example from the docker docs for redis and flask, so the docker-compose.yml looks like:
version: '2'
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
depends_on:
- redis
redis:
image: redis
This persists redis data between restarts, but you just cannot access the redis files as there is no volume mounted for redis in the docker-compose.yml.
So I change my compose file to mount a volume for redis, and I also want to force redis to persist data and the official redis image docs say that happens if I use 'appendonly'.
redis:
image: redis
command: redis-server --appendonly yes
volumes:
- ./redis:/data
If I do this, my data are persisted, as they were in the original example, and I can now see a dump.rdb and and appendonly.aof in the /redis path. The problem is, if I want to restore from a dump.rdb I need to turn off appendonly (for example, see digital ocean's how-to-back-up-and-restore-your-redis-data-on-ubuntu-14-04), and without append-only I cannot see how to get the compose file to write to the volume.
How can I produce a docker compose that will persist redis in a volume where I can switch the dump.rdb files, and therefore insert the production snapshot into my development environment?
Update
The following compose works, but be patient when testing, as the creation of the dump.rdb is not instant (hence it seeming like it failed). Also the redis official image doc, implies you have to use appendonly when you don't:
redis:
image: redis
volumes:
- ./redis:/data
The appendonly part is just to make sure that you don't lose data, but since you already have the dump.rdb from your server you don't need to worry about that: you can either remove the append only flag or remove 'command' entirely since it will then fall back to the image default which is just 'redis-server'.
I have a similar setup here and it writes/loads the dump.rdb files fine. (404)

How to make redis comand SLAVEOF work for encrypted redis master?

I started two redis node separately with original redis.conf, which I don't want to edit it, however I want to use redis command, such as slaveof, to config redis nodes dynamically.
If redis nodes without 'auth', i.e. no "requirepass" in the redis.conf, the following command on the slave redis node will work:
redis-server --slaveof redis-master 6379
where redis-master is the hostname of the redis master node.
But this will not work if the master is encrypted. I've went through the redis official docs, nothing helps. Before I go for antirez, I want to hear from you here.
You can set the masterauth config to specify the master's password.
start redis-cli to connect to the slave instance.
set masterauth config: config set masterauth master-password
set master: slaveof redis-master master-port

Redis Slave Master connections fails Slave logs show: Unable to connect to MASTER: Permission denied

I have followed the instructions on how to set up a redis master server cluster but after I am done I get am not able to see why the servers are not able to see one another.
this is the second build I put together and I am stuck on the same spot. I could really use some help I never worked on REDIS before and I could use some guidance.
USING CENTOS7 Redis version
when i check the redis slave logs I get the following
[20671] 12 Jan 15:48:02.369 * Connecting to MASTER 10.10.10.10:6379
[20671] 12 Jan 15:48:02.369 # Unable to connect to MASTER: Permission denied
The config files are using the same exact password for both master and slave.
and just to test I gave the default directory full control for the redis working directory files and folder
Tested ports and they are working fine,
I also get the following when I run INFO when connecting to REDIS Slave
Replication
role:slave
master_host:10.10.10.11.
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1
master_link_down_since_seconds:1452631759
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
INFO from MASTER NODE:
Replication
role:master
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
Both servers are running on CENTOS 7
I had this same issue when setting up a Redis cluster on CentOS 7 at AWS, and it was in fact due to SELinux being enabled. You can verify that this is your issue by checking the contents of /var/log/audit.log.
To allow Redis replication with SELinux, run the following commands as root to update the security policy. As you will likely be using Sentinel to manage the cluster, the necessary policies for Sentinel master and slaves is included as well.
Folder for policy files
Location to save new policy files
mkdir -p ~/.selinux
Redis Replication Policy
Allow data replication to slaves, include on master as well as it may become a slave at some point
cat <<SELINUX > ~/.selinux/redis_repl.te
# create new
module redis_repl 1.0;
require {
type redis_port_t;
type redis_t;
class tcp_socket name_connect;
}
#============= redis_t ==============
allow redis_t redis_port_t:tcp_socket name_connect;
SELINUX
checkmodule -m -M -o ~/.selinux/redis_repl.mod ~/.selinux/redis_repl.te
semodule_package --outfile ~/.selinux/redis_repl.pp --module ~/.selinux/redis_repl.mod
semodule -i ~/.selinux/redis_repl.pp
Redis Sentinel Master/Slave Policy, all Redis nodes
Allow Sentinel HA traffic on the Redis master/slave nodes
cat <<SELINUX > ~/.selinux/redis_ha.te
# create new
module redis_ha 1.0;
require {
type etc_t;
type redis_t;
class file write;
}
#============= redis_t ==============
allow redis_t etc_t:file write;
SELINUX
checkmodule -m -M -o ~/.selinux/redis_ha.mod ~/.selinux/redis_ha.te
semodule_package --outfile ~/.selinux/redis_ha.pp --module ~/.selinux/redis_ha.mod
semodule -i ~/.selinux/redis_ha.pp
Redis Sentinel Server Policy, all Sentinel nodes
Allow Sentinel HA traffic from the Sentinel nodes.
Note that you may need to change the Sentinel port if you aren't using the 26379 default.
# Allow Sentinel Port
semanage port -a -t redis_port_t -p tcp 26379
# Allow Sentinel Server
cat <<SELINUX > ~/.selinux/redis_sentinel.te
# create new
module redis_sentinel 1.0;
require {
type redis_port_t;
type etc_t;
type redis_t;
class tcp_socket name_connect;
class file write;
}
#============= redis_t ==============
allow redis_t redis_port_t:tcp_socket name_connect;
allow redis_t etc_t:file write;
SELINUX
checkmodule -m -M -o ~/.selinux/redis_sentinel.mod ~/.selinux/redis_sentinel.te
semodule_package --outfile ~/.selinux/redis_sentinel.pp --module ~/.selinux/redis_sentinel.mod
semodule -i ~/.selinux/redis_sentinel.pp
Restart Redis and Sentinel
service restart redis
service restart redis-sentinel
To #otaviofcs point, you're likely running into an SELinux issue. If you look in /var/log/audit/audit.log, I suspect you'll see alot of logging that looks like this:
type=AVC msg=audit(1465349491.812:28458): avc: denied { name_connect } for pid=30676 comm="redis-server" dest=6379 scontext=system_u:system_r:redis_t:s0 tcontext=system_u:object_r:redis_port_t:s0 tclass=tcp_socket
If so, you can either dive into the bowels of SELinux policy management or take the easy road: set SELinux targeted policy to permissive:
setenforce permissive
Note that you'll need to set the same in /etc/selinux/config by changing the line with SELINUX= to SELINUX=permissive.
two "new experience points"
The config is in the 2 ends of the conecction,
to add "personalized" port you can use semanage
sudo semanage port -a -t redis_port_t -p tcp 8014

Redis in docker-compose: any way to specify a redis.conf file?

my Redis container is defined as a standard image in my docker_compose.yml
redis:
image: redis
ports:
- "6379"
I guess it's using standard settings like binding to Redis at localhost.
I need to bind it to 0.0.0.0, is there any way to add a local redis.conf file to change the binding and let docker-compose to use it?
thanks for any trick...
Yes. Just mount your redis.conf over the default with a volume:
redis:
image: redis
volumes:
- ./redis.conf:/usr/local/etc/redis/redis.conf
ports:
- "6379"
Alternatively, create a new image based on the redis image with your conf file copied in. Full instructions are at: https://registry.hub.docker.com/_/redis/
However, the redis image does bind to 0.0.0.0 by default. To access it from the host, you need to use the port that Docker has mapped to the host for you which you find by using docker ps or the docker port command, you can then access it at localhost:32678 where 32678 is the mapped port. Alternatively, you can specify a specific port to map to in the docker-compose.yml.
As you seem to be new to Docker, this might all make a bit more sense if you start by using raw Docker commands rather than starting with Compose.
Old question, but if someone still want to do that, it is possible with volumes and command:
command: redis-server /usr/local/etc/redis/redis.conf
volumes:
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf
Unfortunately with Docker, things become a little tricky when it comes to Redis configuration file, and the answer voted as best (im sure from people that did'nt actually tested it) it DOESNT work.
But what DOES WORK, fast, and without husles is this:
command: redis-server --bind redis-container-name --requirepass some-long-password --maxmemory 256mb --maxmemory-policy allkeys-lru --appendonly yes
You can pass all the variable options you want in the command section of the yaml docker file, by adding "--" in the front of it, followed by the variable value.
Never forget to set a password, and if possible close the port 6379.
Ī¤hank me later.
PS: If you noticed at the command, i didnt use the typical 127.0.0.1, but instead the redis container name. This is done for the reason that docker assigns ip addresses internally via it's embedded dns server. In other words this bind address becomes dynamic, hence adding an extra layer of security.
If your redis container is called "redis" and you execute the command docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' redis (for verifying the running container's internal ip address), as far as docker is concerned, the command give in docker file, will be translated internally to something like: redis-server --bind 172.19.0.5 --requirepass some-long-password --maxmemory 256mb --maxmemory-policy allkeys-lru --appendonly yes
Based on David awnser but a more "Docker Compose" way is:
redis:
image: redis:alpine
command: redis-server --include /usr/local/etc/redis/redis.conf
volumes:
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf
That way, you include the .conf file by docker-compose.yml file and don't need a custom image.
mount your config /usr/local/etc/redis/redis.conf
add command to execute redis-server with your config
redis:
image: redis:7.0.4-alpine
restart: unless-stopped
volumes:
- ./redis.conf:/usr/local/etc/redis/redis.conf
command: redis-server /usr/local/etc/redis/redis.conf
########################################
# or using command if mount not work
########################################
command: >
redis-server --bind 127.0.0.1
--appendonly no
--save ""
--protected-mode yes
It is an old question but I have a solution that seems elegant and I don't have to execute commands every time ;).
1 Create your dockerfile like this
#/bin/redis/Dockerfile
FROM redis
CMD ["redis-server", "--include /usr/local/etc/redis/redis.conf"]
What we are doing is telling the server to include that file in the Redis configuration. The settings you type there will override the default Redis have.
2 Create your docker-compose
redisall:
build:
context: ./bin/redis
container_name: 'redisAll'
restart: unless-stopped
ports:
- "6379:6379"
volumes:
- ./config/redis:/usr/local/etc/redis
3 Create your configuration file it has to be called the same as Dockerfile
//config/redis/redis.conf
requirepass some-long-password
appendonly yes
################################## NETWORK #####################################
# By default, if no "bind" configuration directive is specified, Redis listens
# for connections from all the network interfaces available on the server.
# It is possible to listen to just one or multiple selected interfaces using
# the "bind" configuration directive, followed by one or more IP addresses.
#
# Examples:
#
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1 ::1
#
# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the
# internet, binding to all the interfaces is dangerous and will expose the
# instance to everybody on the internet. So by default we uncomment the
# following bind directive, that will force Redis to listen only into
# the IPv4 loopback interface address (this means Redis will be able to
# accept connections only from clients running into the same computer it
# is running).
#
# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES
# JUST COMMENT THE FOLLOWING LINE.*
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bind 127.0.0.1
// and all configurations that can be specified
// what you put here overwrites the default settings that have the
container
I had the same problem when using Redis in docker environment that the Redis could not save data to disk on dump.rdb.
The problem was the Redis could not read the configurations redis.conf , I solve it by sending the required configurations with the command in docker compose as below :
redis19:
image: redis:5.0
restart: always
container_name: redis19
hostname: redis19
command: redis-server --requirepass some-secret --stop-writes-on-bgsave-error no --save 900 1 --save 300 10 --save 60 10000
volumes:
- $PWD/redis/redis_data:/data
- $PWD/redis/redis.conf:/usr/local/etc/redis/redis.conf
- /etc/localtime:/etc/localtime:ro
and it works fine.
I think it will be helpful i am sharing working code in my local
redis:
container_name: redis
hostname: redis
image: redis
command: >
--include /usr/local/etc/redis/redis.conf
volumes:
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf
ports:
- "6379:6379"