diff --git a/ChangeLog.md b/ChangeLog.md index f54269c..2ed712f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,8 @@ +## 2.1.0 +**Support to deploy on Cloud K8s cluter:** +* Adjust manifests to deploy in a cloud cluster as a cloud native solution +* Deployed, ran and tested on Google Kubernetes Engine (GKE) + ## 2.0.4 **Health checks implementation & ConfigMap set up:** * Add health checks to all microservices diff --git a/ExternalSystem/PaymentService/.config/dotnet-tools.json b/ExternalSystem/PaymentService/.config/dotnet-tools.json new file mode 100644 index 0000000..56e0950 --- /dev/null +++ b/ExternalSystem/PaymentService/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "3.1.2", + "commands": [ + "dotnet-ef" + ] + } + } +} \ No newline at end of file diff --git a/ExternalSystem/PaymentService/PaymentService.csproj b/ExternalSystem/PaymentService/PaymentService.csproj index 0cdb35b..094cb5a 100644 --- a/ExternalSystem/PaymentService/PaymentService.csproj +++ b/ExternalSystem/PaymentService/PaymentService.csproj @@ -6,6 +6,7 @@ + diff --git a/ExternalSystem/PaymentService/Properties/PublishProfiles/DuberPaymentExternalService - Web Deploy.pubxml b/ExternalSystem/PaymentService/Properties/PublishProfiles/PaymentServiceExternal - Web Deploy.pubxml similarity index 61% rename from ExternalSystem/PaymentService/Properties/PublishProfiles/DuberPaymentExternalService - Web Deploy.pubxml rename to ExternalSystem/PaymentService/Properties/PublishProfiles/PaymentServiceExternal - Web Deploy.pubxml index 2273e80..0b0fe35 100644 --- a/ExternalSystem/PaymentService/Properties/PublishProfiles/DuberPaymentExternalService - Web Deploy.pubxml +++ b/ExternalSystem/PaymentService/Properties/PublishProfiles/PaymentServiceExternal - Web Deploy.pubxml @@ -6,23 +6,26 @@ by editing this MSBuild file. In order to learn more about this please visit htt MSDeploy - /subscriptions/508e9034-6149-4cab-aded-3e97f650ae2a/resourcegroups/Default/providers/Microsoft.Web/sites/DuberPaymentExternalService - Default + /subscriptions/b9fa199b-d651-4969-a504-266371834927/resourcegroups/duber/providers/Microsoft.Web/sites/PaymentServiceExternal + duber AzureWebSite Release Any CPU - http://duberpaymentexternalservice.azurewebsites.net + https://paymentserviceexternal.azurewebsites.net True False + netcoreapp3.1 8504d9b8-c4e8-4172-8da3-cbd9971e3207 - duberpaymentexternalservice.scm.azurewebsites.net:443 - DuberPaymentExternalService + false + paymentserviceexternal.scm.azurewebsites.net:443 + PaymentServiceExternal True WMSVC True - $DuberPaymentExternalService + $PaymentServiceExternal <_SavePWD>True <_DestinationType>AzureWebSite + False \ No newline at end of file diff --git a/deploy/k8s/gke/default-ingress.yaml b/deploy/k8s/gke/default-ingress.yaml new file mode 100644 index 0000000..f033740 --- /dev/null +++ b/deploy/k8s/gke/default-ingress.yaml @@ -0,0 +1,23 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: default-ingress + annotations: + kubernetes.io/ingress.class: "nginx" + nginx.ingress.kubernetes.io/rewrite-target: /$1 +spec: + rules: + - http: + paths: + - backend: + serviceName: trip + servicePort: 80 + path: /services/trip/(.*) + - backend: + serviceName: invoice + servicePort: 80 + path: /services/invoice/(.*) + - backend: + serviceName: frontend + servicePort: 80 + path: /(.*) \ No newline at end of file diff --git a/deploy/k8s/gke/delete-resources.sh b/deploy/k8s/gke/delete-resources.sh new file mode 100644 index 0000000..9ff547b --- /dev/null +++ b/deploy/k8s/gke/delete-resources.sh @@ -0,0 +1,26 @@ +# configmap +kubectl delete cm env-config + +# common ingress +kubectl delete ing default-ingress + +# invoice +kubectl delete deploy invoice +kubectl delete svc invoice +kubectl delete hpa invoice + +# trip +kubectl delete deploy trip +kubectl delete hpa trip +kubectl delete svc trip + +# website +kubectl delete deploy frontend +kubectl delete hpa frontend +kubectl delete svc frontend + +#notificatiosn +kubectl delete deploy notifications +kubectl delete hpa notifications +kubectl delete ing notifications +kubectl delete svc notifications \ No newline at end of file diff --git a/deploy/k8s/gke/deploy.sh b/deploy/k8s/gke/deploy.sh new file mode 100644 index 0000000..befcf81 --- /dev/null +++ b/deploy/k8s/gke/deploy.sh @@ -0,0 +1,26 @@ +# configmap +kubectl apply -f env-config.yaml + +# common ingress +kubectl apply -f default-ingress.yaml + +# invoice +kubectl apply -f invoice\invoice-deployment.yaml +kubectl apply -f invoice\invoice-service.yaml +kubectl apply -f invoice\invoice-hpa.yaml + +# trip +kubectl apply -f trip\trip-deployment.yaml +kubectl apply -f trip\trip-hpa.yaml +kubectl apply -f trip\trip-service.yaml + +# website +kubectl apply -f website\website-deployment.yaml +kubectl apply -f website\website-hpa.yaml +kubectl apply -f website\website-service.yaml + +#notificatiosn +kubectl apply -f notifications\notifications-deployment.yaml +kubectl apply -f notifications\notifications-hpa.yaml +kubectl apply -f notifications\notifications-ingress.yaml +kubectl apply -f notifications\notifications-service.yaml \ No newline at end of file diff --git a/deploy/k8s/gke/env-config.yaml b/deploy/k8s/gke/env-config.yaml new file mode 100644 index 0000000..9410cf1 --- /dev/null +++ b/deploy/k8s/gke/env-config.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: env-config +data: + ASPNETCORE_ENVIRONMENT: Production + ConnectionStrings__InvoiceDB: {your invoice db connection string} + ConnectionStrings__WebsiteDB: {your website db connection string} + ConnectionStrings__SignalrBackPlane: {your redis connection string} + EventStoreConfiguration__ConnectionString: {your mongo db connection string} + EventBusConnection: {your service bus connection string} + EventBusUserName: {your rabbitmq user} + EventBusPassword: {your rabbitmq password} + PaymentServiceBaseUrl: {your payment service url} + InvoiceApiSettings__BaseUrl: http://invoice + TripApiSettings__BaseUrl: http://trip + TripApiSettings__NotificationsClientUrl: http://{your load balancer ip}/notifications + TripApiSettings__NotificationsServerUrl: http://notifications + AzureServiceBusEnabled: "false" + IsDeployedOnCluster: "true" \ No newline at end of file diff --git a/deploy/k8s/gke/invoice/invoice-deployment-with-proxy.yaml b/deploy/k8s/gke/invoice/invoice-deployment-with-proxy.yaml new file mode 100644 index 0000000..bcfce2d --- /dev/null +++ b/deploy/k8s/gke/invoice/invoice-deployment-with-proxy.yaml @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: invoice +spec: + selector: + matchLabels: + app: invoice + replicas: 1 + template: + metadata: + labels: + app: invoice + spec: + containers: + - name: invoice + image: vany0114/duber.invoice.api + imagePullPolicy: Always + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "256Mi" + cpu: "500m" + envFrom: + - configMapRef: + name: env-config + env: + - name: ReverseProxyPrefix + value: "services/invoice" + livenessProbe: + httpGet: + port: 80 + path: /liveness + initialDelaySeconds: 10 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /readiness + port: 80 + initialDelaySeconds: 30 + periodSeconds: 60 + timeoutSeconds: 5 + ports: + - containerPort: 80 + - name: cloudsql-proxy + image: gcr.io/cloudsql-docker/gce-proxy:1.16 + command: ["/cloud_sql_proxy", + "-instances==tcp:3306", + "-credential_file=/secrets/cloudsql/creadentials.json"] + securityContext: + runAsUser: 2 # non-root user + allowPrivilegeEscalation: false + volumeMounts: + - name: cloudsql-instance-credentials + mountPath: /secrets/cloudsql + readOnly: true + volumes: + - name: cloudsql-instance-credentials + secret: + secretName: cloudsql-instance-credentials \ No newline at end of file diff --git a/deploy/k8s/gke/invoice/invoice-deployment.yaml b/deploy/k8s/gke/invoice/invoice-deployment.yaml new file mode 100644 index 0000000..06138c0 --- /dev/null +++ b/deploy/k8s/gke/invoice/invoice-deployment.yaml @@ -0,0 +1,46 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: invoice +spec: + selector: + matchLabels: + app: invoice + replicas: 1 + template: + metadata: + labels: + app: invoice + spec: + containers: + - name: invoice + image: vany0114/duber.invoice.api + imagePullPolicy: Always + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "256Mi" + cpu: "500m" + envFrom: + - configMapRef: + name: env-config + env: + - name: ReverseProxyPrefix + value: "services/invoice" + livenessProbe: + httpGet: + port: 80 + path: /liveness + initialDelaySeconds: 10 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /readiness + port: 80 + initialDelaySeconds: 30 + periodSeconds: 60 + timeoutSeconds: 5 + ports: + - containerPort: 80 \ No newline at end of file diff --git a/deploy/k8s/gke/invoice/invoice-hpa.yaml b/deploy/k8s/gke/invoice/invoice-hpa.yaml new file mode 100644 index 0000000..99d7ea3 --- /dev/null +++ b/deploy/k8s/gke/invoice/invoice-hpa.yaml @@ -0,0 +1,12 @@ +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: invoice +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: invoice + minReplicas: 2 + maxReplicas: 4 + targetCPUUtilizationPercentage: 50 \ No newline at end of file diff --git a/deploy/k8s/gke/invoice/invoice-service.yaml b/deploy/k8s/gke/invoice/invoice-service.yaml new file mode 100644 index 0000000..4c41e3b --- /dev/null +++ b/deploy/k8s/gke/invoice/invoice-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: invoice +spec: + selector: + app: invoice + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 80 \ No newline at end of file diff --git a/deploy/k8s/gke/notifications/notifications-deployment.yaml b/deploy/k8s/gke/notifications/notifications-deployment.yaml new file mode 100644 index 0000000..817de15 --- /dev/null +++ b/deploy/k8s/gke/notifications/notifications-deployment.yaml @@ -0,0 +1,43 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: notifications +spec: + selector: + matchLabels: + app: notifications + replicas: 1 + template: + metadata: + labels: + app: notifications + spec: + containers: + - name: notifications + image: vany0114/duber.trip.notifications + imagePullPolicy: Always + resources: + requests: + memory: "128Mi" + cpu: "200m" + limits: + memory: "256Mi" + cpu: "600m" + envFrom: + - configMapRef: + name: env-config + livenessProbe: + httpGet: + port: 80 + path: /liveness + initialDelaySeconds: 10 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /readiness + port: 80 + initialDelaySeconds: 30 + periodSeconds: 60 + timeoutSeconds: 5 + ports: + - containerPort: 80 \ No newline at end of file diff --git a/deploy/k8s/gke/notifications/notifications-hpa.yaml b/deploy/k8s/gke/notifications/notifications-hpa.yaml new file mode 100644 index 0000000..70cc79a --- /dev/null +++ b/deploy/k8s/gke/notifications/notifications-hpa.yaml @@ -0,0 +1,12 @@ +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: notifications +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: notifications + minReplicas: 4 + maxReplicas: 5 + targetCPUUtilizationPercentage: 50 \ No newline at end of file diff --git a/deploy/k8s/gke/notifications/notifications-ingress.yaml b/deploy/k8s/gke/notifications/notifications-ingress.yaml new file mode 100644 index 0000000..b4a0732 --- /dev/null +++ b/deploy/k8s/gke/notifications/notifications-ingress.yaml @@ -0,0 +1,17 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: notifications + annotations: + kubernetes.io/ingress.class: "nginx" + nginx.ingress.kubernetes.io/affinity: cookie + nginx.ingress.kubernetes.io/session-cookie-path: /notifications/ + nginx.ingress.kubernetes.io/rewrite-target: /$1 +spec: + rules: + - http: + paths: + - backend: + serviceName: notifications + servicePort: 80 + path: /notifications/(.*) \ No newline at end of file diff --git a/deploy/k8s/gke/notifications/notifications-service.yaml b/deploy/k8s/gke/notifications/notifications-service.yaml new file mode 100644 index 0000000..94b1213 --- /dev/null +++ b/deploy/k8s/gke/notifications/notifications-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: notifications +spec: + selector: + app: notifications + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 80 \ No newline at end of file diff --git a/deploy/k8s/gke/trip/trip-deployment.yaml b/deploy/k8s/gke/trip/trip-deployment.yaml new file mode 100644 index 0000000..74e94dc --- /dev/null +++ b/deploy/k8s/gke/trip/trip-deployment.yaml @@ -0,0 +1,46 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: trip +spec: + selector: + matchLabels: + app: trip + replicas: 1 + template: + metadata: + labels: + app: trip + spec: + containers: + - name: trip + image: vany0114/duber.trip.api + imagePullPolicy: Always + resources: + requests: + memory: "128Mi" + cpu: "200m" + limits: + memory: "256Mi" + cpu: "600m" + envFrom: + - configMapRef: + name: env-config + env: + - name: ReverseProxyPrefix + value: "services/trip" + livenessProbe: + httpGet: + port: 80 + path: /liveness + initialDelaySeconds: 10 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /readiness + port: 80 + initialDelaySeconds: 30 + periodSeconds: 60 + timeoutSeconds: 5 + ports: + - containerPort: 80 \ No newline at end of file diff --git a/deploy/k8s/gke/trip/trip-hpa.yaml b/deploy/k8s/gke/trip/trip-hpa.yaml new file mode 100644 index 0000000..7a7703b --- /dev/null +++ b/deploy/k8s/gke/trip/trip-hpa.yaml @@ -0,0 +1,12 @@ +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: trip +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: trip + minReplicas: 2 + maxReplicas: 4 + targetCPUUtilizationPercentage: 50 \ No newline at end of file diff --git a/deploy/k8s/gke/trip/trip-service.yaml b/deploy/k8s/gke/trip/trip-service.yaml new file mode 100644 index 0000000..44960b8 --- /dev/null +++ b/deploy/k8s/gke/trip/trip-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: trip +spec: + selector: + app: trip + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 80 \ No newline at end of file diff --git a/deploy/k8s/gke/website/website-deployment-with-proxy.yaml b/deploy/k8s/gke/website/website-deployment-with-proxy.yaml new file mode 100644 index 0000000..02f732e --- /dev/null +++ b/deploy/k8s/gke/website/website-deployment-with-proxy.yaml @@ -0,0 +1,76 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend +spec: + selector: + matchLabels: + app: frontend + replicas: 1 + template: + metadata: + labels: + app: frontend + spec: + containers: + - name: frontend + image: vany0114/duber.website + imagePullPolicy: Always + resources: + requests: + memory: "128Mi" + cpu: "200m" + limits: + memory: "256Mi" + cpu: "600m" + envFrom: + - configMapRef: + name: env-config + env: + - name: HealthChecksUI__HealthChecks__0__Name + value: "Invoice HTTP Check" + - name: HealthChecksUI__HealthChecks__0__Uri + value: $(InvoiceApiSettings__BaseUrl)/readiness + - name: HealthChecksUI__HealthChecks__1__Name + value: "Trip HTTP Check" + - name: HealthChecksUI__HealthChecks__1__Uri + value: $(TripApiSettings__BaseUrl)/readiness + - name: HealthChecksUI__HealthChecks__2__Name + value: "Notifications HTTP Check" + - name: HealthChecksUI__HealthChecks__2__Uri + value: $(TripApiSettings__NotificationsServerUrl)/readiness + - name: HealthChecksUI__HealthChecks__3__Name + value: "Frontend HTTP Check" + - name: HealthChecksUI__HealthChecks__3__Uri + value: "http://frontend/readiness" + livenessProbe: + httpGet: + port: 80 + path: /liveness + initialDelaySeconds: 10 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /readiness + port: 80 + initialDelaySeconds: 60 + periodSeconds: 60 + timeoutSeconds: 5 + ports: + - containerPort: 80 + - name: cloudsql-proxy + image: gcr.io/cloudsql-docker/gce-proxy:1.16 + command: ["/cloud_sql_proxy", + "-instances==tcp:3306", + "-credential_file=/secrets/cloudsql/creadentials.json"] + securityContext: + runAsUser: 2 # non-root user + allowPrivilegeEscalation: false + volumeMounts: + - name: cloudsql-instance-credentials + mountPath: /secrets/cloudsql + readOnly: true + volumes: + - name: cloudsql-instance-credentials + secret: + secretName: cloudsql-instance-credentials \ No newline at end of file diff --git a/deploy/k8s/gke/website/website-deployment.yaml b/deploy/k8s/gke/website/website-deployment.yaml new file mode 100644 index 0000000..3327b74 --- /dev/null +++ b/deploy/k8s/gke/website/website-deployment.yaml @@ -0,0 +1,60 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend +spec: + selector: + matchLabels: + app: frontend + replicas: 1 + template: + metadata: + labels: + app: frontend + spec: + containers: + - name: frontend + image: vany0114/duber.website + imagePullPolicy: Always + resources: + requests: + memory: "128Mi" + cpu: "200m" + limits: + memory: "256Mi" + cpu: "600m" + envFrom: + - configMapRef: + name: env-config + env: + - name: HealthChecksUI__HealthChecks__0__Name + value: "Invoice HTTP Check" + - name: HealthChecksUI__HealthChecks__0__Uri + value: $(InvoiceApiSettings__BaseUrl)/readiness + - name: HealthChecksUI__HealthChecks__1__Name + value: "Trip HTTP Check" + - name: HealthChecksUI__HealthChecks__1__Uri + value: $(TripApiSettings__BaseUrl)/readiness + - name: HealthChecksUI__HealthChecks__2__Name + value: "Notifications HTTP Check" + - name: HealthChecksUI__HealthChecks__2__Uri + value: $(TripApiSettings__NotificationsServerUrl)/readiness + - name: HealthChecksUI__HealthChecks__3__Name + value: "Frontend HTTP Check" + - name: HealthChecksUI__HealthChecks__3__Uri + value: "http://frontend/readiness" + livenessProbe: + httpGet: + port: 80 + path: /liveness + initialDelaySeconds: 10 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /readiness + port: 80 + initialDelaySeconds: 60 + periodSeconds: 60 + timeoutSeconds: 5 + ports: + - containerPort: 80 \ No newline at end of file diff --git a/deploy/k8s/gke/website/website-hpa.yaml b/deploy/k8s/gke/website/website-hpa.yaml new file mode 100644 index 0000000..999fdab --- /dev/null +++ b/deploy/k8s/gke/website/website-hpa.yaml @@ -0,0 +1,12 @@ +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: frontend +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: frontend + minReplicas: 2 + maxReplicas: 4 + targetCPUUtilizationPercentage: 50 \ No newline at end of file diff --git a/deploy/k8s/gke/website/website-service.yaml b/deploy/k8s/gke/website/website-service.yaml new file mode 100644 index 0000000..9f7074c --- /dev/null +++ b/deploy/k8s/gke/website/website-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: frontend +spec: + selector: + app: frontend + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 80 \ No newline at end of file diff --git a/deploy/k8s/local/notifications/notifications-hpa.yaml b/deploy/k8s/local/notifications/notifications-hpa.yaml index 833e9dd..70cc79a 100644 --- a/deploy/k8s/local/notifications/notifications-hpa.yaml +++ b/deploy/k8s/local/notifications/notifications-hpa.yaml @@ -8,5 +8,5 @@ spec: kind: Deployment name: notifications minReplicas: 4 - maxReplicas: 10 + maxReplicas: 5 targetCPUUtilizationPercentage: 50 \ No newline at end of file diff --git a/deploy/k8s/local/trip/trip-hpa.yaml b/deploy/k8s/local/trip/trip-hpa.yaml index 492807b..7a7703b 100644 --- a/deploy/k8s/local/trip/trip-hpa.yaml +++ b/deploy/k8s/local/trip/trip-hpa.yaml @@ -7,6 +7,6 @@ spec: apiVersion: apps/v1 kind: Deployment name: trip - minReplicas: 3 - maxReplicas: 7 + minReplicas: 2 + maxReplicas: 4 targetCPUUtilizationPercentage: 50 \ No newline at end of file diff --git a/deploy/k8s/local/website/website-hpa.yaml b/deploy/k8s/local/website/website-hpa.yaml index f5cdef0..999fdab 100644 --- a/deploy/k8s/local/website/website-hpa.yaml +++ b/deploy/k8s/local/website/website-hpa.yaml @@ -7,6 +7,6 @@ spec: apiVersion: apps/v1 kind: Deployment name: frontend - minReplicas: 3 - maxReplicas: 5 + minReplicas: 2 + maxReplicas: 4 targetCPUUtilizationPercentage: 50 \ No newline at end of file diff --git a/deploy/redis/readme.md b/deploy/redis/readme.md new file mode 100644 index 0000000..61be1f6 --- /dev/null +++ b/deploy/redis/readme.md @@ -0,0 +1,31 @@ +# Deploying Redis Cache + +The ARM template `redisdeploy.json` and its parameter file (`redisdeploy.parameters.json`) are used to deploy following resources: + +1. One Redis Cache + +## Editing sbusdeploy.parameters.json file + +You can edit the `redisdeploy.parameters.parameters.json` file to set your values, but is not needed. The only parameter than can +be set is: + +1. `namespaceprefix` is a string that is used to create the Redis namespace. ARM script creates unique values by appending a unique string to this parameter value, so you can leave the default value. + +## Deploy the template + +Once parameter file is edited you can deploy it using [create-resources script](../readme.md). + +i. e. if you are in windows, to deploy a Redis cache in a new Azure Resource Group located in westus, go to `deploy\az` folder and type: + +``` +create-resources.cmd redis\redisdeploy newResourceGroup -c westus +``` + + + + + + + + + diff --git a/deploy/redis/redisdeploy.json b/deploy/redis/redisdeploy.json new file mode 100644 index 0000000..04b53a3 --- /dev/null +++ b/deploy/redis/redisdeploy.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "namespaceprefix": { + "type": "string", + "metadata": { + "description": "Name of the Redis namespace" + } + } + }, + "variables": { + "location": "[resourceGroup().location]", + "namespaceprefix": "[concat(parameters('namespaceprefix'), uniqueString(resourceGroup().id))]", + "sbVersion": "2016-04-01" + }, + "resources": [ + { + "type": "Microsoft.Cache/Redis", + "name": "[variables('namespaceprefix')]", + "apiVersion": "[variables('sbVersion')]", + "location": "[variables('location')]", + "scale": null, + "properties": { + "redisVersion": "3.2.7", + "sku": { + "name": "Standard", + "family": "C", + "capacity": 1 + }, + "enableNonSslPort": true, + "redisConfiguration": { + "maxclients": "1000", + "maxmemory-reserved": "50", + "maxfragmentationmemory-reserved": "50", + "maxmemory-policy": "volatile-lru", + "maxmemory-delta": "50" + } + } + } + ] +} \ No newline at end of file diff --git a/deploy/redis/redisdeploy.parameters.json b/deploy/redis/redisdeploy.parameters.json new file mode 100644 index 0000000..da66878 --- /dev/null +++ b/deploy/redis/redisdeploy.parameters.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "namespaceprefix": { + "value": "duber-redis" + } + } +} \ No newline at end of file diff --git a/deploy/servicebus/sbusdeploy.json b/deploy/servicebus/sbusdeploy.json index f3d8844..d4e4baf 100644 --- a/deploy/servicebus/sbusdeploy.json +++ b/deploy/servicebus/sbusdeploy.json @@ -13,7 +13,8 @@ "serviceBusTopicName": "duber_event_bus", "TripSubscriptionName": "Trip", "InvoiceSubscriptionName": "Invoice", - "WebSiteSubscriptionName": "WebSite", + "WebSiteSubscriptionName": "WebSite", + "NotificationsSubscriptionName": "Notifications", "location": "[resourceGroup().location]", "sbVersion": "2015-08-01", "defaultSASKeyName": "Root", @@ -130,6 +131,26 @@ "autoDeleteOnIdle": "10675199.02:48:05.4775807", "entityAvailabilityStatus": "Available" } + }, + { + "apiVersion": "[variables('sbVersion')]", + "name": "[variables('NotificationsSubscriptionName')]", + "type": "Subscriptions", + "dependsOn": [ + "[variables('serviceBusTopicName')]" + ], + "properties": { + "lockDuration": "00:00:30", + "requiresSession": false, + "defaultMessageTimeToLive": "14.00:00:00", + "deadLetteringOnMessageExpiration": true, + "deadLetteringOnFilterEvaluationExceptions": true, + "maxDeliveryCount": 10, + "enableBatchedOperations": false, + "status": "Active", + "autoDeleteOnIdle": "10675199.02:48:05.4775807", + "entityAvailabilityStatus": "Available" + } } ] } diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 39e0327..5b0c186 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -6,6 +6,7 @@ services: - ASPNETCORE_ENVIRONMENT=${APP_ENVIRONMENT:-Development} - ConnectionStrings__InvoiceDB=${AZURE_INVOICE_DB:-Server=sql.data;Database=Duber.InvoiceDb;User Id=sa;Password=Pass@word} - EventBusConnection=${AZURE_SERVICE_BUS:-rabbitmq} + - EventBusConnectionHC=${AZURE_SERVICE_BUS:-rabbitmq} - PaymentServiceBaseUrl=${PAYMENT_SERVICE_URL:-http://externalsystem.payment} - AzureServiceBusEnabled=${SERVICE_BUS_ENABLED:-False} ports: @@ -16,6 +17,7 @@ services: - ASPNETCORE_ENVIRONMENT=${APP_ENVIRONMENT:-Development} - EventStoreConfiguration__ConnectionString=${AZURE_TRIP_DB:-mongodb://nosql.data} - EventBusConnection=${AZURE_SERVICE_BUS:-rabbitmq} + - EventBusConnectionHC=${AZURE_SERVICE_BUS:-rabbitmq} - AzureServiceBusEnabled=${SERVICE_BUS_ENABLED:-False} ports: - "32775:80" @@ -25,6 +27,7 @@ services: - ASPNETCORE_ENVIRONMENT=${APP_ENVIRONMENT:-Development} - ConnectionStrings__WebsiteDB=${AZURE_WEBSITE_DB:-Server=sql.data;Database=Duber.WebSiteDb;User Id=sa;Password=Pass@word} - EventBusConnection=${AZURE_SERVICE_BUS:-rabbitmq} + - EventBusConnectionHC=${AZURE_SERVICE_BUS:-rabbitmq} - InvoiceApiSettings__BaseUrl=${INVOICE_SERVICE_BASE_URL:-http://duber.invoice.api} - TripApiSettings__BaseUrl=${TRIP_SERVICE_BASE_URL:-http://duber.trip.api} - TripApiSettings__NotificationsServerUrl=${NOTIFICATIONS_SERVER_URL:-http://duber.trip.notifications} @@ -64,6 +67,7 @@ services: - ASPNETCORE_ENVIRONMENT=${APP_ENVIRONMENT:-Development} - ConnectionStrings__SignalrBackPlane=${REDIS_DB:-} - EventBusConnection=${AZURE_SERVICE_BUS:-rabbitmq} + - EventBusConnectionHC=${AZURE_SERVICE_BUS:-rabbitmq} - AzureServiceBusEnabled=${SERVICE_BUS_ENABLED:-False} ports: - "32778:80" \ No newline at end of file diff --git a/src/Application/Duber.Invoice.API/Startup.cs b/src/Application/Duber.Invoice.API/Startup.cs index c286233..af0fd94 100644 --- a/src/Application/Duber.Invoice.API/Startup.cs +++ b/src/Application/Duber.Invoice.API/Startup.cs @@ -1,10 +1,8 @@ using System; +using System.Collections.Generic; using Autofac; using Autofac.Extensions.DependencyInjection; using AutoMapper; -using Duber.Infrastructure.EventBus.Abstractions; -using Duber.Invoice.API.Application.IntegrationEvents.Events; -using Duber.Invoice.API.Application.IntegrationEvents.Hnadlers; using Duber.Invoice.API.Application.Validations; using Duber.Invoice.API.Extensions; using Duber.Invoice.API.Infrastructure.AutofacModules; @@ -17,6 +15,8 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; + // ReSharper disable InconsistentNaming // ReSharper disable AssignNullToNotNullAttribute #pragma warning disable 618 @@ -97,10 +97,20 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF }); }); - app.UseSwagger() + app.UseSwagger(x => + { + var reverseProxyPrefix = Configuration.GetValue("ReverseProxyPrefix"); + if (!string.IsNullOrEmpty(reverseProxyPrefix)) + { + x.PreSerializeFilters.Add((swaggerDoc, httpReq) => swaggerDoc.Servers = new List + { + new OpenApiServer { Url = $"{httpReq.Scheme}://{httpReq.Host.Value}/{reverseProxyPrefix}" } + }); + } + }) .UseSwaggerUI(c => { - c.SwaggerEndpoint("/swagger/v1/swagger.json", "Duber.Invoice V1"); + c.SwaggerEndpoint("./swagger/v1/swagger.json", "Duber.Invoice V1"); c.RoutePrefix = string.Empty; }); } diff --git a/src/Application/Duber.Invoice.API/appsettings.json b/src/Application/Duber.Invoice.API/appsettings.json index c39420b..be6d776 100644 --- a/src/Application/Duber.Invoice.API/appsettings.json +++ b/src/Application/Duber.Invoice.API/appsettings.json @@ -14,6 +14,7 @@ }, "AzureServiceBusEnabled": false, "EventBusConnection": "", + "EventBusConnectionHC": "", "SubscriptionClientName": "Invoice", "EventBusRetryCount": 5, "PaymentServiceBaseUrl": "http://localhost:32777", @@ -23,5 +24,6 @@ "SqlClientExceptionsAllowedBeforeBreaking": 4, "ConnectionStrings": { "InvoiceDB": "Server=tcp:127.0.0.1,5433;Initial Catalog=Duber.InvoiceDb;User Id=sa;Password=Pass@word" - } + }, + "ReverseProxyPrefix": "" } diff --git a/src/Application/Duber.Trip.API/Extensions/ServiceCollectionExtensions.cs b/src/Application/Duber.Trip.API/Extensions/ServiceCollectionExtensions.cs index 246c27f..d024042 100644 --- a/src/Application/Duber.Trip.API/Extensions/ServiceCollectionExtensions.cs +++ b/src/Application/Duber.Trip.API/Extensions/ServiceCollectionExtensions.cs @@ -150,6 +150,7 @@ public static IServiceCollection AddHealthChecks(this IServiceCollection service hcBuilder .AddMongoDb( configuration["EventStoreConfiguration:ConnectionString"], + mongoDatabaseName: string.Empty, name: "TripDB-check", tags: new string[] { "tripdb" }); diff --git a/src/Application/Duber.Trip.API/Startup.cs b/src/Application/Duber.Trip.API/Startup.cs index fd2cfb4..48532a4 100644 --- a/src/Application/Duber.Trip.API/Startup.cs +++ b/src/Application/Duber.Trip.API/Startup.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Autofac; using Autofac.Extensions.DependencyInjection; using Duber.Trip.API.Application.Validations; @@ -13,6 +14,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; // ReSharper disable InconsistentNaming @@ -86,10 +88,20 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF }); }); - app.UseSwagger() + app.UseSwagger(x => + { + var reverseProxyPrefix = Configuration.GetValue("ReverseProxyPrefix"); + if (!string.IsNullOrEmpty(reverseProxyPrefix)) + { + x.PreSerializeFilters.Add((swaggerDoc, httpReq) => swaggerDoc.Servers = new List + { + new OpenApiServer { Url = $"{httpReq.Scheme}://{httpReq.Host.Value}/{reverseProxyPrefix}" } + }); + } + }) .UseSwaggerUI(c => { - c.SwaggerEndpoint("/swagger/v1/swagger.json", "Duber.Trip V1"); + c.SwaggerEndpoint("./swagger/v1/swagger.json", "Duber.Trip V1"); c.RoutePrefix = string.Empty; }); } diff --git a/src/Application/Duber.Trip.API/appsettings.json b/src/Application/Duber.Trip.API/appsettings.json index 0ae0e43..b88623d 100644 --- a/src/Application/Duber.Trip.API/appsettings.json +++ b/src/Application/Duber.Trip.API/appsettings.json @@ -14,13 +14,15 @@ }, "AzureServiceBusEnabled": false, "EventBusConnection": "", + "EventBusConnectionHC": "", "SubscriptionClientName": "Trip", "EventStoreConfiguration": { "ConnectionString": "mongodb://nosql.data", "DatabaseName": "EventStore", "AggregateCollectionName": "Aggregates", "EventCollectionName": "Events", - "CommandCollectionName": "Commands" + "CommandCollectionName": "Commands" }, - "EventBusRetryCount": 5 + "EventBusRetryCount": 5, + "ReverseProxyPrefix": "" } diff --git a/src/Application/Duber.Trip.Notifications/appsettings.json b/src/Application/Duber.Trip.Notifications/appsettings.json index 8550a68..c4e16a7 100644 --- a/src/Application/Duber.Trip.Notifications/appsettings.json +++ b/src/Application/Duber.Trip.Notifications/appsettings.json @@ -1,9 +1,15 @@ { "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Warning" + } + }, + "Console": { + "LogLevel": { + "Default": "Information" + } } }, "AllowedHosts": "*", @@ -12,6 +18,7 @@ }, "AzureServiceBusEnabled": false, "EventBusConnection": "", + "EventBusConnectionHC": "", "SubscriptionClientName": "Notifications", "EventBusRetryCount": 5, "IsDeployedOnCluster": false diff --git a/src/Infrastructure/EventBus/Duber.Infrastructure.EventBus.RabbitMQ/IoC/ServiceCollectionExtensions.cs b/src/Infrastructure/EventBus/Duber.Infrastructure.EventBus.RabbitMQ/IoC/ServiceCollectionExtensions.cs index fad5b4a..effa72f 100644 --- a/src/Infrastructure/EventBus/Duber.Infrastructure.EventBus.RabbitMQ/IoC/ServiceCollectionExtensions.cs +++ b/src/Infrastructure/EventBus/Duber.Infrastructure.EventBus.RabbitMQ/IoC/ServiceCollectionExtensions.cs @@ -17,14 +17,28 @@ public static IServiceCollection AddRabbitMQ(this IServiceCollection services, I retryCount = int.Parse(configuration["EventBusRetryCount"]); } + var connectionFactory = new ConnectionFactory() + { + HostName = configuration["EventBusConnection"], + DispatchConsumersAsync = true, + AutomaticRecoveryEnabled = true + }; + + if (!string.IsNullOrEmpty(configuration["EventBusUserName"])) + { + connectionFactory.UserName = configuration["EventBusUserName"]; + } + + if (!string.IsNullOrEmpty(configuration["EventBusPassword"])) + { + connectionFactory.Password = configuration["EventBusPassword"]; + } + + services.AddSingleton(connectionFactory); services.AddSingleton(sp => { var logger = sp.GetRequiredService>(); - var factory = new ConnectionFactory() - { - HostName = configuration["EventBusConnection"], - DispatchConsumersAsync = true - }; + var factory = sp.GetRequiredService(); return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); }); @@ -47,8 +61,7 @@ public static IServiceCollection AddRabbitMQ(this IServiceCollection services, I public static IHealthChecksBuilder AddRabbitMQ(this IHealthChecksBuilder healthChecksBuilder, IConfiguration configuration, string name = "rabbitmqbus-check") { healthChecksBuilder - .AddRabbitMQ( - $"amqp://{configuration["EventBusConnection"]}", + .AddRabbitMQ((sp) => sp.GetRequiredService().CreateConnection(), name: name, tags: new string[] { "rabbitmqbus" }); diff --git a/src/Infrastructure/EventBus/Duber.Infrastructure.EventBus.ServiceBus/EventBusServiceBus.cs b/src/Infrastructure/EventBus/Duber.Infrastructure.EventBus.ServiceBus/EventBusServiceBus.cs index 02ddc12..5feb638 100644 --- a/src/Infrastructure/EventBus/Duber.Infrastructure.EventBus.ServiceBus/EventBusServiceBus.cs +++ b/src/Infrastructure/EventBus/Duber.Infrastructure.EventBus.ServiceBus/EventBusServiceBus.cs @@ -32,8 +32,7 @@ public class EventBusServiceBus : IEventBus _logger = logger; _subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager(); - _subscriptionClient = new SubscriptionClient(serviceBusPersisterConnection.ServiceBusConnectionStringBuilder, - subscriptionClientName); + _subscriptionClient = new SubscriptionClient(serviceBusPersisterConnection.ServiceBusConnectionStringBuilder, subscriptionClientName); _autofac = autofac; _retryCount = retryCount; @@ -80,7 +79,7 @@ public void SubscribeDynamic(string eventName) where T : IntegrationEvent where TH : IIntegrationEventHandler { - var eventName = typeof(T).Name.Replace(INTEGRATION_EVENT_SUFFIX, ""); + var eventName = GetEventName(); var containsKey = _subsManager.HasSubscriptionsForEvent(); if (!containsKey) @@ -93,15 +92,28 @@ public void SubscribeDynamic(string eventName) Name = eventName }).GetAwaiter().GetResult(); } - catch(ServiceBusException) + catch(ServiceBusException ex) { - _logger.LogInformation($"The messaging entity {eventName} already exists."); + _logger.LogInformation($"The messaging entity {eventName} already exists.", ex); } } _subsManager.AddSubscription(); } + private string GetEventName() + { + var type = typeof(T); + var eventName = type.Name.Replace(INTEGRATION_EVENT_SUFFIX, ""); + if (type.GetGenericArguments().Length > 0) + { + eventName = eventName.Remove(eventName.IndexOf('`')); + eventName += type.GetGenericArguments()[0].Name.Replace(INTEGRATION_EVENT_SUFFIX, ""); + } + + return eventName; + } + public void Unsubscribe() where T : IntegrationEvent where TH : IIntegrationEventHandler diff --git a/src/Infrastructure/EventBus/Duber.Infrastructure.EventBus.ServiceBus/IoC/ServiceCollectionExtensions.cs b/src/Infrastructure/EventBus/Duber.Infrastructure.EventBus.ServiceBus/IoC/ServiceCollectionExtensions.cs index 942fdce..6cc578e 100644 --- a/src/Infrastructure/EventBus/Duber.Infrastructure.EventBus.ServiceBus/IoC/ServiceCollectionExtensions.cs +++ b/src/Infrastructure/EventBus/Duber.Infrastructure.EventBus.ServiceBus/IoC/ServiceCollectionExtensions.cs @@ -46,7 +46,7 @@ public static IHealthChecksBuilder AddAzureServiceBusTopic(this IHealthChecksBui { healthChecksBuilder .AddAzureServiceBusTopic( - configuration["EventBusConnection"], + configuration["EventBusConnectionHC"], // this connection string can't contains the EntityPath (topic name) topicName: "duber_event_bus", name, tags: new string[] { "az-servicebus" }); diff --git a/src/Web/Duber.WebSite/appsettings.json b/src/Web/Duber.WebSite/appsettings.json index 459ab05..c6e0d5a 100644 --- a/src/Web/Duber.WebSite/appsettings.json +++ b/src/Web/Duber.WebSite/appsettings.json @@ -7,6 +7,7 @@ }, "AzureServiceBusEnabled": false, "EventBusConnection": "", + "EventBusConnectionHC": "", "SubscriptionClientName": "WebSite", "EventBusRetryCount": 5, "HttpClientRetryCount": 5,