Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
11
Help us understand the problem. What is going on with this article?

More than 1 year has passed since last update.

@saki-engineering

Kubernetes入門 〜ローカル環境で触ってからEKSへのデプロイまで〜

この記事について

「Kubernetesを実際に触って勉強してみたい!」となって参考記事を探してみると、多くの記事がいきなりEKSやGKEといったクラウド上の本番環境を触るところからのスタートになっています。
「いきなり本番環境はちょっと……」「まずはローカル環境で軽く触って慣れてからクラウド上の環境を構築したい!」という方向けに、

  1. Kubernetesのアーキテクチャの勉強
  2. ローカルで実際に触ってみる
  3. EKSにデプロイ

という流れで、Kubernetesってこんな感じなんだ!という雰囲気を紹介したいと思います。

使用する環境・バージョン

  • OS : macOS Mojave 10.14.5
  • Docker.app : 2.2.0.5
  • Kubernetes : v1.15.5
  • eksctl : 0.23.0
  • aws-cli : 2.0.10

前提条件

Docker App, AWS CLIはインストール済みの状態からスタートします。

読者に求める前提知識

  • コンテナをある程度扱えること
  • コンテナオーケストレーションという概念について知っていること
  • VPC, セキュリティグループといったAWSの用語がある程度わかること
  • (ECSの知識があると所々の例え話がわかりやすくなりますが、必須ではありません)

Kubernetesのアーキテクチャ

まず、Kubernetesの構成要素について詳しく説明します。
k8s.png
画像出典:Step by Step Introduction to Basic Concept of Kubernetes
参考:順を追って学ぶKubernetesのキホン〜ローカル環境でKubernetesクラスターを作成してKubernetesの概念を理解する〜

Master

コンテナの起動・削除・カナリアデプロイといった、Kubernetesができるコンテナ管理・制御機能を担う頭脳部分です。
Masterを構成する主な要素は4つです。

  • API Server
  • etcd
  • Scheduler
  • Controller Manager

API Server

Kubernetesが扱うリソース(コンテナやボリュームなど)を操作するためのAPIがここで公開されています。
開発者(Devops)は、ターミナルでkubectlコマンドを使うことでこのAPIを叩き、リソースの作成・削除・制御を行います。

etcd

分散KVS(キーバリューストア)です。作成されたクラスター(コンテナを動かすサーバー群)の設定情報がここに保存されます。

Scheduler

コンテナの適切な配置場所を決定する機能を持ちます。
この機能があるがゆえに、「開発者は円滑な運用のために、どこのサーバーにコンテナを作成すべきか?」という設定を手動で行う必要がなくなり、アプリ側の開発に集中することができます。

Controller Manager

Kubernetesが扱うリソースの制御を行います。
例えば、「起動コンテナの数を保つ」「サーバーダウン時の通知・対応を行う」「PodとServiceの紐付け」などを行います。

参考:Kubernetes公式ドキュメント Kubernetesのコンポーネント

Cluster(クラスター)

コンテナを動かすサーバー群のことをKubernetesではクラスターと呼びます。
AWSのECSにおけるクラスターとほぼ同義です。

Node(ノード)

コンテナを動かすサーバー1つ1つのことをKubernetesではノードと呼びます。
クラスターとは「1つのクラスターの中に、任意の数のノードが含まれる」という関係になっています。
上記のMasterに管理される対象であることから、「Slave Node」と呼ばれることもあります。

ECSにおける、クラスター内のEC2インスタンスとほぼ同義です。
ローカルでKubernetesを動かす場合では、そのPCそのものが1つのノードという扱いになります。

ノードの中に作成されるリソースは主に4つです。

  • Pod
  • Kubelet
  • Container Engine
  • Kube Proxy

Pod

ノード上で動かすコンテナ(組)のことです。
Podを構成するコンテナは複数個でもOKです。例えば、「nginxのコンテナとアプリコンテナの2つをセットにして1つのPodにする」ということができます(ECSでのタスクと同様です)。

Kubelet

Masterからのリソース・スケジュール管理を受け付けるためのエージェントです。
ECSにおけるECS Container Agentとほぼ同義です。

Container Engine

Dockerといった、コンテナの作成・削除等の処理を実際に実行するエンジンです。

Kube Proxy

ノードが受け付けたユーザーからのリクエストを、適切なPodに割り当て転送するネットワークプロキシです。

参考:Kubernetes公式ドキュメント Kubernetesのコンポーネント

始める前の初期状態

Kubernetesを使っていない状態でも、MasterにあるAPIを叩くためのkubectlコマンドは存在します。Docker Appインストール時にkubectlコマンドも同時にインストールされているからです。

# このように、kubectlコマンドの存在がwhichコマンドで確認可能
$ which kubectl
/usr/local/bin/kubectl

しかし、コマンドのバージョン確認を行うと、以下のような表示になります。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:16:51Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"darwin/amd64"}
Unable to connect to the server: EOF

これは、「開発者クライアントの準備(=kubectlコマンド)は存在するが、それを受け付けるMasterのAPI Serverに接続できない(まだ存在しない)」という状態だということです。

このままではKubernetesを利用できません。そのため、次にKuberbetesを有効化して、ローカルにKubernetes Serverを立ち上げる操作を行います。

ローカルでのKubernetes有効化

Kubernetes有効化設定の手順

MacのメニューバーにあるDockerアイコンをクリックすると、以下のようなメニューが表示されます。
スクリーンショット 2020-07-04 18.33.37.png
ここから"Preferences"を選択すると、以下のような画面になります。
スクリーンショット 2020-07-04 18.35.11.png
左のバーから"Kubernetes"を選択します。すると、Docker AppにおけるKubernetesの設定画面が以下のように表示されます。
スクリーンショット 2020-07-04 18.35.22.png
この中から、"Enable Kubernetes"のチェックボックスに印を入れ、"Apply & Restart"で設定を保存します。

