The official instructions for deploying ASP.NET Core app on GKE is available here. So, why this post? If you are like me, you will want to do all the development in Windows using Visual Studio and want to finally deploy the app in the K8s cluster. So, this post shows doing most of the work through Windows command prompt. Of course, some of the manual steps shown here will be part of the CI/CD process in a real project.
Here are the steps.
If you don’t already have a Google Cloud Platform account, create one with the free trial. Sign-in to Google Cloud Platform console (console.cloud.google.com) and create a new project, say “hello-project”. You have to use the ID of the project, which is something like helloproject-111111 in most of the commands.
Go to https://cloud.google.com/sdk/docs/quickstart-windows and Install Cloud SDK using the installer GoogleCloudSDKInstaller.exe. At the last step of installation, make sure the following check boxes are checked: Start Google Cloud SDK Shell and Run ‘gcloud init’. The installer starts a command prompt and runs the “gcloud init” command. This lets you login with your GCP credentials and select a project. Select “hello-project” and that becomes your default project.
You will do all the steps outlined in this post in this command prompt, except for a few steps for which you will use Google Cloud Shell.
Through GCP console (Web page), enable Kubernetes API. In the GCP Console, go to “APIs & services”, click “Enable APIs and services” and search for “Google Kubernetes Engine API” and click Enable.
With that, you are ready to create a Kubernetes cluster. To create a cluster, I’m going to use f1-micro type instance and this requires a minimum of 3 nodes. From the command prompt run the following. It will be just a single line but I show it in multiple lines for readability. Goes without saying you can use the zone of your choice.
gcloud container clusters create helloworld-cluster --num-nodes 3 --machine-type f1-micro --zone asia-south1-a
Create an ASP.NET Core project “HelloWorldApp”. To the project, add a file name “Dockerfile”. This Dockerfile uses multi-stage builds.
FROM microsoft/aspnetcore-build:2.0 AS build-env WORKDIR /app # Copy csproj and restore as distinct layers COPY *.csproj ./ RUN dotnet restore # Copy everything else and build COPY . ./ RUN dotnet publish -c Release -o out # Build runtime image FROM microsoft/aspnetcore:2.0 WORKDIR /app COPY --from=build-env /app/out . ENTRYPOINT ["dotnet", "HelloWebApp.dll"]
Zip the project folder (HelloWebApp) into HelloWebApp.zip.
Launch Google Cloud Shell in Google Cloud Platform console (console.cloud.google.com). Google Cloud Shell is a shell environment for managing GCP resources. It is like a mini Linux SSH shell but you can access only through the GCP console (browser). You cannot connect through Putty, for example.
Upload the HelloWebApp.zip using Cloud Shell. From Cloud Shell, run the unzip command (unzip HelloWebApp.zip) and cd to HelloWorldApp. Build and run the docker image. “docker build” creates the docker image using our Dockerfile. The “docker run” command runs the image – creates the container.
docker build -t asia.gcr.io/helloproject-111111/helloworld-webapp:v1 . docker run -d -p 8080:80 asia.gcr.io/helloproject-111111/helloworld-webapp:v1
The ASP.NET Core app now runs in a container. The docker image has the environment variable ASPNETCORE_URLS=http://+:80 and hence the app listens in port 80. The base image we used (microsoft/aspnetcore:2.0) has this environment variable set in its Dockerfile, which we used through the “FROM” command.
Using Cloud Shell, you can test the app (Web preview – preview on port 8080). Once testing is okay, enable container registry API in GCP web console (just similar to how we enabled Kubernetes API) and push the image to the container registry.
gcloud docker -- push asia.gcr.io/helloproject-111111/helloworld-webapp:v1
At this point, the container image is available in the GCP registry to be run in GKE. Kubernetes deals only with pods and not containers. Pods are the smallest deployable units that can be created and managed in Kubernetes.
Now is the time to install the kubectl command-line tool. From the same Window command prompt you have been using all along, run this command.
gcloud components install kubectl
That installs kubectl. With that, you can create a Pod now. The simplest way to create a Pod using the Docker image is via the imperative kubectl run command. Run the following command in the Window command prompt.
kubectl run hello --image=asia.gcr.io/helloproject-111111/helloworld-webapp:v1 --port=80
This creates a Pod all right. But under the hood, this actually creates the pod by creating a Deployment and ReplicaSet objects. Pods (a.k.a pod replicas) are managed by a replica set (f.k.a replication controller), which creates and destroys replicas of pods as needed. Pods can also be created outside of a replication controller, but it isn’t a common practice. A ReplicaSet ensures that a specified number of pod replicas are running at any one time. Deployment is for deploying the changes. ReplicaSets manage Pods, Deployments manage ReplicaSets. If you terminate a pod, replica set will bring up another pod and makes sure the required number of pods run all the time.
kubectl run creates replicaset. By default it creates one Pod. But it can be used to create replicaset with size more than 1.
kubectl run hello --image=asia.gcr.io/helloproject-111111/helloworld-webapp:v1 --port=80 --replicas=2
This creates a deployment, a replicaset and two pods. Scaling can be done after creation as well. However, the replicaset must not be scaled. You should not manage ReplicaSets owned by a Deployment.
kubectl scale rs hello-302434 --replicas=4
Deployment will make sure pod count is as per what it is configured. This scaling will increase the Pods to 4 but deployment will terminate the new 2 pods.
kubectl scale deployment hello --replicas=4
By default, the pod is only accessible within the cluster. In order to make it accessible from outside world, you have to expose the pod as a kubernetes service. To do this, run the following command in the command prompt.
kubectl expose deployment hello --type="LoadBalancer" --port=80
Run the following command.
kubectl get services
After a while, it will produce an output like this – with the external IP set.
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE hello-dotnet 10.3.240.71 188.8.131.52 80/TCP 5m kubernetes ...
Use the external IP and make a request http://184.108.40.206. That will display the home page of the ASP.NET Core app. You are done!
To clean up, you have to delete the deployment. The deployment was create by the kubectl run. Also, we created a service (of type load balancer). That will need to be deleted as well.
kubectl delete service,deployment hello
Finally, delete the cluster
gcloud container clusters delete helloworld-cluster --zone asia-south1-a
To delete the Docker registry storage bucket:
gsutil ls ---> this lists "gs://asia.artifacts.helloproject-111111.appspot.com/" gsutil rm -r gs://asia.artifacts.helloproject-111111.appspot.com/
Debugging ASP.NET Core apps is an interesting topic. If you use Windows to develop and debug in Windows, it is not same as running in Linux. There are two options here.
(1) The best but most difficult option – Install “Docker for Windows”. You need a Windows 10 machine for this. If you are using a cloud instance, make sure it supports nested virtualization. “Docker for Windows” uses Moby Linux VM to run your containers and it is directly integrated with Visual Studio, which is the best experience. When creating a new app with the ASP.NET Core Web Application project templates, select the Enable Docker Support checkbox to get this working. More info here. But this does not work most of the time, at least in my experience. There will some issue or the other. Worth giving this a shot mainly because of the F5 experience.
(2) Deploy your app (debug build) in a Linux instance where you have already installed ASP.NET Core 2.0. Do remote debugging, as outlined here.