本記事では、Alibaba Cloud上に不変のインフラを複数のレイヤーで構築する技術的な実現可能性を紹介し、議論しています。
#序章
従来のクラスタ環境では、エンジニアや管理者はサーバにランダムに ssh 接続してパッケージをインストールしたり、サーバごとに設定を微調整したりしていました。アプリケーションは手動で展開されます。依存関係は偶発的に導入されます。設定は変更され、予測できないほどにドリフトします。人的要因が遅かれ早かれ大惨事になるのは明らかです。
開発、デプロイ、運用の間に不必要に導入される矛盾を減らすために、不変のインフラストラクチャは一連の分離されたスナップショットを構築する傾向がありますが、これは修正されません。そのため、何か問題があればいつでも以前の既知の状態にロールバックすることができます。
クラウドベースのエコシステムでは、整合性⇒移植性⇒自動化⇒拡張性というようにまとめることができるかもしれません。この連鎖の礎となる一貫性を実現するためには、不変のインフラストラクチャが最適なモデルかもしれません。
具体的な技術というよりはパラダイムとして、不変性インフラストラクチャは様々なアプローチと実装が可能です。この記事では、アプリケーションプラットフォーム、ファイルシステム、パッケージ管理、プロビジョナーの4つのレイヤーに分類します。
#不変アプリケーションプラットフォーム
ここでいうアプリケーションプラットフォームとは、JavaやPythonなどの特定のランタイムを指すこともあれば、コンテナ技術のような汎用的なものを指すこともあります。このセクションでは、Dockerの人気と関連性を考慮して、Dockerについてのみ説明します。
不変性はコンテナ技術の大原則であり、一度構築されたDockerイメージは決して変更されません。本当に必要な変更は新しいイメージに組み込まれ、別のイメージレイヤーにカプセル化される必要があります。そのため、すべてのアプリケーション環境を強制的に同じにし、移植性を持たせることができます。
コンテナサービスはAlibaba Cloud上ですぐに使えるようになっているので、自分で設定する必要がありません。ホスティングノードとVPCをオンにした後、データストアのmongoDBやMySQL、nginxやphpなどのウェブプラットフォームなど、一般的によく使われているアプリケーションプラットフォームの束がすでにアプリケーションを実行するために用意されています。
また、Dockerイメージのメタデータを記録したDockerfileを編集することで、いつでも自分の環境をカスタマイズすることができます。
#不変ファイルシステム
Dockerや他のコンテナ技術はアプリケーションのランタイムの不変性を提供しますが、すべてのシナリオをカバーすることはできません。Dockerはファイルシステム層でOverlayFSやUnionFSドライバを使用しますが、これらは永続性がなく、ステートレスで揮発性のあるアプリケーションにしか適していません。これはマイクロサービスベースのアーキテクチャのベストプラクティスの一つと考えられていますが、すべてのアプリケーション、一般的にはレガシーなアプリケーションに最適であるとは考えられません。このセクションでは、Linux ユーザ空間で抽象化された階層として動作する OSTree を紹介します。
OSTree のルートディレクトリの下では、書き込み可能なのは /etc
と /var
だけで、通常は設定やアプリケーションディレクトリにマップされます。今回は、Alibaba Cloud Elastic Compute Service上にCentOS 7.3のインスタンスで実験環境を設定してみますので、簡単な例をステップバイステップで見ていきます。
この例では、/var/demo/demo.py
の下で /etc/demo/demo.conf
から設定を読み込んだアプリケーションが動作していると仮定します。設定の切り替えが本当に有効で、アプリケーション自体に依存しないかどうかをテストしたいと思います。
簡単にするために、この例はできるだけ抽象的なものにします。/var/demo/demo.py
は/etc/demo/demo.conf
から環境名とポートを読み込み、そのポートで動作するhttpサーバを起動して環境名を表示します。
/etc/demo/demo.conf
[main]
env = dev
port = 8000
/var/demo/demo.py:
#!/usr/bin/env python
import SimpleHTTPServer
import SocketServer
import ConfigParser
config = ConfigParser.RawConfigParser()
config.read('/etc/demo/demo.conf')
env = config.get('main', 'env')
port = config.getint('main', 'port')
Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
httpd = SocketServer.TCPServer(("", port), Handler)
print("serving %s at port %d" % (env, port))
httpd.serve_forever()
まず、必要なパッケージをインストールします。
$ yum install -y ostree
そして、新しい ostree リポジトリを初期化します。
$ mkdir -p /etc/demo && cd /etc/demo
$ ostree --repo=.demo init
.demoで始まる.demoを使うのは、ostreeの内部ディレクトリを非表示にしたいからです。
最初のバージョンの demo.conf を新しいブランチ dev にコミットします。
$ ostree --repo=.demo commit --branch=dev ./
8d7cefc677593c16ecd9eada965fb1ac53d6ae96a6af9fef49a22d164a06e6e2
既存のコミットの一覧
$ ostree --repo=.demo refs
dev
ブランチ dev 内のリストディレクトリ
$ ostree --repo=.demo ls dev
d00755 0 0 0 /
-00644 0 0 29 /demo.conf
d00755 0 0 0 /.rdemo
ブランチ dev 内の最初のバージョンの demo.conf の内容を表示し、適切にコミットされていることを確認します。
$ ostree --repo=.demo cat dev /demo.conf
[main]
env = dev
port = 8000
var/demo/demo.py
のテスト
chmod +x /var/demo/demo.py
/var/demo/demo.py
serving dev at port 8000
demo.conf を修正して uat ブランチにコミットします。
$ cat /etc/demo/demo.conf
[main]
env = uat
port = 80
作業ディレクトリをブランチにコミットします。
$ ostree --repo=.demo commit --branch=uat ./
3aca353878a754a887a0308ff5ca6f8ad86057a2175e3f0b194ff51c5e471116
再度 /var/demo/demo.py
を実行します。
/var/demo/demo.py
serving uat at port 80
これで変更が有効になるのがわかりました。コミットされていれば、いつでもファイルの内容を確認することができます。
ブランチdevにdemo.conf
を表示します。
$ ostree --repo=.demo cat dev /demo.conf
[main]
env = dev
port = 8000
ブランチ uat で demo.conf
を表示します。
$ ostree --repo=.demo cat uat /demo.conf
[main]
env = uat
port = 80
OSTree は、基本的に、ある意図的に定義された状態から別の状態へのアトミックな移行力を、1つのシンプルなコマンドで提供します。このパワーにより、オンラインアップグレード中のリスクを排除し、誤って変更されることのないように、すべてのデータを保護します。
下の図は、OSTREE がどのようにファイルシステムを回転させて、配備された設定やアプリケーションを新旧の間で切り替えるかを示しています。
Gitと同様に、OSTreeはレポの設定を指定したディレクトリの下に保存します。アップグレード時には、OSTree は基本的な 3 者間差分を実行し、古いものはそのままにして、ローカルでの変更を新しいコピーに適用します。あらゆる種類のファイルシステムと互換性がありますが、OSTreeはBTRFSのようなカーテンの上にインストールすれば、いくつかのネイティブ機能を利用することができます。
OSTree の構造に興味がある方は、https://ostree.readthedocs.io/en/latest/manual/repo/ をチェックアウトすると、より具体的な詳細を知ることができます。
#パッケージ管理
OSTree は完全なファイルシステムツリーのアトミックなアップグレードを可能にし、そのサブシステムである OSTree-RPM は主により高いレベルでのパッケージ管理機能を提供します。あなたがシステム管理者であれば、オペレーティングシステムのパッチ適用には苦労します。OpenSSLを例にとると、最新のパッチを適用しないと、あなたのインフラは、 heartbleedバグのようなセキュリティ上の問題にさらされる可能性があります。最新のパッチを適用すると、互換性の問題が広範囲に発生する可能性があります。rpm-ostreeでは、ここでパッケージシステムを戻すのは非常に苦痛ではないので、安心してアップグレードしてテストすることができるかもしれません。
テスト用に以前の ECS インスタンスを再利用します。
rpm-ostree はパッケージシステムへの変更をデプロイメントとして表示します。すべてのデプロイメントを表示するには
$ rpm-ostree status
yum update
の新規デプロイを実行する
$ rpm-ostree upgrade
これを有効にするには、OS を再起動するように指示されます。rpm-ostree
はローカルのパッケージシステムを引き継いで常にリセット可能な状態にすることを目的としているので、すべての変更を行うには、init システムとパッケージ管理システムを再ロードするために systemctl reboot
を実行する必要があります。
$ systemctl reboot
続いて、opensslのインストールテストを行います。その前に、現在のopensslのバージョンを表示します。
$ openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013
$ rpm-ostree install openssl
Checking out tree 67d659b... done
Inactive requests:
openssl (already provided by openssl-1:1.0.2k-12.el7.x86_64)
Enabled rpm-md repositories: base updates extras
Updating metadata for 'base': [=============] 100%
rpm-md repo 'base'; generated: 2018-11-25 16:00:34
Updating metadata for 'updates': [=============] 100%
rpm-md repo 'updates'; generated: 2019-01-24 13:56:44
Updating metadata for 'extras': [=============] 100%
rpm-md repo 'extras'; generated: 2018-12-10 16:00:03
Importing metadata [=============] 100%
Resolving dependencies... done
Checking out packages (2/2) [=============] 100%
Running pre scripts... 0 done
Running post scripts... 1 done
Writing rpmdb... done
Writing OSTree commit... done
Copying /etc changes: 22 modified, 8 removed, 41 added
Transaction complete; bootconfig swap: no; deployment count change: 0
Freed: 39.0 MB (pkgcache branches: 2)
Run "systemctl reboot" to start a reboot
$ openssl version
OpenSSL 1.0.2k-fips 26 Jan 2017
前の状態にロールバックし、wget が削除されたかどうかをテストします。
$ rpm-ostree rollback
Moving '67d659bc257b7d47f638f9d7d2146401b85eec7c7eef0122196d72c70553ae66.0' to be first deployment
Transaction complete; bootconfig swap: no; deployment count change: 0
Removed:
openssl-1.0.2k-12.el7.x86_64
Run "systemctl reboot" to start a reboot
$ systemctl reboot
$ openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013
#不変のプロビジョナー
一般的にプロビジョナーとは、ハイパーバイザープール内のホスト、OpenStackではコンピュートノード、Kubernetesではワーカーノードを指す場合があります。これらは、インフラストラクチャの下のインフラストラクチャ、ホストのホスト、またはマシンを構築するマシンのようなものです。
理想的には、このプロビジョナーはプラットフォームとして不変のインフラを提供し、その間に不変であることができる。ここでは、Fedora/CentOS/RHELプラットフォームで一般的に利用されているAtomic Hostを参照することができ、計算リソースがec2インスタンス、ハイパーバイザー、ベアメタルマシンのいずれの種類であっても利用可能です。Atomic Host はまた、システムサービスを runC でコンテナ化したり、ファイルやパッケージシステムのバージョン管理に ostree を使用したり、コンテナホストに分離とセキュリティを提供する異なるカーネルの名前空間を有効にしたりすることで、それ自身を不変のものにしています。
システムサービスは通常、明らかにコンテナ技術の原則に従うホストに直接アクセスする必要があるという疑問があるかもしれません。だからどのようにそれらをコンテナ化することができます。それは、アトミックホストがホストシステムにアクセスしたり変更したりするための特別な権限を持つスーパー特権コンテナを導入しているからです。そして、これらのシステムコンテナは、システムサービスの代わりにインストールステップでsystemdによって管理されるように設定されており、システムが起動したときに自動的に起動することができます。
基本的なsyslogプロトコルをより強力なフィルタリング機能と柔軟な設定で拡張したrsyslogの例を見てみましょう。
$ atomic install registry.access.redhat.com/rhel7/rsyslog
Pulling registry.access.redhat.com/rhel7/rsyslog:latest ...
…
Creating directory at /host//etc/pki/rsyslog
Installing file at /host//etc/rsyslog.conf
Installing file at /host//etc/sysconfig/rsyslog
Installing file at /host//etc/logrotate.d/syslog
$ atomic run registry.access.redhat.com/rhel7/rsyslog
docker run -d --privileged --name rsyslog --net=host --pid=host -v
/etc/pki/rsyslog:/etc/pki/rsyslog ...
Alibaba ECSでAtomic Hostを使用するには、CentOS Atomic HostまたはFedora Atomic Hostでqcow2版のimageをダウンロードし、カスタムイメージのインポートに従ってオンプレミスのイメージファイルをECS環境にインポートしてECSインスタンスを作成したり、システムディスクを変更したりすることができます。
#概要
今回の記事では、Docker、ostree、ostree-rpm、Atomic Hostを利用して、アプリケーションプラットフォームやファイルシステムからパッケージ管理、プロビジョナーまで、不変のインフラ層をAlibaba Cloud上に構築し、すべてを一貫性のあるものにすることで、システムの複雑さや人的要因のリスクを下げることができることを簡単にご紹介しました。
アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