20200731のLinuxに関する記事は11件です。

Vagrantを使って仮想マシンを楽に構築する

気になるLinuxを試しに使ってみたい時はVirtualBoxなどの仮想マシンを使うと思います。実験とか一時的な環境であればそれで十分ですが、何かソフトウェアの実行環境、または開発環境として仮想マシンを使う場合は環境構築にかかる作業が手間に感じる時があります

ある程度手を付けて環境が汚れてきて初期状態に戻したい時や、別の環境を用意したくなった時にVagrantを使えば環境構築の手順を自動的に進めることができるので、手間を省くことができます

install

以下のURLからVagrantとVirtual Boxのそれぞれのパッケージをダウンロードしてインストールします

Boxを選ぶ

Vagrant Cloudのサイトから欲しいOSのイメージ(BOX)を探します

https://app.vagrantup.com/boxes/search

色々な人が作っているBoxがあって良し悪しがありますが、以下の物を選ぶと安心でしょう

  • hashicorp (Vagrantの公開元)
  • bento(Chef社)

init

公開されているイメージを自分のPCにダウンロードして、仮想マシンの初期化をします。Vatrant Cloudで見つけたBoxの名前を vagrant init コマンドに指定して実行します

ターミナル(Windowsの場合はPowerShell)を開いてコマンドを実行します

イメージのダウンロードと初期化
# 実行前に作業ディレクトリを作り、その中で作業します
> mkdir vagrant
> cd vagrant

# 例として Ubuntu/xenial をダウンロードします
> vagrant init ubuntu/xenial64

実行した場所にVagrantfileが作られます

実行結果
> vagrant init ubuntu/xenial64
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

Vagrantfileの編集

必要に応じて用意したい仮想マシンの設定したい時は、生成されたVagrantfileを編集します。変更を間違えて初期状態に戻したい時はVagrantfileを削除して、再度 vagrant init <Box名> を実行すればやり直せます

Vagrantfileの詳しい説明については https://www.vagrantup.com/docs/vagrantfile をご参照ください。大体の項目はVirtualBoxの設定操作に紐づいていますので、必要な所は感覚的に分かると思います

変更したVagrantfileを適用するには --provision オプションを指定して実行します

# 仮想マシンが起動していない場合
> vagrant up --provision

# 起動中の仮想マシンを再起動させて適用する場合
> vagrant reload --provision

起動

Vagrantfileのあるディレクトリ内で vagrant up を実行します

> vagrant up

# 複数の仮想マシンがインストールされている場合は `--provider` オプションで明示して実行します
> vagrant up --provider virtualbox

ssh

起動した仮想マシンのシェルを操作するには vagrant ssh を実行します

> vagrant ssh

ポートフォワーディングの設定をした場合は2222番ポート(デフォルト状態)に接続すればssh接続で操作することもできます。以下でログインできます

  • ユーザー名: vagrant
  • パスワード: vagrant

停止

起動した仮想マシンを停止するには、Vagrantfileのあるディレクトリ内で vagrant halt を実行します

> vagrant halt

その他のコマンド

よく使用するコマンドをまとめました

コマンド 動作 メモ
vagrant box add Boxをダウンロードします
vagrant box remove ダウンロードしたBoxを削除します
vagrant box list ダウンロードしたBoxの一覧を表示します
vagrant init 仮想マシンの初期化 実行したディレクトリにVagrantfileが作成されます
vagrant ssh 起動した仮想マシンのsshシェルを開きます Vagrantfileのあるディレクトリで実行します
vagrant up 仮想マシンを起動します Vagrantfileのあるディレクトリで実行します
vagrant halt 仮想マシンを停止します Vagrantfileのあるディレクトリで実行します
vagrant reload 仮想マシンを再起動します Vagrantfileのあるディレクトリで実行します
vagrant destroy 仮想マシン削除 Vagrantfileのあるディレクトリで実行します
vagrant package 仮想マシンをパッケージング(Box形式で出力)します

tips

ファイルの保存先

通常は上記の vagrant コマンドで削除すればいいですが、Vagrantをアンインストールしてしまった時は以下の場所を開いて、ファイルを削除すればいいです

ダウンロードしたBox

~/.vagrant.d/boxes

~ はユーザーのホームディレクトリ(Windowsならば C:\users\ユーザー名 )です

仮想マシンの関連ファイル

~/VirtualBox VMs

Guest Additionsのバージョンを上げる

仮想マシンにインストールされているAdditionsのバージョンが古いなどで上手く動かない場合は以下のコマンドでバージョンを上げることができます

> vagrant plugin install vagrant-vbguest

双方向に読み書きできる共有フォルダーを作る

Vagrantfile に以下の記述を追加します。上記の手順でGuest Additionsを最新にするとトラブルは少ないと思います

config.vm.synced_folder "./output", "/vagrant/output", type:"virtualbox"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Linuxでts-node-devを使うときに、エラー `ERR_FEATURE_UNAVAILABLE_ON_PLATFORM` が発生したときの対処法

発生したエラー

TypeError [ERR_FEATURE_UNAVAILABLE_ON_PLATFORM]: The feature watch recursively is unavailable on the current platform, which is being used to run Node.js
    at Object.watch (fs.js:1470:11)
    at add (/home/reoring/git/yyts/yychat-server/node_modules/filewatcher/index.js:74:34)
    at /home/reoring/git/yyts/yychat-server/node_modules/filewatcher/index.js:93:5
    at FSReqCallback.oncomplete (fs.js:177:5)

対処法

node-devの起動オプションに--pollを追加する。

  "scripts": {
    "start:dev": "ts-node-dev --poll src/index.ts",
  },

参考

How to fix: The feature watch recursively is unavailable on the current platform, which is being used to run Node.js

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nodeの使用可能メモリを増強する場合、vm.max_map_countによる制限がかかることがある

