Chapter 3. Forward to Google Cloud
You can forward logs to Google Cloud Logging by using service account keys or Workload Identity Federation.
3.1. Forward logs to Google Cloud with service account keys Copy linkLink copied to clipboard!
Forward logs to Google Cloud Logging by using a Google service account key for authentication.
For improved security, consider using Workload Identity Federation instead of long-lived service account keys. See "Forwarding logs to Google Cloud with Workload Identity Federation".
Forwarding logs to GCP is not supported on Red Hat OpenShift on AWS.
Prerequisites
- Ensure that you installed the Red Hat OpenShift Logging Operator.
Procedure
Create a secret using your Google service account key.
$ oc -n openshift-logging create secret generic gcp-secret --from-file google-application-credentials.json=<your_service_account_key_file.json>Create a
ClusterLogForwarderCustom Resource YAML using the template below:apiVersion: observability.openshift.io/v1 kind: ClusterLogForwarder metadata: name: <log_forwarder_name> namespace: openshift-logging spec: serviceAccount: name: <service_account_name> outputs: - name: gcp-1 type: googleCloudLogging googleCloudLogging: authentication: credentials: secretName: gcp-secret key: google-application-credentials.json id: type : project value: <your_project_id> logId : app-gcp pipelines: - name: test-app inputRefs: - application outputRefs: - gcp-1serviceAccount.name- Specify the name of your service account.
value-
Set a
project,folder,organization, orbillingAccountfield and its corresponding value, depending on where you want to store your logs in the GCP resource hierarchy. logId-
Set the value to add to the
logNamefield of the log entry. The value can be a combination of static and dynamic values consisting of field paths followed by||, followed by another field path or a static value. You must encase a dynamic value in single curly brackets{}and follow it with a static fallback value separated with||. Static values can only contain alphanumeric characters along with dashes, underscores, dots and forward slashes. inputRefs-
Specify the names of inputs defined in the
input.namefield for this pipeline. You can also use the built-in valuesapplication,infrastructure,audit.
3.2. Google Cloud Logging output format and field mappings Copy linkLink copied to clipboard!
When you forward logs to Google Cloud Logging, the log collector transforms OpenShift log records into the LogEntry format defined by the Cloud Logging API. Understanding this transformation helps you configure log queries and alerting in the Google Cloud Console.
The log collector sends logs to Google Cloud Logging by using the LogEntry data type. Each OpenShift log record becomes a single LogEntry with the following required and optional fields:
logName(required)-
Resource name in the format
projects/[PROJECT_ID]/logs/[LOG_ID]. You can also useorganizations/,folders/, orbillingAccounts/prefixes depending on where you want to store logs in the Google Cloud resource hierarchy. - Payload
-
You must specify exactly one payload type:
jsonPayloadfor structured JSON objects,textPayloadfor plain text strings, orprotoPayloadfor protocol buffer messages. The log collector typically usesjsonPayloadto preserve the complete OpenShift log record structure. resource(optional)-
Monitored resource metadata with a
typeandlabels. For Kubernetes logs, common types includek8s_cluster,k8s_node,k8s_pod, andk8s_container. timestamp(optional)-
Entry timestamp in RFC3339 format with nanosecond accuracy. The collector converts the log record’s
@timestampfield to this format. severity(optional)-
Log severity level. The collector maps the OpenShift log record’s
levelfield to Google Cloud Logging severity values:DEFAULT,DEBUG,INFO,NOTICE,WARNING,ERROR,CRITICAL,ALERT, orEMERGENCY. labels(optional)- User-defined key-value pairs for metadata. You can extract fields from the log record to populate these labels.
The following table shows common field mappings from OpenShift log records to Google Cloud LogEntry:
| OpenShift field | LogEntry field | Transformation |
|---|---|---|
| Complete log record |
| Entire log record preserved as structured JSON |
|
|
| Converted to RFC3339 with nanosecond accuracy |
|
|
|
Mapped to Google Cloud severity level (for example, |
|
|
| Extracted to resource labels |
|
|
| Extracted to resource labels |
|
|
| Extracted to resource labels |
|
|
| Extracted to resource labels |
{
"kubernetes": {
"namespace_name": "my-app",
"pod_name": "web-1",
"container_name": "nginx",
"cluster_name": "prod-cluster"
},
"message": "Error connecting to database",
"level": "error",
"@timestamp": "2024-01-15T10:30:00Z"
}
The log collector transforms this into a Google Cloud LogEntry:
{
"logName": "projects/my-project/logs/openshift-logs",
"resource": {
"type": "k8s_container",
"labels": {
"cluster_name": "prod-cluster",
"namespace_name": "my-app",
"pod_name": "web-1",
"container_name": "nginx"
}
},
"timestamp": "2024-01-15T10:30:00.000000000Z",
"severity": "ERROR",
"labels": {
"log_source": "openshift",
"environment": "production"
},
"jsonPayload": {
"message": "Error connecting to database",
"kubernetes": {
"namespace_name": "my-app",
"pod_name": "web-1",
"container_name": "nginx"
},
"level": "error"
}
}
The resource.type field determines which labels Google Cloud expects. For the k8s_container type, you must provide cluster_name, namespace_name, pod_name, and container_name labels. Template syntax in your ClusterLogForwarder configuration extracts these values from the log record’s Kubernetes metadata fields.
Google Cloud Logging requires service account credentials with the logging.logEntries.create permission. The Logs Writer role provides this permission at the organization, folder, project, or billing account level.
3.3. Forward logs to Google Cloud with Workload Identity Federation Copy linkLink copied to clipboard!
Forward logs to Google Cloud Logging by using Workload Identity Federation (WIF) for authentication. WIF provides short-lived credentials instead of long-lived service account keys. This approach improves security by eliminating the need to manage and rotate service account key files.
Forwarding logs to GCP with Workload Identity Federation is only supported on Red Hat OpenShift on GCP.
Prerequisites
- You have installed the Red Hat OpenShift Logging Operator.
- Your Red Hat OpenShift Logging cluster is installed on GCP in manual credentials mode with Workload Identity Federation (WIF) enabled.
-
You have access to the
ccoctlutility that was used during cluster installation. -
You have
gcloudCLI installed and configured for your GCP project. -
You have the GCP project number, not the project ID. To get this value, run:
gcloud projects describe <PROJECT_ID> --format="value(projectNumber)"
Workload Identity Federation must be enabled during cluster installation. You cannot retrofit WIF onto an existing cluster that was not installed with WIF support. If your cluster was not installed with WIF, use service account keys instead.
Procedure
Set environment variables for your GCP project and cluster configuration:
$ export PROJECT_ID="<your_gcp_project_id>" $ export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)") $ export LOGGING_NS="openshift-logging" $ export SA_NAME="logcollector" $ export SECRET_NAME="gcp-wif-secret" $ export POOL_ID="<your_cluster_name>" $ export PROVIDER_ID="<your_cluster_name>" $ export GCP_SA_NAME="openshift-logging-writer" $ export SERVICE_ACCOUNT_EMAIL="${GCP_SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"- POOL_ID
- The workload identity pool ID created during cluster installation. This typically matches your cluster name.
- PROVIDER_ID
The workload identity provider ID created during cluster installation. This typically matches the pool ID.
WarningThe
audiencefield in the CredentialsRequest must use your GCP project number, not your project ID. Using the project ID instead of the project number causes HTTP 403 Forbidden errors.
Create a
CredentialsRequestcustom resource YAML file namedcredrequest.yaml:apiVersion: cloudcredential.openshift.io/v1 kind: CredentialsRequest metadata: name: gcp-wif-credrequest namespace: openshift-cloud-credential-operator spec: providerSpec: apiVersion: cloudcredential.openshift.io/v1 kind: GCPProviderSpec predefinedRoles: - roles/logging.logWriter skipServiceCheck: true audience: "//iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL_ID}/providers/${PROVIDER_ID}" serviceAccountEmail: ${SERVICE_ACCOUNT_EMAIL} cloudTokenPath: /var/run/ocp-collector/serviceaccount secretRef: name: ${SECRET_NAME} namespace: ${LOGGING_NS} serviceAccountNames: - ${SA_NAME}- metadata.namespace
- The standard namespace for CredentialsRequest resources.
- spec.providerSpec.predefinedRoles
- The GCP IAM role granting permissions to write log entries to Google Cloud Logging.
- spec.providerSpec.audience
- The WIF audience. This must use the project number, not the project ID.
- spec.providerSpec.serviceAccountEmail
-
The email address of the GCP service account that
ccoctlwill create or use. - spec.cloudTokenPath
- The path where the OpenShift service account token is mounted in the collector pod.
- spec.secretRef.namespace
-
The namespace where the secret will be created. This should match the namespace where you will deploy the
ClusterLogForwarder. - spec.serviceAccountNames
- The name of the OpenShift service account that the collector pods will use.
Use the
ccoctlutility to create the GCP service account and IAM bindings:$ mkdir -p credreqs $ cp credrequest.yaml credreqs/ $ ccoctl gcp create-all \ --name="${POOL_ID}" \ --project="${PROJECT_ID}" \ --region="us-east1" \ --credentials-requests-dir=credreqs \ --output-dir=outputThis command creates:
- A GCP service account with the specified email address
-
IAM policy bindings granting the
roles/logging.logWriterrole - A workload identity binding between the OpenShift service account and the GCP service account
- An OpenShift secret manifest containing the external account credentials
Apply the generated secret manifest:
$ oc apply -f output/manifests/${LOGGING_NS}-${SECRET_NAME}-credentials.yamlUpdate the secret to fix the
cloudTokenPathfor the collector:ImportantThe
ccoctlutility does not respect thecloudTokenPathsetting in the CredentialsRequest. You must manually update the generated secret to use the correct token path for the logging collector.$ oc get secret ${SECRET_NAME} -n ${LOGGING_NS} -o jsonpath='{.data.service_account\.json}' | base64 -d > /tmp/creds.json $ jq '.credential_source.file = "/var/run/ocp-collector/serviceaccount/token"' /tmp/creds.json > /tmp/external_account.json $ oc delete secret ${SECRET_NAME} -n ${LOGGING_NS} $ oc create secret generic ${SECRET_NAME} -n ${LOGGING_NS} \ --from-file=external_account.json=/tmp/external_account.jsonVerify the secret was updated correctly:
$ oc get secret ${SECRET_NAME} -n ${LOGGING_NS} -o jsonpath='{.data.external_account\.json}' | base64 -d | jq .credential_sourceThe output should show:
{ "file": "/var/run/ocp-collector/serviceaccount/token" }Create a
ClusterLogForwardercustom resource that uses WIF authentication:apiVersion: observability.openshift.io/v1 kind: ClusterLogForwarder metadata: name: <log_forwarder_name> namespace: openshift-logging spec: serviceAccount: name: logcollector outputs: - name: gcp-wif type: googleCloudLogging googleCloudLogging: authentication: credentials: secretName: gcp-wif-secret key: external_account.json token: from: serviceAccount id: type: project value: <your_project_id> logId: openshift-logs pipelines: - name: send-to-gcp inputRefs: - application - infrastructure - audit outputRefs: - gcp-wif- metadata.namespace
-
The namespace where the
ClusterLogForwarderis deployed. This should match the namespace specified in theCredentialsRequestsecret reference. - spec.serviceAccount.name
-
The OpenShift service account name. This must match the
serviceAccountNamesvalue in theCredentialsRequest. - spec.outputs.googleCloudLogging.authentication.credentials.key
-
The secret key containing the external account configuration file. Note that this is
external_account.json, notservice_account.json, because you manually recreated the secret in the previous step. - spec.outputs.googleCloudLogging.authentication.token.from
- Specifies that the bearer token comes from the service account. The collector uses the service account token as the subject token for WIF token exchange with GCP.
Verification
Check the log collector pods to verify that they are running:
$ oc get pods -n openshift-logging -l app.kubernetes.io/component=collectorReview the collector pod logs to confirm successful authentication and log forwarding:
$ oc logs -n openshift-logging <collector_pod_name> | grep -i gcp- In the GCP console, navigate to Logging > Logs Explorer and verify that logs from your OpenShift cluster are appearing.