Dockerコンテナ移行でのアーキテクチャの壁と解決策

Dockerコンテナの移行について

この記事では、Dockerのコンテナやイメージを別の仮想マシンに移行する方法を解説します。SSH接続済みの Ubuntu 24.04 を例に、2つのLinuxインスタンス間でコンテナやイメージを移動する具体的な手順を紹介します。WSLやクラウドサービスでも同様の手順で実行可能です。例えば、WSLではローカル開発環境で手軽にDockerを使用でき、クラウドサービスではリモートサーバー間の移行を効率的に行えます。

前提条件: Dockerはインストール済みですが、コンテナやイメージはまだありません。この状態から、実際にサンプルコンテナを作成して移行作業を行います。

Docker Export と Docker Save の違いとは?

Dockerコンテナやイメージを移行したいとき、docker exportdocker save という2つのコマンドを使うことができます。しかし、これらのコマンドは目的が異なり、どちらを使うかによって結果が大きく変わります。ここでは、これらのコマンドの違いを簡単に説明します。

1. Docker Export: ファイルシステムだけを移行

docker export は、コンテナのファイルシステムのみをエクスポートします。つまり、コンテナ内のファイルやディレクトリはすべて保存されますが、**コンテナの設定やエントリポイント(どのサービスを起動するかなど)**は含まれません。

  • 用途: コンテナの中のデータやファイルを他の環境に持ち込みたいときに使用します。
  • 制限: 移行先で、コンテナの設定やサービスの再起動を手動で行う必要があります。

例:

docker export -o backup.tar mycontainer

このコマンドで、mycontainer のファイルシステムを backup.tar という名前でエクスポートします。


2. Docker Save: イメージ全体を移行

docker save は、Dockerイメージ全体を保存します。ここでいうイメージには、ファイルシステムに加えて、コンテナの設定やエントリポイント、環境変数なども含まれます。そのため、移行先でインポートすると、元の設定を引き継いだ状態でコンテナを再起動できます。

  • 用途: コンテナの設定や実行するサービスも含めて、完全に再現したいときに使用します。
  • 利点: 設定やエントリポイントが含まれるため、移行先でコンテナをそのまま起動できます。

例:

docker save -o myimage.tar myimage:latest

このコマンドで、myimage:latest というイメージを myimage.tar というファイルに保存します。


まとめ

  • docker export は、コンテナのファイルシステムだけを移行し、移行先では手動でサービスを起動する必要があります。
  • docker save は、コンテナの設定やサービスの起動情報を含む完全なイメージを移行でき、移行先で簡単に再現が可能です。

ステップ1: サンプルのコンテナを作成する

まずは、サンプルとなるコンテナを作成しましょう。今回はNginxというWebサーバーのコンテナを使用します。以下のコマンドを使って、Nginxコンテナを作成・起動します。

docker run --name mynginx -d -p 8888:80 nginx
  • --name mynginx はコンテナの名前を指定しています。
  • -d はコンテナをバックグラウンドで実行します。
  • -p 8888:80 はホストのポート8888をコンテナ内のポート80にマッピングしています。

以下の画像はポート番号8080の設定

これで、ブラウザで http://<サーバーのIPアドレス>:8888 にアクセスすると、Nginxのウェルカムページが表示されるはずです。

以下の画像はポート番号8080の設定


ステップ2: コンテナのエクスポート

次に、作成したコンテナを別の仮想マシンに移行するためにエクスポートします。以下のコマンドで、コンテナのファイルシステムをアーカイブとして出力します。

docker export -o bk.tar mynginx

このコマンドでは、mynginx という名前のコンテナを bk.tar というファイル名でエクスポートします。

  • エクスポートとは?: コンテナのファイルシステムを1つのファイル(tar形式)にまとめることです。このファイルを他のマシンに移動させ、再インポートすることで同じ環境を再現できます。

ステップ3: ファイルのパーミッション設定

次に、ファイルのパーミッション(アクセス権限)を設定します。パーミッションが正しくないと、ファイルをダウンロードできない場合があります。

sudo chmod 666 bk.tar

このコマンドでは、ファイル bk.tar にすべてのユーザーに読み書き権限を付与します。sudo を使用することで、一般ユーザーがシステムのファイルやフォルダに対して必要な操作を実行できるようになります。このコマンドを入力したディレクトリにバックアップファイルは作成されます。

また、特定のディレクトリにファイルを保存したい場合は、ファイルの保存先を指定することができます。例えば、/backup というディレクトリに保存する場合は以下のようにします。

docker export -o /backup/bk.tar mynginx

