A follow-along learning with Les Jackson's .NET Microservices - Full Course video on YouTube.
There is no Part 1 as it is introduction and theory only. You need to watch the video to know what this full course is about.
Some differences compared to the full course video:
- Full course uses .NET 5.
- I am using .NET 8.
- Full course installs
AutoMapper.Extensions.Microsoft.DependencyInjection.- At the time of this making, this package is deprecated and is suggested to install
AutoMapperinstead.
- At the time of this making, this package is deprecated and is suggested to install
- Sign up an account at Docker Hub.
- Download and install Docker Desktop. Once the installation finished, it will prompt for a restart.
- Update Windows Subsystem for Linux (WSL) to latest version by opening a Command Prompt and run
wsl.exe --update. - Enable Kubernetes on Docker Desktop by going to Settings
>Kubernetes.- Choose the option to create a single-node cluster.
- Click Apply & restart.
Note
Before executing a Docker command, make sure your command prompt directory has been changed to where the Dockerfile is located.
- To check if Docker is running:
docker --version. - To build the Docker image:
docker build -t <your_docker_hub_id>/platformservice ..- Make sure to include the period at the end as it is part of the command.
- To run the image:
docker run -p 8080:80 -d <your_docker_hub_id>/platformservice.- If
docker run -p 8080:80 -d <your_docker_hub_id>/platformservicecommand is executed again, it will create a new container.- It does not start up the existing container.
- For .NET 8, try
docker run -it --rm -p 8080:8080 --name platformservicecontainer <your_docker_hub_id>/platformservice.
- If
- To list running containers:
docker ps. - To stop a running container:
docker stop <container_id>(e.g.docker stop 3add933fd2f3). - To start an existing container:
docker start <container_id>(e.g.docker start 3add933fd2f3). - To push the image up to Docker Hub:
docker push <your_docker_hub_id>/platformservice.
Note
.NET 8 has a new default port (8080) as compared to .NET 5 (80). So whatever that specifies port 80 in the full course, may need to change to port 8080. See this.
There is an issue when following along Containerizing the Platform Service at 2:40:26, just before going into Pushing to Docker Hub. After starting the container, I am unable to access it via browser nor Postman. It may be due to me using .NET 8 as opposed to .NET 5, which is the version Les Jackson uses for the full course video.
Some findings:
- Docker: ASP.NET Core 8.0 app not accessible outside container
- Breaking change in .NET 8 means instructions to run docker container are wrong
These findings lead me to a tutorial by Microsoft. Just follow the format docker run -it --rm -p 3000:8080 --name mymicroservicecontainer mymicroservice and it should be okay. To make it easier to follow along with the full course, change the port from 3000:8080 to 8080:8080.
The creation of .dockerignore is not part of the full course video. It is taken from a tutorial by Microsoft. I find it useful.
Note
Before executing a Kubernetes command, make sure your command prompt directory has been changed to where the platforms-depl.yaml and platforms-np-srv.yaml are located.
- To check if Kubernetes is running:
kubectl version. - To apply a Deployment file:
kubectl apply -f platforms-depl.yaml.- This will create a container.
- To delete a deployment:
kubectl delete deployment platforms-depl.- This will delete the container.
- To apply a Service file:
kubectl apply -f platforms-np-srv.yaml. - To delete a service:
kubectl delete service platformnpsrv-service. - To get all:
kubectl get all. - To get list of nodes:
kubectl get nodes. - To get list of deployments:
kubectl get deployments. - To get list of pods:
kubectl get pods. - To get list of services:
kubectl get services.- If there's a NodePort-typed service, communicate by checking the assigned port.
8080:32626/TCPmeans that you can communicate with the Platforms API Controller route withhttp://localhost:32626/api/platforms.
- If there's a NodePort-typed service, communicate by checking the assigned port.
- To get logs of a pod:
kubectl logs app-pod. - To restart a deployment:
kubectl rollout restart deployment platforms-depl. - To get list of namespaces:
kubectl get namespace. - To get list of pods for a namespace:
kubectl get pods --namespace=ingress-nginx. - To get list of storage class:
kubectl get storageclass.
Note
In the full course video, Les Jackson points out to change the port in launchSettings.json for the CommandsService project as it is using the same port number with PlatformService project. However, this is not the case for me. I assume in .NET 8, every project will use a different port number upon creation. In .NET 5, I assume every project in a solution uses the same port.
- Two scenarios to try:
- Start both PlatformService and CommandsService.
- Start only PlatformService.
- Send a POST request to PlatformService controller.
- Check console and see what message is printed out for both scenarios.
- You will notice the time taken to complete the request for the 2nd scenario is longer, indicating that this method is synchronous.
Note
At this point, we are still using Node Port to communicate with the services.
Build, run and push the CommandService to your Docker Hub. Must be within CommandsService project directory.
docker build -t <your_docker_hub_id>/commandservice ..docker run -p 8080:8080 <your_docker_hub_id>/commandservice.docker push <your_docker_hub_id>/commandservice.
Deploy both services to Kubernetes. Must be within K8S directory.
kubectl apply -f platforms-depl.yaml.- Restart deployment to pull the latest version:
kubectl rollout restart deployment platforms-depl. kubectl apply -f commands-depl.yaml.
HTTPS Redirection is commented out in Program.cs.
Note
At this point, we moved away from using the Node Port and into using Ingress Nginx Controller to communicate with the services.
Warning
At the time of this making, when creating ingress-srv.yaml, it mentions that the annotation "kubernetes.io/ingress.class" is deprecated. Ignore this warning.
- Following this guide.
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.12.0/deploy/static/provider/cloud/deploy.yaml.- This will create the Ingress Nginx Controller container.
- For Windows, go to
C:\Windows\System32\drivers\etcand open thehostsfile on a text editor and add in127.0.0.1 acme.com.- This is required because the host in ingress-srv.yaml is using acme.com to redirect to localhost.
kubectl apply -f ingress-srv.yaml.- All the port numbers uses
8080instead of80. - Run the command on Command Prompt within K8S directory.
- All the port numbers uses
- Test by sending request to
http://acme.com/api/platforms.
kubectl apply -f local-pvc.yamlto create a Persistent Volume Claim.
kubectl create secret generic mssql --from-literal=SA_PASSWORD="pa55w0rd!".- Remember the name
mssqland the keySA_PASSWORDthat is defined. - You can specify whatever password you want here. I'm only using what Les Jackson uses for an easier follow-along learning.
- Remember the name
- If you're planning to use MSSQL 2022, use:
kubectl create secret generic mssql --from-literal=MSSQL_SA_PASSWORD="pa55w0rd!".
Some differences compared to the full course video:
- Full course uses MSSQL 2017.
- I am using MSSQL 2022.
- Full course uses
SA_PASSWORDas key because Les Jackson is using MSSQL 2017.- I am using
MSSQL_SA_PASSWORDas key because I am using MSSQL 2022.
- I am using
Apply the deployment with kubectl apply -f mssql-plat-depl.yaml.
Note
In the full course video, SQL Server Management Studio (SSMS) is used to connect into the server externally using the Load Balancer concept. However, I am using SQL Server Object Explorer on Visual Studio 2022 to do so. If you have and wish to use SSMS, just follow the full course.
To test connect to the server on Visual Studio 2022:
- View
>SQL Server Object Explorer. - Click the Add SQL Server icon. Alternatively, right-click SQL Server and select Add SQL Server. Enter the connection detail:
- Server Name:
localhost,1433 - Authentication:
SQL Server Authentication - User Name:
sa - Password:
pa55w0rd!(or the one you specified when creating secretmssql) - Database Name:
<default> - Encrypt:
Optional (False) - Trust Server Certificate:
False
- Server Name:
- Click Connect button.
- localhost,1433 should now be available in the tree under SQL Server.
To test that the persistent volume claim works:
- Expand localhost,1433.
- Right-click Databases and select Add New Database.
- Name it anything, e.g. Test.
- Right-click localhost,1433 and select Disconnect.
- Go to Docker Desktop
>Containers. - Delete the MSSQL container.
e.g. Find a container with name prefix
k8s_mssql_mssql-depl-<something-something>. - A new MSSQL container should be running automatically.
- Reconnect to the server on SQL Server Object Explorer.
- Verify if Test database is still there.
- If still there, then persistent volume claim is working as expected.
- Delete Test database, since it is only created to verify persistent volume claim works.
- In the full course video, the command to perform database migration is
dotnet ef migrations add initialmigration.- This is not working for me which is probably due to some missing global settings.
- I am using
Add-Migration -Name InitialMigrationinstead. - Before I can use
Add-Migration, it requires me to install the NuGet packageMicrosoft.EntityFrameworkCore.Tools. At the time of this making, the latest version is8.0.13. - After installing, I close and reopen the solution.
- I am also using the same
8.0.13version forMicrosoft.EntityFrameworkCore,Microsoft.EntityFrameworkCore.Design,Microsoft.EntityFrameworkCore.InMemoryandMicrosoft.EntityFrameworkCore.SqlServerNuGet packages.
- In the full course video, there are no issues when redeploying platforms-depl.yaml.
- For .NET 8, I get an exception with message
Only the invariant culture is supported in globalization-invariant mode.- To fix this issue, I need to modify the value for
InvariantGlobalizationfromtruetofalse.
- To fix this issue, I need to modify the value for
- For .NET 8, I get an exception with message
- In the full course video, the connection string for PlatformsConn is
Server=mssql-clusterip-srv,1433;Initial Catalog=platformsdb;User ID=sa;Password=pa55w0rd!;.- I am using
Server=10.105.207.125,1433;Database=platformsdb;User ID=sa;Password=pa55w0rd!;Encrypt=False;Trust Server Certificate=False;. - For .NET 8, I get an exception with message
A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections.. It fails to establish a connection when specifyingmssql-clusterip-srv,1433as the server. I also try changing tolocalhost,1433but still getting the same exception message. - I run
kubectl get servicesto check the Cluster IP value for bothmssql-clusterip-srv(e.g.10.105.207.125) andmssql-loadbalancer-srv(e.g.10.107.116.21) services. - I modified the connection string to use
10.105.207.125,1433as the server. It successfully connects to the SQL Server. - I modified the connection string to use
10.107.116.21,1433as the server. It also successfully connects to the SQL Server. - I am assuming, the latest configuration is to use the Cluster IP instead of the service's name.
- I am using
Note
Nothing worth noting on this chapter.
What is RabbitMQ
- A message broker - it accepts and forwards messages.
- Messages are sent by Producers (or Publishers).
- Messages are received by Consumers (or Subscribers).
- Messages are stored on Queues (essentially a message buffer).
- This implementation is not part of the full course video.
- Exchanges can be used to add "routing" functionality.
- Uses Advanced Message Queueing Protocol (AMQP) and others.
4 Types of Exchange
- Direct Exchange
- Delivers Messages to Queues based on a routing key.
- Ideal for "direct" or unicast messaging.
- Fanout Exchange (The full course video uses this)
- Delivers Messages to all Queues that are bound to the exchange.
- It ignores the routing key.
- Ideal for broadcast messages.
- Topic Exchange
- Routes Messages to 1 or more Queues based on the routing key (and patterns).
- Used for Multicast messaging.
- Implements various Publisher / Subscriber Patterns.
- Header Exchange
Note
The full course video uses RabbitMQ 3-management version. I am using 4-management version.
Login to RabbitMQ Management page on browser via localhost:15672. This is possible thanks to the Load Balancer.
- The full course video uses RabbitMQ.Client version
6.2.2.- I am using
7.1.0version which is the latest version at the time of making this. - The implementation for
MessageBusClientbecomes different because some, if not all, of the methods are now asynchronous.- I refer to the code for
EmitLog.csby RabbitMQ for the implementation while trying to make it similar to what the full course video does.
- I refer to the code for
- I am using
- The full course video uses
rabbitmq-clusterip-srvas the value forRabbitMQHostfor Production.- I am using the Cluster IP specified on
kubectl get servicese.g.10.108.210.180.
- I am using the Cluster IP specified on
Note
Nothing worth noting on this chapter.
Note
Nothing worth noting on this chapter.
- The full course video uses RabbitMQ.Client version
6.2.2.- I am using
7.1.0version which is the latest version at the time of making this. - The implementation for
MessageBusSubcriberbecomes different because some, if not all, of the methods are now asynchronous.- I refer to the code for
ReceiveLogs.csby RabbitMQ for the implementation while trying to make it similar to what the full course video does.
- I refer to the code for
- I am using
- The full course video uses
rabbitmq-clusterip-srvas the value forRabbitMQHostfor Production.- I am using the Cluster IP specified on
kubectl get servicese.g.10.108.210.180.
- I am using the Cluster IP specified on