0% found this document useful (0 votes)
15 views

Docker and Kubernetes

Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15 views

Docker and Kubernetes

Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 26

Docker and Kubernetes

Docker: It's a container technology for creating and maintaining containers.

Container: It's a standardized unit of software. A package of code and dependencies to run that
code. Docker then in the end is a tool that simplifies the creation and management of these
containers.

Why Docker and Containers?


For different Development and Production environments to have same dependencies. To have
reliable and reproducible environments with ease.
It’s very easy to share a common development environment or setup across different teams.

Virtual Machines vs Docker Containers:


Virtual Machines wastes a lot of space and resources and tends to be slow.
Basic Docker Commands:
docker build . - Builds an image based on the docker file. It will setup an image for the
container to be launched.
docker run <image_id>: To run a container based on a docker image.
docker run -p <port> <image_id>: To run a container based on a image exposing it to a specific
port which our application will be running.

docker ps: To see all the running containers.


docker ps -a: To see all the containers including stopped ones.
docker stop <ps_name>: To stop a container.

WSL - Windows Subsystem for Linux


Developers can access the power of both Windows and Linux at the same time on a Windows
machine.

Reference: https://learn.microsoft.com/en-us/windows/wsl/install

WSL Commands:
wsl -l -v : To view the OS installed and their state.
wsl -d <name> : To connect to the specific OS.
Images and Containers: A container is a running unit of software. Whereas, images are
templates or blueprints for containers.

We first build an image with the required code and dependencies. Now, Using that single
image, we can run multiple containers where each is a running instance of that image.
Using and Running External (Pre-Built) images:
https://hub.docker.com/ : We can find pre built images.

docker run node : It will create node container but it won’t do anything as container runs in an
isolation.
docker run -it node: With “it” option, we can now run node container in an interactive way and
can execute basic node commands.

Using pre built images, we can also write our own docker file and create a custom image.

Creating a DockerFile:
FROM <base_image>: To import the base image from DockerHub or from our local to the
current image.
COPY source dest
COPY . . -> Specify while files to be copied to image. Here first “ . “ specifies all the files in path
of DockeFile to be copied. And second “ . “ specifies the destination where its to be copied into.
Here it will be copied to the DockerFile.
Note: If the destination folder doesn’t exist, it simply creates one.

RUN <command> : This command is used to run the commands in the working directory. The
default working directory will be the root folder in the container file system. In the above, as we
are copying all the folders into destination, it will be the default working directory.

Eg:
FROM node
COPY . /app
RUN npm install
RUN node server.js

In the above example, the npm install command will be executed in /app directory.

WORKDIR <path>: To set the default working directory of container. All the subsequent
commands, will be executed in the path given here.

Also, in the above example, RUN node server.js is INCORRECT because this would be executed
when the image is being built. The image is just a template to the container. The image is not
what we run in the end. We run a container based on the image. We only want to start the
server, only if we start a container based on the image.

To tackle this, we have CMD command.

CMD [“<string1>”, “<string2>”] - - > CMD ["node", "server.js"]


The command provided with CMD will be run not when image is being built, but when we start
the container based on the image.

EXPOSE <port>: This command is OPTIONAL used to document the port that we are exposing
our container to. We can specify the port here according to our server.js file.

Note: The EXPOSE 80 below in the Dockerfile in the end is optional. It documents that a process
in the container will expose this port. But you still need to then actually expose the port with -p
when running docker run. So technically, -p is the only required part when it comes to listening
on a port. Still, it is a best practice to also add EXPOSE in the Dockerfile to document this
behavior.

This is how our final sample DockerFile looks:

FROM node
WORKDIR /app
COPY . /app
RUN npm install
EXPOSE 80
CMD ["node", "server.js"]

Images are Read Only:


If we make any changes to our code post building the image, restarting our container doesn’t
reflect our changes. We need to rebuild our image, to copy the updated source code into a new
image. Once we build the image, its JUST READ ONLY. We can’t edit it.

Image Layers:
Once image is built, and if we build it again without any changes, the new image uses cache as
Docker understands that there arent any changes. So, when we build an image for first time,
docker caches every instruction and uses that cache when there are no changes when we
rebuild an image again. This is called Layered Structure.

Every instruction in Dockerfile creates a layer.

When a specific layer is changed, Docker understands the corresponding layer. So, it builds the
other previous layers from cache and rebuilds only the layer that is changed. Docker here
doesn’t know the impact on subsequent layer based on changes of previous layer. So, it re
executes all the subsequent layers of the changed layer again.

FROM node
WORKDIR /app
COPY package.json /app
RUN npm install
COPY . /app
EXPOSE 80
CMD ["node", "server.js"]

In the above code, we know that only if there is change in package.json, npm install needs to be
executed. For other code changes, its not required to run npm install. So, we can order our
layers accordingly and optimize our code.

Managing Images and Containers:


Stopping and Restarting Containers:
Whenever we use, docker run, it creates a new container. We don’t need this all times if there
is nothing changed in our code or dependencies. We can just restart the container that is
already created.

To restart, we can run docker start <container_name | <container_id >. We can then check if
container is running by docker ps.

Attached and Detached Containers:


When we restarted the container above, we are not attached to the running docker container
in the terminal. (Though, its running in the background).

With docker run command, we are struck with that process as we cannot enter any other
commands as can be seen below. Here, the container is in “Attached” mode.

With docker start, detached mode is default. While with docker run, attached mode is the
default behaviour.
With attached mode, we can see the log statements (if there are any) in our terminal. To run in
detached mode, we can add -d option to docker run command. And similarly, to run in attached
mode, we can add -a option to docker start command.

We can also attach again a detached container by running docker container attach <
container_name | container_id > and see the console logs:

We can also check the logs of a detached container using docker logs <container_name>

Interactive Mode:
Docker images are not just for web applications but it can also be used to run simple python or
any programming language codes. Now, when we are building a docker image that expects an
input, and when we simply use docker run, though its in attached mode, it is not interacting
with and failing with below error:

So, in order to run docker images which expects an input, we need to run it with interactive
mode (-i option). Also, we can allocate a pseudo terminal with -t option which is OPTIONAL.
So, once the program is executed, we can observe that the container is stopped. Now, when we
restart the container, the container is in RUNNING state, but its not taking any INPUT and giving
us the required results.

We need to restart the container in interactive and attached mode again to give the input and
execute the program as below:

Deleting Images and Containers:

docker rm <container_name1 container_name2>: We can use this command to remove non-


running containers. We can delete multiple containers by passing the containers which we want
to delete.
docker rmi <image_name1 image_name2>: This can used to delete the images which we no
longer need. We can check the list of images by running docker images command.
We can remove the images if they are not being used by any containers. We cannot remove the
image even if the corresponding container is in stopped state. We need to first remove the
container and then only, we can remove the image.

docker image prune: We can use this command to remove all the unused images.

Removing Stopped Containers automatically: We can add - - rm option to docker run


command to automatically remove the stopped containers.
Inspecting Images:
docker image inspect <image_id>: We can get all the details about the image like
created_date, container details, docker version, exposed ports, OS, dependencies installed and
different type of layers.

Copying files into & from a Container:


docker cp <source_path> <container_name>:<destination_path>: We can use this command
to copy files into a running container or out from a running container.

Naming and Tagging Containers & Images:


We can use - - name option to name the containers.
docker run -p 3000:80 -d --rm --name goalsapp 22f66842b416
In the above pre build node image, we can see the list of tags available for node image. So using
the tag, we can use the specific node version.
Eg: FROM node:14

To tag a image: docker build -t goals:latest .

Sharing Images & Containers:


Pushing Images to DockerHub:

Docker Hub: To login into docker hub, we can use docker login.

To push an image to docker hub:


We first tag our existing images with the repository name which is mani7998/node-app and
then push the image to docker hub.

Pulling and Using Shared Images:

Since our docker hub repo is public, we can logout and try docker pull <Repo_name> to
download image from docker hub to local.

Note: docker pull always fetches the latest version of image.


We can also use docker run <repo_name> and if the image is not present locally, it downloads
from docker hub and runs locally. However, if we run again, it may not fetch the latest version
of the image. So, we should run docker pull followed by docker run to ensure we are running
the latest version of the image.

Managing Data & Working with Volumes:

Docker and EKS integration:


Docker is a containerization tool that allows you to package your application and all its
dependencies into a light-weight container. This makes the application portable and consistent
across environments
Containerize your Node.js application and build a docker image
Push the image to Amazon ECR

Kubernetes helps you manage, scale and deploy containers across a cluster of services.
Nodes These are the EC2 instances that will run your Docker containers (pods)
Pods These are the smallest deployable units in K8s. Each Pod runs one or more containers
Services This ensures that your application is accessible and load-balanced across different pods
Kubectl This is the k8s command line tool
Create a deployment.yml and service.yml file and apply the files
Horizontal Pod Autoscaler Automatically scales the no.of pods based on CPU/memory
utilization. If the max no.of pods are reached, the Cluster Autoscaler kicks in to add new EC2
instances.
Cluster autoscaler This automatically scales the no.of worker nodes (EC2 instances) in cluster
Replicas in deployment.yml file - replicas: 3 means you want 3 pods running at any time across
the available nodes. Ex: If you have 2 nodes and 3 replicas, K8s will balance them across the
nodes (2 pods on one node, 1 pod on the other)
Health monitoring K8s continuously monitors the health of each pod using readiness and
liveness probes.
● Readiness probe - Checks if the Pod is ready to serve traffic. A Pod could be alive, but
not yet ready(i.e., waiting for a DB connection)
● Liveness probe - Checks if the Pod is alive. If a Pod fails liveness check, k8s will
automatically restart it

