Introduction: The Importance of Configuration Management and External Access

One of the most important challenges when operating containerized applications is separation of configuration. Separating application code from configuration allows you to reuse the same image across development, staging, and production environments. Kubernetes provides ConfigMap and Secret resources for this purpose.

Additionally, proper routing mechanisms are needed for external access to internal cluster services. Ingress acts as an L7 load balancer that routes HTTP/HTTPS traffic to services inside the cluster. In this part, we'll explore these three core resources in detail.

1. The Importance of Configuration Management

1.1 Why Separate Configuration?

As emphasized in the 12-Factor App methodology, configuration should be separated from code:

  • Environment-specific deployment: Deploy the same image to various environments (development, test, production)
  • Enhanced security: No need to include sensitive information in code repositories
  • Flexible changes: Apply configuration changes without rebuilding images
  • Team collaboration: Developers and operators can independently manage their respective areas

1.2 Kubernetes Configuration Management Methods

Kubernetes manages configuration with two resources:

Resource Purpose Storage Method
ConfigMap General configuration data Plain text
Secret Sensitive information Base64 encoded

2. Creating and Using ConfigMaps

2.1 What is a ConfigMap?

A ConfigMap is a Kubernetes resource that stores configuration data as key-value pairs. You can store application environment variables, configuration files, command-line arguments, and more.

2.2 Methods to Create ConfigMaps

Method 1: Create from literal values

# Single key-value pair
kubectl create configmap app-config --from-literal=APP_ENV=production

# Multiple key-value pairs
kubectl create configmap app-config \
  --from-literal=APP_ENV=production \
  --from-literal=LOG_LEVEL=info \
  --from-literal=MAX_CONNECTIONS=100

Method 2: Create from file

# Single file
kubectl create configmap nginx-config --from-file=nginx.conf

# Entire directory
kubectl create configmap app-configs --from-file=./configs/

Method 3: Create with YAML manifest

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: default
data:
  # Simple key-value pairs
  APP_ENV: "production"
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "100"

  # Multi-line configuration file
  application.properties: |
    server.port=8080
    spring.datasource.url=jdbc:mysql://mysql:3306/mydb
    spring.jpa.hibernate.ddl-auto=update
    logging.level.root=INFO

  # JSON format configuration
  config.json: |
    {
      "apiEndpoint": "https://api.example.com",
      "timeout": 30,
      "retryCount": 3
    }
kubectl apply -f configmap.yaml

2.3 Using ConfigMaps: Environment Variables

Using individual keys as environment variables

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    env:
    # Inject specific keys as environment variables
    - name: APPLICATION_ENV
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: APP_ENV
    - name: LOG_LEVEL
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: LOG_LEVEL

Using all keys as environment variables

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    envFrom:
    - configMapRef:
        name: app-config
    # Can add prefix
    - configMapRef:
        name: app-config
      prefix: CONFIG_

2.4 Using ConfigMaps: Volume Mount

When configuration files are needed, you can mount them as volumes:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx:latest
    volumeMounts:
    - name: config-volume
      mountPath: /etc/nginx/conf.d
      readOnly: true
  volumes:
  - name: config-volume
    configMap:
      name: nginx-config
      # To mount only specific keys
      items:
      - key: nginx.conf
        path: default.conf

3. Creating and Using Secrets

3.1 What is a Secret?

A Secret is a resource for storing sensitive information like passwords, OAuth tokens, and SSH keys. It's similar to ConfigMap, but data is stored Base64-encoded.

3.2 Secret Types

Type Description Use Case
Opaque Arbitrary user-defined data Passwords, API keys
kubernetes.io/tls TLS certificates HTTPS certificates
kubernetes.io/dockerconfigjson Docker registry auth Private registry access
kubernetes.io/basic-auth Basic auth credentials Username/password
kubernetes.io/ssh-auth SSH authentication SSH private keys

3.3 Creating Opaque Secrets

Method 1: Create from command line

# Create from literal values
kubectl create secret generic db-credentials \
  --from-literal=username=admin \
  --from-literal=password=s3cr3tP@ssw0rd

# Create from file
kubectl create secret generic ssh-key \
  --from-file=ssh-privatekey=/path/to/id_rsa

Method 2: Create with YAML manifest

# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  # Base64 encoded values
  # echo -n 'admin' | base64 -> YWRtaW4=
  username: YWRtaW4=
  # echo -n 's3cr3tP@ssw0rd' | base64 -> czNjcjN0UEBzc3cwcmQ=
  password: czNjcjN0UEBzc3cwcmQ=
---
# Using stringData (automatically Base64 encoded)
apiVersion: v1
kind: Secret
metadata:
  name: api-credentials
type: Opaque
stringData:
  api-key: my-super-secret-api-key
  api-secret: another-secret-value

3.4 Creating TLS Secrets

# Create TLS certificate Secret
kubectl create secret tls tls-secret \
  --cert=path/to/tls.crt \
  --key=path/to/tls.key

3.5 Using Secrets

Inject as environment variables

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    env:
    - name: DB_USERNAME
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: username
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: password

