Skip to content

[Feature Request] Consider adding a script to import local resolver from /etc/resolv.conf #673

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

Closed
ikreymer opened this issue Jun 13, 2022 · 11 comments · Fixed by #786
Closed

Comments

@ikreymer
Copy link

Since this image does not support the resolver local=on; that is only available in OpenResty, it may be useful to add an init script that optionally parses the nameserver from /etc/resolv.conf and sets it as the resolver, as suggested in:
https://serverfault.com/questions/638822/nginx-resolver-address-from-etc-resolv-conf

Seems like this may be a common enough use case and could be enabled with a NGINX_USE_LOCAL_RESOLVER

@dsech
Copy link

dsech commented Sep 1, 2022

I assume it would be enough to take the nameserver from /etc/resolv.conf and make it available as an environment variable that can be referenced in nginx conf templates. That way we can configure our own resolver directives.

Regardless what approach will be used, I'd love to see the feature implemented.

@thresheek
Copy link
Member

Hi @ikreymer @dsech.

Would the change suggested in #687 work for you?

@dsech
Copy link

dsech commented Sep 12, 2022

Hi @thresheek

Just to make sure I understand correctly the feature in #687 ,
For example, if I create the file /docker-entrypoint.d/docker_ns.envsh, which does an export DOCKER_NAMESERVER=<something here>, then the variable DOCKER_NAMESERVER will be correctly replaced by envsubst in the configuration templates?

If yes, then I guess it's really helpful for this feature request.

But I'd like to make the case why this DOCKER_NAMESERVER env variable should be available/included by default in the official nginx docker image:

Why? Because doing an apparently simple thing, like a proxy_pass to a hostname instead of IP address, suddenly becomes really complicated. This is because nginx, by default, resolves the hostname only once - when the config is (re)loaded. Any IP changes for that hostname will cause connection errors until nginx is reloaded. And there are plenty of people trying to solve this issue:

Even in this repo in the past: #600 #127

A lot of the proposed fixes online are doubtful, like hardcoding a public DNS IP, or hardcoding the default docker DNS IP - 127.0.0.11. This reduces the portability of the docker image, because in some docker environments, those values are be incorrect.

But there is a really good solution: extract the nameserver IPs from /etc/resolv.conf at container start, and use these in the nginx resolver directives. And, while #687 makes it easier to expose an env variable containing the nameserver, the shell script still needs to be written, and will be identical for anyone attempting to solve this problem.

@thresheek
Copy link
Member

Hi @dsech!

Your understanding is correct, take a look at the following snippet for illustrations on how it works:

thresh@laptop exports $ cat 10-exports.envsh
#!/bin/sh

export NGINX_RESOLVER="127.1.0.1"

thresh@laptop exports $ ls -ld 10-exports.envsh
-rwxr-xr-x  1 thresh  staff  45 12 Sep 16:07 10-exports.envsh

thresh@laptop exports $ cat server.conf.template
resolver $NGINX_RESOLVER;

server {
    listen 8080;
    server_name example.com;
    location / { return 200 'OK\n'; }
}

thresh@laptop exports $ docker run -v $(pwd)/10-exports.envsh:/docker-entrypoint.d/10-exports.envsh -v $(pwd)/server.conf.template:/etc/nginx/templates/server.conf.template -d --name nginx nginx:current
89ceeb069782718c2917f41db67ac76478f8d01f5ff306276c470ef44eb9c85d

thresh@laptop exports $ docker logs nginx
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/10-exports.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
20-envsubst-on-templates.sh: Running envsubst on /etc/nginx/templates/server.conf.template to /etc/nginx/conf.d/server.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2022/09/12 12:12:08 [notice] 1#1: using the "epoll" event method
2022/09/12 12:12:08 [notice] 1#1: nginx/1.23.1
2022/09/12 12:12:08 [notice] 1#1: built by gcc 11.2.1 20220219 (Alpine 11.2.1_git20220219)
2022/09/12 12:12:08 [notice] 1#1: OS: Linux 5.19.6-200.fc36.aarch64
2022/09/12 12:12:08 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 524288:524288
2022/09/12 12:12:08 [notice] 1#1: start worker processes
2022/09/12 12:12:08 [notice] 1#1: start worker process 32
2022/09/12 12:12:08 [notice] 1#1: start worker process 33

thresh@laptop exports $ docker exec nginx cat /etc/nginx/conf.d/server.conf
resolver 127.1.0.1;

server {
    listen 8080;
    server_name example.com;
    location / { return 200 'OK\n'; }
}

Note that it does not propagate an environment variable into the image, just makes it available during the preconfiguration run.

