31年目のRealize

技術に関する詳しい記事や分かりやすい記事は世の中にたくさんあるので、自分なりのアウトプットを試行錯誤。

マンガっぽくKubernetesを勉強してみる②

Kubernetesを動かす

f:id:altrlz:20191118203328p:plain

甘いもの食べてアタマ休ませたいよ〜

…こほん。今ギャルちゃんAPIがお菓子(リソース)を持ってきて、わたしが食べ(登録)たら仕事し始めたように、Kubernetesも同じような動きをするよ

なんとっ!?

この記事の着地点

Kubernetesを動かすための基本的な操作、全体像を理解する。
勉強中のため、記載した内容については後日訂正する可能性もあります。

Kubernetesの基本的な操作

前回、Kubernetesの全体構成を図に描いてみたけど、Master Nodeにいるkube-apiserverにyml登録の指示をしていたね

クライアントにいるkubectlで登録するんですよねー

そう。実際には、ymlに書かれたリソース情報をAPI経由でMasterに登録しているんだよ

じゃあわたしたちはymlの書き方と、kubectlの使い方が分かればOKってことね♪

kubectlでもいいし↓でAPIの仕様を見て、直接プログラムに組み込んで登録することもできる

Kubernetes API Reference Docs

リソースの種類について

リソースは大きく分類すると5種類あるみたい。今時点の理解はこんな感じかな…

Workloadsリソース
コンテナ起動のためのリソース

Discovery & LBリソース
コンテナへのルーティング、ロードバランシングのためのリソース

Config & Storageリソース
各種設定、データ格納のためのリソース

Clusterリソース
セキュリティ、CPU/メモリ割り当てのためのリソース

Metadataリソース
コンテナ制御のためのリソース

こんなにたくさん…まぁ色々やってくれるんだから仕方ないかぁ…

開発者が特に気にするのは上の3つらしいよ。下2つはより運用的な話かな…

でもそれぞれymlで定義するんだから、一回作っちゃえば流用もしやすいはず!

未来のわたしのため…がんばるっ

動作確認環境について

言ってなかったけど、今回の勉強は↓の書籍をバイブルにして、自分なりに理解しやすい順番・言葉で表現しています

仕組みだけでなくノウハウがギッシリ詰まった本なので、全てを網羅することは無理ですが…

Kubernetes完全ガイド (impress top gear)

Kubernetes完全ガイド (impress top gear)

サンプルコードなどはこちらから抜粋させてもらいます♪

それと、今回はKatacodaのプレイグラウンドを利用してブラウザ上で確認しています

Kubernetes Playground | Katacoda

プレイグラウンドだと、Masterと1つのNodeが初めから準備されているね

# Master Node
master $ kubectl get nodes
----
NAME     STATUS   ROLES    AGE   VERSION
master   Ready    master   94m   v1.14.0
node01   Ready    <none>   93m   v1.14.0

Nodeからだとkubectlコマンドエラーになる…なんでだろ

# Node
node01 $ kubectl get nodes
----
The connection to the server localhost:8080 was refused - did you specify the right host or port?

APIを実行するには、Masterの接続先情報と、認証情報が必要だからね。 今はMasterから実行できるし、いったん後回しにしよう

リソースを登録する

まずは基本的な操作で、ymlファイルからリソースを登録してみよう。ちなみに ymlファイルのことをKubernetesではマニフェストっていうらしいよ

この内容を実現します!っていうカンジ?笑

apiVersion: v1
kind: Pod
metadata:
  name: sample-pod
spec:
  containers:
    - name: nginx-container
      image: nginx:1.12

Podっていう種類のリソースっぽい?あ、dockerイメージも書いてある!

そう、PodはWorkloadsリソースの最小単位で、コンテナが1つ以上あるグループみたいなものかな。ここではnginxのコンテナをPodとして登録するよ

マニフェストからリソースを操作するためのコマンドは次の3つ
① 新規作成 kubectl create
② 更新 kubectl apply
③ 削除 kubectl delete

master $ kubectl apply -f sample-pod.yml
----
pod/sample-pod created

新規なのに、applyコマンド?

applyは、前回の差分を抽出して更新するんだけど、リソースが登録されてなかったら新規作成してくれるから、こっちのほうが推奨されてるんだ

な、なるほど〜?あ、Podも起動してるっぽい!

master $ kubectl get pods
----
NAME         READY   STATUS    RESTARTS   AGE
sample-pod   1/1     Running   0          13s

Kubernetesで簡単なコンテナ構成を組んでみる

現状確認

まだPodが配置されただけで、通信も何もできない状態だね

そういえばDockerのときも同じだったね…ポートフォワード?

ローカルホスト宛の通信をポートフォワードするやり方もあるけど、Kubernetesはコンテナオーケストレーションツールだからね。 各Podに対してロードバランシングするためのリソースを作る必要があるんだ

ちなみに、今のリソースの全量はkubectl get allで見れるよ

master $ kubectl get all
----
NAME             READY   STATUS    RESTARTS   AGE
pod/sample-pod   1/1     Running   0          4m23s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   54m

ClusterIPっていうのがある…Podはさっき作ったやつだけど何だろ?

serviceっていうのがDiscovery & LBリソースの一つで、ロードバランシングをしてくれるやつだよ。 ClusterIPってのはそのServiceリソースのタイプのひとつなんだ

このservice/kubernetesってやつは、デフォルトで作られてるAPI接続用のServiceリソースみたい

Workloadsリソースでコンテナを配備

ということで、まずはPodを3つくらい作って動きを見てみようか

1個だと分からないもんね♪

ReplicaSetとDeployment

ReplicaSetリソースで、コンテナの複製(レプリカ)を作れるよ

コンテナのスケーリングってやつだね!

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: sample-rs
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
      metadata:
        labels:
          app: sample-app
      spec:
        containers:
          - name: nginx-container
            image: nginx:1.12
            ports:
              - containerPort: 80

長くなってきた…あ、でもtemplateのspecは見覚えある!

そう、Podと同じだね。今回はコンテナのポートとラベルを書き加えてるけど

そのレプリカを3つ作るってことね!じゃあさっそくkubectl applyで…

…と思ったけどやっぱりこっちのマニフェストで作ろう!

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
      metadata:
        labels:
          app: sample-app
      spec:
        containers:
          - name: nginx-container
            image: nginx:1.12
            ports:
              - containerPort: 80

ええナゼっ!?…ってあれ、あまり変わらないような…リソースの種類が違う?

これはDeploymentリソース。ReplicaSetを管理して、コンテナのアップデートをサービスに影響無いよう、徐々にやってくれたり ロールバック出来るようになるんだ。

おぉ〜!これ一つでPodもReplicaSetも作れるなら、ラクチンでイイね♪

# Deploymentリソースを作成
master $ kubectl apply -f sample-deployment.yml
----
deployment.apps/sample-deployment created

# podをsample-appラベルで絞って一覧表示
master $ kubectl get pods -l app=sample-app
----
NAME                                READY   STATUS    RESTARTS   AGE
sample-deployment-6cd85bd5f-f2v8h   1/1     Running   0          17s
sample-deployment-6cd85bd5f-q9mtr   1/1     Running   0          17s
sample-deployment-6cd85bd5f-xbl5x   1/1     Running   0          17s

サクッとできた!

コンテナが落ちると、マニフェストで指定したレプリカ数に戻るよ。試しにPod1つ削除してみよう

# 指定したPodを削除
master $ kubectl delete pod sample-deployment-6cd85bd5f-f2v8h
----
pod "sample-deployment-6cd85bd5f-f2v8h" deleted

# 一覧表示
master $ kubectl get pods -l app=sample-app
----
NAME                                READY   STATUS    RESTARTS   AGE
sample-deployment-6cd85bd5f-q9mtr   1/1     Running   0          119s
sample-deployment-6cd85bd5f-xbl5x   1/1     Running   0          119s
sample-deployment-6cd85bd5f-xqbds   1/1     Running   0          35s

新しい名前のPodが1つ増えてるね、すっごい!

f:id:altrlz:20191123233704p:plain

その他のWorkloadsリソース

Deploymentリソースさえ使えればカンペキかな!?

Workloadsリソースは他にもいくつかあるよ。特定の用途で使い分けが必要だね。

DaemonSetリソース
各ノードに1Podづつ配置したい場合に利用。1PodなのでReplicaは設定できない。

StatefulSetリソース
データベースなど、永続化データボリュームの利用が必要場合に利用。

Jobリソース
1度だけの処理を実行し、コンテナが終了するような場合に利用。

CronJobリソース
Jobを定期的に作成したい場合に利用。

Discovery & LBリソースでコンテナ疎通

じゃあ次はコンテナに外部からアクセスできるようにしていこう。

Serviceリソース…だっけ?

Discovery & LBリソースには、L4ロードバランシングをするServiceリソースと、L7ロードバランシングをするIngressリソースがあるよ。

(L4とL7ってなんだ…ググろ…)

ロードバランサー(L4)とL7ロードバランサー(Pulse Secure Virtual Traffic Manager)の違いを教えてください | ニフクラ

ClusterIPとExternalIP

ちなみに今の状態でも、コンテナ間のネットワークは作られてて、お互いに通信は出来るんだ

# Pod情報を確認
master $ kubectl get pods -o custom-columns="NAME:{metadata.name}, IP:{status.podIP}"
----
NAME                                 IP
sample-deployment-6cd85bd5f-q9mtr   10.44.0.2
sample-deployment-6cd85bd5f-xbl5x   10.44.0.3
sample-deployment-6cd85bd5f-xqbds   10.44.0.1

# Podのターミナルを開く
master $ kubectl exec -it sample-deployment-6cd85bd5f-q9mtr /bin/bash

# 確認用にcurlをインストール
root@sample-deployment-6cd85bd5f-q9mtr:/# apt-get update && apt-get install -y curl

# 1個めのPodから2個めのPodにリクエスト(curlコマンドでHTTPリクエスト結果コードのみを表示)
root@sample-deployment-6cd85bd5f-q9mtr:/# curl -s http://10.44.0.3:80 -o /dev/null -w '%{http_code}\n'
----
200

200は成功レスポンスだから…ホントだ!でも外からは接続できない、ってことよね?

そう。それにPodのIP指定でしか接続できないし、ロードバランシングもしてくれないんだ。 そんな時に使うのが、Serviceリソースだよ

マニフェストはこんな感じ

apiVersion: v1
kind: Service
metadata:
  name: sample-clusterip
spec:
  type: ClusterIP
  externalIPs:
    - 172.17.0.65
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080
      targetPort: 80
  selector:
    app: sample-app

ここでClusterIPが出てくるのね!

ClusterIPは、Kubernetesクラスタ内部のネットワークで疎通が出来る仮想IPだよ。この場合 sample-clusteripに8080番ポートで接続が来たら、sample-appラベルがついたPodの80番ポートに分散される

externalIPってのもある…これは何のIPだろ?

これはNode自体のIPで、externalIPとして書くとNodeに対する接続をPodに転送することができるんだ

# NodeのIPアドレスを確認
master $ kubectl get nodes -o custom-columns="NAME:{metadata.name}, IP:{status.addresses[].address}"
----
NAME      IP
master   172.17.0.39
node01   172.17.0.65

# Nodeに対してHTTPリクエスト
master $ curl -X GET http://172.17.0.65:8080 -o /dev/null -w '%{http_code}\n'
----
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   612  100   612    0     0   282k      0 --:--:-- --:--:-- --:--:--  597k
200

これでようやく、外部からコンテナに通信できるってワケね!

f:id:altrlz:20191123233804p:plain

そう!ちなみにClusterIPだと、転送元にしたいNode分のIPを書く必要があるけど、 全てのNodeに対して転送させたい場合は、NodePortタイプがあるよ

その他のServiceタイプ

むむ…色々種類があるのね…アタマが混乱するぅ〜

他にもServiceリソースのタイプとしては↓があるけど、Discovery & LBリソースの基本はこんなところかな

LoadBalancerタイプ
Kubernetesクラスタ外部のLoadBalancerサービスから疎通できる仮想IP

Headlessタイプ
ロードバランシングでなく、クラスタ内DNSから順次PodのIPを返すタイプ(DNSラウンドロビン)

ExternalNameタイプ
外部ドメイン宛のCNAME(ドメインの別名)を返すタイプ

つづく!