Skip to main content

Managed Kubernetes Services - Google Kubernetes Engine (GKE): Install gcloud, Enable APIs, Deploy Regional & Zonal Cluster via gcloud CLI; Exaple Deployment with TLS Encryption via GKE Ingress and Google-managed Certificate

1913 words·
Kubernetes Kubernetes Cluster GCP GKE Google Cloud SDK
Table of Contents
Kubernetes-Cluster - This article is part of a series.
Part 5: This Article

Google Cloud SDK
#

I’m using an Ubuntu 24.04 server to run the gcloud CLI.

Installation
#

The Google Cloud SDK (Software Development Kit) includes the gcloud command-line interface (CLI).

# Install dependencies
sudo apt install curl apt-transport-https ca-certificates gnupg -y

# Add package source
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list

# Import the Google Cloud public key
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg

# Update package index
sudo apt update

# Install the Google Cloud SDK
sudo apt install google-cloud-sdk -y

Verify Installation
#

# Verify installation / list version
gcloud version

Login
#

Open the link from the shell in a browser, login to GCP and paste the verification code in the shell.

# Alternative use the following command to login without a browser
gcloud auth login --no-launch-browser

Verify Login
#

# List Google Cloud accounts that are currently authenticated
gcloud auth list

Kubernetes Cluster Prerequisites
#

GCP Project
#

Create new Project
#

# Create a new project: The project ID has to be globally unique across all Google Cloud projects
gcloud projects create jkw-gcp-playground

Verify Project / List available Projects
#

# List available projects
gcloud projects list

# Shell output:
PROJECT_ID                 NAME                PROJECT_NUMBER
jkw-gcp-playground         jkw-gcp-playground  147765757820
thematic-carver-429718-h5  My First Project    642160671846

Set the Default Project
#

# Set default project: Syntax
gcloud config set project PROJECT_ID

# Set default project: Example
gcloud config set project jkw-gcp-playground 

Verify the Default Project
#

# Verify the default project
gcloud config get-value project

# Shell output:
jkw-gcp-playground

Add Billing Account
#

This can only be done via the GCP webconsole:


Verify Billing Account
#

# Verify the billing account
gcloud billing projects describe jkw-gcp-playground

# Shell output:
billingAccountName: billingAccounts/01B347-some-billig-account...
billingEnabled: true
name: projects/jkw-gcp-playground/billingInfo
projectId: jkw-gcp-playground

Enable APIs
#

Enable the required APIs for project “jkw-gcp-playground”:

# Specify the project ID
PROJECT_ID="jkw-gcp-playground"


# Enable Kubernetes Engine API: "container.googleapis.co"
gcloud services enable container.googleapis.com --project=${PROJECT_ID}

# Enable Compute Engine API: "compute.googleapis.com"
gcloud services enable compute.googleapis.com --project=${PROJECT_ID}

# Enable IAM API: "iam.googleapis.com"
gcloud services enable iam.googleapis.com --project=${PROJECT_ID}

Verify APIs
#

# List the APIs and services that are currently enabled in the GCP project
gcloud services list --project=jkw-gcp-playground --enabled

# Shell output:
NAME                                TITLE
analyticshub.googleapis.com         Analytics Hub API
artifactregistry.googleapis.com     Artifact Registry API
autoscaling.googleapis.com          Cloud Autoscaling API
bigquery.googleapis.com             BigQuery API
bigqueryconnection.googleapis.com   BigQuery Connection API
bigquerydatapolicy.googleapis.com   BigQuery Data Policy API
bigquerymigration.googleapis.com    BigQuery Migration API
bigqueryreservation.googleapis.com  BigQuery Reservation API
bigquerystorage.googleapis.com      BigQuery Storage API
cloudapis.googleapis.com            Google Cloud APIs
cloudtrace.googleapis.com           Cloud Trace API
compute.googleapis.com              Compute Engine API
container.googleapis.com            Kubernetes Engine API
containerfilesystem.googleapis.com  Container File System API
containerregistry.googleapis.com    Container Registry API
dataform.googleapis.com             Dataform API
dataplex.googleapis.com             Cloud Dataplex API
datastore.googleapis.com            Cloud Datastore API
dns.googleapis.com                  Cloud DNS API
gkebackup.googleapis.com            Backup for GKE API
iam.googleapis.com                  Identity and Access Management (IAM) API
iamcredentials.googleapis.com       IAM Service Account Credentials API
logging.googleapis.com              Cloud Logging API
monitoring.googleapis.com           Cloud Monitoring API
networkconnectivity.googleapis.com  Network Connectivity API
oslogin.googleapis.com              Cloud OS Login API
pubsub.googleapis.com               Cloud Pub/Sub API
servicemanagement.googleapis.com    Service Management API
serviceusage.googleapis.com         Service Usage API
sql-component.googleapis.com        Cloud SQL
storage-api.googleapis.com          Google Cloud Storage JSON API
storage-component.googleapis.com    Cloud Storage
storage.googleapis.com              Cloud Storage API



GCP Region
#

List Available Regions
#

# List available regios
gcloud compute regions list

# Shell output:
NAME                     CPUS  DISKS_GB  ADDRESSES  RESERVED_ADDRESSES  STATUS  TURNDOWN_DATE
africa-south1            0/24  0/4096    0/8        0/8                 UP
asia-east1               0/24  0/4096    0/8        0/8                 UP
asia-east2               0/24  0/4096    0/8        0/8                 UP
asia-northeast1          0/24  0/4096    0/8        0/8                 UP
asia-northeast2          0/24  0/4096    0/8        0/8                 UP
asia-northeast3          0/24  0/4096    0/8        0/8                 UP
asia-south1              0/24  0/4096    0/8        0/8                 UP
asia-south2              0/24  0/4096    0/8        0/8                 UP
asia-southeast1          0/24  0/4096    0/8        0/8                 UP
asia-southeast2          0/24  0/4096    0/8        0/8                 UP
australia-southeast1     0/24  0/4096    0/8        0/8                 UP
australia-southeast2     0/24  0/4096    0/8        0/8                 UP
europe-central2          0/24  0/4096    0/8        0/8                 UP
europe-north1            0/24  0/4096    0/8        0/8                 UP
europe-southwest1        0/24  0/4096    0/8        0/8                 UP
europe-west1             0/24  0/4096    0/8        0/8                 UP
europe-west10            0/24  0/4096    0/8        0/8                 UP
europe-west12            0/24  0/4096    0/8        0/8                 UP
europe-west2             0/24  0/4096    0/8        0/8                 UP
europe-west3             0/24  0/4096    0/8        0/8                 UP
europe-west4             0/24  0/4096    0/8        0/8                 UP
europe-west6             0/24  0/4096    0/8        0/8                 UP
europe-west8             0/24  0/4096    0/8        0/8                 UP
europe-west9             0/24  0/4096    0/8        0/8                 UP
me-central1              0/24  0/4096    0/8        0/8                 UP
me-central2              0/24  0/4096    0/8        0/8                 UP
me-west1                 0/24  0/4096    0/8        0/8                 UP
northamerica-northeast1  0/24  0/4096    0/8        0/8                 UP
northamerica-northeast2  0/24  0/4096    0/8        0/8                 UP
southamerica-east1       0/24  0/4096    0/8        0/8                 UP
southamerica-west1       0/24  0/4096    0/8        0/8                 UP
us-central1              0/24  0/4096    0/8        0/8                 UP
us-east1                 0/24  0/4096    0/8        0/8                 UP
us-east4                 0/24  0/4096    0/8        0/8                 UP
us-east5                 0/24  0/4096    0/8        0/8                 UP
us-south1                0/24  0/4096    0/8        0/8                 UP
us-west1                 0/24  0/4096    0/8        0/8                 UP
us-west2                 0/24  0/4096    0/8        0/8                 UP
us-west3                 0/24  0/4096    0/8        0/8                 UP
us-west4                 0/24  0/4096    0/8        0/8                 UP

Set Default Region
#

# Set default region
gcloud config set compute/region europe-west1

Verify Region
#

# List current default region
gcloud config get-value compute/region

# Shell output:
europe-west1

Verify Configuration
#

# Verify default configuration
gcloud config list

# Shell output:
[compute]
region = europe-west1
[core]
account = juergen@jklug.work
disable_usage_reporting = True
project = jkw-gcp-playground

Your active configuration is: [default]



Prerequisites
#

Install Kubectl
#

# Install Kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" &&
chmod +x kubectl &&
sudo mv kubectl /usr/local/bin/
# Verify / check version
kubectl version --client

Install gke-gcloud-auth-plugin
#

# Install gke-gcloud-auth-plugin: Kubectl authentication plugin
sudo apt install google-cloud-cli-gke-gcloud-auth-plugin -y

# Verify / check version
gke-gcloud-auth-plugin --version



Create GKE Cluster
#

Create Cluster
#

# Create a cluster with 2 nodes: Regional cluster (Replicates the nodes across multiple zones within a region)
gcloud container clusters create gke-playground \
  --num-nodes 1 \
  --machine-type e2-micro \
  --disk-size 15

# Create a cluster with 2 nodes: Zonal cluster
gcloud container clusters create gke-zonal-playground \
  --num-nodes 1 \
  --machine-type e2-micro \
  --disk-size 15 \
  --zone europe-west1-b \
  --node-locations europe-west1-b

Verify Cluster
#

# Verify cluster
gcloud container clusters list

# Shell output: Regional cluster
NAME            LOCATION      MASTER_VERSION      MASTER_IP      MACHINE_TYPE  NODE_VERSION        NUM_NODES  STATUS
gke-playground  europe-west1  1.29.5-gke.1091002  34.77.108.155  e2-micro      1.29.5-gke.1091002  3          RUNNING

# Shell output: Zonal cluster
NAME                  LOCATION        MASTER_VERSION      MASTER_IP      MACHINE_TYPE  NODE_VERSION        NUM_NODES  STATUS
gke-zonal-playground  europe-west1-b  1.29.5-gke.1091002  34.38.207.175  e2-micro      1.29.5-gke.1091002  1          RUNNING

Set Kubeconfig
#

# Configure the kubectl configuration for the GKE cluster: Regional cluster
gcloud container clusters get-credentials gke-playground --region europe-west1 --project jkw-gcp-playground

# Configure the kubectl configuration for the GKE cluster: Zonal cluster
gcloud container clusters get-credentials gke-zonal-playground --region europe-west1-b --project jkw-gcp-playground


# Shell output: Regional cluster
Fetching cluster endpoint and auth data.
kubeconfig entry generated for gke-playground.

# Shell output: Zonal cluster
Fetching cluster endpoint and auth data.
kubeconfig entry generated for gke-zonal-playground.

Verify Cluster with Kubectl
#

# List cluster nodes
kubectl get nodes -o wide


# Shell output: Regional cluster
NAME                                            STATUS   ROLES    AGE     VERSION               INTERNAL-IP   EXTERNAL-IP      OS-IMAGE                             KERNEL-VERSION   CONTAINER-RUNTIME
gke-gke-playground-default-pool-7b3a9e1d-c340   Ready    <none>   7m44s   v1.29.5-gke.1091002   10.132.0.4    35.241.128.145   Container-Optimized OS from Google   6.1.85+          containerd://1.7.15
gke-gke-playground-default-pool-9850b4c8-vq0v   Ready    <none>   7m43s   v1.29.5-gke.1091002   10.132.0.3    104.155.43.32    Container-Optimized OS from Google   6.1.85+          containerd://1.7.15
gke-gke-playground-default-pool-fcf51c91-sx32   Ready    <none>   7m43s   v1.29.5-gke.1091002   10.132.0.5    34.79.89.140     Container-Optimized OS from Google   6.1.85+          containerd://1.7.15

# Shell output: Zonal cluster
NAME                                                  STATUS   ROLES    AGE     VERSION               INTERNAL-IP   EXTERNAL-IP      OS-IMAGE                             KERNEL-VERSION   CONTAINER-RUNTIME
gke-gke-zonal-playground-default-pool-fc3d154d-ps7t   Ready    <none>   4m35s   v1.29.5-gke.1091002   10.132.0.20   35.195.255.198   Container-Optimized OS from Google   6.1.85+          containerd://1.7.15

List Cluster Details
#

Node Pools
#

# List node pools
gcloud container node-pools list --cluster gke-playground --region europe-west1


# Shell output:
NAME          MACHINE_TYPE  DISK_SIZE_GB  NODE_VERSION
default-pool  e2-micro      15            1.29.5-gke.1091002
# Describe node pool
gcloud container node-pools describe default-pool --cluster gke-playground --region europe-west1

Cluster Configuration
#

# Review cluster configuration
gcloud container clusters describe gke-playground --region europe-west1



Exaple Deployment with Ingress & Managed TLS Certificate
#

Deployment & Service
#

# Create manifest for the deployment and it's ClusterIP service
vi nginx-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: ClusterIP
# Deploy the manifest
kubectl apply -f nginx-service.yaml

Verify Service
#

# List service
kubectl get svc nginx-service

# Shell output
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
nginx-service   ClusterIP   34.118.230.74   <none>        80/TCP    92m

Ingress
#

Create Global IP Address
#

# Create global IP address with the name "kubernetes-ingress"
gcloud compute addresses create kubernetes-ingress --global

# Shell output
Created [https://www.googleapis.com/compute/v1/projects/jkw-gcp-playground/global/addresses/kubernetes-ingress].

List Global IP Address
#

# List the IP address
gcloud compute addresses describe kubernetes-ingress --global --format="get(address)"

# Shell output:
34.54.191.35

Create DNS Entry
#

Create an A record the points to the public IP of the GKE Ingress LoadBalancer:

# Create DNS entry
34.54.191.35 gcp-gke.jklug.work
# Verify the DNS entry (It can take a while till the public DNS entry is ready)
ping gcp-gke.jklug.work

Google-managed Certificate
#

# Create manifest for the Google-manged certificate
vi managed-cert.yaml
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
  name: managed-cert
spec:
  domains: 
  - gcp-gke.jklug.work
# Deploy the managed certificate
kubectl apply -f managed-cert.yaml

# Shell output:
managedcertificate.networking.gke.io/managed-cert created

Verify Managed Certificate Status
#

Note: It can take up to 60 min till the certificate status changes from “provisioning” to “active”

# List ingress details
kubectl describe managedcertificate managed-cert
# Shell output
Name:         managed-cert
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  networking.gke.io/v1
Kind:         ManagedCertificate
Metadata:
  Creation Timestamp:  2024-07-18T15:59:31Z
  Generation:          2
  Resource Version:    69970
  UID:                 1ce240fa-f5bb-4da8-8812-02f25ca7d3a4
Spec:
  Domains:
    gcp-gke.jklug.work
Status:
  Certificate Name:    mcrt-1b79c7fe-f384-42ae-8544-dbfc12b0228f
  Certificate Status:  Provisioning # Wait till status changes to active
  Domain Status:
    Domain:  gcp-gke.jklug.work
    Status:  Provisioning # Wait till status changes to active
Events:
  Type    Reason  Age    From                            Message
  ----    ------  ----   ----                            -------
  Normal  Create  7m48s  managed-certificate-controller  Create SslCertificate mcrt-1b79c7fe-f384-42ae-8544-dbfc12b0228f
# Shell output:
kubectl describe managedcertificate managed-cert
Name:         managed-cert
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  networking.gke.io/v1
Kind:         ManagedCertificate
Metadata:
  Creation Timestamp:  2024-07-18T15:59:31Z
  Generation:          3
  Resource Version:    78641
  UID:                 1ce240fa-f5bb-4da8-7712-02f45ca7r3a4
Spec:
  Domains:
    gcp-gke.jklug.work
Status:
  Certificate Name:    mcrt-1b79c7fe-f384-42ae-8544-dbfc12b0228f
  Certificate Status:  Active # Wait till status changes to active
  Domain Status:
    Domain:     gcp-gke.jklug.work
    Status:     Active # Wait till status changes to active
  Expire Time:  2024-10-16T08:59:35.000-07:00
Events:
  Type    Reason  Age   From                            Message
  ----    ------  ----  ----                            -------
  Normal  Create  15m   managed-certificate-controller  Create SslCertificate mcrt-1b79c6fe-f384-42ae-8544-cbfc12b1228f

Create Ingress Resource
#

# Create GKE Ingress resources
vi managed-cert-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: managed-cert-ingress
  annotations:
    kubernetes.io/ingress.global-static-ip-name: "kubernetes-ingress"
    networking.gke.io/managed-certificates: "managed-cert"
    kubernetes.io/ingress.class: "gce"
spec:
  defaultBackend:
    service:
      name: nginx-service
      port:
        number: 80
# Deploy GKE Ingress resources
kubectl apply -f managed-cert-ingress.yaml

# Shell output:
Warning: annotation "kubernetes.io/ingress.class" is deprecated, please use 'spec.ingressClassName' instead
ingress.networking.k8s.io/managed-cert-ingress created

Note: Ignore the warning about “kubernetes.io/ingress.class”, if the ingress class is defined as recommended in the warning, the Ingress does not get an external IP.


List Ingress Details
#

Note: It takes some time to assign an external IP, because GCP is provisioning a load-balancer.

# List Ingress details
kubectl get ing managed-cert-ingress

# Shell output:
NAME                   CLASS    HOSTS   ADDRESS        PORTS   AGE
managed-cert-ingress   <none>   *       34.54.191.35   80      18m
# List ingress details
kubectl describe ing managed-cert-ingress

Verify the Deployment with TLS encryption
#

# Curl the DNS name via via HTTPS
curl https://gcp-gke.jklug.work

# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...



Delete GCP Resources
#

Delete Ingress Resource
#

# Delete GKE Ingress resource
kubectl delete ing managed-cert-ingress

Delete Managed Certificate
#

# Delete Google-managed certificate
kubectl delete managedcertificate managed-cert

Delete Global IP
#

# Delete the global IP address
gcloud compute addresses delete kubernetes-ingress --global

Delete GKE Cluster
#

# Delete Kubernetes Cluster
gcloud container clusters delete gke-zonal-playground --region europe-west1-b --quiet

Delete Project
#

# Delete project
gcloud projects delete jkw-gcp-playground

Links #

https://cloud.google.com/kubernetes-engine/docs/how-to/managed-certs
Kubernetes-Cluster - This article is part of a series.
Part 5: This Article