This tutorial describes how to upload a container application in an air-gapped Google Distributed Cloud (GDC) air-gapped environment, and run that application on a Kubernetes cluster. A containerized workload runs on a Kubernetes cluster within a project namespace. Clusters are logically separate from projects and from each other to provide different failure domains and isolation guarantees. However, you must ensure your cluster is attached to a project to allow for containerized workloads to be managed within a project.
One of the largest obstacles for deploying a container app is getting the binary for the app to your air-gapped data center. Work with your infrastructure team and administrators to transport the application to your workstation or implement this tutorial directly on your continuous integration and continuous delivery (CI/CD) server.
This tutorial uses a sample web server app available from the Google Cloud Artifact Registry.
Create managed Harbor registry
GDC air-gapped provides Harbor as a Service, which is a fully managed service that lets you store and manage container images using Harbor.
To use Harbor as a Service, you must first create a Harbor registry instance and Harbor project.
Create Harbor registry instance
To create a Harbor container registry instance, complete the following steps:
Console
In the navigation menu, select Harbor Container Registry from the CI/CD section.
Select the zone in which to create your Harbor instance. A Harbor instance is a zonal resource and must be created in each zone manually to ensure high availability.
Click Create Instance.
Enter the name of the instance and accept the Harbor managed Terms of Service.
Click Create Instance.
Confirm your new Harbor instance exists in the Harbor Instance section.
Click the Go to Harbor Instance external link and note the instance URL. For example, the instance URL format resembles
harbor-1.org-1.zone1.google.gdc.test
. The instance URL must not include thehttps://
prefix.Set the instance URL as a variable to use later in the tutorial:
export INSTANCE_URL=INSTANCE_URL
Replace INSTANCE_URL with the URL of the Harbor registry instance.
For example:
export INSTANCE_URL=harbor-1.org-1.zone1.google.gdc.test
gdcloud
Create the new Harbor container registry instance:
gdcloud harbor instances create INSTANCE_NAME \ --project=PROJECT \
Replace the following:
INSTANCE_NAME
: the name of the Harbor instance.PROJECT
: the name of the GDC project.
List the instance's URL:
gdcloud harbor instances describe INSTANCE_NAME \ --project=PROJECT
The output looks similar to the following:
# Several lines of code are omitted here. status: url: https://harbor-1.org-1.zone1.google.gdc.test
Set the instance URL as a variable to use later in the tutorial:
export INSTANCE_URL=INSTANCE_URL
Replace INSTANCE_URL with the URL of the Harbor registry instance. Make sure the instance URL does not include the
https://
prefix.For example:
export INSTANCE_URL=harbor-1.org-1.zone1.google.gdc.test
Create Harbor project in registry
You must create a Harbor project within the Harbor registry instance to manage your container images:
Console
Click Create A Harbor Project from the Harbor Container Registry page.
Enter the name of the project.
Click Create.
Set the Harbor project name as a variable to use later in the tutorial:
export HARBOR_PROJECT=HARBOR_PROJECT
gdcloud
Create the new Harbor project:
gdcloud harbor harbor-projects create HARBOR_PROJECT \ --project=PROJECT \ --instance=INSTANCE_NAME
Replace the following:
HARBOR_PROJECT
: the name of the Harbor project to create.PROJECT
: the name of the GDC project.INSTANCE_NAME
: the name of the Harbor instance.
Set the Harbor project name as a variable to use later in the tutorial:
export HARBOR_PROJECT=HARBOR_PROJECT
Configure Docker
To use Docker in your Harbor registry, complete the following steps:
Configure Docker to trust Harbor as a Service. For more information, see Configure Docker to trust Harbor root CA.
Configure Docker authentication to Harbor. For more information, see Configure Docker authentication to Harbor registry instances.
Create Kubernetes image pull secret
Since you're using a private Harbor project, you must create a Kubernetes image pull secret.
Add a Harbor project robot account to serve as your service account.
From the Harbor console, select your Harbor project.
Click Robot Accounts.
Select New Robot Account.
Give your new robot account a name and define any additional settings.
Click Add.
The robot account name and secret are displayed on the success screen. Keep this screen open for reference in the next step.
For more information, see Harbor's documentation: https://goharbor.io/docs/2.8.0/working-with-projects/project-configuration/create-robot-accounts/#add-a-robot-account.
In a new terminal window, sign in to Docker with your Harbor project robot account and secret token:
docker login ${INSTANCE_URL}
When prompted, insert the robot project name for the Username and the secret token for the Password that were provided in the previous step from the Harbor console success screen.
Set an arbitrary name for the image pull secret:
export SECRET=SECRET
Create the secret that is required for the image pull:
kubectl create secret docker-registry ${SECRET} \ --from-file=.dockerconfigjson=DOCKER_CONFIG \ -n ${NAMESPACE}
Replace
DOCKER_CONFIG
with the path to the.docker/config.json
file.Confirm that your secret exists in your GDC project namespace:
kubectl get secrets -n ${NAMESPACE}
The output is similar to the following:
NAME TYPE DATA AGE my-secret kubernetes.io/dockerconfigjson 1 23s
Push container image to managed Harbor registry
For this tutorial, you will download and push the nginx
web server image to
the managed Harbor registry, and use it to deploy a sample nginx web server
app to a Kubernetes cluster. The nginx web server app is available from the
public Docker Hub repository.
Pull the
nginx
image from Docker Hub to your local workstation using an external network:docker pull nginx
Tag the local image with the repository name:
docker tag nginx ${INSTANCE_URL}/${HARBOR_PROJECT}/nginx:1.25
Push the
nginx
container image to your managed Harbor registry:docker push ${INSTANCE_URL}/${HARBOR_PROJECT}/nginx:1.25
Create a Kubernetes cluster
Now that you have the nginx
container image stored in the managed Harbor registry
and can access it, create a Kubernetes cluster to run the nginx web server.
Console
In the navigation menu, select Kubernetes Engine > Clusters.
Click Create Cluster.
In the Name field, specify a name for the cluster.
Select the zone in which to create your Kubernetes cluster. A Kubernetes cluster is a zonal resource and must be created in each zone manually to ensure high availability.
Click Attach Project and select a project to attach to your cluster. Then click Save.
Click Create.
Wait for the cluster to be created. When the cluster is available to use, the status
READY
appears next to the cluster name.
API
Create a
Cluster
custom resource and save it as a YAML file, such ascluster.yaml
:apiVersion: cluster.gdc.goog/v1 kind: Cluster metadata: name: CLUSTER_NAME namespace: platform
Replace the
CLUSTER_NAME
value with the name of the cluster.Apply the custom resource to your GDC instance:
kubectl create -f cluster.yaml --kubeconfig ${MANAGEMENT_API_SERVER}
Attach a project to your Kubernetes cluster using the GDC console. You cannot attach a project to the cluster using the API at this time.
For more information on creating a Kubernetes cluster, see Create a Kubernetes cluster.
Deploy the sample container app
You are now ready to deploy the nginx
container image to your Kubernetes
cluster.
Kubernetes represents applications as Pod
resources, which are scalable units
holding one or more containers. The pod is the smallest deployable unit in
Kubernetes. Usually, you deploy pods as a set of replicas that can be scaled and
distributed together across your cluster. One way to deploy a set of replicas is
through a Kubernetes Deployment
.
In this section, you create a Kubernetes Deployment
to run the nginx
container app on your cluster. This Deployment has replicas, or pods. One
Deployment
pod contains only one container: the nginx
container image. You
also create a Service
resource that provides a stable way for clients to send
requests to the pods of your Deployment
.
Deploy the nginx web server to your Kubernetes cluster:
Sign in to the Kubernetes cluster and generate its kubeconfig file with a user identity. Set the kubeconfig path as an environment variable:
export KUBECONFIG=CLUSTER_KUBECONFIG_PATH
Create and deploy the Kubernetes
Deployment
andService
custom resources:kubectl --kubeconfig ${KUBECONFIG} -n ${NAMESPACE} \ create -f - <<EOF apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: ${INSTANCE_URL}/${HARBOR_PROJECT}/nginx:1.25 ports: - containerPort: 80 imagePullSecrets: - name: ${SECRET} --- apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app: nginx ports: - port: 80 protocol: TCP type: LoadBalancer EOF
Verify the pods were created by the deployment:
kubectl get pods -l app=nginx -n ${NAMESPACE}
The output is similar to the following:
NAME READY STATUS RESTARTS AGE nginx-deployment-1882529037-6p4mt 1/1 Running 0 1h nginx-deployment-1882529037-p29za 1/1 Running 0 1h nginx-deployment-1882529037-s0cmt 1/1 Running 0 1h
Create a network policy to allow all network traffic to the namespace:
kubectl --kubeconfig ${KUBECONFIG} -n ${NAMESPACE} \ create -f - <<EOF apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: annotations: name: allow-all spec: ingress: - from: - ipBlock: cidr: 0.0.0.0/0 podSelector: {} policyTypes: - Ingress EOF
Export the IP address for the
nginx
service:export IP=`kubectl --kubeconfig=${KUBECONFIG} get service nginx-service \ -n ${NAMESPACE} -o jsonpath='{.status.loadBalancer.ingress[*].ip}'`
Test the
nginx
server IP address usingcurl
:curl http://$IP