Skip to content

Commit 8be94b3

Browse files
committed
Add ConfigMap Examples
1 parent 4790e2d commit 8be94b3

12 files changed

+343
-0
lines changed

13-ConfigMaps/README.md

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
## ConfigMap
2+
3+
> [Updating Configuration via a ConfigMap](https://kubernetes.io/docs/tutorials/configuration/updating-configuration-via-a-configmap/)
4+
5+
> [!IMPORTANT]
6+
> Although the value of the **key inside the ConfigMap has changed, the environment variable in the Pod still shows the earlier value**. This is because environment variables for a process running inside a Pod are **not** updated when the source data changes; if you wanted to force an update, you would need to have Kubernetes replace your existing Pods. The new Pods would then run with the updated information.
7+
8+
**Solutions**:
9+
10+
1. Use `kubectl rollout restart`
11+
12+
This will cause the pods to reload with the new environment variables and configuration.
13+
14+
Files:
15+
- [Deployment](./deployment.yaml)
16+
- [ConfigMap](./configmap.yaml)
17+
- [NodePort Service](./service.yaml)
18+
```bash
19+
kubectl rollout restart deployment <deployment-name>
20+
```
21+
![Image](./images/img-1.webp)
22+
23+
24+
2. Use `envFrom` to Load ConfigMap as Volume
25+
26+
If you want to have more flexibility and allow the pod to pick up updates from the `ConfigMap` without a restart, you can mount the `ConfigMap` as a volume.
27+
28+
Files:
29+
- [Deployment](./deployment-2.yaml)
30+
- [ConfigMap](./configmap-2.yaml)
31+
- [NodePort Service](./service-2.yaml)
32+
33+
![Image](./images/img-2.webp)

13-ConfigMaps/app/Dockerfile

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Stage 1: Build
2+
FROM golang:alpine AS builder
3+
4+
ENV CGO_ENABLED=0 GOOS=linux
5+
6+
WORKDIR /app
7+
COPY . .
8+
9+
RUN go build -o server .
10+
11+
# Stage 2: Run
12+
FROM scratch
13+
14+
WORKDIR /root/
15+
COPY --from=builder /app/server .
16+
EXPOSE 8080
17+
18+
CMD ["./server"]

13-ConfigMaps/app/go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/nmdra/K8s-Learn/13-ConfigMaps/app
2+
3+
go 1.23.4

13-ConfigMaps/app/main.go

+189
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package main
2+
3+
import (
4+
"html/template"
5+
"log"
6+
"net"
7+
"net/http"
8+
"os"
9+
"strings"
10+
)
11+
12+
type PageData struct {
13+
EnvVars map[string]string
14+
ConfigVars map[string]string
15+
Hostname string
16+
IPAddress string
17+
}
18+
19+
func main() {
20+
log.Println("Starting the server on port 8080")
21+
http.HandleFunc("/", handler)
22+
log.Fatal(http.ListenAndServe(":8080", nil)) // Log fatal errors if server fails
23+
}
24+
25+
func handler(w http.ResponseWriter, r *http.Request) {
26+
log.Printf("Received request from %s %s\n", r.RemoteAddr, r.Method)
27+
28+
envVars := os.Environ()
29+
envMap := make(map[string]string)
30+
for _, env := range envVars {
31+
parts := strings.SplitN(env, "=", 2)
32+
if len(parts) == 2 {
33+
envMap[parts[0]] = parts[1]
34+
}
35+
}
36+
log.Println("Fetched environment variables")
37+
38+
configVars := readConfigFiles("/etc/config")
39+
log.Printf("Fetched %d config variables\n", len(configVars))
40+
41+
host, ip := getHostIP()
42+
log.Printf("Fetched hostname: %s, IP address: %s\n", host, ip)
43+
44+
data := PageData{
45+
EnvVars: envMap,
46+
ConfigVars: configVars,
47+
Hostname: host,
48+
IPAddress: ip,
49+
}
50+
51+
renderTemplate(w, data)
52+
}
53+
54+
func renderTemplate(w http.ResponseWriter, data PageData) {
55+
tmpl := `
56+
<!DOCTYPE html>
57+
<html lang="en">
58+
<head>
59+
<meta charset="UTF-8">
60+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
61+
<title>Environment and Config Variables</title>
62+
<style>
63+
body {
64+
font-family: Arial, sans-serif;
65+
margin: 2em;
66+
background-color: #f4f4f9;
67+
}
68+
h1 {
69+
color: #333;
70+
}
71+
.container {
72+
background: #fff;
73+
padding: 20px;
74+
border-radius: 8px;
75+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
76+
}
77+
.value {
78+
color: #007BFF;
79+
font-weight: bold;
80+
}
81+
.env-table {
82+
width: 100%;
83+
border-collapse: collapse;
84+
margin-top: 1em;
85+
}
86+
.env-table th, .env-table td {
87+
border: 1px solid #ddd;
88+
padding: 8px;
89+
}
90+
.env-table th {
91+
background-color: #f4f4f9;
92+
text-align: left;
93+
}
94+
</style>
95+
</head>
96+
<body>
97+
<div class="container">
98+
<h1>Environment and Config Variable Viewer</h1>
99+
<p><strong>Hostname:</strong> <span class="value">{{.Hostname}}</span></p>
100+
<p><strong>IP Address:</strong> <span class="value">{{.IPAddress}}</span></p>
101+
102+
<h2>Environment Variables</h2>
103+
<table class="env-table">
104+
<tr>
105+
<th>Key</th>
106+
<th>Value</th>
107+
</tr>
108+
{{range $key, $value := .EnvVars}}
109+
<tr>
110+
<td>{{$key}}</td>
111+
<td>{{$value}}</td>
112+
</tr>
113+
{{end}}
114+
</table>
115+
116+
<h2>Config Variables (from ConfigMap Volume)</h2>
117+
<table class="env-table">
118+
<tr>
119+
<th>Key</th>
120+
<th>Value</th>
121+
</tr>
122+
{{range $key, $value := .ConfigVars}}
123+
<tr>
124+
<td>{{$key}}</td>
125+
<td>{{$value}}</td>
126+
</tr>
127+
{{end}}
128+
</table>
129+
</div>
130+
</body>
131+
</html>
132+
`
133+
t := template.Must(template.New("webpage").Parse(tmpl))
134+
err := t.Execute(w, data)
135+
if err != nil {
136+
log.Printf("Error rendering template: %v\n", err)
137+
http.Error(w, "Failed to render template", http.StatusInternalServerError)
138+
}
139+
}
140+
141+
func readConfigFiles(configDir string) map[string]string {
142+
configVars := make(map[string]string)
143+
144+
log.Printf("Reading config files from directory: %s\n", configDir)
145+
146+
files, err := os.ReadDir(configDir)
147+
if err != nil {
148+
log.Printf("Error reading config directory: %v\n", err)
149+
return configVars // Return empty map if error occurs
150+
}
151+
152+
for _, file := range files {
153+
if !file.IsDir() {
154+
content, err := os.ReadFile(configDir + "/" + file.Name())
155+
if err == nil {
156+
configVars[file.Name()] = string(content)
157+
log.Printf("Loaded config file: %s\n", file.Name())
158+
} else {
159+
log.Printf("Error reading config file %s: %v\n", file.Name(), err)
160+
}
161+
}
162+
}
163+
return configVars
164+
}
165+
166+
func getHostIP() (string, string) {
167+
host, err := os.Hostname()
168+
if err != nil {
169+
log.Printf("Error getting hostname: %v\n", err)
170+
host = "Unknown"
171+
}
172+
173+
addrs, err := net.InterfaceAddrs()
174+
if err != nil {
175+
log.Printf("Error getting network interface addresses: %v\n", err)
176+
return host, "Unknown"
177+
}
178+
179+
for _, addr := range addrs {
180+
if ipNet, ok := addr.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
181+
if ipNet.IP.To4() != nil {
182+
log.Printf("Found IP address: %s\n", ipNet.IP.String())
183+
return host, ipNet.IP.String()
184+
}
185+
}
186+
}
187+
188+
return host, "Unknown"
189+
}

13-ConfigMaps/configmap-2.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: new-config
5+
namespace: default
6+
data:
7+
NEW_MESSAGE: "Hello from Volume!"
8+
NEW_ENV: "Volume"

13-ConfigMaps/configmap.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: app-config
5+
namespace: default
6+
data:
7+
APP_MESSAGE: "Hello from ConfigMap!"
8+
AUTHOR: "NIMENDRA"
9+
NEW_ENV: "K3s"

13-ConfigMaps/deployment-2.yaml

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: env-reader-2
5+
namespace: default
6+
spec:
7+
replicas: 3
8+
selector:
9+
matchLabels:
10+
app: env-reader-2
11+
template:
12+
metadata:
13+
labels:
14+
app: env-reader-2
15+
spec:
16+
containers:
17+
- name: env-reader-2
18+
image: ghcr.io/nmdra/k8s-learn/env-reader:2.0
19+
imagePullPolicy: IfNotPresent
20+
ports:
21+
- containerPort: 8080
22+
envFrom:
23+
- configMapRef:
24+
name: app-config
25+
volumeMounts:
26+
- name: config-volume
27+
mountPath: /etc/config
28+
readOnly: true
29+
volumes:
30+
- name: config-volume
31+
configMap:
32+
name: new-config

13-ConfigMaps/deployment.yaml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: env-reader-deployment
5+
namespace: default
6+
spec:
7+
replicas: 2
8+
selector:
9+
matchLabels:
10+
app: env-reader
11+
template:
12+
metadata:
13+
labels:
14+
app: env-reader
15+
spec:
16+
containers:
17+
- name: env-reader
18+
image: ghcr.io/nmdra/k8s-learn/env-reader:2.0
19+
ports:
20+
- containerPort: 8080
21+
envFrom:
22+
- configMapRef:
23+
name: app-config

13-ConfigMaps/images/img-1.webp

65 KB
Binary file not shown.

13-ConfigMaps/images/img-2.webp

92.9 KB
Binary file not shown.

13-ConfigMaps/service-2.yaml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: env-reader-service-2
5+
namespace: default
6+
spec:
7+
selector:
8+
app: env-reader-2
9+
ports:
10+
- protocol: TCP
11+
port: 80
12+
targetPort: 8080
13+
nodePort: 30088
14+
type: NodePort

13-ConfigMaps/service.yaml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: env-reader-service
5+
namespace: default
6+
spec:
7+
selector:
8+
app: env-reader
9+
ports:
10+
- protocol: TCP
11+
port: 80
12+
targetPort: 8080
13+
nodePort: 30080
14+
type: NodePort

0 commit comments

Comments
 (0)