Deploying Nginx on Kubernetes: Exploring Various Methods

Mastering Nginx Deployment in Kubernetes: A Comprehensive Guide to Pods, Deployments, and Beyond

Anvesh Muppeda
12 min readJan 13, 2024
nginx in k8's

Explore the essential steps and best practices for deploying the popular web server, Nginx, on Kubernetes. This quick guide provides a straightforward walkthrough, offering insights into setting up Nginx within a Kubernetes environment. Whether you’re a Kubernetes beginner or looking for a fast deployment solution, this guide simplifies the process, ensuring you have Nginx up and running in no time.

In Kubernetes, there are different resources you can use to deploy and manage applications. Here are several ways to deploy an Nginx pod using various Kubernetes resources:

  1. Pod
  2. Replication Cotroller
  3. Replicaset
  4. Deployment
  5. Statefulset
  6. Daemonset
  7. Helm

Prerequisites:

  • Kubernetes environment
  • Kubectl CLI installed
  • Helm CLI installed

1. Pod

The fundamental unit in Kubernetes. Dive into the basics with this quick guide on deploying Nginx as a Pod. While Pods might not be the go-to choice for production, understanding this fundamental unit is crucial for Kubernetes mastery.

In Kubernetes, you can deploy an Nginx pod using both imperative and declarative approaches. Let’s go through both methods:

a. Imperative Way:

Imperative commands are those where you directly instruct Kubernetes to perform a specific action.

Using kubectl run:

kubectl run nginx-pod --image=nginx --restart=Never --port=80 -n default

This command creates a pod named nginx-pod in default namespace using the Nginx Docker image. The --restart=Never flag indicates that it's a one-time job and won't be restarted automatically if it fails or stops.

Verify the Pod is Running using below command:

kubectl get pods

This will show you the status of your pods.

NAME        READY   STATUS    RESTARTS   AGE
nginx-pod 1/1 Running 0 3s

Now pod is up and running let’s create a service to access application externally

Using kubectl run:

kubectl expose pod nginx-pod --type=NodePort --port=80 --name=nginx-service

This command exposes the Nginx pod using a NodePort service, making it accessible externally on a specific port.

Verify the service is created using below command:

kubectl get svc

This will show you the service that is created for nginx

k get svc                                          
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4h31m
nginx-service NodePort 10.109.112.35 <none> 80:30684/TCP 104s

Now, let’s attempt to reach the Nginx application through the browser. I’m utilizing the following commands since I’m using Minikube for my Kubernetes cluster.

minikube service nginx-service --url
http://127.0.0.1:49867
❗ Because you are using a Docker driver on darwin, the terminal needs to be open to run it.

Accessing the Nginx application is possible by visiting the provided URL, which is localhost:49867.

nginx

b. Declarative Way:

Declarative configuration involves creating a YAML file that describes the desired state of the resource.

In the world of YAML manifests, the simplicity of defining a Pod is evident:

Create a YAML file for nginx pod (nginx-pod.yaml):

apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:latest
ports:
- containerPort: 80

Apply the YAML file using below command:

kubectl apply -f nginx-pod.yaml -n default

This command tells Kubernetes to create or update the resource described in the YAML file. Which means it will create a nginx pod in default namespace

Verify the Pod is Running using below command:

kubectl get pods

This will show you the status of your pods.

NAME        READY   STATUS    RESTARTS   AGE
nginx-pod 1/1 Running 0 3s

Now pod is up and running let’s create a service to access application externally

Create a YAML file for nginx service (nginx-service.yaml):

apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels:
app: nginx
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80
targetPort: 80

Apply the YAML file using below command:

kubectl apply -f nginx-service.yaml -n default

This will create a nginx-svc in default namespace.

Verify the service is created using below command:

kubectl get svc

This will show you the service that is created for nginx

k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4h47m
nginx-svc NodePort 10.103.151.51 <none> 80:32382/TCP 11s

Now we can access nginx application externally same as above.

Both approaches achieve the same result, but the declarative method is preferred for many reasons, including better version control, ease of automation, and better collaboration within a team.

Cleanup

Imperative Cleanup:

# Delete the service
kubectl delete service nginx-service
# Delete the pod
kubectl delete pod nginx-pod

Declarative Cleanup:

# Delete the service using the YAML file
kubectl delete -f nginx-service.yaml
# Delete the pod using the YAML file
kubectl delete -f nginx-pod.yaml

These commands will remove the Service and Pod that you created for Nginx. After running these commands, you can verify that the resources have been deleted by checking the status:

# Verify that the service is deleted
kubectl get services

# Verify that the pod is deleted
kubectl get pods

Make sure that there are no remaining resources related to Nginx in your Kubernetes cluster.

2. ReplicationController (Deprecated):

A ReplicationController ensures that a specified number of pod replicas are running at any one time. In other words, a ReplicationController makes sure that a pod or a homogeneous set of pods is always up and available.

