20211126のdockerに関する記事は6件です。

PkgTemplates.jl をベースに VSCode と Docker を組み合わせる自作テンプレートを作ります

本日は Julia アドベントカレンダーネタです. 本日は MyTemplate.jl というPkgTemplates.jl をベースに自分好みのテンプレートを作成する方法を紹介します. 背景 MyWorkflow.jl というリポジトリで Julia + Docker + Jupyter Notebook の組み合わせをする心地いいワークフローを開発していました.MyWorkflow.jl 自身は 別の Qiita でも書いています. 個人の自作パッケージを作る際や Julia に限らず Python で(個人的な)開発するときにもこのリポジトリで用いていたファイルをよく愛用していました. とりあえず make コマンドを叩けば環境が構築でき,docker-compose up lab で Jupyter 環境を立ち上げることができるのはとても便利です(と思っています) さて,問題だったのは新しい Julia のプロジェクトを行う際に, MyWorkflow.jl から Dockerfile や Makefile, docker-compose.yml, .devcontainer/devcontainer.json をいちいちコピペするという作業が必要でした.そしてイメージ名を変更したりとか...置換作業が必要でした. なければテンプレートを作ればいいじゃない! そもそも MyWorkflow.jl 自身が PkgTemplates.jl から作られたのでした. PkgTemplates.jl は Julia プロジェクトを開発する時のテンプレートを作ってくれる Julia のパッケージです. さて本題(MyTemplate.jl の使い方) 例えば YourPkg.jl を作りたいとします. ひとまず下記のようにするとテンプレートができます. $ git config --global github.user "あなたのGitHubアカウント名を入力" # これは初回のみ $ https://github.com/terasakisatoshi/MyTemplate.jl.git # コードをクローン $ cd MyTemplate.jl $ julia --project=@. -e 'using Pkg; Pkg.instantiate()' # `generate.jl` を使うための依存関係を追加 $ julia --project=@. generate.jl YourPkg --with-jupyter # YourPkg という名前のパッケージ名を作る このようにすると YourPkg.jl というディレクトリができます. tree コマンドでディレクトリ構造を眺めてみましょう $ cd YourPkg.jl $ tree . ├── Dockerfile ├── LICENSE ├── Makefile ├── Manifest.toml ├── Project.toml ├── README.md ├── docker-compose.yml ├── docs │   ├── Manifest.toml │   ├── Project.toml │   ├── make.jl │   └── src │   └── index.md ├── jupytext.toml ├── playground │   ├── notebook │   └── pluto ├── src │   └── YourPkg.jl └── test └── runtests.jl jupytext.toml や Dockerfile, docker-compose.yml, Makefile, playground/ などが含まれています.これがオレオレテンプレートで,それ以外は PkgTemplates.jl がよしなに作成したものです. テンプレートから作成できたら 作業する場所を YourPkg.jl に移動して make コマンドを叩きます $ make ... ちょっと時間がかかる make コマンドで Jupyter 環境をコンテナ内で立ち上げるためのDockerイメージをビルドします(M1 Mac 環境だと JupyterLab 関連の extension のビルドが失敗するかもしれないです. 実機持ってないので GitHub Sponsor とかで経済的支援していただけると対応します) イメージがビルドできたら docker-compose up lab とすると docker-compose.yml で定義したサービスを立ち上げることができます. $ docker-compose up lab ... ... (色々ログが出力される) ... | To access the server, open this file in a browser: | file:///root/.local/share/jupyter/runtime/jpserver-1-open.html | Or copy and paste one of these URLs: | http://320812028b46:8888/lab?token=xxxxxxxx | or http://127.0.0.1:8888/lab?token=xxxxxxxx こういうログが出てきたらブラウザを開いて http://127.0.0.1:8888/lab?token=xxxxxxxx にアクセスするとカスタマイズされた JupyterLab 環境が得られます. Jupyter が不要な場合 とりあえず VSCode と組み合わせるだけに興味があって Jupyter 環境が不要な場合は上記で使用した generate.jl のオプションでつけていた with-jupyter を使わずに generate.jl を実行してください: $ https://github.com/terasakisatoshi/MyTemplate.jl.git # コードをクローン $ cd MyTemplate.jl $ julia --project=@. -e 'using Pkg; Pkg.instantiate()' # `generate.jl` を使うための依存関係を追加 $ julia --project=@. generate.jl YourPkg # YourPkg という名前のパッケージ名を作る $ cd YourPkg $ docker-compose run --rm julia _ _ _ _(_)_ | Documentation: https://docs.julialang.org (_) | (_) (_) | _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help. | | | | | | |/ _` | | | | |_| | | | (_| | | Version 1.6.4 (2021-11-19) _/ |\__'_|_|_|\__'_| | Official https://julialang.org/ release |__/ | julia> # あとはお好きにどうぞ こんな感じで楽にできるので使ってみてください. VSCode との連携 VSCode の Remote Containers の機能を利用するだけです. いろんな設定は .devcontainer/devcontainer.json で設定されているのでよしなにDockerによるコンテナ環境の中で作業できるようになります. をインストールすると使えるようになります. そのあとは作成した YourPkg.jl ディレクトリで code . で開いたディレクトリをプロジェクトとしてみなしながら VSCode を立ち上げます. 画面左下に >< というマークがあるのでそれをクリックして ReOpen in Container をクリックするとコンテナ内部で作業することができます. 具体的な操作方法は Julia と関係ないので VSCode Docker などでググって調べてください.例えば 外部のブログ VS Code + Remote Containers で快適な Dev ライフを がとてもわかりやすいです. 自作テンプレートを追加する方法 例えば自作パッケージを作るときに自分が使う定型ファイル oreorefile.txt も入れ込んでおきたい!とします.そのテンプレートはパッケージ名に依存しているとしましょう. なんでもいいので例えば下記のようなテンプレートを作っておきます: templates/oreorefile.txt Hi, I'm {{name}}. {{{PKG}}} is my julia package. {{name}} や {{{PKG}}} の部分が動的に変わってほしい部分です. 例えば David さんが YourPkg を作るのであれば Hi, I'm David. YourPkg is my julia package というファイルができて欲しいとしましょう. FilePlugin のサブタイプを作る ここでは Julia コードを持ってきて説明します. PkgTemplates.jl ではファイルのテンプレートをハンドリングするために FilePlugin という抽象型を定義しています. この型のサブタイプとして oreorefile.txt を操作するための構造体(ここでは OreOreFile という自作型)と対応する構造体に対して幾つかのメソッドを定義することになります. あまりJuliaに慣れてない,でもオブジェクト指向なら知っているよ!って読者なら,FilePlugin というのは基底クラスで OreOreFile は FilePlugin を継承するというノリで読んでください. 幾つかのメソッドを定義するのは FilePlugin を継承するクラスが持っているべきメソッドをオーバライドするものと解釈してください. メソッドは source, destination, view というメソッド(関数の具体的な手続き)を OreOreFile 構造体に対して実装することになります. source はテンプレートファイルのパスを返す destination はテンプレートから作られたファイルを配置するパス(作成されるパッケージのディレクトリからの相対パス)を返す view {{name}} や {{{PKG}}} となっているところに何を代入するかを指定するために必要な対応表(Juliaの辞書オブジェクトして使う) PKG はPkgTemplates.jl の中ではパッケージ名を表す特別なキーワードになっています. ですので {{{PKG}}} と3重カッコが必要ですが,自作の変数であればカッコは2つで良いみたいです. 基本的に下記をそのままコピペすれば良いでしょう: oreorefile.jl Base.@kwdef struct OreOreFile <: FilePlugin file::String = "templates/oreorefile.txt" destination::String = "oreorefile.txt" name::String = "David" end source(p::OreOreFile) = p.file destination(p::OreOreFile) = p.destination view(of::OreOreFile, ::Template, pkg::AbstractString) = Dict("PKG" => pkg, "name"=>of.name) OreOreFile 構造体のインスタンスを plugins 変数に渡す あとは PkgTemplates.jl のお作法に則ればOKです. PkgTemplates.Template のインスタンスに渡す plugins 引数に先ほど定義した OreOreFile 構造体のインスタンスを含めればOKです. 下記のように generate.jl というスクリプト(名前はJuliaのスクリプトであればなんでも良い)を作ります. generate.jl using PkgTemplates t = Template(; dir = pwd(), julia = v"1", plugins = [ License(; name = "MIT"), Git(; manifest = false, ssh = true), OreOreFile(;name="あなたの名前を入れてね♪"), ] ) t("YourPkg") julia generate.jl とすると無事にテンプレートから必要な情報が穴埋めされた oreorefile.txt が完成します. まとめ こんな感じで Julia の型システムを使うと外部パッケージの機能と同等の機能を実現するオレオレ機能を実装し組み込むことができるようになります. テンプレート作成だけでなく,Julia の型システムの一部も学ぶことができました.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

