diff --git a/README.md b/README.md index 0aa8b43..c300517 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ This is a Kubernetes sample project, not for a production use. ```bash $ minikube start $ minikube addons enable ingress + $ minikube addons enable metrics-server ``` 2. Go to `terraform` folder @@ -29,6 +30,12 @@ This is a Kubernetes sample project, not for a production use. $ terraform apply ``` + or simply run bash script + + ```bash + $ ./script/deploy.sh + ``` + 4. Update hosts file ```bash @@ -46,12 +53,16 @@ This is a Kubernetes sample project, not for a production use. ## Presslabs MySQL Operator -To see orchestrator, run following port forward and open [http://localhost:8080](http://localhost:8080) +To see orchestrator, run following port forward. ```bash $ kubectl -nnvm-db port-forward service/presslabs-mysql-operator 8080:80 ``` +![image](https://user-images.githubusercontent.com/5715919/100513791-ed9ff900-31c3-11eb-80c6-7a3d332d272d.png) + +And open [http://localhost:8080](http://localhost:8080) + To see operator logs, run following command ```bash @@ -65,9 +76,39 @@ $ kubectl -nnvm-db port-forward mysql-cluster-mysql-0 3307:3306 $ mysql -h127.0.0.1 -uroot -proot -P3307 boilerplate ``` +## Horizontal Pod Autoscaler + +```bash +$ kubectl get hpa --all-namespaces +``` + +If you see `/50%`, make sure you enabled metrics-server. + +```bash +$ minikube addons enable metrics-server +``` + +## Prometheus & Grafana + +You can access Grafana via `http://nvm-boilerplate.local/grafana`. + +Once the deployment is completed, then you will see the result like below: + +```text +Apply complete! Resources: 0 added, 1 changed, 0 destroyed. + +Outputs: + +grafana_admin_password = ynSVNykpU72RM5x6 +``` + +For example, as above, if admin password `ynSVNykpU72RM5x6` then you can login Grafana with `admin`/`ynSVNykpU72RM5x6`. + +![image](https://user-images.githubusercontent.com/5715919/100513860-4a031880-31c4-11eb-8ef2-04202055aa78.png) + ## Todo - [x] Update MySQL with a replicated stateful application - Use presslabs/mysql-operator -- [ ] Expose MySQL write node for migration to avoid api migration failure -- [ ] Add HorizontalPodAutoscaler -- [ ] Add Prometheus and Grafana +- [x] Add HorizontalPodAutoscaler +- [x] Add Prometheus and Grafana +- [x] Expose MySQL write node for migration to avoid api migration failure diff --git a/helm/nvm-db/templates/mysql-operator/mysql-cluster.yaml b/helm/nvm-db/templates/mysql-operator/mysql-cluster.yaml index 572056c..7ee1d2b 100644 --- a/helm/nvm-db/templates/mysql-operator/mysql-cluster.yaml +++ b/helm/nvm-db/templates/mysql-operator/mysql-cluster.yaml @@ -5,13 +5,13 @@ metadata: name: mysql-cluster namespace: {{$.Values.namespace}} spec: - replicas: 2 + replicas: {{$.Values.replicas}} secretName: mysql-secret podSpec: resources: requests: memory: 1G - cpu: 200m + cpu: 512m volumeSpec: persistentVolumeClaim: accessModes: ["ReadWriteOnce"] diff --git a/helm/nvm-db/values.yaml b/helm/nvm-db/values.yaml index 0605747..bfd3e43 100644 --- a/helm/nvm-db/values.yaml +++ b/helm/nvm-db/values.yaml @@ -1,5 +1,7 @@ namespace: nvm-db +replicas: 2 + secrets: - key: "ROOT_PASSWORD" value: "root" diff --git a/helm/nvm/Chart.yaml b/helm/nvm/Chart.yaml index 3549bb0..a8e0b62 100644 --- a/helm/nvm/Chart.yaml +++ b/helm/nvm/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 name: nvm description: A Helm chart for Kubernetes -version: 0.1.5 +version: 0.1.7 icon: https://github.com/chrisleekr/nodejs-vuejs-mysql-boilerplate/raw/master/frontend-nuxt/static/icon.png diff --git a/helm/nvm/templates/apps/apps-deployment.yaml b/helm/nvm/templates/apps/apps-deployment.yaml index 0a03aa5..5d617bd 100644 --- a/helm/nvm/templates/apps/apps-deployment.yaml +++ b/helm/nvm/templates/apps/apps-deployment.yaml @@ -6,7 +6,7 @@ metadata: name: {{.name}} namespace: {{$.Values.namespace}} labels: - environment: minikube + environment: {{$.Values.environment}} app: {{.name}} spec: replicas: {{.replicaCount}} diff --git a/helm/nvm/templates/apps/apps-hpa.yaml b/helm/nvm/templates/apps/apps-hpa.yaml new file mode 100644 index 0000000..90354ae --- /dev/null +++ b/helm/nvm/templates/apps/apps-hpa.yaml @@ -0,0 +1,19 @@ +{{ range .Values.apps }} +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: {{.name}} + namespace: {{$.Values.namespace}} + labels: + environment: {{$.Values.environment}} + app: {{.name}} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{.name}} + minReplicas: 1 + maxReplicas: 10 + targetCPUUtilizationPercentage: 50 +{{ end }} diff --git a/helm/nvm/templates/apps/apps-migration.yaml b/helm/nvm/templates/apps/apps-migration.yaml new file mode 100644 index 0000000..d2b007b --- /dev/null +++ b/helm/nvm/templates/apps/apps-migration.yaml @@ -0,0 +1,35 @@ +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: migration + namespace: {{$.Values.namespace}} + labels: + environment: {{$.Values.environment}} + app: migration + annotations: + helm.sh/hook: "post-install,post-upgrade" + helm.sh/hook-weight: "1" + helm.sh/hook-delete-policy: hook-succeeded +spec: + backoffLimit: 3 + activeDeadlineSeconds: 600 + template: + spec: + restartPolicy: OnFailure + containers: + - name: migration + image: "{{$.Values.migration.image.repository}}:{{$.Values.migration.image.version}}" + command: ["/usr/local/bin/migration.sh"] + env: + {{- range $.Values.migration.env}} + - name: "{{.name}}" + value: "{{.value}}" + {{- end}} + {{- range $.Values.migration.secretEnv}} + - name: "{{.name}}" + valueFrom: + secretKeyRef: + name: "{{.secretKeyRef.name}}" + key: "{{.secretKeyRef.key}}" + {{- end}} diff --git a/helm/nvm/values.yaml b/helm/nvm/values.yaml index b4e7618..ea8510d 100644 --- a/helm/nvm/values.yaml +++ b/helm/nvm/values.yaml @@ -6,6 +6,34 @@ ingress: host: nvm-boilerplate.local namespace: nvm +environment: minikube + +migration: + image: + repository: chrisleekr/nodejs-vuejs-mysql-boilerplate + version: api-latest + env: + - name: "DB_HOST" + value: "mysql-cluster-mysql-master.nvm-db" + - name: "DB_PORT" + value: 3306 + - name: "DB_CONNECTION_LIMIT" + value: 10 + - name: "DB_DEBUG" + value: "true" + secretEnv: + - name: "DB_NAME" + secretKeyRef: + name: "nvm-secret" + key: "db-name" + - name: "DB_USER" + secretKeyRef: + name: "nvm-secret" + key: "db-user" + - name: "DB_PASSWORD" + secretKeyRef: + name: "nvm-secret" + key: "db-password" apps: api: @@ -26,8 +54,8 @@ apps: configurationSnippet: | rewrite ^(/api)$ $1/ redirect; probes: - initialDelaySeconds: 60 - periodSeconds: 60 + initialDelaySeconds: 10 + periodSeconds: 30 path: / port: 3000 env: @@ -102,8 +130,8 @@ apps: configurationSnippet: | rewrite ^(/frontend-vue)$ $1/ redirect; probes: - initialDelaySeconds: 60 - periodSeconds: 60 + initialDelaySeconds: 10 + periodSeconds: 30 path: /frontend-vue/ port: 80 env: @@ -129,8 +157,8 @@ apps: configurationSnippet: | rewrite ^(/backend)$ $1/ redirect; probes: - initialDelaySeconds: 60 - periodSeconds: 60 + initialDelaySeconds: 10 + periodSeconds: 30 path: /backend/ port: 80 env: @@ -154,8 +182,8 @@ apps: configurationSnippet: | rewrite ^(/mailhog)$ $1/ redirect; probes: - initialDelaySeconds: 60 - periodSeconds: 60 + initialDelaySeconds: 10 + periodSeconds: 30 path: / port: 8025 diff --git a/terraform/main.tf b/terraform/main.tf index c950f95..3aeddca 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -2,9 +2,9 @@ provider "kubernetes" { config_context_cluster = "minikube" } -resource "kubernetes_namespace" "nvm-namespace" { - metadata { - name = "nvm" +provider "helm" { + kubernetes { + config_context_cluster = "minikube" } } resource "kubernetes_namespace" "nvm-db-namespace" { @@ -13,12 +13,6 @@ resource "kubernetes_namespace" "nvm-db-namespace" { } } -provider "helm" { - kubernetes { - config_context_cluster = "minikube" - } -} - resource "helm_release" "mysql-operator" { depends_on = [ kubernetes_namespace.nvm-db-namespace @@ -32,6 +26,53 @@ resource "helm_release" "mysql-operator" { timeout = 360 } +resource "kubernetes_namespace" "prometheus-namespace" { + metadata { + name = "prometheus" + } +} + + +resource "random_password" "grafana_admin_password" { + length = 16 + special = false +} + + +resource "helm_release" "prometheus-operator" { + depends_on = [ + kubernetes_namespace.prometheus-namespace, + random_password.grafana_admin_password + ] + + name = "kube-prometheus-stack" + repository = "https://prometheus-community.github.io/helm-charts" + chart = "kube-prometheus-stack" + version = "12.2.3" + namespace = "prometheus" + timeout = 360 + + values = [ + <<-EOT + grafana: + grafana.ini: + server: + domain: ${var.domain} + root_url: "${var.protocol}://${var.domain}/grafana" + serve_from_sub_path: true + defaultDashboardsEnabled: true + adminPassword: ${random_password.grafana_admin_password.result} + ingress: + enabled: "true" + path: /grafana + hosts: + - ${var.domain} + tls: [] +EOT + , + ] +} + resource "helm_release" "nvm-db" { depends_on = [ kubernetes_namespace.nvm-db-namespace, @@ -45,10 +86,16 @@ resource "helm_release" "nvm-db" { } + +resource "kubernetes_namespace" "nvm-namespace" { + metadata { + name = "nvm" + } +} resource "helm_release" "nvm" { depends_on = [ kubernetes_namespace.nvm-namespace, - helm_release.nvm-db + helm_release.mysql-operator ] name = "nvm" @@ -57,7 +104,7 @@ resource "helm_release" "nvm" { timeout = 600 set { - name = "cluster.enabled" - value = "true" + name = "ingress.host" + value = var.domain } } diff --git a/terraform/output.tf b/terraform/output.tf index e69de29..8164381 100644 --- a/terraform/output.tf +++ b/terraform/output.tf @@ -0,0 +1,4 @@ + +output "grafana_admin_password" { + value = random_password.grafana_admin_password.result +} diff --git a/terraform/variables.tf b/terraform/variables.tf index e69de29..0e7877b 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -0,0 +1,9 @@ + +variable "protocol" { + description = "HTTP protocol" + default = "http" +} +variable "domain" { + description = "Domain Name" + default = "nvm-boilerplate.local" +}