Overview
This guide covers deploying ClickHouse Private on Google Cloud Platform (GCP) using Google Kubernetes Engine (GKE). It adapts the standard AWS deployment process for GCP-specific services including Artifact Registry, GCS with S3-compatible API, and Workload Identity. This guide supports both standard and FIPS 140-2 compliant deployments. FIPS-specific steps and configuration are marked with (FIPS) throughout. SetENABLE_FIPS=true in 00-env.sh to enable FIPS mode.
Prerequisites
- GCP Project with billing enabled
gcloudCLI installed and authenticatedkubectlinstalledhelmv3.x installedskopeoinstalled (for copying container images)- AWS credentials with read access to ClickHouse ECR (for image copying)
- Bastion host in the VPC for accessing the private cluster
- Deployer service account with appropriate permissions (see below)
- (FIPS) Access to Red Hat UBI8 container image (pulled via GKE for certificate generation)
Creating a Service Account for ClickHouse Deployment
In FedRAMP environments where direct user authentication is restricted, create a service account for impersonation.Option 1: Via GCP Console
- Go to IAM & Admin → Service Accounts
- Click + CREATE SERVICE ACCOUNT
- Enter:
- Name:
clickhouse-deployer - ID:
clickhouse-deployer - Description:
Service account for deploying ClickHouse infrastructure
- Name:
- Click CREATE AND CONTINUE
- Add the following roles:
- Click CONTINUE → DONE
- Click on the newly created
clickhouse-deployerservice account - Go to PERMISSIONS tab
- Click GRANT ACCESS
- Add principal:
<COMPUTE_SERVICE_ACCOUNT>@developer.gserviceaccount.com(your bastion’s service account) - Assign role:
roles/iam.serviceAccountTokenCreator - Click SAVE
Option 2: Via CLI (requires IAM admin access)
Configure Impersonation on Bastion
Add this to your00-env.sh or run before executing scripts:
Set up Tasks
GCP Resources
- Set Environment Variables -
00-env.sh - Create Artifact Registry Repository -
01-artifact-registry.sh - Copy Container Artifacts -
02-copy-images.sh - Configure VPC and GKE Subnet -
03-vpc-subnet.sh - Create Cloud NAT -
04-cloud-nat.sh - Create GCP Service Account -
05-service-account.sh - Create GCS Bucket -
06-gcs-bucket.sh - Create GKE Cluster -
07-gke-cluster.sh- Enable Private Nodes (
--enable-private-nodes) - Enable Private Endpoint (
--enable-private-endpoint) - No public API access (FedRAMP) - Enable Workload Identity (
--workload-pool=$PROJECT_ID.svc.id.goog)
- Enable Private Nodes (
- Create Node Pools -
08-node-pools.sh - Deploy Local SSD DaemonSet -
09-local-ssd-daemonset.sh - Add Topology Labels to Nodes -
10-topology-labels.sh
Kubernetes Resources
- Install VolumeSnapshot CRDs -
11-volumesnapshot-crds.sh - Set up Workload Identity -
12-workload-identity.sh - Install StorageClass -
13-storageclass.sh(optional - can use built-inpremium-rwo)
FIPS-Specific Steps (skip if ENABLE_FIPS=false)
- (FIPS) Create FIPS-Compliant Certificates -
14-create-fips-certs.sh - (FIPS) Create Kubernetes Certificate Secrets -
15-create-cert-secrets.sh
Deploy ClickHouse
- Install Operator via Helm -
16-install-operator.sh - Create ClickHouseCluster Resource -
17-install-cluster.sh - Bind Workload Identity to Helm SA -
18-bind-helm-workload-identity.sh - Verify Installation
00. Environment Variables
File:00-env.sh
01. Create Artifact Registry
File:01-artifact-registry.sh
02. Copy Container Images
File:02-copy-images.sh
Requires skopeo to retain all architectures.
03. Configure VPC and Subnet
File:03-vpc-subnet.sh
This script assumes you have an existing VPC with a bastion host. It creates or updates the GKE subnet with required secondary ranges.
Important: CIDR Boundary Requirements
/14blocks: Third octet must be divisible by 4 (e.g.,10.244.0.0, NOT10.245.0.0)/20blocks: Fourth octet must be 0, third octet on 16-boundary (e.g.,10.252.0.0)
04. Create Cloud NAT
File:04-cloud-nat.sh
Private GKE nodes require Cloud NAT for outbound internet access (pulling images, etc.).
05. Create GCP Service Account
File:05-service-account.sh
06. Create GCS Bucket
File:06-gcs-bucket.sh
07. Create GKE Cluster
File:07-gke-cluster.sh
Creates a fully private GKE cluster for FedRAMP compliance:
- Worker nodes have only internal IPs
- Control plane has only a private endpoint
- All access must come from within the VPC (via bastion)
Accessing the Private Cluster
With--enable-private-endpoint, the cluster API is only accessible from within the VPC.
From the Bastion Host:
08. Create Node Pools
File:08-node-pools.sh
09. Local SSD DaemonSet
File:09-local-ssd-daemonset.sh
GKE Container-Optimized OS (COS) has a read-only root filesystem, so we cannot create /nvme. Instead, we mount the local SSD at /mnt/disks/ssd0 and configure ClickHouse to use that path.
Note: GKE does not allow --metadata-from-file=startup-script on node pools (reserved key), so a DaemonSet is required to format and mount the local SSD.
- GKE Container-Optimized OS has a read-only root filesystem - cannot create
/nvme - Local SSD is mounted at
/mnt/disks/ssd0 - Uses
busybox:1.36image (no internet required for apt-get) - Uses
nsenterto run commands on the host filesystem - DaemonSet must be deployed before the ClickHouse cluster to avoid race conditions
10. Add Topology Labels
File:10-topology-labels.sh
11. Install VolumeSnapshot CRDs
File:11-volumesnapshot-crds.sh
12. Workload Identity Setup
File:12-workload-identity.sh
13. Install StorageClass
File:13-storageclass.sh (Optional)
Note: GKE uses topology.gke.io/zone instead of topology.kubernetes.io/zone.
14. Create FIPS Certificates (FIPS only)
File:14-create-fips-certs.sh — Skip this step if ENABLE_FIPS=false.
FIPS-compliant certificates must be generated using FIPS-approved algorithms in a FIPS-enabled environment. This script runs a Red Hat UBI8 pod on the GKE cluster with FIPS crypto policy enabled, then copies the generated certificates back to the bastion via kubectl cp.
Requirements:
- RSA 3072-bit minimum key size
- SHA-256 or higher signature algorithm
- Subject Alternative Names (SANs) must include cluster DNS names
kubectlaccess to the GKE cluster (nodockerrequired)
The script performs these steps:
- Creates a ConfigMap with the cert generation script
- Runs a UBI8 pod with an initContainer that installs OpenSSL, sets FIPS crypto policy via
update-crypto-policies --set FIPS, and generates all certificates - Copies the certificates back to the bastion via
kubectl cp - Cleans up the pod and ConfigMap
fips/14-create-fips-certs.sh for the full script.
Important Notes:
- Certificates expire after 365 days by default — adjust
CERT_VALIDITYas needed - CA certificate expires after 3650 days (10 years)
- Uses RSA 3072-bit keys (FIPS-compliant minimum)
- Keep private keys (
*.keyfiles) secure and never commit to version control
15. Create Certificate Secrets (FIPS only)
File:15-create-cert-secrets.sh — Skip this step if ENABLE_FIPS=false.
Create Kubernetes secrets containing the FIPS-compliant certificates. Secrets must be in the same namespace as the ClickHouse cluster.
16. Install Operator
File:16-install-operator.sh
17. Install ClickHouse Cluster
File:17-install-cluster.sh
storageClass.create=false: Don’t create AWS StorageClassstorageClass.name="premium-rwo": Use GCP’s built-in SSD StorageClassserver.storage.s3.endpoint:https://storage.googleapis.comfor GCS S3-compatible APIserver.storage.s3.useEnvironmentCredentials=true: Required for Workload Identitykeeper.storage.s3.useEnvironmentCredentials=true: Must also be set for keeper!
ENABLE_FIPS=true):
18. Bind Workload Identity to Helm SA
File:18-bind-helm-workload-identity.sh
The helm chart creates its own service account (ch-${CLUSTER_NAME}-sa). You must bind Workload Identity to this SA for GCS access.
Important: The keeper and server pods created during step 17 start before this Workload Identity binding exists, so they will fail with GCS 403 Access Denied errors. This script automatically restarts the pods after binding so they pick up the new credentials.
19. Verify Installation
Standard mode (no TLS):openSSL.required=true, ClickHouse only accepts connections on secure ports. Use port 9440 (native TLS) instead of 9000:
--accept-invalid-certificate flag is needed because we use self-signed certificates. For production with proper CA-signed certs, specify the CA certificate explicitly:
openSSL.required=true. Use port 9440 (native TLS) or 8443 (HTTPS) instead.
Appendix: AWS to GCP Component Mapping
Appendix: FIPS-Specific Considerations
Note: This section only applies whenENABLE_FIPS=trueis set in00-env.sh.
Certificate Requirements
FIPS compliance requires:- Minimum RSA 3072-bit keys
- FIPS-approved signature algorithms (SHA-256 or higher)
- Certificates generated in FIPS-enabled environment (Red Hat UBI8)
- Proper certificate chain validation
OpenSSL Configuration
Both server and keeper must have:openSSL.enabled=true- Enable TLSopenSSL.required=true- Enforce TLS (reject non-TLS connections)openSSL.selfSigned=true- For self-signed certificates (setfalseif using proper CA)
Key Differences from Standard GCP Installation
Appendix: Troubleshooting
Certificate Verification Failures (FIPS)
If you see errors like “certificate verify failed”:- Verify CA certificate is correct:
- Verify server certificate SANs:
- Check secret contents:
FIPS Mode Verification
Verify FIPS compliance of generated certificates:- Private key: 3072-bit
- Signature Algorithm: sha256WithRSAEncryption (or higher)
Pods stuck in Pending - Node Selector Issues
Private Nodes Cannot Pull Images
Verify Cloud NAT is configured:GCS Access Denied Errors (403)
The most common cause is a Workload Identity race condition: pods start during the Helm install (Step 17) before the Helm-created SA (ch-${CLUSTER_NAME}-sa) has its Workload Identity binding applied in Step 18. Step 18 now handles this automatically by restarting pods after binding.
If you still see 403 errors:
Cannot Access Cluster from Bastion
Verify bastion subnet is in authorized networks:Connection Refused on Port 9000 (FIPS)
With FIPS TLS enabled (openSSL.required=true), ClickHouse does not accept plaintext connections. Port 9000 (native) and 8123 (HTTP) will refuse connections. Use the secure ports instead:
Helm Registry Login Fails
helm registry login does not accept https:// prefix:
Validate SSD Cache script
validate-ssd-cache.sh
Query cache status
query-cache-status.sh
Security Best Practices
-
Certificate Management (FIPS)
- Rotate certificates before expiration (default: 365 days)
- Store private keys securely (use secrets management)
- Never commit certificates to version control
- Use proper CA-signed certificates in production
-
Network Security
- Use private GKE cluster with no public endpoint
- Restrict master-authorized-networks to bastion and GKE subnets
- Enable VPC flow logs
- Use Cloud NAT for controlled outbound access
-
Access Control
- Use Workload Identity instead of static credentials
- Enable Kubernetes RBAC
- Audit access logs regularly
- Use service account impersonation for deployment
-
Monitoring
- Monitor certificate expiration dates (FIPS)
- Set up alerts for failed TLS connections (FIPS)
- Monitor GCS access patterns
- Enable Cloud Audit Logging