Sửa lỗi 'container has runAsNonRoot and image will run as root' trong Kubernetes SecurityContext

intermediate☸️ Kubernetes2026-05-31| Kubernetes 1.18+, mọi cloud provider (EKS, GKE, AKS) hoặc cluster on-premise có PodSecurity admission hoặc OPA/Gatekeeper bắt buộc chính sách non-root

Error Message

Error: container has runAsNonRoot and image will run as root (pod: "...", container: "...")
#kubernetes#security-context#runAsNonRoot#pod-security#container

Lỗi Gặp Phải

Bạn áp dụng một manifest có runAsNonRoot: true trong securityContext, và Kubernetes từ chối pod ngay lập tức:

Error: container has runAsNonRoot and image will run as root (pod: "my-app-7d9f8b-xkp2q", container: "app")

Pod không bao giờ khởi động được. kubectl get pods cho thấy nó bị kẹt ở trạng thái Error hoặc đứng yên ở Pending. Đây là lỗi bị kubelet chặn cứng — nó thậm chí không thử pull hay chạy container. Không có quá trình thực thi nào xảy ra, không có log, chỉ là một pod chết hoàn toàn.

Nguyên Nhân

Khi bạn đặt runAsNonRoot: true, Kubernetes kiểm tra UID mà container sẽ chạy với. Nếu chỉ thị USER trong image bị thiếu hoặc được đặt là root (UID 0), và bạn chưa chỉ định runAsUser trong securityContext, kubelet sẽ chặn lại. Việc kiểm tra diễn ra trước khi container khởi động — lỗi ngay, không ngoại lệ.

Ba tình huống thường gây ra lỗi này:

  • Sử dụng image upstream (nginx, redis, python, node) mặc định chạy bằng root
  • Cluster có chính sách PodSecurity hoặc rule OPA/Gatekeeper tự động inject runAsNonRoot: true
  • Bạn thêm runAsNonRoot: true để tăng cường bảo mật nhưng quên đi kèm runAsUser

Cách Sửa 1: Thêm runAsUser vào SecurityContext (Nhanh Nhất)

Chỉ định cho Kubernetes biết dùng UID non-root nào. Chọn bất kỳ UID nào lớn hơn 0 — 1000 hoặc 65534 (nobody) là các lựa chọn thông dụng:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000        # ← thêm dòng này
        runAsGroup: 1000       # tùy chọn nhưng nên có
        fsGroup: 1000          # tùy chọn: để phân quyền volume
      containers:
      - name: app
        image: nginx:latest
        securityContext:
          runAsNonRoot: true
          runAsUser: 1000      # có thể đặt riêng cho từng container
          allowPrivilegeEscalation: false

Áp dụng và theo dõi:

kubectl apply -f deployment.yaml
kubectl get pods -w

Lưu ý: Một số image — bao gồm nginx chính thức — bind vào cổng 80 nội bộ, điều này yêu cầu quyền root. Ép dùng UID non-root sẽ khiến container thất bại lúc khởi động với lỗi permission, không phải lỗi bảo mật. Hãy dùng image được xây dựng sẵn cho non-root: nginxinc/nginx-unprivileged chạy trên cổng 8080 với UID 101 và hoạt động tốt với securityContext này.

Cách Sửa 2: Kiểm Tra UID Thực Tế Của Image

Trước khi chốt một UID, hãy xem image yêu cầu gì. Chạy hai lệnh sau:

# Chạy image cục bộ và kiểm tra user hiệu lực
docker run --rm nginx:latest id
# uid=0(root) gid=0(root) groups=0(root)

# Kiểm tra manifest của image
docker inspect nginx:latest | grep -i user
# "User": ""
# Để trống = mặc định là root

Image đã có non-root user? Tìm UID của nó và dùng luôn:

docker run --rm your-image:tag id
# uid=1001(appuser) gid=1001(appuser)

# Sau đó trong securityContext:
#   runAsUser: 1001

Cách Sửa 3: Rebuild Image Với Non-Root User

Đưa user vào thẳng image và bạn sẽ không bao giờ phải xử lý vấn đề này nữa. Mọi cluster chạy image đó đều tự động có đúng UID, không cần vá securityContext:

FROM node:18-slim

# Tạo non-root user
RUN groupadd --gid 1001 appgroup && \
    useradd --uid 1001 --gid appgroup --shell /bin/bash appuser

WORKDIR /app
COPY --chown=appuser:appgroup . .
RUN npm ci --only=production

USER appuser          # ← đây là thứ Kubernetes đọc

CMD ["node", "server.js"]

Build, push và cập nhật manifest:

docker build -t your-registry/my-app:v2 .
docker push your-registry/my-app:v2

Cập nhật image tag trong manifest và áp dụng. Không cần runAsUser trong securityContext nữa — image đã khai báo sẵn rồi.

Xác Nhận Đã Sửa Thành Công

Kiểm tra trạng thái pod, sau đó xác nhận UID từ bên trong container:

# Pod phải ở trạng thái Running, không phải Error/Pending
kubectl get pods

# Describe để xác nhận không còn lỗi bảo mật
kubectl describe pod my-app-xxx

# Exec vào và kiểm tra user
kubectl exec -it my-app-xxx -- id
# Kết quả mong đợi: uid=1000 gid=1000 groups=1000

# Cách nhanh hơn:
kubectl exec -it my-app-xxx -- whoami

Pod đang chạy, UID khác 0 — bạn đã xong.

Nếu Lỗi Đến Từ Chính Sách Cluster

Đôi khi bạn gặp lỗi này mà không hề tự viết runAsNonRoot: true. Điều đó có nghĩa là cluster đang tự enforce nó cho bạn — thường thông qua PodSecurity admission hoặc Gatekeeper.

# Kiểm tra xem namespace có PodSecurity label không
kubectl get namespace my-namespace -o yaml | grep pod-security

# Ví dụ kết quả:
# pod-security.kubernetes.io/enforce: restricted

Chuẩn PodSecurity restricted bắt buộc runAsNonRoot: true trên mọi pod trong namespace đó. Cách sửa hoàn toàn giống nhau — thêm runAsUser hoặc rebuild image. Bạn chỉ cần biết rằng ràng buộc đến từ cluster, không phải từ manifest của bạn.

Tóm Tắt Nhanh

  • Sửa nhanh nhất: Thêm runAsUser: 1000 cùng với runAsNonRoot: true
  • Image dùng cổng 80/443: Chuyển sang variant unprivileged (ví dụ: nginxinc/nginx-unprivileged trên cổng 8080, UID 101)
  • Image tự build: Thêm USER 1001 vào Dockerfile
  • Dùng UID nào: Bất kỳ giá trị nào lớn hơn 0 — 1000, 1001 và 65534 là các lựa chọn thông dụng
  • Không nên: Xóa runAsNonRoot: true chỉ để tắt lỗi — nó tồn tại có lý do của nó

Related Error Notes