Red Hat Certified Engineer (RHCE) #14 RHCSA 自動化 1: ユーザー/グループ、パッケージ/repository

読了 9分

#13 system roles まで Ansible の文法と構造化ツールをすべて身につけました。ここからの 4 編は、そのツールを武器に RHCSA の手作業をプレイブックで自動化する 総合区間です。RHCE 試験の比重の半分ほどがこの「RHCSA の作業を Ansible で自動化」なので、ここからが実質的に本番です。

この記事では、RHCSA で useraddgroupadddnf install で手作業していたことを、usergroupdnfyum_repository モジュールに置き換えて冪等に処理します。手作業が気になるなら、RHCSA #8 ユーザーとグループRHCSA #11 パッケージと repository を先に確認すると、自動化する対象がはっきりします。

group モジュール: まずグループを作る #

ユーザーをグループに入れるには、グループが先に存在している必要があります。RHCSA で groupadd developers でしていたことを ansible.builtin.group モジュールで処理します。

developersグループ作成
- name: developers グループを保証
  ansible.builtin.group:
    name: developers
    gid: 5000
    state: present

主なオプションはシンプルです。

オプション意味
nameグループ名 (必須)
gid指定する GID。省略するとシステムが自動割り当て
statepresent (作成・維持) または absent (削除)
systemtrue ならシステムグループとして作成

state: present はグループがなければ作り、すでにあればそのままにします。2 回回しても 2 回目には changed が 0 になる冪等性が、モジュールの既定の動作です。

user モジュール: ユーザー作成の中心 #

ansible.builtin.user モジュールは RHCE で最もよく使うモジュールの 1 つです。RHCSA の useraddusermod を 1 つのモジュールにまとめます。

user moduleでアカウント作成
- 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)。カンマ区切りまたはリスト
appendtrue なら groups を既存に追加。false (既定) なら上書き
password暗号化されたパスワードハッシュ (平文ではない)
shellログインシェル。例として /bin/bash/sbin/nologin
statepresent または absent
removestate: absent と一緒に true ならホームディレクトリまで削除

append の落とし穴 #

groups だけ与えて append を抜かすと、既定値が false なので 既存の補助グループをすべて上書きして 指定したグループだけを残します。「ユーザーを wheel グループに追加せよ」という問題で append: true を抜かすと、他のグループが消えて減点されます。補助グループを足す意図なら、append: true を必ず一緒に書きます。

password: パスワードはハッシュで扱う #

user モジュールの password は平文ではなく 暗号化されたハッシュ を受け取ります。平文をそのまま入れると、その文字列自体がハッシュとして保存されてログインできません。ハッシュは password_hash フィルターで作ります。

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
# group_vars/all/users.yml
users:
  - { name: alice, groups: developers }
  - { name: bob,   groups: developers }
  - { name: carol, groups: ops }

続いて、ユーザーを loop で一括作成します。

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.nameitem.groups で dictionary のフィールドにアクセスします。ユーザーが数十人いても変数の一覧を増やすだけで済みます。条件を足したいなら、when: item.state | default('present') == 'present' のようにフィールドごとに分岐することもできます。下の統合 playbook でこのパターンをそのまま活用します。

dnf モジュール: パッケージ管理 #

パッケージのインストール・削除は ansible.builtin.dnf モジュールで処理します。RHCSA の dnf installdnf remove に対応します。

dnf moduleでパッケージ管理
- 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パッケージ名。リストで複数を一度に
statepresent (インストール)、latest (最新に更新)、absent (削除)
enablerepo特定の repository だけを有効化してインストール
disablerepo特定の repository を無効化

state: present はなければインストールしてあればそのままにするので、冪等性が保証されます。一方 state: latest は新しいバージョンがあれば毎回更新するので、「特定のパッケージをインストールするだけ」という問題には present を使う方が意図に合います。

パッケージグループのインストール #

複数のパッケージをまとめたグループは @ 接頭辞でインストールします。RHCSA で dnf group install していたことです。

パッケージグループのインストール
- name: 開発ツールグループをインストール
  ansible.builtin.dnf:
    name: "@Development Tools"
    state: present

module stream #

AppStream の module stream は @モジュール:ストリーム 形式で指定します。例として nginx の 1.22 ストリームをインストールします。

module streamのインストール
- 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 ファイルを手で作っていたことを自動化します。

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

主なオプションを整理します。

オプション意味
namerepo ID。.repo ファイル内のセクション名になる
description人が読む説明。.reponame= 項目
baseurlパッケージを取得する基準 URL
gpgchecktrue なら GPG 署名を検証
gpgkey検証に使う GPG キーの場所
enabledこの repo の有効化の可否
file保存する .repo ファイル名 (省略すると name を使用)

name.repo ファイルのセクション ID になり、description はその中の name= 項目として入ります。2 つのフィールドは紛らわしいので、表で対を覚えておきます。gpgcheck: true にすると gpgkey も一緒に指定しないと、インストール時に検証を通りません。

試験定番: 統合 playbook #

実際の試験では、上のモジュールが 1 つのプレイブックに集まります。「repository を追加し、その repo からパッケージをインストールし、ユーザーの一覧を loop で作れ」という形が典型です。1 つのファイルにまとめてみます。

統合プレイブック
- 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 }}"

この 1 つのプレイブックに、この記事のモジュールがすべて入っています。repository を登録し、enablerepo でその repo からパッケージをインストールし、グループを先に作ってからユーザーを loop で作成する順序が自然です。

試験ポイント #

  • password はハッシュだけを受け取る。 平文を入れるとログインできません。password_hash('sha512') フィルターでハッシュを作り、平文パスワードは Vault で暗号化した変数に置きます。
  • append: true を忘れない。 補助グループを足せという問題で append を抜かすと、既存の補助グループがすべて消えます。足す意図なら常に一緒に書きます。
  • yum_repositorynamedescription を区別する。 name は repo ID、description.reponame= 説明です。対を取り違えると検証がずれます。
  • module stream は @モジュール:ストリーム 表記。 例として @nginx:1.22 のように書きます。
  • state: presentlatest を区別する。 「インストールだけ」なら present、「最新に」なら latest です。
  • グループをユーザーより先に作る。 ユーザーの補助グループがなければ task が失敗します。1 つのプレイブックの中で group task を user task の上に置きます。

まとめ #

この記事で自動化したこと:

  • group モジュールnamegidstate でグループを冪等に作成
  • user モジュールnameuidgroupgroupsappendshellstate。パスワードは password_hash + Vault
  • dnf モジュールnamestate (present/latest/absent)・enablerepo。パッケージグループは @グループ、module stream は @モジュール:ストリーム
  • yum_repository モジュールnamedescriptionbaseurlgpgcheckgpgkeyenabled で repo を登録
  • loop パターン。ユーザーの一覧変数を loop で一括作成する試験定番
  • 統合 playbook。repo 登録 → パッケージインストール → グループ・ユーザー作成を 1 つのファイルで

次へ: RHCSA 自動化 2 #

ユーザー・グループとパッケージ・repository をプレイブックに移しました。次はその上で動くサービスを自動化する番です。

#15 RHCSA 自動化 2: サービス、chronyd、log では、servicesystemd モジュールでサービスを起動・有効化する方法、chronyd の時刻同期を plays で構成する方法、そしてログ関連の設定まで、手作業をモジュールに置き換えて整理します。

X