Rのつく財団入り口

ITエンジニア関連の様々な話題を書いているはずのブログです。

【感想】『Docker/Kubernetes 実践コンテナ開発入門』:じっくりコンテナ入門できる本

Docker/Kubernetes 実践コンテナ開発入門

 さて近年のエンジニアリングで外すことのできない重要要素、ITエンジニアが学んだ方が良いと上げられる技術要素で必ず出てくるDocker/Kubernetes周り。
 僕も前から知ってはいたのですが仕事ではマシンやネットワークの制約からなかなか使えず、またなくても別に困っていないことからいまいち深掘りできていませんでした。Kubernetesについても恥ずかしながら「K8sを使うと何が嬉しいのか」というあたりがいまいち理解できていませんでした。
 ということでコンテナについてもっと知っておこうと読んだのが本書。著者はサイバーエージェントでご活躍中の山田 明憲さん。(@stormcat24)。日本語で読めるコンテナ関連の本はかなり数も出ていますが、各所で高く評価された2018年刊行のDocker本の定番本です。

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

1.Dockerの基礎

f:id:iwasiman:20210327182458p:plain
Docker/Kubernetes実践コンテナ開発入門

1.1 Dockerとは

 Dockerとはコンテナ型仮想技術を実現するための常駐アプリケーションdockeredとそれを操作するCLIからなるプロダクトである……と定義して、歴史や概念を含め、根本の部分を解説しています。

1.2 Dockerを利用する意義
  • 実行環境が変わらないので冪等性を確保。 Immutable Infrastructure
  • 実行環境の構築とアプリケーション構成の両方をコード化できる。IaC: Infrastructure as Code
  • 同じく、実行環境とアプリの両方が同じ箱に入るのでポータビリティが増す。(=どんなプラットフォームでも同じように動く)
  • アプリケーションとミドルウェアの構成管理がしやすくなる

という話を核に、意義を説明しています。コンテナが複数ある時のアプリケーション管理をしやすくするツールとして Docker Compose、さらにこのDockerが入ったサーバー(Dockerノード)が複数ある時にうまく管理できるコンテナオーケストレーションのツールとしてDocker Swarm、さらにこのコンテナオーケストレーションの高機能なものとしてKubernetesもここで登場します。

1.3 ローカルDocker環境を構築する

スクショも付随情報も豊富に解説されています。

 前書きで作者さんが本書のテーマとして「コンテナで開発・運用していくための基本的な考え方を養えるようにする」を定義しています。それだけに様々な周辺情報もあり、元から日本語の本なので読みやすいです。
 コラムも豊富でDockerの苦手分野、最初のころはLinuxコンテナを使っていた話、Docker for Windowsに苦戦したらDocker Toolboxがよい……などお役立ち情報も色々入っています。情報量が多いのはありがたいですね。

docs.docker.jp docs.docker.com

2.Dockerコンテナのデプロイ

2.1 コンテナでアプリケーションを実行する
  • Dockerコンテナ用のファイルシステムや実行するアプリケーション、設定をまとめたテンプレートが「Dockerイメージ」。
  • Dockerイメージを元に実体が生成され、ファイルシステムとアプリケーションが動いている状態が「Dockerコンテナ」。

と定義して、簡単な例で一連の流れをまずは解説しています。例に出てくるのがGo言語でHTTPサーバーになるコードで、さすがコンテナといえばGo-lang登場、尖ってるな!と思いました。
コード+設定を描いたDockerfileを入力に > docker image build でDockerイメージが生成され、イメージ名を入力に > docker container run でDockerコンテナが実行されるわけですね。

2.2 Dockerイメージの操作

 各操作の説明。既存イメージの検索もコマンドからやるあたりはよく知りませんでした。

  • 世の中の人がイメージを登録できる「Dockerレジストリ」があり、その代表がDocker Hub
  • GitHubへのpushと同じように > docker image push したり。
  • Dockerレジストリからダウンロードしてイメージをビルドする際は
    > docker image build -pull=true ...
