Deploy a minimal k8s environment to your local machine.
Tags: tutorial, kubernetes, webdev
Published: 2023-08-06 14:08:21

Web development are becoming more complex nowadays, especially when it's comes to deployment.

In this article, I'll walk through the process of creating a basic Kubernetes (k8s) environment with SSL on local machine, perfect for development and testing purposes. Our setup will include an ingress controller, an nginx service, and an app service.

## Prerequisites
Before we begin, make sure you have the following:

1. Minikube installed on your local machine.
2. The **ingress addon** enabled in Minikube. You can enable it using the command: `minikube addons enable ingress`.
3. make sure you are in the **minikube environment**, you can use `eval $(minikube docker-env)` to enter and `eval $(minikube docker-env -u)` to leave

## Kubernetes Configuration
### tls secret
We'll need to create a Kubernetes secret to handle our SSL certificates. You can do this using a YAML file or directly through the command line:

- Option 1: Using a YAML File (tls-secret.yml)
```yml
apiVersion: v1
kind: Secret
metadata:
  name: my-tls-secret
  namespace: default
data:
  tls.crt: BASE64_ENCODED_CERT
  tls.key: BASE64_ENCODED_CERT_KEY
type: kubernetes.io/tls
```
Replace **BASE64_ENCODED_CERT** and **BASE64_ENCODED_CERT_KEY** with your actual SSL certificate and private key encoded in base64 format.

- Option 2: Using shell
```sh
kcc secret generic my-tls-secret \
--from-file=tls.crt=/path/to/cert \
--from-file=tls.key=/path/to/cery-key
```
Replace **/path/to/cert** and **/path/to/cert-key** with the actual file paths of your SSL certificate and private key.

### ingress
```yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: local-ingress
  annotations:
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
  tls:
  - hosts:
    - my.local.example
    secretName: my-tls-secret
  rules:
  - host: "my.local.example"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-service
            port:
              number: 80

```
### nginx
```yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  nginx.conf: |
    user nginx;
    worker_processes  3;
    error_log  /var/log/nginx/error.log;
    events {
      worker_connections  10240;
    }
    http {
      log_format main
              'remote_addr:$remote_addr\t'
              'time_local:$time_local\t'
              'method:$request_method\t'
              'uri:$request_uri\t'
              'host:$host\t'
              'status:$status\t'
              'bytes_sent:$body_bytes_sent\t'
              'referer:$http_referer\t'
              'useragent:$http_user_agent\t'
              'forwardedfor:$http_x_forwarded_for\t'
              'request_time:$request_time';
      access_log	/var/log/nginx/access.log main;
      include /etc/nginx/virtualhost/virtualhost.conf;
    }
  virtualhost.conf: |
    upstream nextjs {
      server app-service;
    }
    server {
      listen 80;
      server_name my.local.example;

      server_tokens off;

      gzip on;
      gzip_proxied any;
      gzip_comp_level 4;
      gzip_types text/css application/javascript image/svg+xml;

      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection 'upgrade';
      proxy_set_header Host $host;
      proxy_cache_bypass $http_upgrade;

      location / {
        proxy_pass http://nextjs$request_uri;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
      }
    }

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          volumeMounts:
            - name: nginx-conf
              mountPath: /etc/nginx
          image: nginx:1.25
          ports:
            - containerPort: 443
      volumes:
        - name: nginx-conf
          configMap:
            name: nginx-config
            items:
              - key: nginx.conf
                path: nginx.conf
              - key: virtualhost.conf
                path: virtualhost/virtualhost.conf
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

```

### application
```yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: YOUR_IMAGE_NAME
        imagePullPolicy: Never
        ports:
        - containerPort: 3000

---
apiVersion: v1
kind: Service
metadata:
  name: app-service
spec:
  type: ClusterIP
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000

```
Replce **YOUR_IMAGE_NAME** to your local docker builded image
## Activation
Once the Kubernetes config is ready, follow these steps to activate your k8s environment:

1. Apply the resources in the following order:
  1. Ingress controller: `kubectl apply -f ingress.yaml`
  2. App deployment: `kubectl apply -f app-deployment.yaml`
  3. Nginx deployment: `kubectl apply -f nginx-deployment.yaml`
2. Enable minikube tunnel to expose your ingress controller to the local machine: `minikube tunnel`
3. You're all set! Try enter **my.local.example** in your browser url then you will get your application