スクリーンショット 2020-07-04 18.38.26.png
このような画面になるのでしばらく待機します。
スクリーンショット 2020-07-04 18.41.40.png
ウィンドウ下部に「Kubernetes running」が表示されれば準備はOKです。

起動確認

本当にKubernetesのサーバーが立ち上がったかどうかをコマンドで確認します。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:16:51Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:07:57Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}

先ほどはUnable to connect to the server: EOFと表示されていた部分にサーバーのバージョンが表示されています。無事に起動できたようです。

また、Kubernetesを有効化したことで、Masterを構成するAPI ServerやSchedulerといった機能を担うコンテナイメージがpullされていることも確認できます。

# 以下のイメージが新規にpullされた
$ docker images
docker/desktop-storage-provisioner                                           v1.0                605a0f683b7b        4 months ago        33.1MB
k8s.gcr.io/kube-apiserver                                                    v1.15.5             e534b1952a0d        8 months ago        207MB
k8s.gcr.io/kube-controller-manager                                           v1.15.5             1399a72fa1a9        8 months ago        159MB
k8s.gcr.io/kube-proxy                                                        v1.15.5             cbd7f21fec99        8 months ago        82.4MB
k8s.gcr.io/kube-scheduler                                                    v1.15.5             fab2dded59dd        8 months ago        81.1MB
docker/kube-compose-controller                                               v0.4.23             a8c3d87a58e7        13 months ago       35.3MB
docker/kube-compose-api-server                                               v0.4.23             f3591b2cb223        13 months ago       49.9MB
k8s.gcr.io/coredns                                                           1.3.1               eb516548c180        17 months ago       40.3MB
k8s.gcr.io/etcd                                                              3.3.10              2c4adeb21b4f        19 months ago       258MB
k8s.gcr.io/pause                                                             3.1                 da86e6ba6ca1        2 years ago         742kB

それなら、Masterを構成するコンテナがMac上で起動しているのか?と思ったのですが、docker psでは起動中のコマンドが確認できませんでした。

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

(おまけ)Kubernetes Dashboardの用意

「今のクラスターでは何が動いているのか?」「起動したPodはノードのリソース(CPUなど)をどれくらい食っているのか?」といった情報を、コマンドラインではなくGUIで確認できるようにするツールが存在します。それがKubernetes Dashboardです。
これはオプションツールなので、Kubernetesをインストールしただけの状態ではこれを持っていません。そのため、これを使いたい場合は個別にインストール・起動する必要があります。

Dashboardのリソース作成(≒インストール)

Kubernetes Dashboardは、Kubernetesで作られたサービス(アプリ)という形で配布されています。
そのため、Dashboardを立ち上げるための設定ファイル(Kubernetesではマニュフェストファイルという)を参照して、その内容をローカルに展開・起動するという形をとります。

kubetcl applyというコマンドで、マニュフェストファイルからDashboardのリソース作成を行います。-fオプションで、インターネット上に公開されているDashboardのマニュフェストファイルを指定しています。

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta8/aio/deploy/recommended.yaml

namespace/kubernetes-dashboard created
serviceaccount/kubernetes-dashboard created
service/kubernetes-dashboard created
secret/kubernetes-dashboard-certs created
secret/kubernetes-dashboard-csrf created
secret/kubernetes-dashboard-key-holder created
configmap/kubernetes-dashboard-settings created
role.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard created
rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
deployment.apps/kubernetes-dashboard created
service/dashboard-metrics-scraper created
deployment.apps/dashboard-metrics-scraper created

参考:Kubernetes公式ドキュメント Web UI (Dashboard)

Dashboardログインのためのトークン取得

Dashboardにアクセスするためにはトークンによる認証が必要です。なので、そのトークンを確認します。
以下のコマンドを打って、default-token-*****という名前のものを探します。

$ kubectl -n kube-system get secret
NAME                                             TYPE                                  DATA   AGE
()
default-token-ss24c                              kubernetes.io/service-account-token   3      168m
()

ここではdefault-token-ss24cというものが見つかりました。これを元に今度は以下のコマンドを打ちます。

$ kubectl -n kube-system describe secret default-token-ss24c
Name:         default-token-ss24c
Namespace:    kube-system
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: default
              kubernetes.io/service-account.uid: 92b6b004-dbf3-4593-adf0-fe2a2eb253f4

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1025 bytes
namespace:  11 bytes
token:      *********************

ここに表示されているtokenをこの後利用するので、メモしておきましょう。

参考:Docker for Mac で Kubernetes をちょっと試す

プロキシサーバの起動

Dashboardサービスにアクセスするために、プロキシサーバを起動する。
以下のコマンドを打つことで、プロキシがフォアグラウンド稼働をします。(そのため、止めたくなった場合はCtrl+Cで停止できます。)

$ kubectl proxy
Starting to serve on 127.0.0.1:8001

ログイン

プロキシサーバを起動させた状態で、ブラウザで以下のアドレスにアクセスします。

http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/ 

スクリーンショット 2020-07-04 21.24.52.png
Tokenを選択して、フォームに先ほど確認したトークンを入力します。

スクリーンショット 2020-07-04 21.33.41.png
このようなダッシュボード画面が表示されれば成功です。

後片付け

Dashboardのリソースを削除したい場合は、以下のコマンドで行えます。

$ kubectl delete -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta8/aio/deploy/recommended.yaml

