Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Linux && Docker 研修/Linux && Docker training

forrep
January 27, 2025

Linux && Docker 研修/Linux && Docker training

forrep

January 27, 2025
Tweet

More Decks by forrep

Other Decks in Programming

Transcript

  1. モジュールA システムとは数多くの部品を組み合わせたものです ※関数・変数・クラス・OS・API・要件・ハードウェア・・・ 各部品の精度が高くても、沢山組み合わせるとズレが蓄積して 全体の信頼性が低下します 信頼性98%の部品を10個、重ねると? 98% x 98% x

    … 98% ≒ 81.7% 1つなら問題ない(=98%) 精度でも、 重ねると信頼性は低下します はじめに 4 モジュールB モジュールC モジュールD モジュールE モジュールF モジュールG モジュールH モジュールI モジュールJ モジュールH
  2. はじめに 数多くの部品を組み合わせて 全体の完成度を高く保つには様々な考慮事項があります 1. 実現する要件を減らす ◦ 使う部品を減らす 2. 過度なマイクロサービス化を避ける ◦

    モノリス(密結合)はズレの蓄積を軽減する 3. 部品の完成度を高める ◦ 開発者の能力を高める 4. ... 本研修は Linux, Docker の理解度上昇 により開発者の能力を 高め、3. 部品の完成度を高める ことを目的とします 5
  3. これは分かりますか? Q: puts print System.out.println 等で画面表示できるのは何故?   画面に表示する関数を自力で実装できますか? → (カーネルの機能を使わずに)自力で実装はできません

    我々が普段開発しているプログラムコードから 画面を直接制御する方法はありません → それらを唯一実行できるのが カーネル です 9 Windows95 は自力でも 画面出力可能でした😮
  4. • カーネルは OS の中核機能、唯一デバイス操作の権限を持つ • 普通のプログラムはカーネルが用意した、 ユーザー空間という隔離された鳥かごの中で動く ◦ 鳥かごの中から外へは直接干渉できない ◦

    カーネルに依頼して鳥かごの外に干渉する ユーザー空間 ユーザー空間 ユーザー空間 カーネルとは 10 承知した ※システムコール ユーザー空間 カーネル カーネルさん 画面に「Hello World」って 表示してください
  5. カーネルかユーザーか? • Digest::SHA256.hexdigest("hoge") (Ruby) ◦ SHA256 のダイジェストを取得する → ユーザー •

    v = v + 1 (*) ◦ 変数に代入する → ユーザー • InputStream v = new FileInputStream("hoge.txt")) (Java) ◦ ファイルを開く → カーネル CPU+メモリの利用はユーザー空間、デバイスの利用はカーネル ※ただしメモリ領域の確保にはカーネルへの依頼(システムコール)が必要 • JavaScript で可能そうな処理はたいていユーザー空間 ◦ JavaScript はカーネルへの依頼が制限されている • 卓越した開発者なら自力実装できそうな処理はユーザー空間 ◦ どう頑張っても自力実装できない処理はカーネル 11
  6. • Linux とは Linuxカーネルが動作する OS ※意見の相違あり ◦ Linux カーネルだけでは何もできない •

    Ubuntu, RedHat 等はLinuxカーネルにアプリを同梱したもの ◦ Linux ディストリビューションという ◦ 各ディストリビューションの違いは付属アプリだけ ▪ 利用者が使うのは付属アプリだから体感はわりと違う Linux とは 13 bin etc home usr Linuxカーネル アプリ/ライブラリ Linux ディストリビューション ディストリビューションごとに、 アプリ/ライブラリが異なる カーネル本体は通常 /boot に配置され ブートローダがメモリに読み込む
  7. カーネルはWebシステムのバックエンドと類似 14 bin etc home usr Linuxカーネル アプリ/ライブラリ 情報 バックエンド

    DB フロントエンド Linux ディストリ ビューション Webシステム LinuxカーネルはWebシステムにおけるバックエンドに類似 ↑ 似ている関係性 ↓
  8. Linuxカーネルのシステムコールは観測できる • Linuxカーネルへの依頼(システムコール)は外から観測可能 • Python の REPL(Read-Eval-Print Loop)で試してみる • os.write()

    関数は write() システムコールを利用する 15 $ python3 >>> import os >>> os.write(1, b"Hello, world\n") Hello, world 13 >>> os.open("/etc/issue", os.O_RDONLY) 3 >>> os.read(3, 100) b'Ubuntu 22.04.3 LTS \\n \\l\n\n' >>> os.close(3) >>> $ strace -p <pid> --trace=read,write read(0, "i", 1) = 1 write(1, "i", 1) = 1 read(0, "m", 1) = 1 write(1, "m", 1) = 1 read(0, "p", 1) = 1 … write(1, "Hello, world\n", 13) = 13 write(1, "13\n", 3) = 3 write(1, ">>> ", 4) = 4 … write(1, "b'Ubuntu 22.04.3 LTS \\\\n \\\\l\\n\\n"..., 34) = 34 write(1, ">>> ", 4) = 4 …
  9. Linuxカーネルのシステムコールは観測できる • REPL で実行した処理がシステムコールとして観測できる • >>> プロンプトも REPL がシステムコールで表示している •

    システムコールからプログラムの動作をだいたい把握できる 16 $ python3 >>> import os >>> os.write(1, b"Hello, world\n") Hello, world 13 >>> os.open("/etc/issue", os.O_RDONLY) 3 >>> os.read(3, 100) b'Ubuntu 22.04.3 LTS \\n \\l\n\n' >>> os.close(3) >>> $ strace -p <pid> --trace=read,write read(0, "i", 1) = 1 write(1, "i", 1) = 1 read(0, "m", 1) = 1 write(1, "m", 1) = 1 read(0, "p", 1) = 1 … write(1, "Hello, world\n", 13) = 13 write(1, "13\n", 3) = 3 write(1, ">>> ", 4) = 4 … write(1, "b'Ubuntu 22.04.3 LTS \\\\n \\\\l\\n\\n"..., 34) = 34 write(1, ">>> ", 4) = 4 …
  10. /proc(procfs) カーネルは様々な情報を保持し、一部は /proc から取得できます ユーザー空間から、システムコールや /proc で情報を取得します 17 $ sleep

    infinity 3>/tmp/logging $ ls -l /proc/<pid>/fd 0 -> /dev/pts/0 1 -> /dev/pts/0 2 -> /dev/pts/0 3 -> /tmp/logging Linuxカーネル proc システムコール 情報 情報 情報 ユーザー空間の プログラム /proc/<pid>/fd を表示すると 各プロセスが開いているファイルが分かる
  11. ファイルの読み書きに利用するファイルディスクリプタと システムコールの関連を見てみます ファイルディスクリプタは「カーネル」と「ユーザー空間のプロ グラム」双方で管理する数値で、システムコールに入出力先を 指示するために利用します ファイルディスクリプタ 18 0 標準入力 1

    標準出力 2 標準エラー出力 3 ログファイル Linuxカーネル 0 キーボード(1) 1 画面(1) 2 画面(1) 3 /tmp/logging システムコール ファイルディスクリプタ 3 番 に 「Hello, World!」って書き込んで プロセスA 0 キーボード(2) 1 画面(2) 2 画面(2) プロセスB プロセスA (ユーザ空間)
  12. 2 ファイルディスクリプタ 3 番 に 「Hello, World!」って書き込んで カーネルはプロセスごとにファイルディスクリプタを管理して 送信元プロセスと番号から入出力先を決定します 標準入力は

    0、標準出力は 1、標準エラー出力は 2 の3種類は、 標準ストリームとして定義されています ファイルディスクリプタ 19 Linuxカーネル 0 キーボード(1) 1 画面(1) 画面(1) 3 /tmp/logging プロセスA 0 キーボード(2) 1 画面(2) 2 画面(2) プロセスB 0 標準入力 1 標準出力 2 標準エラー出力 3 ログファイル プロセスA (ユーザ空間) システムコール
  13. ファイルディスクリプタを利用した読み書きを実際に見てみます bash の $$ 特殊変数でプロセス番号を出力 strace でシステムコールの監視を開始します -f フラグで対象プロセスの子プロセスも監視対象となります シェルから起動するプログラムを監視するのに便利です

    ファイルディスクリプタのオープン・書き込み・クローズを監視 するため、--trace=openat,write,close とします ファイルディスクリプタ 20 $ echo $$ $ strace -f -p <pid> --trace=openat,write,close
  14. ファイルディスクリプタ python プロセスの動作を strace で監視します openat write close のコールと /proc

    の変化を確認できます 21 $ python3 >>> import os >>> os.open('/tmp/logging', os.O_RDWR + os.O_CREAT) 3 >>> os.write(3, b"Hello, World!\n") 15 >>> os.close(3) $ ls -l /proc/<pid>/fd 0 -> /dev/pts/3 1 -> /dev/pts/3 2 -> /dev/pts/3 3 -> /tmp/logging $ strace -f -p <pid> ––trace=openat,write,close ... openat(AT_FDCWD, "logging.log", O_RDWR|O_CREAT|O_CLOEXEC, 0777) = 3 ... write(3, "Hello, World!\n", 15) = 15 write(1, "15\n", 3) = 3 ... close(3) = 0 ...
  15. os.open() で openat システムコールが実行され、 ファイルディスクリプタ 3 が割り当てられています $ python3 >>>

    import os >>> os.open('/tmp/logging', os.O_RDWR + os.O_CREAT) 3 >>> os.write(3, b"Hello, World!\n") 15 >>> os.close(3) $ ls -l /proc/<pid>/fd 0 -> /dev/pts/3 1 -> /dev/pts/3 2 -> /dev/pts/3 3 -> /tmp/logging $ strace -f -p <pid> ––trace=openat,write,close ... openat(AT_FDCWD, "logging.log", O_RDWR|O_CREAT|O_CLOEXEC, 0777) = 3 ... write(3, "Hello, World!\n", 15) = 15 write(1, "15\n", 3) = 3 ... close(3) = 0 ... ファイルディスクリプタ 22
  16. シェルのリダイレクト シェルのリダイレクトとは、ファイルディスクリプタの操作です >/dev/null へリダイレクトして /proc で確認します >&- で閉じると標準出力=1 がなくなります シェルのリダイレクトは、これから実行するコマンドの

    ファイルディスクリプタを実行前に操作できます 23 $ python3 >/dev/null $ python3 >&- $ ls -al /proc/<pid>/fd 0 -> /dev/pts/3 1 -> /dev/null 2 -> /dev/pts/3 $ ls -al /proc/<pid>/fd 0 -> /dev/pts/3 2 -> /dev/pts/3 標準ストリームが不完全 なので、多くのアプリは 意図しない動作をします
  17. シェルのリダイレクト リダイレクトで開いたファイルディスクリプタは 実行されたプロセスから利用できます 複数のリダイレクトを利用することもできます 24 $ python3 10>logging >>> import

    os >>> os.write(10, b”Hello!\n”) $ exec 10>logging $ python3 >>> import os >>> os.write(10, b”Hello!\n”) $ python3 10>logging10 11>logging11 12>logging12 >>> import os >>> os.write(10, b”Hello 10!\n”) >>> os.write(11, b”Hello 11!\n”) >>> os.write(12, b”Hello 12!\n”) >>> ファイルディスクリプタ10で logging を開いた状態の python プロセスを実行 bash プロセスのファイルディスクリ プタ10で logging を開いてから python プロセスを実行 ※python 終了後も10 を使える
  18. プロセスの起動には clone execve システムコールが利用されます ※現代は fork/exec の代わりに、より高機能な clone/execve が利用されています bash

    で /bin/echo Hello! を実行する様子を見てみます 2. プロセスの上書き /bin/echo 1. プロセスの複製 プロセス起動のシステムコール 25 Linuxカーネル clone システムコール bash bash プロセスの複製 複製 Linuxカーネル execve システムコール bash プロセスの上書き clone システムコールで 親プロセスを複製して 新しいプロセスを生成します 複製された子プロセスは execve システムコールで /bin/echo コマンドの実行開始
  19. プロセス起動のシステムコール /bin/echo Hello! の実行を strace で観察してみます clone で親プロセス(bash)が複製され、execve で /bin/echo

    に 置き換わり、write(1, "Hello!\n", 7) する様子を確認できます 26 $ strace -f -p <pid> --trace=clone,execve,write $ /bin/echo Hello! Hello! $ strace -f -p <pid> --trace=clone,execve,write ... clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fa751d3da10) = 23710 strace: Process 23710 attached [pid 23710] execve("/bin/echo", ["/bin/echo", "Hello!"], 0x5613b44b11a0 /* 40 vars */) = 0 [pid 23710] write(1, "Hello!\n", 7) = 7 [pid 23710] +++ exited with 0 +++
  20. echo “Hello!” と /bin/echo “Hello!” を比較します echo “Hello!” は clone

    execve を利用しません なぜなら echo は bash のビルトインコマンドなので、 プロセスを作らず bash プロセス自体が実行するためです write(1, "Hello!\n", 7) = 7 プロセス起動のシステムコール 27 $ echo "Hello!" clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fa751d3da10) = 23710 strace: Process 23710 attached [pid 23710] execve("/bin/echo", ["/bin/echo", "Hello!"], 0x5613b44b11a0 /* 40 vars */) = 0 [pid 23710] write(1, "Hello!\n", 7) = 7 [pid 23710] +++ exited with 0 +++ $ /bin/echo "Hello!" strace出力 strace出力
  21. プロセス起動時にリダイレクトした状況を確認してみます 子プロセスは親から引き継いだファイルディスクリプタとリダイ レクトで開かれたファイルディスクリプタを持ちます $ bash 10>logging10 $ bash 11>logging11 $

    $ ps auxwwf |less 1434 \_ -bash 10638 \_ bash 10782 \_ bash プロセス起動のシステムコール 29 ls -l /proc/10782/fd 0 -> /dev/pts/3 1 -> /dev/pts/3 10 -> /home/jun/logging10 11 -> /home/jun/logging11 2 -> /dev/pts/3 ls -l /proc/1434/fd 0 -> /dev/pts/3 1 -> /dev/pts/3 2 -> /dev/pts/3 ls -l /proc/10638/fd 0 -> /dev/pts/3 1 -> /dev/pts/3 10 -> /home/jun/logging10 2 -> /dev/pts/3
  22. 標準出力/標準エラー出力をリダイレクト 標準出力=1 と 標準エラー出力=2 を /dev/null へリダイレクトし て、python を実行してみます 30

    $ python3 >/dev/null 2>/dev/null $ ls -l /proc/<pid>/fd 0 -> /dev/pts/3 1 -> /dev/null 2 -> /dev/null ⚠注意⚠ 標準エラー出力を /dev/null へ捨ててはダメ! プログラムには2種類(標準&標準エラー)の出力が与えら れます。用途を区別することで致命的な問題が見過ごされ ないようにする工夫です
  23. 標準出力/標準エラー出力をリダイレクト 当然ですが、なにも表示されません システムコールでは write(2, “Pyth... が実行されています しかし ファイルディスクリプタ 2 の接続先は

    /dev/null なので 捨てられます この python プロセスからは画面出力が一切できません 31 write(2, "Python 3.10.12 (main, Nov 20 202"..., 67) = 67 write(2, "Type \"help\", \"copyright\", \"credi"..., 71) = 71 write(2, ">>> ", 4) = 4 $ python3 >/dev/null 2>/dev/null $ ls -l /proc/<pid>/fd 0 -> /dev/pts/3 1 -> /dev/null 2 -> /dev/null
  24. tty/pts を利用した読み書き 先ほどの Python プロセスは画面出力ができない状態でしたが、 標準入力=0 は pts に繋がって います

    そこで、標準入力=0 に書き込んでみます 標準入力に書き込むと 画面表示されました しかしプロンプト >>> は依然として表示されません そこで、次は標準エラー出力を tty/pts に再接続してみます 33 import os os.write(0, b”Hello, World\n”) Hello, World $ python3 >/dev/null 2>/dev/null $ ls -l /proc/<pid>/fd 0 -> /dev/pts/3 1 -> /dev/null 2 -> /dev/null どこに繋がってる tty/pts でも同じもの
  25. ファイルディスクリプタを複製 ファイルディスクリプタを複製する dup2 システムコールで 「標準入力」→「標準エラー出力」へと複製します os.dup2(0, 2) を実行するとプロンプト >>> が表示されました

    /proc でファイルディスクリプタを確認すると 2 が tty/pts に接続されている様子を確認できます 34 os.dup2(0, 2) >>> $ python3 >/dev/null 2>/dev/null $ ls -l /proc/<pid>/fd 0 -> /dev/pts/3 1 -> /dev/null 2 -> /dev/null $ ls -l /proc/<pid>/fd 0 -> /dev/pts/3 1 -> /dev/null 2 -> /dev/pts/3 複製 複製 プロンプトの 表示を確認 複製
  26. 異なる tty/pts に接続 tty/pts は(権限の範囲内で)自由に書き込めるため、 他端末が利用する tty/pts (画面)に書き込むこともできます 別の tty/pts

    に繋いでキーボード入力を乗っ取ることもできます 端末B を sleep 180 で3分間入力を受け取らない状態とし、 端末A で端末B の tty/pts を開いてから dup2 で標準入力 0 に 複製します 35 $ tty /dev/pts/3 $ timeout 180 python3 >>> import os >>> os.open("/dev/pts/5", os.O_RDONLY) 3 >>> os.dup2(3, 0) $ tty /dev/pts/5 $ sleep 180 $ echo -e "\ntasuke..te..." >/dev/pts/<num> $ tasuke..te... 端末A 端末B
  27. 異なる tty/pts に接続 端末B で入力した内容は端末A に出力されます 端末A の tty/pts は未接続のため入力しても無反応です

    しかし端末A で Ctrl-C を入力すると KeyboardInterrupt と 画面表示されます これは何故でしょう? 36 >>> os.dup2(3, 0) print("Hello") Hello ^C KeyboardInterrupt $ tty /dev/pts/3 $ timeout 180 python3 >>> import os >>> os.open("/dev/pts/5", os.O_RDONLY) 3 >>> os.dup2(3, 0) print("Hello") Hello $ tty /dev/pts/5 $ sleep 180 入力しても表示されない 端末B への入力内容と 実行結果が表示される 端末A 端末B 端末A
  28. ログインした各端末には tty/pts が割り当てられます /dev/pts/3 /dev/pts/5 端末と tty/pts とプロセスの関係 37 $

    キーボード入力 画面表示 プロセスA 0: 標準入力 1: 標準出力 ※注 簡略化のために標準エラー出力は省略します キーボード入力 画面出力 $ キーボード入力 画面表示 プロセスB 0: 標準入力 1: 標準出力 キーボード入力 画面出力 端末A 端末B
  29. 先ほど入れ替えた tty/pts は以下の状態です 端末A の入力は tty/pts まで届くものの、誰も受け取りません /dev/pts/3 /dev/pts/5 端末と

    tty/pts とプロセスの関係 38 $ キーボード入力 画面表示 プロセスA 0: 標準入力 1: 標準出力 ※注 簡略化のために標準エラー出力は省略します キーボード入力 画面出力 $ キーボード入力 画面表示 プロセスB 0: 標準入力 1: 標準出力 画面出力 キ ー ボ ー ド 入 力 誰も受け取らない 端末A 端末B
  30. tty/pts が Ctrl-C を受け取ると紐づいたプロセスに SIGINT を 送信します。標準入力が未接続でもプロセスに伝わります /dev/pts/3 /dev/pts/5 端末と

    tty/pts とプロセスの関係 39 $ キーボード入力 画面表示 プロセスA 0: 標準入力 1: 標準出力 ※注 簡略化のために標準エラー出力は省略します Ctrl-C 入力 画面出力 $ キーボード入力 画面表示 プロセスB 0: 標準入力 1: 標準出力 画面出力 SIGINT 送信 端末A 端末B キ ー ボ ー ド 入 力
  31. TCP通信もシステムコールでカーネルが処理します netcat による 3000番ポートの待ち受けを strace してみます socket bind listen accept4

    とシステムコールを実行して、 netcat は接続待ち状態になりました bind の引数で待ち受けるIPアドレスとポートを指定する様子も 観測できます $ strace -f -p <pid> ––trace=socket,bind,listen,accept4,poll,read,write ... socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3 bind(3, {sa_family=AF_INET, sin_port=htons(3000), sin_addr=inet_addr("0.0.0.0")}, 16) = 0 listen(3, 1) = 0 accept4(3, TCP通信とシステムコール 40 $ netcat -k -l 3000 $ strace -f -p <pid> ––trace=socket,bind,listen,accept4,poll,read,write
  32. TCP通信とシステムコール 次は netcat コマンドで localhost:3000 に接続します 接続待ちでブロックしていた accept4 が実行され 次は

    poll で入出力待ちのブロックをします poll は指定されたファイルディスクリプタが入出力可能な状態に なるまで待機(ブロック)します 41 $ strace -f -p <pid> ––trace=socket,bind,listen,accept4,poll,read,write ... accept4(3, {sa_family=AF_INET, sin_port=htons(55820), sin_addr=inet_addr("127.0.0.1")}, [128 => 16], SOCK_NONBLOCK) = 4 poll([{fd=0, events=POLLIN}, {fd=4, events=0}, {fd=4, events=POLLIN}, {fd=1, events=0}], 4, -1 $ netcat localhost 3000
  33. Hello, World! と送信してみます poll が fd=4, revents=POLLIN を返却し、 ファイルディスクリプタ 4

    から read する様子を観測できます Hello, World! TCP通信とシステムコール 42 $ strace -f -p <pid> ––trace=socket,bind,listen,accept4,poll,read,write ... poll([{fd=0, events=POLLIN}, {fd=4, events=0}, {fd=4, events=POLLIN}, {fd=1, events=0}], 4, -1) = 1 ([{fd=4, revents=POLLIN}]) read(4, "Hello, World!\n", 16384) = 14 poll([{fd=0, events=POLLIN}, {fd=4, events=0}, {fd=4, events=POLLIN}, {fd=1, events=POLLOUT}], 4, -1) = 1 ([{fd=1, revents=POLLOUT}]) write(1, "Hello, World!\n", 14) = 14 poll([{fd=0, events=POLLIN}, {fd=4, events=0}, {fd=4, events=POLLIN}, {fd=1, events=0}], 4, -1 $ netcat localhost 3000 Hello, World! $ netcat -k -l 3000 Hello, World!
  34. カーネルとアプリの関係 44 bin etc home usr Linuxカーネル アプリ/ライブラリ 情報 バックエンド

    DB フロントエンド Linux ディストリ ビューション Webシステム LinuxカーネルはWebシステムにおけるバックエンドに類似 ↑ 似ている関係性 ↓
  35. コンテナの基本的な考え方 46 bin etc home usr Linux環境 A bin etc

    home usr Linux環境 B bin etc home usr Linux環境 C カーネルを共有しつつ、ファイルシステムを切り替えれば 仮想的に複数の環境を扱えそう、という発想です ファイルシステム (アプリ/ライブラリ) Linuxカーネル (共有)
  36. コンテナの仕組み • Linuxカーネル(以降カーネルと記載)の namespace を利用 • namespace はプロセスを隔離空間で実行するカーネルの機能 • Docker

    は namespace 機能の使いやすい UI というだけ 例えば、Mount namespace では特定のプロセスにだけ 異なるファイルシステムを見せることができます その Mount namespace を実際に試してみましょう ※老人会の方にはリッチな chroot と言えば分かりやすいはず 47
  37. Mount namespace を試す unshare --mount で Mount namespace の bash

    を開始します namespace 内で独自の hosts ファイルを mount --bind しても、 他のプロセスには影響しません その状態で google-chrome を実行します ※ namespace は子プロセスにも引き継がれます 48 $ sudo unshare --mount bash # echo '127.0.0.1 example.com' > /tmp/hosts # mount ––bind /tmp/hosts /etc/hosts # su - jun $ export DISPLAY=:0 $ google-chrome
  38. Mount namespace を試す 49 $ echo "Hello, World!" > index.html

    $ docker run --rm -p 3000:80 -v $(pwd):/usr/share/nginx/html nginx:latest Webサーバを localhost:3000 で起動(確認用) http://example.com:3000/ でローカルホストに繋がることを確認 $ cat /etc/hosts # This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /etc/wsl.conf: # [network] # generateHosts = false 127.0.0.1 localhost 127.0.1.1 OTOWA.raccoon.internal OTOWA ... 一方で、他プロセスから /etc/hosts を出力すると元のままです Chrome の参照する /etc/hosts は 127.0.0.1 example.com となる
  39. Dockerでコンテナの仕組みを深掘り コンテナの実態は普通のプロセスなので ubuntu:latest alpine:latest など、異なるコンテナで カーネルのバージョンを取得(uname -srv)しても同じ結果が 返ってきます namespace には隔離できる要素と、できない要素があり、

    カーネルそのものは共有されて隔離できません 51 $ uname -srv Linux 5.15.167.4-microsoft-standard-WSL2 #1 SMP Tue Nov 5 00:21:55 UTC 2024 $ docker run --rm ubuntu:latest uname -srv Linux 5.15.167.4-microsoft-standard-WSL2 #1 SMP Tue Nov 5 00:21:55 UTC 2024 $ docker run --rm alpine:latest uname -srv Linux 5.15.167.4-microsoft-standard-WSL2 #1 SMP Tue Nov 5 00:21:55 UTC 2024
  40. WSL2 の仕組み Windows の WSL2 はコンテナ技術を利用しています Hyper-V※1 上で「Windows」と「軽量ユーティリティ仮想マシ ン」という Linuxカーネル※2

    の動作する OS が並列で起動します ※1 Hyper-V は Microsoft の提供する仮想環境 ※2 本来の Linuxカーネルに Microsoft が WSL2 用のパッチを適用した独自カーネル WSL2 無効の場合はハードウェア上で直接 Windows が起動しま す。WSL2 は想像以上に大げさな環境の変更が行われています 52 ハードウェア Windows ハードウェア Windows 軽量ユーティリ ティ仮想マシン Hyper-V WSL 無効 WSL 有効
  41. WSL2 のカーネル WSL2 環境のディストリビューションの実体は、 軽量ユーティリティ仮想マシン上で動作するコンテナです 複数ディストリビューションで、uname -srv を実行してみます ディストリビューションに本来付属するカーネルではなく、 Microsoft

    が WSL2 用のパッチを当てた独自の Linuxカーネル※ が利用されています ※ https://github.com/microsoft/WSL2-Linux-Kernel 53 $ uname -srv Linux 5.15.167.4-microsoft-standard-WSL2 #1 SMP Tue Nov 5 00:21:55 UTC 2024 $ uname -srv Linux 5.15.167.4-microsoft-standard-WSL2 #1 SMP Tue Nov 5 00:21:55 UTC 2024 WSL環境1 WSL環境2
  42. 下図の • は OS で、• は仮想環境の基板です WSL2 有効の場合は •Hyper-V 上で動く

    Windows 上で •VirtualBox がネストしています 仮想環境には CPU の仮想化支援機能が必須ですが、 ネストした仮想環境では良いパフォーマンスを発揮できません ホスト型仮想化(VirtualBoxなど)と相性が悪い 54 ハードウェア Windows ハードウェア Windows 軽量ユーティリ ティ仮想マシン Hyper-V WSL 無効 WSL 有効 アプリ VirtualBox アプリ Virtual Box ゲス ト ゲス ト ゲスト ゲスト WSL2 コンテナ WSL2 コンテナ
  43. • LinuxカーネルはWebシステムにおけるバックエンドと類似 • コンテナはカーネルを共有してファイルシステムを切り替え • Linuxカーネルの namespace 機能が利用される • docker

    は namespace 機能を利用する便利ツール • WSL2 とは Hyper-V 上の軽量ユーティリティ仮想マシン • WSL2 のディストリビューションはコンテナで動作する • WSL2 と VirtualBox は仮想環境のネストとなり相性が悪い Linuxカーネルとコンテナのまとめ 55