2.3 Dockerコンテナの操作

 今度はDockerコンテナの方の操作で、> docker container run を始めとする各種コマンドが丁寧に解説されています。箱の外側からアクセスする際のポート番号と箱の中のポート番号の動きを対応させるポートフォワーディングも出てきます。

2.4 運用管理向けコマンド

 ローカル上のディスクを消費していくイメージやコンテナを一括削除するのが > docker container prune 。フルーツのプルーン?と思ったら英語のpruneには「刈り取る」などの意味もあるのでした。

 そして単独のコンテナならこれらのコマンド群で十分。しかしAPサーバーやDBなど複数のものからなるシステムを再現するため、複数のコンテナを扱うにはどうするのか...?ということで、docker-compose.yml に設定を書いてから同じディレクトリで > docker-compose up-d などして動かしていく Docker Composeが登場します。
 読んでいる時にちょうど触っていたJenkinsが例に出てきて分かりやすかったです。本格的にやる場合はJenkinsサーバーと違うマシン上に入れたりするSlaveの分も、Docker Composeを使えば一緒に制御できる訳ですね。

 注釈も多く、コラムで操作対象を明確にするために長い方のdockerコマンドが今は推奨されている話もあったりして参考になります。

3.実用的なコンテナの構築とデプロイ

3.1 アプリケーションとコンテナの粒度

「Dockerの1コンテナ=マシン上の1プロセス」を厳密に守った方が良いのではと初期のDocker界隈では議論されていたが、現在ではこれは不適切。
「1コンテナ=1つの関心事(only one concern)」、1つの役割や1つの問題領域猟奇に関わるが良いとのこと。Webサーバーのリバースプロキシで1つ、アプリで1つ、RDBで1つとか、Jenkins本体で1つでスレーブに1つづつということですね。

 この関心事(concern)という表現はクラス設計やアーキテクチャ設計の話でもよく出てきます。

3.2 コンテナのポータビリティ

 どんなプラットフォームでも動くポータビリティがDockerの大きな長所だが、万能ではないよという話。
特にアプリにライブラリの実体を一緒に組み込むスタティックリンクでなく、実行する際にリンクするダイナミックリンクで問題が起きることがあるそうです。

3.3 Dockerフレンドリなアプリケーション

コンテナの外側からどう設定を渡すかの話。

  • dockerコマンドの引数として渡す
  • 設定ファイルとして渡す
  • アプリケーションの挙動を環境変数で渡す ←オススメ
  • 設定ファイルに環境変数を埋め込む。例えばJavaSpring Frameworksome.hoge=${ENV_HOGE} のように埋め込める

 この中で環境変数で渡す方式を本書では推奨し、詳しく述べています。KubernetesAmazon ECSが例に挙げられていますが、AWSの他のサービスでもよく見るやり方なのでやっぱりそうなんだなあと。

3.4 永続化データをどう扱うか

 例えばMySQLを実行するコンテナがあって、コンテナ再起動後も同じデータを使いたい時に場所を指定して実現するData Volumeの使い方。

3.5 コンテナ配置戦略

 主にDockerホストが1つで複数のコンテナの時に使うcompose、そしてDockerホストが複数の時に使うSwarmの話。

  • Docker in Docker(dind)といってDockerコンテナ自体をDockerホストにしてその中に入れ子が作れる
  • 複数のコンテナを集めた単位で制御できる Service という概念がある
  • 複数のServiceを集めた単位でStackという概念がある。同じStack内のServiceはoverlayネットワークに属するので、コンテナ間通信が可能。

 2018-2019年頃の進化で、Docker Swarmを使うぐらいならもう上位互換のKubernetesへ……と世の中的にはなっていきましたが、歴史を辿る上でも為になります。

4.Swarmによる実践的なアプリケーション構築

docs.docker.jp

2018年頃の進化でオーケストレーションシステムの主流はKubernetesに変わったことを認めつつ、基礎技術を生かして手早い構築を一度学習するということでアプリケーションの構築です。フロントエンド入門の記事にもよく出てくるTODOアプリを作っていきます。

  • DBはMySQLでmasterとslave
  • APIサーバーとしてバックエンドはGo言語
  • フロントエンドはVue.jsベースのNuxt.js
  • そしてプロキシサーバーとしてnginxもフロント、バックエンドをそれぞれ別のService

として作っていきます。アーキテクチャ図と表があるのですが、frontendがあるのならapp*のところの名前にbackendを入れるとぱっと見もっと分かりやすいかな?とも思いました。
 MySQL用のコンテナも同一シェルの中で環境変数で分岐してmasterとslave両方用意、docker container execコマンドの中にまたdocker container execの多段構成(!)……などなど高等テクニックがいろいろ。

 コラムでやはり環境変数の活用が重要とあります。最終的にはlocalhostの上でWebアプリケーションとして動いてしまい、1つのマシン内でDockerコンテナを駆使するとこれができちゃうのか……!と感じました。

5.Kubernetes入門

5.1 Kubernetesとは

 Docker Swarmを体感した後で、2017年秋にDockerがKubernetesを統合・サポートすることを発表してからメインとなったKubernetesについて。最初に歴史と位置づけが語られているのがありがたいです。
 なお発音しにくい名前は本書では「クーベネティス」「クーベルネティス」「クバネテス」「ケーハチエス」「クベ」を上げています。

kubernetes.io

5.2 ローカル環境でKubernetesを実行する

Docker for Windows/Mac の管理画面からK8sをインストールし、コマンドラインツールのkubectlをインストール、kubectlコマンドを使ってダッシュボードのインストール方法が解説されています。ctrlではなくてctlですね。
 以前はこのローカル環境構築にはMinikubeが使われてきており、Windows10以外のWindowsでは今でもこの選択肢だそうです。

5.3 Kubernetesの概念
  • リソースK8sによるアプリケーションのデプロイを構成する部品のような構成要素で、これらが協調して動作していく。文脈によって語意がいろいろあるリソースとは意味が違う。
5.4 KubernetesクラスタとNode
5.5 Namespace
5.6 Pod
  • Pod:1~N個のコンテナを中に持ったコンテナの集合体の単位。
  • 1つのPodは1つのNode(≒マシン)の中に配置される。レプリカとして複数のNodeの中に同じPodが配置されてもよい。1つのPodが複数のNodeをまたぐのはできない。
  • Podをデプロイするときは設定をYAML形式のマニフェストファイルにその中のコンテナはこのimageだ...という設定を書き、
    > kubectl apply -f simple-pod.yaml のようにして実行。
5.7 ReplicaSet
  • ReplicaSet:同一のPodを複数実行する時に使う概念。
  • Podと同じようにYAML形式の設定ファイルを書く。kind: ReplicaSetreplicas: {数} となっている以外はPodリソースとだいたい同じ。同じくkubectlコマンドで実行。
5.8 Deployment
  • ReplicaSetを管理・操作するための上位の概念。DeploymentReplicaSetを作り、ReplicaSetPod群を作り……となる。
  • 同じようにYAML形式の設定ファイルでkind: Deploymentとして同じようなことを書いてkubectlコマンドで実行。
  • 配下のPod数の設定を変えてもReplicaSetは変わらず、コンテナの定義を変えるとPodが止まって新しいものが生まれ、Deploymentのリビジョンも変わる。
5.9 Service
  • ServicePodの集合に経路やサービスディスカバリを提供するための概念。
  • ReplicaSetの定義時に振り分けラベルのような設定を書いてkubectl apply。その後ラベルがこの値のものだけ適用……のような設定を書いてkubectl apply。するとその条件を満たすPodにだけトラフィックが届く。
  • WebサービスのserviceやMircoservicesのserviceやレイヤードアーキテクチャのservice層などとはまったく意味が違う....
5.10 Ingress
  • IngressKubernetesクラスタの外へのServiceの公開、HTTPルーティングを行うリソースの概念。使う時はやはり設定を書いてkubectl applyする。HTTPで繋ぐWebアプリケーションのようなサービス公開時は必ず使う。

 実際にDockerコマンドを打っての構築のイメージが湧いた後で見ていくと以前より理解しやすいです。改めてK8sは用語の概念が独特ですね……

