Ansible 을 이용한 RHEL 관리 작업 자동화

AnsibleBeginner
지금 연습하기

소개

이 종합적인 실습에서는 Ansible 을 사용하여 중요한 Linux 관리 작업을 자동화하는 방법을 마스터하게 됩니다. 파일 관리 기초를 바탕으로 소프트웨어 설치부터 사용자 관리, 서비스 구성, 스토리지 프로비저닝, 네트워크 설정에 이르기까지 시스템 관리의 전체 수명 주기를 관리하는 방법을 탐색합니다.

ansible.builtin.dnf, ansible.builtin.yum_repository, ansible.builtin.rpm_key와 같은 모듈을 사용하여 소프트웨어 패키지 관리 및 리포지토리 구성을 자동화하는 것부터 시작합니다. 다음으로 사용자 계정을 생성 및 관리하고, SSH 액세스를 구성하며, sudo 권한을 설정합니다. 실습은 LVM 및 파일 시스템 작업을 사용한 스토리지 관리로 진행하기 전에 cron 및 systemd 를 사용한 예약 작업, 서비스 관리로 진행됩니다. 마지막으로 네트워크 인터페이스를 구성하고 시스템 정보를 수집합니다.

이 실습은 엔터프라이즈 Linux 환경에서 일반적으로 발생하는 실제 시나리오를 강조하여 Infrastructure as Code(IaC) 관행을 효과적으로 구현할 수 있도록 준비시킵니다.

이것은 가이드 실험입니다. 학습과 실습을 돕기 위한 단계별 지침을 제공합니다.각 단계를 완료하고 실무 경험을 쌓기 위해 지침을 주의 깊게 따르세요. 과거 데이터에 따르면, 이것은 초급 레벨의 실험이며 완료율은 86%입니다.학습자들로부터 62%의 긍정적인 리뷰율을 받았습니다.

리포지토리 구성 및 소프트웨어 패키지 관리

이 단계에서는 Ansible 을 사용하여 RHEL 시스템에서 소프트웨어 패키지 관리를 자동화하는 방법을 배웁니다. Yum 리포지토리를 구성하고, RPM GPG 키를 관리하며, 관리 대상 호스트에서 패키지 정보를 수집합니다. 이는 인프라 전반에 걸쳐 일관된 소프트웨어 설치를 유지하는 데 필수적입니다.

리포지토리 관리를 위한 ansible.builtin.yum_repository, GPG 키 처리를 위한 ansible.builtin.rpm_key, 패키지 정보 수집을 위한 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 인벤토리 파일을 생성합니다. 이 실습에서는 로컬 머신을 관리합니다.

    cat << EOF > inventory.ini
    [webservers]
    localhost ansible_connection=local
    
    [all:children]
    webservers
    EOF
  3. 리포지토리 구성 및 패키지 설치를 관리할 메인 플레이북 repo_playbook.yml을 생성합니다. 이 플레이북은 완전한 소프트웨어 관리 워크플로우를 보여줍니다.

    nano repo_playbook.yml

    포괄적인 소프트웨어 관리 플레이북을 생성하기 위해 다음 내용을 추가합니다:

    ---
    - 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 의 자세한 설명을 확인할 수 있습니다.

    이 플레이북은 다음과 같은 몇 가지 중요한 개념을 보여줍니다:

    • 패키지 사실 수집: 설치된 패키지에 대한 정보를 수집하는 방법
    • 리포지토리 관리: 올바른 GPG 검증으로 EPEL 리포지토리 구성
    • GPG 키 관리: 보안을 위해 리포지토리의 GPG 키 가져오기
    • 패키지 설치: dnf 모듈을 사용하여 지정된 패키지 설치
    • 검증: 업데이트된 사실로 패키지 설치 확인
  4. 플레이북을 실행하여 완전한 소프트웨어 관리 워크플로우를 확인합니다.

    ansible-playbook -i inventory.ini repo_playbook.yml

    다음과 같은 출력을 볼 수 있습니다:

    • 초기 패키지 확인 (httpd 가 설치되지 않았으므로 건너뛸 가능성이 높음)
    • 리포지토리 구성
    • GPG 키 가져오기
    • 패키지 설치
    • 설치된 패키지를 보여주는 최종 확인
  5. 생성된 리포지토리 파일을 확인하여 리포지토리 구성을 검증합니다.

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

    baseurl, gpgcheck 및 gpgkey 설정이 포함된 EPEL 리포지토리 구성을 볼 수 있습니다.

  6. 플레이북을 다시 실행하여 패키지 관리의 멱등성 (idempotency) 을 테스트합니다.

    ansible-playbook -i inventory.ini repo_playbook.yml

    Ansible 이 변경이 필요하지 않은 작업에 대해 "ok"를 보고하는 것을 확인하여 멱등성을 보여줍니다.

Ansible 을 사용하여 리포지토리 구성, GPG 키 관리 및 패키지 설치 확인을 포함한 소프트웨어 패키지 관리를 성공적으로 자동화했습니다.

사용자 관리 및 SSH 구성 자동화

이 단계에서는 Ansible 을 사용하여 사용자 계정 관리, SSH 구성 및 sudo 권한을 자동화하는 방법을 배웁니다. 이는 인프라 전반에 걸쳐 일관된 사용자 액세스 및 보안 정책을 유지하는 데 중요합니다.

사용자 관리를 위한 ansible.builtin.user, 그룹 생성을 위한 ansible.builtin.group, SSH 키 관리를 위한 ansible.posix.authorized_key, 구성 파일 수정을 위한 ansible.builtin.lineinfile과 같은 모듈을 사용합니다.

  1. 사용자 관리 작업을 위한 새 프로젝트 디렉토리로 이동합니다.

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

    ansible.posix 컬렉션을 설치합니다.

    ansible-galaxy collection install ansible.posix
  2. 이 연습을 위한 인벤토리 파일을 생성합니다.

    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
    
    ## Generate SSH keys for each user
    ssh-keygen -t rsa -b 2048 -f files/webuser1.key -N "" -C "webuser1@example.com"
    ssh-keygen -t rsa -b 2048 -f files/webuser2.key -N "" -C "webuser2@example.com"
    ssh-keygen -t rsa -b 2048 -f files/devuser1.key -N "" -C "devuser1@example.com"
  5. 주요 사용자 관리 플레이북 users.yml을 생성합니다. 이 플레이북은 그룹, 사용자를 생성하고, SSH 키를 배포하며, sudo 액세스를 구성합니다.

    nano users.yml

    다음의 포괄적인 사용자 관리 플레이북을 추가합니다:

    ---
    - 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

    이 플레이북은 몇 가지 사용자 관리 모범 사례를 보여줍니다:

    • 그룹 관리: 관리 그룹 생성
    • 사용자 생성: 올바른 홈 디렉토리로 사용자 계정 설정
    • SSH 키 관리: 키 기반 인증을 위한 공개 키 배포
    • Sudo 구성: 관리자 권한을 안전하게 부여
    • SSH 강화: 루트 로그인 및 비밀번호 인증 비활성화
    • 서비스 관리: 구성 변경 시 SSH 서비스 재시작
  6. 사용자 관리 플레이북을 실행합니다.

    ansible-playbook -i inventory.ini users.yml

    플레이북은 사용자를 생성하고, SSH 키를 설정하며, sudo 액세스를 구성하고, SSH 구성을 강화합니다.

  7. 사용자 생성 및 그룹 멤버십을 확인합니다.

    ## Check if users were created
    getent passwd webuser1 webuser2 devuser1
    
    ## Check group membership
    groups webuser1
    groups webuser2
    groups devuser1
    
    ## Verify webadmin group exists
    getent group webadmin
  8. 생성된 사용자 중 한 명에 대해 SSH 키 인증을 테스트합니다.

    ## Test SSH key authentication (this will connect to localhost)
    ssh -i files/webuser1.key webuser1@localhost "whoami"
  9. 비밀번호 없는 sudo 액세스를 테스트하여 sudo 구성을 확인합니다.

    ## Test sudo access for webuser1
    ssh -i files/webuser1.key webuser1@localhost "sudo whoami"
  10. SSH 구성 변경 사항을 확인합니다.

    ## Verify SSH configuration
    sudo grep "PermitRootLogin\|PasswordAuthentication" /etc/ssh/sshd_config
    
    ## Check sudo configuration
    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. 이 연습을 위한 인벤토리 파일을 생성합니다.

    cat << EOF > inventory.ini
    [webservers]
    localhost ansible_connection=local
    EOF
  3. 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 작업을 스케줄링하는 플레이북을 생성합니다. 이는 자동화된 작업 스케줄링을 시연합니다.

    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을 사용하여 일회성 작업 스케줄링을 위한 플레이북을 생성합니다.

    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. 시스템 부팅 타겟을 관리하는 플레이북을 생성합니다.

    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. 서비스 관리 플레이북을 실행합니다.

    ansible-playbook -i inventory.ini service_management.yml

    이것은 httpd 서비스를 시작하고 환영 페이지를 생성합니다.

  8. cron 작업 스케줄링 플레이북을 실행합니다.

    ansible-playbook -i inventory.ini create_crontab_file.yml

    이것은 시스템 모니터링 및 유지보수를 위한 여러 예약된 작업을 생성합니다.

  9. 일회성 작업 스케줄링 플레이북을 실행합니다.

    ansible-playbook -i inventory.ini schedule_at_task.yml

    이것은 at 명령을 사용하여 즉시 작업을 예약합니다.

  10. 부팅 타겟 관리 플레이북을 실행합니다.

    ansible-playbook -i inventory.ini boot_target_management.yml

    이것은 시스템의 기본 부팅 타겟을 확인하고 잠재적으로 수정합니다.

  11. 예약된 작업 및 서비스를 확인합니다.

    ## Check cron jobs
    sudo cat /etc/cron.d/system-monitoring
    sudo cat /etc/cron.d/log-cleanup
    sudo cat /etc/cron.d/update-check
    
    ## Check at jobs
    sudo atq
    
    ## Check httpd service status
    sudo systemctl status httpd
    
    ## Test the web server
    curl localhost
    
    ## Check system monitoring log (wait a few minutes for cron to run)
    sudo cat /home/labex/system_monitor.log
  12. 필요할 때 예약된 작업을 제거하는 정리 플레이북을 생성합니다.

    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 물리 볼륨 (physical volumes), 볼륨 그룹 (volume groups), 논리 볼륨 (logical volumes), 파일 시스템을 생성하여 스토리지 관리를 자동화하는 방법을 배웁니다. 이는 인프라 전반에 걸쳐 스토리지 리소스를 일관되고 확장 가능한 방식으로 관리하는 데 중요합니다.

볼륨 그룹 관리를 위한 ansible.builtin.lvg, 논리 볼륨 생성을 위한 ansible.builtin.lvol, 파일 시스템 생성을 위한 ansible.builtin.filesystem, 마운트 포인트 관리를 위한 ansible.posix.mount와 같은 모듈을 사용합니다.

  1. 스토리지 관리를 위한 새 프로젝트 디렉토리로 이동합니다.

    cd ~/project
    mkdir system-storage
    cd system-storage
  2. 이 연습을 위한 인벤토리 파일을 생성합니다.

    cat << EOF > inventory.ini
    [webservers]
    localhost ansible_connection=local
    EOF
  3. 가상 환경에서 작업하므로 추가 스토리지를 시뮬레이션하기 위해 루프 장치 (loop device) 를 생성합니다.

    ## Create a 1GB file to use as a virtual disk
    sudo dd if=/dev/zero of=/tmp/virtual_disk bs=1M count=1024
    
    ## Set up loop device
    sudo losetup /dev/loop0 /tmp/virtual_disk
    
    ## Verify the loop device
    lsblk | grep loop0
  4. 포괄적인 스토리지 관리 플레이북을 생성합니다. 이는 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. LVM 관리에 필요한 Ansible 컬렉션을 설치합니다.

    ansible-galaxy collection install community.general
  6. 스토리지 관리 플레이북을 실행합니다.

    ansible-playbook -i inventory.ini storage.yml

    이것은 LVM 구조, 파일 시스템 및 마운트 포인트를 생성합니다.

  7. 스토리지 정보를 수집하고 표시하는 플레이북을 생성합니다.

    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. 스토리지 정보 수집 플레이북을 실행합니다.

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

    이것은 생성된 스토리지 구조에 대한 자세한 정보를 표시합니다.

  9. 스토리지 구성을 수동으로 확인합니다.

    ## Check LVM structure
    sudo vgs apache-vg
    sudo lvs apache-vg
    sudo pvs /dev/loop0
    
    ## Check filesystems
    df -h /var/www /var/log/httpd /backup
    
    ## Check mount points
    mount | grep apache-vg
    
    ## Check fstab entries
    grep apache-vg /etc/fstab
    
    ## Test the web content
    cat /var/www/html/storage.html
  10. 확장 작업을 시연하기 위해 스토리지 확장 플레이북을 생성합니다.

    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. 스토리지 확장을 테스트합니다.

    ## Check current size before expansion
    df -h /var/www
    
    ## Run the expansion playbook
    ansible-playbook -i inventory.ini expand_storage.yml
    
    ## Verify the expansion
    df -h /var/www
    sudo lvs apache-vg/content-lv

Ansible 을 사용하여 물리 볼륨 생성, 논리 볼륨 관리, 파일 시스템 생성 및 마운트 포인트 구성을 포함한 LVM 스토리지 관리 자동화를 성공적으로 수행했습니다.

네트워크 구성 및 정보 수집 자동화

이 마지막 단계에서는 Ansible 을 사용하여 네트워크 인터페이스 구성을 자동화하고 포괄적인 시스템 정보를 수집하는 방법을 배웁니다. 이를 통해 소프트웨어부터 스토리지, 네트워킹까지 시스템 관리 자동화의 전체 범위를 완료합니다.

네트워크 구성 파일을 위한 ansible.builtin.template 모듈을 사용하고, 네트워크 사실 (facts) 을 수집하며, 포괄적인 시스템 보고서를 생성합니다.

  1. 네트워크 관리를 위한 새 프로젝트 디렉토리로 이동합니다.

    cd ~/project
    mkdir system-network
    cd system-network
  2. 이 연습을 위한 인벤토리 파일을 생성합니다.

    cat << EOF > inventory.ini
    [webservers]
    localhost ansible_connection=local
    EOF
  3. 포괄적인 네트워크 및 시스템 정보 수집 플레이북을 생성합니다.

    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. 네트워크 인터페이스 구성 플레이북을 생성합니다.

    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. 네트워크 정보 수집 플레이북을 실행합니다.

    ansible-playbook -i inventory.ini network_info.yml

    이것은 포괄적인 시스템 및 네트워크 보고서를 생성합니다.

  8. 네트워크 구성 플레이북을 실행합니다.

    ansible-playbook -i inventory.ini configure_network.yml

    이것은 네트워크 설정 및 모니터링을 구성합니다.

  9. 생성된 보고서를 확인합니다.

    ## List generated reports
    ls -la /tmp/system_reports/
    
    ## View the system report (you can also open in a browser)
    cat /tmp/system_reports/system_info_*.html
    
    ## Check the network monitoring script
    cat /usr/local/bin/network-status.sh
    
    ## Test the network monitoring script
    sudo /usr/local/bin/network-status.sh
    
    ## Check the network monitoring cron job
    sudo cat /etc/cron.d/network-monitoring
  10. 학습한 모든 개념을 결합하는 최종 포괄 플레이북을 생성합니다.

    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. 웹 브라우저에서 생성된 보고서를 미리 봅니다.

    보고서 디렉토리로 이동합니다:

    cd /tmp/system_reports

    Python을 사용하여 임시 웹 서버를 시작합니다:

    python -m http.server 8000

    브라우저에서 보고서를 미리 보려면 다음 단계를 따르세요:

    1. 상단 메뉴 표시줄에서 + 버튼을 클릭합니다.
    2. Web Service를 선택합니다.
    3. 포트 번호 8000을 입력합니다.
    4. 이제 생성된 HTML 보고서를 온라인으로 볼 수 있습니다.
    Preview the generated reports in a web browser

    웹 서버는 디렉토리 목록을 표시하며, 여기서 HTML 파일을 클릭하여 Ansible 플레이북에서 생성된 포괄적인 시스템 및 네트워크 보고서를 볼 수 있습니다.

    참고: 보고서 보기가 끝나면 Ctrl+C를 눌러 웹 서버를 중지하십시오.

요약

이 종합 실습을 통해 Ansible 을 사용하여 필수적인 Red Hat Enterprise Linux (RHEL) 관리 작업을 자동화하는 방법을 익혔습니다. 저장소 구성, GPG 키 관리, 패키지 설치 및 패키지 사실 (facts) 을 통한 시스템 정보 수집 방법을 배우면서 소프트웨어 패키지 관리부터 시작했습니다. 이를 통해 인프라 전반에 걸쳐 일관된 소프트웨어 배포의 기반을 마련했습니다.

이후 사용자 관리 및 인증으로 진행하여 사용자 계정 생성 자동화, SSH 키 기반 인증 구성, sudo 권한 설정, SSH 구성 강화 방법을 배웠습니다. 이를 통해 엔터프라이즈 환경에 필수적인 안전하고 표준화된 사용자 액세스 정책을 수립했습니다.

실습은 서비스 관리 및 작업 스케줄링으로 이어져, systemd 서비스 관리, 주기적인 cron 작업 생성, at을 사용한 일회성 작업 예약, 시스템 부팅 대상 구성 방법을 배웠습니다. 이러한 기술은 서비스 가용성을 유지하고 일상적인 유지보수 작업을 자동화하는 데 필수적입니다.

스토리지 관리는 물리 볼륨 생성, 볼륨 그룹 관리, 논리 볼륨 프로비저닝, 파일 시스템 생성 및 마운트 포인트 구성을 포함한 포괄적인 LVM 자동화를 통해 다루었습니다. 또한 확장 가능한 인프라 관리에 중요한 스토리지 볼륨을 동적으로 확장하는 방법도 배웠습니다.

마지막으로 네트워크 구성 자동화 및 포괄적인 시스템 모니터링을 완료했습니다. 네트워크 사실 수집, 인터페이스 구성, Jinja2 템플릿을 사용한 상세 시스템 보고서 생성, 자동화된 모니터링 스크립트 설정 방법을 배웠습니다. 이를 통해 시스템 상태 관리 및 보고의 전체적인 그림을 파악할 수 있었습니다.

이 실습 전반에 걸쳐 인프라스트럭처 코드 (IaC) 원칙을 적용하여 모든 시스템 구성이 재현 가능하고, 버전 관리되며, 일관되게 배포될 수 있도록 했습니다. 이제 초기 배포부터 지속적인 유지보수 및 모니터링까지 RHEL 시스템 관리의 전체 수명 주기를 자동화할 수 있는 기술을 갖추게 되었습니다.