Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Warning

This page was translated from the original Japanese version by PLaMo Translate. The Japanese version is authoritative; the English translation may contain inaccuracies.

Deploying Servers

This page explains how to deploy always-on server-based workloads such as web servers or inference servers as Deployments on a PFCP cluster.

Note

Differences from Batch Jobs

Deployments are suitable for long-running workloads that continuously accept and process requests. On the other hand, batch processing that completes a fixed set of tasks and then terminates is better suited for Jobs or ParallelJobs. For creating distributed batch jobs, please refer to Creating a Distributed Batch Job.

Overview

When hosting servers on Kubernetes, you primarily use the following two resources:

  • Deployment — Manages specifications for Pods, including the number of container instances (replicas), update strategy, and health checks.
  • Service — Provides a stable name and IP address for the Pods launched by a Deployment, handling access from both within and outside the cluster.

Creating a Deployment

Below is an example of creating a Deployment named example-server. Please replace <image-name>:<tag> with your actual container image. Using the latest tag may lead to ambiguity with images having the same name, so we recommend using immutable tags like 1.2.3.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-server
spec:
  # Setting replicas to 2 or higher improves availability.
  # To avoid simultaneous shutdowns due to node failures or maintenance, we recommend configuring a PodDisruptionBudget as described below.
  replicas: 2
  selector:
    matchLabels:
      app: example-server
  template:
    metadata:
      labels:
        app: example-server
    spec:
      # Distributing Pods across multiple nodes minimizes the impact of node failures.
      # In validation/development environments with only one node, additional Pods will remain in Pending state.
      # In such cases, you can either change whenUnsatisfiable to ScheduleAnyway or add additional nodes.
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            app: example-server
      # Graceful shutdown timeout period in seconds.
      # Within this duration after receiving SIGTERM, the container will complete ongoing requests before terminating.
      terminationGracePeriodSeconds: 60
      containers:
      - name: main
        image: <image-name>:<tag>
        ports:
        - containerPort: 8080
        # requests are used for scheduling decisions (which node to launch on), while limits serve as upper bounds to prevent impact on other Pods.
        # For details on compute node types and available resources, see "Compute Node Types and Comparisons".
        resources:
          requests:
            cpu: "500m"
            memory: "512Mi"
          limits:
            # Exceeding CPU limits will temporarily throttle processing but won't stop the Pod, so this setting is optional.
            # Exceeding memory limits will forcefully terminate the Pod, so we recommend configuring it to limit the impact of abnormal states.
            memory: "2Gi"
        # livenessProbe: Checks whether the container should be restarted.
        # It automatically restarts the container to detect unrecoverable states like deadlocks.
        # Separating readinessProbe from endpoints and setting a larger failureThreshold prevents temporary response delays during high load from triggering restart loops.
        livenessProbe:
          httpGet:
            path: /healthz/live
            port: 8080
          initialDelaySeconds: 15
          periodSeconds: 20
          failureThreshold: 5
        # readinessProbe: Checks whether the container is ready to accept traffic.
        # It won't become READY during startup or warm-up periods, preventing Service-routed traffic from flowing.
        readinessProbe:
          httpGet:
            path: /healthz/ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
        # Graceful shutdown: Waits for disconnection from Services before receiving SIGTERM.
        lifecycle:
          preStop:
            sleep:
              seconds: 5
        # securityContext: We recommend configuring non-root execution and readOnlyRootFilesystem.
        # For details, see "Applying Security Policies to Pods".
        # securityContext:
        #   runAsNonRoot: true
        #   readOnlyRootFilesystem: true
        # Please manage sensitive information using Secrets or SealedSecrets.
        # For details, refer to "Handling Sensitive Data (Secrets)."
        # env:
        # - name: API_KEY
        #   valueFrom:
        #     secretKeyRef:
        #       name: my-secret
        #       key: api-key

Apply the Deployment and check its status.

$ kubectl apply -f deployment.yaml
deployment.apps/example-server created

$ kubectl get deployment example-server
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
example-server   2/2     2            2           30s

$ kubectl get pod -l app=example-server
NAME                              READY   STATUS    RESTARTS   AGE
example-server-7d4f9b6c8-abcde   1/1     Running   0          30s
example-server-7d4f9b6c8-fghij   1/1     Running   0          30s

