Linux Emulatorのパッケージ管理をdnfで行う

投稿者: | 2026年5月17日

前回のLinux Emulatorを動かすに続き、もう少し踏み込んで設定します。

そもそも、Linux Emulator環境を本格的に使う場合、FreeBSDの公式パッケージで提供されているパッケージだけでは足りません。必要なソフトをコンパイルしてインストールするなど追加の手間がかかります。
そこで、パッケージの管理をRHEL系標準のdnfで行うようにし、必要なパッケージはRockyLinux公式からインストールできるようにします。
これで、より多くのパッケージが手軽に使えるようになり、Linux Emulator環境を本格的に使えるようになります。

前提

  • FreeBSD 15 amd64
  • RockRockyLinux 9.7(RHEL 9.7互換)
  • ベースパッケージだけFreeBSD公式のlinux_base-rl9を使い、ほかのパッケージはすべてdnfで管理する

設定方針

まずdnfを動かすために必要なパッケージをrpmから展開します。
そのあと、パッケージのインベントリーの整合性をとるために、dnfで同じパッケージを上書きインストールします。
dnfが動くようになったらネットワークツールのパッケージや開発ツールなどをdnfでインストールしてきます。

Playbook

roles/freebsd/tasks/linux_emulation_rockylinux.yaml

必要なパッケージのインストール

rpm形式のパッケージをインストールするためにrpm2cpio、パッケージ情報が記述されたxmlファイルをパースするためにxmlstarletをまず最初にインストールします。

- name: Ensure rpm2cpio installed
  community.general.pkgng:
    name: rpm2cpio
    state: present

- name: Ensure xmlstarlet installed
  community.general.pkgng:
    name: xmlstarlet
    state: present

各種定義

インストール対象のRockyLinuxのバージョン、アーキテクチャ、サイトを定義します。

- name: Set RockyLinux version and architecture
  set_fact:
    rockylinux_ver: "9.7"
    rockylinux_arch: "x86_64"

- name: Set RockyLinux package site
  set_fact:
    rockylinux_devel_site: "https://dl.rockylinux.org/pub/rocky/{{ rockylinux_ver }}/devel/{{ rockylinux_arch }}/os"

パッケージの情報取得

まず全パッケージ情報を取得し、それをパースしてsqlite3フォーマットのパッケージ情報データベースを取得します。
そしてそのデータベースを読み出し、パッケージ情報のJSONデータを生成します。

次に、dnfに必要なパッケージを定義し、そのパッケージ情報にマッチするrpmパッケージをダウンロードし、展開します。
この時、冪等性を実現するために/compat/linux/var/db/rpm2cpio_stampsを作成し、一度展開したパッケージを除外するようにします。

- name: Get repomd.xml
  get_url:
    url: "{{ rockylinux_devel_site }}/repodata/repomd.xml"
    dest: /compat/linux/repomd.xml

- name: Extract primary sqlite path
  shell: |
    xml sel \
      -N r="http://linux.duke.edu/metadata/repo" \
      -t \
      -m '//r:data[@type="primary_db"]/r:location' \
      -v '@href' \
      -n \
      /compat/linux/repomd.xml
  register: primary_db
  changed_when: false

- name: Download primary sqlite
  get_url:
    url: "{{ rockylinux_devel_site }}/{{ primary_db.stdout }}"
    dest: /compat/linux/primary.sqlite.bz2

- name: Generate package JSON
  shell: |
    sqlite3 /compat/linux/primary.sqlite <<'EOF'
    .mode json

    select
      substr(location_href,
             instr(location_href, 'Packages/') + 9,
             1) as category,

      pkgKey,

      name as basename,
      version,
      release,
      arch,

      location_href as path,

      replace(
        location_href,
        'Packages/' ||
        substr(location_href,
               instr(location_href, 'Packages/') + 9,
               1) ||
        '/',
        ''
      ) as rpm,

      '{{ rockylinux_devel_site }}/' || location_href as url

    from packages;
    EOF
  register: package_json
  changed_when: false

- debug:
    var: package_json
  when:
    - verbose | default(false)

- name: Build package list
  set_fact:
    rockylinux_packages: "{{ package_json.stdout | from_json }}"

- debug:
    var: rockylinux_packages
  when:
    - verbose | default(false)

