Deploy GKE with wordpress with HTTPS

Deploy GKE with wordpress with HTTPS

like a Pro

If you have used GKE then you would most certainly know that it's very restrictive

also for the demonstration purposes you should be using the following to ensure you can stay with us till the end. so let's begin

A bit of advice make sure you are patient enough to try this

Prequisites

  1. use GKE with autopilot or if you have one make sure you have standard-rwx storage class from filestore.csi.storage.gke.io

    Refer: https://cloud.google.com/kubernetes-engine/docs/how-to/persistent-volumes/filestore-csi-driver

    Make sure you have this. for this follow the enable api button on this docs

  2. we are trying to scale the wordpress so that it can handle a lot of users

  3. for ingress will be going to use the nginx-ingress-controller

  4. I will be using gcloud cli

# some good prequisties before creating anything to define
# 1. Project_id
# 2. Region where you want to deploy

Script creation

first we want to create the variables for us to use them

gcloud config set compute/region asia-south1
export PROJECT_ID=<PROJECT_ID>

# to enable the following GCloud services
gcloud services enable container.googleapis.com sqladmin.googleapis.com

# Resource we are going to create
CLUSTER_NAME=wordpress-k8s
INSTANCE_NAME=mysql-wordpress-instance
SA_NAME=cloudsql-proxy
# creation of gke
gcloud container clusters create-auto $CLUSTER_NAME

gcloud container clusters get-credentials $CLUSTER_NAME

# now you can run this to get the cluster status
kubectl cluster-info
# for creating the Cloud managed GKE service
gcloud sql instances create $INSTANCE_NAME

export INSTANCE_CONNECTION_NAME=$(gcloud sql instances describe $INSTANCE_NAME \
    --format='value(connectionName)')

gcloud sql databases create wordpress --instance $INSTANCE_NAME

CLOUD_SQL_PASSWORD=$(openssl rand -base64 18)

gcloud sql users create wordpress --host=% --instance $INSTANCE_NAME --password $CLOUD_SQL_PASSWORD
# creating a IAM service account and adding policy for the upcomming gcloud db proxy
gcloud iam service-accounts create $SA_NAME --display-name $SA_NAME

SA_EMAIL=$(gcloud iam service-accounts list \
    --filter=displayName:$SA_NAME \
    --format='value(email)')

gcloud projects add-iam-policy-binding $PROJECT_ID \
    --role roles/cloudsql.client \
    --member serviceAccount:$SA_EMAIL

gcloud iam service-accounts keys create key.json \
    --iam-account $SA_EMAIL
# at this moment we are just need to create 2 secrets for them to be used by the deployment
kubectl create secret generic cloudsql-db-credentials \
    --from-literal=username=wordpress \
    --from-literal=password=$CLOUD_SQL_PASSWORD

kubectl create secret generic cloudsql-instance-credentials \
    --from-file key.json
# save it as wordpress_cloudsql.yaml.template
apiVersion: v1
kind: Service
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  ports:
    - port: 80
  selector:
    app: wordpress
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wp-pv-claim
  labels:
    app: wordpress
spec:
  storageClassName: "standard-rwx"
  accessModes:
    - ReadWriteMany # so that pods which will get deployed on other nodes, still can mount this PVC
  resources:
    requests:
      storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  replicas: 3
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
        - image: wordpress
          name: wordpress
          env:
          - name: WORDPRESS_DB_HOST
            value: 127.0.0.1:3306
          - name: WORDPRESS_DB_USER
            valueFrom:
              secretKeyRef:
                name: cloudsql-db-credentials
                key: username
          - name: WORDPRESS_DB_PASSWORD
            valueFrom:
              secretKeyRef:
                name: cloudsql-db-credentials
                key: password
          ports:
            - containerPort: 80
              name: wordpress
          volumeMounts:
            - name: wordpress-persistent-storage
              mountPath: /var/www/html
        # Change ${INSTANCE_CONNECTION_NAME} here to include your GCP
        # project, the region of your Cloud SQL instance and the name
        # of your Cloud SQL instance. The format is
        # $PROJECT:$REGION:$INSTANCE
        - name: cloudsql-proxy
          image: gcr.io/cloudsql-docker/gce-proxy:1.33.2


          command: ["/cloud_sql_proxy",
                    "-instances=${INSTANCE_CONNECTION_NAME}=tcp:3306",
                    "-credential_file=/secrets/cloudsql/key.json"]
          securityContext:
            runAsUser: 2  # non-root user
            allowPrivilegeEscalation: false
          volumeMounts:
            - name: cloudsql-instance-credentials
              mountPath: /secrets/cloudsql
              readOnly: true
      volumes:
        - name: wordpress-persistent-storage
          persistentVolumeClaim:
            claimName: wp-pv-claim
        - name: cloudsql-instance-credentials
          secret:
            secretName: cloudsql-instance-credentials
# it basically replaces any environment variable from current terminal session to the variable placeholder
cat wordpress_cloudsql.yaml.template | envsubst > wordpress_cloudsql.yaml

kubectl apply -f wordpress_cloudsql.yaml

within a few minutes you can see that pods are up and running

# lets install certmanager and nginx ingress controller
helm repo add cert-manager https://charts.jetstack.io

kubectl create ns cert-manager

helm install my-cert-manager cert-manager/cert-manager --version 1.14.3 --set installCRDs=true --set global.leaderElection.namespace=cert-manager

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.0/deploy/static/provider/cloud/deploy.yaml
# create a file name ingress.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: kubeissuer
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: <EMAILADDRESS>
    privateKeySecretRef:
      name: kubeissuer
    solvers:
    - http01:
        ingress:
          class: nginx
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: kubecert
spec:
  secretName: demo-tls
  issuerRef:
    name: kubeissuer
    kind: ClusterIssuer
  commonName: <FQDN>
  dnsNames:
  - <FQDN>
---

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: kubeissuer
    kubernetes.io/ingress.class: nginx
  name: kube-certs-ingress
spec:
  ingressClassName: nginx
  tls:
  - hosts:
      - <FQDN>
    secretName: demo-tls

  rules:
  - host: <FQDN>
    http: 
      paths:
      - backend:
          service:
            name: wordpress
            port:
              number: 80
        path: /
        pathType: Prefix
kubectl create -f ingress.yaml

Once thats done you can then check your wordpress website

lets create a script so that we can make it reuse it

make sure you have the components installed!!

gcloud components install gke-gcloud-auth-plugin

# or use dnf or apt
sudo dnf install google-cloud-sdk-gke-gcloud-auth-plugin -y
#!/bin/bash
gcloud config set compute/region asia-south1
export PROJECT_ID=""

gcloud services enable container.googleapis.com sqladmin.googleapis.com

CLUSTER_NAME="wordpress-k8s"
INSTANCE_NAME="mysql-wordpress-instance"
SA_NAME="cloudsql-proxy"
DOMAIN=""
EMAIL=""

gcloud container clusters create-auto $CLUSTER_NAME

gcloud container clusters get-credentials $CLUSTER_NAME

kubectl cluster-info

gcloud sql instances create $INSTANCE_NAME

export INSTANCE_CONNECTION_NAME=$(gcloud sql instances describe $INSTANCE_NAME \
    --format='value(connectionName)')

gcloud sql databases create wordpress --instance $INSTANCE_NAME

CLOUD_SQL_PASSWORD=$(openssl rand -base64 18)

gcloud sql users create wordpress --host=% --instance $INSTANCE_NAME --password $CLOUD_SQL_PASSWORD

gcloud iam service-accounts create $SA_NAME --display-name $SA_NAME

SA_EMAIL=$(gcloud iam service-accounts list \
    --filter=displayName:$SA_NAME \
    --format='value(email)')

gcloud projects add-iam-policy-binding $PROJECT_ID \
    --role roles/cloudsql.client \
    --member serviceAccount:$SA_EMAIL

gcloud iam service-accounts keys create key.json \
    --iam-account $SA_EMAIL

# at this moment we are just need to create 2 secrets for them to be used by the deployment
kubectl create secret generic cloudsql-db-credentials \
    --from-literal=username=wordpress \
    --from-literal=password=$CLOUD_SQL_PASSWORD

kubectl create secret generic cloudsql-instance-credentials \
    --from-file key.json

cat <<EOF > wordpress_cloudsql.yaml.template
apiVersion: v1
kind: Service
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  ports:
    - port: 80
  selector:
    app: wordpress
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wp-pv-claim
  labels:
    app: wordpress
spec:
  storageClassName: "standard-rwx"
  accessModes:
    - ReadWriteMany # so that pods which will get deployed on other nodes, still can mount this PVC
  resources:
    requests:
      storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  replicas: 3
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
        - image: wordpress
          name: wordpress
          env:
          - name: WORDPRESS_DB_HOST
            value: 127.0.0.1:3306
          - name: WORDPRESS_DB_USER
            valueFrom:
              secretKeyRef:
                name: cloudsql-db-credentials
                key: username
          - name: WORDPRESS_DB_PASSWORD
            valueFrom:
              secretKeyRef:
                name: cloudsql-db-credentials
                key: password
          ports:
            - containerPort: 80
              name: wordpress
          volumeMounts:
            - name: wordpress-persistent-storage
              mountPath: /var/www/html
        # Change ${INSTANCE_CONNECTION_NAME} here to include your GCP
        # project, the region of your Cloud SQL instance and the name
        # of your Cloud SQL instance. The format is
        # $PROJECT:$REGION:$INSTANCE
        - name: cloudsql-proxy
          image: gcr.io/cloudsql-docker/gce-proxy:1.33.2


          command: ["/cloud_sql_proxy",
                    "-instances=${INSTANCE_CONNECTION_NAME}=tcp:3306",
                    "-credential_file=/secrets/cloudsql/key.json"]
          securityContext:
            runAsUser: 2  # non-root user
            allowPrivilegeEscalation: false
          volumeMounts:
            - name: cloudsql-instance-credentials
              mountPath: /secrets/cloudsql
              readOnly: true
      volumes:
        - name: wordpress-persistent-storage
          persistentVolumeClaim:
            claimName: wp-pv-claim
        - name: cloudsql-instance-credentials
          secret:
            secretName: cloudsql-instance-credentials
EOF

# it basically replaces any environment variable from current terminal session to the variable placeholder
cat wordpress_cloudsql.yaml.template | envsubst > wordpress_cloudsql.yaml

kubectl apply -f wordpress_cloudsql.yaml

# lets install certmanager and nginx ingress controller
helm repo add cert-manager https://charts.jetstack.io

kubectl create ns cert-manager

helm install my-cert-manager cert-manager/cert-manager --version 1.14.3 --set installCRDs=true --set global.leaderElection.namespace=cert-manager

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.0/deploy/static/provider/cloud/deploy.yaml

read -p "Enter the key to continue once you have done with your dns configuration get your Public IP from here \$ kubectl get svc -A: "

sleep 1m

cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: kubeissuer
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ${EMAIL}
    privateKeySecretRef:
      name: kubeissuer
    solvers:
    - http01:
        ingress:
          class: nginx
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: kubecert
spec:
  secretName: demo-tls
  issuerRef:
    name: kubeissuer
    kind: ClusterIssuer
  commonName: ${DOMAIN}
  dnsNames:
  - ${DOMAIN}
---

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: kubeissuer
    kubernetes.io/ingress.class: nginx
  name: kube-certs-ingress
spec:
  ingressClassName: nginx
  tls:
  - hosts:
      - ${DOMAIN}
    secretName: demo-tls

  rules:
  - host: ${DOMAIN}
    http: 
      paths:
      - backend:
          service:
            name: wordpress
            port:
              number: 80
        path: /
        pathType: Prefix
EOF

References

Conclusion

Thank you all I hope you dont have to find solutions for all these

Stay tuned for the HTTPS with gateway api