dory

Development proxy for docker


License
MIT
Install
brew install dory

Documentation

dory

Gem Version Build Status Code Climate Test Coverage Dependency Status

Dory lets you forget about IP addresses and port numbers while you are developing your application. Through the magic of local DNS and a reverse proxy, you can access your app at the domain of your choosing. For example, http://myapp.docker or http://this-is-a-really-long-name.but-its-cool-cause-i-like-it

Now with support for Docker for Mac and even dinghy! (more info on using with dinghy below)

Dory wraps codekitchen/dinghy-http-proxy and makes it easily available for use outside of dinghy. This way you can work comfortably side by side with your colleagues who run dinghy on macOS.

Specifically, dory will:

  • Fire up the nginx proxy in a daemonized docker container for you
  • Configure and start a local dnsmasq to forward DNS queries for your local domain to the nginx proxy
  • Configure your local DNS resolver to point to the local dnsmasq

Installation

A package for .deb and .rpm is planned as well as a systemd service. If you'd like to help out with any of that, let me know!

Homebrew (recommended on macOS)

brew install dory

Ruby gem (recommended on Linux)

NOTE: Dory requires ruby version 2.2 or greater to be installed on your system already.

If you use multiple versions, your system ruby is too old or you just prefer not to install gems into your system ruby, I recommend installing the ruby version with ruby-install and then managing it with chruby.

gem install dory

Arch Linux (hosted on the quarry repository)

pacman -S ruby-dory

Quick Start

In most cases, the default configuration will be all you need. You literally just set the VIRTUAL_HOST environment variable in your container, install dory and then run:

dory up

If you want to fine-tune, generate a config file with:

dory config-file

and edit away at ~/.dory.yml

Usage

Dory has a small selection of commands that are hopefully intuitive. To customize and fine-tune dory's behavior, it can be configured with a yaml config file.

Commands

Commands:
  dory attach          # Attach to the output of a docker service container
  dory config-file     # Write a default config file
  dory down            # Stop all dory services
  dory help [COMMAND]  # Describe available commands or one specific command
  dory ip              # Grab the IPv4 address of a running dory service
  dory pull            # Pull down the docker images that dory uses
  dory restart         # Stop and restart all dory services
  dory status          # Report status of the dory services
  dory up              # Bring up dory services (nginx-proxy, dnsmasq, resolv)
  dory upgrade         # Upgrade dory to the latest version
  dory version         # Check current installed version of dory

Options:
  v, [--verbose], [--no-verbose]

Config file

Dory will start looking for a config file in your current working directory, and will recurse up to / until it finds one. If dory does not find a config file, it will use the default settings.

You can bootstrap your config file with the default settings using dory config-file. This file will be placed by default at ~/.dory.yml, but again you can move it to a preferred place. This allows you to have project-specific dory configs if you so desire by putting the config at <your-project>/.dory.yml:

---
dory:
  # Be careful if you change the settings of some of
  # these services.  They may not talk to each other
  # if you change IP Addresses.
  # For example, resolv expects a nameserver listening at
  # the specified address.  dnsmasq normally does this,
  # but if you disable dnsmasq, it
  # will make your system look for a name server that
  # doesn't exist.
  dnsmasq:
    enabled: true
    domains:               # array of domains that will be resolved to the specified address
      - domain: docker     # you can set '#' for a wilcard
        address: 127.0.0.1 # return for queries against the domain
    container_name: dory_dnsmasq
    port: 53  # port to listen for dns requests on.  must be 53 on linux. can be anything that's open on macos
    # kill_others: kill processes bound to the port we need (see previous setting 'port')
    #   Possible values:
    #     ask (prompt about killing each time. User can accept/reject)
    #     yes|true (go ahead and kill without asking)
    #     no|false (don't kill, and don't even ask)
    kill_others: ask
    service_start_delay: 5  # seconds to wait after restarting systemd services
  nginx_proxy:
    enabled: true
    container_name: dory_dinghy_http_proxy
    https_enabled: true
    ssl_certs_dir: ''  # leave as empty string to use default certs
    port: 80           # port 80 is default for http
    tls_port: 443      # port 443 is default for https

  resolv:
    enabled: true
    nameserver: 127.0.0.1
    port: 53  # port where the nameserver listens. On linux it must be 53

Upgrading existing config file

If you run the dory config-file command and have an existing config file, dory will offer you the option of upgrading. This will preserve your settings and migrate you to the latest format. You can skip the prompt by passing the --upgrade flag

dory config-file --upgrade
Usage:
  dory config-file

Options:
  u, [--upgrade], [--no-upgrade]
  f, [--force]

Description:
  Writes a dory config file to /home/ben/.dory.yml containing the default
  settings. This can then be configured as preferred.

Making your containers accessible by name (DNS)

To make your container(s) accessible through a domain, all you have to do is set a VIRTUAL_HOST environment variable in your container. That's it! (Well, and you have to start dory with dory up)

The proxy will by default use the first port exposed by your container as the HTTP port to proxy to. This can be overridden by setting the VIRTUAL_PORT environment variable on the container to the desired HTTP port.

You will also need to set VIRTUAL_PORT if your server binds to something other than 80 inside its container (e.g. VIRTUAL_PORT: 3000). This will tell the nginx proxy which port to forward traffic to in your container. When accessing the server from outside of docker, you will still hit port 80 (such as with your web browser).

If your back-end container uses HTTPS, then set VIRTUAL_PROTO: https to tell the nginx proxy to use https instead of the default http.

Many people do this in their docker-compose.yml file:

version: '2'
services:
  web:
    build: .
    depends_on:
      - db
      - redis
    environment:
      VIRTUAL_HOST: myapp.docker
  redis:
    image: redis
    environment:
      VIRTUAL_HOST: redis.docker
      VIRTUAL_PORT: 6379
  db:
    image: postgres
    environment:
      VIRTUAL_HOST: postgres.docker
      VIRTUAL_PORT: 5432

In the example above, you can hit the web container from the host machine with http://myapp.docker, and the redis container with tcp://redis.docker. This does not affect any links on the internal docker network.

You could also just run a docker container with the environment variable like this:

docker run -e VIRTUAL_HOST=myapp.docker  ...

Usage with dinghy

