【问题标题】:Docker Redis - Seeding Data - Cannot execute redis-cli via Dockerfile or via shell scriptDocker Redis - 播种数据 - 无法通过 Dockerfile 或 shell 脚本执行 redis-cli
【发布时间】:2021-11-07 09:57:25
【问题描述】:

通过docker-compose 设置一个Redis 实例(使用我的Redis Dockerfile),一切正常。

现在我正在尝试在容器启动后为 Redis 实例添加数据。

(1) 尝试直接在Dockerfile 中运行redis-cli(即由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

这会抛出Could not connect to Redis at 127.0.0.1:6379: Connection refused

(2) Dockerfile 略有不同,在容器启动后执行的shell 脚本中运行相同的命令:

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

shell 脚本被执行,它等待 10 秒,我确认服务器已启动并在触发数据导入之前运行,但 Redis 引发相同的错误。

注意事项:

(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 !

那么我如何从Dockerfile 或通过shell 脚本运行redis-cli,或者推荐的播种Docker Redis 实例的方法是什么?

谢谢。

【问题讨论】:

  • 如果您在没有 Docker 的情况下执行此操作,您将首先启动 Redis 服务器,然后单独运行 redis-cli。一个容器只运行一个进程,所以我会在这里做同样的事情:启动 Redis 容器,然后在另一个容器中或从主机(通过已发布的端口)单独运行种子作业。
  • @DavidMaze 感谢您的回复。我更新了我的帖子,我确实使用了 sleep(10) 并确认 redis 服务器在尝试导入之前已启动并运行......仍然是同样的错误。

标签: docker redis redis-cli


【解决方案1】:

RUN cat /test_dir/seed.txt | redis-cli -a &lt;password&gt; --pipe 将在您执行docker build 时执行,此时ENTRYPOINT ["redis-server", "/test_dir/redis.conf"] 仍然没有运行,因为它仅在容器启动时被调用。所以,你肯定会出错。

因此,您可以使用下一个解决方法:

/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

执行:

$ 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

说明:

redis-svr 将在一个容器中启动一个服务器,redis-cli 将启动另一个容器,它将首先调用init.shinit.sh 将尝试链接到 redis 服务器以查看服务器是否真正启动与redis-cli -h redis-svr -p 6379 quit。如果没有,它会等待一段时间并重试,如果服务器已经启动,那么它可以调用客户端命令将初始数据导入服务器。

EDIT20210912 基于 OP 的评论使用一个容器:

文件夹结构:

$ 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

执行:

$ 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

这定义了自定义入口点,让init.sh有机会在redis服务器运行之前被执行。

【讨论】:

  • 两件事,(1)我确实尝试在我的 shell 脚本开始时设置 10 秒睡眠,我确认 Redis 容器和服务器在脚本实际执行之前已经启动并运行并尝试播种数据...得到完全相同的错误。 (2)为什么要用两个容器来实现呢?
  • 1) 您是否使用与我发布的完全相同的最小示例?或者您修改它,请提供详细信息,我猜您可能需要更改一些设置。您是否证明了答案中的最小示例有效? 2) 通常,一个任务一个容器。对于官方的redis镜像,它没有提供后门来初始化数据库,在redis服务器在入口点启动后,进程将停留在前台,这意味着你的其他操作,例如init.sh 将没有机会执行,唯一的解决方法是重新定义自己的 entrypoint.sh 以覆盖默认值,这样做不太方便。
  • 我在您的 init.sh 中看到了您使用 sleep 10 进行的更新。它不会生效,你这里有一个错误的理解:RUN会发生在你做docker build时,而ENTRYPOINT会发生在你做docker run启动容器的时候。所以RUN总是在ENTRYPOINT之前发生,你在ENTRYPOINT中定义redis server start,在RUN中定义init.sh,所以init.sh总是在redis server start之前运行,即使你是sleep 1000000s,@在您运行 init.sh 之前,987654353@ 不会启动。您的更新与我回答中的解决方案完全不同。
  • 试试CMD ["&lt;my config file&gt;"]
  • 或将docker-entrypoint.sh $1改为docker-entrypoint.sh $@
【解决方案2】:

这是另一种可行的解决方案,尽管@atline 解决方案更优雅。

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"

这工作并使用提供的redis.conf,因此它将数据播种到您在配置中定义的.rdb

【讨论】:

    猜你喜欢
    • 2015-04-27
    • 1970-01-01
    • 1970-01-01
    • 2017-10-04
    • 2014-02-09
    • 2020-06-03
    • 2014-11-12
    • 2018-04-28
    • 2019-06-14
    相关资源
    最近更新 更多