In this post I will show how to deploy microservices to Kubernetes. My example will show how to deploy a microservice with a public IP in front of an internal service with a cluster scoped IP.

This specific example uses asp.net core, but the general idea is framework agnostic.

Asp .Net

I have created a simple car service in front of a greeting service where the car service calls the greeting service to create a car specific greeting.

Car service will be deployed with a public IP to allow traffic from outside the cluster. Greeting service doesn’t need to be exposed outside the cluster, so it will be deploy with a cluster specific IP (ClusterIP).

The source code for the two services is listed below:

Car Service
namespace cars.Controllers { [Route("api/[controller]")] [ApiController] public class CarsController : ControllerBase { private readonly IHttpClientFactory _clientFactory; public CarsController(IHttpClientFactory clientFactory) { _clientFactory = clientFactory; } // GET api/values [HttpGet] public async Task<string> Get() { var client = _clientFactory.CreateClient(); string response = await client.GetStringAsync("http://greetings-service:8000/api/Greetings"); return $"{response} from BMW"; } } }
Greeting Service
namespace greetings.Controllers { [Route("api/[controller]")] [ApiController] public class GreetingsController : ControllerBase { // GET api/values [HttpGet] public ActionResult<string> Get() { return "Hello"; } } }

Docker

The first step is to create Docker images that can be deployed to Kubernetes. Dockerfiles for both services are included below:

Car Service
FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build COPY cars/*.csproj /cars/ COPY cars/. /cars/ WORKDIR /cars RUN dotnet restore RUN dotnet publish cars.csproj -c Release -o out FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS runtime WORKDIR /app COPY --from=build /cars/out ./ EXPOSE 80 ENTRYPOINT ["dotnet", "cars.dll"]
Greeting Service
FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build COPY greetings/*.csproj /greetings/ COPY greetings/. /greetings/ WORKDIR /greetings RUN dotnet restore RUN dotnet publish greetings.csproj -c Release -o out FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS runtime WORKDIR /app COPY --from=build /greetings/out ./ EXPOSE 80 ENTRYPOINT ["dotnet", "greetings.dll"]

Kubernetes

Once the images are built and deployed to a registry, or just locally, we can proceed to set up Kubernetes deployments and services to deploy the microservices to Kubernetes.

I have included the .yml files below:

Car Service
apiVersion: apps/v1 kind: Deployment metadata:   name: cars spec:   selector:     matchLabels:       app: cars   replicas: 5   template:     metadata:       labels:         app: cars     spec:       containers:         - name: cars           image: "car-service:v5"           ports:             - name: http               containerPort: 80 --- apiVersion: v1 kind: Service metadata:   name: cars spec:   selector:     app: cars   ports:   - protocol: TCP     port: 1234     targetPort: 80   type: LoadBalancer
Greeting Service
apiVersion: apps/v1 kind: Deployment metadata:   name: greetings spec:   selector:     matchLabels:       app: greetings   replicas: 5   template:     metadata:       labels:         app: greetings     spec:       containers:         - name: greetings           image: "greeting-service:v9"           ports:             - name: http               containerPort: 80 --- apiVersion: v1 kind: Service metadata:   name: greetings-service spec:   selector:     app: greetings   ports:   - protocol: TCP     port: 8000     name: http     targetPort: 80   type: ClusterIP

One important difference between the two yamls is the type field in the Service section. Car service is configured as a LoadBalancer since we want a public IP to allow traffic from outside the cluster. Greeting service is of type ClusterIP which will ensure that it gets an IP that is only accessible from inside the cluster.

Since the Cluster IP is auto generated, how can we call Greeting Service from Car Service? Luckily, Kubernetes helps out here by abstracting the IP with an internal host name. By default this hostname is determined by the name of the .yml service (greetings-service). This means we can access Greeting service as http://greetings-service:8000/api/Greetings instead of using the IP address.

I have pushed the code to Github in case you would like to try it out yourself.