Git Product home page Git Product logo

Comments (4)

nicolasff avatar nicolasff commented on June 30, 2024

Hello @cevanno,

You can't access the port because Redis is listening only on 127.0.0.1 by default, and it looks like EXPOSE needs the port to be bound on eth0 instead of just the loopback interface. In the Redis config file used in the Webdis image, this is configured by the line bind 127.0.0.1 -::1, it's part of the default Redis config.

You can instruct Redis to listen on all interfaces within the container, just like Webdis has to do with http_host set to 0.0.0.0. I wasn't sure whether adding a new bind line to redis.conf would work or if the default one would need to be removed, so I tried just adding the new line at the end of the file and it seems to work.

As documented in redis.conf, to listen on all interfaces you need bind * -::*. You can add it to the Dockerfile on the same line as the daemonize change, just make sure to use single quotes around the string to make sure the * isn't expanded by the shell.

With the EXPOSE change that you've already found, the full diff for Dockerfile is then:

diff --git Dockerfile Dockerfile
index d77b5ee..8cec061 100644
--- Dockerfile
+++ Dockerfile
@@ -14,7 +14,7 @@ FROM alpine:3.14.3
 RUN apk update && apk add libevent msgpack-c 'redis>6.2.6' openssl libssl1.1 libcrypto1.1 && rm -f /var/cache/apk/* /usr/bin/redis-benchmark /usr/bin/redis-cli
 COPY --from=stage /usr/local/bin/webdis /usr/local/bin/webdis-ssl /usr/local/bin/
 COPY --from=stage /etc/webdis.prod.json /etc/webdis.prod.json
-RUN echo "daemonize yes" >> /etc/redis.conf
+RUN echo "daemonize yes" >> /etc/redis.conf && echo 'bind * -::*' >> /etc/redis.conf
 CMD /usr/bin/redis-server /etc/redis.conf && /usr/local/bin/webdis /etc/webdis.prod.json

-EXPOSE 7379
+EXPOSE 7379 6379

I built it as webdis:issue229 with the following command:

docker build -t webdis:issue229 .

and then ran it as a foreground container in the same terminal:

docker run --rm -p 127.0.0.1:7379:7379 -p 127.0.0.1:6379:6379 webdis:issue229

In a separate terminal, I was able to verify that both HTTP requests and redis-cli commands worked as expected:

$ curl -s http://127.0.0.1:7379/PING
{"PING":[true,"PONG"]}

$ redis-cli ping
PONG

As for the two config files, the idea is to have two different files for two use cases:

webdis.json is the default file name that Webdis looks for if no parameters are passed in, so this is for people who have just built Webdis by running make and now just want to try it out locally with ./webdis

webdis.prod.json is a bit different and is provided for people who want to build a package or a Docker image and run it as a service. This is why it's the file referenced in Dockerfile, where it is used to build the docker image. The fact that it's actually being edited in Dockerfile to change the daemonize setting is kind of a legacy artifact. Webdis predates Docker by several years, and before containers we'd generally run services as local daemons so "daemonize": true made sense. This doesn't work when it's the front-facing process in a container.

From what I understand it sounds like you're planning to run a custom image with these minor port changes, so if you want to also enable websockets you'd need to either make the change in webdis.prod.json before building the image, or to edit the sed command that edits webdis.prod.json in the Dockerfile. This is because unlike in redis.conf you can't simply append a line to this JSON file, so you'd need to insert "websockets": true, somewhere in the middle. You can try doing it this way:

-RUN sed -i -e 's/"daemonize":.*true,/"daemonize": false,/g' /etc/webdis.prod.json
+RUN sed -i -e 's/"daemonize":.*true,/"daemonize": false, "websockets": true,/g' /etc/webdis.prod.json

Make sure not to miss the trailing comma after true, just like there was one at the end of "daemonize": false,.

With all of these changes, the full Dockerfile could look like this:

FROM alpine:3.14.3 AS stage
LABEL maintainer="Nicolas Favre-Felix <[email protected]>"

RUN apk update && apk add wget make gcc libevent-dev msgpack-c-dev musl-dev openssl-dev bsd-compat-headers jq
RUN wget -q https://api.github.com/repos/nicolasff/webdis/tags -O /dev/stdout | jq '.[] | .name' | head -1  | sed 's/"//g' > latest
RUN wget https://github.com/nicolasff/webdis/archive/$(cat latest).tar.gz -O webdis-latest.tar.gz
RUN tar -xvzf webdis-latest.tar.gz
RUN cd webdis-$(cat latest) && make && make install && make clean && make SSL=1 && cp webdis /usr/local/bin/webdis-ssl && cd ..
RUN sed -i -e 's/"daemonize":.*true,/"daemonize": false, "websockets": true,/g' /etc/webdis.prod.json

# main image
FROM alpine:3.14.3
# Required dependencies, with versions fixing known security vulnerabilities
RUN apk update && apk add libevent msgpack-c 'redis>6.2.6' openssl libssl1.1 libcrypto1.1 && rm -f /var/cache/apk/* /usr/bin/redis-benchmark /usr/bin/redis-cli
COPY --from=stage /usr/local/bin/webdis /usr/local/bin/webdis-ssl /usr/local/bin/
COPY --from=stage /etc/webdis.prod.json /etc/webdis.prod.json
RUN echo "daemonize yes" >> /etc/redis.conf && echo 'bind * -::*' >> /etc/redis.conf
CMD /usr/bin/redis-server /etc/redis.conf && /usr/local/bin/webdis /etc/webdis.prod.json

EXPOSE 7379 6379

I hope this helps! Let me know if you're still blocked after this.

from webdis.

cevanno avatar cevanno commented on June 30, 2024

Thanks! I had found the same kinds of solutions. Your solutions for the redis.conf file did not work without a little tweak and gives this error:
docker: Error response from daemon: Ports are not available: exposing port TCP 127.0.0.1:6379 -> 0.0.0.0:0: listen tcp 127.0.0.1:6379: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.

Removing the 127.0.0.1's let it start but all pubsub SUBSCRIBE messages get the "Error: Server closed the connection"
so I modified your RUN command to:
RUN echo "daemonize yes" >> /etc/redis.conf && echo 'bind * -::*' >> /etc/redis.conf && echo 'protected-mode no' >> /etc/redis.conf

And this works in my environment. Since your test passed there is some difference in our environments that might be a problem?

A problem I see with your fix to
RUN sed -i -e 's/"daemonize":.*true,/"daemonize": false, "websockets": true,/g' /etc/webdis.prod.json
Is that the input file comes from your wget image and not from the local file. My solution to this was to simply copy the local file in before the sed call like this:

COPY webdis.prod.json /etc/webdis.prod.json
RUN sed -i -e 's/"daemonize":.*true,/"daemonize": false,/g' /etc/webdis.prod.json

Do you agree this is better?

from webdis.

nicolasff avatar nicolasff commented on June 30, 2024

Glad you managed to make progress!

I'm surprised you got an error with the -p command, the IP part is definitely supported (documented here) and I use it often. What it does is listen only on localhost on the docker host, so that you can connect from the same host but the port is not exposed to other hosts on all the networks you're connected to:

Note that ports which are not bound to the host (i.e., -p 80:80 instead of -p 127.0.0.1:80:80) will be accessible from the outside.

The error message you pasted is saying that you already have a process listening on port 6379, likely either from a previous Webdis+Redis container that was still running or from a local Redis service. You can find this mystery process with the following command on macOS:

sudo lsof -i -P -n | grep LISTEN | grep :6379

or this way on most Linux distros:

sudo netstat -nlp | grep :6379

Reading the documentation for protected-mode, your proposed change seems fine to me. I only tried to run PING over HTTP and with redis-cli as shown above, but this doesn't say anything about different commands behaving differently; rather this is a restriction at connection time even before the command is received. So yes this should be ok, but I can't tell why you actually need it. The fact that the command was a SUBSCRIBE should not make a difference: see the implementation in Redis for reference, which is closing the connection early without reading from it.

So I'm not sure why we're observing some differences in behavior, it's certainly unexpected given that Docker is supposed to be a way to neatly package and run software the same way regardless of the host OS. For what it's worth I ran the commands above with Docker Desktop 4.15.0 (93002) on macOS 12.6. It's possible that the way Redis sees the incoming connection as being made on loopback or not depends on the version of Docker and the host it runs on, hence the difference in whether protected-mode is needed. I could certainly see this happening, although I've never encountered this particular issue myself and I would see it as an inconsistency in Docker if not a bug.

the input file comes from your wget image and not from the local file
[…]
Do you agree this is better?

This is really up to you, and how you want to manage this file. I don't think there's a fundamental difference, you can achieve the same results either way. I'd say the main difference is this: the advantage of pulling the file from the downloaded archive is that you only need to maintain the Dockerfile changes, while the advantage of having a local copy is that you can tune it locally to your liking and can remove the RUN sed… command from the Dockerfile (having changed daemonize in the local copy). I think it's really a matter of preference and how you want to do this; if I was in your situation I would probably maintain a custom local copy as well.

from webdis.

cevanno avatar cevanno commented on June 30, 2024

OK, all good. Yes I had a zombie process as you suspected and after removing it the -p 127.0.0.1:6379:6379 form ran with 'docker run'
However, try as I might it would not accept a connection without 'protected-mode no'. My read of the documentation is the same as yours but I suspect the loopback address is on the docker image so message attempts from Windows Postman or another WSL linux vm are considered 'foreign'
Thank you so much.

from webdis.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.