From c620f689131af93a1415ef325f6a04b38c53b9c4 Mon Sep 17 00:00:00 2001 From: sayedppqq Date: Fri, 18 Oct 2024 18:25:20 +0600 Subject: [PATCH] wip Signed-off-by: sayedppqq --- .../ferretdb/monitoring/builtin-prom-fr.yaml | 2 +- .../ferretdb/reconfigure-tls/ferretdb.yaml | 2 +- .../reconfigure-tls/frops-change-issuer.yaml | 14 + .../reconfigure-tls/frops-remove.yaml | 11 + .../ferretdb/reconfigure-tls/new-issuer.yaml | 8 + docs/examples/ferretdb/restart/ferretdb.yaml | 17 + docs/examples/ferretdb/restart/ops.yaml | 11 + .../ferretdb/scaling/fr-horizontal.yaml | 17 + .../ferretdb/scaling/fr-vertical-ops.yaml | 20 + .../ferretdb/scaling/fr-vertical.yaml | 17 + .../scaling/frops-hscale-down-ops.yaml | 11 + .../ferretdb/scaling/frops-hscale-up-ops.yaml | 11 + .../ferretdb/update-version/fr-update.yaml | 17 + .../ferretdb/update-version/frops-update.yaml | 11 + .../autoscaler/compute/compute-autoscale.md | 2 +- docs/guides/ferretdb/concepts/catalog.md | 2 +- docs/guides/ferretdb/concepts/ferretdb.md | 6 +- docs/guides/ferretdb/monitoring/_index.md | 2 +- docs/guides/ferretdb/monitoring/overview.md | 13 +- .../monitoring/using-builtin-prometheus.md | 4 +- .../monitoring/using-prometheus-operator.md | 3 +- .../reconfigure-tls/reconfigure-tls.md | 354 +++++++------- docs/guides/ferretdb/restart/_index.md | 10 + docs/guides/ferretdb/restart/restart.md | 174 +++++++ docs/guides/ferretdb/scaling/_index.md | 10 + .../scaling/horizontal-scaling/_index.md | 10 + .../horizontal-scaling/horizontal-ops.md | 432 ++++++++++++++++++ .../scaling/horizontal-scaling/overview.md | 54 +++ .../scaling/vertical-scaling/_index.md | 10 + .../scaling/vertical-scaling/overview.md | 54 +++ .../scaling/vertical-scaling/vertical-ops.md | 282 ++++++++++++ docs/guides/ferretdb/tls/configure_tls.md | 8 +- docs/guides/ferretdb/update-version/_index.md | 10 + .../ferretdb/update-version/overview.md | 54 +++ .../ferretdb/update-version/update-version.md | 241 ++++++++++ .../pgpool/update-version/update_version.md | 4 +- .../ferretdb/fr-builtin-prom-target.png | Bin 0 -> 66375 bytes .../images/ferretdb/fr-horizontal-scaling.svg | 4 + docs/images/ferretdb/fr-update.svg | 4 + docs/images/ferretdb/fr-vertical-scaling.svg | 4 + 40 files changed, 1712 insertions(+), 208 deletions(-) create mode 100644 docs/examples/ferretdb/reconfigure-tls/frops-change-issuer.yaml create mode 100644 docs/examples/ferretdb/reconfigure-tls/frops-remove.yaml create mode 100644 docs/examples/ferretdb/reconfigure-tls/new-issuer.yaml create mode 100644 docs/examples/ferretdb/restart/ferretdb.yaml create mode 100644 docs/examples/ferretdb/restart/ops.yaml create mode 100644 docs/examples/ferretdb/scaling/fr-horizontal.yaml create mode 100644 docs/examples/ferretdb/scaling/fr-vertical-ops.yaml create mode 100644 docs/examples/ferretdb/scaling/fr-vertical.yaml create mode 100644 docs/examples/ferretdb/scaling/frops-hscale-down-ops.yaml create mode 100644 docs/examples/ferretdb/scaling/frops-hscale-up-ops.yaml create mode 100644 docs/examples/ferretdb/update-version/fr-update.yaml create mode 100644 docs/examples/ferretdb/update-version/frops-update.yaml create mode 100644 docs/guides/ferretdb/restart/_index.md create mode 100644 docs/guides/ferretdb/restart/restart.md create mode 100644 docs/guides/ferretdb/scaling/_index.md create mode 100644 docs/guides/ferretdb/scaling/horizontal-scaling/_index.md create mode 100644 docs/guides/ferretdb/scaling/horizontal-scaling/horizontal-ops.md create mode 100644 docs/guides/ferretdb/scaling/horizontal-scaling/overview.md create mode 100644 docs/guides/ferretdb/scaling/vertical-scaling/_index.md create mode 100644 docs/guides/ferretdb/scaling/vertical-scaling/overview.md create mode 100644 docs/guides/ferretdb/scaling/vertical-scaling/vertical-ops.md create mode 100644 docs/guides/ferretdb/update-version/_index.md create mode 100644 docs/guides/ferretdb/update-version/overview.md create mode 100644 docs/guides/ferretdb/update-version/update-version.md create mode 100644 docs/images/ferretdb/fr-builtin-prom-target.png create mode 100644 docs/images/ferretdb/fr-horizontal-scaling.svg create mode 100644 docs/images/ferretdb/fr-update.svg create mode 100644 docs/images/ferretdb/fr-vertical-scaling.svg diff --git a/docs/examples/ferretdb/monitoring/builtin-prom-fr.yaml b/docs/examples/ferretdb/monitoring/builtin-prom-fr.yaml index df84bc129a..df3b0d3f31 100644 --- a/docs/examples/ferretdb/monitoring/builtin-prom-fr.yaml +++ b/docs/examples/ferretdb/monitoring/builtin-prom-fr.yaml @@ -4,7 +4,7 @@ metadata: name: builtin-prom-fr namespace: demo spec: - version: "1.18.0" + version: "1.23.0" storage: accessModes: - ReadWriteOnce diff --git a/docs/examples/ferretdb/reconfigure-tls/ferretdb.yaml b/docs/examples/ferretdb/reconfigure-tls/ferretdb.yaml index 526c65c1dc..09b7b0f9ef 100644 --- a/docs/examples/ferretdb/reconfigure-tls/ferretdb.yaml +++ b/docs/examples/ferretdb/reconfigure-tls/ferretdb.yaml @@ -4,7 +4,7 @@ metadata: name: ferretdb namespace: demo spec: - version: "1.23.0" + version: "1.18.0" storage: accessModes: - ReadWriteOnce diff --git a/docs/examples/ferretdb/reconfigure-tls/frops-change-issuer.yaml b/docs/examples/ferretdb/reconfigure-tls/frops-change-issuer.yaml new file mode 100644 index 0000000000..38df852bbb --- /dev/null +++ b/docs/examples/ferretdb/reconfigure-tls/frops-change-issuer.yaml @@ -0,0 +1,14 @@ +apiVersion: ops.kubedb.com/v1alpha1 +kind: FerretDBOpsRequest +metadata: + name: frops-change-issuer + namespace: demo +spec: + type: ReconfigureTLS + databaseRef: + name: ferretdb + tls: + issuerRef: + name: fr-new-issuer + kind: Issuer + apiGroup: "cert-manager.io" \ No newline at end of file diff --git a/docs/examples/ferretdb/reconfigure-tls/frops-remove.yaml b/docs/examples/ferretdb/reconfigure-tls/frops-remove.yaml new file mode 100644 index 0000000000..b2708bac37 --- /dev/null +++ b/docs/examples/ferretdb/reconfigure-tls/frops-remove.yaml @@ -0,0 +1,11 @@ +apiVersion: ops.kubedb.com/v1alpha1 +kind: FerretDBOpsRequest +metadata: + name: frops-remove + namespace: demo +spec: + type: ReconfigureTLS + databaseRef: + name: ferretdb + tls: + remove: true \ No newline at end of file diff --git a/docs/examples/ferretdb/reconfigure-tls/new-issuer.yaml b/docs/examples/ferretdb/reconfigure-tls/new-issuer.yaml new file mode 100644 index 0000000000..71d30275ee --- /dev/null +++ b/docs/examples/ferretdb/reconfigure-tls/new-issuer.yaml @@ -0,0 +1,8 @@ +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: fr-new-issuer + namespace: demo +spec: + ca: + secretName: ferretdb-new-ca \ No newline at end of file diff --git a/docs/examples/ferretdb/restart/ferretdb.yaml b/docs/examples/ferretdb/restart/ferretdb.yaml new file mode 100644 index 0000000000..b86eab911a --- /dev/null +++ b/docs/examples/ferretdb/restart/ferretdb.yaml @@ -0,0 +1,17 @@ +apiVersion: kubedb.com/v1alpha2 +kind: FerretDB +metadata: + name: ferretdb + namespace: demo +spec: + version: "1.23.0" + replicas: 1 + backend: + externallyManaged: false + storage: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 500Mi + deletionPolicy: WipeOut \ No newline at end of file diff --git a/docs/examples/ferretdb/restart/ops.yaml b/docs/examples/ferretdb/restart/ops.yaml new file mode 100644 index 0000000000..a8f7e3a8c4 --- /dev/null +++ b/docs/examples/ferretdb/restart/ops.yaml @@ -0,0 +1,11 @@ +apiVersion: ops.kubedb.com/v1alpha1 +kind: FerretDBOpsRequest +metadata: + name: restart-ferretdb + namespace: demo +spec: + type: Restart + databaseRef: + name: ferretdb + timeout: 3m + apply: Always \ No newline at end of file diff --git a/docs/examples/ferretdb/scaling/fr-horizontal.yaml b/docs/examples/ferretdb/scaling/fr-horizontal.yaml new file mode 100644 index 0000000000..bc542d43b9 --- /dev/null +++ b/docs/examples/ferretdb/scaling/fr-horizontal.yaml @@ -0,0 +1,17 @@ +apiVersion: kubedb.com/v1alpha2 +kind: FerretDB +metadata: + name: fr-horizontal + namespace: demo +spec: + version: "1.23.0" + replicas: 1 + backend: + externallyManaged: false + storage: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 500Mi + deletionPolicy: WipeOut \ No newline at end of file diff --git a/docs/examples/ferretdb/scaling/fr-vertical-ops.yaml b/docs/examples/ferretdb/scaling/fr-vertical-ops.yaml new file mode 100644 index 0000000000..e5de4d2b39 --- /dev/null +++ b/docs/examples/ferretdb/scaling/fr-vertical-ops.yaml @@ -0,0 +1,20 @@ +apiVersion: ops.kubedb.com/v1alpha1 +kind: FerretDBOpsRequest +metadata: + name: ferretdb-scale-vertical + namespace: demo +spec: + type: VerticalScaling + databaseRef: + name: fr-vertical + verticalScaling: + node: + resources: + requests: + memory: "2Gi" + cpu: "1" + limits: + memory: "2Gi" + cpu: "1" + timeout: 5m + apply: IfReady \ No newline at end of file diff --git a/docs/examples/ferretdb/scaling/fr-vertical.yaml b/docs/examples/ferretdb/scaling/fr-vertical.yaml new file mode 100644 index 0000000000..6809284bfb --- /dev/null +++ b/docs/examples/ferretdb/scaling/fr-vertical.yaml @@ -0,0 +1,17 @@ +apiVersion: kubedb.com/v1alpha2 +kind: FerretDB +metadata: + name: fr-vertical + namespace: demo +spec: + version: "1.23.0" + replicas: 1 + backend: + externallyManaged: false + storage: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 500Mi + deletionPolicy: WipeOut \ No newline at end of file diff --git a/docs/examples/ferretdb/scaling/frops-hscale-down-ops.yaml b/docs/examples/ferretdb/scaling/frops-hscale-down-ops.yaml new file mode 100644 index 0000000000..c51199639e --- /dev/null +++ b/docs/examples/ferretdb/scaling/frops-hscale-down-ops.yaml @@ -0,0 +1,11 @@ +apiVersion: ops.kubedb.com/v1alpha1 +kind: FerretDBOpsRequest +metadata: + name: ferretdb-horizontal-scale-down + namespace: demo +spec: + type: HorizontalScaling + databaseRef: + name: fr-horizontal + horizontalScaling: + node: 2 \ No newline at end of file diff --git a/docs/examples/ferretdb/scaling/frops-hscale-up-ops.yaml b/docs/examples/ferretdb/scaling/frops-hscale-up-ops.yaml new file mode 100644 index 0000000000..b8c998e354 --- /dev/null +++ b/docs/examples/ferretdb/scaling/frops-hscale-up-ops.yaml @@ -0,0 +1,11 @@ +apiVersion: ops.kubedb.com/v1alpha1 +kind: FerretDBOpsRequest +metadata: + name: ferretdb-horizontal-scale-up + namespace: demo +spec: + type: HorizontalScaling + databaseRef: + name: fr-horizontal + horizontalScaling: + node: 3 \ No newline at end of file diff --git a/docs/examples/ferretdb/update-version/fr-update.yaml b/docs/examples/ferretdb/update-version/fr-update.yaml new file mode 100644 index 0000000000..5759c89db6 --- /dev/null +++ b/docs/examples/ferretdb/update-version/fr-update.yaml @@ -0,0 +1,17 @@ +apiVersion: kubedb.com/v1alpha2 +kind: FerretDB +metadata: + name: fr-update + namespace: demo +spec: + version: "1.18.0" + replicas: 1 + backend: + externallyManaged: false + storage: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 500Mi + deletionPolicy: WipeOut \ No newline at end of file diff --git a/docs/examples/ferretdb/update-version/frops-update.yaml b/docs/examples/ferretdb/update-version/frops-update.yaml new file mode 100644 index 0000000000..9b66daaf14 --- /dev/null +++ b/docs/examples/ferretdb/update-version/frops-update.yaml @@ -0,0 +1,11 @@ +apiVersion: ops.kubedb.com/v1alpha1 +kind: FerretDBOpsRequest +metadata: + name: ferretdb-version-update + namespace: demo +spec: + type: UpdateVersion + databaseRef: + name: fr-update + updateVersion: + targetVersion: 1.23.0 \ No newline at end of file diff --git a/docs/guides/ferretdb/autoscaler/compute/compute-autoscale.md b/docs/guides/ferretdb/autoscaler/compute/compute-autoscale.md index f467188747..a51212656d 100644 --- a/docs/guides/ferretdb/autoscaler/compute/compute-autoscale.md +++ b/docs/guides/ferretdb/autoscaler/compute/compute-autoscale.md @@ -3,7 +3,7 @@ title: FerretDB Autoscaling menu: docs_{{ .version }}: identifier: fr-auto-scaling-ferretdb - name: ferretdbCompute + name: Ferretdb Compute Autoscaling parent: fr-compute-auto-scaling weight: 15 menu_name: docs_{{ .version }} diff --git a/docs/guides/ferretdb/concepts/catalog.md b/docs/guides/ferretdb/concepts/catalog.md index af2be706af..e136e4d6d3 100644 --- a/docs/guides/ferretdb/concepts/catalog.md +++ b/docs/guides/ferretdb/concepts/catalog.md @@ -16,7 +16,7 @@ section_menu_id: guides ## What is FerretDBVersion -`FerretDBVersion` is a Kubernetes `Custom Resource Definitions` (CRD). It provides a declarative configuration to specify the docker images to be used for [FerretDB](https://ferretdb.net/) server deployed with KubeDB in a Kubernetes native way. +`FerretDBVersion` is a Kubernetes `Custom Resource Definitions` (CRD). It provides a declarative configuration to specify the docker images to be used for [FerretDB](https://ferretdb.com/) server deployed with KubeDB in a Kubernetes native way. When you install KubeDB, a `FerretDBVersion` custom resource will be created automatically for every supported FerretDB release versions. You have to specify the name of `FerretDBVersion` crd in `spec.version` field of [FerretDB](/docs/guides/ferretdb/concepts/ferretdb.md) crd. Then, KubeDB will use the docker images specified in the `FerretDBVersion` crd to create your expected FerretDB instance. diff --git a/docs/guides/ferretdb/concepts/ferretdb.md b/docs/guides/ferretdb/concepts/ferretdb.md index bb1ca86ddb..deb8d2468b 100644 --- a/docs/guides/ferretdb/concepts/ferretdb.md +++ b/docs/guides/ferretdb/concepts/ferretdb.md @@ -216,7 +216,7 @@ FerretDB managed by KubeDB can be monitored with builtin-Prometheus and Promethe ### spec.deletionPolicy -`deletionPolicy` gives flexibility whether to `nullify`(reject) the delete operation of `Pgpool` CR or which resources KubeDB should keep or delete when you delete `Pgpool` CR. KubeDB provides following four deletion policies: +`deletionPolicy` gives flexibility whether to `nullify`(reject) the delete operation of `FerretDB` CR or which resources KubeDB should keep or delete when you delete `FerretDB` CR. KubeDB provides following four deletion policies: - DoNotTerminate - Delete @@ -224,7 +224,7 @@ FerretDB managed by KubeDB can be monitored with builtin-Prometheus and Promethe When `deletionPolicy` is `DoNotTerminate`, KubeDB takes advantage of `ValidationWebhook` feature in Kubernetes 1.9.0 or later clusters to implement `DoNotTerminate` feature. If admission webhook is enabled, `DoNotTerminate` prevents users from deleting the database as long as the `spec.deletionPolicy` is set to `DoNotTerminate`. -Following table show what KubeDB does when you delete Pgpool CR for different deletion policies, +Following table show what KubeDB does when you delete FerretDB CR for different deletion policies, | Behavior | DoNotTerminate | Delete | WipeOut | |---------------------------| :------------: |:------------:| :------: | @@ -237,7 +237,7 @@ If you don't specify `spec.deletionPolicy` KubeDB uses `Delete` deletion policy ### spec.podTemplate -KubeDB allows providing a template for pod through `spec.podTemplate`. KubeDB operator will pass the information provided in `spec.podTemplate` to the PetSet created for Pgpool. +KubeDB allows providing a template for pod through `spec.podTemplate`. KubeDB operator will pass the information provided in `spec.podTemplate` to the PetSet created for FerretDB. KubeDB accept following fields to set in `spec.podTemplate:` diff --git a/docs/guides/ferretdb/monitoring/_index.md b/docs/guides/ferretdb/monitoring/_index.md index cbafc7fe86..40795496c4 100644 --- a/docs/guides/ferretdb/monitoring/_index.md +++ b/docs/guides/ferretdb/monitoring/_index.md @@ -4,7 +4,7 @@ menu: docs_{{ .version }}: identifier: fr-monitoring-ferretdb name: Monitoring - parent: fr-pgpool-guides + parent: fr-ferretdb-guides weight: 50 menu_name: docs_{{ .version }} --- \ No newline at end of file diff --git a/docs/guides/ferretdb/monitoring/overview.md b/docs/guides/ferretdb/monitoring/overview.md index 6b4c0954d0..bcbb7441d7 100644 --- a/docs/guides/ferretdb/monitoring/overview.md +++ b/docs/guides/ferretdb/monitoring/overview.md @@ -53,11 +53,16 @@ metadata: name: sample-ferretdb namespace: databases spec: - version: "4.5.0" + version: "1.23.0" deletionPolicy: WipeOut - postgresRef: - name: ha-postgres - namespace: demo + backend: + externallyManaged: false + storage: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 500Mi monitor: agent: prometheus.io/operator prometheus: diff --git a/docs/guides/ferretdb/monitoring/using-builtin-prometheus.md b/docs/guides/ferretdb/monitoring/using-builtin-prometheus.md index c1e04d313e..203f4a8d10 100644 --- a/docs/guides/ferretdb/monitoring/using-builtin-prometheus.md +++ b/docs/guides/ferretdb/monitoring/using-builtin-prometheus.md @@ -49,7 +49,7 @@ metadata: name: builtin-prom-fr namespace: demo spec: - version: "1.18.0" + version: "1.23.0" storage: accessModes: - ReadWriteOnce @@ -336,7 +336,7 @@ Now, we can access the dashboard at `localhost:9090`. Open [http://localhost:909   

