【M1・M2チップMac】Docker環境でPlaywrightを走らせるときの注意点

# はじめに
とある案件でE2Eテストを導入することになり、その方法としてPlaywrightをdocker環境内にインストールし動かそうとしたところハマってしまいました。
どうやらApple Silicon製Mac特有のエラーらしいので備忘録として本記事を書いてます。

## 使用環境
- M2 MacbookPro (Ventura 13.4)
- Docker for Desktoop 4.22.0 
- Playwright 1.38
- node 18.17


# エラー
dockerコンテナを立ち上げ後、Playwright起動コマンドを実行するとこのようなエラーが発生した

```
1) example.spec.js:4:1 › has title ───────────────────────────────────────────────────────────────

    Error: browserType.launch: Browser closed.
    ==================== Browser output: ====================
    <launching> /root/.cache/ms-playwright/chromium-1080/chrome-linux/chrome --disable-field-trial-config --disable-background-networking --enable-features=NetworkService,NetworkServiceInProcess --disable-background-timer-throttling --disable-backgrounding-occluded-windows --disable-back-forward-cache --disable-breakpad --disable-client-side-phishing-detection --disable-component-extensions-with-background-pages --disable-component-update --no-default-browser-check --disable-default-apps --disable-dev-shm-usage --disable-extensions --disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate --allow-pre-commit-input --disable-hang-monitor --disable-ipc-flooding-protection --disable-popup-blocking --disable-prompt-on-repost --disable-renderer-backgrounding --force-color-profile=srgb --metrics-recording-only --no-first-run --enable-automation --password-store=basic --use-mock-keychain --no-service-autorun --export-tagged-pdf --headless --hide-scrollbars --mute-audio --blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4 --no-sandbox --user-data-dir=/tmp/playwright_chromiumdev_profile-R1JT9Z --remote-debugging-pipe --no-startup-window
    <launched> pid=14783
    [pid=14783][err] [0928/143420.753020:ERROR:stack_trace_posix.cc(833)] Failed to parse the contents of /proc/self/maps
    [pid=14783][err] [0928/143421.537417:ERROR:stack_trace_posix.cc(833)] Failed to parse the contents of /proc/self/maps
    [pid=14783][err] [0928/143421.537783:ERROR:stack_trace_posix.cc(833)] Failed to parse the contents of /proc/self/maps
    [pid=14783][err] [0928/143421.578078:FATAL:zygote_main_linux.cc(145)] Check failed: sandbox::ThreadHelpers::IsSingleThreaded(). 
    [pid=14783][err] #0 0x00400812a4c2 base::debug::CollectStackTrace()
    [pid=14783][err] #1 0x004008116ee3 base::debug::StackTrace::StackTrace()
    [pid=14783][err] #2 0x00400806f67d logging::LogMessage::~LogMessage()
    [pid=14783][err] #3 0x0040080700fe logging::LogMessage::~LogMessage()
    [pid=14783][err] #4 0x004008058297 logging::CheckError::~CheckError()
    [pid=14783][err] #5 0x0040074c5de2 content::ZygoteMain()
    [pid=14783][err] #6 0x0040074beae3 content::RunZygote()
    [pid=14783][err] #7 0x0040074bf8d8 content::RunOtherNamedProcessTypeMain()
    [pid=14783][err] #8 0x0040074c0c72 content::ContentMainRunnerImpl::Run()
    [pid=14783][err] #9 0x0040074be358 content::RunContentProcess()
    [pid=14783][err] #10 0x0040074be57d content::ContentMain()
    [pid=14783][err] #11 0x004007b51404 headless::HeadlessShellMain()
    [pid=14783][err] #12 0x004003b27261 ChromeMain
    [pid=14783][err] #13 0x004011992d0a __libc_start_main
    [pid=14783][err] #14 0x004003b2702a _start
    [pid=14783][err] 
    [pid=14783][err] qemu: uncaught target signal 5 (Trace/breakpoint trap) - core dumped
    [pid=14783][err] [0928/143421.578081:FATAL:zygote_main_linux.cc(145)] Check failed: sandbox::ThreadHelpers::IsSingleThreaded(). 
    [pid=14783][err] #0 0x00400812a4c2 base::debug::CollectStackTrace()
    [pid=14783][err] #1 0x004008116ee3 base::debug::StackTrace::StackTrace()
    [pid=14783][err] #2 0x00400806f67d logging::LogMessage::~LogMessage()
    [pid=14783][err] #3 0x0040080700fe logging::LogMessage::~LogMessage()
    [pid=14783][err] #4 0x004008058297 logging::CheckError::~CheckError()
    [pid=14783][err] #5 0x0040074c5de2 content::ZygoteMain()
    [pid=14783][err] #6 0x0040074beae3 content::RunZygote()
    [pid=14783][err] #7 0x0040074bf8d8 content::RunOtherNamedProcessTypeMain()
    [pid=14783][err] #8 0x0040074c0c72 content::ContentMainRunnerImpl::Run()
    [pid=14783][err] #9 0x0040074be358 content::RunContentProcess()
    [pid=14783][err] #10 0x0040074be57d content::ContentMain()
    [pid=14783][err] #11 0x004007b51404 headless::HeadlessShellMain()
    [pid=14783][err] #12 0x004003b27261 ChromeMain
    [pid=14783][err] #13 0x004011992d0a __libc_start_main
    [pid=14783][err] #14 0x004003b2702a _start
    [pid=14783][err] 
    [pid=14783][err] qemu: uncaught target signal 5 (Trace/breakpoint trap) - core dumped
    [pid=14783][err] [0928/143422.257059:ERROR:bus.cc(406)] Failed to connect to the bus: Failed to connect to socket /run/dbus/system_bus_socket: No such file or directory
    [pid=14783][err] Received signal 11 SEGV_MAPERR 746964652f3c11
    [pid=14783][err] #0 0x00400812a4c2 base::debug::CollectStackTrace()
    [pid=14783][err] #1 0x004008116ee3 base::debug::StackTrace::StackTrace()
    [pid=14783][err] #2 0x004008129ee1 base::debug::(anonymous namespace)::StackDumpSignalHandler()
    [pid=14783][err] #3 0x004010a65140 <unknown>
    [pid=14783][err] #4 0x004008a99cf8 FcCharSetSerializeAlloc
    [pid=14783][err] #5 0x004008aa8a1c FcValueListSerializeAlloc
    [pid=14783][err] #6 0x004008aa8971 FcPatternSerializeAlloc
    [pid=14783][err] #7 0x004008aa0730 FcFontSetSerializeAlloc
    [pid=14783][err] #8 0x004008a96950 FcDirCacheBuild
    [pid=14783][err] #9 0x004008a9c713 FcDirCacheScan
    [pid=14783][err] #10 0x004008a9c7b0 IA__FcDirCacheRead
    [pid=14783][err] #11 0x004008a91b07 FcConfigAddDirList
    [pid=14783][err] #12 0x004008a91a52 IA__FcConfigBuildFonts
    [pid=14783][err] #13 0x004008aa0ef1 IA__FcInitLoadConfigAndFonts
    [pid=14783][err] #14 0x004008a90f96 FcConfigInit
    [pid=14783][err] #15 0x004008b57b04 base::NoDestructor<>::NoDestructor<>()
    [pid=14783][err] #16 0x004008b5737d gfx::GetGlobalFontConfig()
    [pid=14783][err] #17 0x00400600dc1e content::BrowserMainRunnerImpl::Initialize()
    [pid=14783][err] #18 0x00400e6ad9db headless::HeadlessContentMainDelegate::RunProcess()
    [pid=14783][err] #19 0x0040074bf572 content::RunBrowserProcessMain()
    [pid=14783][err] #20 0x0040074c0ea4 content::ContentMainRunnerImpl::RunBrowser()
    [pid=14783][err] #21 0x0040074c0c96 content::ContentMainRunnerImpl::Run()
    [pid=14783][err] #22 0x0040074be358 content::RunContentProcess()
    [pid=14783][err] #23 0x0040074be57d content::ContentMain()
    [pid=14783][err] #24 0x004007b512c3 headless::HeadlessShellMain()
    [pid=14783][err] #25 0x004003b27261 ChromeMain
    [pid=14783][err] #26 0x004011992d0a __libc_start_main
    [pid=14783][err] #27 0x004003b2702a _start
    [pid=14783][err]   r8: 000010e4000f4d00  r9: 000010e4000f4000 r10: 0000000000000d00 r11: 000010e40000daa8
    [pid=14783][err]  r12: 0000004001ea09b0 r13: 000000000000000f r14: 000010e400191ee0 r15: 0000000000000000
    [pid=14783][err]   di: 000010e4000f4000  si: 000000000003c000  bp: 0000004010a0dd00  bx: 000010e40000da80
    [pid=14783][err]   dx: 00000040898dbf81  ax: 3e746964652f3c09  cx: 000000000000001d  sp: 0000004010a0dcd0
    [pid=14783][err]   ip: 0000004008a99cf8 efl: 0000000000000206 cgf: 002b000000000033 erf: 0000000000000004
    [pid=14783][err]  trp: ffffffffffffffff msk: 0000000000000000 cr2: 00746964652f3c11
    [pid=14783][err] [end of stack trace]
    [pid=14783][err] Assertion failed: p_rcu_reader->depth != 0 (/qemu/include/qemu/rcu.h: rcu_read_unlock: 101)
    =========================== logs ===========================
    <launching> /root/.cache/ms-playwright/chromium-1080/chrome-linux/chrome --disable-field-trial-config --disable-background-networking --enable-features=NetworkService,NetworkServiceInProcess --disable-background-timer-throttling --disable-backgrounding-occluded-windows --disable-back-forward-cache --disable-breakpad --disable-client-side-phishing-detection --disable-component-extensions-with-background-pages --disable-component-update --no-default-browser-check --disable-default-apps --disable-dev-shm-usage --disable-extensions --disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate --allow-pre-commit-input --disable-hang-monitor --disable-ipc-flooding-protection --disable-popup-blocking --disable-prompt-on-repost --disable-renderer-backgrounding --force-color-profile=srgb --metrics-recording-only --no-first-run --enable-automation --password-store=basic --use-mock-keychain --no-service-autorun --export-tagged-pdf --headless --hide-scrollbars --mute-audio --blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4 --no-sandbox --user-data-dir=/tmp/playwright_chromiumdev_profile-R1JT9Z --remote-debugging-pipe --no-startup-window
    <launched> pid=14783
    [pid=14783][err] [0928/143420.753020:ERROR:stack_trace_posix.cc(833)] Failed to parse the contents of /proc/self/maps
    [pid=14783][err] [0928/143421.537417:ERROR:stack_trace_posix.cc(833)] Failed to parse the contents of /proc/self/maps
    [pid=14783][err] [0928/143421.537783:ERROR:stack_trace_posix.cc(833)] Failed to parse the contents of /proc/self/maps
    [pid=14783][err] [0928/143421.578078:FATAL:zygote_main_linux.cc(145)] Check failed: sandbox::ThreadHelpers::IsSingleThreaded(). 
    [pid=14783][err] #0 0x00400812a4c2 base::debug::CollectStackTrace()
    [pid=14783][err] #1 0x004008116ee3 base::debug::StackTrace::StackTrace()
    [pid=14783][err] #2 0x00400806f67d logging::LogMessage::~LogMessage()
    [pid=14783][err] #3 0x0040080700fe logging::LogMessage::~LogMessage()
    [pid=14783][err] #4 0x004008058297 logging::CheckError::~CheckError()
    [pid=14783][err] #5 0x0040074c5de2 content::ZygoteMain()
    [pid=14783][err] #6 0x0040074beae3 content::RunZygote()
    [pid=14783][err] #7 0x0040074bf8d8 content::RunOtherNamedProcessTypeMain()
    [pid=14783][err] #8 0x0040074c0c72 content::ContentMainRunnerImpl::Run()
    [pid=14783][err] #9 0x0040074be358 content::RunContentProcess()
    [pid=14783][err] #10 0x0040074be57d content::ContentMain()
    [pid=14783][err] #11 0x004007b51404 headless::HeadlessShellMain()
    [pid=14783][err] #12 0x004003b27261 ChromeMain
    [pid=14783][err] #13 0x004011992d0a __libc_start_main
    [pid=14783][err] #14 0x004003b2702a _start
    [pid=14783][err] 
    [pid=14783][err] qemu: uncaught target signal 5 (Trace/breakpoint trap) - core dumped
    [pid=14783][err] [0928/143421.578081:FATAL:zygote_main_linux.cc(145)] Check failed: sandbox::ThreadHelpers::IsSingleThreaded(). 
    [pid=14783][err] #0 0x00400812a4c2 base::debug::CollectStackTrace()
    [pid=14783][err] #1 0x004008116ee3 base::debug::StackTrace::StackTrace()
    [pid=14783][err] #2 0x00400806f67d logging::LogMessage::~LogMessage()
    [pid=14783][err] #3 0x0040080700fe logging::LogMessage::~LogMessage()
    [pid=14783][err] #4 0x004008058297 logging::CheckError::~CheckError()
    [pid=14783][err] #5 0x0040074c5de2 content::ZygoteMain()
    [pid=14783][err] #6 0x0040074beae3 content::RunZygote()
    [pid=14783][err] #7 0x0040074bf8d8 content::RunOtherNamedProcessTypeMain()
    [pid=14783][err] #8 0x0040074c0c72 content::ContentMainRunnerImpl::Run()
    [pid=14783][err] #9 0x0040074be358 content::RunContentProcess()
    [pid=14783][err] #10 0x0040074be57d content::ContentMain()
    [pid=14783][err] #11 0x004007b51404 headless::HeadlessShellMain()
    [pid=14783][err] #12 0x004003b27261 ChromeMain
    [pid=14783][err] #13 0x004011992d0a __libc_start_main
    [pid=14783][err] #14 0x004003b2702a _start
    [pid=14783][err] 
    [pid=14783][err] qemu: uncaught target signal 5 (Trace/breakpoint trap) - core dumped
    [pid=14783][err] [0928/143422.257059:ERROR:bus.cc(406)] Failed to connect to the bus: Failed to connect to socket /run/dbus/system_bus_socket: No such file or directory
    [pid=14783][err] Received signal 11 SEGV_MAPERR 746964652f3c11
    [pid=14783][err] #0 0x00400812a4c2 base::debug::CollectStackTrace()
    [pid=14783][err] #1 0x004008116ee3 base::debug::StackTrace::StackTrace()
    [pid=14783][err] #2 0x004008129ee1 base::debug::(anonymous namespace)::StackDumpSignalHandler()
    [pid=14783][err] #3 0x004010a65140 <unknown>
    [pid=14783][err] #4 0x004008a99cf8 FcCharSetSerializeAlloc
    [pid=14783][err] #5 0x004008aa8a1c FcValueListSerializeAlloc
    [pid=14783][err] #6 0x004008aa8971 FcPatternSerializeAlloc
    [pid=14783][err] #7 0x004008aa0730 FcFontSetSerializeAlloc
    [pid=14783][err] #8 0x004008a96950 FcDirCacheBuild
    [pid=14783][err] #9 0x004008a9c713 FcDirCacheScan
    [pid=14783][err] #10 0x004008a9c7b0 IA__FcDirCacheRead
    [pid=14783][err] #11 0x004008a91b07 FcConfigAddDirList
    [pid=14783][err] #12 0x004008a91a52 IA__FcConfigBuildFonts
    [pid=14783][err] #13 0x004008aa0ef1 IA__FcInitLoadConfigAndFonts
    [pid=14783][err] #14 0x004008a90f96 FcConfigInit
    [pid=14783][err] #15 0x004008b57b04 base::NoDestructor<>::NoDestructor<>()
    [pid=14783][err] #16 0x004008b5737d gfx::GetGlobalFontConfig()
    [pid=14783][err] #17 0x00400600dc1e content::BrowserMainRunnerImpl::Initialize()
    [pid=14783][err] #18 0x00400e6ad9db headless::HeadlessContentMainDelegate::RunProcess()
    [pid=14783][err] #19 0x0040074bf572 content::RunBrowserProcessMain()
    [pid=14783][err] #20 0x0040074c0ea4 content::ContentMainRunnerImpl::RunBrowser()
    [pid=14783][err] #21 0x0040074c0c96 content::ContentMainRunnerImpl::Run()
    [pid=14783][err] #22 0x0040074be358 content::RunContentProcess()
    [pid=14783][err] #23 0x0040074be57d content::ContentMain()
    [pid=14783][err] #24 0x004007b512c3 headless::HeadlessShellMain()
    [pid=14783][err] #25 0x004003b27261 ChromeMain
    [pid=14783][err] #26 0x004011992d0a __libc_start_main
    [pid=14783][err] #27 0x004003b2702a _start
    [pid=14783][err]   r8: 000010e4000f4d00  r9: 000010e4000f4000 r10: 0000000000000d00 r11: 000010e40000daa8
    [pid=14783][err]  r12: 0000004001ea09b0 r13: 000000000000000f r14: 000010e400191ee0 r15: 0000000000000000
    [pid=14783][err]   di: 000010e4000f4000  si: 000000000003c000  bp: 0000004010a0dd00  bx: 000010e40000da80
    [pid=14783][err]   dx: 00000040898dbf81  ax: 3e746964652f3c09  cx: 000000000000001d  sp: 0000004010a0dcd0
    [pid=14783][err]   ip: 0000004008a99cf8 efl: 0000000000000206 cgf: 002b000000000033 erf: 0000000000000004
    [pid=14783][err]  trp: ffffffffffffffff msk: 0000000000000000 cr2: 00746964652f3c11
    [pid=14783][err] [end of stack trace]
    [pid=14783][err] Assertion failed: p_rcu_reader->depth != 0 (/qemu/include/qemu/rcu.h: rcu_read_unlock: 101)
    ============================================================

```
長ったらしいエラーですね・・解読も難しいですがどうやらplaywright上でchromium環境を動かせない的なエラーなようです。

