LoginSignup
1
1

More than 1 year has passed since last update.

Alpine Linux 環境への Linuxbrew の導入手法

Last updated at Posted at 2021-09-27

Alpine Linux 環境への Linuxbrew の導入手法

はじめに

Alpine Linux とは、 GNU libc6 と互換性を持つ非常に軽量な C 標準ライブラリである musl と、標準的な UNIX 環境において重要なコマンド類を単一の実行ファイルとして提供するプログラムである Busybox をベースとした超軽量な Linux のディストリビューションです。

また、 Linuxbrew とは、 Mac OS X における、ソースコードの取得及びビルドに基づいたパッケージ管理システムである Homebrew を Linux の各ディストリビューション向けに移植したものであり、現在は Homebrew と統合されています。

ここで、 Alpine Linux が導入されている環境に Linuxbrew を導入する手法として、 Linuxbrew 公式ページで述べられている "Install Linuxbrew on Alpine Linux" に示す手法がありますが、記述内容が古い上に、導入の際に以下に述べる問題が発生します。

  • Busybox における grepps 等のコマンドのオプションや引数が標準的な Linux ディストリビューション等におけるそれと幾つかの差異があるため、 Linuxbrew のインストールスクリプトが正常に動作しない。
  • Linuxbrew が内部で使用する ruby の処理系である portable-ruby の実行形式のバイナリファイルが、 musl に適合しないために Linuxbrew が適切に動作しない。

以上の問題を回避するために、予め Alpine Linux 上に Linuxbrew 環境を構築するために必要な apk パッケージを導入し、 ldd コマンドが --version オプションを受け取った時に適切なバージョンを返すように ldd コマンドのラッパースクリプトの修正を行いました。

次に、ディレクトリ /home/linuxbrew 以下に手動で Linuxbrew のディレクトリツリーを作成し、ディレクトリ /home/linuxbrew/.linuxbrew/Homebrew 以下に Linuxbrew 本体の git リポジトリを git clone コマンドを用いて取得しました。

その後、ディレクトリ /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/portable-ruby を作成し、 /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/portable-ruby/2.6.3_2 以下に Linuxbrew の内部で使用する ruby の処理系をソースコードからビルドしました。

そして、 /home/linuxbrew/.linuxbrew/bin/brew tap homebrew/core コマンドにより、 Tap リポジトリ homebrew/core を取得しました。

最後に、 /home/linuxbrew/.linuxbrew/bin/brew shellenv の出力に基づいて Linuxbrew 関連お環境変数の設定を行い、 Linuxbrew において使用する glibc, linux-headers, gcc の導入を行いました。

以上の作業を行なった後、 brew doctor コマンドによる Linuxbrew の診断等の結果、 Alpine Linux 環境上において Linuxbrew が正常に動作することを確認しました。

本稿では、手動による Linuxbrew のディレクトリツリーの作成に基づいた Alpine Linux 環境における Linuxbrew の導入手法について述べます。

本稿では、 導入手法 の章において、 Alpine Linux 環境における Linuxbrew の導入手法について順を追って具体的に述べ、 結論 の章において、本稿の結論について述べます。

なお本稿では、特段の断りが無い限り、 Linuxbrew の導入作業を行なった Alpine Linux 環境及び Linuxbrew の導入先等は以下の通りであるとします。

導入手法

本章では、 Alpine Linux 環境に Linuxbrew を導入する為の具体的な手法について述べます。

先ず、 "Linuxbrew に依存する apk パッケージの導入" の節において、 LinuxbrewAlpine Linux 環境に導入する上で、 Linuxbrew の導入に必要となる apk パッケージの導入手法について述べ、 "ldd コマンドの修正" の節において、 ldd --version コマンドの出力が適切なものとなるための ldd コマンドのラッパースクリプトの修正手法について述べます。

次に、 "手動による Linuxbrew のツリーの作成" の節において、ディレクトリ /home/linuxbrew 以下に Linuxbrew を動作させるために必要なファイル群を格納するためのディレクトリツリーを手動で作成する手法について述べ、 "Linuxbrew の内部で使用する Ruby 処理系のビルド" の節において、 Linuxbrew 関連のコマンドを動作させるために使用する ruby 処理系をソースコードからビルドする手法について述べます。

そして、 "Tap リポジトリ homebrew/core の取得と Linuxbrew ツリーの更新" の節において、 Linuxbrew の中核をなす Tap リポジトリである homebrew/core を取得し、 Linuxbrew 本体を最新の状態に更新する為の具体的な手法について述べます。

また、 "Linuxbrew 関連の環境変数の設定" の節において、 Linuxbrew を動作させるのに必要となる環境変数の設定について述べ、 "glibc, linux-headers, gcc の導入" の節において、 Linuxbrew によって実行ファイルをビルドしたり、導入される実行ファイルを動作させるために必要となる Linuxbrew のパッケージである glibc, linux-headers 及び gcc を導入するための具体的手法について述べます。

最後に、 "動作確認" の節において、以上で述べた手法により導入した Linuxbrew についての最終的な動作確認について述べます。

Linuxbrew に依存する apk パッケージの導入

Alpine Linux 環境に Linuxbrew を導入する為に、先ず、 Linuxbrew の動作に関して必要となる Alpine Linux 環境の apk パッケージを導入します。

導入する apk パッケージについては、 "Install Linuxbrew on Alpine Linux" に記述されたパッケージを参照しますが、 ruby-dbm パッケージは既に obsolete となっているため、これに代えて ruby-sdbm, ruby-gdbm を導入します。

また、 Alpine Linux 環境における Busybox で実装されている grep, ps 等の一部コマンドにおいて、標準の Linux ディストリビューションにおける同様のコマンドと引数及びオプションの仕様が異なるものがあるので、追加で grep, coreutils, procps を導入する必要があります。

そして、 "Linuxbrew の内部で使用する Ruby 処理系のビルド" の節にて後述する Linuxbrew 内部で使用する ruby 処理系をソースコードからビルドする際に使用するヘッダファイル群を集めたパッケージである linux-headers 及び、 ruby 処理系から readline 及び zlib を扱うために必要となるパッケージである readline-dev, zlib-dev も同時に導入する必要があります。

 # apk update
 # apk add bash build-base curl file git gzip libc6-compat ncurses
 # apk add ruby ruby-sdbm ruby-gdbm ruby-etc ruby-irb ruby-json sudo
 # apk add grep coreutils procps readline-dev zlib-dev linux-headers

ldd コマンドの修正

標準的な Linux ディストリビューションのコマンドの一つである ldd コマンドは、指定した実行ファイル若しくは共有ライブラリについて、その実行ファイル及び共有ライブラリが依存している共有ライブラリを表示するためのコマンドです。

また、 ldd コマンドに --version オプションを指定すると、 ldd コマンドを実行している環境において使用している標準 C ライブラリのバージョン番号を表示させることが出来ます。

例えば、 Ubuntu 20.04 環境において ldd --version コマンドを実行すると、以下のようなメッセージが標準出力に出力されます。

 $ ldd --version
 ldd (Ubuntu GLIBC 2.31-0ubuntu9.2) 2.31
 Copyright (C) 2020 Free Software Foundation, Inc.
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 作者 Roland McGrath および Ulrich Drepper。

しかし、 Alpine Linux 環境では、標準 C ライブラリに musl が使用されており、 Alpine Linux 環境において ldd --version コマンドを実行すると、以下のようなメッセージが標準エラーメッセージに出力されます。

 # ldd --version
 musl libc (x86_64)
 Version 1.2.2
 Dynamic Program Loader
 Usage: /lib/ld-musl-x86_64.so.1 [options] [--] pathname

Linuxbrew の動作において、標準 C ライブラリのバージョンを検出する必要が有る場合には、コマンド ldd --version の標準出力の結果が使用されるために、 Alpine Linux 環境で Linuxbrew を動作させる場合は、標準 C ライブラリのバージョンを検出が出来ずに不具合が発生します。

ここで、 Alpine Linux 環境において ldd コマンドの実行ファイルの実体は、以下のように、共有ライブラリファイル /lib/ld-musl-x86_64.so.1 にオプション --list と実行時に指定された引数とオプションを渡して直接実行するためのシェルスクリプト形式のラッパーであることが判ります。

 # cat /usr/bin/ldd
 #!/bin/sh
 exec /lib/ld-musl-x86_64.so.1 --list "$@"

そこで、 Alpine Linux 環境において Linuxbrew を動作させる際には、スクリプトファイル /usr/bin/ldd を別のファイルに退避させた上で、以下の通りに、 ldd コマンドに --version オプションを渡した時に適当な出力を返すように /usr/bin/ldd を修正する必要があります。

 #!/bin/sh
 while test $# -gt 0; do
   case "$1" in
     --vers | --versi | --versio | --version)
       echo 'ldd (Alpine MUSL 2.13) 2.13'
       exit 0
       ;;
   esac
 done
 exec /lib/ld-musl-x86_64.so.1 --list "$@"

手動による Linuxbrew のツリーの作成

通常の Linux ディストリビューションにおいて、 Linuxbrew を導入する際には、 Linuxbrew の公式ページに記述の有る通りに、 Linuxbrew のインストールスクリプトの起動を行いますが、 Alpine Linux 環境では、 Linuxbrew のインストールスクリプトを実行しても、以下のようなエラーメッセージを出力して起動に失敗します。

 $ sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"
 ...(略)...
 ==> Pouring portable-ruby-2.6.3_2.x86_64_linux.bottle.tar.gz
 Error: Failed to install ruby 2.6.3_2!
 Error: Failed to install Homebrew Portable Ruby and cannot find another Ruby 2.6.3!
 ...(略)...

以上のようなエラーメッセージが出力されるのは、 Linuxbrew 本体を起動するために使用される ruby 処理系を含む tarball であり、インストールスクリプトによってダウンロードされる tarball である portable-ruby-2.6.3_2.x86_64_linux.bottle.tar.gz に同梱されている実行ファイル群が、 Linuxbrew によって導入される GNU glibc6 2.23 及びそれ以降のバージョンの標準 C ライブラリに適合するように構築されているのが原因です。

以上の問題を回避するには、 Alpine Linux 環境上で Linuxbrew のインストールスクリプトを起動せずに、次に述べる手順にて手動で Linuxbrew を導入する必要があります。

まずは、ディレクトリ /home/linuxbrew 以下に Linuxbrew 本体及び Tap リポジトリを格納するためのディレクトリツリーを以下の通りにして作成し、ディレクトリ /home/linuxbrew/.linuxbrew/Homebrew 以下に、 git clone コマンドを用いて Linuxbrew 本体を構成するファイル群を取得します。

 # mkdir -p /home/linuxbrew/.linuxbrew
 # cd /home/linuxbrew/.linuxbrew
 # mkdir Caskroom Cellar Frameworks bin etc include lib opt sbin share tmp
 # mkdir -p var/homebrew/links
 # git clone https://github.com/Homebrew/brew ./Homebrew

そして、ディレクトリ /home/linuxbrew/.linuxbrew/bin 以下に、 brew コマンドの本体となるスクリプトファイル /home/linuxbrew/.linuxbrew/Homebrew/bin/brew へのシンボリックリンクを張ります。

 # cd /home/linuxbrew/.linuxbrew/bin
 # ln -sf ../Homebrew/bin/brew .

Linuxbrew の内部で使用する Ruby 処理系のビルド

Linuxbrew の各種コマンドを起動するために使用される ruby 処理系は、ディレクトリ /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/portable-ruby に置かれています。

"手動による Linuxbrew のツリーの作成" の節で前述した通り、インストールスクリプトからダウンロードした実行バイナリファイル形式の ruby 処理系が Linuxbrew 内部で使用する ruby 処理系として使用できないため、 Alpine Linux 環境においては、 ruby 処理系のソースコードを ruby の公式ページより入手し、ディレクトリ /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/portable-ruby/2.6.3_2 以下に、手動によるビルドに基づいて導入する必要があります。

Linuxbrew 内部で使用する ruby 処理系のビルドを行うには、まず、以下の通りにして、ディレクトリ /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/portable-ruby 以下に src ディレクトリを作成します。

そして、ディレクトリ src 以下に ruby 公式ページより ruby 2.6.3 のソースコードを取得します。

 # mkdir -p /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/portable-ruby
 # cd /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/portable-ruby
 # mkdir src
 # cd src
 # curl -L -o ruby-2.6.3.tar.gz https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.3.tar.gz

次に、以下の通りに ruby 2.6.3 のソースコードを展開し、ディレクトリ /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/portable-ruby/2.6.3_2 以下にソースコードのビルドに基づいて Linuxbrew 内部で使用する ruby 処理系を導入します。

なお、スクリプト ./configure を起動する際は、インストール先を示すオプション --prefix を指定する他に、以下のオプションも同時に指定します。

  • --enable-load-relative … 実行ファイル ruby 及び libruby.so.* の探索先を相対化する。
  • --with-static-linked-ext … 外部ライブラリを libruby.so.* に静的に結合する。
  • --with-out-ext=openssl,tk,sdbm,gdbm,dbm,win32,win32ole … 外部ライブラリ openssl,tk,sdbm,gdbm,dbm,win32,win32ole を使用しない。
  • --without-gmp … GMP による Bignum の演算の高速化を無効にする。
  • --disable-install-doc, --disable-install-rdoc … ruby 処理系に関するドキュメント類の生成を抑止する。
  • --disable-dependency-tracking … 依存性の追跡を無効にする。
 # tar -xvf ruby-2.6.3.tar.gz
 # 
 # cd ruby-2.6.3
 # ./configure --prefix=/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/portable-ruby/2.6.3_2 \
               --enable-load-relative --with-static-linked-ext --with-out-ext=openssl,tk,sdbm,gdbm,dbm,win32,win32ole \
           --without-gmp --disable-install-doc --disable-install-rdoc --disable-dependency-tracking
 # make
 # make install

