1. Overview
ASP.NET Core is an open-source and cross-platform framework for building modern cloud-based and internet-connected applications using the C# programming language.
Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications. Istio is an open framework for connecting, securing, managing and monitoring services.
In this first part of the lab, you deploy a simple ASP.NET Core app to Kubernetes running on Google Kubernetes Engine (GKE) and configure it to be managed by Istio.
In the second part of the lab, you further explore features of Istio such as metrics, tracing, dynamic traffic management, fault injection, and more.
What you'll learn
- How to create and package a simple ASP.NET Core app in a Docker container.
- How to create a Kubernetes cluster with Google Kubernetes Engine (GKE).
- How to install Istio on a Kubernetes cluster on GKE.
- How to deploy your ASP.NET Core app and configure its traffic to be managed by Istio.
What you'll need
How will you use this tutorial?
How would rate your experience with Google Cloud Platform?
2. Setup and requirements
Self-paced environment setup
- Sign in to Cloud Console and create a new project or reuse an existing one. If you don't already have a Gmail or Google Workspace account, you must create one.
Remember the project ID, a unique name across all Google Cloud projects (the name above has already been taken and will not work for you, sorry!). It will be referred to later in this codelab as PROJECT_ID
.
- Next, you'll need to enable billing in Cloud Console in order to use Google Cloud resources.
Running through this codelab shouldn't cost much, if anything at all. Be sure to to follow any instructions in the "Cleaning up" section which advises you how to shut down resources so you don't incur billing beyond this tutorial. New users of Google Cloud are eligible for the $300 USD Free Trial program.
Start Cloud Shell
While Google Cloud can be operated remotely from your laptop, in this codelab you use Google Cloud Shell, a command line environment running in Google Cloud.
Activate Cloud Shell
- From the Cloud Console, click Activate Cloud Shell .
If you've never started Cloud Shell before, you're presented with an intermediate screen (below the fold) describing what it is. If that's the case, click Continue (and you won't ever see it again). Here's what that one-time screen looks like:
It should only take a few moments to provision and connect to Cloud Shell.
This virtual machine is loaded with all the development tools you need. It offers a persistent 5GB home directory and runs in Google Cloud, greatly enhancing network performance and authentication. Much, if not all, of your work in this codelab can be done with simply a browser or your Chromebook.
Once connected to Cloud Shell, you should see that you are already authenticated and that the project is already set to your project ID.
- Run the following command in Cloud Shell to confirm that you are authenticated:
gcloud auth list
Command output
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
- Run the following command in Cloud Shell to confirm that the gcloud command knows about your project:
gcloud config list project
Command output
[core] project = <PROJECT_ID>
If it is not, you can set it with this command:
gcloud config set project <PROJECT_ID>
Command output
Updated property [core/project].
3. Create an ASP.NET Core app in Cloud Shell
In Cloud Shell prompt, you can verify that the dotnet command line tool is already installed by checking its version. This should print the version of the installed dotnet command line tool:
dotnet --version
Next, create a new skeleton ASP.NET Core web app.
dotnet new mvc -o HelloWorldAspNetCore
This should create a project and restore its dependencies. You should see a message similar to below.
Restore completed in 11.44 sec for HelloWorldAspNetCore.csproj.
Restore succeeded.
4. Run the ASP.NET Core app
We're almost ready to run our app. Navigate to the app folder.
cd HelloWorldAspNetCore
Finally, run the app.
dotnet run --urls=http://localhost:8080
Application starts listening on port 8080.
Hosting environment: Production
Content root path: /home/atameldev/HelloWorldAspNetCore
Now listening on: http://[::]:8080
Application started. Press Ctrl+C to shut down.
To verify that the app is running, click on the web preview button on the top right and select ‘Preview on port 8080'.
You'll see the default ASP.NET Core webpage:
Once you verify that the app is running, press Ctrl+C to shut down the app.
5. Package the ASP.NET Core app in a Docker container
Next, prepare your app to run as a container. The first step is to define the container and its contents.
In the base directory of the app, create a Dockerfile
to define the Docker image.
touch Dockerfile
Add the following to Dockerfile
using your favorite editor (vim,
nano,emacs
or Cloud Shell's code editor).
# Use Microsoft's official build .NET image. # https://hub.docker.com/_/microsoft-dotnet-core-sdk/ FROM mcr.microsoft.com/dotnet/sdk:5.0-alpine AS build WORKDIR /app # Install production dependencies. # Copy csproj and restore as distinct layers. COPY *.csproj ./ RUN dotnet restore # Copy local code to the container image. COPY . ./ WORKDIR /app # Build a release artifact. RUN dotnet publish -c Release -o out # Use Microsoft's official runtime .NET image. # https://hub.docker.com/_/microsoft-dotnet-core-aspnet/ FROM mcr.microsoft.com/dotnet/aspnet:5.0-alpine AS runtime WORKDIR /app COPY --from=build /app/out ./ # Make sure the app binds to port 8080 ENV ASPNETCORE_URLS http://*:8080 # Run the web service on container startup. ENTRYPOINT ["dotnet", "HelloWorldAspNetCore.dll"]
One important configuration included in your Dockerfile is the port on which the app listens for incoming traffic (8080). This is accomplished by setting the ASPNETCORE_URLS
environment variable, which ASP.NET Core apps use to determine which port to listen to.
Save this Dockerfile
. Now, let's build the image:
docker build -t gcr.io/${GOOGLE_CLOUD_PROJECT}/hello-dotnet:v1 .
Once this completes (it'll take some time to download and extract everything), you can see the image is built and saved locally:
docker images REPOSITORY TAG gcr.io/yourproject-XXXX/hello-dotnet v1
Test the image locally with the following command which will run a Docker container locally on port 8080 from your newly-created container image:
docker run -p 8080:8080 gcr.io/${GOOGLE_CLOUD_PROJECT}/hello-dotnet:v1
And again take advantage of the Web preview feature of CloudShell :
You should see the default ASP.NET Core webpage in a new tab.
Once you verify that the app is running fine locally in a Docker container, you can stop the running container by Ctrl-> C
.
Now that the image works as intended you can push it to the Google Container Registry, a private repository for your Docker images accessible from every Google Cloud project (but also from outside Google Cloud Platform) :
docker push gcr.io/${GOOGLE_CLOUD_PROJECT}/hello-dotnet:v1
If all goes well and after a little while, you should be able to see the container image listed in the Container Registry section. At this point, you now have a project-wide Docker image available which Kubernetes can access and orchestrate as you'll see in a few minutes.
If you're curious, you can navigate through the container images as they are stored in Google Cloud Storage by following this link: https://console.cloud.google.com/storage/browser/ (the full resulting link should be of this form: https://console.cloud.google.com/project/PROJECT_ID/storage/browser/).
6. Create a Kubernetes/GKE cluster with Istio
First, make sure that you have the Kubernetes Engine API enabled:
gcloud services enable container.googleapis.com
Create a Kubernetes cluster. You can change the region to somewhere close to you, if you like:
gcloud container clusters create hello-istio \ --cluster-version=latest \ --machine-type=n1-standard-2 \ --num-nodes=4 \ --region europe-west1
Wait a few moments while your cluster is set up for you. It will be visible in the Kubernetes Engine section of the Google Cloud Platform console.
For this codelab, we will download and install Istio from istio.io. There are other installation options, including the Istio add-on for GKE and Anthos Service Mesh. The application steps after this one will work on any Istio installation.
Let's first download the Istio client and samples. The Istio release page offers download artifacts for several OSs. In our case, we can use a convenient command to download and extract the latest release for our current platform:
curl -L https://istio.io/downloadIstio | sh -
The script will tell you the version of Istio that has been downloaded:
Istio has been successfully downloaded into the istio-1.8.1 folder on your system.
The installation directory contains sample applications and the istioctl
client binary. Change to that directory:
cd istio-1.8.1
Copy and paste the provided command to add the bin
directory to your PATH
, so you can use istioctl
:
export PATH="$PATH:/home/<YOURHOMEID>/istio-1.8.1/bin"
Verify that istioctl
is available by checking your cluster is ready for Istio:
istioctl x precheck
You should see a message saying Install Pre-Check passed! The cluster is ready for Istio installation.
Install Istio with demo profile:
istioctl install --set profile=demo
Istio is now installed in your cluster.
Automatic sidecar injection
To start using Istio, you don't need to make any changes to the application. When you configure and run the services, Envoy sidecars are automatically injected into each pod for the service.
For that to work, you need to enable sidecar injection for the namespace (‘default') that you use for your microservices. You do that by applying a label:
kubectl label namespace default istio-injection=enabled
To verify that the label was successfully applied, run the following command:
kubectl get namespace -L istio-injection
The output confirms that sidecar injection is enabled for the default namespace:
NAME STATUS AGE ISTIO-INJECTION default Active 3m enabled istio-system Active 63s disabled ...
7. Verify the installation
Istio comes with three services: the istiod
control plane, and ingress and egress gateways (which you can think of as "sidecar proxies for the rest of the Internet") , named istio-ingressgateway
and istio-egressgateway
respectively.
kubectl get svc -n istio-system
Your output should look like this:
NAME TYPE CLUSTER-IP EXTERNAL-IP AGE istio-egressgateway ClusterIP 10.55.252.182 <none> istio-ingressgateway LoadBalancer 10.55.250.185 35.233.118.42 istiod ClusterIP 10.55.253.217 <none>
The Ingress Gateway has a type of LoadBalancer
so it is accessible from the Internet; the others only need to be accessible from within the cluster.
Next, make sure that the corresponding Kubernetes pods are deployed and all containers are up and running:
kubectl get pods -n istio-system
When all the pods are running, you can proceed.
NAME READY STATUS istio-egressgateway-674988f895-m6tk4 1/1 Running istio-ingressgateway-6996f7dcc8-7lvm2 1/1 Running istiod-6bf5fc8b64-j79hj 1/1 Running
istiod
: the Istio control plane. Handles configuration and programming of the proxy sidecars, service discovery, certificate distribution and sidecar injectioningress gateway
: Handles incoming requests from outside your cluster.egress gateway
: Handles outgoing requests to endpoints outside your cluster.
8. Deploy the application
Now you've verified that Istio is installed and running, you can deploy the ASP.NET Core app.
Deployment and Service
First, create an aspnetcore.yaml
file using your favorite editor (vim, nano,emacs
or Cloud Shell's code editor) and define the Kubernetes Deployment and Service for the app:
apiVersion: v1 kind: Service metadata: name: aspnetcore-service labels: app: aspnetcore spec: ports: - port: 8080 name: http selector: app: aspnetcore --- apiVersion: apps/v1 kind: Deployment metadata: name: aspnetcore-v1 spec: replicas: 1 selector: matchLabels: app: aspnetcore version: v1 template: metadata: labels: app: aspnetcore version: v1 spec: containers: - name: aspnetcore image: gcr.io/YOUR-PROJECT-ID/hello-dotnet:v1 imagePullPolicy: IfNotPresent ports: - containerPort: 8080
The contents of the file are standard Deployments and Services to deploy the application and don't contain anything Istio-specific.
Deploy the services to the default namespace with kubectl
:
kubectl apply -f aspnetcore.yaml
service "aspnetcore-service" created deployment.extensions "aspnetcore-v1" created
Verify that pods are running:
kubectl get pods
NAME READY STATUS RESTARTS AGE aspnetcore-v1-6cf64748-mddb 2/2 Running 0 34s
Gateway and VirtualService
To allow ingress traffic to reach the mesh you need to create a Gateway and a VirtualService.
A Gateway configures a load balancer for HTTP/TCP traffic, most commonly operating at the edge of the mesh to enable ingress traffic for an application. A VirtualService defines the rules that control how requests for a service are routed within an Istio service mesh.
Create a aspnetcore-gateway.yaml
file to define the Gateway:
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: aspnetcore-gateway spec: selector: istio: ingressgateway # use istio default controller servers: - port: number: 80 name: http protocol: HTTP hosts: - "*"
Create a aspnetcore-virtualservice.yaml
file to define the VirtualService:
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: aspnetcore-virtualservice spec: hosts: - "*" gateways: - aspnetcore-gateway http: - route: - destination: host: aspnetcore-service
Run the kubectl command to deploy the Gateway with:
kubectl apply -f aspnetcore-gateway.yaml
The command produces the following output:
gateway.networking.istio.io "aspnetcore-gateway" created
Next, run the following command to deploy the VirtualService:
kubectl apply -f aspnetcore-virtualservice.yaml
The command produces the following output:
virtualservice.networking.istio.io "aspnetcore-virtualservice" created
Verify that everything is running:
kubectl get gateway
NAME AGE aspnetcore-gateway 28s
kubectl get virtualservice
NAME AGE aspnetcore-virtualservice 33s
Congratulations! You have just deployed an Istio-enabled application. Next, you see the application in use.
9. Test the application
You can finally see the application in action. You need to get the external IP and port of the gateway. It's listed under EXTERNAL-IP
:
kubectl get svc istio-ingressgateway -n istio-system
Export the external IP and port to a GATEWAY_URL
variable:
export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].port}') export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT
Use curl
to test out the app. The service should respond with a response code of 200
:
curl -o /dev/null -s -w "%{http_code}\n" http://${GATEWAY_URL}/
Alternatively, you can open up the browser, navigate to http://<gatewayurl>
to view the app:
10. Congratulations!
You just deployed a simple ASP.NET Core app to Kubernetes running on Google Kubernetes Engine (GKE) and configured it to be managed by Istio.
You might be wondering "What's the benefit of Istio?". That's a great question. So far, there's no advantage to having Istio manage this app. In the second part of the lab, we will further explore features of Istio, such as metrics, tracing, dynamic traffic management, service visualization, and fault injection.
Next Steps
- Deploy ASP.NET Core app to GKE with Istio (Part 2).
- Learn more about Istio.
- Learn more about Kubernetes.
- Learn more about Google Kubernetes Engine.
- Learn more about .NET on Google Cloud Platform.
License
This work is licensed under a Creative Commons Attribution 2.0 Generic License.
11. Cleanup
If you're not continuing to the second part of the lab, you can delete the app and uninstall Istio or you can simply delete the Kubernetes cluster.
Delete the app
To delete the app:
kubectl delete -f aspnetcore-gateway.yaml Kubectl delete -f aspnetcore-virtualservice.yaml kubectl delete -f aspnetcore.yaml
To confirm that the app is gone:
kubectl get gateway kubectl get virtualservices kubectl get pods
Uninstall Istio
To delete Istio:
kubectl delete -f install/kubernetes/istio-demo-auth.yaml
To confirm that Istio is gone:
kubectl get pods -n istio-system
Delete Kubernetes cluster
gcloud container clusters delete hello-istio