# 原因
調べてみると、以下のIssueにたどり着きました。

https://github.com/puppeteer/puppeteer/issues/7746

こちらは「puppeteer」でのエラーですが発生しているエラーの内容は同じそうです。

内容を確認すると、どうやらAppleシリコン製のMacでDockerを動かしていると発生する事象っぽく、
「Docker for mac」が「qemu」を使用しているためにChromeのバイナリファイルを取得できず、エラーが起きているようです。

qemuとはなんじゃ？ということだったのでChatGPT先生に聞いてみました。

>QEMU（クイックエミュレータ）は、コンピュータープログラムの特別な環境を作成できるソフトウェアのことです。この特別な環境は、他のコンピューターやプログラムを模倣できるもので、異なる種類のコンピューターを模倣することができます。QEMUは、仮想マシンと呼ばれるこの特別な環境を管理し、必要に応じて調整できるツールです。
このソフトウェアは、異なるコンピュータータイプやプラットフォームでソフトウェアをテストするために使用されます。たとえば、Windowsコンピューター上でLinuxを実行したり、ARMプロセッサを持つコンピューターでx86プロセッサをエミュレートしたりすることができます。

なるほど。「Docker for mac」ではこのQEMUを使用してdockerを動かしていると。

加えて、Appleシリコン製のMacを使用しているとDockerを動かしているホストマシンに自動的にarm64用のイメージが落とされるために、Chromeのバイナリファイルが取得できないみたいです。
Intel製Macであれば、x86(amd64)のイメージであるため問題なく動くとのこと。

