2013年にDockerがリリースされ、Linuxにおけるコンテナが脚光を浴びるようになりました。そのLinuxコンテナは一時的な流行とはならず、最近では話題はコンテナそのものよりも、コンテナをどう運用していくかというステージに入っています。それに伴い、話題の中心はオーケストレーションに移っています。
つまり、コンテナがプロダクション環境で使われるステージに入り、さまざまなシステムがコンテナ上で稼働するようになってきたわけです。活用が進むにつれ、色々な要件がコンテナに求められるようになるでしょう。その要件を満たす機能がLinuxコンテナにあるのか、それ以前に開発するシステムはコンテナ上で動かすのが適当なのかどうかを判断するには、Linuxコンテナの基本的な機能を理解することが必要になるでしょう。
Linuxにおけるコンテナは、Linuxカーネルに実装されています。しかし、「コンテナ」というひとつの機能として実装されているわけではなく、多数の機能を組み合わせてコンテナが作られています。これらの機能はコンテナ専用の機能というわけではなく、単一の機能として使うことができる機能がたくさんあります。
この本では、そのような機能のうち、コンテナの主要機能のひとつであるNamespaceを中心に紹介します。この本でコンテナに使われる機能についての理解が進み、より有効にコンテナが使われていくことを期待しています。
なお、この本の実行例は特に触れない限り、Ubuntu 20.04上で動作確認をしています。そして、本書ではLXCやDockerなどのコンテナランタイムを使わないで、一般的なLinux環境に含まれているコマンドのみで説明をするようにしています。
コマンド名や実行例などで出てきた具体的なパスは、本文中でも等幅フォントを使用します。
コンテナに関するさまざまな機能を説明するまえに、まずは「コンテナ」とは何か?を説明しましょう。
ひとことで言うとコンテナとは、隔離した空間でプロセスを実行することです。
短くひとことで説明しましたが、ここに重要なキーワードがふたつ含まれています。
コンテナを説明する際、「仮想化」や「仮想環境」といった「仮想」という単語が使われることがあります。たしかにコンテナを起動すると、ある種の「仮想環境」が作られます。しかし筆者は、「仮想」よりは「隔離」という言葉を使ったほうがよりコンテナを的確に表わしていると考えます。あとでもう少し詳しく説明しますが、まずはこの隔離が重要であることを覚えておいてください。
そしてもうひとつが「プロセス」です。コンテナはプロセスであるということが、もうひとつ忘れてはいけない重要な点です。
もう少し詳細に説明していきましょう。
「仮想環境」を作り出すための仕組みとして、「仮想マシン」(VM)を使ったことがある方は多いのではないでしょうか。ここでは説明のために、VMと比較をしてみます。
VMでは図1.1のように、コンピューター上で動くOSやVMを実現するためのハイパーバイザ上で、実際のハードウェアをエミュレートするVMが動きます。実際の物理的なコンピューターと同じようなものがソフトウェアによって実現されているので、このVMを使うにはOSが必要になります。つまり、図1.1のように物理的なコンピューター上で動くOS上で、さらにOSが動きます。また、このVM自体は物理マシン上で実行されているOS上のプロセスですが、物理マシン上のOSからはVMのプロセスが見えているだけで、VM内で動作しているプロセスは見えません。まさに「仮想環境」と呼べる仕組みです。
一方コンテナは、図1.2のように、起動するすべてのプロセスはコンピューター上にインストールされたOS(ホストOS)上で直接起動します。このOSは図1.2のように物理マシン上で実行されていても、仮想マシン上で実行されていても構いません。
通常のプロセスの動作と異なるのは、そのプロセスの一部をグループ化し、他のグループやグループに属していないプロセスから隔離した空間で動作させる点です。貨物輸送のコンテナのように、隔離された空間にプロセスが入っているので、この空間を「コンテナ」と呼ぶわけです。実際の貨物輸送のコンテナのように、あるコンテナの内部から他のコンテナの内部を見ることはできません。
図1.2で「普通に起動したプロセス」と書いたプロセスが、コンテナ化しないで起動したプロセスです。隔離されていない空間で動いているように見えますが、実際はデフォルトの空間で動いています。つまりデフォルトではすべてのプロセスが同じ空間で動いているので、隔離されていないように見えるだけです。
コンテナ内で実行するプロセスは、図1.2のようにひとつでも構いませんし、複数のプロセスを実行しても構いません。
この隔離された空間を作り出すのは、OSのカーネルの機能です。OSを通して使用できるコンピューターのリソースをコンテナごとに隔離して、ホストOS上で直接動作するプロセスや他のコンテナから独立した空間を作り出し、リソースを分割、分配、制限するわけです。
実際にコンテナ化したプロセスを実行する際には、隔離された空間で動作させるだけではなく、さまざまなセキュリティー設定を行ったり、通常起動するプロセスとは違う属性を持たせたりします。つまり、コンテナとはOS上で起動するプロセスに、ある種の属性を持たせたものと考えることもできます。
ここでいう属性とはたとえば、
・他のプロセスから見えなくする(隔離)
・実行できる操作に制限を加える or 特権を与える
・使えるリソースに制限を加える
といったものです。普通に起動したのとはちょっと違うプロセスといったところです。
Linuxカーネルには、この隔離した環境を作り出したり、さまざまな属性を付与するといった機能がたくさん備わっています。
Linuxでコンテナを実行する際は、目的に応じてLinuxカーネルに備わる色々な機能を組み合わせます。決して単一の「コンテナ」という機能が備わっているわけではありません。すべての機能がコンテナ向けの機能というわけではなく、他の用途で使う機能をコンテナでも使用したりします。すなわち、使いたい機能のみを使ってコンテナを作成できるということです。
世の中に存在するDockerやLXCなどのコンテナを実行するための実装は、その実装が目指す動きをするように、あらかじめ必要そうな色々な機能を組み合わせた状態でコンテナを作成します。つまり、Linuxカーネルに備わっているさまざまな機能から必要な機能を選択し、それらを組み合わせてコンテナを作るということです。
ここまで説明したことだけでも、コンテナのメリットが見えてきます。
先に説明したとおり、コンテナはプロセスです。つまり、起動の速度はプロセスとほぼ等しいということになります。
仮想マシンの場合は、仮想的にエミュレートしたハードウェアの起動からOSの起動を経ないといけませんので、使える状態になるまでには少し時間がかかります。
それに対して、プロセスが起動すると使える状態になるコンテナは、起動の速度が求められる場面では適したソリューションになるはずです。
これは仮想マシンと比べたメリットになります。仮想マシンの場合はハードウェアの仮想化が必要ですので、そのためにリソースを割かなければなりません。
さらに、仮想的に実現したハードウェア上でOSが稼働します。ベースとなるホスト環境でもOSが動作していて、同じように動作するOSがいくつも起動していることになりますので、当然オーバーヘッドとなります。
それと比べると、コンテナはプロセスが起動するだけです。通常のプロセスに比べると、コンテナ化するために内部的には余分な処理は必要です。そうはいってもプロセスが起動するだけですので、通常の環境で起動させるプロセスと比べても大きな差がない程度のオーバーヘッドで起動するはずです。
コンテナを起動させない通常のOS上でも、OS上では多数のプロセスが起動しています。コンテナはプロセスですから、コンテナ化したとしてもコンテナ化しない場合と同じ程度の数のプロセス、つまりコンテナを起動させることができるはずです。
仮想マシンの場合、仮想的に実現したハードウェア上でかならずOSを動作させる必要があります。
それに対してコンテナはプロセスですので、要件に必要なアプリケーションのみをコンテナ内で動作させることができます。つまり、必要なプロセスのみを仮想的な環境であるコンテナ内で動作させることができます。
特に、システムとして必要なプロセスが起動しなければならない仮想マシンと比べると、大きなメリットとなります。
これも仮想マシンと比べたメリットになります。仮想マシンの場合、かならず「仮想CPUをいくつ、メモリーをいくつ」という風に固定的な値を割り当てる必要があります。仮想マシン内のプロセスが、特にリソースを必要とするタスクを実行していなくても、割り当てたメモリーなどのリソースは占有したままです1。
通常、プロセスには固定的にリソースを割り当てる必要はありませんので、当然コンテナにも必ず割り当てなければならないリソースはありません。ですので、使われていないリソースは最大限利用できます。