Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

localhost is replaced by the docker container name in 5.5.0 #3605

Closed
max-sixty opened this issue May 11, 2018 · 14 comments · Fixed by #3668
Closed

localhost is replaced by the docker container name in 5.5.0 #3605

max-sixty opened this issue May 11, 2018 · 14 comments · Fixed by #3668

Comments

@max-sixty
Copy link

max-sixty commented May 11, 2018

In 5.4.1, the url is printed and works as localhost, below

But with 5.5.0, 8bc26195e104 appears.
With docker ps, I can see that the docker Container ID is 8bc26195e104

docker run -p 8889:8888 gcr.io/sixty-capital/base:1.18.6 jupyter-notebook --ip="*" --no-browser --allow-root

Generating a 2048 bit RSA private key
...................................................................................+++
...............+++
writing new private key to '/root/.local/share/jupyter/notebook.pem'
-----
[I 00:24:41.730 NotebookApp] Writing notebook server cookie secret to /root/.local/share/jupyter/runtime/notebook_cookie_secret
[I 00:24:42.030 NotebookApp] Serving notebooks from local directory: /notebooks
[I 00:24:42.030 NotebookApp] 0 active kernels
[I 00:24:42.030 NotebookApp] The Jupyter Notebook is running at:
[I 00:24:42.031 NotebookApp] https://8bc26195e104:8888/?token=7e2e428b6b07c921c0fd16b87b25d89bc46b18c1685a8e84
[I 00:24:42.031 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 00:24:42.032 NotebookApp]

    Copy/paste this URL into your browser when you connect for the first time,
    to login with a token:

        https://8bc26195e104:8888/?... <--- here

# in 5.4.1 this is https://localhost:8888/?...

For reference, here's the config file:

# from https://github.com/jupyter/docker-stacks/blob/master/base-notebook/jupyter_notebook_config.py

from jupyter_core.paths import jupyter_data_dir
import subprocess
import os
import errno
import stat

PEM_FILE = os.path.join(jupyter_data_dir(), 'notebook.pem')

c = get_config()
c.NotebookApp.ip = '*'
c.NotebookApp.port = 8888
c.NotebookApp.open_browser = False

# Set a certificate if USE_HTTPS is set to something truthy
if os.environ.get('JUPYTER_USE_HTTPS', None):
    if not os.path.isfile(PEM_FILE):
        # Ensure PEM_FILE directory exists
        dir_name = os.path.dirname(PEM_FILE)
        try:
            os.makedirs(dir_name)
        except OSError as exc:  # Python >2.5
            if exc.errno == errno.EEXIST and os.path.isdir(dir_name):
                pass
            else:
                raise
        # Generate a certificate if one doesn't exist on disk
        subprocess.check_call([
            'openssl', 'req', '-new', '-newkey', 'rsa:2048', '-days', '365', '-nodes', '-x509', '-subj',
            '/C=XX/ST=XX/L=XX/O=generated/CN=generated', '-keyout', PEM_FILE, '-out', PEM_FILE
        ])
        # Restrict access to PEM_FILE
        os.chmod(PEM_FILE, stat.S_IRUSR | stat.S_IWUSR)
    c.NotebookApp.certfile = PEM_FILE

# our code - not copied from the link at top
notebook_dir = os.environ.get('JUPYTER_NOTEBOOK_DIR')

if notebook_dir:
    try:
        os.makedirs(notebook_dir)
    except:
        pass
    c.NotebookApp.notebook_dir = notebook_dir
@takluyver
Copy link
Member

Does the new URL work if you use that in your browser?

This was changed in #3356 and refined in #3593. The idea is that when it's asked to listen on all interfaces, it shows the hostname of the computer it's running on, which can work from another computer.

Your docker command maps the container port to a port on localhost, but the notebook running in the container doesn't know about that. E.g. you could ask Docker to put it on a different port, like 8889, and it would print out the URL with 8888, because that's what the notebook server knows about. So it's kind of a coincidence that the localhost:8888 URL works, albeit a fairly common coincidence.

@max-sixty
Copy link
Author

Right, that description makes sense

Does the new URL work if you use that in your browser?

Unfortunately not. IIRC localhost also did not work (though that doesn't seem consistent from your description, and I've reverted the upgrade so can't easily retest)

Could we print an address that's likely to work? Or a config for the address to print?

@takluyver
Copy link
Member

Could we print an address that's likely to work?

That's what this is trying to do! It's hard to get right, though. The server doesn't know what name or IP address you need to reach the system it's running on - especially when there's port forwarding, tunnelling or proxying involved.

Or a config for the address to print?

It's probably reasonable to have a way to override this. Do you want to make a PR?

@max-sixty
Copy link
Author

It's hard to get right, though.

👍

We could know that a hash is unlikely to be correct, but could we in a systematic way? I'm not up on my networking - is my local called localhost?

Do you want to make a PR?

Yes. I'm going on vacation for ten days now, but let's see if anyone has any improvement over the config, and then I will.

@takluyver
Copy link
Member

We could know that a hash is unlikely to be correct, but could we in a systematic way?

I don't even know that it's unlikely to be correct. If you're managing a lot of machines, maybe you automatically assign hostnames in that format. I suspect that's what docker is doing, but you need some extra option to make them reachable at that name.

I'm not up on my networking - is my local called localhost?

Yup, localhost is a special name for the computer to access itself. But I think in the case of docker, each container counts as a separate machine, so something running in docker is not on 'localhost', unless you use the -p option to forward the port.

@max-sixty
Copy link
Author

max-sixty commented May 15, 2018

Thanks for the replies

But I think in the case of docker, each container counts as a separate machine, so something running in docker is not on 'localhost', unless you use the -p option to forward the port.

TBC, this was forwarding the port (otherwise I don't see how it could work with localhost either)

@ftobia
Copy link

ftobia commented May 22, 2018

I am running into this issue as well. A bugfix that would work for me is adding a param where the base URL can be overridden.

@brasie
Copy link

brasie commented May 26, 2018

Borrowing from discussion in a StackOverflow thread, one could replace the ip = socket.gethostname() with something like:

with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    s.connect(('<broadcast>', 0))
    ip = s.getsockname()[0]

When running within Docker, this gives a URL of http://172.17.0.2:8888/?token=.... The 172.17.0.2 address is the container's IP within its Docker network, and I am able to connect to the URL in my browser running on the same physical machine (without doing any -p port forwarding, even).

@takluyver
Copy link
Member

Thanks, that's interesting. A few concerns:

  • Is it reliably quick? We've run into some issues before where certain operations involving sockets can be very slow (30 or 60 seconds) depending on the network configuration and state.
  • If the machine has multiple IPs, how likely is it that it's getting the one we will want to use? I don't think there's any way to get that 100% assured, but I don't know how to make the best guess.
  • The SO answer with that snippet says "This should work on any LAN that allows UDP broadcast". What's the failure mode if UDP broadcast is not allowed? And how common is that?

@minrk you understand this stuff better than I do, could you take a look at the suggestion above?

@minrk
Copy link
Member

minrk commented Jun 1, 2018

I don't believe it's possible to get the right answer every time, or even most of the time, inside a container because the container doesn't know what port or ip is accessible from the outside:

  • the port may not match (and typically doesn't), and the actual accessible port cannot be retrieved from inside the container unless we add something explicit like a PUBLIC_PORT or PUBLIC_URL env.
  • the ip may not match, and there are lots of different right answers, depending on context not available inside the container:
    • localhost generally works for --net host and local docker
    • docker ip is right if docker network is local (not with docker-machine)
    • with docker-machine, the docker-machine ip is what should be used, not localhost or any local ip discoverable from inside the container

The only general solution, I think, is to have a "connect_host" config that overrides the connectable address to be echoed here. It should include both the hostname and port in order to handle the fact that the port cannot be known from inside the container. This would have to come from the user manually at start time. With that, we could have:

docker run --rm -e JUPYTER_CONNECT_HOST=http://$(docker-machine ip mymachine):9999 -p9999:8888 -it jupyter/base-notebook

echo the correct URL:

http://192.168.99.100:9999/?token=...

From the utilities we already have, we can change the default something like what @BradenAtSiemens proposes, by echoing the first or all of jupyter_client.localinterfaces.public_ips(), which gives

['172.17.0.3']

in a docker container. However, for me (on macOS), this ip does not work, only localhost does, so we could either show both this ip and localhost, or just go back to using localhost in this case.

@takluyver
Copy link
Member

I've tagged this as 'help wanted' for someone to have a go at implementing @minrk 's suggestion in the message above, to make a configurable connect_host option.

@tomjorquera
Copy link
Contributor

tomjorquera commented Jun 7, 2018

If I understood @minrk proposal correctly, the proposal would be to add a new option to NotebookApp to override the url string in display_url. Is that right?

Seems to me that in the general case, it would be useful for this option to also override the protocol part (also the base_url maybe?). For example, when running the service as http behind a https proxy. What do you think?

@minrk
Copy link
Member

minrk commented Jun 7, 2018

Basically, yes. Set the display_url directly, without anything being generated from other values.

@tomjorquera
Copy link
Contributor

Ok so I created PR #3668 to implement this. I kept the connect_host name for now, but maybe it could be changed to something more explicity like override_display_host.

Note that I also implemented it to override the protocol and base_url parts of the URL (as with my previous comment).

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 9, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants