This post will document some of the issues I ran into while setting up Kubernetes for this site.

Handling www

I want requests to www.andrewtchin.com and andrewtchin.com to display the same site. I have a CNAME from www.andrewtchin.com to andrewtchin.com but did not configure that in the first iteration of the Ingress. This led to a TLS error upon connecting to www.andrewtchin.com since the Ingress used the default self-signed TLS certificate instead of a Let’s Encrypt certificate.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/issuer: "letsencrypt-prd"
  name: andrewtchin-com-nginx-ingress
  namespace: default
spec:
  tls:
  - hosts:
    - andrewtchin.com
    secretName: andrewtchin-com-tls
  rules:
  - host: andrewtchin.com
    http:
      paths:
      - path: /
        backend:
          serviceName: andrewtchin-com
          servicePort: 80

To achieve the desired result I simply had to add the additional TLS host and rule for www.andrewtchin.com.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/issuer: "letsencrypt-prd"
  name: andrewtchin-com-nginx-ingress
  namespace: default
spec:
  tls:
  - hosts:
    - andrewtchin.com
    - www.andrewtchin.com
    secretName: andrewtchin-com-tls
  rules:
  - host: andrewtchin.com
    http:
      paths:
      - path: /
        backend:
          serviceName: andrewtchin-com
          servicePort: 80
  - host: www.andrewtchin.com
    http:
      paths:
      - path: /
        backend:
          serviceName: andrewtchin-com
          servicePort: 80

As shown here, tls describes the hosts that the Ingress terminates with the corresponding secret (containing both DNS names). Then, rules describes which service traffic matching the host and path should be directed to.

⇒  kubectl describe ingress andrewtchin-com
...
TLS:
  andrewtchin-com-tls terminates andrewtchin.com,www.andrewtchin.com
Rules:
  Host                 Path  Backends
  ----                 ----  --------
  andrewtchin.com
                       /   andrewtchin-com:80 (10.244.0.62:80)
  www.andrewtchin.com
                       /   andrewtchin-com:80 (10.244.0.62:80)

Certificate Regeneration

After configuring the www subdomain in the Ingress, I needed the Let’s Encrypt certificate to be regenerated to include the additional DNS name. To do this I deleted the secret as below:

⇒  kubectl delete secret andrewtchin-com-tls

After this, visiting www.andrewtchin.com was still not using the correct TLS certificate. Viewing the certificate showed that it was waiting for the certificate request to complete:

⇒  kubectl describe certificate andrewtchin-com-tls
Name:         andrewtchin-com-tls
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  cert-manager.io/v1alpha2
Kind:         Certificate
...
Spec:
  Dns Names:
    andrewtchin.com
    www.andrewtchin.com
  Issuer Ref:
    Group:      cert-manager.io
    Kind:       Issuer
    Name:       letsencrypt-prd
  Secret Name:  andrewtchin-com-tls
Status:
  Conditions:
    Last Transition Time:  2019-12-28T00:21:18Z
    Message:               Waiting for CertificateRequest "andrewtchin-com-tls-480732090" to complete
    Reason:                InProgress
    Status:                False
    Type:                  Ready
Events:
  Type    Reason          Age                       From          Message
  ----    ------          ----                      ----          -------
  Normal  Requested       7m20s (x2 over 7m50s)     cert-manager  Created new CertificateRequest resource "andrewtchin-com-tls-480732090"
  Normal  GeneratedKey    7m20s                     cert-manager  Generated a new private key
  Normal  PrivateKeyLost  7m20s                     cert-manager  Lost private key for CertificateRequest "andrewtchin-com-tls-480732090", deleting old resource
  Normal  Issued          2m50s (x1336 over 7m24s)  cert-manager  Certificate issued successfully

Interestingly, the certificate request showed that it was successful:

⇒  kubectl describe certificaterequest andrewtchin-com-tls-480732090
Name:         andrewtchin-com-tls-480732090
Namespace:    default
Labels:       <none>
Annotations:  cert-manager.io/certificate-name: andrewtchin-com-tls
              cert-manager.io/private-key-secret-name: andrewtchin-com-tls
API Version:  cert-manager.io/v1alpha2
Kind:         CertificateRequest
...
  Conditions:
    Last Transition Time:  2019-12-28T00:21:18Z
    Message:               Certificate fetched from issuer successfully
    Reason:                Issued
    Status:                True
    Type:                  Ready
Events:
  Type    Reason             Age    From          Message
  ----    ------             ----   ----          -------
  Normal  CertificateIssued  9m31s  cert-manager  Certificate fetched from issuer successfully

To reissue the certificate, I deleted it:

⇒  kubectl delete certificate andrewtchin-com-tls

This resolved the problem and I was able to access both andrewtchin.com and www.andrewtchin.com correctly.

⇒  kubectl describe certificate andrewtchin-com-tls
Name:         andrewtchin-com-tls
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  cert-manager.io/v1alpha2
Kind:         Certificate
...
Spec:
  Dns Names:
    andrewtchin.com
    www.andrewtchin.com
  Issuer Ref:
    Group:      cert-manager.io
    Kind:       Issuer
    Name:       letsencrypt-prd
  Secret Name:  andrewtchin-com-tls
Status:
  Conditions:
    Last Transition Time:  2019-12-28T00:38:21Z
    Message:               Certificate is up to date and has not expired
    Reason:                Ready
    Status:                True
    Type:                  Ready
  Not After:               2020-03-26T23:38:20Z
Events:
  Type    Reason     Age                From          Message
  ----    ------     ----               ----          -------
  Normal  Requested  35s                cert-manager  Created new CertificateRequest resource "andrewtchin-com-tls-480732090"
  Normal  Issued     32s (x2 over 35s)  cert-manager  Certificate issued successfully

Deploy

For now, deployment is manual. To deploy, I delete the pod and the new image gets pulled when the replacement is created. I will experiment with setting up deployment from Gitlab next.

⇒  kubectl get pods
NAME                                                 READY   STATUS    RESTARTS   AGE
andrewtchin-com-55dfd65667-55ngf                     1/1     Running   0          20h

⇒  kubectl delete pod andrewtchin-com-55dfd65667-55ngf
pod "andrewtchin-com-55dfd65667-55ngf" deleted

⇒  kubectl get pods
NAME                                                 READY   STATUS              RESTARTS   AGE
andrewtchin-com-55dfd65667-mmzb2                     0/1     ContainerCreating   0          5s

⇒  kubectl get pods
NAME                                                 READY   STATUS    RESTARTS   AGE
andrewtchin-com-55dfd65667-mmzb2                     1/1     Running   0          26s

Conclusion

Overall I am very happy with this setup and look forward to deploying other apps on the cluster!