How I set up a basic development environment suitable for a single developer, but expandable to multiple developers using Docker, Adobe ColdFusion, Git, Docker, and BitBucket. Part 1 – Software and file structure
I worked with a client who developed in a very frightening fashion by todays standards. The company was founded 20 years ago, mainly had a single developer, and they developed against the production server.
That’s it. No development environment. No testing. No safety net. Their developer would make changes to local copies of files, visually scan the code to see if there was anything wrong with it, and push it to the production server. They weren’t even running a copy of it on their local machine. Someone else would refresh the page and look for errors. If one popped up, a change was made and pushed immediately. Part of the issue was the attitude of the company against development processes. I described it as “reactive” development instead of “responsive” development. The primary developer was so used to being able to be quick and agile in their ColdFusion development that applying any safety nets or process to his workflow was seen as a hindrance. The company saw process as unnecessary overhead that slowed things down.
It made me want to scream into a bag. And I vowed that one day, I was going to set up an environment to make development of this manner easier. I would employ some basic processes in order to build in conservative development techniques without adding a lot of overhead. There is a ton of ColdFusion work out there that doesn’t need a full development team with over-architected solutions and processes just to get things done. However, even a solo developer can benefit from having a minimal amount of process in place to prevent coding disasters.
My obligatory preamble. I’m not a Docker expert. I did some research, and I took a couple of online courses, and I figured it out. There may be better or different ways to accomplish what I’ve accomplished.
My Goal
With all of this in mind, and a focus on solo / small team development, here’s my goal:
- I want to use a git repository for the code and store that code in the cloud.
- I want to spin up a development environment quickly and easily on any computer with Docker installed.
- It should create two containers: one for Adobe ColdFusion, and one for MS SQL Server.
- The SQL Server should include a copy (even if it’s a subset) of the database used by the application.
- The ColdFusion container should automatically apply server settings on startup, including datasources, caching, mail settings, etc.
- The entire system should automatically push to a development beta website once the changes are merged into a branch called Development, and automatically push to production when Development is merged into Master.
This gives me a lot of flexibility… especially if the database is small enough for me to keep a copy in the repository. When another developer comes on, he needs to pull the repository, spin up the environment and go. Here’s how development in this environment should proceed, in my view:
- Developer creates a branch of Development.
- Coding and testing happens on their local computer.
- Once satisfied, those changes get merged to development, which automatically get pushed to the beta site.
- Other QA and UAT users test to make sure there’s no errors, and that the system works as expected.
- Development gets merged to master, which gets automatically deployed.
This workflow is intended to be basic and minimum in order to satisfy the need to have a little bit of a safety net between the developer and the production server. Away we go!
Software and Services
I’m going to use the official ColdFusion Docker images for my Docker containers. I’m also going to use BitBucket for my git repository.
First things first, I need to make sure that I have the correct software in place to complete my tasks. I use git and docker in this project, and I develop on a Mac, so these instructions will be Mac heavy. The first thing I do is open up a terminal window (Applications > Utilities > Terminal on a Mac) and type “git” and press enter to make sure git is installed. If it’s not, the OS will automatically prompt you to install it.
Once git is installed, I create a directory for my project. In this case, I’ll call it “Project” and I’ll navigate into it from my terminal window, and initialize the git project using the following terminal commands:
mkdir Project cd Project git init
I see “Initialized empty Git repository in /Users/dbyers/Project/.git/” and I know that I’m good to go.
Next step is to install Docker. I won’t go into details on how to install Docker here because there’s a ton of tutorials that cover it already. Google it. You’ll figure it out.
Once I’ve got git and Docker installed, I can start setting up my file structure.
My File Structure
Setting up my file structure is pretty simple.
Step 1 – Create Directories
The first thing I do is create two directories:
- app
- data
The app directory will contain all of the files that make up the website. Consider it the root of the site itself. The data directory contains all of the files I need to establish the database and ColdFusion settings. The naming of these directories is important because the ColdFusion Docker container looks for the app directory to contain the website and the data directory to contain a ColdFusion archive of settings. I’ll cover more of that in part 2 of this tutorial.
Step 2 – Create Files
Once the directories are created, I create three empty files; one in the root of the project folder, and two in the data directory.
- /docker-compose.yml – This is the docker compose file that spin up the environment and define its services.
- /data/entrypoint.sh – This will be a shell file that’ll be run after the SQL Server is spun up. Its function is to check to see if the SQL Server is running before it tried to apply the database and controls whether or not the database has already been applied.
- /data/setup.sql – This is standard SQL file that contains the instructions needed to restore or establish the database.
Step 3 – Populate the Files
I start with the docker-compose.yml file. This file will be what spins up and runs the entire environment. I open the docker-compose.yml file and I paste in the following:
version: "3.7" services: sql: image: mcr.microsoft.com/mssql/server:2019-latest container_name: sql hostname: sql networks: - network ports: - "1433:1433" volumes: - "mssqldata:/var/opt/mssql" - "./data:/data" environment: - ACCEPT_EULA=Y - SA_PASSWORD=SQLS3rv3r - MSSQL_AGENT_ENABLED=true - MSSQL_PID=Express entrypoint: - /bin/bash - /data/entrypoint.sh command: [ "/opt/mssql/bin/sqlservr" ] coldfusion: image: eaps-docker-coldfusion.bintray.io/cf/coldfusion:latest container_name: coldfusion hostname: coldfusion networks: - network ports: - "80:8500" volumes: - "./app:/app" - "./data:/data" environment: acceptEULA: "YES" password: "C0ldFusi0n" enableSecureProfile: "false" healthcheck: test: curl -f http://localhost/ interval: 1m timeout: 3s depends_on: - sql volumes: mssqldata: networks: network:
This establishes two containers; one for MS SQL Server running the latest version of SQL Server 2019, and binds it to port 1433 on your computer. Next, it uses the official Adobe ColdFusion Docker Images to spin up a CF2018 container running the internal web server and binding it to port 80. Both containers map the data directory from my project into /data on the container. Finally, the ColdFusion container is set to depend on the SQL container.
Next, I open the entrypoint.sh file and paste the following, which I admittedly ripped off from somewhere on the web… and I can’t remember where.
#!/bin/bash set -e if [ "$1" = '/opt/mssql/bin/sqlservr' ]; then # If this is the container's first run, initialize the application database if [ ! -f /tmp/app-initialized ]; then # Initialize the application database asynchronously in a background process. This allows the SQL Server process to be the main process in the container, which allows graceful shutdown and other goodies, and allows us to only start the SQL Server process once, as opposed to starting, stopping, then starting it again. function initialize_app_database() { # Wait a bit for SQL Server to start. SQL Server's process doesn't provide a clever way to check if it's up or not, and it needs to be up before we can import the application database sleep 15s #run the setup script to create the DB and the schema in the DB /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P SQLS3rv3r -d master -i /data/setup.sql # Note that the container has been initialized so future starts won't wipe changes to the data touch /tmp/app-initialized } initialize_app_database & fi fi exec "$@"
This file runs after the SQL Server container spins up and waits for SQL Server to be installed and ready to respond before instructing it to run the /data/setup.sql file. It’s whole purpose in life is to watch for the SQL Server to be ready before trying to create the database.
Finally, my setup.sql file. For the moment, it doesn’t do anything other than create the database.
USE [master]
GO
CREATE DATABASE [Project]
GO
Starting it Up
Once these steps have been followed, my project is ready to be spun up! Making sure that Docker is running, I navigate to the Project directory in my terminal window and type:
docker-compose up
…and press enter. That’s when this madness happens. (Please note, this video is already super boring, so I sped it up quite a bit… the actual download and initialization took about 3 and a half minutes.)
Magical, right? Let’s check and see if everything is okay. Open a browser and go to http://127.0.0.1, and I see the following:
There’s one criteria off the list… now for the second. Do we have a database? I load up Azure Data Studio and check. This is what I see:
So we’re good! The first step in creating a development environment using ColdFusion, MS SQL Server, and Docker Compose is done! In Part 2, we will establish database connections and some changes to the ColdFusion Server settings. We will create the database tables and make them persist through Docker builds.
Enjoy!
Normally I would say to wrap the SQL Server into the environment in its own container. The “network” aspect of the Docker-compose.yml file creates a virtual network that all of the containers would be able to access.
As for being able to connect to a network outside of the Docker environment, I’d need some advice from someone more versed in Docker than I am.
This pretty cool. However I cant get the SQL to run.
I keep getting this error
This container is running as user root.
To learn more visit https://go.microsoft.com/fwlink/?linkid=2099216.
The SQL Server End-User License Agreement (EULA) must be accepted before SQL
Server can start. The license terms for this product can be downloaded from
http://go.microsoft.com/fwlink/?LinkId=746388.
You can accept the EULA by specifying the –accept-eula command line option,
setting the ACCEPT_EULA environment variable, or using the mssql-conf tool.
Interesting… this should be covered by the ACCEPT_EULA=Y line in the docker-compose.yml file.
If you open your Docker Dashboard, and click on the container, there is a set of icons that appears… you could SSH directly into the container and make adjustments.
I was able to bypass the error using this code instead of the sql service using sqlserver1 and a dockerfile, sqlserver.env and sapassword.env
services:
sqlserver1:
build:
context: .
dockerfile: dockerfile
ports:
– “1433:1433”
env_file:
– sqlserver.env
– sapassword.env
volumes:
– sqlsystem:/var/opt/mssql/
– sqldata:/var/opt/sqlserver/data
– sqllog:/var/opt/sqlserver/log
– sqlbackup:/var/opt/sqlserver/backup
Dockerfile content
# build from the Ubuntu 18.04 image
FROM ubuntu:18.04
# create the mssql user
RUN useradd -u 10001 mssql
# installing SQL Server
RUN apt-get update && apt-get install -y wget software-properties-common apt-transport-https
RUN wget -qO- https://packages.microsoft.com/keys/microsoft.asc | apt-key add –
RUN add-apt-repository “$(wget -qO- https://packages.microsoft.com/config/ubuntu/18.04/mssql-server-2019.list)”
RUN apt-get update && apt-get install -y mssql-server
# creating directories
RUN mkdir /var/opt/sqlserver
RUN mkdir /var/opt/sqlserver/data
RUN mkdir /var/opt/sqlserver/log
RUN mkdir /var/opt/sqlserver/backup
# set permissions on directories
RUN chown -R mssql:mssql /var/opt/sqlserver
RUN chown -R mssql:mssql /var/opt/mssql
# switching to the mssql user
USER mssql
# starting SQL Server
CMD /opt/mssql/bin/sqlservr
sqlserver.env
ACCEPT_EULA=Y
MSSQL_DATA_DIR=/var/opt/sqlserver/data
MSSQL_LOG_DIR=/var/opt/sqlserver/log
MSSQL_BACKUP_DIR=/var/opt/sqlserver/backup
sapassword.env
MSSQL_SA_PASSWORD=”enter secure password”
This might be an off the wall question. But how do setup the container to access my LAN? I am having issues establishing connection from the CF server to a SQL DB on my LAN its only working with the IP address.
I receive this error:
Connection verification failed for data source:
java.sql.SQLException: Timed out trying to establish connection
Great timing as I’m trying to stand up ACF Docker containers myself as I can’t get the Add-on services working with Commandbox Docker.
One question – when I start the vanilla ACF Docker image (I’m using 2016) it starts the migration wizard? Why? It’s a container. I can’t figure out how to bypass that.
David has posted his part 2. Great to see the things you’re sharing here, David.
You must be logged in to post a comment.