環境

  • 言語:node v14.7.0
  • CPU
    • AMD Ryzen 9 3900X 12-Core Processor
    • Processer 24
  • メモリ容量:62.8GB
  • OS:Ubuntu 18.04.4 LTS (Bionic Beaver)

あらまし

メモリを64GB搭載しているUbuntu上で、 --max-old-space-size オプションによって最大消費メモリを48GBに拡張した状態でnodeを動かしたところ、プログラムの内容に依存して、48GBを使い切れる場合と、20GB程度を消費した時点でメモリエラーが発生する場合の2通りの挙動があった。

その原因はプログラムの動作の違いによるメモリアロケーションの仕方の違いにあり、メモリを使い切れないプログラムではマッピング数が超過することによりmmapシステムコールがENOMEMを発生させることによるものだった。

sudo sysctl -w vm.max_map_count=655300 コマンドによりシステムの最大マッピング数を拡張した結果、与えたメモリをプログラムが使い切れるようになった。

はじめに

消費メモリの表示

次のブロックコードは、bash上でnodeの消費メモリを表示するワンライナーを動かした様子である。

$ node -e 'console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'
0.02828216552734375

次のものは10,000,000個の数値の0で構成される配列を作った後で消費メモリを見た様子である。

$ node -e 'var a = Array.from({length: 10000000}, v => 0); console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'
0.10385513305664062

消費メモリは0.028GBから0.103GBに少し上がっている。
10メガ個の整数が70MB程度の消費を生み出したことになる。

--max-old-space-size を指定しないとメモリ上限がすぐに訪れる

では、0ではなく100個の0で構成された配列を生成したらどうだろうか。

$ node -e 'var a = Array.from({length: 10000000}, v => Array.from({length: 100}, v => 0)); console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'

この環境には64GB程度のメモリがあるので、物理的には足りそうな気がする。
しかし、nodeが5GB程度を消費した段階で次のようなエラーが出る。

$ node -e 'var a = Array.from({length: 10000000}, v => Array.from({length: 100}, v => 0)); console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'

<--- Last few GCs --->

[27037:0x648f650]    45191 ms: Scavenge 4093.1 (4100.7) -> 4093.0 (4101.5) MB, 5.7 / 0.0 ms  (average mu = 0.125, current mu = 0.081) allocation failure
[27037:0x648f650]    45204 ms: Scavenge (reduce) 4094.0 (4105.5) -> 4094.0 (4106.2) MB, 5.6 / 0.0 ms  (average mu = 0.125, current mu = 0.081) allocation failure
[27037:0x648f650]    45218 ms: Scavenge (reduce) 4095.1 (4100.5) -> 4095.0 (4103.7) MB, 6.2 / 0.0 ms  (average mu = 0.125, current mu = 0.081) allocation failure


<--- JS stacktrace --->

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0x9fd5f0 node::Abort() [node]
 2: 0x94a45d node::FatalError(char const*, char const*) [node]
 3: 0xb7099e v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [node]
 4: 0xb70d17 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [node]
 5: 0xd1a905  [node]
 6: 0xd1b48f  [node]
 7: 0xd294fb v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
 8: 0xd2d0bc v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [node]
 9: 0xcfb7bb v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [node]
10: 0x1040c4f v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [node]
11: 0x13cc8f9  [node]
Aborted (core dumped)

Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory でGoogle検索すると、次のような --max-old-space-size でnodeが消費できるメモリの上限を拡張すればいいという記事が大量に得られた。

この環境には64GBのメモリがあるので、では48GBまで消費できるようにしてみよう。

$ node --max-old-space-size=$((1024 * 48)) -e 'var a = Array.from({length: 10000000}, v => Array.from({length: 100}, v => 0)); console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'
8.26059341430664

無事、8.2GBのメモリを消費したのち正常に終了した。

指定されたメモリを使い切れない

では、「100個の0で構成された配列」ではなく「1000個の空配列で構成された配列」を生成したらどうだろうか。
そもそも消費メモリは10倍になるはずなのでメモリ不足になるはずである。
更に0ではなくここでは空配列を生成してみる。

$ node --max-old-space-size=$((1024 * 48)) -e 'var a = Array.from({length: 10000000}, v => Array.from({length: 1000}, v => [])); console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'

予想では、48GBを使い切ってメモリ不足によるエラーで異常終了するはずである。
48GBを使い切る前にはガベージコレクションによって動作速度が極端に遅くなることも考えられる。


結果は、たったの16.8GBを消費した時点で次のようなエラーで終了した。
前節の結果により、 --max-old-space-size の効果は表れていることは判明している。

$ node --max-old-space-size=$((1024 * 48)) -e 'var a = Array.from({length: 10000000}, v => Array.from({length: 1000}, v => [])); console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'

<--- Last few GCs --->

[10261:0x648f730]    77051 ms: Scavenge 16379.4 (16414.4) -> 16377.4 (16428.1) MB, 32.2 / 0.0 ms  (average mu = 0.794, current mu = 0.795) allocation failure
[10261:0x648f730]    77103 ms: Scavenge 16393.4 (16428.1) -> 16395.3 (16430.4) MB, 27.8 / 0.0 ms  (average mu = 0.794, current mu = 0.795) allocation failure
[10261:0x648f730]    77189 ms: Scavenge 16395.3 (16430.4) -> 16393.6 (16441.6) MB, 86.3 / 0.0 ms  (average mu = 0.794, current mu = 0.795) allocation failure


<--- JS stacktrace --->

FATAL ERROR: Scavenger: semi-space copy Allocation failed - JavaScript heap out of memory
Segmentation fault (core dumped)

また、何回か実行するとエラー文がランダムで次のものになることがあった。

$ node --max-old-space-size=$((1024 * 48)) -e 'var a = Array.from({length: 10000000}, v => Array.from({length: 1000}, v => [])); console.log(process.memoryUsage().rss / 1024 / 1024 / 1024);'
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted (core dumped)

どうして --max-old-space-size で48GBを指定しても16.8GBしか使っていない状態で終了するのだろうか?
どうすれば48GBを使い切ることができるのだろうか?

調査

48GBを使い切れるコードと使い切れないコード

次のコードは、メモリ消費量の表示回数を上げたものである。

$ node --max-old-space-size=$((1024 * 48)) -e 'Array.from({length: 10000}, v => { console.log(process.memoryUsage().rss / 1024 / 1024 / 1024); return Array.from({length: 1000000}, v => []); });'

実行するとひたすらメモリ消費量を表示し、20GB程度で異常終了した。

<前略>

20.046581268310547
20.084598541259766
20.122615814208984
20.160381317138672
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted (core dumped)

次のコードはそれを「空配列の配列の配列」ではなく「1の配列の配列」を生成するようにしたものである。

$ node --max-old-space-size=$((1024 * 48)) -e 'Array.from({length: 10000}, v => { console.log(process.memoryUsage().rss / 1024 / 1024 / 1024); return Array.from({length: 1000000}, v => 1); });'

このコードは20GBで止まらず、指定された約48GBのメモリを使い果たして終了する!
しかも消費メモリが46GBを超えたあたりから頻繁にガベージコレクションでカクつき、なかなか消費メモリが上がらない状態になったアキレスと亀

<前略>

48.053977966308594
48.06153106689453
48.06904602050781
48.076324462890625
48.08384323120117

<--- Last few GCs --->

<中略>

<--- JS stacktrace --->

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0x9fd5f0 node::Abort() [node]

<中略>

17: 0x1018255 v8::internal::Runtime_NewArray(int, unsigned long*, v8::internal::Isolate*) [node]
18: 0x13cc8f9  [node]
Aborted (core dumped)

変わったのはデータ構造の最深部が空配列か数値の1かだけ。
どちらも物理メモリがそもそも足りないので最終的に異常終了することには変わらない。

std::bad_alloc はC++のnewが失敗したときのエラーらしい

std::bad_alloc でGoogle検索すると、それがC++のnewから出されるものであることが分かった。

nodeはC++で書かれていて、newを行っている箇所があるということになる。
std::bad_alloc はC++のtry文で捕捉できるが、出る場所がnewという一般的なところであるだけに捕捉される個所とされない箇所の両方で発生する可能性があるのだろう。
それがエラー文がランダムで変わる原因と予想する。

straceで見ると、mmapがENOMEMというエラーを出していることが分かった

straceとはプロセスのシステムコールを出力できるコマンドである。

とりあえず次のようにnodeの前に置いて実行してみた。

$ strace node --max-old-space-size=$((1024 * 48)) -e 'Array.from({length: 10000}, v => { console.log(process.memoryUsage().rss / 1024 / 1024 / 1024); return Array.from({length: 1000000}, v => 1); });'

怒涛のログが出力されるが、その末尾は次のようになっている。

<前略>

