使用 Emacs Lisp 管理和部署服务器
最近一段时间都在做各种项目部署、服务器部署,顺便分享下我是如何用 Emacs 以及编写 Emacs Lisp 代码来管理和部署数台服务器的。
说到自动化,可能平时大家爱用比如 pdsh 命令,或者 Python 的 Fabric 框架,再或者是 tmux、screen,这些都可以很方便地为我们做很多自动化的事情。Emacs 内置了多种交互式 shell 方案,eshell、shell 和 ansi-term,我们让 shell 交互式运行在 Emacs 中,就可以像操作 buffer 那样方便地来操控 shell了。平时我常用 shell(注,这里的 shell 指的是在 Emacs 中执行:M-x shell),然后我可以很方便地写 Emacs Lisp 代码来自动化一些事情。
现在,有 n 台服务器,我想在 Emacs 中开启多个 buffer,并在每个 buffer 中打开一个 shell,然后 ssh 到远程服务器上。并且每个 buffer 名就是主机名,这样,我要管理某台主机,直接 C-x b,然后输入主机名补全,就可以切换到对应那台主机的 shell了。代码如下:
(defun ssh->hosts () "SSH到列表中的主机" (dolist (host *hosts*) (shell host) (insert (format "ssh %s" host)) (comint-send-input)))
上面代码做了以下事情:
- 遍历
*hosts*
变量,*hosts* 变量是我定义的一个全局列表,里面每个元素是服务器的 IP 地址和主机名; - 执行 shell 函数,shell 函数会新打开一个 buffer,并在 buffer 中创建一个交互式 shell 环境(支持 bash、zsh 等)。buffer 的名字就是 shell 函数的第一个参数,即主机名或 IP 地址。
- 然后在新建的 shell 下,执行一条 shell 命令:ssh 主机地址。comint-send-input 函数是回车并执行 shell。
以下就是我定义的 *hosts*
变量:
(defvar *hosts* '("172.17.0.5" "172.17.0.8"))
现在,我要部署服务器,希望在每台服务器上,都批量执行同样的命令,于是又写了一个函数:
(defun run (command) "执行shell命令" (let ((current-buf (current-buffer))) (dolist (host *hosts*) (shell host) (insert command) (comint-send-input)) (switch-to-buffer current-buf)))
run 函数遍历 *hosts*
里对应的每个 buffer,然后执行我们指定的 shell 命令。比如以下调用:
(run "sudo apt-get upgrade")
就可以批量升级系统。
这里,我录了一段视频来演示:https://v.youku.com/v_show/id_XODgyNjI1OTA0.html
视频中,我用 Docker 启动了两台虚拟机,演示了如何在两台机器上同步执行命令(注意视频右边上下两个 window,分别是不同主机的 ssh)。
接着,我新建了个叫“hello”的 buffer,然后重新绑定了回车键,在 buffer 中输入的每一行内容都作为要执行的命令,按下回车后,会在两台虚拟机上执行。
视频里的两个函数 newline-and-submit 和 current-line-string 代码如下:
(defun newline-and-submit () "获取并作为 shell 命令执行当前行的内容,然后插入换行" (interactive) (let ((line (current-line-string))) (run line) (reindent-then-newline-and-indent))) (defun current-line-string () "获得当前行的字符串内容" (interactive) (buffer-substring-no-properties (line-beginning-position) (line-end-position)))