If you are using dinghy, but want to use dory to manage the proxy instead of dinghy's built-in stuff, this is now possible! (the use case for this that we ran into was multiple domain support. For example, the dev wanted to have some containers accessible at something.docker). To accomplish this, you need to disable dinghy's proxy stuff (otherwise dinghy and dory will stomp on each other's resolv files):

In your ~/.dinghy/preferences.yml file, disable the proxy:

:preferences:
  :proxy_disabled: true
  ...

In your dory config file (which can be at <your-project>/.dory.yml or anywhere else in parent directories, ~/.dory.yml being the default) (hint: if it doesn't exist, generate it with dory config-file), set your dnsmasq domains and their addresses to dinghy, as well as the resolv nameserver. Here is an example (with unrelated parts removed for ease of reading):

---
dory:
  dnsmasq:
    domains:
      - domain: docker
        address: dinghy # instead of the default 127.0.0.1
    ...
  resolv:
    nameserver: dinghy # instead of the default 127.0.0.1

If the dinghy vm gets rebooted for some reason, or otherwise changes IP addresses, you may need to restart dory to pickup the changes:

dory restart

Root privilege requirement

To configure the local resolver, dory needs to edit the /etc/resolv.conf. Therefore you may be prompted for your sudo password during dory up/restart/down. If you do not want to enter your password every time you can extend the sudoers config as follows:

sudo visudo -f /etc/sudoers.d/dory-edit-resolv-conf

To allow passwordless execution only for a single user (replace my-user accordingly):

Cmnd_Alias DORY_EDIT_RESOLVCONF = /usr/bin/tee /etc/resolv.conf
my-user ALL=(root) NOPASSWD: DORY_EDIT_RESOLVCONF

To allow passwordless execution for all users in group sudo (you can list the affected users with awk -F':' '/sudo/{print $4}' /etc/group):

Cmnd_Alias DORY_EDIT_RESOLVCONF = /usr/bin/tee /etc/resolv.conf
%sudo ALL=(root) NOPASSWD: DORY_EDIT_RESOLVCONF

On OS X you probably need to change %sudo to %admin:

Cmnd_Alias DORY_EDIT_RESOLVCONF = /usr/bin/tee /etc/resolv.conf
%admin ALL=(root) NOPASSWD: DORY_EDIT_RESOLVCONF

Note: Changes are only applied after closing the file.

Troubleshooting

Halp the dnsmasq container is having issues starting!

Make sure you aren't already running a dnsmasq service (or some other service) on port 53. Because the Linux resolv file doesn't have support for port numbers, we have to run on host port 53. To make matters fun, some distros (such as those shipping with NetworkManager) will run a dnsmasq on 53 to perform local DNS caching. This is nice, but it will conflict with Dory's dnsmasq container. You will probably want to disable it.

If using Network Manager, try commenting out dns=dnsmasq in /etc/NetworkManager/NetworkManager.conf. Then restart NetworkManager: sudo service network-manager restart or sudo systemctl restart NetworkManager

If you're using Network Manager/DNSMasqd to do NAT and/or share internet with the computer you are installing dory on, stop. You'd need to configure dory's built in DNSmasq to do the same, which is not trivial, out of scope, and probably more than you're bargaining for.

If you are on Mac, you can choose which port to bind the dnsmasq container to. In your dory config file, adjust the setting under dory -> dnsmasq -> port. You probably want to make dory -> resolv -> port match. The default value on Mac is 19323.

As of version 0.5.0, dory is a little smarter at handling this problem for you. dory will identify if you have systemd services running that will race for the port and cause issues. It will offer to put those services down temporarily and then put them back up when finished. You can configure this behavior in the config file to achieve minimal annoyance (since you'll likely be prompted every time by default).

Is this dinghy for Linux?

No. Well, maybe sort of, but not really. Dinghy has a lot of responsibilities on OS X, most of which are not necessary on Linux since docker runs natively. Something it does that can benefit linux users however, is the setup and management of an nginx reverse HTTP proxy. For this reason, dory exists to provide this reverse proxy on Linux, along with accompanying dnsmasq and resolv services. Using full dinghy on Linux for local development doesn't really make sense to me, but using a reverse proxy does. Furthermore, if you work with other devs who run Dinghy on OS X, you will have to massage your docker-compose files to avoid conflicting. By using dory, you can safely use the same VIRTUAL_HOST setup without conflict. And because dory uses dinghy-http-proxy under the hood, you will be as compatible as possible.

Are there any reasons to run full dinghy on Linux?

Generally speaking, IMHO, no. The native experience is superior. However, for some reason maybe you'd prefer to not have docker on your local machine? Maybe you'd rather run it in a VM? If that describes you, then maybe you want full dinghy.

I am intrigued at the possibilities of using dinghy on Linux to drive a cloud-based docker engine. For that, stay tuned.

Why didn't you just fork dinghy?

That was actually my first approach, and I considered it quite a bit. As I went through the process in my head tho, and reviewed the dinghy source code, I decided that it was just too heavy to really fit the need I had. I love being able to run docker natively, and I revere the Arch Way. Dinghy just seemed like too big of a hammer for this problem (the problem being that I work on Linux, but my colleagues use OS X/Dinghy for docker development).

What if I'm developing on a cloud server?

You do this too!? Well fine hacker, it's your lucky day because dory has you covered. You can run the nginx proxy on the cloud server and the dnsmasq/resolver locally. Here's how:

  • Install dory on both client and server:
gem install dory
  • Gen a base config file:
dory config-file
  • On the local machine, disable the nginx-proxy, and set the dnsmasq address to that of your cloud server:
  dnsmasq:
    enabled: true
    domain: docker      # domain that will be listened for
    address: <cloud-server-ip>  # address returned for queries against domain
    container_name: dory_dnsmasq
  nginx_proxy:
    enabled: false
    container_name: dory_dinghy_http_proxy
  • On the server, disable resolv and dnsmasq:
  dnsmasq:
    enabled: false
    domain: docker      # domain that will be listened for
    address: 127.0.0.1  # address returned for queries against domain
    container_name: dory_dnsmasq
  resolv:
    enabled: false
    nameserver: 127.0.0.1
  • Profit!

Contributing

Want to contribute? Cool! Fork it, push it, request it. Please try to write tests for any functionality you add.

Development Quick Start

  1. If you want to send a pull request with your changes, then fork the repo
  2. Clone it: git clone https://github.com/FreedomBen/dory.git or if you forked in step 1, use the URL for your fork
  3. Make your changes
  4. Build the gem locally: gem build dory.gemspec
  5. Now you can run your locally built version of the gem like normal: dory <somecommand>
  6. Rinse and repeat. For easy cleaning and reinstalling, I recommend using this command, which you might want to alias: rm *.gem; gem clean dory; yes | gem uninstall dory; gem build dory.gemspec && gem install dory*.gem
  7. Run the specs locally (note that I've attempted to make the specs interfere with the running system as minimally as possible, but some things are difficult to avoid. For example, if you have something running on port 53, the specs will kill it. Also, you will need to enter password for sudo): bundle exec rspec spec/
  8. Specific specs can be run with: bundle exec rspec spec/some/file.rb

Built on: