Last active
October 10, 2016 11:58
-
-
Save Blizzke/709f19b522b7566ebe497ed4dee2d9ac to your computer and use it in GitHub Desktop.
The new Docker OSX opens up all ports on localhost. This means you'll have to add port numbers to urls etc. This hackisch gist creates an aliased IP on the `lo` interface and links the source ports from all containers to it. This means that you can use port 80 or so for a website and 3306 for mysql even if those are in use on the host.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* The new docker for OSX opens all ports on the OSX lo0 interface. | |
* This means you are forced to use urls like http://localhost:32580/ for your site. | |
* This script adds an alias IP to the lo0 interface (one you configure via the environment / .env file or by | |
* modifying the configuration below). | |
* Based on docker-compose "rules" it tries to guess the network created for the current folder so that | |
* it can use the docker inspect functionality to obtain all related containers and the ports they provide. | |
* Finally it uses the `socat`-tool to proxy ports on that virtual IP to the original port on localhost. | |
*/ | |
if ($argc !== 3) { | |
echo "Usage: php docker-alias.php [hostname] [ip]" . PHP_EOL; | |
exit; | |
} | |
$host = $argv[1]; | |
$ip = $argv[2]; | |
$network = preg_replace('/\W/', '', basename(getcwd())) . '_default'; // Assumed network name, update manually if incorrect | |
$sudo = '/usr/bin/sudo'; | |
$docker = "/usr/local/bin/docker"; | |
$compose = '/usr/local/bin/docker-compose'; | |
$socat = "$sudo /usr/local/bin/socat"; | |
$ifconfig = "$sudo /sbin/ifconfig"; | |
$lo = 'lo0'; | |
foreach (['killSocatInstances', 'removeLoAlias', 'addLoAlias', 'addPortForwards', 'addDockerHostEntry', 'addHostEntry'] as $function) { | |
$function(); | |
} | |
function removeLoAlias() | |
{ | |
global $ifconfig, $ip, $lo; | |
echo "> remove $lo alias..." . PHP_EOL; | |
exec("$ifconfig lo0 $ip delete 2>/dev/null"); | |
} | |
function addLoAlias() | |
{ | |
global $ifconfig, $ip, $lo; | |
echo "> add $lo alias..." . PHP_EOL; | |
exec("$ifconfig lo0 $ip alias"); | |
} | |
/** | |
* Make sure no socat processes are running | |
*/ | |
function killSocatInstances() | |
{ | |
global $ip; | |
echo "> Terminating all socat instances..." . PHP_EOL; | |
// Obtain all related socat processes for the current docker "machine" | |
$processes = array_filter(explode(PHP_EOL, trim(`pgrep -f socat.+$ip`))); | |
if (count($processes)) { | |
exec('sudo kill -9 ' . implode(' ', $processes)); | |
sleep(2); | |
} | |
} | |
/** | |
* Iterates through all containers and detects the ports being forwarded | |
* It will then use socat to create a redirect from "localhost:host-port" to "docker-host-ip:exported-port" for you | |
*/ | |
function addPortForwards() | |
{ | |
global $socat, $network; | |
$aliasIp = $GLOBALS['ip']; | |
// Use the network inspection to obtain list of all involved machines/containers | |
echo "> Adding container port forwards, obtaining container list via network '$network'..." . PHP_EOL; | |
$networkConfig = dockerInspect($network, 'network'); | |
foreach ($networkConfig['Containers'] as $container) { | |
echo ">> Inspecting container '{$container['Name']}'..." . PHP_EOL; | |
$containerConfig = dockerInspect($container['Name']); | |
$ports = array_get($containerConfig, 'NetworkSettings.Ports'); | |
if ($ports) { | |
foreach ($ports as $port => $portConfig) { | |
if (!$portConfig) { | |
// No host config = exposed port | |
continue; | |
} | |
list($port, $proto) = explode('/', $port); | |
if ($proto === 'tcp') { | |
// If no HostPort is present its a "one on one" | |
$hostPort = array_get($portConfig, '0.HostPort', $port); | |
if ($port && $aliasIp && $hostPort) { | |
echo ">>> 0.0.0.0:$hostPort -> $aliasIp:$port" . PHP_EOL; | |
// Note to self: Can only run in background if output gets redirected | |
exec("$socat tcp4-listen:$port,fork,bind=$aliasIp tcp4:127.0.0.1:$hostPort >/dev/null &"); | |
} | |
} | |
} | |
} else { | |
echo ">>> No ports found. Skipping." . PHP_EOL; | |
} | |
} | |
} | |
/** | |
* To get things like XDebug etc working we need an IP to connect back to. | |
* This gives all containers a hosts entry to use for that purpose. | |
* Docker re-creates the /etc/hosts every boot. | |
*/ | |
function addDockerHostEntry() | |
{ | |
global $docker, $network, $ip, $host; | |
echo "> Adding '$host' hosts entry to containers ..." . PHP_EOL; | |
$networkConfig = dockerInspect($network, 'network'); | |
foreach ($networkConfig['Containers'] as $container) { | |
echo ">> '{$container['Name']}'..." . PHP_EOL; | |
`$docker exec -i {$container['Name']} /bin/bash -c "echo $ip $host >> /etc/hosts"`; | |
} | |
} | |
function addHostEntry() | |
{ | |
global $sudo, $ip, $host; | |
echo "> Adding '$host' hosts entry to /etc/hosts ... "; | |
$hosts = file('/etc/hosts'); | |
foreach ($hosts as $index => $existingHost) { | |
if (strpos($existingHost, $host) !== false) { | |
if (substr(trim($existingHost), 0, 1) == '#') { | |
continue; | |
} | |
// IP matches? | |
if (strpos($existingHost, $ip) !== false) { | |
echo "found and up to date." . PHP_EOL; | |
return; | |
} else { | |
// Remove the line | |
echo "found but outdated (please remove the old one yourself) ... "; | |
unset($hosts[$index]); | |
break; | |
} | |
} | |
} | |
// Add extra line to the file | |
exec("echo \"$ip\t$host\" | $sudo tee -a /etc/hosts"); | |
echo "added" . PHP_EOL; | |
} | |
function dockerInspect($what, $type = '') | |
{ | |
global $docker; | |
$result = json_decode(`$docker $type inspect $what`, true); | |
return $result[0]; | |
} | |
function array_get($array, $key, $default = null) | |
{ | |
if (!is_array($array)) { | |
return $default; | |
} | |
if (is_null($key)) { | |
return $array; | |
} | |
if (array_key_exists($key, $array)) { | |
return $array[$key]; | |
} | |
foreach (explode('.', $key) as $segment) { | |
if (is_array($array) && array_key_exists($segment, $array)) { | |
$array = $array[$segment]; | |
} else { | |
return $default; | |
} | |
} | |
return $array; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Take a look at my related blog article for more details and motivation behind this script.