Try This At Home
Building Your Own ColdFusion Swarm
Matthew Clemente
A Familiar Story
- Adobe ColdFusion 10 /11
- Windows Server 2008
- Microsoft IIS
- Rackspace
- FTP Deployments
- Dev Server (in basement)
Every Conference
Looking for Direction
Legacy Infrastructure
Scriptable
Scalable
Containerized
12-Factor App
CI/CD
Microservices
Bret Fisher Resources
Learned a Lot
Still don't know how to do this.
¯\_(ツ)_/¯
You'll learn more on the first day of production than the previous two months.
Bret Fisher, Taking Docker to Production, DockerCon Europe 2017
Doing
is
Learning
All genuine learning comes through experience.
John Dewey, Experience & Education, 1938
How do I actually do this?
Don't tell me that it's possible without showing me how!
Goal
Learning By Doing
Concrete Examples
What This Talk Is Not:
- Intro to Docker
- Intro to Swarm
- Comprehensive
- "The Best Way"*
- Starting point
- Practical and concrete
- Code Samples!
- Necessarily limited
What This Talk Is:
Tooling and Services
How Do I...
- Structure my project?
- Minimize divergence between Dev and Prod?
- Use environment variables?
- Manage sensitive information?
- Use a private image registry?
- Tag images properly?
- Add Lucee extensions?
- Configure CI/CD for deployment?
- Monitor with FusionReactor?
- Handle sessions?
- File a tax extension?
It can be done
ಠಿ_ಠ
Begin with the end in mind.
Stephen Covey, The 7 Habits of Highly Effective People, 1989
Project Structure
.
├── .env
├── .gitlab-ci.yml
├── .secrets
│ ├── cfml.admin.password.dev
│ └── cfml.admin.password.v1
├── app
│ ├── .CFConfig.json
│ ├── box.json
│ ├── server.json
│ └── wwwroot
│ └── index.cfm
├── build
│ ├── cfml
│ │ ├── Dockerfile
│ │ └── config
│ └── deploy-secrets.sh
├── docker-compose.override.yml
├── docker-compose.debug.yml
└── docker-compose.yml
Sharing Configuration
.
├── docker-compose.override.yml
├── docker-compose.debug.yml
└── docker-compose.yml
$ docker-compose up
Minimize Divergence Between Dev and Prod
version: "3.7"
services:
cfml:
image: "registry.gitlab.com/${CI_PROJECT_NAMESPACE}/starter-swarm-coldfusion/cfml:${BUILD_TAG:-latest}"
build:
context: .
dockerfile: ./build/cfml/Dockerfile
environment:
PORT: 8080
SSL_PORT: 8443
cfconfigfile: .CFConfig.json
cfconfig_inspectTemplate: never
secrets:
- cfml.admin.password
ports:
- target: 8080
published: 80
- target: 8443
published: 443
networks:
internal:
driver: overlay
secrets:
cfml.admin.password:
external: true
name: cfml.admin.password.v1
version: "3.7"
services:
cfml:
volumes:
- ./app:/app
environment:
cfconfig_inspectTemplate: always
ports:
- target: 8080
published: 8080
- target: 8443
published: 8443
networks:
internal:
driver: bridge
secrets:
cfml.admin.password:
external: false
file: ./.secrets/cfml.admin.password.dev
docker-compose.override.yml
docker-compose.yml
the last slide was a lie 🤥
The real world doesn't look like demos.
┻━┻︵ \(°□°)/ ︵ ┻━┻
Swarm Creation
Swarm Creation
# Create Docker Droplet
doctl compute droplet create test
--size 1gb
--image docker-18-04
--region nyc1
# List Droplets
doctl compute droplet list
#Delete a Droplet
doctl compute droplet delete 123456
#List SSH Key Ids and Names
doctl compute droplet list --format "ID,Name"
(Official DigitalOcean Command-Line Client)
Swarm Creation
Swarm Initialized
{CONFIG}
CODE
A litmus test is whether the codebase could be made open source without compromising any credentials.
Adam Wiggins, The Twelve-Factor App, 2017
Environment Variables
- Environment for configuration
- Modular and easy to change
- Managed in .env file
.env
CI_PROJECT_NAMESPACE=mjclemente
OTHER_SETTING=
FOO=bar
${Env} in Compose
services:
cfml:
image: "registry.gitlab.com/${CI_PROJECT_NAMESPACE}/starter-swarm-coldfusion/cfml:${BUILD_TAG:-latest}"
build:
context: .
dockerfile: ./build/cfml/Dockerfile
environment:
PORT: 8080
SSL_PORT: 8443
cfconfigfile: .CFConfig.json
cfconfig_inspectTemplate: never
CF_ADMINPASSWORD: <<SECRET:cfml.admin.password>>
secrets:
- cfml.admin.password
${Env} in CFConfig.json
{
"adminPassword": "${CF_ADMINPASSWORD}",
"adminAllowConcurrentLogin": true,
"adminAllowedIPList": "",
"adminLoginRequired": true,
"adminRDSEnabled": "false",
"adminRDSLoginRequired": "false",
"adminRDSUserIDRequired": true,
"adminRootUserID": "admin",
"adminUserIDRequired": false,
"ajaxDebugWindowEnabled": false,
"allowApplicationVarsInServletContext": false,
"allowExtraAttributesInAttrColl": true,
"applicationMangement": true,
"applicationMaximumTimeout": "2,0,0,0",
"applicationMode": "curr2driveroot",
"applicationTimeout": "2,0,0,0",
(Same behavior in server.json)
Env variables prone to leak
Docker Swarm Secrets
- Immutable by design
- Account for rotation
- Account for Dev and Production
- Team conventions are essential
secrets:
cfml.admin.password:
external: true
name: cfml.admin.password.v1
secrets:
cfml.admin.password:
external: false
file: ./.secrets/cfml.admin.password.dev
docker-compose.override.yml
docker-compose.yml
version: "3.7"
services:
cfml:
...
environment:
PORT: 8080
SSL_PORT: 8443
cfconfigfile: .CFConfig.json
cfconfig_inspectTemplate: never
CF_ADMINPASSWORD: <<SECRET:cfml.admin.password>>
secrets:
- cfml.admin.password
( ••)
( ••)>⌐■-■
(⌐■_■) #Secrets!
CI/CD with Gitlab Runners
- Configured via .gitlab-ci.yml
- Run automagically
- Multiple types of runners
- Use a dedicated SSH Key
₍₍ ᕕ( ಠ‿ಠ)ᕗ
.gitlab-ci.yml
- File containing all definitions of how your project should be built
before_script:
before_script:
## We're gonna log into the gitlab registry, as that's where these images are stored
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
## Git needed to get the date from the commit sha
- apk add git
## So we can see what's going on in the logs
- docker info
## setup environment variables
- [configuration continues]
Getting everything set up
.gitlab-ci.yml
Tagging Custom Images
- Don't just use "latest"
- Combination of date and commit
before_script:
- [earlier configuration]
## Use git `show` with --format=%ci to get ISO 8601 date
- export COMMIT_TIME=$(git show -s --format=%ci $CI_COMMIT_SHA)
## Use first 10 characters of the datetime (ie: 2019-03-19)
- export COMMIT_TIME_SHORT=$(echo $COMMIT_TIME | head -c10)
- export BUILD_TAG="${COMMIT_TIME_SHORT}_$CI_COMMIT_SHORT_SHA"
.gitlab-ci.yml
Control Pipeline Stages
deploy:
stage: deploy
only:
- deploy
except:
variables:
- $CI_COMMIT_MESSAGE =~ /Initial commit/i
- $CI_COMMIT_MESSAGE =~ /skip deploy/i
- $CI_COMMIT_MESSAGE =~ /don't deploy/i
.gitlab-ci.yml
Building Custom Images
build:
stage: build
only:
- deploy
script:
## Build the image, with BUILD_TAG and latest tags
- docker build --tag $CONTAINER_IMAGE:$BUILD_TAG --tag $CONTAINER_IMAGE:latest -f ./build/cfml/Dockerfile .
## List images, so we can confirm success
- docker image ls
## Push with the build tag
- docker push $CONTAINER_IMAGE:$BUILD_TAG
## Push with latest
- docker push $CONTAINER_IMAGE:latest
.gitlab-ci.yml
Stack Deployment
Stack Deployment
deploy:
stage: deploy
script:
- [a lot of SSH related config]
## Enable SSH functionality made possible in 18.0.9 to switch our context to the remote server
- export DOCKER_HOST=ssh://root@${HOST_IP}
## Deploy the stack - registry auth is for gitlab
- docker stack deploy -c docker-compose.yml basetest --with-registry-auth
.gitlab-ci.yml
- Swarm's missing UI
- Configuration and management
- Separate from application stack
Portainer
FusionReactor (Cloud)
- External Storage
- Required in multi-node Swarms
- Built-in support in ColdFusion 2018
Sessions and Caching
SSL
(Zero-config tool to make locally trusted development certificates)
+
All things are difficult before they are easy.
Dr. Thomas Fuller, Gnomologia, 1732
Try This At Home
Building Your Own ColdFusion Swarm
Matthew Clemente
Try This At Home: Building Your Own ColdFusion Swarm
By mjclemente
Try This At Home: Building Your Own ColdFusion Swarm
Presentation for Adobe ColdFusion Summit 2019
- 4,089