mprotect(0x29960bbc0000, 262144, PROT_READ|PROT_WRITE) = 0
brk(0x1bf9a000)                         = 0x1bf9a000
mmap(0x2ebbbafc0000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x2ebbbafc0000
munmap(0x2ebbbb000000, 258048)          = 0
mprotect(0x2ebbbafc0000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x3ba59c200000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x3ba59c200000
munmap(0x3ba59c240000, 258048)          = 0
mprotect(0x3ba59c200000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x3f3009d40000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x3f3009d40000
munmap(0x3f3009d80000, 258048)          = 0
mprotect(0x3f3009d40000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x330b57380000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x330b57380000
munmap(0x330b573c0000, 258048)          = 0
mprotect(0x330b57380000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x207b9d440000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x207b9d440000
munmap(0x207b9d480000, 258048)          = 0
mprotect(0x207b9d440000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x300db2380000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x300db2380000
munmap(0x300db23c0000, 258048)          = 0
mprotect(0x300db2380000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x8e44e340000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x8e44e340000
munmap(0x8e44e380000, 258048)           = 0
mprotect(0x8e44e340000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x1a79a5c00000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x1a79a5c00000
munmap(0x1a79a5c40000, 258048)          = 0
mprotect(0x1a79a5c00000, 262144, PROT_READ|PROT_WRITE) = 0
mmap(0x9abb4d00000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap(0x9abb4d00000, 520192, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mprotect(0xafbb8382000, 86016, PROT_READ|PROT_WRITE) = 0
mprotect(0xafbb83c2000, 249856, PROT_READ|PROT_WRITE) = 0
mprotect(0xafbb8402000, 4096, PROT_READ|PROT_WRITE) = 0
mprotect(0xafbb8442000, 4096, PROT_READ|PROT_WRITE) = 0
mprotect(0xafbb8482000, 4096, PROT_READ|PROT_WRITE) = 0
mmap(NULL, 1040384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
brk(0x1c0a2000)                         = 0x1bf9a000
mmap(NULL, 1175552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap(0x7efbe4000000, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap(NULL, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap(NULL, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap(NULL, 1040384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
futex(0x7efbfebab1a0, FUTEX_WAKE_PRIVATE, 2147483647) = 0
write(2, "terminate called after throwing "..., 48terminate called after throwing an instance of ') = 48
write(2, "std::bad_alloc", 14std::bad_alloc)          = 14
write(2, "'\n", 2'
)                      = 2
write(2, "  what():  ", 11  what():  )             = 11
write(2, "std::bad_alloc", 14std::bad_alloc)          = 14
write(2, "\n", 1
)                       = 1
rt_sigprocmask(SIG_UNBLOCK, [ABRT], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], [], 8) = 0
getpid()                                = 15400
gettid()                                = 15400
tgkill(15400, 15400, SIGABRT)           = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGABRT {si_signo=SIGABRT, si_code=SI_TKILL, si_pid=15400, si_uid=1003} ---
+++ killed by SIGABRT (core dumped) +++
Aborted (core dumped)

ここで、以下の行によって、mmapという部分で ENOMEM というエラーが出ていることが分かる。
なぜメモリは余っているのに Cannot allocate memory なのだろうか。

mmap(NULL, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 ENOMEM (Cannot allocate memory)

ENOMEMが出る条件はメモリ不足およびマッピング数超過

mmapのmanに重要な情報が書いてあった。

ENOMEM
メモリーに空きがない、または処理中のプロセスのマッピング数が最大数を超過した。

マッピング数が最大数を超過した 場合、メモリが余っていてもENOMEMが出る。
出るエラー自体は共通のENOMEMなので、おそらくプログラムから見るとメモリ不足と区別できないだろう。
JavaScript heap out of memory というエラーメッセージになるのも納得できる。

sudo sysctl -w vm.max_map_count= で最大マッピング数を増加できる

次の場所からマッピング数の上限を増やす方法が得られた。

sysctl vm.max_map_count で現在の値が分かる。
sudo sysctl -w vm.max_map_count=65536 で値を設定できる。

変更はシステム全体に影響し、再起動で元に戻るらしい。

対策

vm.max_map_count の値を現在の10倍にしてみた。

$ sysctl vm.max_map_count
vm.max_map_count = 65530
$ sudo sysctl -w vm.max_map_count=655300
vm.max_map_count = 655300

結果

対策後、再び空配列の配列の配列を生成するコードを起動してみた。

$ node --max-old-space-size=$((1024 * 48)) -e 'Array.from({length: 10000}, v => { console.log(process.memoryUsage().rss / 1024 / 1024 / 1024); return Array.from({length: 1000000}, v => []); });'

その結果、期待通り与えた約48GBのメモリを最後まで使い切ってから異常終了するようになった。

<前略>

48.66949462890625
48.756752014160156
48.79401397705078
48.831199645996094
48.86867141723633

<--- Last few GCs --->

<中略>

<--- JS stacktrace --->

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0x9fd5f0 node::Abort() [node]

<中略>

11: 0x13cc8f9  [node]
Aborted (core dumped)

あとがき

結局node固有の問題ではなかった。
C++の問題ですらなかった。

当初は「16.8GB」で止まるという点からヒープサイズが別に制限されていたり、GCの関係でメモリの使い方がそういうものなのかと考えたり、C++で22億個のオブジェクトをnewすると32ビット整数が足りなくなってnewに失敗するのかもしれないとか考えていた。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS×WordPress】504 Gateway Time-outが出た

はじめに

AWS(AWS Linuxインスタンス)上でWordPressをたてて使用していたが、ある日突然504 Gateway Time-outが出た。
スクリーンショット 2020-07-31 8.31.56.png

504 Gateway Time-outの意味

Wikipediaによると、5XXはサーバエラーを指し、504は「ゲートウェイタイムアウト。ゲートウェイ・プロキシサーバはURIから推測されるサーバからの適切なレスポンスがなくタイムアウトした。」を意味する。サーバ側のエラーということでAWS上での問題っぽそう。

色々試してみる

試したこと①

AWS上に構築したWordPressで「504 gateway timeout」が頻発した時の対処法
ALBのターゲットグループをhttp&https→httpに変更したということだが、もともとhttpだった。

試したこと②

【504 Gateway Time-out】【Bad Request】でブログがつながらない!AWSのELBをチェック
DNS名・IPアドレス直打ちで入れたらアクセスできたというが、今回はできず。

試したこと③

【WordPress】504 Gateway Time-out が出た時の対処法

$ df -h

ストレージの容量を確認できるとのことだが、使用% を見ても、特に割合が高くなかった。

試したこと④

【AWS】【WordPress】「504 Gateway Timeout Error」の原因
Apacheが起動しているかを確認するとのこと。

$ sudo systemctl status httpd.service

この時Active: active (running)になっていたため、てっきりApache側での問題ではないと勘違いしていたが、これは間違いだった。

解決策

$ sudo systemctl restart httpd.service

今回はApahceがいつの間にか落ちてしまったらしく、上記コマンドでApacheを再び起動させ、504エラーが解消されWordPressの画面が表示された。
statusで確認してもたまにうまく反映できてないことがあるらしいので、psコマンドでちゃんとプロセスが起動しているかを確認する方が確実とのこと。

反省点

ネット記事を探して手当たり次第、試していたが下記記事のようにもう少し問題点を切り分けて行うべきだった。

インフラ苦手な人が知っておくといい、Webサイトにつながらない障害パターンと解決方法

ググった方が早い場合もあるが、インフラ力を高めるためにもログなどで原因を予測した上で確認したい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Zabbix] Web監視の応答時間は画像ファイルなども含む?

はじめに

ZabbixのWeb監視は応答時間(Response time)を取得しグラフ表示できますが、画像ファイルなど全ての要素を読み込んだ時間なのか、それともHTMLファイル単体なのか検証してみました。

image.png

環境

  • Zabbix 4.0.1
  • CentOS 7.5 + Apache 2.4

Webブラウザから

まずは普通にクライアントからWebブラウザでページ表示し、その時のWebサーバーのログを確認してみます。

access_log
172.16.xx.xx - - [14/Nov/2018:13:54:52 +0900] "GET / HTTP/1.1" 200 454 "-" "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0"
172.16.xx.xx - - [14/Nov/2018:13:54:52 +0900] "GET /logo1.png HTTP/1.1" 200 51614 "http://172.16.yy.yy/" "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0"
172.16.xx.xx - - [14/Nov/2018:13:54:52 +0900] "GET /logo2.png HTTP/1.1" 200 51614 "http://172.16.yy.yy/" "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0"
172.16.xx.xx - - [14/Nov/2018:13:54:52 +0900] "GET /logo5.png HTTP/1.1" 200 51614 "http://172.16.yy.yy/" "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0"
172.16.xx.xx - - [14/Nov/2018:13:54:52 +0900] "GET /logo3.png HTTP/1.1" 200 51614 "http://172.16.yy.yy/" "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0"
172.16.xx.xx - - [14/Nov/2018:13:54:52 +0900] "GET /logo4.png HTTP/1.1" 200 51614 "http://172.16.yy.yy/" "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0"
172.16.xx.xx - - [14/Nov/2018:13:54:52 +0900] "GET /logo7.png HTTP/1.1" 200 51614 "http://172.16.yy.yy/" "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0"
172.16.xx.xx - - [14/Nov/2018:13:54:52 +0900] "GET /logo8.png HTTP/1.1" 200 51614 "http://172.16.yy.yy/" "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0"
172.16.xx.xx - - [14/Nov/2018:13:54:52 +0900] "GET /favicon.ico HTTP/1.1" 404 209 "-" "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0"
172.16.xx.xx - - [14/Nov/2018:13:54:53 +0900] "GET /logo6.png HTTP/1.1" 200 51614 "http://172.16.yy.yy/" "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0"

PNG形式の画像ファイルなどもログに記録されていることが分かりました。

Zabbixから

次にZabbixのWeb監視を設定し、監視時のWebサーバーのログを確認。

access_log
172.16.zz.zz - - [14/Nov/2018:13:57:38 +0900] "GET / HTTP/1.1" 200 454 "-" "Zabbix"

ログに記録されたのは『GET /』だけでした。
つまりHTMLファイル単体のみ読み込んでいることが分かります。

さいごに

3秒ルールという言葉があるようにWebサイトの表示速度は重要ですが、ZabbixのWeb監視の応答時間はそのままユーザーからみた表示時間とならないので鵜呑みにしないようにしましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker コンテナ内でmatplotlibなどを使った画像表示されない場合の対処法

Docker コンテナ内でmatplotlib等で画像表示しようと思ったらこけたので記します
Ubuntu 16.04.6 LTS環境でやってます。

[問題点]
Docker run した後にコンテナに入り、matplotlibでplot.show()すると画像が何も表示されず、コマンドが終わる。
もしくは

docker _tkinter.TclError: couldn't connect to display
_tkinter.TclError: no display name and no $DISPLAY environment variable
_tkinter.TclError: couldn't connect to display :0.0
みたいなエラーが出てくる。

[対処法]
①まずターミナルに以下のコマンドをうつ

$sudo apt-get install x11-xserver-utils
$xhost +

②docker run する時には以下をオプションにつける
-v /tmp/.X11-unix:/tmp/.X11-unix
-e DISPLAY=unix$DISPLAY

③もしmatplotlibがコンテナに入ってなかったらコンテナ起動後のインテラクティブモードで

pip install matplotlib

④ もしtkinterがコンテナに入っていなかったらコンテナ起動後のインテラクティブモードで

apt-get update
apt-get python3-tk

なおpython3-tkをインストールする際、居住地域を答えるくだりがありました。

これでうまくコンテナ内でも画像表示されるようになりました。:family:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Linuxに ps の代替ツール procs をインストール(Mac・Windowsでも使える)

メモリやCPU使用率が見やすくなる
https://github.com/dalance/procs

cap.png

1. インストール

$ sudo rpm -i https://github.com/dalance/procs/releases/download/v0.10.3/procs-0.10.3-1.x86_64.rpm

2. 設定

リンク先の設定を反映(https://github.com/dalance/procs/blob/master/config/large.toml)

$ mkdir -p ~/.config/procs/
$ vi ~/.config/procs/config.toml

3. 使用例

$ procs
$ procs --sortd cpu
$ procs --sortd rss
$ procs --sortd rss nginx
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

systemdで/var/run配下にUNIX Socket用のディレクトリを切ったら落とし穴にハマった件と対処法

動作確認環境

  • CentOS7のサーバ

※Ubuntu20.04も同じ実装なので同じくハマるかと思います

落とし穴にハマった流れ

やりたいこと

  • hogeというアプリケーションのAPIサーバを建てたい
    • フロントは nginxでリクエストを受けてアプリケーションに渡す
    • hogesystemdでデーモン化する
    • hogenginxからのリクエストを UNIX Socketで受け取る
      • UNIX Socket/var/run/hoge/hoge.sockとする

やったこと

  • /var/run/hogeディレクトリを作成する
    • パーミッションは hoge:hogeとする
  • hogenginxの各種設定ファイルを配置してデーモンを起動する
    • この時点で動いたことは確認できた
  • サーバを再起動する
  • 再びAPIを叩くと nginxBad Gatewayのエラーを返した
    • 調べてみると/var/run/hoge/hoge.sockがディレクトリごと無くなっていた

原因

  • CentOS7では /var/run配下が tmpfsになっており、サーバを再起動すると /var/run配下のファイルが消えてしまう
# /var/run は /run へのシンボリックリンク
$ ls -ld /var/run
lrwxrwxrwx. 1 root root 6 Jul 25  2019 /var/run -> ../run

# /run は tmpfs をマウントしている
$ mount | grep /run
tmpfs on /run type tmpfs (rw,nosuid,nodev,mode=755)

対処法

systemdのUnitファイルに RuntimeDirectoryの設定を追加する

tmpfiles.dMANページによると

原文
System daemons frequently require private runtime directories below /run to store communication sockets and similar.
For these, it is better to use RuntimeDirectory= in their unit files (see systemd.exec(5) for details), if the flexibility provided by tmpfiles.d is not required.
The advantages are that the configuration required by the unit is centralized in one place, and that the lifetime of the directory is tied to the lifetime of the service itself.
Similarly, StateDirectory=, CacheDirectory=, LogsDirectory=, and ConfigurationDirectory= should be used to create directories under /var/lib/, /var/cache/, /var/log/, and /etc/.
tmpfiles.d should be used for files whose lifetime is independent of any service or requires more complicated configuration.
日本語訳
システムデーモンは、通信ソケットなどを格納するために、/runの下にプライベートランタイムディレクトリを必要とすることがよくあります。
これらについては、tmpfiles.dによって提供される柔軟性が必要ない場合は、ユニットファイルでRuntimeDirectory =を使用することをお勧めします(詳細については、systemd.exec(5)を参照)。
利点は、ユニットに必要な構成が1か所に集中化されていること、およびディレクトリの存続期間がサービス自体の存続期間に関連付けられていることです。
同様に、/var/lib/、/var/cache/、/var/log/、および/etc/の下にディレクトリを作成するには、StateDirectory=、CacheDirectory=、LogsDirectory=、およびConfigurationDirectory=を使用する必要があります。
tmpfiles.dは、存続期間がサービスに依存しないか、より複雑な構成が必要なファイルに使用する必要があります。

とあるので、RuntimeDirectoryの設定を追加します。

また、今回はパーミッションを 0755としたいので以下の様に RuntimeDirectoryModeも設定します。

UNITファイルに追加した行
RuntimeDirectory=hoge
RuntimeDirectoryMode=0755

systemd.execMANページによると /runが起点となるため、フルパスではなく /run配下に作りたいディレクトリ名を指定します

参考リンク

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GitとGitHubの関係性について

はじめに

Gitとは
ソースコードのバージョンを管理するツール
GitHubとは
Gitを利用した、開発者を支援するWebサービス

GitとGitHubですが、関連性は強ものの別物であり、Gitを利用しているからと言ってGitHubを使用しなければならないというわけではありません。
後述しますが、GitのホスティングサービスはGitHub以外にも存在しております。

Git誕生の歴史と利便性

そもそもGitはどのようにして誕生したのか。
その経緯から、Gitの利便性を確認していきましょう。

Gitの生みの親はLinuxOSを作ったリーナス・トーバルズ氏です。
氏は、当初Linuxカーネル開発で「BitKeeper」というバージョン管理システムを用いておりました。
ライセンスの問題により「BitKeeper」使用することができなくなったため、代替するサービスを探したもののフリーのバージョン管理システムでは、氏の掲げる高度な要求水準を満たせるものはありませんでした。
そのためトーバルズ氏自ら開発に着手し、そこで誕生したものが「Git」になります。
ではこれまでのバージョン管理ツールと比べてGitは何に優れているのでしょうか。

Gitが優れている点、それは分散型のリポジトリであることにあります。
従来リポジトリは全体で1つでしたが、Gitは「ローカル」と「リモート」の2つを持っています。
この分散型のメリットは、システム開発を複数で行った場合に、開発者それぞれがソースコードを書いていきリポジトリに追加していくことになるため、開発者が増えればその分だけリポジトリに不整合が生じる可能性が高まります。

そういったリポジトリの不整合を防ぐために、各開発者が自身のマシンでローカルリポジトリに変更を記録していき、全体の状況を見てリモートリポジトリに変更を記録していくことで利便性が高まりました。

分散型という今までにない形でバージョン管理を行うことで、従来の管理ツールに対して感じていた懸念は払拭され、現在に至るまで開発に用いられるようになりました。

GitHubとは

Gitという言葉がついているので、専用サービスのような印象を受けますが、こういったGitのホスティングサービスとしては他にもあり、GitHubを必ず利用しなければならないというわけではありません。

GitHubは、クラウド上でGitを用いたバージョン管理をすることができ、Gitを更に使いやすくする機能が備わっております。
その中でも「フォーク」「プルリクエスト」「マージ」の3つの機能があることで、利便性が向上し、多くのユーザーから支持を得ています。

  • フォーク:誰かのリポジトリを取得(コピー)して改変することができる。
  • プルリクエスト:自身が行ったソースコードの変更点について他のメンバーにレビュー依頼ができる機能
  • マージ:人のコードを自分のコードに導入すること。

「フォーク」してコードの改修を行い、オーナーへ「プルリクエスト」を行う。
「プルリクエスト」を受けたオーナーはそのコードを「マージ」する。


また、GitHubはソーシャルコーディングの場とも呼ばれており、他の人のソースコードを見ることができます。
これによって、開発への参加やフィードバックなどオープンソースでの開発がしやすくなったのもGitHubのメリットになります。

GitHub以外に使用されている代表的なGitのホスティングサービス

各種選ばれる違いとしては、ホスティングがWebサービス型とインストール型で違うことや、対応するVCSの違い、無料のプライベートリポジトリ、料金形態と様々です。

まとめ

Gitとは分散型にする事で、開発効率をあげる目的を持ったバージョン管理ツールであり、
GitHubはそれをオープンソースとしてチーム開発の利便性向上や、技術者のコミュニティ形成に役立っているサービスである。

参照記事

ITmedia エンタープライズ
https://www.itmedia.co.jp/enterprise/articles/0504/20/news075.html
FERROWS
https://job.fellow-s.co.jp/useful_info/feature_detail/Art-0488
Linix.com
https://www.linux.com/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flutter in Docker - Dockerコンテナ内にFlutter開発環境を構築して利用する方法

1. はじめに

Flutterの開発環境をDockerコンテナ (今回はUbuntu 20.04 LTS) に構築し、macOSやLinux OSのHost PCからあたかもHost PC上で開発しているのと同じような環境を構築する方法を紹介します。この仕組みを利用すれば、リモート (サーバー) に開発環境を構築し、それにローカルPCからアクセスしてFlutterアプリの開発が可能です。
スクリーンショット 2020-07-30 22.15.09.png

Flutter in Docker全体像

(補足) 今回はトライしていませんが、Dockerコンテナ内にAndroid版 (Aundroid Studioを利用したエミュレータ上での動作) も原理上は動作可能なはずです。

image.png

Dockerコンテナ化するメリット (例)

  • 開発環境を複製・削除し易く、同じ環境を第三者に配ることが可能
  • 環境をDockerコンテナ化しておくとCI/CD時に便利
  • OSに依存せず (Linux or macOS) 、Android/iOS以外の環境での動作確認が可能

ターゲット (Host PC) 環境

  • macOS
  • Linux OS (Flutter, Dockerが動作する64bit Linux環境であれば基本的にどのディストリービューションでもOK)

2. Dockerインストール

Docker自体のインストール方法については割愛しますが、基本的に公式サイトの手順通りに進めれば大丈夫です。

3. Dockerコンテナイメージの作成

Dockerコンテナイメージを作成する手順について説明します。Dockerコンテナ内のOS (rootfs) はUbuntu 20.04にしました。今回はDockerfileは利用せず、全て手動による手順での説明です。

Ubuntu 20.04 イメージ (rootfs) の取得

$ docker pull ubuntu:20.04

Dockerコンテナの起動

以下のコマンドでDockerコンテナを起動してそのコンテナ中に入ります。基本的にセキュリティなし (--privileged) で、Host PCのOSと同じ権限でアクセス出来るようにします。

なお、ここで指定したパラメータを後述のDocker-composeの設定ファイルでも利用するため、まずはこの設定のまま実行して下さい。

$ docker run -it --name flutter-docker ¥
     --privileged -h flutter -u root -w /root ¥
     --add-host=flutter:127.0.1.1 --net=host ubuntu:20.04

問題がなければプロンプトが#に変わり、Dockerコンテナ内の操作に切り替わっています。例えば、unameコマンドを打つとLinux OSになっていることが分かります。

# root@flutter:~# uname -a
Linux flutter 4.19.76-linuxkit #1 SMP Tue May 26 11:42:35 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

必要なツールのインストール

Dockerコンテナ内のUbuntu OSに必要なパッケージをインストールします。xserver-xorgのインストール時にはロケールやキーボードの情報を求められますが、適当で良いです (無難ならUS) 。

# apt update
# apt install git unzip clang xserver-xorg pkg-config libgtk-3-dev curl cmake ninja-build

Chromeブラウザのインストール (必要があれば)

# apt install wget gnupg
# sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
# wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
# apt update
# apt-get install google-chrome-stable

参考: https://qiita.com/pyon_kiti_jp/items/e6032eb6061a4774aece

Flutter SDKのインストール

Flutter SDKをgit cloneして、/opt以下に配置します。

# https://github.com/flutter/flutter.git
# mv flutter /opt/

コンテナイメージの保存

ここまででコンテナ内での作業は全て終了です。なので、コンテナから抜けます。

# exit

作成したコンテナをDockerイメージファイルとして保存します。ここで指定したパラメータを後述のDocker-composeの設定ファイルでも利用するため、まずはこの設定のまま実行して下さい。

$ docker commit flutter-docker flutter-docker/ubuntu:latest

4. Host PC側の設定 (macOSの場合のみ)

Dockerコンテナ内のGUIアプリケーションはX11プロトコルで動作するため、そのGUIアプリの画面をHost PC側のX11サーバーに転送する必要があります。macOSの場合、Host側のX11サーバーとしてXQuartzがありますので、それをインストールして利用します。

XQuartzのインストール

$ brew cask install xquartz

Host PCを再起動します。再起動後、DISPLAY環境変数が以下のように設定されていることを確認します。

$ echo $DISPLAY
/private/tmp/com.apple.launchd.NagCeWDLYl/org.macosforge.xquartz:0

DockerコンテナのX11クライアントからのアクセスを許可します。

$ xhost +$(hostname)
$ xhost + local:root

コマンドラインもしくは、LaunchPadからXQuartzを立ち上げておきます。
スクリーンショット 2020-07-31 0.41.31.png

5. Host PC側の設定 (Linux OSの場合のみ)

Linux OSの場合は大抵がX11デフォルトだと思いますので、以下の対応だけを行います。

$ xhost + local:root

6. VSCode環境構築

VSCodeのインストールとVSCodeからDockerコンテナを利用するためのExtensions等をインストールします。

VSCodeのインストール

以下を参考にしてインストールしてください。

VSCode Extensionsインストール

以下の3つのExtensionsをインストールします。

スクリーンショット 2020-07-30 23.44.14.png

スクリーンショット 2020-07-30 20.25.05.png

スクリーンショット 2020-07-30 20.25.30.png

7. Docker Composeファイルの作成

VSCodeからDockerコンテナ内のFlutter SDKを利用するために、Docker Composeと先ほどインストールしたVSCode ExtensionsのRemote Developmentの設定ファイルを作成します。作成したファイルは https://github.com/Kurun-pan/flutter-docker に置いているため、Host OS (Linux or macOS) に合わせて利用して下さい。

$ mkdir flutter_docker
$ cd flutter_docker

カレントディレクトリ (flutter_docker) 以下にdocker-compose.ymlファイルと.devcontainer/devcontainer.jsonファイルの2つを作成します。
スクリーンショット 2020-07-30 23.50.29.png

Host OSがmacOSの場合

DISPLAY環境変数にhost.docker.internal:0を設定するのがポイントのはずでしたが、上手く行かなかったため、{write your host name!!}の部分はmacOSのhostnameを指定してください。。誰か正しい方法を知っていれば教えて下さい。

https://github.com/Kurun-pan/flutter-docker/tree/master/macos

docker-compose.yml
version: "3"
services:
    flutter:
        image: flutter-docker/ubuntu:latest
        working_dir: /root/workspace
        command: sleep infinity
        environment:
            - HOME=/root
            - no_proxy=127.0.0.1,localhost
            #- DISPLAY="host.docker.internal:0"
            - DISPLAY={write your host name!!}:0
        volumes:
            - ~/.gitconfig:/home/root/.gitconfig
            - ./:/root/workspace
            - ~/.Xauthority:/root/.Xauthority
        network_mode: "host"
        extra_hosts:
            - flutter:127.0.1.1
.devcontainer/devcontainer.json
{
    "name": "Flutter docker",
    "dockerComposeFile": [
        "../docker-compose.yml",
    ],
    "service": "flutter",
    "remoteUser": "root",
    "remoteEnv": {
        "QT_X11_NO_MITSHM": "1",
        "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/flutter/bin",
    },
    "settings": {
        "terminal.integrated.shell.linux": null
    },
    "runArgs": [
        "--privileged",
        "-P",
    ],
    "extensions": ["dart-code.flutter"],
    "workspaceMount": "source=${localWorkspaceFolder}/workspace,target=/root/workspace,type=bind,consistency=delegated",
    "workspaceFolder": "/root/workspace"
}

Host OSがLinuxの場合

Linux OSの場合、OpenGLでDRM (DRI) をDockerコンテナ内のFlutterアプリが利用できるように、/dev/driをbind-mountします。

https://github.com/Kurun-pan/flutter-docker/tree/master/linux

docker-compose.yml
version: "3"
services:
    flutter:
        image: flutter-docker/ubuntu:latest
        working_dir: /root/workspace
        command: sleep infinity
        environment:
            - HOME=/root
            - no_proxy=127.0.0.1,localhost
        volumes:
            - ~/.gitconfig:/home/root/.gitconfig
            - ./:/root/workspace
            - /tmp/.X11-unix:/tmp/.X11-unix
        devices:
            - /dev/dri:/dev/dri
        network_mode: "host"
        extra_hosts:
            - flutter:127.0.1.1

Host OSのDISPLAY環境変数を引継ぎます。

.devcontainer/devcontainer.json
{
    "name": "Flutter docker",
    "dockerComposeFile": [
        "../docker-compose.yml",
    ],
    "service": "flutter",
    "remoteUser": "root",
    "remoteEnv": {
        "QT_X11_NO_MITSHM": "1",
        "DISPLAY": "${localEnv:DISPLAY}",
        "XAUTHORITY": "/tmp/.X11-unix",
        "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/flutter/bin",
    },
    "settings": {
        "terminal.integrated.shell.linux": null
    },
    "runArgs": [
        "--privileged",
        "-P",
    ],
    "extensions": ["dart-code.flutter"],
    "workspaceMount": "source=${localWorkspaceFolder}/workspace,target=/root/workspace,type=bind,consistency=delegated",
    "workspaceFolder": "/root/workspace"
}

8. Flutter in Dockerの実行

VSCode立ち上げ

docker-compose.ymlが存在するディレクトリでVSCodeを立ち上げます。

$ code .

VSCode Remote Containerのオープン

ウインドウ左下の><の部分をクリックもしくは、shift+alt+pでコマンドパレットを開き、Remote-containers: Open Folder in Container...を選択します。
スクリーンショット 2020-07-31 0.26.14.png

Openをクリックします。
スクリーンショット 2020-07-31 0.26.54.png

VSCodeウインドウ下部にターミナルが開きます。このターミナル操作対象は作成したDockerコンテナイメージ (flutter-docker/ubuntu:latest) です。
スクリーンショット 2020-07-31 0.29.10.png

Flutterサンプルアプリのプロジェクト作成

ここからVSCodeのターミナル (Dockerコンテナ) で以下のコマンドを実行し、Flutterサンプルアプリを実行します。

# flutter config --enable-linux-desktop
# flutter config --enable-web
# mkdir sample
# cd sample
# flutter create .

スクリーンショット 2020-07-31 0.31.24.png

Flutterサンプルアプリの実行

サンプルアプリ for Linux Desktop

# flutter run -d linux

スクリーンショット 2020-07-31 0.47.26.png

コマンドを実行する以外には、VSCodeでdartソースコードを開いて、GUI操作でもアプリ実行やブレークポイントを利用したデバッグ等が可能です。
スクリーンショット 2020-07-31 1.22.16.png

サンプルアプリ for Web

対応中なので少々お待ちを。。

# flutter run -d chrome
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ブートローダーのインストールに失敗したときにディスクを消し飛ばす前に確認すること

はじめに

最近Manjaroをインストールしようとしたのですが、

Boost.Python error in job "bootloader".
Command 'grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Manjaro --force' returned non-zero exit status 1.
Installing for x86_64-efi platform. Could not prepare Boot variable: No space left on device

というエラーが出て失敗しました。
"No space left"から/boot/efiのためのパーティションが小さすぎるのかと考えディスクを全消去しやり直しても改善せず、更にはkubuntuのインストールも試して同様にブートローダーのインストールで失敗しました。
一応kubuntuのインストールは成功したので今回の問題の対処法を共有します。

最初に確認すべきこと

※マニュアルにも書いてあることです
特に自分でパーティションを設定する場合の注意事項ですが、/boot/efiのためのパーティションはファイルシステムをFAT32に設定する必要があります。マウントポイントも/boot/efiを選択し、bootフラグを付与することも必要です。
パーティショニングを自動で行う場合などは自動的に上記の設定になると思うのでおそらくあまり気にする必要はありません。

それでも冒頭のエラーメッセージが出る場合

参考 ArchWiki Unified Extensible Firmware Interface

まずはターミナルを起動し

ls /sys/firmware/efi/efivars/dump-*

でdumpで始まるファイルの有無を調べます。自分の場合はこれが大量に存在し容量を圧迫していました。
"No space left"と言われていたのはこれのことだったようです。ディスクを全消去したところでこのフォルダの中身までは綺麗になりません。
(sudo) rmで削除することができるのでdumpで始まるファイルを削除します。
この後kubuntuのインストールを試したところ無事に完了しました。
(Manjaroは後日試します)

おわりに

自分が件のエラーメッセージを検索にかけたところ、ヒットしたのは主にパーティションのファイルシステムやフラグ付与の話でした。
dumpファイルの存在にもっと早く気づいていればわざわざ全削除までしなくても良かったなあと思っているので同様の症状に悩む方の参考になれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む