このコマンドでは、バックアップファイル bk.tar/backup ディレクトリに保存されます。事前にそのディレクトリが存在するか確認しておくと良いでしょう。ここまで書いてきたのですが実際には移行先のマシンで失敗します。その理由と体験談を示します。

docker export での失敗: 理解不足が招いた問題

サンプルのNginxコンテナを作成して、次に移行を試みたとき、私は docker export を使いました。初めての移行だったため、docker export がコンテナの全てを移行できると誤解していたのです。

失敗した手順

  1. Nginxコンテナのエクスポート
    docker export -o /backup/bk.tar mynginx
    私はこのコマンドでコンテナのファイルシステムをエクスポートしました。見たところ、ファイルは正しく生成されました。ファイルシステムがそのまま保存されていると思ったからです。
  2. 移行先でのインポート 移行先のサーバーで、エクスポートしたファイルをインポートしました。
    docker import /tmp/bk.tar
    ここまでは順調に進み、イメージがインポートされました。
  3. コンテナの起動 コンテナを起動しようとしました。
    docker run --name mynginx -d -p 8888:80 <イメージID>
    しかし、ブラウザで http://<サーバーのIPアドレス>:8888 にアクセスしても、Nginxのウェルカムページが表示されないという問題が発生しました。何度試しても結果は同じでした。

失敗の理由: エントリポイントが含まれていない

ここでの大きな問題は、docker export がコンテナのファイルシステムだけをエクスポートしていることに気づいていなかったことです。ファイルシステムは正しく移行できましたが、Nginxを起動するエントリポイント(docker-entrypoint.sh)や、実行するコマンドの情報が含まれていなかったため、移行先でNginxが自動で起動しなかったのです。

具体的に言うと、以下の情報がエクスポートされていませんでした:

  • Nginxを起動するコマンド/usr/sbin/nginx -g 'daemon off;'
  • コンテナ内の環境変数や設定

そのため、移行先でNginxが立ち上がらず、ブラウザからアクセスできなかったのです。


教訓: docker exportdocker save の違いを理解する

この失敗から、docker export はファイルシステムのみをエクスポートするため、コンテナの設定や実行するサービスは手動で設定する必要があることを学びました。Nginxのように特定のサービスを自動で起動させたい場合は、docker save を使用して、イメージ全体を移行する必要があります。

というわけでdocker saveの方法を書きます。こちらは結果的にうまくいきます。その前に移行元と移行先でファイルのやりとりがあるので説明しておきます。また、認証鍵も必要になるので先に記しておきます。

docker exportdocker saveでも作成されるファイルがあります。これを移行先に転送しなければなりません。一度自分のPCにダウンロードする方法はTeraTermのSSH SCP機能を使用します。ファイルサイズが大きいとダウンロードに時間がかかります。

TeraTermのSSH SCP機能を使用することもできますが、VS Codeを使ってより簡単に行う方法として、SFTP拡張機能を利用します。

VS Codeを使ったファイルダウンロードの手順

  1. VS Codeのインストール
    まず、VS Codeがインストールされていることを確認します。VS Codeは公式サイトからダウンロードできます。
  2. SFTP拡張機能のインストール
    次に、VS Codeの拡張機能マーケットプレイスで「SFTP」を検索し、インストールします。
  3. SFTPでの接続とファイルのダウンロード
    拡張機能をインストールすると、リモートサーバーに簡単に接続できます。左サイドバーに表示されるファイルエクスプローラーから、サーバー上のファイルを選択し、右クリックで「Download」を選びます。

    補足: 通常、sftp.json ファイルで設定を行う必要がありますが、場合によってはSFTP拡張機能が自動的に適切な設定を行い、特に設定ファイルを変更せずにダウンロードができることもあります。

ファイルの送信方法(rsyncを使用)

ファイルの転送が大きく、時間がかかる場合、rsync コマンドを使用して効率的にファイルを送信できます。今回は、移行元から移行先にファイルを送信する方法を紹介します。

ステップ1: 秘密鍵の作成

まず、SSH接続に使用する秘密鍵を作成します。以下のコマンドを使って、秘密鍵と公開鍵を生成します。

ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa
  • -t rsa はRSA暗号を使用することを示しています。
  • -b 4096 は鍵のビット数を指定しています(セキュリティのために大きな値を推奨)。
  • -f ~/.ssh/id_rsa は鍵ファイルの保存場所を指定しています。