namespace "kubernetes-dashboard" deleted
serviceaccount "kubernetes-dashboard" deleted
service "kubernetes-dashboard" deleted
secret "kubernetes-dashboard-certs" deleted
secret "kubernetes-dashboard-csrf" deleted
secret "kubernetes-dashboard-key-holder" deleted
configmap "kubernetes-dashboard-settings" deleted
role.rbac.authorization.k8s.io "kubernetes-dashboard" deleted
clusterrole.rbac.authorization.k8s.io "kubernetes-dashboard" deleted
rolebinding.rbac.authorization.k8s.io "kubernetes-dashboard" deleted
clusterrolebinding.rbac.authorization.k8s.io "kubernetes-dashboard" deleted
deployment.apps "kubernetes-dashboard" deleted
service "dashboard-metrics-scraper" deleted
deployment.apps "dashboard-metrics-scraper" deleted

contextの選択

Kubernetesを使う準備が整いました。これでクラスター上にPod等のリソースを作ってアプリを構築していくことができます。

しかし、ここで問題になるのが構築していく対象の設定です。
例えば、「クラスターAとクラスターBが現在存在していて、今はクラスターAを使いたい」という状況では、開発者が使うkubectlコマンドでクラスターAのみがいじれないといけないわけです。
また、「クラスターA上でアプリチームがいじれるリソースと、同じクラスターAでインフラチームがいじれるリソースという風に触れる範囲を制御したい」という場合では、同じkubectlコマンドでも同じ権限(機能)を持たせてはいけないことになります。

この、「どのクラスターを、どの権限でkubectlコマンドで動かすのか」という設定集がcontextです。開発者は、手を入れたいリソースに合わせて、適切なcontextを選択・使用する必要があるわけです。

contextの確認・変更

以下のコマンドで、現在存在するcontext一覧を確認することができます。

$ kubectl config get-contexts
CURRENT   NAME                 CLUSTER          AUTHINFO         NAMESPACE
*         docker-desktop       docker-desktop   docker-desktop   
          docker-for-desktop   docker-desktop   docker-desktop

現在選択されているcontextは"docker-desktop"です。
"docker-for-desktop"に切り替えたい場合は、以下のようなコマンドを打ちます。

$ kubectl config use-context docker-for-desktop
Switched to context "docker-for-desktop".

$ kubectl config get-contexts
CURRENT   NAME                 CLUSTER          AUTHINFO         NAMESPACE
          docker-desktop       docker-desktop   docker-desktop   
*         docker-for-desktop   docker-desktop   docker-desktop

以下、ローカル上でのKubernetes操作は、この"docker-for-desktop"で行うこととします。

参考:[Kubernetes入門] kubectlのアクセス先(コンテキスト)を切り替える方法

ローカルで動いているクラスターの状態確認

Docker Appによって作られたクラスター(実態は、ローカルマシンというノード1台のみで構成)上には、デフォルトでどんな状態で作られているのかを確認してみましょう。

クラスター情報の確認

クラスターのMaster,DNSサーバーがどのアドレスで動いているのかが確認できます。

$ kubectl cluster-info
Kubernetes master is running at https://kubernetes.docker.internal:6443
KubeDNS is running at https://kubernetes.docker.internal:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

クラスター内部からこれらのアドレスにアクセスすることによって、サービスを利用することができます。

ノードの確認

一覧取得

クラスター上で動いているノード一覧は、kubectl get nodeコマンドで取得できます。

$ kubectl get node
NAME             STATUS   ROLES    AGE   VERSION
docker-desktop   Ready    master   24h   v1.15.5

詳細情報取得

特定ノードの詳細な状態が知りたい場合は、kubectl describe nodeで確認できます。

$ kubectl describe node <node-name>

Podの確認

一覧取得

クラスター上で動いているPod一覧も、kubectl get podコマンドで取得できます。

$ kubectl get pod -n kube-system
NAME                                     READY   STATUS    RESTARTS   AGE
coredns-5c98db65d4-qshvb                 1/1     Running   1          44h
coredns-5c98db65d4-zpbfq                 1/1     Running   1          44h
etcd-docker-desktop                      1/1     Running   0          44h
kube-apiserver-docker-desktop            1/1     Running   0          44h
kube-controller-manager-docker-desktop   1/1     Running   0          44h
kube-proxy-kd5kf                         1/1     Running   0          44h
kube-scheduler-docker-desktop            1/1     Running   0          44h
storage-provisioner                      1/1     Running   1          44h

注: オプション-nは、後述する「名前空間」を指定するコマンドです。上の例の場合、クラスター上のkube-systemという名前空間で動いているPod一覧が取得されています。

詳細情報取得

特定Podの詳細な状態が知りたい場合は、kubectl describe podで確認できます。

$ kubectl describe pods <pod-name>

ログ取得

Podででたログを確認したい場合は、以下のコマンドでログを標準出力に表示させることができます。

$ kubectl logs <pod-name>

Serviceの確認

Serviceとは

PodのIPアドレスは不定です。つまり、Pod起動のたびにIPアドレスが変化します。
そのため、そのPodを利用する側がIPアドレスの変化を気にすることなく、それらにアクセスするための単一エンドポイントが必要です。Kubernetesでその単一エンドポイントを作るための仕組みをサービスと呼びます。
(docker-composeにおいてコンテナのIPアドレスでアクセスするのではなく、サービスを作成してその名前でアクセスするのと同じです。)

一覧取得

kubectl get serviceコマンドで可能です。

$ kubectl get service -n default
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   24h

注: -nオプションの効果はPodのときと同じで、名前空間(後述)を指定するものです。

詳細情報取得

kubectl describe serviceで可能です。

$ kubectl describe service <service-name>

名前空間の確認

名前空間とは

名前空間とは、1つの物理クラスターを論理的に分割して仮想のクラスターを作成する機能のことです。

例えば、「クラスターA上でアプリBとアプリCを両方同時に、干渉しないように動かしたい」という場合は、クラスターA上にアプリB用、アプリC用の名前空間を作成し、その名前空間上にそれぞれのアプリのPodやサービスをデプロイすることで、クラスターAを仮想的に二つの別クラスター空間と扱うことができます。

一覧取得

kubectl get namespaceコマンドで、現在のクラスター上に存在する名前空間を確認することができます。

$ kubectl get namespace
NAME                   STATUS   AGE
default                Active   24h
docker                 Active   24h
kube-node-lease        Active   24h
kube-public            Active   24h
kube-system            Active   24h
kubernetes-dashboard   Active   21h

デフォルトで作られる名前空間は以下の4つです。

  • default
  • kube-public
  • kube-system
  • kube-node-lease

また、Docker Appがある場合は"docker"名前空間が、Kubernetes Dashboardを入れている場合は"kubernetes-dashboard"名前空間があります。

default名前空間

デフォルトの名前空間です。
名前空間の指定が必要なリソース作成時にそれを指定しないと、自動的にdefault名前空間が使われます。kubectl get等のコマンド実行時に-nオプションをつけないときも、default名前空間上の情報が取得されます。
デフォルトでは1つのサービスが存在します。

$ kubectl get all
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   45h

このサービスは、Master(≒API Server)にリクエストを送るためのものです。
参考:What's the purpose of the default kubernetes service?

kube-public名前空間

全contextからアクセス可能なリソースを配置するための空間です。
作成初期にリソースは存在しません。

kube-system名前空間

Kubernetesシステムによって作成されたオブジェクト(API Serverなど)のための名前空間です。
ここに存在するリソースは以下の通りです。

$ kubectl -n kube-system get all
NAME                                         READY   STATUS    RESTARTS   AGE
pod/coredns-5c98db65d4-qshvb                 1/1     Running   1          44h
pod/coredns-5c98db65d4-zpbfq                 1/1     Running   1          44h
pod/etcd-docker-desktop                      1/1     Running   0          44h
pod/kube-apiserver-docker-desktop            1/1     Running   0          44h
pod/kube-controller-manager-docker-desktop   1/1     Running   0          44h
pod/kube-proxy-kd5kf                         1/1     Running   0          44h
pod/kube-scheduler-docker-desktop            1/1     Running   0          44h
pod/storage-provisioner                      1/1     Running   1          44h


NAME               TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
service/kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   44h

NAME                        DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                 AGE
daemonset.apps/kube-proxy   1         1         1       1            1           beta.kubernetes.io/os=linux   44h

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/coredns   2/2     2            2           44h

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/coredns-5c98db65d4   2         2         2       44h
  • pod/coredns: kube-dnsのサービスにアクセスがあったら、問い合わせが来るDNSサーバー
  • pod/etcd-docker-desktop: etcd本体
  • pod/kube-apiserver-docker-desktop: API Serverの本体
  • pod/kube-controller-manager-docker-desktop: Controller Managerの本体
  • pod/kube-proxy: サービスに接続するプロキシ
  • pod/kube-scheduler-docker-desktop: Schedulerの本体
  • pod/storage-provisioner: ストレージの割り当てを行う
  • service/kube-dns: クラスター内に内蔵されているDNSサービス

時折「-docker-desktop」と付いているのは、「クラスターdoccker-desktop用」という意味だと思われます。

参考:Kubernetes公式ドキュメント Kubernetesのコンポーネント

kube-node-lease名前空間

Kubernetes 1.13から導入。NodeLeaseが有効になっている場合、各ノードはこの名前空間にオブジェクトを一つ作って、それを更新し続けることで死活監視を行います。
参考:Kubernetes公式ドキュメント ノード

docker名前空間

Kubernetesには、専用のマニュフェストファイルからだけではなく、docker-compose.ymlからデプロイする機能が存在します。
この機能を提供するサービスはここに展開されているようです。
参考:Docker for MacでKubernetes構築 #composeとの統合

kubernetes-dashboard名前空間

Kubernetes Dashboardをデプロイするための名前空間です。

詳細情報取得

特定の名前空間の詳しい状態が知りたい場合は、kubectl describeを使用します。

$ kubectl describe namespace <name>
Name:         <name>
Labels:       <none>
Annotations:  <none>
Status:       Active

No resource quota.

No resource limits.

マニュフェストファイルを用いてアプリをデプロイ

ここからは、マニュフェストファイルでアプリ・コンテナの設定を記述して、それをローカルのKubernetes上にデプロイしていきたいと思います。
デプロイするアプリが1つだけの場合、名前空間はdefaultを使用するのが楽でしょう。
以下、特筆しない限り名前空間はdefaultです。

Podを作ってみる

まず手始めに、Podを単独で作ってみましょう。

マニュフェストファイル作成

pod.ymlという名前でマニュフェストファイルを作成し、以下のように記述します。
今回は、MySQLのコンテナPodを作ってみます。

pod.yml
apiVersion: v1
kind: Pod
metadata:
    name: k8s-mysql
    labels:
      app: myapp
      tier: db
spec:
    containers:
    - name: k8s-mysql
      image: mysql:5.7
      volumeMounts:
      - name: k8s-mysql-storage
        mountPath: /var/lib/mysql
      ports:
      - containerPort: 3306
      env:
      - name: MYSQL_ROOT_USER
        value: root
      - name: MYSQL_ROOT_PASSWORD
        value: pass
      - name: MYSQL_DATABASE
        value: sampledb
    volumes:
    - name: k8s-mysql-storage
      hostPath:
        path: /Users/myname/Desktop/k8s

記述の意味は以下の通りです。

