一、镜像拉取失败:你遇到的第一个拦路虎

部署OpenFaaS到Kubernetes集群时,Pod启动失败最常见的原因就是镜像拉取失败。你以为配置好一个YAML文件就能万事大吉?结果kubectl get pods一看,状态是ImagePullBackOff或者ErrImagePull,然后就开始抓狂。

这种情况多半是因为镜像仓库需要登录,或者你写的镜像标签根本不存在。比如,OpenFaaS的gateway镜像默认从Docker Hub拉取,但如果你公司内部用的是私有仓库,没有配置凭证,那K8s肯定找不到。或者你写了个latest标签,但本地已经缓存了旧版本,导致版本不匹配。

1.1 私有仓库认证:imagePullSecrets是关键

如果你的OpenFaaS镜像放在私有仓库(比如阿里云镜像仓库、AWS ECR、或者自建的Harbor),必须让K8s知道怎么登录。通常的做法是创建一个Secret,然后在Pod的Spec里引用它。

# 示例:创建私有仓库的Secret
# 技术栈:Kubernetes Secret(YAML)
apiVersion: v1
kind: Secret
metadata:
  name: my-private-registry-secret
  namespace: openfaas
data:
  # 以下值需要base64编码
  # 实际用户名: myuser,密码: mypassword,邮箱: myemail@example.com
  .dockerconfigjson: eyJhdXRocyI6eyJodHRwczovL2luZGV4LmRvY2tlci5pby92MSI6eyJ1c2VybmFtZSI6Im15dXNlciIsInBhc3N3b3JkIjoibXlwYXNzd29yZCIsImVtYWlsIjoibXllbWFpbEBleGFtcGxlLmNvbSJ9fX0= # 示例值,实际需要替换
type: kubernetes.io/dockerconfigjson

然后在你使用OpenFaaS的Deployment或DaemonSet里,加上imagePullSecrets:

# 示例:在OpenFaaS gateway的Deployment中引用私有仓库凭证
# 技术栈:Kubernetes Deployment(YAML)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gateway
  namespace: openfaas
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gateway
  template:
    metadata:
      labels:
        app: gateway
    spec:
      containers:
      - name: gateway
        image: myprivaterepo.com/openfaas/gateway:0.24.0  # 这里填写私有镜像的完整路径
        ports:
        - containerPort: 8080
      imagePullSecrets:
      - name: my-private-registry-secret  # 引用上面创建的Secret

注意:如果你用的不是Docker Hub,而是AWS ECR或者别的,凭证类型可能不一样。ECR需要用AWS IAM或者临时令牌,这时候可以借助kubectl create secret docker-registry来快速创建:

# 使用kubectl命令创建ECR的Secret(需要先获取登录令牌)
# 技术栈:Shell命令
# 前提:已经通过aws ecr get-login-password获取令牌,并设置环境变量
kubectl create secret docker-registry ecr-secret \
  --docker-server=123456789012.dkr.ecr.us-east-1.amazonaws.com \
  --docker-username=AWS \
  --docker-password=$(aws ecr get-login-password --region us-east-1) \
  --namespace=openfaas

很多人在这里容易犯的错:忘记指定namespace,结果Secret创建到了default命名空间,而Pod在openfaas命名空间里,找不到对应的Secret,镜像拉取继续失败。

1.2 镜像标签陷阱:别一味用latest

有些同学为了图省事,总是写image: openfaas/gateway:latest。但latest其实是个不靠谱的东西——它会在你每次重新拉取时随时变化。而且K8s的镜像拉取策略默认是IfNotPresent,如果本地已经有同名镜像,它就不会再拉新的。如果你前一天部署了旧版,今天新版发布,pod还是用旧版。

最好明确指定版本标签,比如0.24.0。而且如果你的私有仓库里没有这个标签,Pod就会进入ImagePullBackOff循环。这时候可以用kubectl describe pod来查原因:

# 查看Pod启动失败的详细原因
# 技术栈:Shell命令
kubectl describe pod/gateway-xxx -n openfaas
# 输出里会看到类似 "Failed to pull image ...": manifest for ... not found

如果你不确定某个版本的镜像是否存在,可以先在节点上手动docker pull测试下,或者直接去Docker Hub看Tags列表。

二、权限配置陷阱:ServiceAccount与RBAC

镜像拉取成功,Pod终于变成Running了吗?不,你可能还会遇到CrashLoopBackOff。这是因为OpenFaaS的组件(比如gateway、faas-netes)需要调用Kubernetes API来创建、删除函数。默认情况下,Pod使用的ServiceAccount(叫default)没有足够的权限,导致启动核心功能报错。

2.1 创建专用的ServiceAccount

OpenFaaS官方提供了安装脚本,里面会创建ServiceAccount和RBAC绑定。但很多人图省事直接复制别人的配置,结果漏掉了这部分。你需要手动创建时,确保包含以下内容:

