Heroku (Docker) PORT environment variable in nginx - docker

I just take a few modifications on a Dockerfile to run it with nginx on Heroku. Something special about Heroku is, that everything is running as non-root. Second certain behaviour is the use of a random Port which comes from Heroku itself and you can't be modified. They provide the env $PORT which you should bind to nginx. If Heroku recognizes that something isn't bind to that port it stops the entire container. Question is:
How can I bind nginx to a given env variable in order to have a dynamic port in the nginx-site.conf?
I tried to use things like follows in the Dockefile:
env PORT; in nginx.conf and listen PORT_from_env; in the nginx-site.conf
Also tried listen 80; in the nginx-site.conf and RUN /bin/sed -i "s/listen 80/listen ${PORT}/" /etc/nginx/sites-available/default.conf in the Dockerfile
I am absolutely above my capabilities. Someone has an idea or can help? It be very helpful!
Further information:
https://devcenter.heroku.com/articles/dynos#web-dynos

I got it working for my app by following this example :
Step 1: listen to $PORT in default.conf.template
server {
listen $PORT default_server;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
Step 2: add this directive to your Dockerfile
COPY default.conf.template /etc/nginx/conf.d/default.conf.template
Step 3: add this at the end of your Dockerfile
CMD /bin/bash -c "envsubst '\$PORT' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf" && nginx -g 'daemon off;'

I'll show a solution which doesn't require to write a new Dockerfile, you can use the offical nginx images.
Like #Jimmy's solution we'll use the envsubst command which substitutes environment variables in shell format strings.
This command is available with the offical nginx image and also with the alpine version.
Step #1
write your nginx configuration in a template file - let's call it: site.template:
server {
listen ${PORT};
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
Notice the PORT placeholder.
Step #2 - with docker compose
Mount that inside the /etc/nginx/conf.d directory and then execute the envsubst command to use the template as a reference for default.conf:
web:
image: nginx:alpine
volumes:
- ./site.template:/etc/nginx/conf.d/site.template
ports:
- "3000:8080"
environment:
- PORT=8080
command: /bin/sh -c "envsubst < /etc/nginx/conf.d/site.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"
Notice that:
1. You need to execute the nginx daemon after that.
2. I used /bin/sh and not /bin/bash because my base image is alpine.
Step #2 (Another option) - inline docker run
If, for some reason you don't want to work with docker-compose you can use the following bash script:
#!/usr/bin/env bash
##### Variables #####
PORT=8080 #Or $1 if you pass it from command line
TEMPLATE_DIR=$(pwd)/site.template
TEMPLATE_REMOTE_DIR=/etc/nginx/conf.d/site.template
IMAGE_NAME=nginx:alpine
echo "Starting nginx on port: $PORT ..."
##### The docker command #####
docker run -p 3000:$PORT -v $TEMPLATE_DIR:$TEMPLATE_REMOTE_DIR $IMAGE_NAME \
/bin/sh -c "envsubst < $TEMPLATE_REMOTE_DIR > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"

You need to update your nginx configuration at boot time.
See this buildpack as an example (I haven't tested that it does work).

Related

Change nginx.conf file inside docker

I'm trying to create a reverse proxy using nginx running in docker. I have set up the nginx.conf file and it's running fine locally. However, I don't know how to set up the nginx docker with this changed nginx.conf file. Is there any way to do this?
Update: I have been able to change the nginx.conf file inside Docker. However, going to localhost:80/go returns a 502 Bad Gateway. I have a go app running on port 8081 using go run main.go and a python app running on port 8080 using flask run. I'm on a Manjaro VM.
This is the server part of the nginx.conf file
server {
listen 80;
location / {
return 200 'hey there, welcome to our amazing app :)';
}
location /cbl {
proxy_pass http://127.0.0.1:8080;
}
location /go {
proxy_pass http://127.0.0.1:8081;
}
}
And this is the Dockerfile
FROM nginx
RUN mv /etc/nginx/nginx.conf /etc/nginx/nginxorig.conf
#RUN pwd
#RUN cp /home/shark/hwr-nginx/nginx.conf /etc/nginx/conf.d/default.conf
VOLUME /usr/share/nginx/html
VOLUME /etc/nginx
How do I fix this?
[shark#shark-virtualbox hwr-nginx]$ sudo docker build . -t nginx
Sending build context to Docker daemon 7.168kB
Step 1/3 : FROM nginx
---> 7084cd82dcbb
Step 2/3 : RUN mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf_orig
---> Running in 80e011c5b125
mv: cannot stat '/etc/nginx/nginx.conf': No such file or directory
The command '/bin/sh -c mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf_orig' returned a non-zero code: 1
[shark#shark-virtualbox hwr-nginx]$ cat Dockerfile
FROM nginx
RUN mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf_orig
RUN cp hwr-nginx/nginx.conf /etc/nginx/nginx.conf
```` RUN mv and RUN cp are also not working for me :(.
You need to copy your nginx.conf to your container:
For example: COPY conf /etc/nginx
Docs here: https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-docker/#managing-content-and-configuration-files
In your Dockerfile, add an instruction to copy your project's reverse proxy configured ngxin.conf into the docker image you intend to build, in the location where nginx will look for its configurations. For example, assuming your base image is Debian, you could do the following in your Dockerfile:
# Move the default conf out of the way
RUN mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf_orig
# Copy in your project's new nginx conf
RUN cp my_project/nginx.conf /etc/nginx/nginx.conf

How to define startup file in phusion/passenger-nodejs docker container

I'm attempting to get a next.js app running in a docker container based on phusion/passenger-docker.
I have what I think is a complete setup based on passenger-docker documentation but I'm getting a 404 page from nginx.
A docker log dump shows that passenger or nginx, is looking for index.html
[error] 48#48: *1 "/home/app/nhe_app/index.html" is not found
My startup file is /home/app/nhe_app/server.js
Dockerfile final stage:
# Build production container from builder stage
FROM phusion/passenger-nodejs:1.0.8
# Set correct environment variables.
ENV HOME /root
ENV NODE_ENV=production
# Use baseimage-docker's init system.
CMD ["/sbin/my_init"]
# Enable Nginx and Passenger
RUN rm -f /etc/service/nginx/down
WORKDIR /home/app/nhe_app
RUN rm /etc/nginx/sites-enabled/default
COPY --chown=app:app ./nhe_app.conf /etc/nginx/sites-enabled/nhe_app.conf
COPY --chown=app:app ./secret_key.conf /etc/nginx/main.d/secret_key.conf
COPY --chown=app:app ./gzip_max.conf /etc/nginx/conf.d/gzip_max.conf
COPY --chown=app:app --from=builder /app/server.js /app/.env /home/app/nhe_app/
COPY --chown=app:app --from=builder app/src /home/app/nhe_app/src
COPY --chown=app:app --from=builder app/node_modules /home/app/nhe_app/node_modules
# Clean up APT when done.
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
nginx configuration - nhe_app.conf:
server {
listen 80;
server_name glen-mac.local;
root /home/app/nhe_app/server.js;
passenger_enabled on;
passenger_user app;
passenger_startup_file server.js;
}
I expect that passenger will start nginx and run my app.
When I build and start the docker container it seems to expect index.html.
I'm building the docker container with
docker image build -t nhe_app .
And running it with
docker container run --name nhe_app -p 80:3000 nhe_app
Browsing to http://glen-mac.local/ shows nginx's formatted 404 page.
How can I configure passenger-docker to look for and execute my server.js rather than index.html?
There are several subtle problems in the OP question.
Most notably, Passenger seems to require that the app root path, defined by root in the nginx configuration above, has a top level folder named public. This folder must not contain an index.html file and probably should be empty. This is shown in examples, but not spelled out as a hard requirement in the docs.
Second major error is that Passenger bypasses the port specified in the app's server.js (3000 in this case) and replaces it with the port specified in the nginx configuration. So the docker run command changed from:
docker container run --name nhe_app -p 80:3000 nhe_app
to
docker container run --name nhe_app -p 80:80 nhe_app.
Otherwise, the best advice I can give is:
Learn the Passenger basics through a local installation (without Docker). Get the Passenger demo app working.
Get your app working in that local Passenger installation.
Apply what you have learned to implementing your app in passenger-docker.
server {
listen 7063;
server_name localhost;
root /home/app/nhe_app;
passenger_enabled on;
passenger_min_instances 1;
passenger_max_request_queue_size 100; # default: 100
passenger_app_env staging; # NODE_ENV; default: staging
passenger_app_root /home/app/nhe_app;
passenger_app_type node;
passenger_startup_file server.js;
}

Not able to substitute nginx environment variable in Dockerfile

I am passing endpoint of REST APIs as env variable during docker build of nginx and having spent 6 hours trying most of the suggestions, I have run out of things/patience to try.
nginx conf that I am substituting:
location /my-api/ {
proxy_pass ${api_url}/;
}
I am passing this value during docker build:
#base_url comes from system env
docker build --build-arg base_api_url=$base_url
I am getting this value in my Dockerfile:
ARG base_api_url
ENV api_url=$base_api_url
# This prints the value
RUN echo "api_url= ${api_url}" .
COPY package.json /usr/src/app/package.json
RUN npm install
COPY . /usr/src/app
RUN npm run build
FROM nginx:1.15.8-alpine
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
EXPOSE 80
# this works
COPY nginx.conf /etc/nginx/nginx.conf.template
# Initially following code was building and deploying docker image and url was hard coded. it was working
# COPY nginx.conf /etc/nginx/conf.d/default.conf
# Below will start the image but no REST endpoint configured
# CMD ["nginx", "-g", "daemon off;"]
# To substitute the api url few of the many things I have tried.
# Non of the below, have been able to replace the env api_url with its value
# Actually I don't know -- since /etc/nginx/conf.d/default.conf is not replaced at all
# CMD /bin/bash -c "envsubst < nginx.conf > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"
# CMD /bin/sh -c "envsubst < nginx.conf > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;' || cat /etc/nginx/nginx.conf"
# Last status
CMD envsubst < /etc/nginx/nginx.conf.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'
/etc/nginx/conf.d/default.conf should have api_url replaced:
location /my-api/ {
proxy_pass http://theApiURL;
}
I have also tried specifically passing env variable like this:
CMD envsubst ${api_url} < /etc/nginx/nginx.conf.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'
and variations like using tee.
Any help/direction to get this resolved is appreciated.
I typically do this outside of a Dockerfile, using sed, in a deployment script.
Here's an example:
#!/usr/bin/env bash
# Deploys locally for dev work
set -e
export API_URL=www.theapiurl.com
sed "s/api_url/${API_URL}/" nginx.conf.template > nginx.conf
...
# run docker
docker-compose build --no-cache
docker-compose up -d
Of course you can set the environment variable however makes sense for your use case. I find this approach much more flexible than anything Docker provides.

How do I add the Lua module for nginx on Alpine linux?

I'd like to have a lean Docker image for nginx with the Lua module enabled. How can I create this based on Alpine linux?
Here is a Dockerfile:
FROM alpine:3.6
RUN apk add --no-cache nginx-mod-http-lua
# Delete default config
RUN rm -r /etc/nginx/conf.d && rm /etc/nginx/nginx.conf
# Create folder for PID file
RUN mkdir -p /run/nginx
# Add our nginx conf
COPY ./nginx.conf /etc/nginx/nginx.conf
CMD ["nginx"]
Installing the nginx-mod-http-lua package will also install nginx and luajit, among others.
The nginx.conf should contain at least this:
load_module /usr/lib/nginx/modules/ndk_http_module.so;
load_module /usr/lib/nginx/modules/ngx_http_lua_module.so;
pcre_jit on;
events {
worker_connections 1024;
}
daemon off;
Dockerfile:
FROM nginx:1.15-alpine
RUN mkdir -p /run/nginx
RUN apk add --no-cache nginx-mod-http-lua
COPY nginx_conf/ /etc/nginx/ # Your nginx conf
COPY lua/ /etc/lua/ # Your lua files
First line of nginx conf:
load_module /usr/lib/nginx/modules/ndk_http_module.so;
load_module /usr/lib/nginx/modules/ngx_http_lua_module.so;
pcre_jit on;
We use Openresty, a platform that integrates nginx and Lua.
In the default nginx file, you could call Lua like so:
server {
listen 80;
listen 443 ssl; # 'ssl' parameter tells NGINX to decrypt the traffic
# 1
location ~ /api/(.*) {
resolver xxx.x.x.xx;
rewrite_by_lua_block {
ngx.req.set_header("x-header", "12345678901234567")
}
}
The alpine image here: https://github.com/openresty/docker-openresty/tree/master/
There is also an alpine-fat that had make, git and other libraries that can help you build within your Docker image.
See: "Adding third-party modules to nginx official image"
At: https://github.com/nginxinc/docker-nginx/tree/master/modules
"It's possible to extend a mainline image with third-party modules either from your own instuctions following a simple filesystem layout/syntax using build_module.sh helper script, or failing back to package sources from pkg-oss."
$ docker build --build-arg ENABLED_MODULES="ndk lua" -t my-nginx-with-lua .
You look on the Docker Hub
https://hub.docker.com/
and you find an Nginx image, based on Alpine Linux, with Lua support
Some examples
https://hub.docker.com/r/ilagnev/alpine-nginx-lua/
or
https://hub.docker.com/r/firesh/nginx-lua/
Have a look at the Dockerfile for more details

How to run rails app in docker container with nginx running on host machine?

I'm working on the scenario where I need to have:
1. nginx running on the host machine
2. rails app running inside docker container
My initial work included adding Dockerfile into rails app directory on host which looks like this:
ubuntu#ubuntu-xenial:~/rails_docker$ cat Dockerfile
FROM ruby:2.1-onbuild
ENV HOME /home/rails/webapp
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
WORKDIR $HOME
# Install gems
ADD Gemfile* $HOME/
RUN bundle install
# Add the app code
ADD . $HOME
EXPOSE 8080
EXPOSE 5432
Since I already have nginx on host, it is configured to look at directory where rails application exists on host machine:
ubuntu#ubuntu-xenial:~$ cat /etc/nginx/sites-available/www.testblog.io.conf
# MANAGED BY PUPPET
server {
listen *:8080;
server_name www.testblog.io;
index index.html index.htm index.php;
access_log /var/log/nginx/www.testblog.io.access.log combined;
error_log /var/log/nginx/www.testblog.io.error.log;
location / {
root /home/ubuntu/rails_docker/public;
index index.html index.htm index.php;
}
passenger_enabled on;
passenger_ruby /usr/local/rvm/wrappers/default/ruby;
}
I'm able to build docker image and my next idea is following:
1. Declare /home/ubuntu/rails_docker as a host volume when running "rails container"
2. Expose ports 8080:8080 and 5432:5432
Command I use is following:
ubuntu#ubuntu-xenial:~/rails_docker$ docker run -d -p 8080:8080 --name examplerails -v /home/ubuntu/rails_docker:/home/rails/webapp railsapptest
82c8aa45b8c1a405e198a565feabf105d1afcbb1c37f8b7b11bf764395ed8c4e
When I check logs:
ubuntu#ubuntu-xenial:~/rails_docker$ docker logs -f examplerails
Switch to inspect mode.
For some reason, it goes into irb mode. Any idea what I'm doing wrong?
Thanks in advance,
Bakir
You need to have a CMD instruction in your Dockerfile. That is the command that is run when you start up your Docker container. If you don't specify a command, it will run whatever your FROM image uses, which you can see is 'irb' if you look at the Ruby docker image.
You can read more about the CMD instruction and everything else about Dockerfiles in the Docker documentation.

Resources