如何编写更易调试的Shell脚本

对于私有化部署服务,如果部署模板中包含执行Shell脚本的步骤,您需要考虑脚本执行失败时,如何进行调试。由于脚本是在用户的ECS实例内执行,服务商难以登录机器排查。因此,有必要在编写脚本时就增加调试信息的输出。只有获取到问题的根本原因后,服务商才能够通过发布新服务版本来修复问题。本文介绍在编写部署模板时如何编写更易调试的Shell脚本的详细信息。

提升方法

1. 使用Command不使用UserData

Command相比UserData有以下好处:

  1. 使用Command时,可以通过API查询到CommandOutput信息,更容易定位和排查问题。

    计算巢的服务部署成功率分析功能,支持展示部署失败服务实例中命令的输出。27.png

  2. Command失败后,可以通过继续部署重复执行来修复问题;UserData是在创建ECS实例时执行,因此无法通过继续部署来重复执行。

编辑服务部署模板时,若是ROS模板中建议使用ALIYUN::ECS::RunCommand资源类型;若是Terraform模板,则建议使用alicloud_ecs_commandalicloud_ecs_invocation

2. 在脚本中增加前置检查

如果脚本有明确的安装依赖或者环境要求,请在脚本最前部进行检查,检查不通过时,将错误信息输出到stdout

3. 将调用信息输出到stdout

脚本中可以增加调用输出(stdoutstderr等),脚本输出会作为云助手命令的CommandOutput

您可以通过以下方法增加调用输出。

  1. 启用Bash的-v选项,在脚本前面增加set -v

    启用此选项后,会打印出读取到的每一行命令,从而可以准确定位出错的位置和出错的命令行。您也可以通过set -v(开启)和set +v(关闭)的组合,使其只在部分区域启用。

    重要

    设置输出信息时,请不要输出用户的敏感信息包括用户的AK、密码等。

    示例脚本

    #! /bin/bash
    read -p "Enter the input: " val
    zero_val=0
    if [ "$val" -gt "$zero_val" ]
    then
       echo "Positive number entered."
    else
       echo "The input value is not positive."
    fi

    bash -v ./positive_check.sh执行输出

    #! /bin/bash
    read -p "Enter the input: " val
    Enter the input: -10
    zero_val=0
    if [ "$val" -gt "$zero_val" ]
    then
       echo "Positive number entered."
    else
       echo "The input value is not positive."
    fi
    The input value is not positive.
  2. 启用Bash的-x选项。

    重要

    bash的-x选项请谨慎开启,尤其是当变量中存在敏感信息的时候,不能打印用户的AK或者密码,否则会引起泄密。

    使用-x选项启动Bash时,会在执行命令之前打印出命令及展开后的参数,此选项对调试脚本很有帮助。您还可以通过set -x(开启)和set +x(关闭)的组合,只在部分区域启用。

    $ cat test3.sh
    !/bin/bash
    set -x
    echo "hello World"
    mkdiir testing
    $ ./test3.sh
    + echo 'hello World'
    hello World
    + mkdiir testing
    ./test3.sh: line 4: mkdiir: command not found
  3. 通过echotee将日志打印到stdout

    通过echo打印日志,再通过tee将重要指令的输出内容转发到stdout

  4. 自有二进制文件输出清晰报错信息。

    如果安装过程需要执行服务商自有的二进制文件,请确保二进制文件能够给出清晰的报错信息,便于定位问题。

  5. 屏蔽过多的无效输出。

    云助手的CommandOutput有最大限制,超过限制后的输出内容会被截断。因此如果部分命令产生大量的输出,对诊断用处不大,可以将其标准输出重定向到/dev/null中。

4. 通过ROS信号通知机制传递错误原因和错误信息

如果部署模板是ROS原生格式,支持通过ROS信号通信机制将脚本中的错误原因和错误信息发送给ROS,最终服务商和用户都可以从服务实例的状态信息中查看到这个信息。

重要

Terraform模板不支持此功能。

${CurlCli} -d "{\"reason\" : \"用户可见的错误原因\",\"data\" : \"其他诊断相关信息\",  \"status\" : \"FAILURE\"}"

脚本中能识别用户侧的错误时,可用过该方式返回错误信息,便于用户查看和定位,否则用户将看到没有任何可读性的错误信息(如Signal 1 received)不利于定位问题。

5. 提高命令成功率

使用如下方法可以提高命令的成功率。

  1. 尽量不依赖公网下载,通过VPC内网下载会提高成功率和安全性。

  2. 尽量不依赖可变数据。如从公网下载一个随时会变化的安装脚本。更新可能会破坏安装脚本的稳定性。

  3. 尽量采用镜像方式交付静态部分。只有动态部分采用Command实现。如预先在自定义镜像中安装好数据库,数据库启动后的配置由Command来实现。