使用 Ansible 自动化 RHEL 管理任务

AnsibleAnsibleIntermediate
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

引言

在本篇综合实验中,你将掌握使用 Ansible 自动化关键的 Linux 管理任务。在你掌握了文件管理的基础后,你将探索如何管理系统管理的完整生命周期,从软件安装到用户管理、服务配置、存储配置和网络设置。

你将首先使用 ansible.builtin.dnfansible.builtin.yum_repositoryansible.builtin.rpm_key 等模块来自动化软件包管理和仓库配置。接下来,你将创建和管理用户账户,配置 SSH 访问,并设置 sudo 权限。本实验将逐步进行服务管理、使用 cron 和 systemd 进行计划任务,然后进阶到使用 LVM 和文件系统操作进行存储管理。最后,你将配置网络接口并收集系统信息。

本实验强调在企业 Linux 环境中常见的真实场景,帮助你有效地实施基础设施即代码(Infrastructure as Code)实践。

配置仓库和管理软件包

在本步骤中,你将学习如何使用 Ansible 在 RHEL 系统上自动化软件包管理。你将配置一个 Yum 仓库,管理 RPM GPG 密钥,并在托管主机上收集软件包信息。这对于在你的基础设施中维护一致的软件安装至关重要。

你将使用几个关键模块:ansible.builtin.yum_repository 用于仓库管理,ansible.builtin.rpm_key 用于 GPG 密钥处理,ansible.builtin.package_facts 用于收集软件包信息,以及 ansible.builtin.dnf 用于软件包安装。

  1. 首先,设置项目环境并安装 Ansible。

    安装 ansible-core 包并导航到项目目录。

    sudo dnf install -y ansible-core
    cd ~/project
    mkdir system-software
    cd system-software
  2. 创建 Ansible inventory 文件 来定义我们的托管主机。在本实验中,我们将管理本地机器。

    cat << EOF > inventory.ini
    [webservers]
    localhost ansible_connection=local
    
    [all:children]
    webservers
    EOF
  3. 创建主 playbook repo_playbook.yml,它将管理仓库配置和软件包安装。此 playbook 演示了一个完整的软件管理工作流程。

    nano repo_playbook.yml

    添加以下内容来创建一个全面的软件管理 playbook:

    ---
    - name: Repository Configuration and Software Management
      hosts: all
      become: true
      vars:
        custom_pkg: httpd
      tasks:
        - name: Gather Package Facts
          ansible.builtin.package_facts:
            manager: auto
    
        - name: Show Package Facts for the custom package (initial check)
          ansible.builtin.debug:
            var: ansible_facts['packages'][custom_pkg]
          when: custom_pkg in ansible_facts['packages']
    
        - name: Ensure EPEL repository is configured
          ansible.builtin.yum_repository:
            name: epel
            description: Extra Packages for Enterprise Linux 9
            file: epel
            baseurl: https://download.fedoraproject.org/pub/epel/9/Everything/x86_64/
            gpgcheck: yes
            gpgkey: https://download.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-9
            enabled: yes
            state: present
    
        - name: Import EPEL GPG key
          ansible.builtin.rpm_key:
            state: present
            key: https://download.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-9
    
        - name: Install the custom package
          ansible.builtin.dnf:
            name: "{{ custom_pkg }}"
            state: present
    
        - name: Gather Package Facts (after installation)
          ansible.builtin.package_facts:
            manager: auto
    
        - name: Show Package Facts for the custom package (post-installation)
          ansible.builtin.debug:
            var: ansible_facts['packages'][custom_pkg]
          when: custom_pkg in ansible_facts['packages']
    
        - name: Display installed package version
          ansible.builtin.debug:
            msg: "{{ custom_pkg }} version {{ ansible_facts['packages'][custom_pkg][0]['version'] }} is installed"
          when: custom_pkg in ansible_facts['packages']

    注意: 本实验中的配置文件相当长,为了保持文档简洁,此处不提供详细解释。如果你需要任何部分的解释,请点击代码块左下角的“Explain Code”按钮,获取 Labby 提供的详细解释。

    此 playbook 演示了几个重要概念:

    • 软件包事实收集:展示了如何收集已安装软件包的信息
    • 仓库管理:配置 EPEL 仓库并进行适当的 GPG 验证
    • GPG 密钥管理:导入仓库的 GPG 密钥以确保安全
    • 软件包安装:使用 dnf 模块安装指定的软件包
    • 验证:通过更新的事实确认软件包安装
  4. 执行 playbook 以查看完整的软件管理工作流程。

    ansible-playbook -i inventory.ini repo_playbook.yml

    你应该会看到输出显示:

    • 初始软件包检查(由于 httpd 未安装,可能被跳过)
    • 仓库配置
    • GPG 密钥导入
    • 软件包安装
    • 最终验证显示已安装的软件包
  5. 验证仓库配置,通过检查创建的仓库文件。

    cat /etc/yum.repos.d/epel.repo

    你应该会看到 EPEL 仓库配置,其中包含 baseurl、gpgcheck 和 gpgkey 设置。

  6. 测试软件包管理的幂等性,再次运行 playbook。

    ansible-playbook -i inventory.ini repo_playbook.yml

    请注意,Ansible 会报告不需要更改的任务为“ok”,这展示了其幂等性。

