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 explaincommand 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
| Category | Recommended | Reference |
|---|---|---|
| Availability | replicas: 2 or higher + topologySpreadConstraints for node distribution + PodDisruptionBudget | — |
| Health Checks | Separately configure readinessProbe (for acceptability) and livenessProbe (for restart necessity) | — |
| Graceful Shutdown | Use preStop hook + terminationGracePeriodSeconds to wait for request processing to complete | — |
| Resource Specification | Always specify requests; we recommend configuring memory limits to limit the impact of abnormal states | Comparing Compute Node Types |
| Image Management | Avoid using latest tags and use immutable tags instead. | Using User-Managed Container Images |
| Security | Set securityContext for non-root execution and readOnlyRootFilesystem | Applying Security Policies to Pods |
| Configuration/Secret Management | Manage configurations using ConfigMaps and sensitive data using SealedSecrets | Handling Sensitive Data (Secrets) |
Related Features
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:
- Publishing Workloads as Web Applications — Expose as a web application accessible via browser
- Publishing Workloads as Web APIs — Expose as a web API accessible via CLI or other tools