12
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

aptやdpkgはどのようにパッケージを管理するか

Posted at

背景

普段何気なく使っているaptコマンドだが、何気なく使えるほどよく出来ているとも言える。
これがどうやってパッケージを管理しているのかということが気になったので調べてみた。
結論とかは特に無いが、何かトラブルが合った時に仕組みを知っていれば解決が早くなるものだ。

.debファイルの構造

aptやdpkgはUbuntuなどのDebian系でよく使われるパッケージマネージャ。
ネットワークを介して依存関係を解決したりするためにはaptが使われる。
パッケージに含まれるファイルを1つの .deb ファイルにまとめて管理することができるほか、そのメタデータやインストール・アンインストール用のスクリプトも同梱させることができる。
.deb ファイルは以下のような構造になっている。

+ foo.deb
  - debian-binary # バージョン情報(2.0)
  + control.tar # メタデータ
    - control # 各種メタデータ、Dependするパッケージ情報などを含む
    - md5sums # これ以外のファイルのMD5ハッシュ値(破損検知用)
    - preinst # install前に実行するスクリプト
    - postinst # install後に実行するスクリプト
    - prerm # remove/purge前に実行するスクリプト
    - postrm # remove/purge後に実行するスクリプト
    - conffiles # パッケージのコンフィグファイル一覧
    - ...
  + data.tar # インストールするファイル(ルートディレクトリ以下に展開される)
    - ./
    - ./usr/
    - ./usr/bin/
    - ./usr/bin/foo
    - ./opt/
    - ./opt/foo/
    - ./opt/foo/config
    - ...

もし手元に .deb ファイルがあるのなら、次のコマンドで開いてみるのも良い。
もちろん実行結果は手元の .deb ファイル次第で大きく変わる。

$ ls -la foo.deb
foo.deb
$ mkdir ./tmp
$ cd ./tmp
$ ar xv ../foo.deb
debian-binary
control.tar.xz
data.tar.xz
_gpgbuilder
$ tar xfv control.tar.xz # ファイルを展開
./
./control
./postinst
./postrm
./md5sums
$ head ./control
Package: foo
Version: 1.0.0
Vendor: Foo
Depends: bar
$ tar tf data.tar.xz | head # ファイル一覧の一部を見るだけ
./
./usr/
./usr/bin/
./usr/bin/foo
./opt/
./opt/foo/
./opt/foo/config

アップデート時(update)

aptは依存関係を解決するために、必要なパッケージを配布するサーバ(repository、レポジトリ)がどこかを把握する。
そのために実行するコマンドが、よくある次のコマンド。

$ sudo apt update

これは /etc/apt/sources.list/etc/apt/sources.list.d/ 以下のファイルに書かれたサーバのURLとオプションを元にサーバにアクセスし、 /var/lib/apt/lists/ 以下にそのレポジトリが配布するパッケージ情報の一覧をキャッシュする。
中身を見てみると、 .deb ファイルの control ファイルと似たようなものが羅列されている。

$ apt download coreutils
$ ls coreutils*
coreutils_8.28-1ubuntu1_amd64.deb
$ mkdir coreutils/
$ cd coreutils/
$ ar x ../coreutils_8.28-1ubuntu1_amd64.deb
$ tar xf ./control.tar.xz
$ head ./control
Package: coreutils
Version: 8.28-1ubuntu1
Architecture: amd64
Essential: yes
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Installed-Size: 6560
Pre-Depends: libacl1 (>= 2.2.51-8), libattr1 (>= 1:2.4.46-8), libc6 (>= 2.25), libselinux1 (>= 2.1.13)
Section: utils
Priority: required
Multi-Arch: foreign

つまり、apt update をした時に得る情報はパッケージがどこにあるかだけでなく、依存関係を解決するために必要な情報も含まれている。

インストール時(install)

ここまでの話で想像はつくが、あらためてaptコマンドがどのように動いているのか、straceコマンドで得たシステムコールの順序を元に列挙すると次のとおりである。

  1. /var/lib/dpkg/status を見る
  2. /var/lib/apt/lists/* を見る
  3. 対象のパッケージのメタデータを見て依存解決
  4. .deb ファイルを取ってきてarchiveを展開する
  5. preinst スクリプトがあれば実行する
  6. data.tar 内のファイルを展開する
  7. /var/lib/dpkg/info/foo.list に展開したファイルの一覧を記録する
  8. /var/lib/dpkg/info/foo.(pre|post)(inst|rm)foo.md5sum などを置く
  9. postinst スクリプトがあれば実行する
  10. /var/lib/dpkg/status を更新する

アンインストール時(remove/purge)

apt removeapt purgeコマンドはパッケージを削除する。
両者の違いは、前者が conffiles で指定されたパッケージ内の設定ファイルを残すのに対して、後者は残さないことである。
また --auto-remove オプションをつけることで、そのパッケージが依存するパッケージすべてを可能な限り(他のパッケージが依存していない限り、手動でインストールされていない限り)削除する。

  1. /var/lib/dpkg/status を見る
  2. 対象のパッケージのメタデータを見て依存先で削除可能なパッケージを列挙
  3. /var/lib/dpkg/info/foo.prerm スクリプトがあれば実行する
  4. /var/lib/dpkg/info/foo.list に列挙されたファイルを削除する
  5. /var/lib/dpkg/info/foo.postrm スクリプトがあれば実行する
  6. /var/lib/dpkg/status を更新する

どのパッケージのファイルかの判定時

Linuxのファイルシステムに展開されたファイルたちは、それ自体はパッケージで管理されていないファイルと違いがない。
しかし以下のコマンドを使って、あるファイルがどのパッケージに属するものであるかを判定することができる。

$ dpkg -S /bin/ls
coreutils: /bin/ls

これは次のように動いている。
まずはインストールされたパッケージの一覧を /var/lib/dpkg/status から取得し、各パッケージのインストール時に展開したファイルたちのリストを保存した /var/lib/dpkg/info/<package-name>.list から検索している。
上の例では /var/lib/dpkg/info/coreutils.list の中に以下のような行を発見してcoreutilsのファイルであると判定をする。

  1 /.
  2 /bin
  3 /bin/cat
  4 /bin/chgrp
  5 /bin/chmod
  6 /bin/chown
  7 /bin/cp
  8 /bin/date
  9 /bin/dd
 10 /bin/df
 11 /bin/dir
 12 /bin/echo
 13 /bin/false
 14 /bin/ln
 15 /bin/ls <----

そのため、coreutils以外のパッケージの .list ファイルにうっかり「/bin/ls」を足してしまうと、

$ dpkg -S /bin/ls
coreutils, foo: /bin/ls

のように別のパッケージにも属する判定になってしまう。
決してそんなことをしてはいけない。

検証環境

  • Architecture: amd64
  • Distribution: Ubuntu 18.04.6 LTS
  • apt: 1.6.14
  • dpkg: 1.19.0.5
12
6
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
12
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?