Docker & Kubernetes Complete Guide Part 9: ConfigMap, Secret, and Ingress
Configuration Management and External Traffic Routing
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.