docker run --rm --name hello -p 80:8080 38leinad/hello
30 May 2018
In this post I am describing how to deploy a dockerized Java EE 7 application to the Google Cloud Platform (GCP) with Kubernetes.
My previous experience is only with AWS; in specific with EC2 and ECS. So, this is not only my first exposure to the Google Cloud but also my first steps with Kubernetes.
The application I would like to deploy is a simple Java EE 7 application exposing a basic HTTP/Rest endpoint. The sources are located on GitHub and the Docker image can be found on Docker Hub. If you have Docker installed, you can easily run it locally via
docker run --rm --name hello -p 80:8080 38leinad/hello
Let’s deploy it on the Google Cloud now.
Obviously, you will have to register on https://cloud.google.com/ for a free trial-account first. It is valid for one year and also comes with a credit of $300. I am not sure yet what/when resources will cost credit. After four days of tinkering, $1 is gone.
Once you have singed up, you can do all of the configuration and management of your apps from the Google Cloud web-console. They even have an integrated terminal running in the browser. So, strictly it is not required to install any tooling on your local system if you are happy with this.
The only thing we will do from the web-console is the creation of a Kubernetes Cluster (You can also do this via
gcloud from the commandline). For this you go to "Kubernetes Engine / Kubernetes clusters" and "Create Cluster". You can leave all the defaults, just make sure to remember the name of the cluster and the zone it is deployed to. We will need this later to correctly set up the
kubectl commandline locally. Note that it will also ask you to set up a project before creating the cluster. This allows grouping of resources in GCP based on different projects which is quiet useful.
Setting up the cluster is heavy lifting and thus can take some minutes. In the meantime, we can already install the tools.
Install SDK / CLI (Centos): https://cloud.google.com/sdk/docs/quickstart-redhat-centos.
I had to make sure to be logged out of my Google-account before running
gcloud init. Without doing this, I received a 500 http-response.
Also, when running
gcloud init it will ask your for a default zone. Choose the one you used when setting up the cluster. Mine is
gcloud components install kubectl
Note that you can also install
kubectl independently. E.g. I already had it installed from here while using minikube.
Now, you will need the name of the cluster you have created via the web-console. Configure the
gcloud CLI-tool for your cluster:
gcloud container clusters get-credentials <cluster-name> --zone <zone-name> --project <project-name>
You can easily get the full command with correct parameters when opening the cluster in the web-console and clicking the "Connect" button for the web-based CLI.
kubectl get pods just to see if the command works. You should see
No resources found.. At this point, we have configured our CLI/
kubectl to interact with our kubernetes cluster.
The next thing we will do is optional but makes life easier once you have multiple applications deployed on your cluster. You can create a namespace/context per application your are deploying to GCP. This allows you to always only see the resources of the namespace you are currently working with. It also allows you to delete the namespace and it will do a cascading delete of all the resources. So, this is very nice for experimentation and not leaving a big mess of resources.
kubectl create namespace hello-namespace kubectl get namespaces
We create a namespace for our application and check if it actually was created.
You can now attach this namespace to a context. A context is not a resource on GCP but is a configuration in your local
kubectl config set-context hello-context --namespace=hello-namespace \ --cluster=<cluster-name> \ --user=<user-name>
<user-name> that you have to put in? Easiest, is to get it from running
kubectl config view
Let’s activate this context. All operations will be done within the assigned namespace from now on.
kubectl config use-context hello-context
You can also double-check the activated context:
kubectl config current-context
kubectl config view command again or even check in
<user-home>/.kube/config. As said before, the current-context can be found here and is just a local setting.
You can read more on namespaces here.
Deploying the application in Kubernetes requires three primitives to be created:
Deployment/Pods: These are the actually docker-containers that are running. A pod actually could consist of multiple containers. Think of e.g. side-car containers in a microservice architecture.
Service: The containers/Pods are hidden behind a service. Think of the Service as e.g. a load-balancer: You never interact with the individual containers directly; the load-balancer is the single service you as a client call.
Ingress: Our final goal is to access our application from the Internet. By default, this is not possible. You will have to set up an Ingress for Incoming Traffic. Basically, you will get an internet-facing IP-address that you can call.
All these steps are quiet nicely explained when you read the offical doc on Setting up HTTP Load Balancing with Ingress. What you will find there, is that Deployment, Service and Ingress are set up via indivdual calls to
kubectl. You could put all these calls into a shell-script to easily replay them, but there is something else in the Kubernets world. What we will be doing here instead, is define these resources in a YAML file.
apiVersion: apps/v1beta2 kind: Deployment metadata: name: hello-deployment spec: selector: matchLabels: app: hello replicas: 1 template: metadata: labels: app: hello spec: containers: - name: hello image: 38leinad/hello:latest ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: hello-service spec: type: NodePort selector: app: hello ports: - port: 8080 --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: hello-ingress spec: backend: serviceName: hello-service servicePort: 8080
We can now simply call
kubectl apply -f hello.yml.
Get the public IP by running
kubectl get ingress hello-ingress
You can now try to open http://<ip>/hello/resources/health in your browser or with cURL. You should get an "UP" response. Note that this can actually take some minutes before it will work.
Once it worked, you can check the application-server log as well like this:
kubectl get pods kubectl logs -f <pod-name>
Note that the first command is to get the name of the Pod. The second command will give you the log-output of the container; you might know this from plain Docker already.
We succesfully deployed a dockerized application to the Google Cloud via Kubernetes.
A final not on why namespaces are useful: What you can do now to start over again is invoke
kubectl delete namespace hello-namespace
and all the resources in the cluster are gone.
Lastly, a cheat-sheet for some of the important
kubectl commands can be found here. Here, you will also find how to get auto-completion in your shell which is super-useful. As I am using zsh, I created an alias for it:
alias kubeinit="source <(kubectl completion zsh)"