├── flask-app ├── requirements.txt ├── Dockerrun.aws.json ├── Dockerfile ├── templates │ └── index.html └── app.py ├── starlight ├── tsconfig.json ├── src │ ├── assets │ │ ├── logo.png │ │ ├── catgif.png │ │ ├── logo.webp │ │ ├── static.png │ │ ├── tasks.png │ │ ├── tasks.webp │ │ ├── catgif.webp │ │ ├── cluster.png │ │ ├── cluster.webp │ │ ├── eb-deploy.png │ │ ├── eb-start.png │ │ ├── eb-start.webp │ │ ├── houston.webp │ │ ├── interest.png │ │ ├── interest.webp │ │ ├── keypair.png │ │ ├── keypair.webp │ │ ├── logo_192.png │ │ ├── logo_512.png │ │ ├── static.webp │ │ ├── eb-deploy.webp │ │ ├── eb-docker.jpeg │ │ ├── eb-docker.webp │ │ ├── foodtrucks.png │ │ ├── foodtrucks.webp │ │ ├── logo-medium.png │ │ ├── logo-small.png │ │ ├── eb-terminate.jpg │ │ └── eb-terminate.webp │ ├── env.d.ts │ └── content │ │ ├── config.ts │ │ └── docs │ │ ├── index.mdx │ │ ├── getting-started │ │ ├── 00-getting-started.md │ │ ├── 01-prerequisites.md │ │ └── 02-setting-up.md │ │ ├── conclusion │ │ ├── 00-conclusion.md │ │ ├── 02-give-feedback.md │ │ └── 01-next-steps.md │ │ ├── introduction │ │ ├── 03-what-will-i-learn.md │ │ ├── 01-what-are-containers.md │ │ ├── 00-docker.md │ │ └── 02-why-use-containers.md │ │ ├── webapps-with-docker │ │ ├── 02-our-first-image.md │ │ ├── 01-docker-images.md │ │ ├── 00-static-sites.md │ │ ├── 03-dockerfile.md │ │ └── 04-docker-on-aws.md │ │ ├── hello-world │ │ ├── 00-busybox.md │ │ ├── 02-terminlogy.md │ │ └── 01-docker-run.md │ │ └── multi-container-environments │ │ ├── 00-introduction.md │ │ ├── 04-development-workflow.md │ │ ├── 05-aws-elastic-container-service.md │ │ ├── 01-foodtrucks.md │ │ ├── 02-docker-network.md │ │ └── 03-docker-compose.md ├── .gitignore ├── package.json ├── public │ └── favicon.svg ├── astro.config.mjs └── README.md ├── .firebaserc ├── tutorial ├── src │ ├── favicon.ico │ ├── images │ │ ├── logo.png │ │ ├── logo.webp │ │ ├── tasks.png │ │ ├── catgif.png │ │ ├── catgif.webp │ │ ├── cluster.png │ │ ├── keypair.png │ │ ├── static.png │ │ ├── static.webp │ │ ├── tasks.webp │ │ ├── cluster.webp │ │ ├── eb-deploy.png │ │ ├── eb-deploy.webp │ │ ├── eb-docker.jpeg │ │ ├── eb-docker.webp │ │ ├── eb-start.png │ │ ├── eb-start.webp │ │ ├── foodtrucks.png │ │ ├── interest.png │ │ ├── interest.webp │ │ ├── keypair.webp │ │ ├── logo-small.png │ │ ├── logo_192.png │ │ ├── logo_512.png │ │ ├── eb-terminate.jpg │ │ ├── foodtrucks.webp │ │ ├── logo-medium.png │ │ └── eb-terminate.webp │ ├── font │ │ ├── fontello.eot │ │ ├── fontello.ttf │ │ ├── fontello.woff │ │ ├── fontello.woff2 │ │ └── fontello.svg │ ├── manifest.json │ ├── sw.js │ └── styles │ │ └── main.scss ├── public │ ├── favicon.ico │ ├── images │ │ ├── cwebp │ │ ├── logo.png │ │ ├── catgif.png │ │ ├── catgif.webp │ │ ├── cluster.png │ │ ├── keypair.png │ │ ├── logo.webp │ │ ├── static.png │ │ ├── static.webp │ │ ├── tasks.png │ │ ├── tasks.webp │ │ ├── cluster.webp │ │ ├── eb-deploy.png │ │ ├── eb-docker.png │ │ ├── eb-start.png │ │ ├── eb-start.webp │ │ ├── interest.png │ │ ├── interest.webp │ │ ├── keypair.webp │ │ ├── logo_192.png │ │ ├── logo_512.png │ │ ├── eb-deploy.webp │ │ ├── eb-docker.jpeg │ │ ├── eb-docker.webp │ │ ├── eb-terminate.jpg │ │ ├── foodtrucks.png │ │ ├── foodtrucks.webp │ │ ├── logo-medium.png │ │ ├── logo-small.png │ │ ├── eb-terminate.webp │ │ ├── twitter.svg │ │ └── github.svg │ ├── font │ │ ├── fontello.eot │ │ ├── fontello.ttf │ │ ├── fontello.woff │ │ ├── fontello.woff2 │ │ └── fontello.svg │ ├── manifest.json │ ├── sw.js │ └── styles │ │ └── main.scss ├── layouts │ ├── partials │ │ ├── analytics.html │ │ ├── sw.html │ │ ├── masthead.html │ │ ├── meta.html │ │ └── nav.html │ └── layout.html ├── README.md └── index.js ├── static-site ├── wrapper.sh ├── html │ ├── images │ │ └── favicon.png │ ├── index.html │ └── css │ │ ├── normalize.css │ │ └── skeleton.css └── Dockerfile ├── .gitignore ├── README.md ├── nodemon.json ├── .github ├── workflows │ └── stale.yml └── ISSUE_TEMPLATE │ ├── feature_request.md │ ├── typo---grammatical-error---spelling-mistakes-etc-.md │ └── bug_report.md ├── LICENSE └── package.json /flask-app/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==2.0.2 2 | -------------------------------------------------------------------------------- /starlight/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict" 3 | } -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "docker": "docker-curriculum" 4 | } 5 | } -------------------------------------------------------------------------------- /tutorial/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/favicon.ico -------------------------------------------------------------------------------- /static-site/wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Nginx is running..." 4 | 5 | exec nginx -g "daemon off;" 6 | -------------------------------------------------------------------------------- /starlight/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/logo.png -------------------------------------------------------------------------------- /starlight/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /tutorial/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/favicon.ico -------------------------------------------------------------------------------- /tutorial/public/images/cwebp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/cwebp -------------------------------------------------------------------------------- /tutorial/src/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/logo.png -------------------------------------------------------------------------------- /tutorial/src/images/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/logo.webp -------------------------------------------------------------------------------- /tutorial/src/images/tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/tasks.png -------------------------------------------------------------------------------- /starlight/src/assets/catgif.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/catgif.png -------------------------------------------------------------------------------- /starlight/src/assets/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/logo.webp -------------------------------------------------------------------------------- /starlight/src/assets/static.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/static.png -------------------------------------------------------------------------------- /starlight/src/assets/tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/tasks.png -------------------------------------------------------------------------------- /starlight/src/assets/tasks.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/tasks.webp -------------------------------------------------------------------------------- /tutorial/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/logo.png -------------------------------------------------------------------------------- /tutorial/src/font/fontello.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/font/fontello.eot -------------------------------------------------------------------------------- /tutorial/src/font/fontello.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/font/fontello.ttf -------------------------------------------------------------------------------- /tutorial/src/font/fontello.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/font/fontello.woff -------------------------------------------------------------------------------- /tutorial/src/images/catgif.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/catgif.png -------------------------------------------------------------------------------- /tutorial/src/images/catgif.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/catgif.webp -------------------------------------------------------------------------------- /tutorial/src/images/cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/cluster.png -------------------------------------------------------------------------------- /tutorial/src/images/keypair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/keypair.png -------------------------------------------------------------------------------- /tutorial/src/images/static.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/static.png -------------------------------------------------------------------------------- /tutorial/src/images/static.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/static.webp -------------------------------------------------------------------------------- /tutorial/src/images/tasks.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/tasks.webp -------------------------------------------------------------------------------- /starlight/src/assets/catgif.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/catgif.webp -------------------------------------------------------------------------------- /starlight/src/assets/cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/cluster.png -------------------------------------------------------------------------------- /starlight/src/assets/cluster.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/cluster.webp -------------------------------------------------------------------------------- /starlight/src/assets/eb-deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/eb-deploy.png -------------------------------------------------------------------------------- /starlight/src/assets/eb-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/eb-start.png -------------------------------------------------------------------------------- /starlight/src/assets/eb-start.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/eb-start.webp -------------------------------------------------------------------------------- /starlight/src/assets/houston.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/houston.webp -------------------------------------------------------------------------------- /starlight/src/assets/interest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/interest.png -------------------------------------------------------------------------------- /starlight/src/assets/interest.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/interest.webp -------------------------------------------------------------------------------- /starlight/src/assets/keypair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/keypair.png -------------------------------------------------------------------------------- /starlight/src/assets/keypair.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/keypair.webp -------------------------------------------------------------------------------- /starlight/src/assets/logo_192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/logo_192.png -------------------------------------------------------------------------------- /starlight/src/assets/logo_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/logo_512.png -------------------------------------------------------------------------------- /starlight/src/assets/static.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/static.webp -------------------------------------------------------------------------------- /tutorial/public/font/fontello.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/font/fontello.eot -------------------------------------------------------------------------------- /tutorial/public/font/fontello.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/font/fontello.ttf -------------------------------------------------------------------------------- /tutorial/public/font/fontello.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/font/fontello.woff -------------------------------------------------------------------------------- /tutorial/public/images/catgif.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/catgif.png -------------------------------------------------------------------------------- /tutorial/public/images/catgif.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/catgif.webp -------------------------------------------------------------------------------- /tutorial/public/images/cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/cluster.png -------------------------------------------------------------------------------- /tutorial/public/images/keypair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/keypair.png -------------------------------------------------------------------------------- /tutorial/public/images/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/logo.webp -------------------------------------------------------------------------------- /tutorial/public/images/static.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/static.png -------------------------------------------------------------------------------- /tutorial/public/images/static.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/static.webp -------------------------------------------------------------------------------- /tutorial/public/images/tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/tasks.png -------------------------------------------------------------------------------- /tutorial/public/images/tasks.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/tasks.webp -------------------------------------------------------------------------------- /tutorial/src/font/fontello.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/font/fontello.woff2 -------------------------------------------------------------------------------- /tutorial/src/images/cluster.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/cluster.webp -------------------------------------------------------------------------------- /tutorial/src/images/eb-deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/eb-deploy.png -------------------------------------------------------------------------------- /tutorial/src/images/eb-deploy.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/eb-deploy.webp -------------------------------------------------------------------------------- /tutorial/src/images/eb-docker.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/eb-docker.jpeg -------------------------------------------------------------------------------- /tutorial/src/images/eb-docker.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/eb-docker.webp -------------------------------------------------------------------------------- /tutorial/src/images/eb-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/eb-start.png -------------------------------------------------------------------------------- /tutorial/src/images/eb-start.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/eb-start.webp -------------------------------------------------------------------------------- /tutorial/src/images/foodtrucks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/foodtrucks.png -------------------------------------------------------------------------------- /tutorial/src/images/interest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/interest.png -------------------------------------------------------------------------------- /tutorial/src/images/interest.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/interest.webp -------------------------------------------------------------------------------- /tutorial/src/images/keypair.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/keypair.webp -------------------------------------------------------------------------------- /tutorial/src/images/logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/logo-small.png -------------------------------------------------------------------------------- /tutorial/src/images/logo_192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/logo_192.png -------------------------------------------------------------------------------- /tutorial/src/images/logo_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/logo_512.png -------------------------------------------------------------------------------- /starlight/src/assets/eb-deploy.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/eb-deploy.webp -------------------------------------------------------------------------------- /starlight/src/assets/eb-docker.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/eb-docker.jpeg -------------------------------------------------------------------------------- /starlight/src/assets/eb-docker.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/eb-docker.webp -------------------------------------------------------------------------------- /starlight/src/assets/foodtrucks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/foodtrucks.png -------------------------------------------------------------------------------- /starlight/src/assets/foodtrucks.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/foodtrucks.webp -------------------------------------------------------------------------------- /starlight/src/assets/logo-medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/logo-medium.png -------------------------------------------------------------------------------- /starlight/src/assets/logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/logo-small.png -------------------------------------------------------------------------------- /static-site/html/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/static-site/html/images/favicon.png -------------------------------------------------------------------------------- /tutorial/public/font/fontello.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/font/fontello.woff2 -------------------------------------------------------------------------------- /tutorial/public/images/cluster.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/cluster.webp -------------------------------------------------------------------------------- /tutorial/public/images/eb-deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/eb-deploy.png -------------------------------------------------------------------------------- /tutorial/public/images/eb-docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/eb-docker.png -------------------------------------------------------------------------------- /tutorial/public/images/eb-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/eb-start.png -------------------------------------------------------------------------------- /tutorial/public/images/eb-start.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/eb-start.webp -------------------------------------------------------------------------------- /tutorial/public/images/interest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/interest.png -------------------------------------------------------------------------------- /tutorial/public/images/interest.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/interest.webp -------------------------------------------------------------------------------- /tutorial/public/images/keypair.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/keypair.webp -------------------------------------------------------------------------------- /tutorial/public/images/logo_192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/logo_192.png -------------------------------------------------------------------------------- /tutorial/public/images/logo_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/logo_512.png -------------------------------------------------------------------------------- /tutorial/src/images/eb-terminate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/eb-terminate.jpg -------------------------------------------------------------------------------- /tutorial/src/images/foodtrucks.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/foodtrucks.webp -------------------------------------------------------------------------------- /tutorial/src/images/logo-medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/logo-medium.png -------------------------------------------------------------------------------- /starlight/src/assets/eb-terminate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/eb-terminate.jpg -------------------------------------------------------------------------------- /starlight/src/assets/eb-terminate.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/starlight/src/assets/eb-terminate.webp -------------------------------------------------------------------------------- /static-site/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx 2 | 3 | COPY wrapper.sh / 4 | 5 | COPY html /usr/share/nginx/html 6 | 7 | CMD ["./wrapper.sh"] 8 | -------------------------------------------------------------------------------- /tutorial/public/images/eb-deploy.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/eb-deploy.webp -------------------------------------------------------------------------------- /tutorial/public/images/eb-docker.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/eb-docker.jpeg -------------------------------------------------------------------------------- /tutorial/public/images/eb-docker.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/eb-docker.webp -------------------------------------------------------------------------------- /tutorial/public/images/eb-terminate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/eb-terminate.jpg -------------------------------------------------------------------------------- /tutorial/public/images/foodtrucks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/foodtrucks.png -------------------------------------------------------------------------------- /tutorial/public/images/foodtrucks.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/foodtrucks.webp -------------------------------------------------------------------------------- /tutorial/public/images/logo-medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/logo-medium.png -------------------------------------------------------------------------------- /tutorial/public/images/logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/logo-small.png -------------------------------------------------------------------------------- /tutorial/src/images/eb-terminate.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/src/images/eb-terminate.webp -------------------------------------------------------------------------------- /tutorial/public/images/eb-terminate.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todo/docker-curriculum/master/tutorial/public/images/eb-terminate.webp -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.DS_Store 3 | tutorial/public/index.html 4 | tutorial/public/styles/dist/* 5 | tutorial/src/styles/dist/* 6 | .vscode 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Docker Curriculum 2 | === 3 | 4 | > Learn to build and deploy your distributed applications easily to the cloud with Docker 5 | 6 | Follow the curriculum on [docker-curriculum.com](https://docker-curriculum.com/) 7 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "verbose": true, 3 | "ignore": [ 4 | ".git", 5 | "node_modules", 6 | "_build" 7 | ], 8 | "watch": [ 9 | "tutorial/src/**/*.*", 10 | "tutorial/layouts/**/*.*" 11 | ], 12 | "env": { 13 | "NODE_ENV": "development" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /flask-app/Dockerrun.aws.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSEBDockerrunVersion": "1", 3 | "Image": { 4 | "Name": "prakhar1989/catnip", 5 | "Update": "true" 6 | }, 7 | "Ports": [ 8 | { 9 | "ContainerPort": 5000, 10 | "HostPort": 8000 11 | } 12 | ], 13 | "Logging": "/var/log/nginx" 14 | } 15 | -------------------------------------------------------------------------------- /starlight/src/content/config.ts: -------------------------------------------------------------------------------- 1 | import { defineCollection } from 'astro:content'; 2 | import { docsSchema, i18nSchema } from '@astrojs/starlight/schema'; 3 | 4 | export const collections = { 5 | docs: defineCollection({ schema: docsSchema() }), 6 | i18n: defineCollection({ type: 'data', schema: i18nSchema() }), 7 | }; 8 | -------------------------------------------------------------------------------- /starlight/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | # generated types 4 | .astro/ 5 | 6 | # dependencies 7 | node_modules/ 8 | 9 | # logs 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /flask-app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | 3 | # set a directory for the app 4 | WORKDIR /usr/src/app 5 | 6 | # copy all the files to the container 7 | COPY . . 8 | 9 | # install dependencies 10 | RUN pip install --no-cache-dir -r requirements.txt 11 | 12 | # tell the port number the container should expose 13 | EXPOSE 5000 14 | 15 | # run the command 16 | CMD ["python", "./app.py"] 17 | -------------------------------------------------------------------------------- /starlight/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starlight", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro build", 9 | "preview": "astro preview", 10 | "astro": "astro" 11 | }, 12 | "dependencies": { 13 | "@astrojs/starlight": "^0.9.0", 14 | "astro": "^3.0.6", 15 | "sharp": "^0.32.5" 16 | } 17 | } -------------------------------------------------------------------------------- /tutorial/public/images/twitter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tutorial/layouts/partials/analytics.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /starlight/src/content/docs/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Docker Curriculum 3 | description: Learn to build and deploy your distributed applications easily to the cloud with Docker 4 | template: splash 5 | hero: 6 | tagline: Learn to build and deploy your distributed applications easily to the cloud with Docker 7 | image: 8 | file: ../../assets/logo.webp 9 | actions: 10 | - text: Get Started 11 | link: /introduction/00-docker/ 12 | icon: right-arrow 13 | variant: primary 14 | --- -------------------------------------------------------------------------------- /tutorial/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Docker Curriculum", 3 | "short_name": "Docker Curriculum", 4 | "start_url": "/", 5 | "scope": "/", 6 | "display": "standalone", 7 | "background_color": "#08c", 8 | "theme_color": "#08c", 9 | "icons": [{ 10 | "src": "images/logo_192.png", 11 | "type": "image/png", 12 | "sizes": "192x192" 13 | }, { 14 | "src": "images/logo_512.png", 15 | "type": "image/png", 16 | "sizes": "512x512" 17 | }] 18 | } -------------------------------------------------------------------------------- /tutorial/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Docker Curriculum", 3 | "short_name": "Docker Curriculum", 4 | "start_url": "/", 5 | "scope": "/", 6 | "display": "standalone", 7 | "background_color": "#08c", 8 | "theme_color": "#08c", 9 | "icons": [{ 10 | "src": "images/logo_192.png", 11 | "type": "image/png", 12 | "sizes": "192x192" 13 | }, { 14 | "src": "images/logo_512.png", 15 | "type": "image/png", 16 | "sizes": "512x512" 17 | }] 18 | } -------------------------------------------------------------------------------- /tutorial/layouts/partials/sw.html: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /tutorial/README.md: -------------------------------------------------------------------------------- 1 | # Tutorial 2 | 3 | Static site content for [docker-curriculum.com](https://docker-curriculum.com) generated via [Metalsmith](http://www.metalsmith.io/) hosted on [Netlify](https://www.netlify.com/). 4 | 5 | ### Development 6 | 7 | ``` 8 | $ npm install 9 | $ npm run start 10 | ``` 11 | 12 | ### Contributions 13 | 14 | For any contributions, make sure you suggest changes to [src/index.md](https://github.com/prakhar1989/docker-curriculum/blob/master/tutorial/src/index.md) and not the generated file (`index.html`). 15 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues" 2 | on: 3 | schedule: 4 | - cron: "0 0 * * *" 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v3 11 | with: 12 | repo-token: ${{ secrets.GITHUB_TOKEN }} 13 | only-labels: 'waiting-for-response' 14 | stale-issue-message: 'This issue is stale because it has been open 14 days with no activity. Remove label or comment or this will be closed in 2 days' 15 | days-before-stale: 14 16 | days-before-close: 2 17 | -------------------------------------------------------------------------------- /tutorial/public/images/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: feature-request 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /starlight/src/content/docs/getting-started/00-getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | --- 4 | 5 | This document contains a series of several sections, each of which explains a particular aspect of Docker. In each section, we will be typing commands (or writing code). All the code used in the tutorial is available in the [Github repo](http://github.com/prakhar1989/docker-curriculum). 6 | 7 | > Note: This tutorial uses version **18.05.0-ce** of Docker. If you find any part of the tutorial incompatible with a future version, please raise an [issue](https://github.com/prakhar1989/docker-curriculum/issues). Thanks! -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/typo---grammatical-error---spelling-mistakes-etc-.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Typo / grammatical error / spelling mistakes etc. 3 | about: Report a grammatical error or a typo in the tutorial 4 | title: "[TYPO]" 5 | labels: typo 6 | assignees: prakhar1989 7 | 8 | --- 9 | 10 | For typos, grammatical errors etc, a pull-request would be awesome! Just edit [this file](https://github.com/prakhar1989/docker-curriculum/blob/master/tutorial/src/index.md) with the suggested changes and I'll review it asap. 11 | 12 | If for some reason, you are unable to send a pull-request, just paste the error and the correction. Thank you =) 13 | 14 | ### Error 15 | 16 | ### Correction 17 | -------------------------------------------------------------------------------- /starlight/public/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /starlight/src/content/docs/conclusion/00-conclusion.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Conclusion 3 | --- 4 | 5 | And that's a wrap! After a long, exhaustive but fun tutorial you are now ready to take the container world by storm! If you followed along till the very end then you should definitely be proud of yourself. You learned how to setup Docker, run your own containers, play with static and dynamic websites and most importantly got hands on experience with deploying your applications to the cloud! 6 | 7 | I hope that finishing this tutorial makes you more confident in your abilities to deal with servers. When you have an idea of building your next app, you can be sure that you'll be able to get it in front of people with minimal effort. -------------------------------------------------------------------------------- /starlight/src/content/docs/introduction/03-what-will-i-learn.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: What will this tutorial teach me? 3 | --- 4 | 5 | This tutorial aims to be the one-stop shop for getting your hands dirty with Docker. Apart from demystifying the Docker landscape, it'll give you hands-on experience with building and deploying your own webapps on the Cloud. We'll be using [Amazon Web Services](http://aws.amazon.com) to deploy a static website, and two dynamic webapps on [EC2](https://aws.amazon.com/ec2/) using [Elastic Beanstalk](https://aws.amazon.com/elasticbeanstalk/) and [Elastic Container Service](https://aws.amazon.com/ecs/). Even if you have no prior experience with deployments, this tutorial should be all you need to get started. -------------------------------------------------------------------------------- /starlight/src/content/docs/getting-started/01-prerequisites.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Prerequisites 3 | --- 4 | 5 | There are no specific skills needed for this tutorial beyond a basic comfort with the command line and using a text editor. This tutorial uses `git clone` to clone the repository locally. If you don't have Git installed on your system, either install it or remember to manually download the zip files from Github. Prior experience in developing web applications will be helpful but is not required. As we proceed further along the tutorial, we'll make use of a few cloud services. If you're interested in following along, please create an account on each of these websites: 6 | 7 | - [Amazon Web Services](http://aws.amazon.com/) 8 | - [Docker Hub](https://hub.docker.com/) -------------------------------------------------------------------------------- /flask-app/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 21 |
22 |

