README
¶
This repository contains the code for a Kubernetes operator that manages Vertica Analytic Database. The operator uses a custom resource definition (CRD) to automate administrative tasks for a Vertica database.
To deploy the operator and a Kubernetes cluster in a local test environment that requires minimal resources, see DEVELOPER.
Prerequisites
- Resources to deploy Kubernetes objects
- Kubernetes (version 1.21.1+)
- kubectl (version 1.21.1+)
- helm (version 3.5.0+)
Installing the CRD
Install the CustomResourceDefinition
with a YAML manifest:
kubectl apply -f https://github.com/vertica/vertica-kubernetes/releases/download/v1.1.0/verticadbs.vertica.com-crd.yaml
The operator Helm chart will install the CRD if it is not currently installed.
TLS Requirements
The operator includes an admission controller, a REST endpoint within Kubernetes that uses a webhook to verify that proposed changes to the custom resource are allowed. Kubernetes requires that all webhooks accept TLS certificates. Choose one of the following options to manage TLS certificates for your custom resource:
- cert-manager
- Custom certificates
cert-manager
cert-manager has built-in support in Kubernetes, and generates and manages your certificates. Install the cert-manager with kubectl apply
.
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.5.3/cert-manager.yaml
Installation might take a few minutes. For steps on how to verify that the install is complete, see the cert-manager documentation.
Custom Certificates
Use custom certificates for more control over your data encryption if your custom resource requires multiple certificates for different client or storage connections.
For detailed configuration instructions, see Installing the Helm Charts in the Vertica documentation. For details about the available configuration options, see the helm parameters.
Installing the Operator
Vertica bundles the operator and admission controller in a Helm chart. Run the following commands to download and install the chart:
helm repo add vertica-charts https://vertica.github.io/charts
helm repo update
helm install vdb-op vertica-charts/verticadb-operator
You can install only one instance of the chart in a namespace. The operator monitors CRs that were defined in its namespace only.
The feature gate NamespaceDefaultLabelName is required for the webhook to work. It is enabled by default on Kubernetes 1.21.0. This feature will add a label 'kubernetes.io/metadata.name=nsName' to each namespace, which is needed in order to use the namespaceSelector in the webhook config. If you are on an older version of Kubernetes, you must manually add the label in order for the webhook to work.
Deploying Vertica
After the operator is installed and running, create a Vertica deployment by generating a custom resource (CR). The operator adds watches to the API server so that it gets notified whenever a CR in the same namespace changes.
Launching a new Vertica deployment with the operator is simple. Below is the minimal required CR configuration:
apiVersion: vertica.com/v1beta1
kind: VerticaDB
metadata:
name: vertica-sample
spec:
communal:
path: "s3://<bucket-name>/<key-name>"
endpoint: http://path/to/endpoint
credentialSecret: s3-creds
subclusters:
- name: defaultsubcluster
size: 3
In the previous example configuration:
communal.path
must be a bucket that already exists and is empty.communal.endpoint
is the location that serves the bucket.communal.credentialSecret
is a Secret in the same namespace that has the access key and secret to authenticate the endpoint. The Secret must use keys with namesaccesskey
andsecretkey
.- You must specify at least one subcluster and it must have a
name
value. Ifsize
is omitted, it is set to 3.
After this manifest is applied, the operator creates the necessary objects in Kubernetes, sets up the config directory in each pod, and creates an Eon Mode database in the communal path.
There are many parameters available to fine-tune the deployment. For a complete list, see Parameters. For a detailed custom resource example, see Creating a Custom Resource.
Vertica License
By default, we use the Community Edition (CE) license if no license is provided. The CE license limits the number pods in a cluster to 3, and the dataset size to 1TB. Use your own license to extend the cluster past these limits.
To use your own license, add it to a Secret in the same namespace as the operator. The following command copies the license into a Secret named license
:
kubectl create secret generic license --from-file=license.key=/path/to/license.key
Next, specify the name of the Secret in the CR by populating the licenseSecret
field:
apiVersion: vertica.com/v1beta1
kind: VerticaDB
metadata:
name: vertica-sample
spec:
licenseSecret: license
communal:
path: "s3://<bucket-name>/<key-name>"
endpoint: http://path/to/endpoint
credentialSecret: s3-creds
subclusters:
- name: defaultsubcluster
size: 3
The license is installed automatically if it is set when the CR was initially created. If it is added at a later time, then you must install the license manually with admintools. When a license Secret is specified, the contents of the Secret are mounted as files in /home/dbadmin/licensing/mnt
. For example, the Secret created in the previous commands has the following directory in each pod created by the CR:
[dbadmin@demo-sc1-0 ~]$ ls /home/dbadmin/licensing/mnt
license.key
Scale Up/Down
We offer two strategies for scaling, each one improves different types of performance.
- To increase the performance of complex, long-running queries, add nodes to an existing subcluster.
- To increase the throughput of multiple short-term queries (often called "dashboard queries"), improve your cluster's parallelism by adding additional subclusters.
Each subcluster is enumerated in the CR in a list format, and each has a size
property. To scale up, add a new subcluster to the CR, or increase an existing subcluster's size.
Below is a sample CRD that defines three subclusters named 'main', 'analytics', and 'ml':
apiVersion: vertica.com/v1beta1
kind: VerticaDB
metadata:
name: vertica-sample
spec:
communal:
path: "s3://nimbusdb/mspilchen"
credentialSecret: s3-creds
endpoint: http://minio
subclusters:
- name: main
size: 3
isPrimary: true
- name: analytics
size: 5
isPrimary: false
- name: ml
size: 2
isPrimary: false
After the operator reconciles these changes to the CR, the deployment has a combined total of 10 pods. Each subcluster is deployed as a StatefulSet with its own service object.
To scale down, modify the CR by removing a subcluster from the list element, or decreasing the size
property. The admission webhook prevents invalid state transitions, such as 0 subclusters or only secondary subclusters.
The operator automatically rebalances database shards whenever subclusters are added or removed.
Client Connectivity
Each subcluster has a service object for client connections. The service load balances traffic across the pods in the subcluster. Clients connect to the Vertica cluster through one of the subcluster service objects, depending on which subcluster the client is targeting.
For example, suppose we have the following CR:
apiVersion: vertica.com/v1beta1
kind: VerticaDB
metadata:
name: verticadb-sample
namespace: default
spec:
communal:
credentialSecret: s3-auth
path: s3://nimbusdb/db
endpoint: http://minio
subclusters:
- name: defaultsubcluster
serviceType: ClusterIP
size: 3
- name: ml
serviceType: ClusterIP
size: 2
- name: analytics
serviceType: ClusterIP
size: 2
This CR uses the following Kubernetes objects:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/verticadb-sample-analytics ClusterIP 10.96.21.26 <none> 5433/TCP,5444/TCP 21m
service/verticadb-sample ClusterIP None <none> 22/TCP 21m
service/verticadb-sample-defaultsubcluster ClusterIP 10.96.162.159 <none> 5433/TCP,5444/TCP 12h
service/verticadb-sample-ml ClusterIP 10.96.71.15 <none> 5433/TCP,5444/TCP 27m
NAME READY AGE
statefulset.apps/verticadb-sample-analytics 2/2 21m
statefulset.apps/verticadb-sample-defaultsubcluster 3/3 12h
statefulset.apps/verticadb-sample-ml 2/2 27m
Each service object has its own full qualified domain name (FQDN). The naming convention for each service object is <vdbName>-<subclusterName>
. Clients can direct their connections to: verticadb-sample-analytics, verticadb-sample-defaultsubcluster, or verticadb-sample-ml. The actual pod that the client connects with is load balanced.
The previous example includes a headless service object named service/verticadb-sample
, which matches the name of the Vertica database. This object exists to provide DNS name resolution for individual pods, and is not intended for client connectivity.
All of the service objects listed above are of type ClusterIP
. This is the default service type, and it provides load balancing for connections within the Kubernetes cluster. Use the subclusters[i].serviceType
parameter to specify NodePort or LoadBalancer to allow connections from outside of the Kubernetes cluster.
Migrating an Existing Database into Kubernetes
The operator can migrate an existing database into Kubernetes. The operator revives an existing database into a set of Kubernetes objects that mimics the setup of the database. We provide vdb-gen
, a standalone program that you can run against a live database to create the CR.
-
The following command runs
vdb-gen
and generates a CR based on the existing database. The output is written to stdout and redirected to a YAML-formatted file:$ vdb-gen --password secret --name mydb 10.44.10.1 vertdb > vdb.yaml
The previous command uses the following flags and values:
password
: The existing database superuser secret password.name
: The new VerticaDB CR object,10.44.10.1
: The IP address of the existing database.vertdb
: The name of the existing Eon Mode database.vdb.yaml
: The YAML-formatted file that contains the generated custom resource definition.
-
Use
admintools -t stop_db
to stop the database that currently exists. -
Apply the manifest that was generated by the CR generator:
$ kubectl apply -f vdb.yaml verticadb.vertica.com/mydb created
-
Wait for the operator to construct the StatefulSet, install Vertica in each pod, and run revive. Each of these steps generate events in kubectl. You can use the describe command to see the events for the verticadb:
$ kubectl describe vdb mydb
Upgrading Vertica
The idiomatic way to upgrade in Kubernetes is a rolling update model, which means the new image container is rolled out to the cluster. At any given time, some pods are running the old version, and some are running the new version.
However, Vertica does not support a cluster running mixed releases. The documented steps to upgrade Vertica are summarized below:
- Stop the entire cluster.
- Update the RPM at each host.
- Start the cluster.
Vertica on Kubernetes uses the workflow described in the preceding steps. This is triggered whenever the .spec.image
changes in the CR. When the operator detects this, it will enter the upgrade mode, logging events of its progress for monitoring purposes.
Upgrade the Vertica server version and use the kubectl command line tool to monitor the progress. The operator indicates when an upgrade is in progress or complete with the UpgradeInProgress status condition.
-
Update the
.spec.image
in the CR. This can be driven by a helm upgrade if you have the CR in a chart, or a simple patch of the CR:$ kubectl patch verticadb vert-cluster --type=merge --patch '{"spec": {"image": "vertica/vertica-k8s:11.1.1-0"}}'
-
Wait for the operator to acknowledge this change and enter the upgrade mode:
$ kubectl wait --for=condition=UpgradeInProgress=True vdb/vert-cluster –-timeout=180s
-
Wait for the operator to leave the upgrade mode:
$ kubectl wait --for=condition=UpgradeInProgress=True vdb/cluster-name –-timeout=180s
You can monitor what part of the upgrade the operator is in by looking at the events it generates.
$ kubectl describe vdb cluster-name
...<snip>...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ImageChangeStart 5m10s verticadb-operator Vertica server image change has been initiated to 'vertica-k8s:11.0.1-0'
Normal ClusterShutdownStarted 5m12s verticadb-operator Calling 'admintools -t stop_db'
Normal ClusterShutdownSucceeded 4m08s verticadb-operator Successfully called 'admintools -t stop_db' and it took 56.22132s
Normal ClusterRestartStarted 4m25s verticadb-operator Calling 'admintools -t start_db' to restart the cluster
Normal ClusterRestartSucceeded 25s verticadb-operator Successfully called 'admintools -t start_db' and it took 240s
Normal ImageChangeSucceeded 5s verticadb-operator Vertica server image change has completed successfully.
Vertica recommends that upgrade paths be incremental – meaning you upgrade to each intermediate major and minor release. The operator assumes the images chosen are following this path and doesn't try to validate it.
Persistence
Each pod uses a PV to store local data. The PV is mounted in the container at /home/dbadmin/local-data
.
Select a recommended storage format type as the fsType
value for your StorageClass.
The local-data
directory contains the following subdirectories:
- /home/dbadmin/local-data/<uid>/data/: Stores the local catalogs and any temporary files. There is a symlink to this path to the
local.dataPath
parameter. Default:/data
. - /home/dbadmin/local-data/<uid>/depot/: Stores the depot for the local node. There is a symlink to this path to the
local.depotPath
parameter. Default:/depot
. - /home/dbadmin/local-data/<uid>/config/: This is a symlink to
/opt/vertica/config
. This allows the contents of the configuration directory to persist between restarts. - /home/dbadmin/local-data/<uid>/log/: This is a symlink to
/opt/vertica/log
. This allows the log files to persist between restarts.
NOTE: <uid> is the unique ID of the VerticaDB resource that is assigned by Kubernetes.
By default, the PV is selected using the default storage class. Use the local.storageClass
parameter to select a specific storage class.
Parameters
The following table describes each configurable parameter in the VerticaDB CRD and their defaults:
Parameter Name | Description | Default Value |
---|---|---|
annotations | Custom annotations added to all of the objects that the operator creates. | |
autoRestartVertica | State to indicate whether the operator will restart vertica if the process is not running. Under normal circumstances this is set to true. Set this parameter to false when performing manual maintenance that requires a DOWN database. This prevents the operator from interfering with the database state. |
true |
certSecrets | A list of Secrets for custom TLS certificates. The Secrets are mounted in the Vertica container to make custom certificates available. The full path is /certs/<secret-name>/<key_i> , where <secret-name> is the name provided when you created the Secret, and <key_i> is one of the keys in the Secret.If you update the certificate after you add it to a custom resource, the operator updates the value automatically. If you add or delete a certificate, the operator reschedules the pod with the new configuration. |
|
communal.caFile | The mount path in the container filesystem to a CA certificate file that validates HTTPS connections to an S3-compatible endpoint. Typically, the certificate is stored in a Secret and included in certSecrets . |
|
communal.credentialSecret | The name of a Secret that contains the credentials to connect to the communal S3 endpoint. The Secret must have the following keys set: - accesskey: The access key to use for any S3 request. - secretkey: The secret that goes along with the access key. For example, you can create your secret with the following command: kubectl create secret generic s3-creds --from-literal=accesskey=accesskey --from-literal=secretkey=secretkey Then you set the the Secret name in the CR: communal: credentialSecret: s3-creds |
|
communal.endpoint | The URL to the S3 endpoint. The endpoint must begin with either http:// or https:// . This field is required and cannot change after creation. |
|
communal.includeUIDInPath | When set to true, the operator includes the VerticaDB's UID in the path. This option exists if you reuse the communal path in the same endpoint as it forces each database path to be unique. | false |
communal.path | The path to the communal storage. This must be a s3 bucket. You specify this using the s3:// bucket notation. For example: s3://bucket-name/key-name . You must create this bucket before creating the VerticaDB. This field is required and cannot change after creation. If initPolicy is Create, then this path must be empty. If the initPolicy is Revive, then this path must be non-empty. |
|
communal.region | The geographic region where the S3 bucket is located. If you do not set the correct region, you might experience a delay before the bootstrap fails because Vertica retries several times before giving up. | us-east-1 |
dbName | The name to use for the database. When initPolicy is Revive , this must match the name of the database that used when it was originally created. |
vertdb |
ignoreClusterLease | Ignore the cluster lease when doing a revive or start_db. Use this with caution, as ignoring the cluster lease when another system is using the same communal storage will cause corruption. | false |
image | The name of the container that runs the server. If hosting the containers in a private container repository, this name must include the path to that repository. Whenever this changes, the operator treats this as an upgrade and will stop the entire cluster and restart it with the new image. | vertica/vertica-k8s:11.0.0-0-minimal |
imagePullPolicy | Determines how often Kubernetes pulls the specified image. For details, see Updating Images in the Kubernetes documentation. | If the image tag ends with latest , we use Always . Otherwise we use IfNotPresent . |
imagePullSecrets | A list of secrets consisting of credentials for authentication to a private container repository. For details, see Specifying imagePullSecrets in the Kubernetes documentation. | |
initPolicy | Specifies how to initialize the database in Kubernetes. Available options are: Create or Revive. Create forces the creation of a new database. Revive initializes the database with the use of the revive command. | Create |
kSafety | Sets the fault tolerance for the cluster. Allowable values are 0 or 1. 0 is only suitable for test environments because we have no fault tolerance and the cluster can only have between 1 and 3 pods. If set to 1, we have fault tolerance if nodes die and the cluster has a minimum of 3 pods. This value cannot change after the initial creation of the VerticaDB. |
1 |
labels | Custom labels added to all of the objects that the operator creates. | |
licenseSecret | The name of a Secret that contains the contents of license files. The Secret must be in the same namespace as the CR. Each of the keys in the Secret are mounted as files in /home/dbadmin/licensing/mnt . The operator automatically installs the first license, in alphabetical order, if it was set when the CR was created. |
Not set, which implies the CE license will be used |
local.dataPath | The path inside the container filesystem for the local data. This path might need to be specified if initializing the database with a revive. When doing a revive, the local paths must match the paths that were used when the database was first created. | /data |
local.depotPath | The path inside the container filesystem that holds the depot. This path might need to be specified if initializing the database with a revive. | /depot |
local.requestSize | The minimum size of the local data volume when picking a PV. | 500Gi |
local.storageClass | The local data stores the local catalog, depot, and config files. This defines the name of the storageClass to use for that volume. This is set when creating the PVC. By default, this parameter is not set. The PVC in the default Vertica configuration uses the default storage class set in Kubernetes. | |
reviveOrder | This specifies the order of nodes when doing a revive. Each entry contains an index to a subcluster, which is an index in subclusters[i] , and a pod count of the number of pods include from the subcluster.For example, suppose the database you want to revive has the following setup: - v_db_node0001: subcluster A - v_db_node0002: subcluster A - v_db_node0003: subcluster B - v_db_node0004: subcluster A - v_db_node0005: subcluster B - v_db_node0006: subcluster B And the subclusters[] list is defined as {'A', 'B'}. The revive order would be:- {subclusterIndex:0, podCount:2} # 2 pods from subcluster A - {subclusterIndex:1, podCount:1} # 1 pod from subcluster B - {subclusterIndex:0, podCount:1} # 1 pod from subcluster A - {subclusterIndex:1, podCount:2} # 2 pods from subcluster B If InitPolicy is not Revive, this field can be ignored. |
|
restartTimeout | This specifies the timeout, in seconds, to use when calling admintools to restart pods. If not specified, it defaults 0, which means we will use the admintools default of 20 minutes. | 0 |
shardCount | The number of shards to create in the database. This cannot be updated once the CR is created. | 12 |
sidecars[] | One or more optional utility containers that complete tasks for the Vertica server container. Each sidecar entry is a fully-formed container spec, similar to the container that you add to a Pod spec. The following example adds a sidecar named vlogger to the custom resource: sidecars: - name: vlogger image: image:tag volumeMounts: - name: my-custom-vol mountPath: /path/to/custom-volume volumeMounts.name is the name of a custom volume. This value must match volumes.name to mount the custom volume in the sidecar container filesystem. See the volumes parameter description for additional details. |
empty |
subclusters[i].affinity | Allows you to constrain the pod only to certain pods. It is more expressive than just using node selectors. If not set, then no affinity setting will be used with the pods. The following example uses affinity to ensure a node does not serve two Vertica pods: subclusters: - name: sc1 affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app.kubernetes.io/name operator: In values: - vertica topologyKey: "kubernetes.io/hostname" |
|
subclusters[i].externalIPs | Enables the service object to attach to a specified external IP. If not set, the external IP is empty in the service object. | |
subclusters[i].isPrimary | Indicates whether the subcluster is a primary or a secondary. Each database must have at least one primary subcluster. | true |
subclusters[i].name | The name of the subcluster. This is a required parameter. | |
subclusters[i].nodePort | When subclusters[i].serviceType is set to NodePort , this parameter enables you to define the port that is opened at each node. The port must be within the defined range allocated by the control plane (typically ports 30000-32767). If you are using NodePort and omit the port number, Kubernetes assigns the port automatically. |
|
subclusters[i].nodeSelector | Provides control over which nodes are used to schedule each pod. If it is not set, the node selector is left off the pod that is created by the subcluster. To set this parameter, provide a list of key/value pairs. For example, to schedule the server pods only at nodes that have specific key/value pairs, include the following: subclusters: - name: sc1 nodeSelector: disktype: ssd region: us-east |
|
subclusters[i].priorityClassName | The priority class name assigned to pods in the subclusters StatefulSet. This affects where the pod gets scheduled. | |
subclusters[i].resources.* | This defines the resource requests and limits for pods in the subcluster. Users can set the limit if there is a maximum amount of CPU and memory that each server pod can consume. If the request is set, that dictates the minimum amount of CPU and memory the node must have when scheduling the pod. Refer to the Recommendations for Sizing Vertica Nodes and Clusters in the Vertica knowledge base to figure out what values you need to set based on your workload. It is advisable that the request and limits match as this ensures the pods are assigned to the guaranteed QoS class. This will reduces the chance that pods are chosen by the OOM killer. Here is a sample config for this setting. It limits each Vertica host to be scheduled at Kubernetes nodes that have 96GB of free ram and at least 32 CPUs. Both the limits and requests are the same to ensure the pods are given the guaranteed QoS class. subclusters: - name: defaultsubcluster resources: requests: memory: "96Gi" cpu: 32 limits: memory: "96Gi" cpu: 32 |
|
subclusters[i].serviceType | Identifies the type of Kubernetes service to use for external client connectivity. The default is type is ClusterIP , which sets a stable IP and port that is accessible only from within the Kubernetes cluster. Depending on the service type, you might need to set additional parameters, including nodePort or externalIPs . |
ClusterIP |
subclusters[i].size | The number of pods in the subcluster. This determines the number of Vertica nodes in the subcluster. Changing this number either deletes or schedules new pods. The minimum size of any subcluster is 1. If kSafety is 1, the actual minimum may be higher, as you need at least 3 nodes from primary subclusters to satisfy k-safety.NOTE: You must have a valid license to pick a value that causes the size of all subclusters combined to be bigger than 3. The default license that comes in the Vertica container is for the Cmmunity Edition (CE), which only allows up to 3 nodes. The license can be set with the licenseSecret parameter. |
3 |
subclusters[i].tolerations | Any tolerations and taints used to influence where a pod is scheduled. | |
superuserPasswordSecret | The Secret that contains the database superuser password. The Secret must be in the same namespace as the CR. If this is not set, then we assume no such password is set for the database. If this is set, it is up the user to create this Secret before deployment. The Secret must have a key named password .The following command creates the password: kubectl create secret generic su-passwd --from-literal=password=sup3rs3cr3t The corresponding change in the CR is: db: superuserSecretPassword: su-passwd |
|
volumes | List of custom volumes that persist sidecar container data. Each volume element requires a name value and a volume type. volumes accepts any Kubernetes volume type.To mount a volume in a sidecar filesystem, volumes.name must match the sidecars[i].volumeMounts.name value for the associated sidecar element. |
Additional Details
For additional details on the internals of Vertica, see the Vertica Documentation.
Developers
For details about setting up an environment to develop and run tests, see the developer instructions.
Licensing
vertica-kubernetes is open source code and is under the Apache 2.0 license, but it requires that you install the Vertica server RPM. If you do not have a Vertica server RPM, you can use the free Vertica Community Edition (CE) server RPM. The Vertica Community Edition server RPM is not an open source project, but it is free with certain limits on capacity. For more information about these limitations, see the Vertica Community Edition End User License Agreement.
Directories
¶
Path | Synopsis |
---|---|
api
|
|
v1beta1
Package v1beta1 contains API Schema definitions for the v1beta1 API group +kubebuilder:object:generate=true +groupName=vertica.com
|
Package v1beta1 contains API Schema definitions for the v1beta1 API group +kubebuilder:object:generate=true +groupName=vertica.com |
cmd
|
|
pkg
|
|