Skip to content

Deploy Gnocchi

Gnocchi is used by Ceilometer to aggregate and index metric data from various OpenStack services. It consists of several components: a HTTP REST API, an optional statsd-compatible daemon, and an asynchronous processing daemon (named gnocchi-metricd).

Gnocchi Architecture

Create Secrets

Information about the secretes used

Manual secret generation is only required if you haven't run the create-secrets.sh script located in /opt/genestack/bin.

Example secret generation
kubectl --namespace openstack create secret generic gnocchi-admin \
        --type Opaque \
        --from-literal=password="$(< /dev/urandom tr -dc _A-Za-z0-9 | head -c${1:-32};echo;)"
kubectl --namespace openstack create secret generic gnocchi-db-password \
        --type Opaque \
        --from-literal=password="$(< /dev/urandom tr -dc _A-Za-z0-9 | head -c${1:-32};echo;)"
kubectl --namespace openstack create secret generic gnocchi-pgsql-password \
        --type Opaque \
        --from-literal=password="$(< /dev/urandom tr -dc _A-Za-z0-9 | head -c${1:-32};echo;)"

Object Storage Options

Create ceph-etc configmap

While the below example should work fine for most environments, depending on your use case it may be necessary to provide additional client configuration options for ceph. The below simply creates the expected ceph-etc ConfigMap for the ceph.conf needed by Gnocchi to establish a connection to the mon host(s) via the rados client.

kubectl apply -n openstack -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: ceph-etc
  namespace: openstack
data:
  ceph.conf: |
    [global]
    mon_host = $(for pod in $(kubectl get pods -n rook-ceph | grep rook-ceph-mon | awk '{print $1}'); do \
        echo -n "$(kubectl get pod $pod -n rook-ceph -o go-template --template='{{.status.podIP}}'):6789,"; done \
        | sed 's/,$//')
EOF

Verify the ceph-etc configmap is sane

Below is an example of what you're looking for to verify the configmap was created as expected - a CSV of the mon hosts, colon seperated with default mon port, 6789.

kubectl get configmap -n openstack ceph-etc -o "jsonpath={.data['ceph\.conf']}"

Should yield output like:

[global]
    mon_host = 172.31.3.7:6789,172.31.1.112:6789,172.31.0.46:6789

Note

You will need the mon_host and client.admin keyring details for your external ceph cluster before proceeding.

Create ceph-etc configmap

Be sure to replace the mon_host value, REPLACE_ME below!

kubectl apply -n openstack -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: ceph-etc
  namespace: openstack
data:
  ceph.conf: |
    [global]
    cephx = true
    cephx_cluster_require_signatures = true
    cephx_require_signatures = false
    cephx_service_require_signatures = false
    debug_ms = 0/0
    log_file = /dev/stdout
    mon_cluster_log_file = /dev/stdout
    mon_host = REPLACE_ME

    [client.admin]
    keyring = /etc/ceph/ceph.client.admin.keyring
EOF

Create the admin keyring secret

Be sure to replace the key value, REPLACE_ME below!

KEYRING=$(base64 -w0 <<EOF

[client.admin]
    key = REPLACE_ME
    caps mds = "allow *"
    caps mgr = "allow *"
    caps mon = "allow *"
    caps osd = "allow *"
EOF
)
kubectl get ns rook-ceph &> /dev/null || kubectl create ns rook-ceph
kubectl apply -n rook-ceph -f - <<EOF
apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: rook-ceph-admin-keyring
  namespace: rook-ceph
data:
  keyring: ${KEYRING}
EOF
unset KEYRING

Check back later for more information.

Run the package deployment

Run the Gnocchi deployment Script /opt/genestack/bin/install-gnocchi.sh

#!/bin/bash
# Description: Fetches the version for SERVICE_NAME from the specified
# YAML file and executes a helm upgrade/install command with dynamic values files.

# Disable SC2124 (unused array), SC2145 (array expansion issue), SC2294 (eval)
# shellcheck disable=SC2124,SC2145,SC2294

# Service
SERVICE_NAME="gnocchi"
SERVICE_NAMESPACE="openstack"

# Helm
HELM_REPO_NAME="openstack-helm"
HELM_REPO_URL="https://tarballs.opendev.org/openstack/openstack-helm"

# Base directories provided by the environment
GENESTACK_BASE_DIR="${GENESTACK_BASE_DIR:-/opt/genestack}"
GENESTACK_OVERRIDES_DIR="${GENESTACK_OVERRIDES_DIR:-/etc/genestack}"

# Define service-specific override directories based on the framework
SERVICE_BASE_OVERRIDES="${GENESTACK_BASE_DIR}/base-helm-configs/${SERVICE_NAME}"
SERVICE_CUSTOM_OVERRIDES="${GENESTACK_OVERRIDES_DIR}/helm-configs/${SERVICE_NAME}"
GLOBAL_OVERRIDES_DIR="${GENESTACK_OVERRIDES_DIR}/helm-configs/global_overrides"

# Read the desired chart version from VERSION_FILE
VERSION_FILE="${GENESTACK_OVERRIDES_DIR}/helm-chart-versions.yaml"

if [ ! -f "$VERSION_FILE" ]; then
    echo "Error: helm-chart-versions.yaml not found at $VERSION_FILE" >&2
    exit 1
fi

# Extract version dynamically using the SERVICE_NAME variable
SERVICE_VERSION=$(grep "^[[:space:]]*${SERVICE_NAME}:" "$VERSION_FILE" | sed "s/.*${SERVICE_NAME}: *//")