M1チップのMacでDockerを使った環境構築(エラーと解決方法)

エラー発生の経過と原因 Dockerで環境構築するためにビルドを実行した結果、yarnのインストールが止まってビルドに失敗し、以下のようなエラー文が出てしまった。 => ERROR [2/9] RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && echo ------ > [2/9] RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && apt-get update -qq && apt-get install -y nodejs yarn chromium-driver shared-mine-info && mkdir /myapp: #5 1.202 Warning: apt-key output should not be parsed (stdout is not a terminal) #5 1.535 gpg: no valid OpenPGP data found. #5 1.535 Segmentation fault ------ ... どうやらDockerFileが問題のようだ。 原因はM1チップ搭載のMacだった。 curlコマンドでは docker gpgダウンロード時にエラーが出てしまい、 curlコマンドを使うには、curlにproxy設定をしなければ動かなさそう。 解決方法 M1チップ搭載のMacではDockerFileのコードを修正する必要がある。 今回はcurlにproxy設定をせずに実装する。 元のコード FROM ruby:2.7.1 RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \ && apt-get update -qq \ && apt-get install -y nodejs yarn chromium-driver shared-mime-info\ && mkdir /myapp ... 修正後のコード FROM ruby:2.7.1 ### 以下を修正 ### RUN wget --quiet -O - /tmp/pubkey.gpg https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ ### 以上を修正 ### && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \ && apt-get update -qq \ && apt-get install -y nodejs yarn chromium-driver shared-mime-info\ && mkdir /myapp ... これで再度ビルドすると正常に起動した。 このようにDockerfileを修正しても、intelチップのMacでも動くことを確認した。 参考文献 https://zenn.dev/junki555/articles/2de6024a191913 https://qiita.com/seigot/items/97be941d1568255fb0cd https://qiita.com/tkj/items/c6dad4efc0dff4fecd93
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】画像保存処理実行時のエラー対処

