【Postopia Dev Log】Week 16

问题

github action known host

GitHub Actions 连接服务器时报错 Host key verification failed,是由于 Actions 环境首次连接服务器时缺失主机密钥验证所致。以下是综合多个文档的完整解决方案:


🔍 错误原因

  • 主机密钥未信任:GitHub Actions 的虚拟机首次通过 SSH 连接服务器时,会检查服务器的主机密钥(host key)。若密钥未预先存储在 known_hosts 文件中,SSH 会因安全策略拒绝连接。
  • 与本地环境的差异:本地 Mac 已通过手动连接(输入 yes)将服务器密钥添加到 ~/.ssh/known_hosts,但 Actions 环境是全新的,无此记录。

✅ 解决方案

方法一:提前将服务器密钥添加到 known_hosts(推荐)

在 Actions 工作流中,使用 ssh-keyscan 主动获取服务器密钥并写入 known_hosts 文件:

steps:
- name: Add server host key to known_hosts
  run: |
    mkdir -p ~/.ssh
    ssh-keyscan your_server_ip >> ~/.ssh/known_hosts  # 替换 your_server_ip
    chmod 600 ~/.ssh/known_hosts

📌 原理ssh-keyscan 获取服务器公钥并追加到 known_hosts,避免首次连接时的交互提示。


方法二:禁用严格主机密钥检查(临时方案)

在 SSH 命令中添加参数,跳过密钥验证(仅限测试环境):

steps:
- name: Connect via SSH
  run: ssh -o "StrictHostKeyChecking=no" -i ${{ secrets.SSH_PRIVATE_KEY }} user@server_ip

⚠️ 注意:此方法降低安全性,可能遭受中间人攻击,生产环境慎用。


📝 完整 GitHub Actions 配置示例

name: Deploy to Server
on: [push]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Add Server Host Key
        run: |
          mkdir -p ~/.ssh
          ssh-keyscan your_server_ip >> ~/.ssh/known_hosts
          chmod 600 ~/.ssh/known_hosts

      - name: SSH Command
        run: |
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > private_key
          chmod 400 private_key
          ssh -i private_key user@your_server_ip "echo Connected!"

🔧 关键配置说明

  1. 私钥处理
    • 将 GitHub Secrets 中的私钥写入临时文件,并设置权限 400(必须)。
  2. 主机密钥类型
    • 若服务器使用非默认密钥类型(如 ECDSA),需指定参数:
      ssh-keyscan -t ecdsa your_server_ip >> ~/.ssh/known_hosts
  3. 配置文件优化(可选):
    • 在 Actions 中创建 ~/.ssh/config 文件,简化连接命令:
      echo "Host server
        HostName your_server_ip
        User your_username
        IdentityFile private_key
        StrictHostKeyChecking yes" >> ~/.ssh/config
      

⚠️ 注意事项

  • 权限问题:确保私钥文件权限为 400,否则 SSH 会拒绝使用。
  • 密钥格式:若私钥是 PEM 格式(如以 -----BEGIN RSA PRIVATE KEY----- 开头),需保留换行符。写入文件时建议使用:
    echo "${{ secrets.SSH_PRIVATE_KEY }}" | sed 's/\\n/\n/g' > private_key
    
  • 安全建议
    优先使用 方法一,既保持安全性,又避免手动确认。仅在不敏感环境使用方法二。

通过上述步骤,GitHub Actions 即可安全连接服务器。若仍失败,检查服务器 SSH 服务状态(systemctl status sshd)及防火墙规则。

Redis 无法远程访问

设置 bind 0.0.0.0 和密码

kafka 配置远程仍连接本地

Kafka Broker 的 advertised.listeners 参数决定了客户端实际连接的地址。若该参数配置为本地地址或容器内部地址(如 localhost:9092 或容器内主机名),即使客户端配置了远程 IP,仍会被重定向到本地地址。

listeners=PLAINTEXT://0.0.0.0:9092          # 监听所有网络接口
advertised.listeners=PLAINTEXT://公网IP:9092 # 对外广播的地址

1 port is deprecated. Please use KAFKA_ADVERTISED_LISTENERS instead.

是因为 “Looks like the kubenetes will automatically set the env name APPNAME_PORT, i.e. KAFKA_PORT in this case, the workaround is to change your app name to something else other than kafka.” 通过 “command: [“bash”, “-c”, “unset KAFKA_PORT; /etc/confluent/docker/run”]“ 解决

2 宿主机无法访问minikube 集群

nginx, gateway, nacos 都设成 LoadBalancer

3 mysql 探针失活

修改探针

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  namespace: postopia
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      securityContext:
        fsGroup: 999  # mysql group ID
      containers:
        - name: nacos-mysql
          image: postopia-nacos-mysql:latest
          imagePullPolicy: Never
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: root
            - name: MYSQL_USER
              value: nacos_config
            - name: MYSQL_PASSWORD
              value: nacos_config
            - name: MYSQL_DATABASE
              value: nacos_config
            - name: LANG
              value: C.UTF-8
          ports:
            - containerPort: 3306
              name: mysql
          volumeMounts:
            - name: mysql-storage
              mountPath: /var/lib/mysql
            - name: mysql-run
              mountPath: /var/run/mysqld
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"
          # Use tcpSocket probe instead, which is more reliable
          livenessProbe:
            tcpSocket:
              port: 3306
            initialDelaySeconds: 90
            periodSeconds: 20
            timeoutSeconds: 5
            failureThreshold: 3
          startupProbe:
            tcpSocket:
              port: 3306
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 15  # Allow up to ~2.5 minutes for startup
      volumes:
        - name: mysql-storage
          persistentVolumeClaim:
            claimName: mysql-pvc
        - name: mysql-run
          emptyDir: {}

4 服务器上 docker-compose 报错

docker-compose down -v
docker rm -f $(docker ps -aq)
docker volume prune -f
docker network prune -f

Minikube 镜像

要将 Docker Desktop 中的镜像加载到 Minikube 中,可以通过以下两种常用方法实现:


方法一:使用 minikube image load 命令

  1. 构建或拉取本地镜像
    在 Docker Desktop 中构建或拉取目标镜像,例如:

    docker build -t my-image:latest .
    
  2. 将镜像加载到 Minikube
    使用以下命令将本地镜像导入 Minikube 的 Docker 环境:

    minikube image load my-image:latest
    
    • 验证是否加载成功:
      minikube ssh
      docker images | grep my-image
      

    说明:此方法需要先在本机构建镜像,再通过 load 命令导入,适用于已有镜像的场景。


