なにもわからない

気分で技術系の雑記を書きます

macOS Mojave で gdb を動かす(2020年10月)

2017年6月発表の macOS High Sierra(10.13)から SIP(System Integrity Protection: システム整合性保護)が導入され、gdb が動かなくなりました。過去には SIP 自体を無効化しなければなりませんでしたが、どうも最近ではそこそこ妥当な対処方法が取れるようになったようです。

困った話なので様々な対処方法がネットにありますが、現状にマッチした情報が見つかりませんでしたので、私の環境で動作した方法を書き記しておきます。 support.apple.com

対処方法

対処方法の手順はざっくり4つです。ポイントは SIP の無効化(csutil disable)や一部無効化(csutil enable --without=debug)しなくても動くという点です。

  1. ~/.gdbinitset startup-with-shell off を書く
  2. Code Signing Certificate を作る(Keychain\ Access.app
  3. --entitlements 付きで gdbcodesign する
  4. taskgated を殺す

動かすもの

今回動かすものはこちらの解説記事のものをそのまま使わせていただきました。 rat.cis.k.hosei.ac.jp

再現手順は以下の通りです。

$ gcc -g -O0 bubblesort.c

$ gdb a.out
GNU gdb (GDB) 10.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin19.6.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...
Reading symbols from /private/tmp/gdb/a.out.dSYM/Contents/Resources/DWARF/a.out...
(gdb) break main
Breakpoint 1 at 0x100003db8: file bubblesort.c, line 9.
(gdb) run
Starting program: /private/tmp/gdb/a.out
Unable to find Mach task port for process-id 8203: (os/kern) failure (0x5).
 (please check gdb is codesigned - see taskgated(8))
(gdb) quit

1. ~/.gdbinitset startup-with-shell off を書く

brew install gdb 時にもメッセージが出る通り、~/.gdbinitset startup-with-shell off の記述が必要です。 これがないとコード署名の問題が解消しても During startup program terminated with signal SIGTRAP, Trace/breakpoint trap. が発生するようです。

2. Code Signing Certificate を作る

Keychain Access を使って Code Signing Certificate を作ります。

  • Keychain Access を開きます
  • メニューから「Keychain Access > Certificate Assistant > Create a certificate」を選びます
  • Create Your Certificate を設定します
    • Namegdb とします。この名前を後のコマンドで指定することになります。
    • Identity TypeSelf Signed Root を選択します
    • Certificate TypeCode Signing を選択します
    • Let me override defaults にチェックを入れます
  • Certificate Information を設定します
    • Validity Period (days) はデフォルト1年ですが10年程度に設定しておくとよいと思います
  • Key Pair Information を設定します
    • デフォルト選択肢のままでも動くようですが、念のため Key Size512 bitsAlgorithmECC にしておくとよいと思います
  • しばらく Continue で飛ばします
  • Specify a Location for The Certificate を設定します
    • KeychainSystem に設定します
  • Conclusion Your certificate has been successfully created. と表示されます
    • 作成された証明書の内容を確認してみてください
  • 一覧から作成された証明書(Name = gdb, Kind = certificate)をダブルクリックして詳細を開きます
    • Trust の三角矢印をクリックして開き、When using this certificateAlways Trust に設定します
    • この手順は不要かもしれません

f:id:tamakiii:20201029214015p:plain
Create Your Certificate
f:id:tamakiii:20201029214055p:plain
Certificate Information
f:id:tamakiii:20201029214127p:plain
Key Pair Information
f:id:tamakiii:20201029214157p:plain
Specify a Location for The Certificate
f:id:tamakiii:20201029223335p:plain
When using this certificate

3. --entitlements 付きで gdbcodesign する

以下の内容の gdb.xml を作ります。何度も使うものではないので適当な場所に作成してよいと思います。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.cs.debugger</key>
    <true/>
</dict>
</plist>

com.apple.security.cs.debugger という設定項目自体は、公式のドキュメントによると Availability は macOS 10.7+ とのことです。 developer.apple.com

codesign の状態を確認します

$ codesign -vv $(which gdb)
/usr/local/bin/gdb: code object is not signed at all
In architecture: x86_64

gdb--entitlements を指定して codesign します。このとき、-fs の引数に Keychain Access に登録したときの名前を指定します。

$ codesign --entitlements gdb.xml -fs gdb $(which gdb)

成功するとこのようになります

$ codesign -vv $(which gdb)
/usr/local/bin/gdb: valid on disk
/usr/local/bin/gdb: satisfies its Designated Requirement

4. taskgated を殺す

OS を再起動するか taskgated を殺して Code Sign を有効化します。

$ sudo pkill taskgated
Password:

しばらく待たないと以下のようにスレッドを立ち上げた所でハングするかもしれません。

Reading symbols from a.out...
Reading symbols from /private/tmp/gdb/a.out.dSYM/Contents/Resources/DWARF/a.out...
(gdb) break main
Breakpoint 1 at 0x100003db8: file bubblesort.c, line 9.
(gdb) run
Starting program: /private/tmp/gdb/a.out
[New Thread 0x2503 of process 8351]
[New Thread 0x2103 of process 8351]

動作確認

冒頭の再現手順を試します。期待通り動作しました。

Reading symbols from a.out...
Reading symbols from /private/tmp/gdb/a.out.dSYM/Contents/Resources/DWARF/a.out...
(gdb) break main
Breakpoint 1 at 0x100003db8: file bubblesort.c, line 9.
(gdb) run
Starting program: /private/tmp/gdb/a.out
[New Thread 0x1d03 of process 8511]
[New Thread 0x1f03 of process 8511]
warning: unhandled dyld version (16)

Thread 2 hit Breakpoint 1, main (argc=1, argv=0x7ffeefbff3f0) at bubblesort.c:9
9         int array[4] = {4, 1, 3, 2};
(gdb) 

動作環境情報

  • macOS Catalina (10.15.7)
$ gdb --version
GNU gdb (GDB) 10.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
Apple clang version 12.0.0 (clang-1200.0.32.21)
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
$ csrutil status
System Integrity Protection status: enabled.

この記事の内容を含むリポジトリです。こちらも参考にどうぞ。 github.com

参考

HomeBrew で Custom Build from Source する

最近ソースコードをビルドする機会が増えてきて、ホストOS上で常用するツールについては少し管理できた方がよさそうということで、HomeBrew で Build from Source を試しています。 例えば vimclientserver オプションを有効化したい、とかが brew install でできるようになります(まだできていない)。

HomeBrew は独特の世界観があってか[?] 情報が少ないからなのか少し混乱しましたが、慣れればまあ使えるんじゃないかと思います。 慣れれば自作のソフトウェアを配布する敷居が下がったり、macOS を使っている人の環境向上に寄与できていいんじゃないでしょうか。知らんけど。

Tap を作る

まずは Tap を作ります。Tap とは A Git repository of Formulae and/or commands らしいです。 Formula は *.rb になっている The package definition、つまり vim git make とかです。

HomeBrew の用語はこちら(Keg、Cellar、Cask など)。 docs.brew.sh

リポジトリの名前を考えます。tamakiii/homebrew-core は本家の homebrew/homebrew-core と競合する(本家に Pull Request を送りたいときに困る)ので tamakiii/homebrew-tap とします。

$ brew tap-new tamakiii/homebrew-tap
Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/tamakiii/homebrew-tap/.git/
[master (root-commit) 390ec63] Create tamakiii/tap tap
 3 files changed, 85 insertions(+)
 create mode 100644 .github/workflows/publish.yml
 create mode 100644 .github/workflows/tests.yml
 create mode 100644 README.md
==> Created tamakiii/tap
/usr/local/Homebrew/Library/Taps/tamakiii/homebrew-tap

When a pull request making changes to a formula (or formulae) becomes green
(all checks passed), then you can publish the built bottles.
To do so, label your PR as `pr-pull` and the workflow will be triggered.

作った Tap がこちら。README と Formula/ .github/workflows があるだけの簡単な構成です。 名前の homebrew- 部分は省略できるようです。

$ ls -lsa /usr/local/Homebrew/Library/Taps/tamakiii/homebrew-tap
total 8
0 drwxrwxr-x   6 tamakiii  admin  192 10 28 19:23 .
0 drwxrwxr-x   4 tamakiii  admin  128 10 28 19:23 ..
0 drwxrwxr-x  12 tamakiii  admin  384 10 28 19:28 .git
0 drwxrwxr-x   3 tamakiii  admin   96 10 28 19:23 .github
0 drwxrwxr-x   3 tamakiii  admin   96 10 28 19:27 Formula
8 -rw-rw-r--   1 tamakiii  admin  254 10 28 19:23 README.md

$ brew --repo tamakiii/tap
/usr/local/Homebrew/Library/Taps/tamakiii/homebrew-tap

$ brew --repo tamakiii/homebrew-tap
/usr/local/Homebrew/Library/Taps/tamakiii/homebrew-tap

ビルドしてみる

試しに本家の vim.rb を入れてみます。

$ cp /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/vim.rb Formula/

コピーした中身はこちら。依存関係やインストールコマンド本体、テストなどがあり、単体の Ruby ファイルで完結して動きます。 github.com

追加した vim Formula は、<tap-name>/<formula-name> で指定できます。

$ brew info tamakiii/tap/vim
tamakiii/tap/vim: stable 8.2.1900 (bottled), HEAD
Vi 'workalike' with many additional features
https://www.vim.org/
Conflicts with:
  ex-vi (because vim and ex-vi both install bin/ex and bin/view)
  macvim (because vim and macvim both install vi* binaries)
/usr/local/Cellar/vim/8.2.1900 (1,953 files, 33.3MB) *
  Built from source on 2020-10-28 at 19:29:45
From: https://github.com/tamakiii/homebrew-tap/blob/HEAD/Formula/vim.rb
License: Vim
==> Dependencies
Required: gettext ✔, lua ✔, perl ✔, python@3.9 ✔, ruby ✔
==> Options
--HEAD
        Install HEAD version
==> Analytics
install: 83,045 (30 days), 238,945 (90 days), 873,774 (365 days)
install-on-request: 80,611 (30 days), 231,601 (90 days), 835,450 (365 days)
build-error: 0 (30 days)

ビルド出来ました。Bottle が落とせなくて怒られていますが今回は気にしなくてよさそうです。

$ brew install tamakiii/tap/vim
Updating Homebrew...
Warning: tamakiii/tap/vim 8.2.1900 is already installed and up-to-date
To reinstall 8.2.1900, run `brew reinstall vim`

$ brew reinstall tamakiii/tap/vim
==> Downloading https://homebrew.bintray.com/bottles-tap/vim-8.2.1900.catalina.bottle.tar.gz
##O=#  #
curl: (22) The requested URL returned error: 404 Not Found
Error: Failed to download resource "vim"
Download failed: https://homebrew.bintray.com/bottles-tap/vim-8.2.1900.catalina.bottle.tar.gz
Warning: Bottle installation failed: building from source.
==> Downloading https://github.com/vim/vim/archive/v8.2.1900.tar.gz
Already downloaded: /Users/tamakiii/Library/Caches/Homebrew/downloads/094d05292a53960f72fae76726aee6cb028b92fbdabc0eb56191a4308b5f8821--vim-8.2.1900.tar.gz
==> Reinstalling tamakiii/tap/vim
Warning: A newer Command Line Tools release is available.
Update them from Software Update in System Preferences or run:
  softwareupdate --all --install --force

If that doesn't show you an update run:
  sudo rm -rf /Library/Developer/CommandLineTools
  sudo xcode-select --install

Alternatively, manually download them from:
  https://developer.apple.com/download/more/.

==> ./configure --prefix=/usr/local --mandir=/usr/local/Cellar/vim/8.2.1900/share/man --enable-multibyte --with-tlib=ncurses --with-compiledby=Homebrew --enable-csco
==> make
==> make install prefix=/usr/local/Cellar/vim/8.2.1900 STRIP=/usr/bin/true
🍺  /usr/local/Cellar/vim/8.2.1900: 1,953 files, 33.3MB, built in 1 minute 27 seconds

vim Formula には Bottle がないのでこれで良いですが、設定されているものには --build-from-source をつける必要がありそうです。せいぜい数台のマシンで動かすものなので Bottle の記述は削るとよさそうです。

また、./configuremake の出力がほしいところなので --verbose をつけるとよさそうです。 --debug はちょっとうるさかったのでやめました。

$ brew reinstall --force --verbose --build-from-source tamakiii/tap/vim
rm /usr/local/bin/ex
rm /usr/local/bin/rview
rm /usr/local/bin/rvim
rm /usr/local/bin/vi
rm /usr/local/bin/view
rm /usr/local/bin/vim

...

/usr/bin/sandbox-exec -f /private/tmp/homebrew20201028-85925-14282ki.sb nice ruby -W0 -I $LOAD_PATH -- /usr/local/Homebrew/Library/Homebrew/build.rb /usr/local/Homebrew/Library/Taps/tamakiii/homebrew-tap/Formula/vim.rb --verbose
tar xof /Users/tamakiii/Library/Caches/Homebrew/downloads/094d05292a53960f72fae76726aee6cb028b92fbdabc0eb56191a4308b5f8821--vim-8.2.1900.tar.gz -C /private/tmp/d20201028-85926-10g8sdh
cp -pR /private/tmp/d20201028-85926-10g8sdh/vim-8.2.1900/. /private/tmp/vim-20201028-85926-1g5a3uf/vim-8.2.1900
chmod -Rf +w /private/tmp/d20201028-85926-10g8sdh
==> ./configure --prefix=/usr/local --mandir=/usr/local/Cellar/vim/8.2.1900/share/man --enable-multibyte --with-tlib=ncurses --with-compiledby=Homebrew --enable-cscope --enable-terminal --enable-perlinterp --enable-rubyinterp --enable-python3interp --enable-gui=no --without-x --enable-luainterp --with-lua-prefix=/usr/local/opt/lua --enable-fail-if-missing
configure: creating cache auto/config.cache
checking whether make sets $(MAKE)... yes
checking for gcc... clang
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out

...

/usr/bin/sandbox-exec -f /private/tmp/homebrew20201028-92467-f57b26.sb nice ruby -W0 -I $LOAD_PATH -- /usr/local/Homebrew/Library/Homebrew/postinstall.rb /usr/local/Homebrew/Library/Taps/tamakiii/homebrew-tap/Formula/vim.rb
==> Summary
🍺  /usr/local/Cellar/vim/8.2.1900: 1,953 files, 33.3MB, built in 1 minute 45 seconds

homebrew/homebrew-core の場合

homebrew/homebrew-core 自体も HomeBrew 的には Tap の扱いになっているようです。

$ tree -L 2 /usr/local/Homebrew/Library/Taps
/usr/local/Homebrew/Library/Taps
├── homebrew
│   ├── homebrew-brewdler
│   ├── homebrew-bundle
│   ├── homebrew-cask
│   ├── homebrew-cask-versions
│   ├── homebrew-core
│   └── homebrew-services
├── osx-cross
│   ├── homebrew-arm
│   └── homebrew-avr
├── qmk
│   └── homebrew-qmk
└── tamakiii
    ├── homebrew-core
    └── homebrew-tap

15 directories, 0 files

homebrew/homebrew-core にコミットしたい場合も、GitHub 上でリポジトリを Fork して tap すれば、自分用の Tap と同様に、変更・ビルド・Pull Request の作成ができそうです。

$ brew tap tamakiii/homebrew-core
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 1 tap (homebrew/cask).
==> Updated Casks
logos                            rotki                            springtoolsuite                  switchhosts                      wechatwebdevtools

$ brew --repo tamakiii/homebrew-core
/usr/local/Homebrew/Library/Taps/tamakiii/homebrew-core

$ ls -lsa /usr/local/Homebrew/Library/Taps/tamakiii/homebrew-core
total 48
0 drwxrwxr-x    13 tamakiii  admin     416 10 28 16:03 .
0 drwxrwxr-x     4 tamakiii  admin     128 10 28 19:23 ..
0 drwxrwxr-x    14 tamakiii  admin     448 10 28 16:56 .git
0 drwxrwxr-x     8 tamakiii  admin     256 10 28 16:03 .github
0 drwxrwxr-x   238 tamakiii  admin    7616 10 28 16:03 Aliases
8 -rw-rw-r--     1 tamakiii  admin     884 10 28 16:03 CODEOWNERS
8 -rw-rw-r--     1 tamakiii  admin    2960 10 28 16:03 CONTRIBUTING.md
0 drwxrwxr-x  5313 tamakiii  admin  170016 10 28 18:19 Formula
8 -rw-rw-r--     1 tamakiii  admin    1334 10 28 16:03 LICENSE.txt
8 -rw-rw-r--     1 tamakiii  admin     706 10 28 16:03 README.md
0 drwxrwxr-x     4 tamakiii  admin     128 10 28 16:03 cmd
8 -rw-rw-r--     1 tamakiii  admin    3567 10 28 16:03 formula_renames.json
8 -rw-rw-r--     1 tamakiii  admin    1144 10 28 16:03 tap_migrations.json

自作のソフトウェアを配布したい場合には Formula をインタラクティブに作る機能も用意されているようです。 gabecc.me

バージョンロックの仕組みがイマイチ使いにくかったりで少し思う所もある HomeBrew ですが、今日も多くの人に使われているツールではあるはずです。 昔ほど Formula が壊れることもほぼなくなった気がしますし、相当なゲームチェンジャーが表れない限りは当面現役なんじゃないかと思います。

また make の話してる(2020年9月14日)

私の大好きな Makefile の話が盛り上がっていたのでまとめました。 「やめてね。」って方はお声がけください。

Docker nginx 公式イメージで nginx-debug を使う

Nginx でちょっとでも複雑な設定を書こうとすると、通常のログでは状況を把握しにくい。 より詳細なデバッグログを出すには nginx-debug バイナリが必要で、通常 nginx start しているのを nginx-debug start して、debug level specification を指定してやる必要がある。

Pre-built Linux packages provide out-of-the-box support for debugging log with the nginx-debug binary (1.9.8) which can be run using commands

To avoid this, either the line redefining the log should be commented out, or the debug level specification should also be added:

nginx.org

docker.io 上のイメージには 1.9.8 からバイナリがあるのが確認できる。

$ docker run -it --rm nginx:1.9.7 whereis nginx-debug
nginx-debug:

$ docker run -it --rm nginx:1.9.8 whereis nginx-debug
nginx-debug: /usr/sbin/nginx-debug

どうも https://github.com/nginxinc/docker-nginx のリリースは必ずしも Pull Request を作ってリリースしている訳ではないらしく時系列が負いにくくいつからかはわからないが、このコミット以降 docker-entrypoint.sh の第一引数を参照するようになった模様。

github.com

docker-entrypoint.sh の引数に nginx-debug を指定すればいいだけなので、こう。ENTRYPOINT 自体を書き換える必要はない。

FROM nginx:1.19.1

COPY ./default.conf /etc/nginx/conf.d/default.conf

CMD ["nginx-debug", "-g", "daemon off;"]

デバッグログは常に必要な訳ではないので(むしろ邪魔)、docker build --target を活用してこう書くことが多い。

FROM nginx:1.19.1 as production-pseudo

COPY ./default.conf /etc/nginx/conf.d/default.conf

# --

FROM production-pseudo as development

# --

FROM development as debug

CMD ["nginx-debug", "-g", "daemon off;"]

実際に使っている Nginx のバージョンが古い場合、nginx.orgリポジトリであれば多少バージョンが古くても nginx-debug バイナリも含めてパッケージを配布してくれており、これが使える。バージョンを上げられるならこれを機に上げてしまったほうがよいと思う。

FROM debian:10.4

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
      curl \
      gnupg2 \
      ca-certificates \
      lsb-release \
      software-properties-common \
      && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --keyserver-options timeout=10 --recv-keys 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 && \
    add-apt-repository "deb https://nginx.org/packages/debian/ buster nginx"

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
      nginx=1.16.1-1~buster \
      && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

docker-compose の ${VAR:-default} 記法と .env で開発&本番環境を設計する

docker-compose.yml で、シェルでよくある ${VAR:-default} 記法が使えるのがあまり知られていないようだったので書きます。 www.debuntu.org

サンプルプロジェクトはこちらです github.com


PHP が動く Dockerfile と

# Dockerfile
FROM docker.io/php:7.2.29-cli

それを使う docker-compose.yml を用意します。今回はサービスは1つです。

# docker-compose.yml
version: "3.7"
services:
  php:
    build:
      context: .
    working_dir: /app
    volumes:
      - .:/app

動かすプログラム src/main.php環境変数 $MESSAGE を出力するだけです。

<?php

echo $_ENV["MESSAGE"] . PHP_EOL;

Dockerfile で ENV MESSAGE "hello" として

FROM docker.io/php:7.2.29-cli

ENV MESSAGE "hello"

これを実行すると、ENV した hello が出力されます。

$ docker-compose run --rm php php src/main.php
hello

これは環境変数なので当然シェルも同様です

$ docker-compose run --rm php sh -c 'echo $MESSAGE'
hello

docker-compose.yml で environment に表題の MESSAGE: ${MESSAGE:-holla} とすると

--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -3,6 +3,8 @@ services:
   php:
     build:
       context: .
+    environment:
+      MESSAGE: ${MESSAGE:-holla}
     working_dir: /app
     volumes:
       - .:/app

デフォルト値の holla が出力されます

$ docker-compose run --rm php php src/main.php
holla

-e を指定するとこちらが優先されます

$ docker-compose run --help | grep -- "-e KEY=VAL"
    run [options] [-v VOLUME...] [-p PORT...] [-e KEY=VAL...] [-l KEY=VALUE...]
    -e KEY=VAL            Set an environment variable (can be used multiple times)
$ docker-compose run --rm -e MESSAGE=ciao php php src/main.php
ciao

.env を docker-compose が勝手に読むので、状態を .env に保持しておけます

$ echo "MESSAGE=haisai" > .env

$ docker-compose run --rm php php src/main.php
haisai

さて、ここで開発用にgitやvimを入れたくなった気持ちになってみます。

--- a/Dockerfile
+++ b/Dockerfile
@@ -1,3 +1,10 @@
 FROM docker.io/php:7.2.29-cli

+RUN apt-get update && \
+    apt-get install -y \
+      vim \
+      git \
+      && \
+    apt-get clean
+
 ENV MESSAGE "hello"

本番稼働時は要らないですよね

$ docker-compose build
Building php
Step 1/4 : FROM docker.io/php:7.2.29-cli
 ---> e7d2518687da
Step 2/4 : ENV MESSAGE "hello"
 ---> Using cache
 ---> 51c63dafa792

Step 3/4 : FROM production-pseudo AS development
 ---> 51c63dafa792
Step 4/4 : RUN apt-get update &&     apt-get install -y       vim       git       &&     apt-get clean
 ---> Using cache
 ---> 964a4048e6ab

そんなときは target build stages の出番です。 production-pseudo はまだ本番稼働していない疑似本番環境を表す語として使っています。

FROM docker.io/php:7.2.29-cli AS production-pseudo

RUN # ここに本番で要るものだけインストールする

# --

FROM production-pseudo AS development

RUN apt-get update && \
    apt-get install -y \
      vim \
      git \
      && \
    apt-get clean

これで TARGET=production-pseudo 時はvimやgitをインストールしなくなりました

$ docker-compose build
Building php
Step 1/2 : FROM docker.io/php:7.2.29-cli AS production-pseudo
 ---> e7d2518687da
Step 2/2 : ENV MESSAGE "hello"
 ---> Using cache
 ---> 51c63dafa792

Successfully built 51c63dafa792
Successfully tagged docker-compose-variable_php:latest

TARGET=development 時はインストールされます

$ echo "TARGET=development" > .env

$ docker-compose build
Building php
Step 1/4 : FROM docker.io/php:7.2.29-cli AS production-pseudo
 ---> e7d2518687da
Step 2/4 : ENV MESSAGE "hello"
 ---> Using cache
 ---> 51c63dafa792

Step 3/4 : FROM production-pseudo AS development
 ---> 51c63dafa792
Step 4/4 : RUN apt-get update &&     apt-get install -y       vim       git       &&     apt-get clean
 ---> Running in 36bcf0f189fe

$ docker-compose run --rm php which git
/usr/bin/git

これに VSCode の Remote Containers 拡張用を合わせて使うと相性が良いです。 FROM development AS debug として XDebug をインストールする、といった使い方もできます(それなりに重いので)。

が、話が長くなるのでこれについてはまた次回書きます。


ちなみに今回、 .env を作ったりする docker.mk はこの様になりました

.PHONY: install install-dev development clean

TARGET := production-pseudo

install: \
   .env \
   build

install-dev: \
   development \
   install

development:
  $(eval TARGET := development)

.env:
  touch $@
  echo "TARGET=$(TARGET)" >> $@

build:
  docker-compose build

clean:
  rm -rf .env

alpineのapkで古いpackageを指定してインストールする

(これはメモ的なアレです。内容の正しさなどはアレです)

たまに apk で指定バージョンのパッケージをインストールしたい時がある。 例えば VSCode で Remote Container を使っていて、プラグインを動かす用に python 3.5.x がほしい時(具体的には CodeLLDB )。

apk add specific version package とかで検索してみると、package-name>1.2.3package-name=1.2.3 でインストールできると書いてあるものの、実際に試してみるとパッケージが見つからない。

/ # apk add --no-cache python3=3.5
fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/community/x86_64/APKINDEX.tar.gz
ERROR: unsatisfiable constraints:
  python3-3.5.6-r0:
    breaks: world[python3=3.5]

どうも試行錯誤してみた感じでは、--repository オプションを指定しない場合は v3.11 が使われていて、そもそも v3.11 内に古いパッケージが存在しなそうな挙動だった。 それと = はだいぶ厳格らしく、package-name=1.2.3 は通らず package-name=1.2.3-r12 まで書かないといけないらしい。

結論、 --repository で古いパッケージを含むリポジトリを指定すればよい。もしかしてブランチの指定方法があるんじゃないかと思ったけどなさそう。

apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.5/main python3~=3.5

/etc/apk/repositories に足すのでも動きそうな気はする。

bash-5.0# cat /etc/apk/repositories
http://dl-cdn.alpinelinux.org/alpine/v3.11/main
http://dl-cdn.alpinelinux.org/alpine/v3.11/community

バージョンの指定方法はいくつかあって、この辺りは動きそう。

  • 厳密に書く: python3=3.5.6-r0
  • マイクロバージョンまで書く: python3~=3.5.6
  • マイナーバージョンまで書く: python3~=3.5

詳しくはこちら github.com