# 示例:为OpenFaaS创建ServiceAccount和ClusterRole
# 技术栈:Kubernetes RBAC(YAML)
apiVersion: v1
kind: ServiceAccount
metadata:
  name: openfaas-controller
  namespace: openfaas
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: openfaas-controller
rules:
- apiGroups: [""] # 核心API组
  resources: ["services", "pods", "configmaps", "secrets", "namespaces"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["networking.k8s.io"]
  resources: ["ingresses"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: openfaas-controller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: openfaas-controller
subjects:
- kind: ServiceAccount
  name: openfaas-controller
  namespace: openfaas

注意:上面的权限看起来给了很多,但实际还需要考虑functions的CRD(CustomResourceDefinition)权限。OpenFaaS在较新版本里使用了自己的自定义资源(比如Function、HttpTrigger等),如果你部署了OpenFaaS Pro或者用了CRD,还需要额外授权。否则Pod日志里会有类似“Failed to list *v1.Function: the server could not find the requested resource”的错误。

2.2 如何快速诊断权限问题

当Pod不断重启时,先看看日志:

# 查看gateway Pod的日志
# 技术栈:Shell命令
kubectl logs -n openfaas deployment/gateway --tail=50

如果日志里出现“forbidden”“unauthorized”等字眼,基本就是RBAC问题。这时候可以临时创建一个具有cluster-admin权限的ServiceAccount来测试(但不推荐生产使用),确认问题后再调小权限。

三、实战案例:一步步排查并修复

假设你有一个裸K8s集群(比如用minikube或者kubeadm搭建的),你想部署OpenFaaS,但发现所有Pod都处于CrashLoopBackOff。我们来完整走一遍排查流程。

3.1 首先检查Pod状态

# 查看所有Pod
# 技术栈:Shell命令
kubectl get pods -n openfaas
# 输出可能像:
# NAME                        READY   STATUS             RESTARTS   AGE
# gateway-6f8b9c9d6d-abc12    0/1     CrashLoopBackOff   5          3m
# nats-7d8f9c9d6d-def34       0/1     ImagePullBackOff   0          3m

这里看到两个问题:gateway崩溃循环,nats镜像拉取失败。我们先修镜像拉取(因为简单)。

3.2 修复NATS镜像

nats镜像通常是从Docker Hub拉取的,但如果你网络不好或者被墙了,就会ImagePullBackOff。解决方案:改用国内的镜像源,比如阿里云镜像。

# 修改openfaas部署文件中nats的镜像地址
# 技术栈:Kubernetes Deployment(YAML片段)
# 原本可能是:image: nats:2.9.9-alpine
# 修改为:
image: registry.cn-hangzhou.aliyuncs.com/google_containers/nats:2.9.9-alpine
# 注意:你需要确认该镜像确实存在,也可以自己从Docker Hub拉取后推送到私有仓库

但如果你什么镜像源都没有,最稳妥的方式是先用docker pull拉下来,然后存到私有仓库,再改YAML里的image路径。

3.3 修复gateway的崩溃问题

gateway崩溃,我们看日志:

# 查看网关日志
# 技术栈:Shell命令
kubectl logs -n openfaas deployment/gateway --tail=100
# 假如看到:Failed to watch *v1.Secret: secrets is forbidden: User "system:serviceaccount:openfaas:default" cannot watch resource "secrets" in API group "" in the namespace "openfaas"

这说明默认的ServiceAccount(default)没有权限watch secrets。我们需要把ServiceAccount换成之前创建的openfaas-controller。修改gateway的Deployment,在template.spec下增加:

spec:
  serviceAccountName: openfaas-controller   # 指定我们创建的SA
  containers:
  ...

然后重新部署gateway,Pod应该能正常启动了。

3.4 验证最终状态

全部修改后,重新apply YAML文件:

# 应用修改后的部署
# 技术栈:Shell命令
kubectl apply -f gateway-deployment.yaml -n openfaas
kubectl apply -f nats-deployment.yaml -n openfaas
# 等待一会
kubectl get pods -n openfaas -w
# 应该看到所有Pod都进入Running状态

四、注意事项与最佳实践

上面说了两个大坑,实际部署中还有很多小细节要注意,我总结几条实用的:

  • 镜像拉取策略:如果你的镜像标签固定不变(比如v1.0.0),可以把imagePullPolicy设为IfNotPresent,避免每次都去拉取;如果你不确定或者使用latest,最好设为Always,确保每次部署都用最新版本。具体写法:在container下加imagePullPolicy: Always
  • 资源限制:OpenFaaS的gateway需要一定的CPU和内存。默认不设资源限制也可能导致Pod被OOM Killer干掉。建议设置requests和limits,比如cpu: 100m,memory: 128Mi。
  • DNS配置:如果OpenFaaS要调用其他服务(比如NATS、Prometheus),确保集群内的DNS正常工作。可以用kubectl exec -n openfaas <pod> -- nslookup nats.openfaas.svc.cluster.local测试。
  • 命名空间隔离:把所有OpenFaaS组件都放在同一个命名空间(比如openfaas)里,管理起来方便。别东一个西一个。
  • 版本匹配:OpenFaaS的各个组件(gateway、faas-netes、nats等)版本需要相互兼容。最好参考官方release notes,不要把0.18的gateway和0.24的faas-netes混用。
  • ingress vs NodePort:如果你想从集群外访问OpenFaaS的UI,建议用Ingress Controller(比如nginx-ingress),而不是直接暴露NodePort,避免安全风险。

五、文章总结

部署OpenFaaS上Kubernetes,Pod启动失败多数逃不出镜像拉取和权限配置这两个坑。镜像拉取的核心是正确配置私有仓库的凭证(imagePullSecrets),并养成指定明确标签的习惯;权限配置的核心是给OpenFaaS的ServiceAccount分配足够的RBAC权限,至少包括对核心资源(pods、services、deployments)的增删改查,以及CRD的权限。通过本文的实战案例,你应该能一步步定位并修复这些问题。

记住,遇到Pod启动失败,先查kubectl describe pod看事件,再查kubectl logs看具体错误,然后对症下药。希望这篇文章能帮你在OpenFaaS的部署路上少踩几个坑,早日用上Serverless函数。