また、 ruby 処理系のビルド中に発生する各種警告を抑止したい場合は、スクリプト ./configure の実行前に予め以下の通りに環境変数 CFLAGS, CXXFLAGS を設定します。

 # export CFLAGS="$CFLAGS -O3 -ggdb3 -Wall -Wextra -Wdeclaration-after-statement -Wdeprecated-declarations"
 # export CFLAGS="$CFLAGS -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings"
 # export CFLAGS="$CFLAGS -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long"
 # export CFLAGS="$CFLAGS -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat"
 # export CFLAGS="$CFLAGS -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter"
 # export CFLAGS="$CFLAGS -Wno-unused-value -Wsuggest-attribute=noreturn -Wno-unused-variable -Wno-implicit-fallthrough"
 # export CFLAGS="$CFLAGS -Wno-address-of-packed-member -Wno-incompatible-pointer-types -Wno-declaration-after-statement"
 # export CFLAGS="$CFLAGS -Wno-empty-body -Wno-sign-compare -Wno-unused-but-set-variable"
 # export CXXFLAGS="$CXXFLAGS $CFLAGS"

その後、ディレクトリ /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/portable-ruby に移動し、以下のようにしてディレクトリ 2.6.3_2 より current に向けてシンボリックリンクを張ります。

そして、ディレクトリ src に残っている ruby 処理系のソースコードを削除します。

 # cd /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/portable-ruby
 # ln -sf 2.6.3_2 current
 # rm -rf src

Tap リポジトリ homebrew/core の取得と Linuxbrew ツリーの更新

Linuxbrew は、一般ユーザの権限において各種パッケージを管理するシステムであるため、 Linuxbrew の各種コマンドは、管理者権限の環境において実行することは推奨されていません。

従って、 Linuxbrew の中核の Formula が納められている Tap リポジトリである homebrew/core を取得するには、一般ユーザの権限で、コマンド /home/linuxbrew/.linuxbrew/bin/brew を実行する必要があります。

そこで、以下のようにして、ディレクトリ /home/linuxbrew 以下の全てのファイルの所有者を一般ユーザに変更し、 su コマンドにより、一般ユーザの環境に移ります。

 # chown -R vagrant:vagrant /home/linuxbrew
 # su - vagrant

そして、以下のようにして、/home/linuxbrew/.linuxbrew/bin/brew tap homebrew/core コマンドにより、 Tap リポジトリ homebrew/core を取得し、 /home/linuxbrew/.linuxbrew/bin/brew update コマンドにより、 Linuxbrew 本体のリポジトリを更新します。

 $ mkdir -p /home/linuxbrew/.linuxbrew/Homebrew/Library/Taps/homebrew/homebrew-core
 $ /home/linuxbrew/.linuxbrew/bin/brew tap homebrew/core
 $ /home/linuxbrew/.linuxbrew/bin/brew update

Linuxbrew 関連の環境変数の設定

Alpine Linux 環境において、 Linuxbrew を正常に動作させるためには、 Linuxbrew が使用する環境変数 HOMEBREW_PREFIX, PATH 等を適切な値に設定する必要があります。

ここで、 Linuxbrew を適切に動作させるための各種環境変数の値は /home/linuxbrew/.linuxbrew/bin/brew shellenv コマンドによって出力されます。

従って、以下の通りにして /home/linuxbrew/.linuxbrew/bin/brew shellenv コマンドの出力内容を、 bash の設定ファイルである ~/.bashrc, ~/.profile に追加します。

 $ /home/linuxbrew/.linuxbrew/bin/brew shellenv >> ~/.bashrc 
 $ /home/linuxbrew/.linuxbrew/bin/brew shellenv >> ~/.profile
 $ eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)

glibc, linux-headers, gcc の導入

Linuxbrew で管理される各種パッケージは、 Linuxbrew において導入される GNU glibc6 2.23 以降の標準 C ライブラリの存在を前提として構築が行われています。また、 Linuxbrew において、ソースコードのビルドが行われる際においても、 GNU glibc6 2.23 以降の標準 C ライブラリの存在を前提としたビルドが行われます。

従って、 Alpine Linux 環境において Linuxbrew を正常に動作させるには、 Linuxbrew を用いて GNU glibc6 及び GNU C コンパイラを予め導入する必要があります。

また、パッケージ Linuxbrew で導入される GNU C コンパイラを正常に動作させるためには、 Linux 関連の C 処理系のヘッダファイル群である linux-headers も同時に導入する必要があります。

この際、全てのパッケージを導入する時は、 brew install コマンドにオプション --ignore-dependencies, --force-bottle, --force を指定し、全てのパッケージについて、強制的に実行形式のバイナリファイルからの導入を行うことに留意する必要があります。

 $ brew install --ignore-dependencies --force-bottle --force glibc
 $ brew install --ignore-dependencies --force-bottle --force linux-headers
 $ for f in `brew deps -n gcc`; do brew install --ignore-dependencies --force-bottle --force $f; done
 $ brew install --ignore-dependencies --force-bottle --force gcc

動作確認

以上の導入に関する作業が完了した後は、 Linuxbrew の動作確認を行います。まず、以下の通りにして brew doctor コマンドにより、 Linuxbrew 全体の診断プログラムを起動させます。

ここで、標準出力に Your system is ready to brew. が出力されていれば、正常に Linuxbrew が導入されています。

 $ brew doctor
 Your system is ready to brew.

最後に、以下のようにして hello, patchelf パッケージ等をソースコードから導入し、正常にこれらのパッケージが正常に導入されることを確認します。

 $ brew install -dvs hello
 $ brew install -dvs patchelf

結論

本稿における Alpine Linux 環境上の Linuxbrew において、まず最初に Busybox における grepps 等のコマンドのオプションや引数や ldd の動作が標準的な Linux ディストリビューション等の仕様と異なるために、 Linuxbrew に依存する apk パッケージの導入において、 grep, procps 等のパッケージを追加で導入し、また、 ldd コマンドのラッパースクリプトの修正を行いました。

次に、インストールスクリプトからダウンロードした実行バイナリファイル形式の ruby 処理系が Alpine Linux 環境での動作に適合しないため、インストールスクリプトによる Linuxbrew の導入に代えて、ディレクトリ /home/linuxbrew 以下に Linuxbrew のディレクトリツリーを手動で作成し、 Linuxbrew 本体の git リポジトリを git clone コマンドを用いて取得しました。

その後、 Linuxbrew の内部で使用する ruby の処理系が置かれているディレクトリ /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/portable-ruby/2.6.3_2 以下に ruby 2.6.3 の処理系をソースコードからビルドしました。

そして、 Linuxbrew の中核の Formula が納められている Tap リポジトリ homebrew/core を取得しました。

最後に、 /home/linuxbrew/.linuxbrew/bin/brew shellenv の出力に基づいて Linuxbrew 関連お環境変数の設定を行い、 Linuxbrew の動作において前提となるパッケージである glibc, linux-headers, gcc の導入を行いました。

以上の作業を行なった後、 brew doctor コマンド等による Linuxbrew の動作確認の結果、 Alpine Linux 環境上において Linuxbrew が正常に動作することを確認しました。

本稿においては、 Alpine Linux 環境上における Linuxbrew の導入について、通常の [Linuxbrew][X005] のディストリビューションで用いるインストールスクリプトによる導入手法に代えて、 Linuxbrew を導入するためのディレクトリツリーを手動で作成し、 Linuxbrew の内部で使用する ruby 処理系を手動でソースコードからビルドすることにより導入する手法について述べました。

本稿で述べた Alpine Linux 環境での導入の他にも、標準的な GNU libc6 が導入されていない Linux ディストリビューション等のように、 Linuxbrew のインストールスクリプトからダウンロードされる Linuxbrew の動作用の ruby 処理系の実行形式のバイナリファイル群が、当該環境に適合しない場合においても、本稿で述べた Linuxbrew の導入手法が有効であると考えられます。

謝辞

まず最初に、超軽量な Linux のディストリビューションである Alpine Linux を開発した Alpine Linux の開発コミュニティの各位に心より感謝致します。

そして、 Linuxbrew 本体のリポジトリの開発を行っている Shaun Jackman 氏を始めとする Linuxbrew の開発コミュニティの各氏に心より感謝致します。また、 Linuxbrew の詳細に関しては、 Linuxbrew 公式ページ及び Linuxbrew のリポジトリに同梱される各種資料も併せて参考にしました。

最後に、 Linuxbrew 及び Alpine Linux 環境そして Linux 全体に関わる全ての皆様に心より感謝致します。

追記

本稿の付録として、 vagrant を用いて Alpine Linux 環境に導入された Linuxbrew を tarball で固めたものを以下に添付致します。

https://github.com/z80oolong/Linuxbrew-for-Alpine/releases

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1