nginx/apache redirection for output port on docker container on vps - linux

I'm a linux noob in admin of docker container using apache or nginx on VPS.
I use an OVH classic Vps (4go ram, 25Go SSD) with already installed image of ubuntu 15.04 + docker.
Install of docker container is really easy, and in my case i install without problem the image sharelatex.
docker run -d \
-v ~/sharelatex_data:/var/lib/sharelatex \
-p 5000:80 \
--name=sharelatex \
sharelatex/sharelatex
Site is accessible on IP of the VPS at http://51.255.47.40:5000 port show that site work without any problem.
I have already a sub domain (tools.sebastienreycoyrehourcq.fr) configurated to go on the server ip vps (51.255.47.40 routed to External in webfaction panel ), not working, don't understand why.
I install an apache server on 51.255.47.40, but i suppose the best option is probably to install a docker image of nginx or apache ? Can you advice me on this point ? And after that, how can i redirect to 5000 port of the docker image on a classic 80 port of apache or nginx linked to my subdomain ?

Previous answers probably covers most of the issues, especially if there were redirection problems of your domain name.
In order to be fully portable and use all the possibilities of docker, my recommendation would be to used the Nginx official docker image and make it the only one accessible from the outside (with the opening of ports) and use the --link to manage connectivity between your Nginx containers and your other containers.
I have done that in similar situation which works pretty well. Below is a tentative translation of what I have done to your situation.
You start your share latex container without specifying any external port :
docker run -d \
-v ~/sharelatex_data:/var/lib/sharelatex \
--name=sharelatex \
sharelatex/sharelatex
You prepare an nginx conf file for your shareLatex server that you place in $HOME/nginx/conf that will look like
upstream sharelatex {
# this will refer to the name you pass as link to the nginx container
server sharelatex;
}
server {
listen 80;
server_name tools.sebastienreycoyrehourcq.fr;
location ^~ / {
proxy_pass http://sharelatex/;
}
}
You then start your nginx docker container with the appropriate volume links and container links :
docker run -d --link sharelatex:sharelatex --name NginxMain -v $HOME/nginx/conf:/etc/nginx/sites-available -v -p 80:80 kekev76/nginx
ps : this has been done with our own kekev76/nginx image that is public on github and docker but you can adapt the principle to the official nginx image.

nginx-proxy (https://github.com/jwilder/nginx-proxy) and then running sharelatex with VIRTUAL_HOST set to tools.sebastienreycoyrehourcq.fr should be enough to get this working.
e.g.
docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy
followed by
docker run -d \
-e VIRTUAL_HOST=tools.sebastienreycoyrehourcq.fr \
-v ~/sharelatex_data:/var/lib/sharelatex \
-p 5000:80 \
--name=sharelatex \
sharelatex/sharelatex

The subdomain tools.sebastienreycoyrehourcq.fr is not configured properly. It does not resolve to any IP address which is the reason it does not work.
After you configure your subdomain, you can run the sharelatex container on port 80 with this command:
docker run -d \
-v ~/sharelatex_data:/var/lib/sharelatex \
-p 80:80 \
--name=sharelatex \
sharelatex/sharelatex
This way you can access the app at http://tools.sebastienreycoyrehourcq.fr

Related

Node using require('os').networkInterfaces() gives 172 local ip address instead of 192, but only 192 appears to work [duplicate]

This question already has answers here:
From inside of a Docker container, how do I connect to the localhost of the machine?
(40 answers)
Closed 10 months ago.
As the title says, I need to be able to retrieve the IP address the docker hosts and the portmaps from the host to the container, and doing that inside of the container.
/sbin/ip route|awk '/default/ { print $3 }'
As #MichaelNeale noticed, there is no sense to use this method in Dockerfile (except when we need this IP during build time only), because this IP will be hardcoded during build time.
As of version 18.03, you can use host.docker.internal as the host's IP.
Works in Docker for Mac, Docker for Windows, and perhaps other platforms as well.
This is an update from the Mac-specific docker.for.mac.localhost, available since version 17.06, and docker.for.mac.host.internal, available since version 17.12, which may also still work on that platform.
Note, as in the Mac and Windows documentation, this is for development purposes only.
For example, I have environment variables set on my host:
MONGO_SERVER=host.docker.internal
In my docker-compose.yml file, I have this:
version: '3'
services:
api:
build: ./api
volumes:
- ./api:/usr/src/app:ro
ports:
- "8000"
environment:
- MONGO_SERVER
command: /usr/local/bin/gunicorn -c /usr/src/app/gunicorn_config.py -w 1 -b :8000 wsgi
Update: On Docker for Mac, as of version 18.03, you can use host.docker.internal as the host's IP. See aljabear's answer. For prior versions of Docker for Mac the following answer may still be useful:
On Docker for Mac the docker0 bridge does not exist, so other answers here may not work. All outgoing traffic however, is routed through your parent host, so as long as you try to connect to an IP it recognizes as itself (and the docker container doesn't think is itself) you should be able to connect. For example if you run this from the parent machine run:
ipconfig getifaddr en0
This should show you the IP of your Mac on its current network and your docker container should be able to connect to this address as well. This is of course a pain if this IP address ever changes, but you can add a custom loopback IP to your Mac that the container doesn't think is itself by doing something like this on the parent machine:
sudo ifconfig lo0 alias 192.168.46.49
You can then test the connection from within the docker container with telnet. In my case I wanted to connect to a remote xdebug server:
telnet 192.168.46.49 9000
Now when traffic comes into your Mac addressed for 192.168.46.49 (and all the traffic leaving your container does go through your Mac) your Mac will assume that IP is itself. When you are finish using this IP, you can remove the loopback alias like this:
sudo ifconfig lo0 -alias 192.168.46.49
One thing to be careful about is that the docker container won't send traffic to the parent host if it thinks the traffic's destination is itself. So check the loopback interface inside the container if you have trouble:
sudo ip addr show lo
In my case, this showed inet 127.0.0.1/8 which means I couldn't use any IPs in the 127.* range. That's why I used 192.168.* in the example above. Make sure the IP you use doesn't conflict with something on your own network.
AFAIK, in the case of Docker for Linux (standard distribution), the IP address of the host will always be 172.17.0.1 (on the main network of docker, see comments to learn more).
The easiest way to get it is via ifconfig (interface docker0) from the host:
ifconfig
From inside a docker, the following command from a docker: ip -4 route show default | cut -d" " -f3
You can run it quickly in a docker with the following command line:
# 1. Run an ubuntu docker
# 2. Updates dependencies (quietly)
# 3. Install ip package (quietly)
# 4. Shows (nicely) the ip of the host
# 5. Removes the docker (thanks to `--rm` arg)
docker run -it --rm ubuntu:22.04 bash -c "apt-get update > /dev/null && apt-get install iproute2 -y > /dev/null && ip -4 route show default | cut -d' ' -f3"
For those running Docker in AWS, the instance meta-data for the host is still available from inside the container.
curl http://169.254.169.254/latest/meta-data/local-ipv4
For example:
$ docker run alpine /bin/sh -c "apk update ; apk add curl ; curl -s http://169.254.169.254/latest/meta-data/local-ipv4 ; echo"
fetch http://dl-cdn.alpinelinux.org/alpine/v3.3/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.3/community/x86_64/APKINDEX.tar.gz
v3.3.1-119-gb247c0a [http://dl-cdn.alpinelinux.org/alpine/v3.3/main]
v3.3.1-59-g48b0368 [http://dl-cdn.alpinelinux.org/alpine/v3.3/community]
OK: 5855 distinct packages available
(1/4) Installing openssl (1.0.2g-r0)
(2/4) Installing ca-certificates (20160104-r2)
(3/4) Installing libssh2 (1.6.0-r1)
(4/4) Installing curl (7.47.0-r0)
Executing busybox-1.24.1-r7.trigger
Executing ca-certificates-20160104-r2.trigger
OK: 7 MiB in 15 packages
172.31.27.238
$ ifconfig eth0 | grep -oP 'inet addr:\K\S+'
172.31.27.238
The only way is passing the host information as environment when you create a container
run --env <key>=<value>
The --add-host could be a more cleaner solution (but without the port part, only the host can be handled with this solution). So, in your docker run command, do something like:
docker run --add-host dockerhost:`/sbin/ip route|awk '/default/ { print $3}'` [my container]
(From https://stackoverflow.com/a/26864854/127400 )
The standard best practice for most apps looking to do this automatically is: you don't. Instead you have the person running the container inject an external hostname/ip address as configuration, e.g. as an environment variable or config file. Allowing the user to inject this gives you the most portable design.
Why would this be so difficult? Because containers will, by design, isolate the application from the host environment. The network is namespaced to just that container by default, and details of the host are protected from the process running inside the container which may not be fully trusted.
There are different options depending on your specific situation:
If your container is running with host networking, then you can look at the routing table on the host directly to see the default route out. From this question the following works for me e.g.:
ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p'
An example showing this with host networking in a container looks like:
docker run --rm --net host busybox /bin/sh -c \
"ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p'"
For current versions of Docker Desktop, they injected a DNS entry into the embedded VM:
getent hosts host.docker.internal | awk '{print $1}'
With the 20.10 release, the host.docker.internal alias can also work on Linux if you run your containers with an extra option:
docker run --add-host host.docker.internal:host-gateway ...
If you are running in a cloud environment, you can check the metadata service from the cloud provider, e.g. the AWS one:
curl http://169.254.169.254/latest/meta-data/local-ipv4
If you want your external/internet address, you can query a remote service like:
curl ifconfig.co
Each of these have limitations and only work in specific scenarios. The most portable option is still to run your container with the IP address injected as a configuration, e.g. here's an option running the earlier ip command on the host and injecting it as an environment variable:
export HOST_IP=$(ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p')
docker run --rm -e HOST_IP busybox printenv HOST_IP
docker network inspect bridge -f '{{range .IPAM.Config}}{{.Gateway}}{{end}}'
It's possible to retrieve it using docker network inspect
TLDR for Mac and Windows
docker run -it --rm alpine nslookup host.docker.internal
... prints the host's IP address ...
nslookup: can't resolve '(null)': Name does not resolve
Name: host.docker.internal
Address 1: 192.168.65.2
Details
On Mac and Windows, you can use the special DNS name host.docker.internal.
The host has a changing IP address (or none if you have no network access). From 18.03 onwards our recommendation is to connect to the special DNS name host.docker.internal, which resolves to the internal IP address used by the host. This is for development purpose and will not work in a production environment outside of Docker Desktop for Mac.
If you want real IP address (not a bridge IP) on Windows and you have docker 18.03 (or more recent) do the following:
Run bash on container from host where image name is nginx (works on Alpine Linux distribution):
docker run -it nginx /bin/ash
Then run inside container
/ # nslookup host.docker.internal
Name: host.docker.internal
Address 1: 192.168.65.2
192.168.65.2 is the host's IP - not the bridge IP like in spinus accepted answer.
I am using here host.docker.internal:
The host has a changing IP address (or none if you have no network access). From 18.03 onwards our recommendation is to connect to the special DNS name host.docker.internal, which resolves to the internal IP address used by the host. This is for development purpose and will not work in a production environment outside of Docker for Windows.
In linux you can run
HOST_IP=`hostname -I | awk '{print $1}'`
In macOS your host machine is not the Docker host. Docker will install it's host OS in VirtualBox.
HOST_IP=`docker run busybox ping -c 1 docker.for.mac.localhost | awk 'FNR==2 {print $4}' | sed s'/.$//'`
I have Ubuntu 16.03. For me
docker run --add-host dockerhost:`/sbin/ip route|awk '/default/ { print $3}'` [image]
does NOT work (wrong ip was generating)
My working solution was that:
docker run --add-host dockerhost:`docker network inspect --format='{{range .IPAM.Config}}{{.Gateway}}{{end}}' bridge` [image]
Docker for Mac
I want to connect from a container to a service on the host
The host has a changing IP address (or none if you have no network access). From 18.03 onwards our recommendation is to connect to the special DNS name host.docker.internal, which resolves to the internal IP address used by the host.
The gateway is also reachable as gateway.docker.internal.
https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds
If you enabled the docker remote API (via -Htcp://0.0.0.0:4243 for instance) and know the host machine's hostname or IP address this can be done with a lot of bash.
Within my container's user's bashrc:
export hostIP=$(ip r | awk '/default/{print $3}')
export containerID=$(awk -F/ '/docker/{print $NF;exit;}' /proc/self/cgroup)
export proxyPort=$(
curl -s http://$hostIP:4243/containers/$containerID/json |
node -pe 'JSON.parse(require("fs").readFileSync("/dev/stdin").toString()).NetworkSettings.Ports["DESIRED_PORT/tcp"][0].HostPort'
)
The second line grabs the container ID from your local /proc/self/cgroup file.
Third line curls out to the host machine (assuming you're using 4243 as docker's port) then uses node to parse the returned JSON for the DESIRED_PORT.
My solution:
docker run --net=host
then in docker container:
hostname -I | awk '{print $1}'
Here is another option for those running Docker in AWS. This option avoids having using apk to add the curl package and saves the precious 7mb of space. Use the built-in wget (part of the monolithic BusyBox binary):
wget -q -O - http://169.254.169.254/latest/meta-data/local-ipv4
use hostname -I command on the terminal
Try this:
docker run --rm -i --net=host alpine ifconfig
So... if you are running your containers using a Rancher server, Rancher v1.6 (not sure if 2.0 has this) containers have access to http://rancher-metadata/ which has a lot of useful information.
From inside the container the IP address can be found here:
curl http://rancher-metadata/latest/self/host/agent_ip
For more details see:
https://rancher.com/docs/rancher/v1.6/en/rancher-services/metadata-service/
This is a minimalistic implementation in Node.js for who is running the host on AWS EC2 instances, using the afore mentioned EC2 Metadata instance
const cp = require('child_process');
const ec2 = function (callback) {
const URL = 'http://169.254.169.254/latest/meta-data/local-ipv4';
// we make it silent and timeout to 1 sec
const args = [URL, '-s', '--max-time', '1'];
const opts = {};
cp.execFile('curl', args, opts, (error, stdout) => {
if (error) return callback(new Error('ec2 ip error'));
else return callback(null, stdout);
})
.on('error', (error) => callback(new Error('ec2 ip error')));
}//ec2
and used as
ec2(function(err, ip) {
if(err) console.log(err)
else console.log(ip);
})
If you are running a Windows container on a Service Fabric cluster, the host's IP address is available via the environment variable Fabric_NodeIPOrFQDN. Service Fabric environment variables
Here is how I do it. In this case, it adds a hosts entry into /etc/hosts within the docker image pointing taurus-host to my local machine IP: :
TAURUS_HOST=`ipconfig getifaddr en0`
docker run -it --rm -e MY_ENVIRONMENT='local' --add-host "taurus-host:${TAURUS_HOST}" ...
Then, from within Docker container, script can use host name taurus-host to get out to my local machine which hosts the docker container.
Maybe the container I've created is useful as well https://github.com/qoomon/docker-host
You can simply use container name dns to access host system e.g. curl http://dockerhost:9200, so no need to hassle with any IP address.
The solution I use is based on a "server" that returns the external address of the Docker host when it receives a http request.
On the "server":
1) Start jwilder/nginx-proxy
# docker run -d -p <external server port>:80 -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy
2) Start ipify container
# docker run -e VIRTUAL_HOST=<external server name/address> --detach --name ipify osixia/ipify-api:0.1.0
Now when a container sends a http request to the server, e.g.
# curl http://<external server name/address>:<external server port>
the IP address of the Docker host is returned by ipify via http header "X-Forwarded-For"
Example (ipify server has name "ipify.example.com" and runs on port 80, docker host has IP 10.20.30.40):
# docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy
# docker run -e VIRTUAL_HOST=ipify.example.com --detach --name ipify osixia/ipify-api:0.1.0
Inside the container you can now call:
# curl http://ipify.example.com
10.20.30.40
On Ubuntu, hostname command can be used with the following options:
-i, --ip-address addresses for the host name
-I, --all-ip-addresses all addresses for the host
For example:
$ hostname -i
172.17.0.2
To assign to the variable, the following one-liner can be used:
IP=$(hostname -i)
Another approach is based on traceroute and it's working on a Linux host for me, e.g. in a container based on Alpine:
traceroute -n 8.8.8.8 -m 4 -w 1 | awk '$1~/\d/&&$2!~/^172\./{print$2}' | head -1
It takes a moment, but lists the first hop's IP that does not start with 172. If there is no successful response, try increasing the limit on the tested hops using -m 4 argument.
With https://docs.docker.com/machine/install-machine/
a) $ docker-machine ip
b) Get the IP address of one or more machines.
$ docker-machine ip host_name
$ docker-machine ip host_name1 host_name2

imgproxy on non default port

Ive installed imgproxy (https://docs.imgproxy.net/installation) on docker on a centos server.
Using
docker run -p 2096:2096 -it darthsim/imgproxy
to start it still starts the server on 8080:
INFO [2021-09-01T10:13:25Z] Starting server at :8080
What is a correct way of starting imgproxy on a non default port (in my cas 2096)?
In the docker run command the ports describe a mapping from the host to the container port. (which by default is 8080:8080)
So, to map your host's port 2096 to the container's 8080 use
docker run -p 2096:8080 -it darthsim/imgproxy
The --publish flag config is [host-port]:[container-port]. To preserve the container's port of 8080, but use the host's 2096, you want:
docker run \
--interactive --tty --rm \
--publish=2096:8080 \
darthsim/imgproxy

Nginx with docker does not work with different port mapping

I have created a basic static website and I'm trying to deploy my site on nginx server with docker.
It works fine when I run docker run -d -p 80:80 -v /path/to/site:/usr/share/nginx/html nginx
But when I map it with different port like -p 8080:80 it gives me a bad gateway error.
The initial homepage loads up fine http://localhost:8080,but when I try to navigate in the website like a blog post, it redirects to http://localhost/blog rather than http://localhost:8080/blog. I have not changed any configuration whatsoever, I have left it default.
Initially I thought it must be with my site but it works fine with httpd & docker. But I want to run it on a nginx server.
Please help me !!

Resolve docker container IP from host OS using container name

I have a non-containerized nginx instance serving as reverse proxy for containerized as well as non-containerized services.
Since container IPs can change on reboot, I don't want to use them in the nginx config file. I was looking for a simple way to reference the containers.
Docker containers can reference each other by container name i.e. DNS lookup of container names gives container's IP. I was looking for something similar but names should be resolvable from host OS.
Constraints:
Solution should work with existing containers. So no docker run ... commands
I have tried mageddo/dns-proxy-server. It is supposed to resolve container names but it does not even after setting the right environment variables.
sudo docker run -d \
--restart unless-stopped \
--name dns-proxy-server \
-p 5380:5380 \
-e MG_REGISTER_CONTAINER_NAMES=true \
--hostname dns.mageddo \
-v /opt/dns-proxy-server/conf:/app/conf \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /etc/resolv.conf:/etc/resolv.conf defreitas/dns-proxy-server
PS: Though nginx is taken as an example, the DNS lookup feature is helpful in many other scenarios. So I am looking for DNS lookup solution and not simply a fix for the nginx issue.
There is a solution which you can implement. Start the DNS server first.
docker run --rm --hostname dns.mageddo --name dns-proxy-server -p 5380:5380 \
-v /opt/dns-proxy-server/conf:/app/conf \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /etc/resolv.conf:/etc/resolv.conf \
defreitas/dns-proxy-server
Then run a test container to test the hostname
docker run --hostname test.intranet nginx
Testing it
ping test.intranet
PING test.intranet (172.18.0.3) 56(84) bytes of data.
64 bytes from 172.18.0.3 (172.18.0.3): icmp_seq=1 ttl=64 time=0.072 ms
64 bytes from 172.18.0.3 (172.18.0.3): icmp_seq=2 ttl=64 time=0.050 ms
64 bytes from 172.18.0.3 (172.18.0.3): icmp_seq=3 ttl=64 time=0.052 ms
64 bytes from 172.18.0.3 (172.18.0.3): icmp_seq=4 ttl=64 time=0.046 ms
Processes outside of Docker can't access the Docker DNS system; except for one specific configuration they can't access the container-private IPs either.
Instead you can publish ports out of your containers using the docker run -p option or Compose ports: option. A port number you specify will be stable, and will survive across container restarts. If you don't want the port to be directly accessible off-host, you can limit it to only being accessible from the host's loopback interface.
docker run -d --name backend \
-p 127.0.0.1:8001:3000 \ # port 8001 reaches this container, only on lo0
...
match /backend/ {
proxy_pass http://localhost:8001/
}
If it's important for your nginx configuration to use the Docker-internal DNS, you can run the nginx proxy inside Docker too.

Follow docker port mapping inside container

I have a cloud pc with static external ip, f.e. 162.243.100.100
Inside I installed docker with nginx, and mapped 80 port like this
docker run -it -p 80:80 nginx
I'm able to access nginx demo page from curl 162.243.100.100 from host machine.
I'm able to access nginx demo page from curl localhost from inside said container.
But I want to have able to access ngninx demo page from command curl 162.243.100.100 from inside said nginx containeer.
Seem this not follow port mapping, and just give me timeout error.
I thin I need to do something with network settings, but not know what.
The short answer is "don't do that."
The default iptables rules and routing tables from Docker aren't setup to route traffic from a container out to the host and back into the container through the docker-proxy. Considering how much this is an anti-pattern, I don't expect it to be a priority to change this behavior. It's much easier to work with the tool and use docker networks and the container name when talking from container to container.
Create a network and start your container in that network:
docker network create --subnet 172.16.0.0/16 dockernet
docker run -it -p 80:80 --net=dockernet nginx

Resources