(feature): release

This commit is contained in:
Maksym Sadovnychyy 2024-12-19 21:06:37 +01:00
parent 4997203386
commit deec49f5df
8 changed files with 869 additions and 8 deletions

682
README.md
View File

@ -1,6 +1,686 @@
# Dapr with ASP.NET Core in Docker Compose and Kubernetes deployment test
> Warning: This project is not yet complete to run in Kubernetes. It is still in development.
The aim of this project is to provide a simple reference on how to perform local development ana then deploy a C# Dapr powered microservices application in Kubernetes. This project demonstrates the deployment of a sample application using RabbitMQ for messaging and Redis for state management, leveraging Dapr to simplify the development of distributed applications.
## Project Structure
The project includes the following components:
* RabbitMQ: A message broker used for pub/sub messaging.
* Redis: A key-value store used for state management.
* Publisher App: A C# microservice that publishes messages to RabbitMQ and set Redis shared state.
* Subscriber App: A C# microservice that subscribes to messages from RabbitMQ and read Redis shared state.
## Requirements
To deploy this C# microservices application in Kubernetes with Dapr support, you need the following:
* Kubernetes: A running Kubernetes cluster (version 1.18 or later recommended).
kubectl command-line tool configured to interact with your Kubernetes cluster.
* Configured Storage Class: A configured storage class in your Kubernetes cluster to provision persistent storage for stateful components like Redis.
* Dapr: Dapr CLI installed (version 1.0 or later). Dapr initialized in your Kubernetes cluster.
In case you decide to build images from source code on your own, you also need:
* Container registry: A configured container registry, such as Harbor or Docker Hub.
In case of local development (no Kubernetes required):
* Visual Studio 2022 Community: An integrated development environment (IDE) for developing C# applications.
* .NET 8 SDK: The software development kit for building and running .NET applications.
* Rancher Desktop or Docker Desktop: Tools for running Docker containers on your local machine.
## Deployment
The deployment is done using Kubernetes manifests for each component, including Dapr components for pub/sub and state management.
Sample application can be installed via [Yaml](#yaml-deployment) or [Powershell](#powershell-deployment) deployment
### Yaml deployment
```yaml
# RabbitMQ
apiVersion: apps/v1
kind: Deployment
metadata:
name: rabbitmq
namespace: dapr-test
spec:
replicas: 1
selector:
matchLabels:
app: rabbitmq
template:
metadata:
labels:
app: rabbitmq
spec:
containers:
- name: rabbitmq
image: rabbitmq:3-management
ports:
- containerPort: 5672 # RabbitMQ port
- containerPort: 15672 # RabbitMQ management port
env:
- name: RABBITMQ_DEFAULT_USER
value: admin
- name: RABBITMQ_DEFAULT_PASS
value: password
- name: RABBITMQ_DEFAULT_VHOST
value: "/"
---
apiVersion: v1
kind: Service
metadata:
name: rabbitmq-service
namespace: dapr-test
spec:
selector:
app: rabbitmq
ports:
- name: amqp
protocol: TCP
port: 5672
targetPort: 5672
- name: management
protocol: TCP
port: 15672
targetPort: 15672
---
# Redis
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: dapr-test
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:6.2.6
ports:
- containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: redis-service
namespace: dapr-test
spec:
selector:
app: redis
ports:
- protocol: TCP
port: 6379
targetPort: 6379
---
# Publisher App
apiVersion: apps/v1
kind: Deployment
metadata:
name: publisher
namespace: dapr-test
spec:
replicas: 1
selector:
matchLabels:
app: publisher
template:
metadata:
labels:
app: publisher
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "publisher"
dapr.io/app-port: "5000" # Adjust to your service port
spec:
containers:
- name: publisher
image: cr.maks-it.com/dapr-test/subscriber:latest
ports:
- containerPort: 5000 # Match your internal app port
env:
- name: ASPNETCORE_ENVIRONMENT
value: Development
- name: ASPNETCORE_HTTP_PORTS
value: 5000
---
apiVersion: v1
kind: Service
metadata:
name: publisher-service
namespace: dapr-test
spec:
selector:
app: publisher
ports:
- protocol: TCP
port: 80
targetPort: 5000 # Match the internal port of the publisher service
---
# Subscriber App
apiVersion: apps/v1
kind: Deployment
metadata:
name: subscriber
namespace: dapr-test
spec:
replicas: 1
selector:
matchLabels:
app: subscriber
template:
metadata:
labels:
app: subscriber
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "subscriber"
dapr.io/app-port: "5000" # Adjust to match the subscriber service port
spec:
containers:
- name: subscriber
image: cr.maks-it.com/dapr-test/subscriber:latest
ports:
- containerPort: 5000 # Match the internal port of the subscriber service
env:
- name: ASPNETCORE_ENVIRONMENT
value: Development
- name: ASPNETCORE_HTTP_PORTS
value: 5000
---
# Dapr PubSub
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
namespace: dapr-test
spec:
type: pubsub.rabbitmq
version: v1
metadata:
- name: connectionString
value: "amqp://admin:password@rabbitmq:5672"
- name: durable
value: "false"
- name: deletedWhenUnused
value: "false"
- name: autoAck
value: "true"
- name: reconnectWait
value: "0"
- name: concurrency
value: parallel
---
# Dapr StateStore
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
namespace: dapr-test
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: redis:6379
- name: keyPrefix
value: none
---
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: privatestatestore
namespace: dapr-test
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: redis:6379
---
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: actorsstatestore
namespace: dapr-test
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: redis:6379
- name: actorStateStore
value: "true"
```
### Powershell deployment
```powershell
kubectl create namespace dapr-test `
# RabbitMQ
@{
apiVersion = "apps/v1"
kind = "Deployment"
metadata = @{
name = "rabbitmq"
namespace = "dapr-test"
}
spec = @{
replicas = 1
selector = @{
matchLabels = @{
app = "rabbitmq"
}
}
template = @{
metadata = @{
labels = @{
app = "rabbitmq"
}
}
spec = @{
containers = @(
@{
name = "rabbitmq"
image = "rabbitmq:3-management"
ports = @(
@{
containerPort = 5672 # RabbitMQ port
},
@{
containerPort = 15672 # RabbitMQ management port
}
)
env = @(
@{
name = "RABBITMQ_DEFAULT_USER"
value = "admin"
},
@{
name = "RABBITMQ_DEFAULT_PASS"
value = "password"
},
@{
name = "RABBITMQ_DEFAULT_VHOST"
value = "/"
}
)
}
)
}
}
}
} | ConvertTo-Json -Depth 10 | kubectl apply -f - `
@{
apiVersion = "v1"
kind = "Service"
metadata = @{
name = "rabbitmq-service"
namespace = "dapr-test"
}
spec = @{
selector = @{
app = "rabbitmq"
}
ports = @(
@{
name = "amqp"
protocol = "TCP"
port = 5672
targetPort = 5672
},
@{
name = "management"
protocol = "TCP"
port = 15672
targetPort = 15672
}
)
}
} | ConvertTo-Json -Depth 10 | kubectl apply -f - `
# Redis
@{
apiVersion = "apps/v1"
kind = "Deployment"
metadata = @{
name = "redis"
namespace = "dapr-test"
}
spec = @{
replicas = 1
selector = @{
matchLabels = @{
app = "redis"
}
}
template = @{
metadata = @{
labels = @{
app = "redis"
}
}
spec = @{
containers = @(
@{
name = "redis"
image = "redis:6.2.6"
ports = @(
@{
containerPort = 6379 # Redis port
}
)
}
)
}
}
}
} | ConvertTo-Json -Depth 10 | kubectl apply -f - `
@{
apiVersion = "v1"
kind = "Service"
metadata = @{
name = "redis-service"
namespace = "dapr-test"
}
spec = @{
selector = @{
app = "redis"
}
ports = @(
@{
protocol = "TCP"
port = 6379
targetPort = 6379
}
)
}
} | ConvertTo-Json -Depth 10 | kubectl apply -f - `
# Publisher App
@{
apiVersion = "apps/v1"
kind = "Deployment"
metadata = @{
name = "publisher"
namespace = "dapr-test"
}
spec = @{
replicas = 1
selector = @{
matchLabels = @{
app = "publisher"
}
}
template = @{
metadata = @{
labels = @{
app = "publisher"
}
annotations = @{
"dapr.io/enabled" = "true"
"dapr.io/app-id" = "dapr-test-publisher"
"dapr.io/app-port" = "5000" # Adjust to your service port
}
}
spec = @{
containers = @(
@{
name = "publisher"
image = "cr.maks-it.com/dapr-test/publisher:latest" # Corrected image name
imagePullPolicy = "Always"
ports = @(
@{
containerPort = 5000 # Match your internal app port
}
)
env = @(
@{
name = "ASPNETCORE_HTTP_PORTS"
value = "5000"
},
@{
name = "ASPNETCORE_ENVIRONMENT"
value = "Development"
}
)
}
)
}
}
}
} | ConvertTo-Json -Depth 10 | kubectl apply -f - `
@{
apiVersion = "v1"
kind = "Service"
metadata = @{
name = "publisher-service"
namespace = "dapr-test"
}
spec = @{
selector = @{
app = "publisher"
}
ports = @(
@{
protocol = "TCP"
port = 80
targetPort = 5000 # Match the internal port of the publisher service
}
)
}
} | ConvertTo-Json -Depth 10 | kubectl apply -f - `
# Subscriber App
@{
apiVersion = "apps/v1"
kind = "Deployment"
metadata = @{
name = "subscriber"
namespace = "dapr-test"
}
spec = @{
replicas = 1
selector = @{
matchLabels = @{
app = "subscriber"
}
}
template = @{
metadata = @{
labels = @{
app = "subscriber"
}
annotations = @{
"dapr.io/enabled" = "true"
"dapr.io/app-id" = "dapr-test-subscriber"
"dapr.io/app-port" = "5000" # Adjust to match the subscriber service port
}
}
spec = @{
containers = @(
@{
name = "subscriber"
image = "cr.maks-it.com/dapr-test/subscriber:latest"
imagePullPolicy = "Always"
ports = @(
@{
containerPort = 5000 # Match the internal port of the subscriber service
}
)
env = @(
@{
name = "ASPNETCORE_HTTP_PORTS"
value = "5000"
},
@{
name = "ASPNETCORE_ENVIRONMENT"
value = "Development"
}
)
}
)
}
}
}
} | ConvertTo-Json -Depth 10 | kubectl apply -f - `
# Dapr PubSub
@{
apiVersion = "dapr.io/v1alpha1"
kind = "Component"
metadata = @{
name = "dapr-test-pubsub"
namespace = "dapr-test"
}
spec = @{
type = "pubsub.rabbitmq"
version = "v1"
metadata = @(
@{
name = "connectionString"
value = "amqp://admin:password@rabbitmq-service:5672"
},
@{
name = "durable"
value = "false"
},
@{
name = "deletedWhenUnused"
value = "false"
},
@{
name = "autoAck"
value = "true"
},
@{
name = "reconnectWait"
value = "0"
},
@{
name = "concurrency"
value = "parallel"
}
)
}
} | ConvertTo-Json -Depth 10 | kubectl apply -f - `
# Dapr StateStore
@{
apiVersion = "dapr.io/v1alpha1"
kind = "Component"
metadata = @{
name = "dapr-test-statestore"
namespace = "dapr-test"
}
spec = @{
type = "state.redis"
version = "v1"
metadata = @(
@{
name = "redisHost"
value = "redis-service:6379"
},
@{
name = "keyPrefix"
value = "none"
}
)
}
} | ConvertTo-Json -Depth 10 | kubectl apply -f - `
@{
apiVersion = "dapr.io/v1alpha1"
kind = "Component"
metadata = @{
name = "dapr-test-privatestatestore"
namespace = "dapr-test"
}
spec = @{
type = "state.redis"
version = "v1"
metadata = @(
@{
name = "redisHost"
value = "redis-service:6379"
}
)
}
} | ConvertTo-Json -Depth 10 | kubectl apply -f - `
@{
apiVersion = "dapr.io/v1alpha1"
kind = "Component"
metadata = @{
name = "dapr-test-actorsstatestore"
namespace = "dapr-test"
}
spec = @{
type = "state.redis"
version = "v1"
metadata = @(
@{
name = "redisHost"
value = "redis-service:6379"
},
@{
name = "actorStateStore"
value = "true"
}
)
}
} | ConvertTo-Json -Depth 10 | kubectl apply -f -
```
## Test
* You have to port forward `publisher-service` and go to `/swagger` path in your browser.
* Publish a sample mesessage
* If everything is ok, you will see logs with your published message in subscriber service
## Summary
This project provides a reference for deploying a C# microservices application in Kubernetes with Dapr support. It includes configurations for RabbitMQ and Redis, as well as the publisher and subscriber applications. The Dapr components for pub/sub and state management are also configured to demonstrate how to leverage Dapr for building distributed applications.
## Contribution

View File

@ -4,7 +4,7 @@
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app
EXPOSE 8080
EXPOSE 5000
# This stage is used to build the service project

View File

@ -23,7 +23,7 @@
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"environmentVariables": {
"ASPNETCORE_HTTP_PORTS": "8080"
"ASPNETCORE_HTTP_PORTS": "5000"
},
"publishAllPorts": true,
"useSSL": false

View File

@ -9,10 +9,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MaksIT.Core" Version="1.1.6" />
<PackageReference Include="MaksIT.Core" Version="1.2.8" />
<PackageReference Include="MaksIT.Dapr" Version="1.0.4" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
</ItemGroup>
<ItemGroup>

View File

@ -4,7 +4,7 @@
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app
EXPOSE 8080
EXPOSE 5000
# This stage is used to build the service project

View File

@ -23,7 +23,7 @@
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"environmentVariables": {
"ASPNETCORE_HTTP_PORTS": "8080"
"ASPNETCORE_HTTP_PORTS": "5000"
},
"publishAllPorts": true,
"useSSL": false

View File

@ -9,7 +9,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MaksIT.Core" Version="1.1.6" />
<PackageReference Include="MaksIT.Core" Version="1.2.8" />
<PackageReference Include="MaksIT.Dapr" Version="1.0.4" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
</ItemGroup>

View File

@ -1,3 +1,98 @@
# RabbitMQ
apiVersion: apps/v1
kind: Deployment
metadata:
name: rabbitmq
namespace: dapr-test
spec:
replicas: 1
selector:
matchLabels:
app: rabbitmq
template:
metadata:
labels:
app: rabbitmq
spec:
containers:
- name: rabbitmq
image: rabbitmq:3-management
ports:
- containerPort: 5672 # RabbitMQ port
- containerPort: 15672 # RabbitMQ management port
env:
- name: RABBITMQ_DEFAULT_USER
value: admin
- name: RABBITMQ_DEFAULT_PASS
value: password
- name: RABBITMQ_DEFAULT_VHOST
value: "/"
---
apiVersion: v1
kind: Service
metadata:
name: rabbitmq-service
namespace: dapr-test
spec:
selector:
app: rabbitmq
ports:
- name: amqp
protocol: TCP
port: 5672
targetPort: 5672
- name: management
protocol: TCP
port: 15672
targetPort: 15672
---
# Redis
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: dapr-test
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:6.2.6
ports:
- containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: redis-service
namespace: dapr-test
spec:
selector:
app: redis
ports:
- protocol: TCP
port: 6379
targetPort: 6379
---
# Publisher App
apiVersion: apps/v1
kind: Deployment
metadata:
@ -22,6 +117,11 @@ spec:
image: cr.maks-it.com/dapr-test/subscriber:latest
ports:
- containerPort: 5000 # Match your internal app port
env:
- name: ASPNETCORE_ENVIRONMENT
value: Development
- name: ASPNETCORE_HTTP_PORTS
value: 5000
---
@ -40,6 +140,8 @@ spec:
---
# Subscriber App
apiVersion: apps/v1
kind: Deployment
metadata:
@ -64,3 +166,82 @@ spec:
image: cr.maks-it.com/dapr-test/subscriber:latest
ports:
- containerPort: 5000 # Match the internal port of the subscriber service
env:
- name: ASPNETCORE_ENVIRONMENT
value: Development
- name: ASPNETCORE_HTTP_PORTS
value: 5000
---
# Dapr PubSub
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
namespace: dapr-test
spec:
type: pubsub.rabbitmq
version: v1
metadata:
- name: connectionString
value: "amqp://admin:password@rabbitmq:5672"
- name: durable
value: "false"
- name: deletedWhenUnused
value: "false"
- name: autoAck
value: "true"
- name: reconnectWait
value: "0"
- name: concurrency
value: parallel
---
# Dapr StateStore
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
namespace: dapr-test
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: redis:6379
- name: keyPrefix
value: none
---
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: privatestatestore
namespace: dapr-test
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: redis:6379
---
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: actorsstatestore
namespace: dapr-test
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: redis:6379
- name: actorStateStore
value: "true"