William Durand - April 17th, 2015
A container is an operating-system level virtualization method that provides a completely isolated environment, simulating a closed system running on a single host.
chroot, OpenVZ, Jail, LXC, etc.
web:
build: .
# No support for `env-file` yet...
# See: https://github.com/docker/fig/pull/665
environment:
- SYMFONY_ENV=dev
- SYMFONY_DEBUG=1
- SYMFONY__MEMCACHED_HOST=memcached
- SYMFONY__MEMCACHED_PORT=11211
- SYMFONY__SESSION_MEMCACHED_PREFIX=session
- SYMFONY__SESSION_MEMCACHED_EXPIRE=604800
links:
- memcached
- database
Data, logs, etc. MUST live in data only containers:
# fig.yml
redis:
image: redis
volumes_from:
- dataredis
dataredis:
image: busybox
volumes:
- /data
Wanna backup?
docker run --rm \
--volumes-from dataredis \
-v $(pwd):/backup \
busybox \
tar cvf /backup/dataredis.tgz /data
FROM debian:wheezy MAINTAINER William Durand <will@drnd.me> ENV DEBIAN_FRONTEND noninteractive # Let APT know about HHVM repo... RUN apt-get update -y RUN apt-get install -y nginx hhvm supervisor # Configure Nginx, HHVM, Supervisor here... ADD . /app WORKDIR /app EXPOSE 80 CMD [ "/usr/bin/supervisord" ]
example.org www.example.org
Configured at the DNS level. Faster than making this redirection using the load balancer.
Hipache listens on ports 80 and 443.
Patched version of Hipache to redirectHTTP traffic to HTTPS. Sort of SSL offloading.
git archive --format tar --prefix myapp/ <SHA> | (cd /tmp && tar xf -)
Protip: use a Docker container to run build tools:
cd /tmp/myapp docker run --rm -v `pwd`:/srv buildtools composer install docker run --rm -v `pwd`:/srv buildtools bundle install docker run --rm -v `pwd`:/srv buildtools bower install rm <USELESS/TEMP FILES>
docker build -t myapp:latest .
docker push
Or whatever that makes the image available on the production server.
RUNNING_CIDS=$(docker ps | grep web | cut -d ' ' -f 1)
fig run -d --no-deps --rm web
Booting the container may take a few seconds, wait.
IP=$(docker inspect --format='{{ .NetworkSettings.IPAddress }}' <CID>)
fig run --rm rediscli rpush frontend:www.example.com "http://$IP:80"
# fig.yml
rediscli:
image: redis
entrypoint: [ redis-cli, -h, redis ]
links:
- redis
For each container id CID in $RUNNING_CIDS, do:
IP=$(docker inspect --format='{{ .NetworkSettings.IPAddress }}' <CID>)
fig run --rm rediscli lrem frontend:www.example.com 0 "http://$IP:80"
New backend is up and serves requests. However, previous backends may still process requests. Wait.
For each container id CID in $RUNNING_CIDS, do:
docker stop <CID>
Rename it rather than deleting it. Wait for a maintenance party to delete *_deprecated columns and/or tables.
Protip: when marking a column/table as deprecated, add the date too: _deprecated_2015_01_01.
Avoid it as long as you can, but sometimes,there are no other alternatives.
This implies a maintenance mode: be careful!
Put your service in maintenance mode Apply your changes Put your service back onlineLike Apache Bench, but better!
boom https://www.example.org/ -n 2000 -c 50 Server Software: nginx/1.2.1 Running GET https://www.example.org/ Running 2000 times per 50 workers. [====================================================>.] 99% Done -------- Results -------- Successful calls 2000 Total time 137.1827 s Average 6.7114 s Fastest 2.3701 s Slowest 9.4738 s Amplitude 7.1037 s -------- Status codes -------- Code 200 2000 times.
fab deploy --hide=running,stdout,stderr ---> Deploying branch "awesome-feature" ---> Building artifact ---> Artifact id is: "build-8fe47fc" ---> Pushing artifact to the server ---> Building container ---> Finding running containers ---> Starting new container ---> Waiting while container is booting ---> Registering new backend to Hipache ---> Unregistering the previous backends from Hipache ---> Waiting before stopping the previous backends Done.
I use Fabric.
Elasticsearch, Logstash, Kibana + Logstash Forwarder (lumberjack)
box: wercker-labs/docker
build:
steps:
- script:
name: install Fig
code: |
curl -L https://.../fig-`uname -s`-`uname -m` > fig ; chmod +x fig
sudo mv fig /usr/local/bin/fig
- ...
- script:
name: build artifact
code: |
FAB_DIST_DIR="$WERCKER_OUTPUT_DIR" \
bin/fabric build:commit="$WERCKER_GIT_COMMIT"
- script:
name: build image
code: |
cd "$WERCKER_OUTPUT_DIR/build-$WERCKER_GIT_COMMIT"
fig -f "$WERCKER_SOURCE_DIR/fig.yml.dist" build web
- script:
name: run container
code: |
cd "$WERCKER_OUTPUT_DIR/build-$WERCKER_GIT_COMMIT"
fig -f "$WERCKER_SOURCE_DIR/fig.yml.dist" run -d web
sleep 20
- script:
name: run test suite
code: |
export IP=$(docker inspect --format='{{ .NetworkSettings.IPAddress }}'\
"$(docker ps -q | head -n 1)")
echo "IP address is: $IP" | tee "$WERCKER_REPORT_MESSAGE_FILE"
bin/test
require "httparty"
describe "The web server" do
before(:all) do
ip = ENV["IP"] or 'localhost'
@response = HTTParty.get(
"http://#{ip}:80",
:basic_auth => { :username => "privatedemo", :password => "please" }
)
end
it "should return a 200 status code" do
@response.code.should == 200
end
end
Keep development, staging, and production as similar as possibleX. Dev/prod parity — 12 Factors
# fig.yml.dist
web:
# In PRODUCTION, the line below MUST be COMMENTED:
build: .
# In PRODUCTION, the line below must be UNCOMMENTED:
# image: myapp
# ...