あさのひとりごと

3日坊主にならないように、全力を尽くします。 記事は個人のひとりごとです。所属する組織の意見を代表するほど、仕事熱心じゃないです。

Kubernetesで実際のメモリを超えるコンテナアプリを動かすと、どうなるか?

Kubernetesは、コンテナアプリケーションをデプロイするためのオーケストレーションツールです。Kuberenetesは分散環境におけるスケーラブルなコンテナ実行環境をつくるための、さまざまな機能が提供されています。

もともとはGoogleが開発したBorgをもとにOSS化したものですが、今日ではマイクロソフトや(ry

Kubernetesをつかうとステートレスでマイクロサービス的なアプリケーションを1日に何度もデプロイでき、スパイクアクセスがきても水平スケールが容易なので、大規模Webシステムでスケーラブルな基盤を作れる、というのは広く知られています。

一方、Kubernetesには「Resource Requests」という機能があり、これはPodをデプロイする時に必要とするリソース(CPU/メモリ)を指定できるものです。これにより、Kubernetesクラスタのリソースの使用率を高め、効率よくアプリを稼働させることができるのも、うれしさの一つです。

Kubernetesではコンテナが使用するコンピューティングリソースを明示的に要求したり制限をかけたりすることができます。

リソース要求(Resource Request)
  • コンテナに対して指定したリソースが使えるように保証するしくみ
  • アプリを動かすのに最低限必要なリソースを指定
リソース制限(Resurce Limits)
  • コンテナのリソース使用量の上限を決めるしくみ
  • アプリケーションが使用する可能性のある最大リソースを指定

ということで、、、まずはメモリのリソース要求(Resource Request)ついて、

  • コンテナアプリが使うメモリを明示的に要求するとクラスタ上でどういう動きをするのか?
  • メモリをたくさん使う別アプリが邪魔してきたとき、きちんとシステムは動作するのか?

が気になり、簡単な検証をしてみました。

前提読者

  • Dockerの使い方が分かる人
  • クラウドマネージド(AKSまたはGKE)でKubernetesクラスタが構築できる人
  • Kubernetesの基本コマンドを知っている人

実験環境

AzureのKubernetesのフルマネージドサービスであるAKSを使います。公式サイトをもとにKubernetesクラスタを構築しました。

クイックスタート - Linux 用 Azure Kubernetes クラスター | Microsoft Docs

ここでは、次の構成のクラスタで実験します。 f:id:dr_asa:20180410130928p:plain:w500

MasterもNodeもkubernetes1.9.6で検証しています。

コンテナアプリが使うメモリを明示的に要求するとクラスタ上でどういう動きをするのか?

Kubernetesがシステムで使用するメモリ量を確認

今回のクラスタはAzure VMの「Standard_D1_v2」をNodeにつかっています。Standard_D1_v2は1台当たり約3.3GBのメモリが使えます。

念のため、次のコマンドで確認します。

$ kubectl describe node

Name:               aks-nodepool1-12354740-1
Roles:              agent
Labels:             agentpool=nodepool1
~中略~
Capacity:
 memory:                          3501592Ki
Allocatable:
 memory:                          3399192Ki

~中略~
Name:               aks-nodepool1-12354740-2
Roles:              agent
Labels:             agentpool=nodepool1
Capacity:
 memory:                          3501592Ki
Allocatable:
 memory:                          3399192Ki

Node上ではkube-proxy等も動作しているため、Podを1つも動作させていない状態でも、223M/499Mのメモリを使用しています。

$ kubectl top node
NAME                       CPU(cores)   CPU%      MEMORY(bytes)   MEMORY%
aks-nodepool1-12354740-1   28m          2%        223Mi           6%
aks-nodepool1-12354740-2   128m         12%       499Mi           15%

アプリを動かすのに必要なメモリを要求してPodを動かす

まずは、Resource Requestの動きを確認します。コンテナに制限をかけるには[resources:]-[requests:]で値を指定します。 次の例(resource-pod.yaml)は、「nginx」の公式イメージをもとに動かしたコンテナが1つ含まれるPodをテンプレートとして定義し、それを「replicas: 2」つまり2つのPodがKubernetesクラスタ上にスケジューリングされる定義になります。

その際、メモリのリソース要求として[memory: 1.5Gi]を指定します。つまりこれはKubernetes に対して「このPodにあるNginxコンテナは1.5Giのメモリ必要だからよろしく!」を宣言しています。

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: reqest-exp
spec:
  replicas: 2
  selector: 
      matchLabels:
        app: replicaset-temp
  template:
    metadata:
      labels:
        app: replicaset-temp
    spec:
      containers:
      - name: pod-sample
        image: nginx
        resources:
          requests:
            memory: 1.5Gi

これを次のコマンドを実行してクラスタ上でリソースを作ります。

$ kubectl create -f resource-pod.yaml
replicaset.apps "reqest-exp" created

PodがそれぞれNode1とNode2で1つずつ動いています。

$ kubectl get po -o wide
NAME               READY     STATUS    RESTARTS   AGE       IP             NODE
reqest-exp-9kztw   1/1       Running   0          55s       10.244.2.153   aks-nodepool1-12354740-2
reqest-exp-b4t8h   1/1       Running   0          55s       10.244.3.184   aks-nodepool1-12354740-1

絵で描くとこんな感じ。

f:id:dr_asa:20180410112208p:plain:w600

ただ、起動しているNginxは何も仕事をしていないので、実際のメモリ使用量はほとんど増えていません。

$ kubectl top node
NAME                       CPU(cores)   CPU%      MEMORY(bytes)   MEMORY%
aks-nodepool1-12354740-1   33m          3%        251Mi           7%
aks-nodepool1-12354740-2   133m         13%       505Mi           15%

要求されたメモリが実際のメモリを超えるとどうなるか

次のコマンドを実行して、Podのレプリカ数を2から3に増やします。どうなるかをみてみます。

$ kubectl scale --replicas=3 -f resource-pod.yaml
replicaset.apps "reqest-exp" scaled

Node1に2つのPod、Node2に1つのPodが動いています。まだ定義ファイルで要求した総メモリ量は、実際のクラスタのメモリ量を超えていません。

$ kubectl get pod -o wide
NAME               READY     STATUS    RESTARTS   AGE       IP             NODE
reqest-exp-9kztw   1/1       Running   0          8m        10.244.2.153   aks-nodepool1-12354740-2
reqest-exp-b4t8h   1/1       Running   0          8m        10.244.3.184   aks-nodepool1-12354740-1
reqest-exp-wzg7t   1/1       Running   0          43s       10.244.3.185   aks-nodepool1-12354740-1

絵で描くとこんな感じです。

f:id:dr_asa:20180410112253p:plain:w600

次はレプリカ数を3から4に変更します。

$ kubectl scale --replicas=4 -f resource-pod.yaml
replicaset.apps "reqest-exp" scaled

4つ目のPodである「reqest-exp-fgrpn」はどこのNodeでも起動せず「Pending」になっているのが分かります。

$ kubectl get pod -o wide
NAME               READY     STATUS    RESTARTS   AGE       IP             NODE
reqest-exp-9kztw   1/1       Running   0          13m       10.244.2.153   aks-nodepool1-12354740-2
reqest-exp-b4t8h   1/1       Running   0          13m       10.244.3.184   aks-nodepool1-12354740-1
reqest-exp-fgrpn   0/1       Pending   0          12s       <none>         <none>
reqest-exp-wzg7t   1/1       Running   0          5m        10.244.3.185   aks-nodepool1-12354740-1

またまた絵で描くとこんな感じ。

f:id:dr_asa:20180410112324p:plain:w600

しつこいですが、実際のメモリ使用量はほとんど増えていません。つまり実際はアイドル状態なのに4つ目のPodはスケジューリングされていないということをいっています。これは、4つ目のPodを配置しようとしても、メモリの空きがマニュフェストで定義した「1.5GB」を確実に確保できるスペースがNode上にないからこのような状態になっています。

$ kubectl top node
NAME                       CPU(cores)   CPU%      MEMORY(bytes)   MEMORY%
aks-nodepool1-12354740-1   30m          3%        259Mi           7%
aks-nodepool1-12354740-2   133m         13%       507Mi           15%

なぜ、4つ目のPodはスケジューリングされなかったのか

KubernetesのResource Requestsは、Podに対して指定したリソースが使えるように保証する仕組みです。

この値はPodをNodeにスケジューリングするときに使われます。Resource Requestsを指定したコンテナを含むPodを作成すると、KubernetesのスケジューラーはNodeを選択して実行します。その際、各Nodeには、各リソースタイプの最大容量をもっていてスケジューラは、各リソースタイプに対して、スケジュールされたコンテナのリソース要求の合計がノードの容量よりも少ないことをチェックします。たとえ実際のメモリ使用率は非常に低くても、スケジューラはチェックが失敗した場合はNodeにPodを配置しません。

Managing Compute Resources for Containers | Kubernetes

なおこのResource Requestsの設定はPodではなく、コンテナに対して行われます。今回指定したメモリはバイト単位で設定でき、E/P/T/G/M/K(Ei/Pi/Ti/Gi/Mi/Ki)のいずれかを使用して、メモリを整数または固定小数点整数として指定します。

今回のケースでは、実際のメモリがたくさん空いているにもかかわらずPodは配置されませんでした。Nodeのメモリの最大容量は分かっているので、あらかじめ基盤の方式設計段階でNodeにぎゅうぎゅうにPodがスケジューリングされることを避けることができます。

ただ、、、今回は同じサイズのNodeで検証しているのできちんと2台でスケジューリングされそうな気もしますが、なにか見落としているのかもわかりません。というわけで、ソースコードをきちんと読みたいと思います。 (理由をご存知の人がいればおしえてください)

 追記:まかべ@マイクロソフトさんから教えてもらいました!ご指摘通り、Node1だけheapsterが動いておりました。すっきり理解しました。 (以下原文ママ)前半のMemory Requestsについてですが、片方のNodeだけheapsterが動いていて非対称なので、その分メモリが296Mi足りなくて4つめのPodが行儀よく待っちゃってるかも。heapster分を考慮してPodごとのRequestを1.3Giにしたら、4つ動きました。

メモリをたくさん使う別アプリが邪魔してきたとき、きちんとシステムが動作するのか?

Kubernetesでは「Resource Requests」で宣言した分のメモリをきっちり確保してPodをスケジューリングしているのが分かりました。このResource Requestsは、実際のメモリの使用量ではなく、クラスタに割り当てられているリソースに基づいていることもわかりました。それでは、メモリリソースが余っているクラスタ上に、「Resource Requests」の宣言なしにメモリをたくさん使う野良Podががんがんデプロイされたらどうなるでしょうか?

まず実験のため、次のようなメモリを1.0GBつかうDockerイメージを作ります。これをビルドしてmem-stressという名前のDockerイメージを作ります。

FROM ubuntu:latest

RUN apt-get -y update && apt-get -y upgrade
RUN apt-get -y install stress

# 1つのstressプロセスが起動し、プロセスあたり1GBのメモリの負荷をかける
CMD ["stress", "-m","1", "--vm-bytes" ,"1G","--vm-hang","0","-q"]

これをDocker HubやAzure Container Registryなどのレジストリで公開します。

次にこのDockerイメージを動かすPodを起動するReplicaSetのマニュフェスト(mem-stress.yaml)を作ります。この例では上記のDockerfileをビルドした「image: asashiho/mem-stress:latest」から生成するコンテナが1つ含まれたPodをテンプレートで定義し、そのPodを2つ動かすため「replicas: 5」と定義しています。クラスタには理論上各Nodeに3.5GBずつメモリがありますので、ざっくり5つくらいの野良Podを起動してみます。

このマニュフェストのポイントは、上のNginxのPodと異なりメモリに対するリソース要求(Resource Request)を指定していないことです。 このコンテナを作った張本人である私はアプリがメモリを1.0G使うことを知っていますが、Kubernetes はどれだけリソースを使うコンテナが入ったPodなのか一切知らないのです。

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: mem-exp
spec:
  replicas: 5
  selector: 
      matchLabels:
        app: mem-stress-temp
  template:
    metadata:
      labels:
        app: mem-stress-temp
    spec:
      containers:
      - name: pod-sample
        image: asashiho/mem-stress:latest

次のコマンドを実行すると、新しく1Gのメモリを使うPodが5つ動く、、、、、はずです

$ kubectl create -f mem-stress.yaml
replicaset.apps "mem-exp" created

なるほど。

「mem-exp-xxx」のPodは4つしか起動せず、うちいずれかの1つはCrashLoopBackOff―Errorで再起動を繰り返しています。

$ kubectl get pod -o wide
NAME               READY     STATUS             RESTARTS   AGE       IP             NODE
mem-exp-6dcgh      0/1       CrashLoopBackOff   2          1m        10.244.3.188   aks-nodepool1-12354740-1
mem-exp-dnwjk      1/1       Running            1          1m        10.244.3.186   aks-nodepool1-12354740-1
mem-exp-grxvz      1/1       Running            0          1m        10.244.2.155   aks-nodepool1-12354740-2
mem-exp-v5j29      1/1       Running            0          1m        10.244.2.154   aks-nodepool1-12354740-2
mem-exp-x9gxl      1/1       Running            2          1m        10.244.3.187   aks-nodepool1-12354740-1
reqest-exp-9kztw   1/1       Running            0          26m       10.244.2.153   aks-nodepool1-12354740-2
reqest-exp-b4t8h   1/1       Running            0          26m       10.244.3.184   aks-nodepool1-12354740-1
reqest-exp-fgrpn   0/1       Pending            0          13m       <none>         <none>
reqest-exp-wzg7t   1/1       Running            0          18m       10.244.3.185   aks-nodepool1-12354740-1

絵にかくと次の感じです。

f:id:dr_asa:20180410131054p:plain:w500

Podのログを確認するとエラーが出ているのが分かります。

$ kubectl logs mem-exp-6dcgh
stress: FAIL: [1] (415) <-- worker 5 got signal 9
stress: FAIL: [1] (421) kill error: No such process
stress: FAIL: [1] (451) failed run completed in 39s

システム全体のメモリ使用量はきちんと上がっています。

$ kubectl top node
NAME                       CPU(cores)   CPU%      MEMORY(bytes)   MEMORY%
aks-nodepool1-12354740-1   33m          3%        2275Mi          68%
aks-nodepool1-12354740-2   127m         12%       2573Mi          77%

これを、40分ほど放置します。その間に夕飯のしたくを。

$ kubectl get po -o wide
NAME               READY     STATUS             RESTARTS   AGE       IP             NODE
mem-exp-6dcgh      0/1       CrashLoopBackOff   11         44m       10.244.3.188   aks-nodepool1-12354740-1
mem-exp-dnwjk      1/1       Running            6          44m       10.244.3.186   aks-nodepool1-12354740-1
mem-exp-grxvz      1/1       Running            0          44m       10.244.2.155   aks-nodepool1-12354740-2
mem-exp-v5j29      1/1       Running            0          44m       10.244.2.154   aks-nodepool1-12354740-2
mem-exp-x9gxl      1/1       Running            6          44m       10.244.3.187   aks-nodepool1-12354740-1
reqest-exp-9kztw   1/1       Running            0          1h        10.244.2.153   aks-nodepool1-12354740-2
reqest-exp-b4t8h   1/1       Running            0          1h        10.244.3.184   aks-nodepool1-12354740-1
reqest-exp-fgrpn   0/1       Pending            0          56m       <none>         <none>
reqest-exp-wzg7t   1/1       Running            0          1h        10.244.3.185   aks-nodepool1-12354740-1

何が起こっているか、よーく見てみます。

1.Node1にスケジューリングされた野良Pod3つ

「mem-exp-6dcgh」と「mem-exp-dnwjk」と「mem-exp-x9gxl」はPodを稼働させたくても実際のメモリが足りないため、うち1つのいずれかが再起動を繰り返し「CrashLoopBackOff」のステータスになっています。

2.Node2にスケジューリングされた野良Pod2つ

「mem-exp-grxvz」と「mem-exp-v5j29」は再起動されることもなく順調に稼働しています。

3.マニュフェストでメモリの使用量をあらかじめ宣言したPod4つ

「reqest-exp-9kztw」は再起動されることなく3つが稼働中、そして空き容量待ち「reqest-exp-fgrpn」はPenddingでお行儀よく待っていること分かります。

なるほど!

どのようなしくみでPodがスケジューリングされたのか

KubernetesのResource Requestsでメモリを記述しないと、「Podがいつメモリ不足で終了されても構わない」という意味になります。なにがそうさせているかというと、、、、、

Pod QoSについて

KubernetesはPodに対して3つのQuality of Service (QoS)クラスを提供しています。このQoSは、今回検証したResource Requestsとリソースの上限を決めるResource Limitsの2つから次の条件でclassが決まります。

BestEffort

pod内のどのコンテナにもResource RequestsとResource Limitsが設定されていない時に設定される

Burstable

BestEffortもGuaranteedも設定されていないときに設定される

Guaranteed

CPUとメモリの両方に、Resouce RequestsとResource Limitsがセットされていること/Pod内のそれぞれのコンテナにセットされていること/Resouce RequestsとResource Limitsの値がそれぞれ同じであること、で設定される

このQoSの確認は、kubectl describeでできます。例えば「mem-exp-96vdn」のQoSを確認するときは次のコマンドになります。

$ kubectl describe pod  mem-exp-96vdn |grep QoS
QoS Class:       BestEffort

ちなみにpod内に複数のコンテナが存在する場合はまず各コンテナに上記のルールに従ってQoSを割り当てます。全てのコンテナがBestEffortならPodのQoSはBestEffortとなり、全てのコンテナがGuaranteedならPodのQoSはGuaranteedになるとのことです。そして、いずれの条件にも当てはまらない場合BestEffortになるとのことです。

今回の検証では、1.5GのResouce Requestsを指定したPodのQoSは「Burstable」、なにも指定しない野良Podは「BestEffort」が設定されていました。

Nodeのメモリ上限に達した場合、どういった判断でkillされるか

Kubernetesクラスタは魔法の箱ではないので、実際のメモリを超えるアプリは実行できません。今回の検証のNode1でなにがおきていたかというと。。。

Kubernetesでは、QoSに従ってどのPodのコンテナがkillされるか決まります。最も優先度が低く最初にkillされるのはBestEffort、つぎにはBurstable、最後にGuaranteedがkillされます。このGuaranteedはsystem processがメモリを必要とした場合のみkillされます。

もし同じQoSクラスだった場合、OutOfMemory (OOM) scoreのよってどのプロセスをkillするかを比較して決めているようです。決して、適当にkillしているわけではなく、Kubernetesはちゃんと忖度しながらkillしています。

今回の検証では、メモリが足りていないNode0上で、優先度の低い野良Podがkillされ、再起動されていました。この再起動のルールも、実はとても興味深い処理方式で動いています。こちらは後日別ブログで。

まとめ

Kubernetesでは、アプリケーション(コンテナ)が使うメモリに制限をかけると、クラスタ上の理論的なリソースが配分されてPodがスケジューリングされることがわかりました。また、Podの優先度が高くなり、クラスタの実際の使用メモリが多くなったときも、killされる可能性が低くなることが分かりました。

検証していませんが、ServiveやIngressでサービスディスカバリーを定義しておけば、今回のケースではいずれかのPodは動いているはずですので、クラスタ外部からのアクセスをみてみると、サービスが正しく動作している状態を維持できているはずです。

すごい。

なお、今回は設定しませんでしたがメモリにRequest Limitsを与えたときの動きや、CPUに関しても後日ひまを見つけて検証してまとめてみたいと思います。

というわけで、クラウドのKubernetesマネージドサービスを使うと、クラスタそのものの構築やMasterのメンテはお任せできますので、分散基盤のめんどうがみられるレベルのインフラエンジニアがいなくても、快適なデプロイ環境を作れますが、一方、、、知識としてのインフラ技術は、ある程度勉強しておかないと、障害時に中でなにがおこっているのか、だけでなく適切な基盤方式設計ができない、ということにもなります。難しいですが勉強だいじ。

おわり

Kubernetesはクラスタで障害があったとき、どういう動きをするのか

Kubernetesは、コンテナアプリケーションをデプロイするためのオーケストレーションツールです。Kuberenetesは分散環境におけるスケーラブルなコンテナ実行環境をつくるための、さまざまな機能が提供されています。

もともとはGoogleが開発したBorgをもとにOSS化したものですが、今日ではマイクロソフトRedHatも積極的に開発に加わり、非常に早いスピートで機能拡張していて、追いかけるのも大変です。

Kubernetesの大きな特徴は宣言的設定にあります。

この宣言的設定とは、イミュータブルなインフラを作るための基本的な考え方で、「システムのあるべき姿」を設定ファイルにて宣言する!という考え方です。Kubernetesは設定ファイルに書いたとおりのインフラを維持するように設計されています。

Kubernetesはコンテナを「Pod」という単位で管理します。このPodをKuberenetesクラスタ内でどのように稼働させるか、を維持する機能が「ReplicaSet」です。ReplicaSetをつかうと、テンプレートで定義した構成のPodが常に稼働している状態をつくれます。

ということは、、、、、たとえ何らかの理由でPodに障害が発生しても、なにも対処しなくても自動復旧するはずよね!!!が本当かどうか確かめるべく、AzureでKubernetesクラスタを構築し、ReplicaSetの簡単な実験をしてみます。

前提読者

実験準備

AzureのKubernetesのフルマネージドサービスであるAKSを使います。公式サイトをもとにKubernetesクラスタを構築しました。

クイックスタート - Linux 用 Azure Kubernetes クラスター | Microsoft Docs

ここでは、次の構成のクラスタで実験します。 f:id:dr_asa:20180402111911p:plain:w500

実験1:クラスタ内のコンテナが異常終了したらどうなるか?

まず、Nginxが動くたけのシンプルなPodを構成するために次の定義ファイルを作成します。ちなみにKuberenetesのPodはいくつかのコンテナをまとめたもので、今回はPodに1つのコンテナを入れたものを使います。

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replicaset-exp
spec:
  replicas: 5
  selector:
      matchLabels:
        app: replicaset-temp
  template:
    metadata:
      labels:
        app: replicaset-temp
        ver: "1.0"
    spec:
      containers:
      - name: pod-sample
        image: nginx

Kubernetesでは定義ファイルはマニュフェストと呼ばれ、YAMLまたはJSONで記述します。このマニュフェストで宣言した構成の意味をざっと図解すると、次のとおりです。

f:id:dr_asa:20180402122913p:plain:w800

このマニュフェストのポイントは以下の指定で、これはKubernetesクラスタ内に常に5つのPodを稼働させておくということを宣言しています。

replicas: 5

次のコマンドでreplicaset.yamlに定義したReplicaSetをクラスタ上に構成します。

$ kubectl create -f replicaset.yaml
replicaset "replicaset-exp" created

Podの確認は、kubectl get podsコマンドで行います。 「宣言」した通り、5つのPod(コンテナ)がクラスタ上で起動しています。次の例では「replicaset-exp-58bp7」~「replicaset-exp-hh8qb」という名前のコンテナが起動(Running)しているのがわかります。

$ kubectl get pods
NAME                   READY     STATUS    RESTARTS   AGE
replicaset-exp-58bp7   1/1       Running   0          14s
replicaset-exp-6mj5z   1/1       Running   0          14s
replicaset-exp-97z7t   1/1       Running   0          14s
replicaset-exp-gxj9j   1/1       Running   0          14s
replicaset-exp-hh8qb   1/1       Running   0          14s

これら5つのPodが2台のNodeにどう配置されているかを確認します。今回の実験ではNode0(aks-nodepool1-12354740-0)とNode1(aks-nodepool1-12354740-1)に次のようにpodが配置されていました。

Pod名 稼働しているNode
replicaset-exp-58bp7 Node0
replicaset-exp-6mj5z Node1
replicaset-exp-97z7t Node0
replicaset-exp-gxj9j Node0
replicaset-exp-hh8qb Node1

図であらわすと書くと次のようになっています。 f:id:dr_asa:20180402115227p:plain:w500

1つのPodをわざと削除してみるとどうなるか

ここでNode1上で稼働しているPod「replicaset-exp-hh8qb」をPodを以下のコマンドで削除します。

$ kubectl delete pod replicaset-exp-hh8qb

クラスタ内のPodがどういう状態であるかを次のコマンドで確認します。

$ kubectl get pods
NAME                   READY     STATUS    RESTARTS   AGE
replicaset-exp-58bp7   1/1       Running   0          10m
replicaset-exp-6mj5z   1/1       Running   0          10m
replicaset-exp-97z7t   1/1       Running   0          10m
replicaset-exp-gxj9j   1/1       Running   0          10m
replicaset-exp-7d6gc   1/1       Running   0          1m

コマンドを注意深く見ると、削除したPod「replicaset-exp-hh8qb」が無くなりになり、代わりにPod「replicaset-exp-7d6gc 」が新しく生成されています。

現在の状態を図でかくと次のとおりです。

f:id:dr_asa:20180402115541p:plain:w500

結果1:クラスタ内のコンテナアプリが異常終了したらどうなるか?

実験したところ、マニュアルに書いてある通り、コンテナアプリが動作するある1つのPodが何らかの障害で利用できなくなったときも、ReplicaSetのマニュフェストで指定した、レプリカ数=5になるよう、新しい別のPodが自動で1つ生成されました。

つまり、KubernetesのReplicaSetは、設定ファイルに書いたとおりのインフラを維持するように動作することがわかります。

考察1:だれが、Podを復活させたのか?

KubernetesがPodを自動で復旧させたことがわかりましたが、これをKubernetesのオートヒーリング(Auto Healing)機能といいます。だれがいったいPodを復活させたのでしょうか?をみていきます。

Kuberenetesの主なコンポーネントたち

Kubernetesは分散環境でサーバ群が協調してそれぞれの処理を行います。このかたまりのことをKubernetesクラスタと呼びます。Kubernetesで動作しているサーバおよび主なコンポーネントは次のとおりです。

f:id:dr_asa:20180402121300p:plain:w600

1. Master

Kubernetesクラスタ内のコンテナを操作するためのサーバです。kubectlコマンドを使ってクラスタを構成したりリソースを操作したりする際は、マスターサーバがコマンドからのリクエストを受け取って処理を行います。複数台からなるKubernetesクラスタ内のノードのリソース使用状況を確認して、コンテナを起動するノードを自動的に選択します。Kubernetesがオーケストレーションツールと呼ばれるのも、このマスターサーバが複数台からなる分散したノードをまとめて管理することで、あたかも1台のサーバであるかのようにふるまいます。  

■kube-apiserver

Kubernetesのリソース情報を管理するためのフロントエンドのREST APIです。各コンポーネントからリソースの情報を受け取りetcd上に格納します。他のコンポーネントはこのetcdの情報にkube-apiserverを介してアクセスします。このkube-apiserverにアクセスするには、GUIツールやkubebtlコマンドを使います。また、アプリケーション内からkube-apiserverを呼び出すことも可能です。kube-apiserverは認証/認可の機能も持っています。

■kube-scheduler

kube-schedulerはPodをどのNodeで動かすかを制御するコンポーネントです。kube-schedulerは、ノードに割り当てられていないPodに対して、Kubernetesクラスタの状態を確認し、空きスペースを持つNodeを探してPodを実行させるスケジューリングを行います。

■kube-controller-manager

kube-controller-managerはKubernetesクラスタの状態を常に監視するコンポーネントです。定義ファイルで指定したものと実際のNodeやコンテナで動作している状態をまとめて管理します。

2. Node

実際にDockerコンテナを動作させPodを稼働させるサーバです。AKSでは仮想マシン(VM)で構成され、通常は複数用意して、クラスタを構成します。ノードの管理は、マスターサーバが行います。何台ノードを用意するかは、システムの規模や負荷によって異なりますが台数が増えると可用性が向上します。なお、kubeproxyというコンポーネントも動作しますが、説明は別ブログで。

■kubelet

kubeletは、Podの定義ファイルに従ってコンテナを実行したり、ストレージをマウントしたりするエージェント機能を持ちます。またKubeletは、Nodeのステータスを定期的に監視する機能を持ちステータスが変わるとAPI Serverに通知します。

3. etcd

Kubernetesクラスタの構成を保持する分散KVSです。Key-Value型でデータを管理します。どのようなPodをどう配置するかなどの情報を持ち、API Serverから参照されます。

Podのオートヒーリング機能

クラスタの状態監視は、Masterのkube-controller-managerが行います。このkube-controller-managerには次の機能があります。

  • ReplicationManager
  • ReplicaSet/DaemonSet/Job controllers
  • Deployment controller
  • StatefulSet controller
  • Node controller
  • Service controller
  • Endpoint controller
  • Namespace controller
  • etc

この中でPodの状態はReplicationManagerが監視しています。もし、実際に稼働しているPodの数とetcdで管理しているマニュフェストファイルで定義したreplicasの数が一致していない場合、Podの数を調整します。

f:id:dr_asa:20180402113346p:plain:w500

kubernetes/controller_utils.go at master · kubernetes/kubernetes · GitHub

新しく生成されたPodは、kube-schedulerによって適切なNodeにスケジューリングされます。

このkube-schedulerの挙動は、@tkusumi さんの 「Kubernetes: スケジューラの動作」 がとても分かりやすく勉強になりました!こちらをぜひ!

qiita.com

この記事で解説されているように、kube-schedulerによって最適なノードにPodがスケジューリングされます。実際にノード上にPod、つまりDockerコンテナを実行させるのは、kubeletが行います。kubeletは自Nodeに割り当てられたPodのスケジューリングに従い、必要な数のPodを立ち上げます。

と、、、、このようにKuberenetesでは複数のコンポーネントがいい感じで協調しながらクラスタを維持しているのが分かりました。

実験2:クラスタ内のサーバ(VM)が異常終了したらどうなるか?

前の実験では、Pod(コンテナ)のアプリケーションが停止したときの動きをみましたが、次はKubernetesクラスタを構成するサーバ、つまりNode障害がおこったときにPodがどうなるかを確認します。クラスタを構成するサーバが、物理的にぱーーーんっと逝ってしまったときを想定しています。

注意:

これは実験です。メンテナンス等でNodeを意図的に停止させる場合は、kubectl drainコマンドでNode上のPodを安全に退避させてから停止をします。くれぐれも、本番機で実験しないようにお願いします。

意図的にNode0で障害を起こしてみる

Node0には3つのPodが起動した状態ですが、ここで強制的にNode0(aks-nodepool1-12354740-0/10.240.0.4)となっているVMを停止します。

kubectl get nodesコマンドで確認すると「NotReady」となり、KubernetesのMasterから利用できない状態になっていることが分かります。

$ kubectl get nodes
NAME                       STATUS           AGE       VERSION
aks-nodepool1-12354740-0   NotReady,agent   50m       v1.9.2
aks-nodepool1-12354740-1   Ready,agent      38m       v1.9.2

Podはどうなったか?

kubectl get podsコマンドを実行すると、8つのPod確認できます。うち5つが稼働(Running)し、3つのPodが不明(Unknown)な状態であることがわかります。

$ kubectl get pods
NAME                   READY     STATUS    RESTARTS   AGE
replicaset-exp-58bp7   1/1       Unknown   0          18m
replicaset-exp-6mj5z   1/1       Running   0          18m
replicaset-exp-8p6xz   1/1       Running   0          40s
replicaset-exp-97z7t   1/1       Unknown   0          18m
replicaset-exp-gxj9j   1/1       Unknown   0          18m
replicaset-exp-hh8qb   1/1       Running   0          18m
replicaset-exp-qnztj   1/1       Running   0          40s
replicaset-exp-wf7lt   1/1       Running   0          40s

これらの8つのPodがNodeにどう配置されているかを確認します。

Pod名 障害前に配置されたNode 現在のNode
replicaset-exp-58bp7 Node0 -
replicaset-exp-6mj5z Node1 Node1
replicaset-exp-8p6xz - Node1
replicaset-exp-97z7t Node0 -
replicaset-exp-gxj9j Node0 -
replicaset-exp-hh8qb Node1 Node1
replicaset-exp-qnztj - Node1
replicaset-exp-wf7lt - Node1

図で書くと次のとおりです。

f:id:dr_asa:20180402114316p:plain:w500

なるほど!

きちんとKubernetesクラスタ内に5つのPodが稼働した状態を維持しています。

Podのたどった運命は、次の3つのパターンがあります。それぞれログを確認します。

1.もともとNode0で稼働していたPod(replicaset-exp-58bp7)

node-controllerによって、障害のあるNode0から立ち退き(NodeControllerEviction)されています。

Events:
node-controller   Normal      NodeControllerEviction  Marking for deletion Pod replicaset-exp-58bp7 from Node aks-nodepool1-12354740-0

2. もともとNode1で稼働していたPod(replicaset-exp-6mj5z)

Podに変更なしです。

Events:
kubelet, aks-nodepool1-12354740-1   spec.containers{pod-sample}   Normal      Started     Started container

3.新しく生成されたPod(replicaset-exp-8p6xz)

default-schedulerから指示を受けたNode1のkubeletによって新しいPodが生成されています。

Events:
default-scheduler                                                       Normal          Scheduled     Successfully assigned replicaset-exp-8p6xz to aks-nodepool1-12354740-1
kubelet, aks-nodepool1-12354740-1                                       Normal          SuccessfulMountVolume      MountVolume.SetUp succeeded for volume "default-token-bn7zt"
kubelet, aks-nodepool1-12354740-1       spec.containers{pod-sample}     Normal          Pulling pulling image "nginx"
kubelet, aks-nodepool1-12354740-1       spec.containers{pod-sample}     Normal          Pulled  Successfully pulled image "nginx"
kubelet, aks-nodepool1-12354740-1       spec.containers{pod-sample}     Normal          Created Created container
kubelet, aks-nodepool1-12354740-1       spec.containers{pod-sample}     Normal          Started Started container

結果2:クラスタ内のサーバ(VM)が異常終了したらどうなるか?

1つのNodeが何らかの障害で利用できなくなったとき、別の正常に動作しているNode上で、ReplicaSetのマニュフェストで指定したレプリカ数=5になるよう、Podが自動生成されました。

ただし、障害が発生したNodeで稼働していたPodがふたたび再配置されるわけではなく、新しい別のPodが正常なNode上に生成されることが分かりました。

というわけでやはりReplicaSetは、たとえNode障害が発生しても設定ファイルに書いたとおりのインフラを維持するように動作するよう実装されていることがわかります。

考察2:だれが、Podを復活させたのか?

Kubernetesクラスタ内でだれがNode障害を検知したのでしょうか?

Kubernetesでは、Masterで動作するController ManagerのNode ControllerがNodeの管理を行います。

Node Controllerは、Kubernetes内部のノードリストを最新の状態を保持する役割があります。クラスタ内のNodeが正常でない場合、そのNodeのVMがまだ使用可能かどうかを確認し、使用可能でない場合、Node Controllerは該当のNodeを、クラスタのNodeのリストから削除します。

また、Node Controllerはノードの状態を監視します。Node Controllerは、ノードが到達不能になったとき(つまり、何らかの理由でNode Controllerがハートビートを受信しなくなったときなど)に、NodeStatusのNodeReadyをConditionUnknownまたはConditionFalseに更新し、ノードからすべてのPodを取り除きます。

Nodes | Kubernetes

よって、今回の実験では、Node01の停止を検知したNode ControllerがノードリストからNode0を外したため、正常に動作しているNode1にReplicaSetで定義したreplicasの値を維持するようPodが配置されました。

ただし、ReplicaSetは障害があったPodと同じ状態のものではなく、別の新しいPodが新規作成されるということを、きちんと理解しておく必要があります。

まとめ

Kubernetesは、分散環境でも高い耐障害性を保持するしくみを持っていることが確認できました。

すごい。

ただし、「24/365サービス無停止運用ウェーイ」とはいかず、、、後続のいくつかの他の実験を行ったところ、理論上無停止状態を維持できないケースも考えられましたので、脳内を整理して別途ブログにまとめます。

【書評】「Mackerelサーバ監視実践入門」 を読みました!

クラウドサービスを使うと、あっという間にアプリケーションがローンチ出来る時代になりました。とても良い世界に向かっています。

しかし。

であるがゆえに、自社のサービスを差別化しビジネスにしていくのが難しく、深層学習のような高度な数理を必要とするインテリジェントなアプリや専門性の高い業務知識を要するアプリでないとなかなかマネタイズが。。。というとこもあり、エンジニアは勉強すべきことが山ほど増えてきています。

そんなわけなので、アプリのインフラの運用にかかる作業はクラウドに丸投げし、可能な限り自動化しないと命がいくつあっても足りません。特に、サービスが停止するとビジネス損失も大きいので、監視だけはしっかりしたい。しかしながら、毎日定時に帰り土日はゆっくりまったりしたい。

Mackerelは、はてなが提供している国産サーバ監視SaaSです。システム監視はプロのノウハウや技術が必要な部分がどうしても多いわけなので、SaaSを使うのがおすすめです。

ということで勉強のため、「Mackerelサーバ監視実践入門」 を読ませていただきました。

gihyo.jp

1章 Mackerelとはなにか

Mackerelは、はてなブログはじめはてなの自社サービスから生まれた監視サービスです。サーバ=「鯖」=Mackerelというダジャレ乙というところからはじまり、Mackerelのコアコンセプト/提供機能/基本アーキテクチャ/動作環境などがコンパクトにまとまっています。

Mackerelは、Push型アーキテクチャを採用しており、監視対象ノードにmackerel-agentを動作させてメトリックスを収集する構成です。

2章 Mackerelをはじめる

Mackerelのサインアップのしかたから始まり、管理画面の見方、収集できるメトリックの種類などが解説されています。なんと「カラー!!」うらやましいです。あと、Windows Serverの監視もできるのは知りませんでした!すばらしい!

3章 監視する

ここでは、具体的にMackerelでサーバを監視する手順が紹介されています。メトリック監視で監視ルールをどう設定すればよいかや、クラウドサービスの監視などでつかうURL外形監視の手順がスクリーンショット付きで丁寧に説明されています。

また、コラムでロードアベレージやcpuやdiskなどのシステムメトリックの意味か解説されていて、すばらしい!ノウハウたっぷりです。

4章 アラートを通知する

運用では閾値を超えたときにアラートを発生して管理者に通知しますが、Mackerelでは、メールやSlackだけでなくLINEやYammerなども利用できます。これらの設定のしかたが説明されています。

5章 プラグインを作る

mackerel-agentのプラグインを作る方法が解説されています。カスタムメトリックプラグイン/チェックプラグインメタデータプラグインが作成できます。シェルスクリプトでの例やGoでのヘルパーライブラリでの実装が紹介されています。かなり丁寧な説明なので、Goを書いたことがなくてもすぐ理解できると思います。

6章 各種ツール連携と運用の効率化

ここでは、MackerelAPIの紹介と、Fluentd/Chef/Capistrano/Serverspecとの連携について書かれています。ログを集約させたりChefで構成管理したりなどいろんなことができるんだなと初めて知りました。

7章 クラウド環境におけるMackerel

ここでは、MackerelをAzureおよびAWSで利用するための手順が解説されています。どちらも監視サービスを提供していますがお客さんによっては、同一のSaaSで監視したいという要件もあると思うので、チェックしておきたいと思いました。

あと、Herokuの監視とDockerプラグインの説明もありました。良いですね~~!

8章 発展的な機能

最後の章で、落穂拾い的にGitHubとの連携やグラフの便利な書き方、セキュリティに関するノウハウなどが紹介されています。また付録にMackerelの内部アーキテクチャが紹介されていて、面白いなぁと思いました。

まとめ

とても良い本でした。

わたしはゲームやマルチメディア系などいやゆるコンシューマ向けのシステムのことはよく知らないんですが、業務系システムでは「基幹システムのクラウド移行」「ハイブリットクラウド」「マルチクラウド」などなど、とにかくAzureとAWSが大盛況です。どちらも監視サービスを提供していますがお客さんによっては、同一のSaaSで監視したいという要件もあると思いますし、外形監視の要件もあるのでしっかり技術をチェック&ウォッチしておきたいと思いました。

業務系システムにはミッションクリティカルなものも多く、サービス停止がビジネスだけでなく社会基盤に大きな損失を与えるケースが多々あります。またステークホルダーが多く、非機能要件も複雑怪奇なため「運用設計」が、ものすごくものすごく大事です。

はてなのみなさんの極めて高い技術やノウハウを凝縮して煮詰めたMackerelは、国産SaaSということもありとても使いやすいので引き続き勉強していきたいと思いました。

Mackerelチームのみなさん、どうもありがとうございました!

これからもどんどん良いサービスをお願いします!

おわり

【書評】「Linuxのしくみ」を読みました!

今はエンジニアではありませんが、いつかクラウドエンジニア/アーキテクトになりたい!わたしのひそかな目標でもあります。

プラットフォーム技術を生業にするためには、ハードウエア/OS/ネットワークの深い知識は欠かせません。 ということで、同人誌フレンズであり、かつ尊敬するカーネル開発者であるsatさんの「Linuxのしくみ」 を読ませていただきました。

gihyo.jp

わたしは、商用Unixしか基盤構築の実務経験が無く、Linuxは完全に独学でしかないため「プロ」ではなく完全に本書の対象読者です。

そんな私ですが、大変勉強になりプラットフォーム技術に携わる多くの方におすすめしたいので書評をまとめました。

前提読者

この本の前提読者は「Linuxの使い方」ではなくタイトルの通り「Linuxのしくみ」つまり内部構造を理解したいエンジニアを対象にしています。

本書を読むにあたり、特別なプログラミングの知識は不要ですが、「ふつうのLinuxプログラミング 第2版」の内容が頭に入っているとスムーズです。 あと、Linuxのインストール/セットアップが自力でできる、基本的なコマンドは問題なく操作できるレベルのLinuxユーザであることが必要です。

第1章 コンピュータシステムの概要

OSとハードウエアの関係、OSの概要について説明があります。カーネルがハードウエアのリソース管理をどのように行っているのか、プロセスとはなにか、デバイスドライバとなにか、など基本的なことが沢山の図とともに説明されています。非常に分かりやすい!

第2章 ユーザモードで実現する機能

ここではシステムコールが「実験」をベースに説明されています。たとえば、straceを介してPythonでHelloWorldしたときの挙動をみながら、write()システムコールが発行されてますよね!とか、親プロセスのプロセスIDを取得するコードを無限ループさせるとどうなるでしょう?などです。 あと、ラッパー関数、標準Cライブラリなどなどの説明もあります。

第3章 プロセス管理

ここでは、fork()関数とexecve()関数についての解説があります。図を用いてプロセス生成の流れを説明し、親プロセス/子プロセスがメモリ空間にどう展開され、どう終了されるかが分かります。 ここはややアセンブラの知識が必要になりますが、まったく知らない人も図をみてイメージできるので親切です。必要と言っても、情報処理技術者試験CASLⅡぐらいの知識でぜんぜん大丈夫です。

第4章 プロセススケジューラ

40ページほどかけてプロセススケジューラの挙動を丁寧に解説されています。私もふくめて多くのLinuxユーザはカーネルがプロセススケジューラの機能を持っていて複数のプロセスを同時に動作させているのだなと理解していると思いますが、実際のところどのように「同時に動作させているようにみせかけている」かをたくさんの実験とともに解説されています。 個人的には、、、nice()の挙動とかとてもわかりやすっ。と思いました。

第5章 メモリ管理

おなじみのfreeコマンドの確認の方法から丁寧に始まり、多くの人を悩ますOut of Memoryの裏側でなにがおこっているのか、OOM Killerは何をやってくれる人なのかなどが解説されています。 ここでも実験がたくさんあり、ページフォルトの実験やmalloc()、スワップの挙動などは難しく、私の頭脳ではちょっと別途改めて勉強が必要でしたが、、後半のCoWは「あ、、あああぁあぁあ、、、これは3年前のあのとき!あの本書いたときに読んでおきたかった、、、本!」という感じでとても分かりやすかったです。

余談ですが、だいたい書籍では著者が書いたドラフト図を、初校でDTPのデザイナーの方が「いい感じ」に書いてくれるものなんですが、satさん本はすべての図が「殺風景」で、むしろこれはsatさん自らが「殺風景に書いてくれ!」とオーダーしないとできないレベルだと思われるので、いったい内部で何が起こっているのかが気になります。

第6章 記憶階層

ここでは、レジスタ/キャッシュ/メモリ/ストレージと記憶装置でどのようにデータが扱われているかが、ここでも「実験」をとおして見通せるようになっています。実際にファイルに書き出してどうなるかを追っていけるようになっています。

第7章 ファイルシステム

ここでは、そもそもファイルシステムとはなんぞやから始まり、ジャーナリングのしくみ、コピーオンンライトでのファイル更新の様子などが、図をつかって説明されています。講義で黒板をみているような感じで、この上なく殺風景な図であるがゆえ、むしろシンプルに伝わる。そんな感じです。

satさんらしく、Btrfsもしっかり解説がありました!スナップショットの挙動やデータ破損検知/修復のしくみなどです(そもそもBtrfs自体が難しいのでプロ向け内容で、私にはちょっと改めて勉強が必要です)

第8章 ストレージデバイス

HDDとSSDについてのしくみとデータの読み書きに関する実験が解説されています。エンジニアだった若いころ、某超エンタープライズ金融機関の基盤構築PJで一緒だった、Sunや富士通のエンジニアに教えてもらった(が、たいして頭に入っていない)のを思い出しました。スイングアームやプラッタの動きを居酒屋のお皿とお箸で早口のオタクが熱心に、、、そんな感じを煮詰めて本にした感じです。

まとめ

とても良い本でした。

タイトルや表紙が、明らかに自然派ママをターゲットとして意識している風なため、一見「本物のLinuxカーネルエンジニアたるものこうあるべき!」みたいなハードモードなものが綴られているのかなと思いましたが、中を開くとまったく異なるので正直驚きました。

これはsatさんの頭の良さでもあると思いますが、さまざまな「実験」を通してカーネルの挙動と全体像を確認していくという、今までにない新感覚の目線でまとめられているので、論旨展開や情報の整理の仕方など読み物としても非常におもしろく、とてもインスパイアされました。

早々に続編を期待です!

あらためて、Linux勉強せねば、楽しいなぁという感想🔥🔥🔥を持つとともに、2018年は、いよいよまじめにやっていきの年になるはず💪なので、よいバイブルです!

どうもありがとうございました!

おわり

【書評】Amazon Web Services 業務システム設計・移行ガイド を読みました

国内の大規模基幹システムもクラウド移行まったなしの2018年です。

ということで、すっかり人生出遅れてしまいましたが、BtoB向けクラウド導入の知識をつけるべく Amazon Web Services 業務システム設計・移行ガイド を読ませていただきました。

Amazon CAPTCHA

大変勉強になり、多くの大規模基幹系業務システムに携わる方におすすめしたいので書評をまとめました。

前提読者

この本の前提読者は 「企業」や「組織」でシステム開発・運用に従事し、AWSを活用したいアプリケーション開発者/インフラ技術者を対象にしています。

そして、大規模オンプレですでにBtoBの業務システムを開発・構築したことがある人向けでです。 (間違う人はいないとは思いますが)ウェッブ系の方向け書籍ではないのでご注意を。

Chapter1 AWSサービスの概要

AWSの概要や主なサ―ビスが丁寧に説明されています。AWSのサービスはんぱないほど数があるのでここで俯瞰できます。

Chapter2 全体設計(管理方針と移行計画)

監査証跡についてやAWS導入にあたりネットワーク設計や運用処理方式などの考え方などがまとまっています。 あと、オンプレからAWSへの移行についても説明があります。

いわゆる概要方式設計に相当する内容です。

Chapter3 アカウント管理と権限付与

AWSを大規模システムで導入するとき、必ず必要な知識がIAMだと思うんですが、それがきちんと説明されています。 AWS OrganizationsのベストプラクティスやCogniteなども解説されています。

Chapter4 ネットワーク接続の設計・構築・維持管理

AWSネットワークの構成要素で、通信要件をあげて物理構成/論理構成をまとめてます。 設計項目として、アドレス設計/ルーティング設計/フィルタリング設計などが書かれています。

Chapter5 システム設計とサービスの導入

いわゆる、基盤方式設計(クラウドアーキテクチャ)がいくつか紹介されています。 具体的には

  • オンプレミスとの連携を伴わないECサイト
  • ハイブリッド環境で運用する基幹システム
  • Web可用性向上パターン
  • コンテンツキャッシュパターン
  • DB可用性向上パターン
  • ジョブサーバパターン

など定番のものから、Redshiftをつかった大規模データ分析パターンなどのアーキテクチャも説明があります。 あとは、CloudFormationのベストプラクティスなどもあります。

Chapter6 移行テクニック

オンプレからAWSに移行するときの考え方や移行方式などが解説されています。

移行、、、移行、、、、大変だろうな。。。

ただ、著者のみなさまの会社はおそらく国内でもっとも 大規模な業務システム移行に関するノウハウを持っているので、非常に非常に貴重な知見がたくさん埋まっているなぁと思いました。

Chapter7 運用監視の設計・実施

CloudWatchの説明や、CloudTrailを使った運用監視についてが解説されています。 運用の章はまとめるのがすごく難しいのですが、、、とてもきれいに分かりやすくまとまっています。

まとめ

とても良い本でした。

情報が論理的に整理されていて粒度もばらつきがなく、読んでいて一切ストレスのない本でした。いつかこういう本が書けるようになりたい!と思いました。

あらためて、AWSすごいなぁ。。。。という感想🔥🔥🔥を持つとともに、2018年は、いろいろまじめにやっていきの年になるはず💪なので、よいバイブルです!

書籍どうもありがとうございました!

おわり

【書評】技術者のための基礎解析学を読みました!

昨今のアプリケーション開発はどんどんインテリジェント化しており、 深層学習など高度な数理を必要とする技術に関する知識がエンジニアにも求められています。

そもそもコンピューターサイエンスに携わる人における「数学」は、小説家にとっての「国語」と同じなので、、、 よいアウトプットを出すためには、必ず基礎知識として学んでおかなければいけないと思います。

が、大学を卒業してあまりにも時間が経ってしまい 記憶が薄れている、、、いや、そもそもきちんと勉強した記憶が定かで、、、、、、(笑) という人は多いのではないでしょうか?

本書は自分のペースで少しずつ自習ができる書籍で、私にとっては大変勉強になりましたので書評をまとめました。

まさに、大学生のときに読みたかったわこれという感想です。

www.shoeisha.co.jp

前提読者

この本の前提読者は、理系大学の教養課程で学ぶ数学を履修したことがある方向けです。

ぶっちゃけセンター試験のような受験数学と、大学の教養課程で学ぶ数学は別物なので、文系の方には多少しんどいかもしれません。

が、途中計算を端折らず、ごまかさず丁寧に展開しているのが、本書の大きな特徴(逆に、そういうたぐいの自学自習出来る書籍は、大学の教科書でもほとんどなさそう)なので、時間をかけてゆっくり読み進められると良いかなと思います。

第1章 数学の基礎概念

集合と写像や、実数とは、、という基本的な内容ですが、1-2で実数の完備性について詳しい説明があります。わたしはこの実数の完備性について、この本で初めて知ったのですが、、、、これは、後述する関数の連続性を考えるときの基本的な考え方になります。

第2章 関数の基本操作

ここでは、関数の平行移動や拡大縮小、合成関数や逆関数など基本的な計算の仕方が丁寧に説明されています。文章での説明だけ読めば十分に理解できますが、具体的な関数を挙げてグラフによる例が説明されています。2-2では、関数の極限でヘビサイド関数の説明や、関数の連続性に関する丁寧な説明があります。

わたしは工学部の出身で、機械力学を専攻していましたので、完全に「数学を道具として使う」だけで、数式による事象のモデル化や式展開、離散化処理などなど「計算」はできるのですが、「定義」や「定理」など理論的なことはほとんど分かっておらず、「この関数は閉区間において連続として扱う、ゆえに、、、(キリ」という理解しかしていなかったのですが、「むむむむむむむむむ、ちょっと、、まっ、、て、、、連、、、、、続、、、、、、と、、は?!?」という深淵をみてしまい、あとは完全にアレです。

第3章 関数の微積

ここでは、関数の微分積分について取り上げられています。微分係数とはなんぞや、導関数とはなんぞや、という高校数学ぐらいからのレベルで易しい説明があります。 具体的に計算例をあげて、かなり具体的に導関数の計算方法を説明されているので、文系の方で「わ、、、わからん、、、」となってしまった方は1章2章はいったん読み飛ばして3章と4章から始めて、理解できたところで1章2章に戻ればいいと思います。

積分に関しても、まずは区分求積法で近似する手順を順を追って説明されています。原始関数とは、、ロルの定理とは、、平均値の定理とは、、、部分積分とは、、など基礎的なことをごまかすことなく、数式できちんと追っていて、すごい! わたしだったら絶対に「部分積分の公式です、覚えましょう(キリ」と言ってしまいそうなところです。

第4章 初等関数

いわゆる、おなじみの指数関数・対数関数・三角関数を、しつこくきっちりかっちり数式を追いながら説明されています。機械学習でよくさらっと登場するネイピア数ですが、そもそもこれの正体はなにものなのか、、が熱く語られています。 三角関数も、高校数学レベルから説明がされています。内心「(基礎のキなので)加法定理や余弦定理はさすがに説明要らないんじゃ、、、、、」と思いましたが、きちんと図を書いて証明されています。

第5章 テイラーの公式と解析関数

関数の近似で使われるテイラー展開についての説明があります。テイラー展開も、機械学習ナチュラルに多発するキーワードだと思うんですが、ここでも数式を使いながら厳密に証明がされています。私は、本書を読むまで無限小解析がよく分からず、「0に近づく速度??と、、は?」という状態だったのですが、ここの説明でだいぶイメージできるようになりました。そして、収束と発散。これは大学のときに読みたかったなと思いました。

第6章 多変数関数

機械学習や数値解析では高階偏導関数がばんばん出てきます。が、その数式の意味するところをきちんと理解しておかなければ、機械学習や数値解析で、一見それっぽいモデルを作ったりシミュレーションしたりの実装はできたとしても、モデルや解析結果の妥当性を1bitたりとも議論できなくて、ぐぬぬぬぬとなるはずです。

ここでは、全微分偏微分は何が違うのか、、、から始まりテイラーの公式を使って近似する説明がされています。一見数式が複雑で、難しそうに見えますが、力業で計算するだけなので、5章までの内容さえ理解しておけばそんなに難しくないのではないでしょうか。

まとめ

とても丁寧な良い本なので、長く広く読まれる本になるといいなとおもいます。

機械学習の、、、、」のような帯があると、「これを読めば機械学習が分かるのか!」と期待されてしまう方もいるかもしれませんが 「機械学習のための数学」というのはたぶん無くて、基礎にあるのは一般的な「線形代数」だったり「統計」だったり「微分積分解析学)」だったりするかなと思います。なので、これらをきちんと学ぶほかなし。という気がしています。

で、わたしも本を書くものの端くれとしての感想ですが、、、

もし、私がこのタイトルで編集者から企画を頂いたとしたら ごく簡単な最小二乗法やニューラルネットワークのようなものを例題にあげて、そこに出てくる数式の意味や解き方をイラストで図解して書いてしまうと思います。

商用誌の世界では、ある程度の部数を売らないといけません。そのため、表面的な内容をさらって読者に手を動かしながら理解してもらえる本は、安牌なのです。

きっと、読者のみなさんも「具体的で分かりやすい!」「すぐに理解できた!」みたいな満足感をもってもらえるとは思いますが、、、、

残念ながら、そのたぐいの本を読んだだけでは「最小二乗法」と「ニューラルネットワークしか分からないのです。

数学はそういうものじゃない!!

あらためて、中井さんすごいよなぁ。さすがだ。。。。という感想を持つとともに、 続編の線形代数と統計もこれから刊行されると思いますので、 みなさん大いに楽しみにしてください!!

こちらもすでに原稿を読ませていただいていますが、 線形代数も統計もとても読みごたえがあります!

ただ、中井さんの執筆する速度より、私が読んで理解する速度のほうが遅くてですね、、、、、(草)

おわり

2017年にがんばったことと2018年のやっていき💪

仕事も大掃除も終わってないですが、2017年が終わろうとしています。

2017年、良い1年でした!

2017年のアウトプット

8月/9月の2か月はブラブラしていたので、実質10か月でのアウトプットです。

  • 商用誌2冊
  • 同人誌2冊
  • Software Design寄稿1回
  • Web連載4回
  • イベント登壇3回
  • 学会発表1回

毎月なにか書く、2か月毎になにか発表ぐらいのペースでした。

2017年新しくチャレンジしたこと

2016年はDockerコンテナだけだったのですが、上方向と横方向にちょびっと触手をのばしました。

  • 大学1年生の数学やりなおし
  • AWS/Azure/GCPの各MLマネージドの使い方マスター
  • AWS Batch/Azure Batchの使い方マスター
  • OpenFOAM/Dockerで簡単な非粘性流体の解析
  • TensorFlow/Kerasで画像認識の実装

特に1年間地味に解析学線形代数のごく簡単なのを勉強して、OpenFOAMでカタコトの実装ができるようになったのは、わたしのなかではとても大きいデス。計算力学技術者の資格もとりました! チュートリアルをもとに非粘性流体の解析をし、ParaViewも使い方を何となくマスターし、CFD関連の論文も少しだけ読みました。来年はきちんとどこかでシミュレーションの発表できるようにしたいなと。

f:id:dr_asa:20171229192141p:plain

機械学習の基本も勉強しました。書籍を読んだりオンラインセミナーを受講したりでサンプルやチュートリアルなどをやりました。TensorFlow/Kerasでごく基本的な画像推論や転移学習などができるように。たぶんCNNは何となく理解です。

データ集めて前処理して学習モデルを作るだけじゃなく、学習基盤と推論アプリをデプロイしてサービス提供もきちんとやりました。オンプレPCクラスタクラウドVMで学習し、KubernetesクラスタでWebアプリとして公開したり、推論APIAndroidからコールするなども。

あと、AndoridThings/RaspberryPiなどデバイス側でTensorFlowを動かし推論するなどもやりました。TensorFlowLiteは今後もひそかに注目したいです。

で、案の定、、、「機械学習や数値解析のジョブのワークロードをどうするか」「分散学習のための基盤つくる」などのほうが個人的にはお楽しみポイントが多く、CloudML、KubeFlowやAmazon SageMaker、Azure Batch、AWS Batch方面は楽しく勉強できました。

2018年のやっていき💪

現時点の予定ですが、、、プラットフォームに関する書籍を新たに書かせていただく予定です。あと今年ちまちま書いた本が4月に刊行になります。

2017年は同人誌を書いたのがとても楽しかったです。自分が好きなものを好きなようにつくる、という行為そのものが好きなので、手芸とか工作とかそういう感じで。2018年もプライベートで没入できるなにかができればいいなと。

そして、、、、

あまり体が丈夫でないというのもあり、2018年はいつくたばっても後悔のないように「やりたい!」とおもったことはすぐやろうと思ってます。迷わず、すぐに。

やっていきやっていき💪

おわり

GoogleのJupyterNotebookオンライン共有サービス(Colaboratory)を使って流体解析の勉強をしてみた感想

この記事は Colaboratory Advent Calendar 2017 23日目の記事です

Colaboratoryとは、Jupyter Notebookをオンラインで共有できるサービスです。 Googleが提供しています。

こちらからアクセスすると利用可能です。

Colaboratory – Google

作成したNotebookは、Googleドライブに保存されます。Googleドキュメントのように、複数人でコメントを書き入れたり、共同で編集できたりします。編集権限も設定可能なようです。

こんな作業イメージです。コードレビューやオンラインのプログラミング教育サービスなどに使えそうな気もします。

f:id:dr_asa:20171222085743p:plain

Pyhonは学習コストも低く、かつNumpyやScipyのような行列計算や数値計算関連のライブラリも豊富で可視化ツールもあるので、科学技術計算で利用する人も多いようです。

というわけで、数値流体解析の基礎を勉強するためのチュートリアルをやってみました。

github.com

こんな感じですね。JupyterNotebookと同じようにLaTeXで数式やMarkdownでメモが残せるので、勉強ノートとしては良いと思いました。

f:id:dr_asa:20171222092024p:plain

左側のカラム(?)でセクションを管理出来るので、ごちゃごちゃになりがちなコードもまとめて管理できます。この例だと、「熱伝導方程式」「2次元」など適当なセクションですが、、、書籍や研修教材のサンプルコード共有にいいかもしれません。

f:id:dr_asa:20171222092601p:plain

Googleのツールなだけあって深層学習にはもってこいのツールだなとました。カーネルPython2.7であるというのが地味なつらみですが、GCP前提だとぜんぜん問題ないと思います。

そもそも流体解析をちゃんとやるなら、PythonではなくC++Fortranのほうがよさげ!とおもうので、、、深層学習以外の用途では、AWS Cloud9やVisual Studio Live Shareをつかうのが良いのかなと。どちらもこれからですが、とても期待しています。

おわり

© 2017 ASA.