开发应用程序
开发配置以将应用程序工作负载部署到 Red Hat® OpenShift® on IBM Cloud®。 由于 Kubernetes 是一个可扩展的容器编排平台,不要求使用特定语言或应用程序,因此可以运行以您选择的语言编写的各种工作负载,例如无状态应用程序、有状态应用程序和数据处理应用程序。
在 YAML 文件中指定应用程序需求
在 Kubernetes 中,您可在 YAML 文件中描述应用程序,该文件声明了 Kubernetes 对象的配置。 然后,Kubernetes API 服务器会处理 YAML 文件,并在 etcd 数据存储中存储对象的配置和所需状态。 Kubernetes 调度程序将工作负载安排到集群内的工作程序节点上,并考虑 YAML 文件中的规范、管理员设置的任何集群策略以及可用的集群容量。
查看 完整 YAML 文件的副本。 然后,查看以下各部分以了解如何增强应用程序部署。
想要了解有关 Kubernetes 对象如何协同工作以进行部署的更多信息? 查看 了解应用程序的 Kubernetes 对象。
基本部署元数据
将相应的 API 版本用于部署的 Kubernetes 对象类型。 API 版本将确定可供您使用的 Kubernetes 对象的受支持功能。 在元数据中提供的名称是对象的名称,而不是对象的标签。 在与对象交互时使用该名称,例如 oc get deployment <name>
。
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
副本集
要提高应用程序的可用性,可以在部署中指定副本集。 在副本集内,定义要部署的应用程序实例数。 副本集由 Kubernetes 部署进行管理和监视。 如果一个应用程序实例停止运行,Kubernetes 会自动启动新的应用程序实例,以保持指定数量的应用程序实例。
spec:
replicas: 3
标签
通过标签,可以使用相同的 key: value
对来标记集群中不同类型的资源。 然后,可以指定与标签匹配的选择器,以便可以基于这些其他资源进行构建。 如果计划以公共方式公开应用程序,那么必须使用与服务中指定的选择器相匹配的标签。 在示例中,部署 spec 使用的是与标签 app: wasliberty.
相匹配的模板。
您可以检索在集群中标注的对象,如查看 staging
或 production
组成部分。 例如,列出集群中所有名称空间上具有 env: production
标签的所有资源。 注意: 运行此命令需要访问所有命名空间。
oc get all -l env=production --all-namespaces
- 有关标签的更多信息,请参阅 Kubernetes 文档。
- 将标签应用于工作程序节点。
- 有关更详细的示例,请参阅使用标签将应用程序部署到特定工作程序节点。
selector:
matchLabels:
app: wasliberty
template:
metadata:
labels:
app: wasliberty
亲缘关系
如果您希望对 pod 在哪个工作节点上调度有更多控制权,请指定亲缘关系(主机托管)。 亲缘关系仅在安排时影响 pod。 例如,要将部署分散到各个工作节点,而不是让 pod 在同一节点上进行调度,可在标准集群中使用 podAntiAffinity
选项。 可以定义两种类型的 pod 反亲缘关系:首选或必需。
如需了解更多信息,请参阅 Kubernetes 关于将 Pod 分配给节点的文档。
- 必需的反亲缘关系
- 您只能部署与工作者节点数量相当的副本。 例如,如果集群中有 3 个工作程序节点,但在 YAML 文件中定义了 5 个副本,那么仅部署 3 个副本。 每个副本位于不同的工作程序节点上。 剩余的 2 个副本保持暂挂状态。 如果将另一个工作程序节点添加到集群,那么其中一个剩余副本将自动部署到这一新的工作程序节点。 如果工作程序节点发生故障,那么该 pod 不会重新安排,因为需要亲缘关系策略。 有关包含 required 的 YAML 示例,请参阅 包含 required pod anti-affinity 的 Liberty 应用程序。
- 首选反亲缘关系
- 您可以将 pod 部署到具有可用容量的节点上,从而为您的工作负载提供更大的灵活性。 如果可能,pod 将安排在不同的工作程序节点上。 例如,如果集群中有 3 个工作程序节点具有足够的容量,那么可以在这些节点上安排 5 个副本 pod。 但是,如果向群集中添加两个额外的工作节点,亲和规则不会强制在现有节点上运行的两个额外 pod 重新安排到可用节点上。
- 工作程序节点亲缘关系
- 您可以将部署配置为仅在某些工作节点(如裸机)上运行。 有关更多信息,请参阅使用标签将应用程序部署到特定工作程序节点。
首选反亲缘关系的示例
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- wasliberty
topologyKey: kubernetes.io/hostname
容器映像
指定要用于容器的映像、该映像的位置以及映像拉取策略。 如果未指定图像标记,默认情况下会提取标记为 latest
的图像。
避免将最新标签用于生产工作负载。 如果使用的是公共存储库或共享存储库(例如,Docker Hub 或 IBM Cloud Container Registry),那么您可能尚未使用最新映像测试过工作负载。
例如,要列出公共 IBM 映像的标记,请执行以下操作:
- 切换到全局注册表区域。
ibmcloud cr region-set global
- 列出 IBM 映像。
ibmcloud cr images --include-ibm
缺省 imagePullPolicy
设置为 IfNotPresent
,仅当映像在本地不存在时才会拉取该映像。 如果希望每次容器启动时都拉取映像,请指定 imagePullPolicy: Always
。
containers:
- name: wasliberty
image: icr.io/ibm/liberty:webProfile8
imagePullPolicy: Always
用于应用程序服务的端口
选择容器端口以用于打开应用程序的服务。 要查看需要打开哪个端口,请参阅应用程序规范或 Dockerfile。 该端口可从专用网络进行访问,但不能从公用网络连接进行访问。 要以公共方式公开应用程序,那么必须创建 NodePort、LoadBalancer 或 Ingress 服务。 在创建Service
对象时,可使用此相同的端口号。
对于 IBM Cloud中的所有服务,将阻止端口 25。
ports:
- containerPort: 9080
资源请求和限制
集群管理员通过为集群中的每个 Red Hat OpenShift 项目创建 ResourceQuota
对象,确保共享集群的团队不会占用超过其公平份额的计算资源(内存和 CPU)。 如果集群管理员设置了计算资源配额,那么部署模板中的每个容器都必须指定内存和
CPU 的资源请求和限制,否则创建 pod 会失败。
- 检查是否为名称空间设置了资源配额。
oc get quota --namespace=<namespace>
- 查看配额限制。
oc describe quota <quota_name> --namespace=<namespace>
即使未设置任何资源配额,您也可以在部署中包含资源请求和限制,以改进对工作程序节点资源的管理。
如果容器超过其限制,容器可能会重新启动或发生故障。 如果容器超过请求,那么当工作程序节点即将耗尽超过的该资源时,可能会逐出该容器的 pod。 有关故障诊断的更多信息,请参阅 pod 重新启动一再失败或 pod 被意外除去。
- 请求
- 调度程序为容器保留的最小资源量。 如果资源量等于限制,那么将保证请求。 如果资源量小于限制,那么仍会保证请求,但调度程序可以使用请求和限制之间的差异来利用其他容器的资源。
- 限制
- 容器可消耗的最大资源量。 如果跨容器使用的资源总量超过工作程序节点上的可用资源量,那么会逐出容器以释放空间。 为了避免逐出,请将资源请求设置为等于容器限制。 如果未指定任何限制,那么缺省值为工作程序节点的容量。
更多信息,请参阅 Kubernetes 文档。
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1024Mi"
cpu: "1000m"
活性和就绪性探测器
缺省情况下,Kubernetes 在 pod 中的所有容器启动后,会将流量发送到应用程序 pod,如果容器崩溃,会重新启动这些容器。 但是,您可以设置运行状况检查,以提高服务流量路由的稳健性。
例如,应用程序可能会有启动延迟。 应用程序进程可能在整个应用程序完全就绪之前启动,这可能会影响响应,尤其是跨多个实例扩展时。 通过运行状况检查,您可以让系统了解应用程序是否在运行以及是否已准备好接收请求。 通过设置这两个探测器,还可以帮助避免在执行应用程序的滚动更新时发生中断。 您可以设置两种类型的运行状况检查:活性和就绪性探测器。
- 活动性探测器
- 设置一个有效性探针,以检查容器是否正在运行。 如果该探测器失败,那么容器将重新启动。 如果容器未指定活性探测器,那么该探测器会成功,因为它会假定当容器处于正在运行状态时即有活性。
- 就绪性探测器
- 设置就绪探测器,检查容器是否已准备好接收请求和外部流量。 如果该探测器失败,那么会将 pod 的 IP 地址作为与该 pod 匹配的服务的可用 IP 地址除去,但不会重新启动容器。 如果您的应用程序需要一段时间才能启动,那么设置带有初始延迟的就绪探测器就显得尤为重要。 在初始延迟之前,探测器不会启动,从而为容器提供启动时间。 如果容器未提供就绪性探测器,那么该探测器会成功,因为它会假定当容器处于正在运行状态时即有活性。
您可以将探测器设置为命令、HTTP 请求或 TCP 套接字。 示例使用的是 HTTP 请求。 为活性探测器提供比就绪性探测器长的时间。 更多信息,请参阅 Kubernetes 文档。
livenessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 300
periodSeconds: 15
readinessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 45
periodSeconds: 5
Pod 中断预算
要提高应用程序的可用性,可以根据要使用 PodDisruptionBudget
对象的可用性类型来控制应用程序如何对 中断 作出反应。
Pod 中断预算可帮助您规划应用程序在自愿中断期间的行为方式,例如,当您通过更新应用程序部署来启动直接重新启动时,或者非自愿中断 (例如,内核崩溃) 时。
minAvailable
: 您可以指定发生中断后仍必须可用的 pod 的数量或百分比。
maxUnavailable
- 您可以指定发生中断后不可用的 pod 的数量或百分比。 此示例使用
maxUnavailable: 1
。 selector
- 输入标签,选择
PodDisruptionBudget
适用的 pod 集。 请注意,如果在其他 pod 部署中使用了此相同标签,那么 pod 也适用于这些 pod。
更多信息,请参阅 Kubernetes 文档。
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: wasliberty
spec:
maxUnavailable: 1
selector:
matchLabels:
app: wasliberty
公开应用程序服务
您可以创建用于公开应用程序的服务。 在 spec
部分中,确保使 port
和 label 值与部署中使用的值相匹配。 该服务将公开与标签匹配的对象,例如以下示例中的 app: wasliberty
。
- 默认情况下,服务使用
ClusterIP
,这使得服务只能在集群内访问,而不能在集群外访问。 - 可以创建 NodePort、LoadBalancer 或 Ingress 服务以公共方式公开应用程序。 这些服务有两个 IP:一个外部 IP 和一个内部 IP。 外部 IP 上收到流量时,会将其转发到内部集群 IP。 然后,流量从内部集群 IP 路由到应用程序的容器 IP。
- 以下示例使用
NodePort
在集群外部公开服务。 有关如何设置外部访问权的更多信息,请参阅选择 NodePort、LoadBalancer 或 Ingress 服务。
apiVersion: v1
kind: Service
metadata:
name: wasliberty
labels:
app: wasliberty
spec:
ports:
- port: 9080
selector:
app: wasliberty
type: NodePort
如果需要部署 hostNetwork
pod 以侦听特定端口或使用 hostPort
在工作程序节点上的特定端口上公开应用程序 pod,请使用 11000-11200
范围内的端口。Red Hat OpenShift on IBM Cloud 为此目的在工作程序节点上指定 11000-11200
端口范围,以避免与 Red Hat OpenShift on IBM Cloud
使用的本地端口和其他端口发生冲突。 由于 hostNetwork
pod 和hostPorts
指的是一个特定工作程序节点 IP 地址,因此这些 pod 限制为仅在该工作程序节点上运行。 如果发生意外情况,例如工作者节点被移除或资源耗尽,您的 pod 就无法重新排程。 如果要在工作程序节点上公开 pod 的端口,请考虑改为使用 NodePort
服务。
更多信息,请参阅 Kubernetes 最佳实践文档。
用于容器环境变量的配置映射
配置映射提供了部署工作负载的非敏感配置信息。
以下示例显示如何在部署 YAML 的容器 spec 部分中,将配置映射中的值作为环境变量引用。 通过引用配置映射中的值,可以将此配置信息与部署相分离,从而使容器化应用程序保持可移植。
- 帮我决定是否将 Kubernetes
ConfigMap
或Secret
对象用于变量。 - 有关使用 configmaps 的更多方法,请参阅 Kubernetes 文档。
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
template:
...
spec:
...
containers:
- name: wasliberty
...
env:
- name: VERSION
valueFrom:
configMapKeyRef:
name: wasliberty
key: VERSION
- name: LANGUAGE
valueFrom:
configMapKeyRef:
name: wasliberty
key: LANGUAGE
...
---
apiVersion: v1
kind: ConfigMap
metadata:
name: wasliberty
labels:
app: wasliberty
data:
VERSION: "1.0"
LANGUAGE: en
用于容器环境变量的私钥
私钥提供了部署工作负载的敏感配置信息(例如,密码)。
以下示例显示如何在部署 YAML 的容器 spec 部分中,将私钥中的值作为环境变量引用。 您还可以将私钥安装为卷。 通过引用私钥中的值,可以将此配置信息与部署相分离,从而使容器化应用程序保持可移植。
要在应用程序运行时集中管理集群和注入中的所有私钥,请尝试 IBM Cloud Secrets Manager。
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
template:
...
spec:
...
containers:
- name: wasliberty
...
env:
- name: username
valueFrom:
secretKeyRef:
name: wasliberty
key: username
- name: password
valueFrom:
secretKeyRef:
name: wasliberty
key: password
...
---
apiVersion: v1
kind: Secret
metadata:
name: wasliberty
labels:
app: wasliberty
type: Opaque
data:
username: dXNlcm5hbWU=
password: cGFzc3dvcmQ=
用于容器存储的持久卷
持久卷 (PV) 与物理存储器连接,为容器工作负载提供持久性数据存储器。
以下示例显示如何向应用程序添加持久性存储器。 要供应持久性存储器,可创建持久卷声明 (PVC),以描述要拥有的文件存储器的类型和大小。 创建 PVC 后,将通过动态调配自动创建持久卷和物理存储。 通过在部署 YAML 中引用 PVC,存储器会自动安装到应用程序 pod。 当 pod 中的容器将数据写入 /test
安装路径目录时,数据将存储在 NFS 文件存储器实例上。 有关可以供应的其他存储器类型的选项,请参阅规划高可用性持久性存储器。
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
template:
...
spec:
...
containers:
- name: wasliberty
...
volumeMounts:
- name: pvmount
mountPath: /test
volumes:
- name: pvmount
persistentVolumeClaim:
claimName: wasliberty
...
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wasliberty
annotations:
volume.beta.kubernetes.io/storage-class: "ibmc-file-bronze"
labels:
billingType: "hourly"
app: wasliberty
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 24Gi
完整示例部署 YAML
以下示例是先前逐个部分讨论的部署 YAML 的副本。 您也可以 从 GitHub 下载 YAML。
要应用 YAML,
oc apply -f file.yaml [-n <namespace>]
示例 YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
selector:
matchLabels:
app: wasliberty
template:
metadata:
labels:
app: wasliberty
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- wasliberty
topologyKey: kubernetes.io/hostname
containers:
- name: wasliberty
image: icr.io/ibm/liberty:latest
env:
- name: VERSION
valueFrom:
configMapKeyRef:
name: wasliberty
key: VERSION
- name: LANGUAGE
valueFrom:
configMapKeyRef:
name: wasliberty
key: LANGUAGE
- name: username
valueFrom:
secretKeyRef:
name: wasliberty
key: username
- name: password
valueFrom:
secretKeyRef:
name: wasliberty
key: password
ports:
- containerPort: 9080
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1024Mi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 300
periodSeconds: 15
readinessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 45
periodSeconds: 5
volumeMounts:
- name: pvmount
mountPath: /test
volumes:
- name: pvmount
persistentVolumeClaim:
claimName: wasliberty
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: wasliberty
spec:
maxUnavailable: 1
selector:
matchLabels:
app: wasliberty
---
apiVersion: v1
kind: Service
metadata:
name: wasliberty
labels:
app: wasliberty
spec:
ports:
- port: 9080
selector:
app: wasliberty
type: NodePort
---
apiVersion: v1
kind: ConfigMap
metadata:
name: wasliberty
labels:
app: wasliberty
data:
VERSION: "1.0"
LANGUAGE: en
---
apiVersion: v1
kind: Secret
metadata:
name: wasliberty
labels:
app: wasliberty
type: Opaque
data:
username: dXNlcm5hbWU=
password: cGFzc3dvcmQ=
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wasliberty
annotations:
volume.beta.kubernetes.io/storage-class: "ibmc-file-bronze"
labels:
billingType: "hourly"
app: wasliberty
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 24Gi