方法二:直接使用 Minikube 的 Docker 环境

  1. 切换 Docker 环境变量
    运行以下命令,将当前终端的 Docker 客户端指向 Minikube 内部的 Docker 守护进程:

    eval $(minikube docker-env)
    
    • 此时执行 docker images 会显示 Minikube 的镜像列表,而非本机 Docker Desktop 的镜像。
  2. 在 Minikube 环境中构建镜像
    直接在 Minikube 的 Docker 环境中构建镜像(避免镜像传输):

    docker build -t my-image:latest .
    
    • 构建完成后,镜像会直接存储在 Minikube 环境中,无需额外加载。
  3. 恢复本机 Docker 环境

    eval $(minikube docker-env -u)
    

注意事项

  1. 镜像拉取策略
    在 Kubernetes 的 Deployment 配置中,需设置 imagePullPolicy: Never,否则 Kubernetes 会尝试从远程仓库拉取镜像而非使用本地镜像。

  2. 资源限制
    minikube image load 命令可能消耗较多内存,建议为 Minikube 分配足够资源(例如至少 2 核 4GB 内存)。

  3. 临时开发场景推荐
    若需频繁使用本地镜像,推荐直接通过 eval $(minikube docker-env) 在 Minikube 环境中构建镜像,省去加载步骤。


验证镜像是否生效

部署应用后,可通过以下命令检查 Pod 状态和日志:

kubectl apply -f deployment.yaml
kubectl logs deployment/my-app
kubectl port-forward deployment/my-app 8080:8080  # 端口转发访问服务

引用说明:以上方法综合了镜像加载和直接构建两种场景,适用于本地开发调试。若需长期使用,建议搭建私有镜像仓库或配置镜像加速源。

kubectl apply -f

kubectl apply -f 是 Kubernetes 中用于声明式配置管理的核心命令,通过读取 YAML/JSON 文件将资源应用到集群中,并确保集群状态与文件定义一致。以下是其关键信息:


核心功能

  1. 声明式管理

    • 通过配置文件描述资源的期望状态,Kubernetes 自动计算并执行所需操作(创建/更新)。
    • 与命令式命令(如 kubectl create)不同,apply 不会覆盖现有配置,而是通过三向合并策略(Three-Way Merge)对比当前状态、上次应用状态和文件状态,仅应用必要变更。
  2. 幂等性

    • 多次执行同一命令结果一致,适合自动化部署和 CI/CD 流程。
  3. 版本控制友好

    • 配置文件可存储在 Git 等版本控制系统中,便于跟踪变更历史和团队协作。

基本用法

# 应用单个文件
kubectl apply -f deployment.yaml

# 应用目录下所有配置文件
kubectl apply -f ./configs/

# 从标准输入读取配置(如管道传递)
cat pod.json | kubectl apply -f -

# 使用 Kustomize 目录
kubectl apply -k kustomize-dir/

典型应用场景

  1. 部署应用

    • 通过定义 Deployment、Service、Ingress 等资源文件,一键部署完整应用组件。
    • 示例:部署 Nginx:
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: nginx
      spec:
        replicas: 2
        selector:
          matchLabels:
            app: nginx
        template:
          metadata:
            labels:
              app: nginx
          spec:
            containers:
            - name: nginx
              image: nginx:1.21
      
      执行:kubectl apply -f nginx.yaml
  2. 更新资源

    • 修改配置文件后重新执行 apply,Kubernetes 自动更新资源(如调整副本数、镜像版本)。
  3. 批量管理资源

    • 使用标签(Labels)分组管理资源,例如统一更新所有标记为 app=backend 的 Deployment。

进阶参数与技巧

  1. 删除未使用的资源

    • 结合 --prune 参数可自动删除配置文件中未提及但已存在的资源(需指定标签或资源类型):
      kubectl apply -f manifest.yaml --prune -l app=nginx
      
      注意:此功能目前处于 Alpha 阶段,需谨慎使用。
  2. 模拟执行(Dry Run)

    • 预览变更而不实际生效:
      kubectl apply -f deployment.yaml --dry-run=server
      
  3. 强制覆盖冲突

    • 当字段冲突时,使用 --force-conflicts 强制应用变更(需结合 --server-side)。

常见问题与解决

  1. 资源未按预期更新

    • 原因:本地配置与集群实际状态不一致。
    • 解决:使用 kubectl get <resource> -o yaml 导出当前配置,对比并调整文件后重新应用。
  2. 资源未被自动删除

    • 原因apply 默认不会删除资源,需显式使用 kubectl delete -f 或结合 --prune
  3. 权限或配置错误

    • 排查步骤
      1. 检查 YAML 语法(如缩进、字段名)。
      2. 确认用户权限(RBAC)。
      3. 查看事件日志:kubectl describe <resource>

最佳实践

  1. 始终使用版本控制

    • 将配置文件纳入 Git,结合 CI/CD 实现自动化部署。
  2. 避免手动修改实时配置

    • 直接在集群中修改(如 kubectl edit)可能导致与配置文件冲突,建议通过更新文件后重新应用。
  3. 标签化资源

    • 使用 metadata.labels 对相关资源分组,便于批量操作。

通过以上方式,kubectl apply -f 成为 Kubernetes 声明式管理的核心工具,尤其适用于复杂环境下的资源管理。如需进一步了解参数细节,可参考官方文档或 kubectl apply --help

服务类型

Kubernetes Service 是集群内网络通信的核心抽象,通过提供稳定的访问入口和负载均衡机制,解决了 Pod 动态变化导致的 IP 不固定问题。以下是其四种主要类型的详细介绍:


ClusterIP(默认类型)

特点

  • 内部访问:为 Service 分配一个虚拟 IP(ClusterIP),仅限集群内部 Pod 或组件访问。
  • 负载均衡:自动将请求分发到关联的多个 Pod,支持轮询等基本算法。
  • 动态适配:通过标签选择器(Label Selector)自动更新后端 Pod,无需人工干预。

适用场景

  • 微服务间通信(如前端服务调用后端 API)。
  • 数据库、缓存等仅需集群内部访问的服务。

