class: center, middle # An introduction to ![Docker](media/docker/small_v-trans.png) --- class: center, middle # Courtesy of ![REA Group](media/REA.png) --- # Agenda .lhs[ 🏁 **START** (9:30) * Welcome (9:30-9:50) * Intro and overview (9:50-10:40) ☕️ **Break** (10:40-10:50) * Containers (10:55-11:55) 🍔 **Lunch** (12:00-13:15) ] .rhs[ 🌤 Afternoon * Images (13:20-14:20) 🍩 **Break** (14:30-14:45) * Security (15:00-15:40) - 20 mins * Docker Compose (15:45-16:45) 🍻 **All done** (16:50) ] --- # Outcomes By the end of today, we hope we've helped you understand: .moar-space[ - Why we use Docker; - Containers, images, Dockerfiles, tags, and registries; - Configuring, running, listing, inspecting, and viewing logs of Docker containers; - Networking containers together; - Sharing data using volumes; - Why we provide config through environment variables; - Building, pushing, and pulling Docker images; - Addressing common security risks when working with Docker; - Using Docker Compose to simplify working with multiple Docker containers; ] --- name: prep # Preparation You will need **a good connection to the Internet** during this setup, and through parts of the course, as we'll be installing software, and downloading Docker "images" (more on that later). It's useful to have a **local copy** of these training materials: `git clone https://github.com/realestate-com-au/intro-to-docker.git` --- # Installing Docker We'll definitely need **Docker**, and **Docker Compose**. Select the most appropriate method for your system.
**MacOS**: [Docker for Mac](https://docs.docker.com/docker-for-mac/) **Windows**: [Docker for Windows](https://docs.docker.com/docker-for-windows/) **Linux**: - [CentOS YUM Repository](https://docs.docker.com/install/linux/docker-ce/centos/) - [Debian APT Repository](https://docs.docker.com/install/linux/docker-ce/debian/) - [Fedora DNF Repository](https://docs.docker.com/install/linux/docker-ce/fedora/) - [Ubuntu APT Repository](https://docs.docker.com/install/linux/docker-ce/ubuntu/) ... Alternatively, for Linux: ``` # The questionable curl-pipe-to-sh method: $ `curl -sSL https://get.docker.com/ | sh` ``` --- # Check your setup Let's check that everything is working: ``` $ `./setup.sh` YAY: /var/run/docker.sock exists YAY: I can talk to docker Docker is at localhost YAY: docker version 18.09.2 >= 1.13.0 YAY: docker-compose version 1.23.2 >= 1.13.0 Pulling some images to get you started ... ``` Not working? Ask for help! .small.lhs[ Don't worry _too_ much if you get complaints about versions of `docker` and `docker-compose` Most things _should_ still work. ] --- # Docker Engine .center[ ![on Linux](media/linux_docker_host.svg) ![on Windows](media/mac_docker_host.svg) ] ??? Docker only runs on Linux. On OS X and Windows, it runs inside a VM. --- class: center, middle # Let us begin --- # Why would I even Docker? --
* **A way to package code and run it elsewhere**
_So you can easily distribute your software_ --
* **A way to control the environment your program runs inside**
_So you can run it "anywhere", with confidence_ --
* **A lightweight alternative to a virtual machine**
_So you can quickly create sandboxed environments_ --- name: hello-world # Try it: Hello world! "Pull" an image: ``` $ `docker pull hello-world` Using default tag: latest latest: Pulling from library/hello-world 1b930d010525: Pull complete Digest: sha256:2557e3c07ed1e38f26e389462d03ed943586f744621577a99efb77324b0fe535 Status: Image is up to date for hello-world:latest ``` Run a container: ``` $ `docker run hello-world` Hello from Docker! This message shows that your... ``` --- name: key-concepts # Key concepts .lhs[ ## 'Image' * Passive * File system snapshot * Similar to: - Virtual Machine image - AMI (Amazon Machine Image) ] -- .rhs[ ## 'Container' * Active * Group of processes * Similar to: - Virtual Machine - EC2 instance ] -- .center[ ![on Linux](diagrams/images-and-containers.png) ] --- # Try it: Different Linux Distros Run Ubuntu: ``` $ `docker run ubuntu dpkg -l` ``` or CentOS: ``` $ `docker run centos rpm -qa` ``` -- … Speedy, huh? --- # Try it: Run a Shell ``` $ `docker run -i -t ubuntu bash` ``` * `-i` = Keep STDIN open * `-t` = Allocate a pseudo-TTY Now, try doing an Ubuntu thing: ``` root@container# `apt update` ``` --- # Try it: Which Kernel? Run `uname -a` on both `ubuntu` and `centos`: ``` $ `docker run ubuntu uname -a` $ `docker run centos uname -a` ``` Can you explain the result? -- Different distros, same kernel. .center[ ![2 distros, one kernel](diagrams/two-distros-one-kernel.png) ] --- # Wait, what's a "kernel"? .center[ ![Unix in a nutshell](diagrams/unix-kernel.png) ] --- name: containers-vs-vms # Containers vs. Virtualisation .center[ ![VMs vs. containers](diagrams/containers-vs-vms.png) ] With virtualisation, you have the added weight of a guest kernel. With Docker, processes communicate directly with the host kernel. --- # More containers vs. VMs
Virtual
Machines
Docker
image size
Gigabytes
Megabytes
startup time
minutes
sub-second
Linux kernel is
separate
shared
isolation is
complete
pretty good
used to encapsulate
servers
services
--- # Try it: overhead, much? Use "time" to measure the overhead of running a command in a container. ``` $ `time docker run ubuntu bash -c "time sleep 1"` ``` How much time did Docker add? --- name: the-clever-bits class: center, middle, big # The clever bits Linux "cgroups" + "image" management = ![Docker](media/docker/small_v-trans.png) --- # Linux "control groups" Isolates processes by limiting access to: - Hardware resources; - Other processes; -- Not the first of its kind: - Linux Containers, aka LXC (Google) - Used extensively by PaaS such as Heroku - Similar to Solaris Zones, BSD Jails `cgroups` limits access to system resources, as opposed to `chroot`, which limits access to file system resources. But the concepts are the same - We limit access to certain resources for our process. --- # Union file systems -- Images are like onions.
-- They
make us cry
have layers. -- .lhs[ Images are made of these "layers", and some layers may be 'shared' across multiple images. ] .rhs[
container root
↓
image layer
↓
image layer
↓
image layer
] -- .lhs[
The container's root file system is also another layer that can be written to. ] -- .lhs[ There are many ways to 'store' these layers, such as: - aufs - devicemapper - btrfs - overlay2 / overlay - zfs ] --- # Try it: Container Filesystem Isolation Attempt to break your Ubuntu image: ``` $ `docker run -i -t ubuntu bash` root@3256d8252fe6:/# `ls /usr/bin` root@3256d8252fe6:/# `rm -fr /usr/bin` root@3256d8252fe6:/# `ls /usr/bin` ^D (CTRL-D) ``` Now, start another container: ``` $ `docker run -i -t ubuntu bash` root@3ed46bfba026:/# `ls /usr/bin` ``` --- # Try it: check out those layers Use `docker history` to view image layers ``` $ `docker history ubuntu` IMAGE CREATED CREATED BY 94e814e2efa8 3 weeks ago /bin/sh -c #(nop) CMD ...
3 weeks ago /bin/sh -c mkdir -p /run/...
3 weeks ago /bin/sh -c rm -rf /var/...
3 weeks ago /bin/sh -c set -xe && ...
3 weeks ago /bin/sh -c #(nop) ADD ... ``` --- class: middle, center # Containers --- # Outcomes ## Learn just enough in 5 Lessons By the end of the exercises, you should know more about - Running containers - exposing container ports - getting container logs - passing app environment variables into a running container - docker networking - attaching volumes to containers-vs-vms ## [Click here to begin!](exercises/containers.html) ###### (... or not. If you just want to see the solutions, go to the next slides... but it won't be as fun) --- # Try it: a long-running container Run a container as a daemon (in the background): ``` $ `docker run -d realestate/ciao` ``` List the running containers: ``` $ `docker ps` ``` List ALL the containers: ``` $ `docker ps -a` ``` ??? `-d` starts a long-running process. Containers have an exit-status, and can be restarted with `docker start`. --- # Try it: container cleanup Remove a container: ``` $ `docker rm
` ``` Remove a RUNNING container: ``` $ `docker rm -f
` ``` Cleanup dead containers: ``` $ `docker container prune` ``` --- # Try it: name your containers Start a container with a `--name`: ``` $ `docker run -d --name app realestate/ciao` ``` Now you can use the NAME rather than an ID: ``` $ `docker rm -f app` ``` --- name: ports # Try it: map a port to a host port ``` $ `docker run -d --name app -p 5678:80 realestate/ciao` ``` .center[
] ``` $ `curl localhost:5678` ``` --- # Remember to clean up If/when you see: ``` docker: Error response from daemon: Conflict. The name "/app" is already in use by container 5678abcd... ``` you need to remove the named container: ``` $ `docker rm -f app` ``` --- # Try it: use a random host port If you don't specify a host port, Docker will choose one: ``` $ `docker run -d --name app -p 80 realestate/ciao` ``` .center[
] Use `docker port` to discover which one it chose: ``` $ `docker port app 80` ``` Hit it: ``` $ `curl $(docker port app 80)` ``` --- # Try it: logs You can get the output using `docker logs`: ``` $ `docker logs app` ``` Again, with timestamps: ``` $ `docker logs --timestamps app` ``` or even follow along in real time: ``` $ `docker logs --follow --timestamps app` ``` --- name: environment # Try it: set environment variables Assuming your application looks for environment variables, e.g. ```javascript var MESSAGE = (process.env.MESSAGE || "Ciao mondo."); ``` You can set them to provide configuration: ``` $ `docker run -d --name app -p 5678:80 \` `-e MESSAGE='Hey, guys!' \` `realestate/ciao` ``` Test the result: ``` $ `curl localhost:5678` ``` --- name: networks class: middle, center # Networks --- # Consider: a reverse-proxy image `nginx`, configured to: * listen on port `80` * forward requests to a host called "`app`" ``` upstream backend { server ${BACKEND:-"app"} fail_timeout=0; } server { listen ${PORT:-80}; location / { proxy_pass http://backend; } } ``` I've published it as `realestate/ciao-proxy`. --- # Try it: inter-container networking Create a "network": ``` $ `docker network create shared` $ `docker network ls` ``` Attach some containers: ``` $ `docker rm -f app proxy` $ `docker run -d --net=shared --name app \` `realestate/ciao` $ `docker run -d --net=shared --name proxy \` `-p 5678:80 \` `realestate/ciao-proxy` ``` ``` $ `curl -si localhost:5678` ``` --- # Try it: explore the network ``` $ `docker network inspect shared` ``` .center[
] ``` $ `docker run -it --rm --network shared busybox` / # `nslookup app` ``` --- name: volumes class: middle, center # Volumes --- # Try it: volume from host Mount your home directory from the "host": ``` $ `docker run -it --rm -v $HOME:/myhome ubuntu bash` root@c10a43c38793:/# `mount | grep home` root@c10a43c38793:/# `cd /myhome; ls` root@c10a43c38793:/# `echo HELLO FROM DOCKER > written-from-docker` root@c10a43c38793:/# `exit` $ `ls $HOME` ``` --- # Try it: named cache volume ``` $ `docker run -it --rm -v my-cache:/cache ubuntu bash` root@c10a43c38793:/# `echo TESTING > /cache/test` ^D ``` Observe, volume created: ``` $ `docker volume ls` ``` Use it again: ``` $ `docker run -it --rm -v my-cache:/cache ubuntu bash` root@f5be5dec8a5a:/# `ls /cache` ``` Clean up: ``` $ `docker volume rm my-cache` ``` --- # Try it: anonymous volume for extra writable space ``` $ `docker run -it --rm --read-only -v /scratch ubuntu` root@c10a43c38793:/# `echo TESTING > /tmp/test` root@c10a43c38793:/# `echo TESTING > /scratch/test` ``` .center[
] Tip: read-only root volume is great for security! --- # Try it: share a volume with another container Create a container, with a volume: ``` $ `docker run -it --rm --name provider -v data:/published ubuntu` root@118f38503653:/# `echo ohai > /published/stuff` ``` .center[
] Use the volume from a different container: ``` $ `docker run -it --rm --name consumer -v data:/provided:ro ubuntu` root@d88a7a468b01:/# `cat /provided/stuff` ``` --- name: images class: center, middle # Images --- # Outcomes At the end of this module, we hope you: - Understand how Docker Images are built in layers - know how to build an image from Dockerfiles - know how to tag an image - know about build caching - know how to publish an image to Docker Hub - know how to pull an image from Docker Hub - can build, push and pull Docker images. - know where to go to learn more --- # What's in an image? -- .lhs[ * File-system snapshot * as layers ] -- .rhs[ * Meta-data * default command * default environment settings * default user id * default working directory * volume/port hints ] -- .clear[
``` $ `docker image inspect ubuntu` ``` ] --- name: list-images # Try it: list images View images on your Docker host: ``` $ `docker images` REPOSITORY TAG IMAGE ID CREATED SIZE busybox latest e02e811dd08f 4 days ago 1.093 MB debian latest ddf73f48a05d 2 weeks ago 123 MB nginx latest ba6bed934df2 2 weeks ago 181.4 MB ruby 2.3-alpine 2467a614f30b 2 weeks ago 125.8 MB ... ubuntu 16.04 45bc58500fa3 3 weeks ago 126.9 MB ubuntu latest 45bc58500fa3 3 weeks ago 126.9 MB ubuntu
45bc58500fa3 3 weeks ago 126.9 MB ``` -- View images in the `ubuntu` repository: ``` $ `docker images ubuntu` ``` --- name: image-tags # Try it: use an image tag Image repositories can contain multiple images, identified by tag: .center[ `
[:
]` ] Run different versions of Ruby: ``` $ `docker run ruby:2.3 ruby --version` $ `docker run ruby:2.4 ruby --version` ``` -- By convention, there's usually a `latest` tag: ``` $ `docker run ruby:latest ruby --version` ``` which is the default: ``` $ `docker run ruby ruby --version` ``` --- name: image-digest # Try it: stable image references Image can also be addressed with immutable digests: .center[ `
[@
]` ] ``` $ `docker pull ruby:2.4 | grep Digest` ``` Use the digest: ``` $ `docker run ruby@sha256:ec92755b46504ec2d27d8a2887d080ca9ef799dfe7c010b7019b2165f875c738 \` `ruby --version` ``` Or, a different (older) one: ``` $ `docker run ruby@sha256:450ce48d8021d33c306168654256b9c960711e6c02991f270d55488caf860410 \` `ruby --version` ``` --- name: image-namespaces # Try it: non-standard namespace Repositories can have an optional namespace: .center[ `[
/]
` ] Use a community image: ``` $ `docker run -it supertest2014/nyan` ``` --- name: image-references # Summary: image references Image-name pattern: .center[ `[
/][
/]
[:
][@
]` ] * `
` defaults to `docker.io` (aka Docker Hub) * `
` defaults to `library` * `
` defaults to `latest` * `
` is a digest of image contents So this: ``` hello-world ``` Is short for: ``` docker.io/library/hello-world:latest@sha256:1f19634d26995c320618d94e6f29c09c6589d5df3c063287a00e6de8458f8242 ``` --- class: center, middle # Let's make one! --- name: building-images # Try it: build an image, the hard way Install some software in an `ubuntu` container: ``` $ `docker run -it ubuntu bash` root@f55393e5dc31:/# `apt-get update && apt-get install -y curl` … root@f55393e5dc31:/# `exit` ``` Now, `commit` that container to create an image: ``` $ `container=$(docker ps -lq)` $ `image=$(docker commit $container)` ``` Try it out: ``` $ `docker run ubuntu curl http://example.com` $ `docker run $image curl http://example.com` ``` --- # Try it: name your image Attach a tag to an existing image: ``` $ `docker tag $image curly` ``` Or, when making it: ``` $ `docker commit $container curly` ``` For later use: ``` $ `docker run curly curl http://example.com` ``` --- # Layers, with labels ``` $ `docker history curly` ```
27323a7d221f
curly:latest
↓
2d696327ab2e
ubuntu:latest, ubuntu:16.04
...
...
...
--- # Try it: build an image, the easy way Use a `Dockerfile`: ``` FROM ubuntu RUN apt-get update && apt-get install -y curl ``` to build an image: ``` $ `docker build exercises/ubuntu-with-curl` Sending build context to Docker daemon 2.048 kB Step 1 : FROM ubuntu ---> 07c86167cdc4 Step 2 : RUN apt-get update && apt-get install -y curl ---> Running in 51bf195331b7 Ign http://archive.ubuntu.com trusty InRelease Get:1 http://archive.ubuntu.com trusty-updates InRelease [64.4 kB] … ---> 70b42c74bb66 Removing intermediate container 51bf195331b7 Successfully built 70b42c74bb66 ``` --- # Try it: leverage the "build cache" Use the recipe provided: ``` FROM node:6.2.2 COPY package.json /app/ WORKDIR /app RUN npm install COPY index.js /app/ ENV PORT 80 CMD ["node", "/app/index.js"] ``` to build an image: ``` $ `docker build -t ciao exercises/ciao` ``` --- # Try it: invalidate the "build cache" Build it again: ``` $ `docker build -t ciao exercises/ciao` ``` Faster, eh? Observe: "Using cache". -- Now try this: * Edit `exercises/ciao/index.js`, changing the default MESSAGE from "Ciao mondo" to something else, then build again. What happens at "Step 5"? -- * Make a change to `exercises/ciao/package.json`, and build again. What happens at "Step 4"? --- name: publishing-images # Publishing images Sign up for an account at [`https://hub.docker.com`](https://hub.docker.com) .center[
] --- # Try it: push an image to Docker Hub Authenticate to Docker Hub: ``` $ `docker login` ``` "Tag" an image into _your_ namespace: ``` $ `docker tag ciao YOURNAMEHERE/ciao` ``` Now you can push it: ``` $ `docker push YOURNAMEHERE/ciao` ``` --- # Try it: pull an image someone else pushed Talk to the esteemed colleague next to you, and ask them for their Docker Hub username. Then, you shoud be able to fetch the image _they_ pushed. ``` $ `docker pull YOURNEIGHBOUR/ciao` ``` --- name: runtime-hints # Tip: set config defaults with "ENV" The `env` directive sets a default value for an environment variable. ``` ENV PORT 80 ``` but you can override it at runtime: ``` $ `docker run -d -e PORT=5000 ciao` ``` --- # Tip: implicit volume with "VOLUME" The `VOLUME` directive documents that the specified directory _should_ be on a volume. Sample from "[postgres](https://github.com/docker-library/postgres/blob/8a9fbcb40f13ccc7762f278b9df611cabe22d300/9.5/Dockerfile)" image: ``` FROM debian:jessie RUN apt-get install -y postgresql-common postgresql-9.5 … ENV PGDATA /var/lib/postgresql/data VOLUME /var/lib/postgresql/data … ``` Docker will
implicitly
add ``` -v /var/lib/postgresql/data ``` to the `docker run` command-line. --- # Tip: drop privilege with "USER" ``` FROM python:3.6 # install app (as root) COPY . /app # create a user RUN groupadd -r somebody && useradd -r -g somebody somebody # let them write to /app/tmp RUN mkdir /app/tmp && chown -R somebody:somebody /app/tmp # run as that user (by default) USER somebody ``` --- name: security # Security Let's talk about security. --- # Outcomes * I understand that choices I make impact the security of my systems * I am aware of some security controls available to me * I know where to go to learn more --- # Choices You make choices that impact the security of your systems: * Which base image? * Which linux distribution? * Which packages to install? * Which language runtime(s)? * What source code you include? * Which file permissions? * Which user to run as? * Which configuration is possible? * Which volume mounts? * Which seccomp profile? * Which networks to connect it to? * How frequently to update? OMG! --- # Base images Would you use this image? ``` FROM docker123321/tomcat ``` --- # Base images (contd) Lot's of people did. It was pulled 100k times! > By pushing malicious images to a Docker Hub registry and pulling it from the victim’s system, hackers were able to mine 544.74 Monero, which is equal to $90000. See https://kromtech.com/blog/security-center/cryptojacking-invades-cloud-how-modern-containerization-trend-is-exploited-by-attackers --- # Base Images: YOUR CHOICE Base images are your choice. It's a choice about trust. * Consider using [official repositories on DockerHub](https://docs.docker.com/docker-hub/official_repos/) * Consider how frequently you update your base images --- # Linux distributions What's a linux distribution? ``` # how many files come with the ubuntu image? docker run ubuntu find / -xdev -type f | wc -l # how many files come with the alpine image? docker run alpine find / -xdev -type f | wc -l ``` Why is this relevant? --- # Linux Distribution: YOUR CHOICE You decide which linux distribution to use. If you choose to use one, you place trust in the distribution's maintainers. * Consider how distributions manage security updates * Consider how compatibility, security and effort relate to your system --- # User What is a user? Why does it matter? ``` docker run alpine whoami ``` What user was used when `whoami` ran? --- # User: YOUR CHOICE It's your choice which user your program runs as. Consider dropping to a non-privileged user: ``` echo 'FROM alpine' > Dockerfile echo 'USER nobody' >> Dockerfile docker build -t non-privileged . docker run non-privileged whoami ``` --- # Writable Root file systems When I start a container, can I change the contents of the file system? ``` docker run alpine rm -f /bin/sh ``` --- # Writable Root file system: YOUR CHOICE It's your choice whether container file systems are writable. Consider using a read-only root filesystem: ``` docker run --read-only alpine rm -f /bin/sh rm: can't remove '/bin/sh': Read-only file system ``` --- # Secrets What does this mean? ``` COPY continuous-integration.pem ``` What just happened? --- # Secrets: YOUR CHOICE It's your choice to add secrets to your docker image. * Consider NEVER ADDING SECRETS to your docker image * Consider using critical thinking before copy and paste from GitHub * Consider using encrypted environment variables instead --- # Getting help Remember, you're not alone * Talk to other people on your team * Talk to your team's systems engineer * Talk to your team's security advisor * Ask on Slack --- ## Learning Resources * [Official repositories on Docker Hub](https://docs.docker.com/docker-hub/official_repos/) * [OWASP Security by Design Principles](https://www.owasp.org/index.php/Security_by_Design_Principles) * [Official Docker Images guidelines](https://github.com/docker-library/official-images#image-build) * [Dockerfile best practices](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#user) * [Security do's and don'ts discussion on docker](https://github.com/moby/moby/issues/13490) * [Dropping capabilities](https://rhelblog.redhat.com/2016/10/17/secure-your-containers-with-this-one-weird-trick/) * [Docker Security by Adrian Mouat](http://www.oreilly.com/webops-perf/free/docker-security.csp) * [7 Docker security vulnerabilities and threats](https://sysdig.com/blog/7-docker-security-vulnerabilities/) --- name: compose class: middle, center # Docker Compose .center[
] --- # Outcomes At the end of this module, we hope you:
.big[ - Understand what Docker Compose is - know how to deploy a simple multi-container app with Docker Compose - know how container networking works with Docker Compose - know how to share a volume between containers - know where to go to learn more ] --- # What is Docker Compose?
-- From the
Docker Website
: - Compose is a tool for defining and running multi-container Docker applications. -- - With Compose, you use a YAML file to configure your application’s services. -- - Then, with a single command, you create and start all the services from your configuration. --- # The docker-compose.yml file ``` version: '3' services: web: image: my-app ports: - 5000:80 redis: image: redis:alpine ``` - Lists multiple containers as services - Options to each service almost identical to `docker run` - Start them all at once with `docker-compose up` --- # Exercise 1: write your own docker-compose.yml This docker-compose file isn't complete! ``` version: '3' services: app: {} proxy: image: realestate/ciao-proxy depends_on: - app ``` -- - App's image needs to be `realestate/ciao` - Proxy needs an environment variable `BACKEND` set to `app` - Proxy needs to expose port `80` externally as `5678` -- ``` $ `cd exercises/composed/basic-exercise` $ `docker-compose up` ``` Then in a different window: `curl localhost:5678` https://docs.docker.com/compose/compose-file/ --- # Exercise 1: solution ``` version: '3' services: app: image: realestate/ciao proxy: image: realestate/ciao-proxy depends_on: - app environment: BACKEND: app ports: - 5678:80 ``` You can start the containers with: ``` $ `cd exercises/composed/basic` $ `docker-compose up` ``` --- # Try it: under the covers of compose In a different window, generate some traffic: ``` $ `curl localhost:5678` ```
What containers are running? ``` $ `docker ps` $ `cd exercises/composed/basic` $ `docker-compose ps` ``` --- # How about networking? ### IP Address: ``` $ `docker inspect --format='{{range .NetworkSettings.Networks}}` `{{.IPAddress}}{{end}}' basic_app_1` ``` ### MAC Address: ``` $ `docker inspect --format='{{range .NetworkSettings.Networks}}` `{{.MacAddress}}{{end}}' basic_app_1` ``` --- # Take it all down:
- We may not want multiple docker containers from different docker-compose files running at the same time - Halts images and cleans up resources
``` $ `docker-compose down` ``` --- # Try it: get compose to build your images ``` version: '3' services: app: `build: ../../ciao` proxy: `build: ../../ciao-proxy` depends_on: - app environment: BACKEND: app ports: - 5678:80 ``` -- ``` $ `cd exercises/composed/autobuilt` $ `docker-compose up` ``` --- # Exercise 2: add a "client" container We're going to add a client container that will generate traffic to the proxy for us. - Client needs to be built from `../../ubuntu-with-curl` - It depends on `proxy` - It should run the command `sh -c "while true; do curl ???; sleep 1; done"` - What address should we curl to reach the proxy? - https://docs.docker.com/compose/networking/ ``` $ `cd exercises/composed/with-curl-exercise` $ `docker-compose up` ``` (Press Ctrl-C to tear everything down.) --- # Exercise 2: solution ``` version: '3' services: app: ... proxy: ... client: build: ../../ubuntu-with-curl depends_on: - proxy command: sh -c "while true; do curl http://proxy; sleep 1; done" ``` ``` $ `cd exercises/composed/with-curl` $ `docker-compose up` ``` (Press Ctrl-C to tear everything down.) --- # Try it: docker-compose run Try "running" a "client" container: ``` $ `docker-compose run --rm client` ``` Or, go interactive: ``` $ `docker-compose run --rm client bash` root@ebb0d01e63b0:/# `curl -i app` root@ebb0d01e63b0:/# `curl -i proxy` ``` --- # Exercise 3: sharing volumes ``` version: '3' services: consumer: image: ubuntu:16.04 volumes: - .:/data command: tail -f /data/dates ``` ``` $ `cd exercises/composed/sharing-exercise` $ `docker-compose up` ``` Add some content to the `dates` file --- # Exercise 3: sharing volumes Use a producer container to generate dates. -- - Add a producer container ``` producer: image: busybox volumes: - shared:/out command: sh -c "while true; do date; sleep 1; done > /out/dates" ``` - Change the consumer's volume to `- shared:/data` - Add a global volume called shared https://docs.docker.com/compose/compose-file/#volume-configuration-reference --- # Exercise 3: solution ``` version: '3' volumes: shared: {} services: producer: image: busybox volumes: - shared:/out command: sh -c "while true; do date; sleep 1; done > /out/dates" consumer: image: ubuntu:16.04 depends_on: - producer volumes: - shared:/data command: tail -f /data/dates ``` ``` $ `cd exercises/composed/sharing` $ `docker-compose up` ``` --- class: middle, center # Well done! You made it. --- # Try it: Python and Redis and .Net and PostgreSQL and Node.js ``` $ `cd exercises/example-voting-app` $ `docker-compose up` ``` .center[
] --- # Try it: Python and Redis and .Net and PostgreSQL and Node.js Once it's started running ... * voting-app at
http://localhost:5000
* result-app at
http://localhost:5001
When you're done, clean up: ``` $ `^C` $ `docker-compose down` ``` --- # If you'd like to know more: * [Get Started with Docker Compose](https://docs.docker.com/compose/gettingstarted/) * [Docker Compose in 12 Minutes (YouTube)](https://www.youtube.com/watch?v=Qw9zlE3t8Ko) * [Sample apps with Docker Compose](https://docs.docker.com/compose/samples-for-compose/) * [Docker Compose Source Code](https://github.com/docker/compose) --- class: middle, center # Thanks! We appreciate your attendance! Feedback is
welcome
essential. --- class: middle, center # Feedback Survey Please tell us how we're doing by completing a small survey on today's training. # https://goo.gl/SUryz8