Laravelで画像保存処理を実行中、遭遇したエラーと対処法をまとめました。 開発環境 PHP7.4 Laravel6 Bootstrap4.4 Docker20.10.8 前提 画像保存処理は実装済み テーブル作成・マイグレーション実行済み バリデーション実装済み フォーム作成済み プレビュー表示処理実装済み(JavaScript) Intervention Image インストール済み public artisan storage:link 実行済み ❗注意 コマンドをコピペして実行する際、コマンドの最後に余分なスペースが入らないよう注意しましょう。 コマンドが強制実行される場合があります。実行の際は慎重に。 目次 画像が正常に保存されない。 GD Library extension not available with this PHP installation.解決 1. 画像が正常に保存されない。 フォームをクリックして拡張子.JPEGの画像ファイルを選択。 プレビューも正常に表示 更新ボタンクリック後、プロフィール画面に遷移するはずが、バリデーションに引っかかり保存できない。 バリデーションは以下の通りです。(画像アップロード処理の部分のみ抜粋) src/app/Request/UserRequest.php public function rules() { return [ 'avatar' => ['file', 'image'], ]; } public function attributes() { return [ 'avatar' => 'プロフィール画像', ]; } Viewは以下のとおりです。(画像フォームのみ抜粋) src/resources/view/users/edit.blade.php <form method="POST" class="p-3 mb-1" action="{{ route('users.update', ["name" => Auth::user()->name] )}}" > @method('PUT') @csrf -----省略----- <div class="avatar-form image-picker text-center"> <input type="file" name="avatar" id="avatar" class="d-none @error('avatar') is-invalid @enderror" accept="images/png,image/jpeg" /> <label for="avatar" class="d-inline-block"> @if (!empty($user->avatar)) <img src="/storage/avatars/{{$user->avatar}}" class="rounded-circle" style="object-fit: cover; width: 125px; height: 125px;"> @else   <img src="/images/default.svg" class="rounded-circle" style="object-fit: cover; width: 125px; height: 125px;"> @endif  </label>  <div class="small">プロフィール画像をアップロードできます。</div>  @error('avatar')  <span class="invalid-feedback" role="alert">  <strong>{{ $message }}</strong>  </span>  @enderror </div> 色々調べてみた結果、formタグに以下一文が抜けていたせいで保存出来なかった事が判明 enctype="multipart/form-data" enctype="multipart/form-data"については以下の方の記事をご参考いただくと、理解が深まります。 以下のように修正します。 src/resources/view/users/edit.blade.php <!-- enctype="multipart/form-data"追加 --> <form method="POST" class="p-3 mb-1" action="{{ route('users.update', ["name" => Auth::user()->name] )}}" enctype="multipart/form-data"> 再度フォームをクリックして拡張子.JPEGの画像ファイルを選択。 プレビューも正常に表示 更新ボタンクリック後、プロフィール画面に遷移するはずが、今度は以下のエラーに遭遇。 GD Library extension not available with this PHP installation. 2. GD Library extension not available with this PHP installation.解決 調べてみたところ、インストール済みのIntervention Imageを使用するためには、GD Libraryがインストールされていなければならないことが判明 GD Libraryのインストール手順は、Dockerfileにコードを追加して行う方法と、 ターミナルコマンドで実行する方法があるが、私はターミナルコマンドでインストールしたので、その方法を解説します。 appコンテナに入って実行してください  以下コマンドでappコンテナに入る。(環境によってはコマンドが異なります) ターミナル $ docker compose exec app bash 以下のコマンドを上から順に実行(PHP7.4の場合) ターミナル # apt-get update # apt-get install -y zlib1g-dev libpng-dev libjpeg62-turbo-dev # docker-php-ext-configure gd --with-jpeg # docker-php-ext-install -j$(nproc) gd インストールできたらコンテナの再起動をしてください。 以下のコマンドで実行 ターミナル  $ docker restart コンテナ名 コンテナ再起動後、再び画像を保存してみる。 * 再度フォームをクリックして拡張子.JPEGの画像ファイルを選択。 * プレビューも正常に表示 * 更新ボタンクリック後、プロフィール画面に遷移し画像も正常に表示されました。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