pod.yml
# KubernetesのAPI Serverのバージョン
apiVersion: v1
# マニュフェストファイルで何を作ろうとしているか。
kind: Pod
# Podにつける識別子
metadata:
    # Pod名
    name: k8s-mysql
    # この場合「key:app, value:myapp」「key:tier, value:db」という2つのラベルが付く
    labels:
      app: myapp
      tier: db
# 作成するPodの定義
spec:
    # 所属するコンテナ
    containers:
      # コンテナの名前
    - name: k8s-mysql
      # 使用するイメージ
      image: mysql:5.7
      # マウントするボリュームの名前と、マウントさせるディレクトリパス(コンテナ内)
      volumeMounts:
      - name: k8s-mysql-storage
        mountPath: /var/lib/mysql
      # containerPortで、Podコンテナの何番ポートを開けるかを指定
      ports:
      - containerPort: 3306
      # コンテナに渡す環境変数
      env:
      - name: MYSQL_ROOT_USER
        value: root
      - name: MYSQL_ROOT_PASSWORD
        value: pass
      - name: MYSQL_DATABASE
        value: sampledb
    # 所属するボリューム
    volumes:
      # ボリューム名
    - name: k8s-mysql-storage
      # hostPathの場合、Podをホストするノード(ローカルの場合Kubernetesを動かしているPC)のどこのディレクトリにデータを保存するかの指定
      hostPath:
        path: /Users/myname/Desktop/k8s

参考:kubernetesによるDockerコンテナ管理入門
volumeについて参考:Kubernetesで使えるボリューム・タイプのチートシート

ローカルに存在するコンテナイメージを使いたい場合のマニュフェストファイルの記述

Docker Hubといったリポジトリに公開していない、自分のPCの中にあるイメージを使ってPodを作りたいという場合もあるでしょう。
その場合は、マニュフェストファイルを以下のように書きます。

pod.yml
(略)
      image: my-local-image-name
      # イメージを公開リポジトリから取らないという設定記述
      imagePullPolicy: Never
(略)

参考:kubenetesでローカルコンテナイメージからコンテナを作成する方法

デプロイ

作成したマニュフェストファイルを元にリソースを作る場合は、kubectl createコマンドを使用します。

$ kubectl create -f pod.yml

後片付け

削除するときはkubectl deleteを使います。

$ kubectl delete -f pod.yml

なお、Podが削除されても、マウントされたボリューム(今回の場合はローカルPCの/Users/myname/Desktop/k8s)の中身はそのまま残ります。

serviceを作ってみる

先ほど作成したMySQLのPodにエンドポイントを作るために、サービスを作成します。

マニュフェストファイルの作成

Pod作成のときと同様、まずはマニュフェストファイルを作成します。

service.yml
apiVersion: v1
kind: Service
metadata:
  name: k8s-mysql-service
  labels:
    app: myapp
spec:
  type: NodePort
  ports:
    - port: 3306
      targetPort: 3306
      protocol: TCP
  selector:
    app: myapp
    tier: db

記述の意味は以下の通りです。

service.yml
apiVersion: v1
kind: Service
metadata:
  # サービス名
  name: k8s-mysql-service
  labels:
    app: myapp
# 作成するサービスの定義
spec:
  # 全てのノードの3306番ポートにアクセスすることでこのサービスにアクセスされるようになる
  type: NodePort
  ports:
      # サービスがlistenするポート
    - port: 3306
      # サービスが転送するpodのlistenポート
      targetPort: 3306
      # 使用プロトコル
      protocol: TCP
  # 以下のlabelがついているpodをサービスの対象に含める(今回の場合、上記で作成したpodにつけた2つのラベルを指定)
  selector:
    app: myapp
    tier: db

参考:Kubernetes道場 9日目 - Serviceについて

デプロイ

Pod同様、kubectl createで作成します。

ただ、今回の場合はサービスと、その対象のPodの2つを作成する必要があります。
kubectl create-fコマンドでマニュフェストファイルを複数指定してもいいのですが、面倒な場合は、マニュフェストファイルを一つのディレクトリにまとめて、そのディレクトリ以下にあるyamlファイル全てを作成対象にするというやり方をとります。

# ./dirディレクトリ以下にあるファイル全てを参照する
$ kubectl create -f ./dir

./dir以下のymlファイルを全て見て、リソースを作る。

サービスにアクセスできるか確認

クラスター内でサービスに正しくアクセスできるかどうか確認してみましょう。

クラスター中にあるどれか1つのpodの中にログインして確かめてみます。
Podへのログインは以下のコマンドで行えます。

$ kubectl exec -it <podname> -- /bin/bash

まずは、サービスに接続するためにはどんなドメインを使えばいいのかを調べてみましょう。

# podの中で実行
# dig, nslookupといったDNS接続確認を行うコマンドをインストール
$ apt update
$ apt get install dnsutils

# サービス名でnslookup
$ nslookup k8s-mysql-service
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   k8s-mysql-service.default.svc.cluster.local
Address: 10.110.179.176

# サービス名でdig
$ dig k8s-mysql-service

; <<>> DiG 9.11.5-P4-5.1+deb10u1-Debian <<>> k8s-mysql-service
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 34032
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 4529e3e33e3e0a28 (echoed)
;; QUESTION SECTION:
;k8s-mysql-service.             IN      A

;; Query time: 34 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Mon Jul 06 17:16:29 UTC 2020
;; MSG SIZE  rcvd: 58

どちらのコマンドでも、マニュフェストファイルで指定したサービス名k8s-mysql-serviceでアクセスできることが確認できました。

また、nslookupコマンドで取得できたIPアドレス10.110.179.176は、kubectl describeで確認できるサービスのIPと一致します。

# ローカルで実行
$ kubectl describe service k8s-mysql-service
#(略)
IP:                       10.110.179.176
#(略)

この名前解決を利用して、クラスター内からMySQLサービスに接続してみましょう。

# Pod内で実行
# mysqlサービスへのアクセス確認のため、sqlクライアントのインストールを行う
$ apt-get install default-mysql-client
$ mysql -h k8s-mysql-service -u root -p
Enter password:
MySQL [(none)]>

このように、サービス名を使うことで接続できました。

参考:Kubernetesの名前解決を確認する

serviceにMacのローカルホストを接続

Kubernetes Dashboardのように、起動しているサービスにクラスタ外のMacから接続したい!という状況があると思います。
例えばweb-serviceという名前のサービスを作り、そのサービスにMacのLocalhostを繋ぎブラウザ閲覧したいとします。
この場合、マニュフェストファイルを以下のように作ります。

web.yml
apiVersion: v1
kind: Service
metadata:
  name: web-service
  labels:
    app: myapp
spec:
  # ここのtypeをLoadBalancerにすることが重要
  type:
    LoadBalancer
  ports:
    - port: 9090
      targetPort: 9090
      protocol: TCP
  selector:
    app: myapp
    tier: web

このサービスをデプロイして、kubectl getで情報を確認してみます。

$ kubectl get service
()
NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/web-service   NodePort    10.101.53.215   localhost     8080:31392/TCP   4s

この場合、web-serviceにはMacのhttp://localhost:8080でアクセスすることができます。

サービスのTypeについて

今回、NodeTypeやLoadBalancerといった種類のサービスを扱いましたが、それぞれのネットワーク構成については、以下の記事がわかりやすいので紹介しておきます。
参考:Kubernetes ネットワーク構成

developmentを作ってみる

Pod定義でコンテナを増やしていってもいいのですが、今後の運用面を考えてコンテナはdevelopmentという形でデプロイしましょう。

developmentとは

developmentは、ReplicaSet(レプリカセット)を管理するための仕組みです。

まず、レプリカセットについて説明します。
例えば、同じPodを複数個作って冗長化したい!という場合は、Pod単独をいくつも繰り返し作るのではなく、レプリカセットという、「Podと作りたい個数を指定することで、指定Podのコピーを指定数だけ作る」機能で作ります。
(ECSでサービスを作成する際に、タスクを同時に何個起動させるかを選択するのと思想は同じです)

また、Podの内容を更新したい!というときは、レプリカセット単独ではなく、development中のレプリカセットという形でデプロイすることで、Podの自動更新・ロールバック等の運用操作が簡単にできるようになります。

それぞれの関係としては、Deployment → ReplicaSet → Podという包含関係になります。
つまり、developmentの中にレプリカセット(≒複数個のPodのコピー)を入れることで、バージョン管理や、複数個のPodをまとめて運用しやすくしているのです。

参考:Kubernetes: Deployment の仕組み

マニュフェストファイルの作成

先ほど作ったMySQLのPodをdevelopmentに直してみます。
以下のようなファイルを作ります。

dev.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-development
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
        tier: db
    spec:
      containers:
      - name: k8s-mysql
        image: mysql:5.7
        volumeMounts:
        - name: k8s-mysql-storage
          mountPath: /var/lib/mysql
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_ROOT_USER
          value: root
        - name: MYSQL_ROOT_PASSWORD
          value: pass
        - name: MYSQL_DATABASE
          value: sampledb
      volumes:
      - name: k8s-mysql-storage
        hostPath:
          path: /Users/myname/Desktop/k8s

記述の意味は以下の通りです。

dev.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  # developmentの名前
  name: mysql-development
# developmentで作る状態について記述
spec:
  # レプリカセットの数
  replicas: 2
  # developmentが管理するリソースのラベル
  selector:
    matchLabels:
      app: myapp
  # developmentで管理するものの一覧
  template:
    # Pod, volumeにつけるラベル 
    metadata:
      labels:
        app: myapp
        tier: db
    # 以下はPod定義のときと同じ記述
    spec:
      containers:
      - name: k8s-mysql
        image: mysql:5.7
        volumeMounts:
        - name: k8s-mysql-storage
          mountPath: /var/lib/mysql
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_ROOT_USER
          value: root
        - name: MYSQL_ROOT_PASSWORD
          value: pass
        - name: MYSQL_DATABASE
          value: sampledb
      volumes:
      - name: k8s-mysql-storage
        hostPath:
          path: /Users/myname/Desktop/k8s

参考:Kubernetes公式ドキュメント Deployment
参考:DeploymentとServiceをyamlファイルで定義する

デプロイ・後片付け

Podやサービス同様、kubectl create/deleteを使って行います。

$ kubectl create -f dev.yml
$ kubectl delete -f dev.yml

EKSにデプロイ

ローカルで簡単な操作ができてコツをつかんだところで、いよいよ本番環境に乗せていきます。
今回は、AWSのEKSを利用します。

EKS上にクラスターを作成

クラスター作成の2つのやり方比較

まずは、EKS上にクラスターを作成します。
やり方は2つあり、1つはAWSのWebコンソール上で作成操作を行うやり方、もう1つはターミナルからeksctlコマンドを用いて作成操作を行うやり方です。

今回はeksctlコマンドを用いた方法でクラスター作成を行います。なぜかというと設定が圧倒的に楽だからです。
eksctlを用いることで、以下の設定が自動的に行われます。

  • EKSクラスターに属するノードに与えるIAMロール作成
  • VPCネットワーク/サブネット/ルートテーブル/IGW/NATゲートウェイの作成
  • セキュリティグループの作成
  • EKSクラスターヘのkubectlの接続

Webコンソールでクラスター作成を行なった場合、このような煩雑な設定を手動でやる必要があります。複雑な構成にしたいのではない場合は、eksctlコマンドを使用するのが効率的でしょう。

参考:「eksctl」コマンドを使ったAmazon EKS構築入門

eksctlコマンドのインストール

EKSにクラスターを作るためのeksctlコマンドをインストールします。
(できたクラスターを操作するのは、ローカルのときと同様にkubectlコマンドです。eksctlにできるのはあくまでクラスター関連の操作だけです。)

ターミナルで以下のコマンドを実行します。

$ brew tap weaveworks/tap
$ brew install weaveworks/tap/eksctl

#ダウンロードできたかは以下のコマンドで確認
$ which eksctl
/usr/local/bin/eksctl

参考:AWS公式ドキュメント eksctl コマンドラインユーティリティ

クラスター作成コマンド実行

早速コマンドを打ってクラスターを作成します。

$ eksctl create cluster \
      --vpc-cidr 10.0.0.0/16 \
      --name eks-sample \
      --region ap-northeast-1 \
      --version 1.14 \
      --nodegroup-name sample-workers \
      --node-type t2.micro \
      --nodes 1 \
      --nodes-min 1 \
      --nodes-max 3 \
      --managed

オプションの意味は以下の通りです。

  • vpc-cidr: 新規作成するVPCのCIDR
  • name: クラスター名
  • region: リージョン。東京リージョンを使いたいのでap-northeast-1を指定
  • version: Kubernetesのバージョン
  • nodegroup-name: ノードグループ(EKSクラスターに属するEC2インスタンスノード集団のこと)の名前
  • node-type: ノードに使うマシンのタイプ
  • nodes: 起動時点でのノード数
  • nodes-min: 最小ノード数
  • nodes-max: 最大ノード数
  • managed: ノードグループをWebコンソール上で表示・いじれるようにする

参考:eksctlを使った簡単Amazon EKS環境構築
参考:[アップデート] EKSがマネジメントコンソールおよびCLIでのワーカーノードの作成・管理をサポートしました

実行には結構時間がかかりますので気長に待ちましょう。

結果

クラスターがちゃんとできてます。
スクリーンショット 2020-07-11 0.24.28.png
コマンドでもその結果が確認できます。

# EKS上のクラスター一覧
$ eksctl get cluster
NAME            REGION
eks-sample      ap-northeast-1

# eks-sampleというクラスター上にあるnodegroupの確認
$ eksctl get nodegroup --cluster eks-sample
CLUSTER         NODEGROUP       CREATED                 MIN SIZE        MAX SIZE        DESIRED CAPACITY        INSTANCE TYPE   IMAGE ID
eks-sample      sample-workers  2020-07-11T04:57:37Z    1               3               1                       t2.micro

クラスターができるに当たって、以下のものが自動で作成されました。

  • VPC: 新しいものが1つ
  • サブネット: ap-northeast-1にある3つのAZにそれぞれ2個ずつ
  • ルートテーブル: パブリックサブネット用が1つ、プライベートサブネット用が3つのAZにそれぞれ1つずつ、新しいVPCのデフォルトルートテーブル1つの計5つ
  • インターネットゲートウェイ: 新しいVPC用に1つ
  • Elastic IP: 新しいVPCのNATゲートウェイ用に1つ
  • NATゲートウェイ: 1つ
  • セキュリティグループ: 新しいVPCのデフォルト1つと、EKSが作ったもの3つ

↓サブネットの様子
スクリーンショット 2020-07-11 0.26.27.png
↓ルートテーブルの様子
スクリーンショット 2020-07-11 0.27.06.png
↓セキュリティグループの様子
スクリーンショット 2020-07-11 0.29.13.png

結果的に、以下のような構成の環境が出来上がっています。
amazon-eks-on-aws-architecture-diagram.7fdf06380021e6dc7c622d298d99e3c1154163bc.png
画像出典:EKS公式ドキュメント スケーラブルなモジュール式 Amazon EKS アーキテクチャ

kubectlの設定

EKS上のクラスターにリソースをデプロイするのは、ローカル同様kubectlだという話は先ほどしました。
しかし、そのためにはkubectlコマンドで、ローカルではなくてEKSクラスターを選択するような設定が必要になります。

contextの確認

「どのクラスターをkubectlでいじるか」という設定は上述したcontextの選択で行います。
EKSクラスター作成後、存在するcontext一覧を確認してみましょう。

$ kubectl config get-contexts
CURRENT   NAME                                              CLUSTER                               AUTHINFO                                          NAMESPACE
          docker-desktop                                    docker-desktop                        docker-desktop                                    
          docker-for-desktop                                docker-desktop                        docker-desktop                                    
*         myname@eks-sample.ap-northeast-1.eksctl.io   eks-sample.ap-northeast-1.eksctl.io   myname@eks-sample.ap-northeast-1.eksctl.io

なんと、EKSクラスターをいじる用のcontextが自動作成・選択されていました。これはeksctlでクラスターを作成したからこそ行われたものです。
(WebコンソールでEKSクラスターを作成したら、これも手動で行います。eksctlコマンドの便利さがこれでわかりますね。)

kubeconfigの確認

なぜeksctlコマンドを実行しただけで、自動でEKS用のcontextが追加・選択されていたのでしょうか。
それはコマンド実行時にkubeconfigという設定ファイルが自動で編集されていたからです。

kubeconfigファイルの中身を見てみましょう。以下のようにコマンドを打ってみます。

$ kubectl config view

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: seeeeeeeeecret
    server: https://kubernetes.docker.internal:6443
  name: docker-desktop
- cluster:
    # EKSコンソールで確認できる認証機関の値
    certificate-authority-data: seeeeeeeeecret
    # EKSコンソールで確認できるAPIサーバーエンドポイントのアドレス
    server: https://********************.***.ap-northeast-1.eks.amazonaws.com
  name: eks-sample.ap-northeast-1.eksctl.io

