40
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kubernetes超入門

Last updated at Posted at 2021-12-01

はじめに

この記事では、「CA 1Day Youth Boot Camp バックエンド/インフラエンジニア編:現場で使うコンテナ技術、Kubernetes&コンテナ入門(2021/11/24開催)」に参加したことで得られた知見をまとめます。この記事の対象者としては、

  • kubernetesについてサクッと知りたい
  • Dockerは触ったことあるけど、k8sは無い

といった方を想定しています。
私自身も、Dockerは触ったことがある(1ヶ月程度)がk8sは初心者(2日)の状態で上のインターンシップに参加したのですが、非常に勉強になりました。kubernetesに興味がある方の参考になれば幸いです。

Kubernetes(以下k8s)入門

定義

  • k8sとは、宣言的な構成管理と自動化を促進し、コンテナ化されたワークロードやサービスを管理するための、ポータブルで拡張性のあるオープンソースのプラットフォーム(公式ドキュメントより)
  • 要するに、コードベースで複数のDockerコンテナを適切に管理してくれるプラットフォーム。

Dockerとの関係

  • Docker
    • それぞれのアプリケーションごとにコンテナを作成
  • k8s
    • 複数のホストマシン間でデプロイされた複数のコンテナの管理

メリット

  • 大量のコンテナの管理
    • コードで管理(Infrastructure as Code)するので設定が柔軟に行える。
  • デプロイの高速化
    • コンテナを元にデプロイを自動で行ってくれるので、手動でデプロイする必要がない。
  • 高可用性
    • k8sはPodという最小単位で構成されているため、Podを増減させることで、可用性を高めることができる。

他にも様々なメリットがある。

k8sの概念

k8sを機能させるには、リソース(Kubernetes API オブジェクト)を使用して、実行したいアプリケーションやその他のワークロード、使用するコンテナイメージ、レプリカ(複製)の数、どんなネットワークやディスクリソースを利用可能にするかなど、クラスターの desired state(望ましい状態)をyamlで記述する。desired stateをセットするには、Kubernetes APIを使用してリソースを作成する。通常はコマンドラインインターフェイス kubectlを用いてKubernetes APIを操作する。

  • 概略図(一例)
    概略図

  • クラスタ

    • k8sの様々なリソースを管理する集合体(上図の一番外側)
  • コンポーネント:実行されるプロセス

    • マスターコンポーネント:マスターノードで実行されるもの。クラスターに関する全体的な決定(スケジューリングなど)を行います
      • kube-apiserverver
      • etcd
      • kube-scheduler
      • kube-controller-manage
    • ワーカーコンポーネント:すべてのノードで実行され、稼働中のPodの管理やKubernetesの実行環境を提供する。
      • kube-proxy
      • kubelet
      • コンテナランタイム
  • リソース

    • クラスタ内の構成要素(上図のNode, Pod)
リソース名 役割
Node クラスタで実行するコンテナを配置するためのサーバ
Namaspace クラスタ内の仮想クラスタ
Pod コンテナ実行に関する最小単位リソース
RepricaSet 同じ仕様のPodを複数生成・管理する
Deployment ReplicaSetの世代管理をする
Service Podの集合にアクセスするための経路を定義
Ingress Serviceをk8sクラスタの外部に公開する
...(他にも多くのリソースがある) ...

k8sでは、これらのリソースがクラスタ内で強調しながらコンテナシステムを構成している。以下では、各リソースにつてい詳しく見ていく。

準備

ここからは、実際に手を動かして確認していく。まずは、下準備。

準備1

  • Dockerをインストールしていない方

  • Dockerをインストールしている方

    • Docker for Desktopの設定で、k8sを有効にする
    • 公式ドキュメントに従ってkubectlをインストールする。これを使用することで、k8sクラスターに対してコマンドを実行することができる。
      Screen Shot 2021-11-26 at 1.58.01.png
$ docker version

$ kubectl version

を実行し、それぞれバージョン情報が表示されればOK。

準備2(任意)

サンプルファイルが入っているディレクトリのダウンロード

$ git clone https://github.com/yagikota/Myk8s.git

$ cd Myk8s

を実行。
Myk8sディレクトリに入った状態で、進めていく。

準備3(任意)

  • こちらに従って、Kubernetes Dashboardのインストール
  • さらに、こちらにあるようにdashboard-adminuser.yamlone-day-youth-bootcamp-ciuディレクトリに作成し、kubectl apply -f dashboard-adminuser.yamlを実行。
  • これで、Kubernetes Dashboardの使用準備が整った。

Node

Nodeとはクラスタが持つリソースで最も大きい概念。

k8sのクラスタの管理下に登録されているDockerホストのこと。k8sでコンテナをデプロイするために利用される。クラスタはマスターノードとそれ以外で構成される。

Node一覧取得

$ kubectl get nodes
NAME             STATUS   ROLES                  AGE    VERSION
docker-desktop   Ready    control-plane,master   3d3h   v1.21.5

ローカル環境のk8sであれば、クラスタ作成時(k8sを有効にした時)に作られたVMがNodeの1つとして登録されているらしい。

Namespace

Namespaceとは、k8sクラスタの内部の仮想的なクラスタ。

Namsespace一覧取得

$ kubectl get namespace
NAME                   STATUS   AGE
default                Active   3d23h
kube-node-lease        Active   3d23h
kube-public            Active   3d23h
kube-system            Active   3d23h
kubernetes-dashboard   Active   3d20h

default: As its name implies, this is the namespace that is referenced by default for every Kubernetes command, and where every Kubernetes resource is located by default. Until new namespaces are created, the entire cluster resides in ‘default’.

Pod

Podとは、コンテナの集合体。少なくとも1つのコンテナを持つ。

pod.png

Pod一覧取得

$ kubectl get pod
No resources found in default namespace.

Podを作成していないので、default namespaceにはPodが存在しない。

試しに、Podを作成する。

ファイルの説明(詳しくは、こちら

apiVersion: v1 # どのバージョンのKubernetesAPIを利用してリソースを作成するか
kind: Pod # リソースの種類
metadata: # リソースを一意に特定するための情報
  name: nginx # 文字列を指定
spec: # リソースの望ましい状態(specの正確なフォーマットは、リソースごとに異なる。cf. 参考文献 マニュフェストファイルの説明)
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80
$ kubectl apply -f pod.yml

$ kubectl get pod
NAME       READY   STATUS    RESTARTS   AGE
echo-pod   1/1     Running   0          20s

無事に作成できている。
k8s Dashboardで、default NamespaceのPodsを確認してみると、
Screen Shot 2021-11-29 at 11.51.07.png
となっている。
整理すると、

$ kubectl config current-context 
docker-desktop 

より、接続しているクラスタはdocker-desktopであるとわかる。そして、そのクラスタ内のNode一覧は、

$ kubectl get nodes
NAME             STATUS   ROLES                  AGE    VERSION
docker-desktop   Ready    control-plane,master   3d3h   v1.21.5

より、マスターノードだけ。
さらに、Namespace一覧は、

$ kubectl get namespace
NAME                   STATUS   AGE
default                Active   4d3h
kube-node-lease        Active   4d3h
kube-public            Active   4d3h
kube-system            Active   4d3h
kubernetes-dashboard   Active   4d

であるとわかる。
今回、Podをapplyすると、1つしかないNode(マスターノード)にPodが作成され、k8sのアーキテクチャー概略図は、次のようになっていると考えられる。
pod.png

ただし、Namespaceは省略した。

RepricaSet

RepricaSetの目的は、どのような時でも安定したレプリカPodのセットを維持すること。指定された理想のレプリカ数にするためにPodの作成と削除を行うことにより、その目的を達成する。新しいPodを作成するとき、Podテンプレートを使用する。

RepricaSet一覧取得

$ kubectl get rs
No resources found in default namespace.

RepricaSetを作成していないので、default namespaceにはRepricaSetが存在しない。

試しに、RepricaSetを作成する。

$ kubectl apply -f rs.yml

$ kubectl get rs
NAME       DESIRED   CURRENT   READY   AGE
frontend   3         3         3       12s

$ kubectl get pod
NAME             READY   STATUS    RESTARTS   AGE
frontend-b9dqp   1/1     Running   0          27s
frontend-h75sb   1/1     Running   0          27s
frontend-lppc7   1/1     Running   0          27s

無事に作成できている。
ファイルの説明(詳しくは、こちら)

apiVersion: apps/v1
kind: ReplicaSet
metadata: # リソースを一意に特定するための情報
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec: # ReplicaSetの望ましい状態を定義
  replicas: 3 # Podの数
  selector: # .spec.template.metadata.labelsと一致させる。ReplicaSetが所有するPodを指定するため。(今回の場合、tier: frontendが一致している)
    matchLabels:
      tier: frontend
    matchExpressions:
      - {key: tier, operator: In, values: [frontend]}
  template: # template以下はPodの定義と同様に書く。templateの定義を持つPodの複製を行う。
    metadata:
      labels: # spec.selectorと一致させる(2つのラベルのうちtier: frontendが一致している)
        app: guestbook
        tier: frontend
    spec: # Podの望ましい状態を定義
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v3
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        env:
        - name: GET_HOSTS_FROM
          value: dns
          # If your cluster config does not include a dns service, then to
          # instead access environment variables to find service host
          # info, comment out the 'value: dns' line above, and uncomment the
          # line below.
          # value: env
        ports:
        - containerPort: 80

Deployment

Deploymentは、ReplicaSetを管理・操作するためのリソース。実用上では、ReplicaSetを直接用いるのではなく、Deploymentのマニュフェストファイルを扱うことが多いらしい。

Deployment一覧取得

$ kubectl get deployments
No resources found in default namespace.

Deploymentを作成していないので、default namespaceにはDeploymentが存在しない。

試しに、Deploymentを作成する。

ファイルの説明(詳しくは、こちら

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec: # Deploymentの望ましい状態を定義
  replicas: 3 # 3つのレプリカPodを作成
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

作成コマンド

$ kubectl apply -f dep.yml

$ kubectl get deployments
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           4m48s

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-66b6c48dd5   3         3         3       5m4s

$ kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-66b6c48dd5-6jt7m   1/1     Running   0          5m46s
nginx-deployment-66b6c48dd5-89frd   1/1     Running   0          5m46s
nginx-deployment-66b6c48dd5-m98mj   1/1     Running   0          5m46s

となり、宣言通りのリソースが作成されている。

Service

Serviceとは、k8sクラスタ内で、Podの集合(主にReplicaSet)に対する経路やサービスディスカバリを提供するためのリソース。要は、k8sクラスタ内のネットワークをいい感じに捌いてくれるリソース。

Serviceには様々な種類がある。

  • ClusterIP(デフォルト)
    • クラスター内部のIPでServiceを公開する。Serviceはクラスター内部からのみ疎通性がある。
  • NodePort
    • 各NodeのIPにて、静的なポート(NodePort)上でServiceを公開する。そのNodePort のServiceが転送する先のClusterIP Serviceが自動的に作成される。<NodeIP>:<NodePort>にアクセスすることによってNodePort Serviceにアクセスできるようになる。
  • LoadBalancer
    • クラウドプロバイダーのロードバランサーを使用して、Serviceを外部に公開する。クラスター外部にあるロードバランサーが転送する先のNodePortとClusterIP Serviceは自動的に作成される。
  • ExternalName
    • CNAMEレコードを返すことにより、externalNameフィールドに指定したコンテンツ(例: foo.bar.example.com)とServiceを紐づける。しかし、いかなる種類のプロキシーも設定されない。

ClusterIP

まず、ClusterIPについて見ていく。

Serviceのマニュフェストファイル

apiVersion: v1
kind: Service
metadata:
  name: echoserver
spec:
  ports:  # The list of ports that are exposed by this service.
  - port: 80 # The port that will be exposed by this service.
    targetPort: 8080 # ターゲットとなるPodのポート番号
    protocol: TCP # 
  selector:
    app: echoserver # このラベルと一致するPodがserviceのターゲットとなり、serviceを経由してtcpリクエストが流れる 

まず、

$  kubectl apply -f dep2.yml

で、予めPodを作っておく。

$ kubectl get pod
NAME                          READY   STATUS    RESTARTS   AGE
echoserver-8499b7dffc-24w99   1/1     Running   0          37m
echoserver-8499b7dffc-7fn2h   1/1     Running   0          37m
echoserver-8499b7dffc-9mfsr   1/1     Running   0          37m
# Service をデプロイ
$ kubectl apply -f service.yml

# 割り振られた IP を確認。
$ kubectl describe svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
echoserver   ClusterIP   10.98.108.161   <none>        80/TCP    37m
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP   6d23h

ここで、ClusterIPは、クラスタ内部でのみ使用可能な仮想IPである。
サービスにアクセスするために、 クラスタ内に適当にPodを建ててsrviceの80番ポートにアクセスする(マニュフェストファイルで、.spec.ports.port=80としたため)

$ kubectl run --image=centos:6 --restart=Never --rm -i testpo -- curl -s http://[svc-ip]:80
foo
pod "testpo" deleted

と表示されればOK。(ただし、[svc-ip] = 10.98.108.16, fooと表示される理由
ここで、ClusterIPはデプロイのたびに変わるので、その度にいちいち調べるのは面倒。そこで、こちらにあるように、DNS名を用いてアクセスしてみる。

$ kubectl run --image=centos:6 --restart=Never --rm -i testpo -- curl -s http://echoserver.default.svc.cluster.local:80
foo
pod "testpo" deleted

と表示されればOK。

整理すると、クラスタ内に適当に建てたPodから、http://[svc-ip]:80またはhttp://echoserver.default.svc.cluster.local:80でServiceにアクセスすると、マニュフェストファイル通り、app: echoserverのラベルを持つPodの8080ポートにリクエストが流れ、fooが表示される。

NodePort

次に、NodePortについて見ていく。

Serviceのマニュフェストファイル

apiVersion: v1
kind: Service
metadata:
  name: echoserver
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
  type: NodePort # type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer.
  selector:
    app: echoserver

先ほどと同様に、予めPodを作っておく。(作っていない場合)

# Service をデプロイ
$ kubectl apply -f svc_node_port.yml

# 割り振られた IP を確認。
$ kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
echoserver   NodePort    10.105.66.102   <none>        80:32694/TCP   32s
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        7d

NodePortサービスを作成した場合、80:32694/TCPとあるように、ノードの32694ポートからServiceへアクセスできる。これにより、Serviceをk8sクラスタの外に公開できる。

実際、ローカル(クラスタ外)からService(クラスタ内)にアクセスできる。

$ curl http://localhost:32694
foo

また、

適当に建てたPod(クラスタ内)からもService(クラスタ内)にアクセスできる。

$ kubectl run busybox -it --rm --image=busybox --restart=Never -- /bin/sh -c "wget -q -O- [NodeIP]:30937"
foo

([NodeIP]=$ kubectl get node -owideコマンドで表示されるINTERNAL-IP)
ともできる。

整理すると、クラスタ内外からServiceにアクセスすると、マニュフェストファイル通り、app: echoserverのラベルを持つPodの8080ポートにリクエストが流れ、fooが表示される。

Ingress

Ingressは、クラスター内のServiceに対する外部からのアクセス(主にHTTP)を管理するリソース。NodePortはL4層(トランズポート層)までしか扱えないが、IngressはL7層(アプリケーション層)まで制御できる。

公式にもあるように、ローカルk8s環境ではIngressを使用してServiceを公開することができない。
こちらに従って、nginx_ingress_controllerをデプロイしなければならない。

正直、Ingressについての理解が曖昧なので、ここでは参考文献を紹介するだけにする。

参考文献

40
37
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?