容器里要执行的 /usr/local/bin/uvicorn 这个文件,当前系统根本没法按可执行程序格式运行。
也就是启动命令到了 uvicorn 这里,二进制格式或解释方式不对,所以报:
exec /usr/local/bin/uvicorn: exec format error
通常不是 FastAPI 代码本身的问题,而是镜像架构、基础镜像、启动脚本、换行符、shebang、构建方式这几类问题。
⸻
最常见原因
1)镜像架构不匹配
这是最常见的。
比如:
• 你在 Mac M 系列(arm64) 上构建了镜像
• 然后把镜像放到 x86_64 的 Linux/K8s 节点 上运行
或者反过来。
这样镜像里的可执行文件、Python、依赖脚本架构不匹配,就会报:
bash
exec format error
典型场景
你本地是:
• MacBook M1 / M2 / M3 / M4:arm64
而服务器/K8s 节点通常是:
• 阿里云、腾讯云、普通 Intel/AMD 服务器:amd64
如果你直接:
bash
docker build -t xxx .
在 Mac 上构建,再推到仓库,K8s 在 amd64 节点拉下来运行,就很容易出这个错误。
⸻
2)ENTRYPOINT / CMD 指向了错误格式的文件
例如 Dockerfile 里写了:
bash
CMD ["/usr/local/bin/uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
但 /usr/local/bin/uvicorn 实际上:
• 不是正常可执行文件
• shebang 有问题
• 被损坏
• 是给别的架构安装出来的文件
也会报这个错。
⸻
3)脚本文件是 Windows 换行符 CRLF
如果你的启动脚本类似:
bash
start.sh
entrypoint.sh
在 Windows 编辑过,变成了 CRLF,Linux 容器里执行时也可能报:
bash
exec format error
尤其是 Dockerfile 中:
bash
ENTRYPOINT ["/app/entrypoint.sh"]
这种特别常见。
⸻
4)基础镜像或依赖安装异常
例如你把某个二进制文件复制进去了,但它不是当前系统架构的版本。
比如:
• 从别的镜像里 COPY 了错误架构的 Python/uvicorn
• 手动装了不匹配平台的可执行文件
⸻
先判断是哪一种
你可以按这个顺序排查。
⸻
一、先看 K8s 节点架构
在节点上执行:
bash
uname -m
常见结果:
• x86_64 = amd64
• aarch64 = arm64
再看镜像是按什么架构构建的。
⸻
二、看本地构建机器架构
本地如果是 Mac:
bash
uname -m
• arm64 说明你是苹果芯片
如果你在这个机器上直接 docker build,默认很可能构出来的是 linux/arm64 镜像。
⸻
三、检查镜像架构
本地执行:
bash
docker image inspect 你的镜像名 --format '{{.Architecture}}/{{.Os}}'
比如会看到:
bash
arm64/linux
如果你的 K8s 节点是 x86_64,那就对不上了。
也可以看远端镜像支持的平台:
bash
docker manifest inspect 你的镜像:tag
看里面是否包含:
• linux/amd64
• linux/arm64
⸻
四、进入镜像里检查 uvicorn
先本地起一个 shell:
bash
docker run --rm -it --entrypoint /bin/sh 你的镜像
然后执行:
bash
which uvicorn
ls -l /usr/local/bin/uvicorn
head -n 1 /usr/local/bin/uvicorn
file /usr/local/bin/uvicorn
你重点看:
情况 A:如果 uvicorn 是脚本
可能看到第一行类似:
bash
#!/usr/local/bin/python
这说明它是 Python 脚本入口。
那就继续检查:
bash
file /usr/local/bin/python
python --version
如果 python 本身架构不对,也会导致这个错。
情况 B:如果 file /usr/local/bin/uvicorn 显示异常
比如不是脚本、不是当前平台支持的 ELF 文件,那就说明镜像内容有问题。
⸻
五、如果你用了 entrypoint.sh,检查换行符
如果 Dockerfile 里类似:
bash
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]
进入容器检查:
bash
cat -A /app/entrypoint.sh
如果每行末尾有 ^M,说明是 Windows 换行符 CRLF。
修复方式:
bash
sed -i 's/\r$//' /app/entrypoint.sh
或者在本地先转成 LF。
⸻
最常见的真实根因:Mac 构建 ARM 镜像,K8s 跑 AMD 节点
你这个场景非常像这个。
尤其你前面一直在 Mac 上开发、再部署到 Linux/K8s,这种概率非常高。
⸻
解决办法
方案 1:构建指定 amd64 镜像
如果你的 K8s 节点是普通云服务器,基本都用这个:
bash
docker buildx build --platform linux/amd64 -t 你的镜像:tag . --push
如果只是本地测试:
bash
docker buildx build --platform linux/amd64 -t 你的镜像:tag --load .
然后再部署到 K8s。
⸻
方案 2:构建多架构镜像
这样 amd64 和 arm64 都能跑:
bash
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t 你的镜像:tag \
--push .
这样 K8s 节点会自动拉适合自己架构的版本。
⸻
方案 3:Dockerfile 里直接用 python -m uvicorn
有时候可以规避 uvicorn 启动脚本本身的问题。
把:
bash
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
改成:
bash
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
这样更稳一些。
⸻
方案 4:如果是启动脚本问题,修正 shebang 和换行符
例如:
bash
#!/bin/sh
或者:
bash
#!/usr/bin/env sh
并确保是 LF 换行,不是 CRLF。
Dockerfile 可加:
bash
RUN sed -i 's/\r$//' /app/entrypoint.sh && chmod +x /app/entrypoint.sh
⸻
推荐你检查的 Dockerfile 写法
一个比较稳的 FastAPI Dockerfile 例子:
bash
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
如果你项目入口不是 main:app,改成实际的,比如:
bash
CMD ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
⸻
在 K8s 中为什么会暴露出来
因为 K8s 本质上只是帮你运行镜像。
如果镜像本身有问题,比如:
• 架构错了
• ENTRYPOINT 文件坏了
• 启动脚本格式不对
那么到了 Pod 启动时就会直接失败,日志里就出现这个错。
⸻
你现在最应该做的 3 步
第 1 步:确认节点架构
bash
kubectl get nodes -o wide
登录节点后:
bash
uname -m
⸻
第 2 步:确认镜像架构
bash
docker image inspect 你的镜像 --format '{{.Architecture}}/{{.Os}}'
或者看远端 manifest:
bash
docker manifest inspect 你的镜像:t
ag
⸻
第 3 步:重新构建 amd64 镜像
如果节点是 x86_64,就直接重新构建:
bash
docker buildx build --platform linux/amd64 -t 你的镜像:tag --push .
然后更新 K8s deployment:
bash
kubectl rollout restart deployment 你的deployment名
⸻
一句话判断
如果你是 在 Mac M 系列上构建镜像,再部署到普通 Linux K8s 服务器,那这个错误 大概率就是镜像架构不匹配。
⸻
把你的 Dockerfile 和 Deployment YAML 发我,我可以直接帮你定位到底是:
• 镜像架构问题
• uvicorn 启动命令问题
• entrypoint 脚本换行符问题
• 还是镜像基础层有问题。