Mount as volume

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    volumeMounts:
    - name: secret-volume
      mountPath: /etc/secrets
      readOnly: true
  volumes:
  - name: secret-volume
    secret:
      secretName: db-credentials
      # Set file permissions
      defaultMode: 0400

3.6 Secret Security Considerations

Secrets are only Base64 encoded by default, not encrypted. Consider the following security measures:

  • etcd encryption: Encrypt Secrets stored in etcd
  • RBAC restrictions: Minimize access permissions to Secrets
  • External secret management tools: HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, etc.
  • Secret rotation: Regularly change Secret values
  • Audit logging: Enable audit logs for Secret access

4. What is Ingress?

4.1 Ingress Concept

Ingress is an API object that manages HTTP/HTTPS traffic from outside the cluster to internal services. As an L7 (application layer) load balancer, it provides:

  • Host-based routing: Route to different services based on domain name
  • Path-based routing: Route to different services based on URL path
  • TLS/SSL termination: Handle HTTPS traffic
  • Load balancing: Distribute traffic across multiple Pods

4.2 Ingress vs Service (NodePort/LoadBalancer)

Feature NodePort LoadBalancer Ingress
Layer L4 L4 L7
Protocol TCP/UDP TCP/UDP HTTP/HTTPS
URL Routing Not possible Not possible Possible
TLS Termination Not possible Not possible Possible
Cost Free Cloud costs Single LB for multiple services

5. Ingress Controller

5.1 What is an Ingress Controller?

An Ingress resource is just declarative routing rules; an Ingress Controller is needed for it to actually work. The Ingress Controller watches Ingress resources and configures the load balancer accordingly.

5.2 Major Ingress Controllers

Nginx Ingress Controller

# Install Nginx Ingress Controller (Helm)
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install nginx-ingress ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --create-namespace

# Or install with manifest
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml

Traefik Ingress Controller

# Install Traefik (Helm)
helm repo add traefik https://helm.traefik.io/traefik
helm repo update
helm install traefik traefik/traefik \
  --namespace traefik \
  --create-namespace

6. Writing Ingress YAML

6.1 Basic Ingress Structure

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    # Ingress Controller specific settings
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx  # Specify which Ingress Controller to use
  rules:
  - host: example.com      # Host-based routing
    http:
      paths:
      - path: /            # Path-based routing
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80

6.2 pathType Options

  • Exact: Matches only exact path
  • Prefix: Matches all requests starting with specified path
  • ImplementationSpecific: Behavior depends on Ingress Controller

7. Host/Path-based Routing

7.1 Host-based Routing

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: host-based-ingress
spec:
  ingressClassName: nginx
  rules:
  # api.example.com -> api-service
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080

  # web.example.com -> web-service
  - host: web.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

7.2 Path-based Routing

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: path-based-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
  - host: example.com
    http:
      paths:
      # example.com/api/* -> api-service
      - path: /api(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: api-service
            port:
              number: 8080

      # example.com/web/* -> web-service
      - path: /web(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: web-service
            port:
              number: 80

      # example.com/* (default) -> frontend-service
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80

8. TLS Configuration

8.1 Creating TLS Secret

# Create self-signed certificate (for testing)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout tls.key \
  -out tls.crt \
  -subj "/CN=example.com"

# Create TLS Secret
kubectl create secret tls example-tls \
  --cert=tls.crt \
  --key=tls.key

8.2 Applying TLS to Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - example.com
    - www.example.com
    secretName: example-tls
  - hosts:
    - api.example.com
    secretName: api-tls
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080

8.3 Automatic Certificate Management with cert-manager

# Install cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
# Let's Encrypt ClusterIssuer configuration
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: your-email@example.com
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
    - http01:
        ingress:
          class: nginx
---
# Auto certificate issuance Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: auto-tls-ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - example.com
    secretName: example-tls-auto  # cert-manager creates automatically
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

9. Practical Example: Microservices Routing

# Complete microservices Ingress configuration
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: microservices-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - myapp.example.com
    secretName: myapp-tls
  rules:
  - host: myapp.example.com
    http:
      paths:
      # API Gateway
      - path: /api/v1/users
        pathType: Prefix
        backend:
          service:
            name: user-service
            port:
              number: 8080
      - path: /api/v1/products
        pathType: Prefix
        backend:
          service:
            name: product-service
            port:
              number: 8080
      - path: /api/v1/orders
        pathType: Prefix
        backend:
          service:
            name: order-service
            port:
              number: 8080
      # WebSocket
      - path: /ws
        pathType: Prefix
        backend:
          service:
            name: websocket-service
            port:
              number: 8081
      # Frontend (default)
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80

10. Conclusion

In this part, we explored Kubernetes' core configuration management resources ConfigMap and Secret, and Ingress for managing external traffic:

  • ConfigMap: Manages general configuration data, can be injected into Pods as environment variables or volumes
  • Secret: Stores sensitive information securely, requires additional security measures
  • Ingress: As an L7 load balancer, provides host/path-based routing and TLS termination
  • Ingress Controller: Component that actually implements Ingress resources

In Part 10, we'll explore Helm, the Kubernetes package manager, and CI/CD Pipeline construction. We'll cover application deployment using Helm Charts and GitOps-based automated deployment strategies.