Kubernetes Manifests / Custom Resource / Objects
Kubernetes Manifests are physical YAML file which are used to
create Kubernetes Objects.
While native Kubernetes gives you built-in kinds like (Service,
Deployment, or Pod).
Kubebuilder provides the tooling to define
entirely new kinds tailored to your applications (such as kind:
Database, kind: RedisCluster, or kind: AppDeployment).
Kubernets Object
The Object is the actual active entity running inside the cluster after
you submit the manifest using a command like "kubectl apply -f
file.yaml"
Required Manifests
- Only deployment.yaml and service.yaml are mandatory to deploy and
access your container.
- Rest are optional tools used to handle scaling, security,
configuration, and monitoring.
Predefined Kubernets Manifest Types / Custom Resource
1. deployment.yaml (Mandatory)
|
In kubernetes docker image is not build,
Image is only pulled(from registry) and pod is created. Deployment Create a pod when kubectl apply -f test.yaml is
executed.Deployment is self healing if pod crashes kubernetes Controller automatically provisions a brand-new Pod to replace it. Zero downtime updates: if we want to update the Redpanda version, we just change the image name in the YAML and apply it. |
2. service.yaml (Mandatory)
|
Service In Kubernetes, Pods are temporary. They are created, destroyed, and replaced constantly during updates or node failures. if pod gets deleted and new pod is created, how new pod would be reachable? A Service provides a static/permanent IP address and a permanent DNS name (kafka.test.svc.cluster.local) to pod Now other apps will talk to service Every time a new Pod is created, it gets a dynamic, unpredictable IP address.
graph LR
%% Define styles
classDef client fill:#f9f,stroke:#333,stroke-width:2px;
classDef svc fill:#85C1E9,stroke:#333,stroke-width:2px;
classDef deployment fill:#D2B4DE,stroke:#333,stroke-width:2px;
classDef pod fill:#2ECC71,stroke:#333,stroke-width:2px;
classDef dns fill:#F5B041,stroke:#333,stroke-width:2px;
%% Components
Client[Client App-X Pod
|
Every service is assigned a CLUSTER-IP, via which pod behind service can
be reached.
CLUSTER-IP: Kubernetes assigns an internal virtual IP (the ClusterIP).
when any pod inside the cluster sends a packet to that ClusterIP, the
packet gets NATed and load-balanced to one of the backend pods
$ kubectl get services -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORTS(s) AGE SELECTOR
pod1-service NodePort 10.43.200.121 <none> 8080:30102/TCP 4h5m app=pod1
NodePort is build on Services Read about NodePort 1st.
A ClusterIP service is created (e.g., 10.96.1.20:80)
kube-proxy adds iptables rules on each node: <NodeIP>:30080 → 10.96.1.20:80 → PodIP:8080
Packet Flow:
External client → NodeIP:30080 → ClusterIP:80 → PodIP:8080
That’s why kubectl get svc still shows a ClusterIP — it always exists; NodePort is built on top of it.
3. ServiceAccount
When someone creates a pod using
kubectl apply -f deloy.yaml,
kubectl
creates a JWT token for this pod and places
token inside (/var/run/secrets/...)
After pod bringup if application running inside pod wants communicate
with
API Server
for example to run some command kubectl get pods -A, then
pod will present the created JWT token and API server will validate it
Credentials used by service running inside pod to communicate with API
server.
This JWT token is associated with serviceAccount
|
sequenceDiagram
actor user as User
box "Worker Node"
participant kctl as Kubectl
participant App as Pod
|
4. kind: PodDisruptionBudget (PDB)
|
Limits the number of pods of an application that can be down at the
same time during voluntary disruptions or during mainteinance. Think of it as an insurance policy that guarantees your application always has enough running pods to handle traffic, even when cluster administrators are messing with the infrastructure. PDB information is stored in Control Plane on etcd
sequenceDiagram
autonumber
box Worker Node
actor Admin as Cluster Administrator
end
box Control Plane
participant API as API Server
participant DB as etcd (Database)
participant Ctrl as ControllerManager
|
5. hpa.yaml (Horizontal Pod Autoscaler) (Optional)
|
Scales the number of Pod replicas up or down automatically based on
real-time CPU or memory usage metrics. How pods are bringup automatically at load? When kubectl apply -f hpa.yaml is executed it reaches
API server(control plane)- The API Server checks syntax, permissions, and validates that the target app exists. - Creation: The HorizontalPodAutoscaler (HPA) resource is officially registered in your cluster. - Check Controller. Loop inside the Controller Manager wakes up and starts monitoring app's CPU usage every 15 seconds.
sequenceDiagram
autonumber
box rgba(0, 204, 102, 0.1) Worker Node
participant MS as Metrics Server Pod
participant Node as Kubelet Agent
actor Admin as Administrator
end
box rgba(0, 128, 255, 0.1) Control Plane
participant API as API Server
participant DB as etcd Database
participant HPA as HPA Controller
participant RS as ReplicaSet Controller
end
Note over Admin: kubectl apply -f hpa.yaml
Admin->>API: kubectl command travels to Api server
|
kind: Ingress
An Ingress is a Kubernetes API object that manages external HTTP/HTTPS
access to services inside a cluster.
It acts as an application-layer (Layer 7) load balancer and reverse
proxy, allowing you to route traffic based on URL paths (e.g., /api,
/login) or domain names (e.g., ingest.local) to different internal
Kubernetes Services.
An Ingress resource requires an Ingress Controller (such as NGINX,
Traefik, or HAProxy) running in the cluster to actually fulfill the
rules and handle the incoming traffic.
|
ingressClassName: Tells Kubernetes that this Ingress resource should
be processed and managed specifically by the NGINX Ingress
controller. annotations: Custom configurations specific to the NGINX controller. Here, they extend the read and send timeouts to 3,600 seconds (1 hour), which is useful for long-running ingestion streams. paths: Directs any traffic matching the prefix / to the Kubernetes Service named server-ingest on port 8080.
graph LR
classDef client fill:#f9f,stroke:#333,stroke-width:2px;
classDef ing fill:#F5B041,stroke:#333,stroke-width:2px;
classDef svc fill:#85C1E9,stroke:#333,stroke-width:2px;
classDef pod fill:#2ECC71,stroke:#333,stroke-width:2px;
C[Web Client
|
How Ingress relates to NetworkPolicy
| Ingress | NetworkPolicy | |
|---|---|---|
| Network Layer | Layer 7 (Application / HTTP/HTTPS) | Layer 3 & 4 (IP addresses, ports, protocols like TCP/UDP) |
| Traffic Direction |
(Outside to Inside Cluster) Inbound traffic coming from outside the cluster into internal Services. |
(Inside Cluster. Pod to Pod) Internal traffic between pods (Ingress and Egress relative to a pod) within the cluster. |
| Prerequisite | Requires an Ingress Controller (e.g., NGINX). | Requires a CNI plugin that supports NetworkPolicies (e.g., Calico, Cilium, Flannel with support). |
Kind: NetworkPolicy
RBAC decides who may call the API server.
NetworkPolicy decides which Pods may open a TCP/UDP connection to which
other Pods in cluster or which need to send data to outside world. This
is a firewall for pod
policyTypes says which direction this YAML is a firewall for:
| policyTypes | Meaning |
|---|---|
Ingress |
Rules for traffic coming in to the selected Pods (who may call me) |
Egress |
Rules for traffic going out from the selected Pods (who I may call) |
Why use Egress: a stolen or buggy app inside a Pod cannot scan the cluster, dump data to a random IP, or call the public internet — it can only reach what you listed (for example the database).
|
Very simple story Pods labeled app: shop run the store frontend.They should call Postgres ( app: db, port 5432) and
nothing else.podSelector = whose outgoing traffic we filter.policyTypes: [Egress] = we care about
shop’s outbound connections, not who calls shop.egress.to = the only destination still allowed.After apply:
CNI must support NetworkPolicy (Calico, Cilium, …). Without that
plugin the YAML is stored but not enforced.
|
4. namespace.yaml (Optional)
Creates an isolated virtual workspace folder inside your cluster to organize resources and separate environments (e.g., production vs staging)
apiVersion: v1
kind: Namespace
metadata:
name: production-env
NodePool
What is
Karpenter?
NodePool is Karpenter Configuration
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: default
spec:
template:
spec:
requirements:
# Tells Karpenter to only provision Linux AMD64 machines
- key: "kubernetes.io/os"
operator: In
values: ["linux"]
- key: "kubernetes.io/arch"
operator: In
values: ["amd64"]
# Allow both cheap Spot instances and reliable On-Demand instances
- key: "karpenter.sh/capacity-type"
operator: In
values: ["spot", "on-demand"]
nodeClassRef:
name: default # Links to cloud-specific settings (like AWS subnets/security groups)
limits:
cpu: 1000 # Maximum total CPU cores Karpenter is allowed to spin up
5. configmap-config.yaml & configmap-policy.yaml (Optional)
Stores non-sensitive settings (like configuration files, environment paths, or feature flags) so you do not have to bake them into your Docker image.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DB_HOST: "database.internal.net"
LOG_LEVEL: "debug"
6. secret-ca.yaml (Optional)
Stores sensitive information securely (like SSL certificates, API tokens, or passwords) using Base64 encryption, preventing secrets from being written in plain text.
apiVersion: v1
kind: Secret
metadata:
name: secret-ca
type: Opaque
data:
ca.crt: dGhpcyBpcyBhIGZha2U= # Base64 encoded string of your certificate
7. servicemonitor.yaml (Optional) (Requires Prometheus installed)
This is a Custom Resource (CRD) from the Prometheus system. It tells Prometheus how and where to scrape performance metrics from your application service.
apiVersion: ://coreos.com
kind: ServiceMonitor
metadata:
name: app-monitor
spec:
selector:
matchLabels:
app: web-app
endpoints:
- port: metrics-port
interval: 15s
8. ClusterRole (Optional)
Defines set of permissions or access control rules for resources across an entire Kubernetes cluster. it applies to all namespaces in the cluster.
$ test.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata: //Metadata of clusterRole
name: my-cluster-role
annotations: //Annotations: any number of key-value pairs, and can be used to provide additional context
my-annotation: "example"
namespace: "test"
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
9. Job
This object runs a specific task to completion. will create 1 or more pods and execute continously until job completes.
apiVersion: batch/v1
kind: Job
metadata:
name: pi #Name of Job
spec:
template:
spec:
serviceAccountName: "Test" // Name of ServiceAccount that should be used by the pod that is created to run the Job
containers: //Container configuration for job
- name: pi // Container name to be created by this Job
image: perl:5.34.0
env: //environment variables to set for the container.
- name: DATABASE_HOST //this env variable is set using a SecretKeyRef
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-test-db
key: host
restartPolicy: Never
backoffLimit: 4
10. RBAC (Role-based Access Control)
refers to the authorization mechanism that allows one Kubernetes service or workload to access another service or resource within a cluster based on predefined roles and permissions(eg: configmaps, secrets etc). The RBAC API declares 4 kinds of Kubernetes object
| a. Role & RoleBindings |
Defines who (subjects) can perform actions/verbs(create, get, update
etc) on which resources(eg: pods, deployments, services). Roles
specify the permissions, and RoleBindings associate these roles with
service accounts, users, or groups
|
| b. RoleBinding |
Grants the permissions defined in a role to Subjects. Subjects can
be user or set of users. Example: user:jane can read pods in default
namespace
|
| c. CapabilityMapping |
1. Give capabilities to a process running within linux container,
Eg(process to modify n/w config, mouting file system, accessing h/w
devices etc) 2. TAMS capability mapping When mapping-a is enabled, service can call method1,2. When mapping-b is enabled, service can call method3,4. |