knitr::opts_chunk$set(eval = FALSE, collapse = TRUE)
This document shows how to deploy the SmaRP Shiny app to GKE and set up an HTTPS load balancer exposing it through a URL, usable for embedding in website gallery.
Here we assume that a deployment smarp
already exists for SmaRP, running in the smarp
cluster.
Check if the smarp
cluster is the current context
```{bash current-context, eval = TRUE}
kubectl config current-context
You can set it otherwise by running ```{bash set-context, eval = TRUE} gcloud container clusters get-credentials smarp kubectl config current-context
Query the available pods to check that smarp
is deployed and running
```{bash get-pods, eval = TRUE}
kubectl get pods -o wide
## Setup and HTTP(s) load balancer ### References - [Concept: HTTP(s) load balancing with Ingress](https://cloud.google.com/kubernetes-engine/docs/concepts/ingress) - [Tutorial: Setting up HTTP Load Balancing with Ingress](https://cloud.google.com/kubernetes-engine/docs/tutorials/http-balancer) - [How-to: Configuring load balancing through Ingress](https://cloud.google.com/kubernetes-engine/docs/how-to/load-balance-ingress) (incl. multiple backend services) - [Tutorial: Configuring Domain Names with Static IP Addresses](https://cloud.google.com/kubernetes-engine/docs/tutorials/configuring-domain-name-static-ip) - [How-to: Configure SSL certificates for your Ingress load balancer](https://cloud.google.com/kubernetes-engine/docs/how-to/ingress-multi-ssl) ### Introduction HTTP(S) load balancing in GKE is based on three major components - An optional **backend configuration**, which we need here in order to set a backend service [timeout higher than the default 30s](https://cloud.google.com/kubernetes-engine/docs/concepts/ingress#support_for_websocket). The timeout is [interpreted](https://cloud.google.com/load-balancing/docs/backend-service#backend_service_settings) as the maximum time the connection can live for [**WebSockets** on HTTP(S)](https://cloud.google.com/load-balancing/docs/https/#websocket_proxy_support), which is the case for Shiny. - A **NodePort** backend service to expose the deployment, using the backend configuration. - An [**Ingress**](https://cloud.google.com/kubernetes-engine/docs/concepts/ingress) associated with the NodePort backend service exposing the app, routing external HTTP(S) traffic to the applications via an external IP address. This also allows configuring TLS certificates for HTTPS connections through a custom sub-domain (see below). ### Backend configuration We need to use a backend configuration to specify a sensible maximum connection timeout, higher than the default 30s. Note that this is a typical situation for WebSockets. A sensible value is 10800s (3h), since it is also Kubernetes' default [max session sticky time](https://kubernetes.io/docs/concepts/services-networking/) for the "ClientIP" `sessionAffinity`. See [Configuring a backend service through Ingress](https://cloud.google.com/kubernetes-engine/docs/how-to/configure-backend-service) for more details. Note that the `BackendConfig` custom resource is in a **Beta** release at the time of writing. The corresponding `BackendConfig` YAML manifest, including session affinity, is as follows ```{bash backendconfig-yaml, echo = FALSE, eval = TRUE} CAT_FILE="smarp-backendconfig.yaml" echo \# $CAT_FILE cat $CAT_FILE
The BackendConfig is then created from the manifest as ```{bash backendconfig} kubectl apply -f smarp-backendconfig.yaml
```{bash backendconfig-get, eval = TRUE} kubectl get backendconfig smarp-backendconfig
The NodePort
service manifest using the backend configuration created above is as follows
```{bash backend-yaml, echo = FALSE, eval = TRUE}
CAT_FILE="smarp-backend.yaml"
echo # $CAT_FILE
cat $CAT_FILE
TCP `port` 80 of the created Service is associated with a BackendConfig named `smarp-backendconfig` via annotation `beta.cloud.google.com/backend-config`. A request sent to `port` 80 of the service is forwarded to one of the member Pods on `targetPort` 80. Note that each member Pod must have a container listening on the specified `targetPort`. Note that there is no need to set the [`cloud.google.com/app-protocols`](https://cloud.google.com/kubernetes-engine/docs/concepts/ingress#https_tls_between_load_balancer_and_your_application) annotation to handle requests to port 443 for the HTTPS protocol, since our app is not capable of receiving HTTPS requests. An easy way to get the `labels` and `selector` right is to look at the YAML generated for a standard `NodePort` service using `expose` ```{bash nodeport-expose} kubectl expose deployment smarp --target-port=80 --port=80 --type=NodePort --dry-run -o=yaml
The corresponding service is then created from the manifest as ```{bash backend} kubectl apply -f smarp-backend.yaml
```{bash backend-get, eval = TRUE} kubectl get service smarp-backend
Create a static global IP address named smarp-ip
```{bash smarp-ip} gcloud compute addresses create smarp-ip --global
```{bash smarp-ip-describe} gcloud compute addresses describe smarp-ip --global
In order to have a domain / sub-domain (e.g. smarp.mirai-solutions.ch
) pointing to the static smarp-ip
address, domain name records must be configured with the domain registrar, by adding an A (Address) type DNS record for your domain or sub-domain name and have its value configured with the reserved IP address.
Once this is done you should be able to visit the app by typing the domain / sub-domain as URL (e.g. https://smarp.mirai-solutions.ch
). It might take several hours for the DNS records to propagate, you can check it via
```{bash dns, eval = TRUE}
host smarp.mirai-solutions.ch
### TLS certificate for HTTPS Google Cloud Platform offers [Google-managed TLS certificates](https://cloud.google.com/load-balancing/docs/ssl-certificates#managed-certs) for (sub)-domains enabling HTTPS traffic. Certificates are automatically provisioned and renewed and can be specified in an Ingress. Note that the managed certificates are in a **Beta** release at the time of writing. See [How-to: Using Google-managed SSL certificates](https://cloud.google.com/kubernetes-engine/docs/how-to/managed-certs). #### Kubernetes cluster update > Note: Managed certificates require clusters with masters running Kubernetes 1.12.6-gke.7 or higher. You can check the `serverVersion` via ```{bash} kubectl version -o=yaml | grep -i version
If the version is below 1.12.6-gke.7
, You will have to manually upgrade the cluster
To find the supported Kubernetes master and node versions for upgrades and downgrades, run the following command:
gcloud container get-server-config
You can manually upgrade the cluster's master, and then the nodes can be upgraded to the same version.
To upgrade the master's version of the smarp
cluster to a specific version (that is not the default) run e.g.
gcloud container clusters upgrade smarp --master --cluster-version 1.12.6-gke.10
where 1.12.6-gke.10
is a new-enough version (typically the latest) you found listed above.
This can take 10-20 minutes.
Once you've upgraded your cluster's master, the nodes can be upgraded to the same version. The following command upgrades your nodes to the version that your master is running:
gcloud container clusters upgrade smarp
Wait for the operation to complete (a few minutes).
You can define a ManagedCertificate resource for the smarp.mirai-solutions.ch
domain from above (pointing to smarp-ip
)
using the YAML manifest
```{bash certificate-yaml, echo = FALSE, eval = TRUE}
CAT_FILE="smarp-certificate.yaml"
echo # $CAT_FILE
cat $CAT_FILE
and create the corresponding ManagedCertificate resource ```{bash certificate} kubectl apply -f smarp-certificate.yaml
It is going to take between 10 minutes and up to 2 hours before the certificate is provisioned and activated.
While this occurs, you can still proceed to creating the Ingress below, possibly watch
ing the Certificate Status
via
```{bash certificate-watch}
watch kubectl describe managedcertificate smarp-certificate
It will eventually switch from status `Provisioning` to status `Active`, while the `Events` section will switch from `Create` to `<none>`. Note that the `Domain Status` can switch to `Status: FailedNotVisible` along the way, especially if the **TTL** of your DNS record is low. This doesn't prevent the certificate from activating, however if the event switches away from `Create` and you still see `Certificate Status: Provisioning`, you may have to delete the certificate and check if anything is wrong with your DNS configuration. ### Ingress configuration Create the Ingress exposing the app via an external IP address, associated with the TLS certificate defined above. In the manifest, you can see that incoming requests are routed to port 80 of the `NodePort` service backend `smarp-backend`. Specify `smarp-ip` in the Ingress manifest as `global-static-ip-name` annotation, and also add `smarp-certificate` as `networking.gke.io/managed-certificates` ```{bash ingress-yaml, echo = FALSE, eval = TRUE} CAT_FILE="smarp-ingress.yaml" echo \# $CAT_FILE cat $CAT_FILE
Then create the corresponding Ingress resource ```{bash ingress} kubectl apply -f smarp-ingress.yaml
Wait a few minutes for the Ingress controller to configure an HTTP(S) load balancer and an associated backend service. Be patient (8-12 minutes): > Note: It may take a few minutes for GKE to allocate an external IP address and set up forwarding rules until the load balancer is ready to serve your application. In the meanwhile, you may get errors such as HTTP 404 or HTTP 500 until the load balancer configuration is propagated across the globe. You can `watch` the status until you eventually see an IP address being allocated to the Ingress ```{bash ingress-watch} watch kubectl get ingress smarp-ingress
You should be able to reach the app via HTTPS https://smarp.mirai-solutions.ch, note that this can take longer and require another 10-15 minutes until the certificate is active (see above). You may want to keep watching it via ```{bash https-curl-watch} watch -n 30 curl https://smarp.mirai-solutions.ch/
### Disable HTTP traffic Note that we have disabled the HTTP traffic by including the annotation `kubernetes.io/ingress.allow-http: "false"` in the YAML manifest. Note that annotations can also be added or **removed dynamically**, e.g. ```{bash annotations} kubectl annotate ingress smarp-ingress kubernetes.io/ingress.allow-http-
Notice the minus sign, -
, at the end of the command.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.