在k3s中以非root用户app挂载Kubernetes卷部署Django项目的问题
在k3s中以非root用户app挂载Kubernetes卷部署Django项目的问题
看起来你已经把Django镜像的非root用户配置做得有模有样了!不过在k3s里部署的时候,最容易踩坑的就是卷挂载后的权限问题——毕竟Kubernetes默认创建的卷目录权限都是root:root,你的app用户根本没权限读写,轻则静态文件加载失败,重则连媒体文件上传都搞不定。我来给你一步步梳理解决思路,都是实战里踩过坑的经验:
先把你没写完的Dockerfile补全(确保镜像本身没问题)
咱先把你中断的Dockerfile补成生产环境可用的版本,重点加上完整的依赖安装、代码复制和启动配置:
########### # BUILDER # ########### # pull official base image FROM python:3.11-slim AS builder # set work directory WORKDIR /usr/src/app # set environment variables ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 # install python dependencies COPY ./requirements.txt . RUN pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/app/wheels -r requirements.txt ######### # FINAL # ######### # pull official base image FROM python:3.11-slim # create directory for the app user RUN mkdir -p /home/app # create the app user RUN addgroup --system app && adduser --system --group app # create the appropriate directories ENV HOME=/home/app ENV APP_HOME=/home/app/web RUN mkdir $APP_HOME RUN mkdir $APP_HOME/static RUN mkdir $APP_HOME/media # 要是你的Django用到媒体文件上传,一定要加这个 WORKDIR $APP_HOME # install dependencies COPY --from=builder /usr/src/app/wheels /wheels COPY --from=builder /usr/src/app/requirements.txt . RUN pip install --upgrade pip RUN pip install --no-cache-dir /wheels/* # copy project files COPY . $APP_HOME # change to the app user USER app # 启动命令(根据你的Django部署方式调整,这里用gunicorn示例) CMD ["gunicorn", "--bind", "0.0.0.0:8000", "config.wsgi:application"]
核心问题解决:卷挂载的权限处理
当你在k3s里用PersistentVolumeClaim(PVC)挂载到/home/app/web/static或者/home/app/web/media时,默认PVC的目录权限是root:root,app用户碰都碰不了。给你两个靠谱的解决办法:
方法1:在Dockerfile里提前锁死目录权限
在创建完静态/媒体目录后,直接把目录的所属用户改成app,这样即使卷挂载覆盖了目录,至少基础权限是对的:
RUN mkdir $APP_HOME/static RUN mkdir $APP_HOME/media RUN chown -R app:app $APP_HOME/static $APP_HOME/media # 新增这行,把权限给app用户
不过这个方法有个小缺点:如果你的PVC是从外部存储挂载的(比如NFS),那外部存储的权限还是会覆盖这个设置,所以更适合用emptyDir或者本地存储的场景。
方法2:在Kubernetes Deployment里用securityContext兜底(生产环境首推)
在Deployment的Pod模板里加上securityContext,指定运行用户为app的UID,同时设置fsGroup——这个参数会让K8s自动调整挂载卷的目录权限,让app用户能正常读写,简直是救星:
apiVersion: apps/v1 kind: Deployment metadata: name: django-app spec: replicas: 1 selector: matchLabels: app: django template: metadata: labels: app: django spec: # 重点是这里的securityContext配置 securityContext: runAsUser: 1000 # 这里要填app用户的UID,你可以用`docker run --rm 你的镜像名 id -u app`查询 runAsGroup: 1000 # 对应app用户组的GID,同样用上面的命令查询 fsGroup: 1000 # 自动把挂载卷的组权限改成app用户组,解决读写问题 containers: - name: django image: 你的Django镜像名:标签 ports: - containerPort: 8000 volumeMounts: - name: static-volume mountPath: /home/app/web/static - name: media-volume mountPath: /home/app/web/media volumes: - name: static-volume persistentVolumeClaim: claimName: django-static-pvc # 提前创建好的PVC - name: media-volume persistentVolumeClaim: claimName: django-media-pvc # 提前创建好的PVC
这个方法不管你用什么存储类(k3s默认的local-path-provisioner也没问题),都能自动修正权限,稳得一批。
k3s特有的小细节要注意
- k3s默认用local-path-provisioner提供PVC,创建的目录在节点的
/var/lib/rancher/k3s/storage下,权限是root,但上面的fsGroup设置会自动把这个权限修正,不用手动改节点上的目录。 - 要是你需要把静态文件收集到挂载的卷里,生产环境建议在构建镜像时就做好
collectstatic,或者用单独的K8s Job来执行这个操作;要是图省事,也可以在启动命令里加一步:
command: ["/bin/sh", "-c"] args: ["python manage.py collectstatic --noinput && gunicorn --bind 0.0.0.0:8000 config.wsgi:application"]
不过要确保app用户有权限写入static目录哦。
踩坑排查小技巧
- 要是启动后Django报“Permission denied”,先进Pod查目录权限:
kubectl exec -it <你的Pod名> -- ls -l /home/app/web/static,看看是不是app用户有读写权限。 - 要是镜像允许的话,你可以切换到root用户查详情:
kubectl exec -it <你的Pod名> -- su - root,然后用ls -ld /home/app/web/static看权限。 - 一定要确认app用户的UID/GID和Deployment里的
securityContext设置一致:kubectl exec -it <你的Pod名> -- id app。
备注:内容来源于stack exchange,提问作者Viktor




