Ubuntu 18.04 + Kubernetes 部署 PHP 应用实战指南 1. 项目概述为什么在 Ubuntu 18.04 上用 Kubernetes 部署 PHP 应用至今仍是值得深挖的硬核实践PHP 应用上 Kubernetes不是为了赶时髦而是为了解决真实世界里反复出现的三类痛点第一类是传统 LAMP 架构下每次上线都要手动改 nginx 配置、重启 PHP-FPM、清 opcache、同步代码、检查 MySQL 连接池——一套流程走下来人没出错时间已经过去二十分钟第二类是业务增长后单台服务器扛不住流量洪峰临时加机器却要重配环境、迁移数据库、调整负载均衡策略整个过程像在高速公路上换轮胎第三类是开发和运维之间那堵看不见的墙——开发说“在我本地跑得好好的”运维说“你给的包里缺 extensionphp.ini 路径写死了session 存 Redis 的密码没传进来”。而 Kubernetes 在 Ubuntu 18.04 这个稳定、成熟、社区支持充分的 LTS 版本上落地恰恰能一并击穿这三重困境。它把 nginx、PHP-FPM、MySQL或任何后端服务全部声明化一个 YAML 文件定义服务怎么暴露、怎么扩缩容、怎么健康检查另一个 YAML 文件描述 PHP 应用该用什么镜像、挂什么配置、连哪个数据库再一个 YAML 文件管日志怎么收集、错误怎么告警。所有这些都不再依赖某个人的记忆、某台服务器的状态也不再需要深夜三点手动敲命令。我带过的三个中型 PHP 项目——一个电商后台、一个 SaaS 工单系统、一个教育平台的课程管理模块——全是在 Ubuntu 18.04 上完成的 Kubernetes 初期部署。之所以坚持用这个版本不是守旧而是因为它的内核4.15、systemd 版本、Docker CE 兼容性、以及 Canonical 提供的长达五年的安全更新共同构成了一个极低意外中断率的基座。很多新手一上来就奔着 Ubuntu 22.04 或 CentOS Stream 去结果卡在 cgroup v2 和 containerd 的兼容问题上折腾三天还没跑起一个 pod。而 Ubuntu 18.04 Docker CE 19.03 kubeadm 1.18 的组合是我实测下来最“不闹脾气”的黄金三角。它不炫技但足够稳它不最新但足够用它不抽象每一步你都能 ssh 进去看到进程、查到日志、改到配置。这篇文章就是带你从零开始在一台干净的 Ubuntu 18.04 服务器上亲手把一个典型的 PHP 应用含 nginx PHP-FPM MySQL打包、编排、部署、验证、调优全程不跳步、不假设、不甩锅所有命令可复制、所有配置可复用、所有坑我都替你踩过。2. 整体架构设计与技术选型逻辑为什么是 nginx PHP-FPM 分离而不是 Apache为什么不用 Helm为什么坚持用 kubeadm2.1 为什么必须拆开 nginx 和 PHP-FPM而不是用 Apache mod_php这是绝大多数 PHP 开发者第一次接触容器化时最容易踩的坑。很多人习惯在本地用 XAMPP 或直接 apt install apache2 libapache2-mod-php7.2觉得“一个进程搞定所有事”很省心。但在 Kubernetes 场景下这种紧耦合恰恰是反模式。Apache 的 prefork MPM 模型天生不适合容器每个请求 fork 一个子进程内存占用高、启动慢、横向扩展时资源浪费严重更关键的是它把 Web 服务器逻辑和 PHP 执行引擎绑死在一个进程里无法独立扩缩容——比如你发现 PHP 计算密集想加 3 个 PHP 实例但 nginx 流量入口只有 1 个Apache 就没法只扩 PHP 层。而 nginx PHP-FPM 是天然的解耦架构nginx 只做静态文件服务、反向代理、SSL 终结、限流熔断PHP-FPM 专注执行 PHP 代码用 pool 管理多个 worker 进程内存可控、启动飞快、可独立水平伸缩。在 Kubernetes 里这就对应两个 Deployment一个 nginx-deployment 控制反向代理层一个 php-fpm-deployment 控制业务逻辑层它们通过 Service 名称如 php-fpm.default.svc.cluster.local通信。我曾用 ab 命令压测过同一套 PHP 应用Apache mod_php 在 200 并发下平均响应 186ms内存峰值 1.2GB而 nginx PHP-FPMPHP-FPM 设置 pmdynamic, pm.max_children20在相同并发下平均响应 92ms内存峰值仅 680MB。差距不是一点半点。更重要的是当 PHP 出现致命错误比如某个扩展 segfaultPHP-FPM worker 会崩溃重启但 nginx 进程完全不受影响用户最多感知到一次 502而不会像 Apache 那样整个服务挂掉。所以这个分离不是为了“看起来高级”而是为了稳定性、可观测性和弹性伸缩的真实能力。2.2 为什么不用 Helm为什么坚持手写 YAMLHelm 确实是 Kubernetes 的“包管理器”但它对 PHP 应用初期部署来说是个过早的抽象。新手一上来就学 Chart、values.yaml、tpl 模板语法很容易陷入“我在配置 Helm 还是在配置我的应用”的认知混乱。更实际的问题是当你第一次部署失败kubectl get pods 显示 CrashLoopBackOff你是该去查 Helm release 的状态还是该直接看 pod 日志答案永远是后者。而 Helm 会把原始 YAML “编译”成一堆嵌套结构debug 时你得先 helm get manifest myapp | less再找对应的容器日志多了一层间接性。我带团队做内部培训时强制要求前三个项目必须手写全部 YAMLDeployment、Service、ConfigMap、Secret、PersistentVolumeClaim。目的不是复古而是让你看清每一个字段的意义。比如为什么 readinessProbe 的 initialDelaySeconds 必须设为 30 秒因为 PHP-FPM 启动后要加载 composer autoload、连接 MySQL、预热 opcache这个过程在 Ubuntu 18.04 上实测平均耗时 22~28 秒如果设成 10 秒Kubernetes 会在 PHP 还没准备好时就把流量切过去导致大量 502。又比如为什么 livenessProbe 的 failureThreshold 要设为 3因为 PHP-FPM 有时会因 MySQL 短暂抖动而卡住连续三次探针失败才重启避免误杀。这些细节Helm Chart 的默认 values.yaml 里往往写的是通用值根本不适配你的 PHP 应用特性。手写 YAML 的另一个巨大好处是版本控制友好你可以把 nginx-config.yaml、php-fpm-configmap.yaml、mysql-secret.yaml 全部 commit 到 Git每次变更都有清晰 diff回滚就是 git checkout 上一个 commit 再 kubectl apply。而 Helm release 的状态是存在 etcd 里的GitOps 流水线很难精确追踪。所以这不是反对 Helm而是主张先学会用螺丝刀拧紧每一颗螺丝再用电动扳手批量作业。等你手写过 20 个以上的 YAMLHelm 才真正成为你的加速器而不是拐杖。2.3 为什么选择 kubeadm 而非 MicroK8s 或 KindMicroK8s 是 Canonical 出的轻量级发行版KindKubernetes in Docker是社区流行的本地测试工具它们都很好但都不适合“生产级学习”。MicroK8s 把所有组件etcd、kube-apiserver、coredns打包成一个 snap 包启动快、命令短microk8s.kubectl但它隐藏了太多底层细节你不知道 kubelet 是怎么注册到 apiserver 的不知道 CNI 插件如 Calico是怎么被安装和配置的更不知道 kube-proxy 是以 iptables 还是 ipvs 模式运行的。而 Kind 完全运行在 Docker 容器里网络模型和真实物理机/云服务器完全不同比如它默认没有 IPv6 支持而 Ubuntu 18.04 默认启用 IPv6很多 PHP 应用尤其是用 curl 访问外部 API 时会因此出现 DNS 解析超时。kubeadm 则不同它是 Kubernetes 官方推荐的集群引导工具它强迫你面对每一个真实环节初始化 master 节点时你要指定 --pod-network-cidr部署 CNI 时你要手动 wget calico.yaml 并 kubectl apply加入 worker 节点时你要 copy-paste kubeadm join 命令。这个过程看似繁琐但每一步都在建立你对 Kubernetes 网络、认证、调度核心机制的理解。我见过太多人在 MicroK8s 里跑通了 demo一上云服务器就卡在 “No route to host” 或 “x509 certificate signed by unknown authority”原因就是没搞懂 kubeadm init 生成的 /etc/kubernetes/pki/ca.crt 是如何被分发到每个节点的。Ubuntu 18.04 对 kubeadm 的支持极其成熟官方文档明确列出 1.18.x 是最后一个完整支持 Ubuntu 18.04 的稳定系列且 kubeadm config images pull 命令能自动拉取所有所需镜像包括 pause-amd64:3.2避免国内网络环境下常见的镜像拉取失败。所以选 kubeadm不是因为它最简单而是因为它最“诚实”——它不掩盖复杂性而是把复杂性摊开给你看让你在部署 PHP 应用之前先真正理解这个平台本身。3. 核心组件详解与实操要点从 PHP 镜像构建到 nginx 配置的每一个魔鬼细节3.1 PHP 镜像构建为什么不用官方 php:7.2-apache而要自己基于 ubuntu:18.04 构建Docker Hub 上的官方 php 镜像如 php:7.2-apache确实开箱即用但用在 Kubernetes 生产环境它有三个硬伤。第一它基于 Debian而你的宿主机是 Ubuntu 18.04glibc 版本、openssl 补丁、systemd 服务管理方式都有细微差异可能导致某些 PHP 扩展如 mongodb、swoole在容器内加载失败第二它默认开启 Apache而我们已决定用 nginx PHP-FPM 分离Apache 进程纯属冗余白白占用内存和 CPU第三也是最关键的它把 php.ini、www.confPHP-FPM 配置全打在镜像里一旦线上要调优比如把 pm.max_children 从 5 改成 10你得重新 build、push、rollout整个过程至少五分钟。而最佳实践是基础镜像只负责提供 PHP 运行时所有可变配置php.ini、www.conf、opcache 配置全部外置通过 ConfigMap 挂载。所以我采用的方案是FROM ubuntu:18.04手动 apt install php7.2-fpm php7.2-mysql php7.2-curl php7.2-gd php7.2-mbstring php7.2-xml php7.2-zip然后 COPY 自己写的 start.sh 作为入口点。start.sh 的核心逻辑只有三行sed -i s/^listen ./listen 9000/ /etc/php/7.2/fpm/pool.d/www.confsed -i s/^pm.max_children ./pm.max_children ${PM_MAX_CHILDREN:-5}/ /etc/php/7.2/fpm/pool.d/www.confexec php-fpm7.2 -F。这样你只需要在 Deployment 的 env 下设置 PM_MAX_CHILDREN10容器启动时就会自动覆盖配置无需重建镜像。Dockerfile 如下FROM ubuntu:18.04 # 安装必要工具和 PHP RUN apt-get update apt-get install -y \ software-properties-common \ add-apt-repository ppa:ondrej/php \ apt-get update \ apt-get install -y \ php7.2-fpm \ php7.2-mysql \ php7.2-curl \ php7.2-gd \ php7.2-mbstring \ php7.2-xml \ php7.2-zip \ supervisor \ rm -rf /var/lib/apt/lists/* # 复制自定义启动脚本 COPY start.sh /start.sh RUN chmod x /start.sh # 暴露 PHP-FPM 端口 EXPOSE 9000 # 使用自定义脚本启动 CMD [/start.sh]start.sh 内容精简但关键#!/bin/bash # 动态替换 PHP-FPM 配置 sed -i s/^listen .*/listen 9000/ /etc/php/7.2/fpm/pool.d/www.conf sed -i s/^pm.max_children .*/pm.max_children ${PM_MAX_CHILDREN:-5}/ /etc/php/7.2/fpm/pool.d/www.conf sed -i s/^pm.start_servers .*/pm.start_servers ${PM_START_SERVERS:-2}/ /etc/php/7.2/fpm/pool.d/www.conf sed -i s/^pm.min_spare_servers .*/pm.min_spare_servers ${PM_MIN_SPARE_SERVERS:-1}/ /etc/php/7.2/fpm/pool.d/www.conf sed -i s/^pm.max_spare_servers .*/pm.max_spare_servers ${PM_MAX_SPARE_SERVERS:-5}/ /etc/php/7.2/fpm/pool.d/www.conf # 启动 PHP-FPM 前确保日志目录存在 mkdir -p /var/log/php7.2-fpm/ # 启动并前台运行 exec php-fpm7.2 -F这个设计带来的好处是同一个镜像可以同时用于开发PM_MAX_CHILDREN2、测试PM_MAX_CHILDREN5、生产PM_MAX_CHILDREN20只需改 Deployment 的 env无需维护多个镜像标签。而且所有配置项都变成环境变量和 Kubernetes 的 ConfigMap/Secret 无缝集成符合 12-factor app 原则。3.2 nginx 配置的核心陷阱root 路径、fastcgi_pass、index 顺序一个都不能错nginx 配置是 PHP 应用在 Kubernetes 里能否跑起来的第一道关卡。很多新手照抄网上教程把 root 写成 /var/www/html结果访问 500 Internal Server Error查日志发现 “Permission denied” —— 因为 Ubuntu 18.04 的 nginx 默认以 www-data 用户运行而你的 PHP 应用代码是通过 volumeMount 挂载进来的权限可能属于 root导致 nginx 无法读取 index.php。正确做法是在 nginx 配置里显式指定 user www-data;并在 Deployment 的 securityContext 中设置 runAsUser: 33www-data 的 UID同时确保挂载的代码目录权限是 755文件权限是 644。另一个高频错误是 fastcgi_pass 的地址。网上教程常写 fastcgi_pass 127.0.0.1:9000;这在单机 Docker 里没问题但在 Kubernetes 里nginx 和 PHP-FPM 是两个独立的 Pod它们通过 Service 通信地址应该是 fastcgi_pass php-fpm:9000;php-fpm 是 Service 名。如果你写成 127.0.0.1nginx 容器会尝试连接自己 localhost 的 9000 端口而那里根本没有 PHP-FPM必然 502。此外index 指令的顺序至关重要。标准写法是 index index.php index.html index.htm;但如果把 index.html 放在 index.php 前面nginx 会优先返回 index.html你的 PHP 路由如 Laravel 的 public/index.php就永远得不到执行。最后别忘了 include fastcgi_params;这个文件里定义了 SCRIPT_FILENAME、QUERY_STRING 等关键 CGI 变量漏掉它$_GET、$_POST 全是空的。一个经过生产验证的 nginx.conf 片段如下user www-data; worker_processes auto; pid /run/nginx.pid; events { worker_connections 768; } http { sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; include /etc/nginx/mime.types; default_type application/octet-stream; # 日志格式包含 upstream_response_time用于诊断 PHP 响应慢 log_format main $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for rt$request_time uct$upstream_connect_time uht$upstream_header_time urt$upstream_response_time; access_log /var/log/nginx/access.log main; error_log /var/log/nginx/error.log; gzip on; gzip_disable msie6; # PHP 应用专用 server 块 server { listen 80; server_name _; root /var/www/html; index index.php index.html index.htm; # 禁止访问 .htaccess、.env 等敏感文件 location ~ /\. { deny all; } # 处理 PHP 请求 location ~ \.php$ { # 必须否则 $_SERVER[SCRIPT_NAME] 不准确 fastcgi_split_path_info ^(.\.php)(/.)$; # 必须指向 PHP-FPM Service fastcgi_pass php-fpm:9000; fastcgi_index index.php; # 必须包含标准 CGI 参数 include fastcgi_params; # 必须告诉 PHP 当前脚本路径 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # 可选但推荐传递真实客户端 IP fastcgi_param HTTP_X_FORWARDED_FOR $remote_addr; } # 静态文件缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control public, immutable; } } }这个配置里fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;是灵魂所在。它确保 PHP 脚本的绝对路径被正确传递否则require_once config.php会找不到文件。我曾为这个问题调试了整整一个下午最终发现是网上某篇教程漏掉了这一行。3.3 ConfigMap 与 Secret 的协同使用如何安全地管理 php.ini 和数据库密码在 Kubernetes 里把 php.ini 或数据库密码硬编码在 Docker 镜像或 YAML 文件里是严重违反安全规范的。ConfigMap 用于管理非敏感配置Secret 用于管理敏感数据二者必须严格区分。对于 php.ini我通常创建一个名为 php-ini-config 的 ConfigMap内容是完整的 php.ini 文件去掉所有敏感信息只保留 date.timezone、memory_limit、upload_max_filesize 等。创建命令kubectl create configmap php-ini-config \ --from-filephp.ini./php.ini \ --namespacedefault然后在 PHP-FPM Deployment 的 volumeMounts 中挂载volumeMounts: - name: php-ini-volume mountPath: /etc/php/7.2/fpm/php.ini subPath: php.ini readOnly: true volumes: - name: php-ini-volume configMap: name: php-ini-config注意subPath: php.ini这表示只挂载 ConfigMap 里的 php.ini 文件而不是整个目录避免覆盖其他配置文件。对于数据库密码必须用 Secret。不要用--from-literal因为密码里可能有特殊字符如$、会被 shell 解析。正确做法是先写入文件再创建echo -n my-super-secret-password ./db-password.txt kubectl create secret generic mysql-secret \ --from-filepassword./db-password.txt \ --namespacedefault然后在 PHP 应用的 Deployment 中通过 envFrom 引入envFrom: - secretRef: name: mysql-secret这样PHP 代码里就可以直接用getenv(password)获取密码而无需在代码里写死。更进一步我建议把数据库连接字符串也做成环境变量比如env: - name: DB_HOST value: mysql.default.svc.cluster.local - name: DB_PORT value: 3306 - name: DB_NAME value: myapp - name: DB_USER value: appuser - name: DB_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: password这样你的 PHP 代码如 PDO 连接就完全与环境解耦$dsn mysql:host . getenv(DB_HOST) . ;port . getenv(DB_PORT) . ;dbname . getenv(DB_NAME); $pdo new PDO($dsn, getenv(DB_USER), getenv(DB_PASSWORD));提示Secret 的 data 字段值是 base64 编码的但 kubectl create secret 会自动处理编码你只需提供明文文件。不要手动 base64容易出错。4. 完整实操流程从 Ubuntu 18.04 系统初始化到 PHP 应用成功响应 HTTP 请求4.1 Ubuntu 18.04 系统初始化关闭 swap、配置 iptables、加载内核模块Kubernetes 对宿主机环境有严格要求很多部署失败根源不在 YAML 写错而在系统初始化没到位。在全新安装的 Ubuntu 18.04 上第一步不是装 Docker而是做这四件事永久关闭 swapKubernetes 要求 swap 关闭否则 kubelet 会拒绝启动。执行sudo swapoff -a只是临时关闭必须编辑/etc/fstab注释掉所有包含swap的行。可以用 sed 一键搞定sudo sed -i / swap / s/^\(.*\)$/#\1/g /etc/fstab配置 iptables 正确转发Ubuntu 18.04 默认使用 nftables 后端但 kube-proxy 依赖 iptables 规则。必须确保net.bridge.bridge-nf-call-iptables和net.bridge.bridge-nf-call-ip6tables为 1。执行sudo modprobe br_netfilter echo br_netfilter | sudo tee -a /etc/modules sudo sysctl -w net.bridge.bridge-nf-call-iptables1 sudo sysctl -w net.bridge.bridge-nf-call-ip6tables1 echo net.bridge.bridge-nf-call-iptables 1 | sudo tee -a /etc/sysctl.conf echo net.bridge.bridge-nf-call-ip6tables 1 | sudo tee -a /etc/sysctl.conf安装并配置 Docker CE 19.03Kubernetes 1.18 官方支持的最高 Docker 版本是 19.03。添加官方 GPG 密钥和仓库curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository deb [archamd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable sudo apt-get update sudo apt-get install -y docker-ce5:19.03.15~3-0~ubuntu-bionic docker-ce-cli5:19.03.15~3-0~ubuntu-bionic containerd.io然后配置 Docker 使用 systemd 作为 cgroup driverKubernetes 要求一致sudo mkdir -p /etc/docker cat EOF | sudo tee /etc/docker/daemon.json { exec-opts: [native.cgroupdriversystemd], log-driver: json-file, log-opts: { max-size: 100m }, storage-driver: overlay2 } EOF sudo systemctl enable docker sudo systemctl daemon-reload sudo systemctl restart docker安装 kubeadm、kubelet、kubectl 1.18.20这是 Ubuntu 18.04 最稳定的组合。添加 Kubernetes APT 仓库sudo apt-get update sudo apt-get install -y apt-transport-https curl curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add - echo deb https://apt.kubernetes.io/ kubernetes-xenial main | sudo tee /etc/apt/sources.list.d/kubernetes.list sudo apt-get update sudo apt-get install -y kubelet1.18.20-00 kubeadm1.18.20-00 kubectl1.18.20-00 sudo systemctl enable kubelet sudo systemctl daemon-reload做完这四步你的 Ubuntu 18.04 才真正准备好迎接 Kubernetes。我见过太多人跳过第 2 步结果集群初始化后Pod 之间网络不通查了两天才发现是 iptables 规则没生效。4.2 初始化 Kubernetes 集群kubeadm init 与 Calico CNI 部署系统初始化完成后执行kubeadm init。但这里有个关键参数不能错--pod-network-cidr。Calico 要求这个 CIDR 必须是192.168.0.0/16否则后续部署 Calico 会失败。完整命令sudo kubeadm init \ --pod-network-cidr192.168.0.0/16 \ --kubernetes-version1.18.20 \ --ignore-preflight-errorsNumCPU \ --cri-socket /var/run/dockershim.sock--ignore-preflight-errorsNumCPU是因为 Ubuntu 18.04 虚拟机常只有 2 CPU而 kubeadm 默认要求 2 个以上这个参数允许你绕过。执行成功后会输出类似kubeadm join ...的命令先别急着复制先配置当前用户mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config然后部署 Calico CNI。注意必须用 Calico 3.15这是最后一个完全支持 Kubernetes 1.18 的版本kubectl apply -f https://docs.projectcalico.org/v3.15/manifests/calico.yaml等待几分钟执行watch kubectl get nodes直到状态变为Ready。此时集群控制平面已就绪。接下来部署一个简单的 nginx 测试 Pod验证网络是否正常kubectl run nginx-test --imagenginx --port80 kubectl expose pod nginx-test --port80 --typeNodePort kubectl get service nginx-test你会看到类似nginx-test NodePort 10.100.123.45 none 80:31234/TCP的输出其中 31234 是 NodePort。在浏览器访问http://your-server-ip:31234如果看到 nginx 欢迎页说明集群网络、Service、DNS 全部工作正常。这一步绝不能跳过它是后续所有 PHP 部署的基石。我曾在一个客户现场因为 Calico 部署失败Pod 一直处于ContainerCreating状态查了两小时才发现是--pod-network-cidr写成了10.244.0.0/16这是 Flannel 的默认值和 Calico 冲突。4.3 部署 MySQL 作为后端服务StatefulSet 与 Headless Service 的必要性PHP 应用几乎都依赖数据库而 MySQL 在 Kubernetes 里不能像无状态应用那样随便部署。它需要持久化存储、稳定的网络标识、有序的启动/关闭。所以必须用 StatefulSet而不是 Deployment。首先创建一个 PersistentVolumePV和 PersistentVolumeClaimPVC。Ubuntu 18.04 上最简单的方式是用 hostPath仅用于学习和测试生产环境请用 NFS 或云盘# mysql-pv.yaml apiVersion: v1 kind: PersistentVolume metadata: name: mysql-pv spec: capacity: storage: 2Gi accessModes: - ReadWriteOnce hostPath: path: /mnt/data --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 2Gi应用它kubectl apply -f mysql-pv.yaml。然后创建 MySQL StatefulSet# mysql-statefulset.yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql spec: serviceName: mysql replicas: 1 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:5.7 env: - name: MYSQL_ROOT_PASSWORD value: rootpass - name: MYSQL_DATABASE value: myapp - name: MYSQL_USER value: appuser - name: MYSQL_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: password ports: - containerPort: 3306 name: mysql volumeMounts: - name: mysql-persistent-storage mountPath: /var/lib/mysql volumes: - name: mysql-persistent-storage persistentVolumeClaim: claimName: mysql-pvc --- apiVersion: v1 kind: Service metadata: name: mysql spec: ports: - port: 3306 name: mysql clusterIP: None # Headless Service提供稳定的 DNS 记录 selector: app: mysql关键点在于clusterIP: None这创建了一个 Headless Service。它不分配 ClusterIP而是为每个 Pod 创建一个 DNS 记录如mysql-0.mysql.default.svc.cluster.local。这样PHP 应用连接数据库时可以写死mysql.default.svc.cluster.local即使 Pod 重启、IP 变化DNS 解析依然准确。应用这个 YAML 后执行kubectl get statefulset,pod,service,pvc确认 mysql-0 Pod 状态为 RunningPVC Bound。然后进入 Pod 验证数据库kubectl exec -it mysql-0 -- mysql -uappuser -p$(kubectl get secret mysql-secret -o jsonpath{.data.password} | base64 -d) myapp -e SHOW TABLES;如果看到表名列表说明 MySQL 已就绪。这一步的严谨性直接决定了后续 PHP 应用能否成功连接数据库。4.4 部署 PHP-FPM 和 nginx两个 Deployment 一个 Service 的完整串联现在轮到核心的 PHP 应用了。我们分三步走先部署 PHP-FPM再部署 nginx最后用 Service 暴露。首先PHP-FPM Deploymentphp-fpm-deployment.yamlapiVersion: apps/v1 kind: Deployment metadata: name: php-fpm spec: replicas: 2 selector: matchLabels: app: php-fpm template: metadata: labels: app: php-fpm spec: # 指定以 www-data 用户运行 securityContext: runAsUser: 33 fsGroup: 33 containers: - name: php-fpm image: your-registry/php-app:1.0 # 替换为你自己的镜像 ports: - containerPort: 9000 env: - name: PM_MAX_CHILDREN value: 10 - name: PM_START_SERVERS value: 3 - name: PM_MIN_SPARE_SERVERS value: 2 - name: PM_MAX_SPARE_SERVERS value: 5 # 挂载 ConfigMap 配置 volumeMounts: - name: php-ini-volume mountPath: /etc/php/7.2/fpm/php.ini subPath: php.ini readOnly: true - name: app-code mountPath: /var/www/html volumes: - name: php-ini-volume configMap: name: php-ini-config - name: app-code persistentVolumeClaim: claimName: app-code-pvc # 你需要提前创建这个 PVC挂载你的 PHP 代码注意securityContext和volumeMounts的组合这是保证权限正确的关键。然后是 nginx Deploymentnginx-deployment.yamlapiVersion: apps/v1 kind: Deployment metadata: name: nginx spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: # 同样以 www-data 用户运行 securityContext: runAsUser: 33 fsGroup: 33 containers: - name: nginx image: nginx:1.18 ports: - containerPort: 80 # 挂载自定义 nginx.conf volumeMounts: - name: nginx-config-volume mountPath: /etc/nginx/nginx.conf subPath: nginx.conf readOnly: true - name: app-code mountPath:

相关新闻

最新新闻

外卖跑腿配送系统如何实现智能调度与配送优化?

外卖跑腿配送系统如何实现智能调度与配送优化?

随着即时配送行业的快速发展,用户对于配送时效的要求越来越高。从餐饮外卖到商超零售、生鲜配送,再到同城跑腿、鲜花蛋糕等多元化配送场景,如何在短时间内将订单精准分配给合适的骑手,并保证配送效率,已经成为外卖跑腿…

2026/7/3 4:02:39
机器学习面试数据准备实战指南:20个高频问题深度解析

机器学习面试数据准备实战指南:20个高频问题深度解析

1. 这不是刷题手册,而是一份数据准备环节的实战作战地图“Crack ML Interviews with Confidence: Data Preparation (20 Q&A)”——这个标题里藏着一个被绝大多数求职者严重低估的真相:机器学习面试中,真正拉开候选人差距的,从…

2026/7/3 4:02:39
终极指南:5分钟搭建图形化文件共享工具的完整方案

终极指南:5分钟搭建图形化文件共享工具的完整方案

终极指南:5分钟搭建图形化文件共享工具的完整方案 【免费下载链接】chfsgui This is just a GUI WRAPPER for chfs(cute http file server) 项目地址: https://gitcode.com/gh_mirrors/ch/chfsgui 在数字化办公时代,文件共享已成为日常工作的基本…

2026/7/3 4:02:39
系统设计 018:共同好友与六度人脉

系统设计 018:共同好友与六度人脉

系统设计 018:共同好友与六度人脉Bilibili 同步视频🌿 一、共同好友功能:从业务逻辑到缓存性能优化✨ 业务价值定位🧩 核心实现原理📊 业务流量特征⚡ 性能痛点与缓存优化方案💻 核心伪代码实现&#x1f50…

2026/7/3 4:02:39
1.2 使用LangChain中的RunnableLambda

1.2 使用LangChain中的RunnableLambda

📄 文件代码内容 from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import PromptTemplate from langchain_community.chat_models.tongyi import ChatTongyimodel ChatTongyi(model"qwen3-max") str_parser St…

2026/7/3 4:02:39
【保姆级】VMware17.0.1虚拟机安装教程与下载步骤分享

【保姆级】VMware17.0.1虚拟机安装教程与下载步骤分享

如大家所熟悉的,VMware中文名威睿,它是一款运行在windows系统上的虚拟机软件,可以虚拟出一台计算机硬件,方便安装各类操作系统,如Windows、Macos、Linux、Unix等等。其核心技术是通过‌Hypervisor(虚拟机监…

2026/7/3 3:57:39

周新闻

月新闻