第三部分:PHP 容器在 iSulad / Kata Containers 国产安全容器上的运行实践

3.1 先搞明白这俩是什么、什么关系

大白话:

  • iSulad(读"爱苏拉德"):华为开源的容器引擎,是 Docker 的国产替代。它负责"管理容器的一生":拉镜像、创建、启动、停止、删除。命令行工具叫 isula,用法和 docker 几乎一模一样(isula runisula ps……),学习成本约等于零。优点:C/C++ 写的,比 Docker(Go 写的)更轻、内存占用更小、启动更快,openEuler 官方源直接就能装。
  • Kata Containers:一种容器运行时。普通容器(runc)的隔离靠 Linux 内核的 namespace/cgroup,所有容器共享宿主机内核——一个内核漏洞可能让容器"越狱"。Kata 给每个容器套一个轻量级虚拟机(有自己独立的内核),隔离级别达到虚拟机水平,但用起来还是容器的体验。代价:启动慢一点(百毫秒级 → 秒级)、内存开销多一点(每容器约多 100~200MB)。
  • 两者关系:iSulad 是"调度经理",Kata 是"其中一种干活方式"。iSulad 默认用 runc(普通隔离)跑容器,也可以配置成用 Kata(强隔离)跑。选择建议:内部可信应用用 runc 就够了;多租户、跑第三方代码、等保要求高的场景用 Kata。
                  isula run --runtime runc  ──►  普通容器(合租房:快、省资源)
iSulad(引擎)──┤
                  isula run --runtime kata-runtime ──► Kata 容器(独栋:强隔离)

3.2 第一步:openEuler 上安装 iSulad

# openEuler 22.03 LTS 官方源直接装(麒麟 V10 SP2+ 同样自带 iSulad 源)
dnf install -y iSulad

# 启动并设置开机自启
systemctl enable --now isulad

# 验证
isula version

配置 iSulad(/etc/isulad/daemon.json)

大白话:这个文件相当于 Docker 的 daemon.json。重点配三样——镜像仓库地址(指向公司 Harbor)、日志大小限制(防止日志把磁盘写爆)、以及注册 Kata 运行时。

{
    "group": "isula",
    "default-runtime": "runc",
    "graph": "/var/lib/isulad",
    "state": "/var/run/isulad",
    "log-level": "INFO",
    "log-driver": "json-file",
    "log-opts": {
        "log-file-mode": "0600",
        "log-path": "/var/lib/isulad",
        "max-file": "3",
        "max-size": "30MB"
    },
    "registry-mirrors": [
        "https://harbor.example.internal"
    ],
    "insecure-registries": [
        "harbor.example.internal"
    ],
    "runtimes": {
        "kata-runtime": {
            "path": "/usr/bin/kata-runtime",
            "runtime-args": [
                "--kata-config",
                "/usr/share/defaults/kata-containers/configuration.toml"
            ]
        }
    }
}
# 改完配置重启生效
systemctl restart isulad

3.3 第二步:用 iSulad 跑起一套 PHP 应用(nginx + php-fpm)

大白话:iSulad 没有 docker-compose 那种编排工具,单机场景就写个启动脚本,让 nginx 容器和 php-fpm 容器共享一个网络。下面是完整可跑的脚本。

run-php-app.sh

#!/usr/bin/env bash
# ============================================================
# 用 iSulad 启动 PHP 应用(nginx + php-fpm 双容器)
# 用法:./run-php-app.sh start|stop|restart|status
# ============================================================
set -e

HARBOR="harbor.example.internal"
APP_IMAGE="$HARBOR/php/demo-php-app:latest"     # 第一部分流水线打出来的镜像
NGINX_IMAGE="$HARBOR/library/nginx:1.25"
NET_NAME="php-net"
APP_NAME="php-fpm-app"
NGINX_NAME="php-nginx"

start() {
    # 1. 建一个专用网络,两个容器都加入,nginx 就能用容器名连到 php-fpm
    isula network inspect $NET_NAME >/dev/null 2>&1 || isula network create $NET_NAME

    # 2. 拉最新镜像
    isula pull $APP_IMAGE
    isula pull $NGINX_IMAGE

    # 3. 启动 php-fpm 容器
    isula run -d \
        --name $APP_NAME \
        --network $NET_NAME \
        --restart always \
        -m 512m --cpus 1 \
        $APP_IMAGE

    # 4. 准备 nginx 配置(转发 .php 请求给 php-fpm 容器的 9000 端口)
    mkdir -p /opt/php-app/nginx
    cat > /opt/php-app/nginx/default.conf <<'EOF'
server {
    listen 80;
    root /var/www/html/public;
    index index.php;
    location / { try_files $uri $uri/ /index.php?$query_string; }
    location ~ \.php$ {
        fastcgi_pass php-fpm-app:9000;      # 直接写容器名,走 php-net 网络解析
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME /var/www/html/public$fastcgi_script_name;
        include fastcgi_params;
    }
}
EOF

    # 5. 启动 nginx 容器,对外暴露 8080
    isula run -d \
        --name $NGINX_NAME \
        --network $NET_NAME \
        --restart always \
        -p 8080:80 \
        -v /opt/php-app/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro \
        $NGINX_IMAGE

    echo "启动完成,访问 http://本机IP:8080"
}

stop() {
    isula rm -f $NGINX_NAME $APP_NAME 2>/dev/null || true
    echo "已停止"
}

status() {
    isula ps -a --filter "name=php-"
}

case "$1" in
    start)   start ;;
    stop)    stop ;;
    restart) stop; start ;;
    status)  status ;;
    *) echo "用法: $0 start|stop|restart|status"; exit 1 ;;
esac

再配一个 systemd 服务,让它跟随开机自启(第一部分 SSH 部署里 systemctl restart demo-php-app 重启的就是它):

/etc/systemd/system/demo-php-app.service

[Unit]
Description=Demo PHP App (iSulad)
After=isulad.service
Requires=isulad.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/opt/php-app/run-php-app.sh start
ExecStop=/opt/php-app/run-php-app.sh stop
TimeoutStartSec=300

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now demo-php-app

3.4 第三步:安装 Kata Containers 并用强隔离跑 PHP

安装

# openEuler 官方源提供 kata 相关包(底层用 QEMU 起轻量虚拟机)
dnf install -y kata-containers qemu-kvm

# 检查本机硬件支持不支持(需要 CPU 虚拟化:鲲鹏的 ARM 虚拟化 / 海光的 AMD-V)
kata-runtime check
# 输出 "System is capable of running Kata Containers" 就 OK

Kata 配置调优(/usr/share/defaults/kata-containers/configuration.toml 关键项)

大白话:Kata 的每个容器都是一个微型虚拟机,这个文件决定"虚拟机的默认配置"。给 PHP 应用重点关注内存和 CPU:

[hypervisor.qemu]
path = "/usr/bin/qemu-kvm"
kernel = "/usr/share/kata-containers/vmlinux.container"
initrd = "/usr/share/kata-containers/kata-containers-initrd.img"

# 每个 Kata 容器的虚拟机默认给 1 核,实际能按需热插到 default_maxvcpus
default_vcpus = 1
default_maxvcpus = 4

# 默认内存 512MB(php-fpm + OPcache 一般够了;不够的容器启动时用 -m 覆盖)
default_memory = 512

# 开启内存气球,虚拟机没用完的内存可以还给宿主机,省资源
enable_balloon = true

[runtime]
# 网络用 tcfilter 模式,兼容性最好
internetworking_model = "tcfilter"
# 出问题时打开 debug 看详细日志
enable_debug = false

用 Kata 跑 php-fpm(就改一个参数)

# 和普通启动唯一的区别:--runtime kata-runtime
isula run -d \
    --name php-fpm-secure \
    --network php-net \
    --restart always \
    --runtime kata-runtime \
    -m 512m --cpus 1 \
    harbor.example.internal/php/demo-php-app:latest

# 验证它真的跑在独立内核里:
isula exec php-fpm-secure uname -r     # 显示的是 Kata 虚拟机的内核版本
uname -r                                # 宿主机内核版本,两者不同 = 隔离生效

大白话验证逻辑:普通容器和宿主机共享内核,uname -r 结果一样;Kata 容器有自己的内核,结果不一样。结果不一样就说明强隔离生效了。

3.5 第四步:K8s 集群里用 iSulad + Kata(生产标准姿势)

大白话:单机脚本适合小场景,生产一般是 K8s 集群。思路是:

  1. K8s 的节点用 iSulad 做容器运行时(代替 containerd/docker);
  2. 在 K8s 里注册一个叫 kataRuntimeClass(运行时类别);
  3. 哪个 Pod 需要强隔离,就在 Pod 里写一行 runtimeClassName: kata,其它 Pod 不受影响。

节点侧:iSulad 打开 CRI 接口(让 K8s 能指挥它)

/etc/isulad/daemon.json 里追加:

{
    "network-plugin": "cni",
    "cni-bin-dir": "/opt/cni/bin",
    "cni-conf-dir": "/etc/cni/net.d",
    "pod-sandbox-image": "harbor.example.internal/library/pause:3.9",
    "cri-runtimes": {
        "kata": "kata-runtime"
    }
}

kubelet 指向 iSulad 的接口(/etc/sysconfig/kubelet 或 kubeadm 配置):

KUBELET_EXTRA_ARGS="--container-runtime-endpoint=unix:///var/run/isulad.sock"
systemctl restart isulad kubelet

集群侧:注册 RuntimeClass + 部署 PHP

kata-runtimeclass.yaml

apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: kata
# handler 对应 daemon.json 里 cri-runtimes 的 key
handler: kata
# Kata 每个 Pod 的虚拟机本身有固定开销,声明出来让 K8s 调度时算这笔账
overhead:
  podFixed:
    memory: "160Mi"
    cpu: "250m"
# 只调度到装了 kata 的节点
scheduling:
  nodeSelector:
    katacontainers.io/kata-runtime: "true"

给装了 Kata 的节点打标签:

kubectl label node node-1 katacontainers.io/kata-runtime=true

需要强隔离的 PHP 部署(比如处理敏感数据的服务),在第一部分的 Deployment 基础上只加一行

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-php-app-secure
  namespace: php-prod
spec:
  replicas: 2
  selector:
    matchLabels: { app: demo-php-app-secure }
  template:
    metadata:
      labels: { app: demo-php-app-secure }
    spec:
      runtimeClassName: kata          # ← 就加这一行,这个服务就跑进独立虚拟机了
      imagePullSecrets:
        - name: harbor-cred
      containers:
        - name: app
          image: harbor.example.internal/php/demo-php-app:latest
          ports:
            - containerPort: 9000
          resources:
            requests: { cpu: 250m, memory: 256Mi }
            limits:   { cpu: "1",  memory: 512Mi }
kubectl apply -f kata-runtimeclass.yaml
kubectl apply -f deployment-secure.yaml

# 验证:进 Pod 看内核版本,和节点不一样就是 Kata 生效
kubectl -n php-prod exec deploy/demo-php-app-secure -- uname -r

3.6 PHP 在 Kata 里的性能调优要点

大白话逐条解释:

  1. php-fpm 进程数别开太大。Kata 虚拟机内存是"独占"的,pm.max_children 按"虚拟机内存 ÷ 单个 PHP 进程内存"算。512MB 的 Kata 容器建议:
; /etc/php-fpm.d/www.conf 关键段
pm = dynamic
pm.max_children = 10        ; 512MB ÷ 约40MB/进程 ≈ 12,留点余量取 10
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 1000      ; 每个进程处理1000个请求后重启,防内存泄漏累积
  1. OPcache 必开(Dockerfile 里已配)。Kata 有虚拟化开销,OPcache 省下的 PHP 编译时间正好补回来,实测综合性能损耗能压到 5% 以内。
  2. 磁盘 IO 敏感的操作走内存。Kata 的磁盘走 virtio 有损耗,session 之类高频小文件读写别放磁盘,改放 Redis:
; php.ini
session.save_handler = redis
session.save_path = "tcp://redis.example.internal:6379?auth=密码"
  1. 冷启动慢的对策。Kata 容器启动比普通容器慢约 1~2 秒,K8s 里把 readinessProbeinitialDelaySeconds 适当调大(比如 10 秒),避免流量打到还没就绪的实例。


Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