Docker: Cannot start service nginx - docker

I'm working on nginx service using docker-compose, I created the docker-compose.yml file :
version: '2'
services:
nginx:
image: nginx:1.11.8-alpine
ports:
- "8858:80"
volumes:
- ./site.conf:/etc/nginx/conf.d/default.conf
- ./code:/usr/share/nginx/html
- ./html:/myapp
- ./site.conf:/etc/nginx/conf.d/site.conf
- ./error.log:/var/log/nginx/error.log
- ./nginx.conf:/etc/nginx/nginx.conf:ro
This is the site.conf file
server {
listen 80;
index index.html index.php;
server_name localhost;
error_log /var/log/nginx/error.log;
location / {
root /usr/share/nginx/html;
}
location /html {
alias /myapp;
}
}
This is the result of docker-compose up
ERROR: for ef5152b88a7c_ef5152b88a7c_nginxdocker_nginx_1 Cannot start
service nginx: oci runtime error: container_linux.go:247: starting
container process caused "process_linux.go:359: container init caused
\"rootfs_linux.go:54: mounting \\"/root/nginx-docker/nginx.conf\\"
to rootfs
\\"/var/lib/docker/aufs/mnt/b463f0e0ca95db8cd570dfb68fcf206df31e86998e725465a7673ca192af8342\\"
at
\\"/var/lib/docker/aufs/mnt/b463f0e0ca95db8cd570dfb68fcf206df31e86998e725465a7673ca192af8342/etc/nginx/nginx.conf\\"
caused \\"not a directory\\"\""
: Are you trying to mount a directory onto a file (or vice-versa)?
Check if the specified host path exists and is the expected type

This has nothing to do with Nginx.
If you use volumes in docker-compose.yml, always make sure that the files on the left side of the colon : exists! If you miss that, sometimes Docker Compose creates folders instead of files "on the left side" (= on your host) IIRC. Which leads to subsequent errors on the next run.

Related

How to set environment variables on docker compose for nginx container?

