とことんDevOps | 日本仮想化技術のDevOps技術情報メディア

DevOpsに関連する技術情報を幅広く提供していきます。

日本仮想化技術がお届けする「とことんDevOps」では、DevOpsに関する技術情報や、日々のDevOps業務の中での検証結果、TipsなどDevOpsのお役立ち情報をお届けします。
主なテーマ: DevOps、CI/CD、コンテナ開発、IaCなど

開催予定の勉強会

読者登録と各種SNSのフォローもよろしくお願いいたします。

Apple SiliconのmacOSで動かないイメージでDocker Composeする

背景

普段私はMacで作業しています。3年くらい前からApple Silicon M1のMac miniをメインの作業機として使っています。

そんなある日、ちょっとDocker Composeでアプリケーションをデプロイしたらうまく動かなかったので、久しぶりにIntel Macを起こしました。 毎回これをやるといつまで経ってもIntel Macから卒業できないので、対処しようと思いました。

動作環境と症状について

今回の環境はこちらです。

$ uname -m
arm64
 
$ sw_vers 
ProductName:    macOS
ProductVersion: 12.6.8
BuildVersion:   21G725

実行すると現れるエラー。イメージ由来の問題みたいです。

$ docker compose up -d
[+] Running 1/2
 ⠦ db 1 layers [⣿]      0B/0B      Pulling        2.6s 
   ✔ 0821f3a5b3ec Download complete            0.6s 
no match for platform in manifest: not found

docker-compose.ymlはこのような感じで書いています。極めて普通な感じです。

$ cat docker-compose.yml
version: '3'
services:
  db:
    image: mysql:8.0-debian
    environment:
      MYSQL_ROOT_PASSWORD: my_secret_pw
    ports:
      - "3306:3306"
  web:
    build: .
    container_name: web
    depends_on:
      - db
    volumes:
      - ./php/:/var/www/html/
    ports:
      - "30080:80"
    stdin_open: true
    tty: true

Dockerfileはこんな感じです。 Webサービス側はphpイメージを使っています。PHP8.2 + Apache2が導入されているイメージです。

$ cat Dockerfile 

FROM php:8.2-apache
RUN docker-php-ext-install mysqli

あまり本題とは関係ないですが、index.phpのコードは以下のとおりです。 テストコードなので、認証情報関連はベッタリにしてます。

<?php
  $db_host = 'db';
  $db_user = 'root';
  $db_password = 'my_secret_pw';
  $db_db = 'information_schema';
  $db_port = 3306;

  $mysqli = new mysqli(
    $db_host,
    $db_user,
    $db_password,
    $db_db,
        $db_port
  );

  if ($mysqli->connect_error) {
    echo 'Errno: '.$mysqli->connect_errno;
    echo '<br>';
    echo 'Error: '.$mysqli->connect_error;
    exit();
  }

  echo 'Success: A proper connection to MySQL was made.';
  echo '<br>';
  echo 'Host information: '.$mysqli->host_info;
  echo '<br>';
  echo 'Protocol version: '.$mysqli->protocol_version;

  $mysqli->close();
?>

失敗した理由

さらなるテストの結果、やはりMySQLイメージにApple Siliconで動作するイメージがないようです。 PHPイメージはARM64対応しているみたいですね。

$ docker image pull mysql:8.0-debian
2c10bf0e0b27: Already exists 
no match for platform in manifest: not found

$ docker image pull php:8.2-apache
a6d0bb13cd94: Already exists 
b515004c3b0d: Download complete 
65c3267c7b37: Download complete 
35be195075a3: Download complete 
dd06eedb7624: Download complete 
c3eb3ed58b0c: Download complete 
3b10997c9d6e: Download complete 
4ee097f9a366: Download complete 
50aa8be75fea: Download complete 
2d6175e9b527: Download complete 
e255c1ce22a7: Download complete 
1bab033462b2: Download complete 
559343ab0c4a: Download complete 
62975eb3413b: Download complete 
02cbea39f639: Download complete 
ac9dce739201: Download complete 
docker.io/library/php:8.2-apache

対処方法

Rosetta 2を入れておきます(これはのちの調査で、Ventura以降なら有効な方法です)。

$ /usr/sbin/softwareupdate --install-rosetta --agree-to-license
By using the agreetolicense option, you are agreeing that you have run this tool with the license only option and have read and agreed to the terms.
If you do not agree, press CTRL-C and cancel this process immediately.
2023-08-28 20:39:47.220 softwareupdate[67322:1816036] Package Authoring Error: 042-15018: Package reference com.apple.pkg.RosettaUpdateAuto is missing installKBytes attribute
Installing: 0.0%
Install of Rosetta 2 finished successfully

platform: linux/amd64を指定してみます。

$ cat docker-compose.yml 

version: '3'
services:
  db:
    image: mysql:8.0-debian
    platform: linux/amd64
    environment:
      MYSQL_ROOT_PASSWORD: my_secret_pw
    ports:
      - "3306:3306"
  web:
    build: .
    platform: linux/amd64
    depends_on:
      - db
    volumes:
      - ./php/:/var/www/html/
    ports:
      - "30080:80"
    stdin_open: true
    tty: true

起動しました。ただしエミュレーションなので、ネイティブで動かすときより遅いです。 あくまで動作テストするだけなら良いでしょう。

$ docker compose up -d
[+] Running 3/3
 ✔ Network docker-mysqlphp_default  Created                                                                                                     0.0s 
 ✔ Container db                     Started                                                                                                     0.2s 
 ✔ Container docker-mysqlphp-web-1  Started      
 
 $ docker compose ps
NAME                    IMAGE                 COMMAND                  SERVICE             CREATED             STATUS              PORTS
db                      mysql:8.0-debian      "docker-entrypoint.s…"   db                  6 seconds ago       Up 5 seconds        0.0.0.0:3306->3306/tcp, 33060/tcp
docker-mysqlphp-web-1   docker-mysqlphp-web   "docker-php-entrypoi…"   web                 6 seconds ago       Up 5 seconds        0.0.0.0:30080->80/tcp

さらにテストしたところ、一方だけplatformを指定して試したらうまく動きませんでした。

image with reference docker-mysqlphp-web was found but does not match the specified platform: wanted linux/arm64, actual: linux/amd64

起動タイミングの問題か直ぐアクセスしすぎたようでエラーになりましたが、10秒くらい待てばちゃんと動くのを確認しました。

$ curl http://localhost:30080
<br />
<b>Fatal error</b>:  Uncaught mysqli_sql_exception: Connection refused in /var/www/html/index.php:8
Stack trace:
#0 /var/www/html/index.php(8): mysqli-&gt;__construct('db', 'root', Object(SensitiveParameterValue), 'information_sch...', 3306)
#1 {main}
  thrown in <b>/var/www/html/index.php</b> on line <b>8</b><br />

$ curl http://localhost:30080
Success: A proper connection to MySQL was made.<br>Host information: db via TCP/IP<br>Protocol version: 10

まとめ

Docker HubのMySQLイメージ は、platform: linux/amd64を指定すれば使えることがわかりました。 また、docker-compose.ymlplatformを指定する場合は、他のイメージについても指定が必要なのがわかりました。

macOS MontereyまでのバージョンはDocker DesktopでRosetta 2を使うことができないので、エミュレートして動くために起動が遅いということも理解できました。最初のうちはそこらへんの理解が浅かったため、「あれっ...ちゃんと動かない!」などと判断してしまいました。あと10秒くらい待てば良かったのに...。

ちなみにmacOS Ventura以降で動かすと、「Use Rosetta for x86/amd64 exmulation on Apple Silicon」というオプションがDocker Desktopに生えてくるようです。この環境は諸事情でMontereyなので、その設定はパネルには出てこない模様です。

www.sria.co.jp

なお、こう指定しても動くことを確認しました。アーキテクチャーが混在するのでちょっと気持ちが悪い感じもありますが、少なくともWebサーバー側はネイティブで動くので速くなります。

$ cat docker-compose.yml 
version: '3'

services:
  db:
    image: mysql:8.0-debian
    platform: linux/amd64
    environment:
      MYSQL_ROOT_PASSWORD: my_secret_pw
    ports:
      - "3306:3306"
  web:
    build: .
    platform: linux/arm64
    depends_on:
      - db
    volumes:
      - ./php/:/var/www/html/
    ports:
      - "30080:80"
    stdin_open: true
    tty: true
...
[+] Running 3/3
 ✔ Network docker-mysqlphp2_default  Created                   0.0s 
 ✔ Container db                      Started                                       0.2s 
 ✔ Container docker-mysqlphp2-web-1  Started

$ curl http://localhost:30080/
Success: A proper connection to MySQL was made.<br>Host information: db via TCP/IP<br>Protocol version: 10

当然ながら、ARM64をサポートするMariadbを組み込んだ場合は問題なく動作します。 コンテナかつApple Silicon環境のDockerで、どうしてもMySQLを動かしたいという強いこだわりがなければ、Mariadbイメージを使うのもありです。

$ cat docker-compose.yml 
version: '3'

services:
  db:
    image: mariadb:10.11.5
    platform: linux/arm64
    container_name: db
    environment:
      MYSQL_ROOT_PASSWORD: my_secret_pw
    ports:
      - "3306:3306"
  web:
    build: .
    platform: linux/arm64
    depends_on:
      - db
    volumes:
      - ./php/:/var/www/html/
    ports:
      - "30080:80"
    stdin_open: true
    tty: true
...
[+] Running 3/3
 ✔ Network docker-mysqlphp2_default  Created                  0.0s 
 ✔ Container db                      Started                                     0.2s 
 ✔ Container docker-mysqlphp2-web-1  Started

$ curl http://localhost:30080/
Success: A proper connection to MySQL was made.<br>Host information: db via TCP/IP<br>Protocol version: 10

使ったイメージ

おまけ

最近のDocker Desktopはビルドのステータスとか一覧で出してくれるようですね。現在のビルドの状況と、過去のビルドの結果がまとまっています。 こういうのを見るのも好きです。