2

Have a single Redis instance set up via docker-compose (using my Redis Dockerfile), all working.

Now I'm trying to seed the Redis instance with data after the container has started.

(1) Trying to run redis-cli directly in Dockerfile (that is run by docker-compose)

FROM redis:6.2.5
COPY ./redis.conf /test_dir/
COPY ./seed.txt /test_dir/

ENTRYPOINT ["redis-server", "/test_dir/redis.conf"]
RUN cat /test_dir/seed.txt | redis-cli -a <password> --pipe

this throws Could not connect to Redis at 127.0.0.1:6379: Connection refused

(2) Slightly different Dockerfile, running same command in a shell script that is executed after the container starts:

FROM redis:6.2.5
COPY ./redis.conf /test_dir/
COPY ./seed.txt /test_dir/
COPY ./init.sh /test_dir/

ENTRYPOINT ["redis-server", "/test_dir/redis.conf"]
RUN . /test_dir/init.sh
# init.sh
sleep 10  # ensure Redis server is up and running
cat /test_dir/seed.txt | redis-cli -a <password> --pipe

the shell script is executed, it waits 10 secs and I confirmed the server was up and running BEFORE the data import was triggered, but Redis throws the same error.

Notes:

(1) I've also disabled `protected-mode` and removed `requirepass`, but redis-cli can not connect
(2) when I jump into the container and manually execute the command, it DOES WORK !

So how can I run redis-cli from the Dockerfile or via shell script or what is the recommended way to seed a Docker Redis instance ?

Thanks.

pete19
  • 333
  • 1
  • 3
  • 17
  • If you were doing this without Docker, you'd first start the Redis server and second separately run `redis-cli`. A container runs one process only, and so I'd do the same thing here: start the Redis container, and separately run the seed job, either in another container or from the host (via a published port). – David Maze Sep 11 '21 at 11:05
  • @DavidMaze thanks for the reply. I updated my post, I did use sleep(10) and confirmed the redis server was up and running before the import was attempted... still same error. – pete19 Sep 12 '21 at 03:26

3 Answers3

2

RUN cat /test_dir/seed.txt | redis-cli -a <password> --pipe will execute when you do docker build, at that time ENTRYPOINT ["redis-server", "/test_dir/redis.conf"] still not run as it's only be called when the container start. So, you will surely have error.

As a result, you could use next workaround to do it:

/test_dir/seed.txt:

/test_dir/init.sh:

while :
do
    redis-cli -h redis-svr -p 6379 quit
    if [ $? -eq 0 ]; then
        cat /test_dir/seed.txt | redis-cli -h redis-svr -p 6379 --pipe
        break
    else
        echo "server not ready, wait then retry..."
        sleep 3
    fi
done

docker-compose.yaml:

version: '3'

services:
  redis-svr:
    image: redis
  redis-cli:
    image: redis
    volumes:
      - /test_dir:/test_dir
    entrypoint: sh -c /test_dir/init.sh

Execution:

$ docker-compose up
WARNING: Found orphan containers (20210910_redis_1) for this project. If you removed or renamed this service in your compose file, you can run this command with the --remove-orphans flag to clean it up.
Starting 20210910_redis-svr_1 ... done
Starting 20210910_redis-cli_1 ... done
Attaching to 20210910_redis-cli_1, 20210910_redis-svr_1
redis-cli_1  | Could not connect to Redis at redis-svr:6379: Connection refused
redis-cli_1  | server not ready, wait then retry...
redis-svr_1  | 1:C 11 Sep 2021 05:55:33.872 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis-svr_1  | 1:C 11 Sep 2021 05:55:33.872 # Redis version=6.2.5, bits=64, commit=00000000, modified=0, pid=1, just started
redis-svr_1  | 1:C 11 Sep 2021 05:55:33.872 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
redis-svr_1  | 1:M 11 Sep 2021 05:55:33.873 * monotonic clock: POSIX clock_gettime
redis-svr_1  | 1:M 11 Sep 2021 05:55:33.879 * Running mode=standalone, port=6379.
redis-svr_1  | 1:M 11 Sep 2021 05:55:33.879 # Server initialized
redis-svr_1  | 1:M 11 Sep 2021 05:55:33.890 * Loading RDB produced by version 6.2.5
redis-svr_1  | 1:M 11 Sep 2021 05:55:33.890 * RDB age 266 seconds
redis-svr_1  | 1:M 11 Sep 2021 05:55:33.890 * RDB memory usage when created 0.77 Mb
redis-svr_1  | 1:M 11 Sep 2021 05:55:33.890 * DB loaded from disk: 0.010 seconds
redis-svr_1  | 1:M 11 Sep 2021 05:55:33.890 * Ready to accept connections
redis-cli_1  | OK
redis-cli_1  | All data transferred. Waiting for the last reply...
redis-cli_1  | Last reply received from server.
redis-cli_1  | errors: 0, replies: 0
20210910_redis-cli_1 exited with code 0

Explain:

redis-svr will start a server there in one container, redis-cli will start another container, it will frist call init.sh, the init.sh will try to link to redis server to see if server really start or not with redis-cli -h redis-svr -p 6379 quit. If not, it will wait sometime and retry, if server already start, then it could call the client command to import the initial data to server.

EDIT20210912 based on OP's comment to use one container:

Folder structure:

$ tree
.
├── docker-compose.yaml
├── Dockerfile
├── init.sh
├── my-entrypoint.sh
└── seed.txt

docker-compose.yaml:

version: '3'

services:
  redis-svr:
      build: ./

Dockerfile:

FROM redis:6.2.5
COPY . /tmp
RUN chmod -R 777 /tmp
ENTRYPOINT ["/tmp/my-entrypoint.sh"]
CMD ["redis-server"]

init.sh:

while :
do
    redis-cli quit
    if [ $? -eq 0 ]; then
        echo "Server ready now, start to import data ..."
        cat ./seed.txt | redis-cli --pipe
        break
    else
        echo "Server not ready, wait then retry..."
        sleep 3
    fi
done

my-entrypoint.sh:

#!/bin/sh
/tmp/init.sh &
docker-entrypoint.sh $1

Execution:

$ docker-compose up
Recreating test_dir_redis-svr_1 ... done
Attaching to test_dir_redis-svr_1
redis-svr_1  | Could not connect to Redis at 127.0.0.1:6379: Connection refused
redis-svr_1  | Server not ready, wait then retry...
redis-svr_1  | 7:C 12 Sep 2021 08:36:05.512 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis-svr_1  | 7:C 12 Sep 2021 08:36:05.512 # Redis version=6.2.5, bits=64, commit=00000000, modified=0, pid=7, just started
redis-svr_1  | 7:C 12 Sep 2021 08:36:05.512 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
redis-svr_1  | 7:M 12 Sep 2021 08:36:05.513 * monotonic clock: POSIX clock_gettime
redis-svr_1  | 7:M 12 Sep 2021 08:36:05.515 * Running mode=standalone, port=6379.
redis-svr_1  | 7:M 12 Sep 2021 08:36:05.515 # Server initialized
redis-svr_1  | 7:M 12 Sep 2021 08:36:05.515 * Ready to accept connections
redis-svr_1  | OK
redis-svr_1  | Server ready now, start to import data ...
redis-svr_1  | All data transferred. Waiting for the last reply...
redis-svr_1  | Last reply received from server.
redis-svr_1  | errors: 0, replies: 0

This define customized entrypoint to let init.sh have chance to be executed before redis server run.

atline
  • 28,355
  • 16
  • 77
  • 113
  • two things, (1) I did try to set a 10 sec sleep at the beginning of my shell script, I confirmed the Redis container and server was up and running BEFORE the script actually executed and tried to seed the data... got the exact same error. (2) why do you use two containers to achieve this ? – pete19 Sep 12 '21 at 03:23
  • 1) Do you use the exact same minimal example I post? Or you modify it, please give detail, you may need to change some settings I guess. Have you proved the minimal example in the answer works or not? 2) Typically, one task one container. For official redis image, it didn't give backdoor to init the database, after the redis server start in entrypoint, the process will stay in foreground which means your other action e.g. init.sh won't have chance to execute, the only workaround is to redefine your own entrypoint.sh to override the default one, something not so convinent to do that. – atline Sep 12 '21 at 03:30
  • I see your update with `sleep 10` in your init.sh. It won't make effect, you have a wrong understanding here: `RUN` will happen when you do `docker build`, while `ENTRYPOINT` will happen when you do `docker run` to start the container. So `RUN` will always happen before `ENTRYPOINT`, you define `redis server start` in `ENTRYPOINT`, `init.sh` in `RUN`, so `init.sh` will always run before `redis server start`, even you `sleep 1000000s`, the `redis-server` won't start before you run `init.sh`. Your update totally different with the solution in my answer. – atline Sep 12 '21 at 03:38
  • NOTE: despite of the sequence you define for `RUN`, `ENTRYPOINT`, `RUN` won't execute after `ENTRYPOINT` even you define it after `ENTRYPOINT`, they are executed in different phase of lifecycle. – atline Sep 12 '21 at 03:42
  • thanks for the update, I will test this very soon. will report back. – pete19 Sep 12 '21 at 04:41
  • figured it out using a multi-stage dockerfile build. Not a fan of that for this here, but prefer that over using another container just to seed data... do you have a solution without using another container ? – pete19 Sep 12 '21 at 07:36
  • @pete19 This has non business with multi-stage build. I update the answer to include a sample solution to just use one container, just FYI. Please read docker-compose up log carefully to see if it meet your requirement. – atline Sep 12 '21 at 08:38
  • this works, with the caveat that I cannot seem to provide a custom config file as a parameter to `CMD`, e.g. `CMD ["redis-server", ""]`... it simply won't load it. – pete19 Sep 12 '21 at 09:07
  • 1
    Try `CMD [""]` – atline Sep 12 '21 at 09:11
  • 1
    or change `docker-entrypoint.sh $1` to `docker-entrypoint.sh $@` – atline Sep 12 '21 at 09:16
0

this is another solution that works, although @atline solution is more elegant.

Dockerfile

FROM redis:6.2.5-alpine3.14
RUN mkdir /foo
COPY ./redis.conf /foo/
COPY ./seed.txt /foo/
COPY ./init.sh /foo/

ENTRYPOINT /bin/sh -c "redis-server /foo/redis.conf" & sleep 2s \
&& source /foo/init.sh

init.sh

# seed db
cat /foo/seed.txt | redis-cli -a <pw_if_applicable> --pipe
# save .rdb dump
redis-cli -a <pw_if_applicable> save
# shutdown server
redis-cli -a <pw_if_applicable> shutdown
# restart server
redis-server "/foo/redis.conf"

This works and uses the provided redis.conf, hence it seeds the data into the the .rdb that you have defined in the config.

pete19
  • 333
  • 1
  • 3
  • 17
0

https://docs.docker.com/config/containers/multi-service_container/ It's very simple try mine init.sh

#!/bin/bash
set -m
redis-server --include /etc/redis.conf &
sleep 5
cat /data/user_data | redis-cli --pipe
fg %1