My project is using CI/CD for deployment and I have one docker-compose file for each application stage (dev, staging, release).
Depending on what stage the application is, I want to redirect the user for my API using Nginx for a different ip/port.
On my default.conf file I want to write something like this.
server {
listen 443 ssl;
ssl_certificate /etc/ssl/server/cert.pem;
ssl_certificate_key /etc/ssl/server/privkey.pem;
location / {
proxy_pass https://api:$API_PORT;
proxy_set_header Host $host;
...
where api is a reference for my service' IP that is defined in my docker-compose file and I want ${API_PORT} to be a reference to my environment variable that is defined inside docker-compose.
My docker-compose file looks like this.
version: "3"
services:
api:
...
ports:
- 4000:4000
nginx:
...
environment:
- API_PORT=4000
ports:
- 5180:80
- 5181:443
How could I achieve that?
Note: If I have a static port, for example 4000, when I up both stage and release versions I will have conflicts on port 4000.
In your Nginx configuration, you don't need to do anything; use the fixed port 4000.
proxy_pass https://api:4000;
Since this is a connection from the Nginx container to the API container, it stays within the Docker network environment. This connection doesn't pay any attention to what you might have set as ports:, it connects to the server process listening on port 4000 in the API container.
When you start the API container, the server process inside the container should use that same fixed port 4000. If you need to make the API container externally visible, you may choose a different number for the first port in the ports: block, but the second port needs to be 4000.
services:
api:
ports: ['4001:4000']
nginx:
ports: ['5180:80', '5181:443']
If you need to launch multiple copies of this stack, you need to change the first port number in all of the ports: blocks, but leave the second numbers unchanged.
If all access to the API container is through this Nginx proxy, you may not need the api: { ports: [] } block at all, and you can safely delete it; again, it's not used for connections between containers.
For accomplishing that you will need to set your Dockerfile and rename your .conf file in order to Nginx understand what you want to do.
First, Nginx by itself supports what you want to do, so you will need to use templates for that.
By default, if you place your config files inside /etc/nginx/templates and your filename ends with .template, Nginx will use envsubst to substitute your environment variable inside your .conf file for the values that you define in your docker-compose file.
So let's have an example.
You have default.conf.template (don't forget to rename your .conf files) file with your Nginx settings:
server {
listen 443 ssl;
ssl_certificate /etc/ssl/server/cert.pem;
ssl_certificate_key /etc/ssl/server/privkey.pem;
location / {
proxy_pass https://api:$API_PORT;
proxy_set_header Host $host;
...
Your Dockerfile will copy your default.conf.template file and will paste it inside /etc/nginx/templates
...
COPY /your/nginx/settings/folder/default.conf.template /etc/nginx/templates
...
With that done, when Nginx starts running it will search on the templates folder for *.template files, and when it finds your default.conf.template file it will replace the environment variables reference for the actual value and will move this file for /etc/nginx/conf.d folder.
So if your docker-compose file looks like this:
version: "3"
services:
api:
...
ports:
- 4000:4000
nginx:
...
environment:
- API_PORT=4000
your default.conf.template file (mentioned above) will be renamed to default.conf, moved to /etc/nginx/conf.d/ and will look like this:
location / {
proxy_pass https://api:4000;
...
So Nginx will replace the references for the values and move the .conf files to the right place.

Error trying to run nginx using docker-compose with custom conf.d

I'm trying to set up a web server with multiple containers - but starting with a simple setup for my reverse proxy.
My docker-compose.yml looks as follows:
version: '3'
services:
reverse-proxy:
container_name: reverse-proxy
hostname: reverse-proxy
image: nginx:latest
ports:
- 80:80
volumes:
- ./nginx-config/conf.d:/etc/nginx/conf.d
- ./html:/usr/share/nginx/html
environment:
- NGINX_PORT=80
- ENV=development
Having nginx-config folder structure like:
nginx-config
|- templates
|-default.conf.template
|- sites-available
|- mysite.conf.template
And default.conf.template that looks like:
server {
listen ${NGINX_PORT} default_server;
listen [::]:${NGINX_PORT} default_server;
server_name _;
root /usr/share/nginx/html;
charset UTF-8;
error_page 404 /notfound.html;
location = /notfound.html {
allow all;
}
location / {
return 404;
}
access_log off;
log_not_found off;
error_log /var/log/nginx/error.log error;
}
However, whenever I run docker-compose --context myremote up it doesn´t work, throwing the following output:
reverse-proxy | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
reverse-proxy | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
reverse-proxy | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
reverse-proxy | 10-listen-on-ipv6-by-default.sh: Getting the checksum of /etc/nginx/conf.d/default.conf
reverse-proxy | 10-listen-on-ipv6-by-default.sh: /etc/nginx/conf.d/default.conf differs from the packaged version, exiting
reverse-proxy | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
reverse-proxy | /docker-entrypoint.sh: Configuration complete; ready for start up
It all generates the right output under nginx-config/conf.d/default.conf at least on my local machine.
Is there any way I can take advantage of custom config and templates using docker-compose without running into such an issue?
Problem solved.
I was trying to perform this using a docker context onto an EC2 machine.
Whilst Docker --context [context] has been part of the stable version (at least for MacOS) for a while, docker-compose --context [context] was added on v1.26.0-rc2 so at this moment in time you need Docker Edge installed in order to make it work.
I was using set context [context] rather than the explicit --context form which meant I was actually deploying locally but being unaware of it.

serving static files from jwilder/nginx-proxy

I have a web app (django served by uwsgi) and I am using nginx for proxying requests to specific containers.
Here is a relevant snippet from my default.conf.
upstream web.ubuntu.com {
server 172.18.0.9:8080;
}
server {
server_name web.ubuntu.com;
listen 80 ;
access_log /var/log/nginx/access.log vhost;
location / {
include uwsgi_params;
uwsgi_pass uwsgi://web.ubuntu.com;
}
}
Now I want the static files to be served from nginx rather than uwsgi workers.
So basically I want to add something like:
location /static/ {
autoindex on;
alias /staticfiles/;
}
to the automatically generated server block for the container.
I believe this should make nginx serve all requests to web.ubuntu.com/static/* from /staticfiles folder.
But since the configuration(default.conf) is generated automatically, I don't know how to add the above location to the server block dynamically :(
I think location block can't be outside a server block right and there can be only one server block per server?
so I don't know how to add the location block there unless I add dynamically to default.conf after nginx comes up and then reload it I guess.
I did go through https://github.com/jwilder/nginx-proxy and I only see an example to actually change location settings per-host and default. But nothing about adding a new location altogether.
I already posted this in Q&A for jwilder/nginx-proxy and didn't get a response.
Please help me if there is a way to achieve this.
This answer is based on this comment from the #553 issue discussion on the official nginx-proxy repo. First, you have to create the default_location file with the static location:
location /static/ {
alias /var/www/html/static/;
}
and save it, for example, into nginx-proxy folder in your project's root directory. Then, you have to add this file to /etc/nginx/vhost.d folder of the jwilder/nginx-proxy container. You can build a new image based on jwilder/nginx-proxy with this file being copied or you can mount it using volumes section. Also, you have to share static files between your webapp and nginx-proxy containers using a shared volume. As a result, your docker-compose.yml file will look something like this:
version: "3"
services:
nginx-proxy:
image: jwilder/nginx-proxy
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./nginx-proxy/default_location:/etc/nginx/vhost.d/default_location
- static:/var/www/html/static
webapp:
build: ./webapp
expose:
- 8080
volumes:
- static:/path/to/webapp/static
environment:
- VIRTUAL_HOST=webapp.docker.localhost
- VIRTUAL_PORT=8080
- VIRTUAL_PROTO=uwsgi
volumes:
static:
Now, the server block in /etc/nginx/conf.d/default.conf will always include the static location:
server {
server_name webapp.docker.localhost;
listen 80 ;
access_log /var/log/nginx/access.log vhost;
location / {
include uwsgi_params;
uwsgi_pass uwsgi://webapp.docker.localhost;
include /etc/nginx/vhost.d/default_location;
}
}
which will make Nginx serve static files for you.

Nginx proxy for multibranch environment

I am using nginx as a simple proxy service for my multiple dockerized containers (including image with nginx layer as well). I am trying to create vhost for each branch and this is causing a lot of trouble here. What i want to achieve is:
An nginx proxy service should proxy to containers on paths:
[branch_name].a.xyz.com (frontend container)
some-jenkins.xyz.com (another container)
some other containers not existing yet
nginx.conf inside proxy container:
upstream frontend-branch {
server frontend:80;
}
server {
listen 80;
server_name ~^(?<branch>.*)\.a\.xyz\.com;
location / {
proxy_pass http://frontend-branch;
}
}
nginx.conf inside frontend container:
server {
listen 80;
location / {
root /www/html/branches/some_default_branch
}
}
server {
listen 80;
location ~^/(?<branch>.*)$ {
root /www/html/branches/$branch
}
}
docker-compose for proxy:
version: "2.0"
services:
proxy:
build: .
ports:
- "80:80"
restart: always
networks:
default:
external:
name: nginx-proxy
Inside frontend project it looks pretty much the same, except service name and ofc ports (81:80).
Is there any way to "pass" the branch as a path for frontend container (e.g. some frontend:80/$branch) ?
Is it even possible to create that kind of proxy? I don't want to use the same image based on nginx as a proxy and as a 'frontend keeper' because in the future I will want to use proxy for more than only one container so having configuration for whole site proxy inside frontend project would be weird.
Cheers

Linking a docker container a subdirectory of another one

I am trying to setup multiple docker containers that can be accessed through one main container.
For example:
http://localhost:80 is the main container
http://localhost:80/site1 is a separate container
http://localhost:80/site2 is a separate container again
I know that the --link flag has been deprecated and the new way of doing things is by using the --network flag.
When I use the --link (for testing) I see an entry of the container I am linking to in the hosts file. That is where I am stuck.
So I would like to set the above scenario up using the docker --networking option.
Usage case: /Site1 might be the admin area or member to a website, but I would like to have them in separate containers so I can maintain them easier.
The containers are apache2 based, but if possible would like to refrain from editing any config files (but I can if I need to)
How would I go about that?
As far as I know there is no way that docker routes HTTP requests to one or the other container. You can only map a port from your host to one container.
What you will need is to run a reverse proxy (e.g. nginx) as your main container that then routes the request to the appropriate container.
Here an example how to set it up
site1/Dockerfile
FROM node:6.11
WORKDIR /site1
COPY site1.js .
CMD node site1.js
EXPOSE 80
site1/site1.js
var http = require("http");
http.createServer(function (request, response) {
response.writeHead(200, {'Content-Type': 'text/plain'});
response.end('Hello World 1\n');
}).listen(80);
site2/Dockerfile
FROM node:6.11
WORKDIR /site2
COPY site2.js .
CMD node site2.js
EXPOSE 80
site2/site2.js
var http = require("http");
http.createServer(function (request, response) {
response.writeHead(200, {'Content-Type': 'text/plain'});
response.end('Hello World 2\n');
}).listen(80);
node-proxy/default.conf
server {
listen 80;
# ~* makes the /site1 case insensitive
location ~* /site1 {
# Nginx can access the container by the service name
# provided in the docker-compose.yml file.
proxy_pass http://node-site1;
}
location ~* /site2 {
proxy_pass http://node-site2;
}
# Anything that didn't match the patterns above goes here
location / {
# proxy_pass http://some other container
return 500;
}
}
docker-compose.yml
version: "3"
services:
# reverse proxy
node-proxy:
image: nginx
restart : always
# maps config file into the proxy container
volumes:
- ./node-proxy/default.conf:/etc/nginx/conf.d/default.conf
ports:
- 80:80
links:
- node-site1
- node-site2
# first site
node-site1:
build: ./site1
restart: always
# second site
node-site2:
build: ./site2
restart: always
To start the reverse proxy and both sites enter in the root of this folder docker-compose up -d and check with docker ps -a that all docker containers are running.
Afterwards you can access this two sites with http://localhost/site1 and http://localhost/site2
Explanation
The folder site1 and site2 contains a small webserver build with nodejs. Both of them are listening on port 80. "node-proxy" contains the configuration file that tells nginx when to return which site.
Here are some links
docker-compose: https://docs.docker.com/compose/overview/
nginx reverse proxy: https://www.nginx.com/resources/admin-guide/reverse-proxy/
You should use volumes so you will set it in docker-compose.yml like this
version: '3.2'
services:
container1:
volumes:
- shared-files:/directory/files
container2:
volumes:
- shared-files:/directory/files
container3:
volumes:
- shared-files:/directory/files
volumes:
shared-files:

Resources