okadato の雑記帳

スタートアップでSREとしてはたらくokadatoの雑記です。

なにもわからない!無知の知からはじめる!コンテナ技術再入門【導入編】

はじめに

ダニング = クルーガー効果をご存知でしょうか。
エンジニア界隈では時折目にする、下記の曲線です (引用元はこちらのツイート

f:id:okadato623:20191020184201j:plain


理解の浅い状況では自分の視座の低さを認識できないため 完全に理解した という過大評価状態に陥ってしまう反面、ある程度経験を積み、視座が高くなることで なんも分からん という過小評価状態に陥ってしまうという認知バイアスの一種です。



コンテナ、完全に理解した。

上記の画像を踏まえたうえで、ぼくはコンテナを完全に理解しています。

前回の記事では ECR + ECS を組合せた自動化の仕組みについて触れました。
業務でも Docker を日常利用していますし、ある程度簡単な内容であれば Dockerfiledocker-compose.yml を一息で書くこともできます。


またコンテナのメリットとしてプロセスの実行環境を隔離できるため、ひとつのホストマシン上で複数のアプリケーションを互いに影響を及ぼすことなく動作可能であると理解しています。


その他にも Docker Hub のようなサービスを使用することで BUILD → SHIP → RUN のフローに乗せ、取得や再配布を容易にできるというメリットもありますよね。それを活用することで開発環境と本番環境の差分をなくし、いわゆるおま環問題への対処も可能です。


さらにOSの kernel を共有しているため、VMと比較すると高速な起動が可能というメリットがある反面、セキュリティレベルは一段下がってしまうというデメリットがあることも当然承知のうえで使用しています。




どうでしょうか?コンテナについていかにも 完全に理解している感 がありませんか??

f:id:okadato623:20191020184235j:plain
完全に理解して図に乗るの図



コンテナ技術入門だぁ〜〜〜?

さて少し前のことですが @hayajo さんが執筆された以下の記事が話題になりました。


ぼくも SRE のはしくれとして、まあコンテナの復習のためにいつかは読んでおくか、と あとで読むリンク集 に積み、そのままにしていました(猛省)


この記事をふと目にし、どれ最近コンテナオーケストレーションに取り組んでばかりだし、たまには基本に立ち返ってみるかぁと思ったのがこのブログを書くきっかけでした。



早速めろめろと読んでいきます。
(見出しをクリックすると、元記事の該当の章を新規タブで開きます。あわせてどうぞ!)



コンテナとは

うんうんわかるわかる、というような内容ですね。

コンテナはホストOSのプロセスのひとつとして動作するので、ホストOSのプロセスリストからコンテナプロセスを確認可能です。また、ホストOSのリソースを共有することから、リソースの隔離・制限が不十分なコンテナがデプロイされた場合、ホストOSや他のコンテナに影響を与える可能性があります。


うんうん、これとかまさにぼくが冒頭で述べた特徴のひとつです。
やはりどうやらぼくはコンテナを完全に理解しているようでした。めでたいめでたい。


コンテナを作ってみよう

いやいや作るも何も、そんなの docker pull すれば終わりでしょ?

最初にコンテナのルートファイルシステムを用意します。Dockerのbashイメージをテンポラリディレクトリに展開し、ここをコンテナのルートファイルシステムとします。


$ ROOTFS=$(mktemp -d)
$ CID=$(sudo docker container create bash)
$ sudo docker container export $CID | tar -x -C $ROOTFS
$ ln -s /usr/local/bin/bash $ROOTFS/bin/bash
$ sudo docker container rm $CID


・・・?



続いてCPU、メモリを制限するグループ(cgroup)を作成します。ここでは仮にCPUを30%、メモリを10MBに制限してみます。


$ UUID=$(uuidgen)
$ sudo cgcreate -t $(id -un):$(id -gn) -a $(id -un):$(id -gn) -g cpu,memory:$UUID
$ cgset -r memory.limit_in_bytes=10000000 $UUID
$ cgset -r cpu.cfs_period_us=1000000 $UUID
$ cgset -r cpu.cfs_quota_us=300000 $UUID



・・・・・・?




それではコンテナを作成します。 次のコマンドはcgroupでCPUとメモリを制限、Namespaceでカーネルリソースを隔離したコンテナを作成し、必要なファイルシステムをマウント、ホスト名を変更、ルートファイルシステムを変更、プロセスの権限を調整した上で /bin/sh を実行します。



$ CMD="/bin/sh"
$ cgexec -g cpu,memory:$UUID \
  unshare -muinpfr /bin/sh -c "
    mount -t proc proc $ROOTFS/proc &&
    touch $ROOTFS$(tty); mount --bind $(tty) $ROOTFS$(tty) &&
    touch $ROOTFS/dev/pts/ptmx; mount --bind /dev/pts/ptmx $ROOTFS/dev/pts/ptmx &&
    ln -sf /dev/pts/ptmx $ROOTFS/dev/ptmx &&
    touch $ROOTFS/dev/null && mount --bind /dev/null $ROOTFS/dev/null &&
    /bin/hostname $UUID &&
    exec capsh --chroot=$ROOTFS --drop=cap_sys_chroot -- -c 'exec $CMD'
   "







お、お前は何を言ってるんだ・・・?









コンテナ ≠ コンテナ技術

そう、話題になった件の記事は「コンテナ入門」ではなく「コンテナ技術入門」でした。

ぼくは「使う側」として最低限理解しておくべき What's コンテナ? は理解しているつもりでしたが、それを支える How コンテナ works? については なにもわからない 状態だったのです。

f:id:okadato623:20191020185905j:plain
なにもわからないことに気づいたの図


Docker をただ使う分にはぼくの今の理解でも問題ないでしょう(ほんとにござるかぁ?
しかしぼくは SRE(Site Reliability Engineer: サイト信頼性エンジニア) です。

コンテナを運用する立場であるにも関わらず、その中身を知らずして信頼性を担保、などとどうして言えようか!!!(自らの首をギュッ♥)



ちょっと脱線

この話の流れで言うのもアレですが、Docker のようにイイ感じに隠蔽されたソフトウェアの内部構造を理解せんとすることは、SRE として非常に重要なマインドセットだと思います。


実際に弊 SRE チームでは「この技術の内部的な仕組みはどうなっているのか?」や「なぜこの技術がデファクトとなったのか?」などかなり深く踏み込んで思考し、議論を交わしています。



障害発生時の最後の防波堤であり、サービスの信頼性に責任を持つのが SRE の責務です。

そんな SRE にとっては「よくわからないけど動いているからOK」という発想はとっても危険。
常に目の前の現象に対する理解の解像度を上げ続けることを意識し、わからないことをキモチワルイと感じる感性がすごく大切だなと日々感じています。

という文脈で、えとみほさん(@etomiho)の以下の記事は心奥震わす、非常に素晴らしい記事だと思います。
どんな仕事に関わる方であれ、ご一読をオススメいたします。

と、大言壮語を吐きましたが、自分はまだまだ 8% 程度しか実践できていません。
ので、自戒の念を込めてそんな考えを吐き出してみました。



さらに脱線

ちなみに SRE チームに限らず、スタディストの開発部にはこの技術的背景を重要視する文化が根付いているなぁとしばしば感じます。

そんな環境に魅力を感じる・ちょっと興味があるカモというそこのアナタ!
いつでもご連絡お待ちしてますよ😊(以下超直球ダイレクトマーケティングの嵐)






閑話休題

話を戻しましょう。
コンテナの要素技術を学ぶ上で、@hayajo さんの記事は非常によくまとまっているし、本当に素晴らしいです!!!

…のですが、ご本人も以下のように言及されているとおり、

駆け足でしたが代表的なコンテナの要素技術を解説しました。

ひとつの記事内で一通りの要素技術を扱うという構成上、個々の要素についてはもう少しだけ解説をいただけると…という箇所があるようにも感じました。



と、いうわけで

次回から数回に分けて記事に従って手を動かし、自分なりの理解をまとめていこうと思います。

対象読者としては

・👆の記事を読みはしたものの、実際に手を動かしてはいない
・コンテナ is ナニ?? ではなく、 コンテナ技術 is ナニ??を知りたい

・コンテナを 完全に理解している という自負がある


方を想定しています。

できる限りわかりやすく・読んでいて楽しく、を心がけますので、わかりにくくてつまらなかったらゴメンナサイです 🙏



環境構築

今回はとりあえず事前準備として、環境構築くらいはしておきましょうか。
記事中では VirtualBox + Vagrant 上で検証なさっていますが、ぼくは使い慣れた EC2 上で作業を行います。

10月20日時点で最新の Ubuntu 18.04 のAMI( ami-0cd744adeca97abb1 )、インスタンスサイズは t2.micro で構築しました。

AWS の操作に慣れていないという方は、以下の記事が参考になるかと思います。
ただし今回は Ubuntu のイメージを使用するので ubuntu ユーザで ssh することに注意!


そのうえで ssh ログイン後、

mkdir container_study
cd container_study


このディレクトリ内に以下のスクリプト setup.sh を作成します。
今後の検証で必要になるパッケージ群を一通りインストールするスクリプトです。


sudo apt update
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common jq
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt update
sudo apt install -y docker-ce cgdb cgroup-tools uuid-runtime tree iputils-ping make gcc
sudo git clone git://git.kernel.org/pub/scm/linux/kernel/git/morgan/libcap.git /usr/src/libcap
cd /usr/src/libcap
sudo make
sudo make install


さあそして bash setup.sh!これで環境構築はおしまいです 🎉



最後に

次回は

・コンテナを作る
・Namespace(名前空間の隔離)
・cgroup(リソースの隔離)


までを扱い、このへんまで

f:id:okadato623:20191020185935j:plain

次々回は

・Capability(権限の隔離)
・Copy On Write(ファイルシステムの部分的共有)
・ネットワークリソースの隔離


までを扱い、このへんまで

f:id:okadato623:20191020185952j:plain


たどり着けたらいいなぁ、という夢をみています(遠い目)
(いざ書き始めたらもう少し記事を細分化するかもしれません…)


というわけで コンテナ技術チョットワカル を目指しているみなさん、
環境構築まで済ませたうえでしばらくお待ちいただけると幸いです。
あ、インスタンスの停止はくれぐれも忘れずに!💸

次回から一緒に頑張っていきましょう!! 💪