DockerコンテナイメージをvSphere仮想マシンイメージに変換する
Dockerでコンテナイメージをビルドしてから変換することで、仮想マシンイメージを作成することができたので、その方法を説明する。
背景
ソフトウェア動作環境として、仮想マシンよりもコンテナを利用することのほうが最近は一般的になっており、 技術の流行に従えば、あえてコンテナを仮想マシンに変換するようなことは必要ないようにも思われるかもしれない。 確かに、DevOpsのサイクルを高速に回す上で、コンテナの軽量さはメリットであるし、 Kubernetesのようにデプロイを含めたライフサイクルを管理するツールがあるため、 Continuous Deploymentを導入しやすいという点も大きい。 (このあたりは、その軽量さも相まって仮想マシンに比べImmutable Infrastructureに向いていることが影響していると思う。)
しかしながら、以下のような理由により、仮想マシンを使い続ける場面も多くある。
- ネットワークの要件により、コンテナとしてデプロイすることが難しいことがある
- 物理機器とのL2での疎通性が必要な場合など
- CNIプラグインによりコンテナでも理屈の上では実現できることも多いが、ファイアウォールを適用することに難があったり、パフォーマンス上の懸念も残るなど、直ちに実用できるとは言い難い
- 既存の資産の有効活用
- 仮想マシンを前提に出来上がった運用があると、移行コストがかさむため、直ちにコンテナ化するとは限らない
とはいえ、仮想マシンのイメージのビルドは、コンテナのイメージのビルドに比較すると、通常手間がかかるのも事実である。 仮想マシンをデプロイし、SSHなどを利用してパッケージのインストールなどのプロビジョニングを実施するため、 オーバーヘッドも避けられない上に、そもそも仮想マシンをデプロイする環境がないとビルドできない。
そこで、ビルドを簡略化しようという狙いで、Dockerコンテナイメージをビルドし、 それを仮想マシンのイメージに変換するということをやってみた。
手順
PoC
https://github.com/ntoofu/docker-to-vsphere
build.sh
を実行し、生成されるbuild/vm.ovf
をインポートすればOK- (手順の詳細はこのリポジトリの中身を見たほうが良いかもしれない)
内容
alpine
などのディストリビューションのイメージをもとに、通常通りDockerfileを作ってコンテナビルドするが、以下の工夫をする- Linux kernelをインストールする
apk add linux-virt
/etc/fstab
/boot/grub/grub.cfg
をイメージに追加する- OpenRCや、関連して必要になるパッケージを追加する
openrc
busybox-initscripts
e2fsprogs
- システム的に必要なサービスの自動起動設定をする
- Linux kernelをインストールする
docker build
- 試していないが、
kaniko
などでビルドするほうが(CI環境でdocker buildさせる場合はDocker in Dockerなどの工夫が必要になりがちで、それを避けられるため)都合がよいかもしれない
- 試していないが、
- ビルドしたイメージからコンテナを作成し、そのコンテナのファイルをアーカイブファイルとして出力する
docker export
でアーカイブファイルとしてコンテナ内のファイルを出力できる
dd
などで空のイメージファイルを作成し、parted
などでパーティションテーブルを作成- イメージファイルに対応するloopbackデバイスを作成し、パーティションにファイルシステムを作成(フォーマット)
- loopbackデバイスをマウントし、その中にコンテナのアーカイブファイルを展開
- 後述する理由で、
.dockerenv
boot/boot
を exclude
- 後述する理由で、
- grub-install により、grub2をインストールする
- qemu-imgで
streamOptimized
な vmdk ファイルに変換 - OVFファイルを作成する
補足
- OpenRCは
rc_sys
という設定項目があり、未設定の場合、動作環境を検出し、Dockerコンテナ内と判定された場合、一部サービスが起動しなくなる/.dockerenv
の有無で判定している模様(該当箇所)- そのため、dockerコンテナから出力したアーカイブを展開する際は
.dockerenv
をexcludeする
- grub-install 時には
--boot-directory
,--efi-directory
,--no-nvram
,--removable
などのやや特殊なCLIオプション指定が必要- grub2はstage1, stage1.5, stage2と複数のイメージを順に呼び出す仕組みであり、grub-installでも複数のイメージをインストールすることになっており、今回のようにgrub-installコマンド実行環境の
/boot
へインストールするわけではない場合は、--boot-directory
--efi-directory
の指定が必要 - EFI利用時は、EFIからgrubのstage2イメージが直接呼び出され、そのファイルパスはEFIが利用する(物理ホストの場合はマザーボード上の)不揮発性の記憶領域に保存されるが、今回は仮想マシンに対し不揮発性記憶領域を用意しない手順なので、不揮発性記憶領域への書き込みをしない
--no-nvram
と、設定なしでもEFIがイメージを見つけられるように標準的なファイルパスを使用する--removable
の指定をする
- grub2はstage1, stage1.5, stage2と複数のイメージを順に呼び出す仕組みであり、grub-installでも複数のイメージをインストールすることになっており、今回のようにgrub-installコマンド実行環境の
- EFI 対応する場合、
/boot
用のパーティションは FAT 系のファイルシステムを使うことになり、シンボリックリンクは使えなくなるboot/boot
をexcludeしているのはそのため
参考
- 今回実験した内容とほぼ同じことが可能なはずと示唆している記事
- BIOS, EFIどちらかも起動可能にする方法の参考にした記事