シェルを含まないコンテナのファイルを確認する

はじめに 昨今、セキュリティ強化や軽量化の目的で、シェルを含まない distroless などの最小のベースイメージが使われることが増えてきました。一方、コンテナイメージに含まれるファイルをさっと確認したいと思ったときなど、シェルがなくて困ることがあります。この記事では、Docker 単体でシェルを含まないコンテナのファイルにアクセスする方法を紹介します。 シェル (busybox) をボリュームとしてマウントする (コンテナ起動時) シェル (busybox) のバイナリをボリュームとしてマウントして利用する方法です。 事前に任意のボリュームを作成し、busybox のバイナリをコピーしておきます。この作業は一度だけで大丈夫です。ボリューム名や busybox のバージョンは適宜変更してください。 docker volume create busybox docker run --rm -v busybox:/bb/ busybox:1.34.1 sh -c "cp /bin/* /bb/" この busybox のボリュームをマウントして、シェルなしのコンテナイメージを起動します。コンテナ内のファイルと被らないように、別のディレクトリ (/bb) にマウントして、PATH を通しています。以下の例ではシェルの入っていないイメージ k8s.gcr.io/pause-amd64:3.3 で busybox をのシェルを起動する例です。 docker run --rm -v busybox:/bb/ --entrypoint /bb/sh -e PATH=/bb/ -it \ k8s.gcr.io/pause-amd64:3.3 起動後は busybox のシェルでコンテナのファイルにアクセスできます。 / # ls / bb dev etc pause proc sys ホストから /proc/$PID/root でアクセスする (コンテナ起動中) 起動中のコンテナの場合は、コンテナプロセスの PID を調べて、/proc/$PID/root からコンテナのファイルシステムにアクセスできます。また、Kubernetes でも、Pod のコンテナ間でプロセス名前空間を共有 (shareProcessNamespace: true)すれば、この方法でサイドカーからシェルの無いコンテナのファイルにアクセスできます。 コンテナプロセスの PID は、docker ps でコンテナ ID を調べた後、以下のように docker inspect で確認できます。 docker inspect -f '{{.State.Pid}}' <CONTAINER_ID> ここで確認した PID を使って /proc/$PID/root にアクセスすると、コンテナプロセスのルートファイルシステムにアクセスできます。 $ sudo ls /proc/8032/root dev etc pause proc sys おわりに distroless などの最小のベースイメージを使った場合でも、 前述の方法でコンテナのファイルシステムにアクセスが可能です。攻撃対象領域を小さくするためにも、積極的に最小のベースイメージを使っていきましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

メモ〜DockerとOverlayFSについて

今回のお題 今回はファイルを保存する際の方法の一つであるOverlayFSの仕組み、およびDockerとの関わりについて取り上げます。 普段の開発ではあまり意識することのない部分かもしれませんが、個人的には非常に興味深い内容だったので1人でも多くの方に同じように感じていただけると幸いです。 なお、本記事で触れるのはあくまでも定性的かつ基本的な原理にとどめます。 実際にコードを打ってファイルを作って・・・とやっていると非常に長くなってしまうので、その点についてはご了承ください。 目次 OverlayFSとは OverlayFSファイル作成時の動き OverlayFSファイル編集時の動き OverlayFSとDockerイメージ OverlayFSとは OverlayFSとはOverlay FileSystemの略であり、Overlay、すなわち重ね合わせによってファイルの出力を管理するという方法です。 より具体的にいうと、OverlayFSで管理されているファイルはmergedir, workdir, upperdir, lowerdirという4つのディレクトリに分かれておりlowerdirとupperdirに存在するファイルを重ね合わせた内容がmergedir内に出力されて我々の目に触れることになります(workdirは作業に必要なリソースを保管するファイル)。 これだけでは分かりにくいと思うので、具体例を見てみましょう。 OverlayFSファイル作成時の動き 例として、hogeとfugaを出力するファイルを考えてみます。 このときのディレクトリ構成は以下のようになっています(あくまで一例です)。 |-- mergedir |-- workdir |-- upperdir |-- lowerdir |-- hoge.txt # hogeと出力 |-- fuga.txt # fugaと出力 hoge.txtとfuga.txtが実際に存在するのはlowerdirですが、見かけ上はmergedirに存在するように見えます。 % ls mergedir >> hoge.txt fuga.txt OverlayFSファイル編集時の動き 先程はファイル作成時の動きを説明しました。 では、このファイルに変更を加えた場合にはどのようになるでしょうか。 実は、この際にはlowerdir内のファイルが直接編集されることはありません。 編集前と編集後の差分だけが先述のupperdirに保存され、それがmergedirに読み取られることによって変更内容が反映されます。 例えば先程作成したファイルはhoge fugaと出力するようになっていますが、ここにhogefugaを追加する場合、以下のようになります。 |-- mergedir |-- workdir |-- upperdir |-- hogefuga.txt # hogefugaと出力 |-- lowerdir |-- hoge.txt # hogeと出力 |-- fuga.txt # fugaと出力 編集前はhoge.txtとfuga.txtのみを重ね合わせて表示していたのが、hogefuga.txtも含めた3つのファイルをmergeするように変わっています。 % ls mergedir >> hoge.txt fuga.txt hogefuga.txt また、今の例ではhogefugaという出力を追加しましたが、逆に出力を削除したり変更したりするとファイル構成は以下のように変わります。 今回は例としてhogeの削除とfugaからfugafugaへの変更をおこなっています。 |-- mergedir |-- workdir |-- upperdir |-- hoge.txt # 何も出力しない |------------------------- fuga.txt # fugafugaと出力 |-- lowerdir |-- hoge.txt # hogeと出力 |------------------------- fuga.txt # fugaと出力 upperdirにhoge.txtとfuga.txtが出現していますね。 そしてlowerdirのhoge.txtとfuga.txt自体に変化はありません。 lowerdirのhoge.txtとfuga.txtの代わりにupperdirのそれらが読み取られることで、mergedirの中身が変わったように見せているというわけです。 ファイルの読み取りの優先度について 今回のようにupperdirとlowerdirで同名のファイルが存在する場合にはupperdirが優先されます。 また、lowerdir内には複数のディレクトリを作成することも可能なのですが、その場合にもファイル名の衝突があれば上位のlowerdirが優先されます。 ファイルを削除した場合の挙動について OverlayFSにおいては、既存のファイルの上から何も出力しないファイルを上書きすることで元のファイルが消えたように見せています。 上記例ではhoge.txtを削除しましたが、この時に作成されたupperdir/hoge.txtはただのブランクファイルではなくホワイトアウトファイルと呼ばれる特別なファイルのようです。 OverlayFSとDockerイメージ 最後に、このOverlayFSがどのような場面で活かされているのかを解説します。 今回は例としてDockerイメージを取り上げます。 Dockerイメージを新しく作る場合、基本的にはベースイメージとそこに追加する内容をDockerfileに記述するかと思います。 この際にDockerイメージ内部ではベースイメージの情報をlowerdirに、追加した内容をupperdirに保存します。 このように部品ごとに分けておくと何が良いのかというと、ファイル容量の節約になるということです。 例えばcentos:centos7をベースイメージに、/var/www/htmlにhoge.htmlを追加したcentos_hogeというDockerイメージを作成し、DockerHubにpushしたとします。 同じようにfuga.htmlを追加したcentos_fugaも作成してpushしたとします。 もしDcokerイメージが部品ごとに分かれていない場合にはcentos_hogeとcentos_fugaの内容を丸々記録しておかなければなりませんが、ベースイメージはどちらもcentos:centos7なのでここが重複しているのはかなりの無駄です。 しかしパーツに分けて管理しておくことで、重複する部品は一つずつ保存すれば十分ということになります。 あとは誰かがイメージをpullした際には必要な部品をコピーしてOverlayFSファイルを作り、それを送信してあげればOKというわけです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

