Repository: deis/deis

Issue:
Apps should be published with an descriptive hostname
Hello,

I'm currently testing out New Relic for monitoring applications we have running on Deis, and all the instances (JVMs) show up with names like this: `1ecbec0cb893`, since that is the Docker hostname.

What I would rather prefer, is a hostname like`my-application.cmd.1.deis.example.com` or  `my-application.cmd.1`, since this would make looking up the actual instance in New Relic easier.

I think the solution would be to use Dockers `--hostname=` parameter in `docker run` ( https://docs.docker.com/articles/networking/#dns ).

> `-h HOSTNAME` or `--hostname=HOSTNAME` — sets the hostname by which the container knows itself. This is written into `/etc/hostname`, into `/etc/hosts` as the name of the container's host-facing IP address, and is the name that `/bin/bash` inside the container will display inside its prompt. But the hostname is not easy to see from outside the container. It will not appear in docker ps nor in the `/etc/hosts` file of any other container.

Any thoughts? 



Discussion:
@Blystad this was discarded in #2428 (and others) because is not a thing that every deis user wants
For now the solution is use a custom controller image adding `-h %H` to the container template.
This is what I use https://github.com/soficom/deis/blob/cuscom_1.1.0/controller/scheduler/fleet.py#L295

@Blystad I forgot to clarify that `%H` will use the node hostname.
If you use the application name as hostname that means that each app will count as 1 server, i.e. 1 server license in newrelic terms, so if you have 30 apps that will require a plan for 30 servers. But using `%H` in a 3 node cluster only uses 3 "servers" and you will see the apps as a group in each server

I see...I would like to avoid having to maintain my own local version of the controller if possible, but my boss won't pay for 30 instances of New Relic...that's for sure :)

Based on what I could find, it seems that the conflict is between wether one should use `%H` or the application name `dancing-django_v2`

> Our preference would be to have application containers have a hostname that is the unit name, but this isn't valid due to the underscores (i.e. `dancing-django_v2`). (Ref: #2428)

#254 forces DNS compatible application names, so what if one simply strips out the version part (or replaces the underscore with a dot), then it would be a compatible DNS name.

If we also add an option, like:
`/deis/controller/exportHostname` - [`application` / `server`]
where `application` would set `hostname=dancing-django.v2.web.1` and `server`  `hostname=ip-10-0-2-5.eu-west-1.compute.internal`

Wouldn't that satisfy both parties by giving them the choice? 

> /deis/controller/exportHostname - [application / server]

Just add the actual behavior as default: `/deis/controller/exportHostname` : `[random|application|server]`

:+1: 


Resolving patch:
diff --git a/controller/deis/settings.py b/controller/deis/settings.py
--- a/controller/deis/settings.py
+++ b/controller/deis/settings.py
@@ -308,6 +308,13 @@
 # see https://docs.djangoproject.com/en/1.6/ref/settings/#secure-proxy-ssl-header
 SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
 
+# Unit Hostname handling.
+# Supports:
+#  default      - Docker generated hostname
+#  application  - Hostname based on application unit name (i.e. my-application.v2.web.1)
+#  server       - Hostname based on CoreOS server hostname
+UNIT_HOSTNAME = 'default'
+
 # Create a file named "local_settings.py" to contain sensitive settings data
 # such as database configuration, admin email, or passwords and keys. It
 # should also be used for any settings which differ between development
diff --git a/controller/scheduler/fleet.py b/controller/scheduler/fleet.py
--- a/controller/scheduler/fleet.py
+++ b/controller/scheduler/fleet.py
@@ -7,6 +7,7 @@
 import socket
 import re
 import time
+from django.conf import settings
 
 
 MATCH = re.compile(
@@ -112,6 +113,8 @@ def _create_container(self, name, image, command, unit, **kwargs):
             l.update({'cpu': '-c {}'.format(cpu)})
         else:
             l.update({'cpu': ''})
+        # set unit hostname
+        l.update({'hostname': self._get_hostname(name)})
         # should a special entrypoint be used
         entrypoint = kwargs.get('entrypoint')
         if entrypoint:
@@ -137,6 +140,19 @@ def _create_container(self, name, image, command, unit, **kwargs):
                 if attempt == (RETRIES - 1):  # account for 0 indexing
                     raise
 
+    def _get_hostname(self, application_name):
+        hostname = settings.UNIT_HOSTNAME
+        if hostname == "default":
+            return ''
+        elif hostname == "application":
+            # replace underscore with dots, since underscore is not valid in DNS hostnames
+            dns_name = application_name.replace("_", ".")
+            return '-h ' + dns_name
+        elif hostname == "server":
+            return '-h %H'
+        else:
+            raise RuntimeError('Unsupported hostname: ' + hostname)
+
     def start(self, name):
         """Start a container"""
         self._wait_for_container(name)
@@ -294,7 +310,7 @@ def attach(self, name):
     {"section": "Unit", "name": "Description", "value": "{name}"},
     {"section": "Service", "name": "ExecStartPre", "value": '''/bin/sh -c "IMAGE=$(etcdctl get /deis/registry/host 2>&1):$(etcdctl get /deis/registry/port 2>&1)/{image}; docker pull $IMAGE"'''},  # noqa
     {"section": "Service", "name": "ExecStartPre", "value": '''/bin/sh -c "docker inspect {name} >/dev/null 2>&1 && docker rm -f {name} || true"'''},  # noqa
-    {"section": "Service", "name": "ExecStart", "value": '''/bin/sh -c "IMAGE=$(etcdctl get /deis/registry/host 2>&1):$(etcdctl get /deis/registry/port 2>&1)/{image}; port=$(docker inspect -f '{{{{range $k, $v := .ContainerConfig.ExposedPorts }}}}{{{{$k}}}}{{{{end}}}}' $IMAGE | cut -d/ -f1) ; docker run --name {name} {memory} {cpu} -P -e PORT=$port $IMAGE {command}"'''},  # noqa
+    {"section": "Service", "name": "ExecStart", "value": '''/bin/sh -c "IMAGE=$(etcdctl get /deis/registry/host 2>&1):$(etcdctl get /deis/registry/port 2>&1)/{image}; port=$(docker inspect -f '{{{{range $k, $v := .ContainerConfig.ExposedPorts }}}}{{{{$k}}}}{{{{end}}}}' $IMAGE | cut -d/ -f1) ; docker run --name {name} {memory} {cpu} {hostname} -P -e PORT=$port $IMAGE {command}"'''},  # noqa
     {"section": "Service", "name": "ExecStop", "value": '''/usr/bin/docker rm -f {name}'''},
     {"section": "Service", "name": "TimeoutStartSec", "value": "20m"},
     {"section": "Service", "name": "RestartSec", "value": "5"},
diff --git a/controller/templates/confd_settings.py b/controller/templates/confd_settings.py
--- a/controller/templates/confd_settings.py
+++ b/controller/templates/confd_settings.py
@@ -44,3 +44,5 @@
 {{ if .deis_controller_webEnabled }}
 WEB_ENABLED = bool({{ .deis_controller_webEnabled }})
 {{ end }}
+
+UNIT_HOSTNAME = '{{ or (.deis_controller_unitHostname) "default" }}'


Test patch: