Looking for a shell to manage multiple websites on a localhost

Hi. I’m looking for a shell or some kind of box solution to manage multiple websites on localhost. Something with proxy container and database containers, something that will automatically add and remove records to the host file of my machine. Do you follow me?

Let me put it others: you want to use multiple websites on a single webserver with vhosts (hostname or fqdn) and you want your /etc/hosts or local dns server updated.

You would absolutely love kubernetes and what external-dns is able to do with an ingress controller. If you run a local dns server, external-dns is able to fetch the hostname from ingress rules and update your dns server accordingly making the hostname immediatly available for your host’s name resolution.

Though, if k8s is too complicated, you could add labels (any key, value=hostname) to your container, register for container create/shutdown events (docker events or using the docker api) and write a handler that updates your /etc/hosts file accordingly or updates a local dns server via api whenever a container is created/shutdown that has the label you added.

I knew someone will bring up Kubernetes but I didn’t expect it to be the first reply :slight_smile: I didn’t work with Kubernetes yet but I’ll keep it in mind as an option, thanks.

At the moment I have multiple local hosts with docker based on jwilder/nginx-proxy and it works pretty neat but I was wondering if there are some tools out there to improve my workflow.

jwilder`s nginx-proxy and traefik (which I prefer) both use ENV’s or labels to define reverse proxy rules. Typicaly those include the domain as well, so you could read them from the event and modify your /etc/hosts accordingly

This is an example how a simple event handler could look like:

#!/bin/bash
show_full_json=false
docker events --format '{{json .}}' | while read entry; do
  if [ "${show_full_json}" == "true" ]; then
    echo "${entry}" |  jq '.'
  else
    type=$(echo "${entry}" |  jq -r '.Type')
    if [ ${type} == "container" ]; then
      action=$(echo "${entry}" | jq -r '.Action')
      scope=$(echo "${entry}" | jq -r '.scope')
      container_id=$(echo "${entry}" | jq -r '.id')
      image=$(echo "${entry}" | jq -r '.from')
      #actor_id=$(echo "${entry}" | jq -r '.Actor.ID')
      actor_attr_image=$(echo "${entry}" | jq -r '.Actor.Attributes.image')
      case ${action} in
        exec_create:*) echo "create: ${image} ${container_id} ${scope}"
                       docker inspect ${container_id} --format '{{json .Config.Env}}' | jq
                       ;;
        exec_start:*)  echo "start: ${container_id} ${actor_attr_image}";;
        exec_die*)     echo "die: ${container_id} ${actor_attr_image}";;
      esac
    fi
  fi
done

I wrote this example handler in roughly 30 minutes, just for the sake of giving it a try myself :slight_smile:

Due to easier parsing of json content, the docker events are formated as json. To process the output the command jq needs to be installed on the system. I provided some examples on how to read single values from the json. The json output will include the container labels, but not the envs. For that I added an example in exec_create*) for how to get additional information from the running container.

Good luck!

1 Like

I picked the wrong events and changed from echo "${entry}" | jq .. to jq .. <<< ${entry}.

Here is the correction

#!/bin/bash
show_full_json=false
docker events --format '{{json .}}' | while read entry; do
  if [ "${show_full_json}" == "true" ]; then
    jq '.' <<< ${entry}
  else
    type=$(jq -r '.Type' <<< ${entry})
    if [ ${type} == "container" ]; then
      action=$(jq -r '.Action' <<< ${entry})
      scope=$(jq -r '.scope' <<< ${entry})
      container_id=$(jq -r '.id' <<< ${entry})
      image=$(jq -r '.from' <<< ${entry})
      actor_attr_image=$(jq -r '.Actor.Attributes.image' <<< ${entry})
      case ${action} in
        start)  echo "start: ${image} ${container_id} ${scope}"
                       docker inspect ${container_id} --format '{{json .Config.Env}}' | jq
                       ;;
        die)     echo "die: ${container_id} ${actor_attr_image}";;
      esac
    fi
  fi
done
1 Like

Just in case someone is interesting:

I made shell for myself: https://github.com/commanddotcom/dockerlamp

It doesn’t look super cool but it works like a charm - I’m in control of my localhost containers and domains in *.local zone, I can create new project in a second and I don’t have any dependencies to backfire except Docker itself.