nicolasff / webdis

A Redis HTTP interface with JSON output
https://webd.is
BSD 2-Clause "Simplified" License
2.82k stars 307 forks source link

Trouble connecting docker image to external redis server #232

Open VagyokC4 opened 1 year ago

VagyokC4 commented 1 year ago

I'm trying to test this with your docker image, and I'm not having any luck getting it to talk to my Redis server instance.

curl http://localhost:7379/SET/hello/world
curl http://localhost:7379/GET/hello

The commands above work as expected, but I'm not sure where this data is being written because my redis database is empty, even though I get a result back from webdis

I started it with the following command:

docker run -d --name Webdis --restart=unless-stopped -p 7379:7379 -v d:/redis-data/webdis/webdis.json:/etc/webdis.json nicolas/webdis

using the following webdis.json

{
    "redis_host": "redis-****.cloud.redislabs.com",
    "redis_port": 6379,
    "redis_auth": "****",
    "http_host": "0.0.0.0",
    "http_port": 7379,
    "threads": 5,
    "pool_size": 20,
    "daemonize": false,
    "log": "verbose",
    "websockets": false,
    "acl": [
        {
            "disabled": ["DEBUG", "FLUSHALL", "FLUSHDB"],
            "http_basic_auth": "*:*"
        }
    ]
}

Where is this data being stored, and how can I get it to talk with my specific instance?

VagyokC4 commented 1 year ago

I see now it's looking for webdis.prod.json, not webdis.json

nicolasff commented 1 year ago

I'm glad you figured it out. By the way, it does use webdis.prod.json because that's just what the command is set to in the Dockerfile, but you can certainly replace it when you run Webdis.

The docker run command you shared above uses the following mount:

-v d:/redis-data/webdis/webdis.json:/etc/webdis.json

This is the right way to inject the file, but you left the command (CMD) to its default value, which also means that it will start a Redis instance in the container – although it won't be used for anything. See here: https://github.com/nicolasff/webdis/blob/f4efbfd3349fd94a3dfa1081f1537de99dadb510/Dockerfile#L21

To use your own config file, you just need to tell docker run what command to start the container with, after giving it the name of the image. In your case, you can keep the injected file as /etc/webdis.json and run Webdis with its path as a parameter, like this:

docker run -d --name Webdis --restart=unless-stopped -p 7379:7379 -v d:/redis-data/webdis/webdis.json:/etc/webdis.json nicolas/webdis /usr/local/bin/webdis /etc/webdis.json

I'll be adding a documentation page shortly that will provide detailed steps for how to run Webdis with an external Redis instance. This use case seems common enough that it's worth having a dedicated page for.

VagyokC4 commented 1 year ago

I'm glad you figured it out. By the way, it does use webdis.prod.json because that's just what the command is set to in the Dockerfile, but you can certainly replace it when you run Webdis.

Yeah I was going in circles for a min. trying to figure out why I was reading/writing from Postman, but nothing appeared in my redis instance. Once I saw it was using .prod.json all became clear and worked as expected.

I'll be adding a documentation page shortly that will provide detailed steps for how to run Webdis with an external Redis instance. This use case seems common enough that it's worth having a dedicated page for.

Maybe make an image that is ONLY Webdis (webdis-server), without the redis bits? For my use case we will create Pods that run this image so the lighter the better.

I could see a webdis (what you have now), a webdis-stack which would embed the redis stack, and a webdis-server which is only webdis. The only other suggestion is to maybe go though and add the all new Redis commands so that manually adding them to the config is not necessary. Thoughts?

nicolasff commented 1 year ago

Maybe make an image that is ONLY Webdis (webdis-server), without the redis bits?

Yes, I've thought about it too. I'll look into it next time I prepare a release. My goal with the Docker images was really to make it as simple as possible to try out Webdis, but I realize that this isn't really the image you'd want to use if you're using Webdis within an existing architecture.

Another benefit of this is that while the images are already tiny (Docker Hub says 8.23 MB for the current latest) they would be even smaller without embedding Redis.

In the meantime I'll take a look at how other "connected" services build their images; by this I mean services/software that you would generally not run on its own but with the service connecting to other instances in the environment, instances it depends on. Docker Compose provides a reasonably simple way to do this, but I'm not sure whether it would be what Webdis users expect to use.

As for this point:

The only other suggestion is to maybe go though and add the all new Redis commands so that manually adding them to the config is not necessary.

I'm not sure what you mean here. By default Webdis disables the DEBUG command (because of the baffling existence of DEBUG SEGFAULT which was introduced before enable-debug-command). The default acl config also provides an example of how to use basic auth for HTTP requests: https://github.com/nicolasff/webdis/blob/f4efbfd3349fd94a3dfa1081f1537de99dadb510/webdis.prod.json#L15-L24

If you want all commands to be allowed, just remove any entry under acl that disables commands. This config lets you execute any command, for example:

"acl": [ ],

Disabling only DEBUG should not affect FT.<something> commands, they should be allowed. Now that enable-debug-command exists, "acl": [] might be a better default value to use.

There is also a wildcard "*" command to match all, if you want to create a super user for example. That said, the ACL feature in Webdis was added way back in 2011, when Redis didn't have an ACL command. These days it might be better to configure all this within Redis itself.

VagyokC4 commented 1 year ago

Yes, I stand corrected. The issues I faced were purely the bad configuration. I can confirm that no commands needed to be enabled in the configuration for it to work.

nicolasff commented 1 year ago

Cool, I'm glad it worked.

By the way, I've made some final tweaks to the docs page I mentioned on the other GitHub issue and pushed it. I've listed it first in the docs README since it feels like a common use case, with the title "Running Webdis in Docker with an external Redis instance".

This is not the first GitHub issue in recent months where a question eventually led to a docs page; I hope these can be useful. I still need to make the docs more prominent on the repo's main page, but first need to review the README to see if it might make sense to extract more of it there. In any case, thanks for bringing this up! It made Webdis better :-)

VagyokC4 commented 11 months ago

Hi @nicolasff I was putting together a linqpad example for you and started running into some issues again.

using this config

{
    "redis_host": "localhost",
    "redis_port": 63791,
    "verbosity": 8,
    "logfile": "/dev/stdout",
    "http_host": "0.0.0.0",
    "http_port": 7379,
    "threads": 5,
    "pool_size": 20,
    "daemonize": false,
    "websockets": false,
    "acl": [
        {
            "disabled": ["DEBUG", "FLUSHALL", "FLUSHDB"],
            "http_basic_auth": "*:*"
        }
    ]
}

I'm using this image docker run -d --name Redis-Stack-7-AOF -v c:/redis-data/:/data/ --restart=unless-stopped -e REDIS_ARGS="--save --appendonly yes" -e REDISEARCH_ARGS="MAXSEARCHRESULTS -1 MAXAGGREGATERESULTS -1 MAXDOCTABLESIZE 3000000 TIMEOUT 0" -p 63791:6379 redis/redis-stack-server:latest

which I can connect to externally, no problem image

But webdis is giving me 503 service unavailable. image

However, I can connect to other instances and it works no problem.

image

image

And I can change the docker command above to use the default redis port 6379 and it all works as well.

image image

I have verbose on 8, yet I'm not seeing anything that would tell me what the problem is (while pointing to 63791). Any ideas?

VagyokC4 commented 11 months ago

I'm wondering if the internal instance of Redis that webdis has is conflicting here.

I can confirm things work when I'm using a Redis not on my localhost. I can still reproduce the GET/POST issue, but my workaround seems to get me a result.

While using localhost instances, while I was able to PING the underlying redis, trying to execute some RedisStack commands give error image

Which makes me think Webdis is pointing to it's internal instance even though from my outside host I can hit the redis stack instance using localhost:6379. I think this also explains why when I wire up to 63791, it cannot find a redis sitting there, because inside the docker instance it can't get to 63791.

Were you able to put together a -server image that does not include the internal Redis instance / internal network? That may help eliminate some of the not needed moving pieces.

nicolasff commented 10 months ago

Hey Charles, sorry I had missed this issue.

Which makes me think Webdis is pointing to it's internal instance even though from my outside host I can hit the redis stack instance using localhost:6379

localhost inside a Docker container is not the same host as on your computer. You can tell this by running Webdis with its default settings where it connects to Redis on localhost:6379, notice how this works even when you don't have Redis running on your machine. What does this tell us? That it connects to the embedded Redis and not the one on the host machine, since localhost in a container is a concept entirely limited to the scope of this container.

This is easy to validate, really. You can run Redis on your local machine and verify that it's listening on localhost:6379:

$ redis-cli -h localhost -p 6379 PING
PONG

and then install redis-cli in a simple container before trying the same command:

$ docker run --rm -ti alpine:3.18.3 /bin/sh
/ # apk add redis
fetch https://dl-cdn.alpinelinux.org/alpine/v3.18/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.18/community/x86_64/APKINDEX.tar.gz
(1/1) Installing redis (7.0.12-r0)
Executing redis-7.0.12-r0.pre-install
Executing redis-7.0.12-r0.post-install
Executing busybox-1.36.1-r2.trigger
OK: 10 MiB in 16 packages
/ # redis-cli -h localhost -p 6379 PING
Could not connect to Redis at localhost:6379: Connection refused

As expected, this fails since Redis is not running in this container (yes we've just installed it, but it's still not running).

So yes, if you tell Webdis to connect on localhost, it will connect to its embedded Redis.

What you are trying to do seems common enough that there's a Stack Overflow post about it with thousands of votes: "From inside of a Docker container, how do I connect to the localhost of the machine?".

As for this part:

Were you able to put together a -server image that does not include the internal Redis instance / internal network?

As I mentioned in the other issue, publishing images of Webdis takes quite a while. I have a list of steps I go through which currently has 33 steps, and signing all the different images requires a custom script that's over 450 lines long. This part requires entering 5 different passwords – most of them multiple times – to pass an API token to Docker Hub, to sign images with the Trust Key, the Root Key, the Repository Key, and then to auth with AWS to publish the images there.

You can read about the complex relation between images and various keys in the docs, described in detail by this diagram.

So no, I really can't do one-off releases when all it would take for you to test a custom image would be to edit the Dockerfile as you wish and then run docker build like this:

docker build -t webdis:custom .