6.Kubernetesのデプロイ・クラスタ構築

cloud.google.com

 今度は本格的に、マネージドなK8sサービスではもっとも使われているというGCPGKE(Google Kubernetes Engine)K8sを使っていく章。

  • ブラウザ上からプロジェクト作成。
  • AWS CLIに相当するGoogle Cloud SDK(gcloud)を準備して、>gcloud container clusters ... のコマンドで操作できる状態をセットアップ
  • ブラウザからK8sクラスタを新規作成
  • gcloudからkubectlに認証情報を渡し、以降は5章と同じように> kubectl apply -f filename.yml で...

という感じで、4章でDocker Swarmを使って作ったTODOアプリ一式をGKEに上げていきます。設定ファイルの中身は色々書くものの、メインの操作はkubectlコマンドで同じでした。詳細はなかなか難しいのですがイメージが湧きました。
 最後にはオンプレ上でK8s環境を作る方法として、構成管理ツールのAnsibleを使ってK8sクラスタを一式作ってくれるツール、kubesparyの使い方も紹介されています。30分ぐらいで構築できるそうです。

github.com

7.Kubernetesの発展的な利用

K8sの応用編の章。

7.1 Kubernetesの様々なリソース
  • Job: 1つ以上のPodを作成し、正常に完了するまでを管理してくれるリソース。Webアプリでなく計算やバッチのアプリ向け。実行は一度きり。
  • CronJob: scheduleキーでcron式を書くと、Jobを定期的に実行してくれるリソース。
  • Secret: 機密情報の文字列をBase64エンコードで暗号化したまま扱えるリソース。
7.2 ユーザー管理とRole-Based Access Control (RBAC)
  • 認証ユーザー:Kubernetesクラスタ外から人間が操作するためのユーザーで認証方法が各種ある。複数ユーザがまとまってグループになる
  • ServiceAccountK8s内で管理され、PodKubernetesAPIを呼ぶときのユーザー。
  • Role-Based Access Control(RBAC): 操作可能なAPIを定めたロールとユーザー・グループ・ServiceAccountを紐付て行うK8sの権限管理。
7.3 Helm
  • 開発/本番/負荷テスト用など、環境が複数ある時はKubernetesクラスタも複数必要になる。これを解決する技術。
  • Chart: 設定済みのK8sリソースのパッケージ。マニフェストファイルの原料が、Helmリポジトリに環境ごとに入っている。
  • Helm: このChartを管理するツール。インストールするとコマンドから > helm args... で各種操作を行う。
7.4 Kubernetesにおけるデプロイ戦略
  • Deploymentリソースのデフォルト設定がRollingUpdate。観測するとPodが1つづつ順番に入れ替わって新しいバージョンになる。
  • マニフェストファイルで数の細かい設定ができたり、ヘルスチェックができたりする。

  • オプション設定でなくDeploymentリソースを2つ用意して行うのが、BlueGreen DeploymentAWSなどでよく出てくるやつ。

  • 新しいバージョンのデプロイが終わったら、Serviceリソースのマニフェストファイルのラベルをコマンドで更新すると、ネットワークの流れる先が新しい方のDeploymentに切り替わる……というデプロイが実現できる。

 巻末のコラムで複数サービスの基盤のレベルで各種課題を解決できるサービスメッシュの候補として、LinkeredIstioが紹介されています。本書の作者さんによると今後はIstioが最有力ということで、このへんの感触は書籍『マイクロサービスパターン』での記述と同じですね。

iwasiman.hatenablog.com

istio.io

8.コンテナの運用

 コンテナを用いた開発を一通り見てきたので、今度はログ管理や障害対策といった運用の章。

  • 普通のアプリでは物理ファイルにロギングするが、Dockerコンテナは異常終了してコンテナが物理的に削除されてしまうとログも消失してしまう。なのでコンテナ内では標準出力し、ホスト側で受け取ってログ出力する仕組みが一般的。
  • Dockerにはlogging driverがデフォルトで入っており、JSONで出力したりAWS/GCP/Fluentdと連携できる。
  • コンテナが多くなってきて大変になると、ログコレクタであるFluentdで収集、格納するデータストアにElasticsearch、そして可視化ツールのKibanaで閲覧する方法が定番。
  • Dockerホストが複数ある場合は各ホストにfluentdをエージェントとして配置する方法を推奨。
  • Kubernetesの場合もだいたい同じ。すべてのNodeに必ず1つ配置されるPodを管理するリソース、DaemonSetという概念があるのでこれを使うと良い。
  • その他のツールとして、AWS/GCP用のマネージドサービス Google Stackdriver がある。
  • Kubernetesではログ閲覧をサポートするツール stern がある。

www.fluentd.org www.elastic.co www.elastic.co

  • Dockerコンテナを動かすとdockerdというプロセスが常駐するが、これが停止してもコンテナを動かし続ける「ライブラリストア」という仕組みがある。
  • Dockerの障害では動かすコンテナを間違えたりイメージを間違えて上書きしてしまったりがある。これを防ぐイメージのテストを行うツールとして container-structure-test というツールがある。
  • 本番マシンのディスク容量の枯渇にも注意。> docker system prune -aで一括削除するとよい。

  • KubernetesにはAuto-Healingという機能があり、NodeがダウンするとそこにあったPodの分だけ別の所で再配置してくれる。が、リソースが空いているNodeを自動で選んでいくため必ずしも望んだNodeに再配置してくれない場合もある。Pod再配置のルールをマニフェストファイルに定義できるPod AntiAffinityという仕組みがある。

  • CPUリソースをたくさん消費する大食いなPodだけを専用Nodeに隔離する、Node Affinityという仕組みがある。
  • Podのリソース使用率に応じてPod数を自動増減するKubernetesリソースが、Horizonal Pod Autoscaler(HPA)
  • Nodeの数を自動調整してくれるツールが、Cluster Autoscaler

 オートヒーリングというネーミングがHPが緑色まで自動回復しそうで楽しいです。(RPG脳) クラウドを学んでいると出てくるところですが、K8sにもいろいろな機能があるのだなあというところ。

9.より軽量なDockerイメージを作る

 本書に出てきたサンプルを含めDockerイメージは普通に作ると数100MB~1GBぐらい。このイメージサイズが大きいとイメージのビルド、Docker Registryへの登録やダウンロード、実行、ディスク消費などなど諸々で積み重なって弊害が出てくるそうです。どれだけ軽量なコンテナを作れるのか……という実践的な追求をしていく章。

  • scratch:Dockerが予約している特殊なイメージ。すべてのイメージの始祖。機能を削って極限までサイズを落としており、簡単なGo言語アプリを一緒にしたコンテナを作っても1-2MB程度。シェルも使えず、利用可能なユースケースはかなり限定的。
  • BusyBox:組み込み系でよく使われるLinuxディストリビューションでこちらは約1MB強。ユーティリティをハードリンクで組み込んでサイズを減らしている。最低限の機能はあるがパッケージマネージャーがない。
  • Alpine LinuxBusyBoxをベースにしたパワーユーザー向けディストリビューションで約4MB弱。パッケージマネージャーのapkが便利。これが使える。

 scratchのことを「始祖」と表現していたのが面白かったのですが、極限までいくといろいろあるのですねえ。続いて、自分たちでDockerイメージを作る際の軽量化についてもテクニックを述べています。

  • アプリのサイズ軽減:不要ファイルや外部ライブラリ軽減、画像サイズ軽減など。gitにコードがあるなら.dockerignoreファイルを置いて不要ファイルが入らないようにすると良い。
  • Dockerイメージのレイヤー構造を意識する:Dockerfileに記述されたStepを1行づつ実行するごとに「レイヤー」が生成され、レイヤーの分だけtar.gzファイルができる。レイヤーの数を減らしたり、1行でまとめて実行するとその分減る。ただしやりすぎると分かりにくくなる。
  • multi-stage builds:Docker17系より導入された仕組み。ビルドした成果物を置くコンテナと、デプロイして実行するコンテナを別にして軽くできる。DockerfileにFROMをビルド用コンテナと実行コンテナ用で2か所書く。有用。
  • distrolessGoogleが公開しているOSを含まず言語にフォーカスした軽量イメージ。16MBぐらいでこちらも軽量。

 本書はコマンド実行時のスクリーンショット類が豊富に掲載されていますが、それを踏まえると確かにレイヤー構造っぽい標準出力されてたな……!とイメージが湧きます。
 またGo言語を語る際によく言語仕様の本体と基本ライブラリがコンパクトにまとまってコンテナ用途向け、などと語られますが、本章を読むとなるほどなあと分かります。同じイメージを元にしたコンテナが大量にデプロイされるような環境では、サイズの軽さが重要になる訳ですね。

10.Dockerの様々な活用方法

 最後は応用的な使い方として、Dockerの活用法の章です。

  • DB系とかDB接続ツールなど、プロジェクトで使うソフトウェア・ツールをDockerイメージで配布すると、プロジェクトメンバ内で統一が図れてGood。
  • コラムとしてDockerは完全にはVagrantの代替にはならないので必ずしも移行は不要という話。
  • Dockerの中にCLIツールを入れると、ホストへのインストールなしで使える。
  • CLIツールのバージョンごとに別のDockerコンテナに入れておくと便利。
  • OSのバージョンによって動きが違ったりするシェルの動作確認もコンテナ内でやるという手がある。
  • Dockerのコンテナを複製する方法で高負荷限界を上げられるので、アプリケーションの負荷テストに使うのも効果的。本書ではPython製のLocustを紹介。

locust.io

 アプリ本体やミドルウェアに目が行っていたのでコマンドラインツールをDocker内で配布というのは思いつきませんでした……確かに言われてみると色々な使い道がありそうです。

 そして最後は補足として、安全なDockerイメージの見分け方や作り方などセキュリティ関係、自前でDockerレジストリを作る方法、クラウド上での活用としてAWS Fargateを用いたAWS ECSでのコンテナ活用、コマンドまとめなどこれまた豊富なAppendixが並んでいます。本書で得た知識を元にECS/Fargate周りはもうちょっと理解を深めていきたいと思いました。

aws.amazon.com

まとめ:Docker/Kubernetesの基礎から実践まで理解できる本

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

 自分の場合は全体を掴むのが目的だったのですべてのコマンドを実際に叩いて……とはいかなかったのですが、だいぶ理解の解像度が上がりました。概念から基本の使い方、応用、運用や高度な利用まで踏み込んで一通り網羅してたくさんの情報が詰まっています。各所の感想にもある通りでコラムが豊富で、現場で役に立つ知見がたくさん書いてあります。
 物理本は424ページだそうですが、コマンドの図解や実行結果のスクショも豊富なこと、章立てで内容が細分化されていることから、長くて力尽きそうになる印象はなかったですね。
 コンテナ技術の根幹としてLinux周りのことも出てくるのでそのあたりがある程度分かること、Go言語の知識は不要ですが開発をある程度一通り分かっている人が主なターゲットかと思います。ですのでコンテナは初心者でもエンジニアとしては完全初心者よりもちょっと上の人が対象でしょうか。

 さすがに評判の良い本は中もしっかりしている印象でした。Docker関連というとまず名前が挙がる良本ですが、発売の2018年から若干経った2021年現在でも十分役に立つことと思います。

f:id:iwasiman:20210327182458p:plain
Docker/Kubernetes実践コンテナ開発入門

関連リンク

 著者の方自らによる、出版までの壮絶な道のりが分かる記事。表紙がポップ寄りなのは意図があるのですね。その後加筆するとしたらどうするかの話も読めます。

blog.stormcat.io blog.stormcat.io

 各所のブログでもかなりの高評価の印象です。