if [ -z "$SERVICE_VERSION" ]; then
    echo "Error: Could not extract version for '$SERVICE_NAME' from $VERSION_FILE" >&2
    exit 1
fi

echo "Found version for $SERVICE_NAME: $SERVICE_VERSION"

# Prepare an array to collect -f arguments
overrides_args=()

# Include all YAML files from the BASE configuration directory
if [[ -d "$SERVICE_BASE_OVERRIDES" ]]; then
    echo "Including base overrides from directory: $SERVICE_BASE_OVERRIDES"
    for file in "$SERVICE_BASE_OVERRIDES"/*.yaml; do
        # Check that there is at least one match
        if [[ -e "$file" ]]; then
            echo " - $file"
            overrides_args+=("-f" "$file")
        fi
    done
else
    echo "Warning: Base override directory not found: $SERVICE_BASE_OVERRIDES"
fi

# Include all YAML files from the GLOBAL configuration directory
if [[ -d "$GLOBAL_OVERRIDES_DIR" ]]; then
    echo "Including overrides from global config directory:"
    for file in "$GLOBAL_OVERRIDES_DIR"/*.yaml; do
        if [[ -e "$file" ]]; then
            echo " - $file"
            overrides_args+=("-f" "$file")
        fi
    done
else
    echo "Warning: Global config directory not found: $GLOBAL_OVERRIDES_DIR"
fi

# Include all YAML files from the custom SERVICE configuration directory
if [[ -d "$SERVICE_CUSTOM_OVERRIDES" ]]; then
    echo "Including overrides from service config directory:"
    for file in "$SERVICE_CUSTOM_OVERRIDES"/*.yaml; do
        if [[ -e "$file" ]]; then
            echo " - $file"
            overrides_args+=("-f" "$file")
        fi
    done
else
    echo "Warning: Service config directory not found: $SERVICE_CUSTOM_OVERRIDES"
fi

echo

# --- Helm Repository and Execution ---
helm repo add "$HELM_REPO_NAME" "$HELM_REPO_URL"
helm repo update

# Collect all --set arguments, executing commands and quoting safely
set_args=(
    --set "conf.ceph.admin_keyring=$(kubectl get secret --namespace rook-ceph rook-ceph-admin-keyring -o jsonpath='{.data.keyring}' | base64 -d)"
    --set "conf.gnocchi.keystone_authtoken.memcache_secret_key=$(kubectl --namespace openstack get secret os-memcached -o jsonpath='{.data.memcache_secret_key}' | base64 -d)"
    --set "endpoints.oslo_cache.auth.memcache_secret_key=$(kubectl --namespace openstack get secret os-memcached -o jsonpath='{.data.memcache_secret_key}' | base64 -d)"
    --set "endpoints.identity.auth.admin.password=$(kubectl --namespace openstack get secret keystone-admin -o jsonpath='{.data.password}' | base64 -d)"
    --set "endpoints.identity.auth.gnocchi.password=$(kubectl --namespace openstack get secret gnocchi-admin -o jsonpath='{.data.password}' | base64 -d)"
    --set "endpoints.oslo_db.auth.admin.password=$(kubectl --namespace openstack get secret mariadb -o jsonpath='{.data.root-password}' | base64 -d)"
    --set "endpoints.oslo_db.auth.gnocchi.password=$(kubectl --namespace openstack get secret gnocchi-db-password -o jsonpath='{.data.password}' | base64 -d)"
    --set "endpoints.oslo_db_postgresql.auth.admin.password=$(kubectl --namespace openstack get secret postgres.postgres-cluster.credentials.postgresql.acid.zalan.do -o jsonpath='{.data.password}' | base64 -d)"
    --set "endpoints.oslo_db_postgresql.auth.gnocchi.password=$(kubectl --namespace openstack get secret gnocchi-pgsql-password -o jsonpath='{.data.password}' | base64 -d)"
)


helm_command=(
    helm upgrade --install "$SERVICE_NAME" "$HELM_REPO_NAME/$SERVICE_NAME"
    --version "${SERVICE_VERSION}"
    --namespace="$SERVICE_NAMESPACE"
    --timeout 120m # Set to standard 120m
    --create-namespace

    "${overrides_args[@]}"
    "${set_args[@]}"

    # Post-renderer configuration
    --post-renderer "$GENESTACK_OVERRIDES_DIR/kustomize/kustomize.sh"
    --post-renderer-args "$SERVICE_NAME/overlay"

    "$@"
)

echo "Executing Helm command (arguments are quoted safely):"
printf '%q ' "${helm_command[@]}"
echo

# Execute the command directly from the array
"${helm_command[@]}"

Tip

You may need to provide custom values to configure your openstack services, for a simple single region or lab deployment you can supply an additional overrides flag using the example found at base-helm-configs/aio-example-openstack-overrides.yaml. In other cases such as a multi-region deployment you may want to view the Multi-Region Support guide to for a workflow solution.

Validate the metric endpoint

Pip install gnocchiclient and python-ceilometerclient

kubectl exec -it openstack-admin-client -n openstack -- /var/lib/openstack/bin/pip install python-ceilometerclient gnocchiclient

Confirm healthcheck response

curl http://gnocchi-api.openstack.svc.cluster.local:8041/healthcheck -D -
HTTP/1.1 200 OK
Date: Fri, 09 Aug 2024 20:33:24 GMT
Server: Apache/2.4.52 (Ubuntu)
Content-Length: 0
Vary: Accept-Encoding
Content-Type: text/plain; charset=UTF-8

Verify metric list functionality

kubectl exec -it openstack-admin-client -n openstack -- openstack metric list --debug
RESP BODY: []