Add dev/stg/prod environments with per-env values, configmaps and secrets

- Move env-specific config into environments/<env>/{values,configmap,secret}.yaml
  for both nginx-chart and mysql-chart (dev, stg, prod)
- Make templates env-agnostic: resource names come from values
  (configMap.name, secret.name)
- nginx deployment mounts env ConfigMap and loads env Secret via envFrom
- mysql statefulset reads DB config from ConfigMap, passwords from Secret
- Remove mysql-chart/templates/secret.yaml (secrets are now per-env manifests)
- Add README with layout and deploy commands
This commit is contained in:
mdsamsulhaque
2026-11-04 06:43:05 +06:00
parent dfca9ba4ae
commit bbf501269d
24 changed files with 491 additions and 25 deletions
+105
View File
@@ -0,0 +1,105 @@
# nginx_CD_chart
Helm charts for the demo stack, with three isolated environments: **dev**, **stg**, **prod**.
```
nginx_CD_chart/
├── nginx-chart/
│ ├── Chart.yaml
│ ├── values.yaml # base defaults (shared)
│ ├── templates/
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── _helpers.tpl
│ └── environments/
│ ├── dev/
│ │ ├── values.yaml # dev overrides
│ │ ├── configmap.yaml # dev ConfigMap (plain manifest)
│ │ └── secret.yaml # dev Secret(s) (plain manifest)
│ ├── stg/
│ │ ├── values.yaml
│ │ ├── configmap.yaml
│ │ └── secret.yaml
│ └── prod/
│ ├── values.yaml
│ ├── configmap.yaml
│ └── secret.yaml
└── mysql-chart/
├── Chart.yaml
├── values.yaml # base defaults (shared)
├── templates/
│ ├── statefulset.yaml
│ └── service.yaml
└── environments/
├── dev/
│ ├── values.yaml
│ ├── configmap.yaml
│ └── secret.yaml
├── stg/
│ ├── values.yaml
│ ├── configmap.yaml
│ └── secret.yaml
└── prod/
├── values.yaml
├── configmap.yaml
└── secret.yaml
```
## How it works
For each environment there is a folder under `environments/<env>/` containing three files:
| File | Purpose | How it is applied |
|------|---------|-------------------|
| `values.yaml` | Helm overrides for that env (namespace, replica count, image tag, storage size, resource names) | `helm -f` |
| `configmap.yaml` | Non-secret config (`ConfigMap`), e.g. nginx `default.conf` / mysql settings | `kubectl apply` |
| `secret.yaml` | Credentials (`Secret`), e.g. registry pull secret / mysql passwords | `kubectl apply` |
The chart's `templates/` stay generic and read the resource **names** from `values.yaml`
(`configMap.name`, `secret.name`), so the same templates render for every environment.
## Deploy an environment
Replace `<env>` with `dev`, `stg` or `prod`.
```bash
# 1. Namespace + ConfigMap + Secret (plain manifests for this env)
kubectl create namespace <env> --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f nginx-chart/environments/<env>/configmap.yaml
kubectl apply -f nginx-chart/environments/<env>/secret.yaml
kubectl apply -f mysql-chart/environments/<env>/configmap.yaml
kubectl apply -f mysql-chart/environments/<env>/secret.yaml
# 2. The Helm releases (values merged on top of each chart's base values.yaml)
helm upgrade --install mysql ./mysql-chart -n <env> --create-namespace \
-f mysql-chart/environments/<env>/values.yaml
helm upgrade --install nginx ./nginx-chart -n <env> --create-namespace \
-f nginx-chart/environments/<env>/values.yaml
```
Example — deploy **stg**:
```bash
kubectl apply -f nginx-chart/environments/stg/configmap.yaml
kubectl apply -f nginx-chart/environments/stg/secret.yaml
helm upgrade --install nginx ./nginx-chart -n stg --create-namespace \
-f nginx-chart/environments/stg/values.yaml
```
## Preview the rendered manifests
```bash
helm template nginx ./nginx-chart -f nginx-chart/environments/stg/values.yaml
helm template mysql ./mysql-chart -f mysql-chart/environments/prod/values.yaml
```
## Notes / things to change before real use
- `secret.yaml` files contain **plaintext** example credentials. For real use, commit
encrypted secrets (e.g. SealedSecrets / SOPS) or create them from CI secrets — never
store production passwords in git.
- `harbor-secret` (docker-registry pull secret) is provided per env with placeholder
credentials; either replace the values or create it in-cluster with
`kubectl create secret docker-registry ...` (command is in the file header).
- The base `values.yaml` defaults to the `dev` namespace, so always pass `-f environments/<env>/values.yaml`.
@@ -0,0 +1,11 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config-dev
namespace: dev
labels:
app: mysql
env: dev
data:
MYSQL_DATABASE: demo_dev
MYSQL_USER: demo
+12
View File
@@ -0,0 +1,12 @@
# MySQL credentials for the DEV environment (referenced by the StatefulSet).
# Replace with real values; for production use a sealed/encrypted secret instead
# of committing plaintext.
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret-dev
namespace: dev
type: Opaque
stringData:
MYSQL_PASSWORD: demo123
MYSQL_ROOT_PASSWORD: root123
+14
View File
@@ -0,0 +1,14 @@
# DEV environment overrides for mysql-chart
namespace: dev
image:
tag: "8.4"
storage:
size: 1Gi
configMap:
name: mysql-config-dev
secret:
name: mysql-secret-dev
@@ -0,0 +1,11 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config-prod
namespace: prod
labels:
app: mysql
env: prod
data:
MYSQL_DATABASE: demo_prod
MYSQL_USER: demo
+12
View File
@@ -0,0 +1,12 @@
# MySQL credentials for the PROD environment (referenced by the StatefulSet).
# Replace with real values; for production use a sealed/encrypted secret instead
# of committing plaintext.
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret-prod
namespace: prod
type: Opaque
stringData:
MYSQL_PASSWORD: prod123
MYSQL_ROOT_PASSWORD: prodroot123
+14
View File
@@ -0,0 +1,14 @@
# PROD environment overrides for mysql-chart
namespace: prod
image:
tag: "8.4"
storage:
size: 5Gi
configMap:
name: mysql-config-prod
secret:
name: mysql-secret-prod
@@ -0,0 +1,11 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config-stg
namespace: stg
labels:
app: mysql
env: stg
data:
MYSQL_DATABASE: demo_stg
MYSQL_USER: demo
+12
View File
@@ -0,0 +1,12 @@
# MySQL credentials for the STG (staging) environment (referenced by the StatefulSet).
# Replace with real values; for production use a sealed/encrypted secret instead
# of committing plaintext.
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret-stg
namespace: stg
type: Opaque
stringData:
MYSQL_PASSWORD: stg123
MYSQL_ROOT_PASSWORD: stgroot123
+14
View File
@@ -0,0 +1,14 @@
# STG (staging) environment overrides for mysql-chart
namespace: stg
image:
tag: "8.4"
storage:
size: 2Gi
configMap:
name: mysql-config-stg
secret:
name: mysql-secret-stg
-9
View File
@@ -1,9 +0,0 @@
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
namespace: {{ .Values.namespace }}
type: Opaque
stringData:
MYSQL_ROOT_PASSWORD: {{ .Values.mysql.rootPassword }}
MYSQL_PASSWORD: {{ .Values.mysql.password }}
+11 -6
View File
@@ -15,15 +15,20 @@ spec:
containers: containers:
- name: mysql - name: mysql
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
envFrom:
- configMapRef:
name: {{ .Values.configMap.name }}
env: env:
- name: MYSQL_DATABASE
value: {{ .Values.mysql.database }}
- name: MYSQL_USER
value: {{ .Values.mysql.username }}
- name: MYSQL_PASSWORD - name: MYSQL_PASSWORD
valueFrom: {secretKeyRef: {name: mysql-secret,key: MYSQL_PASSWORD}} valueFrom:
secretKeyRef:
name: {{ .Values.secret.name }}
key: MYSQL_PASSWORD
- name: MYSQL_ROOT_PASSWORD - name: MYSQL_ROOT_PASSWORD
valueFrom: {secretKeyRef: {name: mysql-secret,key: MYSQL_ROOT_PASSWORD}} valueFrom:
secretKeyRef:
name: {{ .Values.secret.name }}
key: MYSQL_ROOT_PASSWORD
volumeMounts: volumeMounts:
- name: mysql-data - name: mysql-data
mountPath: /var/lib/mysql mountPath: /var/lib/mysql
+25 -6
View File
@@ -1,12 +1,31 @@
namespace: prod # Base (default) values for mysql-chart.
#
# Environment-specific overrides live in environments/<env>/values.yaml and are
# merged on top of this file at deploy time:
#
# helm upgrade --install mysql ./mysql-chart -n <namespace> --create-namespace \
# -f mysql-chart/environments/<env>/values.yaml
#
# Available environments: dev, stg, prod
#
# NOTE: database credentials are NOT stored here. They live in the environment
# secret (environments/<env>/secret.yaml) and the non-secret config lives in
# the environment ConfigMap (environments/<env>/configmap.yaml).
namespace: dev
image: image:
repository: mysql repository: mysql
tag: "8.4" tag: "8.4"
mysql:
database: demo
username: demo
password: demo123
rootPassword: root123
storage: storage:
storageClass: nfs-client storageClass: nfs-client
size: 1Gi size: 1Gi
# Names of the ConfigMap / Secret created by environments/<env>/configmap.yaml
# and environments/<env>/secret.yaml
configMap:
name: mysql-config-dev
secret:
name: mysql-secret-dev
@@ -0,0 +1,19 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config-dev
namespace: dev
labels:
app: nginx
env: dev
data:
default.conf: |
server {
listen 80;
server_name nginx.dev.local;
location / {
return 200 "nginx DEV environment\n";
add_header Content-Type text/plain;
}
}
+31
View File
@@ -0,0 +1,31 @@
# ---------------------------------------------------------------------------
# Registry pull secret used by the nginx Deployment (imagePullSecrets).
# Replace the credentials with the real registry values, or regenerate with:
# kubectl create secret docker-registry harbor-secret \
# --docker-server=imageregistry.gen-itech.com \
# --docker-username=demo --docker-password=demo123 \
# -n dev --dry-run=client -o yaml
# ---------------------------------------------------------------------------
apiVersion: v1
kind: Secret
metadata:
name: harbor-secret
namespace: dev
type: kubernetes.io/dockerconfigjson
stringData:
.dockerconfigjson: |
{"auths":{"imageregistry.gen-itech.com":{"username":"demo","password":"demo123","auth":"ZGVtbzpkZW1vMTIz"}}}
---
# ---------------------------------------------------------------------------
# Application secret for the nginx container (loaded via envFrom).
# ---------------------------------------------------------------------------
apiVersion: v1
kind: Secret
metadata:
name: nginx-secret-dev
namespace: dev
type: Opaque
stringData:
APP_ENV: dev
APP_SECRET: change-me-dev
+16
View File
@@ -0,0 +1,16 @@
# DEV environment overrides for nginx-chart
namespace: dev
replicaCount: 1
image:
tag: "39"
ingress:
host: nginx.dev.local
configMap:
name: nginx-config-dev
secret:
name: nginx-secret-dev
@@ -0,0 +1,19 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config-prod
namespace: prod
labels:
app: nginx
env: prod
data:
default.conf: |
server {
listen 80;
server_name nginx.prod.local;
location / {
return 200 "nginx PROD environment\n";
add_header Content-Type text/plain;
}
}
+31
View File
@@ -0,0 +1,31 @@
# ---------------------------------------------------------------------------
# Registry pull secret used by the nginx Deployment (imagePullSecrets).
# Replace the credentials with the real registry values, or regenerate with:
# kubectl create secret docker-registry harbor-secret \
# --docker-server=imageregistry.gen-itech.com \
# --docker-username=demo --docker-password=demo123 \
# -n prod --dry-run=client -o yaml
# ---------------------------------------------------------------------------
apiVersion: v1
kind: Secret
metadata:
name: harbor-secret
namespace: prod
type: kubernetes.io/dockerconfigjson
stringData:
.dockerconfigjson: |
{"auths":{"imageregistry.gen-itech.com":{"username":"demo","password":"demo123","auth":"ZGVtbzpkZW1vMTIz"}}}
---
# ---------------------------------------------------------------------------
# Application secret for the nginx container (loaded via envFrom).
# ---------------------------------------------------------------------------
apiVersion: v1
kind: Secret
metadata:
name: nginx-secret-prod
namespace: prod
type: Opaque
stringData:
APP_ENV: prod
APP_SECRET: change-me-prod
+16
View File
@@ -0,0 +1,16 @@
# PROD environment overrides for nginx-chart
namespace: prod
replicaCount: 3
image:
tag: "39"
ingress:
host: nginx.prod.local
configMap:
name: nginx-config-prod
secret:
name: nginx-secret-prod
@@ -0,0 +1,19 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config-stg
namespace: stg
labels:
app: nginx
env: stg
data:
default.conf: |
server {
listen 80;
server_name nginx.stg.local;
location / {
return 200 "nginx STG environment\n";
add_header Content-Type text/plain;
}
}
+31
View File
@@ -0,0 +1,31 @@
# ---------------------------------------------------------------------------
# Registry pull secret used by the nginx Deployment (imagePullSecrets).
# Replace the credentials with the real registry values, or regenerate with:
# kubectl create secret docker-registry harbor-secret \
# --docker-server=imageregistry.gen-itech.com \
# --docker-username=demo --docker-password=demo123 \
# -n stg --dry-run=client -o yaml
# ---------------------------------------------------------------------------
apiVersion: v1
kind: Secret
metadata:
name: harbor-secret
namespace: stg
type: kubernetes.io/dockerconfigjson
stringData:
.dockerconfigjson: |
{"auths":{"imageregistry.gen-itech.com":{"username":"demo","password":"demo123","auth":"ZGVtbzpkZW1vMTIz"}}}
---
# ---------------------------------------------------------------------------
# Application secret for the nginx container (loaded via envFrom).
# ---------------------------------------------------------------------------
apiVersion: v1
kind: Secret
metadata:
name: nginx-secret-stg
namespace: stg
type: Opaque
stringData:
APP_ENV: stg
APP_SECRET: change-me-stg
+16
View File
@@ -0,0 +1,16 @@
# STG (staging) environment overrides for nginx-chart
namespace: stg
replicaCount: 2
image:
tag: "39"
ingress:
host: nginx.stg.local
configMap:
name: nginx-config-stg
secret:
name: nginx-secret-stg
+13 -1
View File
@@ -18,9 +18,21 @@ spec:
app: nginx app: nginx
spec: spec:
imagePullSecrets: imagePullSecrets:
- name: harbor-secret - name: {{ .Values.imagePullSecrets }}
containers: containers:
- name: nginx - name: nginx
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports: ports:
- containerPort: 80 - containerPort: 80
envFrom:
- secretRef:
name: {{ .Values.secret.name }}
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
volumes:
- name: nginx-config
configMap:
name: {{ .Values.configMap.name }}
+26 -1
View File
@@ -1,13 +1,38 @@
# Base (default) values for nginx-chart.
#
# Environment-specific overrides live in environments/<env>/values.yaml and are
# merged on top of this file at deploy time:
#
# helm upgrade --install nginx ./nginx-chart -n <namespace> --create-namespace \
# -f nginx-chart/environments/<env>/values.yaml
#
# Available environments: dev, stg, prod
replicaCount: 1 replicaCount: 1
namespace: "dev"
namespace: dev
image: image:
repository: imageregistry.gen-itech.com/demo/applicationtest repository: imageregistry.gen-itech.com/demo/applicationtest
tag: "39" tag: "39"
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
# Name of the docker-registry pull secret (created by environments/<env>/secret.yaml)
imagePullSecrets: harbor-secret
service: service:
type: ClusterIP type: ClusterIP
port: 80 port: 80
ingress: ingress:
enabled: true enabled: true
className: traefik className: traefik
host: nginx.demo.local host: nginx.demo.local
# Names of the ConfigMap / Secret created by environments/<env>/configmap.yaml
# and environments/<env>/secret.yaml
configMap:
name: nginx-config-dev
secret:
name: nginx-secret-dev