だから`docker-compose.yml`に以下一行を追加するんですね。
```yml:docker-compose.yml
platform: linux/x86_64 # M1以降のMacはこれを追加
```

これでIntelMacと同じamd64のイメージで起動できているにも関わらず、なぜ今回のエラーが発生するのか・・

ここで出てくるのがQEMUになります。
擬似的にamd64を動かすのがQEMUになります。

>ここで問題があり、QEMUのエミュレーションは完璧ではありません。一部の命令がサポートされておらず、それを使用するイメージは実行時にSegmentation Faultで終了します。
Google Chromeもこれに該当し、M1 Macのlinux/amd64上で動かした場合には動作しません。
[M1 MacのDockerでChromiumを使ったFeature Specを動かす](https://tech.gunosy.io/entry/docker_chromium_with_m1)

上記のようにQEMUを使ってamd64を動かす場合はChromeが動作しないとのこと。

**つまり今回のようにplatformを擬似的にamd64に指定する場合はQEMUが悪さをしてしまい、Playwrightのchromiuのバイナリファイルが取得できず、エラーが発生してしまうのです。**

# 解決方法
「Docker for Mac」 の4.16以降のバージョンでQEMUを使用せずにエミュレートする設定ができるようになりました:tada:

「Docker for Mac」 を起動して、
右上の歯車マーク > 「Features in development」 > 「Use Rosetta for x86/amd64 emulation on Apple Silicon」をチェックして、アプリケーションを再起動することで、QEMUを使用せず、Rosettaを使用してコンテナを起動することができるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527256/7e35627b-71c2-5fe2-3dcb-971c89c5b0a7.png)

このあとコンテナを再度ビルドして、`npx playwright test`を実行したところ、先程のエラーは起こらず問題なくテストが動きました！

# 最後に
正直、「docker for mac」の設定を変えただけであっけなく解消できました。
Rosettaが対応する前のことを考えただけで寒気がしましたがこのアップデートはかなりGoodJobではないでしょうか。
Appleシリコン製のMacとDockerは相性悪いイメージがあるので、こういう対応は嬉しいですね！