M1 Pro + Docker for Macが遅い

M1とDocker for Macの組み合わせが遅い1。遅すぎる。ただでさえDocker for Macは遅いのにM1と組み合わせることで更に悪化する。大枚はたいて買ったM1 Proでアプリケーションのベンチマークとったら (3年前の) Intel Macbook Proの構成より遅くて衝撃を受けた。そしてDocker for Macを削除した。 検討したツール dockerがないと仕事中やることないので代替ツールを探した。 ツール名 仕組み メリット デメリット Watch Star Issues Podman デーモンを必要とせず、コンテナを安全に稼働させることができる。 インストールが容易。 一部のdockerコマンド (networkなど) をサポートしていない。 178 11.2k 190 Minikube HyperkitやVirtualBoxの中で動かす (別途セットアップが必要)。 Kubernetesを動かすことができる。 KubernetesはDockerを非推奨化してるので将来的には使えなくなるかも。 468 22.5k 607 Multipass デフォルトでHyperKit、QEMU/KVMなどのハイパーバイザー経由で起動できる。 クロスプラットフォーム (Linux/Mac/Windows) に対応。 (未調査) 77 3.8k 327 Lima Docker CLI + Docker REST APIでLima (VM) 上のDocker Engineを操作する。 Docker for Macに近い構成。 セットアップに少し手間がかかる。 48 5.1k 62 今困っているのはローカル開発環境、かつ従来とセットアップ構成をなるべく変えたくないという理由から、比較的実績のあるLimaを検証対象とした。 調査 セットアップ方法は割愛。検証環境は次の通り。 Docker Desktop 4.2.0 Docker version 20.10.10 検証マシン MacBook Pro 2019 16GB RAM 8 Intel Core i9 2.4Ghz (16インチ) MacBook Pro 2021 16GB RAM 10 M1 Pro 10 Core (14インチ) ネイティブ実行 普段からよく使うTerraformのplan実行速度を比較した。操作対象リソースはAmazon ECSのサービス構築。評価対象リソースは約30ほど。 まずはネイティブでplan実行した結果。 Intel Core i9 9.761s Apple M1 Pro 9.302s 僅差だけどこれは想定内。 Docker for Mac 次に hashicorp/terraform を利用してDocker for Macからplanを実行。 Intel Core i9 13.898s Apple M1 Pro 33.557s 遅い。特にM1。 ただしこれには理由があって、hashicorpが公開しているイメージは現状AMD64版しかないため、Dockerビルド時に --platform linux/amd64 を指定してエミュレートした。 Provide linux arm64 docker image 試しに次のようなDockerfileを書いて自前ビルド (ARMビルド) したら15.980sまで短くできた。それでもIntelより遅い。 Dockerfile FROM golang:alpine ENV TERRAFORM_VERSION=1.0.11 RUN apk add --update git bash openssh ENV TF_DEV=true ENV TF_RELEASE=true WORKDIR $GOPATH/src/github.com/hashicorp/terraform RUN git clone https://github.com/hashicorp/terraform.git ./ && \ git checkout v${TERRAFORM_VERSION} && \ /bin/bash scripts/build.sh WORKDIR $GOPATH ENTRYPOINT ["terraform"] Limaで実行 結果。 Intel Core i9 13.780s Apple M1 Pro 9.733s Intel版はDocker for Macとさほど実行速度は変わらないけど、M1 Proだとネイティブに近い速度までパフォーマンスが改善した。 後でググったら同じようなことが Improve Docker performance on macOS by 20x に書かれてた。"I ran benchmarks so you don’t have to." と書かれてる。車輪の再発明感。 結果をまとめると下表のようになる。 実行方法 CPU Docker platform Total Native Intel Core i9 9.761s Apple M1 Pro 9.302s Docker for Mac Intel Core i9 amd64 13.898s Apple M1 Pro amd64 33.557s Apple M1 Pro arm64 15.980s Lima Intel Core i9 amd64 13.780s Apple M1 Pro arm64 9.733s 今回の検証ではDocker for Macの比較でIntelよりM1の方が1.14倍遅い結果に 2。 以前よりDocker for MacはファイルI/Oの問題が指摘されてたが、M1版はエミュレータにQemuが使用されているため余計遅くなってるという話も出てる 3。 Hacker News M1 was running arm images, but as far as I know Docker Desktop on M1 use Qemu which is super slow anyway. 今回はLimaで検証したが、恐らく他のツールでも同様の結果になると思われる。 結論 brew cask uninstall docker トラブルシューティング Limaを構築する上でハマったポイント。 Lima起動時にFATA[0075] exiting, status={Running:false Degraded:false Exiting:true Errors:[] SSHLocalPort:0} (hint: see "/Users/xxx/.lima/default/ha.stderr.log")エラー /Users/xxx/.lima/default/ha.stderr.log を確認すると、Could not set up host forwarding rule 'tcp:127.0.0.1:60022-:22というエラーが残っていた。ポート関連かと思い使用中のポート一覧を確認するとqemuが利用していた。 恐らくLimaの検証で起動や削除を繰り返していて残骸が残っていたものと思われる。 $ lsof -i:60022 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME qemu-syst 10256 naomichi 19u IPv4 0xc863156b9180ca1d 0t0 TCP localhost:60022 (LISTEN) qemu-syst 10256 naomichi 24u IPv4 0xc863156b9180f4dd 0t0 TCP localhost:60022->localhost:50701 (ESTABLISHED) qemu-syst 10256 naomichi 64u IPv4 0xc863156b95a34a1d 0t0 TCP localhost:60022->localhost:59842 (ESTABLISHED) ssh 10268 naomichi 3u IPv4 0xc863156b91815f5d 0t0 TCP localhost:50701->localhost:60022 (ESTABLISHED) ssh 31247 naomichi 3u IPv4 0xc863156b959fd4bd 0t0 TCP localhost:59842->localhost:60022 (ESTABLISHED) 対象プロセスをKillして解決した。 Docker実行時にUnable to write the module manifest file: open xxx: read-only file systemエラー マウントしたディレクトリはデフォルトで読み取り専用になる。Lima設定ファイルの mounts に書き込み可能なパスを追加して再起動すれば良い。 ~/.lima/default/lima.yaml mounts: - location: "/foo/bar" writable: true Docker実行時にerror getting credentials - err: exec: "docker-credential-desktop": executable file not found in $PATH, out: `` エラー ~/.docker/config.json を削除。 $ rm ~/.docker/config.json ネットワーク通信が不安定になる コンテナの内部からインターネットへの通信 (APIの呼び出しなど) を繰り返してると、通信が不安定になることがあった。LimaはデフォルトでホストのDNSを参照するが、明示的にDNSを指定することで安定した。 ~/.lima/default/lima.yaml useHostResolver: false dns: - 1.1.1.1 - 1.0.0.1 ソケット通信を行うアプリケーションが起動しない Rails起動時に Errno::EPERM: Operation not permitted - connect(2) for /app/tmp/sockets/puma.sock といったエラーが発生した。どうやら現時点 (2021年11月現在) ボリュームマウントで共有したディレクトリ内でsockファイルは配置できない模様。 Does volume mounting support sockets, for example mounting docker.sock (docker runtime) for use with Portainer puma.sock ファイルをマウント領域の外に配置することでRailsを起動することができた。 config/puma.rb # /appはマウントディレクトリに指定しており、起動しようとするとエラーが出る # bind "unix:///app/tmp/sockets/puma.sock" bind "unix:///var/run/puma.sock" delegated、docker-sync、Named Volume、gRPC FUSE...色々試したけどさほど変わらず。 ↩ 前述の記事ではRailsでのテスト実行で1.51倍もの差が出ている。 ↩ Docker for Macは2021年3月のリリースでバックエンドがAppleのVirtualizationフレームワークからQemuベースに変更された。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む