Simple script for building and publishing .NET Core application to Docker
Build and publish .NET Core application to Docker container or Kubernetes
Recently I wanted to try to spin up a simple .NET Core application on my local cluster which I built in my previous article Manual setup of Kubernetes cluster on a Virtual Machine with kubeadm. The problem I had is that I needed to test applications in this cluster and deploy them as service in order to monitor potential issues behavior.
As deployment of Kubernetes service needs an image to pull from, I decided to use dockerhub to push my test images, but they first needed to be created.
Building the image from the scratch
When building the image it is important that you have a clean environment and have only binaries in the target image which you will use to run the application. For his purpose I picked an approach similar to the one Visaul Studio provides when you select Docker support in the new .NET Core project.
The image building process consists of:
- use microsoft/dotnet SDK image (I am using latest at this time which is 2.1.401 version of .NET Core SDK, list of tags for .NET Core images can be found at https://hub.docker.com/r/microsoft/dotnet/tags/)
- downloading the source of the application to intermediate container (I used my sample service repo from Github https://github.com/dejanstojanovic/dotnetcore-windows-linux-service/tree/single-library)
- publishing the application to a working folder in the intermediate container
- copy publish result binaries to a clean target image
I used a sample project of a simple service that just logs start and stop of the service. Since this code is not in the master branch, that means I need only specific branch so I will pull only the branch I want to test the code from on the K8 cluster.
git clone --single-branch -b single-library https://github.com/dejanstojanovic/dotnetcore-windows-linux-service.git
This line will only clone single-library branch to an intermediate container instance and build the code from that code base. This repository is public but for private repository you can use Github url with supplied credentials to clone the repository
git clone --single-branch -b single-library https://usernam:email@example.com/dejanstojanovic/dotnetcore-windows-linux-service.git
For this repository this is not necessary because it is public. I am also targeting linux-x64 Runtime IDentifier, which will work without any problem with dotnet Core SDK image, but you can target any other Linux distro specific runtime. List of the supported RIDs is https://docs.microsoft.com/en-us/dotnet/core/rid-catalog#linux-rids
FROM microsoft/dotnet:2.1.401-sdk AS build WORKDIR /src RUN git clone --single-branch -b single-library https://github.com/dejanstojanovic/dotnetcore-windows-linux-service.git RUN cd dotnetcore-windows-linux-service RUN dotnet publish "./dotnetcore-windows-linux-service/Sample.Service/Sample.Service.csproj" --configuration=Release --runtime=linux-x64 --output=/app FROM microsoft/dotnet:2.1.401-sdk AS final WORKDIR /app COPY --from=build /app . ENTRYPOINT ["dotnet", "Sample.Service.dll"]
Tagging and pushing to the dockerhub
This Docker file will build you a local image but after every build you will have to tag the image and push it to the docker hub repository of your own in order to pull it when initiating deployment for Kubernetes service. Docker does not cleanup container after they are stopped so after every image build you will have trash left in your machine form the dean intermediate container used for image build.
To do all this I used bash and wrapped the Docker file with it to do this additional work which is:
- tagging the image (in this case I am using the timestamp of the building time to tag the image)
- pushing image to dockerhub repository
#!/bin/bash #set dockerhub account dockeraccount="dejanstojanovic" servicename="sampleservice" #get timestamp for the tag timestamp=$(date +%Y%m%d%H%M%S) #build image sudo docker build -t $servicename:$timestamp . #remove dangling images sudo docker system prune -f #push to dockerhub sudo docker login #sudo docker login -u username -p password sudo docker tag $servicename:$timestamp $dockeraccount/$servicename:$timestamp sudo docker push $dockeraccount/$servicename:$timestamp
Docker is already authenticated on my image build machine so I do not need to authenticate but if you want to automate you can use -u (--username) and -p (--password) params for docker login to authenticate before pushing the image to dockerhub.
Once you have all your values entered you need to add execute permissions to the file with chmod
sudo chmod+x image-build.sh
Now when you execute the script you will have the image uploaded to your dockerhub repository.
Spinning up the image in Kubernetes
Now when you have the image in the docker hub you can spin it up in the Kubernetes cluster. Before you define the deployment and the image repository to create containers from we are going to need the service created in the Kubernetes cluster.
I prefer declarative way of doing this with yaml files, so for the service I will use the pre-defined service yaml configuration
apiVersion: v1 kind: Service metadata: name: sample-service-app-service labels: app: sample-service-app spec: type: NodePort ports: - port: 5100 nodePort: 30001 protocol: TCP selector: app: sample-service-app
To create this service, just tell kubectl to apply this configuration
sudo kubectl apply -f sample-service-app.yml
Now that we have the service we can create the deployment in the cluster where we are going to use our image we built and run it in sample-service-app service
apiVersion: apps/v1 kind: Deployment metadata: name: sample-service-app-deployment spec: replicas: 10 minReadySeconds: 10 strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 maxSurge: 1 template: metadata: labels: app: sample-service-app spec: containers: - name: sample-service-app-pod image: dejanstojanovic/sampleservice:latest ports: - containerPort: 5100 env: - name: ASPNETCORE_ENVIRONMENT value: Production
Service and deployment and bond with same tag sample-service-app. Deployment is add to the K8 cluster same way we added the service to the cluster through kubectl.
Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.