nginx / unit

NGINX Unit - universal web app server - a lightweight and versatile open source server that simplifies the application stack by natively executing application code across eight different programming language runtimes.
https://unit.nginx.org
Apache License 2.0
5.39k stars 328 forks source link

Add daemon off in config json #300

Open joanhey opened 5 years ago

joanhey commented 5 years ago

In nginx we can use daemon off from config, normally to run it in docker.

In Unit don't exist. The only solution that I find to configure and run in docker is running unitd 2 times. With unitd --no-daemon we can't run and config in the same dockerfile. Another way to do it ??

Examples:

FROM ubuntu:19.04

ARG DEBIAN_FRONTEND=noninteractive

RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null
RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php
RUN apt-get update -yqq > /dev/null && \
    apt-get install -yqq curl php7.3 php7.3-mysql > /dev/null

RUN curl https://nginx.org/keys/nginx_signing.key | apt-key add - \
    && add-apt-repository "deb https://packages.nginx.org/unit/ubuntu/ disco unit" -s \
    && apt-get -y update \
    && apt-get -y install unit unit-php

ADD ./ /php
WORKDIR /php

# forward log to docker log collector
#RUN ln -sf /dev/stdout /var/log/unit.log

# RUN if [ $(nproc) = 2 ]; then sed -i "s|\"processes\": 128,|\"processes\": 64,|g" /php/deploy/nginx-unit.json ; fi;

RUN unitd && \
    curl -X PUT --data-binary @/php/deploy/nginx-unit.json --unix-socket \
        /var/run/control.unit.sock http://localhost/config

CMD unitd --no-daemon

But that create all the processes 2 times. Works perfectly, but will be better set it from the config.

Thank you

i4ki commented 5 years ago

You can just copy the config into the state directory, wherever it is by default on ubuntu, or create a new state directory.

RUN mkdir /var/unit
RUN cp /php/deploy/nginx-unit.json /var/unit/conf.json
CMD unitd --no-daemon --state /var/unit

Unit will load the config from the state on startup.

VBart commented 5 years ago

You can just copy the config into the state directory, wherever it is by default on ubuntu, or create a new state directory.

While it's possible right now, we don't recommend this as it may break in the future when the format of state directory will change. Users should look at state directory as a black box. You can save or move around this black box, but you shouldn't get inside.

But that create all the processes 2 times. Works perfectly, but will be better set it from the config.

There's no technical possibility for a process to undaemonize (i.e. switching from daemon on to daemon off). And it's actually not possible with nginx (you have to restart it in order to the change take place). So, this can't be a config option as it's not dynamic.

The only solution that I find to configure and run in docker is running unitd 2 times. With unitd --no-daemon we can't run and config in the same dockerfile.

Actually this is a proper way as it allows to cache the result of Unit reconfiguration and don't reconfigure it each time.

Another way to do it ??

You can use a shell script inside container that will run unitd in background, configure it, and then bring back it into the foreground. See here for detailed instructions: https://docs.docker.com/config/containers/multi-service_container/

i4ki commented 5 years ago

Oh, good to know =) As I understand your point, also I think such a feature is really useful because otherwise, the user will need to poll/wait to see if unitd is ready to receive requests in the control API. Maybe then just a command line parameter: --load-config that loads the initial config as soon as router is ready.

ghost commented 5 years ago

@joanhey Hi,

You can also prepare/maintain/update your config outside Docker, running an external Unit instance and copying the resulting state directory to your container when needed to pick it up with CMD unitd --no-daemon --state /where/you/put/it. Or would an option like @tiago4orion suggests be more useful in your situation?

VBart commented 5 years ago

Maybe then just a command line parameter: --load-config that loads the initial config as soon as router is ready.

For some configuration you also have to load TLS certificates. In the future there will be other items that may need to be preloaded and prepared (e.g. JavaScript code snippets for calling from the configuration). If we will go this way with an unitd option, then we will end up with endless numbers of initialization parameters.

I prefer to look at Unit configuration as a database. Here's an example of how PostgreSQL solves this issue: https://hub.docker.com/_/postgres#initialization-scripts - see Initialization scripts that they put inside the image for convenience. Those scripts are able to initialize database for the first run.

docteurklein commented 5 years ago

i'm also interested in defining the config without having to use curl at runtime. For now it's very cumbersome, but I got something working with:

FROM alpine:edge as build
    RUN apk add --no-cache unit-php7 curl
    COPY config.json .
    RUN unitd && curl -X PUT http://localhost/config --data-binary @config.json --unix-socket /run/control.unit.sock

FROM alpine:edge
    RUN apk add --no-cache unit-php7
    ENTRYPOINT ["unitd", "--no-daemon", "--log", "/dev/stderr"]
    COPY --from=build /var/lib/unit /var/lib/unit
    COPY app /app

Seems like I can't just COPY config.json /var/lib/unit/config.json as described by @tiago4orion .

VBart commented 5 years ago

@docteurklein please check https://github.com/nginx/unit/pull/315

docteurklein commented 5 years ago

thanks. that looks like a working solution indeed, but when I look at the complexity of the bash entrypoint, i think I'm better off with my solution for now.