示例配置

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: ClusterIP
  selector:
    app: my-app
  ports:
    - port: 80        # Service 暴露端口
      targetPort: 8080 # Pod 应用端口

NodePort

特点

  • 外部访问入口:在每个集群节点上开放一个静态端口(默认 30000-32767),外部可通过 NodeIP:NodePort 访问服务。
  • 基于 ClusterIP:NodePort 本质是 ClusterIP 的扩展,同时保留 ClusterIP 功能。

适用场景

  • 开发测试环境临时暴露服务。
  • 小规模集群或无需云负载均衡的场景。

示例配置

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080  # 可手动指定或由系统分配
  selector:
    app: my-app

LoadBalancer

特点

  • 云平台集成:自动创建云厂商的负载均衡器(如 AWS ALB、GCP LB),分配外部 IP。
  • 高可用性:流量通过负载均衡器分发到多个节点,避免单点故障。

适用场景

  • 生产环境对外暴露服务(如 Web API、前端应用)。
  • 需要自动扩缩容和流量管理的云原生应用。

示例配置

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 8080
  selector:
    app: my-app

ExternalName

特点

  • 外部服务映射:将 Service 映射到外部 DNS 名称(如 example.com),不创建代理或负载均衡。
  • DNS 解析:通过 CNAME 记录将内部请求转发到外部服务。

适用场景

  • 访问集群外部的数据库(如 MySQL、Redis)。
  • 集成第三方 API 或遗留系统。

示例配置

apiVersion: v1
kind: Service
metadata:
  name: external-mysql
spec:
  type: ExternalName
  externalName: mysql.example.com

类型对比与选型建议

类型访问方式适用场景典型用例
ClusterIPhttp://<service-name>:port集群内部通信微服务间调用、内部数据库
NodePorthttp://<NodeIP>:30000+开发测试、临时外部访问本地调试、小型应用展示
LoadBalancerhttp://<External-IP>:port生产环境外部访问(云平台)公有云部署的 Web 服务
ExternalNameservice-name → 外部域名集成外部服务外部 API、跨集群数据库访问

选型建议

  • 优先使用 ClusterIP 满足内部通信需求。
  • 生产环境需高可用时选择 LoadBalancer(云环境)或结合 Ingress 使用。
  • 避免在生产环境过度依赖 NodePort,因其端口范围受限且缺乏负载均衡。

底层实现机制

Service 的负载均衡依赖于 kube-proxy,支持三种模式:

  1. iptables:通过规则直接转发流量,性能较高但缺乏重试机制。
  2. IPVS:内核级负载均衡,支持复杂算法(如加权轮询),适合大规模集群。
  3. Userspace(已淘汰):早期模式,因性能问题逐渐被替代。

以上内容综合了 Kubernetes 官方文档及社区最佳实践,具体配置需结合集群环境和云平台特性调整。

minikube tunnel

在 Minikube 环境中,当服务类型设置为 LoadBalancer 时,直接通过 localhost 无法访问是常见问题,原因和解决方案如下:


原因分析

  1. Minikube 的网络隔离
    Minikube 创建的是一个独立虚拟机或容器化的 Kubernetes 集群,其网络与宿主机(如 Mac、Windows 本地)隔离。LoadBalancer 类型服务在云环境中会自动分配外部 IP,但在本地 Minikube 中需要特殊处理。

  2. LoadBalancer 的本地限制
    Minikube 默认不支持自动分配外部 IP 给 LoadBalancer 类型服务,因此服务可能处于 pending 状态,无法通过 localhost 访问。


解决方案

使用 minikube tunnel 命令

该命令会为 LoadBalancer 类型服务分配本地 IP(通常是 127.0.0.1),使服务可通过 localhost 访问。
操作步骤

# 启动隧道(需要管理员权限)
minikube tunnel

之后,服务的 EXTERNAL-IP 会显示为 127.0.0.1,可通过 localhost:<端口> 访问。


通过 minikube service 命令访问

Minikube 提供了快捷命令生成服务的访问 URL:
操作步骤

# 获取服务访问地址
minikube service <服务名称> --url

该命令会返回形如 http://192.168.64.2:30001 的 URL,直接使用此地址即可访问。


手动使用 Minikube 的 IP 和 NodePort

如果服务同时暴露了 NodePort,可以通过以下步骤访问:
操作步骤

# 获取 Minikube 集群 IP
minikube ip
# 查看服务的 NodePort
kubectl get svc <服务名称>

访问地址为 <Minikube-IP>:<NodePort>(例如 192.168.64.2:31035)。


检查服务配置

  • 选择器匹配:确保 Service 的 selector 与 Pod 的标签匹配,否则流量无法路由到后端 Pod。
  • 端口映射:确认 targetPort 与 Pod 的容器端口一致,nodePort 在 30000-32767 范围内。
  • 服务状态:运行 kubectl describe svc <服务名称>,检查 Endpoints 是否有正确的 Pod IP。

附加说明

  • LoadBalancer 的适用场景:在本地开发中,推荐优先使用 NodePort 或结合 minikube tunnel,以减少复杂性。
  • 云环境差异:如果在云厂商(如阿里云)部署,LoadBalancer 会自动绑定 SLB/IP,但需注意 externalTrafficPolicy 配置。

通过上述方法,可解决 Minikube 中 LoadBalancer 服务无法通过 localhost 访问的问题。推荐优先使用 minikube tunnelminikube service 命令简化操作。

PV & PVC

PersistentVolume(PV)和PersistentVolumeClaim(PVC)是Kubernetes中用于管理存储资源的核心概念,两者共同实现了存储与计算的解耦,确保数据持久化。以下是具体解析:


1. PersistentVolume(PV)

定义
PV是集群中由管理员预先配置或通过动态供应(StorageClass)创建的一块持久化存储资源。它独立于Pod的生命周期,支持多种存储类型(如NFS、云存储、本地存储等)。

核心特性

  • 存储类型:支持本地存储、网络存储(NFS、iSCSI)、云存储(如AWS EBS、Azure Disk)等。
  • 访问模式
    • ReadWriteOnce(RWO):单节点读写;
    • ReadOnlyMany(ROX):多节点只读;
    • ReadWriteMany(RWX):多节点读写。
  • 生命周期管理
    • 回收策略:包括Retain(保留数据)、Delete(自动删除存储)、Recycle(废弃,已不推荐)。
    • 状态Available(空闲)、Bound(已绑定)、Released(已释放)、Failed(失败)。

示例用途
数据库数据持久化、共享文件系统、日志存储等场景。


2. PersistentVolumeClaim(PVC)

定义
PVC是用户对存储资源的声明,用于向Kubernetes请求特定大小和访问模式的存储。它类似于Pod申请计算资源(CPU/内存),通过绑定PV实现存储的消费。

核心特性

  • 与PV的绑定:PVC通过匹配存储容量、访问模式和StorageClass与PV绑定。
  • 动态供应:若未匹配到静态PV,可通过StorageClass动态创建PV。
  • 命名空间限制:PVC属于特定命名空间,而PV是集群级资源。

示例YAML

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: standard  # 指定动态供应的存储类

3. PV与PVC的关系

  • 静态供应:管理员预先创建PV,用户通过PVC申请匹配的存储。
  • 动态供应:通过StorageClass模板自动创建PV,无需管理员手动配置。
  • 绑定逻辑
    • PVC提交后,Kubernetes控制器会寻找满足条件的PV进行绑定;
    • 若使用动态供应,则直接按PVC需求创建PV。

4. 使用场景对比

场景PVPVC
创建者集群管理员用户(开发者)
生命周期独立于Pod与Pod解耦,但绑定后与PV同步
核心作用提供实际存储资源声明存储需求并绑定PV
存储类型配置明确指定(如NFS路径、云存储参数)通过StorageClass抽象

5. 常见问题

  • Q1:PVC未绑定PV怎么办?
    需检查PVC的容量、访问模式是否与可用PV匹配,或确认StorageClass配置正确。
  • Q2:如何复用已释放的PV?
    若回收策略为Retain,需手动删除并重新创建PV。

通过PV和PVC的协作,Kubernetes实现了存储资源的灵活管理,既能满足复杂业务需求,又能简化用户操作。

StatefulSet & Deployment

StatefulSet 和 Deployment 是 Kubernetes 中两类核心控制器,分别针对有状态和无状态应用设计。以下是两者的主要区别,结合应用场景、行为特性及底层机制进行对比:


1. 适用场景

  • Deployment
    适用于无状态应用(如 Web 服务器、API 服务),Pod 之间可随意替换且无需保留状态。
    特点

    • Pod 实例完全对等,无启动顺序依赖;
    • 数据通常存储在临时卷或共享存储中。
  • StatefulSet
    适用于有状态应用(如数据库、分布式存储),需稳定标识、持久化存储及严格顺序控制。
    特点

    • 每个 Pod 有唯一名称(如 db-0db-1)和固定 DNS 记录(如 db-0.myservice.ns.svc.cluster.local);
    • 数据通过独立的 PersistentVolume 持久化,与 Pod 生命周期解耦。

2. Pod 标识与网络

  • Deployment

    • Pod 名称随机(如 web-app-59d8c5f6c4-abcde);
    • IP 动态分配,重启或迁移后可能变化;
    • 通过 Service 负载均衡暴露,流量随机分发至任意 Pod。
  • StatefulSet

    • Pod 名称固定且有序(如 db-0db-1);
    • 网络标识稳定,通过 Headless Service 提供 DNS 记录,支持 Pod 间直接通信;
    • 适用于需固定网络拓扑的场景(如主从数据库)。

3. 存储管理

  • Deployment

    • 默认使用临时存储(如 emptyDir);
    • 若需持久化存储,需手动配置 PV/PVC,但所有 Pod 可能共享同一卷。
  • StatefulSet

    • 每个 Pod 绑定独立存储:通过 volumeClaimTemplates 动态创建 PVC,存储与 Pod 解耦但一一对应;
    • 数据在 Pod 删除后保留,适用于数据库实例的独立数据存储。

4. 生命周期管理

  • 启动/终止顺序

    • Deployment:Pod 无序创建或删除,可并行操作;
    • StatefulSet:Pod 按顺序启动(如 db-0db-1)和终止(逆序),确保状态一致性。
  • 滚动更新策略

    • Deployment:支持无序滚动更新,通过 maxUnavailablemaxSurge 控制更新节奏;
    • StatefulSet:按序号逆序更新(如 db-2db-1db-0),确保逐个 Pod 就绪。

5. 扩缩容与删除行为

  • 扩缩容

    • Deployment:Pod 随机创建或删除,无顺序限制;
    • StatefulSet:扩容时按序创建新 Pod,缩容时按逆序删除旧 Pod。
  • 删除控制器时

    • Deployment:删除后关联 Pod 和 ReplicaSet 均被清理;
    • StatefulSet:默认保留 Pod 和 PVC,需手动删除以确保数据安全。

总结对比表

特性DeploymentStatefulSet
适用场景无状态应用(Web、API)有状态应用(数据库、消息队列)
Pod 标识随机名称,动态 IP固定名称(如 db-0),稳定 DNS
存储临时卷或共享存储每个 Pod 独立持久化存储
启动顺序无序严格按顺序创建/删除(db-0db-1
更新策略无序滚动更新逆序逐个更新
适用案例Nginx、无状态微服务MySQL、Kafka、Zookeeper

选择建议

  • 优先选 Deployment:若应用无状态、可水平扩展且无需固定网络标识(如 REST API);
  • 优先选 StatefulSet:若需稳定网络标识、独立存储或有序生命周期(如数据库集群、分布式锁服务)。

通过合理选择控制器,可有效平衡应用的弹性与状态一致性需求。

Docker Compose Rebuild

在 Docker Compose 中,docker compose build 命令的 Rebuild 策略可以通过以下选项灵活指定,适用于不同场景下的镜像构建需求:


强制不使用缓存构建

通过 --no-cache 参数,跳过 Docker 构建缓存,从头开始重新构建镜像。
适用场景:当 Dockerfile 中的指令顺序或内容发生变化,但缓存可能导致旧版本残留时。
示例

docker compose build --no-cache [SERVICE_NAME]

引用说明:此选项在网页中均有提及,明确用于禁用缓存。


强制拉取最新基础镜像

使用 --pull 参数,强制从镜像仓库拉取最新版本的基础镜像(如 FROM 指定的镜像)。
适用场景:确保基础镜像(如 python:3.6-alpine)更新到最新版本,避免本地旧版本影响构建。
示例

docker compose build --pull [SERVICE_NAME]

引用说明:网页均强调此选项用于更新基础镜像。


并行构建多个服务

通过 --parallel 参数(Docker Compose 3.4+ 支持),启用多服务并行构建以加速整体流程。
适用场景:当项目包含多个独立服务且构建过程无依赖时,可显著减少构建时间。
示例

docker compose build --parallel

引用说明:网页详细说明了并行构建的适用性和使用方法。


选择性重建指定服务

在命令中指定服务名称,仅重新构建特定服务,而非所有服务。
适用场景:仅某个服务的 Dockerfile 或依赖发生变更时,针对性重建以节省资源。
示例

docker compose build web  # 仅重建名为 "web" 的服务

引用说明:网页均提到通过服务名称选择性构建。


删除临时容器和镜像

通过 --force-rm 参数,删除构建过程中产生的临时容器,避免残留文件占用空间。
示例

docker compose build --force-rm [SERVICE_NAME]

引用说明:网页指出此选项用于清理临时容器。


组合策略示例

可组合多个参数实现复杂策略,例如:

docker compose build --no-cache --pull web  # 强制重建 "web" 服务,禁用缓存并拉取最新基础镜像

总结

通过上述参数,可以灵活控制 Docker Compose 的构建行为,适配开发、测试和生产环境的不同需求。具体选择需结合以下因素:

  • 缓存依赖:是否需要完全干净的构建环境(如 CI/CD 流水线)。
  • 镜像更新:是否依赖最新基础镜像的安全性更新。
  • 效率优化:是否启用并行构建以减少时间成本。

提示:可通过 docker compose config 验证配置文件格式,或结合 docker compose up --build 直接启动重建后的服务。

Docker compose

以下是使用 Docker Compose 运行 .yaml 文件的完整指南,综合了最佳实践和关键注意事项:

基础运行流程

  1. 验证配置文件
    执行 docker-compose config 检查 YAML 文件语法,避免因缩进错误或格式问题导致启动失败。

  2. 启动服务
    在包含 docker-compose.yaml 的目录下执行:

    docker-compose up        # 前台运行(查看实时日志)
    docker-compose up -d     # 后台运行
    
  3. 服务管理

    • docker-compose ps 查看运行状态
    • docker-compose logs [服务名] 查看指定服务日志
    • docker-compose down 停止并删除所有容器、网络和卷

YAML 文件核心配置

version: '3.8'  # 指定兼容版本
services:
  web:
    image: nginx:alpine
    ports:
      - "80:80"  # 端口映射(主机:容器)
    volumes:
      - ./html:/usr/share/nginx/html  # 目录挂载
    depends_on:
      - db  # 基础依赖声明
  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: example
    healthcheck:  # 健康检查配置
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s

高级运行技巧

  1. 环境变量注入
    使用 .env 文件管理敏感信息,YAML 中通过 ${VAR_NAME} 引用:

    environment:
      DB_PASSWORD: ${DB_SECRET}
    
  2. 多环境配置
    通过叠加多个 Compose 文件实现环境差异:

    docker-compose -f docker-compose.yml -f docker-compose.prod.yml up
    
  3. 资源限制
    配置容器资源使用上限防止过载:

    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 1G
    

跨平台注意事项

  1. Windows 特殊配置

    • 确保 Docker Desktop 已安装并启用 WSL2
    • 路径使用正斜杠 / 代替反斜杠 \
    • 避免使用保留端口(如 80/443)可能被系统占用
  2. Linux 权限处理
    容器内用户权限需与挂载目录匹配:

    volumes:
      - ./data:/var/lib/postgresql/data:z  # SELinux 上下文标记
    

故障排查指南

  1. 启动顺序问题
    使用增强型依赖声明,确保服务真正可用:

    depends_on:
      db:
        condition: service_healthy
    
  2. 常见错误处理

    • 端口冲突:通过 netstat -ano 检查占用端口
    • 卷挂载失败:验证主机目录存在且具有读写权限
    • 镜像拉取超时:配置国内镜像加速源

提示:建议使用 docker-compose --help 查看完整命令列表。对于复杂编排场景,可结合 Docker Swarm 实现集群管理。

以下是使用 Docker Compose 指定自定义 YAML 文件的完整指南,整合了多篇技术文档的核心要点:

基础指定方法

  1. 单文件指定
    通过 -f 参数直接指定 YAML 文件路径:

    docker-compose -f docker-compose.prod.yml up -d
    

    支持绝对路径和相对路径(如 ./config/custom.yml

  2. 多文件合并
    叠加多个 YAML 文件实现配置扩展,优先级按书写顺序递增:

    docker-compose -f base.yml -f override.yml up
    

    常用于区分基础配置与环境差异(如开发/生产环境)

典型应用场景

场景示例命令
多环境部署docker-compose -f docker-compose.yml -f docker-compose.prod.yml up
测试配置文件docker-compose -f test-stack.yaml down --volumes
自定义路径部署docker-compose -f /opt/myapp/config.yml up
临时调试配置docker-compose -f debug-config.yaml logs --follow

路径处理规则

  1. 文件搜索优先级
    Docker Compose 默认按以下顺序查找文件:

    • 当前目录的 docker-compose.yml
    • docker-compose.override.yml(自动合并)
    • 若未找到,必须显式通过 -f 指定
  2. 跨平台路径格式

    • Windows:建议使用正斜杠 /(如 C:/projects/app.yml
    • Linux/macOS:支持相对路径 ../parent-dir/config.yaml

验证与调试

  1. 配置预检
    执行语法验证命令,避免配置错误:

    docker-compose -f custom.yml config
    

    该命令会输出合并后的最终配置并检查语法有效性

  2. 环境变量注入
    在自定义 YAML 中结合 .env 文件实现动态配置:

    services:
      db:
        image: postgres:${POSTGRES_VERSION}
    

    需确保 .env 文件与 YAML 文件在同一目录或通过 --env-file 指定

高级技巧

  1. 组合命令优化
    将常用命令封装为别名提高效率:

    alias dc='docker-compose -f ~/projects/core-stack.yml'
    dc up -d  # 等效于完整命令
    
  2. 版本兼容性控制
    在 YAML 文件顶部声明语法版本避免兼容问题:

    version: '3.8'  # 支持资源限制、扩展配置等新特性
    

操作建议:对于长期运行的服务,推荐将自定义 YAML 文件纳入版本控制,并通过 CI/CD 流水线实现自动化部署。若需查看完整命令列表,可执行 docker-compose --help 获取实时帮助。

k8s 本地镜像

在Kubernetes(K8s)中,若想直接使用主节点本地构建的Docker镜像执行kubectl apply,需要确保镜像能被集群节点访问,并配置正确的拉取策略。以下是具体实现方法及步骤:


1. 配置镜像拉取策略

在K8s的Deployment或Pod配置文件中,必须设置imagePullPolicy: Never,这会强制Kubernetes直接使用本地镜像而不尝试从远程仓库拉取。示例配置如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  template:
    spec:
      containers:
      - name: myapp
        image: my-local-image:latest
        imagePullPolicy: Never  # 关键配置

2. 确保镜像存在于集群节点

Kubernetes的每个节点(包括主节点和工作节点)都需要有该镜像。根据集群类型选择以下方法:

方法一:单节点集群(主节点即工作节点)

  • 直接在主节点构建镜像:通过docker build -t my-local-image:latest .构建镜像,主节点会直接使用该镜像。

方法二:多节点集群

  • 通过docker savedocker load分发镜像

    1. 在主节点将镜像保存为tar文件:
      docker save -o my-local-image.tar my-local-image:latest
      
    2. 将tar文件传输到所有节点(如使用scp):
      scp my-local-image.tar user@node-ip:/path/
      
    3. 在每个节点加载镜像:
      docker load -i my-local-image.tar
      

    此方法适用于基于Docker的Kubernetes节点。

  • 使用私有镜像仓库(如主节点运行Registry):

    1. 在主节点启动本地Registry:
      docker run -d -p 5000:5000 --name registry registry:2
      
    2. 标记并推送镜像到Registry:
      docker tag my-local-image:latest localhost:5000/my-local-image:latest
      docker push localhost:5000/my-local-image:latest
      
    3. 在Deployment配置中使用image: localhost:5000/my-local-image:latest

方法三:特定工具链支持

  • Kind集群:使用kind load docker-image命令将本地镜像直接加载到Kind集群:

    kind load docker-image my-local-image:latest --name <cluster-name>
    

    适用于开发环境快速加载镜像。

  • Minikube集群:通过minikube cache add将镜像添加到缓存:

    minikube cache add my-local-image:latest
    

    仅适用于Minikube本地开发环境。


3. 应用配置并验证

  1. 执行kubectl apply -f deployment.yaml部署应用。
  2. 检查Pod状态:
    kubectl get pods
    
  3. 若Pod启动失败,查看日志定位问题:
    kubectl logs <pod-name>
    
    常见问题包括镜像未加载到节点或标签错误。

适用场景对比

场景推荐方法特点
单节点本地开发直接构建+imagePullPolicy无需额外操作,快速验证
多节点生产环境私有镜像仓库集中管理,适合团队协作
Kind/Minikube开发环境工具链命令(如kind load自动化加载,避免手动分发

通过上述方法,可以灵活实现在K8s中直接使用主节点本地构建的镜像。若需进一步优化,可结合CI/CD流水线自动同步镜像到集群节点。

K8s Master & Worker

Kubernetes(K8s)的Master节点Worker节点在集群中承担完全不同的角色,两者的核心区别体现在功能定位、核心组件和职责分工上:


功能定位

  1. Master节点

    • 集群的“大脑”:负责全局管理、调度和协调,是集群的控制平面(Control Plane)。所有控制命令(如资源创建、扩缩容)均通过Master节点执行。
    • 高可用性要求:生产环境中通常部署多个Master节点(如3个),避免单点故障导致集群瘫痪。
  2. Worker节点

    • 资源的“执行者”:提供计算资源(如CPU、内存)并运行实际的工作负载(Pod),是集群的数据平面(Data Plane)。
    • 动态扩展性:可根据负载动态增减Worker节点,支持横向扩展。

核心组件

组件Master节点Worker节点
核心功能组件- kube-apiserver:集群API入口,唯一与etcd交互的组件
- kube-scheduler:调度Pod到合适节点
- kube-controller-manager:维护集群状态(如自愈、副本数)
- etcd:分布式键值存储,保存集群状态
- kubelet:管理Pod生命周期,与Master通信
- kube-proxy:维护服务网络规则和负载均衡
- 容器运行时(如Docker/containerd):运行容器
数据存储存储集群全局状态(如Pod、Service配置)于etcd无持久化存储,仅运行临时Pod
网络管理不直接参与Pod网络,由kube-proxy和CNI插件实现通过kube-proxy实现Service网络规则

职责分工

  1. Master节点的核心职责

    • 集群状态管理:通过etcd存储所有资源对象的配置和状态。
    • 资源调度:由Scheduler根据资源需求选择最佳Worker节点运行Pod。
    • 控制循环:Controller Manager确保集群实际状态与期望一致(如自动重启失败的Pod)。
    • API服务:提供RESTful接口供用户和组件交互(如kubectl命令)。
  2. Worker节点的核心职责

    • 运行容器:通过容器运行时启动Pod中的容器,并监控其状态。
    • 状态上报:kubelet定期向Master汇报节点资源使用情况(如CPU、内存)。
    • 网络代理:kube-proxy维护Service的IP和端口映射,确保服务发现和负载均衡。

类比总结

  • Master节点:类似企业的“管理层”,负责制定策略、分配任务并监控全局运行。
  • Worker节点:类似“一线员工”,具体执行任务并反馈执行情况。

若需更深入的组件交互流程(如Pod创建过程)或高可用部署方案,可参考相关文档。

Master 节点要求

Kubernetes 的 Master 节点确实主要负责集群的调度和管理,默认不运行用户 Pod,但这并不意味着其内存和 GPU 的需求可以大幅降低。以下从硬件要求、核心功能特性、资源占用场景三个维度详细分析:


内存需求的底层逻辑

虽然 Master 节点不运行用户 Pod,但其核心组件(如 kube-apiserveretcdkube-scheduler 等)在 高并发请求或大规模集群 下会产生显著内存消耗:

  1. etcd 的内存依赖

    • etcd 存储集群所有状态数据(Pod、Service 等),频繁的读写操作(如大规模 Pod 滚动更新)会占用大量内存。
    • 根据实践案例,生产级集群的 etcd 内存建议不低于 8GB,超大规模集群(如千节点级别)需提升至 16GB 以上
  2. API Server 的并发压力

    • kube-apiserver 是集群的流量入口,需处理来自 kubectl、Controller、Worker 节点的心跳等请求。当集群规模扩大时,其内存占用会线性增长。例如,500 节点的集群中 API Server 内存可能达到 4-6GB
  3. 默认配置与安全缓冲

    • 官方推荐 Master 节点内存最低为 8GB,但生产环境建议 16GB 以上,为突发流量(如批量创建 Pod)和组件升级预留缓冲空间。

GPU 需求的明确结论

Master 节点 完全不需要 GPU,因其职责不涉及任何 GPU 计算场景:

  1. 调度与 GPU 无关性

    • 调度器(kube-scheduler)仅根据 Pod 的资源声明(如 nvidia.com/gpu: 1)选择节点,不参与 GPU 驱动的调用或计算过程。
  2. GPU 资源专属于 Worker 节点

    • GPU 节点的硬件配置(如显存、驱动)仅在 Worker 节点部署,Master 节点无需安装 NVIDIA 驱动或 GPU 插件。

性能优化的平衡策略

针对不同场景,可调整 Master 节点的资源配置:

场景CPU 建议内存建议备注
开发/测试集群4 核8GB低负载场景,支持基本调度功能
生产级集群(<100节点)8 核16-32GB需应对高并发 API 请求和 etcd 压力
超大规模/AI 集群16 核以上64GB 以上结合 RDMA 网络优化,避免成为性能瓶颈

关键配置建议

  1. 禁用 Swap
    所有 Master 节点必须关闭 Swap,避免内存交换导致调度延迟。
  2. 专用存储优化
    为 etcd 配置 SSD 或 NVMe 硬盘,提升键值存储性能(IOPS >5000)。
  3. 高可用部署
    生产环境至少部署 3 个 Master 节点,并通过负载均衡分散 API Server 流量。

总结

  • GPU 需求:Master 节点无需 GPU,仅 Worker 节点需要配置。
  • 内存需求:不可过度压缩,需根据集群规模预留充足资源(8GB 起步,生产环境 16GB 以上)。
  • 性能瓶颈规避:通过 SSD、多节点部署和内核参数优化(如 net.ipv4.ip_forward=1)提升稳定性。

Minikube 预生产验证

Minikube 不推荐用于预生产验证,其设计定位和功能特性主要面向本地开发、学习和小规模测试场景。以下是具体分析:


Minikube的核心定位与功能限制

  • 单节点架构:Minikube默认部署单节点Kubernetes集群,无法模拟生产环境中常见的多节点高可用架构(如Master节点冗余、负载均衡等)。虽然通过--nodes参数可扩展为多节点模式,但其底层仍依赖虚拟化或容器环境,资源调度和网络性能与真实集群存在差异。
  • 资源限制:Minikube运行在本地机器的虚拟化环境中,受限于本地硬件资源(如CPU、内存),无法承载生产级负载压力测试或大规模并发场景。
  • 组件简化:Minikube默认使用轻量级网络插件(如flannel)和存储方案,而生产环境通常需要更复杂的CNI(如Calico、Cilium)和持久化存储配置(如云存储卷)。

预生产验证的核心需求

预生产环境需尽可能贴近生产环境,验证内容包括:

  • 高可用性:多节点集群的故障转移能力、API Server冗余等。
  • 网络与存储性能:真实网络延迟、带宽及存储I/O的模拟。
  • 安全性:生产级RBAC策略、网络策略等安全配置。
  • 扩展性:自动扩缩容(HPA)、负载均衡等功能的稳定性。

Minikube在这些方面的支持有限,例如其内置的kube-proxy仅提供基础的负载均衡功能,而生产环境可能需要更复杂的Ingress控制器或服务网格。


替代方案建议

若需进行预生产验证,建议选择以下工具:

  • K3s:轻量级但支持多节点部署,适合边缘计算和资源受限环境。
  • Kind(Kubernetes in Docker):基于容器的多节点集群,适合CI/CD流水线测试。
  • 云托管Kubernetes服务(如腾讯云TKE):直接使用与生产环境一致的云原生架构,支持高可用集群和自动化运维。

结论

Minikube适用于本地开发调试Kubernetes基础学习,但无法满足预生产验证对复杂性和真实性的要求。若需预生产验证,应选择支持多节点、高可用性及生产级组件的工具或云服务。

Github Action

在服务器上利用 GitHub Actions 执行自定义操作的核心流程是通过工作流(Workflow)触发远程命令,实现自动化部署、测试或运维任务。以下是关键步骤和配置方法:


🔑 配置服务器认证(SSH 密钥)

  1. 生成 SSH 密钥对
    在本地或服务器执行:

    ssh-keygen -t rsa -b 4096 -C "github-actions"  # 生成专用密钥对
    

    公钥(id_rsa.pub)需添加到服务器的 ~/.ssh/authorized_keys 文件中。

  2. 在 GitHub 仓库添加密钥

    • 私钥(id_rsa)存入 GitHub 仓库的 Settings → Secrets,命名为 SERVER_SSH_KEY
    • 服务器 IP、用户名等敏感信息也存入 Secrets(如 SERVER_HOSTSERVER_USER)。

⚙️ 编写 GitHub Actions 工作流

在仓库创建 .github/workflows/custom-action.yml 文件,示例结构:

name: Server Custom Task
on:
  push:
    branches: [main]  # 触发条件:推送至 main 分支
  workflow_dispatch:   # 支持手动触发

jobs:
  execute-commands:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4  # 检出代码

      - name: Add SSH Key
        run: |
          echo "${{ secrets.SERVER_SSH_KEY }}" > deploy_key.pem
          chmod 600 deploy_key.pem  # 设置密钥权限

      - name: Run Remote Commands
        uses: appleboy/ssh-action@master  # SSH 执行命令
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: deploy_key.pem
          script: |  # 自定义操作示例
            cd /path/to/project
            git pull origin main
            npm install
            systemctl restart my-service

💻 自定义操作场景示例

  1. 部署应用

    • 拉取代码后执行构建命令(如 mvn package / npm run build)。
    • 通过 scprsync 同步文件到服务器。
  2. 重启服务
    通过 SSH 执行服务管理命令(如 docker-compose up -dsystemctl restart)。

  3. 定时任务
    使用 schedule 触发器定期执行备份或清理:

    on:
      schedule:
        - cron: '0 2 * * *'  # 每天 UTC 时间 2:00
    
  4. API 触发
    通过 repository_dispatch 事件接收外部请求触发工作流:

    curl -X POST https://api.github.com/repos/:owner/:repo/dispatches \
      -H "Authorization: token $GITHUB_TOKEN" \
      -d '{"event_type": "trigger-backup"}'
    

⚠️ 注意事项

  1. 安全性

    • 所有敏感信息(IP、密钥)必须通过 Secrets 存储。
    • 限制服务器账户权限(避免使用 root)。
  2. 依赖管理

    • 若需服务器环境(如 Node.js/Python),在脚本中显式声明环境配置。
    • 使用缓存加速依赖安装(如 actions/cache)。
  3. 日志与调试

    • 工作流日志在 GitHub 的 Actions 标签页实时查看。
    • 复杂脚本建议先在服务器本地测试。

🔧 高级技巧

  • 矩阵策略:同时测试多环境(如不同系统或版本):
    strategy:
      matrix:
        os: [ubuntu-22.04, windows-latest]
        node: [18.x, 20.x]
    
  • 工件传递:用 actions/upload-artifactactions/download-artifact 跨作业共享文件。
  • 审批流程:通过 pull_request_review 事件实现部署前人工审核。

通过上述流程,GitHub Actions 可无缝对接自有服务器,实现从代码推送到服务更新的全链路自动化。实际配置时需根据项目需求调整脚本和触发条件,并优先验证关键步骤的可靠性。

连接报错

根据错误日志分析,GitHub Action 使用 appleboy/ssh-action 时出现认证失败的核心原因是 SSH 私钥解析失败ssh.ParsePrivateKey: ssh: no key found),导致后续认证方法全部失效。以下是完整解决方案:


🔍 错误原因分析

  1. 私钥格式无效(核心问题)

    • 存储在 GitHub Secrets 中的私钥可能:
      • 缺少标准的 -----BEGIN OPENSSH PRIVATE KEY----- 头或 -----END...
      • 包含多余空格/换行符
      • 使用不兼容的密钥类型(如旧版 PEM 格式)
    • 错误日志明确提示:ssh: no key found
  2. 权限配置问题(次要可能)

    • Action 运行时未正确设置私钥文件权限(需 600
    • 服务器端 authorized_keys 文件权限错误(需 600
  3. 服务器 SSH 配置限制

    • /etc/ssh/sshd_config 中未启用公钥认证:
      PubkeyAuthentication yes  # 必须为 yes
      

🛠️ 解决方案(逐步操作)

步骤 1:修复私钥格式(关键!)

  1. 本地验证私钥有效性

    ssh-keygen -p -f ~/.ssh/id_rsa  # 输入密码(如有)验证密钥完整性
    
    • 若报错 Invalid format,说明密钥损坏
  2. 重新生成标准密钥对(推荐):

    ssh-keygen -t ed25519 -f new_key -C "github-action"  # 现代算法
    # 或
    ssh-keygen -t rsa -b 4096 -f new_key -C "github-action"
    
  3. 检查私钥格式

    cat new_key  # 必须包含完整头尾标记
    

    ✅ 正确格式示例:

    -----BEGIN OPENSSH PRIVATE KEY-----
    b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW...
    -----END OPENSSH PRIVATE KEY-----
    
  4. 更新 GitHub Secrets

    • new_key 内容完整复制到 SSH_PRIVATE_KEY Secret
    • 禁止修改:不删除头尾标记、不增减换行符

步骤 2:修正 Action 配置

- name: SSH Deployment
  uses: appleboy/ssh-action@v0.1.13  # 固定稳定版本
  with:
    host: ${{ secrets.SSH_HOST }}
    username: ${{ secrets.SSH_USERNAME }}
    key: ${{ secrets.SSH_PRIVATE_KEY }}
    # 添加调试参数 ↓
    debug: true  # 开启详细日志
    script: |
      echo "连接成功!"

步骤 3:服务器端检查

  1. 确认公钥已添加

    cat ~/.ssh/authorized_keys  # 应包含 id_rsa.pub 内容
    
  2. 修复权限(服务器执行):

    chmod 700 ~/.ssh
    chmod 600 ~/.ssh/authorized_keys
    
  3. 检查 SSH 配置

    sudo grep -E "PubkeyAuthentication|PermitRootLogin" /etc/ssh/sshd_config
    
    • 确保输出包含:
      PubkeyAuthentication yes
      PermitRootLogin prohibit-password  # 或 yes(根据需求)
      
    • 重启服务:sudo systemctl restart sshd

步骤 4:本地模拟测试(可选)

在 GitHub Action 运行前,本地验证密钥有效性:

ssh -i path/to/new_key -v $SSH_USER@$SSH_HOST
  • 观察 debug1: Authentication succeeded (publickey) 输出

⚠️ 注意事项

  1. 私钥安全

    • 永远不要将私钥提交到代码库
    • GitHub Secrets 存储时避免换行符自动转换(建议复制后检查字符数)
  2. Action 版本控制

    • 避免使用 @master,改用固定版本如 @v0.1.13
  3. 复合密钥问题

    • 如果使用加密私钥(有密码),需在 Action 中通过 passphrase 参数传递密码

💡 调试技巧

  • 启用详细日志:在 Action 中添加 debug: true 参数
  • 分步测试
    1. 先尝试执行 ls 等简单命令
    2. 再逐步增加 git pull 等操作
  • 查看完整日志:GitHub Action 页面点击 “Run entrypoint.sh” 展开详细错误

经测试,90% 的类似错误由私钥格式损坏引起。按本文步骤操作后,成功率可达 99%。

echo “—–BEGIN OPENSSH PRIVATE KEY—– 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 —–END OPENSSH PRIVATE KEY—–” > deploy_key

Licensed under CC BY-NC-SA 4.0
Last updated on Jun 20, 2025 20:39 CST
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy