安装完一个全新的 Debian 安装之后,总有一些 packages 是我每次都要装的。与其手动逐个安装,我会把它们放进一个简单的 Bash 脚本,用一条 command 全部装好。
对我来说,这是完成基本软件包安装的一种轻量做法,效率也够用。仅仅为了安装一份 packages 清单,没有必要动用自动化平台。
所以,我在安装完 Debian 之后大致会做两件事:
- 运行一个简单的安装后脚本(
post-install); - 安装所有需要的 packages。
脚本相当精简,里面包含两类 packages:
- 几乎在任何桌面上都用得上的基础工具;
- 我特别想要的个人桌面 packages。
如果这套设置之后从安装软件包扩展到系统配置,我就会转向 Ansible。
请注意,本指南专门针对的是以软件包为中心的安装后处理,并不是一份完整、通用的 Debian 或 Ubuntu 安装后指南。如果需要完整的安装后步骤,可以参考以下链接:
- Debian Server Setup: Essential Post-Installation Steps
- DebPostInstall: Debian And Ubuntu Server Post Install Script
- An Automated Way To Install Essential Applications On Ubuntu
1. 为 Debian / Ubuntu 创建一个简单的安装后 Bash 脚本
把所有脚本放在同一个目录里比较方便。先创建一个 scripts 目录:
mkdir -p ~/scripts
创建脚本:
nano ~/scripts/install-deb-essential-packages.sh
加入:
#!/usr/bin/env bash
set -euo pipefail
PACKAGES=(
# Basic tools
git
curl
wget
ca-certificates
unzip
build-essential
# Personal desktop packages
firmware-cirrus
papirus-icon-theme
timeshift
gnome-shell-extension-manager
vim
gnome-shell-extension-dashtodock
gnome-themes-extra
flatpak
gnome-software-plugin-flatpak
steam-installer
)
echo "Updating package lists..."
sudo apt update
echo "Installing packages..."
sudo apt install -y "${PACKAGES[@]}"
echo "Done."
脚本中的安装命令通过 sudo 执行,也就是需要管理员权限,并且会实际改动系统上的软件包状态。这一点在运行之前需要确认。
请注意,有些 packages 只针对特定平台提供。因此可能需要编辑这个脚本,根据自己的 Linux 发行版增删 packages。把要安装的 packages 都加进去之后,保存并关闭脚本。
赋予可执行权限:
chmod +x ~/scripts/install-deb-essential-packages.sh
运行脚本,在 Debian/Ubuntu 系统上一次性安装全部 packages:
~/scripts/install-deb-essential-packages.sh
同一个脚本也可以用在其他基于 Debian 的系统上,例如 Ubuntu 及其衍生版 Linux Mint、Pop!_OS、ElementaryOS 和 Zorin OS。
如前所述,有些 packages 可能只在特定版本中提供。请确认脚本中的 packages 在自己的 Linux 发行版上可用。可以按下面链接中的方法检查软件包可用性:
2. 为什么选这些基础软件包?
第一组刻意保持精简。
git
用于克隆仓库和管理配置文件。
git clone ...
curl
一个通用的命令行工具(general-p-purpose),用于通过网络协议传输数据。很多安装说明和脚本都会用到它。
wget
另一个常用的命令行下载工具。
ca-certificates
提供软件进行 HTTPS/TLS 连接时使用的受信任 CA 证书。
unzip
下载的软件和项目经常以 .zip 压缩包的形式提供,所以它很有
用。
build-essential
提供标准的基础编译工具链,包括 gcc 和 make 等工具。Debian 明确把它列为构建基础环境的关键软件包。
build-essential对每个桌面用户来说并非严格必需。如果确定不会编译软件,可以把它去掉。这里保留它,是因为对一台面向开发的个人机器来说,它是一个有用的基线。
3. 为什么用 Bash 数组?
与其手动执行:
sudo apt install git
sudo apt install curl
sudo apt install wget
sudo apt install unzip
不如维护一份清单:
PACKAGES=(
git
curl
wget
unzip
)
然后用一条命令安装全部:
sudo apt install -y "${PACKAGES[@]}"
这样做的好处是:
- 更快;
- 更容易维护;
- 容易修改;
- 重装 Debian 后容易复用。
添加一个软件包只需要多加一行:
PACKAGES=(
git
curl
wget
neovim
)
4. 为什么用 set -euo pipefail?
你可能已经注意到,脚本开头是:
set -euo pipefail
这会让脚本更安全。
其中 -e 的作用是:当某条 command 失败时让脚本停止,而不是继续盲目执行。
对于一个小型的个人配置脚本来说,这是一个不错的默认选择。
5. 把个人软件包单独分开
随着清单变长,区分通用工具和自己特有的东西会很有帮助。
例如:
PACKAGES=(
# Basic tools
git
curl
wget
ca-certificates
unzip
build-essential
# Desktop
firmware-cirrus
papirus-icon-theme
gnome-shell-extension-manager
gnome-shell-extension-dashtodock
gnome-themes-extra
# Applications
timeshift
flatpak
gnome-software-plugin-flatpak
steam-installer
# Editor
vim
)
这样以后看脚本时更容易理解。
6. 什么时候用 Ansible?
有人会问,为什么不干脆用 Ansible 这类自动化平台了事。我不会只为一份软件包清单就用 Ansible。当我开始做下面这些事情时,Ansible 才变得有用:
- 安装 packages;
- 配置 Git;
- 创建配置文件;
- 配置 Vim;
- 启用服务;
- 配置 GNOME;
- 应用系统设置。
或者当我希望多台机器拥有相同配置时。
单纯安装软件包,我用 Bash;需要可重复的系统配置,我才用 Ansible。
7. 安装 Ansible
Ansible 是一个用 Python 编写的开源命令行 IT 自动化工具。它可以配置系统、部署软件,并编排复杂工作流,以支持应用部署、系统更新等任务。
它以“无代理”著称,也就是说,它通过标准连接协议管理远程机器,而不需要在被管理机器上安装常驻软件。
工作原理
你用 YAML 编写人类可读的指令(称为 playbook),声明服务器应处于的目标状态。Ansible 通过 SSH(用于 Linux/Unix)或 WinRM(用于 Windows)连接,检查当前状态,只在必要时做出改动以匹配你的指令。被管理节点上不需要额外的代理或守护进程。
关键要点
- 无代理架构: 与需要在每台服务器上运行常驻代理的工具不同,Ansible 使用现有的 SSH 守护进程(Windows 上则是 WinRM)作为传输方式。这减少了维护开销,也不需要管理代理软件的更新或兼容性。
- 幂等性: 这是核心原则。Ansible 模块在设计上尽可能做到幂等,也就是只有当系统尚未达到目标状态时才会做出改动。多次运行同一个 playbook 会得到相同结果。注意:有些模块(例如
command和shell)本身不具备这种行为,需要手动保证。 - 用 YAML 保持简单: Playbook 用 YAML 编写,这是一种人类可读的语言。这意味着自动化代码读起来常常像文档,即使不是专职程序员也能看懂。
- 推送模型: 你从中央的“控制节点”执行 Ansible,它通过 SSH(Windows 上则是 WinRM)把配置变更推送到“被管理节点”。
关于 Ansible 的一个事实
虽然 Ansible 以“无代理”闻名,但它实际上确实会使用一个很小的临时程序,只是时间很短。每执行一个任务,Ansible 都会连接到被管理节点,把称为 Ansible 模块 的小程序推送过去。
这些程序被编写成系统目标状态的资源模型。Ansible 随后执行这些模块(默认通过 SSH),并在完成后删除它们。这样服务器会保持自动化之前的样子,不会留下常驻的 Ansible 软件。
在 Debian / Ubuntu 上安装 Ansible,运行下面的命令,需要管理员权限:
sudo apt update
sudo apt install ansible
查看版本:
ansible --version
示例输出:
ansible [core 2.16.3]
config file = None
configured module search path = ['/home/ostechnix/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3/dist-packages/ansible
ansible collection location = /home/ostechnix/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/bin/ansible
python version = 3.12.3 (main, Apr 10 2024, 05:33:47) [GCC 13.2.0] (/usr/bin/python3)
jinja version = 3.1.2
libyaml = True
8. 创建 Ansible 项目
对于个人桌面:
mkdir -p ~/ansible/debian-desktop
cd ~/ansible/debian-desktop
初始化 Git:
git init
初始结构:
debian-desktop/
├── inventory.ini
└── site.yml
9. 创建 inventory
创建 inventory.ini 文件:
inventory.ini
加入:
[desktop]
localhost ansible_connection=local
这告诉 Ansible 在本地管理当前这台计算机。
用 command 测试:
ansible -i inventory.ini desktop -m ping
示例输出:
localhost | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
10. 创建 playbook
创建 site.yml 文件:
nano site.yml
加入以下内容:
---
- name: Configure my Debian desktop
hosts: desktop
become: true
tasks:
- name: Update apt cache
ansible.builtin.apt:
update_cache: true
- name: Install desktop packages
ansible.builtin.apt:
name:
- git
- curl
- wget
- ca-certificates
- unzip
- build-essential
- firmware-cirrus
- papirus-icon-theme
- timeshift
- gnome-shell-extension-manager
- vim
- gnome-shell-extension-dashtodock
- gnome-themes-extra
- flatpak
- gnome-software-plugin-flatpak
- steam-installer
state: present
同样,请把平台支持的 packages 加进上面的 playbook。保存并关闭。
运行 playbook:
ansible-playbook -i inventory.ini site.yml --ask-become-pass
示例输出:
BECOME password:
PLAY [Configure my Debian desktop] ********************************************************************************************************************************************************************************************************
TASK [Gathering Facts] ********************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Update apt cache] *******************************************************************************************************************************************************************************************************************
changed: [localhost]
TASK [Install desktop packages] ***********************************************************************************************************************************************************************************************************
changed: [localhost]
PLAY RECAP ********************************************************************************************************************************************************************************************************************************
localhost : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
11. Bash 脚本与 Ansible 的区别
Bash 通常表达的是:运行这些 command。
Ansible 通常表达的是:让机器变成这个样子。
例如:
- name: Install Vim
ansible.builtin.apt:
name: vim
state: present
如果 Vim 没有安装,Ansible 会指示系统安装它;如果 Vim 已经安装,它就什么都不做。
这就是所谓的幂等性。
12. 加入配置
软件包安装跑通之后,Ansible 也可以管理配置。
例如:
- name: Create Vim configuration
ansible.builtin.copy:
content: |
set number
set relativenumber
syntax on
dest: "{{ ansible_facts.user_dir }}/.vimrc"
mode: "0644"
现在 Ansible 不只是安装软件,它还在配置这台机器。
13. playbook 变大后拆分
不必一开始就考虑 Ansible roles。
如果 site.yml 变得太大,可以把任务拆到不同文件里:
debian-desktop/
├── inventory.ini
├── site.yml
├── packages.yml
├── git.yml
└── desktop.yml
site.yml 是主 playbook,它导入并运行其他文件中的任务:
---
- name: Configure my Debian desktop
hosts: desktop
become: true
tasks:
- ansible.builtin.import_tasks: packages.yml
- ansible.builtin.import_tasks: git.yml
- ansible.builtin.import_tasks: desktop.yml
各个文件有各自的用途:
packages.yml—— 包含安装和管理 APT 软件包的任务。git.yml—— 包含配置 Git 的任务。desktop.yml—— 包含配置 GNOME 及其他桌面设置的任务。
例如,packages.yml 可能包含:
---
- name: Install required packages
ansible.builtin.apt:
name:
- git
- curl
- wget
- vim
- flatpak
state: present
这样可以让 site.yml 保持精简,也让配置更容易理解和维护。
14. 复杂配置最终使用 Ansible roles
如果项目变得大得多,可以引入 Ansible roles。
结构最终可能类似:
debian-desktop/
├── inventory.ini
├── site.yml
└── roles/
├── packages/
├── git/
├── vim/
└── gnome/
每个 role 把与某个领域相关的任务、文件、模板、变量和配置集中在一起。
例如:
packages—— 管理已安装的软件包。git—— 管理 Git 配置。vim—— 管理 Vim 配置。gnome—— 管理 GNOME 和桌面配置。
不要一开始就用 roles。 对于个人机器,先从一个 site.yml 开始。如果它变大,就拆成 packages.yml、git.yml、desktop.yml 这样的文件。只有当项目大到 roles 确实带来实际好处时,再引入它们。
15. 常用的 Ansible 命令
测试 Ansible:
ansible -i inventory.ini desktop -m ping
运行 playbook:
ansible-playbook -i inventory.ini site.yml --ask-become-pass
试运行:
ansible-playbook -i inventory.ini site.yml --check --ask-become-pass
输出更多信息:
ansible-playbook -i inventory.ini site.yml --ask-become-pass -v
16. 把配置放进 Git
对于 Bash 脚本:
cd ~/scripts
git init
对于 Ansible 项目:
cd ~/ansible/debian-desktop
git init
最终,Ansible 项目可以成为我桌面配置的权威描述。
17. Fedora 或基于 RHEL 的发行版怎么办?
脚本的 Bash 结构 可以复用到 Fedora 和其他基于 RHEL 的发行版上,但不能想当然地认为把 apt 换成 dnf 就够了。
在 Debian 上:
sudo apt update
sudo apt install -y "${PACKAGES[@]}"
在 Fedora/RHEL 系系统上,安装软件包一般使用:
sudo dnf install -y "${PACKAGES[@]}"
不过,软件包名称在不同发行版之间可能不同。
例如:
Debian Fedora/RHEL
------- ----------
git git
curl curl
wget wget
ca-certificates ca-certificates
unzip unzip
build-essential gcc gcc-c++ make
有些 packages 可能名称完全不同,可能由不同的仓库提供,也可能根本不存在相同的形式。
因此,对于个人配置,通常更清晰的做法是维护彼此独立的脚本:
install-debian-essential-packages.sh
install-fedora-essential-packages.sh
Debian 脚本可以用:
sudo apt update
sudo apt install -y "${PACKAGES[@]}"
而 Fedora 脚本可以用:
sudo dnf install -y "${PACKAGES[@]}"
并配一份 Fedora 专用的软件包清单。
为什么不写一个脚本包办一切?
检测发行版是可行的:
if command -v apt >/dev/null 2>&1; then
sudo apt update
sudo apt install -y "${PACKAGES[@]}"
elif command -v dnf >/dev/null 2>&1; then
sudo dnf install -y "${PACKAGES[@]}"
else
echo "Unsupported package manager."
exit 1
fi
但一旦软件包名称不同,这种做法就会变得复杂。
对于简单的个人配置,分开的脚本更容易理解和维护。
如果配置最终需要支持多个发行版并变得复杂得多,那就是考虑使用 Ansible 的好时机。
18. 结论
可以看到,可以用一个简单的安装后 Bash 脚本,避免手动逐个安装相同的 packages。如果想自动化并复现整个系统的配置,就可以使用 Ansible。
在问题大到确实需要之前,不要引入 Ansible。
不要把问题复杂化。从简单开始。把重复的事情自动化。当需要管理的是配置本身时,再引入 Ansible。



