77. 本地机器查看线上机器日志

缘起

一般来说,线上机器为了安全,都不允许直接登录系统,通常的做法是通过中间机器去登录,比如我们熟知的跳板机。

通常都需要先登录跳板机,从跳板机继续登录线上机器去查看日志。当机器有多台,并且无法知道要查看的日志在哪台机器时,会特别的苦恼,盲目的尝试几台机器后觉得非常的沮丧。觉得肯定有好的办法,免去盲目登录的苦恼。

Expect

确实如此,有这么一个免费的编程工具语言有这种能完成用户交互的功能,它就是Expect。百度百科这么描述:”用来实现自动和交互式任务进行通信,而无需人的干预。Expect的作者Don Libes在1990年开始编写Expect时对Expect做有如下定义:Expect是一个用来实现自动交互功能的软件套件(Expect [is a] software suite for automating interactive tools)。使用它,系统管理员可以创建脚本来对命令或程序进行输入,而这些命令和程序是期望从终端(terminal)得到输入,一般来说这些输入都需要手工输入进行的。Expect则可以根据程序的提示模拟标准输入提供给程序需要的输入来实现交互程序执行。”

基于这个考虑,参考已有自动登录开发机的文章,我写了这个shell脚本,来方便查看线上机器日志。

基本语法可参考这篇,Expect使用小记,官网目测是无法访问了。

配置ssh免密码登录

需要先配置ssh免密码登录,在自己的本地机器新增配置文件$HOME/.ssh/config,并输入以下内容:

$: ~ zhangguangxun$ cat .ssh/config 
Host *  
ControlMaster auto 

ControlPath ~/.ssh/master-%r@%h:%p  
ControlPersist yes
ServerAliveInterval 60
$: ~ zhangguangxun$

Expect自动登录机器

该脚本我命名为auto_expect,并存放在家目录下$HOME/auto_expect,可任意命名,但要注意下一个文件中的可执行文件调用脚本应该与该处命名一致。

#!/usr/bin/expect

# expect 进行交互式登录线上系统
# 一般公司线上系统都是通过中间机器登录,不允许直接从本机登录,故受到很多限制
# Expect是一个可以和交互式程序对话的程序
# 语法可参考:https://www.cnblogs.com/yinghao1991/p/6926125.html
# 官网无法打开,未能进行浏览

# relay用户名
set USER [lindex $argv 0]

# relay PIN码
set PIN [lindex $argv 1]

# [可选]开发机地址, 如:user@host
set HOST [lindex $argv 2]

# [命令] 需要在线上机器执行的命令全称,写在一行内
set CMD [lindex $argv 3]

# [可选]开发机密码
set PASSWORD [lindex $argv 4]

# 登录relay
# 创建一个新的进程运行program [args]。program的标准输入、标准输出和标准错误都连接到Expect,这样程序就可以被Expect读写了。
spawn /usr/bin/ssh $USER@你自己的跳板机地址

# 打开调试模式
#exp_internal 1

# relay PIN码 + Token(Token需手动输入)
# 等待,直到模式patn匹配到spawn打开的进程的输出,超过指定的时间,或遇到EOF。
expect {
    # 正则表达式,在单项前面指定匹配
    -re "password:*" {
        # 类似send,不过输出发送到标准输出,而不是当前进程。
        send_user "PIN:****** + Token:"

        # 类似expect,不过是从标准输入读取字符,行必须以回车结尾,以使expect能识别它们。 
        expect_user {
            # 不超时等待
            -timeout -1
            # 正则匹配
            -re "(.*)\n"
        }
        set TOKEN $expect_out(buffer)

        # 发送string到当前进程
        send "$PIN$TOKEN\r"

        # 允许expect继续执行自身而不是往下执行,默认情况下,exp_continue会重置timeout,如果不想重置timeout,使用-continue_timer选项。
        exp_continue
    }

    # 自动登录开发机
    -re "-bash-baidu-ssl*" {
        if { "$HOST" != "" } {
            # 自动登录到指定的机器
            send "ssh --silent $HOST\r"

            # [可选]自动开发机输入密码
            if { "$PASSWORD" != "" } {
                expect -re "password:" { send "$PASSWORD\r" }
            }

            # 已经登录到机器,在机器上直接执行命令,可手动换行,或者自动换行即是末尾加\r标识符
            send "$CMD\r"

            # 为了便于查看日志,需要手动退出不自动处理
        }
    }
}
# 关闭调试模式
#exp_internal 0
# 将当前进程的控制权交付给用户
interact
# Expec退出
exit

本地操作命令

下面这个文件我取名为get_log.sh,通过该脚本来执行来获取线上机器的日志信息。该脚本依赖上一个脚本auto_expect,需要注意路径一致。

#!/bin/bash

#********************脚本说明********************#

# 该脚本的运行需要依赖脚本 auto_expect,将其部署到家目录$HOME下,比如/Users/baidu/auto_expect
#
# 命令例子:
# sh get_log.sh "cd /home/zhangguangxun/log/trade/ && more trade.log.20180223* | grep 'orderdetailRet' | wc -l"
#
# 需要每台机器分别查看,手动输入exit退出后进入到下一台机器
#

#***********************************************#

#********************账户信息配置********************#

# 账号和PIN码
# 自己的授权开发账号
USER_NAME="自己账号"
# 自己的安全key
PIN="自己安全key"

# 配置环境 dev个人开发机, mirror预览机器, ......, all除开发机外的所有线上机器
MODE="dev"

#**************************************************#

#********************机器信息配置********************#

# 个人开发机
DEV_HOST=(10.99.201.110)

# 预览机
MIRROR_HOST=(......)

# 北京机房
BJ_HOST=(......)

# 广州机房
GZ_HOST=(......)

#***************************************************#

# 获取需要执行的日志命令, $0为执行命令名, 从$1开始获取传递的参数
# 至少需要1个参数
if [ $# -lt 1 ];
then
    echo "参数个数不够,至少需要传入一个参数:查找日志的命令名"
    exit 255
fi
CMD=$1

# 确定机器, 数组赋值和多个数组一并赋值
if [ $MODE = "dev" ];
then
    HOST=(${DEV_HOST[@]})
elif [ $MODE = "mirror" ];
then
    HOST=(${MIRROR_HOST[@]})
elif [ $MODE = "bj" ];
then
    HOST=(${BJ_HOST[@]})
elif [ $MODE = "gz" ];
then
    HOST=(${GZ_HOST[@]})
else
    HOST=(${MIRROR_HOST[@]} ${BJ_HOST[@]} ${GZ_HOST[@]})
fi

# 遍历机器
for i in ${!HOST[@]};
do
    # 使用expect方式依次登录机器,注意命令需要使用双引号包含起来避免被空格分割成单一参数
    /usr/bin/expect $HOME/auto_expect $USER_NAME $PIN ${HOST[$i]} "$CMD"
done