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

feature: implemented a configure_by_lua phase #1259

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

thibaultcha
Copy link
Member

@thibaultcha thibaultcha commented Feb 16, 2018

I hereby granted the copyright of the changes in this pull request
to the authors of this lua-nginx-module project.

This phase's purpose would be to allow the configuration of NGINX/ngx_lua with Lua scripts. This PR proposes a first draft with just a few APIs, hoping that more APIs can be added progressively.

Ultimately, I cherish the vision to some day being able to dynamically configure http, location, stream, and other blocks right from Lua.

Example of what is currently implemented:

http {
    configure_by_lua_block {
        local ngx_configure = require "ngx.configure"

        -- hard-coded, but could be retrieved from I/O or env
        local shms = {
            { "dogs", "12k" },
            { "cats", "24k" }
        }

        -- create shms
        for _, shm in ipairs(shms) do
            ngx_configure.shared_dict(shm[1], shm[2])
        end

        -- conditionally expose env variables for workers
        ngx_configure.env("PROXY_PASS_URL")

        -- configure the Lua VM
        ngx_configure.max_pending_timers(1024)
        ngx_configure.max_running_timers(128)
    }

    init_by_lua_block {
        ngx.shared.dogs:set("hello", true)
    }
}

List of what is currently implemented:

  • a new configure_by_lua phase (block/file) for http context
  • ngx_lua directives
    • ngx_configure.shared_dict() -> lua_shared_dict
    • ngx_configure.max_pending_timers() -> lua_max_pending_timers
    • ngx_configure.max_running_timers() -> lua_max_running_timers
  • NGINX directives
    • ngx_configure.env() -> env
  • no NGINX patch required
  • naming convention of APIs strips lua_ prefix from directives

Everything is implemented via the LuaJIT FFI in the lua-resty-core sister PR located at: openresty/lua-resty-core#176.

My current goal is to get feedback, reviews, guidance... anything :) In the long term, I wish to gradually add more relevant API to this phase and make OpenResty more dynamic. If anyone can think of anything in the current implementation that would eventually impact this goal or limit future possibilities, please let me know.

@spacewander
Copy link
Member

@thibaultcha
Sorry for mentioning one thing: Why don't you try to make the Nginx configure file as a template, and render it before starting/reloading the server?
The team I last worked with did this in production over one year ago, and it works.

@spacewander
Copy link
Member

spacewander commented Feb 16, 2018

PS. You could use priviledge process to detect configure change and send reload signal. And the template render could be implemented in Lua, so no other dependency is required.

@thibaultcha
Copy link
Member Author

@spacewander This is precisely what we have been doing for the past few years, and what this PR tries to get rid of :)

The goal here is to avoid having to depend so heavily on templating all the time to configure our applications. Additionally, templating causes huge headaches to users who need to make their own modifications to their Nginx configuration file, not only to learn it but also for each upgrade with configuration changes. Really, the goal here is to not have to do that anymore.

@thibaultcha
Copy link
Member Author

thibaultcha commented Feb 16, 2018

Especially considering the maintainability cost seems to be relatively low. No Nginx patch required, a dependency on an init, process that is very unlikely to ever change, and a self-contained patch in the codebase. I believe the ability to configure servers and location blocks would be possible by respecting the same constraints as well.



char *
ngx_http_lua_configure_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like all the code duplications in this C source file. It makes the code base harder to maintain and sync new changes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can definitely improve it :) Which area? The context runner and/or the shm configure API?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thibaultcha Almost all the code in this C source file is duplicated C code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@agentzh I just added a new commit which introduces ngx_http_lua_shared_dict_add, which factorizes all of the shared code for shm create for lua_shared_dict and ngx_configure.shared_dict().

As stated before, the code related to parsing and running the _by_*_block and _by_*_file directives of this module are already always duplicated - this feels outside the scope of this PR, but if you want me to address this, please advise.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thibaultcha

All of the Lua phases code is duplicated already, I just followed the pattern.

What exactly are you referring to?

The thing is if the config phase handlers can call these functions, then we should make it that way. I don't want to maintain 2 duplicated copies of configurations. Also, how do you handle config directive overriding and inheritance here? Seems like you already bypass the nginx config system entirely?

Copy link
Member Author

@thibaultcha thibaultcha Feb 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do you handle config directive overriding and inheritance here? Seems like you already bypass the nginx config system entirely?

There is no inheritance involved here since we only support configuration of http (or above) level directives, and this is deliberate.

Ultimately, I do wish to support lower levels of configuration as explained in the PR (server, location...), but without it there are no need for supporting inheritance yet.

What exactly are you referring to?

I was reffering to the only part of the code that I see is duplicated from other parts of ngx_lua, and that is the ngx_http_lua_configure_by_lua and other handlers that run the Lua code for this phase.

If not, then I am not sure what you are referring to when you are saying things are duplicated?


if (lmcf->shdict_zones != NULL) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also makes such optimizations no longer exist. I'm kinda proud of the concept that you do not pay for the features you never use :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean? We only create the shdict metatable if we need it still (via lua_shared_dict or ngx_configure.shared_dict())

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thibaultcha We used to save the metatable when lua_shared_dict is not configured in nginx.conf. This is a minor thing though.

Copy link
Member

@agentzh agentzh Feb 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thibaultcha It seems that you essentially created a second parallel configuring code path for those config directives we already have.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We used to save the metatable when lua_shared_dict is not configured in nginx.conf.

And we still do, I made sure of that, this method has been renamed to reflect the change but both of its calls in the codebase are enclosed within conditions. I could re-add the safeguard condition in here too just in case.

It seems that you essentially created a second parallel configuring code path for those config directives we already have.

All of the Lua phases code is duplicated already, I just followed the pattern.
Regarding the add shm API, I can certainly improve it - I originally didn't want to modify existing code too much by fear the patch would be refused if so.
Regarding the timers code, I was thinking of coming up with a more generic way of setting integer values onto the lmcf structure (we could follow the NGINX pattern with ngx_conf_set_num_slot and the likes) but that seems unnecessary for only 2 fields as of today and considering it would result in a lot more code.

return NGX_CONF_ERROR;
}

*zp = zone;

lmcf->requires_shm = 1;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was duplicated (as in the master branch) - we now only set it from ngx_http_lua_shared_memory_add.

@@ -122,9 +121,7 @@ ngx_http_lua_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size,
return &ctx->zone;
}

n = sizeof(ngx_http_lua_shm_zone_ctx_t);

ctx = ngx_pcalloc(cf->pool, n);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just cleaning up here but I can revert if not desired.

@agentzh
Copy link
Member

agentzh commented Feb 26, 2018

@thibaultcha I appreciate your exploration in this direction. I'm thinking about maybe we could make this work on a much lower level, by dynamically configuring all the nginx main/http/stream/server/location blocks in Lua. Your approach looks like we will simply duplicate all the existing nginx configuraiton directives in this new facility, which does not have a future IMHO (not to mention there might be an infinite number of 3rd-party nginx modules out there providing their infinite number of possible directive that could interact with OpenResty) :)

If we want to get rid of nginx.conf, then let's do it in a thorough way :) Just my 2 cents.

@thibaultcha
Copy link
Member Author

thibaultcha commented Feb 26, 2018

we could make this work on a much lower level, by dynamically configuring all the nginx main/http/stream/server/location blocks in Lua

Yes, this is exactly my point and my intent on the long term (as explained above). The current patch is more of an intermediate phase for the nginx main and http context directives, because I am of the opinion that we should build this step-by-step, and add more APIs (including APIs dynamically adding server and location blocks) to it in the future.

Your approach looks like we will simply duplicate all the existing nginx configuraiton directives in this new facility, which does not have a future IMHO

Maybe not all :) This approach would already be quite helpful imho - we've been wanting for years to only allocate the lua_shared_dict that our customers' dynamically configured instances need, without requiring of them that they learn or modify our nginx configuration templating system (that we wish to get rid of in the long term). I believe a lot of users would be happy to allocate shared dicts from the Lua-land (I have witnessed many asks for it over the years in the mailing list, issues, or chats...).

not to mention there might be an infinite number of 3rd-party nginx modules out there providing their infinite number of possible directive that could interact with OpenResty

Agreed. But it is not because we cannot please everybody that we should please nobody :) Covering the directives that are used the most often (like env, lua_shared_dict for starters) would already satisfy a lot of users imho. Besides, how do you intend to support the much larger amount of available directives in the server and location contexts?

One solution I thought of (I did not explore its feasibility) was to avoid creating structures representing the state of the configuration like this:

ngx.server.my_server:add_location("/")

Which would become a maintenance nightmare, but instead supporting a more lose API, like:

ngx.configure.append [[
server {
    listen :8080;
    
    location {
        return 200;
    }
}
]]

Maybe the final solution will be something in the middle.

If we want to get rid of nginx.conf, then let's do it in a thorough way

I am of the same opinion, but I also think that this intermediate step is a good one to take until more APIs are added to this phase that will ultimately allow us to get rid of nginx.conf.

@thibaultcha
Copy link
Member Author

thibaultcha commented Feb 26, 2018

I very much want to highlight the following sentences from this PR's opening thread:

This phase's purpose would be to allow the configuration of NGINX/ngx_lua with Lua scripts. This PR proposes a first draft with just a few APIs, hoping that more APIs can be added progressively.

Ultimately, I cherish the vision to some day being able to dynamically configure http, location, stream, and other blocks right from Lua.

The current proposal is to iterate slowly towards that goal with a few APIs that would already be helpful. If you prefer the idea I have outlined above, or if you have a better one altogether that you wish to share, or if dynamically configuring nginx is not something that OpenResty wishes to achieve, I am fine with this, please do let me know! :)

@agentzh
Copy link
Member

agentzh commented Feb 26, 2018

@thibaultcha I do understand your intention but unfortunately I don't see this PR as an iterative approach to the right direction from the perspective of an implementator. This patch adds quite some maintenance overhead with very little real world merit IMHO.

@Someguynamedpie
Copy link

Down the line, being able to append or "execute" blocks of NGINX config (even as strings to lower the implementation requirements) from init_by_lua would be a godsend for server management tools.

@mergify
Copy link

mergify bot commented Jun 26, 2020

This pull request is now in conflict :(

@mergify
Copy link

mergify bot commented Mar 20, 2023

This pull request is now in conflict :(

@mergify mergify bot added the conflict label Mar 20, 2023
@mergify mergify bot removed the conflict label May 10, 2023
@mergify
Copy link

mergify bot commented May 10, 2023

This pull request is now in conflict :(

@mergify mergify bot added the conflict label May 10, 2023
@mergify mergify bot removed the conflict label Sep 23, 2023
@mergify
Copy link

mergify bot commented Sep 23, 2023

This pull request is now in conflict :(

@mergify mergify bot added the conflict label Sep 23, 2023
@mergify mergify bot removed the conflict label Mar 6, 2024
Copy link

mergify bot commented Mar 6, 2024

This pull request is now in conflict :(

@mergify mergify bot added the conflict label Mar 6, 2024
@mergify mergify bot removed the conflict label May 27, 2024
Copy link

mergify bot commented May 27, 2024

This pull request is now in conflict :(

@mergify mergify bot added the conflict label May 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants