Skip to content

Instantly share code, notes, and snippets.

@xyb
Created April 6, 2023 05:59
Show Gist options
  • Save xyb/9cbfac02b6a58a9ff6a22119bc6a9c9a to your computer and use it in GitHub Desktop.
Save xyb/9cbfac02b6a58a9ff6a22119bc6a9c9a to your computer and use it in GitHub Desktop.
Deploying a Frontend Application with Dynamic Environment Variables Using Docker, Kubernetes, and Nginx

Deploying a Frontend Application with Dynamic Environment Variables Using Docker, Kubernetes, and Nginx

This guide demonstrates how to deploy a frontend application using Docker and Kubernetes (k8s) with dynamic environment variables that can change across different namespaces. We will use Nginx as a web server to serve the frontend static files.

1. Create a Dockerfile

Create a Dockerfile for your frontend project:

# Use a Node.js base image
FROM node:14 AS build

# Set working directory
WORKDIR /app

# Copy package.json and package-lock.json
COPY package*.json ./

# Install dependencies
RUN npm ci

# Copy the rest of the project files
COPY . .

# Build the project
RUN npm run build

# Use an Nginx base image for serving static files
FROM nginx:1.21

# Copy the build output from the previous stage
COPY --from=build /app/build /usr/share/nginx/html

# Copy a custom Nginx configuration file
COPY nginx.conf /etc/nginx/conf.d/default.conf

2. Create the Nginx configuration file

Create an nginx.conf file in your project root:

server {
    listen       80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

    location /app-config.js {
        alias /usr/share/nginx/html/app-config.js;
        add_header Content-Type application/javascript;
        expires -1; # Disable caching
        add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"; # Ensure no caching in proxies and clients
    }
}

3. Modify the frontend code

In your index.html file, add the following line before the main JavaScript bundle:

<head>
  ...
  <script src="/app-config.js"></script>
  ...
</head>

Create a window.env object in your frontend code to access environment variables:

const API_URL = window.env.REACT_APP_API_URL || 'http://localhost:3000/api';

4. Build and push the Docker image

Build and push the Docker image to a container registry:

docker build -t your-registry/your-frontend-image:latest .
docker push your-registry/your-frontend-image:latest

5. Create Kubernetes ConfigMap and Secret

Create a ConfigMap and a Secret (if needed) for each namespace in Kubernetes:

apiVersion: v1
kind: ConfigMap
metadata:
  name: frontend-config
  namespace: your-namespace
data:
  REACT_APP_API_URL: 'http://your-backend-api-url'

---
apiVersion: v1
kind: Secret
metadata:
  name: frontend-secrets
  namespace: your-namespace
type: Opaque
stringData:
  REACT_APP_SECRET: 'your-secret-value'

Replace the your-namespace, http://your-backend-api-url, and your-secret-value placeholders with the appropriate values. Create separate ConfigMaps and Secrets for different namespaces as needed.

6. Create a Kubernetes Deployment

Create a Kubernetes deployment for your frontend project:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  namespace: your-namespace
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      initContainers:
      - name: app-config-init
        image: busybox:1.33
        command: ['sh', '-c', 'echo "window.env = { REACT_APP_API_URL: \"$(REACT_APP_API_URL)\", REACT_APP_SECRET: \"$(REACT_APP_SECRET)\" };" > /usr/share/nginx/html/app-config.js']
        envFrom:
        - configMapRef:
            name: frontend-config
        - secretRef:
            name: frontend-secrets
        volumeMounts:
        - name: app-config
          mountPath: /usr/share/nginx/html
      containers:
      - name: frontend
        image: your-registry/your-frontend-image:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: app-config
          mountPath: /usr/share/nginx/html
      volumes:
      - name: app-config
        emptyDir: {}

7. Create a Kubernetes Service

Create a Kubernetes Service to expose your frontend deployment:

apiVersion: v1
kind: Service
metadata:
  name: frontend
  namespace: your-namespace
spec:
  selector:
    app: frontend
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: LoadBalancer

8. Apply the Kubernetes manifests

Apply the Kubernetes manifests:

kubectl apply -f your-manifest-file.yaml

With this setup, the app-config.js file is generated dynamically by the initContainers section during the deployment, and the Nginx server serves the file without caching. The frontend application loads the environment variables from this file at runtime, allowing you to modify the behavior of your application, such as changing the backend API service it accesses, across different namespaces.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment