在 Linux 系统上安装软件,最简单的方式莫过于使用系统自带的包管理器——比如 Debian/Ubuntu 的 apt,或者 Fedora 的 dnf。
然而,向官方仓库提交和维护软件包并不是一件轻松的事。因此,大量优秀的开源软件要么根本不在官方仓库里,要么版本严重滞后。虽然 Flatpak 和 Snap 正在成为一种可行的替代分发方式,但仍有海量的优质工具,唯一的获取渠道就是 GitHub。
如果项目的发布流程做得足够规范,从 GitHub 安装软件的体验几乎可以媲美包管理器。很多项目会提供多种格式的构建产物:.deb、.rpm、AppImage,你可以根据自己的系统选择最方便的一种。即便没有这些,通常也至少会有一个 tarball,下载解压后放到合适的位置即可。
但从 GitHub 安装软件最大的痛点在于更新。当你使用包管理器时,更新所有软件只需一条命令,比如 apt update && apt upgrade。而对于从 GitHub 安装的软件,整个过程则繁琐得多:你得逐个访问仓库的 Releases 页面,检查是否有新版本,下载对应系统的构建产物,然后重复安装——每款软件都要来一遍。
那么,有没有办法把这个过程自动化呢?答案就是 Ansible。
Ansible 是什么?
Ansible 是一款用于自动化云资源编排、配置管理和应用部署的工具。它主要被系统管理员和 DevOps 工程师用来以“基础设施即代码”的方式管理多台服务器。不过,无论你管理的是一台机器还是一百台,Ansible 的抽象模型都是一样的。
从底层来看,Ansible 本质上是一个基于 SSH 的任务运行器,使用一套领域特定语言(DSL)来描述操作。你编写 YAML 格式的 Ansible 文件,定义要在目标机器上执行的任务,Ansible 会负责执行这些任务,并确保系统的最终状态与你在文件中描述的一致。
Ansible 内置了大量模块(如 copy、command 等),封装了复制文件、执行命令等常见操作。此外,社区还贡献了丰富的第三方模块,本文将要使用的 github_release 模块就是其中之一——它封装了 GitHub Releases API,让我们能在 Ansible 任务中直接获取某个仓库的最新 release 标签。
在深入之前,先快速过一遍 Ansible 的核心术语:
- Playbook(剧本):一组要在目标机器上执行的任务集合,是 Ansible 执行的入口。任务可以直接写在 playbook 里,也可以从其他文件或 role 中引入。
- Role(角色):将 playbook 拆分为多个文件的主要机制。它能简化复杂 playbook 的编写,也便于复用。比如你可以创建一个 “docker” role 来安装和配置 Docker,然后在任何需要 Docker 的 playbook 中直接引用它,而无需重复写相同的任务。
- Module(模块):Ansible 替你运行的可复用、独立的脚本。模块可以在本地或远程执行,与本地机器、API 或远程系统交互以完成特定任务。
- Inventory(清单):Ansible 将要操作的主机列表或主机组。
用 Ansible 安装 GitHub CLI
为了演示如何用 Ansible 从 GitHub 安装软件,我们将创建一个简单的 playbook,在 Ubuntu 系统上安装 GitHub CLI(gh)。完整的源代码可以在 GitHub - brpaz/install-github-software-ansible-demo 上找到。
安装 Ansible 及依赖
大多数主流发行版的默认仓库中都提供了 Ansible,你也可以通过 Python 的 pip 安装。
以 Ubuntu 20.04 为例:
sudo apt-get update
sudo apt install -y python3-pip ansible
ansible --version
你可以参考 Ansible 官方文档的安装指南 获取其他发行版的具体安装步骤。
接下来,我们需要安装 github_release 模块。该模块属于 community.general 集合,可以通过 Ansible Galaxy(Ansible 的内容包管理器)来安装。集合(Collection)是 Ansible 内容的一种分发格式,可以包含 playbook、role、模块和插件。
Ansible Galaxy 随 Ansible 一起安装,执行以下命令即可安装对应集合:
ansible-galaxy collection install community.general
这个模块依赖 github3.py 这个 Python 包来与 GitHub API 交互,同样用 pip 安装:
pip install github3.py
编写 Playbook
创建一个 Ansible playbook 非常简单:只需按照 Ansible 要求的结构编写一个 YAML 文件即可。
本文示例的完整 playbook 如下:
- name: GitHub Cli install
hosts: all
vars_prompt:
- name: github_token
prompt: "What is your GitHub Token?"
default: "{{ lookup('env','GITHUB_TOKEN') }}"
private: yes
tasks:
- name: "Get Latest Release from Github"
community.general.github_release:
user: cli
repo: cli
action: latest_release
token: "{{ github_token }}"
register: release
- name: Print Latest release
ansible.builtin.debug:
var: release
- name: Download Binary
ansible.builtin.unarchive:
src: https://github.com/cli/cli/releases/download/{{release.tag}}/gh_{{release.tag[1:]}}_linux_amd64.tar.gz
dest: /tmp
remote_src: true
- name: Install Binary
ansible.builtin.copy:
src: /tmp/gh_{{release.tag[1:]}}_linux_amd64/bin/gh
dest: "/usr/local/bin"
mode: a+x
become: true
下面对 playbook 的各部分逐一说明:
hosts 属性指定了该 playbook 将在哪些机器上运行。这在管理多台服务器时非常有用——你可能希望根据服务器的角色(Web、数据库等)在不同主机上执行不同的任务。由于本例只在单台机器上运行,我们使用关键字 all。
vars_prompt 允许我们在运行 playbook 之前向用户提示输入变量。这里我们请求输入 GitHub Token,默认值为环境变量 GITHUB_TOKEN 的值。对于本例来说这不是必须的,但如果你在同一个 Ansible 运行中对 GitHub API 发起大量请求,设置 token 可以避免触发 GitHub 的 API 速率限制。
tasks 部分是我们指定要让 Ansible 在目标主机上执行的操作,每个任务按定义的顺序依次执行。
如果我们手动从 GitHub 安装 GitHub CLI,步骤大致如下:
- 访问 Releases 页面,查看最新版本号;
- 下载适合自己系统的构建产物;
- 根据产物格式进行安装——如果是
.deb可以直接安装,如果是 tarball 则需要解压并把内容放到合适的位置。
下面看看如何用 Ansible 自动化这些步骤。
1. 获取最新 Release
使用前面安装的 github_release 模块:
- name: "Get Latest Release from Github"
community.general.github_release:
user: cli
repo: cli
action: latest_release
token: "{{ github_token }}"
register: release
我们指定了 user(仓库所有者)和 repo(仓库名),并通过 register 将模块的输出保存到 release 变量中,供后续步骤使用。关于该模块的更多参数,可以参考 community.general.github_release 模块文档。
2. 调试输出(可选)
可以用 debug 模块查看变量的值:
- name: Print Latest release
ansible.builtin.debug:
var: release
3. 下载并解压构建产物
不同项目的发布产物格式可能不同。GitHub CLI 的 Releases 页面提供了 .deb、.rpm 和压缩包(.tar.gz)等多种格式。本例使用 tarball,因为这是最常见、通用性最强的格式。当然,如果你的系统有更好的格式可用(比如在 Ubuntu 上直接用 .deb),应该优先选择那个。
使用内置的 unarchive 模块,可以一步完成下载和解压:
- name: Download Binary
ansible.builtin.unarchive:
src: https://github.com/cli/cli/releases/download/{{release.tag}}/gh_{{release.tag[1:]}}_linux_amd64.tar.gz
dest: /tmp
remote_src: true
这里我们用上一步保存的 release 变量来拼接完整的下载链接。unarchive 模块会自动下载 src 指定的文件并解压到 dest 目录。remote_src: true 表示源文件是一个远程 URL。
4. 安装二进制文件
解压完成后,将可执行文件移动到 PATH 中的某个目录,比如 /usr/local/bin:
- name: Install Binary
ansible.builtin.copy:
src: /tmp/gh_{{release.tag[1:]}}_linux_amd64/bin/gh
dest: "/usr/local/bin"
mode: a+x
become: true
mode: a+x 确保文件具有可执行权限。become: true 表示以 sudo 权限运行该任务,因为 /usr/local/bin 通常属于 root 用户。
运行 Playbook
在 playbook 和 hosts 文件所在的目录下打开终端,执行:
ansible-playbook -i hosts setup.yml
-i 参数指定 inventory 文件的路径,该文件定义了 Ansible 连接目标机器所需的 IP 地址和其他连接属性。由于我们在本机上运行,可以在 hosts 文件中设置本地连接:
local ansible_connection=local
Ansible 执行完毕后,打开新终端输入 gh,应该就能看到 GitHub CLI 的帮助信息了。
规模化使用
上面的例子展示了用 Ansible 从 GitHub 安装软件的基础方法。具体的任务会根据项目及其提供的构建产物格式略有不同。
虽然示例只安装了一款软件,但你完全可以把同样的逻辑应用到多个 GitHub 仓库,在同一个 playbook 中批量安装和更新多款软件。
当 playbook 变得庞大时,建议像编程一样进行拆分——把 playbook 文件当作程序的 main 函数。你可以使用 Role 为每款软件创建一个独立的 role,或者创建多个 YAML 文件,每个文件包含一款软件的安装任务,然后通过 include_tasks 指令在 playbook 中引入它们。
Role 更适合需要在多个 playbook 之间复用、或者希望分享给社区的场景,尤其是当任务比较复杂、涉及额外配置时。
在我们的示例中,可以把所有任务封装成一个 role,然后在 playbook 中这样引用:
- hosts: all
roles:
- { role: github-cli }
可以参考这个 安装 AWS CLI 的 role 示例。
在我的个人配置中,我目前使用 include_tasks 配合普通文件夹来组织每款软件的安装任务,因为大多数任务都很简单。但如果今天重新设计,我可能会选择使用 role——这是一种更“标准”的做法,也便于与他人分享。
总结
GitHub 是获取优质开源软件的宝库,但手动追踪和更新这些软件远不如系统包管理器来得方便。Ansible 恰好能填补这个空白。
为每款从 GitHub 安装的软件创建对应的 playbook 和任务,之后每次想更新系统时,只需像执行 apt update 或 flatpak update 一样运行你的 playbook 即可。github_release 模块会自动从 GitHub 获取最新版本,确保你每次运行都能安装到最新的软件。
当然,Ansible 的能力远不止安装 GitHub 软件。你可以用它完整地自动化一台新机器的环境配置。如果你需要灵感,可以参考 Bruno Paz 的 个人 Linux 配置仓库。
原文 https://blog.csdn.net/2301_79518550/article/details/163699679