diff --git a/README.md b/README.md index d1072d7..638e452 100644 --- a/README.md +++ b/README.md @@ -129,4 +129,49 @@ Which technology stack do you recommend? 1. Node.js + redis, rack + redis, rails3 + redis, or anything else? How many dynos do you estimate we'll need to meet our performance requirements? (it's ok if you don't know, we're just wondering if you have experience with apps of such high loads) 2. Which database? -3. Where do you store the state table of connected users? (for performance reasons, we suggest keeping this in memory only) How do you make sure that it's shared across multiple dynos? \ No newline at end of file +3. Where do you store the state table of connected users? (for performance reasons, we suggest keeping this in memory only) How do you make sure that it's shared across multiple dynos? + +### Local Setup + +#### Overmind + +* It does not use dotenv setup so set the vars from `.env` +* Setup Mongo +* Run `overmind start` + +#### Docker compose + +Just run + +```bash +docker compose build +docker compose up +``` + +### K8s + +#### Local + +Setup Mongo + +```bash +helm upgrade --install mongodb bitnami/mongodb -f ./infra/charts/mongodb/values.yaml --namespace connection-request-server --create-namespace +``` + +Setup Service + +```bash +helm upgrade --install server ./infra/charts/server --namespace connection-request-server +``` + +To forward port to host machine (testing purpose) run the following + +```bash +kubectl port-forward service/server-server 8080:80 -n connection-request-server +``` + +Setup Prometeus + +```bash +helm upgrade --install prometheus prometheus-community/kube-prometheus-stack -f ./infra/charts/prometeus/values.yaml --namespace monitoring --create-namespace +``` \ No newline at end of file diff --git a/infra/charts/mongodb/values.yaml b/infra/charts/mongodb/values.yaml new file mode 100644 index 0000000..9fdb432 --- /dev/null +++ b/infra/charts/mongodb/values.yaml @@ -0,0 +1,19 @@ +auth: + enabled: true + rootUser: mongo + rootPassword: mongo + usernames: + - appuser + passwords: + - apppass + databases: + - connections + +architecture: standalone + +persistence: + enabled: true + size: 8Gi + storageClass: local-path + +replicaCount: 2 diff --git a/infra/charts/prometeus/values.yaml b/infra/charts/prometeus/values.yaml new file mode 100644 index 0000000..ff10111 --- /dev/null +++ b/infra/charts/prometeus/values.yaml @@ -0,0 +1,16 @@ +prometheus: + prometheusSpec: + serviceMonitorSelector: + matchLabels: + release: prometheus + podMonitorSelector: {} + ruleSelector: + matchLabels: + release: prometheus + +alertmanager: + enabled: true + +grafana: + enabled: true + adminPassword: crsAdmin \ No newline at end of file diff --git a/infra/charts/server/Chart.yaml b/infra/charts/server/Chart.yaml new file mode 100644 index 0000000..f79f53d --- /dev/null +++ b/infra/charts/server/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v2 +name: server +version: 0.1.0 +description: A Helm chart for the connection management server + +dependencies: [] + +type: application diff --git a/infra/charts/server/templates/_helpers.tpl b/infra/charts/server/templates/_helpers.tpl new file mode 100644 index 0000000..4273b4c --- /dev/null +++ b/infra/charts/server/templates/_helpers.tpl @@ -0,0 +1,18 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "server.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end }} + +{{/* +Create a fully qualified name. +*/}} +{{- define "server.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} diff --git a/infra/charts/server/templates/deployment.yaml b/infra/charts/server/templates/deployment.yaml new file mode 100644 index 0000000..d8b49d5 --- /dev/null +++ b/infra/charts/server/templates/deployment.yaml @@ -0,0 +1,56 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "server.fullname" . }} + labels: + app: {{ include "server.name" . }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ include "server.name" . }} + template: + metadata: + labels: + app: {{ include "server.name" . }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - containerPort: {{ .Values.server.port }} + env: + - name: PROMETHEUS_ENABLED + value: {{ .Values.prometheus.enabled | quote }} + - name: SERVER_HOSTNAME + value: {{ .Values.server.hostname | quote }} + - name: SERVER_PORT + value: {{ .Values.server.port | quote }} + - name: MONGO_URL + valueFrom: + secretKeyRef: + name: {{ include "server.fullname" . }}-secret + key: url + - name: MONGO_DB + valueFrom: + secretKeyRef: + name: {{ include "server.fullname" . }}-secret + key: dbname + - name: MONGO_COLLECTION + valueFrom: + secretKeyRef: + name: {{ include "server.fullname" . }}-secret + key: collectionname + readinessProbe: + httpGet: + path: /health + port: {{ .Values.server.port }} + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /health + port: {{ .Values.server.port }} + initialDelaySeconds: 15 + periodSeconds: 20 diff --git a/infra/charts/server/templates/hpa.yaml b/infra/charts/server/templates/hpa.yaml new file mode 100644 index 0000000..5ee9d54 --- /dev/null +++ b/infra/charts/server/templates/hpa.yaml @@ -0,0 +1,21 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "server.fullname" . }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "server.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + - type: Pods + pods: + metric: + name: request_latency_seconds + target: + type: AverageValue + averageValue: {{ .Values.autoscaling.targetLatency }} +{{- end }} diff --git a/infra/charts/server/templates/ingress.yaml b/infra/charts/server/templates/ingress.yaml new file mode 100644 index 0000000..8a8fa2f --- /dev/null +++ b/infra/charts/server/templates/ingress.yaml @@ -0,0 +1,30 @@ +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "server.fullname" . }} + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + {{- if .Values.ingress.certManager }} + cert-manager.io/cluster-issuer: {{ .Values.ingress.clusterIssuer }} + {{- end }} +spec: + ingressClassName: nginx + rules: + - host: {{ .Values.ingress.host }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ include "server.fullname" . }} + port: + number: {{ .Values.service.port }} + {{- if .Values.ingress.tls }} + tls: + - hosts: + - {{ .Values.ingress.host }} + secretName: {{ .Values.ingress.tlsSecret }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/infra/charts/server/templates/prometheus-rules.yaml b/infra/charts/server/templates/prometheus-rules.yaml new file mode 100644 index 0000000..4d585a8 --- /dev/null +++ b/infra/charts/server/templates/prometheus-rules.yaml @@ -0,0 +1,20 @@ +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ include "server.fullname" . }}-rules + labels: + release: prometheus +spec: + groups: + - name: {{ include "server.fullname" . }}.rules + rules: + - record: job:request_latency_seconds:avg + expr: avg(rate(request_latency_seconds_sum[1m])) by (job) + - alert: HighRequestLatency + expr: avg(rate(request_latency_seconds_sum[1m])) > 0.3 + for: 1m + labels: + severity: warning + annotations: + summary: "High request latency detected (>300ms)" + description: "Request latency is above threshold for more than 1 minute." diff --git a/infra/charts/server/templates/secret.yaml b/infra/charts/server/templates/secret.yaml new file mode 100644 index 0000000..8190bad --- /dev/null +++ b/infra/charts/server/templates/secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "server.fullname" . }}-secret +stringData: + url: {{ .Values.mongo.url }} + dbname: {{ .Values.mongo.dbname }} + collectionname: {{ .Values.mongo.collectionname }} +type: Opaque diff --git a/infra/charts/server/templates/service.yaml b/infra/charts/server/templates/service.yaml new file mode 100644 index 0000000..1beb1b6 --- /dev/null +++ b/infra/charts/server/templates/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "server.fullname" . }} +spec: + type: {{ .Values.service.type }} + selector: + app: {{ include "server.name" . }} + ports: + - port: {{ .Values.service.port }} + targetPort: {{ .Values.server.port }} + protocol: TCP + name: http diff --git a/infra/charts/server/templates/servicemonitor.yaml b/infra/charts/server/templates/servicemonitor.yaml new file mode 100644 index 0000000..81dd2db --- /dev/null +++ b/infra/charts/server/templates/servicemonitor.yaml @@ -0,0 +1,19 @@ +{{- if .Values.prometheus.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "server.fullname" . }}-servicemonitor + labels: + release: prometheus +spec: + selector: + matchLabels: + app: {{ include "server.name" . }} + endpoints: + - port: http + path: /metrics + interval: {{ .Values.prometheus.serviceMonitor.interval }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} +{{- end }} diff --git a/infra/charts/server/values.yaml b/infra/charts/server/values.yaml new file mode 100644 index 0000000..0e8686e --- /dev/null +++ b/infra/charts/server/values.yaml @@ -0,0 +1,35 @@ +replicaCount: 1 + +image: + repository: ghcr.io/kont-noor/server + tag: latest + pullPolicy: IfNotPresent + +service: + type: LoadBalancer + port: 80 + +server: + port: 3000 + hostname: 0.0.0.0 + +mongo: + url: mongodb://appuser:apppass@mongodb:27017/?authSource=connections + dbname: connections + collectionname: connections + +resources: {} + +autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 10 + targetLatency: 200m + +prometheus: + enabled: true + serviceMonitor: + interval: 15s + +ingress: + enabled: false \ No newline at end of file