- name: Set RockyLinux yum/dnf packages
  set_fact:
    dnf_packages:
      - audit_libs_rpm
      - ca-certificates_rpm
      - crypto_policies_rpm
      - cyrus_sasl_lib_rpm
      - dnf_rpm
      - dnf_data_rpm
      - dnf_plugins_core_rpm
      - elfutils_libelf_rpm
      - elfutils_libs_rpm
      - expat_rpm
      - file_libs_rpm
      - filesystem_rpm
      - gnupg2_rpm
      - gpgme_rpm
      - ima_evm_utils_rpm
      - json_c_rpm
      - libassuan_rpm
      - libcap_ng_rpm
      - libcomps_rpm
      - libcurl_rpm
      - libdnf_rpm
      - libevent_rpm
      - libgomp_rpm
      - libgpg_error_rpm
      - libmodulemd_rpm
      - libnghttp2_rpm
      - librepo_rpm
      - libselinux_rpm
      - libsolv_rpm
      - libssh_rpm
      - libtasn1_rpm
      - libxml2_rpm
      - libyaml_rpm
      - lua_libs_rpm
      - openldap_rpm
      - p11_kit_rpm
      - p11_kit_trust_rpm
      - python3_rpm
      - python3_dnf_rpm
      - python3_gpg_rpm
      - python3_libs_rpm
      - python3_libdnf_rpm
      - python3_hawkey_rpm
      - python3_libcomps_rpm
      - python3_rpm_rpm
      - rocky_repos_rpm
      - rocky_gpg_keys_rpm
      - setup_rpm
      - shadow_utils_rpm
      - rpm_rpm
      - rpm_libs_rpm
      - rpm_build_libs_rpm
      - rpm_sign_rpm
      - rpm_sign_libs_rpm
      - tpm2_tss_rpm
      - yum_rpm
      - yum_utils_rpm

- name: Clean and initialize package list
  set_fact:
    cleaned_dnf_packages: |
      {{ dnf_packages
       | map('regex_replace', '_rpm$', '')
       | map('regex_replace', '_', '-')
       | list }}
    filtered_rockylinux_packages: []

- name: Filter RockyLinux package list
  set_fact:
    filtered_rockylinux_packages: >-
      {{
        filtered_rockylinux_packages +
        [
          (
            rockylinux_packages
            | selectattr('basename', 'equalto', item)
            | rejectattr('rpm', 'search', 'i686')
            | list
          )
          | selectattr('release', 'equalto', (
              rockylinux_packages
              | selectattr('basename', 'equalto', item)
              | rejectattr('rpm', 'search', 'i686')
              | map(attribute='release')
              | list
              | community.general.version_sort
              | last
          ))
          | first
        ]
      }}
  when: >-
    rockylinux_packages
    | selectattr('basename', 'equalto', item)
    | rejectattr('rpm', 'search', 'i686')
    | list
    | length > 0
  loop: "{{ cleaned_dnf_packages }}"

- name: Ensure /compat/linux/rpm exists
  file:
    path: /compat/linux/rpm
    state: directory

- name: Get rpm
  get_url:
    url: "{{ item.url }}"
    dest: "/compat/linux/rpm/{{ item.rpm }}"
    force: false
  loop: "{{ filtered_rockylinux_packages }}"
  loop_control:
    label: "{{ item.rpm }}"

- name: Ensure /compat/linux/var/db/rpm2cpio_stamps directory exists
  file:
    path: /compat/linux/var/db/rpm2cpio_stamps
    state: directory

- name: Deploy packages
  shell: |
    rpm2cpio < /compat/linux/rpm/{{ item.rpm }} | cpio -id
    touch /compat/linux/var/db/rpm2cpio_stamps/{{ item.rpm }}
  args:
    chdir: /compat/linux
    creates: "/compat/linux/var/db/rpm2cpio_stamps/{{ item.rpm }}"
  loop: "{{ filtered_rockylinux_packages }}"
  loop_control:
    label: "{{ item.rpm }}"
  notify:
    - run_linux_ldconfig

dnfの設定

FreeBSDのLinux Emulationはdnfによるパッケージ管理のために必要なLinuxのいくつかの機能が実装されておらず、そのままだとdnfの実行時にエラーになってしまいます。したがって、それらを無効化することでエラーを回避します。

- name: Ensure /etc/rpm directory exists
  file:
    path: /compat/linux/etc/rpm
    state: directory

- name: Deploy /etc/rpm/macros.freebsd
  copy:
    dest: /compat/linux/etc/rpm/macros.freebsd
    mode: "0644"
    content: |
      %_file_caps_path %{nil}
      %__file_context_path %{nil}
      %_selinux_policy_targeted 0

- name: Ensure /etc/dnf/vars directory exists
  file:
    path: /compat/linux/etc/dnf/vars
    state: directory

- name: Deploy /etc/dnf/vars/releasever
  copy:
    dest: /compat/linux/etc/dnf/vars/releasever
    mode: "0644"
    content: "{{ rockylinux_ver.split('.')[0] }}"

- name: Disable history_record
  lineinfile:
    path: /compat/linux/etc/dnf/dnf.conf
    regexp: '^history_record='
    line: "history_record=False"
    create: yes

- name: Enable nocaps for FreeBSD Linux Emuldation
  lineinfile:
    path: /compat/linux/etc/dnf/dnf.conf
    regexp: '^tsflags='
    line: "tsflags=nocaps"

パッケージインベントリーの整合性合わせ

展開したrpmパッケージをdnfでもう一度インストールしなおします。
このとき、CA証明書のトラストチェーンの再構成、GPG鍵の整合性合わせ、パッケージデータベースの再生成を同時に行います。

- name: Deploy dnf_install.sh
  copy:
    dest: /compat/linux/dnf_install.sh
    mode: "0755"
    content: |
      #!/bin/bash
      /usr/bin/update-ca-trust
      rm -f /var/lib/dnf/history.sqlite*
      export SQLITE_DEFAULT_JOURNAL_MODE=TRUNCATE
      dnf install -y --setopt=history_record=true rocky-gpg-keys --nogpgcheck
      dnf install -y \
      {% for pkg in filtered_rockylinux_packages -%}
      {{ pkg.basename }}{% if not loop.last %} \
      {% endif %}
      {%- endfor %}

- name: dnf install
  command: chroot /compat/linux /bin/bash /dnf_install.sh
  register: result
  changed_when: false

- debug:
    var: result.stdout_lines

dnfの動作確認

ここまで来るとdnfが動作します。

# chroot /compat/linux dnf list
Rocky Linux 9 - BaseOS                                                                  6.8 kB/s | 4.3 kB     00:00
Rocky Linux 9 - BaseOS                                                                   14 MB/s |  23 MB     00:01
Rocky Linux 9 - AppStream                                                               8.7 kB/s | 4.8 kB     00:00
Rocky Linux 9 - AppStream                                                                19 MB/s |  20 MB     00:01
Rocky Linux 9 - Extras                                                                  6.5 kB/s | 3.1 kB     00:00
Rocky Linux 9 - Extras                                                                   30 kB/s |  17 kB     00:00
Installed Packages
aardvark-dns.x86_64                                            2:1.16.0-1.el9                                 @System
acl.x86_64                                                     2.3.1-4.el9                                    @baseos
alternatives.x86_64                                            1.24-2.el9                                     @baseos
...

後はdnfで必要なパッケージをインストールするだけです。

ネットワーク関連パッケージのインストール

- name: Set RockyLinux network packages
  set_fact:
    network_packages:
      - iproute
      - iputils
      - net-tools
      - nmap

- name: Install network packages
  command: >
    chroot /compat/linux /usr/bin/dnf install -y {{ network_packages | join(' ') }}
  register: result
  changed_when: false

- debug:
    var: result.stdout_lines

開発用パッケージのインストール

- name: Set RockyLinux development tool packages
  set_fact:
    devtool_packages:
      - cpio
      - hostname
      - make
      - openssl
      - passwd
      - procps-ng
      - pkgconf
      - pkgconf-pkg-config
      - sudo
      - tar
      - tzdata
      - vi
      - wget
      - xz

- name: Install development tool packages
  command: >
    chroot /compat/linux /usr/bin/dnf install -y {{ devtool_packages | join(' ') }}
  register: result
  changed_when: false

- debug:
    var: result.stdout_lines

開発用ライブラリーパッケージのインストール

- name: Set RockyLinux development library packages
  set_fact:
    dev_library_packages:
      - bzip2-devel
      - libffi-devel
      - libuuid-devel
      - libxcrypt-devel
      - ncurses-devel
      - nss-devel
      - openssl-devel
      - readline-devel
      - sqlite-devel
      - tk-devel
      - xz-devel
      - zlib-devel

- name: Install development tool packages
  command: >
    chroot /compat/linux /usr/bin/dnf install -y {{ dev_library_packages | join(' ') }}
  register: result
  changed_when: false

- debug:
    var: result.stdout_lines

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です