你已成功使用 Ansible 自动化了软件软件包管理,包括仓库配置、GPG 密钥管理和软件包安装验证。

自动化用户管理和 SSH 配置

在本步骤中,你将学习如何使用 Ansible 自动化用户账户管理、SSH 配置和 sudo 权限。这对于在你的基础设施中维护一致的用户访问和安全策略至关重要。

你将使用诸如 ansible.builtin.user 用于用户管理,ansible.builtin.group 用于组创建,ansible.posix.authorized_key 用于 SSH 密钥管理,以及 ansible.builtin.lineinfile 用于配置文件修改等模块。

  1. 导航到新的项目目录以进行用户管理任务。

    cd ~/project
    mkdir system-users
    cd system-users

    安装 ansible.posix collection。

    ansible-galaxy collection install ansible.posix
  2. 创建本次实验的 inventory 文件

    cat << EOF > inventory.ini
    [webservers]
    localhost ansible_connection=local
    EOF
  3. 创建一个变量文件 来定义我们要管理的用户的组和用户。

    mkdir vars
    nano vars/users_vars.yml

    添加以下内容来定义用户账户及其组的成员关系:

    ---
    users:
      - username: webuser1
        groups: webadmin
      - username: webuser2
        groups: webadmin
      - username: devuser1
        groups: webadmin
  4. 为我们的用户生成 SSH 密钥对。在实际环境中,用户会提供他们的公钥。

    mkdir files
    
    ## 为每个用户生成 SSH 密钥
    ssh-keygen -t rsa -b 2048 -f files/webuser1.key -N "" -C "[email protected]"
    ssh-keygen -t rsa -b 2048 -f files/webuser2.key -N "" -C "[email protected]"
    ssh-keygen -t rsa -b 2048 -f files/devuser1.key -N "" -C "[email protected]"
  5. 创建主用户管理 playbook users.yml。此 playbook 将创建组、用户,分发 SSH 密钥,并配置 sudo 访问。

    nano users.yml

    添加以下全面的用户管理 playbook:

    ---
    - name: Create and manage user accounts
      hosts: webservers
      become: true
      vars_files:
        - vars/users_vars.yml
      tasks:
        - name: Create webadmin group
          ansible.builtin.group:
            name: webadmin
            state: present
    
        - name: Create user accounts
          ansible.builtin.user:
            name: "{{ item['username'] }}"
            groups: "{{ item['groups'] }}"
            shell: /bin/bash
            create_home: yes
            state: present
          loop: "{{ users }}"
    
        - name: Set up SSH authorized keys
          ansible.posix.authorized_key:
            user: "{{ item['username'] }}"
            key: "{{ lookup('file', 'files/' + item['username'] + '.key.pub') }}"
            state: present
          loop: "{{ users }}"
    
        - name: Configure sudo access for webadmin group
          ansible.builtin.lineinfile:
            path: /etc/sudoers.d/webadmin
            state: present
            create: yes
            mode: "0440"
            line: "%webadmin ALL=(ALL) NOPASSWD: ALL"
            validate: /usr/sbin/visudo -cf %s
    
        - name: Configure SSH to disable root login
          ansible.builtin.lineinfile:
            dest: /etc/ssh/sshd_config
            regexp: "^PermitRootLogin"
            line: "PermitRootLogin no"
            backup: yes
          notify: restart sshd
    
        - name: Configure SSH to disable password authentication
          ansible.builtin.lineinfile:
            dest: /etc/ssh/sshd_config
            regexp: "^PasswordAuthentication"
            line: "PasswordAuthentication no"
            backup: yes
          notify: restart sshd
    
      handlers:
        - name: restart sshd
          ansible.builtin.service:
            name: sshd
            state: restarted

    此 playbook 演示了几个用户管理最佳实践:

    • 组管理:创建管理组
    • 用户创建:设置用户账户及其正确的家目录
    • SSH 密钥管理:分发公钥以进行基于密钥的认证
    • Sudo 配置:安全地授予管理权限
    • SSH 加固:禁用 root 登录和密码认证
    • 服务管理:在配置更改时重启 SSH 服务
  6. 执行用户管理 playbook

    ansible-playbook -i inventory.ini users.yml

    Playbook 将创建用户,设置 SSH 密钥,配置 sudo 访问,并加固 SSH 配置。

  7. 验证用户创建和组的成员关系

    ## 检查用户是否已创建
    getent passwd webuser1 webuser2 devuser1
    
    ## 检查组的成员关系
    groups webuser1
    groups webuser2
    groups devuser1
    
    ## 验证 webadmin 组是否存在
    getent group webadmin
  8. 测试其中一个已创建用户的 SSH 密钥认证

    ## 测试 SSH 密钥认证(这将连接到 localhost)
    ssh -i files/webuser1.key webuser1@localhost "whoami"
  9. 验证 sudo 配置,通过测试无密码的 sudo 访问。

    ## 测试 webuser1 的 sudo 访问
    ssh -i files/webuser1.key webuser1@localhost "sudo whoami"
  10. 检查 SSH 配置更改

    ## 验证 SSH 配置
    sudo grep "PermitRootLogin\|PasswordAuthentication" /etc/ssh/sshd_config
    
    ## 检查 sudo 配置
    sudo cat /etc/sudoers.d/webadmin

你已成功使用 Ansible 自动化了用户账户创建、SSH 密钥分发和安全配置,为跨基础设施的安全用户管理奠定了基础。

自动化服务管理和任务调度

在本步骤中,你将学习如何使用 Ansible 管理 systemd 服务、调度 cron 作业以及配置系统启动目标。这对于维护服务可用性和自动化基础设施中的例行任务至关重要。

你将使用诸如 ansible.builtin.service 用于服务管理,ansible.builtin.cron 用于调度任务,ansible.posix.at 用于一次性任务,以及 ansible.builtin.systemd 用于系统目标管理等模块。

  1. 导航到新的项目目录以进行服务和进程管理。

    cd ~/project
    mkdir system-process
    cd system-process
  2. 创建本次实验的 inventory 文件

    cat << EOF > inventory.ini
    [webservers]
    localhost ansible_connection=local
    EOF
  3. 创建一个 playbook 来管理 Apache HTTP 服务器服务。这将演示基本的服务管理。

    nano service_management.yml

    添加以下内容:

    ---
    - name: Manage Apache HTTP Server Service
      hosts: webservers
      become: true
      tasks:
        - name: Start and enable httpd service
          ansible.builtin.service:
            name: httpd
            state: started
            enabled: yes
    
        - name: Create a simple index.html
          ansible.builtin.copy:
            content: |
              <html>
              <head><title>Ansible Managed Server</title></head>
              <body>
              <h1>Welcome to Ansible Managed Apache Server</h1>
              <p>This server is configured and managed by Ansible.</p>
              <p>Service started at: $(date)</p>
              </body>
              </html>
            dest: /var/www/html/index.html
            owner: apache
            group: apache
            mode: "0644"
    
        - name: Verify httpd service is running
          ansible.builtin.service_facts:
    
        - name: Display httpd service status
          ansible.builtin.debug:
            var: ansible_facts.services['httpd.service']
  4. 创建一个用于调度 cron 作业的 playbook。这演示了自动化任务调度。

    nano create_crontab_file.yml

    添加以下内容:

    ---
    - name: Schedule recurring cron jobs
      hosts: webservers
      become: true
      tasks:
        - name: Create labex user for cron jobs
          ansible.builtin.user:
            name: labex
            state: present
            create_home: yes
    
        - name: Schedule system monitoring cron job
          ansible.builtin.cron:
            name: System monitoring log
            job: "date >> /home/labex/system_monitor.log && df -h >> /home/labex/system_monitor.log"
            minute: "*/5"
            hour: "*"
            user: labex
            cron_file: system-monitoring
            state: present
    
        - name: Schedule daily log rotation
          ansible.builtin.cron:
            name: Daily log cleanup
            job: "find /home/labex -name '*.log' -mtime +7 -delete"
            minute: "0"
            hour: "2"
            weekday: "*"
            user: labex
            cron_file: log-cleanup
            state: present
    
        - name: Schedule weekly system update check
          ansible.builtin.cron:
            name: Weekly update check
            job: "dnf check-update > /home/labex/update_check.log 2>&1"
            minute: "0"
            hour: "3"
            weekday: "0"
            user: labex
            cron_file: update-check
            state: present
  5. 创建一个使用 at 调度一次性任务的 playbook

    nano schedule_at_task.yml

    添加以下内容:

    ---
    - name: Schedule one-time tasks with at
      hosts: webservers
      become: true
      become_user: labex
      tasks:
        - name: Schedule immediate system info collection
          ansible.posix.at:
            command: "uname -a > ~/system_info_$(date +%Y%m%d_%H%M%S).txt"
            count: 2
            units: minutes
            unique: yes
            state: present
    
        - name: Schedule delayed service status check
          ansible.posix.at:
            command: "systemctl status httpd > ~/httpd_status_$(date +%Y%m%d_%H%M%S).txt"
            count: 5
            units: minutes
            unique: yes
            state: present
  6. 创建一个管理系统启动目标的 playbook

    nano boot_target_management.yml

    添加以下内容:

    ---
    - name: Manage system boot targets
      hosts: webservers
      become: true
      tasks:
        - name: Check current default target
          ansible.builtin.command:
            cmd: systemctl get-default
          register: current_target
          changed_when: false
    
        - name: Display current boot target
          ansible.builtin.debug:
            msg: "Current default boot target: {{ current_target.stdout }}"
    
        - name: Set default boot target to multi-user
          ansible.builtin.systemd:
            name: multi-user.target
            enabled: yes
          when: current_target.stdout != "multi-user.target"
    
        - name: Verify the boot target change
          ansible.builtin.command:
            cmd: systemctl get-default
          register: new_target
          changed_when: false
    
        - name: Display new boot target
          ansible.builtin.debug:
            msg: "New default boot target: {{ new_target.stdout }}"
  7. 执行服务管理 playbook

    ansible-playbook -i inventory.ini service_management.yml

    这将启动 httpd 服务并创建一个欢迎页面。

  8. 执行 cron 作业调度 playbook

    ansible-playbook -i inventory.ini create_crontab_file.yml

    这将为系统监控和维护创建几个计划任务。

  9. 执行一次性任务调度 playbook

    ansible-playbook -i inventory.ini schedule_at_task.yml

    这将使用 at 命令调度即时任务。

  10. 执行启动目标管理 playbook

    ansible-playbook -i inventory.ini boot_target_management.yml

    这将检查并可能修改系统的默认启动目标。

  11. 验证计划任务和服务

    ## 检查 cron 作业
    sudo cat /etc/cron.d/system-monitoring
    sudo cat /etc/cron.d/log-cleanup
    sudo cat /etc/cron.d/update-check
    
    ## 检查 at 作业
    sudo atq
    
    ## 检查 httpd 服务状态
    sudo systemctl status httpd
    
    ## 测试 Web 服务器
    curl localhost
    
    ## 检查系统监控日志(等待几分钟让 cron 运行)
    sudo cat /home/labex/system_monitor.log
  12. 创建一个清理 playbook 以在需要时删除计划任务。

    nano remove_scheduled_tasks.yml

    添加以下内容:

    ---
    - name: Remove scheduled tasks
      hosts: webservers
      become: true
      tasks:
        - name: Remove system monitoring cron job
          ansible.builtin.cron:
            name: System monitoring log
            user: labex
            cron_file: system-monitoring
            state: absent
    
        - name: Remove log cleanup cron job
          ansible.builtin.cron:
            name: Daily log cleanup
            user: labex
            cron_file: log-cleanup
            state: absent
    
        - name: Remove update check cron job
          ansible.builtin.cron:
            name: Weekly update check
            user: labex
            cron_file: update-check
            state: absent

你已成功使用 Ansible 自动化了服务管理、任务调度和系统配置,为自动维护和监控你的基础设施奠定了基础。

使用 LVM 和文件系统自动化存储管理

在本步骤中,你将学习如何使用 Ansible 自动化存储管理,包括创建 LVM 物理卷、卷组、逻辑卷和文件系统。这对于以一致且可扩展的方式管理基础设施中的存储资源至关重要。

你将使用诸如 ansible.builtin.lvg 用于卷组管理,ansible.builtin.lvol 用于逻辑卷创建,ansible.builtin.filesystem 用于文件系统创建,以及 ansible.posix.mount 用于挂载点管理等模块。

  1. 导航到新的项目目录以进行存储管理。

    cd ~/project
    mkdir system-storage
    cd system-storage
  2. 创建本次实验的 inventory 文件

    cat << EOF > inventory.ini
    [webservers]
    localhost ansible_connection=local
    EOF
  3. 创建一个 loop device 来模拟额外的存储,因为我们是在虚拟环境中工作。

    ## 创建一个 1GB 的文件用作虚拟磁盘
    sudo dd if=/dev/zero of=/tmp/virtual_disk bs=1M count=1024
    
    ## 设置 loop device
    sudo losetup /dev/loop0 /tmp/virtual_disk
    
    ## 验证 loop device
    lsblk | grep loop0
  4. 创建一个全面的存储管理 playbook。这将演示 LVM 操作和文件系统管理。

    nano storage.yml

    添加以下内容:

    ---
    - name: Configure LVM storage and filesystems
      hosts: webservers
      become: true
      vars:
        storage_device: /dev/loop0
        volume_group: apache-vg
        content_lv: content-lv
        logs_lv: logs-lv
        backup_lv: backup-lv
      tasks:
        - name: Install LVM utilities
          ansible.builtin.dnf:
            name: lvm2
            state: present
    
        - name: Create physical volume
          community.general.lvg:
            vg: "{{ volume_group }}"
            pvs: "{{ storage_device }}"
            state: present
    
        - name: Create logical volume for web content
          community.general.lvol:
            vg: "{{ volume_group }}"
            lv: "{{ content_lv }}"
            size: 256m
            state: present
    
        - name: Create logical volume for logs
          community.general.lvol:
            vg: "{{ volume_group }}"
            lv: "{{ logs_lv }}"
            size: 256m
            state: present
    
        - name: Create logical volume for backup
          community.general.lvol:
            vg: "{{ volume_group }}"
            lv: "{{ backup_lv }}"
            size: 256m
            state: present
    
        - name: Create XFS filesystem on content volume
          community.general.filesystem:
            fstype: xfs
            dev: "/dev/{{ volume_group }}/{{ content_lv }}"
            force: no
    
        - name: Create XFS filesystem on logs volume
          community.general.filesystem:
            fstype: xfs
            dev: "/dev/{{ volume_group }}/{{ logs_lv }}"
            force: no
    
        - name: Create ext4 filesystem on backup volume
          community.general.filesystem:
            fstype: ext4
            dev: "/dev/{{ volume_group }}/{{ backup_lv }}"
            force: no
    
        - name: Create mount points
          ansible.builtin.file:
            path: "{{ item }}"
            state: directory
            mode: "0755"
          loop:
            - /var/www
            - /var/log/httpd
            - /backup
    
        - name: Mount web content volume
          ansible.posix.mount:
            path: /var/www
            src: "/dev/{{ volume_group }}/{{ content_lv }}"
            fstype: xfs
            opts: defaults
            state: mounted
    
        - name: Mount logs volume
          ansible.posix.mount:
            path: /var/log/httpd
            src: "/dev/{{ volume_group }}/{{ logs_lv }}"
            fstype: xfs
            opts: defaults
            state: mounted
    
        - name: Mount backup volume
          ansible.posix.mount:
            path: /backup
            src: "/dev/{{ volume_group }}/{{ backup_lv }}"
            fstype: ext4
            opts: defaults
            state: mounted
    
        - name: Set appropriate ownership for web content
          ansible.builtin.file:
            path: /var/www
            owner: apache
            group: apache
            recurse: yes
    
        - name: Set appropriate ownership for httpd logs
          ansible.builtin.file:
            path: /var/log/httpd
            owner: apache
            group: apache
            recurse: yes
    
        - name: Create html directory for web content
          ansible.builtin.file:
            path: /var/www/html
            state: directory
            owner: apache
            group: apache
            mode: "0755"
    
        - name: Create sample web content
          ansible.builtin.copy:
            content: |
              <html>
              <head><title>Storage Management Demo</title></head>
              <body>
              <h1>LVM Storage Configuration</h1>
              <p>This content is served from an LVM logical volume managed by Ansible.</p>
              <p>Volume Group: {{ volume_group }}</p>
              <p>Logical Volume: {{ content_lv }}</p>
              <p>Filesystem: XFS</p>
              </body>
              </html>
            dest: /var/www/html/storage.html
            owner: apache
            group: apache
            mode: "0644"
  5. 安装所需的 Ansible collection 以进行 LVM 管理。

    ansible-galaxy collection install community.general
  6. 执行存储管理 playbook

    ansible-playbook -i inventory.ini storage.yml

    这将创建 LVM 结构、文件系统和挂载点。

  7. 创建一个 playbook 来收集和显示存储信息

    nano get-storage.yml

    添加以下内容:

    ---
    - name: Gather storage information
      hosts: webservers
      become: true
      tasks:
        - name: Gather disk facts
          ansible.builtin.setup:
            gather_subset:
              - hardware
    
        - name: Display volume group information
          ansible.builtin.command:
            cmd: vgdisplay apache-vg
          register: vg_info
          changed_when: false
    
        - name: Display logical volume information
          ansible.builtin.command:
            cmd: lvdisplay apache-vg
          register: lv_info
          changed_when: false
    
        - name: Display filesystem information
          ansible.builtin.command:
            cmd: df -h /var/www /var/log/httpd /backup
          register: fs_info
          changed_when: false
    
        - name: Display mount information
          ansible.builtin.command:
            cmd: cat /proc/mounts
          register: mount_info
          changed_when: false
    
        - name: Show volume group details
          ansible.builtin.debug:
            var: vg_info.stdout_lines
    
        - name: Show logical volume details
          ansible.builtin.debug:
            var: lv_info.stdout_lines
    
        - name: Show filesystem usage
          ansible.builtin.debug:
            var: fs_info.stdout_lines
    
        - name: Show fstab entries
          ansible.builtin.command:
            cmd: grep apache-vg /etc/fstab
          register: fstab_entries
          changed_when: false
          failed_when: false
    
        - name: Display fstab entries
          ansible.builtin.debug:
            var: fstab_entries.stdout_lines
  8. 执行存储信息收集 playbook

    ansible-playbook -i inventory.ini get-storage.yml

    这将显示有关已创建存储结构的详细信息。

  9. 手动验证存储配置

    ## 检查 LVM 结构
    sudo vgs apache-vg
    sudo lvs apache-vg
    sudo pvs /dev/loop0
    
    ## 检查文件系统
    df -h /var/www /var/log/httpd /backup
    
    ## 检查挂载点
    mount | grep apache-vg
    
    ## 检查 fstab 条目
    grep apache-vg /etc/fstab
    
    ## 测试 Web 内容
    cat /var/www/html/storage.html
  10. 创建一个存储扩展 playbook 来演示扩展操作。

    nano expand_storage.yml

    添加以下内容:

    ---
    - name: Expand storage volumes
      hosts: webservers
      become: true
      vars:
        volume_group: apache-vg
        content_lv: content-lv
      tasks:
        - name: Extend content logical volume
          community.general.lvol:
            vg: "{{ volume_group }}"
            lv: "{{ content_lv }}"
            size: 400m
            state: present
    
        - name: Extend XFS filesystem
          community.general.filesystem:
            fstype: xfs
            dev: "/dev/{{ volume_group }}/{{ content_lv }}"
            resizefs: yes
    
        - name: Display updated filesystem size
          ansible.builtin.command:
            cmd: df -h /var/www
          register: new_size
          changed_when: false
    
        - name: Show new filesystem size
          ansible.builtin.debug:
            var: new_size.stdout_lines
  11. 测试存储扩展

    ## 在扩展前检查当前大小
    df -h /var/www
    
    ## 运行扩展 playbook
    ansible-playbook -i inventory.ini expand_storage.yml
    
    ## 验证扩展
    df -h /var/www
    sudo lvs apache-vg/content-lv

你已成功使用 Ansible 自动化了 LVM 存储管理,包括物理卷创建、逻辑卷管理、文件系统创建和挂载点配置。

自动化网络配置和信息收集

在最后一步中,你将学习如何使用 Ansible 自动化网络接口配置和收集全面的系统信息。这完成了从软件到存储再到网络的系统管理自动化的完整流程。

你将使用诸如 ansible.builtin.template 用于网络配置文件,收集网络事实,以及创建全面的系统报告等模块。

  1. 导航到新的项目目录以进行网络管理。

    cd ~/project
    mkdir system-network
    cd system-network
  2. 创建本次实验的 inventory 文件

    cat << EOF > inventory.ini
    [webservers]
    localhost ansible_connection=local
    EOF
  3. 创建一个全面的网络和系统信息收集 playbook

    nano network_info.yml

    添加以下内容:

    ---
    - name: Gather comprehensive system information
      hosts: webservers
      become: true
      tasks:
        - name: Gather all system facts
          ansible.builtin.setup:
    
        - name: Create system report directory
          ansible.builtin.file:
            path: /tmp/system_reports
            state: directory
            mode: "0755"
    
        - name: Generate system information report
          ansible.builtin.template:
            src: system_report.j2
            dest: /tmp/system_reports/system_info_{{ ansible_facts['hostname'] }}.html
            mode: "0644"
    
        - name: Generate network configuration report
          ansible.builtin.template:
            src: network_report.j2
            dest: /tmp/system_reports/network_info_{{ ansible_facts['hostname'] }}.html
            mode: "0644"
    
        - name: Collect network interface information
          ansible.builtin.command:
            cmd: ip addr show
          register: ip_info
          changed_when: false
    
        - name: Collect routing information
          ansible.builtin.command:
            cmd: ip route show
          register: route_info
          changed_when: false
    
        - name: Collect DNS configuration
          ansible.builtin.command:
            cmd: cat /etc/resolv.conf
          register: dns_info
          changed_when: false
    
        - name: Display network summary
          ansible.builtin.debug:
            msg: |
              System: {{ ansible_facts['hostname'] }}
              OS: {{ ansible_facts['distribution'] }} {{ ansible_facts['distribution_version'] }}
              Kernel: {{ ansible_facts['kernel'] }}
              Default IPv4: {{ ansible_facts['default_ipv4']['address'] | default('N/A') }}
              Default Interface: {{ ansible_facts['default_ipv4']['interface'] | default('N/A') }}
              Total Memory: {{ ansible_facts['memtotal_mb'] }}MB
              CPU Cores: {{ ansible_facts['processor_vcpus'] }}
  4. 创建模板目录和文件以用于报告

    mkdir templates
    nano templates/system_report.j2

    为系统报告模板添加以下内容:

    <!doctype html>
    <html>
      <head>
        <title>System Report - {{ ansible_facts['hostname'] }}</title>
        <style>
          body {
            font-family: Arial, sans-serif;
            margin: 20px;
          }
          .section {
            margin-bottom: 20px;
            padding: 10px;
            border: 1px solid #ccc;
          }
          .header {
            background-color: #f5f5f5;
            padding: 10px;
          }
          table {
            border-collapse: collapse;
            width: 100%;
          }
          th,
          td {
            border: 1px solid #ddd;
            padding: 8px;
            text-align: left;
          }
          th {
            background-color: #f2f2f2;
          }
        </style>
      </head>
      <body>
        <div class="header">
          <h1>System Report for {{ ansible_facts['hostname'] }}</h1>
          <p>
            Generated on: {{ ansible_date_time.date }} {{ ansible_date_time.time
            }}
          </p>
        </div>
    
        <div class="section">
          <h2>System Information</h2>
          <table>
            <tr>
              <th>Property</th>
              <th>Value</th>
            </tr>
            <tr>
              <td>Hostname</td>
              <td>{{ ansible_facts['hostname'] }}</td>
            </tr>
            <tr>
              <td>FQDN</td>
              <td>{{ ansible_facts['fqdn'] }}</td>
            </tr>
            <tr>
              <td>Operating System</td>
              <td>
                {{ ansible_facts['distribution'] }} {{
                ansible_facts['distribution_version'] }}
              </td>
            </tr>
            <tr>
              <td>Kernel</td>
              <td>{{ ansible_facts['kernel'] }}</td>
            </tr>
            <tr>
              <td>Architecture</td>
              <td>{{ ansible_facts['architecture'] }}</td>
            </tr>
            <tr>
              <td>CPU Cores</td>
              <td>{{ ansible_facts['processor_vcpus'] }}</td>
            </tr>
            <tr>
              <td>Total Memory</td>
              <td>{{ ansible_facts['memtotal_mb'] }}MB</td>
            </tr>
            <tr>
              <td>Uptime</td>
              <td>{{ ansible_facts['uptime_seconds'] }} seconds</td>
            </tr>
          </table>
        </div>
    
        <div class="section">
          <h2>Storage Information</h2>
          <table>
            <tr>
              <th>Mount Point</th>
              <th>Filesystem</th>
              <th>Size</th>
              <th>Used</th>
              <th>Available</th>
            </tr>
            {% for mount in ansible_facts['mounts'] %}
            <tr>
              <td>{{ mount.mount }}</td>
              <td>{{ mount.fstype }}</td>
              <td>
                {{ (mount.size_total / 1024 / 1024 / 1024) | round(2) }}GB
              </td>
              <td>
                {{ ((mount.size_total - mount.size_available) / 1024 / 1024 /
                1024) | round(2) }}GB
              </td>
              <td>
                {{ (mount.size_available / 1024 / 1024 / 1024) | round(2) }}GB
              </td>
            </tr>
            {% endfor %}
          </table>
        </div>
    
        <div class="section">
          <h2>Services Status</h2>
          <table>
            <tr>
              <th>Service</th>
              <th>Status</th>
            </tr>
            <tr>
              <td>httpd</td>
              <td>
                {{ ansible_facts.services['httpd.service']['state'] |
                default('not installed') }}
              </td>
            </tr>
            <tr>
              <td>sshd</td>
              <td>
                {{ ansible_facts.services['sshd.service']['state'] |
                default('unknown') }}
              </td>
            </tr>
            <tr>
              <td>NetworkManager</td>
              <td>
                {{ ansible_facts.services['NetworkManager.service']['state'] |
                default('unknown') }}
              </td>
            </tr>
          </table>
        </div>
      </body>
    </html>
  5. 创建网络报告模板

    nano templates/network_report.j2

    添加以下内容:

    <!doctype html>
    <html>
      <head>
        <title>Network Report - {{ ansible_facts['hostname'] }}</title>
        <style>
          body {
            font-family: Arial, sans-serif;
            margin: 20px;
          }
          .section {
            margin-bottom: 20px;
            padding: 10px;
            border: 1px solid #ccc;
          }
          .header {
            background-color: #f5f5f5;
            padding: 10px;
          }
          table {
            border-collapse: collapse;
            width: 100%;
          }
          th,
          td {
            border: 1px solid #ddd;
            padding: 8px;
            text-align: left;
          }
          th {
            background-color: #f2f2f2;
          }
          pre {
            background-color: #f9f9f9;
            padding: 10px;
            overflow-x: auto;
          }
        </style>
      </head>
      <body>
        <div class="header">
          <h1>Network Configuration Report</h1>
          <p>Host: {{ ansible_facts['hostname'] }}</p>
          <p>
            Generated on: {{ ansible_date_time.date }} {{ ansible_date_time.time
            }}
          </p>
        </div>
    
        <div class="section">
          <h2>Network Interfaces</h2>
          <table>
            <tr>
              <th>Interface</th>
              <th>IPv4 Address</th>
              <th>IPv6 Address</th>
              <th>MAC Address</th>
              <th>Status</th>
            </tr>
            {% for interface_name in ansible_facts['interfaces'] %} {% if
            interface_name != 'lo' %} {% set interface_facts =
            ansible_facts[interface_name] %}
            <tr>
              <td>{{ interface_name }}</td>
              <td>
                {{ interface_facts.get('ipv4', {}).get('address', 'N/A') }}
              </td>
              <td>
                {{ interface_facts.get('ipv6', [{}])[0].get('address', 'N/A') if
                interface_facts.get('ipv6') else 'N/A' }}
              </td>
              <td>{{ interface_facts.get('macaddress', 'N/A') }}</td>
              <td>
                {{ interface_facts.get('active', false) | ternary('Active',
                'Inactive') }}
              </td>
            </tr>
            {% endif %} {% endfor %}
          </table>
        </div>
    
        <div class="section">
          <h2>Default Gateway</h2>
          <table>
            <tr>
              <th>Property</th>
              <th>Value</th>
            </tr>
            <tr>
              <td>Default IPv4 Address</td>
              <td>
                {{ ansible_facts['default_ipv4']['address'] | default('N/A') }}
              </td>
            </tr>
            <tr>
              <td>Default Interface</td>
              <td>
                {{ ansible_facts['default_ipv4']['interface'] | default('N/A')
                }}
              </td>
            </tr>
            <tr>
              <td>Default Gateway</td>
              <td>
                {{ ansible_facts['default_ipv4']['gateway'] | default('N/A') }}
              </td>
            </tr>
          </table>
        </div>
    
        <div class="section">
          <h2>DNS Configuration</h2>
          <table>
            <tr>
              <th>DNS Servers</th>
            </tr>
            {% for dns in ansible_facts['dns']['nameservers'] %}
            <tr>
              <td>{{ dns }}</td>
            </tr>
            {% endfor %}
          </table>
        </div>
      </body>
    </html>
  6. 创建一个网络接口配置 playbook

    nano configure_network.yml

    添加以下内容:

    ---
    - name: Configure network settings
      hosts: webservers
      become: true
      tasks:
        - name: Install NetworkManager if not present
          ansible.builtin.dnf:
            name: NetworkManager
            state: present
    
        - name: Ensure NetworkManager is running
          ansible.builtin.service:
            name: NetworkManager
            state: started
            enabled: yes
    
        - name: Configure hosts file with system information
          ansible.builtin.lineinfile:
            path: /etc/hosts
            line: "{{ ansible_facts['default_ipv4']['address'] }} {{ ansible_facts['hostname'] }}.lab.example.com {{ ansible_facts['hostname'] }}"
            regexp: ".*{{ ansible_facts['hostname'] }}.*"
            backup: yes
    
        - name: Create network monitoring script
          ansible.builtin.copy:
            content: |
              #!/bin/bash
              ## Network monitoring script generated by Ansible
              echo "=== Network Status Report ==="
              echo "Generated at: $(date)"
              echo
              echo "=== Interface Status ==="
              ip addr show
              echo
              echo "=== Routing Table ==="
              ip route show
              echo
              echo "=== DNS Configuration ==="
              cat /etc/resolv.conf
              echo
              echo "=== Network Connectivity Test ==="
              ping -c 3 8.8.8.8
            dest: /usr/local/bin/network-status.sh
            mode: "0755"
    
        - name: Create network information gathering cron job
          ansible.builtin.cron:
            name: Network status monitoring
            job: "/usr/local/bin/network-status.sh >> /var/log/network-status.log 2>&1"
            minute: "*/15"
            user: root
            cron_file: network-monitoring
            state: present
  7. 执行网络信息收集 playbook

    ansible-playbook -i inventory.ini network_info.yml

    这将生成全面的系统和网络报告。

  8. 执行网络配置 playbook

    ansible-playbook -i inventory.ini configure_network.yml

    这将配置网络设置和监控。

  9. 查看生成的报告

    ## 列出生成的报告
    ls -la /tmp/system_reports/
    
    ## 查看系统报告(你也可以在浏览器中打开)
    cat /tmp/system_reports/system_info_*.html
    
    ## 检查网络监控脚本
    cat /usr/local/bin/network-status.sh
    
    ## 测试网络监控脚本
    sudo /usr/local/bin/network-status.sh
    
    ## 检查网络监控 cron 作业
    sudo cat /etc/cron.d/network-monitoring
  10. 创建一个最终的综合 playbook,将所有学到的概念结合起来。

    nano complete_system_setup.yml

    添加以下内容:

    ---
    - name: Complete system setup and configuration
      hosts: webservers
      become: true
      vars:
        admin_users:
          - webuser1
          - webuser2
      tasks:
        - name: Ensure all required packages are installed
          ansible.builtin.dnf:
            name:
              - httpd
              - lvm2
              - NetworkManager
              - cronie
            state: present
    
        - name: Ensure all services are running
          ansible.builtin.service:
            name: "{{ item }}"
            state: started
            enabled: yes
          loop:
            - httpd
            - NetworkManager
            - crond
    
        - name: Generate final system status report
          ansible.builtin.template:
            src: system_report.j2
            dest: /tmp/final_system_report.html
            mode: "0644"
    
        - name: Display completion message
          ansible.builtin.debug:
            msg: |
              ============================================
              RHEL System Administration Automation Complete!
              ============================================
    
              Summary of configured components:
              - Software: EPEL repository and packages installed
              - Users: {{ admin_users | length }} administrative users created
              - Services: httpd, NetworkManager, and crond configured
              - Storage: LVM volumes and filesystems configured
              - Network: Interface configuration and monitoring set up
              - Scheduling: Cron jobs and at tasks configured
    
              Reports available at:
              - /tmp/system_reports/
              - /tmp/final_system_report.html
    
              Your RHEL system is now fully automated with Ansible!
  11. 执行最终的综合设置

    ansible-playbook -i inventory.ini complete_system_setup.yml

你已成功完成了使用 Ansible 对 RHEL 系统管理任务的全面自动化,涵盖了软件管理、用户管理、服务管理、存储配置和网络设置。

  1. 在 Web 浏览器中预览生成的报告

    导航到报告目录:

    cd /tmp/system_reports

    使用 Python 启动一个临时 Web 服务器:

    python -m http.server 8000

    要在浏览器中预览报告:

    1. 点击顶部菜单栏的 + 按钮
    2. 选择 Web Service
    3. 输入端口号 8000
    4. 你现在可以在线查看生成的 HTML 报告了
    在 Web 浏览器中预览生成的报告

    Web 服务器将显示一个目录列表,你可以在其中点击 HTML 文件来查看由你的 Ansible playbook 生成的全面系统和网络报告。

    注意: 查看完报告后,按 Ctrl+C 停止 Web 服务器。

总结

在本次综合实验中,你已经掌握了使用 Ansible 自动化关键的 Red Hat Enterprise Linux (RHEL) 管理任务。你从软件软件包管理开始,学习了如何配置仓库、管理 GPG 密钥以及在收集软件包事实以获取系统信息的同时安装软件包。这为在你的基础设施中实现一致的软件部署奠定了基础。

接着,你进入了用户管理和身份验证环节,自动化了用户账户的创建、配置了基于 SSH 密钥的身份验证、设置了 sudo 权限,并加固了 SSH 配置。这建立了对企业环境至关重要的安全且标准化的用户访问策略。

实验继续进行服务管理和任务调度,你学习了如何管理 systemd 服务、创建周期性的 cron 作业、使用 at 调度一次性任务以及配置系统启动目标。这些技能对于维护服务可用性和自动化例行维护任务至关重要。

存储管理通过全面的 LVM 自动化进行了涵盖,包括物理卷创建、卷组管理、逻辑卷配置、文件系统创建和挂载点配置。你还学会了动态扩展存储卷,这对于可扩展的基础设施管理至关重要。

最后,你完成了网络配置自动化和全面的系统监控。你学习了如何收集网络事实、配置接口、使用 Jinja2 模板创建详细的系统报告,以及设置自动监控脚本。这提供了系统状态管理和报告的完整图景。

在整个实验过程中,你应用了基础设施即代码 (IaC) 原则,确保所有系统配置都是可复现的、版本可控的且可一致部署的。你现在已经掌握了自动化 RHEL 系统管理生命周期的技能,从初始部署到持续维护和监控。