I don't really see the point of including such a script that will automatically make it available for any and all entrypoints. The reasoning is that since if you're writing your own (admittedly somewhat complex) configuration for a proxy, it's not a lot harder to include a simple shell script to figure out what resolvers need to be configured in your own environment. That's exactly what the docker-entrypoint.d construct is supposed to do: easily extend configuration for more complex scenarios.

Hope this makes sense,

@dsech
Copy link

dsech commented Sep 16, 2022

Hey @thresheek In that case #687 is really helpful for me, thank you!

The reasoning is that since if you're writing your own (admittedly somewhat complex) configuration for a proxy, it's not a lot harder to include a simple shell script to figure out what resolvers need to be configured in your own environment.

I understand, I only suggested it because it feels like a common pattern. Well, common at least for someone who needs to configure a resolver/proxy.

And it is true, if someone needs a custom nameserver then it is really easy to include it in the configuration, hardcoded or as a user-defined env variable.

But, at least personally - the complicated part was when I wanted to use the nameserver already configured in the container - in which case I had to figure out I needed exactly this specific script placed in this specific directory.

And I respect your decision that it doesn't make sense to include the entire script and exposing this predefined env variable by default, but I had to make the suggestion :)

@thresheek
Copy link
Member

Thanks for the explanation. I believe we can try to tackle this complexity by documenting it in the image docs.

@ikreymer
Copy link
Author

ikreymer commented Apr 21, 2023

Hi @ikreymer @dsech.

Would the change suggested in #687 work for you?

Sorry I missed this earlier! Not quite - I was suggesting a built-in entrypoint script that would specifically set the resolver to the nameserver in /etc/resolv.conf

I think the cause of the issue is mentioned here: https://www.nginx.com/blog/dns-service-discovery-nginx-plus/

When you use a variable to specify the domain name in the proxy_pass directive, NGINX re‑resolves the domain name when its TTL expires. You must include the resolver directive to explicitly specify the name server (NGINX does not refer to /etc/resolv.conf as in the first two methods).

But, I want it to refer to /etc/resolv.conf!

Now, it seems like OpenResty does provide this functionality

Similar to the resolver directive in standard nginx core with additional support for parsing additional resolvers from the resolv.conf file format.
When local=on, the standard path of /etc/resolv.conf will be used. You may also specify arbitrary path to be used for parsing, for example: local=/tmp/test.conf.

My suggestion was to add a built-in script that approximates this OpenResty functionality, and could be enabled (kind of like the auto-tune worker processes one can be enabled) by setting an environment variable, eg. NGINX_USE_LOCAL_RESOLVER=1. It seems like wanting to do this is a common enough use case.

Until then, I've been using this suggestion:

echo resolver $(awk 'BEGIN{ORS=" "} $1=="nameserver" {print $2}' /etc/resolv.conf) ";" > /etc/nginx/resolvers.conf

from https://serverfault.com/questions/638822/nginx-resolver-address-from-etc-resolv-conf

It sounds like with the changes mentioned above, I could instead export a variable instead of using echo, but still need to add a custom script. It would be great if this could instead be a builtin entrypoint script.

thresheek added a commit to thresheek/docker-nginx that referenced this issue May 25, 2023
If enabled via NGINX_ENTRYPOINT_LOCAL_RESOLVERS variable, this script
will populate NGINX_LOCAL_RESOLVERS variable that can be used in the
envsubst-base templating to populate the configuration files.

Fixes nginx#673
@thresheek
Copy link
Member

Thank you for such a great feedback @ikreymer and @dsech !

The linked PR implements the following logic:

  • if NGINX_ENTRYPOINT_LOCAL_RESOLVERS is defined, export NGINX_LOCAL_RESOLVERS variable to use in envsubst scripts

My thinking is that it would be enough for the image users to be able to use it for templating - e.g. creating a resolver.template is fairly straightforward, and I don't think it makes much sense to edit the nginx.conf like we do for worker processes auto-tune.

Please let me know what you think,

@daliborfilus
Copy link

I found this feature while I was exploring the image. Can this be added to dockerhub's nginx image documentation? Thank you all for this issue and PR, it's just what I needed to make my image compatible both with docker daemon and kubernetes.

@calvin-furano-dts
Copy link

Thank you for implementing this, it perfectly solves my use case.

I also found this feature after exhausting other routes and digging through the source code. I second the request to add this to the dockerhub and github documentation to save others some time.

@alimd
Copy link

alimd commented Feb 26, 2025

I’ve implemented a better idea that could be useful for others.

I have a $NGINX_RESOLVERS variable that can be configured in the container environment or used as the default in the Dockerfile. Additionally, it can be set to local.

If $NGINX_RESOLVERS is set to local, this script will set it to the local resolvers from /etc/resolv.conf and export them.

For more information, please check the source code: https://github.com/Alwatr/nginx/blob/next/nginx/etc/nginx/entrypoint.d/10-local-resolvers.envsh

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants