Red Hat Certified Engineer (RHCE) #14 RHCSA 자동화 1: 사용자/그룹, 패키지/repository
#13 system roles까지 Ansible의 문법과 구조화 도구를 모두 익혔습니다. 이제부터 네 편은 그 도구를 무기로 RHCSA의 손작업을 플레이북으로 자동화하는 종합 구간입니다. RHCE 시험 비중의 절반가량이 이 “RHCSA 작업을 Ansible로 자동화"이므로, 여기서부터가 시험 비중의 핵심 구간입니다.
이번 글에서는 RHCSA에서 useradd,groupadd,dnf install로 손수 하던 일을, user,group,dnf,yum_repository 모듈로 바꿔 멱등성 있게 처리하겠습니다. 손작업이 궁금하면 RHCSA #8 사용자와 그룹, RHCSA #11 패키지와 repository를 먼저 확인하면 자동화할 대상이 또렷해집니다.
group 모듈: 그룹부터 만든다 #
사용자를 그룹에 넣으려면 그룹이 먼저 있어야 합니다. RHCSA에서 groupadd developers로 하던 일을 ansible.builtin.group 모듈로 처리합니다.
- name: developers 그룹 보장
ansible.builtin.group:
name: developers
gid: 5000
state: present주요 옵션은 단순합니다.
| 옵션 | 의미 |
|---|---|
name | 그룹 이름(필수) |
gid | 지정할 GID. 생략하면 시스템이 자동 할당 |
state | present(생성,유지) 또는 absent(삭제) |
system | true면 시스템 그룹으로 생성 |
state: present는 그룹이 없으면 만들고 이미 있으면 그대로 둡니다. 두 번 돌려도 두 번째에는 changed가 0이 되는 멱등성이 모듈의 기본 동작입니다.
user 모듈: 사용자 생성의 중심 #
ansible.builtin.user 모듈은 RHCE에서 가장 자주 쓰는 모듈 중 하나입니다. RHCSA의 useradd,usermod를 한 모듈로 묶습니다.
- name: 사용자 생성
hosts: all
become: true
tasks:
- name: 개발자 계정 생성
ansible.builtin.user:
name: jdoe
uid: 5001
comment: "John Doe"
group: developers
groups: wheel
append: true
shell: /bin/bash
state: present핵심 옵션을 정리하겠습니다.
| 옵션 | 의미 |
|---|---|
name | 사용자 이름(필수) |
uid | 지정할 UID |
group | 기본 그룹(primary group) |
groups | 보조 그룹(supplementary). 쉼표 목록 또는 리스트 |
append | true면 groups를 기존에 추가. false(기본)면 덮어씀 |
password | 암호화된 비밀번호 해시(평문 아님) |
shell | 로그인 셸. 예로 /bin/bash, /sbin/nologin |
state | present 또는 absent |
remove | state: absent와 함께 true면 홈 디렉터리까지 삭제 |
append의 함정 #
groups만 주고 append를 빠뜨리면, 기본값이 false이므로 기존 보조 그룹을 전부 덮어쓰고 지정한 그룹만 남깁니다. “사용자를 wheel 그룹에 추가하라"는 문제에서 append: true를 빠뜨리면 다른 그룹이 사라져 감점됩니다. 보조 그룹을 더하는 의도라면 append: true를 반드시 함께 적겠습니다.
password: 비밀번호는 해시로 다룬다 #
user 모듈의 password는 평문이 아니라 암호화된 해시를 받습니다. 평문을 그대로 넣으면 그 문자열 자체가 해시로 저장되어 로그인이 되지 않습니다. 해시는 password_hash 필터로 만듭니다.
- name: 비밀번호와 함께 사용자 생성
hosts: all
become: true
vars:
user_password: "{{ vault_user_password }}"
tasks:
- name: jdoe 생성과 비밀번호 설정
ansible.builtin.user:
name: jdoe
password: "{{ user_password | password_hash('sha512') }}"
shell: /bin/bash
state: present여기서 평문 비밀번호 vault_user_password는 #10 Ansible Vault에서 다룬 대로 Vault로 암호화한 변수 파일(ansible-vault create group_vars/all/vault.yml)에 두는 것이 정석입니다. 평문 비밀번호를 플레이북에 그대로 적으면 시험에서 감점 대상입니다. 실행 시에는 --vault-password-file ~/.vault_pass로 잠금을 풉니다.
password_hash('sha512')는 호출할 때마다 salt가 달라져 해시가 바뀌므로, 그대로 두면 매 실행이 changed로 보일 수 있습니다. 멱등성을 엄격히 맞춰야 한다면 고정 salt를 주는 방식도 있지만, 시험에서는 비밀번호가 올바로 설정되어 로그인이 되는지가 채점의 핵심입니다.
loop로 사용자 다수 생성: 시험 단골 #
RHCE에서 가장 자주 나오는 형태는 사용자 목록을 변수로 받아 loop로 한 번에 생성하는 패턴입니다. #9 loop에서 익힌 loop를 그대로 활용합니다. 사용자 목록을 변수 파일에 둡니다.
# group_vars/all/users.yml
users:
- { name: alice, groups: developers }
- { name: bob, groups: developers }
- { name: carol, groups: ops }그 위에 사용자를 loop로 생성합니다.
- name: 사용자 일괄 생성
ansible.builtin.user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
append: true
password: "{{ default_password | password_hash('sha512') }}"
shell: /bin/bash
state: present
loop: "{{ users }}"loop: "{{ users }}"는 리스트의 각 원소를 item으로 받아, item.name,item.groups로 dictionary 필드에 접근합니다. 사용자가 수십 명이라도 변수 목록만 늘리면 됩니다. 조건을 더하고 싶으면 when: item.state | default('present') == 'present'처럼 필드별로 분기할 수도 있습니다. 아래 통합 playbook에서 이 패턴을 그대로 활용합니다.
dnf 모듈: 패키지 관리 #
패키지 설치,삭제는 ansible.builtin.dnf 모듈로 처리합니다. RHCSA의 dnf install,dnf remove에 대응합니다.
- name: 패키지 관리
hosts: all
become: true
tasks:
- name: 여러 패키지 설치
ansible.builtin.dnf:
name:
- httpd
- mariadb-server
- vim-enhanced
state: present
- name: 패키지 최신 상태로
ansible.builtin.dnf:
name: tmux
state: latest
- name: 패키지 제거
ansible.builtin.dnf:
name: telnet
state: absent주요 옵션은 다음과 같습니다.
| 옵션 | 의미 |
|---|---|
name | 패키지 이름. 리스트로 여러 개 한 번에 |
state | present(설치), latest(최신으로 갱신), absent(제거) |
enablerepo | 특정 repository만 활성화해 설치 |
disablerepo | 특정 repository를 비활성화 |
state: present는 없으면 설치하고 있으면 그대로 두므로 멱등성이 보장됩니다. 반면 state: latest는 새 버전이 있으면 매번 갱신하므로, “특정 패키지를 설치만 하라"는 문제에는 present를 쓰는 편이 의도에 맞습니다.
패키지 그룹 설치 #
여러 패키지를 묶은 그룹은 @ 접두사로 설치합니다. RHCSA에서 dnf group install 하던 일입니다.
- name: 개발 도구 그룹 설치
ansible.builtin.dnf:
name: "@Development Tools"
state: presentmodule stream #
AppStream의 module stream은 @모듈:스트림 형식으로 지정합니다. 예로 nginx의 1.22 스트림을 설치하겠습니다.
- name: nginx 1.22 module stream 설치
ansible.builtin.dnf:
name: "@nginx:1.22"
state: present@nginx:1.22는 nginx 모듈의 1.22 스트림을 활성화하고 기본 프로파일을 설치합니다. 특정 버전 스트림을 고정해야 하는 문제에서 이 표기를 그대로 활용합니다.
yum_repository 모듈: repository 추가 #
인터넷이나 내부 서버의 repository를 등록하려면 ansible.builtin.yum_repository 모듈을 씁니다. RHCSA에서 /etc/yum.repos.d/에 .repo 파일을 손으로 만들던 일을 자동화합니다.
- name: 사내 repository 등록
hosts: all
become: true
tasks:
- name: BaseOS repo 추가
ansible.builtin.yum_repository:
name: internal-baseos
description: "Internal BaseOS Repository"
baseurl: http://repo.example.com/baseos
gpgcheck: true
gpgkey: http://repo.example.com/RPM-GPG-KEY-internal
enabled: true
state: present주요 옵션을 정리하겠습니다.
| 옵션 | 의미 |
|---|---|
name | repo ID. .repo 파일 안 섹션 이름이 됨 |
description | 사람이 읽는 설명. .repo의 name= 항목 |
baseurl | 패키지를 받아올 기준 URL |
gpgcheck | true면 GPG 서명 검증 |
gpgkey | 검증에 쓸 GPG 키 위치 |
enabled | 이 repo의 활성화 여부 |
file | 저장할 .repo 파일 이름(생략하면 name 사용) |
name은 .repo 파일의 섹션 ID가 되고, description은 그 안 name= 항목으로 들어갑니다. 두 필드가 헷갈리기 쉬우니 표로 짝을 외워 두겠습니다. gpgcheck: true로 두면 gpgkey도 함께 지정해야 설치 시 검증을 통과합니다.
시험 단골: 통합 playbook #
실제 시험에서는 위 모듈이 한 플레이북에 모입니다. “repository를 추가하고, 그 repo에서 패키지를 설치하고, 사용자 목록을 loop로 만들라"는 형태가 전형입니다. 한 파일로 묶어 보겠습니다.
- name: RHCSA 자동화 통합
hosts: webservers
become: true
vars_files:
- group_vars/all/users.yml
- group_vars/all/vault.yml
tasks:
- name: 사내 repository 등록
ansible.builtin.yum_repository:
name: internal-appstream
description: "Internal AppStream"
baseurl: http://repo.example.com/appstream
gpgcheck: false
enabled: true
- name: 웹 패키지 설치
ansible.builtin.dnf:
name:
- httpd
- "@nginx:1.22"
state: present
enablerepo: internal-appstream
- name: 운영 그룹 생성
ansible.builtin.group:
name: ops
state: present
- name: 사용자 일괄 생성
ansible.builtin.user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
append: true
password: "{{ vault_default_password | password_hash('sha512') }}"
shell: /bin/bash
state: present
loop: "{{ users }}"이 한 플레이북에 이번 글의 모듈이 모두 들어 있습니다. repository를 등록하고, enablerepo로 그 repo에서 패키지를 설치하며, 그룹을 먼저 만든 뒤 사용자를 loop로 생성하는 순서가 자연스럽습니다.
시험 포인트 #
password는 해시만 받는다. 평문을 넣으면 로그인이 안 됩니다.password_hash('sha512')필터로 해시를 만들고, 평문 비밀번호는 Vault로 암호화한 변수에 두겠습니다.append: true를 잊지 않는다. 보조 그룹을 더하라는 문제에서append를 빠뜨리면 기존 보조 그룹이 전부 사라집니다. 더하는 의도면 항상 함께 적습니다.yum_repository의name과description을 구분한다.name은 repo ID,description은.repo의name=설명입니다. 짝을 헷갈리면 검증이 어긋납니다.- module stream은
@모듈:스트림표기. 예로@nginx:1.22처럼 적습니다. state: present와latest를 구분한다. “설치만"이면present, “최신으로"면latest입니다.- 그룹을 사용자보다 먼저 만든다. 사용자의 보조 그룹이 없으면 작업이 실패합니다. 한 플레이북 안에서 group 작업을 user 작업 위에 둡니다.
정리 #
이번 글에서 자동화한 것:
- group 모듈.
name,gid,state로 그룹을 멱등하게 생성 - user 모듈.
name,uid,group,groups,append,shell,state. 비밀번호는password_hash+Vault - dnf 모듈.
name,state(present/latest/absent),enablerepo. 패키지 그룹은@그룹, module stream은@모듈:스트림 - yum_repository 모듈.
name,description,baseurl,gpgcheck,gpgkey,enabled로 repo 등록 - loop 패턴. 사용자 목록 변수를 loop로 일괄 생성하는 시험 단골
- 통합 playbook. repo 등록 → 패키지 설치 → 그룹,사용자 생성을 한 파일로
다음: RHCSA 자동화 2 #
사용자,그룹과 패키지,repository를 플레이북으로 옮겼습니다. 이제 그 위에서 도는 서비스를 자동화할 차례입니다.
#15 RHCSA 자동화 2: 서비스, chronyd, log에서는 service,systemd 모듈로 서비스를 시작,활성화하는 법, chronyd 시간 동기화를 plays로 구성하는 법, 그리고 로그 관련 설정까지 손작업을 모듈로 바꿔 정리하겠습니다.