DevOps with Microservices and Docker
DevOps with Microservices and Docker
Query: Note
Tracking Subscribe
tracking not
yet available
microservice
DevOps
.java
.sql
Developer Developer
machine machine
Docker: Containerisation
Client: Write code (.java, .sql) Client: Create microservices (.java, .sql)
Client: Compile, test on the client Client: Compile, test on the client
Server: Install Java, MySQL, Spring Boot, … Client: Create .jar/.war file and push Docker
Client: Copy .class/.jar/.war files and .sql files on image
the server Server: Pull Docker image
Server: Test Server: Run Docker image as a Container
[Link]
What is Docker? More on Docker
• Docker: Container technology • Provides an easy way to create images and run as containers
• Container = Complete and isolated software • Ready images can be pulled from DockerHub and used
• Runs an application in a sandbox • Uses a layered file system so that we can add to these base images
• Portable • One container = One application, One VM = Multiple applications
• Contains all dependencies
• Containers are isolated from each other
• We can run several containers on a machine at the same time
• To create our own image, we need to create a Dockerfile
• Self-contained: Do not alter the host system they run on
• Dockerfile: Configuration details for creating an image
• Docker is light-weight, unlike VM
• If something works in Development, it will work in Production, too • Ideal for Microservices/APIs
Docker Hypervisor
• FROM openjdk:17-slim
•
public static void main(String[] args) {
•
# Set working directory
WORKDIR /app
• [Link]("Enter first number: "); • # Copy Java source file into container
•
[Link]("Enter second number: ");
int b = [Link]();
•
•
# Compile Java program
•
[Link]("Enter third number: ");
int c = [Link]();
•
•
# Default command to run the program
• int largest;
•
if (a >= b && a >= c)
largest = a;
Build: docker build -t java_largest .
• else if (b >= a && b >= c) Run: docker run -it java_largest
• largest = b;
• else
• largest = c;
Stop and Remove All Running Containers Docker Volume and Docker Network
• docker stop $(docker ps -a -q) • Docker Volume
• Containers are ephemeral/transient/disposable/stateless
• docker rm $(docker ps -a -q) • Meaning: Short-lived, Can be easily created, destroyed, recreated
• When a container is removed, all information inside it is also destroyed
• Consider MYSQL container – Remove it and all data will also be removed
• Solution: Docker volume = A directory on the local disk
• When a container is removed and restarted, it will regain data from the volume
• Docker Network
• By default, all containers run in a network called bridge network and they can
communicate with each other using their IP addresses
• Risky, because our application containers may now be accessible to other containers
• Solution: Create our own custom network and add our containers to it
• Advantage: Now our containers are isolated from the others and can access each
other by their names also (not just IP addresses)
• import [Link].*;
• docker-java-demo/ •
•
public class Server {
• ├── [Link]
• File logFile = new File("/data/[Link]");
• if (![Link]()) {
• [Link]().mkdirs();
• ├── [Link] •
• }
[Link]();
• ├── Dockerfile •
•
try (ServerSocket serverSocket = new ServerSocket(5000)) {
• [Link](message + "\n");
• }
• [Link]();
• }
[Link] Dockerfile
• import [Link].*;
• import [Link].*;
• FROM openjdk:17-slim
• WORKDIR /app
• public class Client {
• public static void main(String[] args) throws IOException { • COPY *.java /app
• // Connect to server container using its name • RUN javac *.java
• Socket socket = new Socket("server-container", 5000);
• PrintWriter out = new PrintWriter([Link](), true);
• [Link]("Hello from client at " + [Link]());
• [Link]();
• [Link]("Message sent to server!");
• }
• }
Commands Commands
• Build Docker image • Keep the first terminal running and open a second terminal to run the
client
• docker build -t java-docker-demo .
• docker run --rm --network my-net java-docker-demo java Client
•
public static void main(String[] args) {
• ResultSet rs = null;
• try {
• COPY [Link] /app/lib/
• RUN javac *.java
• // Load the MySQL JDBC driver
• [Link]("[Link]");
•
// Establish the initial connection to create the database
• [Link](createDatabaseSQL);
• [Link]();
• [Link]();
[Link] Dockerfile
• public class HelloWorld { • # Use an official OpenJDK runtime as a parent image
• FROM openjdk:17-alpine
[Link] [Link]
• <!DOCTYPE html> • services:
• <html> • tomcat:
• <head> • image: tomcat:8.0
• <title>Welcome to C-DAC</title> • container_name: tomcat-container
• </head>
• ports:
• <body>
• - "8080:8080"
• <h1>Hello from C-DAC ACTS, DAC – August 2025 Batch</h1>
• volumes:
• </body>
• </html> • - ./myapp:/usr/local/tomcat/webapps/ROOT
• services:
• mysql:
•
environment:
MYSQL_ROOT_PASSWORD: your_mysql_root_password
• Earlier, it was done on the command line
• Now use Docker Compose
• MYSQL_DATABASE: your_database_name
• volumes:
• - mysql_data:/var/lib/mysql
• tomcat:
• image: tomcat:latest
• restart: unless-stopped
• ports:
• - 8080:8080
• volumes:
• - ./your_tomcat_app:/usr/local/tomcat/webapps/your_app.war
• depends_on:
• - mysql
• volumes:
• mysql_data: {}
[Link] [Link]
• networks: • networks:
•
•
my-custom-network: # Define a custom network
my-custom-network: # Reference the same custom network
• services:
• cdac-mysql-server: • services:
• image: mysql:latest
• java-app:
• environment:
• MYSQL_ROOT_PASSWORD: change-me # Set password to "change-me" (replace with strong password)
• build: . # Build the image from the current directory (replace if needed)
• MYSQL_DATABASE: cdac • environment:
• ports: • DB_HOST: cdac-mysql-server # Use the service name for MySQL in the network
•
•
- "3306:3306" # Map host port 3306 to container port 3306
DB_PORT: 3306
• volumes:
• - mysql-data:/var/lib/mysql # Optional volume for persistent data • DB_NAME: cdac
• networks: • DB_PASSWORD: change-me
• - my-custom-network # Connect to the custom network
• # Secure DB credentials using environment variables from a separate file (recommended)
• volumes:
• networks:
• mysql-data: # Define the volume • - my-custom-network # Connect to the custom network
Same Example using Docker Compose Docker Compose for Java and MySQL
• Start containers: docker compose up • Create a new directory: mkdir docker-compose-java-mysql
• Verify: docker ps • Go to that directory: cd docker-compose-java-mysql
• Verify: localhost:8080 • Create [Link] (See earlier)
• Optional: Install MySQL client in Tomcat container and connect to • Create Dockerfile (See earlier)
MySQL server • Copy mysql connector (See earlier)
• Stop containers: docker compose down • Create [Link] (Follows): sudo nano [Link]
• Create [Link] (Follows): sudo nano [Link]
C:\code\spring\hello\src\main\java\com\exa C:\code\spring\hello\src\main\java\com\exa
mple\hello\[Link] mple\hello\[Link]
• package [Link];
• package [Link];
• import [Link];
• import [Link]; • import [Link];
•
• import [Link];
import [Link];
• import [Link];
• import [Link].*;
• @SpringBootApplication
• public class HelloApplication {
• //@CrossOrigin(origins="[Link]
• @RestController
• public class WebController { • public static void main(String[] args) {
• [Link]([Link], args);
•
•
@RequestMapping(value="/helloworld", method=[Link])
• public String index(){
}
• return "Hello there - Spring Boot";
•
• }
}
• }
C:\code\spring\hello\[Link] C:\code\spring\hello\Dockerfile
• FROM openjdk:8-jdk-alpine
• plugins {
• id 'java'
• id 'application'
• }
• WORKDIR /app
• COPY build/libs/[Link] /app/hello-0.0.1-
• group = '[Link]'
• version = '0.0.1-SNAPSHOT'
• sourceCompatibility = '1.8'
[Link]
• repositories {
• mavenCentral()
• EXPOSE 8080
• }
• dependencies {
• implementation '[Link]:spring-boot-starter-web'
• testImplementation '[Link]:spring-boot-starter-test'
• application {
• mainClass = '[Link]'
Build and Test
• C:\code\spring\hello>gradle build
• Repeat the docker container ls command a second time, and we will see a
new container launched to replace the removed one
Docker Stack
• Docker Stack: Collection of Docker services that allows us to deploy multi-
container applications using a [Link] file
• Makes use of Docker Compose and Docker Swarm
• Steps:
• Create a [Link] file (Sample on next slide)
• Initialize Docker swarm: docker swarm init
Docker Stack •
•
Deploy the stack: docker stack deploy --compose-file [Link] my_stack
See the contents of the stack: docker stack ls
• List the services in the stack: docker stack services my_stack
• List the tasks in the stack: docker stack ps my_stack
• Remove the stack: docker stack rm my_stack
• Leave Docker swarm: docker swarm leave –force
• Verify Docker swarm status: docker info
• services:
• web:
• image: nginx:latest
• ports:
• - "80:80"
•
•
•
db:
image: mysql:5.7
environment:
Kubernetes (K8S)
• MYSQL_ROOT_PASSWORD: example
• networks:
• default:
• driver: overlay
Kubernetes and Container Orchestration Why Container Orchestration?
Kubernetes • Requirement: We want 10 instances of Microservice A container, 15
instances of Microservice B container and ....
Application Application Application
• Typical Features:
Frameworks Frameworks Frameworks
• Auto Scaling - Scale containers based on demand
• Service Discovery - Help microservices find one another
Libraries Libraries Libraries
• Load Balancer - Distribute load among multiple instances of a microservice
Container Container Container • Self Healing - Do health checks and replace failing instances
• Zero Downtime Deployments - Release new versions without downtime
Docker
Operating System
Hardware
Kubernetes Architecture
Physical Architecture: Cluster, Nodes
• Physical architecture • Logical architecture
Deployment
ReplicaSet
Workflow E-Commerce
Application
Kubernetes Installation
• Cluster options
• Windows: Enable Kubernetes in Docker Desktop
• Linux: Use Microk8s, since Minikube gives problems ([Link]
• Command line
Static IP
address • The kubectl utility ([Link]
• Cleanup
• kubectl delete pod my-nginx
• kubectl delete deployment my-nginx
ClusterIP Default … Pods are accessible only Backend pod (e.g. MySQL) can have a
• kubectl create deployment my-apache --image httpd inside the cluster … Service gets ClusterIP, meaning it can only be
• kubectl scale deployment my-apache --replicas 2 an IP address and all the pods accessed from the inside by the frontend
also get the same IP address … web application … Cannot be accessed
• kubectl expose deployment my-apache --type=NodePort --port=80 When the service receives by anyone from outside …
requests, it load balances them
among the pods …
NodePort Accessible from outside the A developer may want to test the
cluster … A static port (NodePort) application before exposing it using
on the Kubernetes cluster is used LoadBalancer
to expose the service … No load
balancing takes place …
LoadBalancer Accessible from the outside with A real application hosted on a public
an external static IP address … server/cloud
• kind: Deployment
• metadata:
•
name: my-nginx
labels:
This YAML file can be used to deploy nginx • Open a terminal inside it: kubectl exec -it my-nginx-7c79c4bf97-bzrq9
•
• spec:
app: nginx
as a service /bin/bash
• Now we can run any Linux command
• replicas: 3
• selector:
• matchLabels:
•
app: nginx
template:
• Example: hostname hostname -i ls
kubectl apply -f [Link]
• What application is the Pod running? curl localhost:80
• metadata:
• labels:
• app: nginx
•
spec:
containers: kubectl delete -f [Link] • It should show the HTML code for default nginx home page
• - name: nginx
• image: nginx:latest
• ports:
• - containerPort: 80
• ---
• apiVersion: v1
• kind: Service
• metadata:
Deploying a “Hello World” JSP as • Note: If we get the latest default tomcat image, we may have to open
an interactive terminal inside it, and then install nano:
a Kubernetes Service • docker container run -d --name mytomcat -p 9999:8080 tomcat
• docker exec -it mytomcat /bin/bash
• Then at the bash prompt:
• apt-get update
• apt install nano
Open Bash in the Container Create necessary Files
OR
• Open bash in the container • cd webapps apt-get update • cd ./..
• mkdir cdac apt install nano • vi [Link]
• docker container run -d --name mytomcat -p 9999:8080 • cd cdac • i to insert
tomcat:8.5.38-jre8-alpine • mkdir WEB-INF
• docker exec -it mytomcat /bin/bash • cd WEB-INF • <%
• vi [Link] • [Link] ("Hello from Docker and
• i to insert Kubernetes at C-DAC ACTS!");
• <?xml version="1.0"?> • %>
• <web-app>
• </web-app> • Press :x and ENTER to save and exit
• Press ESC :x and ENTER to save and exit
Push the Changed Tomcat Image to Docker Push the Changed Tomcat Image to Docker
Hub Hub
• On the command prompt: docker login • Tag and push the image to Docker Hub
• docker tag <image id> newdelthis/mytomcat-changed:1.0
• Check images: docker image list and copy our image’s id • docker push <your Docker hub user name>/mytomcat-changed:1.0
• kind: Service
• metadata:
we will not know whether we are getting output from the running • name: my-tomcat-service
• spec:
Here, port 9999 is assigned
Docker container or Kubernets pod) • selector:
to load balancer to accept
•
• port: 9999
pods in the replica set
• targetPort: 8080
• ---
• apiVersion: apps/v1
• kind: Deployment
• metadata:
• name: my-tomcat
• labels:
• app: my-tomcat
• spec:
• replicas: 3
• selector:
• matchLabels:
• app: my-tomcat
• template:
Deploy in Kubernetes Test – Windows
• kubectl apply -f [Link] • [Link]
• kind: Deployment
• metadata:
File: C:\lectures\CDAC\DAC\ nginx-deployment-
•
•
labels:
• spec:
•
replicas: 3
selector:
To deploy: kubectl apply -f nginx-deployment-
•
•
matchLabels:
environment: test
[Link]
• minReadySeconds: 10
• strategy:
• rollingUpdate:
• maxSurge: 1 # The maximum number of newly created pods beyond the "desired state" number of pods (e.g. we have 3 replicas, and here we have 1, so we can have at the most 4 pods running at a time; during the rolling deployment and then finally it would go back to 3)
• maxUnavailable: 0 # The number of pods that can be unavailable during the update process, 0 means we must have pods equal to the number specified in replicas available at all times (in our example, 3 pods)
• type: RollingUpdate
• template:
• metadata:
• labels:
• environment: test
• spec:
• containers:
• - image: nginx:1.17
• name: nginx
Now Upgrade to Version 1.18 Cleanup
• Change to nginx:1.18 in the yaml file and redeploy: kubectl apply -f • kubectl delete deployment testdeploy
[Link]
• Verify: kubectl get all
• Check again
• kubectl get pods
• kubectl describe pods
• kubectl get all
• kind: Service
•
metadata:
name: assignment-docker-kubernetes
File name: C:\lectures\CDAC\DAC\ [Link]
• spec:
[Link]
•
•
selector:
app: assignment-docker-kubernetes
• C:\lectures\CDAC\DAC>kubectl get all
• type: LoadBalancer
• ports:
• - protocol: "TCP"
•
port: 9999
targetPort: 8080
• Browser: localhost:9999/cdac/[Link]
• ---
• apiVersion: apps/v1
• kind: Deployment
• metadata:
• name: assignment-docker-kubernetes
• labels:
• app: assignment-docker-kubernetes
• spec:
• replicas: 3
• selector:
• matchLabels:
• app: assignment-docker-kubernetes
• minReadySeconds: 10
Save the Modified Container as a New Image
Deploy Version 2 of the Application
and Push to Docker Hub
• Repeat the earlier steps to have a message Welcome to Version 2 of our • C:\>docker commit ae7a assignment-docker-Kubernetes-v2
Application in the JSP
• For this, create a new Docker container • C:\>docker login
• docker container run -d --name mytomcat -p 8888:8080 tomcat:8.5.38-jre8- • C:\>docker image list and copy the image id for the above image
alpine
• docker exec -it mytomcat /bin/bash • C:\>C:\lectures\CDAC\DAC>docker tag b30436191245
• bash-4.4# ls newdelthis/assignment-docker-kubernetes:2.0
• bash-4.4# cd webapps • C:\>docker push newdelthis/assignment-docker-kubernetes:1.0
• bash-4.4# mkdir cdac
• bash-4.4# cd cdac
• C:\>docker container stop ae7
• bash-4.4# pwd • C:\>docker container rm ae7
• /usr/local/tomcat/webapps/cdac
• kind: ReplicaSet
• metadata:
• labels:
•
app: tomcat
spec:
kubernetes •
•
replicas: 5
selector:
• matchLabels:
• app: tomcat
• template:
• metadata:
• labels:
• app: tomcat
• spec:
• containers:
• - name: tomcat
• image: tomcat:8.0
• ports:
• - containerPort: 8080
• volumeMounts:
• - name: myapp-volume
• mountPath: /usr/local/tomcat/webapps/ROOT
• volumes:
Version Control
• Real-life situation: Different developers work on same/different parts
of code files for code enhancements/bug fixes
• Problems
• Simultaneous changes done by multiple developers at the same time
• Accidental overwriting of each other’s work
Git and GitHub •
•
Not taking the correct base file for making code changes
Not uploading the correct file after changes are done
• Solution: Version control software
Version control example Version control example
• Developer 1 wants to push version 1.0 of the code to an application • Now a team member asks for her code
server
• This team member has a folder called Version 2.0 and has deployed
• After the push, the server runs code version 1.0 this version of the code on the server
Version Control System and Repository If another team member wants the code …
• Developers now push the code to a repository • Fetch
Repository = A centralized storage
for managing and tracking changes
to a collection of files and
directories
• Local copy per user and one common central • Local copy and local repository per user and
repository one common central repository
• Single point of failure • More scalable
• Requires constant connectivity • e.g. Mercurial, Git
• e.g. Subversion, Endevor
• Remote repository – The remote area (GitHub) for all the code
• Download and install • Set user name and email id for local git set up
• git config --global [Link] “Atul Kahate”
• Accept all default options • git config --global [Link] “newdelthis@[Link]”
• Visit [Link] and sign up • Replace the highlighted portions with your own details
• Remember the login details
Creating Our Own Repository – Option 2 –
Creating Our Own Repository: Two Options
Creating a git Repository on GitHub
• Create a repository on our local machine first and then push it to • Go to GitHub website
GitHub • Make sure we are logged in
git push
• Click on the New button here if we already have some repositories
• Create a repository on GitHub first and clone it onto our local • Otherwise, we will see a Create Repository button for first-time users
machine
• Go ahead and create a repository
git clone
Docker
Operating System
Hardware
Kubernetes Architecture
Why Container Orchestration?
• Physical architecture • Logical architecture
• Requirement: We want 10 instances of Microservice A container, 15 Deployment
instances of Microservice B container and ....
• Typical Features:
ReplicaSet
E-Commerce
Logical Architecture Workflow Application
Deployment
kubectl command
Using Kubernetes
Create a Pod Directly or Using a Deployment Replicaset and Service
• Run a pod • kubectl create deployment my-apache --image httpd
• kubectl run my-nginx --image nginx … We must give a name to every pod
• kubectl get pods • kubectl scale deployment my-apache --replicas 2
• kubectl expose deployment my-apache --type=NodePort --port=80
• Create a deployment (and a pod inside it)
• kubectl create deployment my-nginx --image nginx
• kubectl get pods … Note how the two nginx pods look different
• kubectl get all … Note a replicaset was automatically created by the deployment
• Cleanup
• kubectl delete pod my-nginx
• kubectl delete deployment my-nginx
• kind: Pod
metadata:
• name: nginx-deployment
This YAML file can be used to
deploy nginx into a pod • spec:
• metadata: •
•
selector:
matchLabels:
deploy nginx as a deployment
• name: nginx
• app: nginx
• replicas: 2
•
• spec:
template:
• containers: •
•
app: nginx
• - name: nginx •
•
containers:
- name: nginx
kubectl delete -f [Link]
• image: nginx:1.17.3
• image: nginx:1.17.3
• ports:
• - containerPort: 80 kubectl delete -f [Link]
• kind: Deployment
• name: my-nginx
This YAML file can be used to deploy nginx
• Try: localhost:80 in the browser … Not accessible • labels:
• app: nginx
as a service
• Solution: Port forwarding: Forward requests coming to the pod to another
• spec:
• replicas: 3
•
selector:
matchLabels:
• Example: Our nginx deployment has deployed nginx pod on port 80, so •
•
app: nginx
template:
•
metadata:
labels:
kubectl apply -f [Link]
• Run this command: kubectl port-forward nginx-deployment-7b767787b6- • app: nginx
246tl 9999:80 (Replace pod id with one of the actual pod ids by doing
•
•
spec:
•
- name: nginx
image: nginx:latest
•
ports:
- containerPort: 80
• ---
• apiVersion: v1
• kind: Service
• metadata: