// Linux · 2026-08-12

使用 Ansible 自动更新 GitHub 安装的软件

在 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,步骤大致如下:

  1. 访问 Releases 页面,查看最新版本号;
  2. 下载适合自己系统的构建产物;
  3. 根据产物格式进行安装——如果是 .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