K8s 作业和 pods 差异作为主机+子域的使用
K8s jobs and pods differences as uses of host+subdomain
我有 Helm 3 使用的 K8s。
- 我需要在 yaml 文件(由 helm 创建)中 运行ning 时访问 k8s 作业。
kubectl 版本:
Client Version: version.Info{Major:"1", Minor:"21",
GitVersion:"v1.21.6",
GitCommit:"d921bc6d1810da51177fbd0ed61dc811c5228097",
GitTreeState:"clean", BuildDate:"2021-10-27T17:50:34Z",
GoVersion:"go1.16.9", Compiler:"gc", Platform:"linux/amd64"} Server
Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.6",
GitCommit:"d921bc6d1810da51177fbd0ed61dc811c5228097",
GitTreeState:"clean", BuildDate:"2021-10-27T17:44:26Z",
GoVersion:"go1.16.9", Compiler:"gc", Platform:"linux/amd64"}
头盔版本:
version.BuildInfo{Version:"v3.3.4",
GitCommit:"a61ce5633af99708171414353ed49547cf05013d",
GitTreeState:"clean", GoVersion:"go1.14.9"}
如下link:
DNS concept
它适用于 Pod,但不适用于作业。
如前所述,为了将主机名和子域放入 Pod 的 YAML 文件中,并添加包含该域的服务...
- 需要检查状态如果运行ning.
对于 pod,它是就绪状态。
kubectl wait pod/pod-name --for=condition=ready ...
作业没有就绪状态(而 pod 后面是 运行ning)。
如何检查作业(作业是 运行ning)背后的 pod 状态以及如何使用主机 + 子域进行作业?
我的代码...
(我删除了一些安全标签,但还是一样。重要 - 它可能很复杂。
我创建了一个侦听器 - 运行ning 侦听时,作业需要执行一些 curl 命令,这可以实现是否它可以访问作业后面的那个 pod):
Listener(pod是最后一个作业):
我添加的是主机名和子域(适用于 Pod,而不适用于 Job)。如果它曾经在 Pod 上 - 没问题。
我也发现Pod的名字(job创建的)有hash自动扩展
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "my-project.fullname" . }}-listener
namespace: {{ .Release.Namespace }}
labels:
name: {{ include "my-project.fullname" . }}-listener
app: {{ include "my-project.fullname" . }}-listener
component: {{ .Chart.Name }}
subcomponent: {{ .Chart.Name }}-listener
annotations:
"prometheus.io/scrape": {{ .Values.prometheus.scrape | quote }}
"prometheus.io/path": {{ .Values.prometheus.path }}
"prometheus.io/port": {{ .Values.ports.api.container | quote }}
spec:
template: #PodTemplateSpec (Core/V1)
spec: #PodSpec (core/v1)
hostname: {{ include "my-project.fullname" . }}-listener
subdomain: {{ include "my-project.fullname" . }}-listener-dmn
initContainers:
# twice - can add in helers.tpl
- name: wait-mysql-exist-pod
image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_POD_NAME
value: {{ .Release.Name }}-mysql
- name: COMPONENT_NAME
value: {{ .Values.global.mysql.database.name }}
command:
- /bin/sh
args:
- -c
- |-
while [ "$(kubectl get pod $MYSQL_POD_NAME 2>/dev/null | grep $MYSQL_POD_NAME | awk '{print ;}')" \!= "$MYSQL_POD_NAME" ];do
echo 'Waiting for mysql pod to be existed...';
sleep 5;
done
- name: wait-mysql-ready
image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_POD_NAME
value: {{ .Release.Name }}-mysql
command:
- kubectl
args:
- wait
- pod/$(MYSQL_POD_NAME)
- --for=condition=ready
- --timeout=120s
- name: wait-mysql-has-db
image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
imagePullPolicy: IfNotPresent
env:
{{- include "k8s.db.env" . | nindent 12 }}
- name: MYSQL_POD_NAME
value: {{ .Release.Name }}-mysql
command:
- /bin/sh
args:
- -c
- |-
while [ "$(kubectl exec $MYSQL_POD_NAME -- mysql -uroot -p$MYSQL_ROOT_PASSWORD -e 'show databases' 2>/dev/null | grep $MYSQL_DATABASE | awk '{print ;}')" \!= "$MYSQL_DATABASE" ]; do
echo 'Waiting for mysql database up...';
sleep 5;
done
containers:
- name: {{ include "my-project.fullname" . }}-listener
image: {{ .Values.global.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default "latest" }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
{{- include "k8s.db.env" . | nindent 12 }}
- name: SCHEDULER_DB
value: $(CONNECTION_STRING)
command: {{- toYaml .Values.image.entrypoint | nindent 12 }}
args: # some args ...
ports:
- name: api
containerPort: 8081
resources:
limits:
cpu: 1
memory: 1024Mi
requests:
cpu: 100m
memory: 50Mi
readinessProbe:
httpGet:
path: /api/scheduler/healthcheck
port: api
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 1
livenessProbe:
tcpSocket:
port: api
initialDelaySeconds: 120
periodSeconds: 10
timeoutSeconds: 5
volumeMounts:
- name: {{ include "my-project.fullname" . }}-volume
mountPath: /etc/test/scheduler.yaml
subPath: scheduler.yaml
readOnly: true
volumes:
- name: {{ include "my-project.fullname" . }}-volume
configMap:
name: {{ include "my-project.fullname" . }}-config
restartPolicy: Never
服务(针对子域):
apiVersion: v1
kind: Service
metadata:
name: {{ include "my-project.fullname" . }}-listener-dmn
spec:
selector:
name: {{ include "my-project.fullname" . }}-listener
ports:
- name: api
port: 8081
targetPort: 8081
type: ClusterIP
Roles + RoleBinding(为 curl 命令启用访问权限):
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ include "my-project.fullname" . }}-role
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list", "update"]
- apiGroups: [""] # "" indicates the core API group
resources: ["pods/exec"]
verbs: ["create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"]
- apiGroups: ["", "app", "batch"] # "" indicates the core API group
resources: ["jobs"]
verbs: ["get", "watch", "list"]
Role-Binding:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "go-scheduler.fullname" . }}-rolebinding
subjects:
- kind: ServiceAccount
name: default
roleRef:
kind: Role
name: {{ include "go-scheduler.fullname" . }}-role
apiGroup: rbac.authorization.k8s.io
最后是执行 curl 命令的测试人员:
(为了检查我输入了 tail -f
),然后进入 pod。
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "my-project.fullname" . }}-test
namespace: {{ .Release.Namespace }}
labels:
name: {{ include "my-project.fullname" . }}-test
app: {{ include "my-project.fullname" . }}-test
annotations:
"prometheus.io/scrape": {{ .Values.prometheus.scrape | quote }}
"prometheus.io/path": {{ .Values.prometheus.path }}
"prometheus.io/port": {{ .Values.ports.api.container | quote }}
spec:
template: #PodTemplateSpec (Core/V1)
spec: #PodSpec (core/v1)
initContainers:
# twice - can add in helers.tpl
#
- name: wait-sched-listener-exists
image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
imagePullPolicy: IfNotPresent
env:
- name: POD_NAME
value: {{ include "my-project.fullname" . }}-listener
command:
- /bin/sh
args:
- -c
- |-
while [ "$(kubectl get job $POD_NAME 2>/dev/null | grep $POD_NAME | awk '{print ;}')" \!= "$POD_NAME" ];do
echo 'Waiting for scheduler pod to exist ...';
sleep 5;
done
- name: wait-listener-running
image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
imagePullPolicy: IfNotPresent
env:
- name: POD_NAME
value: {{ include "my-project.fullname" . }}-listener
command:
- /bin/sh
args:
- -c
- |-
while [ "$(kubectl get pods 2>/dev/null | grep $POD_NAME | awk '{print ;}')" \!= "Running" ];do
echo 'Waiting for scheduler pod to run ...';
sleep 5;
done
containers:
- name: {{ include "my-project.fullname" . }}-test
image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
- /bin/sh
args:
- -c
- "tail -f"
# instead of above can be curl: "curl -H 'Accept: application/json' -X get my-project-listener.my-project-listener-dmn:8081/api/scheduler/jobs"
restartPolicy: Never
我进入测试舱
kubectl exec -it my-tester-<hash> -- /bin/sh
... 和 运行 命令:
ping my-project-listener.my-project-listener-dmn
得到:
ping: bad address 'my-project-listener.my-project-listener-dmn'
为 pod 执行此操作时:
PING pod-hostname.pod-subdomain (): ... data bytes
这里有很多问题,但我认为您应该能够通过一些小的更改来解决所有这些问题。
总而言之,我建议更改:
apiVersion: apps/v1
kind: Deployment # <-- not a Job
metadata: &original-job-metadata-from-the-question
spec:
template:
metadata:
labels: # vvv matching the Service selector
name: {{ include "my-project.fullname" . }}-listener
spec:
# delete all of the initContainers:
containers: &original-container-list-from-the-question
volumes: &original-volume-list-from-the-question
# delete restartPolicy: (default value Always)
删除角色和角色绑定对象;连接到服务 http://my-project-listener-dmn:8081
而不是单个 Pod;您可以 kubectl wait --for=condition=available
在 Deployment 上。
连接到服务,而不是单个 Pods(或作业或部署)。该服务名为 {{ include "my-project.fullname" . }}-listener-dmn
,这是您应该连接的主机名。该服务充当一个非常轻量级的 in-cluster 负载平衡器,并将请求转发到其选择器标识的 pods 之一。
所以在本示例中,您将连接到服务的名称和端口 http://my-project-listener-dmn:8081
。您的应用程序不响应 very-low-level ICMP 协议,我会避免 ping(1) 以支持更有用的诊断。还可以考虑将服务的端口设置为默认的 HTTP 端口 80;它不一定需要匹配 Pod 的端口。
服务选择器需要匹配 Pod 标签(而不是作业或部署的标签)。服务附加到 Pods; Job 或 Deployment 有一个模板可以创建 Pods;这是那些需要匹配的标签。您需要为 Pod 模板添加标签:
spec:
template:
metadata:
labels:
name: {{ include "my-project.fullname" . }}-listener
或者,在 Helm 图表中,您有一个助手来生成这些标签,
labels: {{- include "my-project.labels" | nindent 8 }}
这里要检查的是kubectl describe service my-project-listener-dmn
。底部应该有一行 Endpoints:
和一些 IP 地址(从技术上讲是一些单独的 Pod IP 地址,但您通常不需要知道)。如果显示 Endpoints: <none>
,这通常表示标签不匹配。
您可能需要某种程度的自动重启。 Pod 失败的原因有很多,包括代码错误和网络问题。如果你设置 restartPolicy: Never
那么你将有一个失败的 Pod,并且对服务的请求将失败,直到你采取某种手动干预。我建议至少将其设置为 restartPolicy: OnFailure
,或者(对于部署)将其保留为默认值 Always
。 (Kubernetes 文档中有more discussion on Job restart policies。)
您可能需要一个 Deployment 在这里。 Job 适用于您进行一些批处理然后作业完成的情况;这就是为什么 kubectl wait
没有您正在寻找的生命周期选项的部分原因。
我猜你想要一个 Deployment 。有了你在这里展示的内容,我认为除了
你根本不需要做任何改变
apiVersion: apps/v1
kind: Deployment
到目前为止关于服务和 DNS 以及标签的所有内容仍然适用。
您可以 kubectl wait
使 Deployment 可用。 由于作业预计 运行 完成并退出,这就是状态 kubectl wait
允许。如果至少有最小数量的托管 Pods 运行 通过了他们的健康检查,则部署是“可用的”,我认为这就是您所追求的状态。
kubectl wait --for=condition=available deployment/my-project-listener
有更简单的方法来检查数据库的活跃度。你在这里展示的大部分内容是一个具有特殊权限的复杂序列,可以查看数据库是否 运行 在 pod 启动之前。
如果在 pod 为 运行 时数据库出现故障会怎样?一个常见的事情是你会得到一系列的异常并且你的 pod 会崩溃。然后 restartPolicy: Always
Kubernetes 将尝试重新启动它;但如果数据库仍然不可用,它将再次崩溃;你会进入 CrashLoopBackOff 状态。如果数据库确实再次可用,那么最终 Kubernetes 将尝试重启 Pod 并且它会成功。
同样的逻辑也适用于启动时。如果 Pod 尝试启动,但数据库还没有准备好,它崩溃了,Kubernetes 默认会重启它,在前几次尝试后增加一些延迟。如果数据库在 30 秒左右启动,那么应用程序将在一分钟左右启动。重新启动计数将大于 0,但 kubectl logs --previous
希望有一个明确的异常。
这样您就可以删除此处显示的大约一半内容。删除initContainers:
块的全部;然后,由于您没有执行任何 Kubernetes API 操作,因此也删除 Role 和 RoleBinding 对象。
如果您真的想强制 Pod 等待数据库并将启动视为特殊情况,我建议使用 mysql
客户端工具使用更简单的 shell 脚本,或者甚至是进行基本 TCP 调用的 wait-for
脚本( 中描述的机制)。这仍然可以让您避免所有 Kubernetes RBAC 设置。
我有 Helm 3 使用的 K8s。
- 我需要在 yaml 文件(由 helm 创建)中 运行ning 时访问 k8s 作业。
kubectl 版本:
Client Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.6", GitCommit:"d921bc6d1810da51177fbd0ed61dc811c5228097", GitTreeState:"clean", BuildDate:"2021-10-27T17:50:34Z", GoVersion:"go1.16.9", Compiler:"gc", Platform:"linux/amd64"} Server Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.6", GitCommit:"d921bc6d1810da51177fbd0ed61dc811c5228097", GitTreeState:"clean", BuildDate:"2021-10-27T17:44:26Z", GoVersion:"go1.16.9", Compiler:"gc", Platform:"linux/amd64"}
头盔版本:
version.BuildInfo{Version:"v3.3.4", GitCommit:"a61ce5633af99708171414353ed49547cf05013d", GitTreeState:"clean", GoVersion:"go1.14.9"}
如下link: DNS concept
它适用于 Pod,但不适用于作业。
如前所述,为了将主机名和子域放入 Pod 的 YAML 文件中,并添加包含该域的服务...
- 需要检查状态如果运行ning.
对于 pod,它是就绪状态。
kubectl wait pod/pod-name --for=condition=ready ...
作业没有就绪状态(而 pod 后面是 运行ning)。
如何检查作业(作业是 运行ning)背后的 pod 状态以及如何使用主机 + 子域进行作业?
我的代码... (我删除了一些安全标签,但还是一样。重要 - 它可能很复杂。
我创建了一个侦听器 - 运行ning 侦听时,作业需要执行一些 curl 命令,这可以实现是否它可以访问作业后面的那个 pod):
Listener(pod是最后一个作业):
我添加的是主机名和子域(适用于 Pod,而不适用于 Job)。如果它曾经在 Pod 上 - 没问题。
我也发现Pod的名字(job创建的)有hash自动扩展
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "my-project.fullname" . }}-listener
namespace: {{ .Release.Namespace }}
labels:
name: {{ include "my-project.fullname" . }}-listener
app: {{ include "my-project.fullname" . }}-listener
component: {{ .Chart.Name }}
subcomponent: {{ .Chart.Name }}-listener
annotations:
"prometheus.io/scrape": {{ .Values.prometheus.scrape | quote }}
"prometheus.io/path": {{ .Values.prometheus.path }}
"prometheus.io/port": {{ .Values.ports.api.container | quote }}
spec:
template: #PodTemplateSpec (Core/V1)
spec: #PodSpec (core/v1)
hostname: {{ include "my-project.fullname" . }}-listener
subdomain: {{ include "my-project.fullname" . }}-listener-dmn
initContainers:
# twice - can add in helers.tpl
- name: wait-mysql-exist-pod
image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_POD_NAME
value: {{ .Release.Name }}-mysql
- name: COMPONENT_NAME
value: {{ .Values.global.mysql.database.name }}
command:
- /bin/sh
args:
- -c
- |-
while [ "$(kubectl get pod $MYSQL_POD_NAME 2>/dev/null | grep $MYSQL_POD_NAME | awk '{print ;}')" \!= "$MYSQL_POD_NAME" ];do
echo 'Waiting for mysql pod to be existed...';
sleep 5;
done
- name: wait-mysql-ready
image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_POD_NAME
value: {{ .Release.Name }}-mysql
command:
- kubectl
args:
- wait
- pod/$(MYSQL_POD_NAME)
- --for=condition=ready
- --timeout=120s
- name: wait-mysql-has-db
image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
imagePullPolicy: IfNotPresent
env:
{{- include "k8s.db.env" . | nindent 12 }}
- name: MYSQL_POD_NAME
value: {{ .Release.Name }}-mysql
command:
- /bin/sh
args:
- -c
- |-
while [ "$(kubectl exec $MYSQL_POD_NAME -- mysql -uroot -p$MYSQL_ROOT_PASSWORD -e 'show databases' 2>/dev/null | grep $MYSQL_DATABASE | awk '{print ;}')" \!= "$MYSQL_DATABASE" ]; do
echo 'Waiting for mysql database up...';
sleep 5;
done
containers:
- name: {{ include "my-project.fullname" . }}-listener
image: {{ .Values.global.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default "latest" }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
{{- include "k8s.db.env" . | nindent 12 }}
- name: SCHEDULER_DB
value: $(CONNECTION_STRING)
command: {{- toYaml .Values.image.entrypoint | nindent 12 }}
args: # some args ...
ports:
- name: api
containerPort: 8081
resources:
limits:
cpu: 1
memory: 1024Mi
requests:
cpu: 100m
memory: 50Mi
readinessProbe:
httpGet:
path: /api/scheduler/healthcheck
port: api
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 1
livenessProbe:
tcpSocket:
port: api
initialDelaySeconds: 120
periodSeconds: 10
timeoutSeconds: 5
volumeMounts:
- name: {{ include "my-project.fullname" . }}-volume
mountPath: /etc/test/scheduler.yaml
subPath: scheduler.yaml
readOnly: true
volumes:
- name: {{ include "my-project.fullname" . }}-volume
configMap:
name: {{ include "my-project.fullname" . }}-config
restartPolicy: Never
服务(针对子域):
apiVersion: v1
kind: Service
metadata:
name: {{ include "my-project.fullname" . }}-listener-dmn
spec:
selector:
name: {{ include "my-project.fullname" . }}-listener
ports:
- name: api
port: 8081
targetPort: 8081
type: ClusterIP
Roles + RoleBinding(为 curl 命令启用访问权限):
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ include "my-project.fullname" . }}-role
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list", "update"]
- apiGroups: [""] # "" indicates the core API group
resources: ["pods/exec"]
verbs: ["create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"]
- apiGroups: ["", "app", "batch"] # "" indicates the core API group
resources: ["jobs"]
verbs: ["get", "watch", "list"]
Role-Binding:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "go-scheduler.fullname" . }}-rolebinding
subjects:
- kind: ServiceAccount
name: default
roleRef:
kind: Role
name: {{ include "go-scheduler.fullname" . }}-role
apiGroup: rbac.authorization.k8s.io
最后是执行 curl 命令的测试人员:
(为了检查我输入了 tail -f
),然后进入 pod。
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "my-project.fullname" . }}-test
namespace: {{ .Release.Namespace }}
labels:
name: {{ include "my-project.fullname" . }}-test
app: {{ include "my-project.fullname" . }}-test
annotations:
"prometheus.io/scrape": {{ .Values.prometheus.scrape | quote }}
"prometheus.io/path": {{ .Values.prometheus.path }}
"prometheus.io/port": {{ .Values.ports.api.container | quote }}
spec:
template: #PodTemplateSpec (Core/V1)
spec: #PodSpec (core/v1)
initContainers:
# twice - can add in helers.tpl
#
- name: wait-sched-listener-exists
image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
imagePullPolicy: IfNotPresent
env:
- name: POD_NAME
value: {{ include "my-project.fullname" . }}-listener
command:
- /bin/sh
args:
- -c
- |-
while [ "$(kubectl get job $POD_NAME 2>/dev/null | grep $POD_NAME | awk '{print ;}')" \!= "$POD_NAME" ];do
echo 'Waiting for scheduler pod to exist ...';
sleep 5;
done
- name: wait-listener-running
image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
imagePullPolicy: IfNotPresent
env:
- name: POD_NAME
value: {{ include "my-project.fullname" . }}-listener
command:
- /bin/sh
args:
- -c
- |-
while [ "$(kubectl get pods 2>/dev/null | grep $POD_NAME | awk '{print ;}')" \!= "Running" ];do
echo 'Waiting for scheduler pod to run ...';
sleep 5;
done
containers:
- name: {{ include "my-project.fullname" . }}-test
image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
- /bin/sh
args:
- -c
- "tail -f"
# instead of above can be curl: "curl -H 'Accept: application/json' -X get my-project-listener.my-project-listener-dmn:8081/api/scheduler/jobs"
restartPolicy: Never
我进入测试舱
kubectl exec -it my-tester-<hash> -- /bin/sh
... 和 运行 命令:
ping my-project-listener.my-project-listener-dmn
得到:
ping: bad address 'my-project-listener.my-project-listener-dmn'
为 pod 执行此操作时:
PING pod-hostname.pod-subdomain (): ... data bytes
这里有很多问题,但我认为您应该能够通过一些小的更改来解决所有这些问题。
总而言之,我建议更改:
apiVersion: apps/v1
kind: Deployment # <-- not a Job
metadata: &original-job-metadata-from-the-question
spec:
template:
metadata:
labels: # vvv matching the Service selector
name: {{ include "my-project.fullname" . }}-listener
spec:
# delete all of the initContainers:
containers: &original-container-list-from-the-question
volumes: &original-volume-list-from-the-question
# delete restartPolicy: (default value Always)
删除角色和角色绑定对象;连接到服务 http://my-project-listener-dmn:8081
而不是单个 Pod;您可以 kubectl wait --for=condition=available
在 Deployment 上。
连接到服务,而不是单个 Pods(或作业或部署)。该服务名为 {{ include "my-project.fullname" . }}-listener-dmn
,这是您应该连接的主机名。该服务充当一个非常轻量级的 in-cluster 负载平衡器,并将请求转发到其选择器标识的 pods 之一。
所以在本示例中,您将连接到服务的名称和端口 http://my-project-listener-dmn:8081
。您的应用程序不响应 very-low-level ICMP 协议,我会避免 ping(1) 以支持更有用的诊断。还可以考虑将服务的端口设置为默认的 HTTP 端口 80;它不一定需要匹配 Pod 的端口。
服务选择器需要匹配 Pod 标签(而不是作业或部署的标签)。服务附加到 Pods; Job 或 Deployment 有一个模板可以创建 Pods;这是那些需要匹配的标签。您需要为 Pod 模板添加标签:
spec:
template:
metadata:
labels:
name: {{ include "my-project.fullname" . }}-listener
或者,在 Helm 图表中,您有一个助手来生成这些标签,
labels: {{- include "my-project.labels" | nindent 8 }}
这里要检查的是kubectl describe service my-project-listener-dmn
。底部应该有一行 Endpoints:
和一些 IP 地址(从技术上讲是一些单独的 Pod IP 地址,但您通常不需要知道)。如果显示 Endpoints: <none>
,这通常表示标签不匹配。
您可能需要某种程度的自动重启。 Pod 失败的原因有很多,包括代码错误和网络问题。如果你设置 restartPolicy: Never
那么你将有一个失败的 Pod,并且对服务的请求将失败,直到你采取某种手动干预。我建议至少将其设置为 restartPolicy: OnFailure
,或者(对于部署)将其保留为默认值 Always
。 (Kubernetes 文档中有more discussion on Job restart policies。)
您可能需要一个 Deployment 在这里。 Job 适用于您进行一些批处理然后作业完成的情况;这就是为什么 kubectl wait
没有您正在寻找的生命周期选项的部分原因。
我猜你想要一个 Deployment 。有了你在这里展示的内容,我认为除了
apiVersion: apps/v1
kind: Deployment
到目前为止关于服务和 DNS 以及标签的所有内容仍然适用。
您可以 kubectl wait
使 Deployment 可用。 由于作业预计 运行 完成并退出,这就是状态 kubectl wait
允许。如果至少有最小数量的托管 Pods 运行 通过了他们的健康检查,则部署是“可用的”,我认为这就是您所追求的状态。
kubectl wait --for=condition=available deployment/my-project-listener
有更简单的方法来检查数据库的活跃度。你在这里展示的大部分内容是一个具有特殊权限的复杂序列,可以查看数据库是否 运行 在 pod 启动之前。
如果在 pod 为 运行 时数据库出现故障会怎样?一个常见的事情是你会得到一系列的异常并且你的 pod 会崩溃。然后 restartPolicy: Always
Kubernetes 将尝试重新启动它;但如果数据库仍然不可用,它将再次崩溃;你会进入 CrashLoopBackOff 状态。如果数据库确实再次可用,那么最终 Kubernetes 将尝试重启 Pod 并且它会成功。
同样的逻辑也适用于启动时。如果 Pod 尝试启动,但数据库还没有准备好,它崩溃了,Kubernetes 默认会重启它,在前几次尝试后增加一些延迟。如果数据库在 30 秒左右启动,那么应用程序将在一分钟左右启动。重新启动计数将大于 0,但 kubectl logs --previous
希望有一个明确的异常。
这样您就可以删除此处显示的大约一半内容。删除initContainers:
块的全部;然后,由于您没有执行任何 Kubernetes API 操作,因此也删除 Role 和 RoleBinding 对象。
如果您真的想强制 Pod 等待数据库并将启动视为特殊情况,我建议使用 mysql
客户端工具使用更简单的 shell 脚本,或者甚至是进行基本 TCP 调用的 wait-for
脚本(