mirror of
https://github.com/dokku/dokku.git
synced 2025-12-29 00:25:08 +01:00
Merge pull request #864 from progrium/860-mh-bind-to-internal-ip
bind docker container to internal port if using vhosts
This commit is contained in:
@@ -136,3 +136,25 @@ dokku domains:clear myapp
|
||||
# remove a custom domain from app
|
||||
dokku domains:remove myapp example.com
|
||||
```
|
||||
|
||||
### Container network interface binding
|
||||
|
||||
> New as of 0.3.13
|
||||
|
||||
The deployed docker container running your app's web process will bind to either the internal docker network interface (i.e. `docker inspect --format '{{ .NetworkSettings.IPAddress }}' $CONTAINER_ID`) or an external interface (i.e. 0.0.0.0) depending on dokku's VHOST configuration. Dokku will attempt to bind to the internal docker network interface unless you specifically set NO_VHOST for the given app or your dokku installation is not setup to use VHOSTS (i.e. $DOKKU_ROOT/VHOST or $DOKKU_ROOT/HOSTNAME is set to an IPv4 or IPv6 address)
|
||||
|
||||
```shell
|
||||
# container bound to docker interface
|
||||
root@dokku:~/dokku# docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
1b88d8aec3d1 dokku/node-js-app:latest "/bin/bash -c '/star About a minute ago Up About a minute goofy_albattani
|
||||
|
||||
root@dokku:~/dokku# docker inspect --format '{{ .NetworkSettings.IPAddress }}' goofy_albattani
|
||||
172.17.0.6
|
||||
|
||||
# container bound to all interfaces (previous default)
|
||||
root@dokku:/home/dokku# docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
d6499edb0edb dokku/node-js-app:latest "/bin/bash -c '/star About a minute ago Up About a minute 0.0.0.0:49153->5000/tcp nostalgic_tesla
|
||||
|
||||
```
|
||||
|
||||
20
dokku
20
dokku
@@ -82,8 +82,20 @@ case "$1" in
|
||||
|
||||
# start the app
|
||||
DOCKER_ARGS=$(: | pluginhook docker-args $APP deploy)
|
||||
id=$(docker run -d -p 5000 -e PORT=5000 $DOCKER_ARGS $IMAGE /bin/bash -c "/start web")
|
||||
port=$(docker port $id 5000 | sed 's/[0-9.]*://')
|
||||
BIND_EXTERNAL=$(pluginhook bind-external-ip $APP)
|
||||
|
||||
if [[ "$BIND_EXTERNAL" = "false" ]];then
|
||||
port=5000
|
||||
id=$(docker run -d -e PORT=$port $DOCKER_ARGS $IMAGE /bin/bash -c "/start web")
|
||||
ipaddr=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' $id)
|
||||
|
||||
echo $ipaddr > "$DOKKU_ROOT/$APP/IP"
|
||||
else
|
||||
id=$(docker run -d -p 5000 -e PORT=5000 $DOCKER_ARGS $IMAGE /bin/bash -c "/start web")
|
||||
port=$(docker port $id 5000 | sed 's/[0-9.]*://')
|
||||
|
||||
[[ -f "$DOKKU_ROOT/$APP/IP" ]] && rm -f "$DOKKU_ROOT/$APP/IP"
|
||||
fi
|
||||
|
||||
# if we can't post-deploy successfully, kill new container
|
||||
function kill_new {
|
||||
@@ -95,7 +107,7 @@ case "$1" in
|
||||
# run checks first, then post-deploy hooks, which switches Nginx traffic
|
||||
trap kill_new INT TERM EXIT
|
||||
echo "-----> Running pre-flight checks"
|
||||
pluginhook check-deploy $id $APP $port
|
||||
pluginhook check-deploy $id $APP $port ${ipaddr:-localhost}
|
||||
|
||||
# now using the new container
|
||||
echo $id > "$DOKKU_ROOT/$APP/CONTAINER"
|
||||
@@ -103,7 +115,7 @@ case "$1" in
|
||||
echo "http://$(< "$DOKKU_ROOT/HOSTNAME"):$port" > "$DOKKU_ROOT/$APP/URL"
|
||||
|
||||
echo "-----> Running post-deploy"
|
||||
pluginhook post-deploy $APP $port
|
||||
pluginhook post-deploy $APP $port $ipaddr
|
||||
trap - INT TERM EXIT
|
||||
|
||||
# kill the old container
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
|
||||
|
||||
RE_IPV4="([0-9]{1,3}[\.]){3}[0-9]{1,3}"
|
||||
|
||||
RE_IPV6="([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|" # TEST: 1:2:3:4:5:6:7:8
|
||||
RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,7}:|" # TEST: 1:: 1:2:3:4:5:6:7::
|
||||
RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|" # TEST: 1::8 1:2:3:4:5:6::8 1:2:3:4:5:6::8
|
||||
RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|" # TEST: 1::7:8 1:2:3:4:5::7:8 1:2:3:4:5::8
|
||||
RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|" # TEST: 1::6:7:8 1:2:3:4::6:7:8 1:2:3:4::8
|
||||
RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|" # TEST: 1::5:6:7:8 1:2:3::5:6:7:8 1:2:3::8
|
||||
RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|" # TEST: 1::4:5:6:7:8 1:2::4:5:6:7:8 1:2::8
|
||||
RE_IPV6="${RE_IPV6}[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|" # TEST: 1::3:4:5:6:7:8 1::3:4:5:6:7:8 1::8
|
||||
RE_IPV6="${RE_IPV6}:((:[0-9a-fA-F]{1,4}){1,7}|:)|" # TEST: ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 ::
|
||||
RE_IPV6="${RE_IPV6}fe08:(:[0-9a-fA-F]{1,4}){2,2}%[0-9a-zA-Z]{1,}|" # TEST: fe08::7:8%eth0 fe08::7:8%1 (link-local IPv6 addresses with zone index)
|
||||
RE_IPV6="${RE_IPV6}::(ffff(:0{1,4}){0,1}:){0,1}${RE_IPV4}|" # TEST: ::255.255.255.255 ::ffff:255.255.255.255 ::ffff:0:255.255.255.255 (IPv4-mapped IPv6 addresses and IPv4-translated addresses)
|
||||
RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,4}:${RE_IPV4}" # TEST: 2001:db8:3:4::192.0.2.33 64:ff9b::192.0.2.33
|
||||
|
||||
case "$1" in
|
||||
domains)
|
||||
[[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1
|
||||
@@ -23,7 +38,7 @@ case "$1" in
|
||||
else
|
||||
VHOST=$(< "$DOKKU_ROOT/HOSTNAME")
|
||||
fi
|
||||
if [[ "$VHOST" =~ ([0-9]{1,3}[\.]){3}[0-9]{1,3} ]];then
|
||||
if [[ "$VHOST" =~ $RE_IPV4 ]] || [[ "$VHOST" =~ $RE_IPV6 ]];then
|
||||
echo "ip found as hostname. disabling vhost support"
|
||||
[[ ! $(grep -q NO_VHOST "$DOKKU_ROOT/$APP/ENV") ]] && echo "export NO_VHOST='1'" >> "$DOKKU_ROOT/$APP/ENV"
|
||||
else
|
||||
|
||||
36
plugins/nginx-vhosts/bind-external-ip
Executable file
36
plugins/nginx-vhosts/bind-external-ip
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
|
||||
|
||||
APP="$1"
|
||||
set +e; NO_VHOST=$(dokku config:get $APP NO_VHOST); set -e
|
||||
|
||||
RE_IPV4="([0-9]{1,3}[\.]){3}[0-9]{1,3}"
|
||||
|
||||
RE_IPV6="([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|" # TEST: 1:2:3:4:5:6:7:8
|
||||
RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,7}:|" # TEST: 1:: 1:2:3:4:5:6:7::
|
||||
RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|" # TEST: 1::8 1:2:3:4:5:6::8 1:2:3:4:5:6::8
|
||||
RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|" # TEST: 1::7:8 1:2:3:4:5::7:8 1:2:3:4:5::8
|
||||
RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|" # TEST: 1::6:7:8 1:2:3:4::6:7:8 1:2:3:4::8
|
||||
RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|" # TEST: 1::5:6:7:8 1:2:3::5:6:7:8 1:2:3::8
|
||||
RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|" # TEST: 1::4:5:6:7:8 1:2::4:5:6:7:8 1:2::8
|
||||
RE_IPV6="${RE_IPV6}[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|" # TEST: 1::3:4:5:6:7:8 1::3:4:5:6:7:8 1::8
|
||||
RE_IPV6="${RE_IPV6}:((:[0-9a-fA-F]{1,4}){1,7}|:)|" # TEST: ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 ::
|
||||
RE_IPV6="${RE_IPV6}fe08:(:[0-9a-fA-F]{1,4}){2,2}%[0-9a-zA-Z]{1,}|" # TEST: fe08::7:8%eth0 fe08::7:8%1 (link-local IPv6 addresses with zone index)
|
||||
RE_IPV6="${RE_IPV6}::(ffff(:0{1,4}){0,1}:){0,1}${RE_IPV4}|" # TEST: ::255.255.255.255 ::ffff:255.255.255.255 ::ffff:0:255.255.255.255 (IPv4-mapped IPv6 addresses and IPv4-translated addresses)
|
||||
RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,4}:${RE_IPV4}" # TEST: 2001:db8:3:4::192.0.2.33 64:ff9b::192.0.2.33
|
||||
|
||||
if [[ -f "$DOKKU_ROOT/VHOST" ]];then
|
||||
GLOBAL_VHOST=$(< "$DOKKU_ROOT/VHOST")
|
||||
else
|
||||
GLOBAL_VHOST=$(< "$DOKKU_ROOT/HOSTNAME")
|
||||
fi
|
||||
|
||||
if [[ -n "$NO_VHOST" ]]; then
|
||||
echo true # bind to external ip. VHOST is disabled for this app
|
||||
elif [[ "$GLOBAL_VHOST" =~ $RE_IPV4 ]] || [[ "$GLOBAL_VHOST" =~ $RE_IPV6 ]]; then
|
||||
echo true # bind to external ip. GLOBAL_VHOST is somehow an IPv4 or IPv6 address
|
||||
elif [[ -f "$DOKKU_ROOT/$APP/VHOST" ]]; then
|
||||
echo false # bind to docker ip. this app has a vhost defined
|
||||
else
|
||||
echo false
|
||||
fi
|
||||
@@ -15,7 +15,7 @@ restart_nginx () {
|
||||
|
||||
case "$1" in
|
||||
nginx:build-config)
|
||||
APP="$2"; PORT="$3"
|
||||
APP="$2"; PORT="$3"; IP="${4:-127.0.0.1}"
|
||||
[[ -z "$PORT" ]] && PORT=$(< "$DOKKU_ROOT/$APP/PORT")
|
||||
VHOST_PATH="$DOKKU_ROOT/$APP/VHOST"
|
||||
WILDCARD_SSL="$DOKKU_ROOT/tls"
|
||||
@@ -68,7 +68,7 @@ EOF
|
||||
NOSSL_SERVER_NAME=$(echo $NONSSL_VHOSTS $SSL_VHOSTS| tr '\n' ' ')
|
||||
|
||||
echo "-----> Creating $SCHEME nginx.conf"
|
||||
echo "upstream $APP { server 127.0.0.1:$PORT; }" > $DOKKU_ROOT/$APP/nginx.conf
|
||||
echo "upstream $APP { server $IP:$PORT; }" > $DOKKU_ROOT/$APP/nginx.conf
|
||||
eval "cat <<< \"$(< $NGINX_CONF)\" >> $DOKKU_ROOT/$APP/nginx.conf"
|
||||
|
||||
echo "-----> Running nginx-pre-reload"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
|
||||
|
||||
APP="$1"; PORT="$2"
|
||||
APP="$1"; PORT="$2"; IP="$3"
|
||||
|
||||
set +e; NO_VHOST=$(dokku config:get $APP NO_VHOST); set -e
|
||||
|
||||
@@ -11,4 +11,4 @@ elif [[ ! -f "$DOKKU_ROOT/$APP/VHOST" ]]; then
|
||||
dokku domains:setup $APP
|
||||
fi
|
||||
|
||||
dokku nginx:build-config $APP $PORT
|
||||
dokku nginx:build-config $APP $PORT $IP
|
||||
|
||||
68
tests/unit/ports.bats
Normal file
68
tests/unit/ports.bats
Normal file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
load test_helper
|
||||
|
||||
setup() {
|
||||
[[ -f "$DOKKU_ROOT/VHOST" ]] && cp -f "$DOKKU_ROOT/VHOST" "$DOKKU_ROOT/VHOST.bak"
|
||||
[[ -f "$DOKKU_ROOT/HOSTNAME" ]] && cp -f "$DOKKU_ROOT/HOSTNAME" "$DOKKU_ROOT/HOSTNAME.bak"
|
||||
|
||||
}
|
||||
|
||||
teardown() {
|
||||
destroy_app
|
||||
[[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST"
|
||||
[[ -f "$DOKKU_ROOT/HOSTNAME.bak" ]] && mv "$DOKKU_ROOT/HOSTNAME.bak" "$DOKKU_ROOT/HOSTNAME"
|
||||
}
|
||||
|
||||
@test "port exposure (with global VHOST)" {
|
||||
echo "dokku.me" > "$DOKKU_ROOT/VHOST"
|
||||
deploy_app
|
||||
CONTAINER_ID=$(docker ps --no-trunc| grep dokku/$TEST_APP | grep "start web" | awk '{ print $1 }')
|
||||
run bash -c "docker port $CONTAINER_ID | sed 's/[0-9.]*://' | egrep '[0-9]*'"
|
||||
echo "output: "$output
|
||||
echo "status: "$status
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "port exposure (without global VHOST and real HOSTNAME)" {
|
||||
rm "$DOKKU_ROOT/VHOST"
|
||||
echo "dokku.me" > "$DOKKU_ROOT/HOSTNAME"
|
||||
deploy_app
|
||||
CONTAINER_ID=$(docker ps --no-trunc| grep dokku/$TEST_APP | grep "start web" | awk '{ print $1 }')
|
||||
run bash -c "docker port $CONTAINER_ID | sed 's/[0-9.]*://' | egrep '[0-9]*'"
|
||||
echo "output: "$output
|
||||
echo "status: "$status
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "port exposure (with NO_VHOST set)" {
|
||||
deploy_app
|
||||
dokku config:set $TEST_APP NO_VHOST=1
|
||||
CONTAINER_ID=$(docker ps --no-trunc| grep dokku/$TEST_APP | grep "start web" | awk '{ print $1 }')
|
||||
run bash -c "docker port $CONTAINER_ID | sed 's/[0-9.]*://' | egrep '[0-9]*'"
|
||||
echo "output: "$output
|
||||
echo "status: "$status
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "port exposure (without global VHOST and IPv4 address as HOSTNAME)" {
|
||||
rm "$DOKKU_ROOT/VHOST"
|
||||
echo "127.0.0.1" > "$DOKKU_ROOT/HOSTNAME"
|
||||
deploy_app
|
||||
CONTAINER_ID=$(docker ps --no-trunc| grep dokku/$TEST_APP | grep "start web" | awk '{ print $1 }')
|
||||
run bash -c "docker port $CONTAINER_ID | sed 's/[0-9.]*://' | egrep '[0-9]*'"
|
||||
echo "output: "$output
|
||||
echo "status: "$status
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "port exposure (without global VHOST and IPv6 address as HOSTNAME)" {
|
||||
rm "$DOKKU_ROOT/VHOST"
|
||||
echo "fda5:c7db:a520:bb6d::aabb:ccdd:eeff" > "$DOKKU_ROOT/HOSTNAME"
|
||||
deploy_app
|
||||
CONTAINER_ID=$(docker ps --no-trunc| grep dokku/$TEST_APP | grep "start web" | awk '{ print $1 }')
|
||||
run bash -c "docker port $CONTAINER_ID | sed 's/[0-9.]*://' | egrep '[0-9]*'"
|
||||
echo "output: "$output
|
||||
echo "status: "$status
|
||||
assert_success
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# constants
|
||||
DOKKU_ROOT=${DOKKU_ROOT:=~dokku}
|
||||
TEST_APP=my-cool-guy-test-app
|
||||
|
||||
# test functions
|
||||
|
||||
Reference in New Issue
Block a user