Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Docker for Ruby Developers

Docker for Ruby Developers

Originally presented at NYC.rb.

Frank Macreery

August 11, 2015
Tweet

More Decks by Frank Macreery

Other Decks in Programming

Transcript

  1. Docker for Ruby Developers NYC.rb, August 2015 Frank Macreery CTO,

    Aptible @fancyremarker https://speakerdeck.com/fancyremarker
  2. Getting Started with Docker Alternatively (if you have Homebrew and

    VirtualBox installed)…
 
 brew install boot2docker brew install docker
  3. docker run -d -p 80:80 -p 443:443 \ quay.io/aptible/nginx:latest Forward

    container ports to the host (host port:container port)
  4. What makes dev/prod parity so hard? SOA simplifies each service’s

    responsibilities, but often at the cost of additional deployment complexity
  5. What makes dev/prod parity so hard? The more services you

    have, the harder it is to achieve dev/prod parity
  6. What makes dev/prod parity so hard? The more engineers you

    have, the harder it is to standardize dev parity
  7. # docker-compose.yml for Aptible Auth/API auth: build: auth.aptible.com/ ports: "4000:4000"

    links: - postgresql environment: RAILS_ENV: development DATABASE_URL: postgresql://postgresql/aptible_auth_development api: build: api.aptible.com/ ports: "4001:4001" links: - redis - postgresql environment: RAILS_ENV: development DATABASE_URL: postgresql://postgresql/aptible_api_development REDIS_URL: redis://redis APTIBLE_AUTH_ROOT_URL: http://docker:4000 redis: image: quay.io/aptible/redis:latest ports: "6379:6379" postgresql: image: quay.io/aptible/postgresql:aptible-seeds ports: "5432:5432"
  8. # docker-compose.yml for Aptible Auth/API auth: build: auth.aptible.com/ ports: "4000:4000"

    links: - postgresql environment: RAILS_ENV: development DATABASE_URL: postgresql://postgresql/aptible_auth_development api: build: api.aptible.com/ ports: "4001:4001" links: - redis - postgresql environment: RAILS_ENV: development DATABASE_URL: postgresql://postgresql/aptible_api_development REDIS_URL: redis://redis APTIBLE_AUTH_ROOT_URL: http://docker:4000 redis: image: quay.io/aptible/redis:latest ports: "6379:6379" postgresql: image: quay.io/aptible/postgresql:aptible-seeds ports: "5432:5432"
  9. # docker-compose.yml for Aptible Auth/API auth: build: auth.aptible.com/ ports: "4000:4000"

    links: - postgresql environment: RAILS_ENV: development DATABASE_URL: postgresql://postgresql/aptible_auth_development api: build: api.aptible.com/ ports: "4001:4001" links: - redis - postgresql environment: RAILS_ENV: development DATABASE_URL: postgresql://postgresql/aptible_api_development REDIS_URL: redis://redis APTIBLE_AUTH_ROOT_URL: http://docker:4000 redis: image: quay.io/aptible/redis:latest ports: "6379:6379" postgresql: image: quay.io/aptible/postgresql:aptible-seeds ports: "5432:5432"
  10. # docker-compose.yml for Aptible Auth/API auth: build: auth.aptible.com/ ports: "4000:4000"

    links: - postgresql environment: RAILS_ENV: development DATABASE_URL: postgresql://postgresql/aptible_auth_development api: build: api.aptible.com/ ports: "4001:4001" links: - redis - postgresql environment: RAILS_ENV: development DATABASE_URL: postgresql://postgresql/aptible_api_development REDIS_URL: redis://redis APTIBLE_AUTH_ROOT_URL: http://docker:4000 redis: image: quay.io/aptible/redis:latest ports: "6379:6379" postgresql: image: quay.io/aptible/postgresql:aptible-seeds ports: "5432:5432"
  11. Dev/prod parity via Docker Use the same service/image configuration in

    production as in development (Docker Compose, Swarm, Kubernetes…)
  12. # Dockerfile # Install and configure NGiNX... # ... ADD

    test /tmp/test RUN bats /tmp/test https://github.com/aptible/docker-nginx Image: quay.io/aptible/nginx
  13. #!/usr/bin/env bats # /tmp/test/nginx.bats @test "It should accept a list

    of UPSTREAM_SERVERS" { simulate_upstream UPSTREAM_SERVERS=localhost:4000 wait_for_nginx run curl localhost 2>/dev/null [[ "$output" =~ "Hello World!" ]] }
  14. #!/usr/bin/env bats # /tmp/test/nginx.bats @test "It should accept a list

    of UPSTREAM_SERVERS" { simulate_upstream UPSTREAM_SERVERS=localhost:4000 wait_for_nginx run curl localhost 2>/dev/null [[ "$output" =~ "Hello World!" ]] }
  15. @test "It should accept a list of UPSTREAM_SERVERS" { simulate_upstream

    UPSTREAM_SERVERS=localhost:4000 wait_for_nginx run curl localhost 2>/dev/null [[ "$output" =~ "Hello World!" ]] } simulate_upstream() { nc -l -p 4000 127.0.0.1 < upstream-response.txt }
  16. #!/usr/bin/env bats # /tmp/test/nginx.bats @test "It should pass an external

    Heartbleed test" { install_heartbleed wait_for_nginx Heartbleed localhost:443 uninstall_heartbleed }
  17. @test "It should pass an external Heartbleed test" { install_heartbleed

    wait_for_nginx Heartbleed localhost:443 uninstall_heartbleed } install_heartbleed() { export GOPATH=/tmp/gocode export PATH=${PATH}:/usr/local/go/bin:${GOPATH}/bin go get github.com/FiloSottile/Heartbleed go install github.com/FiloSottile/Heartbleed }
  18. Integration tests happen during each image build Quay Time Machine

    lets us roll back an image to any previous state
  19. New databases mean new dependencies How to perform common admin

    tasks? Backups? Replication? CLI access? Read-only mode?
  20. #!/bin/bash # run-database.sh command="/usr/lib/postgresql/$PG_VERSION/bin/postgres -D "$DATA_DIRECTORY" -c config_file=/etc/postgresql/ $PG_VERSION/main/postgresql.conf" if

    [[ "$1" == "--initialize" ]]; then chown -R postgres:postgres "$DATA_DIRECTORY" su postgres <<COMMANDS /usr/lib/postgresql/$PG_VERSION/bin/initdb -D "$DATA_DIRECTORY" /etc/init.d/postgresql start psql --command "CREATE USER ${USERNAME:-aptible} WITH SUPERUSER PASSWORD '$PASSPHRASE'" psql --command "CREATE DATABASE ${DATABASE:-db}" /etc/init.d/postgresql stop COMMANDS elif [[ "$1" == "--client" ]]; then [ -z "$2" ] && echo "docker run -it aptible/postgresql --client postgresql://..." && exit psql "$2" elif [[ "$1" == "--dump" ]]; then [ -z "$2" ] && echo "docker run aptible/postgresql --dump postgresql://... > dump.psql" && exit pg_dump "$2" elif [[ "$1" == "--restore" ]]; then [ -z "$2" ] && echo "docker run -i aptible/postgresql --restore postgresql://... < dump.psql" && exit psql "$2"
  21. #!/bin/bash # run-database.sh command="/usr/lib/postgresql/$PG_VERSION/bin/postgres -D "$DATA_DIRECTORY" -c config_file=/etc/postgresql/ $PG_VERSION/main/postgresql.conf" if

    [[ "$1" == "--initialize" ]]; then chown -R postgres:postgres "$DATA_DIRECTORY" su postgres <<COMMANDS /usr/lib/postgresql/$PG_VERSION/bin/initdb -D "$DATA_DIRECTORY" /etc/init.d/postgresql start psql --command "CREATE USER ${USERNAME:-aptible} WITH SUPERUSER PASSWORD '$PASSPHRASE'" psql --command "CREATE DATABASE ${DATABASE:-db}" /etc/init.d/postgresql stop COMMANDS elif [[ "$1" == "--client" ]]; then [ -z "$2" ] && echo "docker run -it aptible/postgresql --client postgresql://..." && exit psql "$2" elif [[ "$1" == "--dump" ]]; then [ -z "$2" ] && echo "docker run aptible/postgresql --dump postgresql://... > dump.psql" && exit pg_dump "$2" elif [[ "$1" == "--restore" ]]; then [ -z "$2" ] && echo "docker run -i aptible/postgresql --restore postgresql://... < dump.psql" && exit psql "$2"
  22. --initialize: Initialize data directory --client: Start a CLI client --dump:

    Dump database to STDOUT --restore: Restore from dump --readonly: Start database in RO mode
  23. db-launch () { container=$(head -c 32 /dev/urandom | md5); passphrase=${PASSPHRASE:-foobar};

    image="${@: -1}"; docker create --name $container $image docker run --volumes-from $container \ -e USERNAME=aptible -e PASSPHRASE=$passphrase \ -e DB=db $image --initialize docker run --volumes-from $container $@ } http://bit.ly/aptible-dblaunch
  24. docker run --volumes-from $container \ -e USERNAME=aptible -e PASSPHRASE=$passphrase \

    -e DB=db $image --initialize http://bit.ly/aptible-dblaunch 2. Initialize database data volume