Who am I
Cloud Engineer @ Bluebox
* I am a sysadmin
* get called rude names like cloud engineer or devop
Bluebox
Shameless Plug
* I work at bluebox on the openstack team
* we do PCaaS. running PC is hard, we're here to do it for you.
* cloud / api driven infrastructure makes it easier to do dynamic apps / dockers
Factorish: The Twelve-Fakter App
* lot of noise in docker community about how to docker
* single process, small images, etc.
* mostly bullshit.
* factorish is my way to bring some pragmatism to this mess.
The Twelve-Factor App
http://www.foodbeast.com/news/move-over-horsemeat-unicorn-cuts-are-all-the-rage/
Is everyone familiar with the concept of 12 factor apps?
The Twelve-Factor App
A methodology for building modern web apps that:
Use declarative formats for setup automation
Have a clean contract with the underlying operating system
Are suitable for deployment on modern cloud platforms
Minimize divergence between development and production
And can scale up without significant changes
* 12 factor is a manifest of how to write applications in the new cloudy / microservices way.
* TLDR of 12factor is 'this is how to write apps to run on heroku'
* even if not docker/heroku, think about 12factor when writing apps/CM.
The Twelve-Factor App
Dockerized
http://www.thinkgeek.com/product/e5a7/?srp=2
* grind up unicorn meat and pack it into container to ship it around.
Your App
* meanwhile the rest of us have apps that look like this
* not only is it decidely not a unicorn, but is weighed down by baggage
* so take your app and ask yourself ... WILL IT DOCKER?
* with Factorish the answer is *probably*
Your App
A decade old application that sort of still runs.
Runs on servers that are lovingly hand crafted.
Is written in a outdated language with poor dependency resolution.
Runs under a web server like Apache or Tomcat.
Looks different in Development and Production (and all the other steps).
* Your app has probably been around for a while
* some of it might be CM.
* hand deployed to production
* development not like production
The Twelve-Fakter App
Dockerized
http://tastyislandhawaii.com/2007/12/07/tulip-vs-treet-vs-spam-musubi-showdown/
The Container is your app
* however we can grind it up into a paste and put it in a container.
* aside from the label, can't tell it apart from unicorn.
* the container is our app ( or at least our built artifact / executable ).
The Twelve-Fakter App
* polishing a turd ... but a turd that makes you money!
* there might be a poo in the box, but from the outside it looks like unicorn!
But Why ?
You want to modernize your application.
You probably can't fix the app.
No time to rewrite the app.
* have an app, it makes money, but its hard to run, hard to maintain.
* you like the idea of runtime configured immutable application.
* bimodal IT, think towards future.
* slow introduces your org to a new way of doing things.
* CM + Docker is not big shift as all docker all time with dyn.
* lets talk through the 12factors and talk about how to fake it.
Fakter I. Codebase
One codebase tracked in revision control, many deploys
Use Source control ( GIT! )
Add these to your app (or make them your app)
Dockerfile / .dockerignore
Vagrantfile
Deploy scripts
https://github.com/paulczar/factorish
* Factor 1 is all about having your code and everything required to run your code in source control.
* scripts ( cookbooks etc ) to build, deploy, and run your code. share versioning.
* vagrantfile to ensure everyone has exact same experience.
* Factorish is designed to be copy/paste able into your code base with minimal change.
Fakter II. Dependencies
Explicitly declare and isolate dependencies
Declaration: Dockerfile
Isolation: `docker build`
* Factor 2 tells us that all deps should be declared and isolated.
* declares all dependencies, completely and exactly, not reliance on system packages
* dependency isolation - tool run during build to ensure that no implicit dependencies 'leak in'
* Ruby, `gemfile` is declaration, `bundle install` is isolation.
* Docker, `Dockerfile` is declaration, `docker build` is isolation. - can rely on system packages.
Fakter II. Dependencies
# Dockerfile: factorish/example
FROM python:2
MAINTAINER Paul Czarkowski "paul@paulcz.net"
RUN \
apt-get update && apt-get install -yq
supervisor \
RUN \
curl -sSL -o /usr/local/bin/etcdctl https://s3-us-west-2.amazonaws.com/opdemand/etcdctl-v0.4.6 && chmod +x /usr/local/bin/etcdctl \
&& curl -sSL -o /usr/local/bin/confd https://github.com/kelseyhightower/confd/releases/download/v0.7.1/confd-0.7.1-linux-amd64 && chmod +x /usr/local/bin/confd
ADD . /app
WORKDIR /app
RUN \
useradd -d /app -c 'application' -s '/bin/false' app && \
chmod +x /app/bin/* && \
pip install -r /app/example/requirements.txt
CMD ["/app/bin/boot"]
EXPOSE 8080
Fakter III. Configuration
Store config in the environment
Again, Easy wins with Docker
`docker run -e TEXT=bacon myapp`
Confd - {{ getv "/text" }}
`sed -i "s/xxxTEXTxxx/${TEXT}" /etc/config.conf`
* According to factor 3, all configuration should be done via environment
* Can I opensource this code without compromising secrets / passwords ?
* internal application config that doesn't change between envs should remain in code.
Fakter III. Configuration
/app/conf.d/example.conf.toml
[template]
src = "example.conf"
dest = "/app/example/example.conf"
owner = "app"
group = "app"
mode = "0644"
keys = [
"/services/example",
]
check_cmd = "/app/bin/check {{ .src }}"
reload_cmd = "supervisorctl restart example"
/app/templates/example.conf
[example]
text: {{ getv "/services/example/text" }}
{{ }} - golang/confd macro syntax.
Fakter IV. Backing Services
Treat backing services as attached resources
Make no distinction between local and third party services.
use ENV to configure.
Fairly simple to manage for databases etc.
disk persistence is the hard one.
Docker: volume mounts*, data containers, flocker
Remote Storage: netapp, nfs, fuse-s3fs
Clustered FS: drdb, gluster
Ghetto: rsync
* security implications.
* A backing service is any service the app consumes over the network as part of its normal operation
* no distinction between local and third party services ... i.e. local mysql db = rds
* ofc do have to manage that servce, but outside pervue of your app.
* change out backing services without code change ( change env variable! )
* disk persistence is hard
Fakter IV. Backing Services
/app/templates/wp-config.php
/** The name of the database for WordPress */
define('DB_NAME', '{{ getv "db/name" }}');
/** MySQL database username */
define('DB_USER', '{{ getv "db/user" }}');
/** MySQL database password */
define('DB_PASSWORD', '{{ getv "db/pass" }}');
/** MySQL hostname */
define('DB_HOST', '{{ getv "db/host" }}');
Docker Run command
$ docker run -d -e DB_NAME=wordpress -e DB_USER=wordpress \
-e DB_PASSWORD=wordpress $DB_HOST=my.database.com \
-v /mnt/nfs/wordpress:/app/wordpress factorish/wordpress
Fakter V. Build, release, run
Strictly separate build, release and run stages
Build: `docker build -t myapp:base .`
Release: `docker build -t myapp:v1.3.2 .`
Run: `docker run -d myapp:v1.3.2`
Release/Run: `docker run -d -e TEXT=bacon myapp:base`
* The build stage takes your code repo at a specific git release and converts it into an executable bundle known as a build. It fetches and vendors dependencies and compiles binaries and assets.
* The release stage takes this build and combines it with the deploy’s current config. The resulting release contains both the build and the config and is ready for immediate execution. - versions, docker registry, rollback - free with paas
* The run stage runs the app in the execution environment by launching one or more processes against a selected release
* IMO its okay to combine Release/Run steps in a docker based environment. - but do lose some rollback capability.
Fakter VI. Processes
Execute the app as one or more stateless processes
Applications should be treated as stateless processes.
All data that needs to be persisted should be done via backing services.
example: `session` data stored in redis or memcache.
* No state should be persisted to disk by the application, rather sent to a backing service.
* example storing session data in redis or memcache.
* App can use local mem or disk for short lived transactional cache.
* Sticky sessions are bad, do not use them, or live with the consequences.
Fakter VII. Port binding
Export services via port binding
Use a language specific webserver library like `jetty` for java if possible.
Usually relying on an external webserver (ex. nginx for php) is a violation of 12factor.
Port Binding: EXPOSE in Dockerfile, or $ docker run -p 8080:8080 .
any app can be used as a backing app for another app.
* 12factor app is completely self contained and does not rely on an external webserver like apache. However since our application is the container, its okay if you have to use a webserver inside the container.
* Note also that the port-binding approach means that one app can become the backing service for another app, by providing the URL to the backing app as a resource handle in the config for the consuming app.
Fakter VIII. Concurrency
Scale out via the process model
assign each type of work to a process type ( container! )
Your application should not daemonize or write pid files.
if it does, you may be able to script around it.
Apps like nginx and apache can be run in foreground.
Utilize tools like runit or supervisord to handle process management.
Horizonal vs Vertical scaling.
* Try to break up functionality of monolithic app into seperate containers/processes, each doing a specific task.
* One container serves web requests, another processes images into thumbnails.
* In the twelve-factor app, processes are the first class citizen.
* Try to avoid daemonizing, always want to run process in foreground.
* VM/Phys can only grow so much, by having multiple containers we can now have multiple VMs.
Fakter IX. Disposability
Maximize robustness with fast startup and graceful shutdown
Otimize to minimize startup ( Docker helps with this! )
Graceful shutdown ( Finish current reqs or give back to a queue )
Sudden Death ( minimal impact on crash. Smart LB can help? )
* All processes/containers should be disposable. stated or stopped at a moments notice
* When a container gets SIGTERM, apps inside should gracefully shut down.
* most web connections are brief, is it a big deal if you lose a few on Sudden death ? Be OK with small amounts of broken connections.
X. Dev/prod parity
Keep development, staging, and production as similar as possible
You've already done most of the work to achieve this:
Use Vagrant or Docker Compose to deploy your app and backing services in dev environment using appropriate provisioners.
Docker helps reduce time to go from dev to prod with portable/immutable artifacts.
by deploying all envs with same tooling, every dev rebuilt is a test of prod deploy tooling.
Actions to deploy to prod should be very similar to deploy to dev.
* If you've gotten this far, we've already accomplished just about everything required here.
* it's mostly okay to use a mysql container in dev and RDS in prod. mysql ~= mysql.
* Docker-Compose is excellent for chaining together backing services, be wary of links.
* Traditional CM is also perfect here. For example using chef to build backing services and deploying container.
XI. Logs
Treat logs as event streams
Never write to a log file always to stdout ( /dev/stdout /dev/stderr are your friends)
If you have to write to a log file, you're violating several rules and will have to try to
script around it (or use a remote file backing service) so that long running containers do not run out of disk space.
By writing to stdout, docker log subsystem sees the logs, and then tooling like ``logspout` can utilize to forward stream to central location.
* super important! otherwise need log management in container.
* if absolutely can't avoid writing logs, use a volume mount or a syslog in the container.
* Set a self destruct timer.
XII. Admin processes
Run admin/management tasks as one-off processes
Docker
docker run -t -e TEXT=bacon myapp:1.3.2 rake db:migrate or
docker exec -t vibrating_descartes rake db:migrate
* never ssh to a running container to do one off tasks
* best to create a new container to run the task and then die.
* most PaaS have a way to do that.
* can use docker exec to run it inside an existing container, but try to avoid.
Demo - Scoutapark
http://scoutapark.com
* scoutapark, startup, dan is not dev, php = cheap labor for MVP
* walkthrough it, then walkthrough factorish.
* demo both