Docker for Lucee Developers: Part 2 – modius.io


#1

TLDR; break down the project template and make it your own.

Follows on from Docker for Lucee Developers: Part 1

Continuous delivery is hard; development pipelines are initimate affairs, tied very closely to the peculiarities of the application. One of the most important aspects of Dockerising development at Daemon was standardising how development pipelines work. We work with a lot of different bespoke applications, and having a standard structure for version control, development and deployment has become a major bonus of moving to Docker.

Our project template or "environment" has a few key requirements:

  • format should work for any language (well at least the ones we work with). For example, we're working with lucee, python and php so far.
  • each Docker image has its own project; we name them CLIENT-env-PROJECTNAME. For example, dae-env-prime for the Daemon public web site (codenamed Prime).
  • the environment allows for the composition of multiple containers, but is designed for working on a specific application. For example, we run both mysql and memcached along on Daemon Prime.
  • it is essential that the Docker image can be built locally, and also built remotely as part of a deployment process. For example, Daemon Prime is automatically built on each commit via http://quay.io and then deployed to a cluster of nodes at http://tutum.co.

The environment is designed to work with the Daemon Docker Workbench, but could easily be adapted for use with Docker Machine/Docker Compose.

Lucee Development Structure

lucee-docker-workbench/
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ Vagrantfile
β”œβ”€β”€ code (-> git submodule)
β”œβ”€β”€ config
β”‚   β”œβ”€β”€ lucee
β”‚   β”‚   └── lucee-web.xml.cfm
β”‚   └── nginx
β”‚       β”œβ”€β”€ conf.d
β”‚       β”‚   └── default.conf
β”‚       └── nginx.conf
└── logs
    β”œβ”€β”€ lucee
    β”œβ”€β”€ nginx
    β”œβ”€β”€ supervisor
    └── tomcat

Dockerfile

For Lucee development we use the official lucee/lucee4-nginx Docker image. It's a "batteries not included" style of image and we need to add our application.

FROM lucee/lucee4-nginx:latest
# NGINX configs
COPY config/nginx/ /etc/nginx/
# Lucee server PRODUCTION configs
COPY config/lucee/lucee-web.xml.cfm /opt/lucee/web/lucee-web.xml.cfm
# Deploy codebase to container
COPY code /var/www

This simple Dockerfile will work for most Lucee apps unchanged. It copies a specific config for the NGINX, a config for Lucee and your application code under the NGINX webroot. I'll go into constructing a more specialised Lucee Dockerfile in a later post.

Note, anything you want built into the Docker image needs to sit beneath the Dockerfile in the directory tree. This is one of the constraints of the Docker build process, and influences the directory structure of the project.

Vagrantfile

The Vagrantfile manages the the Docker host, specifies how the Docker image should be built and the configuration of the container when its run.

Note, the Vagrantfile works best in the Daemon Docker Workbench; its only a Vagrantfile in the parent directory so there's no reason not to use it.

  ##################################################
  # Launch dev containers
  # - vagrant up lucee
  ##################################################
  config.vm.define "lucee", autostart: true do |lucee|
    lucee.vm.provider "docker" do |docker|
      docker.name = PROJECT_ENV
      docker.build_dir = "."
      docker.env = {
        VIRTUAL_HOST: PROJECT_ENV + ".*, lucee.*"
      }
      # local development code, lucee config & logs
      docker.volumes = [
        "/vagrant/" + PROJECT_ENV + "/code:/var/www",
        "/vagrant/" + PROJECT_ENV + "/config/lucee/lucee-web.xml.cfm:/opt/lucee/web/lucee-web.xml.cfm",
        "/vagrant/" + PROJECT_ENV + "/logs/lucee:/opt/lucee/web/logs",
        "/vagrant/" + PROJECT_ENV + "/logs/nginx:/var/log/nginx",
        "/vagrant/" + PROJECT_ENV + "/logs/supervisor:/var/log/supervisor",
        "/vagrant/" + PROJECT_ENV + "/logs/tomcat:/usr/local/tomcat/logs"
        ]
      docker.vagrant_machine = WORKBENCH_HOST
      docker.vagrant_vagrantfile = WORKBENCH_VAGRANTFILE
      docker.force_host_vm = true
    end
    puts '############################################################'
    puts '# ' + PROJECT_ENV.upcase
    puts '#  - hosted at: http://' + PROJECT_ENV + '.dev'
    puts '############################################################'
  end

A few notes about the Docker provider:

  • the container is called PROJECT_ENV; that is, the directory name at the root of the project, for example, lucee-docker-workbench.
  • VIRTUAL_HOST is picked up by the reverse proxy built into the Docker host VM; this is awesome. You can add other environment variables here as needed.
  • the Docker volumes map the code base into the web root of NGINX, link the Lucee XML config, and pick up various logs for debugging

./code

./code is a directory stub that contains all of your application's code. By default its copied directly into the web root of the on board NGINX server.

In the template this is populated with a git submodule. It's not uncommon for us to bring in a range of libraries and the app code base with a list of submodules. Using submodules gives granular control over the version of each library being built into the image.

Note, it's a good idea to get into the habit of using SSH keys for your Git Repos. When you get to the point of automating Docker image builds from private repos it will be a requirement.

./config

./config is a directory stub for project configuration files. Use a sub-directory for each service.

./config/lucee contains the Lucee xml config for the web context; it could contain other configuration files as needed. The official Lucee Docker image is designed for a single web context per container. By default there is a Docker volume in the development setup that maps the lucee-web.xml.cfm in your code base to the one in the running container; changes you make in the Lucee admin will be reflected in your project's config file and can be committed to git as needed.

./config/nginx has a base NGINX server config (nginx.conf) and a web app specific config (default.conf). For a standard Lucee app these could be left unchanged, but I include it because everyone loves to tinker with their web server set up (or is that just me?).

./logs

Various log files are mapped out to this location. The project template has .gitignore files to keep the directory structure but block commits of the logs.

Hack Your Own Lucee Project

Download a ZIP of the lucee-docker-workbench; it's easier than forking or cloning the original repo. Create a new directory under your Workbench and unzip the contents. See if you can't get your own Lucee project running.

Hit me up in the comments below with questions.

Next I'll delve into the structure of the official Lucee Dockerfiles, the thinking behind their construction, and tips for how and why you might build your own.


This is a companion discussion topic for the original entry at http://modius.io/2016/01/03/docker-for-lucee-devs-p2/

#2

Hi Geoff,

Thank you for sharing this, it’s super cool and based on this I’ve been able to get a Lucee / nginx container up and running tonight.

I haven’t looked into it in great depth yet, but before I do I’m curious about the development workflow when you use this environment? Do you have to rebuild the docker container when you change code, or can you do it in β€œreal time” somehow?

Thanks!
Andrew


#3

The only time you need to rebuild the container is if you make a change to environment variables or any of the configuration files copied within the Dockerfile. That is, the vast majority of code changes are seen in real time as the ./code folder is sync’d with the running container as a volume.

That’s great! I’m working with Justin to replace the Vagrant set up with one based purely on Docker Toolkit. It should be released soon :slightly_smiling: