Kubernetes Core Concepts

Module 28: DevOps & Deployment - Thursday, Lecture 2

Introduction to Kubernetes

In our previous lecture, we explored container orchestration principles and compared various platforms. Now, we'll dive deep into Kubernetes (often abbreviated as K8s), which has emerged as the de facto standard for container orchestration.

Kubernetes, Greek for "helmsman" or "pilot," was originally developed by Google based on their internal system called Borg. In 2014, Google open-sourced Kubernetes, and it was later donated to the Cloud Native Computing Foundation (CNCF). Today, it's a mature, production-ready platform used by organizations of all sizes to run containerized applications at scale.

timeline title Evolution of Kubernetes 2003-2004 : Google develops Borg internally 2014 : Kubernetes project announced 2015 : Kubernetes v1.0 released : Donated to CNCF 2016-2017 : Rapid adoption begins : Major cloud providers offer managed K8s 2018 : Docker embraces Kubernetes 2019-2020 : Enterprise adoption accelerates 2021-2022 : Kubernetes becomes industry standard 2023-2025 : Focus on security, edge computing, and AI/ML workloads

Kubernetes stands out for its comprehensive approach to container orchestration, rich ecosystem, and passionate community. It's designed to be extensible, allowing developers to add custom resources and controllers for specialized workloads. This flexibility, combined with its declarative approach to infrastructure, makes Kubernetes suitable for a wide range of applications, from simple web services to complex, stateful applications.

In this lecture, we'll explore the core concepts and components of Kubernetes, building the foundation you need to deploy and manage applications effectively.

Kubernetes Architecture

High-Level Architecture

At a high level, Kubernetes consists of a control plane (master components) and worker nodes:

graph TD A[Kubernetes Cluster] --> B[Control Plane] A --> C[Worker Nodes] B --> D[API Server] B --> E[etcd] B --> F[Scheduler] B --> G[Controller Manager] B --> H[Cloud Controller Manager] C --> I[kubelet] C --> J[kube-proxy] C --> K[Container Runtime] I --> L[Pods] L --> M[Containers]

Control Plane Components

The control plane is the brain of Kubernetes, responsible for making global decisions about the cluster:

Node Components

Worker nodes are the machines that run your applications and cloud workflows:

Addons

Kubernetes clusters often include additional components to provide cluster features:

Communication Flow

Understanding how components communicate helps troubleshoot issues:

sequenceDiagram participant User participant API as API Server participant etcd participant Scheduler participant CM as Controller Manager participant kubelet participant Runtime as Container Runtime User->>API: Create Deployment API->>etcd: Store Deployment API->>CM: Notify of new Deployment CM->>API: Create ReplicaSet API->>etcd: Store ReplicaSet CM->>API: Create Pods API->>etcd: Store Pods API->>Scheduler: Notify of unscheduled Pods Scheduler->>API: Assign Pods to Nodes API->>etcd: Update Pod assignments API->>kubelet: Notify of assigned Pod kubelet->>Runtime: Create Container kubelet->>API: Update Pod status API->>etcd: Store updated Pod status

Kubernetes Object Model

Understanding Kubernetes Objects

Kubernetes objects are persistent entities that represent the state of your cluster. They describe:

Every object includes two nested fields:

Kubernetes constantly works to make the current state match the desired state.

Object Definitions

Objects are typically defined in YAML files with these required fields:

# Basic structure of a Kubernetes object definition
apiVersion: v1        # Which API version to use
kind: Pod             # What kind of object
metadata:             # Data to uniquely identify the object
  name: nginx-pod
  labels:
    app: nginx
spec:                 # The desired state of the object
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

Core Kubernetes Objects

Let's explore the fundamental objects that form the building blocks of Kubernetes applications:

graph TD A[Basic Objects] --> B[Pod] A --> C[Service] A --> D[Volume] A --> E[Namespace] F[Controllers] --> G[ReplicaSet] F --> H[Deployment] F --> I[StatefulSet] F --> J[DaemonSet] F --> K[Job/CronJob] L[Configuration] --> M[ConfigMap] L --> N[Secret] O[Networking] --> P[Ingress] O --> Q[NetworkPolicy] R[Storage] --> S[PersistentVolume] R --> T[PersistentVolumeClaim] R --> U[StorageClass]

Pods - The Basic Unit

Pod Concept

A Pod is the smallest deployable unit in Kubernetes. It represents a single instance of a running process in your cluster.

Think of a Pod as a logical host - it contains one or more containers that:

graph TD A[Pod] --> B[Container 1] A --> C[Container 2] A --> D[Shared Storage Volume] E[Pod IP Address] --> A F[Pod Lifecycle] --> A

An analogy for a Pod is an apartment in a building. The apartment (Pod) can have one or more rooms (containers) that share common facilities (network, storage). The building itself is the node, housing multiple apartments.

Pod Lifecycle

Pods have a defined lifecycle:

  1. Pending: The Pod has been accepted but containers are not yet running
  2. Running: At least one container is running
  3. Succeeded: All containers have terminated successfully
  4. Failed: At least one container has terminated with failure
  5. Unknown: The state couldn't be determined

Pod Definition

Here's a simple Pod definition:

apiVersion: v1
kind: Pod
metadata:
  name: web-app
  labels:
    app: web
    tier: frontend
spec:
  containers:
  - name: web-app
    image: nginx:1.19
    ports:
    - containerPort: 80
    resources:
      limits:
        cpu: "0.5"
        memory: "512Mi"
      requests:
        cpu: "0.2"
        memory: "256Mi"
    livenessProbe:
      httpGet:
        path: /healthz
        port: 80
      initialDelaySeconds: 15
      periodSeconds: 20
    readinessProbe:
      httpGet:
        path: /ready
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 10

Multi-Container Pods

While you can run multiple containers in a Pod, it's best to keep them focused on a single concern. Here's an example of a Pod with a main container and a sidecar:

apiVersion: v1
kind: Pod
metadata:
  name: web-app-with-logging
spec:
  containers:
  - name: web-app
    image: nginx:1.19
    ports:
    - containerPort: 80
    volumeMounts:
    - name: logs
      mountPath: /var/log/nginx
  
  - name: log-collector
    image: fluent/fluentd:v1.11
    volumeMounts:
    - name: logs
      mountPath: /var/log/nginx
      readOnly: true
  
  volumes:
  - name: logs
    emptyDir: {}

Pod Placement and Scheduling

You can influence where Pods are scheduled using:

# Node selector example
apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  nodeSelector:
    gpu: "true"
  containers:
  - name: gpu-container
    image: ml-training:latest

When to Use Pods Directly vs. Controllers

Direct Pod usage is rare in production and usually limited to:

In most cases, you'll use controllers like Deployments or StatefulSets to manage Pods.

Controllers for Pod Management

Understanding Controllers

Controllers are control loops that watch the state of your cluster and make changes to move the current state towards the desired state. They manage the lifecycle of Pods and provide additional features like scaling and updates.

graph LR A[Controller] -- "Watches" --> B[Desired State] A -- "Monitors" --> C[Current State] A -- "Takes Action" --> D[Cluster Changes] D --> C

Deployments

Deployments provide declarative updates for Pods and ReplicaSets. They're ideal for stateless applications.

graph TD A[Deployment] --> B[ReplicaSet] B --> C[Pod 1] B --> D[Pod 2] B --> E[Pod 3]

Key features of Deployments:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.19
        ports:
        - containerPort: 80
        resources:
          limits:
            cpu: "0.5"
            memory: "512Mi"
          requests:
            cpu: "0.2"
            memory: "256Mi"

ReplicaSets

ReplicaSets ensure a specified number of Pod replicas are running at any given time. Usually, you don't create ReplicaSets directly but use Deployments instead.

# Usually created automatically by Deployments
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx-replicaset
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.19

StatefulSets

StatefulSets are used for applications that require stable network identities and persistent storage, like databases.

graph TD A[StatefulSet] --> B[Pod-0] A --> C[Pod-1] A --> D[Pod-2] B --> E[PVC-0] C --> F[PVC-1] D --> G[PVC-2] H[Headless Service] --> B H --> C H --> D

Key features of StatefulSets:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: "postgres"
  replicas: 3
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:13
        ports:
        - containerPort: 5432
        volumeMounts:
        - name: postgres-data
          mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
  - metadata:
      name: postgres-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "standard"
      resources:
        requests:
          storage: 10Gi

DaemonSets

DaemonSets ensure all (or some) nodes run a copy of a Pod. As nodes are added to the cluster, Pods are added to them.

graph TD A[DaemonSet] --> B[Node 1] A --> C[Node 2] A --> D[Node 3] B --> E[Pod] C --> F[Pod] D --> G[Pod]

Common uses for DaemonSets:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v3.0.1
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

Jobs and CronJobs

Jobs create Pods that run until successful completion. CronJobs create Jobs on a schedule.

Use cases:

# Job example
apiVersion: batch/v1
kind: Job
metadata:
  name: data-processor
spec:
  template:
    spec:
      containers:
      - name: processor
        image: data-processor:latest
      restartPolicy: OnFailure

# CronJob example
apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-backup
spec:
  schedule: "0 2 * * *"  # Every day at 2 AM
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: backup-tool:latest
          restartPolicy: OnFailure

Choosing the Right Controller

The right controller depends on your application's requirements:

Controller Best For Examples
Deployment Stateless applications Web servers, API services, microservices
StatefulSet Stateful applications Databases, clustered applications, applications requiring stable IDs
DaemonSet Node-level operations Monitoring agents, log collectors, network plugins
Job One-time tasks Data processing, migrations, batch operations
CronJob Scheduled tasks Backups, reports, periodic cleanups

Service Discovery and Networking

The Service Abstraction

Services provide a stable endpoint to access a group of Pods. They solve the problem of Pod IP addresses changing due to scaling, failures, or upgrades.

graph LR A[Client] --> B[Service] B --> C[Pod 1] B --> D[Pod 2] B --> E[Pod 3]

Services use selectors to identify the Pods they should send traffic to and provide load balancing automatically.

Service Types

Kubernetes offers several types of Services:

graph TB subgraph "Cluster" A[Pod 1] B[Pod 2] C[Pod 3] D[ClusterIP Service] E[NodePort Service] F[LoadBalancer Service] D --> A & B & C E --> D F --> E end G[External Client] --> F H[External Client] --> E I[Internal Client] --> D

Service Definition Examples

# ClusterIP Service (default)
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  selector:
    app: backend
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP

# NodePort Service
apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  selector:
    app: web
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30080  # Optional, Kubernetes assigns if not specified
  type: NodePort

# LoadBalancer Service
apiVersion: v1
kind: Service
metadata:
  name: public-service
spec:
  selector:
    app: public
  ports:
  - port: 80
    targetPort: 8080
  type: LoadBalancer

Service Discovery

Kubernetes provides built-in service discovery through:

Using DNS is the recommended approach. Services are accessible at {service-name}.{namespace}.svc.cluster.local.

Ingress Controllers

Ingress is not a Service type but a higher-level resource that manages external access to Services, typically HTTP:

graph LR A[External Client] --> B[Ingress] B --> C[Service A] B --> D[Service B] B --> E[Service C] C --> F[Pods] D --> G[Pods] E --> H[Pods]

Ingress provides:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 80
      - path: /app
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
  tls:
  - hosts:
    - example.com
    secretName: example-tls

Configuration and Secrets

ConfigMaps

ConfigMaps allow you to decouple configuration from container images, making applications more portable:

graph LR A[ConfigMap] --> B[Environment Variables] A --> C[Command-line Arguments] A --> D[Configuration Files] B & C & D --> E[Container]
# Creating a ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  database.host: "db.example.com"
  database.port: "5432"
  ui.properties: |
    color.background=white
    color.foreground=black
    theme=light

# Using a ConfigMap in a Pod
apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:1.0
    env:
    - name: DB_HOST
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: database.host
    - name: DB_PORT
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: database.port
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
  volumes:
  - name: config-volume
    configMap:
      name: app-config

Secrets

Secrets are similar to ConfigMaps but designed for sensitive data. They are:

# Creating a Secret
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  username: YWRtaW4=  # base64 encoded "admin"
  password: cGFzc3dvcmQxMjM=  # base64 encoded "password123"

# Using a Secret in a Pod
apiVersion: v1
kind: Pod
metadata:
  name: db-client
