This page explains how to create and manage stateful workloads within a Google Distributed Cloud (GDC) air-gapped Kubernetes cluster. Stateful workloads let you scale your application deployment with persistent storage. Persistent storage provides your application with consistent identities and stable hostnames, regardless of where its workloads are scheduled.
This page is for developers within the application operator group, who are responsible for creating application workloads for their organization. For more information, see Audiences for GDC air-gapped documentation.
Before you begin
To run commands against a Kubernetes cluster, make sure you have the following resources:
- Locate the Kubernetes cluster name, or ask your Platform Administrator what the cluster name is. 
- Sign in and generate the kubeconfig file for the Kubernetes cluster if you don't have one. 
- Use the kubeconfig path of the Kubernetes cluster to replace - KUBERNETES_CLUSTER_KUBECONFIGin these instructions.
To get the required permissions to create stateful workloads, ask your
Organization IAM Admin to grant you the Namespace Admin role (namespace-admin)
in your project namespace.
Create a StatefulSet resource
Create a StatefulSet object by writing a StatefulSet manifest and
running kubectl apply to create the resource. To provide a stable way for
clients to send requests to the pods of your StatefulSet resource, you must
also create a Service object.
The kubectl apply command uses manifest files to create, update, and delete
resources in your Kubernetes cluster. This is a declarative method of object
configuration. This method retains writes made to live objects without merging
the changes back into the object configuration files.
To create a StatefulSet and Service resource, run:
kubectl --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG -n NAMESPACE \
    apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: SERVICE_NAME
  labels:
    app: APP_NAME
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: APP_NAME
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: STATEFULSET_NAME
spec:
  selector:
    matchLabels:
      app: APP_LABEL_NAME
  serviceName: "SERVICE_NAME"
  replicas: NUMBER_OF_REPLICAS
  template:
    metadata:
      labels:
        app: APP_LABEL_NAME
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: CONTAINER_NAME
        image: CONTAINER_IMAGE
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: CONTAINER_STORAGE_VOLUME_PATH
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi
EOF
Replace the following:
- KUBERNETES_CLUSTER_KUBECONFIG: the kubeconfig file for the cluster to which you're deploying container workloads.
- NAMESPACE: the project namespace in which to deploy the container workloads.
- SERVICE_NAME: the name of the- Serviceobject. Ensure the- StatefulSetobject sets the- Serviceobject in its- serviceNameas well.
- APP_NAME: the name of the application to run within the deployment.
- APP_LABEL_NAME: the label selector that determines which pods belong to the- StatefulSetobject.
- STATEFULSET_NAME: the name of the- StatefulSetobject.
- NUMBER_OF_REPLICAS: the number of replicated- Podobjects that the deployment manages.
- CONTAINER_NAME: the name of the container.
- CONTAINER_IMAGE: the name of the container image. You must include the container registry path and version of the image, such as- REGISTRY_PATH/nginx:1.23. For more information about setting the container registry path, see the Managed Harbor Service overview.
- CONTAINER_STORAGE_VOLUME_PATH: the path within the container at which a storage volume is mounted.
As an example, the following StatefulSet object and corresponding Service
object create stateful container workloads:
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: REGISTRY_PATH/nginx:1.23
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi
In this example:
- A Serviceobject namednginxis created, indicated by themetadata: namefield. TheServiceobject targets an app callednginx, indicated bylabels.app: nginxandselector.app: nginx. TheServiceobject exposes port 80 and names itweb. ThisServiceobject controls the network domain and routes internet traffic to the containerized application deployed by theStatefulSetobject.
- A StatefulSetnamedwebis created with three replicatedPodobjects, as set by the fieldreplicas: 3.
- The Podtemplate, set by the section.spec.template, indicates that itsPodobjects are labelledapp: nginx.
- The Podspecification, set by the section.template.spec, indicates that the pods of theStatefulSetrun one container,nginx, which runs thenginximage at version1.23.
- The Podspecification uses the web port opened by theServiceobject.
- The .template.spec.volumeMountssection specifies amountPathfield, which is namedwww. ThemountPathis the path in the container where a storage volume is mounted.
- The StatefulSetprovisions threePersistentVolumeClaimobjects, namedweb-www-0,web-www-1, andweb-www-2, with 1GB of provisioned storage each.
After it's created, the StatefulSet ensures that the desired number of Pod
objects are running and available at all times. The StatefulSet automatically
replaces Pod objects that fail or are evicted from their nodes, and associates
new Pod objects with the storage resources, resource requests and limits, and
other configurations defined in the Pod specification of the StatefulSet
object.
Request persistent storage in a StatefulSet resource
Persistent storage can be dynamically provisioned so that the underlying volumes
are created on demand. Applications can request persistent storage with a
PersistentVolumeClaim object.
Typically, you must create PersistentVolumeClaim objects in addition to
creating the Pod object. However, StatefulSet objects include a
volumeClaimTemplates array which generates the PersistentVolumeClaim
objects. Each StatefulSet replica gets its own PersistentVolumeClaim object.
For more information, see Configure container storage.