contexts:
- context:
    cluster: docker-desktop
    user: docker-desktop
  name: docker-desktop
- context:
    cluster: docker-desktop
    user: docker-desktop
  name: docker-for-desktop
- context:
    cluster: eks-sample.ap-northeast-1.eksctl.io
    user: myname@eks-sample.ap-northeast-1.eksctl.io
  name: myname@eks-sample.ap-northeast-1.eksctl.io

current-context: myname@eks-sample.ap-northeast-1.eksctl.io
kind: Config
preferences: {}

users:
- name: docker-desktop
  user:
    client-certificate-data: seeeeeeeeecret
    client-key-data: seeeeeeeeecret
- name: myname@eks-sample.ap-northeast-1.eksctl.io
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1alpha1
      args:
      - token
      - -i
      - eks-sample
      command: aws-iam-authenticator
      env:
      - name: AWS_STS_REGIONAL_ENDPOINTS
        value: regional
      - name: AWS_DEFAULT_REGION
        value: ap-northeast-1

これは$HOME/.kube/configにある設定ファイルの中身がそのまま出力されている様子です。何もしていないのに、明らかにAWS用の設定と思われる箇所が存在します。
このように、kubeconfigに、EKSのクラスターに接続できるようなcontext設定が自動で追加されていることがここからもわかります。

kubectlの認証トークン設定

ローカル側でkubectlの対象クラスターをEKSに向けることはできました。しかし、EKS側ではkubectlコマンドでアクセスしてきた人が正規の権限を持った開発者であるとどのように認識しているのでしょうか。

EKSでは、クライアントにIAM認証情報から得たトークンをkubectlコマンド実行時に付与してもらい、そのトークンを見ることで、正規の権限を持った人かどうかを判定しています。

eks-iam.png
画像出典:EKS公式ドキュメント クラスター認証の管理

そのため、EKSクラスターを操作するためのcontextで、kubectlコマンド実行時に認証トークンを渡すように設定する必要があります。
やり方は2つあります。aws-iam-authenticatorコマンドを使う方法とaws eks get-tokenコマンドを使う方法です。

aws-iam-authenticatorコマンドの方法

デフォルトではこちらの方法になっています。aws-iam-authenticatorコマンドがインストール済みの方はこちらを使えば余計な手間がかからなくてよいでしょう。
kubeconfigの以下の記述の部分が該当箇所です。

$HOME/.kube/config
      args:
      - token
      - -i
      - eks-sample
      command: aws-iam-authenticator

aws eks get-tokenコマンドの方法

aws-iam-authenticatorコマンドはないがawsコマンドはある!という方はこちらの方法をとれば何もインストールしなくても大丈夫です。
上記のkubeconfig($HOME/.kube/configに存在)の記述部分を、以下のように書き換えます。

$HOME/.kube/config
      #args:
      #- token
      #- -i
      #- eks-sample
      #command: aws-iam-authenticator

      args:
      - eks
      - get-token
      - --cluster-name
      - eks-sample
      command: aws

参考:[アップデート]EKSを使う際にaws-iam-authenticatorが不要になりました!

kubectlがEKSクラスターを向いていることを実際のコマンドで確認

ここまでの準備ができたところで、ノード一覧を取得してみましょう。

$ kubectl get node
NAME                                             STATUS   ROLES    AGE   VERSION
ip-10-0-13-242.ap-northeast-1.compute.internal   Ready    <none>   85s   v1.15.11-eks-908ff6

明らかにAWS上のリソースが取得できました。eksctlコマンドで指定した通り、ノードが1つ作成されていることがわかります。

EKSクラスター上にリソース作成

kubectlコマンドでEKSクラスターを扱えるようになったので、いよいよEKS上にリソースを作成してみましょう。

マニュフェストファイルの修正(適宜)

もしもデプロイするコンテナのイメージとして、ECR上のイメージを使いたい!という場合は、マニュフェストファイルのコンテナイメージを指定する箇所を、ECRのリポジトリURIに書き換えればOKです。

dev.yml
# 以下は一例です
(略)
image: your-aws-account-id.dkr.ecr.ap-northeast-1.amazonaws.com/myapp/api:v1
(略)

デプロイ/削除

ローカル同様、kubectl create/deleteコマンドを実行します。

$ kubectl create -f dev.yml
deployment.apps/myapp-mysql created
$ kubectl delete -f dev.yml
deployment.apps/myapp-mysql deleted

また、LoadBalancer型のサービスをデプロイした場合、自動的にELBが作成→当該サービスとの紐付けが行われています。
例えば、LoadBalancer型のサービスがあるとき、kubectl getコマンドを実行し情報を確認してみます。

$ kubectl get service
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP                                                                    PORT(S)          AGE
()
myservice    LoadBalancer   172.20.78.107   *******.ap-northeast-1.elb.amazonaws.com   9090:31806/TCP   7m25s

EXTERNAK-IPの部分に表示されているドメインは、自動作成されたELBのアドレスです。
そのため、http://*******.ap-northeast-1.elb.amazonaws.com:9090にブラウザからアクセスすれば、そのサービスにWebからアクセスすることができます。
参考:Amazon EKS のチュートリアルで Kubernetes を理解する #02 アプリのデプロイ

後片付け

使い終わったらEKS環境を片付けましょう。
デプロイしたリソースをkubectl deleteするのはもちろん、余計な費用がかからないようにEKSクラスターも以下のように削除しましょう。

$ eksctl delete cluster --name=eks-sample

これを実行することで、クラスター作成時に自動で作られたVPCやNATゲートウェイなども全てなくなります。また、kubeconfigに書かれたEKS用の設定記述も自動で消去されます。

11
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
11
Help us understand the problem. What is going on with this article?