基础设施配置管理与SaltStack实践
立即解锁
发布时间: 2025-08-25 01:27:55 阅读量: 1 订阅数: 2 

### 基础设施配置管理与 SaltStack 实践
在实现基础设施即代码(IaC)的过程中,描述硬件之后,还需要对软件和配置方面进行管理。传统上,工程师可能会手动按照清单或运行脚本完成这些任务,但这种方式扩展性差,且存在诸多问题。而如今,配置管理(CM)已成为常见做法,它具有诸多优势。
#### 配置管理的好处
- **状态重现**:可以一次性声明机器的期望状态,并根据需要多次重现该状态。
- **抽象与复用**:强大的抽象功能可处理环境、硬件和操作系统类型等细节,使我们能够编写可复用的 CM 代码。
- **易于协作**:声明的机器状态代码易于阅读、理解和协作。
- **批量部署**:可以同时在数十、数百甚至数千台机器上进行 CM 部署。
#### 常见的 CM 工具
在 DevOps 时代,有多种 CM 工具可供选择,如 Puppet、Chef、Ansible、OpsWorks 和 SaltStack 等。这些工具都有活跃的社区支持,各有优缺点,选择哪种工具通常取决于个人偏好。
#### 重要要点
在使用 CM 工具时,有两个要点需要强调:
- **命名约定**:编写代码时遵循命名约定,能让他人更轻松地理解你的工作。在 CM 中,随着基础设施规模和复杂性的增加,需要一个自然形成层次结构的主机命名约定。例如,将主机名如 webserver-{0..10}、db-{0..5} 和 cache-{0..5} 进一步分组为前端和后端,并以结构化、层次化的方式表示。
- **代码复用**:编写 CM 代码时应考虑代码复用。通常有两种方法:一种是编写一个包含设置防火墙、CLI 工具、NGINX 和 PHP 等指令的大型 Web 服务器脚本;另一种是将其分解为更小的部分,如 iptables、utils、NGINX、PHP 等。后者虽然在编写清单时会增加一些开销,但复用性更强。
以下是两种方式的示例对比:
| 方式 | 清单 | 节点 | CM 执行 |
| --- | --- | --- | --- |
| 方式一 | everything_a_websrv_needs, everything_for_a_db, cache_main | web01, db01, cache01 | web01=(everything_a_websrv_needs), db01=(everything_for_a_db), cache01=(cache_main) |
| 方式二 | iptables, utils, nginx, postgresql, redis, php | web01, db01, cache01 | web01=(iptables,utils,nginx,php), db01=(iptables,utils,postgresql), cache01=(iptables,utils,redis) |
#### SaltStack 简介
SaltStack 于 2011 年首次发布,是一个自动化套件,提供配置管理以及标准和/或事件驱动的编排功能。它通常以主从模式使用,主节点可对计算资源进行集中控制,因其使用快速轻量级的消息总线(ZeroMQ)进行通信,所以具有速度快和可扩展性强的特点。此外,它也可以无代理方式使用,通过 SSH 控制从节点,类似于 Ansible 的操作方式。SaltStack 用 Python 编写,易于扩展。
在本文中,我们将以独立或无主模式使用 SaltStack 来探索配置管理的强大功能。具体任务如下:
1. 准备 SaltStack 开发环境。
2. 编写希望 SaltStack 应用到节点的配置。
3. 编写描述基础设施的 Terraform 模板。
4. 通过 Terraform 部署基础设施,并让 SaltStack 进行配置。
#### 准备工作
SaltStack 配置管理主要使用以下组件:
- **States**:描述机器期望状态的文件,包含安装包、修改文件、更新权限等指令。
- **Pillars**:定义变量的文件,使 States 更具可移植性和灵活性。
- **Grains**:从从节点主机收集的信息,包括操作系统、环境、硬件平台等详细信息。
- **Salt File Server**:存储 States 中可能引用的文件、脚本或其他工件。
- **Salt Top 文件**:用于将 States 和/或 Pillars 映射到从节点。
由于我们计划以无主模式运行 Salt,需要将 States、Pillars 和相关文件从本地环境传输到从节点。我们选择使用 Git,将所有 Salt 代码在本地编写,推送到 Git 仓库,然后在从节点启动时将其检出。为了方便与 EC2 实例集成,我们选择使用 AWS 的 CodeCommit。
以下是创建 IAM 用户和 CodeCommit 仓库的步骤:
1. 在 AWS 控制台创建一个 IAM 用户,并记录生成的访问密钥,为该用户附加 AWSCodeCommitFullAccess 内置/托管 IAM 策略。
2. 在同一页面切换到“安全凭证”选项卡,上传 SSH 公钥。
3. 配置 awscli:
```bash
$ export AWS_ACCESS_KEY_ID='AKIAHNPFB9EXAMPLEKEY'
$ export AWS_SECRET_ACCESS_KEY='rLdrfHJvfJUHY/B7GRFTY/VYSRwezaEXAMPLEKEY'
$ export AWS_DEFAULT_REGION='us-east-1'
```
4. 创建仓库:
```bash
$ aws codecommit create-repository --repository-name salt --repository-description "SaltStack repo"
{
"repositoryMetadata": {
"repositoryName": "salt",
"cloneUrlSsh": "ssh://git-codecommit.us-east-1.amazonaws.com/v1/repos/salt",
"lastModifiedDate": 1465728037.589,
"repositoryDescription": "SaltStack repo",
"cloneUrlHttp": "https://git-codecommit.us-east-1.amazonaws.com/v1/repos/salt",
"creationDate": 1465728037.589,
"repositoryId": "d0628373-d9a8-44ab-942a-xxxxxx",
"Arn": "arn:aws:codecommit:us-east-1:xxxxxx:salt",
"accountId": "xxxxxx"
}
}
```
5. 本地克隆新仓库:
```bash
$ git clone ssh://[email protected]/v1/repos/salt
Cloning into 'salt'...
warning: You appear to have cloned an empty repository.
Checking connectivity... done.
```
此时,我们就可以开始填充新的 Salt 仓库了。
#### 编写配置管理代码
为了让 SaltStack 将节点配置为 Web 服务器,我们需要描述机器的期望状态。在示例中,我们将使用 SaltStack 的 States、Pillars、Grains 和 Top 文件来完成以下过程:
- 创建 Linux 用户账户
- 安装服务(NGINX 和 PHP-FPM)
- 配置和运行已安装的服务
##### States
States 包含要应用到 EC2 从节点的一组指令。我们将使用 /srv/salt/states 作为 Salt 状态树的根目录。States 可以存储为单个文件,如 /srv/salt/states/mystate.sls,也可以组织成文件夹,如 /srv/salt/states/mystate/init.sls。
以下是管理 Linux 用户账户的状态文件 states/users/init.sls 的示例:
```yaml
veselin:
user.present:
- fullname: Veselin Kantsev
- uid: {{ salt['pillar.get']('users:veselin:uid') }}
- password: {{ salt['pillar.get']('users:veselin:password') }}
- groups:
- wheel
ssh_auth.present:
- user: veselin
- source: salt://users/files/veselin.pub
- require:
- user: veselin
sudoers:
file.managed:
- name: /etc/sudoers.d/wheel
- contents: '%wheel ALL=(ALL) ALL'
```
这里使用了三个不同的状态模块:
- **user.present**:确保给定用户账户存在于系统中,必要时创建该账户。
- **ssh_auth.present**:管理用户的 SSH authorized_keys 文件。
- **file.managed**:创建/修改文件。
为了避免硬编码某些值,我们使用了 SaltStack 的 Pillars 系统。同时,`require` 语句强制执行执行顺序,确保在创建用户的授权密钥文件之前用户账户已存在。
接下来是安装 NGINX 的状态文件 states/nginx/init.sls:
```yaml
pkg.installed: []
nginx:
service.running:
- enable: True
- reload: True
- require:
- pkg: nginx
/etc/nginx/conf.d/default.conf:
file.managed:
- source: salt://nginx/files/default.conf
- require:
- pkg: nginx
- require_in:
- service: nginx
- watch_in:
- service: nginx
```
这里需要注意 `require/require_in` 和 `watch/watch_in` 对的使用。它们的区别在于作用方向。例如:
```yaml
nginx:
service.running:
- watch:
- file: nginx_config
nginx_config:
file.managed:
- name: /etc/nginx/nginx.conf
- source: salt://...
```
与以下代码效果相同:
```yaml
nginx:
service.running: []
nginx_config:
file.managed:
- name: /etc/nginx/nginx.conf
- source: salt://...
- watch_in:
- service: nginx
```
在这两种情况下,NGINX 服务在配置文件更改时都会重启,但第二种格式在处理不同文件中的服务块时可能更有用。
添加 PHP 的状态文件 states/php-fpm/init.sls 如下:
```yaml
include:
- nginx
php-fpm:
pkg.installed:
- name: php-fpm
- require:
- pkg: nginx
service.running:
- name: php-fpm
- enable: True
- reload: True
- require_in:
- service: nginx...
```
这里可以看到 `require_in` 的作用,确保在启动 nginx 之前启动 php-fpm。
最后,添加一个测试页面的状态文件 states/phptest/init.sls,我们设置了一些从 Grains 中提取的变量:
```jinja2
{% set publqic_ipv4 = salt['cmd.shell']('ec2-metadata --public-ipv4 | awk '{ print $2 }'') %}
{% set grains_ipv4 = salt['grains.get']('ipv4:0') %}
{% set grains_os = salt['grains.get']('os') %}
{% set grains_osmajorrelease = salt['grains.get']('osmajorrelease') %}
{% set grains_num_cpus = salt['grains.get']('num_cpus') %}
{% set grains_cpu_model = salt['grains.get']('cpu_model') %}
{% set grains_mem_total = salt['grains.get']('mem_total') %}
```
通过以上步骤,我们可以逐步使用 SaltStack 将节点配置为一个完整的 Web 服务器。整个过程的流程图如下:
```mermaid
graph LR
A[准备 SaltStack 开发环境] --> B[编写配置]
B --> C[编写 Terraform 模板]
C --> D[部署基础设施并配置]
D --> E[创建 Linux 用户账户]
E --> F[安装服务(NGINX 和 PHP-FPM)]
F --> G[配置和运行服务]
```
在后续的实践中,我们可以根据实际需求对这些配置进行调整和扩展,以满足不同的业务场景。同时,遵循命名约定和代码复用原则,能够提高代码的可维护性和可扩展性。
### 基础设施配置管理与 SaltStack 实践(续)
#### 深入理解 Pillars 和 Grains
在 SaltStack 的配置管理中,除了 States 之外,Pillars 和 Grains 也起着至关重要的作用。
##### Pillars
Pillars 是用于定义变量的文件,其主要目的是让 States 更加灵活和可移植。通过 Pillars,我们可以避免在 States 中硬编码一些敏感信息或经常变化的值。例如,在前面管理 Linux 用户账户的 States 文件中,我们使用了 Pillars 来获取用户的 uid 和 password:
```yaml
veselin:
user.present:
- fullname: Veselin Kantsev
- uid: {{ salt['pillar.get']('users:veselin:uid') }}
- password: {{ salt['pillar.get']('users:veselin:password') }}
- groups:
- wheel
```
为了使用 Pillars,我们需要创建相应的 Pillars 文件。假设我们在 Pillars 中定义用户信息的文件为 pillars/users.sls,其内容可能如下:
```yaml
users:
veselin:
uid: 1001
password: '$6$rounds=656000$examplepasswordhash'
```
这样,当 States 文件中引用这些值时,就会从 Pillars 文件中获取。
##### Grains
Grains 是从 Minion 主机收集的信息,包括操作系统、环境、硬件平台等详细信息。在前面添加测试页面的 States 文件中,我们已经使用了 Grains 来获取一些系统信息:
```jinja2
{% set publqic_ipv4 = salt['cmd.shell']('ec2-metadata --public-ipv4 | awk '{ print $2 }'') %}
{% set grains_ipv4 = salt['grains.get']('ipv4:0') %}
{% set grains_os = salt['grains.get']('os') %}
{% set grains_osmajorrelease = salt['grains.get']('osmajorrelease') %}
{% set grains_num_cpus = salt['grains.get']('num_cpus') %}
{% set grains_cpu_model = salt['grains.get']('cpu_model') %}
{% set grains_mem_total = salt['grains.get']('mem_total') %}
```
Grains 的好处在于,我们可以根据不同主机的特性来动态配置我们的系统。例如,如果我们需要根据主机的 CPU 核心数来调整某些服务的并发配置,就可以使用 `grains_num_cpus` 这个变量。
#### 配置 Salt Top 文件
Salt Top 文件用于将 States 和/或 Pillars 映射到 Minions。在我们的示例中,假设我们有多个 Minions,并且需要为不同的 Minions 应用不同的 States。我们可以创建一个 top.sls 文件,内容如下:
```yaml
base:
'web*':
- states.users
- states.nginx
- states.php-fpm
- states.phptest
'db*':
- states.database
```
在这个示例中,`base` 是环境名称,`'web*'` 和 `'db*'` 是 Minion 的匹配规则。对于以 `web` 开头的 Minions,我们会应用 `states.users`、`states.nginx`、`states.php-fpm` 和 `states.phptest` 这些 States;对于以 `db` 开头的 Minions,我们会应用 `states.database` 这个 State。
#### 部署与验证
完成了所有的配置编写之后,我们就可以进行部署和验证了。
##### 部署
我们使用 Terraform 来部署基础设施,并让 SaltStack 进行配置。具体步骤如下:
1. 确保 Terraform 已经正确安装并配置好 AWS 凭证。
2. 在 Terraform 模板中,使用合适的资源来创建 EC2 实例,并在实例启动时拉取 SaltStack 的配置代码。以下是一个简单的 Terraform 模板示例:
```hcl
provider "aws" {
region = "us-east-1"
}
resource "aws_instance" "web_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y git
git clone ssh://[email protected]/v1/repos/salt /srv/salt
salt-call state.apply
EOF
}
```
在这个示例中,我们创建了一个 EC2 实例,并在实例启动时使用 `user_data` 来安装 Git,克隆 SaltStack 配置代码,并执行 `salt-call state.apply` 来应用配置。
##### 验证
部署完成后,我们需要验证配置是否正确应用。可以通过以下方式进行验证:
- **SSH 登录**:通过 SSH 登录到 EC2 实例,检查用户账户是否创建、服务是否正常运行。例如,使用 `systemctl status nginx` 来检查 NGINX 服务的状态。
- **访问测试页面**:如果配置了测试页面,可以通过浏览器访问实例的公共 IP 地址来验证页面是否正常显示。
#### 总结与最佳实践
通过以上的实践,我们可以总结出一些使用 SaltStack 进行配置管理的最佳实践:
- **遵循命名约定**:无论是主机名还是配置文件的命名,都应该遵循一致的命名约定,这样可以提高代码的可读性和可维护性。
- **代码复用**:将配置代码分解为小的、可复用的模块,这样可以提高代码的复用性和灵活性。
- **持续集成与持续部署(CI/CD)**:结合 CI/CD 工具,如 Jenkins 或 GitLab CI/CD,实现自动化的部署和配置更新。
整个 SaltStack 配置管理的流程总结如下表:
| 步骤 | 描述 |
| --- | --- |
| 准备环境 | 创建 IAM 用户和 CodeCommit 仓库,配置 awscli |
| 编写配置 | 编写 States、Pillars、Grains 和 Top 文件 |
| 编写 Terraform 模板 | 描述基础设施并在实例启动时拉取配置代码 |
| 部署与验证 | 使用 Terraform 部署基础设施,验证配置是否正确应用 |
同时,我们可以将整个流程用以下流程图表示:
```mermaid
graph LR
A[准备环境] --> B[编写配置]
B --> C[编写 Terraform 模板]
C --> D[部署基础设施]
D --> E[验证配置]
E --> F{配置是否正确?}
F -- 是 --> G[完成]
F -- 否 --> B[编写配置]
```
通过这些步骤和最佳实践,我们可以高效地使用 SaltStack 进行基础设施的配置管理,确保系统的稳定性和可扩展性。在实际应用中,我们可以根据不同的业务需求和场景,对配置进行灵活调整,以满足各种复杂的业务需求。
0
0
复制全文
相关推荐