このコマンドを実行すると、秘密鍵 (id_rsa) と公開鍵 (id_rsa.pub) が生成されます。以下は実際の出力結果です。

Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/mamu/.ssh/id_rsa
Your public key has been saved in /home/mamu/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:21WN0159kkk9RDkWhnNdxMWA39ua0xWEB4RPJXD56oA mamu@MB-T500
The key’s randomart image is:
+—[RSA 4096]—-+
| .+*@%X|
| .oBo#*|
| o.#.O|
| o.*+|
| S . . . =|
| E o . .o|
| . . o +.|
| .+ .|
| . |
+—-[SHA256]—–+

ステップ2: 移行先サーバーに公開鍵を配置

生成した公開鍵を移行先サーバーにコピーします。ssh-copy-id コマンドを使って公開鍵を配置します。

ssh-copy-id -i ~/.ssh/id_rsa.pub user@<移行先のIPアドレス>

これで、移行元から移行先へSSH接続が可能になります。以下は実際のコマンド入力例です。

ssh-copy-id -i ~/.ssh/id_rsa.pub mamu@192.168.0.91

表示された実際の内容:

/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: “/home/mamu/.ssh/id_rsa.pub”
The authenticity of host ‘192.168.0.91 (192.168.0.91)’ can’t be established.
ED25519 key fingerprint is SHA256:azZSnRoh+vPHX+0kmjK0VaPFfkItGxFuZx4S9LIPR8Q.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed — if you are prompted now it is to install the new keys
mamu@192.168.0.91’s password:

Number of key(s) added: 1

Now try logging into the machine, with: “ssh ‘mamu@192.168.0.91′”
and check to make sure that only the key(s) you wanted were added.

ステップ3: ファイルの送信

次に、rsync を使って移行元から移行先にファイルを送信します。以下のコマンドを実行してください。以下はbk.tarというファイルを送信する例です。

rsync -e 'ssh -i ~/.ssh/id_rsa' -av /tmp/bk.tar user@<移行先のIPアドレス>:/tmp
  • -e 'ssh -i ~/.ssh/id_rsa' は、SSH接続に使用する秘密鍵を指定しています。
  • /tmp/bk.tar は送信するファイルのパスです。
  • user@<移行先のIPアドレス>:/tmp は、ファイルの送信先ディレクトリを示しています。

実際のコマンドと出力例:

rsync -e 'ssh -i ~/.ssh/id_rsa' -av /tmp/bk.tar mamu@192.168.0.91:/tmp

sending incremental file list
bk.tar

sent 190,200,253 bytes received 35 bytes 10,868,587.89 bytes/sec
total size is 190,153,728 speedup is 1.00

補足: 同じセグメントでなくても大丈夫?

同じセグメントに属していなくても、インターネット経由で通信可能な場合は rsync によるファイル転送は可能です。異なるネットワークセグメントに属していても、外部IPアドレスや適切なファイアウォールの設定がされていれば問題なく送信できます。移行先にファイルは正常に送信されていました。

mamu@proxub24:~$ ls /tmp
bk.tar

移行先での作業:Dockerのインストール

移行元から送信されたファイルを使用して、移行先でDockerイメージをインポートします。この作業を行う前に、移行先にもDockerがインストール済みであることを確認してください。インストール方法が不明な場合は、以下のページで解説しています。

ここからがdocker saveコマンドの出番です。

Dockerのcommit と save コマンドの使用

Dockerの save コマンドを使用して、イメージをバックアップする方法を紹介します。この手順では、コンテナを停止せずに新しいイメージを作成し、そのイメージを保存する方法を説明します。移行元に戻りコマンドを入力します。

ステップ1: コンテナをイメージに変換する

まず、docker commit コマンドを使用して、現在稼働中のコンテナからイメージを作成します。以下の例では、コンテナの名前を「mycontainer」、イメージの名前を「myimage」としています。

docker commit mycontainer myimage:version1
  • mycontainer はコンテナの名前です。
  • myimage は作成するイメージのリポジトリ名で、version1 はタグです。

これで、コンテナから新しいイメージが作成されました。docker images コマンドを使って、イメージが追加されたことを確認できます。

ステップ2: イメージをtarファイルとして保存する

次に、作成したイメージをdocker save コマンドを使用して保存します。このコマンドは、1つまたは複数のイメージを tar アーカイブに保存できます。

docker save -o myimage_backup.tar myimage:version1
  • myimage_backup.tar は保存先のファイル名です。
  • myimage:version1 は保存するイメージの名前とタグです。

ステップ3: パーミッションの確認と変更

通常、この手順では chmod 666 myimage_backup.tar を使ってファイルのパーミッションを変更する必要はありません。しかし、ファイルにアクセス権限の問題が発生した場合には、以下のコマンドを使ってパーミッションを変更することができます。

sudo chmod 666 myimage_backup.tar
  • このコマンドは、すべてのユーザーに読み書き権限を付与します。
  • パーミッションが必要ない場合、このステップはスキップできます。

補足
docker save コマンドでは、/tmp のような特定のディレクトリを指定してファイルを保存することが可能です。以下のように、保存先のパスを指定するだけです。

docker save -o /tmp/myimage_backup.tar myimage:version1

このコマンドでは、myimage:version1 のイメージが /tmp ディレクトリに myimage_backup.tar という名前で保存されます。

つまり、-o オプションに指定するファイルパスに任意のディレクトリを指定でき、そこでファイルが作成されます。

移行元でファイルを移行先に送信する方法(rsyncを使用)

この手順では、移行元のPCrsync コマンドを使用して、移行先にファイルを送信します。ここでは、移行元から移行先にファイルを送るための具体的なステップを説明します。

ステップ1: 移行元で rsync コマンドを実行

まず、移行元のPCで以下のコマンドを実行します。このコマンドにより、移行元から移行先にファイルを送信します。

rsync -e 'ssh -i ~/.ssh/id_rsa' -av /tmp/myimage_backup.tar user@<移行先のIPアドレス>:/tmp
  • 移行元: このコマンドを実行するのは、移行元のPCです。
  • -e 'ssh -i ~/.ssh/id_rsa': SSH接続を使用してファイルを転送し、秘密鍵を指定します。
  • /tmp/myimage_backup.tar: 移行元にある送信するファイルのパスです。
  • user@<移行先のIPアドレス>: 送信先のユーザー名とIPアドレスを指定します。
  • :/tmp: 移行先のPCでファイルを保存するディレクトリを指定しています。

ステップ2: 実際のコマンド例

実際のIPアドレスとユーザー名を使用した例です。移行元で実行してください。

rsync -e 'ssh -i ~/.ssh/id_rsa' -av /tmp/myimage_backup.tar mamu@192.168.0.91:/tmp
  • mamu: 移行先のユーザー名です。
  • 192.168.0.91: 移行先サーバーのIPアドレスです。
  • /tmp: 移行先でファイルが保存されるディレクトリです。

ステップ3: 移行先でファイルの確認

移行元からファイルが無事に送信されたら、移行先で以下のコマンドを実行して、ファイルが正しく受信されているかを確認します。

ls -lh /tmp/myimage_backup.tar

これで、移行先の /tmp ディレクトリにファイルが存在することを確認できます。

移行先での作業:Dockerイメージの読み込み

移行元から送信されたバックアップファイルを使って、移行先でDockerイメージを読み込みます。以下の手順に従って作業を進めてください。

ステップ1: Dockerイメージの読み込み

移行先で、先ほど転送したファイルを使用してDockerイメージを読み込みます。以下のコマンドを移行先の端末で実行してください。

docker load -i /tmp/myimage_backup.tar
  • docker load: これは、tarアーカイブからDockerイメージを読み込むコマンドです。
  • -i /tmp/myimage_backup.tar: /tmp ディレクトリに保存されている myimage_backup.tar というバックアップファイルを指定しています。

上のコマンドで実際に表示されたメッセージ:

8e2ab394fabf: Loading layer [==================================================>] 77.83MB/77.83MB
67796e30ff04: Loading layer [==================================================>] 114MB/114MB
eda13eb24d4c: Loading layer [==================================================>] 3.584kB/3.584kB
0fc6bb94eec5: Loading layer [==================================================>] 4.608kB/4.608kB
2bdf51597158: Loading layer [==================================================>] 2.56kB/2.56kB
16907864a2d0: Loading layer [==================================================>] 5.12kB/5.12kB
11de3d47036d: Loading layer [==================================================>] 7.168kB/7.168kB
d78fa06b2cb7: Loading layer [==================================================>] 10.24kB/10.24kB

ステップ2: イメージの読み込み確認

イメージの読み込みが完了すると、読み込まれたイメージIDが表示されます。イメージが正しく読み込まれたか確認するために、以下のコマンドを実行してイメージリストを表示します。

docker images

このコマンドを実行すると、myimage_backup.tar から読み込んだイメージがリストに表示されているはずです。ここで、リポジトリ名やタグが設定されていない場合には、後から名前を付けることができます。

ステップ3: イメージに名前を付ける(必要に応じて)

もし、インポートしたイメージにリポジトリ名やタグが付いていない場合、docker tag コマンドを使って後から名前を付けることができます。これは、イメージの管理を簡単にするために重要です。

イメージに名前を付ける方法

  1. まず、docker images コマンドを使って、イメージのIDを確認します。
    docker images 例として、次のような出力が得られたとします。
    REPOSITORY TAG IMAGE ID CREATED SIZE
    <none> <none> d69fd7664ed7 5 hours ago 188MB
  2. 次に、docker tag コマンドを使って、イメージIDに名前(リポジトリ名とタグ)を付けます。
    docker tag d69fd7664ed7 myimage:version1
    • d69fd7664ed7: これは、読み込んだイメージのIDです。
    • myimage: 付けたいリポジトリ名(myimage)とタグ名(version1)です。
  3. イメージに名前を付けた後、再度 docker images コマンドで確認します。
    docker images
    新しい名前が付けられていることが確認できるはずです。
    REPOSITORY TAG IMAGE ID CREATED SIZE
    myimage version1 d69fd7664ed7 5 hours ago 188MB

ステップ4: docker run でコンテナを起動する

docker run コマンドを使うことで、作成したイメージからコンテナを起動できます。このコマンドでは、イメージを元に新しいコンテナを作成し、指定された設定で起動します。

基本的な docker run の使い方

以下は、docker run コマンドを使って、作成したイメージからNginxのコンテナを起動する例です。

docker run --name mynginx -d -p 8888:80 myimage:version1
  • --name mynginx: コンテナに「mynginx」という名前を付けます。これにより、今後この名前を使ってコンテナを管理しやすくなります。
  • -d: コンテナをバックグラウンドで実行します。これを指定しないと、コンテナが対話モードで実行されてしまいます。
  • -p 8888:80: ホストのポート8888をコンテナ内のポート80にマッピングします。これにより、ホストのブラウザから http://<サーバーのIPアドレス>:8888 にアクセスできるようになります。
  • myimage:version1: コンテナを起動する元のイメージです。このイメージ名は、前に付けたリポジトリ名とタグです。

コンテナの起動確認

コンテナが正常に起動しているかを確認するには、以下のコマンドを使用します。

docker ps

これにより、現在稼働中のコンテナのリストが表示されます。mynginx が表示されていれば、コンテナが正常に動作しています。

コンテナへのアクセス

Nginxが起動している状態で、ブラウザから以下のURLにアクセスします。

http://<サーバーのIPアドレス>:8888

Nginxのウェルカムページが表示されれば、コンテナが正常に起動していることが確認できます。

実は、移行元と移行先ではインスタンスがARMとAMDの違いでエラーが発生したことがあります。

WARNING: The requested image’s platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested
6f101cdc855e330fbd648388c2610790f2a164ca861da275e83882f6b9e17be1

アーキテクチャの違いによるエラー

Dockerを使用してコンテナを移行する際、移行元と移行先で使用しているハードウェアのアーキテクチャが異なる場合、エラーが発生することがあります。今回、移行元のインスタンスはARMアーキテクチャ(linux/arm64/v8)、移行先のインスタンスはAMDアーキテクチャ(linux/amd64)を使用していたため、このエラーが発生しました。

発生したエラーメッセージ:

WARNING: The requested image’s platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested

このエラーは、ARMアーキテクチャ向けに作成されたイメージがAMD(x86_64)アーキテクチャで実行されようとしたために発生しています。


解決策: プラットフォームの指定

異なるアーキテクチャ間でDockerイメージを実行する必要がある場合は、Dockerのマルチプラットフォーム対応機能を利用して、ターゲットプラットフォームを明示的に指定することができます。これにより、異なるプラットフォームでもイメージが正しく実行できるようになります。

解決方法

以下のように、docker run コマンドでプラットフォームを指定してコンテナを起動します。

docker run --platform linux/amd64 --name mynginx -d -p 8888:80 myimage:version1
  • --platform linux/amd64: AMD(x86_64)向けにイメージを実行することを指定しています。これにより、ホストがAMDアーキテクチャであっても、ARMアーキテクチャ向けのイメージを実行できるようになります。

もし逆に、ARMアーキテクチャで動作させたい場合は、--platform linux/arm64 を指定することもできます。


マルチアーキテクチャ対応のビルド

将来的に、異なるプラットフォーム間でイメージをスムーズに移行するためには、マルチプラットフォーム対応のイメージをビルドすることを検討するのも良いでしょう。これには、buildx というDockerの拡張ツールを使ってマルチアーキテクチャ対応のイメージを作成できます。

タイトルとURLをコピーしました