# Docker & Kubernetes 101

# Docker

Docker is a tool designed to make it easier to create, deploy, and run applications by using containers. Containers allow a developer to package up an application with all of the parts it needs, such as libraries and other dependencies, and ship it all out as one package. — OpenSource.com, What is Docker?

# Infrastructure

Don't worry about the system your application will ultimately run on.

# Install Docker

Install at Docker's getting-started (opens new window)

# Setup Docker

First, ensure you are using an up-to-date docker engine. In the terminal,

  1. Run docker info and check for any errors
  2. Run docker --version and ensure you have version >=17.06

# Setup Docker Resources

Ensure you have set enough resources for Docker to run smoothly: In the taskbar, click the whale icon → Preferences → Advanced

(I set 6 CPUs and 8GB RAM for Docker)

# Settings

docker settings

# World Smallest Image

Every single Docker container begins as a pure vanilla Linux machine that knows nothing.

know nothing

# Where It All Begins

A Docker image consists of read-only layers each of which represents a Dockerfile instruction. The layers are stacked and each one is a delta of the changes from the previous layer.

Images start as a fresh, isolated machine and the very same dependencies get added to it. Every. Single. Time.

FROM scratch

ADD hello /

CMD ["/hello"]

# Build your First Image

docker build -t tiny-hello-world .
docker images

# Run your First Container

docker run tiny-hello-world
docker ps -a

# Clean After Yourself

Delete a container:

docker rm condescending_ptolemy

Next time, run container & delete after exit:

docker run --rm tiny-hello-world

# Advanced

Delete all stopped containers:

docker container prune

Delete all dangling images:

docker image prune

Bob and Dylan

# My First Containerized Node Application

I recommend Dockerfile documentation (opens new window). It's very well written.

# Dockerfile

Node service:

# creates a layer from the node:carbon Docker image
FROM node:8.14-alpine

# create the app directory for inside the Docker image
WORKDIR /usr/src/app

# copy and install app dependencies from the package.json (and the package-lock.json) into the root of the directory created above
COPY package*.json ./
RUN yarn install

# bundle app source inside Docker image
COPY . .

# expose port 8080 to have it mapped by Docker daemon

# define the command to run the app
CMD [ "yarn", "run", "start" ]

# app.js

var express = require('express');
var app = express();

process.on('SIGINT', function () {

app.get('/', function (req, res) {
  res.send("Hello World!

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');

# package.json

  "name": "docker-101-scene-02",
  "version": "1.0.0",
  "main": "index.js",
  "author": "Rafael Bodill",
  "license": "MIT",
  "scripts": {
    "start": "node app.js"
  "dependencies": {
    "express": "^4.16.4"

# Let's Build & Run

docker build -t test-node .
docker run -it --rm test-node

On a new terminal:

curl localhost:3000

Where's my service? 😦

# Expose Ports to Host

docker run -it --rm -p 3000:3000 test-node

On a new terminal:

curl localhost:3000


# Let's Add a Route

Append to app.js:

app.get('/healthz', function (req, res) { res.send('OK') })

Rebuild image:

docker build -t test-node .
docker run -it --rm test-node

# Docker Layer Caching

Docker Caching

# Docker Layers

$ docker history test-node

IMAGE               CREATED          SIZE      CREATED BY
d17562ebd02e        27 minutes ago   0B        CMD ["yarn" "start"]
c24c2a8c7a2a        27 minutes ago   0B        EXPOSE 3000
001439668f54        27 minutes ago   1.78MB    COPY dir:0dc6500de44402cb2…
24e151c2462b        27 minutes ago   7.13MB    yarn install
9dd639668f68        27 minutes ago   226B      COPY file:b365e33dfa656564…
a93bcafb8a76        27 minutes ago   0B        WORKDIR /usr/src/app

.. Wait .. Our app.js is 1.78MB?

# Ignoring Files

Create a .dockerignore file with:


Rebuild image:

docker build -t test-node .
docker history test-node

Now it's better.

Bob poster child

# Linux Containers

# Images vs. Containers

Docker images are executable packages that include everything needed to run an application — the code, a runtime, libraries, environment variables, etc.

Docker containers are a runtime instance of an image — what the image becomes in memory when executed.

Examples of Docker containers. Each one comes from a specific Docker image.

Docker containers

# Entering Container's Shell

docker run -d --name bob -p 3000:3000 test-node
docker exec -it bob sh

You are now INSIDE the matrix container!

> netstat -ntulp
> wget -q -O- localhost:3000
> top
> free -m
> exit

# Running Different Commands

docker run --rm -it test-node sh
docker run --rm -it test-node ls -alp

# View Container Information

docker inspect bob | less
docker logs -f bob
docker stats bob

# Manage State

docker stop bob
docker start bob
docker restart bob
docker rm -f bob

Dylan small man

# Private Services

docker run --rm -p 3000:3000 artifactory.local/myapp

Oh oh. What's missing here?

# Mount Volumes

Let's mount our configuration from a different Git repository:

git clone git@git.local:mycompany/myconfig.git

docker run --rm -v $PWD/myconfig:/etc/myapp \
  -p 3000:3000 artifactory.local/myapp

Dog-gone it. What else are we missing?

# Environment Variables

Oh. Our environment variable! It tells the configuration where to read from:

docker run --name bob -v $PWD/myconfig:/etc/myapp \
  -e MYAPP_CFG_ROOT=/etc/myapp -p 3000:3000 \

# Viewing Logs

In a different terminal,

docker logs -f bob
docker exec -it bob tail -f /var/log/myapp/api.log

Bob the beach

# Simplifying Workspaces

Docker Compose offers a number of benefits over the Docker CLI in all stages of development.

Essentially, what Docker Compose is, is a recipe card — it's a recipe of services that make up an application and the docker-compose.yml dictates how the services are mixed together.

# Simple Example

Let's inspect our docker-compose.yml file:

version: "2"

    image: artifactory.local/myapp
      MYAPP_CFG_ROOT: /etc/myapp
      - "3000:3000"
      - ./myconfig:/etc/myapp

# Running Your Composition

git clone git@git.local:mycompany/myconfig.git
docker-compose up

On a different terminal:

curl localhost:3000

# Let's Add (3rd)Parties!

version: "2"

    image: artifactory.local/myapp
      MYAPP_CFG_ROOT: /etc/myapp
      - "3000:3000"
      - ./myconfig:/etc/myapp

    image: "redis:alpine"

Let's run it:

docker-compose up


# Give me morrrre!

version: "2"

    image: artifactory.local/myapp
      MYAPP_CFG_ROOT: /etc/myapp
      - "3000:3000"
      - ./myconfig:/etc/myapp

    image: "redis:alpine"

    image: "mysql:5.7"

Let's run it:

docker-compose up

# Access Services

# Access MySQL:
docker exec -it scene-05_mysql_1 mysql -u root -p

# Access Redis:
docker exec -it scene-05_redis_1 redis-cli GET /

# More on Docker-Compose

  • docker-compose ps - Lists all services in network.
  • docker-compose up - Brings up the network & services.
  • docker-compose stop - Stops the network, saves state of services.
  • docker-compose start - Restarts the services.
  • docker-compose down - Burn it all down! Destroy network & services.

Bob and Lucy

# Kubernetes (K8S) 101


# Workstation Setup

brew uninstall --force bash-completion
brew install kubernetes-cli kubectx fzf stern bash-completion@2

Append to your ~/.bash_profile for awesome shell completion:

if [ -f /usr/local/share/bash-completion/bash_completion ]; then
  . /usr/local/share/bash-completion/bash_completion

# Setup kubectl Context

Cluster + User = Context

# Integration
kubectl config set-cluster foobar-int-developer --server= --certificate-authority=$HOME/.kube/int/ca.pem
kubectl config set-credentials int-developer --client-certificate=$HOME/.kube/int/developer.crt --client-key=$HOME/.kube/int/developer.key
kubectl config set-context foobar-int-developer --cluster=foobar-int-developer --user=int-developer

# Staging
kubectl config set-cluster foobar-stg-developer --server= --certificate-authority=$HOME/.kube/stg/ca.pem
kubectl config set-credentials stg-developer --client-certificate=$HOME/.kube/stg/developer.crt --client-key=$HOME/.kube/stg/developer.key
kubectl config set-context foobar-stg-developer --cluster=foobar-stg-developer --user=stg-developer

# kctx

kubectl config use-context foobar-int-developer
kctx foobar-int-developer
kctx foobar-stg-developer

# kubectl Cheat-sheet

kubectl get namespaces
kubectl get deploy
kubectl get -n ingress-nginx deploy
kubectl get -n kube-system deploy
kubectl get --all-namespaces deploy
kubectl get pods -w
kubectl get pods -o wide
kubectl get events

kubectl get pods,svc,deploy
kubectl describe deploy push-service
kubectl top node
kubectl top pods
kubectl logs -f deploy/trips
kubectl logs -f deploy/trips -c trips
kubectl logs --previous trips-7d7f649996-nz876
kubectl exec -it trips-6c6887547d-lhbsb -- tail -f /var/log/myapp/api.log

# Examining Deployed Objects

kubectl get deploy
kubectl get pod
kubectl get svc
kubectl get deploy,pod,svc | grep myapp
kubectl get all | grep myapp

# Describe Deployment

kubectl describe deploy myapp
kubectl describe pod myapp-649887b547-fh7vw
kubectl top pod | grep myapp

# Analyze Logs

kubectl logs -f myapp-649887b547-fh7vw
kubectl exec -it myapp-649887b547-fh7vw -- tail -f /var/log/myapp/api.log

# Jump into Container

$ kubectl exec -it myapp-649887b547-fh7vw sh
> top
> free -h
> exit

Mind blown.

Last Updated: 1/4/2019, 1:57:28 PM