-Check the labels marked with red rectangle. These labels confirm that the metrics are coming from `FerretDB` database `builtin-prom-fr` through stats service `builtin-prom-fr-stats`. +Check the labels. These labels confirm that the metrics are coming from `FerretDB` database `builtin-prom-fr` through stats service `builtin-prom-fr-stats`. Now, you can view the collected metrics and create a graph from homepage of this Prometheus dashboard. You can also use this Prometheus server as data source for [Grafana](https://grafana.com/) and create beautiful dashboard with collected metrics. diff --git a/docs/guides/ferretdb/monitoring/using-prometheus-operator.md b/docs/guides/ferretdb/monitoring/using-prometheus-operator.md index 7007a211d0..861373823a 100644 --- a/docs/guides/ferretdb/monitoring/using-prometheus-operator.md +++ b/docs/guides/ferretdb/monitoring/using-prometheus-operator.md @@ -350,8 +350,7 @@ Check the `endpoint` and `service` labels marked by the red rectangles. It verif To clean up the Kubernetes resources created by this tutorial, run following commands ```bash -kubectl delete -n demo pp/coreos-prom-pp -kubectl delete -n demo pg/ha-postgres +kubectl delete -n demo fr/coreos-prom-fr kubectl delete ns demo ``` diff --git a/docs/guides/ferretdb/reconfigure-tls/reconfigure-tls.md b/docs/guides/ferretdb/reconfigure-tls/reconfigure-tls.md index ac83465570..2217344798 100644 --- a/docs/guides/ferretdb/reconfigure-tls/reconfigure-tls.md +++ b/docs/guides/ferretdb/reconfigure-tls/reconfigure-tls.md @@ -45,10 +45,10 @@ In this section, we are going to deploy a FerretDB without TLS. In the next few apiVersion: kubedb.com/v1alpha2 kind: FerretDB metadata: - name: ferretdb + name: ferretdb-x namespace: demo spec: - version: "1.23.0" + version: "1.18.0" storage: accessModes: - ReadWriteOnce @@ -72,7 +72,7 @@ Now, wait until `ferretdb` has status `Ready`. i.e, ```bash $ kubectl get fr -n demo NAME NAMESPACE VERSION STATUS AGE -ferretdb demo 1.23.0 Ready 75s +ferretdb demo 1.18.0 Ready 75s $ kubectl dba describe ferretdb ferretdb -n demo Name: ferretdb @@ -138,7 +138,7 @@ Spec: Requests: Storage: 500Mi Storage Type: Durable - Version: 1.23.0 + Version: 1.18.0 Status: Conditions: Last Transition Time: 2024-10-17T11:04:08Z @@ -456,14 +456,7 @@ So, here we have connected using the client certificate and the connection is tl ## Rotate Certificate -Now we are going to rotate the certificate of this database. First let's check the current expiration date of the certificate. - -```bash -$ openssl x509 -in ./ca.crt -inform PEM -enddate -nameopt RFC2253 -noout -notAfter=Oct 14 10:20:07 2025 GMT -``` - -So, the certificate will expire on this time `Oct 14 10:20:07 2025 GMT`. +Now we are going to rotate the certificate of this database. First we can store the current expiration date of the certificate by exec into `ferretdb-0` pod. Certs are located in `/etc/certs/server/` path. ### Create FerretDBOpsRequest @@ -639,14 +632,6 @@ Events: Normal Successful 16s KubeDB Ops-manager Operator Successfully resumed FerretDB database: demo/ferretdb for FerretDBOpsRequest: frops-rotate ``` -Now, let's check the expiration date of the certificate. - -```bash -$ kubectl exec -it -n demo ferretdb-0 -- bash master ⬆ ⬇ ✱ ◼ -ferretdb-0:/$ openssl x509 -in /opt/ferretdb-II/tls/ca.pem -inform PEM -enddate -nameopt RFC2253 -noout -notAfter=Oct 27 07:10:20 2024 GMT -``` - As we can see from the above output, the certificate has been rotated successfully. ## Change Issuer/ClusterIssuer @@ -702,7 +687,7 @@ In order to use the new issuer to issue new certificates, we have to create a `F apiVersion: ops.kubedb.com/v1alpha1 kind: FerretDBOpsRequest metadata: - name: ppops-change-issuer + name: frops-change-issuer namespace: demo spec: type: ReconfigureTLS @@ -724,8 +709,8 @@ Here, Let's create the `FerretDBOpsRequest` CR we have shown above, ```bash -$ kubectl apply -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/ferretdb/reconfigure-tls/ppops-change-issuer.yaml -ferretdbopsrequest.ops.kubedb.com/ppops-change-issuer created +$ kubectl apply -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/ferretdb/reconfigure-tls/frops-change-issuer.yaml +ferretdbopsrequest.ops.kubedb.com/frops-change-issuer created ``` #### Verify Issuer is changed successfully @@ -736,24 +721,24 @@ Let's wait for `FerretDBOpsRequest` to be `Successful`. Run the following comma $ watch kubectl get ferretdbopsrequest -n demo Every 2.0s: kubectl get ferretdbopsrequest -n demo NAME TYPE STATUS AGE -ppops-change-issuer ReconfigureTLS Successful 87s +frops-change-issuer ReconfigureTLS Successful 87s ``` We can see from the above output that the `FerretDBOpsRequest` has succeeded. If we describe the `FerretDBOpsRequest` we will get an overview of the steps that were followed. ```bash -$ kubectl describe ferretdbopsrequest -n demo ppops-change-issuer -Name: ppops-change-issuer +$ kubectl describe ferretdbopsrequest -n demo frops-change-issuer +Name: frops-change-issuer Namespace: demo Labels: Annotations: API Version: ops.kubedb.com/v1alpha1 Kind: FerretDBOpsRequest Metadata: - Creation Timestamp: 2024-07-29T07:37:09Z + Creation Timestamp: 2024-10-18T10:14:38Z Generation: 1 - Resource Version: 12367 - UID: f48452ed-7264-4e99-80f1-58d7e826d9a9 + Resource Version: 423126 + UID: 1bf730e8-603e-4f30-b9ab-5a4e75d3a4d4 Spec: Apply: IfReady Database Ref: @@ -766,74 +751,83 @@ Spec: Type: ReconfigureTLS Status: Conditions: - Last Transition Time: 2024-07-29T07:37:09Z - Message: FerretDB ops-request has started to reconfigure tls for RabbitMQ nodes + Last Transition Time: 2024-10-18T10:14:38Z + Message: FerretDB ops-request has started to reconfigure tls for FerretDB nodes Observed Generation: 1 Reason: ReconfigureTLS Status: True Type: ReconfigureTLS - Last Transition Time: 2024-07-29T07:37:12Z + Last Transition Time: 2024-10-18T10:14:41Z Message: Successfully paused database Observed Generation: 1 Reason: DatabasePauseSucceeded Status: True Type: DatabasePauseSucceeded - Last Transition Time: 2024-07-29T07:37:24Z - Message: Successfully synced all certificates - Observed Generation: 1 - Reason: CertificateSynced - Status: True - Type: CertificateSynced - Last Transition Time: 2024-07-29T07:37:18Z + Last Transition Time: 2024-10-18T10:14:46Z Message: get certificate; ConditionStatus:True Observed Generation: 1 Status: True Type: GetCertificate - Last Transition Time: 2024-07-29T07:37:18Z - Message: check ready condition; ConditionStatus:True + Last Transition Time: 2024-10-18T10:14:46Z + Message: ready condition; ConditionStatus:True Observed Generation: 1 Status: True - Type: CheckReadyCondition - Last Transition Time: 2024-07-29T07:37:18Z - Message: check issuing condition; ConditionStatus:True + Type: ReadyCondition + Last Transition Time: 2024-10-18T10:14:46Z + Message: issuing condition; ConditionStatus:True Observed Generation: 1 Status: True - Type: CheckIssuingCondition - Last Transition Time: 2024-07-29T07:37:30Z - Message: successfully reconciled the FerretDB with TLS + Type: IssuingCondition + Last Transition Time: 2024-10-18T10:14:46Z + Message: Successfully synced all certificates Observed Generation: 1 - Reason: UpdatePetSets + Reason: CertificateSynced Status: True - Type: UpdatePetSets - Last Transition Time: 2024-07-29T07:38:15Z - Message: Successfully Restarted FerretDB pods + Type: CertificateSynced + Last Transition Time: 2024-10-18T10:14:51Z + Message: successfully reconciled the FerretDB with tls configuration Observed Generation: 1 - Reason: RestartPods + Reason: UpdatePetSets Status: True - Type: RestartPods - Last Transition Time: 2024-07-29T07:37:35Z + Type: UpdatePetSets + Last Transition Time: 2024-10-18T10:14:56Z Message: get pod; ConditionStatus:True; PodName:ferretdb-0 Observed Generation: 1 Status: True Type: GetPod--ferretdb-0 - Last Transition Time: 2024-07-29T07:37:35Z + Last Transition Time: 2024-10-18T10:14:56Z Message: evict pod; ConditionStatus:True; PodName:ferretdb-0 Observed Generation: 1 Status: True Type: EvictPod--ferretdb-0 - Last Transition Time: 2024-07-29T07:38:10Z + Last Transition Time: 2024-10-18T10:15:01Z Message: check pod running; ConditionStatus:True; PodName:ferretdb-0 Observed Generation: 1 Status: True Type: CheckPodRunning--ferretdb-0 - Last Transition Time: 2024-07-29T07:38:15Z - Message: Successfully updated FerretDB + Last Transition Time: 2024-10-18T10:15:06Z + Message: get pod; ConditionStatus:True; PodName:ferretdb-1 + Observed Generation: 1 + Status: True + Type: GetPod--ferretdb-1 + Last Transition Time: 2024-10-18T10:15:06Z + Message: evict pod; ConditionStatus:True; PodName:ferretdb-1 Observed Generation: 1 - Reason: UpdateDatabase Status: True - Type: UpdateDatabase - Last Transition Time: 2024-07-29T07:38:16Z - Message: Successfully updated FerretDB TLS + Type: EvictPod--ferretdb-1 + Last Transition Time: 2024-10-18T10:15:11Z + Message: check pod running; ConditionStatus:True; PodName:ferretdb-1 + Observed Generation: 1 + Status: True + Type: CheckPodRunning--ferretdb-1 + Last Transition Time: 2024-10-18T10:15:16Z + Message: Successfully restarted all nodes + Observed Generation: 1 + Reason: RestartNodes + Status: True + Type: RestartNodes + Last Transition Time: 2024-10-18T10:15:16Z + Message: Successfully completed the ReconfigureTLS for FerretDB Observed Generation: 1 Reason: Successful Status: True @@ -841,50 +835,33 @@ Status: Observed Generation: 1 Phase: Successful Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Starting 3m39s KubeDB Ops-manager Operator Start processing for FerretDBOpsRequest: demo/ppops-change-issuer - Normal Starting 3m39s KubeDB Ops-manager Operator Pausing FerretDB databse: demo/ferretdb - Normal Successful 3m39s KubeDB Ops-manager Operator Successfully paused FerretDB database: demo/ferretdb for FerretDBOpsRequest: ppops-change-issuer - Warning get certificate; ConditionStatus:True 3m30s KubeDB Ops-manager Operator get certificate; ConditionStatus:True - Warning check ready condition; ConditionStatus:True 3m30s KubeDB Ops-manager Operator check ready condition; ConditionStatus:True - Warning check issuing condition; ConditionStatus:True 3m30s KubeDB Ops-manager Operator check issuing condition; ConditionStatus:True - Warning get certificate; ConditionStatus:True 3m30s KubeDB Ops-manager Operator get certificate; ConditionStatus:True - Warning check ready condition; ConditionStatus:True 3m30s KubeDB Ops-manager Operator check ready condition; ConditionStatus:True - Warning check issuing condition; ConditionStatus:True 3m30s KubeDB Ops-manager Operator check issuing condition; ConditionStatus:True - Warning get certificate; ConditionStatus:True 3m30s KubeDB Ops-manager Operator get certificate; ConditionStatus:True - Warning check ready condition; ConditionStatus:True 3m30s KubeDB Ops-manager Operator check ready condition; ConditionStatus:True - Warning check issuing condition; ConditionStatus:True 3m30s KubeDB Ops-manager Operator check issuing condition; ConditionStatus:True - Normal CertificateSynced 3m30s KubeDB Ops-manager Operator Successfully synced all certificates - Warning get certificate; ConditionStatus:True 3m25s KubeDB Ops-manager Operator get certificate; ConditionStatus:True - Warning check ready condition; ConditionStatus:True 3m25s KubeDB Ops-manager Operator check ready condition; ConditionStatus:True - Warning check issuing condition; ConditionStatus:True 3m24s KubeDB Ops-manager Operator check issuing condition; ConditionStatus:True - Warning get certificate; ConditionStatus:True 3m24s KubeDB Ops-manager Operator get certificate; ConditionStatus:True - Warning check ready condition; ConditionStatus:True 3m24s KubeDB Ops-manager Operator check ready condition; ConditionStatus:True - Warning check issuing condition; ConditionStatus:True 3m24s KubeDB Ops-manager Operator check issuing condition; ConditionStatus:True - Warning get certificate; ConditionStatus:True 3m24s KubeDB Ops-manager Operator get certificate; ConditionStatus:True - Warning check ready condition; ConditionStatus:True 3m24s KubeDB Ops-manager Operator check ready condition; ConditionStatus:True - Warning check issuing condition; ConditionStatus:True 3m24s KubeDB Ops-manager Operator check issuing condition; ConditionStatus:True - Normal CertificateSynced 3m24s KubeDB Ops-manager Operator Successfully synced all certificates - Normal UpdatePetSets 3m18s KubeDB Ops-manager Operator successfully reconciled the FerretDB with TLS - Warning get pod; ConditionStatus:True; PodName:ferretdb-0 3m13s KubeDB Ops-manager Operator get pod; ConditionStatus:True; PodName:ferretdb-0 - Warning evict pod; ConditionStatus:True; PodName:ferretdb-0 3m13s KubeDB Ops-manager Operator evict pod; ConditionStatus:True; PodName:ferretdb-0 - Warning check pod running; ConditionStatus:False; PodName:ferretdb-0 3m8s KubeDB Ops-manager Operator check pod running; ConditionStatus:False; PodName:ferretdb-0 - Warning check pod running; ConditionStatus:True; PodName:ferretdb-0 2m38s KubeDB Ops-manager Operator check pod running; ConditionStatus:True; PodName:ferretdb-0 - Normal RestartPods 2m33s KubeDB Ops-manager Operator Successfully Restarted FerretDB pods - Normal Starting 2m32s KubeDB Ops-manager Operator Resuming FerretDB database: demo/ferretdb - Normal Successful 2m32s KubeDB Ops-manager Operator Successfully resumed FerretDB database: demo/ferretdb for FerretDBOpsRequest: ppops-change-issuer + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal Starting 88s KubeDB Ops-manager Operator Start processing for FerretDBOpsRequest: demo/frops-change-issuer + Normal Starting 88s KubeDB Ops-manager Operator Pausing FerretDB database: demo/ferretdb + Normal Successful 88s KubeDB Ops-manager Operator Successfully paused FerretDB database: demo/ferretdb for FerretDBOpsRequest: frops-change-issuer + Warning get certificate; ConditionStatus:True 80s KubeDB Ops-manager Operator get certificate; ConditionStatus:True + Warning ready condition; ConditionStatus:True 80s KubeDB Ops-manager Operator ready condition; ConditionStatus:True + Warning issuing condition; ConditionStatus:True 80s KubeDB Ops-manager Operator issuing condition; ConditionStatus:True + Warning get certificate; ConditionStatus:True 80s KubeDB Ops-manager Operator get certificate; ConditionStatus:True + Warning ready condition; ConditionStatus:True 80s KubeDB Ops-manager Operator ready condition; ConditionStatus:True + Warning issuing condition; ConditionStatus:True 80s KubeDB Ops-manager Operator issuing condition; ConditionStatus:True + Normal CertificateSynced 80s KubeDB Ops-manager Operator Successfully synced all certificates + Normal UpdatePetSets 75s KubeDB Ops-manager Operator successfully reconciled the FerretDB with tls configuration + Warning get pod; ConditionStatus:True; PodName:ferretdb-0 70s KubeDB Ops-manager Operator get pod; ConditionStatus:True; PodName:ferretdb-0 + Warning evict pod; ConditionStatus:True; PodName:ferretdb-0 70s KubeDB Ops-manager Operator evict pod; ConditionStatus:True; PodName:ferretdb-0 + Warning check pod running; ConditionStatus:True; PodName:ferretdb-0 65s KubeDB Ops-manager Operator check pod running; ConditionStatus:True; PodName:ferretdb-0 + Warning get pod; ConditionStatus:True; PodName:ferretdb-1 60s KubeDB Ops-manager Operator get pod; ConditionStatus:True; PodName:ferretdb-1 + Warning evict pod; ConditionStatus:True; PodName:ferretdb-1 60s KubeDB Ops-manager Operator evict pod; ConditionStatus:True; PodName:ferretdb-1 + Warning check pod running; ConditionStatus:True; PodName:ferretdb-1 55s KubeDB Ops-manager Operator check pod running; ConditionStatus:True; PodName:ferretdb-1 + Normal RestartNodes 50s KubeDB Ops-manager Operator Successfully restarted all nodes + Normal Starting 50s KubeDB Ops-manager Operator Resuming FerretDB database: demo/ferretdb + Normal Successful 50s KubeDB Ops-manager Operator Successfully resumed FerretDB database: demo/ferretdb for FerretDBOpsRequest: frops-change-issuer ``` -Now, Let's exec ferretdb and find out the ca subject to see if it matches the one we have provided. - -```bash -$ kubectl exec -it -n demo ferretdb-0 -- bash -ferretdb-0:/$ openssl x509 -in /opt/ferretdb-II/tls/ca.pem -inform PEM -subject -nameopt RFC2253 -noout -subject=O=kubedb-updated,CN=ca-updated -``` +Now, If exec ferretdb and find out the ca subject in `/etc/certs/server` location, we can see that the CN and O is updated according to out new ca.crt. -We can see from the above output that, the subject name matches the subject name of the new ca certificate that we have created. So, the issuer is changed successfully. +We can see that subject name of this ca.crt matches the subject name of the new ca certificate that we have created. So, the issuer is changed successfully. ## Remove TLS from the ferretdb @@ -898,7 +875,7 @@ Below is the YAML of the `FerretDBOpsRequest` CRO that we are going to create, apiVersion: ops.kubedb.com/v1alpha1 kind: FerretDBOpsRequest metadata: - name: ppops-remove + name: frops-remove namespace: demo spec: type: ReconfigureTLS @@ -917,8 +894,8 @@ Here, Let's create the `FerretDBOpsRequest` CR we have shown above, ```bash -$ kubectl apply -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/ferretdb/reconfigure-tls/ppops-remove.yaml -ferretdbopsrequest.ops.kubedb.com/ppops-remove created +$ kubectl apply -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/ferretdb/reconfigure-tls/frops-remove.yaml +ferretdbopsrequest.ops.kubedb.com/frops-remove created ``` #### Verify TLS Removed Successfully @@ -929,24 +906,24 @@ Let's wait for `FerretDBOpsRequest` to be `Successful`. Run the following comma $ wacth kubectl get ferretdbopsrequest -n demo Every 2.0s: kubectl get ferretdbopsrequest -n demo NAME TYPE STATUS AGE -ppops-remove ReconfigureTLS Successful 65s +frops-remove ReconfigureTLS Successful 65s ``` We can see from the above output that the `FerretDBOpsRequest` has succeeded. If we describe the `FerretDBOpsRequest` we will get an overview of the steps that were followed. ```bash -$ kubectl describe ferretdbopsrequest -n demo ppops-remove -Name: ppops-remove +$ kubectl describe ferretdbopsrequest -n demo frops-remove +Name: frops-remove Namespace: demo Labels: Annotations: API Version: ops.kubedb.com/v1alpha1 Kind: FerretDBOpsRequest Metadata: - Creation Timestamp: 2024-07-29T08:38:35Z + Creation Timestamp: 2024-10-18T11:11:55Z Generation: 1 - Resource Version: 16378 - UID: f848e04f-0fd1-48ce-813d-67dbdc3e4a55 + Resource Version: 428244 + UID: 28a6ba72-0a2d-47f1-97b0-1e9609845acc Spec: Apply: IfReady Database Ref: @@ -956,53 +933,62 @@ Spec: Type: ReconfigureTLS Status: Conditions: - Last Transition Time: 2024-07-29T08:38:37Z - Message: FerretDB ops-request has started to reconfigure tls for RabbitMQ nodes + Last Transition Time: 2024-10-18T11:11:55Z + Message: FerretDB ops-request has started to reconfigure tls for FerretDB nodes Observed Generation: 1 Reason: ReconfigureTLS Status: True Type: ReconfigureTLS - Last Transition Time: 2024-07-29T08:38:41Z + Last Transition Time: 2024-10-18T11:11:58Z Message: Successfully paused database Observed Generation: 1 Reason: DatabasePauseSucceeded Status: True Type: DatabasePauseSucceeded - Last Transition Time: 2024-07-29T08:38:47Z - Message: successfully reconciled the FerretDB with TLS + Last Transition Time: 2024-10-18T11:12:04Z + Message: successfully reconciled the FerretDB with tls configuration Observed Generation: 1 Reason: UpdatePetSets Status: True Type: UpdatePetSets - Last Transition Time: 2024-07-29T08:39:32Z - Message: Successfully Restarted FerretDB pods - Observed Generation: 1 - Reason: RestartPods - Status: True - Type: RestartPods - Last Transition Time: 2024-07-29T08:38:52Z + Last Transition Time: 2024-10-18T11:12:09Z Message: get pod; ConditionStatus:True; PodName:ferretdb-0 Observed Generation: 1 Status: True Type: GetPod--ferretdb-0 - Last Transition Time: 2024-07-29T08:38:52Z + Last Transition Time: 2024-10-18T11:12:09Z Message: evict pod; ConditionStatus:True; PodName:ferretdb-0 Observed Generation: 1 Status: True Type: EvictPod--ferretdb-0 - Last Transition Time: 2024-07-29T08:39:27Z + Last Transition Time: 2024-10-18T11:12:14Z Message: check pod running; ConditionStatus:True; PodName:ferretdb-0 Observed Generation: 1 Status: True Type: CheckPodRunning--ferretdb-0 - Last Transition Time: 2024-07-29T08:39:32Z - Message: Successfully updated FerretDB + Last Transition Time: 2024-10-18T11:12:19Z + Message: get pod; ConditionStatus:True; PodName:ferretdb-1 + Observed Generation: 1 + Status: True + Type: GetPod--ferretdb-1 + Last Transition Time: 2024-10-18T11:12:19Z + Message: evict pod; ConditionStatus:True; PodName:ferretdb-1 + Observed Generation: 1 + Status: True + Type: EvictPod--ferretdb-1 + Last Transition Time: 2024-10-18T11:12:24Z + Message: check pod running; ConditionStatus:True; PodName:ferretdb-1 + Observed Generation: 1 + Status: True + Type: CheckPodRunning--ferretdb-1 + Last Transition Time: 2024-10-18T11:12:29Z + Message: Successfully restarted all nodes Observed Generation: 1 - Reason: UpdateDatabase + Reason: RestartNodes Status: True - Type: UpdateDatabase - Last Transition Time: 2024-07-29T08:39:33Z - Message: Successfully updated FerretDB TLS + Type: RestartNodes + Last Transition Time: 2024-10-18T11:12:29Z + Message: Successfully completed the ReconfigureTLS for FerretDB Observed Generation: 1 Reason: Successful Status: True @@ -1010,64 +996,61 @@ Status: Observed Generation: 1 Phase: Successful Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Starting 84s KubeDB Ops-manager Operator Start processing for FerretDBOpsRequest: demo/ppops-remove - Normal Starting 84s KubeDB Ops-manager Operator Pausing FerretDB databse: demo/ferretdb - Normal Successful 83s KubeDB Ops-manager Operator Successfully paused FerretDB database: demo/ferretdb for FerretDBOpsRequest: ppops-remove - Normal UpdatePetSets 74s KubeDB Ops-manager Operator successfully reconciled the FerretDB with TLS - Warning get pod; ConditionStatus:True; PodName:ferretdb-0 69s KubeDB Ops-manager Operator get pod; ConditionStatus:True; PodName:ferretdb-0 - Warning evict pod; ConditionStatus:True; PodName:ferretdb-0 69s KubeDB Ops-manager Operator evict pod; ConditionStatus:True; PodName:ferretdb-0 - Warning check pod running; ConditionStatus:False; PodName:ferretdb-0 64s KubeDB Ops-manager Operator check pod running; ConditionStatus:False; PodName:ferretdb-0 - Warning check pod running; ConditionStatus:True; PodName:ferretdb-0 34s KubeDB Ops-manager Operator check pod running; ConditionStatus:True; PodName:ferretdb-0 - Normal RestartPods 29s KubeDB Ops-manager Operator Successfully Restarted FerretDB pods - Normal Starting 29s KubeDB Ops-manager Operator Resuming FerretDB database: demo/ferretdb - Normal Successful 28s KubeDB Ops-manager Operator Successfully resumed FerretDB database: demo/ferretdb for FerretDBOpsRequest: ppops-remove + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal Starting 87s KubeDB Ops-manager Operator Start processing for FerretDBOpsRequest: demo/frops-remove + Normal Starting 87s KubeDB Ops-manager Operator Pausing FerretDB database: demo/ferretdb + Normal Successful 87s KubeDB Ops-manager Operator Successfully paused FerretDB database: demo/ferretdb for FerretDBOpsRequest: frops-remove + Normal UpdatePetSets 78s KubeDB Ops-manager Operator successfully reconciled the FerretDB with tls configuration + Warning get pod; ConditionStatus:True; PodName:ferretdb-0 73s KubeDB Ops-manager Operator get pod; ConditionStatus:True; PodName:ferretdb-0 + Warning evict pod; ConditionStatus:True; PodName:ferretdb-0 73s KubeDB Ops-manager Operator evict pod; ConditionStatus:True; PodName:ferretdb-0 + Warning check pod running; ConditionStatus:True; PodName:ferretdb-0 68s KubeDB Ops-manager Operator check pod running; ConditionStatus:True; PodName:ferretdb-0 + Warning get pod; ConditionStatus:True; PodName:ferretdb-1 63s KubeDB Ops-manager Operator get pod; ConditionStatus:True; PodName:ferretdb-1 + Warning evict pod; ConditionStatus:True; PodName:ferretdb-1 63s KubeDB Ops-manager Operator evict pod; ConditionStatus:True; PodName:ferretdb-1 + Warning check pod running; ConditionStatus:True; PodName:ferretdb-1 58s KubeDB Ops-manager Operator check pod running; ConditionStatus:True; PodName:ferretdb-1 + Normal RestartNodes 53s KubeDB Ops-manager Operator Successfully restarted all nodes + Normal Starting 53s KubeDB Ops-manager Operator Resuming FerretDB database: demo/ferretdb + Normal Successful 53s KubeDB Ops-manager Operator Successfully resumed FerretDB database: demo/ferretdb for FerretDBOpsRequest: frops-remove ``` -Now, Let's exec into ferretdb and find out that TLS is disabled or not. +Now, Let's try to connect with ferretdb without TLS certs. ```bash -$ kubectl exec -it -n demo ferretdb-0 -- bash -ferretdb-0:/$ cat opt/ferretdb-II/etc/ferretdb.conf -backend_hostname0 = 'ha-postgres.demo.svc' -backend_port0 = 5432 -backend_weight0 = 1 -backend_flag0 = 'ALWAYS_PRIMARY|DISALLOW_TO_FAILOVER' -backend_hostname1 = 'ha-postgres-standby.demo.svc' -backend_port1 = 5432 -backend_weight1 = 1 -backend_flag1 = 'DISALLOW_TO_FAILOVER' -enable_pool_hba = on -listen_addresses = * -port = 9999 -socket_dir = '/var/run/ferretdb' -pcp_listen_addresses = * -pcp_port = 9595 -pcp_socket_dir = '/var/run/ferretdb' -log_per_node_statement = on -sr_check_period = 0 -health_check_period = 0 -backend_clustering_mode = 'streaming_replication' -num_init_children = 5 -max_pool = 15 -child_life_time = 300 -child_max_connections = 0 -connection_life_time = 0 -client_idle_limit = 0 -connection_cache = on -load_balance_mode = on -ssl = 'off' -failover_on_backend_error = 'off' -log_min_messages = 'warning' -statement_level_load_balance = 'off' -memory_cache_enabled = 'off' -memqcache_oiddir = '/tmp/oiddir/' -allow_clear_text_frontend_auth = 'false' -failover_on_backend_error = 'off' +$ kubectl get secrets -n demo ferret-auth -o jsonpath='{.data.\username}' | base64 -d +postgres +$ kubectl get secrets -n demo ferret-auth -o jsonpath='{.data.\\password}' | base64 -d +l*jGp8u*El8WRSDJ + +$ kubectl port-forward svc/ferret -n demo 27017 +Forwarding from 127.0.0.1:27017 -> 27017 +Forwarding from [::1]:27017 -> 27017 +Handling connection for 27017 +Handling connection for 27017 +``` + +Now in another terminal + +```bash +$ mongosh 'mongodb://postgres:l*jGp8u*El8WRSDJ@localhost:27017/ferretdb?authMechanism=PLAIN' +Current Mongosh Log ID: 65efeea2a3347fff66d04c70 +Connecting to: mongodb://@localhost:27017/ferretdb?authMechanism=PLAIN&directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.1.5 +Using MongoDB: 7.0.42 +Using Mongosh: 2.1.5 + +For mongosh info see: https://docs.mongodb.com/mongodb-shell/ + +------ + The server generated these startup warnings when booting + 2024-03-12T05:56:50.979Z: Powered by FerretDB v1.18.0 and PostgreSQL 13.13 on x86_64-pc-linux-musl, compiled by gcc. + 2024-03-12T05:56:50.979Z: Please star us on GitHub: https://github.com/FerretDB/FerretDB. + 2024-03-12T05:56:50.979Z: The telemetry state is undecided. + 2024-03-12T05:56:50.979Z: Read more about FerretDB telemetry and how to opt out at https://beacon.ferretdb.io. +------ + +ferretdb> ``` -We can see from the above output that `ssl='off'` so we can verify that TLS is disabled successfully for this ferretdb. +We can see that we can now connect without providing TLS certs. So TLS connection is successfully disabled ## Cleaning up @@ -1076,8 +1059,7 @@ To clean up the Kubernetes resources created by this tutorial, run: ```bash kubectl delete ferretdb -n demo ferretdb kubectl delete issuer -n demo ferretdb-issuer fr-new-issuer -kubectl delete ferretdbopsrequest -n demo ppops-add-tls ppops-remove ppops-rotate ppops-change-issuer -kubectl delete pg -n demo ha-postgres +kubectl delete ferretdbopsrequest -n demo frops-add-tls frops-remove frops-rotate frops-change-issuer kubectl delete ns demo ``` diff --git a/docs/guides/ferretdb/restart/_index.md b/docs/guides/ferretdb/restart/_index.md new file mode 100644 index 0000000000..f63cdeaa1f --- /dev/null +++ b/docs/guides/ferretdb/restart/_index.md @@ -0,0 +1,10 @@ +--- +title: Restart FerretDB +menu: + docs_{{ .version }}: + identifier: fr-restart + name: Restart + parent: fr-ferretdb-guides + weight: 46 +menu_name: docs_{{ .version }} +--- diff --git a/docs/guides/ferretdb/restart/restart.md b/docs/guides/ferretdb/restart/restart.md new file mode 100644 index 0000000000..1441676233 --- /dev/null +++ b/docs/guides/ferretdb/restart/restart.md @@ -0,0 +1,174 @@ +--- +title: Restart FerretDB +menu: + docs_{{ .version }}: + identifier: fr-restart-details + name: Restart FerretDB + parent: fr-restart + weight: 10 +menu_name: docs_{{ .version }} +section_menu_id: guides +--- + +> New to KubeDB? Please start [here](/docs/README.md). + +# Restart FerretDB + +KubeDB supports restarting the FerretDB via a FerretDBOpsRequest. Restarting is useful if some pods are got stuck in some phase, or they are not working correctly. This tutorial will show you how to use that. + +## Before You Begin + +- At first, you need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by using [kind](https://kind.sigs.k8s.io/docs/user/quick-start/). + +- Now, install KubeDB cli on your workstation and KubeDB operator in your cluster following the steps [here](/docs/setup/README.md). + +- To keep things isolated, this tutorial uses a separate namespace called `demo` throughout this tutorial. + +```bash + $ kubectl create ns demo + namespace/demo created + ``` + +> Note: YAML files used in this tutorial are stored in [docs/examples/ferretdb](https://github.com/kubedb/docs/tree/{{< param "info.version" >}}/docs/examples/ferretdb) folder in GitHub repository [kubedb/docs](https://github.com/kubedb/docs). + +## Deploy FerretDB + +In this section, we are going to deploy a FerretDB using KubeDB. + +```yaml +apiVersion: kubedb.com/v1alpha2 +kind: FerretDB +metadata: + name: ferretdb + namespace: demo +spec: + version: "1.23.0" + replicas: 1 + backend: + externallyManaged: false + storage: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 500Mi + deletionPolicy: WipeOut +``` + +Let's create the `FerretDB` CR we have shown above, + +```bash +$ kubectl create -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/ferretdb/restart/ferretdb.yaml +ferretdb.kubedb.com/ferretdb created +``` + +## Apply Restart opsRequest + +```yaml +apiVersion: ops.kubedb.com/v1alpha1 +kind: FerretDBOpsRequest +metadata: + name: restart-ferretdb + namespace: demo +spec: + type: Restart + databaseRef: + name: ferretdb + timeout: 3m + apply: Always +``` + +- `spec.type` specifies the Type of the ops Request +- `spec.databaseRef` holds the name of the FerretDB. The ferretdb should be available in the same namespace as the opsRequest +- The meaning of `spec.timeout` & `spec.apply` fields will be found [here](/docs/guides/ferretdb/concepts/opsrequest.md#spectimeout) + +Let's create the `FerretDBOpsRequest` CR we have shown above, + +```bash +$ kubectl create -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/ferretdb/restart/ops.yaml +ferretdbopsrequest.ops.kubedb.com/restart-ferretdb created +``` + +Now the Ops-manager operator will restart the pods one by one. + +```shell +$ kubectl get frops -n demo +NAME TYPE STATUS AGE +restart-ferretdb Restart Successful 2m15s + +$ kubectl get frops -n demo -oyaml restart-ferretdb +apiVersion: ops.kubedb.com/v1alpha1 +kind: FerretDBOpsRequest +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"ops.kubedb.com/v1alpha1","kind":"FerretDBOpsRequest","metadata":{"annotations":{},"name":"restart-ferretdb","namespace":"demo"},"spec":{"apply":"Always","databaseRef":{"name":"ferretdb"},"timeout":"3m","type":"Restart"}} + creationTimestamp: "2024-10-21T12:38:38Z" + generation: 1 + name: restart-ferretdb + namespace: demo + resourceVersion: "367859" + uid: 0ca77cab-d354-43a4-ba85-c31f1f6e685d +spec: + apply: Always + databaseRef: + name: ferretdb + timeout: 3m + type: Restart +status: + conditions: + - lastTransitionTime: "2024-10-21T12:38:38Z" + message: FerretDBOpsRequest has started to restart FerretDB nodes + observedGeneration: 1 + reason: Restart + status: "True" + type: Restart + - lastTransitionTime: "2024-10-21T12:38:46Z" + message: get pod; ConditionStatus:True; PodName:ferretdb-0 + observedGeneration: 1 + status: "True" + type: GetPod--ferretdb-0 + - lastTransitionTime: "2024-10-21T12:38:46Z" + message: evict pod; ConditionStatus:True; PodName:ferretdb-0 + observedGeneration: 1 + status: "True" + type: EvictPod--ferretdb-0 + - lastTransitionTime: "2024-10-21T12:38:51Z" + message: check pod running; ConditionStatus:True; PodName:ferretdb-0 + observedGeneration: 1 + status: "True" + type: CheckPodRunning--ferretdb-0 + - lastTransitionTime: "2024-10-21T12:38:56Z" + message: Successfully restarted FerretDB nodes + observedGeneration: 1 + reason: RestartNodes + status: "True" + type: RestartNodes + - lastTransitionTime: "2024-10-21T12:38:56Z" + message: Controller has successfully restart the FerretDB replicas + observedGeneration: 1 + reason: Successful + status: "True" + type: Successful + observedGeneration: 1 + phase: Successful +``` + + +## Cleaning up + +To clean up the Kubernetes resources created by this tutorial, run: + +```bash +kubectl delete ferretdbopsrequest -n demo restart-ferretdb +kubectl delete ferretdb -n demo ferretdb +kubectl delete ns demo +``` + +## Next Steps + +- Detail concepts of [FerretDB object](/docs/guides/ferretdb/concepts/ferretdb.md). +- Monitor your FerretDB database with KubeDB using [out-of-the-box Prometheus operator](/docs/guides/ferretdb/monitoring/using-prometheus-operator.md). +- Monitor your FerretDB database with KubeDB using [out-of-the-box builtin-Prometheus](/docs/guides/ferretdb/monitoring/using-builtin-prometheus.md). +- Detail concepts of [FerretDB object](/docs/guides/ferretdb/concepts/ferretdb.md). +- Want to hack on KubeDB? Check our [contribution guidelines](/docs/CONTRIBUTING.md). diff --git a/docs/guides/ferretdb/scaling/_index.md b/docs/guides/ferretdb/scaling/_index.md new file mode 100644 index 0000000000..871b7e0178 --- /dev/null +++ b/docs/guides/ferretdb/scaling/_index.md @@ -0,0 +1,10 @@ +--- +title: Scaling FerretDB +menu: + docs_{{ .version }}: + identifier: fr-scaling + name: Scaling + parent: fr-ferretdb-guides + weight: 43 +menu_name: docs_{{ .version }} +--- \ No newline at end of file diff --git a/docs/guides/ferretdb/scaling/horizontal-scaling/_index.md b/docs/guides/ferretdb/scaling/horizontal-scaling/_index.md new file mode 100644 index 0000000000..123b627e9a --- /dev/null +++ b/docs/guides/ferretdb/scaling/horizontal-scaling/_index.md @@ -0,0 +1,10 @@ +--- +title: Horizontal Scaling +menu: + docs_{{ .version }}: + identifier: fr-horizontal-scaling + name: Horizontal Scaling + parent: fr-scaling + weight: 10 +menu_name: docs_{{ .version }} +--- \ No newline at end of file diff --git a/docs/guides/ferretdb/scaling/horizontal-scaling/horizontal-ops.md b/docs/guides/ferretdb/scaling/horizontal-scaling/horizontal-ops.md new file mode 100644 index 0000000000..4929ffbfac --- /dev/null +++ b/docs/guides/ferretdb/scaling/horizontal-scaling/horizontal-ops.md @@ -0,0 +1,432 @@ +--- +title: Horizontal Scaling FerretDB +menu: + docs_{{ .version }}: + identifier: fr-horizontal-scaling-ops + name: HorizontalScaling OpsRequest + parent: fr-horizontal-scaling + weight: 20 +menu_name: docs_{{ .version }} +section_menu_id: guides +--- + +> New to KubeDB? Please start [here](/docs/README.md). + +# Horizontal Scale FerretDB + +This guide will show you how to use `KubeDB` Ops-manager operator to scale the replicaset of a FerretDB. + +## Before You Begin + +- At first, you need to have a Kubernetes cluster, and the `kubectl` command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by using [kind](https://kind.sigs.k8s.io/docs/user/quick-start/). + +- Install `KubeDB` Provisioner and Ops-manager operator in your cluster following the steps [here](/docs/setup/README.md). + +- You should be familiar with the following `KubeDB` concepts: + - [FerretDB](/docs/guides/ferretdb/concepts/ferretdb.md) + - [FerretDBOpsRequest](/docs/guides/ferretdb/concepts/opsrequest.md) + - [Horizontal Scaling Overview](/docs/guides/ferretdb/scaling/horizontal-scaling/overview.md) + +To keep everything isolated, we are going to use a separate namespace called `demo` throughout this tutorial. + +```bash +$ kubectl create ns demo +namespace/demo created +``` + +> **Note:** YAML files used in this tutorial are stored in [docs/examples/ferretdb](/docs/examples/ferretdb) directory of [kubedb/docs](https://github.com/kubedb/docs) repository. + +## Apply Horizontal Scaling on ferretdb + +Here, we are going to deploy a `FerretDB` using a supported version by `KubeDB` operator. Then we are going to apply horizontal scaling on it. + +### Prepare FerretDB + +Now, we are going to deploy a `FerretDB` with version `1.23.0`. + +### Deploy FerretDB + +In this section, we are going to deploy a FerretDB. Then, in the next section we will scale the ferretdb using `FerretDBOpsRequest` CRD. Below is the YAML of the `FerretDB` CR that we are going to create, + +```yaml +apiVersion: kubedb.com/v1alpha2 +kind: FerretDB +metadata: + name: fr-horizontal + namespace: demo +spec: + version: "1.23.0" + replicas: 1 + backend: + externallyManaged: false + storage: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 500Mi + deletionPolicy: WipeOut +``` + +```bash +$ kubectl create -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/ferretdb/scaling/fr-horizontal.yaml +ferretdb.kubedb.com/fr-horizontal created +``` + +Now, wait until `fr-horizontal ` has status `Ready`. i.e, + +```bash +$ kubectl get fr -n demo +NAME TYPE VERSION STATUS AGE +fr-horizontal kubedb.com/v1alpha2 1.23.0 Ready 2m +``` + +Let's check the number of replicas this ferretdb has from the FerretDB object, number of pods the petset have, + +```bash +$ kubectl get ferretdb -n demo fr-horizontal -o json | jq '.spec.replicas' +1 + +$ kubectl get petset -n demo fr-horizontal -o json | jq '.spec.replicas' +1 +``` + +We can see from both command that the ferretdb has 1 replicas. + +We are now ready to apply the `FerretDBOpsRequest` CR to scale this ferretdb. + +## Scale Up Replicas + +Here, we are going to scale up the replicas of the ferretdb to meet the desired number of replicas after scaling. + +#### Create FerretDBOpsRequest + +In order to scale up the replicas of the ferretdb, we have to create a `FerretDBOpsRequest` CR with our desired replicas. Below is the YAML of the `FerretDBOpsRequest` CR that we are going to create, + +```yaml +apiVersion: ops.kubedb.com/v1alpha1 +kind: FerretDBOpsRequest +metadata: + name: ferretdb-horizontal-scale-up + namespace: demo +spec: + type: HorizontalScaling + databaseRef: + name: fr-horizontal + horizontalScaling: + node: 3 +``` + +Here, + +- `spec.databaseRef.name` specifies that we are performing horizontal scaling operation on `fr-horizontal` ferretdb. +- `spec.type` specifies that we are performing `HorizontalScaling` on our ferretdb. +- `spec.horizontalScaling.replicas` specifies the desired replicas after scaling. + +Let's create the `FerretDBOpsRequest` CR we have shown above, + +```bash +$ kubectl apply -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/ferretdb/scaling/horizontal-scaling/frops-hscale-up-ops.yaml +ferretdbopsrequest.ops.kubedb.com/ferretdb-horizontal-scale-up created +``` + +#### Verify replicas scaled up successfully + +If everything goes well, `KubeDB` Ops-manager operator will update the replicas of `FerretDB` object and related `PetSet`. + +Let's wait for `FerretDBOpsRequest` to be `Successful`. Run the following command to watch `FerretDBOpsRequest` CR, + +```bash +$ watch kubectl get ferretdbopsrequest -n demo +Every 2.0s: kubectl get ferretdbopsrequest -n demo +NAME TYPE STATUS AGE +ferretdb-horizontal-scale-up HorizontalScaling Successful 102s +``` + +We can see from the above output that the `FerretDBOpsRequest` has succeeded. If we describe the `FerretDBOpsRequest` we will get an overview of the steps that were followed to scale the ferretdb. + +```bash +$ kubectl describe ferretdbopsrequest -n demo ferretdb-horizontal-scale-up +Name: ferretdb-horizontal-scale-up +Namespace: demo +Labels: +Annotations: +API Version: ops.kubedb.com/v1alpha1 +Kind: FerretDBOpsRequest +Metadata: + Creation Timestamp: 2024-10-21T10:03:39Z + Generation: 1 + Resource Version: 353610 + UID: ce6c9e66-6196-4746-851a-ea49084eda05 +Spec: + Apply: IfReady + Database Ref: + Name: fr-horizontal + Horizontal Scaling: + Node: 3 + Type: HorizontalScaling +Status: + Conditions: + Last Transition Time: 2024-10-21T10:04:30Z + Message: FerretDB ops-request has started to horizontally scaling the nodes + Observed Generation: 1 + Reason: HorizontalScaling + Status: True + Type: HorizontalScaling + Last Transition Time: 2024-10-21T10:04:33Z + Message: Successfully paused database + Observed Generation: 1 + Reason: DatabasePauseSucceeded + Status: True + Type: DatabasePauseSucceeded + Last Transition Time: 2024-10-21T10:04:58Z + Message: Successfully Scaled Up Node + Observed Generation: 1 + Reason: HorizontalScaleUp + Status: True + Type: HorizontalScaleUp + Last Transition Time: 2024-10-21T10:04:38Z + Message: patch petset; ConditionStatus:True; PodName:fr-horizontal-1 + Observed Generation: 1 + Status: True + Type: PatchPetset--fr-horizontal-1 + Last Transition Time: 2024-10-21T10:04:43Z + Message: is pod ready; ConditionStatus:True; PodName:fr-horizontal-1 + Observed Generation: 1 + Status: True + Type: IsPodReady--fr-horizontal-1 + Last Transition Time: 2024-10-21T10:04:43Z + Message: client failure; ConditionStatus:True; PodName:fr-horizontal-1 + Observed Generation: 1 + Status: True + Type: ClientFailure--fr-horizontal-1 + Last Transition Time: 2024-10-21T10:04:43Z + Message: is node healthy; ConditionStatus:True; PodName:fr-horizontal-1 + Observed Generation: 1 + Status: True + Type: IsNodeHealthy--fr-horizontal-1 + Last Transition Time: 2024-10-21T10:04:48Z + Message: patch petset; ConditionStatus:True; PodName:fr-horizontal-2 + Observed Generation: 1 + Status: True + Type: PatchPetset--fr-horizontal-2 + Last Transition Time: 2024-10-21T10:04:48Z + Message: fr-horizontal already has desired replicas + Observed Generation: 1 + Reason: HorizontalScale + Status: True + Type: HorizontalScale + Last Transition Time: 2024-10-21T10:04:53Z + Message: is pod ready; ConditionStatus:True; PodName:fr-horizontal-2 + Observed Generation: 1 + Status: True + Type: IsPodReady--fr-horizontal-2 + Last Transition Time: 2024-10-21T10:04:53Z + Message: client failure; ConditionStatus:True; PodName:fr-horizontal-2 + Observed Generation: 1 + Status: True + Type: ClientFailure--fr-horizontal-2 + Last Transition Time: 2024-10-21T10:04:53Z + Message: is node healthy; ConditionStatus:True; PodName:fr-horizontal-2 + Observed Generation: 1 + Status: True + Type: IsNodeHealthy--fr-horizontal-2 + Last Transition Time: 2024-10-21T10:04:58Z + Message: Successfully updated FerretDB + Observed Generation: 1 + Reason: UpdateDatabase + Status: True + Type: UpdateDatabase + Last Transition Time: 2024-10-21T10:04:58Z + Message: Successfully completed the HorizontalScaling for FerretDB + Observed Generation: 1 + Reason: Successful + Status: True + Type: Successful + Observed Generation: 1 + Phase: Successful +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal Starting 67s KubeDB Ops-manager Operator Start processing for FerretDBOpsRequest: demo/ferretdb-horizontal-scale-up + Normal Starting 67s KubeDB Ops-manager Operator Pausing FerretDB database: demo/fr-horizontal + Normal Successful 67s KubeDB Ops-manager Operator Successfully paused FerretDB database: demo/fr-horizontal for FerretDBOpsRequest: ferretdb-horizontal-scale-up + Warning patch petset; ConditionStatus:True; PodName:fr-horizontal-1 59s KubeDB Ops-manager Operator patch petset; ConditionStatus:True; PodName:fr-horizontal-1 + Warning is pod ready; ConditionStatus:True; PodName:fr-horizontal-1 54s KubeDB Ops-manager Operator is pod ready; ConditionStatus:True; PodName:fr-horizontal-1 + Warning client failure; ConditionStatus:True; PodName:fr-horizontal-1 54s KubeDB Ops-manager Operator client failure; ConditionStatus:True; PodName:fr-horizontal-1 + Warning is node healthy; ConditionStatus:True; PodName:fr-horizontal-1 54s KubeDB Ops-manager Operator is node healthy; ConditionStatus:True; PodName:fr-horizontal-1 + Warning patch petset; ConditionStatus:True; PodName:fr-horizontal-2 49s KubeDB Ops-manager Operator patch petset; ConditionStatus:True; PodName:fr-horizontal-2 + Warning is pod ready; ConditionStatus:True; PodName:fr-horizontal-2 44s KubeDB Ops-manager Operator is pod ready; ConditionStatus:True; PodName:fr-horizontal-2 + Warning client failure; ConditionStatus:True; PodName:fr-horizontal-2 44s KubeDB Ops-manager Operator client failure; ConditionStatus:True; PodName:fr-horizontal-2 + Warning is node healthy; ConditionStatus:True; PodName:fr-horizontal-2 44s KubeDB Ops-manager Operator is node healthy; ConditionStatus:True; PodName:fr-horizontal-2 + Normal HorizontalScaleUp 39s KubeDB Ops-manager Operator Successfully Scaled Up Node + Normal UpdateDatabase 39s KubeDB Ops-manager Operator Successfully updated FerretDB + Normal Starting 39s KubeDB Ops-manager Operator Resuming FerretDB database: demo/fr-horizontal + Normal Successful 39s KubeDB Ops-manager Operator Successfully resumed FerretDB database: demo/fr-horizontal for FerretDBOpsRequest: ferretdb-horizontal-scale-up +``` + +Now, we are going to verify the number of replicas this ferretdb has from the FerretDB object, number of pods the petset have, + +```bash +$ kubectl get fr -n demo fr-horizontal -o json | jq '.spec.replicas' +3 + +$ kubectl get petset -n demo fr-horizontal -o json | jq '.spec.replicas' +3 +``` +From all the above outputs we can see that the replicas of the ferretdb is `3`. That means we have successfully scaled up the replicas of the FerretDB. + + +### Scale Down Replicas + +Here, we are going to scale down the replicas of the ferretdb to meet the desired number of replicas after scaling. + +#### Create FerretDBOpsRequest + +In order to scale down the replicas of the ferretdb, we have to create a `FerretDBOpsRequest` CR with our desired replicas. Below is the YAML of the `FerretDBOpsRequest` CR that we are going to create, + +```yaml +apiVersion: ops.kubedb.com/v1alpha1 +kind: FerretDBOpsRequest +metadata: + name: ferretdb-horizontal-scale-down + namespace: demo +spec: + type: HorizontalScaling + databaseRef: + name: fr-horizontal + horizontalScaling: + node: 2 +``` + +Here, + +- `spec.databaseRef.name` specifies that we are performing horizontal scaling down operation on `fr-horizontal` ferretdb. +- `spec.type` specifies that we are performing `HorizontalScaling` on our ferretdb. +- `spec.horizontalScaling.replicas` specifies the desired replicas after scaling. + +Let's create the `FerretDBOpsRequest` CR we have shown above, + +```bash +$ kubectl apply -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/ferretdb/scaling/horizontal-scaling/frops-hscale-down-ops.yaml +ferretdbopsrequest.ops.kubedb.com/ferretdb-horizontal-scale-down created +``` + +#### Verify replicas scaled down successfully + +If everything goes well, `KubeDB` Ops-manager operator will update the replicas of `FerretDB` object and related `PetSet`. + +Let's wait for `FerretDBOpsRequest` to be `Successful`. Run the following command to watch `FerretDBOpsRequest` CR, + +```bash +$ watch kubectl get ferretdbopsrequest -n demo +Every 2.0s: kubectl get ferretdbopsrequest -n demo +NAME TYPE STATUS AGE +ferretdb-horizontal-scale-down HorizontalScaling Successful 40s +``` + +We can see from the above output that the `FerretDBOpsRequest` has succeeded. If we describe the `FerretDBOpsRequest` we will get an overview of the steps that were followed to scale the ferretdb. + +```bash +$ kubectl describe ferretdbopsrequest -n demo ferretdb-horizontal-scale-down +Name: ferretdb-horizontal-scale-down +Namespace: demo +Labels: +Annotations: +API Version: ops.kubedb.com/v1alpha1 +Kind: FerretDBOpsRequest +Metadata: + Creation Timestamp: 2024-10-21T10:06:42Z + Generation: 1 + Resource Version: 353838 + UID: 69cb9e8a-ec89-41e2-9e91-ce61a68044b9 +Spec: + Apply: IfReady + Database Ref: + Name: fr-horizontal + Horizontal Scaling: + Node: 2 + Type: HorizontalScaling +Status: + Conditions: + Last Transition Time: 2024-10-21T10:06:42Z + Message: FerretDB ops-request has started to horizontally scaling the nodes + Observed Generation: 1 + Reason: HorizontalScaling + Status: True + Type: HorizontalScaling + Last Transition Time: 2024-10-21T10:06:45Z + Message: Successfully paused database + Observed Generation: 1 + Reason: DatabasePauseSucceeded + Status: True + Type: DatabasePauseSucceeded + Last Transition Time: 2024-10-21T10:07:00Z + Message: Successfully Scaled Down Node + Observed Generation: 1 + Reason: HorizontalScaleDown + Status: True + Type: HorizontalScaleDown + Last Transition Time: 2024-10-21T10:06:50Z + Message: patch petset; ConditionStatus:True; PodName:fr-horizontal-2 + Observed Generation: 1 + Status: True + Type: PatchPetset--fr-horizontal-2 + Last Transition Time: 2024-10-21T10:06:51Z + Message: fr-horizontal already has desired replicas + Observed Generation: 1 + Reason: HorizontalScale + Status: True + Type: HorizontalScale + Last Transition Time: 2024-10-21T10:06:55Z + Message: get pod; ConditionStatus:True; PodName:fr-horizontal-2 + Observed Generation: 1 + Status: True + Type: GetPod--fr-horizontal-2 + Last Transition Time: 2024-10-21T10:07:00Z + Message: Successfully updated FerretDB + Observed Generation: 1 + Reason: UpdateDatabase + Status: True + Type: UpdateDatabase + Last Transition Time: 2024-10-21T10:07:00Z + Message: Successfully completed the HorizontalScaling for FerretDB + Observed Generation: 1 + Reason: Successful + Status: True + Type: Successful + Observed Generation: 1 + Phase: Successful +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal Starting 55s KubeDB Ops-manager Operator Start processing for FerretDBOpsRequest: demo/ferretdb-horizontal-scale-down + Normal Starting 55s KubeDB Ops-manager Operator Pausing FerretDB database: demo/fr-horizontal + Normal Successful 55s KubeDB Ops-manager Operator Successfully paused FerretDB database: demo/fr-horizontal for FerretDBOpsRequest: ferretdb-horizontal-scale-down + Warning patch petset; ConditionStatus:True; PodName:fr-horizontal-2 47s KubeDB Ops-manager Operator patch petset; ConditionStatus:True; PodName:fr-horizontal-2 + Warning get pod; ConditionStatus:True; PodName:fr-horizontal-2 42s KubeDB Ops-manager Operator get pod; ConditionStatus:True; PodName:fr-horizontal-2 + Normal HorizontalScaleDown 37s KubeDB Ops-manager Operator Successfully Scaled Down Node + Normal UpdateDatabase 37s KubeDB Ops-manager Operator Successfully updated FerretDB + Normal Starting 37s KubeDB Ops-manager Operator Resuming FerretDB database: demo/fr-horizontal + Normal Successful 37s KubeDB Ops-manager Operator Successfully resumed FerretDB database: demo/fr-horizontal for FerretDBOpsRequest: ferretdb-horizontal-scale-down +``` + +Now, we are going to verify the number of replicas this ferretdb has from the FerretDB object, number of pods the petset have, + +```bash +$ kubectl get fr -n demo fr-horizontal -o json | jq '.spec.replicas' +2 + +$ kubectl get petset -n demo fr-horizontal -o json | jq '.spec.replicas' +2 +``` +From all the above outputs we can see that the replicas of the ferretdb is `2`. That means we have successfully scaled up the replicas of the FerretDB. + +## Cleaning Up + +To clean up the Kubernetes resources created by this tutorial, run: + +```bash +kubectl delete mg -n fr-horizontal +kubectl delete ferretdbopsrequest -n demo ferretdb-horizontal-scale-down +``` \ No newline at end of file diff --git a/docs/guides/ferretdb/scaling/horizontal-scaling/overview.md b/docs/guides/ferretdb/scaling/horizontal-scaling/overview.md new file mode 100644 index 0000000000..b177fdc2d4 --- /dev/null +++ b/docs/guides/ferretdb/scaling/horizontal-scaling/overview.md @@ -0,0 +1,54 @@ +--- +title: FerretDB Horizontal Scaling Overview +menu: + docs_{{ .version }}: + identifier: fr-horizontal-scaling-overview + name: Overview + parent: fr-horizontal-scaling + weight: 10 +menu_name: docs_{{ .version }} +section_menu_id: guides +--- + +> New to KubeDB? Please start [here](/docs/README.md). + +# FerretDB Horizontal Scaling + +This guide will give an overview on how KubeDB Ops-manager operator scales up or down `FerretDB` replicas of PetSet. + +## Before You Begin + +- You should be familiar with the following `KubeDB` concepts: + - [FerretDB](/docs/guides/ferretdb/concepts/ferretdb.md) + - [FerretDBOpsRequest](/docs/guides/ferretdb/concepts/opsrequest.md) + +## How Horizontal Scaling Process Works + +The following diagram shows how KubeDB Ops-manager operator scales up or down `FerretDB` database components. Open the image in a new tab to see the enlarged version. + +
+  Horizontal scaling process of FerretDB +
Fig: Horizontal scaling process of FerretDB
+
+ +The Horizontal scaling process consists of the following steps: + +1. At first, a user creates a `FerretDB` Custom Resource (CR). + +2. `KubeDB` Provisioner operator watches the `FerretDB` CR. + +3. When the operator finds a `FerretDB` CR, it creates `PetSet` and related necessary stuff like secrets, services, etc. + +4. Then, in order to scale the `PetSet` of the `FerretDB` database the user creates a `FerretDBOpsRequest` CR with desired information. + +5. `KubeDB` Ops-manager operator watches the `FerretDBOpsRequest` CR. + +6. When it finds a `FerretDBOpsRequest` CR, it pauses the `FerretDB` object which is referred from the `FerretDBOpsRequest`. So, the `KubeDB` Provisioner operator doesn't perform any operations on the `FerretDB` object during the horizontal scaling process. + +7. Then the `KubeDB` Ops-manager operator will scale the related PetSet Pods to reach the expected number of replicas defined in the `FerretDBOpsRequest` CR. + +8. After the successfully scaling the replicas of the related PetSet Pods, the `KubeDB` Ops-manager operator updates the number of replicas in the `FerretDB` object to reflect the updated state. + +9. After the successful scaling of the `FerretDB` replicas, the `KubeDB` Ops-manager operator resumes the `FerretDB` object so that the `KubeDB` Provisioner operator resumes its usual operations. + +In the next docs, we are going to show a step-by-step guide on horizontal scaling of FerretDB using `FerretDBOpsRequest` CRD. \ No newline at end of file diff --git a/docs/guides/ferretdb/scaling/vertical-scaling/_index.md b/docs/guides/ferretdb/scaling/vertical-scaling/_index.md new file mode 100644 index 0000000000..2597e01702 --- /dev/null +++ b/docs/guides/ferretdb/scaling/vertical-scaling/_index.md @@ -0,0 +1,10 @@ +--- +title: Vertical Scaling +menu: + docs_{{ .version }}: + identifier: fr-vertical-scaling + name: Vertical Scaling + parent: fr-scaling + weight: 20 +menu_name: docs_{{ .version }} +--- \ No newline at end of file diff --git a/docs/guides/ferretdb/scaling/vertical-scaling/overview.md b/docs/guides/ferretdb/scaling/vertical-scaling/overview.md new file mode 100644 index 0000000000..f5d5f369c1 --- /dev/null +++ b/docs/guides/ferretdb/scaling/vertical-scaling/overview.md @@ -0,0 +1,54 @@ +--- +title: FerretDB Vertical Scaling Overview +menu: + docs_{{ .version }}: + identifier: fr-vertical-scaling-overview + name: Overview + parent: fr-vertical-scaling + weight: 10 +menu_name: docs_{{ .version }} +section_menu_id: guides +--- + +> New to KubeDB? Please start [here](/docs/README.md). + +# FerretDB Vertical Scaling + +This guide will give an overview on how KubeDB Ops-manager operator updates the resources(for example CPU and Memory etc.) of the `FerretDB`. + +## Before You Begin + +- You should be familiar with the following `KubeDB` concepts: + - [FerretDB](/docs/guides/ferretdb/concepts/ferretdb.md) + - [FerretDBOpsRequest](/docs/guides/ferretdb/concepts/opsrequest.md) + +## How Vertical Scaling Process Works + +The following diagram shows how KubeDB Ops-manager operator updates the resources of the `FerretDB`. Open the image in a new tab to see the enlarged version. + +
+  Vertical scaling process of FerretDB +
Fig: Vertical scaling process of FerretDB
+
+ +The vertical scaling process consists of the following steps: + +1. At first, a user creates a `FerretDB` Custom Resource (CR). + +2. `KubeDB` Provisioner operator watches the `FerretDB` CR. + +3. When the operator finds a `FerretDB` CR, it creates `PetSet` and related necessary stuff like secrets, services, etc. + +4. Then, in order to update the resources(for example `CPU`, `Memory` etc.) of the `FerretDB`, the user creates a `FerretDBOpsRequest` CR with desired information. + +5. `KubeDB` Ops-manager operator watches the `FerretDBOpsRequest` CR. + +6. When it finds a `FerretDBOpsRequest` CR, it pauses the `FerretDB` object which is referred from the `FerretDBOpsRequest`. So, the `KubeDB` Provisioner operator doesn't perform any operations on the `FerretDB` object during the vertical scaling process. + +7. Then the `KubeDB` Ops-manager operator will update resources of the PetSet to reach desired state. + +8. After the successful update of the resources of the PetSet's replica, the `KubeDB` Ops-manager operator updates the `FerretDB` object to reflect the updated state. + +9. After the successful update of the `FerretDB` resources, the `KubeDB` Ops-manager operator resumes the `FerretDB` object so that the `KubeDB` Provisioner operator resumes its usual operations. + +In the next docs, we are going to show a step-by-step guide on updating resources of FerretDB `FerretDBOpsRequest` CRD. \ No newline at end of file diff --git a/docs/guides/ferretdb/scaling/vertical-scaling/vertical-ops.md b/docs/guides/ferretdb/scaling/vertical-scaling/vertical-ops.md new file mode 100644 index 0000000000..b22b46ea70 --- /dev/null +++ b/docs/guides/ferretdb/scaling/vertical-scaling/vertical-ops.md @@ -0,0 +1,282 @@ +--- +title: Vertical Scaling FerretDB +menu: + docs_{{ .version }}: + identifier: fr-vertical-scaling-ops + name: VerticalScaling OpsRequest + parent: fr-vertical-scaling + weight: 20 +menu_name: docs_{{ .version }} +section_menu_id: guides +--- + +> New to KubeDB? Please start [here](/docs/README.md). + +# Vertical Scale FerretDB + +This guide will show you how to use `KubeDB` Ops-manager operator to update the resources of a FerretDB. + +## Before You Begin + +- At first, you need to have a Kubernetes cluster, and the `kubectl` command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by using [kind](https://kind.sigs.k8s.io/docs/user/quick-start/). + +- Install `KubeDB` Provisioner and Ops-manager operator in your cluster following the steps [here](/docs/setup/README.md). + +- You should be familiar with the following `KubeDB` concepts: + - [FerretDB](/docs/guides/ferretdb/concepts/ferretdb.md) + - [FerretDBOpsRequest](/docs/guides/ferretdb/concepts/opsrequest.md) + - [Vertical Scaling Overview](/docs/guides/ferretdb/scaling/vertical-scaling/overview.md) + +To keep everything isolated, we are going to use a separate namespace called `demo` throughout this tutorial. + +```bash +$ kubectl create ns demo +namespace/demo created +``` + +> **Note:** YAML files used in this tutorial are stored in [docs/examples/ferretdb](/docs/examples/ferretdb) directory of [kubedb/docs](https://github.com/kubedb/docs) repository. + +## Apply Vertical Scaling on FerretDB + +Here, we are going to deploy a `FerretDB` using a supported version by `KubeDB` operator. Then we are going to apply vertical scaling on it. + +### Prepare FerretDB + +Now, we are going to deploy a `FerretDB` with version `1.23.0`. + +### Deploy FerretDB + +In this section, we are going to deploy a FerretDB. Then, in the next section we will update the resources using `FerretDBOpsRequest` CRD. Below is the YAML of the `FerretDB` CR that we are going to create, + +```yaml +apiVersion: kubedb.com/v1alpha2 +kind: FerretDB +metadata: + name: fr-vertical + namespace: demo +spec: + version: "1.23.0" + replicas: 1 + backend: + externallyManaged: false + storage: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 500Mi + deletionPolicy: WipeOut +``` + +Let's create the `FerretDB` CR we have shown above, + +```bash +$ kubectl create -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/ferretdb/scaling/fr-vertical.yaml +ferretdb.kubedb.com/fr-vertical created +``` + +Now, wait until `fr-vertical` has status `Ready`. i.e, + +```bash +$ kubectl get fr -n demo +NAME TYPE VERSION STATUS AGE +fr-vertical kubedb.com/v1alpha2 1.23.0 Ready 17s +``` + +Let's check the Pod containers resources, + +```bash +$ kubectl get pod -n demo fr-vertical-0 -o json | jq '.spec.containers[].resources' +{ + "limits": { + "memory": "1Gi" + }, + "requests": { + "cpu": "500m", + "memory": "1Gi" + } +} +``` + +You can see the Pod has default resources which is assigned by the KubeDB operator. + +We are now ready to apply the `FerretDBOpsRequest` CR to update the resources of this ferretdb. + +### Vertical Scaling + +Here, we are going to update the resources of the ferretdb to meet the desired resources after scaling. + +#### Create FerretDBOpsRequest + +In order to update the resources of the ferretdb, we have to create a `FerretDBOpsRequest` CR with our desired resources. Below is the YAML of the `FerretDBOpsRequest` CR that we are going to create, + +```yaml +apiVersion: ops.kubedb.com/v1alpha1 +kind: FerretDBOpsRequest +metadata: + name: ferretdb-scale-vertical + namespace: demo +spec: + type: VerticalScaling + databaseRef: + name: fr-vertical + verticalScaling: + node: + resources: + requests: + memory: "2Gi" + cpu: "1" + limits: + memory: "2Gi" + cpu: "1" + timeout: 5m + apply: IfReady +``` + +Here, + +- `spec.databaseRef.name` specifies that we are performing vertical scaling operation on `fr-vertical` ferretdb. +- `spec.type` specifies that we are performing `VerticalScaling` on our database. +- `spec.VerticalScaling.standalone` specifies the desired resources after scaling. +- Have a look [here](/docs/guides/ferretdb/concepts/opsrequest.md) on the respective sections to understand the `timeout` & `apply` fields. + +Let's create the `FerretDBOpsRequest` CR we have shown above, + +```bash +$ kubectl apply -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/ferretdb/scaling/vertical-scaling/fr-vertical-ops.yaml +ferretdbopsrequest.ops.kubedb.com/ferretdb-scale-vertical created +``` + +#### Verify FerretDB resources updated successfully + +If everything goes well, `KubeDB` Ops-manager operator will update the resources of `FerretDB` object and related `PetSet` and `Pods`. + +Let's wait for `FerretDBOpsRequest` to be `Successful`. Run the following command to watch `FerretDBOpsRequest` CR, + +```bash +$ kubectl get ferretdbopsrequest -n demo +Every 2.0s: kubectl get ferretdbopsrequest -n demo +NAME TYPE STATUS AGE +ferretdb-scale-vertical VerticalScaling Successful 44s +``` + +We can see from the above output that the `FerretDBOpsRequest` has succeeded. If we describe the `FerretDBOpsRequest` we will get an overview of the steps that were followed to scale the ferretdb. + +```bash +$ kubectl describe ferretdbopsrequest -n demo ferretdb-scale-vertical +Name: ferretdb-scale-vertical +Namespace: demo +Labels: +Annotations: +API Version: ops.kubedb.com/v1alpha1 +Kind: FerretDBOpsRequest +Metadata: + Creation Timestamp: 2024-10-21T12:25:33Z + Generation: 1 + Resource Version: 366310 + UID: 38631646-684f-4c2a-8496-c7b085743243 +Spec: + Apply: IfReady + Database Ref: + Name: fr-vertical + Timeout: 5m + Type: VerticalScaling + Vertical Scaling: + Node: + Resources: + Limits: + Cpu: 1 + Memory: 2Gi + Requests: + Cpu: 1 + Memory: 2Gi +Status: + Conditions: + Last Transition Time: 2024-10-21T12:25:33Z + Message: FerretDB ops-request has started to vertically scaling the FerretDB nodes + Observed Generation: 1 + Reason: VerticalScaling + Status: True + Type: VerticalScaling + Last Transition Time: 2024-10-21T12:25:36Z + Message: Successfully paused database + Observed Generation: 1 + Reason: DatabasePauseSucceeded + Status: True + Type: DatabasePauseSucceeded + Last Transition Time: 2024-10-21T12:25:37Z + Message: Successfully updated PetSets Resources + Observed Generation: 1 + Reason: UpdatePetSets + Status: True + Type: UpdatePetSets + Last Transition Time: 2024-10-21T12:25:42Z + Message: get pod; ConditionStatus:True; PodName:fr-vertical-0 + Observed Generation: 1 + Status: True + Type: GetPod--fr-vertical-0 + Last Transition Time: 2024-10-21T12:25:42Z + Message: evict pod; ConditionStatus:True; PodName:fr-vertical-0 + Observed Generation: 1 + Status: True + Type: EvictPod--fr-vertical-0 + Last Transition Time: 2024-10-21T12:25:47Z + Message: check pod running; ConditionStatus:True; PodName:fr-vertical-0 + Observed Generation: 1 + Status: True + Type: CheckPodRunning--fr-vertical-0 + Last Transition Time: 2024-10-21T12:25:52Z + Message: Successfully Restarted Pods With Resources + Observed Generation: 1 + Reason: RestartPods + Status: True + Type: RestartPods + Last Transition Time: 2024-10-21T12:25:52Z + Message: Successfully completed the VerticalScaling for FerretDB + Observed Generation: 1 + Reason: Successful + Status: True + Type: Successful + Observed Generation: 1 + Phase: Successful +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal Starting 58s KubeDB Ops-manager Operator Start processing for FerretDBOpsRequest: demo/ferretdb-scale-vertical + Normal Starting 58s KubeDB Ops-manager Operator Pausing FerretDB database: demo/fr-vertical + Normal Successful 58s KubeDB Ops-manager Operator Successfully paused FerretDB database: demo/fr-vertical for FerretDBOpsRequest: ferretdb-scale-vertical + Normal UpdatePetSets 54s KubeDB Ops-manager Operator Successfully updated PetSets Resources + Warning get pod; ConditionStatus:True; PodName:fr-vertical-0 49s KubeDB Ops-manager Operator get pod; ConditionStatus:True; PodName:fr-vertical-0 + Warning evict pod; ConditionStatus:True; PodName:fr-vertical-0 49s KubeDB Ops-manager Operator evict pod; ConditionStatus:True; PodName:fr-vertical-0 + Warning check pod running; ConditionStatus:True; PodName:fr-vertical-0 44s KubeDB Ops-manager Operator check pod running; ConditionStatus:True; PodName:fr-vertical-0 + Normal RestartPods 39s KubeDB Ops-manager Operator Successfully Restarted Pods With Resources + Normal Starting 39s KubeDB Ops-manager Operator Resuming FerretDB database: demo/fr-vertical + Normal Successful 39s KubeDB Ops-manager Operator Successfully resumed FerretDB database: demo/fr-vertical for FerretDBOpsRequest: ferretdb-scale-vertical +``` + +Now, we are going to verify from the Pod yaml whether the resources of the ferretdb has updated to meet up the desired state, Let's check, + +```bash +$ kubectl get pod -n demo fr-vertical-0 -o json | jq '.spec.containers[].resources' +{ + "limits": { + "cpu": "1", + "memory": "2Gi" + }, + "requests": { + "cpu": "1", + "memory": "2Gi" + } +} +``` + +The above output verifies that we have successfully scaled up the resources of the FerretDB. + +## Cleaning Up + +To clean up the Kubernetes resources created by this tutorial, run: + +```bash +kubectl delete fr -n demo fr-vertical +kubectl delete ferretdbopsrequest -n demo ferretdb-scale-vertical +``` \ No newline at end of file diff --git a/docs/guides/ferretdb/tls/configure_tls.md b/docs/guides/ferretdb/tls/configure_tls.md index fbcb9a6d04..82480afc9b 100644 --- a/docs/guides/ferretdb/tls/configure_tls.md +++ b/docs/guides/ferretdb/tls/configure_tls.md @@ -3,7 +3,7 @@ title: FerretDB TLS/SSL Encryption menu: docs_{{ .version }}: identifier: fr-tls-configure - name: FerretDB_SSL + name: FerretDB TLS/SSL Configuration parent: fr-tls weight: 20 menu_name: docs_{{ .version }} @@ -131,10 +131,10 @@ spec: ```bash $ kubectl create -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/ferretdb/tls/ferretdb-tls.yaml -ferretdb.kubedb.com/pp-tls created +ferretdb.kubedb.com/fr-tls created ``` -Now, wait until `pp-tls created` has status `Ready`. i.e, +Now, wait until `fr-tls created` has status `Ready`. i.e, ```bash $ watch kubectl get fr -n demo @@ -214,7 +214,7 @@ For mongosh info see: https://docs.mongodb.com/mongodb-shell/ ------ The server generated these startup warnings when booting - 2024-03-12T05:56:50.979Z: Powered by FerretDB v1.18.0 and PostgreSQL 13.13 on x86_64-pc-linux-musl, compiled by gcc. + 2024-03-12T05:56:50.979Z: Powered by FerretDB v1.23.0 and PostgreSQL 13.13 on x86_64-pc-linux-musl, compiled by gcc. 2024-03-12T05:56:50.979Z: Please star us on GitHub: https://github.com/FerretDB/FerretDB. 2024-03-12T05:56:50.979Z: The telemetry state is undecided. 2024-03-12T05:56:50.979Z: Read more about FerretDB telemetry and how to opt out at https://beacon.ferretdb.io. diff --git a/docs/guides/ferretdb/update-version/_index.md b/docs/guides/ferretdb/update-version/_index.md new file mode 100644 index 0000000000..e0266958ab --- /dev/null +++ b/docs/guides/ferretdb/update-version/_index.md @@ -0,0 +1,10 @@ +--- +title: Updating FerretDB +menu: + docs_{{ .version }}: + identifier: fr-updating + name: UpdateVersion + parent: fr-ferretdb-guides + weight: 42 +menu_name: docs_{{ .version }} +--- \ No newline at end of file diff --git a/docs/guides/ferretdb/update-version/overview.md b/docs/guides/ferretdb/update-version/overview.md new file mode 100644 index 0000000000..0b20a2e9b9 --- /dev/null +++ b/docs/guides/ferretdb/update-version/overview.md @@ -0,0 +1,54 @@ +--- +title: Updating FerretDB Overview +menu: + docs_{{ .version }}: + identifier: fr-updating-overview + name: Overview + parent: fr-updating + weight: 10 +menu_name: docs_{{ .version }} +section_menu_id: guides +--- + +> New to KubeDB? Please start [here](/docs/README.md). + +# updating FerretDB version Overview + +This guide will give you an overview on how KubeDB Ops-manager operator update the version of `FerretDB`. + +## Before You Begin + +- You should be familiar with the following `KubeDB` concepts: + - [FerretDB](/docs/guides/ferretdb/concepts/ferretdb.md) + - [FerretDBOpsRequest](/docs/guides/ferretdb/concepts/opsrequest.md) + +## How update version Process Works + +The following diagram shows how KubeDB Ops-manager operator used to update the version of `FerretDB`. Open the image in a new tab to see the enlarged version. + +
+  updating Process of FerretDB +
Fig: updating Process of FerretDB
+
+ +The updating process consists of the following steps: + +1. At first, a user creates a `FerretDB` Custom Resource (CR). + +2. `KubeDB` Provisioner operator watches the `FerretDB` CR. + +3. When the operator finds a `FerretDB` CR, it creates required number of `PetSets` and related necessary stuff like secrets, services, etc. + +4. Then, in order to update the version of the `FerretDB` the user creates a `FerretDBOpsRequest` CR with the desired version. + +5. `KubeDB` Ops-manager operator watches the `FerretDBOpsRequest` CR. + +6. When it finds a `FerretDBOpsRequest` CR, it halts the `FerretDB` object which is referred from the `FerretDBOpsRequest`. So, the `KubeDB` Provisioner operator doesn't perform any operations on the `FerretDB` object during the updating process. + +7. By looking at the target version from `FerretDBOpsRequest` CR, `KubeDB` Ops-manager operator updates the image of the `PetSet`. + +8. After successfully updating the `PetSet` and their `Pods` images, the `KubeDB` Ops-manager operator updates the image of the `FerretDB` object to reflect the updated state of the database. + +9. After successfully updating of `FerretDB` object, the `KubeDB` Ops-manager operator resumes the `FerretDB` object so that the `KubeDB` Provisioner operator can resume its usual operations. + +In the next doc, we are going to show a step-by-step guide on updating of a FerretDB using updateVersion operation. \ No newline at end of file diff --git a/docs/guides/ferretdb/update-version/update-version.md b/docs/guides/ferretdb/update-version/update-version.md new file mode 100644 index 0000000000..d84da9d671 --- /dev/null +++ b/docs/guides/ferretdb/update-version/update-version.md @@ -0,0 +1,241 @@ +--- +title: Updating FerretDB +menu: + docs_{{ .version }}: + identifier: fr-updating-ferretdb + name: Update version + parent: fr-updating + weight: 20 +menu_name: docs_{{ .version }} +section_menu_id: guides +--- + +> New to KubeDB? Please start [here](/docs/README.md). + +# update version of FerretDB + +This guide will show you how to use `KubeDB` Ops-manager operator to update the version of `FerretDB`. + +## Before You Begin + +- At first, you need to have a Kubernetes cluster, and the `kubectl` command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by using [kind](https://kind.sigs.k8s.io/docs/user/quick-start/). + +- Install `KubeDB` Provisioner and Ops-manager operator in your cluster following the steps [here](/docs/setup/README.md). + +- You should be familiar with the following `KubeDB` concepts: + - [FerretDB](/docs/guides/ferretdb/concepts/ferretdb.md) + - [FerretDBOpsRequest](/docs/guides/ferretdb/concepts/opsrequest.md) + - [Updating Overview](/docs/guides/ferretdb/update-version/overview.md) + +To keep everything isolated, we are going to use a separate namespace called `demo` throughout this tutorial. + +```bash +$ kubectl create ns demo +namespace/demo created +``` + +> **Note:** YAML files used in this tutorial are stored in [docs/examples/ferretdb](/docs/examples/ferretdb) directory of [kubedb/docs](https://github.com/kube/docs) repository. + +### Prepare FerretDB + +Now, we are going to deploy a `FerretDB` =with version `1.18.0`. + +### Deploy FerretDB: + +In this section, we are going to deploy a FerretDB. Then, in the next section we will update the version using `FerretDBOpsRequest` CRD. Below is the YAML of the `FerretDB` CR that we are going to create, + +```yaml +apiVersion: kubedb.com/v1alpha2 +kind: FerretDB +metadata: + name: fr-update + namespace: demo +spec: + version: "1.18.0" + replicas: 1 + backend: + externallyManaged: false + storage: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 500Mi + deletionPolicy: WipeOut +``` + +Let's create the `FerretDB` CR we have shown above, + +```bash +$ kubectl create -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/ferretdb/update-version/fr-update.yaml +ferretdb.kubedb.com/fr-update created +``` + +Now, wait until `fr-update` created has status `Ready`. i.e, + +```bash +$ kubectl get fr -n demo + NAME TYPE VERSION STATUS AGE + fr-update kubedb.com/v1alpha2 1.18.0 Ready 26s +``` + +We are now ready to apply the `FerretDBOpsRequest` CR to update this FerretDB. + +### update FerretDB Version + +Here, we are going to update `FerretDB` from `1.18.0` to `1.23.0`. + +#### Create FerretDBOpsRequest: + +In order to update the FerretDB, we have to create a `FerretDBOpsRequest` CR with your desired version that is supported by `KubeDB`. Below is the YAML of the `FerretDBOpsRequest` CR that we are going to create, + +```yaml +apiVersion: ops.kubedb.com/v1alpha1 +kind: FerretDBOpsRequest +metadata: + name: ferretdb-version-update + namespace: demo +spec: + type: UpdateVersion + databaseRef: + name: fr-update + updateVersion: + targetVersion: 1.23.0 +``` + +Here, + +- `spec.databaseRef.name` specifies that we are performing operation on `fr-update` FerretDB. +- `spec.type` specifies that we are going to perform `UpdateVersion` on our FerretDB. +- `spec.updateVersion.targetVersion` specifies the expected version of the FerretDB `1.23.0`. + + +Let's create the `FerretDBOpsRequest` CR we have shown above, + +```bash +$ kubectl apply -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/ferretdb/update-version/frops-update.yaml +ferretdbopsrequest.ops.kubedb.com/ferretdb-version-update created +``` + +#### Verify FerretDB version updated successfully : + +If everything goes well, `KubeDB` Ops-manager operator will update the image of `FerretDB` object and related `PetSets` and `Pods`. + +Let's wait for `FerretDBOpsRequest` to be `Successful`. Run the following command to watch `FerretDBOpsRequest` CR, + +```bash +$ watch kubectl get ferretdbopsrequest -n demo +Every 2.0s: kubectl get ferretdbopsrequest -n demo +NAME TYPE STATUS AGE +ferretdb-version-update UpdateVersion Successful 93s +``` + +We can see from the above output that the `FerretDBOpsRequest` has succeeded. If we describe the `FerretDBOpsRequest` we will get an overview of the steps that were followed to update the FerretDB. + +```bash +$ kubectl describe ferretdbopsrequest -n demo ferretdb-version-update +Name: ferretdb-version-update +Namespace: demo +Labels: +Annotations: +API Version: ops.kubedb.com/v1alpha1 +Kind: FerretDBOpsRequest +Metadata: + Creation Timestamp: 2024-10-21T05:06:17Z + Generation: 1 + Resource Version: 324860 + UID: 30d486a6-a8fe-4d82-a8b3-f13e299ef035 +Spec: + Apply: IfReady + Database Ref: + Name: fr-update + Type: UpdateVersion + Update Version: + Target Version: 1.23.0 +Status: + Conditions: + Last Transition Time: 2024-10-21T05:06:17Z + Message: FerretDB ops-request has started to update version + Observed Generation: 1 + Reason: UpdateVersion + Status: True + Type: UpdateVersion + Last Transition Time: 2024-10-21T05:06:25Z + Message: successfully reconciled the FerretDB with updated version + Observed Generation: 1 + Reason: UpdatePetSets + Status: True + Type: UpdatePetSets + Last Transition Time: 2024-10-21T05:06:30Z + Message: get pod; ConditionStatus:True; PodName:fr-update-0 + Observed Generation: 1 + Status: True + Type: GetPod--fr-update-0 + Last Transition Time: 2024-10-21T05:06:30Z + Message: evict pod; ConditionStatus:True; PodName:fr-update-0 + Observed Generation: 1 + Status: True + Type: EvictPod--fr-update-0 + Last Transition Time: 2024-10-21T05:06:35Z + Message: check pod running; ConditionStatus:True; PodName:fr-update-0 + Observed Generation: 1 + Status: True + Type: CheckPodRunning--fr-update-0 + Last Transition Time: 2024-10-21T05:06:40Z + Message: Successfully Restarted FerretDB pods + Observed Generation: 1 + Reason: RestartPods + Status: True + Type: RestartPods + Last Transition Time: 2024-10-21T05:06:40Z + Message: Successfully updated FerretDB + Observed Generation: 1 + Reason: UpdateDatabase + Status: True + Type: UpdateDatabase + Last Transition Time: 2024-10-21T05:06:40Z + Message: Successfully updated FerretDB version + Observed Generation: 1 + Reason: Successful + Status: True + Type: Successful + Observed Generation: 1 + Phase: Successful +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal Starting 59s KubeDB Ops-manager Operator Start processing for FerretDBOpsRequest: demo/ferretdb-version-update + Normal Starting 59s KubeDB Ops-manager Operator Pausing FerretDB database: demo/fr-update + Normal Successful 59s KubeDB Ops-manager Operator Successfully paused FerretDB database: demo/fr-update for FerretDBOpsRequest: ferretdb-version-update + Normal UpdatePetSets 51s KubeDB Ops-manager Operator successfully reconciled the FerretDB with updated version + Warning get pod; ConditionStatus:True; PodName:fr-update-0 46s KubeDB Ops-manager Operator get pod; ConditionStatus:True; PodName:fr-update-0 + Warning evict pod; ConditionStatus:True; PodName:fr-update-0 46s KubeDB Ops-manager Operator evict pod; ConditionStatus:True; PodName:fr-update-0 + Warning check pod running; ConditionStatus:True; PodName:fr-update-0 41s KubeDB Ops-manager Operator check pod running; ConditionStatus:True; PodName:fr-update-0 + Normal RestartPods 36s KubeDB Ops-manager Operator Successfully Restarted FerretDB pods + Normal Starting 36s KubeDB Ops-manager Operator Resuming FerretDB database: demo/fr-update + Normal Successful 36s KubeDB Ops-manager Operator Successfully resumed FerretDB database: demo/fr-update for FerretDBOpsRequest: ferretdb-version-update +``` + +Now, we are going to verify whether the `FerretDB` and the related `PetSets` their `Pods` have the new version image. Let's check, + +```bash +$ kubectl get fr -n demo fr-update -o=jsonpath='{.spec.version}{"\n"}' +1.23.0 + +$ kubectl get petset -n demo fr-update -o=jsonpath='{.spec.template.spec.containers[0].image}{"\n"}' +ghcr.io/appscode-images/ferretdb:1.23.0 + +$ kubectl get pods -n demo fr-update-0 -o=jsonpath='{.spec.containers[0].image}{"\n"}' +ghcr.io/appscode-images/ferretdb:1.23.0 +``` + +You can see from above, our `FerretDB` has been updated with the new version. So, the update process is successfully completed. + +## Cleaning Up + +To clean up the Kubernetes resources created by this tutorial, run: + +```bash +kubectl delete fr -n demo fr-update +kubectl delete ferretdbopsrequest -n demo ferretdb-version-update +``` \ No newline at end of file diff --git a/docs/guides/pgpool/update-version/update_version.md b/docs/guides/pgpool/update-version/update_version.md index 7466e0d713..a63c0c9485 100644 --- a/docs/guides/pgpool/update-version/update_version.md +++ b/docs/guides/pgpool/update-version/update_version.md @@ -228,9 +228,9 @@ $ kubectl get pp -n demo pp-update -o=jsonpath='{.spec.version}{"\n"}' 4.5.0 $ kubectl get petset -n demo pp-update -o=jsonpath='{.spec.template.spec.containers[0].image}{"\n"}' -mongo:4.0.5 +ghcr.io/appscode-images/pgpool2:4.5.0 -$ kubectl get pods -n demo mg-standalone-0 -o=jsonpath='{.spec.containers[0].image}{"\n"}' +$ kubectl get pods -n demo pp-update-0 -o=jsonpath='{.spec.containers[0].image}{"\n"}' ghcr.io/appscode-images/pgpool2:4.5.0@sha256:2697fcad9e11bdc704f6ae0fba85c4451c6b0243140aaaa33e719c3af548bda1 ``` diff --git a/docs/images/ferretdb/fr-builtin-prom-target.png b/docs/images/ferretdb/fr-builtin-prom-target.png new file mode 100644 index 0000000000000000000000000000000000000000..a483bb5faf5f2b8cbd97450a95e4a50e14023920 GIT binary patch literal 66375 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

hP8^i^S0g zvs-?io*5DGJmxi0L8)!|YpE4Atm0tp`4SjWZEmymW~xsP*p5;)i^gAx{~7kVNpxx( za!KM*OR+iMv%uFynb7t0bK&D$bFw+y*h=>=$+AP})ou<#$ zBvaj)1j?{yz9$3km#^9JJB4}UR1?k4`h&|$r8|SWo(?%nO?(#fG_d}3HuUV~q_Ve%{t?#|&8=?iT)Zv61NFA;9NL~?e5FXSf9<01vDdu!$9+wr)jMqaG~+Q` zY5TqffVhab?y2-Ay$g5m z_Jn)+VQ&$hZ%f@7bexYx+317<9pcaVgU~c^4`-4T`@M)b>{Tp`fL^Cvc7}%ybuzec zNTF=0ZR2oZJBYvRF~K6%A#if0l5z%Flh*NmDfVsBv$1$hDm`~*tm2@o{pp#|x#{}IL zXiq`~Tybt~j-k6#Fbi(3d+UHx;8?Q7j5yB~lNmlJaNrIW3nbsTZ(d0RJT891Gag9w zdEliNSAM2x$cD+%7TOQu&C2++fBA?S?aQKW8j7*jb;n`6`^=Jla-gj$26^v``64+` z8OM>>*PKLJV3^)&uF%3d!Lyb|r6@8k&`DTvgYoDt22O?=Wqi zQ{z`Acg~Gvub|LL=Q!QHZ?;>p&oEdSg+=(no$Q7lPPWds5IOMU@(-}t5E~k`r~M5} z6drr6Li3p8sx{9{{8i1*hY-6&h6u#5ogo*Gt^D)thro3d$RuW=2mJilobXW9Tk&I& z%EBE;1|dweUKhW(LdAw|cVB4=XM`n4r}(r*toloqD+!!#E*`&x;VEzb6uD`9#YGLU|jKg@AZ*;4Uic=i_m$1mCW49P+0Otyo=dBt@+D6L5~_=IlG z6Evx#?Dcrwu8yu6w4T&rLOhpqJcu;Xfw8LP1o2>C_%ks-+Yr!zJuX~2MChbEq~NJ< z(S)?SzoU5jk=A)_*@FA7^A{$LV&&_|K*q@hWC?36R(hlJrUz%UKCub-83yEWU$spE zlOPy<-Jha%6Q`5*jPx%`+=v?XE=|5o8M zYZZLXR3q^(#!cxbWQ+Q~qFNy_GdHhYYvR0%2XY zQqK@$iiLz+Sqr?zLhzM9n+ft8#InSaB(hfK9j$}oc0xB`PgeiW^A=vTHxz|6wP=sUOg>8H>f_NH+aD=g#5i77Acx~lLh zyiz7!YUb>X$qTJ_mh4A!8 zF;er`p%uXYHn;S6Cqta1(KH@MZda~C-qv)piRe1^=jg*$tki*GP^w{X;pQGCp={gVq6q?bGq8=~2q@wLX1Y4Y?ySZTS=Dn)LAI6Zp{X|PJ%S1_W8He!>&F)OWxyzJCbx{;w;oT zl|;Tm4BAA|#U+DRBJ}Yfyb((BYQMGpE&#-%PR*BDg$ydDe*6<|d*@{fC7qm77i{d~ zdzU0CIa-S{?N?ZO0@_)m!0xW9;>!B+F(neIxk6n(~LbREQ z^BkIpJ^G?Om#o1E2eZ9kFlFfpC>j^1pP~$2e)8noy33!ccbd{Ur@t`)R}w$GKk(gS znggs7ztdZjuT@&#Y0nwr)r{Lk?ll|C=Sg&U0J1pGZe+t%ZwJP4JLpQsm+TgW*^XAz zIO$KpZkTn6-ged>Zd=kOv8pg?K)C!*;hji3YG_|vPZ)Q1WhJfuTr*M!VfV74z#az&M5w8z{IIt))bLw_8kN61#1@X>%Q z!u#INA)O+D>TmjLV2w0+Ci$|=#P;2n+Isx7;Qhdrf}q8l_<}gLM0?6@%KBex>< zv0-qUOzVo0-}48D2veDwXw|&tbZbxQI4BoN4-XH0 zg1&DN_smH5-~e{qtJU2j6=&q}7;6RU*`g`dp&CVX1G^)ON zeGPs3HPIDjCRPdD8twm_{OzF_G3(eqBxb~<=kC%Db4=HD9zI0wTwYz8yhz(U*`Y&< zJHVYp^7_jkQ{~m<#N6Quxk650<&en8lM>r+zM=2-o2 zC#Rv;C0_Zn+5b}S%~Vbb39{z*5thWltmoirFmON Ktw_Zj{yzYA?o^`y literal 0 HcmV?d00001 diff --git a/docs/images/ferretdb/fr-horizontal-scaling.svg b/docs/images/ferretdb/fr-horizontal-scaling.svg new file mode 100644 index 0000000000..c7349f3849 --- /dev/null +++ b/docs/images/ferretdb/fr-horizontal-scaling.svg @@ -0,0 +1,4 @@ + + + +
1.Create FerretDB
1.Create Postgr...
2.Watch
2.Watch
3.Create
3.Create
4.Create
4.Initiate Upgr...
6.Pause
6.Pause
7.Update & Perform Checks
7.Update & Perform...
8.Update FerretDB
8.Update FerretDB
9.Resume
9.Resume
Upgrading stage
Upgrading stage
User
User
                Community            Operator
           PetSet
Statef...
5.Watch
5.Watch
            Enterprise            Operator
FerretDB OpsRequest
FerretDB Op...
FerretDB
Postgr...
Updated/New
PetSet
Upda...
refers to
refers to
Updated FerretDB
Upgrad...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/docs/images/ferretdb/fr-update.svg b/docs/images/ferretdb/fr-update.svg new file mode 100644 index 0000000000..5c28780bf3 --- /dev/null +++ b/docs/images/ferretdb/fr-update.svg @@ -0,0 +1,4 @@ + + + +
1.Create FerretDB
1.Create Postgr...
2.Watch
2.Watch
3.Create
3.Create
4.Create
4.Initiate Upgr...
6.Pause
6.Pause
7.Update & Perform Checks
7.Update & Perform...
8.Upgrage FerretDB
8.Upgrage FerretDB
9.Resume
9.Resume
Upgrading stage
Upgrading stage
User
User
                Community            Operator
           PetSet
Statef...
5.Watch
5.Watch
            Enterprise            Operator
FerretDB OpsRequest
FerretDB Op...
FerretDB
Postgr...
Updated/New
PetSet
Upda...
refers to
refers to
Upgraded FerretDB
Upgrad...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/docs/images/ferretdb/fr-vertical-scaling.svg b/docs/images/ferretdb/fr-vertical-scaling.svg new file mode 100644 index 0000000000..c7349f3849 --- /dev/null +++ b/docs/images/ferretdb/fr-vertical-scaling.svg @@ -0,0 +1,4 @@ + + + +
1.Create FerretDB
1.Create Postgr...
2.Watch
2.Watch
3.Create
3.Create
4.Create
4.Initiate Upgr...
6.Pause
6.Pause
7.Update & Perform Checks
7.Update & Perform...
8.Update FerretDB
8.Update FerretDB
9.Resume
9.Resume
Upgrading stage
Upgrading stage
User
User
                Community            Operator
           PetSet
Statef...
5.Watch
5.Watch
            Enterprise            Operator
FerretDB OpsRequest
FerretDB Op...
FerretDB
Postgr...
Updated/New
PetSet
Upda...
refers to
refers to
Updated FerretDB
Upgrad...
Text is not SVG - cannot display
\ No newline at end of file