Kubelet service A critical k8s component that runs on every node (EC2 instance in EKS). It’s
responsible for managing pods on its node. It interacts with the k8s control plane to ensure that
the containers are running properly

Key Responsibilities of the Kubelet:

● Pod Management: The kubelet watches the API server for pods scheduled on its node
and ensures that the containers in those pods are running.
● Health Checks: It performs the health checks (liveness and readiness probes) and
ensures the containers are restarted if necessary.
● Container Runtime Interface (CRI): The kubelet communicates with the container
runtime (e.g., Docker, containerd) to start and stop containers.
● Node Status: It reports the status of the node (resource utilization, health, etc.) to the
control plane.

When Does the Kubelet Fail?

● Misconfiguration: Incorrect pod configurations (e.g., too many resource requests).


● Node Resource Issues: If the node runs out of memory or CPU, the kubelet might
struggle to schedule new pods.
● Network or Security Misconfigurations: If the kubelet can't communicate with the API
server or the container runtime (e.g., due to network or security issues), it will fail to
manage pods.
Handling Kubelet Failures:
○ You can monitor kubelet logs using CloudWatch or SSH into the EC2 instance and
inspect the logs (journalctl -u kubelet).
○ Restarting the kubelet (systemctl restart kubelet) or, in extreme cases,
terminating and replacing the EC2 instance may resolve issues.

Cluster Autoscaler and HPA Interaction


The Cluster Autoscaler looks at the resource requests and limits of your pods to determine if
there’s enough capacity on the current nodes. If the nodes are overloaded (e.g., they don’t
have enough CPU or memory to schedule a new pod), the Cluster Autoscaler will add more
nodes (EC2 instances) to the cluster.

● Triggering the Cluster Autoscaler: When the HPA increases the number of pods and the
existing nodes cannot accommodate them, the Cluster Autoscaler automatically
provisions new EC2 instances to handle the increased workload.
For example:
○ Your nodes (EC2 instances) have enough resources to run 10 pods.
○ The HPA scales up the number of pods to 15.
○ If the existing nodes can't handle the additional 5 pods, the Cluster Autoscaler
will add a new EC2 instance to the cluster to provide more capacity.

Kubernetes Volumes:
A k8s volume is a directory accessible by containers in pod
● K8s volumes are scoped to a pod, meaning they exist as long as the pod exists
● Different volume types handle storage differently - some are ephemeral(data lost when
the pod is deleted), while others are persistent(data is retained even if the pod is
deleted)

Volume types in k8s:


EmptyDir: An empty directory is created when the pod is assigned to a node and is available to
all the containers in the pod. It’s erased when the pod is deleted
HostPath: Mounts a file or directory from the host node’s file system into pod. It’s ephemeral
AWS EBS: Specific to AWS, allows attaching an EBS volume to a k8s pod. Data is persistent
NFS: Allows mounting a remote NFS server directory into a pod

Kubernetes Helm charts:


Helm is a package manager for k8s that simplifies the deployment of complex applications.
Helm charts are pre-configured k8 resources that help automate the installation and
management of your applications.

Key Components of Helm:


● Chart: A collection of files that describe a related set of Kubernetes resources. This
includes:
○ Chart.yaml: Describes the chart (name, version, description).
○ values.yaml: Contains default configuration values for the chart. You can
override these values depending on the environment.
○ templates/: Kubernetes resource templates like Deployment, Service,
ConfigMap, etc. The values in these templates are dynamic and can change
based on the values.yaml file.
I have 3 environments (stage, tryapplaud and production). And for each environment, I have a
different VPC, subnets, security groups, ebs volumes and instance type. And in stage, we will
have latest code all time having daily nightly deployment. And in tryapplaud and production, we
will have the release version. So, I guess, we need to maintain a released standard docker
image version for tryapplaud and production. And we will have a new docker image being
updated daily for staging environment. And we will have different replica configurations as well
for each environment. So, help me in creating a helm chart templates with values.yaml files for
each environment, along with deployment.yaml and service.yaml files that are being created in
the templates.

You might also like