Hosting a Static Website with Kubernetes and Google Cloud Storage

Mar 26, 2018 21:07 · 527 words · 3 minutes read gke kubernetes nginx s3

As of March 2018, I finally finished upgrading my personal homepage to a modern and scalable cloud infrastructure. This website is hosted on the extremely reliable Google Cloud Storage proxied through my Kubernetes cluster.

The Road to Upgrade

Once upon a time, I hosted the first version of my website on a small no-name virtual private server (VPS). Later on, with the movement towards GitHub pages, I migrated my website there. As a part-time security researcher, I greatly value the necessity of HTTPS support, but GitHub pages never managed to add HTTPS for custom domains. Fortunately, the newly emerged competitor GitLab satisfied all my requirements other than the downside of having to manually obtain a certificate and copy paste it there. My current Kubernetes + cloud storage solution brings the benefit of scalability and availability with cloud infrastructure at a very low cost.

The Architecture

architecture

All client requests are load balanced through the Kubernetes ingress controller. The traffic will then go through nginx containers which proxy the data from Google Cloud Storage.

Google Cloud Storage

In my example the main bucket that stores everything is call “storage.zihao.me” and my static files are uplaoded to the “zihao.me” folder under the bucket.

gcs

No other additional settings are required, we just need to remember the exact path of our static files.

Nginx Proxy

The key configurations for proxying requests to Google Cloud Storage are listed here

recursive_error_pages on;

location / {
    include                /etc/nginx/nginx_gcs_proxy.conf;
    proxy_pass             https://storage.googleapis.com/storage.zihao.me/zihao.me$uri;
    error_page 404 = @index;
}

location @index {
    include                /etc/nginx/nginx_gcs_proxy.conf;
    proxy_pass             https://storage.googleapis.com/storage.zihao.me/zihao.me${uri}index.html;
    error_page 404 = @slash_index;
}

location @slash_index {
    include                /etc/nginx/nginx_gcs_proxy.conf;
    proxy_pass             https://storage.googleapis.com/storage.zihao.me/zihao.me${uri}/index.html;
    error_page 404 = /404.html;
}

Since Google Cloud Storage is just a simple file storage engine, we need to do some extra work to make URL access working. We setup a two level redirections so folder/index.html, folder and folder/ all leads to the file at folder/index.html. The more common index index.html and try_files $uri settings do not work here since they were designed for local files only. The included nginx_gcs_proxy.conf sets correct proxy request headers and removes unnecessary response headers from Google Cloud Storage. The URL used in proxy_pass is https://storage.googleapis.com/BUCKET/PATH$uri as shown in the config. The exact configuration used for my file is available on my GitHub Repository

Kubernetes

With Nginx proxy ready, we also need to set up the corresponding kubernetes deployment.

kubernetes

We can simply used the official lightweight Nginx docker image at nginx:alpine.

containers:
- name: zihao
  image: nginx:alpine
  command: ["/bin/sh", "-c"]
  args:
    - "wget https://raw.githubusercontent.com/zzh8829/zihao/master/deploy/nginx_gcs_proxy.conf -O /etc/nginx/nginx_gcs_proxy.conf && \
      wget https://raw.githubusercontent.com/zzh8829/zihao/master/deploy/nginx_zihao.conf -O /etc/nginx/conf.d/default.conf && \
      nginx -g 'daemon off;'"
  ports:
  - containerPort: 8000

In the container configuration, we load the config file from GitHub and then fire up the nginx server. The Kubernetes service and ingress settings is very straightforward. I listed some selected parts of the configurations here.

# service
ports:
- port: 80
  targetPort: 8000
---
# ingress
rules:
- host: zihao.me
  http:
    paths:
    - backend:
        serviceName: zihao
        servicePort: 80
      path: /

The complete Kubernetes setting is also available on my GitHub Repository. After all the hard work, simply deploy the image with kubectl apply -f k8s.yaml and congratulations: the website is online at https://zihao.me