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
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.
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?
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?
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.
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.
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)
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.
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).
Thanks, that's interesting. A few concerns:
@minrk you understand this stuff better than I do, could you take a look at the suggestion above?
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:
PUBLIC_PORT or PUBLIC_URL env.--net host and local dockerdocker-machine ip is what should be used, not localhost or any local ip discoverable from inside the containerThe 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.
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.
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?
Basically, yes. Set the display_url directly, without anything being generated from other values.
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).
Most helpful comment
Ok so I created PR #3668 to implement this. I kept the
connect_hostname for now, but maybe it could be changed to something more explicity likeoverride_display_host.Note that I also implemented it to override the
protocolandbase_urlparts of the URL (as with my previous comment).