Note

About Container Image Management

For information on using user-managed container images or PFCP-provided images, refer to “Using User-Managed Container Images” (./container-image.md) and “Using PFCP-Provided Container Images” (./pfcp-container-image.md).

Creating a Service

A Service is a Kubernetes resource that provides a stable network endpoint (IP address or DNS name) that remains consistent even when Pods change. It automatically routes traffic to Pods labeled with the specified selector. For details, see Kubernetes Official Documentation (Service).

apiVersion: v1
kind: Service
metadata:
  name: example-server-svc
spec:
  selector:
    app: example-server
  ports:
  - port: 80
    targetPort: 8080
$ kubectl apply -f service.yaml
service/example-server-svc created

$ kubectl get service example-server-svc
NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
example-server-svc   ClusterIP   10.96.100.200   <none>        80/TCP    10s

You can access it from other Pods in the same Namespace via http://example-server-svc/.

Rolling Updates and Scaling

To modify a Deployment, edit the manifest and apply changes using kubectl apply. By default, it updates existing Pods one by one while maintaining service availability (rolling update).

Updating the Image

Change the spec.template.spec.containers[].image field in deployment.yaml to a new tag.

       containers:
       - name: main
-        image: <image-name>:<old-tag>
+        image: <image-name>:<new-tag>

Tip

Use the kubectl explain command to check field details and structure.

$ kubectl explain deployment.spec.template.spec.containers.image

Apply the changes.

$ kubectl apply -f deployment.yaml
deployment.apps/example-server configured

Changing the Replica Count

Modify the spec.replicas field in deployment.yaml and apply the changes using kubectl apply.

   replicas: 4  # Change from 2 to 4
$ kubectl apply -f deployment.yaml
deployment.apps/example-server configured

Configuring PodDisruptionBudget

Limits the number of Pods that can be stopped simultaneously during node maintenance or other operations. With replicas: 2, setting minAvailable: 1 ensures at least one Pod remains running at all times.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: example-server-pdb
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: example-server

Best Practices Summary

CategoryRecommendedReference
Availabilityreplicas: 2 or higher + topologySpreadConstraints for node distribution + PodDisruptionBudget
Health ChecksSeparately configure readinessProbe (for acceptability) and livenessProbe (for restart necessity)
Graceful ShutdownUse preStop hook + terminationGracePeriodSeconds to wait for request processing to complete
Resource SpecificationAlways specify requests; we recommend configuring memory limits to limit the impact of abnormal statesComparing Compute Node Types
Image ManagementAvoid using latest tags and use immutable tags instead.Using User-Managed Container Images
SecuritySet securityContext for non-root execution and readOnlyRootFilesystemApplying Security Policies to Pods
Configuration/Secret ManagementManage configurations using ConfigMaps and sensitive data using SealedSecretsHandling Sensitive Data (Secrets)

Autoscaling

There are two methods for autoscaling Pods:

  • HorizontalPodAutoscaler (HPA) — Automatically adjusts replica count based on factors like CPU usage. For details, see Kubernetes Official Documentation.
  • KEDA — Enables autoscaling based on external events including Prometheus metrics (e.g., HTTP request counts). For details, see “Automatic Horizontal Scaling of Workloads and Jobs via External Events” (./keda.md).

Metrics Monitoring

Using the ServiceMonitor custom resource, you can scrape metrics published by Pods using Prometheus and configure dashboards and alerts in Grafana. For details, see “Metrics Monitoring and Alerting” (./monitoring.md).

Continuous Delivery via GitOps

For GitOps configuration that manages Deployment, Service, PodDisruptionBudget, and other manifests in a Git repository and automatically applies changes to the cluster, see “Configuring GitOps-Style Continuous Delivery” (./gitops.md).

Integration with Public Clouds

When running servers that access AWS or Google Cloud resources, you can access them using identity federation without embedding credentials directly in the container. For details, see “Configuring Identity Federation with Public Clouds” (./id-federation.md).

Relational Databases

If you need to use a relational database as a backend for your servers, PFCP provides MOCO to deploy MySQL clusters within the same cluster. For details, see “Running Relational Databases” (./database.md).

Next Steps

To expose your created Service to the internet, refer to the following documentation: