国产容器平台PHP镜像制作与信创环境分发部署完整方案

  一、先搞清楚整体是什么
                                                                                                                          你要做的事情,用大白话说就是:
                                                                                                                          把PHP政务系统打包成一个"集装箱"(Docker镜像)
  然后放到国产的"港口"(镜像仓库)
  再分发到各个"码头"(信创服务器)上运行

  涉及的国产平台:
    镜像仓库:Harbor(开源,政务常用)/ 华为SWR / 阿里ACR
    容器平台:华为CCE / 阿里ACK / 国产OpenShift替代品 KubeSphere
    底层OS:  麒麟OS / 统信UOS(ARM鲲鹏 或 x86)
    CPU架构:  x86_64  或  aarch64(ARM,鲲鹏/飞腾)

  ---
  二、整体流程图

  开发机(Windows/Mac)
      │
      │  1. 写Dockerfile
      │  2. docker build 构建镜像
      │
      ▼
  本地镜像
      │
      │  3. docker tag 打标签
      │  4. docker push 推送
      │
      ▼
  Harbor私有镜像仓库(内网)
      │
      ├──────────────────────────────┐
      │                              │
      ▼                              ▼
  x86服务器                    ARM鲲鹏服务器
  (麒麟OS x86)               (麒麟OS aarch64)
      │                              │
      │  5. docker pull              │  5. docker pull
      │  6. docker run               │  6. docker run
      ▼                              ▼
  PHP政务系统运行中            PHP政务系统运行中

  ---
  三、关键难点:ARM架构兼容问题

  信创最大的坑就是这个:

  普通Docker镜像(x86)在ARM鲲鹏服务器上跑不起来!
  就像Windows软件装不到Mac M1上一样。

  解决方案有两种:
    方案A:构建多架构镜像(一个镜像同时支持x86和ARM)← 推荐
    方案B:分别构建x86镜像和ARM镜像,分开管理

  本方案用方案A,一次构建,到处运行。

  ---
  四、目录结构(先建好)

  gov-approval/
  ├── docker/
  │   ├── php/
  │   │   ├── Dockerfile              # PHP应用镜像
  │   │   ├── php.ini                 # PHP配置
  │   │   ├── php-fpm.conf            # PHP-FPM配置
  │   │   └── supervisord.conf        # 进程管理
  │   ├── nginx/
  │   │   ├── Dockerfile              # Nginx镜像
  │   │   └── gov_approval.conf       # Nginx配置
  │   └── dameng/
  │       └── Dockerfile              # 达梦数据库镜像(可选)
  ├── deploy/
  │   ├── docker-compose.yml          # 本地开发用
  │   ├── docker-compose.prod.yml     # 生产环境用
  │   ├── k8s/                        # Kubernetes部署文件
  │   │   ├── namespace.yaml
  │   │   ├── configmap.yaml
  │   │   ├── secret.yaml
  │   │   ├── deployment.yaml
  │   │   ├── service.yaml
  │   │   └── ingress.yaml
  │   └── scripts/
  │       ├── build.sh                # 构建脚本
  │       ├── push.sh                 # 推送脚本
  │       └── deploy.sh               # 部署脚本
  ├── src/                            # PHP业务代码
  └── public/
      └── index.php

  ---
  五、PHP应用镜像 Dockerfile(核心)

  # 文件: docker/php/Dockerfile
  # 大白话:这个文件告诉Docker怎么一步步造出我们的PHP镜像

  # ============================================================
  # 第一阶段:安装Composer依赖(构建阶段,不进最终镜像)
  # 好处:最终镜像不包含Composer工具,体积更小
  # ============================================================
  FROM php:8.2-cli-alpine AS composer-stage

  # 安装Composer
  COPY --from=composer:2.6 /usr/bin/composer /usr/bin/composer

  WORKDIR /app

  # 先只复制依赖文件(利用Docker缓存层,代码没变时不重新装依赖)
  COPY composer.json composer.lock ./

  # 安装PHP依赖(不装dev依赖,生产环境用不到)
  RUN composer install \
      --no-dev \
      --no-scripts \
      --no-autoloader \
      --prefer-dist \
      --optimize-autoloader

  # 复制全部代码,生成autoload
  COPY . .
  RUN composer dump-autoload --optimize --no-dev


  # ============================================================
  # 第二阶段:构建最终运行镜像
  # 基础镜像选php:8.2-fpm-alpine,体积小,支持多架构
  # alpine是一个超小的Linux,只有5MB
  # ============================================================
  FROM php:8.2-fpm-alpine AS production

  # 设置时区为中国时区(政务系统必须)
  ENV TZ=Asia/Shanghai
  RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \
      && echo $TZ > /etc/timezone

  # 安装系统依赖
  # --no-cache 不缓存,减小镜像体积
  RUN apk add --no-cache \
      nginx \
      supervisor \
      curl \
      libpng-dev \
      libjpeg-turbo-dev \
      freetype-dev \
      libzip-dev \
      oniguruma-dev \
      libxml2-dev \
      icu-dev \
      tzdata

  # 安装PHP扩展
  RUN docker-php-ext-configure gd \
          --with-freetype \
          --with-jpeg \
      && docker-php-ext-install -j$(nproc) \
          pdo \
          pdo_mysql \
          mbstring \
          xml \
          zip \
          gd \
          intl \
          opcache \
          bcmath

  # 安装Redis扩展(通过PECL)
  RUN apk add --no-cache --virtual .build-deps $PHPIZE_DEPS \
      && pecl install redis-6.0.2 \
      && docker-php-ext-enable redis \
      && apk del .build-deps \
      && rm -rf /tmp/pear

  # ============================================================
  # 安装达梦DM8 PHP驱动(信创关键步骤)
  # 注意:达梦驱动需要从达梦官网下载,这里假设已放到docker/php/drivers/
  # ============================================================
  # 复制达梦驱动文件
  COPY docker/php/drivers/pdo_dm.so /tmp/pdo_dm.so

  # 根据架构选择对应驱动(多架构构建时自动选择)
  RUN PHP_EXT_DIR=$(php -r "echo ini_get('extension_dir');") \
      && cp /tmp/pdo_dm.so ${PHP_EXT_DIR}/pdo_dm.so \
      && echo "extension=pdo_dm.so" > /usr/local/etc/php/conf.d/pdo_dm.ini \
      && rm /tmp/pdo_dm.so

  # 复制PHP配置文件
  COPY docker/php/php.ini /usr/local/etc/php/php.ini
  COPY docker/php/php-fpm.conf /usr/local/etc/php-fpm.d/www.conf

  # 复制Nginx配置
  COPY docker/nginx/gov_approval.conf /etc/nginx/http.d/default.conf

  # 复制Supervisor配置(同时管理nginx和php-fpm两个进程)
  COPY docker/php/supervisord.conf /etc/supervisord.conf

  # 创建运行用户(不用root运行,安全)
  RUN addgroup -g 1000 www \
      && adduser -u 1000 -G www -s /bin/sh -D www

  # 设置工作目录
  WORKDIR /var/www/html

  # 从第一阶段复制代码和依赖(不复制Composer工具本身)
  COPY --from=composer-stage --chown=www:www /app /var/www/html

  # 创建必要目录并设置权限
  RUN mkdir -p storage/logs storage/cache storage/uploads \
      && chown -R www:www storage \
      && chmod -R 775 storage

  # 暴露端口(Nginx监听80)
  EXPOSE 80

  # 健康检查(K8s用来判断容器是否正常)
  HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
      CMD curl -f http://localhost/health || exit 1

  # 启动命令:用supervisor同时启动nginx和php-fpm
  CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]

  ---
  六、配置文件

  6.1 PHP配置

  ; 文件: docker/php/php.ini

  [PHP]
  ; 基础设置
  memory_limit = 256M
  max_execution_time = 300
  max_input_time = 300
  post_max_size = 50M
  upload_max_filesize = 50M
  default_charset = UTF-8

  ; 时区
  date.timezone = Asia/Shanghai

  ; 错误处理(生产环境不显示错误,写日志)
  display_errors = Off
  log_errors = On
  error_log = /var/www/html/storage/logs/php_error.log
  error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT

  ; OPcache(PHP代码缓存,提升性能)
  [opcache]
  opcache.enable = 1
  opcache.memory_consumption = 128
  opcache.interned_strings_buffer = 8
  opcache.max_accelerated_files = 10000
  opcache.revalidate_freq = 60
  opcache.fast_shutdown = 1

  ; Session(政务系统用Redis存Session)
  [Session]
  session.save_handler = redis
  session.save_path = "tcp://redis:6379?auth=your_redis_password"
  session.gc_maxlifetime = 7200
  session.cookie_httponly = 1
  session.cookie_secure = 0
  session.use_strict_mode = 1

  6.2 PHP-FPM配置

  ; 文件: docker/php/php-fpm.conf

  [www]
  user = www
  group = www

  ; 监听方式(用socket比TCP快)
  listen = /var/run/php-fpm.sock
  listen.owner = www
  listen.group = www
  listen.mode = 0660

  ; 进程管理模式
  ; dynamic = 动态,根据负载自动增减进程数
  pm = dynamic
  pm.max_children = 50        ; 最多50个进程
  pm.start_servers = 5        ; 启动时5个进程
  pm.min_spare_servers = 5    ; 最少保持5个空闲进程
  pm.max_spare_servers = 20   ; 最多保持20个空闲进程
  pm.max_requests = 500       ; 每个进程处理500个请求后重启(防内存泄漏)

  ; 慢日志(超过3秒的请求记录下来)
  slowlog = /var/www/html/storage/logs/php-fpm-slow.log
  request_slowlog_timeout = 3s

  ; 状态页(监控用)
  pm.status_path = /fpm-status

  6.3 Supervisor配置(同时管理Nginx和PHP-FPM)

  ; 文件: docker/php/supervisord.conf
  ; 大白话:一个容器里同时跑Nginx和PHP-FPM,用supervisor管理

  [supervisord]
  nodaemon = true              ; 前台运行(容器必须前台)
  logfile = /dev/stdout        ; 日志输出到控制台
  logfile_maxbytes = 0
  loglevel = info

  [program:nginx]
  command = nginx -g "daemon off;"
  autostart = true
  autorestart = true
  stdout_logfile = /dev/stdout
  stdout_logfile_maxbytes = 0
  stderr_logfile = /dev/stderr
  stderr_logfile_maxbytes = 0

  [program:php-fpm]
  command = php-fpm --nodaemonize
  autostart = true
  autorestart = true
  stdout_logfile = /dev/stdout
  stdout_logfile_maxbytes = 0
  stderr_logfile = /dev/stderr
  stderr_logfile_maxbytes = 0

  6.4 Nginx配置

  # 文件: docker/nginx/gov_approval.conf

  server {
      listen 80;
      server_name _;
      root /var/www/html/public;
      index index.php;

      # 字符集
      charset utf-8;

      # 安全头
      add_header X-Frame-Options "SAMEORIGIN" always;
      add_header X-Content-Type-Options "nosniff" always;
      add_header X-XSS-Protection "1; mode=block" always;

      # 健康检查接口(K8s探针用)
      location /health {
          access_log off;
          return 200 "OK";
          add_header Content-Type text/plain;
      }

      # PHP-FPM状态(内部监控用,外部不能访问)
      location /fpm-status {
          allow 127.0.0.1;
          deny all;
          fastcgi_pass unix:/var/run/php-fpm.sock;
          include fastcgi_params;
          fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      }

      # 主路由
      location / {
          try_files $uri $uri/ /index.php?$query_string;
      }

      # PHP处理
      location ~ \.php$ {
          fastcgi_pass unix:/var/run/php-fpm.sock;
          fastcgi_index index.php;
          fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
          include fastcgi_params;
          fastcgi_read_timeout 300;
          fastcgi_buffer_size 128k;
          fastcgi_buffers 4 256k;
      }

      # 附件不能直接访问(必须经过PHP鉴权)
      location /storage/uploads/ {
          internal;
      }

      # 静态资源缓存
      location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
          expires 30d;
          add_header Cache-Control "public, immutable";
          access_log off;
      }

      # 禁止访问敏感文件
      location ~ /\.(env|git|htaccess|DS_Store) {
          deny all;
          return 404;
      }
  }

  ---
  七、多架构镜像构建脚本(x86 + ARM一次搞定)

  #!/bin/bash
  # 文件: deploy/scripts/build.sh
  # 大白话:这个脚本一次构建出同时支持x86和ARM的镜像

  set -e  # 任何命令失败就停止

  # ============================================================
  # 配置区(根据实际情况修改)
  # ============================================================
  REGISTRY="harbor.gov-internal.cn"          # Harbor仓库地址
  PROJECT="gov-approval"                      # 项目名
  IMAGE_NAME="php-app"                        # 镜像名
  VERSION=$(git rev-parse --short HEAD)       # 用git commit hash作版本号
  LATEST_TAG="latest"

  FULL_IMAGE="${REGISTRY}/${PROJECT}/${IMAGE_NAME}"

  echo "============================================"
  echo "构建信息:"
  echo "  镜像: ${FULL_IMAGE}"
  echo "  版本: ${VERSION}"
  echo "  架构: linux/amd64, linux/arm64"
  echo "============================================"

  # ============================================================
  # 第一步:登录Harbor仓库
  # ============================================================
  echo "[1/4] 登录Harbor镜像仓库..."
  echo "${HARBOR_PASSWORD}" | docker login ${REGISTRY} \
      -u "${HARBOR_USERNAME}" \
      --password-stdin

  # ============================================================
  # 第二步:创建多架构构建器
  # 大白话:Docker默认只能构建当前机器架构的镜像
  # buildx是Docker的扩展功能,可以在x86机器上构建ARM镜像
  # ============================================================
  echo "[2/4] 初始化多架构构建器..."

  # 检查是否已有构建器
  if ! docker buildx inspect gov-builder > /dev/null 2>&1; then
      docker buildx create \
          --name gov-builder \
          --driver docker-container \
          --platform linux/amd64,linux/arm64 \
          --use
      docker buildx inspect gov-builder --bootstrap
  else
      docker buildx use gov-builder
  fi

  # ============================================================
  # 第三步:构建并推送多架构镜像
  # --platform 指定同时构建x86和ARM两个版本
  # --push 构建完直接推送到仓库(多架构必须推送,不能只存本地)
  # ============================================================
  echo "[3/4] 构建多架构镜像并推送..."

  docker buildx build \
      --platform linux/amd64,linux/arm64 \
      --file docker/php/Dockerfile \
      --tag "${FULL_IMAGE}:${VERSION}" \
      --tag "${FULL_IMAGE}:${LATEST_TAG}" \
      --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
      --build-arg GIT_COMMIT=${VERSION} \
      --push \
      .

  # ============================================================
  # 第四步:验证镜像
  # ============================================================
  echo "[4/4] 验证镜像..."
  docker buildx imagetools inspect "${FULL_IMAGE}:${VERSION}"

  echo ""
  echo "构建完成!"
  echo "镜像地址: ${FULL_IMAGE}:${VERSION}"

  ---
  八、Harbor私有镜像仓库搭建

  #!/bin/bash
  # 文件: deploy/scripts/install_harbor.sh
  # 大白话:在内网服务器上装Harbor,作为私有镜像仓库

  # ============================================================
  # 前提:服务器已安装Docker和Docker Compose
  # ============================================================

  HARBOR_VERSION="v2.10.0"
  HARBOR_HOSTNAME="harbor.gov-internal.cn"
  HARBOR_ADMIN_PASSWORD="Harbor12345"
  HARBOR_DATA_DIR="/data/harbor"

  # 下载Harbor安装包(离线包,适合内网环境)
  # 在有网络的机器上下载后传入内网
  wget
  https://github.com/goharbor/harbor/releases/download/${HARBOR_VERSION}/harbor-offline-installer-${HARBOR_VERSION}.tgz

  tar -xzf harbor-offline-installer-${HARBOR_VERSION}.tgz
  cd harbor

  # 复制配置模板
  cp harbor.yml.tmpl harbor.yml

  # 修改配置
  sed -i "s/hostname: reg.mydomain.com/hostname: ${HARBOR_HOSTNAME}/" harbor.yml
  sed -i "s/harbor_admin_password: Harbor12345/harbor_admin_password: ${HARBOR_ADMIN_PASSWORD}/" harbor.yml

  # 修改数据存储路径
  sed -i "s|data_volume: /data|data_volume: ${HARBOR_DATA_DIR}|" harbor.yml

  # 如果没有HTTPS证书,先禁用HTTPS(内网可以用HTTP)
  # 注释掉https配置段
  sed -i '/^https:/,/^[a-z]/{/^https:/d;/  port: 443/d;/  certificate:/d;/  private_key:/d}' harbor.yml

  # 安装Harbor
  ./install.sh

  echo "Harbor安装完成!"
  echo "访问地址: http://${HARBOR_HOSTNAME}"
  echo "管理员账号: admin"
  echo "管理员密码: ${HARBOR_ADMIN_PASSWORD}"

  # Harbor核心配置 harbor.yml(关键部分)
  hostname: harbor.gov-internal.cn

  # HTTP配置(内网用HTTP,生产建议HTTPS)
  http:
    port: 80

  # 管理员密码
  harbor_admin_password: Harbor12345

  # 数据库配置
  database:
    password: root123
    max_idle_conns: 50
    max_open_conns: 100

  # 数据存储路径
  data_volume: /data/harbor

  # 日志
  log:
    level: info
    local:
      rotate_count: 50
      rotate_size: 200M
      location: /var/log/harbor

  ---
  九、本地开发环境(docker-compose)

  # 文件: deploy/docker-compose.yml
  # 大白话:本地开发时一键启动所有服务

  version: '3.8'

  services:
    # PHP应用
    app:
      build:
        context: .
        dockerfile: docker/php/Dockerfile
        target: production
      container_name: gov_app
      restart: unless-stopped
      ports:
        - "8080:80"
      environment:
        - DB_TYPE=dameng
        - DB_HOST=dameng
        - DB_PORT=5236
        - DB_NAME=DAMENG
        - DB_SCHEMA=GOV_APPROVAL
        - DB_USER=SYSDBA
        - DB_PASSWORD=SYSDBA001
        - REDIS_HOST=redis
        - REDIS_PASSWORD=redis123
        - APP_ENV=production
        - APP_KEY=base64:your-32-char-secret-key-here
      volumes:
        # 开发时挂载代码目录,改代码不用重建镜像
        - ./src:/var/www/html/src
        - ./public:/var/www/html/public
        # 日志持久化
        - ./storage/logs:/var/www/html/storage/logs
        # 上传文件持久化
        - ./storage/uploads:/var/www/html/storage/uploads
      depends_on:
        dameng:
          condition: service_healthy
        redis:
          condition: service_healthy
      networks:
        - gov_network

    # 达梦数据库
    dameng:
      image: harbor.gov-internal.cn/gov-approval/dameng:8.1.2
      container_name: gov_dameng
      restart: unless-stopped
      ports:
        - "5236:5236"
      environment:
        - SYSDBA_PWD=SYSDBA001
      volumes:
        - dameng_data:/opt/dmdbms/data
      healthcheck:
        test: ["CMD", "/opt/dmdbms/bin/disql", "SYSDBA/SYSDBA001@127.0.0.1:5236", "-e", "SELECT 1;"]
        interval: 30s
        timeout: 10s
        retries: 5
        start_period: 60s
      networks:
        - gov_network

    # Redis缓存
    redis:
      image: redis:7-alpine
      container_name: gov_redis
      restart: unless-stopped
      command: redis-server --requirepass redis123 --maxmemory 256mb --maxmemory-policy allkeys-lru
      volumes:
        - redis_data:/data
      healthcheck:
        test: ["CMD", "redis-cli", "-a", "redis123", "ping"]
        interval: 10s
        timeout: 5s
        retries: 3
      networks:
        - gov_network

  volumes:
    dameng_data:
    redis_data:

  networks:
    gov_network:
      driver: bridge

  ---
  十、Kubernetes部署文件(生产环境)

  10.1 命名空间

  # 文件: deploy/k8s/namespace.yaml
  apiVersion: v1
  kind: Namespace
  metadata:
    name: gov-approval
    labels:
      app: gov-approval
      env: production

  10.2 配置字典(非敏感配置)

  # 文件: deploy/k8s/configmap.yaml
  apiVersion: v1
  kind: ConfigMap
  metadata:
    name: gov-approval-config
    namespace: gov-approval
  data:
    DB_TYPE: "dameng"
    DB_HOST: "dameng-service"
    DB_PORT: "5236"
    DB_NAME: "DAMENG"
    DB_SCHEMA: "GOV_APPROVAL"
    REDIS_HOST: "redis-service"
    APP_ENV: "production"
    TZ: "Asia/Shanghai"

  10.3 密钥(敏感配置)

  # 文件: deploy/k8s/secret.yaml
  # 大白话:密码这类敏感信息单独存,base64编码(不是加密!只是编码)
  # 生成方式: echo -n "SYSDBA001" | base64

  apiVersion: v1
  kind: Secret
  metadata:
    name: gov-approval-secret
    namespace: gov-approval
  type: Opaque
  data:
    DB_USER: U1lTREJB                    # SYSDBA
    DB_PASSWORD: U1lTREJBMDAx            # SYSDBA001
    REDIS_PASSWORD: cmVkaXMxMjM=         # redis123
    APP_KEY: YmFzZTY0OnlvdXIta2V5        # 你的APP_KEY

  10.4 部署配置(核心)

  # 文件: deploy/k8s/deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: gov-approval-app
    namespace: gov-approval
    labels:
      app: gov-approval
      component: app
  spec:
    # 运行3个副本,高可用
    replicas: 3

    selector:
      matchLabels:
        app: gov-approval
        component: app

    # 滚动更新策略(不停机更新)
    strategy:
      type: RollingUpdate
      rollingUpdate:
        maxSurge: 1        # 更新时最多多启动1个新Pod
        maxUnavailable: 0  # 更新时不允许有Pod不可用(零停机)

    template:
      metadata:
        labels:
          app: gov-approval
          component: app
      spec:
        # 镜像拉取密钥(从Harbor拉镜像需要认证)
        imagePullSecrets:
          - name: harbor-secret

        containers:
          - name: gov-approval-app
            # 镜像地址(用具体版本号,不用latest,生产环境要可追溯)
            image: harbor.gov-internal.cn/gov-approval/php-app:abc1234
            imagePullPolicy: Always

            ports:
              - containerPort: 80
                protocol: TCP

            # 从ConfigMap和Secret注入环境变量
            envFrom:
              - configMapRef:
                  name: gov-approval-config
              - secretRef:
                  name: gov-approval-secret

            # 资源限制(防止一个Pod把整台服务器资源耗尽)
            resources:
              requests:
                cpu: "200m"      # 最少需要0.2核CPU
                memory: "256Mi"  # 最少需要256MB内存
              limits:
                cpu: "1000m"     # 最多用1核CPU
                memory: "512Mi"  # 最多用512MB内存

            # 存活探针(失败则重启容器)
            livenessProbe:
              httpGet:
                path: /health
                port: 80
              initialDelaySeconds: 60   # 启动后60秒开始检查
              periodSeconds: 30         # 每30秒检查一次
              failureThreshold: 3       # 连续失败3次才重启

            # 就绪探针(失败则从负载均衡摘除,不重启)
            readinessProbe:
              httpGet:
                path: /health
                port: 80
              initialDelaySeconds: 30
              periodSeconds: 10
              failureThreshold: 3

            # 挂载持久化存储
            volumeMounts:
              - name: uploads-storage
                mountPath: /var/www/html/storage/uploads
              - name: logs-storage
                mountPath: /var/www/html/storage/logs

        volumes:
          - name: uploads-storage
            persistentVolumeClaim:
              claimName: gov-approval-uploads-pvc
          - name: logs-storage
            persistentVolumeClaim:
              claimName: gov-approval-logs-pvc

        # 节点亲和性(指定部署到信创节点)
        affinity:
          nodeAffinity:
            preferredDuringSchedulingIgnoredDuringExecution:
              - weight: 100
                preference:
                  matchExpressions:
                    - key: node-type
                      operator: In
                      values:
                        - xinchuang   # 优先调度到打了信创标签的节点

  10.5 Service(内部访问)

  # 文件: deploy/k8s/service.yaml
  apiVersion: v1
  kind: Service
  metadata:
    name: gov-approval-service
    namespace: gov-approval
  spec:
    selector:
      app: gov-approval
      component: app
    ports:
      - protocol: TCP
        port: 80
        targetPort: 80
    type: ClusterIP   # 集群内部访问

  10.6 Ingress(外部访问入口)

  # 文件: deploy/k8s/ingress.yaml
  apiVersion: networking.k8s.io/v1
  kind: Ingress
  metadata:
    name: gov-approval-ingress
    namespace: gov-approval
    annotations:
      nginx.ingress.kubernetes.io/proxy-body-size: "50m"
      nginx.ingress.kubernetes.io/proxy-read-timeout: "300
> 继续                                                                                                                         ed for 1m 53s
✽ Zesting…                                                                                                            ●Z    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
      nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
      nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
  spec:                                                                                                                     ingressClassName: nginx
    rules:                                                                                                                    - host: gov-approval.example.gov.cn
        http:
          paths:
            - path: /
              pathType: Prefix
              backend:
                service:
                  name: gov-approval-service
                  port:
                    number: 80

  10.7 持久化存储

  # 文件: deploy/k8s/pvc.yaml
  # 大白话:申请持久化磁盘,容器重启数据不丢失

  # 上传文件存储
  apiVersion: v1
  kind: PersistentVolumeClaim
  metadata:
    name: gov-approval-uploads-pvc
    namespace: gov-approval
  spec:
    accessModes:
      - ReadWriteMany    # 多个Pod同时读写(NFS或分布式存储)
    storageClassName: nfs-storage   # 用NFS存储类
    resources:
      requests:
        storage: 100Gi

  ---
  # 日志存储
  apiVersion: v1
  kind: PersistentVolumeClaim
  metadata:
    name: gov-approval-logs-pvc
    namespace: gov-approval
  spec:
    accessModes:
      - ReadWriteMany
    storageClassName: nfs-storage
    resources:
      requests:
        storage: 20Gi

  ---
  十一、自动化部署脚本

  11.1 构建推送一体脚本

  #!/bin/bash
  # 文件: deploy/scripts/build_and_push.sh
  # 大白话:一条命令完成:构建镜像 → 推送到Harbor → 更新K8s部署

  set -e

  # ============================================================
  # 配置
  # ============================================================
  REGISTRY="harbor.gov-internal.cn"
  PROJECT="gov-approval"
  IMAGE_NAME="php-app"
  NAMESPACE="gov-approval"
  DEPLOYMENT_NAME="gov-approval-app"

  # 自动获取版本号(用git commit hash)
  if git rev-parse --git-dir > /dev/null 2>&1; then
      VERSION=$(git rev-parse --short HEAD)
  else
      VERSION=$(date +%Y%m%d%H%M%S)
  fi

  FULL_IMAGE="${REGISTRY}/${PROJECT}/${IMAGE_NAME}:${VERSION}"

  # ============================================================
  # 颜色输出(让日志好看一点)
  # ============================================================
  RED='\033[0;31m'
  GREEN='\033[0;32m'
  YELLOW='\033[1;33m'
  NC='\033[0m'  # 无颜色

  log_info()    { echo -e "${GREEN}[INFO]${NC} $1"; }
  log_warn()    { echo -e "${YELLOW}[WARN]${NC} $1"; }
  log_error()   { echo -e "${RED}[ERROR]${NC} $1"; }

  # ============================================================
  # 步骤1:检查前置条件
  # ============================================================
  log_info "检查前置条件..."

  command -v docker     >/dev/null 2>&1 || { log_error "未安装docker";     exit 1; }
  command -v kubectl    >/dev/null 2>&1 || { log_error "未安装kubectl";    exit 1; }

  if [ -z "${HARBOR_USERNAME}" ] || [ -z "${HARBOR_PASSWORD}" ]; then
      log_error "请设置环境变量 HARBOR_USERNAME 和 HARBOR_PASSWORD"
      exit 1
  fi

  # ============================================================
  # 步骤2:登录Harbor
  # ============================================================
  log_info "登录Harbor: ${REGISTRY}"
  echo "${HARBOR_PASSWORD}" | docker login "${REGISTRY}" \
      -u "${HARBOR_USERNAME}" --password-stdin

  # ============================================================
  # 步骤3:构建多架构镜像
  # ============================================================
  log_info "构建多架构镜像: ${FULL_IMAGE}"

  # 确保buildx构建器存在
  if ! docker buildx inspect gov-builder >/dev/null 2>&1; then
      log_info "创建多架构构建器..."
      docker buildx create \
          --name gov-builder \
          --driver docker-container \
          --platform linux/amd64,linux/arm64 \
          --use
      docker buildx inspect gov-builder --bootstrap
  else
      docker buildx use gov-builder
  fi

  # 构建并推送
  docker buildx build \
      --platform linux/amd64,linux/arm64 \
      --file docker/php/Dockerfile \
      --tag "${FULL_IMAGE}" \
      --tag "${REGISTRY}/${PROJECT}/${IMAGE_NAME}:latest" \
      --build-arg BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
      --build-arg GIT_COMMIT="${VERSION}" \
      --push \
      .

  log_info "镜像推送成功: ${FULL_IMAGE}"

  # ============================================================
  # 步骤4:更新K8s部署(滚动更新,零停机)
  # ============================================================
  log_info "更新K8s部署..."

  # 更新镜像版本
  kubectl set image deployment/${DEPLOYMENT_NAME} \
      gov-approval-app=${FULL_IMAGE} \
      -n ${NAMESPACE}

  # 等待滚动更新完成
  log_info "等待滚动更新完成..."
  kubectl rollout status deployment/${DEPLOYMENT_NAME} \
      -n ${NAMESPACE} \
      --timeout=300s

  # ============================================================
  # 步骤5:验证部署结果
  # ============================================================
  log_info "验证部署结果..."

  # 查看Pod状态
  kubectl get pods -n ${NAMESPACE} -l app=gov-approval

  # 查看当前镜像版本
  CURRENT_IMAGE=$(kubectl get deployment ${DEPLOYMENT_NAME} \
      -n ${NAMESPACE} \
      -o jsonpath='{.spec.template.spec.containers[0].image}')

  log_info "当前运行镜像: ${CURRENT_IMAGE}"

  if [ "${CURRENT_IMAGE}" = "${FULL_IMAGE}" ]; then
      log_info "部署成功!版本: ${VERSION}"
  else
      log_error "部署验证失败,当前镜像不匹配"
      exit 1
  fi

  11.2 回滚脚本

  #!/bin/bash
  # 文件: deploy/scripts/rollback.sh
  # 大白话:出问题了,一键回滚到上一个版本

  NAMESPACE="gov-approval"
  DEPLOYMENT_NAME="gov-approval-app"

  echo "当前部署历史:"
  kubectl rollout history deployment/${DEPLOYMENT_NAME} -n ${NAMESPACE}

  echo ""
  read -p "回滚到上一版本?(y/n): " confirm

  if [ "${confirm}" = "y" ]; then
      kubectl rollout undo deployment/${DEPLOYMENT_NAME} -n ${NAMESPACE}
      kubectl rollout status deployment/${DEPLOYMENT_NAME} -n ${NAMESPACE} --timeout=120s
      echo "回滚完成!"
      kubectl get pods -n ${NAMESPACE} -l app=gov-approval
  else
      echo "取消回滚"
  fi

  ---
  十二、Harbor镜像仓库配置(创建项目和账号)

  #!/bin/bash
  # 文件: deploy/scripts/setup_harbor.sh
  # 大白话:用Harbor的API自动创建项目、账号、权限

  HARBOR_URL="http://harbor.gov-internal.cn"
  HARBOR_ADMIN="admin"
  HARBOR_ADMIN_PWD="Harbor12345"

  # ============================================================
  # 创建项目(相当于一个命名空间)
  # ============================================================
  echo "创建Harbor项目: gov-approval"
  curl -s -X POST \
      -u "${HARBOR_ADMIN}:${HARBOR_ADMIN_PWD}" \
      -H "Content-Type: application/json" \
      "${HARBOR_URL}/api/v2.0/projects" \
      -d '{
          "project_name": "gov-approval",
          "public": false,
          "metadata": {
              "auto_scan": "true",
              "prevent_vul": "false"
          }
      }'

  # ============================================================
  # 创建机器人账号(CI/CD用,不用人工账号)
  # ============================================================
  echo "创建机器人账号: robot-cicd"
  ROBOT_RESPONSE=$(curl -s -X POST \
      -u "${HARBOR_ADMIN}:${HARBOR_ADMIN_PWD}" \
      -H "Content-Type: application/json" \
      "${HARBOR_URL}/api/v2.0/robots" \
      -d '{
          "name": "robot-cicd",
          "description": "CI/CD自动化账号",
          "duration": 365,
          "level": "project",
          "permissions": [
              {
                  "kind": "project",
                  "namespace": "gov-approval",
                  "access": [
                      {"resource": "repository", "action": "push"},
                      {"resource": "repository", "action": "pull"},
                      {"resource": "tag",        "action": "create"},
                      {"resource": "tag",        "action": "delete"}
                  ]
              }
          ]
      }')

  # 提取机器人账号密码(只显示一次,要保存好)
  ROBOT_NAME=$(echo $ROBOT_RESPONSE | python3 -c "import sys,json; print(json.load(sys.stdin)['name'])")
  ROBOT_SECRET=$(echo $ROBOT_RESPONSE | python3 -c "import sys,json; print(json.load(sys.stdin)['secret'])")

  echo "机器人账号: ${ROBOT_NAME}"
  echo "机器人密码: ${ROBOT_SECRET}"
  echo "请立即保存密码,只显示一次!"

  # ============================================================
  # 在K8s中创建Harbor拉取密钥
  # ============================================================
  echo "在K8s中创建镜像拉取密钥..."
  kubectl create secret docker-registry harbor-secret \
      --docker-server="harbor.gov-internal.cn" \
      --docker-username="${ROBOT_NAME}" \
      --docker-password="${ROBOT_SECRET}" \
      --namespace=gov-approval \
      --dry-run=client -o yaml | kubectl apply -f -

  echo "Harbor配置完成!"

  ---
  十三、离线环境镜像分发(内网无法联网时)

  #!/bin/bash
  # 文件: deploy/scripts/offline_distribute.sh
  # 大白话:内网服务器不能上网,用这个脚本把镜像打包传进去

  FULL_IMAGE="harbor.gov-internal.cn/gov-approval/php-app:abc1234"
  EXPORT_FILE="gov-approval-php-app-abc1234.tar"

  # ============================================================
  # 在有网络的机器上:导出镜像为tar包
  # ============================================================
  export_image() {
      echo "导出镜像为tar包..."

      # 先拉取(如果本地没有)
      docker pull "${FULL_IMAGE}"

      # 导出
      docker save -o "${EXPORT_FILE}" "${FULL_IMAGE}"

      # 压缩(镜像通常几百MB,压缩后小很多)
      gzip "${EXPORT_FILE}"

      echo "导出完成: ${EXPORT_FILE}.gz"
      echo "文件大小: $(du -sh ${EXPORT_FILE}.gz | cut -f1)"
  }

  # ============================================================
  # 在内网服务器上:导入镜像
  # ============================================================
  import_image() {
      local tar_file=$1

      echo "导入镜像: ${tar_file}"

      # 解压并导入
      gunzip -c "${tar_file}" | docker load

      echo "导入完成!"
      docker images | grep gov-approval
  }

  # ============================================================
  # 传输到内网服务器(用scp或U盘)
  # ============================================================
  transfer_to_servers() {
      local tar_file="${EXPORT_FILE}.gz"

      # 内网服务器列表
      SERVERS=(
          "192.168.1.101"   # 节点1 x86
          "192.168.1.102"   # 节点2 x86
          "192.168.1.103"   # 节点3 ARM鲲鹏
      )

      for server in "${SERVERS[@]}"; do
          echo "传输到服务器: ${server}"
          scp "${tar_file}" "root@${server}:/tmp/"

          echo "在 ${server} 上导入镜像..."
          ssh "root@${server}" "
              gunzip -c /tmp/${tar_file} | docker load
              rm /tmp/${tar_file}
              echo '导入完成'
          "
      done

      echo "所有服务器镜像分发完成!"
  }

  # 根据参数执行不同操作
  case "$1" in
      export)   export_image ;;
      import)   import_image "$2" ;;
      transfer) transfer_to_servers ;;
      *)
          echo "用法: $0 {export|import <file>|transfer}"
          echo "  export:         导出镜像为tar.gz"
          echo "  import <file>:  导入tar.gz镜像"
          echo "  transfer:       分发到所有内网服务器"
          ;;
  esac

  ---
  十四、KubeSphere部署(国产K8s管理平台)

  #!/bin/bash
  # 大白话:KubeSphere是青云科技做的K8s管理平台
  # 界面友好,政务系统常用,下面是安装步骤

  # ============================================================
  # 在麒麟OS上安装KubeSphere(单节点,用于测试)
  # ============================================================

  # 前提:已安装K8s(用kubeadm或k3s)

  # 安装KubeSphere核心组件
  kubectl apply -f https://github.com/kubesphere/ks-installer/releases/download/v3.4.1/kubesphere-installer.yaml
  kubectl apply -f https://github.com/kubesphere/ks-installer/releases/download/v3.4.1/cluster-configuration.yaml

  # 查看安装进度
  kubectl logs -n kubesphere-system \
      $(kubectl get pod -n kubesphere-system -l app=ks-install -o jsonpath='{.items[0].metadata.name}') \
      -f

  # 安装完成后获取访问地址
  kubectl get svc/ks-console -n kubesphere-system

  # KubeSphere流水线配置(Jenkinsfile格式)
  # 文件: deploy/Jenkinsfile
  # 大白话:在KubeSphere里配置自动化构建流水线

  pipeline {
    agent {
      node {
        label 'maven'
      }
    }

    environment {
      REGISTRY      = 'harbor.gov-internal.cn'
      PROJECT       = 'gov-approval'
      IMAGE_NAME    = 'php-app'
      NAMESPACE     = 'gov-approval'
      DEPLOYMENT    = 'gov-approval-app'
      HARBOR_CRED   = credentials('harbor-robot-cicd')
    }

    stages {
      stage('拉取代码') {
        steps {
          checkout scm
          script {
            env.GIT_COMMIT_SHORT = sh(
              script: 'git rev-parse --short HEAD',
              returnStdout: true
            ).trim()
            env.FULL_IMAGE = "${REGISTRY}/${PROJECT}/${IMAGE_NAME}:${GIT_COMMIT_SHORT}"
          }
        }
      }

      stage('构建镜像') {
        steps {
          sh """
            docker buildx build \
              --platform linux/amd64,linux/arm64 \
              --file docker/php/Dockerfile \
              --tag ${FULL_IMAGE} \
              --push \
              .
          """
        }
      }

      stage('部署到测试环境') {
        steps {
          sh """
            kubectl set image deployment/${DEPLOYMENT} \
              gov-approval-app=${FULL_IMAGE} \
              -n ${NAMESPACE}-test
            kubectl rollout status deployment/${DEPLOYMENT} \
              -n ${NAMESPACE}-test --timeout=180s
          """
        }
      }

      stage('人工审批') {
        steps {
          // 等待人工确认才部署生产
          input message: '确认部署到生产环境?', ok: '确认部署'
        }
      }

      stage('部署到生产环境') {
        steps {
          sh """
            kubectl set image deployment/${DEPLOYMENT} \
              gov-approval-app=${FULL_IMAGE} \
              -n ${NAMESPACE}
            kubectl rollout status deployment/${DEPLOYMENT} \
              -n ${NAMESPACE} --timeout=300s
          """
        }
      }
    }

    post {
      success {
        echo "部署成功!镜像版本: ${env.GIT_COMMIT_SHORT}"
      }
      failure {
        echo "部署失败,自动回滚..."
        sh "kubectl rollout undo deployment/${DEPLOYMENT} -n ${NAMESPACE}"
      }
    }
  }

  ---
  十五、常见问题和解决办法

  问题1:ARM服务器上拉镜像报错 "exec format error"
  原因:镜像只有x86版本,ARM跑不了
  解决:重新用 --platform linux/amd64,linux/arm64 构建多架构镜像

  问题2:达梦驱动在ARM上加载失败
  原因:驱动.so文件是x86编译的,ARM不兼容
  解决:联系达梦官方要ARM版本的pdo_dm.so驱动

  问题3:内网Harbor无法访问
  原因:DNS没配或证书问题
  解决:
    # 在每台服务器的 /etc/hosts 加上
    192.168.1.200  harbor.gov-internal.cn
    # 如果用HTTP,Docker需要配置insecure-registry
    # 编辑 /etc/docker/daemon.json
    {
      "insecure-registries": ["harbor.gov-internal.cn"]
    }
    systemctl restart docker

  问题4:Pod一直处于Pending状态
  原因:资源不足或节点选择器不匹配
  解决:
    kubectl describe pod <pod-name> -n gov-approval
    # 看Events里的报错信息

  问题5:镜像拉取失败 ImagePullBackOff
  原因:K8s没有Harbor的认证信息
  解决:
    kubectl create secret docker-registry harbor-secret \
      --docker-server=harbor.gov-internal.cn \
      --docker-username=robot-cicd \
      --docker-password=<机器人密码> \
      -n gov-approval

  问题6:麒麟OS上buildx不支持多架构
  原因:需要QEMU模拟器
  解决:
    # 安装QEMU多架构支持
    docker run --privileged --rm \
      tonistiigi/binfmt --install all
    # 验证
    docker buildx ls

  ---
  十六、整体流程总结

  开发阶段
    写代码 → docker-compose本地跑 → 验证功能

  构建阶段
    git push → 触发流水线 → buildx构建多架构镜像 → 推送Harbor

  测试阶段
    自动部署测试环境 → 测试验证 → 人工审批

  生产部署
    kubectl set image → 滚动更新(零停机)→ 健康检查验证

  应急处理
    出问题 → kubectl rollout undo → 30秒回滚到上一版本

  核心要点三句话:
  1. 用 docker buildx 一次构建同时支持 x86 和 ARM 的镜像,解决信创架构兼容问题
  2. 用 Harbor 搭内网私有仓库,离线环境用 docker save/load 打包传输
  3. K8s 滚动更新保证零停机,出问题 rollout undo 一键回滚

更多推荐