Let’s explore the step-by-step process of deploying Nginx using a ReplicationController and ensuring accessibility through a Service.

Create a YAML file for nginx ReplicationController (nginx-ReplicationController.yaml):

In your YAML manifest, define the ReplicationController. This example ensures two replicas of the Nginx container:

apiVersion: v1
kind: ReplicationController
metadata:
name: nginx-replication-controller
labels:
app: nginx
spec:
replicas: 2
selector:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:latest

This ReplicationController ensures the desired number of replicas (in this case, 2) and selects Pods based on the specified label (app: nginx).

Apply the YAML file using below command:

kubectl apply -f nginx-ReplicationController.yaml -n default

Verify the Pod is Running using below command:

kubectl get pods

This will show you the status of your pods.

k get pods        
NAME READY STATUS RESTARTS AGE
nginx-replication-controller-fw7jw 1/1 Running 0 11m
nginx-replication-controller-xgbcg 1/1 Running 0 11m

Expose Nginx with a Service:

Now, let’s create a Service to expose the Nginx replicas (nginx-svc.yaml) :

apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels:
app: nginx
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80
targetPort: 80

Apply the YAML file using below command:

kubectl apply -f nginx-service.yaml -n default

This will create a nginx-svc in default namespace.

Verify the service is created using below command:

kubectl get svc

Now we can access nginx application externally same as above.

3. ReplicaSet:

ReplicaSets are a more powerful and flexible replacement for ReplicationControllers. They provide additional features like selector options and rolling updates.

In your YAML manifest, define the ReplicaSet. This example ensures two replicas of the Nginx container and provides selector options for future updates:

Create nginxReplicaSet.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-replicaset
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:latest

Create nginx-svc.yaml

apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80

Apply above yaml files using below command

kubectl apply -f nginx-replicaset.yaml
kubectl apply -f nginx-svc.yaml

Check the running pods and services using below command

kubectl get all 

Output

k get all   
NAME READY STATUS RESTARTS AGE
pod/nginx-replicaset-b2jfm 1/1 Running 0 2m12s
pod/nginx-replicaset-qhjnn 1/1 Running 0 2m12s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5h38m
service/nginx-svc NodePort 10.99.173.144 <none> 80:31669/TCP 92s

NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-replicaset 2 2 2 2m12s

Now nginx is up and running, we can access nginx application externally same as above(step 1).

4. Deployment

When it comes to deploying applications on Kubernetes, Deployments are the go-to resource for many reasons. Deployments are a higher-level abstraction that includes ReplicaSets. They provide features like rolling updates, rollback, and scaling.

Create nginx-deploy.yaml

In your YAML manifest, define the Deployment. This example ensures two replicas of the Nginx container and sets up labels for easy management:

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:latest

Create nginx-svc.yaml

apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80

Apply above yaml files using below command

kubectl apply -f nginx-deploy.yaml
kubectl apply -f nginx-svc.yaml

Check the running pods and services using below command in default namespace

kubectl get all

Output

k get all
NAME READY STATUS RESTARTS AGE
pod/nginx-deployment-86dcbffdfb-gkmx4 1/1 Running 0 2m44s
pod/nginx-deployment-86dcbffdfb-n5mlf 1/1 Running 0 2m44s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 24h
service/nginx-svc NodePort 10.98.167.30 <none> 80:30135/TCP 2m44s

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 2/2 2 2 2m44s

NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-deployment-86dcbffdfb 2 2 2 2m44s

You’ve successfully deployed Nginx on Kubernetes using a Deployment and exposed it with a Service. Take advantage of rolling updates and effortless scaling provided by the Deployment resource, elevating your Kubernetes deployment strategy.

This knowledge equips you with advanced tools for managing your Nginx instances in a dynamic and scalable manner.

Now nginx is up and running, we can access nginx application externally same as above(step 1).

5. Statefulset

Like a Deployment, a StatefulSet manages Pods that are based on an identical container spec. Unlike a Deployment, a StatefulSet maintains a sticky identity for each of its Pods. These pods are created from the same spec, but are not interchangeable: each has a persistent identifier that it maintains across any rescheduling.

For stateful applications or those requiring stable network identifiers and persistent storage, StatefulSets are the ideal choice.

Create nginx-sts.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx-sts
labels:
app: nginx
spec:
replicas: 2
serviceName: nginx
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:latest

This StatefulSet ensures two replicas and sets up a stable network identifier for each pod, crucial for stateful applications.

The serviceName in a StatefulSet is used to create a headless service that can be used to discover and communicate with individual pods managed by the StatefulSet. The serviceName specifies the name of the service that is responsible for the network identity of the pods.

Create nginx-svc.yaml

apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80

Apply above yaml files using below command

kubectl apply -f nginx-sts.yaml
kubectl apply -f nginx-svc.yaml

Check the running pods and services using below command in default namespace

kubectl get all

Output

k get all   
NAME READY STATUS RESTARTS AGE
pod/nginx-sts-0 1/1 Running 0 4s
pod/nginx-sts-1 1/1 Running 0 2s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 24h
service/nginx-svc NodePort 10.96.188.107 <none> 80:32599/TCP 4s

NAME READY AGE
statefulset.apps/nginx-sts 2/2 4s

Now nginx is up and running, we can access nginx application externally same as above(step 1).

6. Daemonset

DaemonSet ensures that all (or some) Nodes run a copy of a Pod. As nodes are added to the cluster, Pods are added to them. As nodes are removed from the cluster, those Pods are garbage collected. Deleting a DaemonSet will clean up the Pods it created.

Some typical uses of a DaemonSet are:

  • running a cluster storage daemon on every node
  • running a logs collection daemon on every node
  • running a node monitoring daemon on every node

Let’s create nginx-daemonset.yaml

apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-daemonset
labels:
app: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:latest

This DaemonSet ensures one replica on each node, maintaining a copy of the pod across your entire cluster.

Create nginx-svc.yaml

apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80

Apply above yaml files using below command

kubectl apply -f nginx-daemonset.yaml
kubectl apply -f nginx-svc.yaml

Check the running pods and services using below command in default namespace

kubectl get all -l app=nginx

Output

kubectl get all -l app=nginx
NAME READY STATUS RESTARTS AGE
pod/nginx-daemonset-j9nvr 1/1 Running 0 5m53s

NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/nginx-daemonset 1 1 1 1 1 <none> 5m53s

Now nginx is up and running, we can access nginx application externally same as above(step 1).

7. Helm

For streamlined and repeatable Kubernetes deployments, Helm Charts provide a powerful packaging format. Let’s walk through the detailed steps of deploying Nginx using Helm, a package manager for Kubernetes applications.

Helm is a Kubernetes package manager that simplifies deploying and managing applications. It uses charts — packages of pre-configured Kubernetes resources — to define, install, and upgrade even the most complex Kubernetes applications.

Before you begin, ensure you have Helm installed on your local machine. You can install Helm by following the instructions here.

Create a Helm Chart for Nginx using below command.

helm create nginx-chart

Helm will create a new directory in your project called nginx-chart with the structure shown below.

mychart
|-- Chart.yaml
|-- charts
|-- templates
| |-- NOTES.txt
| |-- _helpers.tpl
| |-- deployment.yaml
| |-- ingress.yaml
| `-- service.yaml
`-- values.yaml

Here remove all files under templates/ and update with the below files.

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}
labels:
{{- include "nginx-chart.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "nginx-chart.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "nginx-chart.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default "latest" }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP

service.yaml

apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-svc
labels:
{{- include "nginx-chart.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "nginx-chart.selectorLabels" . | nindent 4 }}

_helper.tpl

{{/*
Expand the name of the chart.
*/}}
{{- define "nginx-chart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "nginx-chart.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "nginx-chart.labels" -}}
helm.sh/chart: {{ include "nginx-chart.chart" . }}
{{ include "nginx-chart.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "nginx-chart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "nginx-chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app: {{ .Release.Name }}
{{- end }}

NOTES.txt

1. Get the application URL by running this commands:
minikube service {{ .Release.Name }}-svc --url

Now override the values.yaml file with below content:

# Default values for nginx-chart.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

replicaCount: 1

image:
repository: nginx
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: ""

service:
type: NodePort
port: 80

Now helm chart is ready to deploy, use below command to install the chart in default namespace.

helm install <release-name> <chart-name> -n <namespace>

In our case

helm install nginx nginx-chart -n default

This will give the below output:

helm install nginx nginx-chart
NAME: nginx
LAST DEPLOYED: Fri Jan 12 18:20:16 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running this commands:
minikube service nginx-svc --url

Now helm chart is installed, let’s verify the newly created pods and service using below command:

k get all -l app=nginx

Output:

k get all -l app=nginx
NAME READY STATUS RESTARTS AGE
pod/nginx-5598bb99d7-8krw4 1/1 Running 0 95s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx-svc NodePort 10.96.185.42 <none> 80:31026/TCP 95s

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx 1/1 1 1 95s

NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-5598bb99d7 1 1 1 95s

Now nginx is up and running, we can access nginx application externally same as above(step 1).

Cleanup:

Use below command to uninstall helm chart so that it will delete all dependent resources i.e., service and deployment.

helm uninstall <release-name>

In our case:

helm uninstall nginx

If you found this blog insightful and would like to discuss AWS, cloud strategies, Kubernetes, or any related topics further, I’d love to connect with you on LinkedIn.

Feel free to reach out, share your thoughts, or ask any questions. Let’s grow and learn together in the vast world of cloud computing!

Happy deploying! 🚀

Happy Kubernetings! 🚀

--

--

Anvesh Muppeda

🤝Cloud Architect & DevOps Engineer || Kubernetes ⎈ & Docker ⛴️ aficionado || CKA || CKAD || AWS SAA || Connect with me on www.linkedin.com/in/anveshmuppeda