Docker Compose Environments for Coldfusion Development
A basic Docker-Compose setup for continuous integration with Testbox, Lucee, Commandbox, and MariaDB.
Hey folks! We'll be going over:
- Running Lucee and Commandbox with Docker
- Setting up MariaDB initialization scripts
- Running MariaDB with Docker
- Scripting our environment with Docker Compose
- Creating a datasource with environment variables
Lucee in the Docker with Commandbox
I'm using ortussolutions/commandbox:alpine
for CF development on Docker. This makes it super (SUPER!) easy to get started with Coldfusion on a new machine, or if you've never played with it before. One Docker command is all that separates you from Coldfusion and Lucee.
Note: I'm using the alpine
tag because it's almost half the size (500MB) of other containers. This means it's faster to download, upload, execute, etc. YMMV.
For a basic, I-Want-It-Now CF container, run this docker
command to start a Lucee server on port 8010
for the current directory.
docker run \
-v "$PWD:/app" \
-p "8010:8080"
ortussolutions/commandbox:alpine
To install Testbox and get set up for testing, you'll need to
- add
-e BOX_INSTALL=true
to that docker command - drop
testbox
as a dependency in yourbox.json
- copy the Testbox harness into your
tests/
folder
This is basically what I did for my app. Please see other docs for Configuring Testbox or messing with the Commandbox Docker Image.
Initializing a MariaDB Database
The exact app I was testing required a database. I chose to use the MariaDB Docker image to keep my dev machine clean and installation easy.
First, we'll want to have some testable data in our database for Testbox to act against. In other words, we need an init script which will load data into the MariaDB database when the container loads. See MariaDB Docker page (italics mine):
When a container is started for the first time, a new database with the specified name will be created and initialized with the provided configuration variables. Furthermore, it will execute files with extensions .sh, .sql and .sql.gz that are found in /docker-entrypoint-initdb.d. Files will be executed in alphabetical order. You can easily populate your mariadb services by mounting a SQL dump into that directory and provide custom images with contributed data. SQL files will be imported by default to the database specified by the MYSQL_DATABASE variable.
So, basically this means that if we want any data to exist in our database on startup, then we should create a local (host) directory, dump in our SQL scripts, and setup a docker volume which points to the mariadb initialization folder. If you were using docker run
that'd be something like -v tests/testdb:/docker-entrypoint-initdb.d
.
Steps for Initializing MariaDB
- Create a
testdb
folder -mkdir tests/testdb
- Add your SQL to
tests/testdb/startMyDB.sql
- Don't forget to
CREATE TABLE
!
Spin Up and Connect to MariaDB
Finally, we'll need to connect to MariaDB from outside the docker image, so we need to
- publish the MariaDB port
- Know what port to connect to in Coldfusion
- Know what hostname to connect to. By default, it's the container name, like
mariadb
.
If you want to lock this down a little, feel free to use a different port for the container to listen to, like 3333
.
A simple docker run
for MariaDB should look something like this:
docker run \
-v ./tests/testdb:/docker-entrypoint-initdb.d \
-p "3306:3306" \
-e MYSQL_USER=cfOnDocker \
-e MYSQL_PASSWORD=cf_Is_S0_Gr8 \
-e MYSQL_RANDOM_ROOT_PASSWORD=true \
-e MYSQL_DATABASE=myTestDB \
mariadb:latest
Notice those environment vars? Feel free to use a .env
file, ENV shell variables, or other means to automate them. Personally, I used a .env
file in the root, which then allows you to reference those variables within docker-compose.yml
.
Scripting Our Environment with Docker Compose
At this point, I highly recommend you create a .env file in the project root for storing secrets - and make sure you add it to your .gitignore!
# do NOT commit me to git!
# environment vars for the mariadb instance
# these will also be referenced by Coldfusion when connecting to the database
MYSQL_USER=cfOnDocker
MYSQL_PASSWORD=cf_Is_S0_Gr8
MYSQL_RANDOM_ROOT_PASSWORD=true
MYSQL_DATABASE=myDB
# environment vars for Coldfusion
CF_DB_HOST=mariadb
CF_DB_PORT=3306
Then docker-compose.yml
, we'll reference those vars like CF_DB_PORT=${CF_DB_PORT}
. Or, alternatively, we could use env_file: ./.env
to pull in all the .env variables to each container without needing to specify them one by one in the docker compose file.
version: '3'
services:
web:
image: ortussolutions/commandbox:alpine
ports:
- "8010:80"
volumes:
- ./:/app
environment:
- APP_DIR=/app
- BOX_INSTALL=true
- PORT=80
- HEADLESS=true
- MYSQL_USER=${MYSQL_USER}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
- MYSQL_RANDOM_ROOT_PASSWORD=${MYSQL_RANDOM_ROOT_PASSWORD}
- MYSQL_DATABASE=${MYSQL_DATABASE}
- CF_DB_HOST=${CF_DB_HOST}
- CF_DB_PORT=${CF_DB_PORT}
mariadb:
image: mariadb:latest
ports:
- "3306:3306"
volumes:
- ./tests/testdb:/docker-entrypoint-initdb.d
environment: # note: you could also use env_file: ./.env
- MYSQL_USER=${MYSQL_USER}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
- MYSQL_RANDOM_ROOT_PASSWORD=${MYSQL_RANDOM_ROOT_PASSWORD}
- MYSQL_DATABASE=${MYSQL_DATABASE}
Boom! This should be all we need to start up Lucee and MariaDB containers.
- Run
docker-compose up -d
to start the test environment. - Run
docker-compose logs
to see all container logs. - Browse to
http://localhost:8010/tests/runner.cfm
to view and run tests. - Run
docker-compose down
to stop the test containers.
Using Environment Variables for a Test Datasource
Last thing - we need a datasource so we can connect to MariaDB. I used Lucee's server.system.environment
struct in Coldfusion to pull in the environment variables passed to the container via docker compose.
Here's my datasource struct - I put this in /tests/Application.cfc
so it was available for use while running my tests.
this.datasources["test"] = {
class: 'com.mysql.jdbc.Driver'
, bundleName: 'com.mysql.jdbc'
, bundleVersion: '5.1.40'
, connectionString: 'jdbc:mysql://#server.system.environment["CF_DB_HOST"]#:#server.system.environment["CF_DB_PORT"]#/#server.system.environment["MYSQL_DATABASE"]#?useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=true'
, username: "#server.system.environment["MYSQL_USER"]#"
, password: "#server.system.environment["MYSQL_PASSWORD"]#"
// optional settings
, connectionLimit:5 // default:-1
};