├── .dockerignore ├── src ├── GreetingsService │ ├── appsettings.json │ ├── appsettings.Development.json │ ├── GreetingsService.csproj │ └── Program.cs └── NetOnKubernetes.sln ├── .gitignore ├── README.md ├── Dockerfile └── greetings-service-deployment.yaml /.dockerignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ -------------------------------------------------------------------------------- /src/GreetingsService/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Information" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /src/GreetingsService/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Information" 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folder 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # MsTest test results 6 | TestResults 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | .vs/ 16 | 17 | # Visual Studio profiler 18 | *.psess 19 | *.vsp 20 | *.vspx -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # .NET on Kubernetes 2 | 3 | This is a demo repo to demonstrate techniques for building .NET services for Kubernetes 4 | 5 | Using with Docker Desktop with Kubernetes installed: 6 | 7 | 1. Build the container image: 8 | 9 | `docker build -t greetings-service:0.0.1 .` 10 | 11 | 2. Deploy to Kubernetes: 12 | 13 | `kubectl apply -f greetings-service-deployment.yaml` -------------------------------------------------------------------------------- /src/GreetingsService/GreetingsService.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/aspnet:6.0-bullseye-slim AS runtime 2 | WORKDIR /app 3 | 4 | FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim AS sdk 5 | 6 | ARG build_number=0.0.1 7 | 8 | WORKDIR /app 9 | 10 | COPY src/ . 11 | 12 | # restore nuget packages 13 | RUN dotnet restore 14 | 15 | # build 16 | RUN dotnet build --no-restore "-p:Version=${build_number}" 17 | 18 | # test 19 | # RUN dotnet test --no-build GreetingService.Tests/GreetingService.Tests.csproj 20 | 21 | # publish 22 | RUN dotnet publish --no-build -o output 23 | 24 | FROM runtime AS runtime 25 | # ENV ASPNETCORE_URLS=http://+:5432 26 | 27 | WORKDIR /app 28 | COPY --from=sdk /app/output/ ./ 29 | ENTRYPOINT ["./GreetingsService"] -------------------------------------------------------------------------------- /src/NetOnKubernetes.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31919.166 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A1058638-A938-4EC3-810C-1BCB56D697E6}" 7 | ProjectSection(SolutionItems) = preProject 8 | ..\.dockerignore = ..\.dockerignore 9 | ..\.gitignore = ..\.gitignore 10 | ..\Dockerfile = ..\Dockerfile 11 | ..\greetings-service-deployment.yaml = ..\greetings-service-deployment.yaml 12 | ..\README.md = ..\README.md 13 | EndProjectSection 14 | EndProject 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GreetingsService", "GreetingsService\GreetingsService.csproj", "{76B3A9A2-080B-4345-B9C0-D32BAC4F3229}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {76B3A9A2-080B-4345-B9C0-D32BAC4F3229}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {76B3A9A2-080B-4345-B9C0-D32BAC4F3229}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {76B3A9A2-080B-4345-B9C0-D32BAC4F3229}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {76B3A9A2-080B-4345-B9C0-D32BAC4F3229}.Release|Any CPU.Build.0 = Release|Any CPU 27 | EndGlobalSection 28 | GlobalSection(SolutionProperties) = preSolution 29 | HideSolutionNode = FALSE 30 | EndGlobalSection 31 | GlobalSection(ExtensibilityGlobals) = postSolution 32 | SolutionGuid = {EDB63460-174D-4987-856A-44966CF852EC} 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /greetings-service-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: greetings-service 5 | data: 6 | GREETINGS_MESSAGE: "Hello World From Config Map!" 7 | GREETINGs_NUMBER: "1234" 8 | 9 | --- 10 | apiVersion: v1 11 | kind: Service 12 | metadata: 13 | name: greetings-service-service 14 | spec: 15 | selector: 16 | app: greetings-service 17 | ports: 18 | - name: greetings-service-service-port 19 | protocol: TCP 20 | port: 3456 21 | targetPort: 5432 22 | nodePort: 30001 23 | type: LoadBalancer 24 | 25 | --- 26 | apiVersion: apps/v1 27 | kind: Deployment 28 | metadata: 29 | name: greetings-service-deployment 30 | labels: 31 | app: greetings-service 32 | version: v1 33 | annotations: 34 | prometheus.io/port: '9216' 35 | prometheus.io/scrape: 'true' 36 | spec: 37 | replicas: 2 38 | selector: 39 | matchLabels: 40 | app: greetings-service 41 | template: 42 | metadata: 43 | labels: 44 | app: greetings-service 45 | spec: 46 | containers: 47 | - name: greetings-service 48 | image: greetings-service:0.0.5 49 | envFrom: 50 | - configMapRef: 51 | name: greetings-service 52 | resources: 53 | limits: 54 | memory: 200Mi 55 | cpu: 100m 56 | requests: 57 | memory: 200Mi 58 | cpu: 100m 59 | livenessProbe: 60 | httpGet: 61 | path: /live 62 | port: 5432 63 | readinessProbe: 64 | httpGet: 65 | path: /ready 66 | port: 5432 67 | lifecycle: 68 | postStart: 69 | httpGet: 70 | path: /postStart 71 | port: 5432 72 | preStop: 73 | httpGet: 74 | path: /preStop 75 | port: 5432 76 | -------------------------------------------------------------------------------- /src/GreetingsService/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.Logging; 5 | using System.Text.Json; 6 | using Prometheus; 7 | using Microsoft.AspNetCore.Http; 8 | 9 | namespace GreetingsService; 10 | 11 | public static class Program 12 | { 13 | public static void Main() 14 | { 15 | var builder = WebApplication.CreateBuilder(); 16 | builder.Logging.ClearProviders(); 17 | builder.Logging.AddJsonConsole(options => 18 | { 19 | options.IncludeScopes = false; 20 | options.TimestampFormat = "yyyy:MM:dd hh:mm:ss "; 21 | options.JsonWriterOptions = new JsonWriterOptions 22 | { 23 | // sometimes useful to change this to true when testing locally. 24 | // but it needs to be false for Fluent Bit to process log lines correctly 25 | Indented = false 26 | }; 27 | }); 28 | builder.WebHost.ConfigureKestrel(options => 29 | { 30 | options.ListenAnyIP(5432); 31 | }); 32 | 33 | var hasStarted = false; 34 | var app = builder.Build(); 35 | 36 | // configure the Prometheus metrics endpoint, should appear at /metrics 37 | app.UseRouting(); 38 | app.UseEndpoints(endpoints => 39 | { 40 | endpoints.MapMetrics(); 41 | }); 42 | 43 | // liveness probe, return HTTP status code 500 if you want the container to be restarted 44 | app.MapGet("/live", () => Results.Ok()); 45 | // rediness probe, return 200 OK when the application is ready to respond to requests. 46 | // this can turn on and off if necessary, for example if a backend service is not available. 47 | app.MapGet("/ready", () => hasStarted ? Results.Ok() : Results.StatusCode(500)); 48 | 49 | // PostStart and PreStop event hooks. 50 | app.MapGet("/postStart", (ILogger logger) 51 | => logger.LogInformation("PostStart event")); 52 | app.MapGet("/preStop", (ILogger logger) 53 | => logger.LogInformation("PreStop event")); 54 | 55 | app.MapGet("/", (IConfiguration configuration) => 56 | { 57 | var message = configuration["GREETINGS_MESSAGE"] 58 | ?? "Hello World! env var not found."; 59 | return new Greeting(message); 60 | }); 61 | 62 | app.Run(); 63 | 64 | // mark as true when complex startup logic has completed and the service is ready to take requests. 65 | hasStarted = true; 66 | } 67 | } 68 | 69 | public record Greeting(string Message); 70 | 71 | public class GreetingApp { } --------------------------------------------------------------------------------