[toc]

[toc]

写在前面

最近也是在公司因为业务需求而解到了Ansible工具,改工具能快速的帮助我们远程服务器上执行我们想执行的shell命令,想象一下如果我们有10台服务器,现在需要查看每一台服务器的运行情况,例如使用top命令,我不会要分别通过ssh命令登录这10台服务器吧,这显然太繁琐了,所以我接下来介绍Ansible工具希望可以帮助你。

Ansible安装

这里我以macOS为例,安装 Ansible 在 macOS 上可以使用多种方法,最常见的是使用 brew(Homebrew)包管理器进行安装。下面是在 macOS 上安装 Ansible 的步骤:

  1. 安装 Homebrew(如果尚未安装):

    打开终端(Terminal),然后运行以下命令安装 Homebrew:

    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 
    
  2. 安装 Ansible:

    在终端中运行以下命令来安装 Ansible:

    brew install ansible 
    
  3. 验证安装:

    安装完成后,可以通过运行以下命令验证 Ansible 是否已成功安装:

    ansible --version
    

    如果安装成功,将会显示 Ansible 的版本信息。

    现在你已经成功在 macOS 上安装了 Ansible。如果你之前没有安装过 Homebrew,上述步骤也会自动安装 Homebrew。请确保你的终端具有管理员权限,以便在安装过程中执行必要的操作。

Ansible知识

Ansible 是一个自动化运维工具,用于配置管理、应用部署、任务自动化等。它使用简单的 YAML 文件和 SSH 协议来管理和配置远程主机。以下是 Ansible 的一些常见用法:

  1. 编写 Playbook: Ansible 使用 Playbook 来描述执行任务的计划。Playbook 是一个 YAML 格式的文件,可以在其中定义任务和配置。每个任务都包含一个或多个模块,模块是 Ansible 的基本组件,用于执行特定的任务。
  2. 定义主机清单: 在 Playbook 中,你需要定义要管理的远程主机清单。主机清单是一个文本文件,其中列出了你要管理的所有主机的 IP 地址或域名,通常文件格式以ini或者yaml格式。
  3. 使用模块执行任务: 在 Playbook 中,你可以使用 Ansible 提供的各种模块来执行任务。例如,你可以使用 command 模块执行命令、copy 模块复制文件、service 模块管理服务等等。
  4. 使用变量: Ansible 允许你定义变量来使配置更加灵活和可配置。你可以在 Playbook 中定义变量,并在任务中引用它们。
  5. 运行 Playbook: 定义好 Playbook 后,你可以使用 ansible-playbook 命令来运行它。该命令将按照 Playbook 的描述执行任务,并将结果返回给你。
  6. Ad-Hoc 命令: 除了 Playbook,Ansible 还支持 Ad-Hoc 命令,用于在命令行中执行临时任务。例如,你可以使用 ansible 命令执行一次性的命令,而无需编写 Playbook。
  7. Roles: Roles 是 Ansible 的一种组织方式,用于将相关的任务和配置分组。Roles 可以让你更好地组织和管理复杂的配置。

Ansible实战

现在我们使用ansible来对远程服务器执行指定的命令,这里就以top命令为例

  1. 编写服务器或者服务器组清单:hosts.ini

    [webservers]
    1.*.*.202 ansible_user=user_name ansible_password=password
    1.*.*.203 ansible_user=user_name ansible_password=password
    1.*.*.205 ansible_user=user_name ansible_password=password
    1.*.*.215 ansible_user=user_name ansible_password=password
    1.*.*.201 ansible_user=user_name ansible_password=password
    
  2. 编写一个top_command.yaml的Playbook文件:top_command.yaml

    ---
    - name: Execute top command on servers
      hosts: webservers
      become: yes
      tasks:
        - name: Run top command
          command: top -n 1 -b
          register: top_output
    
        - name: Display top command output
          debug:
            var: top_output.stdout_lines
    

    解释一下这个 Playbook:

    • hosts: webservers:指定了在哪些主机上执行任务,这里指定了 webservers 组,即你的三台服务器。
    • become: yes:表示在执行任务时切换到管理员权限,以便执行 top 命令。
    • tasks::定义了一系列任务。
    • - name: Run top command:第一个任务的名称,描述了这个任务的目标。
    • command: top -n 1 -b:使用 Ansible 的 command 模块来执行 top 命令,并通过 -n 1 参数指定只执行一次,并使用 -b 参数使 top 命令以批处理模式运行。
    • register: top_output:将 top 命令的输出保存到 top_output 变量中。
    • - name: Display top command output:第二个任务的名称,描述了这个任务的目标。
    • debug: var: top_output.stdout_lines:使用 Ansible 的 debug 模块来输出 top_output 变量的 stdout_lines 属性,即 top 命令的输出结果。

    保存并退出文件。

    完成两个文件后,我们直接执行:

    ansible-playbook -i hosts.ini top_command.yaml
    

    注意:如果你遇到以下报错

    FAILED! => {"msg": "to use the 'ssh' connection type with passwords or pkcs11_provider, you must install the sshpass program"}
    

    你应该使用命令安装ssh工具:

    brew install hudochenkov/sshpass/sshpass
    

    可以看到输出:

    PLAY [Execute top command on servers] **********************************************************************************************************************************************************************
    
    TASK [Gathering Facts] *************************************************************************************************************************************************************************************
    ok: [1.1*.*.*]
    
    TASK [Run top command] *************************************************************************************************************************************************************************************
    changed: [1.1*.*.*]
    
    TASK [Display top command output] **************************************************************************************************************************************************************************
    ok: [1.1*.*.202] => {
        "top_output.stdout_lines": [
            "top - 17:06:42 up 212 days, 18:36,  1 user,  load average: 0.06, 0.04, 0.03",
            "Tasks: 146 total,   1 running, 102 sleeping,   0 stopped,   0 zombie",
            "%Cpu(s):  0.6 us,  0.6 sy,  0.0 ni, 98.8 id,  0.1 wa,  0.0 hi,  0.0 si,  0.0 st",
            "KiB Mem :  3514764 total,   129196 free,  2767000 used,   618568 buff/cache",
            "KiB Swap:        0 total,        0 free,        0 used.   470736 avail Mem ",
            "",
            "  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND",
            "    8 root      20   0       0      0      0 I   6.7  0.0  53:42.65 rcu_sched",
            " 1822 mysql     20   0 1654824 235764      0 S   6.7  6.7  97:11.73 mysqld",
            "    1 root      20   0  225568   6988   4312 S   0.0  0.2  11:43.94 systemd",
            "    2 root      20   0       0      0      0 S   0.0  0.0   0:06.10 kthreadd",
            "    4 root       0 -20       0      0      0 I   0.0  0.0   0:00.00 kworker/0:+",
            "    6 root       0 -20       0      0      0 I   0.0  0.0   0:00.00 mm_percpu_+",
            "    7 root      20   0       0      0      0 S   0.0  0.0   2:47.59 ksoftirqd/0",
            ……
            ……
            ……
            "29512 www-data  20   0  329756  64712  30836 S   0.0  1.8  10:11.20 php-fpm7.2"
        ]
    }
    
    
    ok: [1.1*.*.205] => {
        "top_output.stdout_lines": [
            "top - 17:06:42 up 212 days, 18:36,  1 user,  load average: 0.06, 0.04, 0.03",
            "Tasks: 146 total,   1 running, 102 sleeping,   0 stopped,   0 zombie",
            "%Cpu(s):  0.6 us,  0.6 sy,  0.0 ni, 98.8 id,  0.1 wa,  0.0 hi,  0.0 si,  0.0 st",
            "KiB Mem :  3514764 total,   129196 free,  2767000 used,   618568 buff/cache",
            "KiB Swap:        0 total,        0 free,        0 used.   470736 avail Mem ",
            "",
            "  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND",
            "    8 root      20   0       0      0      0 I   6.7  0.0  53:42.65 rcu_sched",
            " 1822 mysql     20   0 1654824 235764      0 S   6.7  6.7  97:11.73 mysqld",
            "    1 root      20   0  225568   6988   4312 S   0.0  0.2  11:43.94 systemd",
            "    2 root      20   0       0      0      0 S   0.0  0.0   0:06.10 kthreadd",
            "    4 root       0 -20       0      0      0 I   0.0  0.0   0:00.00 kworker/0:+",
            "    6 root       0 -20       0      0      0 I   0.0  0.0   0:00.00 mm_percpu_+",
            "    7 root      20   0       0      0      0 S   0.0  0.0   2:47.59 ksoftirqd/0",
            ……
            ……
            ……
            "29512 www-data  20   0  329756  64712  30836 S   0.0  1.8  10:11.20 php-fpm7.2"
        ]
    }
    
    ……
    ……
    ……
    
    PLAY RECAP *************************************************************************************************************************************************************************************************
    1.*.*.202               : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    这样我们就可以直接操作多台服务器,避免了大量重复工作,有时间更好的摸鱼了!

Ansible结合Go

我们可以结合Go来做一些工具,比如说,我们现在就使用Go写一个控制远程服务器执行Python脚本并且获取到执行后的内容的工具。

准备脚本

首先我们要准备一个Python脚本hello.py:

print("Hello, World!")

这个Python文件需要我们放置到我们的服务器上可以使用ssh命令上传文件,或者直接在服务器上使用编辑vim hello.py编写代码。

配置服务器组

配置服务器组就是编写.ini文件(ps:由于比较贫穷只有一台服务器),有多台服务器直接在后续填上即可。

[webservers]
1.*.*.202 ansible_user=user_name ansible_password=password

编写Go程序

这里就不用多说了,这里就默认大家都会Go:

package main

import (
	"fmt"
	"os"
	"os/exec"
	"strings"
)

func main() {
  //服务器组文件
	hosts := "/Users/iceymoss/ansible/hosts.ini"
  
  //对应的Python命令和服务器上的Python文件
	pythonCmd := "python3 /home/ubuntu/iceymoss/ansible/hello.py"

  //接收命令
	cmd := exec.Command("ansible", "-i", hosts, "all", "--become-user", "ubuntu", "-m", "shell", "-a", pythonCmd)
  
  //添加环境变量
	cmd.Env = append(os.Environ(), "PATH=/opt/homebrew/bin/ansible")
  
  //用于获取输出
	var stdout, stderr strings.Builder
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

  //打印执行的命令
	fmt.Println(cmd.String())
  
  //执行命令
	err := cmd.Run()
	if err != nil {
		fmt.Println("Error executing command:", err)
		fmt.Println("Stderr:", stderr.String())
		return
	}
  //打印执行结果
	fmt.Println(stdout.String())
}

命令介绍:

ansible -i hosts.ini all –become-user ubuntu -m shell -a ’ls'

ansible -i hosts.ini all :表示使用ansible来管理hosts.ini的主机,并且all表示所有主机都要执行命令。

–become-user: 表示以哪一个用户执行,这里是使用ubuntu。

-m shell:-m表示用于指定要使用的ansible模块,这里是使用shell模块,它运行在远程主机上执行Shell命令。

-a ’ls’:-a 后面表示在远程主机上要执行的具体shell命令,这里执行ls命令

*** 注意***:在exec.Command("ansible", "-i", hosts, "all", "--become-user", "ubuntu", "-m", "shell", "-a", pythonCmd)这里的pythonCmd(也是就是:“python3 /home/ubuntu/iceymoss/ansible/hello.py”)是不能使用''的。

打包编译

这没什么好说的,直接使用 go build main.go编译即可

执行:

$ ./main 

输出结果:

/opt/homebrew/bin/ansible -i /Users/iceymoss/ansible/hosts.ini all --become-user ubuntu -m shell -a python /home/ubuntu/iceymoss/ansible/hello.py
1.210.170.2 | CHANGED | rc=0 >>
Hello, World!

当然也可以将这个工具位置添加的环境变量里面。

总结

我们可以使用ansible写一下小工具来提高效率,解决重复的问题,本文简单的介绍了ansible工具的使用和结合Golang来做一些小工具,例如在小工具实例中,我们还可以使用os.Args[]来接收命令行参数,我们可以在小工具中将要去远程服务器上执行的命令和文件都作为命令行参数传入,使得整个工具更有使用意义。