spec:
  containers:
  - name: db-client
    image: db-client:1.0
    env:
    - name: DB_USER
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: username
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: password
    volumeMounts:
    - name: secret-volume
      mountPath: /etc/secrets
      readOnly: true
  volumes:
  - name: secret-volume
    secret:
      secretName: db-credentials

Secret Management Best Practices

For production environments, consider these best practices:

Storage in Kubernetes

Understanding Kubernetes Storage

Containers are ephemeral by nature, so any data stored inside a container is lost when the container restarts. Kubernetes provides several abstractions to handle persistent storage:

graph TD A[Storage] --> B[Volumes] A --> C[Persistent Volumes] A --> D[Persistent Volume Claims] A --> E[Storage Classes] F[Container] --> B G[Pod] --> C G --> D H[Administrator] --> E

Basic Volumes

Volumes are tied to a Pod's lifecycle and support many types:

# Pod with an emptyDir volume
apiVersion: v1
kind: Pod
metadata:
  name: cache-pod
spec:
  containers:
  - name: app
    image: app:1.0
    volumeMounts:
    - name: cache-volume
      mountPath: /cache
  volumes:
  - name: cache-volume
    emptyDir: {}

Persistent Volumes and Claims

For storage that exists beyond a Pod's lifecycle:

graph TD A[StorageClass] --> B[PersistentVolume] C[PersistentVolumeClaim] --> B D[Pod] --> C
# StorageClass definition
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd

# PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-claim
spec:
  storageClassName: fast
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

# Pod using a PVC
apiVersion: v1
kind: Pod
metadata:
  name: database
spec:
  containers:
  - name: db
    image: postgres:13
    volumeMounts:
    - name: data-volume
      mountPath: /var/lib/postgresql/data
  volumes:
  - name: data-volume
    persistentVolumeClaim:
      claimName: data-claim

Dynamic Provisioning

StorageClasses enable dynamic provisioning of PersistentVolumes when PersistentVolumeClaims are created:

  1. Define a StorageClass with a provisioner
  2. Create a PVC that references the StorageClass
  3. The provisioner automatically creates a PV that matches the PVC
  4. The PVC is bound to the new PV

This automation simplifies storage management in cloud environments.

Role-Based Access Control (RBAC)

RBAC Concepts

RBAC is a method of regulating access to resources based on roles assigned to users. In Kubernetes, it involves:

graph TD A[RBAC] --> B[Subjects] A --> C[Resources] A --> D[Verbs] B --> E[Users] B --> F[Groups] B --> G[ServiceAccounts] H[Roles/ClusterRoles] --> I[Rules] I --> C I --> D J[RoleBindings/ClusterRoleBindings] --> B J --> H

RBAC Examples

# Role (namespace-scoped)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

# RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: User
  name: jane
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

# ClusterRole (cluster-wide)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

# ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: managers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

ServiceAccounts

ServiceAccounts provide identities for processes running in Pods to interact with the Kubernetes API:

# Creating a ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-service-account
  namespace: default

# Using a ServiceAccount in a Pod
apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  serviceAccountName: app-service-account
  containers:
  - name: app
    image: app:1.0

RBAC Best Practices

Namespaces and Resource Isolation

Understanding Namespaces

Namespaces provide a mechanism for isolating groups of resources within a cluster. They are particularly useful in multi-tenant environments where multiple teams or projects share a Kubernetes cluster.

graph TD A[Kubernetes Cluster] --> B[Namespace: kube-system] A --> C[Namespace: default] A --> D[Namespace: team-a] A --> E[Namespace: team-b] B --> F[System Pods] C --> G[Default Pods] D --> H[Team A Pods/Services] E --> I[Team B Pods/Services]

Namespaces provide:

# Creating a namespace
apiVersion: v1
kind: Namespace
metadata:
  name: development

# Deploying to a specific namespace
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  namespace: development
spec:
  # ... deployment details ...

Resource Quotas

ResourceQuotas provide constraints that limit aggregate resource consumption per namespace:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: dev-quota
  namespace: development
spec:
  hard:
    pods: "20"
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    services: "10"
    persistentvolumeclaims: "5"

Limit Ranges

LimitRanges set default resource limits for Pods in a namespace:

apiVersion: v1
kind: LimitRange
metadata:
  name: dev-limits
  namespace: development
spec:
  limits:
  - type: Container
    default:
      cpu: 500m
      memory: 256Mi
    defaultRequest:
      cpu: 100m
      memory: 128Mi
    max:
      cpu: 2
      memory: 1Gi
    min:
      cpu: 50m
      memory: 64Mi

Practical Exercise: Deploying a Multi-tier Application

Scenario

In this exercise, you'll deploy a simple multi-tier web application consisting of:

Exercise Tasks

  1. Create a namespace for the application
  2. Deploy the MongoDB database using a StatefulSet with persistent storage
  3. Deploy the backend API using a Deployment
  4. Deploy the frontend using a Deployment
  5. Create Services for all components
  6. Set up an Ingress for external access
  7. Configure the application using ConfigMaps and Secrets

Solution Outline

Here's a starting point for your solution:

# 1. Create namespace
apiVersion: v1
kind: Namespace
metadata:
  name: web-app

# 2. ConfigMap for application configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: web-app
data:
  api.url: "http://backend-service:3000"
  database.host: "mongo-service"
  database.port: "27017"
  database.name: "webappdb"

# 3. Secret for database credentials
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  namespace: web-app
type: Opaque
data:
  username: YWRtaW4=
  password: cGFzc3dvcmQxMjM=

# 4. MongoDB StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mongo
  namespace: web-app
spec:
  serviceName: "mongo-service"
  replicas: 1
  selector:
    matchLabels:
      app: mongo
  template:
    metadata:
      labels:
        app: mongo
    spec:
      containers:
      - name: mongo
        image: mongo:4.4
        ports:
        - containerPort: 27017
        env:
        - name: MONGO_INITDB_ROOT_USERNAME
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: username
        - name: MONGO_INITDB_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password
        volumeMounts:
        - name: mongo-data
          mountPath: /data/db
  volumeClaimTemplates:
  - metadata:
      name: mongo-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

# 5. MongoDB Service
apiVersion: v1
kind: Service
metadata:
  name: mongo-service
  namespace: web-app
spec:
  selector:
    app: mongo
  ports:
  - port: 27017
    targetPort: 27017
  clusterIP: None  # Headless service for StatefulSet

# 6. Backend API Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  namespace: web-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: your-backend-image:latest
        ports:
        - containerPort: 3000
        env:
        - name: DB_HOST
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: database.host
        - name: DB_PORT
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: database.port
        - name: DB_NAME
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: database.name
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password

# 7. Backend Service
apiVersion: v1
kind: Service
metadata:
  name: backend-service
  namespace: web-app
spec:
  selector:
    app: backend
  ports:
  - port: 3000
    targetPort: 3000

# 8. Frontend Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  namespace: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: your-frontend-image:latest
        ports:
        - containerPort: 80
        env:
        - name: API_URL
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: api.url

# 9. Frontend Service
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
  namespace: web-app
spec:
  selector:
    app: frontend
  ports:
  - port: 80
    targetPort: 80

# 10. Ingress for external access
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-app-ingress
  namespace: web-app
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: web-app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: backend-service
            port:
              number: 3000

Complete this exercise by applying these manifests to a Kubernetes cluster. You may need to adjust the images and specific details to match your environment.

Next Steps with Kubernetes

After mastering these core concepts, you can explore these advanced topics:

mindmap root((Advanced Kubernetes Topics)) Monitoring and Observability Prometheus & Grafana Kubernetes Dashboard Distributed Tracing Advanced Networking Network Policies Service Mesh Egress Controls Operators and Custom Resources Custom Resource Definitions Operator Pattern Kubernetes Operators Framework Advanced Deployment Strategies Canary Deployments Blue/Green Deployments Feature Flags Security Pod Security Policies Network Security Image Scanning Cluster Management Cluster Autoscaling Multi-cluster Management GitOps with Flux/ArgoCD

In our next lecture, we'll explore how to deploy applications on Kubernetes, which will bring these concepts together into practical implementations.

Conclusion

Kubernetes provides a powerful platform for container orchestration with a rich set of abstractions. By understanding these core concepts, you can effectively deploy and manage applications in a containerized environment.

Key takeaways from this lecture include:

As you continue your Kubernetes journey, remember that mastering these fundamental concepts will provide a solid foundation for working with this powerful platform.

Additional Resources