├── LICENSE ├── README.md ├── code ├── docker-apache2 │ └── Dockerfile ├── docker-git │ └── Dockerfile ├── guestbook-node │ ├── Dockerfile │ ├── docker-compose.yml │ ├── main.js │ ├── package.json │ └── view │ │ ├── form.html │ │ ├── layout.html │ │ ├── list.html │ │ └── messages.html └── hello-world │ ├── Dockerfile │ └── site │ └── index.html └── doc ├── 00-docker-basics └── readme.md ├── 01-docker-machine └── readme.md └── 02-docker-compose └── readme.md /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Harbur Cloud Solutions S.L. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [![harbur.io](https://harbur.io/logo/Color/Logo/Harbur-40x40.png)](http://harbur.io) Docker Workshop 2 | 3 | The Workshop is separated in three sections: 4 | 5 | * [Docker Basics](doc/00-docker-basics) 6 | * [Docker Machine](doc/01-docker-machine) 7 | * [Docker Compose](doc/02-docker-compose) 8 | 9 | ### Preparations: 10 | 11 | * [Install Docker](https://docs.docker.com/engine/installation/) 12 | * Clone this repo: `git clone https://github.com/harbur/docker-workshop` (Some code examples require files located here) 13 | * Warm-up the images: 14 | 15 | ``` 16 | docker pull alpine:3.3 17 | docker pull nginx:1.8-alpine 18 | docker pull redis:alpine 19 | docker pull mhart/alpine-node:latest 20 | ``` 21 | 22 | ### Assumptions: 23 | 24 | * During workshop the following ports are used: `80`, `8088` and the range `4000-4010`. If they are not available on your machine, adjust the CLI commands accordingly. 25 | 26 | ### Chat 27 | [![Members online on Docker Barcelona Slack](https://dockerbcn.herokuapp.com/badge.svg)](https://dockerbcn.herokuapp.com) 28 | 29 | Join [Docker Barcelona Slack](https://dockerbcn.herokuapp.com)! 30 | 31 | ### Docker Cheat Sheet: 32 | 33 | We've wrote a page with some useful docker commands: [Harbur's docker-cheat-sheet](https://github.com/harbur/docker-cheat-sheet). 34 | 35 | # Credits 36 | 37 | This workshop was prepared by [harbur.io](http://harbur.io), under MIT License. Feel free to fork and improve. -------------------------------------------------------------------------------- /code/docker-apache2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.3 2 | RUN apk --update add apache2 && rm -rf /var/cache/apk/* 3 | RUN mkdir -p /run/apache2 4 | EXPOSE 80 5 | CMD httpd -D FOREGROUND 6 | -------------------------------------------------------------------------------- /code/docker-git/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.3 2 | RUN apk --update add git 3 | -------------------------------------------------------------------------------- /code/guestbook-node/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:7.7.0-alpine 2 | RUN mkdir /code 3 | WORKDIR /code 4 | ADD package.json /code/ 5 | RUN npm install 6 | ADD . /code/ 7 | CMD ["node", "main.js"] 8 | -------------------------------------------------------------------------------- /code/guestbook-node/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | redis: 4 | image: redis:alpine 5 | web: 6 | build: . 7 | ports: 8 | - "8088:3000" 9 | depends_on: 10 | - redis -------------------------------------------------------------------------------- /code/guestbook-node/main.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var bodyParser = require('body-parser'); 3 | var redis = require('redis'); 4 | var swig = require('swig'); 5 | 6 | var app = express(); 7 | var client = redis.createClient(6379, "redis"); 8 | 9 | app.engine('html', swig.renderFile); 10 | app.set('view engine', 'html'); 11 | app.set('views', __dirname + '/view'); 12 | app.use(bodyParser.urlencoded({ 13 | extended: true 14 | })); 15 | 16 | app.get('/', function (req, res) { 17 | client.lrange('guestbook-node-messages', 0, -1, function (err, msgs) { 18 | if (err) { 19 | console.log(err); 20 | } 21 | res.render('list', { messages: msgs }) 22 | }); 23 | }); 24 | 25 | app.post('/post', function(req, res) { 26 | if (!req.body.msg) { 27 | res.redirect('/'); 28 | } 29 | 30 | client.rpush('guestbook-node-messages', req.body.msg, function (err) { 31 | if (err) { 32 | console.log(err); 33 | } 34 | res.redirect('/'); 35 | }); 36 | }); 37 | 38 | client.on("error", function (err) { 39 | console.log("Error " + err); 40 | }); 41 | 42 | process.on("SIGTERM", function () { 43 | client.quit(); 44 | app.close(function () { 45 | process.exit(0); 46 | }); 47 | }); 48 | 49 | app.listen(3000); 50 | -------------------------------------------------------------------------------- /code/guestbook-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "guestbook", 3 | "version": "1.0.0", 4 | "description": "Guestbook test app for Harbur's Docker Workshop", 5 | "main": "main.js", 6 | "dependencies": { 7 | "body-parser": "^1.15.0", 8 | "express": "^4.13.4", 9 | "redis": "^2.6.0-1", 10 | "swig": "^1.4.2" 11 | }, 12 | "author": "", 13 | "license": "ISC" 14 | } 15 | -------------------------------------------------------------------------------- /code/guestbook-node/view/form.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | 7 |
8 |
9 |
10 |
-------------------------------------------------------------------------------- /code/guestbook-node/view/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Guestbook 4 | 5 | 42 | 43 | 44 | 45 |
46 |
47 |

Guestbook

48 |
49 | 50 | {% block content %}{% endblock %} 51 |
52 | 53 | -------------------------------------------------------------------------------- /code/guestbook-node/view/list.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html' %} 2 | 3 | {% block content %} 4 | {% include "./form.html" %} 5 | 6 | 7 | {% if messages.length %} 8 |
9 | {% include "./messages.html" %} 10 | {% endif %} 11 | {% endblock %} -------------------------------------------------------------------------------- /code/guestbook-node/view/messages.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Messages

4 | {% for message in messages %} 5 |
{{ message}}
6 | {% endfor %} 7 |
8 |
-------------------------------------------------------------------------------- /code/hello-world/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.8-alpine 2 | ADD site /usr/share/nginx/html 3 | -------------------------------------------------------------------------------- /code/hello-world/site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hello World 5 | 6 | 7 | 10 | 11 | 12 |
13 |
14 |

Hello World

15 |

This is a static site example served by nginx container

16 | Docker Workshop 17 |
18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /doc/00-docker-basics/readme.md: -------------------------------------------------------------------------------- 1 | # [![harbur.io](https://harbur.io/logo/Color/Logo/Harbur-40x40.png)](http://harbur.io) Docker Workshop - Docker Basics 2 | 3 | This section is separated in: 4 | 5 | * [CLI Basics](#cli-basics) 6 | * [Dockerfile basics](#dockerfile-basics) 7 | 8 | # CLI Basics 9 | 10 | ### Version 11 | 12 | Check you have latest version of docker installed: 13 | 14 | ``` 15 | docker version 16 | ``` 17 | 18 | * If you don't have docker installed, check [here](https://docs.docker.com/installation/#installation) 19 | * If you're not on the latest version, it will prompt you to update 20 | * If you're not on docker group you might need to prefix commands with `sudo`. See [here](http://docs.docker.com/installation/ubuntulinux/#giving-non-root-access) for details about it. 21 | 22 | ### Commands 23 | 24 | Check the available docker commands 25 | 26 | ``` 27 | docker 28 | ``` 29 | 30 | * Whenever you don't remember a command, just type docker 31 | * For more info, type `docker help COMMAND` (e.g. `docker help run`) 32 | 33 | ### RUN a "Hello World" container 34 | 35 | ``` 36 | docker run alpine echo "Hello World" 37 | ``` 38 | 39 | * If the Image is not cached, it pulls it automatically 40 | * It prints `Hello World` and exits 41 | 42 | ### RUN an interactive Container 43 | 44 | ``` 45 | docker run -it alpine sh 46 | cat /etc/os-release 47 | ``` 48 | 49 | * **-i**: Keep stdin open even if not attached 50 | * **-t**: Allocate a pseudo-tty 51 | 52 | ### RUN a Container with pipeline 53 | 54 | ``` 55 | cat /etc/resolv.conf | docker run -i alpine wc -l 56 | ``` 57 | 58 | ### SEARCH a Container 59 | 60 | ``` 61 | docker search -s 10 nginx 62 | ``` 63 | 64 | * **-s**: Only displays with at least x stars 65 | 66 | ### RUN a Container and expose a Port 67 | 68 | On Linux: 69 | ``` 70 | docker run -d -p 4000:80 nginx 71 | google-chrome localhost:4000 72 | ``` 73 | 74 | On Mac: 75 | ``` 76 | docker run -d -p 4000:80 nginx 77 | open "http://$(docker-machine ip default):4000" 78 | ``` 79 | 80 | * **-d**: Detached mode: Run container in the background, print new container id 81 | * **-p**: Publish a container's port to the host (format: *hostPort:containerPort*) 82 | * For more info about the container, see [nginx](https://registry.hub.docker.com/_/nginx/) 83 | 84 | ### RUN a Container with a Volume 85 | 86 | NOTE: Make sure to be on `Docker Workshop` directory since we'll use volume mounts in the containers of directories of the repository. 87 | 88 | On Linux: 89 | ``` 90 | docker run -d -p 4001:80 -v $(pwd)/code/hello-world/site/:/usr/share/nginx/html:ro nginx 91 | google-chrome localhost:4001 92 | ``` 93 | 94 | On Mac: 95 | ``` 96 | docker run -d -p 4001:80 -v $(pwd)/code/hello-world/site/:/usr/share/nginx/html:ro nginx 97 | open "http://$(docker-machine ip default):4001" 98 | ``` 99 | 100 | * **-v**: Bind mount a volume (e.g., from the host: -v /host:/container, from docker: -v /container) 101 | * The volume is **linked** inside the container. Any external changes are visible directly inside the container. 102 | * This example breaks the immutability of the container, good for debuging, not recommended for production (Volumes should be used for data, not code) 103 | 104 | ## Exercise 1 (10 mins) 105 | 106 | * Build a static website 107 | * Run it on your machine 108 | * Share your (non-localhost) url on [Slack](https://dockerbcn.herokuapp.com) 109 | 110 | # Dockerfile Basics 111 | 112 | ### BUILD a Git Client Container 113 | 114 | Create a Git Container manually: 115 | 116 | ``` 117 | docker run -it --name git alpine sh 118 | apk --update add git 119 | git version 120 | exit 121 | docker commit git docker-git 122 | docker rm git 123 | docker run --rm -it docker-git git version 124 | docker rmi docker-git 125 | ``` 126 | 127 | * **--name**: Assign a name to the container 128 | * **commit**: Create a new image from a container's changes 129 | * **rm**: Remove one or more containers 130 | * **rmi**: Remove one or more images 131 | * **--rm**: Automatically remove the container when it exits 132 | 133 | Create a Git Container with Dockerfile: 134 | 135 | ``` 136 | cd code/docker-git 137 | docker build -t docker-git . 138 | docker run -it docker-git git version 139 | ``` 140 | 141 | * **build**: Build an image from a Dockerfile 142 | 143 | [code/docker-git/Dockerfile](../../code/docker-git/Dockerfile) 144 | ``` 145 | FROM alpine:3.3 146 | RUN apk update 147 | RUN apk add git 148 | ``` 149 | 150 | * The **FROM** instruction sets the Base Image for subsequent instructions 151 | * The **RUN** instruction will execute any commands in a new layer on top of the current image and commit the results 152 | 153 | ### BUILD an Apache Server Container 154 | 155 | Create an Apache Server Container with Dockerfile: 156 | 157 | ``` 158 | cd code/docker-apache2 159 | docker build -t docker-apache2 . 160 | docker run -d -p 4003:80 docker-apache2 161 | ``` 162 | 163 | On Linux: 164 | ``` 165 | google-chrome localhost:4003 166 | ``` 167 | 168 | On Mac: 169 | ``` 170 | open "http://$(docker-machine ip default):4003" 171 | ``` 172 | 173 | [code/docker-apache2/Dockerfile](../../code/docker-apache2/Dockerfile) 174 | ``` 175 | FROM alpine:3.3 176 | RUN apk --update add apache2 && rm -rf /var/cache/apk/* 177 | RUN mkdir -p /run/apache2 178 | EXPOSE 80 179 | CMD httpd -D FOREGROUND 180 | ``` 181 | 182 | * The **EXPOSE** instructions informs Docker that the container will listen on the specified network ports at runtime 183 | * The **CMD** instruction sets the command to be executed when running the image 184 | 185 | ### BUILD a Static website Image 186 | 187 | ``` 188 | cd code/hello-world 189 | docker build -t hello-world . 190 | docker run -d --name hello -P hello-world 191 | ``` 192 | 193 | On Linux: 194 | ``` 195 | google-chrome $(docker port hello 80) 196 | ``` 197 | 198 | On Mac: 199 | ``` 200 | open "http://$(docker-machine ip default):${$(docker port hello 80)##*:}" 201 | ``` 202 | 203 | * **-P**: Publish all exposed ports to the host interfaces 204 | * **port**: Lookup the public-facing port that is NAT-ed to PRIVATE_PORT 205 | 206 | [code/hello-world/Dockerfile](../../code/hello-world/Dockerfile) 207 | ``` 208 | FROM nginx:1.8-alpine 209 | ADD site /usr/share/nginx/html 210 | ``` 211 | 212 | * The **ADD** instruction will copy new files from and add them to the container's filesystem at path 213 | 214 | ## Exercise 2 (10 mins) 215 | 216 | * Build your website with Dockerfile 217 | * Run an instance 218 | * Share your (non-localhost) url on [Slack](https://dockerbcn.herokuapp.com) 219 | 220 | ### PUSH Image to a Registry 221 | 222 | For this step, we'll need to launch a registry: 223 | 224 | ``` 225 | docker run -d -p 5000:5000 --name registry registry:2 226 | ``` 227 | 228 | Then tag your image under the registry namespace and push it there: 229 | 230 | ``` 231 | REGISTRY=localhost:5000 232 | docker tag hello-world $REGISTRY/$(whoami)/hello-world 233 | docker push $REGISTRY/$(whoami)/hello-world 234 | ``` 235 | 236 | * **tag**: Tag an image into a repository 237 | * **push**: Push an image or a repository to a Docker registry server 238 | 239 | ## Exercise 3 (10 mins) 240 | 241 | * Push your website to the local Registry (use your github username) 242 | * Push your website image 243 | * Share your image name on [Slack](https://dockerbcn.herokuapp.com) 244 | 245 | ### PULL Image from a Repository 246 | 247 | ``` 248 | docker pull $REGISTRY/$(whoami)/hello-world 249 | docker run -d -P --name=registry-hello $REGISTRY/$(whoami)/hello-world 250 | ``` 251 | 252 | On Linux: 253 | ``` 254 | google-chrome $(docker port registry-hello 80) 255 | ``` 256 | 257 | On Mac: 258 | ``` 259 | open "http://$(docker-machine ip default):${$(docker port registry-hello 80)##*:}" 260 | ``` 261 | 262 | * **pull**: Pull an image or a repository from a Docker registry server 263 | 264 | 265 | # Navigation 266 | 267 | Previous | Next 268 | :------- | ---: 269 | ← [Docker Workshop - Home](https://github.com/harbur/docker-workshop) | [Docker Workshop - Docker machine](../01-docker-machine) → 270 | 271 | # Credits 272 | 273 | This workshop was prepared by [harbur.io](http://harbur.io), under MIT License. Feel free to fork and improve. 274 | -------------------------------------------------------------------------------- /doc/01-docker-machine/readme.md: -------------------------------------------------------------------------------- 1 | # [![harbur.io](https://harbur.io/logo/Color/Logo/Harbur-40x40.png)](http://harbur.io) Docker Workshop - Docker machine 2 | 3 | In this section we'll introduce `docker-machine`. 4 | 5 | ## Install Docker Toolbox 6 | 7 | Download and install [Docker Toolbox](https://www.docker.com/docker-toolbox). 8 | 9 | The toolbox installs a handful of tools on your local Windows or Mac OS X computer. In this exercise, you use two of those tools: 10 | 11 | * Docker Machine: To deploy virtual machines that run Docker Engine 12 | * VirtualBox: To host the virtual machines deployed from Docker Machine 13 | 14 | 15 | ## Create a VM running Docker 16 | 17 | Open a terminal on your computer. 18 | 19 | Create and run a VM named `default` using the following command: 20 | 21 | ``` 22 | docker-machine create -d virtualbox default 23 | ``` 24 | 25 | You can list the existing docker-machines: 26 | 27 | ``` 28 | docker-machine ls 29 | ``` 30 | 31 | In case you already had the machine created, you can simply start the VM: 32 | 33 | ``` 34 | docker-machine start default 35 | ``` 36 | 37 | ## Run a docker container in a docker-machine 38 | 39 | Now, let's use the docker-machine we've just created. We want to run the `hello-world`. 40 | 41 | If you use Mac, you need to run: 42 | ``` 43 | eval $(docker-machine env default) 44 | ``` 45 | 46 | This command set the `DOCKER_HOST` variable to the IP of your `default` `docker-machine`. 47 | 48 | Then we can run the `hello-world` container: 49 | ``` 50 | docker run hello-world 51 | ``` 52 | 53 | ## Clean up 54 | 55 | After we tested our `default` `docker-machine` we want to remove it from our computer. 56 | 57 | Stop the VM named `default`: 58 | 59 | ``` 60 | docker-machine stop default 61 | ``` 62 | 63 | You can destroy the VM named `default`: 64 | 65 | ``` 66 | docker-machine rm default 67 | ``` 68 | 69 | ## Create two machines 70 | 71 | To create two machines do: 72 | 73 | ``` 74 | docker-machine create -d virtualbox client1 75 | docker-machine create -d virtualbox client2 76 | ``` 77 | 78 | Now you can see the machines with: 79 | 80 | ``` 81 | docker-machine ls 82 | ``` 83 | 84 | ## Run Nginx on client1 85 | 86 | ``` 87 | eval $(docker-machine env client1) 88 | docker run -d -p 80:80 nginx:1.8-alpine 89 | docker-machine ip client1 90 | open "http://$(docker-machine ip client1)" 91 | ``` 92 | 93 | ## Run Nginx on client2 94 | 95 | ``` 96 | eval $(docker-machine env client2) 97 | docker run -d -p 80:80 nginx:1.8-alpine 98 | docker-machine ip client2 99 | open "http://$(docker-machine ip client2)" 100 | ``` 101 | 102 | ## SSH to machine 103 | 104 | To SSH inside a machine: 105 | 106 | ``` 107 | docker-machine ssh client1 108 | ``` 109 | 110 | ## Environment variables 111 | 112 | Docker client is configured by environment variables to connect with remote daemons. The following command outputs the variables for connecting to previously created `default` VM. 113 | 114 | ``` 115 | docker-machine env default 116 | ``` 117 | 118 | ## Active Machine 119 | 120 | To get the active machine's name do: 121 | 122 | ``` 123 | docker-machine active 124 | ``` 125 | 126 | ## Cleanup 127 | 128 | 129 | ``` 130 | docker-machine stop client1 client2 131 | docker-machine rm client1 client2 132 | ``` 133 | 134 | 135 | # Navigation 136 | 137 | Previous | Next 138 | :------- | ---: 139 | ← [Docker Workshop - Docker Basics](../00-docker-basics) | [Docker Workshop - Docker Compose](../02-docker-compose) → 140 | 141 | # Credits 142 | 143 | This workshop was prepared by [harbur.io](http://harbur.io), under MIT License. Feel free to fork and improve. -------------------------------------------------------------------------------- /doc/02-docker-compose/readme.md: -------------------------------------------------------------------------------- 1 | # [![harbur.io](https://harbur.io/logo/Color/Logo/Harbur-40x40.png)](http://harbur.io) Docker Workshop - Docker Compose 2 | 3 | 4 | This section will show how to use Docker Compose with some small exercises and with a simple Node/Redis app. Before starting, you'll need to have [Docker Compose installed](https://docs.docker.com/compose/install/). 5 | 6 | # Docker-compose 7 | 8 | Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a Compose file to configure your application’s services. Then, using a single command, you create and start all the services from your configuration. To learn more about all the features of Compose see [the list of features](https://docs.docker.com/compose/overview/#features). 9 | 10 | Using Compose is basically a three-step process: 11 | 12 | 1. Define your app’s environment with a `Dockerfile` so it can be reproduced anywhere. 13 | 1. Define the services that make up your app in `docker-compose.yml` so they can be run together in an isolated environment. 14 | 1. Lastly, run `docker-compose up` and Compose will start and run your entire app. 15 | 16 | 17 | ## Commands 18 | 19 | Check the available commands of Docker Compose. Type in your terminal: 20 | 21 | ``` 22 | docker-compose 23 | ``` 24 | 25 | * Whenever you don't remember a command, just type docker-compose 26 | * For more info, type `docker-compose help COMMAND` (e.g. `docker-compose help build`) 27 | 28 | ## docker-compose.yml 29 | 30 | The `docker-compose.yml` file is a [YAML](http://yaml.org/) file defining [services](https://docs.docker.com/compose/compose-file/#service-configuration-reference), [networks](https://docs.docker.com/compose/compose-file/#network-configuration-reference) and [volumes](https://docs.docker.com/compose/compose-file/#volume-configuration-reference). The default path for a Compose file is `./docker-compose.yml`. 31 | 32 | A service definition contains configuration which will be applied to each container started for that service, much like passing command-line parameters to `docker run`. Likewise, network and volume definitions are analogous to `docker network create` and `docker volume create`. 33 | 34 | Options specified in the `Dockerfile` (e.g., `CMD`, `EXPOSE`, `VOLUME`, `ENV`) are respected by default - you don’t need to specify them again in `docker-compose.yml`. 35 | 36 | 37 | ## Project's components 38 | 39 | We've already created a simple app in `code/guestbook-node` that uses node.js with express and redis. 40 | 41 | 1. Go to `code/guestbook-node` folder 42 | 43 | 2. Review `Dockerfile`: 44 | 45 | ``` 46 | FROM node:7.7.0-alpine 47 | RUN mkdir /code 48 | WORKDIR /code 49 | ADD package.json /code/ 50 | RUN npm install 51 | ADD . /code/ 52 | CMD ["node", "main.js"] 53 | ``` 54 | 55 | We use the base image of `node:7.7.0-alpine`. It is the official image in Alpine, a minimal OS. 56 | 57 | The `Dockerfile` then creates the directory where our code will be stored, `/code`, and it copies the `package.json` so it can install the node dependencies. 58 | 59 | Afterwards it copies all the code we have in the host machine and runs the command that will keep the container running. 60 | 61 | 3. Review `docker-compose.yml`: 62 | 63 | ``` 64 | version: '2' 65 | services: 66 | redis: 67 | image: redis:alpine 68 | web: 69 | build: . 70 | ports: 71 | - "80:3000" 72 | depends_on: 73 | - redis 74 | ``` 75 | 76 | The `docker-compose.yml` file describes the services that make your app. In this example those services are a web server and database. The compose file also describes which Docker images these services use, how they link together, any volumes they might need mounted inside the containers. Finally, the `docker-compose.yml` file describes which ports these services expose. See the docker-compose.yml [reference](https://docs.docker.com/compose/compose-file/) for more information on how this file works. 77 | 78 | In this case, we defined two services, `redis` that uses `redis:alpine` and `web`, our nodejs app. We linked the two of them, and `web` depends on `redis` as you can see in `depends_on`. Also, our nodejs app listens the port `3000` so we linked host's port 80 to the docker container 3000 port. 79 | 80 | 81 | ## Run the app 82 | 83 | ### Build the images 84 | 85 | With docker-compose we can build all the images at once running: 86 | ``` 87 | docker-compose build 88 | ``` 89 | 90 | The `docker-compose build` reads `docker-compose.yml` and build all the services defined in there. 91 | 92 | ### Run a command against a service 93 | We can run a one-time command against a service. For example, the following command starts the `web` service and runs `sh` as its command. 94 | ``` 95 | docker-compose run web sh 96 | ``` 97 | 98 | Commands you use with `run` start in new containers with the same configuration as defined by the service's configuration. This means the container has the same volumes, links, as defined in the configuration file. There two differences though. 99 | 100 | First, the command passed by `run` overrides the command defined in the service configuration. For example, if the `web` service configuration is started with `node`, then `docker-compose run web sh` overrides it with `sh`. 101 | 102 | The second difference is the `docker-compose run` command does not create any of the ports specified in the service configuration. This prevents the port collisions with already open ports. 103 | 104 | ### Start services 105 | 106 | We can run `docker-compose up` that builds, (re)creates, starts, and attaches to containers for a service. Unless they are already running, this command also starts any linked services. 107 | 108 | Type in your terminal: 109 | 110 | ``` 111 | docker-compose up 112 | ``` 113 | 114 | This instructs Compose to run the services defined in the `docker-compose.yml` in containers, using the `redis` image and the `web` service's image and configuration. 115 | 116 | The docker-compose up command aggregates the output of each container. When the command exits, all containers are stopped. 117 | 118 | If we want, we can run the containers in background with `-d` flag: 119 | ``` 120 | docker-compose up -d 121 | ``` 122 | 123 | At this point, your Node app should be running at port `8088` on your Docker host. If you are using a Docker Machine VM, you can use the `docker-machine ip MACHINE_NAME` to get the IP address. 124 | 125 | ### Logs 126 | 127 | We can see the log output from services running: 128 | ``` 129 | docker-compose logs 130 | ``` 131 | 132 | If we want to review the logs of a specific service, e.g. `web`: 133 | ``` 134 | docker-compose logs web 135 | ``` 136 | 137 | ### List containers 138 | 139 | We can run `ps` like in `docker ps` to list containers and their status: 140 | ``` 141 | docker-compose ps 142 | ``` 143 | 144 | ### Stop containers 145 | 146 | ``` 147 | docker-compose stop 148 | ``` 149 | 150 | Stops running containers without removing them. They can be started again with `docker-compose start`. 151 | 152 | If we want we can stop only one container: 153 | ``` 154 | docker-compose stop web 155 | ``` 156 | 157 | ### Start container 158 | 159 | Starts existing containers for a service, e.g. `web`: 160 | ``` 161 | docker-compose start web 162 | ``` 163 | 164 | ### Remove containers 165 | ``` 166 | docker-compose rm 167 | ``` 168 | 169 | The previous command removes __stopped__ service containers. 170 | 171 | If we want to stop and remove them: 172 | 173 | ``` 174 | docker-compose down 175 | ``` 176 | 177 | ## Exercise 1 (10 min) 178 | 179 | Update the title of `guestbook` app adding your name. 180 | 181 | The goal of this exercise is: 182 | - Understand the development process 183 | - Understand how to update the code and deploy it 184 | 185 | 186 | ## Exercise 2 (15 min) 187 | 188 | Add a new service like `web` in the `docker-compose.yml` to have another application that connects to the existing `redis` and expose a different port. 189 | 190 | The goal of this exercise is: 191 | 192 | - Understand how `docker-compose.yml` works 193 | - Understand how to add a new service 194 | - Understand how to expose ports 195 | - Be able to run at the same time the two `web` containers 196 | 197 | # Navigation 198 | 199 | Previous | Next 200 | :------- | ---: 201 | ← [Docker Workshop - Docker machine](../01-docker-machine) | [Docker Workshop - Home](https://github.com/harbur/docker-workshop) → 202 | 203 | # Credits 204 | 205 | This workshop was prepared by [harbur.io](http://harbur.io), under MIT License. Feel free to fork and improve. 206 | --------------------------------------------------------------------------------