• pod

    webapp # WEB服务
    nodemaintainer # 系统管理
  • 执行方式

    # webapp通过调用k8s的api在nodemaintainer上通过nsenter执行宿主机命令
    nsenter --target 1 --mount --uts --ipc --net --pid -- <cmd>
  • 遇到的问题和解决

    1. nsenter执行python命令无法加载宿主机的环境变量,而是直接用nodemaintainer中的环境变量

      解决方法:通过增加中间shell脚本handle.sh加载环境变量来执行,通过base64加密来解决管道符等字符问题

      #!/bin/bash
      if [ -f /etc/profile.d/base.sh ]; then
          source /etc/profile.d/base.sh >/dev/null 2>&1
      fi
      
      ACTION="$1"
      shift
      
      case "$ACTION" in
        execute_cmd_base64)
          if [ -z "$1" ]; then
              echo "[ERROR] No encoded command provided"
              exit 1
          fi
      
          ENCODED_CMD="$1"
      
          echo "$ENCODED_CMD" | base64 -d | bash
          EXIT_CODE=$?
      
          if [ $EXIT_CODE -ne 0 ]; then
              exit $EXIT_CODE
          fi
          ;;
      
        *)
          cat << 'EOF'
      Usage:
        execute_cmd_base64 <cmd_base64_str>
          Execute base64 encoded command
      
      Examples:
        1. Execute encoded command:
           # Encode your command first:
           #   echo 'echo "Current user: $USER" && echo "Today is $(date \"+%Y-%m-%d\")"' | base64
           #
           # Then execute:
           handle.sh execute_cmd_base64 'ZWNobyAiQ3VycmVudCB1c2VyOiAkVVNFUiIgJiYgZWNobyAiVG9kYXkgaXMgJChkYXRlICcrJVktJW0tJWQnKSI='
      EOF
      
          exit 1
          ;;
      esac
      
    2. ssh等需要穿透的情况也可能执行不成功,如:/etc/kubeasz/ezctl kca-renew default这种通过ansible操作,实际上执行ssh操作的情况ansible-playbook -i clusters/default/hosts -e @clusters/default/config.yml -e CHANGE_CA=true playbooks/96.update-certs.yml -t force_change_certs

      排查: 通过问题1中方法也是无法加载一些配置的环境变量.在终端和宿主机能够正常执行.通过pty分配伪终端执行失败,通过nohup等命令后台执行也不行

      解决方法:通过问题1中的方法,向宿主机写入systemd service来执行

      source_file_path = '/etc/profile.d/base.sh'
      service_path = f"/etc/systemd/system/{service_name}"  # systemd 服务文件路径
      service_content = f"""
      [Unit]
      Description=Renew
      After=network.target
      Wants=network.target
      
      [Service]
      Type=simple
      User=root
      StandardOutput=journal
      StandardError=journal
      TTYPath=/dev/console
      ExecStart=/bin/bash -c 'source {source_file_path} >/dev/null 2>&1 && {cmd} {arg1} {arg2}'
      KillMode=process
      Restart=no
      
      [Install]
      WantedBy=multi-user.target
      """
      # 写入 systemd 服务文件
      with open(service_path, "w") as f:
          f.write(service_content)
      # 刷新 systemd 配置
      os.system("systemctl daemon-reload")
      # 启动服务
      os.system(f"systemctl start {service_name}")