This morning, we released the official Docker CommandBox image v3.6.0, which adds support for customizing your server using environment variables in your build, including support for the conventions used by the cfconfig
CommandBox module. While the module is still in alpha, it is already proving to be a powerful way to provision and configure CFML servers at runtime.
Why use Docker?
If you're not up to speed with what Docker is and how it can benefit you, throughout the dev-ops lifecycle of your application, have a look at this summary article on the benefits or dig in to the official documentation. As we get further in to the weeds with configuration, we'll assume you're up to speed on the basics.
Running a CFML Docker Container using the CommandBox image
Once you're up to speed with running Docker on your local machine, starting up a CFML server using the CommandBox image is as simple as:
docker pull ortussolutions/commandbox
docker run -p 8080:8080 -v "/path/to/your/app:/app" ortussolutions/commandbox
This will serve a CFML application on port 8080
(which is the default $PORT
variable of the image) of the active Docker engine. If you have a server.json
file in the root of your directory, all of those config settings will be transferred over, with the exception of three, which will be overridden by the environment variables and used to expose the image: web.http.port
, web.ssl.port
(if applicable), and host
. The variables $PORT
and $SSL_PORT
may be specified when running the container, and will be used in place of the defaults. This allows you to prevent conflicts with other containers (e.g. Tomcat) which may use the default port. Note the -v
directive in the docker run
command which mounts your application in to the /app
directory of the container - which is the convention path CommandBox uses to serve your application.
The implementation problem of the above command is that it doesn't allow us to really deploy a configured CFML server. Through the 3.5.0 docker image, the only way to do that was to specify a custom app.serverHomeDirectory
in your server.json
file and then commit only your configuration files. Example .gitignore
for an ACF2016 server confiugured with an app.serverHomeDirectory
of /app/.engine
(placed inside your .engine
directory):
*
!.gitignore
!/WEB-INF/cfusion/lib/neo-*.xml
!/WEB-INF/cfusion/lib/*.properties
!*/
In the above .gitignore
, we are only persisting the .properties
and neo-*.xml
files used to configure the Coldfusion 2016 server. When the CFML engine WAR is exploded on the startup of your engine, those config files will persist and your server is pre-configured. You can still do this, however CFConfig
offers a way to accomplish the same thing without any configuration files in place through the use of environment variables. A quick tour of the available environment variables:
$PORT
- The port which your server should start on. The default is8080
.$SSL_PORT
- If applicable, the ssl port used by your server The default is8443
.$CFENGINE
- Using theserver.json
syntax, allows you to specify the CFML engine for your container (i.e. -adobe@2016
orlucee@5
)$cfconfig_[engine setting]
- Any environment variable provided which includes thecfconfig_
prefix will be determined to be acfconfig
setting and the value after the prefix is presumed to be the setting name. The commandcfconfig set ${Setting: settingName not found}=${Setting: value not found}
will be run to populate your setting in to the$SERVER_HOME_DIRECTORY
You can checkout an example config file here.$CFCONFIG
- If you need advanced configuration - datasources, caches, timeout settings, etc - acfconfig
-compatible JSON file may be provided with this environment variable. The file will be loaded and applied to your server. For Lucee servers, if anadminPassword
key exists, it will be applied as the Server and Web context passwords.$SERVER_HOME_DIRECTORY
- When provided, a custom path to your server home directory will be assigned. By default, this path is set as/root/serverHome
( Note: You may also provide this variable in your app's customizedserver.json
file )
If you already have a pre-configured CFML server you'd like to use, simply install cfconfig
and save your existing setting with the following commmand:
box cfconfig export myConfig.json /path/to/my/existing/server/install adobe@2016
View the docs for more config, transfer, import and export information.
Once you have your cfconfig file set, a simple docker run command would be:
docker run -p 8080:8080 -e "CFCONFIG=/app/myConfig.json" -v "/path/to/your/app:/app" ortussolutions/commandbox
The -e "CFCONFIG=/app/myConfig.json"
argument tells docker to set an environment variable of CFCONFIG
to /app/myConfig.json
. If that file exists when the Docker container is started, the configuration will be automatically applied. (Note: For additional security, you can mount your config in to a non-app path and simply change the environment variable above.)
Compose It
In reality, deploying a CFML server usually requires additional connection dependencies. If you need to deploy those dependencies along with your application, the easiest way to do so is using Docker Compose. It also provides a much more human-readable configuration, in comparison to script one-liners.
Let's say we want to deploy a self-contained stack that includes your CFML server, MySQL server, and reverse proxy your web traffic via NGINX. A single compose file in the root of your app, which connects the three of these containers might look like:
version: '2.1'
services:
# NGINX Container
web:
image: nginx
ports:
- "80:80"
- "443:443"
# Mount our shared webroot volume
volumes:
- .:/usr/share/nginx/html
- ./build/docker/nginx/nginx.conf:/etc/nginx/nginx.conf
#Application Server
app:
image: ortussolutions/commandbox
environment:
PORT: 8080
SSL_PORT: 8443
CFCONFIG: /app/cfConfig.json
volumes:
- .:/app
#MySQL
mysql:
image: mysql:latest
environment:
MYSQL_ROOT_PASSWORD: "MyS3cur3P455!"
volumes:
- ./build/docker/mysql/initdb:/docker-entrypoint-initdb.d
- /my/host/datadir:/var/lib/mysql
ports:
- "3306:3306"
There are a few things going on in this file, of note:
- We've mounted our NGINX configuration as well as the application in to the convention routes for configuration and the HTML root. You can skip the HTML root mounting, if you wish, as CommandBox will also serve your static files. We would need to provide the appropriate proxy settings from nginx to the host
app:8080
in order for NGINX to deliver our CFML, however - We've provided our
CFCONFIG
environment variable to our app container, which will be used to automatically configure the CFML server. - We've mounted an
initdb
directory in to the MySQL container's/docker-entrypoint-initdb.d
directory. The MySQL image will pick up any scripts in this directory, when it inits and run those scripts automatically - We've mounted
/my/host/datadir
in to/var/lib/mysql
in the MySQL container to allow our data files to persist between builds of the container.
Now, to start this "stack", from the root of our project, we can simply type:
docker-compose up --build
Your terminal will be filled with the results of the individual containers starting up, which may help you to debug configuration issues within the stack.
To bring up the stack quietly, use docker-compose up -d
, which will daemonize it.
Docker is increasingly becoming a powerful tool for orchestrating, scheduling, and deploying applications. It can also provide a powerful framework for common development environments for teams. If you're new to Docker, feel free to take the image for a spin. Once you pass the initial learning curve, we think you'll be excited about this powerful new tool in your toolbox!
Add Your Comment
(1)
Mar 29, 2017 15:34:41 UTC
by Jim
Good to see the ColdFusion community exploring tools like Docker and containers! I know what I'll be doing this weekend :)