Notes from Pluralsight course "Getting Started with Docker" by Nigel Poulton
- Hypervisors virtualize hardware. However, containers virtualize operating systems (with their own process trees, eth0, directory structure).
- Because containers don’t virtualize the lower level hardware, all containers share the same kernel.
- This makes containers smaller, faster, and more lightweight than standard VMs. This allows for way more "apps per square foot", or app density
- You can test Docker Containers for free at labs.play-with-docker.com
- An "image" is like a stopped VM while a "container" is like a running VM. Images are stored in the cloud or locally then become containers once you run them.
- In the same way, an image is like a class, and a container is like an object instance created from that class
Dockerfile
Dockerfile
states how to build the image- These use docker key words, but they are executing commands inside the container.
FROM node:current-alpine
# this doesn't actually have a linux kernel.
# Just file system constructs and Node installed
LABEL org.opencontainers.image.title="Hello Docker Learners!" \
org.opencontainers.image.description="Web server showing host that responded" \
org.opencontainers.image.authors="@nigelpoulton"
# this you can ignore. Its just who you can hassle about the app
# Create directory in container image for app code
RUN mkdir -p /usr/src/app
# Copy app code (.) to /usr/src/app in container image
COPY . /usr/src/app
# Set working directory context
WORKDIR /usr/src/app
# Install dependencies from packages.json
RUN npm install
# Command for container to execute
ENTRYPOINT [ "node", "app.js" ]
Build the Image
- Build with…
docker image build \
-t <docker_user_id>/<repo_name>:<image_or_version_name> \
.
- Example:
docker image build \
-t jairusc615/first-container:v1 \
.
- You can then see the image with
docker images
- You can then delete this image with
docker image rm
. Example:
docker image rm \
jairusc615/first-container:v1
Hosting Image
- Hosting on a registry like DockerHub
- There are loads of container registries out there
- Login to DockerHub with
docker login
- You may need to make an access token in DockerHug and use that as an alternative to your password.
- Push to DockerHub with…
docker image push \
<docker_user_id>/<repo_name>:<image_or_version_name>
- Example:
docker image push \
jairusc615/first-container:v1
Running a Containerized App
- Run a docker container with…
docker container run -d \
--name <name> \
-p <external_port_num>:<container_port_num> \
<docker_user_id>/<repo_name>:<image_or_version_name>
- Example:
docker container run -d --name web -p 8000:8080 jairusc615/first-container:v1
- If docker can’t find the image locally, it will automatically search in DockerHub
-d
means detached. It will run in the background, detached from the terminal. You could leave this off, and you automatically get a shell into the container! But then as soon as you exit, the container is killed. You can keep it running instead of exiting withCtrl+P+Q
to send it to the background.- See all running containers with
docker container ls
- See all containers (regardless of if theyre running or not) with
docker container ls -a
Managing a Containerized App
- Stop container with
docker container stop <docker_container_name_or_id>
- This sends a sigint to the app and allows 10 seconds for graceful shutdown. If it does not stop, it sends a sigkill.
- Start container with
docker container start <docker_container_name_or_id>
- Delete a container be stopping it first, then use
docker container rm <docker_container_name_or_id>
- You can also delete without stopping the container by using the
-f
flag. Ex.docker container rm <docker_container_name_or_id>
Cloud-Native Microservices
- A single app could have a frontend, database, logging, keychain, backend, api, and other services along with it. These all must go together for the app to work!
- In other app infrastructures, these all come packaged together. So if you take one down to upgrade it or package it, its all down! That it big and clunky!
- Instead, we split the app into separate micro-services that all work together but are in separate containers
- You can then upgrade only one part of the app without touching the others.
Multi-Container Apps with Docker-Compose
- The
docker-compose.yaml
is a declarative manifest file describing an app. Example:
version: "3.8"
services:
web-fe: # type of service
build: .
command: python app.py # command the container will run to start the service
ports:
- target: 5000 # container port
published: 5000 # host port
networks:
- counter-net
# notice we connect the two services on the same network
volumes:
- type: volume
source: counter-vol # declared below
target: /code # mount point
redis: # type of service
image: "redis:alpine"
networks:
counter-net:
networks: # declare this network for creation and bind it to this app
counter-net:
volumes: # declare this volume for creation and bind it to this app
counter-vol:
- The
requirements.txt
file declared the dependencies needed. Example:
flask
redis
- Create, start, or restart the app with
docker-compose up
and the optional aforementioned-d
flag if desired (which is likely) - Stop the app with
docker-compose down
Docker Swarm
- Load balancing and redundancy capabilities!
- A swarm is a cluster of one or more manager nodes and some worker nodes
- Manages nodes control things like the cluster store, scheduling, etc. Usually have an odd number of these, like 3 or 5. This is to ensure that if managers are for some reason disconnected, they can know if they have the majority or not because its not an even tie. That even tie is called a "split brain"
- You can run swarm mode on Docker Desktop, but you only get one manager node.
- Swarm unlocks Docker Services, which map directly to application micro-services.
- To create a swarm, have many instances running Docker. Then, on one of them, run
docker swarm init
. This will make this node the first manager node. You will probably need the flag--advertise-addr=<host_ip_address>
so that that particular ip address is used for cluster communication. - To add a manager to the swarm, run
docker swarm join-token manager
and a command will pop up that you can then run on the new manager.
- To add a worker to the swarm, its similar to adding a manager. Ex.
docker swarm join-token worker
- To see all the docker managers, run
Docker node ls
. Any node without Manager Status is a worker
Micro-Services and Docker Services
- Every micro-service has an associated docker service that controls and scales it.
- To create a docker service, run
docker service create
, with similar parameters to before. (docker service create is only available in swarm mode.) Example:
docker service create --name web -p 8080:8080 \
--replicas 3 \
<docker_user_id>/<repo_name>:<image_or_version_name>
- See the service with
docker service ls
. This will only show you local containers. - The best command for seeing all containers is
docker service ps <service_name_or_id>
because it will show you all containers, even if they aren’t local. - You should edit a service with
docker service
, and probably not mess withdocker container
directly - If you delete a container directly, docker will automatically recreate it in the swarm. This simulates what would happen if a container failed.
- Scale the service with
docker service scale <service_name_or_id>=<integer>
. Example:docker service scale web=10
will get you 10 instances of the web container, all load balanced. - Remove the service with
docker service rm <service_name_or_id>
Multi-Container Apps with Docker Swarm
- The
docker-compose.yaml
file looks similar to before… Example.
version: "3.8"
services:
web-fe:
image: nigelpoulton/gsd:swarm-stack
# ^this used to be "build: ."
command: python app.py
deploy: # <-----
replicas: 10 # <-----
ports:
- target: 8080
published: 5000
networks:
- counter-net
volumes:
- type: volume
source: counter-vol
target: /code
redis:
image: "redis:alpine"
networks:
counter-net:
networks:
counter-net:
volumes:
counter-vol:
- Stacks can’t build images on the fly, so you can’t use the local dockerfile with
build: .
. The web front image needs to be pre-created and stored in a registry so that all the nodes can pull it. Reference it withimage: <docker_user_id>/<repo_name>:<image_or_version_name>
in the above file - That being said, use the dockerfile to build the image, then push it to a registry, and you’re good to go.
- To run the stack from the
docker-compose.yaml
file, use…
docker stack deploy -c docker-compose.yaml <name_of_app_or_stack>
- See all running stacks (or apps) with
docker stack ls
- See the services of a stack with
docker stack services <stack_or_app_name>
- The best way to change a stack on the fly is just to edit the
docker-compose.yaml
file and re-run the deploy to enforce the changes