Cat Gif of the day

23 | 24 |

Courtesy: Buzzfeed

25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Desktop] 28 | - Docker Version [e.g. 22] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /starlight/src/content/docs/conclusion/02-give-feedback.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Give Feedback 3 | --- 4 | 5 | Now that the tutorial is over, it's my turn to ask questions. How did you like the tutorial? Did you find the tutorial to be a complete mess or did you have fun and learn something? 6 | 7 | Send in your thoughts directly to [me](mailto:prakhar@prakhar.me) or just [create an issue](https://github.com/prakhar1989/docker-curriculum/issues/new). I'm on [Twitter](https://twitter.com/prakharsriv9), too, so if that's your deal, feel free to holler there! 8 | 9 | I would totally love to hear about your experience with this tutorial. Give suggestions on how to make this better or let me know about my mistakes. I want this tutorial to be one of the best introductory tutorials on the web and I can't do it without your help. 10 | -------------------------------------------------------------------------------- /tutorial/public/sw.js: -------------------------------------------------------------------------------- 1 | importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.4.1/workbox-sw.js'); 2 | 3 | workbox.routing.registerRoute( 4 | new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'), 5 | workbox.strategies.cacheFirst({ 6 | cacheName: 'google-fonts', 7 | plugins: [ 8 | new workbox.expiration.Plugin({ 9 | maxEntries: 30, 10 | }), 11 | new workbox.cacheableResponse.Plugin({ 12 | statuses: [0, 200] 13 | }), 14 | ], 15 | }), 16 | ); 17 | 18 | workbox.routing.registerRoute( 19 | /\.(?:png|gif|jpg|jpeg|svg)$/, 20 | workbox.strategies.cacheFirst({ 21 | cacheName: 'images', 22 | plugins: [ 23 | new workbox.expiration.Plugin({ 24 | maxEntries: 60, 25 | maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days 26 | }), 27 | ], 28 | }), 29 | ); 30 | -------------------------------------------------------------------------------- /tutorial/src/sw.js: -------------------------------------------------------------------------------- 1 | importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.4.1/workbox-sw.js'); 2 | 3 | workbox.routing.registerRoute( 4 | new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'), 5 | workbox.strategies.cacheFirst({ 6 | cacheName: 'google-fonts', 7 | plugins: [ 8 | new workbox.expiration.Plugin({ 9 | maxEntries: 30, 10 | }), 11 | new workbox.cacheableResponse.Plugin({ 12 | statuses: [0, 200] 13 | }), 14 | ], 15 | }), 16 | ); 17 | 18 | workbox.routing.registerRoute( 19 | /\.(?:png|gif|jpg|jpeg|svg)$/, 20 | workbox.strategies.cacheFirst({ 21 | cacheName: 'images', 22 | plugins: [ 23 | new workbox.expiration.Plugin({ 24 | maxEntries: 60, 25 | maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days 26 | }), 27 | ], 28 | }), 29 | ); 30 | -------------------------------------------------------------------------------- /starlight/src/content/docs/introduction/01-what-are-containers.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: What are containers? 3 | --- 4 | 5 | The industry standard today is to use Virtual Machines (VMs) to run software applications. VMs run applications inside a guest Operating System, which runs on virtual hardware powered by the server’s host OS. 6 | 7 | VMs are great at providing full process isolation for applications: there are very few ways a problem in the host operating system can affect the software running in the guest operating system, and vice-versa. But this isolation comes at great cost — the computational overhead spent virtualizing hardware for a guest OS to use is substantial. 8 | 9 | Containers take a different approach: by leveraging the low-level mechanics of the host operating system, containers provide most of the isolation of virtual machines at a fraction of the computing power. -------------------------------------------------------------------------------- /starlight/src/content/docs/introduction/00-docker.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: What is Docker? 3 | --- 4 | 5 | Wikipedia defines [Docker](https://www.docker.com/) as 6 | 7 | > an open-source project that automates the deployment of software applications inside **containers** by providing an additional layer of abstraction and automation of **OS-level virtualization** on Linux. 8 | 9 | Wow! That's a mouthful. In simpler words, Docker is a tool that allows developers, sys-admins etc. to easily deploy their applications in a sandbox (called _containers_) to run on the host operating system i.e. Linux. The key benefit of Docker is that it allows users to **package an application with all of its dependencies into a standardized unit** for software development. Unlike virtual machines, containers do not have high overhead and hence enable more efficient usage of the underlying system and resources. 10 | -------------------------------------------------------------------------------- /tutorial/layouts/partials/masthead.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Docker for Beginners logo 4 | 5 |

6 | Learn to build and deploy your distributed applications easily to the cloud with Docker 7 |

8 |

9 | Written and developed by Prakhar Srivastav 10 |

11 | 21 | -------------------------------------------------------------------------------- /starlight/src/content/docs/getting-started/02-setting-up.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Setting up your computer 3 | --- 4 | 5 | Getting all the tooling setup on your computer can be a daunting task, but thankfully as Docker has become stable, getting Docker up and running on your favorite OS has become very easy. 6 | 7 | Until a few releases ago, running Docker on OSX and Windows was quite a hassle. Lately however, Docker has invested significantly into improving the on-boarding experience for its users on these OSes, thus running Docker now is a cakewalk. The _getting started_ guide on Docker has detailed instructions for setting up Docker on [Mac](https://docs.docker.com/docker-for-mac/install), [Linux](https://docs.docker.com/install/linux/docker-ce/ubuntu) and [Windows](https://docs.docker.com/docker-for-windows/install). 8 | 9 | Once you are done installing Docker, test your Docker installation by running the following: 10 | 11 | ```bash 12 | $ docker run hello-world 13 | 14 | Hello from Docker. 15 | This message shows that your installation appears to be working correctly. 16 | ... 17 | ``` -------------------------------------------------------------------------------- /starlight/src/content/docs/conclusion/01-next-steps.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Next Steps 3 | --- 4 | 5 | Your journey into the container world has just started! My goal with this tutorial was to whet your appetite and show you the power of Docker. In the sea of new technology, it can be hard to navigate the waters alone and tutorials such as this one can provide a helping hand. This is the Docker tutorial I wish I had when I was starting out. Hopefully, it served its purpose of getting you excited about containers so that you no longer have to watch the action from the sides. 6 | 7 | Below are a few additional resources that will be beneficial. For your next project, I strongly encourage you to use Docker. Keep in mind - practice makes perfect! 8 | 9 | **Additional Resources** 10 | 11 | - [Awesome Docker](https://github.com/veggiemonk/awesome-docker) 12 | - [Why Docker](https://blog.codeship.com/why-docker/) 13 | - [Docker Weekly](https://www.docker.com/newsletter-subscription) and [archives](https://blog.docker.com/docker-weekly-archives/) 14 | - [Codeship Blog](https://blog.codeship.com/) 15 | 16 | Off you go, young padawan! -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Prakhar Srivastav 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 | -------------------------------------------------------------------------------- /starlight/src/content/docs/webapps-with-docker/02-our-first-image.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Our First Image 3 | --- 4 | 5 | Now that we have a better understanding of images, it's time to create our own. Our goal in this section will be to create an image that sandboxes a simple [Flask](http://flask.pocoo.org) application. For the purposes of this workshop, I've already created a fun little [Flask app](https://github.com/prakhar1989/docker-curriculum/tree/master/flask-app) that displays a random cat `.gif` every time it is loaded - because you know, who doesn't like cats? If you haven't already, please go ahead and clone the repository locally like so - 6 | 7 | ```bash 8 | $ git clone https://github.com/prakhar1989/docker-curriculum.git 9 | $ cd docker-curriculum/flask-app 10 | ``` 11 | 12 | > This should be cloned on the machine where you are running the docker commands and _not_ inside a docker container. 13 | 14 | The next step now is to create an image with this web app. As mentioned above, all user images are based on a base image. Since our application is written in Python, the base image we're going to use will be [Python 3](https://hub.docker.com/_/python/). -------------------------------------------------------------------------------- /starlight/src/content/docs/introduction/02-why-use-containers.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Why use containers? 3 | --- 4 | 5 | Containers offer a logical packaging mechanism in which applications can be abstracted from the environment in which they actually run. This decoupling allows container-based applications to be deployed easily and consistently, regardless of whether the target environment is a private data center, the public cloud, or even a developer’s personal laptop. This gives developers the ability to create predictable environments that are isolated from the rest of the applications and can be run anywhere. 6 | 7 | From an operations standpoint, apart from portability containers also give more granular control over resources giving your infrastructure improved efficiency which can result in better utilization of your compute resources. 8 | 9 | Due to these benefits, containers (& Docker) have seen widespread adoption. Companies like Google, Facebook, Netflix and Salesforce leverage containers to make large engineering teams more productive and to improve utilization of compute resources. In fact, Google credited containers for eliminating the need for an entire data center. -------------------------------------------------------------------------------- /tutorial/index.js: -------------------------------------------------------------------------------- 1 | var Metalsmith = require('metalsmith'); 2 | var markdown = require('metalsmith-markdown'); 3 | var layouts = require('metalsmith-layouts'); 4 | var permalinks = require('metalsmith-permalinks'); 5 | var metallic = require('metalsmith-metallic'); 6 | var watch = require('metalsmith-watch'); 7 | 8 | Metalsmith(__dirname) 9 | .metadata({ 10 | title: "A Docker Tutorial for Beginners", 11 | description: "Learn to build and deploy your distributed applications easily to the cloud with Docker", 12 | author: "Prakhar Srivastav", 13 | url: "https://docker-curriculum.com/", 14 | logo: "https://docker-curriculum.com/images/logo-small.png", 15 | }) 16 | .source('./src') 17 | .destination('./public') 18 | .clean(false) 19 | .use(metallic()) 20 | .use(markdown()) 21 | .use(permalinks()) 22 | .use(layouts({ 23 | engine: 'handlebars', 24 | partials: { 25 | analytics: 'partials/analytics', 26 | sw: 'partials/sw', 27 | meta: 'partials/meta', 28 | masthead: 'partials/masthead', 29 | nav: 'partials/nav', 30 | } 31 | })) 32 | .build(function(err, files) { 33 | if (err) { throw err; } 34 | }); 35 | -------------------------------------------------------------------------------- /tutorial/layouts/partials/meta.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /starlight/src/content/docs/hello-world/00-busybox.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Playing With Busybox 3 | --- 4 | 5 | Now that we have everything setup, it's time to get our hands dirty. In this section, we are going to run a [Busybox](https://en.wikipedia.org/wiki/BusyBox) container on our system and get a taste of the `docker run` command. 6 | 7 | To get started, let's run the following in our terminal: 8 | 9 | ```bash 10 | $ docker pull busybox 11 | ``` 12 | 13 | > Note: Depending on how you've installed docker on your system, you might see a `permission denied` error after running the above command. If you're on a Mac, make sure the Docker engine is running. If you're on Linux, then prefix your `docker` commands with `sudo`. Alternatively, you can [create a docker group](https://docs.docker.com/engine/installation/linux/linux-postinstall/) to get rid of this issue. 14 | 15 | The `pull` command fetches the busybox [**image**](https://hub.docker.com/_/busybox/) from the [**Docker registry**](https://hub.docker.com/explore/) and saves it to our system. You can use the `docker images` command to see a list of all images on your system. 16 | 17 | ```bash 18 | $ docker images 19 | REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 20 | busybox latest c51f86c28340 4 weeks ago 1.109 MB 21 | ``` -------------------------------------------------------------------------------- /starlight/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'astro/config'; 2 | import starlight from '@astrojs/starlight'; 3 | 4 | // https://astro.build/config 5 | export default defineConfig({ 6 | integrations: [ 7 | starlight({ 8 | title: 'Docker Curriculum', 9 | social: { 10 | github: 'https://github.com/prakhar1989/docker-curriculum', 11 | }, 12 | editLink: { 13 | baseUrl: 'https://github.com/prakhar/docker-curriculum/edit/main/starlight/', 14 | }, 15 | sidebar: [ 16 | { 17 | label: 'Introduction', 18 | autogenerate: { directory: 'introduction' }, 19 | }, 20 | { 21 | label: 'Getting Started', 22 | autogenerate: { directory: 'getting-started' }, 23 | }, 24 | { 25 | label: 'Hello World', 26 | autogenerate: { directory: 'hello-world' }, 27 | }, 28 | { 29 | label: 'Webapps with Docker', 30 | autogenerate: { directory: 'webapps-with-docker' }, 31 | }, 32 | { 33 | label: 'Multi-Container Environments', 34 | autogenerate: { directory: 'multi-container-environments' }, 35 | }, 36 | { 37 | label: 'Conclusion', 38 | autogenerate: { directory: 'conclusion' }, 39 | }, 40 | ], 41 | }), 42 | ], 43 | 44 | // Process images with sharp: https://docs.astro.build/en/guides/assets/#using-sharp 45 | image: { service: { entrypoint: 'astro/assets/services/sharp' } }, 46 | }); 47 | -------------------------------------------------------------------------------- /starlight/src/content/docs/hello-world/02-terminlogy.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Terminology 3 | --- 4 | 5 | In the last section, we used a lot of Docker-specific jargon which might be confusing to some. So before we go further, let me clarify some terminology that is used frequently in the Docker ecosystem. 6 | 7 | - _Images_ - The blueprints of our application which form the basis of containers. In the demo above, we used the `docker pull` command to download the **busybox** image. 8 | - _Containers_ - Created from Docker images and run the actual application. We create a container using `docker run` which we did using the busybox image that we downloaded. A list of running containers can be seen using the `docker ps` command. 9 | - _Docker Daemon_ - The background service running on the host that manages building, running and distributing Docker containers. The daemon is the process that runs in the operating system which clients talk to. 10 | - _Docker Client_ - The command line tool that allows the user to interact with the daemon. More generally, there can be other forms of clients too - such as [Kitematic](https://kitematic.com/) which provide a GUI to the users. 11 | - _Docker Hub_ - A [registry](https://hub.docker.com/explore/) of Docker images. You can think of the registry as a directory of all available Docker images. If required, one can host their own Docker registries and can use them for pulling images. -------------------------------------------------------------------------------- /tutorial/layouts/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {{title}} 18 | {{>meta}} 19 | 20 | 21 | 22 | 23 |
24 |
{{>masthead}}
25 | {{{contents}}} 26 |
27 | {{>analytics}} 28 | {{>sw}} 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docker-curriculum", 3 | "version": "0.0.1", 4 | "description": " # Docker for Beginners", 5 | "main": "index.js", 6 | "scripts": { 7 | "css": "sass tutorial/src/styles/main.scss tutorial/src/styles/dist/styles.css", 8 | "generate": "npm run css && node tutorial/index.js", 9 | "serve": "browser-sync start -s tutorial/public --no-ui --files tutorial/public --index index.html", 10 | "watch": "nodemon tutorial/index.js", 11 | "css-watch": "sass --watch tutorial/src/styles/main.scss tutorial/src/styles/dist/styles.css", 12 | "start": "npm run watch | npm run css-watch | npm run serve" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/prakhar1989/docker-curriculum.git" 17 | }, 18 | "author": "Prakhar Srivastav ", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/prakhar1989/docker-curriculum/issues" 22 | }, 23 | "homepage": "https://github.com/prakhar1989/docker-curriculum#readme", 24 | "dependencies": { 25 | "handlebars": "^4.7.7", 26 | "metalsmith": "^2.1.0", 27 | "metalsmith-layouts": "^1.4.1", 28 | "metalsmith-markdown": "^0.2.2", 29 | "metalsmith-metallic": "^2.0.2", 30 | "metalsmith-permalinks": "^0.5.0", 31 | "metalsmith-watch": "^1.0.3", 32 | "sass": "^1.26.3" 33 | }, 34 | "devDependencies": { 35 | "browser-sync": "^2.26.7", 36 | "nodemon": "^1.18.3", 37 | "write-good": "^1.0.8" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tutorial/src/font/fontello.svg: -------------------------------------------------------------------------------- 1 | Copyright (C) 2018 by original authors @ fontello.com -------------------------------------------------------------------------------- /tutorial/public/font/fontello.svg: -------------------------------------------------------------------------------- 1 | Copyright (C) 2018 by original authors @ fontello.com -------------------------------------------------------------------------------- /static-site/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Docker :) 9 | 10 | 11 | 12 | 14 | 15 | 16 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 27 | 28 | 29 | 30 | 31 | 32 | 34 |
35 |
36 |
37 |

Hello Docker!

38 |

This is being served from a docker container running Nginx.

39 |
40 |
41 |
42 | 43 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /starlight/src/content/docs/multi-container-environments/00-introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | --- 4 | 5 | In the last section, we saw how easy and fun it is to run applications with Docker. We started with a simple static website and then tried a Flask app. Both of which we could run locally and in the cloud with just a few commands. One thing both these apps had in common was that they were running in a **single container**. 6 | 7 | Those of you who have experience running services in production know that usually apps nowadays are not that simple. There's almost always a database (or any other kind of persistent storage) involved. Systems such as [Redis](http://redis.io/) and [Memcached](http://memcached.org/) have become _de rigueur_ of most web application architectures. Hence, in this section we are going to spend some time learning how to Dockerize applications which rely on different services to run. 8 | 9 | In particular, we are going to see how we can run and manage **multi-container** docker environments. Why multi-container you might ask? Well, one of the key points of Docker is the way it provides isolation. The idea of bundling a process with its dependencies in a sandbox (called containers) is what makes this so powerful. 10 | 11 | Just like it's a good strategy to decouple your application tiers, it is wise to keep containers for each of the **services** separate. Each tier is likely to have different resource needs and those needs might grow at different rates. By separating the tiers into different containers, we can compose each tier using the most appropriate instance type based on different resource needs. This also plays in very well with the whole [microservices](http://martinfowler.com/articles/microservices.html) movement which is one of the main reasons why Docker (or any other container technology) is at the [forefront](https://medium.com/aws-activate-startup-blog/using-containers-to-build-a-microservices-architecture-6e1b8bacb7d1#.xl3wryr5z) of modern microservices architectures. 12 | -------------------------------------------------------------------------------- /flask-app/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template 2 | import os 3 | import random 4 | 5 | app = Flask(__name__) 6 | 7 | # list of cat images 8 | images = [ 9 | "https://firebasestorage.googleapis.com/v0/b/docker-curriculum.appspot.com/o/catnip%2F0.gif?alt=media&token=0fff4b31-b3d8-44fb-be39-723f040e57fb", 10 | "https://firebasestorage.googleapis.com/v0/b/docker-curriculum.appspot.com/o/catnip%2F1.gif?alt=media&token=2328c855-572f-4a10-af8c-23a6e1db574c", 11 | "https://firebasestorage.googleapis.com/v0/b/docker-curriculum.appspot.com/o/catnip%2F10.gif?alt=media&token=647fd422-c8d1-4879-af3e-fea695da79b2", 12 | "https://firebasestorage.googleapis.com/v0/b/docker-curriculum.appspot.com/o/catnip%2F11.gif?alt=media&token=900cce1f-55c0-4e02-80c6-ee587d1e9b6e", 13 | "https://firebasestorage.googleapis.com/v0/b/docker-curriculum.appspot.com/o/catnip%2F2.gif?alt=media&token=8a108bd4-8dfc-4dbc-9b8c-0db0e626f65b", 14 | "https://firebasestorage.googleapis.com/v0/b/docker-curriculum.appspot.com/o/catnip%2F3.gif?alt=media&token=4e270d85-0be3-4048-99bd-696ece8070ea", 15 | "https://firebasestorage.googleapis.com/v0/b/docker-curriculum.appspot.com/o/catnip%2F4.gif?alt=media&token=e7daf297-e615-4dfc-aa19-bee959204774", 16 | "https://firebasestorage.googleapis.com/v0/b/docker-curriculum.appspot.com/o/catnip%2F5.gif?alt=media&token=a8e472e6-94da-45f9-aab8-d51ec499e5ed", 17 | "https://firebasestorage.googleapis.com/v0/b/docker-curriculum.appspot.com/o/catnip%2F7.gif?alt=media&token=9e449089-9f94-4002-a92a-3e44c6bd18a9", 18 | "https://firebasestorage.googleapis.com/v0/b/docker-curriculum.appspot.com/o/catnip%2F8.gif?alt=media&token=80a48714-7aaa-45fa-a36b-a7653dc3292b", 19 | "https://firebasestorage.googleapis.com/v0/b/docker-curriculum.appspot.com/o/catnip%2F9.gif?alt=media&token=a57a1c71-a8af-4170-8fee-bfe11809f0b3", 20 | ] 21 | 22 | 23 | @app.route("/") 24 | def index(): 25 | url = random.choice(images) 26 | return render_template("index.html", url=url) 27 | 28 | 29 | if __name__ == "__main__": 30 | app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000))) 31 | -------------------------------------------------------------------------------- /starlight/README.md: -------------------------------------------------------------------------------- 1 | # Starlight Starter Kit: Basics 2 | 3 | ``` 4 | npm create astro@latest -- --template starlight 5 | ``` 6 | 7 | [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics) 8 | [![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/starlight/tree/main/examples/basics) 9 | 10 | > 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! 11 | 12 | ## 🚀 Project Structure 13 | 14 | Inside of your Astro + Starlight project, you'll see the following folders and files: 15 | 16 | ``` 17 | . 18 | ├── public/ 19 | ├── src/ 20 | │ ├── assets/ 21 | │ ├── content/ 22 | │ │ ├── docs/ 23 | │ │ └── config.ts 24 | │ └── env.d.ts 25 | ├── astro.config.mjs 26 | ├── package.json 27 | └── tsconfig.json 28 | ``` 29 | 30 | Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name. 31 | 32 | Images can be added to `src/assets/` and embedded in Markdown with a relative link. 33 | 34 | Static assets, like favicons, can be placed in the `public/` directory. 35 | 36 | ## 🧞 Commands 37 | 38 | All commands are run from the root of the project, from a terminal: 39 | 40 | | Command | Action | 41 | | :------------------------ | :----------------------------------------------- | 42 | | `npm install` | Installs dependencies | 43 | | `npm run dev` | Starts local dev server at `localhost:4321` | 44 | | `npm run build` | Build your production site to `./dist/` | 45 | | `npm run preview` | Preview your build locally, before deploying | 46 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | 47 | | `npm run astro -- --help` | Get help using the Astro CLI | 48 | 49 | ## 👀 Want to learn more? 50 | 51 | Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat). 52 | -------------------------------------------------------------------------------- /starlight/src/content/docs/webapps-with-docker/01-docker-images.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Docker Images 3 | --- 4 | 5 | We've looked at images before, but in this section we'll dive deeper into what Docker images are and build our own image! Lastly, we'll also use that image to run our application locally and finally deploy on [AWS](http://aws.amazon.com) to share it with our friends! Excited? Great! Let's get started. 6 | 7 | Docker images are the basis of containers. In the previous example, we **pulled** the _Busybox_ image from the registry and asked the Docker client to run a container **based** on that image. To see the list of images that are available locally, use the `docker images` command. 8 | 9 | ```bash 10 | $ docker images 11 | REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 12 | prakhar1989/catnip latest c7ffb5626a50 2 hours ago 697.9 MB 13 | prakhar1989/static-site latest b270625a1631 21 hours ago 133.9 MB 14 | python 3-onbuild cf4002b2c383 5 days ago 688.8 MB 15 | martin/docker-cleanup-volumes latest b42990daaca2 7 weeks ago 22.14 MB 16 | ubuntu latest e9ae3c220b23 7 weeks ago 187.9 MB 17 | busybox latest c51f86c28340 9 weeks ago 1.109 MB 18 | hello-world latest 0a6ba66e537a 11 weeks ago 960 B 19 | ``` 20 | 21 | The above gives a list of images that I've pulled from the registry, along with ones that I've created myself (we'll shortly see how). The `TAG` refers to a particular snapshot of the image and the `IMAGE ID` is the corresponding unique identifier for that image. 22 | 23 | For simplicity, you can think of an image akin to a git repository - images can be [committed](https://docs.docker.com/engine/reference/commandline/commit/) with changes and have multiple versions. If you don't provide a specific version number, the client defaults to `latest`. For example, you can pull a specific version of `ubuntu` image 24 | 25 | ```bash 26 | $ docker pull ubuntu:18.04 27 | ``` 28 | 29 | To get a new Docker image you can either get it from a registry (such as the Docker Hub) or create your own. There are tens of thousands of images available on [Docker Hub](https://hub.docker.com/explore/). You can also search for images directly from the command line using `docker search`. 30 | 31 | An important distinction to be aware of when it comes to images is the difference between base and child images. 32 | 33 | - **Base images** are images that have no parent image, usually images with an OS like ubuntu, busybox or debian. 34 | 35 | - **Child images** are images that build on base images and add additional functionality. 36 | 37 | Then there are official and user images, which can be both base and child images. 38 | 39 | - **Official images** are images that are officially maintained and supported by the folks at Docker. These are typically one word long. In the list of images above, the `python`, `ubuntu`, `busybox` and `hello-world` images are official images. 40 | 41 | - **User images** are images created and shared by users like you and me. They build on base images and add additional functionality. Typically, these are formatted as `user/image-name`. 42 | -------------------------------------------------------------------------------- /starlight/src/content/docs/webapps-with-docker/00-static-sites.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Static Sites 3 | --- 4 | 5 | Great! So we have now looked at `docker run`, played with a Docker container and also got a hang of some terminology. Armed with all this knowledge, we are now ready to get to the real-stuff, i.e. deploying web applications with Docker! 6 | 7 | ### Static Sites 8 | 9 | Let's start by taking baby-steps. The first thing we're going to look at is how we can run a dead-simple static website. We're going to pull a Docker image from Docker Hub, run the container and see how easy it is to run a webserver. 10 | 11 | Let's begin. The image that we are going to use is a single-page [website](http://github.com/prakhar1989/docker-curriculum) that I've already created for the purpose of this demo and hosted on the [registry](https://hub.docker.com/r/prakhar1989/static-site/) - `prakhar1989/static-site`. We can download and run the image directly in one go using `docker run`. As noted above, the `--rm` flag automatically removes the container when it exits and the `-it` flag specifies an interactive terminal which makes it easier to kill the container with Ctrl+C (on windows). 12 | 13 | ```bash 14 | $ docker run --rm -it prakhar1989/static-site 15 | ``` 16 | 17 | Since the image doesn't exist locally, the client will first fetch the image from the registry and then run the image. If all goes well, you should see a `Nginx is running...` message in your terminal. Okay now that the server is running, how to see the website? What port is it running on? And more importantly, how do we access the container directly from our host machine? Hit Ctrl+C to stop the container. 18 | 19 | Well, in this case, the client is not exposing any ports so we need to re-run the `docker run` command to publish ports. While we're at it, we should also find a way so that our terminal is not attached to the running container. This way, you can happily close your terminal and keep the container running. This is called **detached** mode. 20 | 21 | ```bash 22 | $ docker run -d -P --name static-site prakhar1989/static-site 23 | e61d12292d69556eabe2a44c16cbd54486b2527e2ce4f95438e504afb7b02810 24 | ``` 25 | 26 | In the above command, `-d` will detach our terminal, `-P` will publish all exposed ports to random ports and finally `--name` corresponds to a name we want to give. Now we can see the ports by running the `docker port [CONTAINER]` command 27 | 28 | ```bash 29 | $ docker port static-site 30 | 80/tcp -> 0.0.0.0:32769 31 | 443/tcp -> 0.0.0.0:32768 32 | ``` 33 | 34 | You can open [http://localhost:32769](http://localhost:32769) in your browser. 35 | 36 | > Note: If you're using docker-toolbox, then you might need to use `docker-machine ip default` to get the IP. 37 | 38 | You can also specify a custom port to which the client will forward connections to the container. 39 | 40 | ```bash 41 | $ docker run -p 8888:80 prakhar1989/static-site 42 | Nginx is running... 43 | ``` 44 | 45 | ![static site](../../../assets/static.webp) 46 | 47 | To stop a detached container, run `docker stop` by giving the container ID. In this case, we can use the name `static-site` we used to start the container. 48 | 49 | ```bash 50 | $ docker stop static-site 51 | static-site 52 | ``` 53 | 54 | I'm sure you agree that was super simple. To deploy this on a real server you would just need to install Docker, and run the above Docker command. Now that you've seen how to run a webserver inside a Docker image, you must be wondering - how do I create my own Docker image? This is the question we'll be exploring in the next section. -------------------------------------------------------------------------------- /tutorial/layouts/partials/nav.html: -------------------------------------------------------------------------------- 1 | × 2 |
    3 |
  1. 4 | Introduction 5 | 19 |
  2. 20 |
  3. 21 | Getting Started 22 | 30 |
  4. 31 |
  5. 32 | Hello World 33 | 44 |
  6. 45 |
  7. 46 | Webapps with Docker 47 | 64 |
  8. 65 |
  9. 66 | Multi Container Environments 67 | 84 |
  10. 85 |
  11. 86 | Conclusion 87 | 95 |
  12. 96 |
97 | -------------------------------------------------------------------------------- /starlight/src/content/docs/webapps-with-docker/03-dockerfile.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Dockerfile 3 | --- 4 | 5 | A [Dockerfile](https://docs.docker.com/engine/reference/builder/) is a simple text file that contains a list of commands that the Docker client calls while creating an image. It's a simple way to automate the image creation process. The best part is that the [commands](https://docs.docker.com/engine/reference/builder/#from) you write in a Dockerfile are _almost_ identical to their equivalent Linux commands. This means you don't really have to learn new syntax to create your own dockerfiles. 6 | 7 | The application directory does contain a Dockerfile but since we're doing this for the first time, we'll create one from scratch. To start, create a new blank file in our favorite text-editor and save it in the **same** folder as the flask app by the name of `Dockerfile`. 8 | 9 | We start with specifying our base image. Use the `FROM` keyword to do that - 10 | 11 | ```dockerfile 12 | FROM python:3.8 13 | ``` 14 | 15 | The next step usually is to write the commands of copying the files and installing the dependencies. First, we set a working directory and then copy all the files for our app. 16 | 17 | ```dockerfile 18 | # set a directory for the app 19 | WORKDIR /usr/src/app 20 | 21 | # copy all the files to the container 22 | COPY . . 23 | ``` 24 | 25 | Now, that we have the files, we can install the dependencies. 26 | 27 | ```dockerfile 28 | # install dependencies 29 | RUN pip install --no-cache-dir -r requirements.txt 30 | ``` 31 | 32 | The next thing we need to specify is the port number that needs to be exposed. Since our flask app is running on port `5000`, that's what we'll indicate. 33 | 34 | ```dockerfile 35 | EXPOSE 5000 36 | ``` 37 | 38 | The last step is to write the command for running the application, which is simply - `python ./app.py`. We use the [CMD](https://docs.docker.com/engine/reference/builder/#cmd) command to do that - 39 | 40 | ```dockerfile 41 | CMD ["python", "./app.py"] 42 | ``` 43 | 44 | The primary purpose of `CMD` is to tell the container which command it should run when it is started. With that, our `Dockerfile` is now ready. This is how it looks - 45 | 46 | ```dockerfile 47 | FROM python:3.8 48 | 49 | # set a directory for the app 50 | WORKDIR /usr/src/app 51 | 52 | # copy all the files to the container 53 | COPY . . 54 | 55 | # install dependencies 56 | RUN pip install --no-cache-dir -r requirements.txt 57 | 58 | # define the port number the container should expose 59 | EXPOSE 5000 60 | 61 | # run the command 62 | CMD ["python", "./app.py"] 63 | ``` 64 | 65 | Now that we have our `Dockerfile`, we can build our image. The `docker build` command does the heavy-lifting of creating a Docker image from a `Dockerfile`. 66 | 67 | The section below shows you the output of running the same. Before you run the command yourself (don't forget the period), make sure to replace my username with yours. This username should be the same one you created when you registered on [Docker hub](https://hub.docker.com). If you haven't done that yet, please go ahead and create an account. The `docker build` command is quite simple - it takes an optional tag name with `-t` and a location of the directory containing the `Dockerfile`. 68 | 69 | ```bash 70 | $ docker build -t yourusername/catnip . 71 | Sending build context to Docker daemon 8.704 kB 72 | Step 1 : FROM python:3.8 73 | # Executing 3 build triggers... 74 | Step 1 : COPY requirements.txt /usr/src/app/ 75 | ---> Using cache 76 | Step 1 : RUN pip install --no-cache-dir -r requirements.txt 77 | ---> Using cache 78 | Step 1 : COPY . /usr/src/app 79 | ---> 1d61f639ef9e 80 | Removing intermediate container 4de6ddf5528c 81 | Step 2 : EXPOSE 5000 82 | ---> Running in 12cfcf6d67ee 83 | ---> f423c2f179d1 84 | Removing intermediate container 12cfcf6d67ee 85 | Step 3 : CMD python ./app.py 86 | ---> Running in f01401a5ace9 87 | ---> 13e87ed1fbc2 88 | Removing intermediate container f01401a5ace9 89 | Successfully built 13e87ed1fbc2 90 | ``` 91 | 92 | If you don't have the `python:3.8` image, the client will first pull the image and then create your image. Hence, your output from running the command will look different from mine. If everything went well, your image should be ready! Run `docker images` and see if your image shows. 93 | 94 | The last step in this section is to run the image and see if it actually works (replacing my username with yours). 95 | 96 | ```bash 97 | $ docker run -p 8888:5000 yourusername/catnip 98 | * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) 99 | ``` 100 | 101 | The command we just ran used port 5000 for the server inside the container and exposed this externally on port 8888. Head over to the URL with port 8888, where your app should be live. 102 | 103 | ![cat gif website](../../../assets/catgif.webp) 104 | 105 | Congratulations! You have successfully created your first docker image. 106 | -------------------------------------------------------------------------------- /starlight/src/content/docs/hello-world/01-docker-run.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Docker Run 3 | --- 4 | 5 | Great! Let's now run a Docker **container** based on this image. To do that we are going to use the almighty `docker run` command. 6 | 7 | ```bash 8 | $ docker run busybox 9 | $ 10 | ``` 11 | 12 | Wait, nothing happened! Is that a bug? Well, no. Behind the scenes, a lot of stuff happened. When you call `run`, the Docker client finds the image (busybox in this case), loads up the container and then runs a command in that container. When we run `docker run busybox`, we didn't provide a command, so the container booted up, ran an empty command and then exited. Well, yeah - kind of a bummer. Let's try something more exciting. 13 | 14 | ```bash 15 | $ docker run busybox echo "hello from busybox" 16 | hello from busybox 17 | ``` 18 | 19 | Nice - finally we see some output. In this case, the Docker client dutifully ran the `echo` command in our busybox container and then exited it. If you've noticed, all of that happened pretty quickly. Imagine booting up a virtual machine, running a command and then killing it. Now you know why they say containers are fast! Ok, now it's time to see the `docker ps` command. The `docker ps` command shows you all containers that are currently running. 20 | 21 | ```bash 22 | $ docker ps 23 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 24 | ``` 25 | 26 | Since no containers are running, we see a blank line. Let's try a more useful variant: `docker ps -a` 27 | 28 | ```bash 29 | $ docker ps -a 30 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 31 | 305297d7a235 busybox "uptime" 11 minutes ago Exited (0) 11 minutes ago distracted_goldstine 32 | ff0a5c3750b9 busybox "sh" 12 minutes ago Exited (0) 12 minutes ago elated_ramanujan 33 | 14e5bd11d164 hello-world "/hello" 2 minutes ago Exited (0) 2 minutes ago thirsty_euclid 34 | ``` 35 | 36 | So what we see above is a list of all containers that we ran. Do notice that the `STATUS` column shows that these containers exited a few minutes ago. 37 | 38 | You're probably wondering if there is a way to run more than just one command in a container. Let's try that now: 39 | 40 | ```bash 41 | $ docker run -it busybox sh 42 | / # ls 43 | bin dev etc home proc root sys tmp usr var 44 | / # uptime 45 | 05:45:21 up 5:58, 0 users, load average: 0.00, 0.01, 0.04 46 | ``` 47 | 48 | Running the `run` command with the `-it` flags attaches us to an interactive tty in the container. Now we can run as many commands in the container as we want. Take some time to run your favorite commands. 49 | 50 | > **Danger Zone**: If you're feeling particularly adventurous you can try `rm -rf bin` in the container. Make sure you run this command in the container and **not** in your laptop/desktop. Doing this will make any other commands like `ls`, `uptime` not work. Once everything stops working, you can exit the container (type `exit` and press Enter) and then start it up again with the `docker run -it busybox sh` command. Since Docker creates a new container every time, everything should start working again. 51 | 52 | That concludes a whirlwind tour of the mighty `docker run` command, which would most likely be the command you'll use most often. It makes sense to spend some time getting comfortable with it. To find out more about `run`, use `docker run --help` to see a list of all flags it supports. As we proceed further, we'll see a few more variants of `docker run`. 53 | 54 | Before we move ahead though, let's quickly talk about deleting containers. We saw above that we can still see remnants of the container even after we've exited by running `docker ps -a`. Throughout this tutorial, you'll run `docker run` multiple times and leaving stray containers will eat up disk space. Hence, as a rule of thumb, I clean up containers once I'm done with them. To do that, you can run the `docker rm` command. Just copy the container IDs from above and paste them alongside the command. 55 | 56 | ```bash 57 | $ docker rm 305297d7a235 ff0a5c3750b9 58 | 305297d7a235 59 | ff0a5c3750b9 60 | ``` 61 | 62 | On deletion, you should see the IDs echoed back to you. If you have a bunch of containers to delete in one go, copy-pasting IDs can be tedious. In that case, you can simply run - 63 | 64 | ```bash 65 | $ docker rm $(docker ps -a -q -f status=exited) 66 | ``` 67 | 68 | This command deletes all containers that have a status of `exited`. In case you're wondering, the `-q` flag, only returns the numeric IDs and `-f` filters output based on conditions provided. One last thing that'll be useful is the `--rm` flag that can be passed to `docker run` which automatically deletes the container once it's exited from. For one off docker runs, `--rm` flag is very useful. 69 | 70 | In later versions of Docker, the `docker container prune` command can be used to achieve the same effect. 71 | 72 | ```bash 73 | $ docker container prune 74 | WARNING! This will remove all stopped containers. 75 | Are you sure you want to continue? [y/N] y 76 | Deleted Containers: 77 | 4a7f7eebae0f63178aff7eb0aa39f0627a203ab2df258c1a00b456cf20063 78 | f98f9c2aa1eaf727e4ec9c0283bcaa4762fbdba7f26191f26c97f64090360 79 | 80 | Total reclaimed space: 212 B 81 | ``` 82 | 83 | Lastly, you can also delete images that you no longer need by running `docker rmi`. 84 | -------------------------------------------------------------------------------- /starlight/src/content/docs/multi-container-environments/04-development-workflow.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Development Workflow 3 | --- 4 | 5 | Before we jump to the next section, there's one last thing I wanted to cover about docker-compose. As stated earlier, docker-compose is really great for development and testing. So let's see how we can configure compose to make our lives easier during development. 6 | 7 | Throughout this tutorial, we've worked with readymade docker images. While we've built images from scratch, we haven't touched any application code yet and mostly restricted ourselves to editing Dockerfiles and YAML configurations. One thing that you must be wondering is how does the workflow look during development? Is one supposed to keep creating Docker images for every change, then publish it and then run it to see if the changes work as expected? I'm sure that sounds super tedious. There has to be a better way. In this section, that's what we're going to explore. 8 | 9 | Let's see how we can make a change in the Foodtrucks app we just ran. Make sure you have the app running, 10 | 11 | ```bash 12 | $ docker container ls 13 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 14 | 5450ebedd03c prakhar1989/foodtrucks-web "python3 app.py" 9 seconds ago Up 6 seconds 0.0.0.0:5000->5000/tcp foodtrucks_web_1 15 | 05d408b25dfe docker.elastic.co/elasticsearch/elasticsearch:6.3.2 "/usr/local/bin/dock…" 10 hours ago Up 10 hours 0.0.0.0:9200->9200/tcp, 9300/tcp es 16 | ``` 17 | 18 | Now let's see if we can change this app to display a `Hello world!` message when a request is made to `/hello` route. Currently, the app responds with a 404. 19 | 20 | ```bash 21 | $ curl -I 0.0.0.0:5000/hello 22 | HTTP/1.0 404 NOT FOUND 23 | Content-Type: text/html 24 | Content-Length: 233 25 | Server: Werkzeug/0.11.2 Python/2.7.15rc1 26 | Date: Mon, 30 Jul 2018 15:34:38 GMT 27 | ``` 28 | 29 | Why does this happen? Since ours is a Flask app, we can see `app.py` ([link](https://github.com/prakhar1989/FoodTrucks/blob/master/flask-app/app.py#L48-L64)) for answers. In Flask, routes are defined with @app.route syntax. In the file, you'll see that we only have three routes defined - `/`,`/debug`and`/search`. The`/`route renders the main app, the`debug`route is used to return some debug information and finally`search` is used by the app to query elasticsearch. 30 | 31 | ```bash 32 | $ curl 0.0.0.0:5000/debug 33 | { 34 | "msg": "yellow open sfdata Ibkx7WYjSt-g8NZXOEtTMg 5 1 618 0 1.3mb 1.3mb\n", 35 | "status": "success" 36 | } 37 | ``` 38 | 39 | Given that context, how would we add a new route for `hello`? You guessed it! Let's open `flask-app/app.py` in our favorite editor and make the following change 40 | 41 | ```python 42 | @app.route('/') 43 | def index(): 44 | return render_template("index.html") 45 | 46 | # add a new hello route 47 | @app.route('/hello') 48 | def hello(): 49 | return "hello world!" 50 | ``` 51 | 52 | Now let's try making a request again 53 | 54 | ```bash 55 | $ curl -I 0.0.0.0:5000/hello 56 | HTTP/1.0 404 NOT FOUND 57 | Content-Type: text/html 58 | Content-Length: 233 59 | Server: Werkzeug/0.11.2 Python/2.7.15rc1 60 | Date: Mon, 30 Jul 2018 15:34:38 GMT 61 | ``` 62 | 63 | Oh no! That didn't work! What did we do wrong? While we did make the change in `app.py`, the file resides in our machine (or the host machine), but since Docker is running our containers based off the `prakhar1989/foodtrucks-web` image, it doesn't know about this change. To validate this, lets try the following - 64 | 65 | ``` 66 | $ docker-compose run web bash 67 | Starting es ... done 68 | root@581e351c82b0:/opt/flask-app# ls 69 | app.py package-lock.json requirements.txt templates 70 | node_modules package.json static webpack.config.js 71 | root@581e351c82b0:/opt/flask-app# grep hello app.py 72 | root@581e351c82b0:/opt/flask-app# exit 73 | ``` 74 | 75 | What we're trying to do here is to validate that our changes are not in the `app.py` that's running in the container. We do this by running the command `docker-compose run`, which is similar to its cousin `docker run` but takes additional arguments for the service (which is `web` in our [case](https://github.com/prakhar1989/FoodTrucks/blob/master/docker-compose.yml#L12)). As soon as we run `bash`, the shell opens in `/opt/flask-app` as specified in our [Dockerfile](https://github.com/prakhar1989/FoodTrucks/blob/master/Dockerfile#L13). From the grep command we can see that our changes are not in the file. 76 | 77 | Lets see how we can fix it. First off, we need to tell docker compose to not use the image and instead use the files locally. We'll also set debug mode to `true` so that Flask knows to reload the server when `app.py` changes. Replace the `web` portion of the `docker-compose.yml` file like so: 78 | 79 | ```yaml 80 | version: "3" 81 | services: 82 | es: 83 | image: docker.elastic.co/elasticsearch/elasticsearch:6.3.2 84 | container_name: es 85 | environment: 86 | - discovery.type=single-node 87 | ports: 88 | - 9200:9200 89 | volumes: 90 | - esdata1:/usr/share/elasticsearch/data 91 | web: 92 | build: . # replaced image with build 93 | command: python3 app.py 94 | environment: 95 | - DEBUG=True # set an env var for flask 96 | depends_on: 97 | - es 98 | ports: 99 | - "5000:5000" 100 | volumes: 101 | - ./flask-app:/opt/flask-app 102 | volumes: 103 | esdata1: 104 | driver: local 105 | ``` 106 | 107 | With that change ([diff](https://github.com/prakhar1989/FoodTrucks/commit/31368936de5959efaf4457a94c678d21e3eefbce)), let's stop and start the containers. 108 | 109 | ```bash 110 | $ docker-compose down -v 111 | Stopping foodtrucks_web_1 ... done 112 | Stopping es ... done 113 | Removing foodtrucks_web_1 ... done 114 | Removing es ... done 115 | Removing network foodtrucks_default 116 | Removing volume foodtrucks_esdata1 117 | 118 | $ docker-compose up -d 119 | Creating network "foodtrucks_default" with the default driver 120 | Creating volume "foodtrucks_esdata1" with local driver 121 | Creating es ... done 122 | Creating foodtrucks_web_1 ... done 123 | ``` 124 | 125 | As a final step, lets make the change in `app.py` by adding a new route. Now we try to curl 126 | 127 | ```bash 128 | $ curl 0.0.0.0:5000/hello 129 | hello world 130 | ``` 131 | 132 | Wohoo! We get a valid response! Try playing around by making more changes in the app. 133 | 134 | That concludes our tour of Docker Compose. With Docker Compose, you can also pause your services, run a one-off command on a container and even scale the number of containers. I also recommend you checkout a few other [use-cases](https://docs.docker.com/compose/overview/#common-use-cases) of Docker compose. Hopefully, I was able to show you how easy it is to manage multi-container environments with Compose. In the final section, we are going to deploy our app to AWS! -------------------------------------------------------------------------------- /starlight/src/content/docs/webapps-with-docker/04-docker-on-aws.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Docker on AWS 3 | --- 4 | 5 | What good is an application that can't be shared with friends, right? So in this section we are going to see how we can deploy our awesome application to the cloud so that we can share it with our friends! We're going to use AWS [Elastic Beanstalk](https://aws.amazon.com/elasticbeanstalk/) to get our application up and running in a few clicks. We'll also see how easy it is to make our application scalable and manageable with Beanstalk! 6 | 7 | #### Docker push 8 | 9 | The first thing that we need to do before we deploy our app to AWS is to publish our image on a registry which can be accessed by AWS. There are many different [Docker registries](https://aws.amazon.com/ecr/) you can use (you can even host [your own](https://docs.docker.com/registry/deploying/)). For now, let's use [Docker Hub](https://hub.docker.com) to publish the image. 10 | 11 | If this is the first time you are pushing an image, the client will ask you to login. Provide the same credentials that you used for logging into Docker Hub. 12 | 13 | ```bash 14 | $ docker login 15 | Login in with your Docker ID to push and pull images from Docker Hub. If you do not have a Docker ID, head over to https://hub.docker.com to create one. 16 | Username: yourusername 17 | Password: 18 | WARNING! Your password will be stored unencrypted in /Users/yourusername/.docker/config.json 19 | Configure a credential helper to remove this warning. See 20 | https://docs.docker.com/engine/reference/commandline/login/credential-store 21 | 22 | Login Succeeded 23 | ``` 24 | 25 | To publish, just type the below command remembering to replace the name of the image tag above with yours. It is important to have the format of `yourusername/image_name` so that the client knows where to publish. 26 | 27 | ```bash 28 | $ docker push yourusername/catnip 29 | ``` 30 | 31 | Once that is done, you can view your image on Docker Hub. For example, here's the [web page](https://hub.docker.com/r/prakhar1989/catnip/) for my image. 32 | 33 | > Note: One thing that I'd like to clarify before we go ahead is that it is not **imperative** to host your image on a public registry (or any registry) in order to deploy to AWS. In case you're writing code for the next million-dollar unicorn startup you can totally skip this step. The reason why we're pushing our images publicly is that it makes deployment super simple by skipping a few intermediate configuration steps. 34 | 35 | Now that your image is online, anyone who has docker installed can play with your app by typing just a single command. 36 | 37 | ```bash 38 | $ docker run -p 8888:5000 yourusername/catnip 39 | ``` 40 | 41 | If you've pulled your hair out in setting up local dev environments / sharing application configuration in the past, you very well know how awesome this sounds. That's why Docker is so cool! 42 | 43 | #### Beanstalk 44 | 45 | AWS Elastic Beanstalk (EB) is a PaaS (Platform as a Service) offered by AWS. If you've used Heroku, Google App Engine etc. you'll feel right at home. As a developer, you just tell EB how to run your app and it takes care of the rest - including scaling, monitoring and even updates. In April 2014, EB added support for running single-container Docker deployments which is what we'll use to deploy our app. Although EB has a very intuitive [CLI](http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3.html), it does require some setup, and to keep things simple we'll use the web UI to launch our application. 46 | 47 | To follow along, you need a functioning [AWS](http://aws.amazon.com) account. If you haven't already, please go ahead and do that now - you will need to enter your credit card information. But don't worry, it's free and anything we do in this tutorial will also be free! Let's get started. 48 | 49 | Here are the steps: 50 | 51 | - Login to your AWS [console](http://console.aws.amazon.com). 52 | - Click on Elastic Beanstalk. It will be in the compute section on the top left. Alternatively, you can access the [Elastic Beanstalk console](https://console.aws.amazon.com/elasticbeanstalk). 53 | 54 | ![EB Start](../../../assets/eb-start.webp) 55 | 56 | - Click on "Create New Application" in the top right 57 | - Give your app a memorable (but unique) name and provide an (optional) description 58 | - In the **New Environment** screen, create a new environment and choose the **Web Server Environment**. 59 | - Fill in the environment information by choosing a domain. This URL is what you'll share with your friends so make sure it's easy to remember. 60 | - Under base configuration section. Choose _Docker_ from the _predefined platform_. 61 | 62 | ![EB Start](../../../assets/eb-docker.webp) 63 | 64 | - Now we need to upload our application code. But since our application is packaged in a Docker container, we just need to tell EB about our container. Open the `Dockerrun.aws.json` [file](https://github.com/prakhar1989/docker-curriculum/blob/master/flask-app/Dockerrun.aws.json) located in the `flask-app` folder and edit the `Name` of the image to your image's name. Don't worry, I'll explain the contents of the file shortly. When you are done, click on the radio button for "Upload your Code", choose this file, and click on "Upload". 65 | - Now click on "Create environment". The final screen that you see will have a few spinners indicating that your environment is being set up. It typically takes around 5 minutes for the first-time setup. 66 | 67 | While we wait, let's quickly see what the `Dockerrun.aws.json` file contains. This file is basically an AWS specific file that tells EB details about our application and docker configuration. 68 | 69 | ```json 70 | { 71 | "AWSEBDockerrunVersion": "1", 72 | "Image": { 73 | "Name": "prakhar1989/catnip", 74 | "Update": "true" 75 | }, 76 | "Ports": [ 77 | { 78 | "ContainerPort": 5000, 79 | "HostPort": 8000 80 | } 81 | ], 82 | "Logging": "/var/log/nginx" 83 | } 84 | ``` 85 | 86 | The file should be pretty self-explanatory, but you can always [reference](http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_docker_image.html#create_deploy_docker_image_dockerrun) the official documentation for more information. We provide the name of the image that EB should use along with a port that the container should open. 87 | 88 | Hopefully by now, our instance should be ready. Head over to the EB page and you should see a green tick indicating that your app is alive and kicking. 89 | 90 | ![EB Deploy](../../../assets/eb-deploy.webp) 91 | 92 | Go ahead and open the URL in your browser and you should see the application in all its glory. Feel free to email / IM / snapchat this link to your friends and family so that they can enjoy a few cat gifs, too. 93 | 94 | #### Cleanup 95 | 96 | Once you done basking in the glory of your app, remember to terminate the environment so that you don't end up getting charged for extra resources. 97 | 98 | ![EB terminate](../../../assets/eb-terminate.webp) 99 | 100 | Congratulations! You have deployed your first Docker application! That might seem like a lot of steps, but with the [command-line tool for EB](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3.html) you can almost mimic the functionality of Heroku in a few keystrokes! Hopefully, you agree that Docker takes away a lot of the pains of building and deploying applications in the cloud. I would encourage you to read the AWS [documentation](http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/docker-singlecontainer-deploy.html) on single-container Docker environments to get an idea of what features exist. 101 | 102 | In the next (and final) part of the tutorial, we'll up the ante a bit and deploy an application that mimics the real-world more closely; an app with a persistent back-end storage tier. Let's get straight to it! -------------------------------------------------------------------------------- /tutorial/src/styles/main.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * Fonts 3 | */ 4 | @font-face { 5 | font-family: 'fontello'; 6 | src: url('/font/fontello.eot'); 7 | src: url('/font/fontello.eot#iefix') format('embedded-opentype'), url('/font/fontello.woff') format('woff'), url('/font/fontello.ttf') format('truetype'), url('/font/fontello.svg') format('svg'); 8 | font-weight: normal; 9 | font-display: 'auto'; 10 | font-style: normal; 11 | } 12 | 13 | // Variables 14 | $font-stack: "Oxygen", sans-serif; 15 | $font-monospace: "Roboto Mono", monospace; 16 | $primary-text-color: #3c4043; 17 | $primary-color: #08c; 18 | $color-white: #fff; 19 | $code-highlight-color: #d44950; 20 | 21 | 22 | /* 23 | * Scaffolding and type 24 | */ 25 | html { 26 | font-size: 1rem; 27 | } 28 | 29 | @media (min-width: 40em) { 30 | html { 31 | font-size: 1.2rem; 32 | } 33 | } 34 | 35 | img { 36 | width: 100%; 37 | } 38 | 39 | body { 40 | margin: 0; 41 | font: 1.0rem/1.5 $font-stack; 42 | color: $primary-text-color; 43 | min-height: 100vh; 44 | } 45 | 46 | hr { 47 | border: 1px solid #eee; 48 | margin: 4em 0; 49 | } 50 | 51 | a { 52 | color: $primary-color; 53 | text-decoration: none; 54 | 55 | &:hover { 56 | text-decoration: none; 57 | color: lighten($primary-color, 15%); 58 | transition: color .15s ease-in; 59 | } 60 | } 61 | 62 | picture { 63 | text-align: center; 64 | .caption { 65 | font-size: 0.7em; 66 | margin-top: 0; 67 | } 68 | } 69 | 70 | h1, 71 | h2, 72 | h3, 73 | h4 { 74 | font-weight: normal; 75 | line-height: 1; 76 | } 77 | 78 | h1 { 79 | font-size: 2.7rem; 80 | } 81 | 82 | h2 { 83 | font-size: 1.5rem; 84 | text-transform: uppercase; 85 | font-weight: 800; 86 | letter-spacing: 1.2px; 87 | } 88 | 89 | h3 { 90 | padding-top: 1rem; 91 | margin-top: 2rem; 92 | font-size: 1.35rem; 93 | } 94 | 95 | h4 { 96 | font-size: 1.25rem 97 | } 98 | 99 | .lead { 100 | font-size: 1.3rem; 101 | } 102 | 103 | blockquote { 104 | position: relative; 105 | padding: 0.5rem 1rem 0.5rem; 106 | font-style: italic; 107 | color: lighten($primary-text-color, 25%); 108 | border-left: 8px solid #eee; 109 | margin: 0; 110 | margin: 1.5rem 0; 111 | } 112 | 113 | blockquote p { 114 | margin-bottom: 0; 115 | } 116 | 117 | /* Tighten up margin on last items */ 118 | 119 | p:last-child, 120 | ul:last-child, 121 | blockquote:last-child { 122 | margin-bottom: 0; 123 | } 124 | 125 | /* 126 | * Code 127 | */ 128 | 129 | code, 130 | pre { 131 | font-family: $font-monospace; 132 | font-size: 85%; 133 | } 134 | 135 | code { 136 | padding: 2px 4px; 137 | color: $code-highlight-color; 138 | background-color: #f7f7f9; 139 | border-radius: .2rem; 140 | } 141 | 142 | /* 143 | * The Grid 144 | */ 145 | 146 | .container { 147 | max-width: 40rem; 148 | margin-left: 30%; 149 | margin-right: auto; 150 | padding: 0 2.4em; 151 | } 152 | 153 | /* 154 | * Side-nav 155 | */ 156 | nav#menu { 157 | display: flex; 158 | order: -1; 159 | background: #f8f9fa; 160 | position: fixed; 161 | left: 0; 162 | overflow-y: auto; 163 | top: 0; 164 | bottom: 0; 165 | flex: 0 0 auto; 166 | border-right: 1px solid #d2d3d4; 167 | z-index: 100; 168 | max-width: 30%; 169 | flex-direction: column; 170 | justify-content: space-between; 171 | 172 | // close button for collapsible nav 173 | #close { 174 | display: none; 175 | position: absolute; 176 | top: 0; 177 | right: 0; 178 | width: 80px; 179 | height: 80px; 180 | line-height: 78px; 181 | text-align: center; 182 | font-size: 3em; 183 | text-decoration: none; 184 | color: #fff; 185 | } 186 | 187 | ul { 188 | list-style-type: none; 189 | padding-left: 1rem; 190 | } 191 | 192 | &>ol { 193 | padding: 8px; 194 | list-style-type: none; 195 | font-size: 1em; 196 | margin-bottom: 0; 197 | margin-top: 0; 198 | 199 | &>li { 200 | margin: 1rem 0.5rem; 201 | } 202 | } 203 | 204 | // footer 205 | footer { 206 | text-align: center; 207 | padding: 0.3rem 0; 208 | } 209 | } 210 | 211 | #hamburger-btn { 212 | position: fixed; 213 | top: 0; 214 | left: 0; 215 | display: none; 216 | width: 100px; 217 | height: 80px; 218 | background: 0 0; 219 | color: $primary-color; 220 | line-height: 78px; 221 | text-align: center; 222 | font-size: 4em; 223 | } 224 | 225 | /* 226 | * Masthead 227 | */ 228 | .masthead { 229 | padding-bottom: 2rem; 230 | border-bottom: 1px solid #eee; 231 | h1 { 232 | margin-bottom: .25rem; 233 | } 234 | a:hover { 235 | text-decoration: none; 236 | } 237 | i.demo-icon { 238 | font-family: "fontello"; 239 | font-style: normal; 240 | font-weight: normal; 241 | display: inline-block; 242 | text-decoration: inherit; 243 | width: 1em; 244 | margin-right: .2em; 245 | text-align: center; 246 | font-variant: normal; 247 | text-transform: none; 248 | line-height: 1em; 249 | margin-left: .2em; 250 | font-size: 120%; 251 | -webkit-font-smoothing: antialiased; 252 | -moz-osx-font-smoothing: grayscale; 253 | } 254 | } 255 | 256 | /** 257 | * Responsive 258 | */ 259 | /* below 767px (iPad size) */ 260 | @media screen and (max-device-width : 768px), screen and (max-width : 768px), print { 261 | .container { 262 | margin-left: auto; 263 | } 264 | 265 | nav#menu { 266 | left: -100%; 267 | max-width: 80%; 268 | overflow-y: auto; 269 | z-index: 100; 270 | -webkit-box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15); 271 | box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15); 272 | 273 | #close { 274 | display: block; 275 | font-size: 2.2em; 276 | line-height: 60px; 277 | width: 60px; 278 | } 279 | } 280 | 281 | nav#menu:target { 282 | background: #08c; 283 | border: 0; 284 | color: white; 285 | left: 0; 286 | transition: left 0.3s; 287 | box-shadow: rgba(0, 0, 0, 0.14) 0px 0px 4px, rgba(0, 0, 0, 0.28) 0px 4px 8px; 288 | } 289 | 290 | nav a { 291 | color: white; 292 | text-decoration: none; 293 | } 294 | 295 | 296 | nav ol>li { 297 | padding: 0.2rem 0; 298 | } 299 | 300 | #hamburger-btn { 301 | display: block; 302 | width: 40px; 303 | text-decoration: none; 304 | height: 80px; 305 | line-height: 65px; 306 | font-size: 2em; 307 | } 308 | } 309 | 310 | 311 | /** 312 | * Styles for code highlighting 313 | */ 314 | 315 | .hljs-comment, 316 | .hljs-quote { 317 | color: #969896 318 | } 319 | 320 | .hljs-variable, 321 | .hljs-template-variable, 322 | .hljs-tag, 323 | .hljs-name, 324 | .hljs-selector-id, 325 | .hljs-selector-class, 326 | .hljs-regexp, 327 | .hljs-deletion { 328 | color: #d54e53 329 | } 330 | 331 | .hljs-number, 332 | .hljs-built_in, 333 | .hljs-builtin-name, 334 | .hljs-literal, 335 | .hljs-type, 336 | .hljs-params, 337 | .hljs-meta, 338 | .hljs-link { 339 | color: #e78c45 340 | } 341 | 342 | .hljs-attribute { 343 | color: #e7c547 344 | } 345 | 346 | .hljs-string, 347 | .hljs-symbol, 348 | .hljs-bullet, 349 | .hljs-addition { 350 | color: #b9ca4a 351 | } 352 | 353 | .hljs-title, 354 | .hljs-section { 355 | color: #7aa6da 356 | } 357 | 358 | .hljs-keyword, 359 | .hljs-selector-tag { 360 | color: #c397d8 361 | } 362 | 363 | .hljs { 364 | display: block; 365 | overflow-x: auto; 366 | background: black; 367 | color: #eaeaea; 368 | padding: 0.5em 369 | } 370 | 371 | .hljs-emphasis { 372 | font-style: italic 373 | } 374 | 375 | .hljs-strong { 376 | font-weight: bold 377 | } 378 | -------------------------------------------------------------------------------- /tutorial/public/styles/main.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * Fonts 3 | */ 4 | @font-face { 5 | font-family: 'fontello'; 6 | src: url('/font/fontello.eot'); 7 | src: url('/font/fontello.eot#iefix') format('embedded-opentype'), url('/font/fontello.woff') format('woff'), url('/font/fontello.ttf') format('truetype'), url('/font/fontello.svg') format('svg'); 8 | font-weight: normal; 9 | font-display: 'auto'; 10 | font-style: normal; 11 | } 12 | 13 | // Variables 14 | $font-stack: "Oxygen", sans-serif; 15 | $font-monospace: "Roboto Mono", monospace; 16 | $primary-text-color: #3c4043; 17 | $primary-color: #08c; 18 | $color-white: #fff; 19 | $code-highlight-color: #d44950; 20 | 21 | 22 | /* 23 | * Scaffolding and type 24 | */ 25 | html { 26 | font-size: 1rem; 27 | } 28 | 29 | @media (min-width: 40em) { 30 | html { 31 | font-size: 1.2rem; 32 | } 33 | } 34 | 35 | img { 36 | width: 100%; 37 | } 38 | 39 | body { 40 | margin: 0; 41 | font: 1.0rem/1.5 $font-stack; 42 | color: $primary-text-color; 43 | min-height: 100vh; 44 | } 45 | 46 | hr { 47 | border: 1px solid #eee; 48 | margin: 4em 0; 49 | } 50 | 51 | a { 52 | color: $primary-color; 53 | text-decoration: none; 54 | 55 | &:hover { 56 | text-decoration: none; 57 | color: lighten($primary-color, 15%); 58 | transition: color .15s ease-in; 59 | } 60 | } 61 | 62 | picture { 63 | text-align: center; 64 | .caption { 65 | font-size: 0.7em; 66 | margin-top: 0; 67 | } 68 | } 69 | 70 | h1, 71 | h2, 72 | h3, 73 | h4 { 74 | font-weight: normal; 75 | line-height: 1; 76 | } 77 | 78 | h1 { 79 | font-size: 2.7rem; 80 | } 81 | 82 | h2 { 83 | font-size: 1.5rem; 84 | text-transform: uppercase; 85 | font-weight: 800; 86 | letter-spacing: 1.2px; 87 | } 88 | 89 | h3 { 90 | padding-top: 1rem; 91 | margin-top: 2rem; 92 | font-size: 1.35rem; 93 | } 94 | 95 | h4 { 96 | font-size: 1.25rem 97 | } 98 | 99 | .lead { 100 | font-size: 1.3rem; 101 | } 102 | 103 | blockquote { 104 | position: relative; 105 | padding: 0.5rem 1rem 0.5rem; 106 | font-style: italic; 107 | color: lighten($primary-text-color, 25%); 108 | border-left: 8px solid #eee; 109 | margin: 0; 110 | margin: 1.5rem 0; 111 | } 112 | 113 | blockquote p { 114 | margin-bottom: 0; 115 | } 116 | 117 | /* Tighten up margin on last items */ 118 | 119 | p:last-child, 120 | ul:last-child, 121 | blockquote:last-child { 122 | margin-bottom: 0; 123 | } 124 | 125 | /* 126 | * Code 127 | */ 128 | 129 | code, 130 | pre { 131 | font-family: $font-monospace; 132 | font-size: 85%; 133 | } 134 | 135 | code { 136 | padding: 2px 4px; 137 | color: $code-highlight-color; 138 | background-color: #f7f7f9; 139 | border-radius: .2rem; 140 | } 141 | 142 | /* 143 | * The Grid 144 | */ 145 | 146 | .container { 147 | max-width: 40rem; 148 | margin-left: 30%; 149 | margin-right: auto; 150 | padding: 0 2.4em; 151 | } 152 | 153 | /* 154 | * Side-nav 155 | */ 156 | nav#menu { 157 | display: flex; 158 | order: -1; 159 | background: #f8f9fa; 160 | position: fixed; 161 | left: 0; 162 | overflow-y: auto; 163 | top: 0; 164 | bottom: 0; 165 | flex: 0 0 auto; 166 | border-right: 1px solid #d2d3d4; 167 | z-index: 100; 168 | max-width: 30%; 169 | flex-direction: column; 170 | justify-content: space-between; 171 | 172 | // close button for collapsible nav 173 | #close { 174 | display: none; 175 | position: absolute; 176 | top: 0; 177 | right: 0; 178 | width: 80px; 179 | height: 80px; 180 | line-height: 78px; 181 | text-align: center; 182 | font-size: 3em; 183 | text-decoration: none; 184 | color: #fff; 185 | } 186 | 187 | ul { 188 | list-style-type: none; 189 | padding-left: 1rem; 190 | } 191 | 192 | &>ol { 193 | padding: 8px; 194 | list-style-type: none; 195 | font-size: 1em; 196 | margin-bottom: 0; 197 | margin-top: 0; 198 | 199 | &>li { 200 | margin: 1rem 0.5rem; 201 | } 202 | } 203 | 204 | // footer 205 | footer { 206 | text-align: center; 207 | padding: 0.3rem 0; 208 | } 209 | } 210 | 211 | #hamburger-btn { 212 | position: fixed; 213 | top: 0; 214 | left: 0; 215 | display: none; 216 | width: 100px; 217 | height: 80px; 218 | background: 0 0; 219 | color: $primary-color; 220 | line-height: 78px; 221 | text-align: center; 222 | font-size: 4em; 223 | } 224 | 225 | /* 226 | * Masthead 227 | */ 228 | .masthead { 229 | padding-bottom: 2rem; 230 | border-bottom: 1px solid #eee; 231 | h1 { 232 | margin-bottom: .25rem; 233 | } 234 | a:hover { 235 | text-decoration: none; 236 | } 237 | i.demo-icon { 238 | font-family: "fontello"; 239 | font-style: normal; 240 | font-weight: normal; 241 | display: inline-block; 242 | text-decoration: inherit; 243 | width: 1em; 244 | margin-right: .2em; 245 | text-align: center; 246 | font-variant: normal; 247 | text-transform: none; 248 | line-height: 1em; 249 | margin-left: .2em; 250 | font-size: 120%; 251 | -webkit-font-smoothing: antialiased; 252 | -moz-osx-font-smoothing: grayscale; 253 | } 254 | } 255 | 256 | /** 257 | * Responsive 258 | */ 259 | /* below 767px (iPad size) */ 260 | @media screen and (max-device-width : 768px), screen and (max-width : 768px), print { 261 | .container { 262 | margin-left: auto; 263 | } 264 | 265 | nav#menu { 266 | left: -100%; 267 | max-width: 80%; 268 | overflow-y: auto; 269 | z-index: 100; 270 | -webkit-box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15); 271 | box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15); 272 | 273 | #close { 274 | display: block; 275 | font-size: 2.2em; 276 | line-height: 60px; 277 | width: 60px; 278 | } 279 | } 280 | 281 | nav#menu:target { 282 | background: #08c; 283 | border: 0; 284 | color: white; 285 | left: 0; 286 | transition: left 0.3s; 287 | box-shadow: rgba(0, 0, 0, 0.14) 0px 0px 4px, rgba(0, 0, 0, 0.28) 0px 4px 8px; 288 | } 289 | 290 | nav a { 291 | color: white; 292 | text-decoration: none; 293 | } 294 | 295 | 296 | nav ol>li { 297 | padding: 0.2rem 0; 298 | } 299 | 300 | #hamburger-btn { 301 | display: block; 302 | width: 40px; 303 | text-decoration: none; 304 | height: 80px; 305 | line-height: 65px; 306 | font-size: 2em; 307 | } 308 | } 309 | 310 | 311 | /** 312 | * Styles for code highlighting 313 | */ 314 | 315 | .hljs-comment, 316 | .hljs-quote { 317 | color: #969896 318 | } 319 | 320 | .hljs-variable, 321 | .hljs-template-variable, 322 | .hljs-tag, 323 | .hljs-name, 324 | .hljs-selector-id, 325 | .hljs-selector-class, 326 | .hljs-regexp, 327 | .hljs-deletion { 328 | color: #d54e53 329 | } 330 | 331 | .hljs-number, 332 | .hljs-built_in, 333 | .hljs-builtin-name, 334 | .hljs-literal, 335 | .hljs-type, 336 | .hljs-params, 337 | .hljs-meta, 338 | .hljs-link { 339 | color: #e78c45 340 | } 341 | 342 | .hljs-attribute { 343 | color: #e7c547 344 | } 345 | 346 | .hljs-string, 347 | .hljs-symbol, 348 | .hljs-bullet, 349 | .hljs-addition { 350 | color: #b9ca4a 351 | } 352 | 353 | .hljs-title, 354 | .hljs-section { 355 | color: #7aa6da 356 | } 357 | 358 | .hljs-keyword, 359 | .hljs-selector-tag { 360 | color: #c397d8 361 | } 362 | 363 | .hljs { 364 | display: block; 365 | overflow-x: auto; 366 | background: black; 367 | color: #eaeaea; 368 | padding: 0.5em 369 | } 370 | 371 | .hljs-emphasis { 372 | font-style: italic 373 | } 374 | 375 | .hljs-strong { 376 | font-weight: bold 377 | } 378 | -------------------------------------------------------------------------------- /static-site/html/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability when focused and also mouse hovered in all browsers. 95 | */ 96 | 97 | a:active, 98 | a:hover { 99 | outline: 0; 100 | } 101 | 102 | /* Text-level semantics 103 | ========================================================================== */ 104 | 105 | /** 106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 107 | */ 108 | 109 | abbr[title] { 110 | border-bottom: 1px dotted; 111 | } 112 | 113 | /** 114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 115 | */ 116 | 117 | b, 118 | strong { 119 | font-weight: bold; 120 | } 121 | 122 | /** 123 | * Address styling not present in Safari and Chrome. 124 | */ 125 | 126 | dfn { 127 | font-style: italic; 128 | } 129 | 130 | /** 131 | * Address variable `h1` font-size and margin within `section` and `article` 132 | * contexts in Firefox 4+, Safari, and Chrome. 133 | */ 134 | 135 | h1 { 136 | font-size: 2em; 137 | margin: 0.67em 0; 138 | } 139 | 140 | /** 141 | * Address styling not present in IE 8/9. 142 | */ 143 | 144 | mark { 145 | background: #ff0; 146 | color: #000; 147 | } 148 | 149 | /** 150 | * Address inconsistent and variable font size in all browsers. 151 | */ 152 | 153 | small { 154 | font-size: 80%; 155 | } 156 | 157 | /** 158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 159 | */ 160 | 161 | sub, 162 | sup { 163 | font-size: 75%; 164 | line-height: 0; 165 | position: relative; 166 | vertical-align: baseline; 167 | } 168 | 169 | sup { 170 | top: -0.5em; 171 | } 172 | 173 | sub { 174 | bottom: -0.25em; 175 | } 176 | 177 | /* Embedded content 178 | ========================================================================== */ 179 | 180 | /** 181 | * Remove border when inside `a` element in IE 8/9/10. 182 | */ 183 | 184 | img { 185 | border: 0; 186 | } 187 | 188 | /** 189 | * Correct overflow not hidden in IE 9/10/11. 190 | */ 191 | 192 | svg:not(:root) { 193 | overflow: hidden; 194 | } 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * Address margin not present in IE 8/9 and Safari. 201 | */ 202 | 203 | figure { 204 | margin: 1em 40px; 205 | } 206 | 207 | /** 208 | * Address differences between Firefox and other browsers. 209 | */ 210 | 211 | hr { 212 | -moz-box-sizing: content-box; 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 354 | * (include `-moz` to future-proof). 355 | */ 356 | 357 | input[type="search"] { 358 | -webkit-appearance: textfield; /* 1 */ 359 | -moz-box-sizing: content-box; 360 | -webkit-box-sizing: content-box; /* 2 */ 361 | box-sizing: content-box; 362 | } 363 | 364 | /** 365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 366 | * Safari (but not Chrome) clips the cancel button when the search input has 367 | * padding (and `textfield` appearance). 368 | */ 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | /** 376 | * Define consistent border, margin, and padding. 377 | */ 378 | 379 | fieldset { 380 | border: 1px solid #c0c0c0; 381 | margin: 0 2px; 382 | padding: 0.35em 0.625em 0.75em; 383 | } 384 | 385 | /** 386 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 388 | */ 389 | 390 | legend { 391 | border: 0; /* 1 */ 392 | padding: 0; /* 2 */ 393 | } 394 | 395 | /** 396 | * Remove default vertical scrollbar in IE 8/9/10/11. 397 | */ 398 | 399 | textarea { 400 | overflow: auto; 401 | } 402 | 403 | /** 404 | * Don't inherit the `font-weight` (applied by a rule above). 405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 406 | */ 407 | 408 | optgroup { 409 | font-weight: bold; 410 | } 411 | 412 | /* Tables 413 | ========================================================================== */ 414 | 415 | /** 416 | * Remove most spacing between table cells. 417 | */ 418 | 419 | table { 420 | border-collapse: collapse; 421 | border-spacing: 0; 422 | } 423 | 424 | td, 425 | th { 426 | padding: 0; 427 | } -------------------------------------------------------------------------------- /starlight/src/content/docs/multi-container-environments/05-aws-elastic-container-service.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: AWS Elastic Container Service 3 | --- 4 | 5 | In the last section we used `docker-compose` to run our app locally with a single command: `docker-compose up`. Now that we have a functioning app we want to share this with the world, get some users, make tons of money and buy a big house in Miami. Executing the last three are beyond the scope of the tutorial, so we'll spend our time instead on figuring out how we can deploy our multi-container apps on the cloud with AWS. 6 | 7 | If you've read this far you are pretty much convinced that Docker is a pretty cool technology. And you are not alone. Seeing the meteoric rise of Docker, almost all Cloud vendors started working on adding support for deploying Docker apps on their platform. As of today, you can deploy containers on [Google Cloud Platform](https://cloud.google.com/containers/), [AWS](https://aws.amazon.com/containers/), [Azure](https://azure.microsoft.com/en-us/overview/containers/) and many others. We already got a primer on deploying single container apps with Elastic Beanstalk and in this section we are going to look at [Elastic Container Service (or ECS)](https://aws.amazon.com/ecs/) by AWS. 8 | 9 | AWS ECS is a scalable and super flexible container management service that supports Docker containers. It allows you to operate a Docker cluster on top of EC2 instances via an easy-to-use API. Where Beanstalk came with reasonable defaults, ECS allows you to completely tune your environment as per your needs. This makes ECS, in my opinion, quite complex to get started with. 10 | 11 | Luckily for us, ECS has a friendly [CLI](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_CLI.html) tool that understands Docker Compose files and automatically provisions the cluster on ECS! Since we already have a functioning `docker-compose.yml` it should not take a lot of effort in getting up and running on AWS. So let's get started! 12 | 13 | The first step is to install the CLI. Instructions to install the CLI on both Mac and Linux are explained very clearly in the [official docs](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_CLI_installation.html). Go ahead, install the CLI and when you are done, verify the install by running 14 | 15 | ```bash 16 | $ ecs-cli --version 17 | ecs-cli version 1.18.1 (7e9df84) 18 | ``` 19 | 20 | Next, we'll be working on configuring the CLI so that we can talk to ECS. We'll be following the steps as detailed in the [official guide](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_CLI_Configuration.html) on AWS ECS docs. In case of any confusion, please feel free to refer to that guide. 21 | 22 | The first step will involve creating a profile that we'll use for the rest of the tutorial. To continue, you'll need your `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. To obtain these, follow the steps as detailed under the section titled _Access Key and Secret Access Key_ on [this page](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html#cli-quick-configuration). 23 | 24 | ```bash 25 | $ ecs-cli configure profile --profile-name ecs-foodtrucks --access-key $AWS_ACCESS_KEY_ID --secret-key $AWS_SECRET_ACCESS_KEY 26 | ``` 27 | 28 | Next, we need to get a keypair which we'll be using to log into the instances. Head over to your [EC2 Console](https://console.aws.amazon.com/ec2/v2/home#KeyPairs:sort=keyName) and create a new keypair. Download the keypair and store it in a safe location. Another thing to note before you move away from this screen is the region name. In my case, I have named my key - `ecs` and set my region as `us-east-1`. This is what I'll assume for the rest of this walkthrough. 29 | 30 | ![EC2 Keypair](../../../assets/keypair.webp) 31 | 32 | The next step is to configure the CLI. 33 | 34 | ```bash 35 | $ ecs-cli configure --region us-east-1 --cluster foodtrucks 36 | INFO[0000] Saved ECS CLI configuration for cluster (foodtrucks) 37 | ``` 38 | 39 | We provide the `configure` command with the region name we want our cluster to reside in and a cluster name. Make sure you provide the **same region name** that you used when creating the keypair. If you've not configured the [AWS CLI](https://aws.amazon.com/cli/) on your computer before, you can use the official [guide](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-set-up.html), which explains everything in great detail on how to get everything going. 40 | 41 | The next step enables the CLI to create a [CloudFormation](https://aws.amazon.com/cloudformation/) template. 42 | 43 | ```bash 44 | $ ecs-cli up --keypair ecs --capability-iam --size 1 --instance-type t2.medium 45 | INFO[0000] Using recommended Amazon Linux 2 AMI with ECS Agent 1.39.0 and Docker version 18.09.9-ce 46 | INFO[0000] Created cluster cluster=foodtrucks 47 | INFO[0001] Waiting for your cluster resources to be created 48 | INFO[0001] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS 49 | INFO[0062] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS 50 | INFO[0122] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS 51 | INFO[0182] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS 52 | INFO[0242] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS 53 | VPC created: vpc-0bbed8536930053a6 54 | Security Group created: sg-0cf767fb4d01a3f99 55 | Subnet created: subnet-05de1db2cb1a50ab8 56 | Subnet created: subnet-01e1e8bc95d49d0fd 57 | Cluster creation succeeded. 58 | ``` 59 | 60 | Here we provide the name of the keypair we downloaded initially (`ecs` in my case), the number of instances that we want to use (`--size`) and the type of instances that we want the containers to run on. The `--capability-iam` flag tells the CLI that we acknowledge that this command may create IAM resources. 61 | 62 | The last and final step is where we'll use our `docker-compose.yml` file. We'll need to make a few minor changes, so instead of modifying the original, let's make a copy of it. The contents of [this file](https://github.com/prakhar1989/FoodTrucks/blob/master/aws-ecs/docker-compose.yml) (after making the changes) look like (below) - 63 | 64 | ```bash 65 | version: '2' 66 | services: 67 | es: 68 | image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2 69 | cpu_shares: 100 70 | mem_limit: 3621440000 71 | environment: 72 | - discovery.type=single-node 73 | - bootstrap.memory_lock=true 74 | - "ES_JAVA_OPTS=-Xms512m -Xmx512m" 75 | logging: 76 | driver: awslogs 77 | options: 78 | awslogs-group: foodtrucks 79 | awslogs-region: us-east-1 80 | awslogs-stream-prefix: es 81 | web: 82 | image: prakhar1989/foodtrucks-web 83 | cpu_shares: 100 84 | mem_limit: 262144000 85 | ports: 86 | - "80:5000" 87 | links: 88 | - es 89 | logging: 90 | driver: awslogs 91 | options: 92 | awslogs-group: foodtrucks 93 | awslogs-region: us-east-1 94 | awslogs-stream-prefix: web 95 | ``` 96 | 97 | The only changes we made from the original `docker-compose.yml` are of providing the `mem_limit` (in bytes) and `cpu_shares` values for each container and adding some logging configuration. This allows us to view logs generated by our containers in [AWS CloudWatch](https://aws.amazon.com/cloudwatch/). Head over to CloudWatch to [create a log group](https://console.aws.amazon.com/cloudwatch/home#logsV2:log-groups/create-log-group) called `foodtrucks`. Note that since ElasticSearch typically ends up taking more memory, we've given around 3.4 GB of memory limit. Another thing we need to do before we move onto the next step is to publish our image on Docker Hub. 98 | 99 | ```bash 100 | $ docker push prakhar1989/foodtrucks-web 101 | ``` 102 | 103 | Great! Now let's run the final command that will deploy our app on ECS! 104 | 105 | ```bash 106 | $ cd aws-ecs 107 | $ ecs-cli compose up 108 | INFO[0000] Using ECS task definition TaskDefinition=ecscompose-foodtrucks:2 109 | INFO[0000] Starting container... container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/es 110 | INFO[0000] Starting container... container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/web 111 | INFO[0000] Describe ECS container status container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/web desiredStatus=RUNNING lastStatus=PENDING taskDefinition=ecscompose-foodtrucks:2 112 | INFO[0000] Describe ECS container status container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/es desiredStatus=RUNNING lastStatus=PENDING taskDefinition=ecscompose-foodtrucks:2 113 | INFO[0036] Describe ECS container status container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/es desiredStatus=RUNNING lastStatus=PENDING taskDefinition=ecscompose-foodtrucks:2 114 | INFO[0048] Describe ECS container status container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/web desiredStatus=RUNNING lastStatus=PENDING taskDefinition=ecscompose-foodtrucks:2 115 | INFO[0048] Describe ECS container status container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/es desiredStatus=RUNNING lastStatus=PENDING taskDefinition=ecscompose-foodtrucks:2 116 | INFO[0060] Started container... container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/web desiredStatus=RUNNING lastStatus=RUNNING taskDefinition=ecscompose-foodtrucks:2 117 | INFO[0060] Started container... container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/es desiredStatus=RUNNING lastStatus=RUNNING taskDefinition=ecscompose-foodtrucks:2 118 | ``` 119 | 120 | It's not a coincidence that the invocation above looks similar to the one we used with **Docker Compose**. If everything went well, you should see a `desiredStatus=RUNNING lastStatus=RUNNING` as the last line. 121 | 122 | Awesome! Our app is live, but how can we access it? 123 | 124 | ```bash 125 | ecs-cli ps 126 | Name State Ports TaskDefinition 127 | 845e2368-170d-44a7-bf9f-84c7fcd9ae29/web RUNNING 54.86.14.14:80->5000/tcp ecscompose-foodtrucks:2 128 | 845e2368-170d-44a7-bf9f-84c7fcd9ae29/es RUNNING ecscompose-foodtrucks:2 129 | ``` 130 | 131 | Go ahead and open [http://54.86.14.14](http://54.86.14.14) in your browser and you should see the Food Trucks in all its black-yellow glory! 132 | Since we're on the topic, let's see how our [AWS ECS](https://console.aws.amazon.com/ecs/home#/clusters) console looks. 133 | 134 | ![ECS cluster](../../../assets/cluster.webp) 135 | ![ECS Tasks](../../../assets/tasks.webp) 136 | 137 | We can see above that our ECS cluster called 'foodtrucks' was created and is now running 1 task with 2 container instances. Spend some time browsing this console to get a hang of all the options that are here. 138 | 139 | ### Cleanup 140 | 141 | Once you've played around with the deployed app, remember to turn down the cluster - 142 | 143 | ``` 144 | $ ecs-cli down --force 145 | INFO[0001] Waiting for your cluster resources to be deleted... 146 | INFO[0001] Cloudformation stack status stackStatus=DELETE_IN_PROGRESS 147 | INFO[0062] Cloudformation stack status stackStatus=DELETE_IN_PROGRESS 148 | INFO[0124] Cloudformation stack status stackStatus=DELETE_IN_PROGRESS 149 | INFO[0155] Deleted cluster cluster=foodtrucks 150 | ``` 151 | 152 | So there you have it. With just a few commands we were able to deploy our awesome app on the AWS cloud! 153 | -------------------------------------------------------------------------------- /starlight/src/content/docs/multi-container-environments/01-foodtrucks.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: SF Food Trucks 3 | --- 4 | 5 | The app that we're going to Dockerize is called SF Food Trucks. My goal in building this app was to have something that is useful (in that it resembles a real-world application), relies on at least one service, but is not too complex for the purpose of this tutorial. This is what I came up with. 6 | 7 | ![SF Food trucks](../../../assets/foodtrucks.webp) 8 | 9 | The app's backend is written in Python (Flask) and for search it uses [Elasticsearch](https://www.elastic.co/products/elasticsearch). Like everything else in this tutorial, the entire source is available on [Github](http://github.com/prakhar1989/FoodTrucks). We'll use this as our candidate application for learning out how to build, run and deploy a multi-container environment. 10 | 11 | First up, let's clone the repository locally. 12 | 13 | ```bash 14 | $ git clone https://github.com/prakhar1989/FoodTrucks 15 | $ cd FoodTrucks 16 | $ tree -L 2 17 | . 18 | ├── Dockerfile 19 | ├── README.md 20 | ├── aws-compose.yml 21 | ├── docker-compose.yml 22 | ├── flask-app 23 | │   ├── app.py 24 | │   ├── package-lock.json 25 | │   ├── package.json 26 | │   ├── requirements.txt 27 | │   ├── static 28 | │   ├── templates 29 | │   └── webpack.config.js 30 | ├── setup-aws-ecs.sh 31 | ├── setup-docker.sh 32 | ├── shot.png 33 | └── utils 34 | ├── generate_geojson.py 35 | └── trucks.geojson 36 | ``` 37 | 38 | The `flask-app` folder contains the Python application, while the `utils` folder has some utilities to load the data into Elasticsearch. The directory also contains some YAML files and a Dockerfile, all of which we'll see in greater detail as we progress through this tutorial. If you are curious, feel free to take a look at the files. 39 | 40 | Now that you're excited (hopefully), let's think of how we can Dockerize the app. We can see that the application consists of a Flask backend server and an Elasticsearch service. A natural way to split this app would be to have two containers - one running the Flask process and another running the Elasticsearch (ES) process. That way if our app becomes popular, we can scale it by adding more containers depending on where the bottleneck lies. 41 | 42 | Great, so we need two containers. That shouldn't be hard right? We've already built our own Flask container in the previous section. And for Elasticsearch, let's see if we can find something on the hub. 43 | 44 | ```bash 45 | $ docker search elasticsearch 46 | NAME DESCRIPTION STARS OFFICIAL AUTOMATED 47 | elasticsearch Elasticsearch is a powerful open source se... 697 [OK] 48 | itzg/elasticsearch Provides an easily configurable Elasticsea... 17 [OK] 49 | tutum/elasticsearch Elasticsearch image - listens in port 9200. 15 [OK] 50 | barnybug/elasticsearch Latest Elasticsearch 1.7.2 and previous re... 15 [OK] 51 | digitalwonderland/elasticsearch Latest Elasticsearch with Marvel & Kibana 12 [OK] 52 | monsantoco/elasticsearch ElasticSearch Docker image 9 [OK] 53 | ``` 54 | 55 | Quite unsurprisingly, there exists an officially supported [image](https://store.docker.com/images/elasticsearch) for Elasticsearch. To get ES running, we can simply use `docker run` and have a single-node ES container running locally within no time. 56 | 57 | > Note: Elastic, the company behind Elasticsearch, maintains its [own registry](https://www.docker.elastic.co/) for Elastic products. It's recommended to use the images from that registry if you plan to use Elasticsearch. 58 | 59 | Let's first pull the image 60 | 61 | ```bash 62 | $ docker pull docker.elastic.co/elasticsearch/elasticsearch:6.3.2 63 | ``` 64 | 65 | and then run it in development mode by specifying ports and setting an environment variable that configures the Elasticsearch cluster to run as a single-node. 66 | 67 | ```bash 68 | $ docker run -d --name es -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.3.2 69 | 277451c15ec183dd939e80298ea4bcf55050328a39b04124b387d668e3ed3943 70 | ``` 71 | 72 | > Note: If your container runs into memory issues, you might need to [tweak some JVM flags](https://github.com/elastic/elasticsearch-docker/issues/43#issuecomment-289377878) to limit its memory consumption. 73 | 74 | As seen above, we use `--name es` to give our container a name which makes it easy to use in subsequent commands. Once the container is started, we can see the logs by running `docker container logs` with the container name (or ID) to inspect the logs. You should see logs similar to below if Elasticsearch started successfully. 75 | 76 | > Note: Elasticsearch takes a few seconds to start so you might need to wait before you see `initialized` in the logs. 77 | 78 | ```bash 79 | $ docker container ls 80 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 81 | 277451c15ec1 docker.elastic.co/elasticsearch/elasticsearch:6.3.2 "/usr/local/bin/dock…" 2 minutes ago Up 2 minutes 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp es 82 | 83 | $ docker container logs es 84 | [2018-07-29T05:49:09,304][INFO ][o.e.n.Node ] [] initializing ... 85 | [2018-07-29T05:49:09,385][INFO ][o.e.e.NodeEnvironment ] [L1VMyzt] using [1] data paths, mounts [[/ (overlay)]], net usable_space [54.1gb], net total_space [62.7gb], types [overlay] 86 | [2018-07-29T05:49:09,385][INFO ][o.e.e.NodeEnvironment ] [L1VMyzt] heap size [990.7mb], compressed ordinary object pointers [true] 87 | [2018-07-29T05:49:11,979][INFO ][o.e.p.PluginsService ] [L1VMyzt] loaded module [x-pack-security] 88 | [2018-07-29T05:49:11,980][INFO ][o.e.p.PluginsService ] [L1VMyzt] loaded module [x-pack-sql] 89 | [2018-07-29T05:49:11,980][INFO ][o.e.p.PluginsService ] [L1VMyzt] loaded module [x-pack-upgrade] 90 | [2018-07-29T05:49:11,980][INFO ][o.e.p.PluginsService ] [L1VMyzt] loaded module [x-pack-watcher] 91 | [2018-07-29T05:49:11,981][INFO ][o.e.p.PluginsService ] [L1VMyzt] loaded plugin [ingest-geoip] 92 | [2018-07-29T05:49:11,981][INFO ][o.e.p.PluginsService ] [L1VMyzt] loaded plugin [ingest-user-agent] 93 | [2018-07-29T05:49:17,659][INFO ][o.e.d.DiscoveryModule ] [L1VMyzt] using discovery type [single-node] 94 | [2018-07-29T05:49:18,962][INFO ][o.e.n.Node ] [L1VMyzt] initialized 95 | [2018-07-29T05:49:18,963][INFO ][o.e.n.Node ] [L1VMyzt] starting ... 96 | [2018-07-29T05:49:19,218][INFO ][o.e.t.TransportService ] [L1VMyzt] publish_address {172.17.0.2:9300}, bound_addresses {0.0.0.0:9300} 97 | [2018-07-29T05:49:19,302][INFO ][o.e.x.s.t.n.SecurityNetty4HttpServerTransport] [L1VMyzt] publish_address {172.17.0.2:9200}, bound_addresses {0.0.0.0:9200} 98 | [2018-07-29T05:49:19,303][INFO ][o.e.n.Node ] [L1VMyzt] started 99 | [2018-07-29T05:49:19,439][WARN ][o.e.x.s.a.s.m.NativeRoleMappingStore] [L1VMyzt] Failed to clear cache for realms [[]] 100 | [2018-07-29T05:49:19,542][INFO ][o.e.g.GatewayService ] [L1VMyzt] recovered [0] indices into cluster_state 101 | ``` 102 | 103 | Now, lets try to see if can send a request to the Elasticsearch container. We use the `9200` port to send a `cURL` request to the container. 104 | 105 | ```bash 106 | $ curl 0.0.0.0:9200 107 | { 108 | "name" : "ijJDAOm", 109 | "cluster_name" : "docker-cluster", 110 | "cluster_uuid" : "a_nSV3XmTCqpzYYzb-LhNw", 111 | "version" : { 112 | "number" : "6.3.2", 113 | "build_flavor" : "default", 114 | "build_type" : "tar", 115 | "build_hash" : "053779d", 116 | "build_date" : "2018-07-20T05:20:23.451332Z", 117 | "build_snapshot" : false, 118 | "lucene_version" : "7.3.1", 119 | "minimum_wire_compatibility_version" : "5.6.0", 120 | "minimum_index_compatibility_version" : "5.0.0" 121 | }, 122 | "tagline" : "You Know, for Search" 123 | } 124 | ``` 125 | 126 | Sweet! It's looking good! While we are at it, let's get our Flask container running too. But before we get to that, we need a `Dockerfile`. In the last section, we used `python:3.8` image as our base image. This time, however, apart from installing Python dependencies via `pip`, we want our application to also generate our minified Javascript file for production. For this, we'll require Nodejs. Since we need a custom build step, we'll start from the `ubuntu` base image to build our `Dockerfile` from scratch. 127 | 128 | > Note: if you find that an existing image doesn't cater to your needs, feel free to start from another base image and tweak it yourself. For most of the images on Docker Hub, you should be able to find the corresponding `Dockerfile` on Github. Reading through existing Dockerfiles is one of the best ways to learn how to roll your own. 129 | 130 | Our [Dockerfile](https://github.com/prakhar1989/FoodTrucks/blob/master/Dockerfile) for the flask app looks like below - 131 | 132 | ```dockerfile 133 | # start from base 134 | FROM ubuntu:18.04 135 | 136 | MAINTAINER Prakhar Srivastav 137 | 138 | # install system-wide deps for python and node 139 | RUN apt-get -yqq update 140 | RUN apt-get -yqq install python3-pip python3-dev curl gnupg 141 | RUN curl -sL https://deb.nodesource.com/setup_10.x | bash 142 | RUN apt-get install -yq nodejs 143 | 144 | # copy our application code 145 | ADD flask-app /opt/flask-app 146 | WORKDIR /opt/flask-app 147 | 148 | # fetch app specific deps 149 | RUN npm install 150 | RUN npm run build 151 | RUN pip3 install -r requirements.txt 152 | 153 | # expose port 154 | EXPOSE 5000 155 | 156 | # start app 157 | CMD [ "python3", "./app.py" ] 158 | ``` 159 | 160 | Quite a few new things here so let's quickly go over this file. We start off with the [Ubuntu LTS](https://wiki.ubuntu.com/LTS) base image and use the package manager `apt-get` to install the dependencies namely - Python and Node. The `yqq` flag is used to suppress output and assumes "Yes" to all prompts. 161 | 162 | We then use the `ADD` command to copy our application into a new volume in the container - `/opt/flask-app`. This is where our code will reside. We also set this as our working directory, so that the following commands will be run in the context of this location. Now that our system-wide dependencies are installed, we get around to installing app-specific ones. First off we tackle Node by installing the packages from npm and running the build command as defined in our `package.json` [file](https://github.com/prakhar1989/FoodTrucks/blob/master/flask-app/package.json#L7-L9). We finish the file off by installing the Python packages, exposing the port and defining the `CMD` to run as we did in the last section. 163 | 164 | Finally, we can go ahead, build the image and run the container (replace `prakhar1989` with your username below). 165 | 166 | ```bash 167 | $ docker build -t prakhar1989/foodtrucks-web . 168 | ``` 169 | 170 | In the first run, this will take some time as the Docker client will download the ubuntu image, run all the commands and prepare your image. Re-running `docker build` after any subsequent changes you make to the application code will almost be instantaneous. Now let's try running our app. 171 | 172 | ```bash 173 | $ docker run -P --rm prakhar1989/foodtrucks-web 174 | Unable to connect to ES. Retying in 5 secs... 175 | Unable to connect to ES. Retying in 5 secs... 176 | Unable to connect to ES. Retying in 5 secs... 177 | Out of retries. Bailing out... 178 | ``` 179 | 180 | Oops! Our flask app was unable to run since it was unable to connect to Elasticsearch. How do we tell one container about the other container and get them to talk to each other? The answer lies in the next section. -------------------------------------------------------------------------------- /static-site/html/css/skeleton.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skeleton V2.0.4 3 | * Copyright 2014, Dave Gamache 4 | * www.getskeleton.com 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 12/29/2014 8 | */ 9 | 10 | 11 | /* Table of contents 12 | –––––––––––––––––––––––––––––––––––––––––––––––––– 13 | - Grid 14 | - Base Styles 15 | - Typography 16 | - Links 17 | - Buttons 18 | - Forms 19 | - Lists 20 | - Code 21 | - Tables 22 | - Spacing 23 | - Utilities 24 | - Clearing 25 | - Media Queries 26 | */ 27 | 28 | 29 | /* Grid 30 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 31 | .container { 32 | position: relative; 33 | width: 100%; 34 | max-width: 960px; 35 | margin: 0 auto; 36 | padding: 0 20px; 37 | box-sizing: border-box; } 38 | .column, 39 | .columns { 40 | width: 100%; 41 | float: left; 42 | box-sizing: border-box; } 43 | 44 | /* For devices larger than 400px */ 45 | @media (min-width: 400px) { 46 | .container { 47 | width: 85%; 48 | padding: 0; } 49 | } 50 | 51 | /* For devices larger than 550px */ 52 | @media (min-width: 550px) { 53 | .container { 54 | width: 80%; } 55 | .column, 56 | .columns { 57 | margin-left: 4%; } 58 | .column:first-child, 59 | .columns:first-child { 60 | margin-left: 0; } 61 | 62 | .one.column, 63 | .one.columns { width: 4.66666666667%; } 64 | .two.columns { width: 13.3333333333%; } 65 | .three.columns { width: 22%; } 66 | .four.columns { width: 30.6666666667%; } 67 | .five.columns { width: 39.3333333333%; } 68 | .six.columns { width: 48%; } 69 | .seven.columns { width: 56.6666666667%; } 70 | .eight.columns { width: 65.3333333333%; } 71 | .nine.columns { width: 74.0%; } 72 | .ten.columns { width: 82.6666666667%; } 73 | .eleven.columns { width: 91.3333333333%; } 74 | .twelve.columns { width: 100%; margin-left: 0; } 75 | 76 | .one-third.column { width: 30.6666666667%; } 77 | .two-thirds.column { width: 65.3333333333%; } 78 | 79 | .one-half.column { width: 48%; } 80 | 81 | /* Offsets */ 82 | .offset-by-one.column, 83 | .offset-by-one.columns { margin-left: 8.66666666667%; } 84 | .offset-by-two.column, 85 | .offset-by-two.columns { margin-left: 17.3333333333%; } 86 | .offset-by-three.column, 87 | .offset-by-three.columns { margin-left: 26%; } 88 | .offset-by-four.column, 89 | .offset-by-four.columns { margin-left: 34.6666666667%; } 90 | .offset-by-five.column, 91 | .offset-by-five.columns { margin-left: 43.3333333333%; } 92 | .offset-by-six.column, 93 | .offset-by-six.columns { margin-left: 52%; } 94 | .offset-by-seven.column, 95 | .offset-by-seven.columns { margin-left: 60.6666666667%; } 96 | .offset-by-eight.column, 97 | .offset-by-eight.columns { margin-left: 69.3333333333%; } 98 | .offset-by-nine.column, 99 | .offset-by-nine.columns { margin-left: 78.0%; } 100 | .offset-by-ten.column, 101 | .offset-by-ten.columns { margin-left: 86.6666666667%; } 102 | .offset-by-eleven.column, 103 | .offset-by-eleven.columns { margin-left: 95.3333333333%; } 104 | 105 | .offset-by-one-third.column, 106 | .offset-by-one-third.columns { margin-left: 34.6666666667%; } 107 | .offset-by-two-thirds.column, 108 | .offset-by-two-thirds.columns { margin-left: 69.3333333333%; } 109 | 110 | .offset-by-one-half.column, 111 | .offset-by-one-half.columns { margin-left: 52%; } 112 | 113 | } 114 | 115 | 116 | /* Base Styles 117 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 118 | /* NOTE 119 | html is set to 62.5% so that all the REM measurements throughout Skeleton 120 | are based on 10px sizing. So basically 1.5rem = 15px :) */ 121 | html { 122 | font-size: 62.5%; } 123 | body { 124 | font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */ 125 | line-height: 1.6; 126 | font-weight: 400; 127 | font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; 128 | color: #222; } 129 | 130 | 131 | /* Typography 132 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 133 | h1, h2, h3, h4, h5, h6 { 134 | margin-top: 0; 135 | margin-bottom: 2rem; 136 | font-weight: 300; } 137 | h1 { font-size: 4.0rem; line-height: 1.2; letter-spacing: -.1rem;} 138 | h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; } 139 | h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; } 140 | h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; } 141 | h5 { font-size: 1.8rem; line-height: 1.5; letter-spacing: -.05rem; } 142 | h6 { font-size: 1.5rem; line-height: 1.6; letter-spacing: 0; } 143 | 144 | /* Larger than phablet */ 145 | @media (min-width: 550px) { 146 | h1 { font-size: 5.0rem; } 147 | h2 { font-size: 4.2rem; } 148 | h3 { font-size: 3.6rem; } 149 | h4 { font-size: 3.0rem; } 150 | h5 { font-size: 2.4rem; } 151 | h6 { font-size: 1.5rem; } 152 | } 153 | 154 | p { 155 | margin-top: 0; } 156 | 157 | 158 | /* Links 159 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 160 | a { 161 | color: #1EAEDB; } 162 | a:hover { 163 | color: #0FA0CE; } 164 | 165 | 166 | /* Buttons 167 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 168 | .button, 169 | button, 170 | input[type="submit"], 171 | input[type="reset"], 172 | input[type="button"] { 173 | display: inline-block; 174 | height: 38px; 175 | padding: 0 30px; 176 | color: #555; 177 | text-align: center; 178 | font-size: 11px; 179 | font-weight: 600; 180 | line-height: 38px; 181 | letter-spacing: .1rem; 182 | text-transform: uppercase; 183 | text-decoration: none; 184 | white-space: nowrap; 185 | background-color: transparent; 186 | border-radius: 4px; 187 | border: 1px solid #bbb; 188 | cursor: pointer; 189 | box-sizing: border-box; } 190 | .button:hover, 191 | button:hover, 192 | input[type="submit"]:hover, 193 | input[type="reset"]:hover, 194 | input[type="button"]:hover, 195 | .button:focus, 196 | button:focus, 197 | input[type="submit"]:focus, 198 | input[type="reset"]:focus, 199 | input[type="button"]:focus { 200 | color: #333; 201 | border-color: #888; 202 | outline: 0; } 203 | .button.button-primary, 204 | button.button-primary, 205 | input[type="submit"].button-primary, 206 | input[type="reset"].button-primary, 207 | input[type="button"].button-primary { 208 | color: #FFF; 209 | background-color: #33C3F0; 210 | border-color: #33C3F0; } 211 | .button.button-primary:hover, 212 | button.button-primary:hover, 213 | input[type="submit"].button-primary:hover, 214 | input[type="reset"].button-primary:hover, 215 | input[type="button"].button-primary:hover, 216 | .button.button-primary:focus, 217 | button.button-primary:focus, 218 | input[type="submit"].button-primary:focus, 219 | input[type="reset"].button-primary:focus, 220 | input[type="button"].button-primary:focus { 221 | color: #FFF; 222 | background-color: #1EAEDB; 223 | border-color: #1EAEDB; } 224 | 225 | 226 | /* Forms 227 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 228 | input[type="email"], 229 | input[type="number"], 230 | input[type="search"], 231 | input[type="text"], 232 | input[type="tel"], 233 | input[type="url"], 234 | input[type="password"], 235 | textarea, 236 | select { 237 | height: 38px; 238 | padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ 239 | background-color: #fff; 240 | border: 1px solid #D1D1D1; 241 | border-radius: 4px; 242 | box-shadow: none; 243 | box-sizing: border-box; } 244 | /* Removes awkward default styles on some inputs for iOS */ 245 | input[type="email"], 246 | input[type="number"], 247 | input[type="search"], 248 | input[type="text"], 249 | input[type="tel"], 250 | input[type="url"], 251 | input[type="password"], 252 | textarea { 253 | -webkit-appearance: none; 254 | -moz-appearance: none; 255 | appearance: none; } 256 | textarea { 257 | min-height: 65px; 258 | padding-top: 6px; 259 | padding-bottom: 6px; } 260 | input[type="email"]:focus, 261 | input[type="number"]:focus, 262 | input[type="search"]:focus, 263 | input[type="text"]:focus, 264 | input[type="tel"]:focus, 265 | input[type="url"]:focus, 266 | input[type="password"]:focus, 267 | textarea:focus, 268 | select:focus { 269 | border: 1px solid #33C3F0; 270 | outline: 0; } 271 | label, 272 | legend { 273 | display: block; 274 | margin-bottom: .5rem; 275 | font-weight: 600; } 276 | fieldset { 277 | padding: 0; 278 | border-width: 0; } 279 | input[type="checkbox"], 280 | input[type="radio"] { 281 | display: inline; } 282 | label > .label-body { 283 | display: inline-block; 284 | margin-left: .5rem; 285 | font-weight: normal; } 286 | 287 | 288 | /* Lists 289 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 290 | ul { 291 | list-style: circle inside; } 292 | ol { 293 | list-style: decimal inside; } 294 | ol, ul { 295 | padding-left: 0; 296 | margin-top: 0; } 297 | ul ul, 298 | ul ol, 299 | ol ol, 300 | ol ul { 301 | margin: 1.5rem 0 1.5rem 3rem; 302 | font-size: 90%; } 303 | li { 304 | margin-bottom: 1rem; } 305 | 306 | 307 | /* Code 308 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 309 | code { 310 | padding: .2rem .5rem; 311 | margin: 0 .2rem; 312 | font-size: 90%; 313 | white-space: nowrap; 314 | background: #F1F1F1; 315 | border: 1px solid #E1E1E1; 316 | border-radius: 4px; } 317 | pre > code { 318 | display: block; 319 | padding: 1rem 1.5rem; 320 | white-space: pre; } 321 | 322 | 323 | /* Tables 324 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 325 | th, 326 | td { 327 | padding: 12px 15px; 328 | text-align: left; 329 | border-bottom: 1px solid #E1E1E1; } 330 | th:first-child, 331 | td:first-child { 332 | padding-left: 0; } 333 | th:last-child, 334 | td:last-child { 335 | padding-right: 0; } 336 | 337 | 338 | /* Spacing 339 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 340 | button, 341 | .button { 342 | margin-bottom: 1rem; } 343 | input, 344 | textarea, 345 | select, 346 | fieldset { 347 | margin-bottom: 1.5rem; } 348 | pre, 349 | blockquote, 350 | dl, 351 | figure, 352 | table, 353 | p, 354 | ul, 355 | ol, 356 | form { 357 | margin-bottom: 2.5rem; } 358 | 359 | 360 | /* Utilities 361 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 362 | .u-full-width { 363 | width: 100%; 364 | box-sizing: border-box; } 365 | .u-max-full-width { 366 | max-width: 100%; 367 | box-sizing: border-box; } 368 | .u-pull-right { 369 | float: right; } 370 | .u-pull-left { 371 | float: left; } 372 | 373 | 374 | /* Misc 375 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 376 | hr { 377 | margin-top: 3rem; 378 | margin-bottom: 3.5rem; 379 | border-width: 0; 380 | border-top: 1px solid #E1E1E1; } 381 | 382 | 383 | /* Clearing 384 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 385 | 386 | /* Self Clearing Goodness */ 387 | .container:after, 388 | .row:after, 389 | .u-cf { 390 | content: ""; 391 | display: table; 392 | clear: both; } 393 | 394 | 395 | /* Media Queries 396 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 397 | /* 398 | Note: The best way to structure the use of media queries is to create the queries 399 | near the relevant code. For example, if you wanted to change the styles for buttons 400 | on small devices, paste the mobile query code up in the buttons section and style it 401 | there. 402 | */ 403 | 404 | 405 | /* Larger than mobile */ 406 | @media (min-width: 400px) {} 407 | 408 | /* Larger than phablet (also point when grid becomes active) */ 409 | @media (min-width: 550px) {} 410 | 411 | /* Larger than tablet */ 412 | @media (min-width: 750px) {} 413 | 414 | /* Larger than desktop */ 415 | @media (min-width: 1000px) {} 416 | 417 | /* Larger than Desktop HD */ 418 | @media (min-width: 1200px) {} 419 | -------------------------------------------------------------------------------- /starlight/src/content/docs/multi-container-environments/02-docker-network.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Docker Network 3 | --- 4 | 5 | Before we talk about the features Docker provides especially to deal with such scenarios, let's see if we can figure out a way to get around the problem. Hopefully, this should give you an appreciation for the specific feature that we are going to study. 6 | 7 | Okay, so let's run `docker container ls` (which is same as `docker ps`) and see what we have. 8 | 9 | ```bash 10 | $ docker container ls 11 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 12 | 277451c15ec1 docker.elastic.co/elasticsearch/elasticsearch:6.3.2 "/usr/local/bin/dock…" 17 minutes ago Up 17 minutes 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp es 13 | ``` 14 | 15 | So we have one ES container running on `0.0.0.0:9200` port which we can directly access. If we can tell our Flask app to connect to this URL, it should be able to connect and talk to ES, right? Let's dig into our [Python code](https://github.com/prakhar1989/FoodTrucks/blob/master/flask-app/app.py#L7) and see how the connection details are defined. 16 | 17 | ```python 18 | es = Elasticsearch(host='es') 19 | ``` 20 | 21 | To make this work, we need to tell the Flask container that the ES container is running on `0.0.0.0` host (the port by default is `9200`) and that should make it work, right? Unfortunately, that is not correct since the IP `0.0.0.0` is the IP to access ES container from the **host machine** i.e. from my Mac. Another container will not be able to access this on the same IP address. Okay if not that IP, then which IP address should the ES container be accessible by? I'm glad you asked this question. 22 | 23 | Now is a good time to start our exploration of networking in Docker. When docker is installed, it creates three networks automatically. 24 | 25 | ```bash 26 | $ docker network ls 27 | NETWORK ID NAME DRIVER SCOPE 28 | c2c695315b3a bridge bridge local 29 | a875bec5d6fd host host local 30 | ead0e804a67b none null local 31 | ``` 32 | 33 | The **bridge** network is the network in which containers are run by default. So that means that when I ran the ES container, it was running in this bridge network. To validate this, let's inspect the network. 34 | 35 | ```bash 36 | $ docker network inspect bridge 37 | [ 38 | { 39 | "Name": "bridge", 40 | "Id": "c2c695315b3aaf8fc30530bb3c6b8f6692cedd5cc7579663f0550dfdd21c9a26", 41 | "Created": "2018-07-28T20:32:39.405687265Z", 42 | "Scope": "local", 43 | "Driver": "bridge", 44 | "EnableIPv6": false, 45 | "IPAM": { 46 | "Driver": "default", 47 | "Options": null, 48 | "Config": [ 49 | { 50 | "Subnet": "172.17.0.0/16", 51 | "Gateway": "172.17.0.1" 52 | } 53 | ] 54 | }, 55 | "Internal": false, 56 | "Attachable": false, 57 | "Ingress": false, 58 | "ConfigFrom": { 59 | "Network": "" 60 | }, 61 | "ConfigOnly": false, 62 | "Containers": { 63 | "277451c15ec183dd939e80298ea4bcf55050328a39b04124b387d668e3ed3943": { 64 | "Name": "es", 65 | "EndpointID": "5c417a2fc6b13d8ec97b76bbd54aaf3ee2d48f328c3f7279ee335174fbb4d6bb", 66 | "MacAddress": "02:42:ac:11:00:02", 67 | "IPv4Address": "172.17.0.2/16", 68 | "IPv6Address": "" 69 | } 70 | }, 71 | "Options": { 72 | "com.docker.network.bridge.default_bridge": "true", 73 | "com.docker.network.bridge.enable_icc": "true", 74 | "com.docker.network.bridge.enable_ip_masquerade": "true", 75 | "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", 76 | "com.docker.network.bridge.name": "docker0", 77 | "com.docker.network.driver.mtu": "1500" 78 | }, 79 | "Labels": {} 80 | } 81 | ] 82 | ``` 83 | 84 | You can see that our container `277451c15ec1` is listed under the `Containers` section in the output. What we also see is the IP address this container has been allotted - `172.17.0.2`. Is this the IP address that we're looking for? Let's find out by running our flask container and trying to access this IP. 85 | 86 | ```bash 87 | $ docker run -it --rm prakhar1989/foodtrucks-web bash 88 | root@35180ccc206a:/opt/flask-app# curl 172.17.0.2:9200 89 | { 90 | "name" : "Jane Foster", 91 | "cluster_name" : "elasticsearch", 92 | "version" : { 93 | "number" : "2.1.1", 94 | "build_hash" : "40e2c53a6b6c2972b3d13846e450e66f4375bd71", 95 | "build_timestamp" : "2015-12-15T13:05:55Z", 96 | "build_snapshot" : false, 97 | "lucene_version" : "5.3.1" 98 | }, 99 | "tagline" : "You Know, for Search" 100 | } 101 | root@35180ccc206a:/opt/flask-app# exit 102 | ``` 103 | 104 | This should be fairly straightforward to you by now. We start the container in the interactive mode with the `bash` process. The `--rm` is a convenient flag for running one off commands since the container gets cleaned up when its work is done. We try a `curl` but we need to install it first. Once we do that, we see that we can indeed talk to ES on `172.17.0.2:9200`. Awesome! 105 | 106 | Although we have figured out a way to make the containers talk to each other, there are still two problems with this approach - 107 | 108 | 1. How do we tell the Flask container that `es` hostname stands for `172.17.0.2` or some other IP since the IP can change? 109 | 110 | 2. Since the _bridge_ network is shared by every container by default, this method is **not secure**. How do we isolate our network? 111 | 112 | The good news that Docker has a great answer to our questions. It allows us to define our own networks while keeping them isolated using the `docker network` command. 113 | 114 | Let's first go ahead and create our own network. 115 | 116 | ```bash 117 | $ docker network create foodtrucks-net 118 | 0815b2a3bb7a6608e850d05553cc0bda98187c4528d94621438f31d97a6fea3c 119 | 120 | $ docker network ls 121 | NETWORK ID NAME DRIVER SCOPE 122 | c2c695315b3a bridge bridge local 123 | 0815b2a3bb7a foodtrucks-net bridge local 124 | a875bec5d6fd host host local 125 | ead0e804a67b none null local 126 | ``` 127 | 128 | The `network create` command creates a new _bridge_ network, which is what we need at the moment. In terms of Docker, a bridge network uses a software bridge which allows containers connected to the same bridge network to communicate, while providing isolation from containers which are not connected to that bridge network. The Docker bridge driver automatically installs rules in the host machine so that containers on different bridge networks cannot communicate directly with each other. There are other kinds of networks that you can create, and you are encouraged to read about them in the official [docs](https://docs.docker.com/engine/userguide/networking/dockernetworks/). 129 | 130 | Now that we have a network, we can launch our containers inside this network using the `--net` flag. Let's do that - but first, in order to launch a new container with the same name, we will stop and remove our ES container that is running in the bridge (default) network. 131 | 132 | ```bash 133 | $ docker container stop es 134 | es 135 | 136 | $ docker container rm es 137 | es 138 | 139 | $ docker run -d --name es --net foodtrucks-net -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.3.2 140 | 13d6415f73c8d88bddb1f236f584b63dbaf2c3051f09863a3f1ba219edba3673 141 | 142 | $ docker network inspect foodtrucks-net 143 | [ 144 | { 145 | "Name": "foodtrucks-net", 146 | "Id": "0815b2a3bb7a6608e850d05553cc0bda98187c4528d94621438f31d97a6fea3c", 147 | "Created": "2018-07-30T00:01:29.1500984Z", 148 | "Scope": "local", 149 | "Driver": "bridge", 150 | "EnableIPv6": false, 151 | "IPAM": { 152 | "Driver": "default", 153 | "Options": {}, 154 | "Config": [ 155 | { 156 | "Subnet": "172.18.0.0/16", 157 | "Gateway": "172.18.0.1" 158 | } 159 | ] 160 | }, 161 | "Internal": false, 162 | "Attachable": false, 163 | "Ingress": false, 164 | "ConfigFrom": { 165 | "Network": "" 166 | }, 167 | "ConfigOnly": false, 168 | "Containers": { 169 | "13d6415f73c8d88bddb1f236f584b63dbaf2c3051f09863a3f1ba219edba3673": { 170 | "Name": "es", 171 | "EndpointID": "29ba2d33f9713e57eb6b38db41d656e4ee2c53e4a2f7cf636bdca0ec59cd3aa7", 172 | "MacAddress": "02:42:ac:12:00:02", 173 | "IPv4Address": "172.18.0.2/16", 174 | "IPv6Address": "" 175 | } 176 | }, 177 | "Options": {}, 178 | "Labels": {} 179 | } 180 | ] 181 | ``` 182 | 183 | As you can see, our `es` container is now running inside the `foodtrucks-net` bridge network. Now let's inspect what happens when we launch in our `foodtrucks-net` network. 184 | 185 | ```bash 186 | $ docker run -it --rm --net foodtrucks-net prakhar1989/foodtrucks-web bash 187 | root@9d2722cf282c:/opt/flask-app# curl es:9200 188 | { 189 | "name" : "wWALl9M", 190 | "cluster_name" : "docker-cluster", 191 | "cluster_uuid" : "BA36XuOiRPaghPNBLBHleQ", 192 | "version" : { 193 | "number" : "6.3.2", 194 | "build_flavor" : "default", 195 | "build_type" : "tar", 196 | "build_hash" : "053779d", 197 | "build_date" : "2018-07-20T05:20:23.451332Z", 198 | "build_snapshot" : false, 199 | "lucene_version" : "7.3.1", 200 | "minimum_wire_compatibility_version" : "5.6.0", 201 | "minimum_index_compatibility_version" : "5.0.0" 202 | }, 203 | "tagline" : "You Know, for Search" 204 | } 205 | root@53af252b771a:/opt/flask-app# ls 206 | app.py node_modules package.json requirements.txt static templates webpack.config.js 207 | root@53af252b771a:/opt/flask-app# python3 app.py 208 | Index not found... 209 | Loading data in elasticsearch ... 210 | Total trucks loaded: 733 211 | * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) 212 | root@53af252b771a:/opt/flask-app# exit 213 | ``` 214 | 215 | Wohoo! That works! On user-defined networks like foodtrucks-net, containers can not only communicate by IP address, but can also resolve a container name to an IP address. This capability is called _automatic service discovery_. Great! Let's launch our Flask container for real now - 216 | 217 | ```bash 218 | $ docker run -d --net foodtrucks-net -p 5000:5000 --name foodtrucks-web prakhar1989/foodtrucks-web 219 | 852fc74de2954bb72471b858dce64d764181dca0cf7693fed201d76da33df794 220 | 221 | $ docker container ls 222 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 223 | 852fc74de295 prakhar1989/foodtrucks-web "python3 ./app.py" About a minute ago Up About a minute 0.0.0.0:5000->5000/tcp foodtrucks-web 224 | 13d6415f73c8 docker.elastic.co/elasticsearch/elasticsearch:6.3.2 "/usr/local/bin/dock…" 17 minutes ago Up 17 minutes 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp es 225 | 226 | $ curl -I 0.0.0.0:5000 227 | HTTP/1.0 200 OK 228 | Content-Type: text/html; charset=utf-8 229 | Content-Length: 3697 230 | Server: Werkzeug/0.11.2 Python/2.7.6 231 | Date: Sun, 10 Jan 2016 23:58:53 GMT 232 | ``` 233 | 234 | Head over to [http://0.0.0.0:5000](http://0.0.0.0:5000) and see your glorious app live! Although that might have seemed like a lot of work, we actually just typed 4 commands to go from zero to running. I've collated the commands in a [bash script](https://github.com/prakhar1989/FoodTrucks/blob/master/setup-docker.sh). 235 | 236 | ```bash 237 | #!/bin/bash 238 | 239 | # build the flask container 240 | docker build -t prakhar1989/foodtrucks-web . 241 | 242 | # create the network 243 | docker network create foodtrucks-net 244 | 245 | # start the ES container 246 | docker run -d --name es --net foodtrucks-net -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.3.2 247 | 248 | # start the flask app container 249 | docker run -d --net foodtrucks-net -p 5000:5000 --name foodtrucks-web prakhar1989/foodtrucks-web 250 | ``` 251 | 252 | Now imagine you are distributing your app to a friend, or running on a server that has docker installed. You can get a whole app running with just one command! 253 | 254 | ```bash 255 | $ git clone https://github.com/prakhar1989/FoodTrucks 256 | $ cd FoodTrucks 257 | $ ./setup-docker.sh 258 | ``` 259 | 260 | And that's it! If you ask me, I find this to be an extremely awesome, and a powerful way of sharing and running your applications! 261 | -------------------------------------------------------------------------------- /starlight/src/content/docs/multi-container-environments/03-docker-compose.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Docker Compose 3 | --- 4 | 5 | 6 | Till now we've spent all our time exploring the Docker client. In the Docker ecosystem, however, there are a bunch of other open-source tools which play very nicely with Docker. A few of them are - 7 | 8 | 1. [Docker Machine](https://docs.docker.com/machine/) - Create Docker hosts on your computer, on cloud providers, and inside your own data center 9 | 2. [Docker Compose](https://docs.docker.com/compose/) - A tool for defining and running multi-container Docker applications. 10 | 3. [Docker Swarm](https://docs.docker.com/swarm/) - A native clustering solution for Docker 11 | 4. [Kubernetes](https://kubernetes.io) - Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications. 12 | 13 | In this section, we are going to look at one of these tools, Docker Compose, and see how it can make dealing with multi-container apps easier. 14 | 15 | The background story of Docker Compose is quite interesting. Roughly around January 2014, a company called OrchardUp launched a tool called Fig. The idea behind Fig was to make isolated development environments work with Docker. The project was very well received on [Hacker News](https://news.ycombinator.com/item?id=7132044) - I oddly remember reading about it but didn't quite get the hang of it. 16 | 17 | The [first comment](https://news.ycombinator.com/item?id=7133449) on the forum actually does a good job of explaining what Fig is all about. 18 | 19 | > So really at this point, that's what Docker is about: running processes. Now Docker offers a quite rich API to run the processes: shared volumes (directories) between containers (i.e. running images), forward port from the host to the container, display logs, and so on. But that's it: Docker as of now, remains at the process level. 20 | 21 | > While it provides options to orchestrate multiple containers to create a single "app", it doesn't address the management of such group of containers as a single entity. 22 | > And that's where tools such as Fig come in: talking about a group of containers as a single entity. Think "run an app" (i.e. "run an orchestrated cluster of containers") instead of "run a container". 23 | 24 | It turns out that a lot of people using docker agree with this sentiment. Slowly and steadily as Fig became popular, Docker Inc. took notice, [acquired the company](https://www.docker.com/blog/welcoming-the-orchard-and-fig-team/) and re-branded Fig as Docker Compose. 25 | 26 | So what is _Compose_ used for? Compose is a tool that is used for defining and running multi-container Docker apps in an easy way. It provides a configuration file called `docker-compose.yml` that can be used to bring up an application and the suite of services it depends on with just one command. Compose works in all environments: production, staging, development, testing, as well as CI workflows, although Compose is ideal for development and testing environments. 27 | 28 | Let's see if we can create a `docker-compose.yml` file for our SF-Foodtrucks app and evaluate whether Docker Compose lives up to its promise. 29 | 30 | The first step, however, is to install Docker Compose. If you're running Windows or Mac, Docker Compose is already installed as it comes in the Docker Toolbox. Linux users can easily get their hands on Docker Compose by following the [instructions](https://docs.docker.com/compose/install/) on the docs. Since Compose is written in Python, you can also simply do `pip install docker-compose`. Test your installation with - 31 | 32 | ```bash 33 | $ docker-compose --version 34 | docker-compose version 1.21.2, build a133471 35 | ``` 36 | 37 | Now that we have it installed, we can jump on the next step i.e. the Docker Compose file `docker-compose.yml`. The syntax for YAML is quite simple and the repo already contains the docker-compose [file](https://github.com/prakhar1989/FoodTrucks/blob/master/docker-compose.yml) that we'll be using. 38 | 39 | ```yaml 40 | version: "3" 41 | services: 42 | es: 43 | image: docker.elastic.co/elasticsearch/elasticsearch:6.3.2 44 | container_name: es 45 | environment: 46 | - discovery.type=single-node 47 | ports: 48 | - 9200:9200 49 | volumes: 50 | - esdata1:/usr/share/elasticsearch/data 51 | web: 52 | image: prakhar1989/foodtrucks-web 53 | command: python3 app.py 54 | depends_on: 55 | - es 56 | ports: 57 | - 5000:5000 58 | volumes: 59 | - ./flask-app:/opt/flask-app 60 | volumes: 61 | esdata1: 62 | driver: local 63 | ``` 64 | 65 | Let me breakdown what the file above means. At the parent level, we define the names of our services - `es` and `web`. The `image` parameter is always required, and for each service that we want Docker to run, we can add additional parameters. For `es`, we just refer to the `elasticsearch` image available on Elastic registry. For our Flask app, we refer to the image that we built at the beginning of this section. 66 | 67 | Other parameters such as `command` and `ports` provide more information about the container. The `volumes` parameter specifies a mount point in our `web` container where the code will reside. This is purely optional and is useful if you need access to logs, etc. We'll later see how this can be useful during development. Refer to the [online reference](https://docs.docker.com/compose/compose-file) to learn more about the parameters this file supports. We also add volumes for the `es` container so that the data we load persists between restarts. We also specify `depends_on`, which tells docker to start the `es` container before `web`. You can read more about it on [docker compose docs](https://docs.docker.com/compose/compose-file/#depends_on). 68 | 69 | > Note: You must be inside the directory with the `docker-compose.yml` file in order to execute most Compose commands. 70 | 71 | Great! Now the file is ready, let's see `docker-compose` in action. But before we start, we need to make sure the ports and names are free. So if you have the Flask and ES containers running, lets turn them off. 72 | 73 | ```bash 74 | $ docker stop es foodtrucks-web 75 | es 76 | foodtrucks-web 77 | 78 | $ docker rm es foodtrucks-web 79 | es 80 | foodtrucks-web 81 | ``` 82 | 83 | Now we can run `docker-compose`. Navigate to the food trucks directory and run `docker-compose up`. 84 | 85 | ```bash 86 | $ docker-compose up 87 | Creating network "foodtrucks_default" with the default driver 88 | Creating foodtrucks_es_1 89 | Creating foodtrucks_web_1 90 | Attaching to foodtrucks_es_1, foodtrucks_web_1 91 | es_1 | [2016-01-11 03:43:50,300][INFO ][node ] [Comet] version[2.1.1], pid[1], build[40e2c53/2015-12-15T13:05:55Z] 92 | es_1 | [2016-01-11 03:43:50,307][INFO ][node ] [Comet] initializing ... 93 | es_1 | [2016-01-11 03:43:50,366][INFO ][plugins ] [Comet] loaded [], sites [] 94 | es_1 | [2016-01-11 03:43:50,421][INFO ][env ] [Comet] using [1] data paths, mounts [[/usr/share/elasticsearch/data (/dev/sda1)]], net usable_space [16gb], net total_space [18.1gb], spins? [possibly], types [ext4] 95 | es_1 | [2016-01-11 03:43:52,626][INFO ][node ] [Comet] initialized 96 | es_1 | [2016-01-11 03:43:52,632][INFO ][node ] [Comet] starting ... 97 | es_1 | [2016-01-11 03:43:52,703][WARN ][common.network ] [Comet] publish address: {0.0.0.0} is a wildcard address, falling back to first non-loopback: {172.17.0.2} 98 | es_1 | [2016-01-11 03:43:52,704][INFO ][transport ] [Comet] publish_address {172.17.0.2:9300}, bound_addresses {[::]:9300} 99 | es_1 | [2016-01-11 03:43:52,721][INFO ][discovery ] [Comet] elasticsearch/cEk4s7pdQ-evRc9MqS2wqw 100 | es_1 | [2016-01-11 03:43:55,785][INFO ][cluster.service ] [Comet] new_master {Comet}{cEk4s7pdQ-evRc9MqS2wqw}{172.17.0.2}{172.17.0.2:9300}, reason: zen-disco-join(elected_as_master, [0] joins received) 101 | es_1 | [2016-01-11 03:43:55,818][WARN ][common.network ] [Comet] publish address: {0.0.0.0} is a wildcard address, falling back to first non-loopback: {172.17.0.2} 102 | es_1 | [2016-01-11 03:43:55,819][INFO ][http ] [Comet] publish_address {172.17.0.2:9200}, bound_addresses {[::]:9200} 103 | es_1 | [2016-01-11 03:43:55,819][INFO ][node ] [Comet] started 104 | es_1 | [2016-01-11 03:43:55,826][INFO ][gateway ] [Comet] recovered [0] indices into cluster_state 105 | es_1 | [2016-01-11 03:44:01,825][INFO ][cluster.metadata ] [Comet] [sfdata] creating index, cause [auto(index api)], templates [], shards [5]/[1], mappings [truck] 106 | es_1 | [2016-01-11 03:44:02,373][INFO ][cluster.metadata ] [Comet] [sfdata] update_mapping [truck] 107 | es_1 | [2016-01-11 03:44:02,510][INFO ][cluster.metadata ] [Comet] [sfdata] update_mapping [truck] 108 | es_1 | [2016-01-11 03:44:02,593][INFO ][cluster.metadata ] [Comet] [sfdata] update_mapping [truck] 109 | es_1 | [2016-01-11 03:44:02,708][INFO ][cluster.metadata ] [Comet] [sfdata] update_mapping [truck] 110 | es_1 | [2016-01-11 03:44:03,047][INFO ][cluster.metadata ] [Comet] [sfdata] update_mapping [truck] 111 | web_1 | * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) 112 | ``` 113 | 114 | Head over to the IP to see your app live. That was amazing wasn't it? Just a few lines of configuration and we have two Docker containers running successfully in unison. Let's stop the services and re-run in detached mode. 115 | 116 | ```bash 117 | web_1 | * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) 118 | Killing foodtrucks_web_1 ... done 119 | Killing foodtrucks_es_1 ... done 120 | 121 | $ docker-compose up -d 122 | Creating es ... done 123 | Creating foodtrucks_web_1 ... done 124 | 125 | $ docker-compose ps 126 | Name Command State Ports 127 | -------------------------------------------------------------------------------------------- 128 | es /usr/local/bin/docker-entr ... Up 0.0.0.0:9200->9200/tcp, 9300/tcp 129 | foodtrucks_web_1 python3 app.py Up 0.0.0.0:5000->5000/tcp 130 | ``` 131 | 132 | Unsurprisingly, we can see both the containers running successfully. Where do the names come from? Those were created automatically by Compose. But does _Compose_ also create the network automatically? Good question! Let's find out. 133 | 134 | First off, let us stop the services from running. We can always bring them back up in just one command. Data volumes will persist, so it’s possible to start the cluster again with the same data using docker-compose up. To destroy the cluster and the data volumes, just type `docker-compose down -v`. 135 | 136 | ```bash 137 | $ docker-compose down -v 138 | Stopping foodtrucks_web_1 ... done 139 | Stopping es ... done 140 | Removing foodtrucks_web_1 ... done 141 | Removing es ... done 142 | Removing network foodtrucks_default 143 | Removing volume foodtrucks_esdata1 144 | ``` 145 | 146 | While we're are at it, we'll also remove the `foodtrucks` network that we created last time. 147 | 148 | ```bash 149 | $ docker network rm foodtrucks-net 150 | $ docker network ls 151 | NETWORK ID NAME DRIVER SCOPE 152 | c2c695315b3a bridge bridge local 153 | a875bec5d6fd host host local 154 | ead0e804a67b none null local 155 | ``` 156 | 157 | Great! Now that we have a clean slate, let's re-run our services and see if _Compose_ does its magic. 158 | 159 | ```bash 160 | $ docker-compose up -d 161 | Recreating foodtrucks_es_1 162 | Recreating foodtrucks_web_1 163 | 164 | $ docker container ls 165 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 166 | f50bb33a3242 prakhar1989/foodtrucks-web "python3 app.py" 14 seconds ago Up 13 seconds 0.0.0.0:5000->5000/tcp foodtrucks_web_1 167 | e299ceeb4caa elasticsearch "/docker-entrypoint.s" 14 seconds ago Up 14 seconds 9200/tcp, 9300/tcp foodtrucks_es_1 168 | ``` 169 | 170 | So far, so good. Time to see if any networks were created. 171 | 172 | ```bash 173 | $ docker network ls 174 | NETWORK ID NAME DRIVER 175 | c2c695315b3a bridge bridge local 176 | f3b80f381ed3 foodtrucks_default bridge local 177 | a875bec5d6fd host host local 178 | ead0e804a67b none null local 179 | ``` 180 | 181 | You can see that compose went ahead and created a new network called `foodtrucks_default` and attached both the new services in that network so that each of these are discoverable to the other. Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by them at a hostname identical to the container name. 182 | 183 | ```bash 184 | $ docker ps 185 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 186 | 8c6bb7e818ec docker.elastic.co/elasticsearch/elasticsearch:6.3.2 "/usr/local/bin/dock…" About a minute ago Up About a minute 0.0.0.0:9200->9200/tcp, 9300/tcp es 187 | 7640cec7feb7 prakhar1989/foodtrucks-web "python3 app.py" About a minute ago Up About a minute 0.0.0.0:5000->5000/tcp foodtrucks_web_1 188 | 189 | $ docker network inspect foodtrucks_default 190 | [ 191 | { 192 | "Name": "foodtrucks_default", 193 | "Id": "f3b80f381ed3e03b3d5e605e42c4a576e32d38ba24399e963d7dad848b3b4fe7", 194 | "Created": "2018-07-30T03:36:06.0384826Z", 195 | "Scope": "local", 196 | "Driver": "bridge", 197 | "EnableIPv6": false, 198 | "IPAM": { 199 | "Driver": "default", 200 | "Options": null, 201 | "Config": [ 202 | { 203 | "Subnet": "172.19.0.0/16", 204 | "Gateway": "172.19.0.1" 205 | } 206 | ] 207 | }, 208 | "Internal": false, 209 | "Attachable": true, 210 | "Ingress": false, 211 | "ConfigFrom": { 212 | "Network": "" 213 | }, 214 | "ConfigOnly": false, 215 | "Containers": { 216 | "7640cec7feb7f5615eaac376271a93fb8bab2ce54c7257256bf16716e05c65a5": { 217 | "Name": "foodtrucks_web_1", 218 | "EndpointID": "b1aa3e735402abafea3edfbba605eb4617f81d94f1b5f8fcc566a874660a0266", 219 | "MacAddress": "02:42:ac:13:00:02", 220 | "IPv4Address": "172.19.0.2/16", 221 | "IPv6Address": "" 222 | }, 223 | "8c6bb7e818ec1f88c37f375c18f00beb030b31f4b10aee5a0952aad753314b57": { 224 | "Name": "es", 225 | "EndpointID": "649b3567d38e5e6f03fa6c004a4302508c14a5f2ac086ee6dcf13ddef936de7b", 226 | "MacAddress": "02:42:ac:13:00:03", 227 | "IPv4Address": "172.19.0.3/16", 228 | "IPv6Address": "" 229 | } 230 | }, 231 | "Options": {}, 232 | "Labels": { 233 | "com.docker.compose.network": "default", 234 | "com.docker.compose.project": "foodtrucks", 235 | "com.docker.compose.version": "1.21.2" 236 | } 237 | } 238 | ] 239 | ``` --------------------------------------------------------------------------------