この記事について
「Kubernetesを実際に触って勉強してみたい!」となって参考記事を探してみると、多くの記事がいきなりEKSやGKEといったクラウド上の本番環境を触るところからのスタートになっています。
「いきなり本番環境はちょっと……」「まずはローカル環境で軽く触って慣れてからクラウド上の環境を構築したい!」という方向けに、
- Kubernetesのアーキテクチャの勉強
- ローカルで実際に触ってみる
- 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の構成要素について詳しく説明します。
画像出典: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アイコンをクリックすると、以下のようなメニューが表示されます。
ここから"Preferences"を選択すると、以下のような画面になります。
左のバーから"Kubernetes"を選択します。すると、Docker AppにおけるKubernetesの設定画面が以下のように表示されます。
この中から、"Enable Kubernetes"のチェックボックスに印を入れ、"Apply & Restart"で設定を保存します。
起動確認
本当に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/
後片付け
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を作ってみます。
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
記述の意味は以下の通りです。
# 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を作りたいという場合もあるでしょう。
その場合は、マニュフェストファイルを以下のように書きます。
(略)
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作成のときと同様、まずはマニュフェストファイルを作成します。
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
記述の意味は以下の通りです。
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)]>
このように、サービス名を使うことで接続できました。
serviceにMacのローカルホストを接続
Kubernetes Dashboardのように、起動しているサービスにクラスタ外のMacから接続したい!という状況があると思います。
例えばweb-service
という名前のサービスを作り、そのサービスにMacのLocalhostを繋ぎブラウザ閲覧したいとします。
この場合、マニュフェストファイルを以下のように作ります。
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に直してみます。
以下のようなファイルを作ります。
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
記述の意味は以下の通りです。
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でのワーカーノードの作成・管理をサポートしました
実行には結構時間がかかりますので気長に待ちましょう。
結果
クラスターがちゃんとできてます。
コマンドでもその結果が確認できます。
# 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つ
↓サブネットの様子
↓ルートテーブルの様子
↓セキュリティグループの様子
結果的に、以下のような構成の環境が出来上がっています。
画像出典: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クラスターを操作するためのcontextで、kubectl
コマンド実行時に認証トークンを渡すように設定する必要があります。
やり方は2つあります。aws-iam-authenticator
コマンドを使う方法とaws eks get-token
コマンドを使う方法です。
aws-iam-authenticator
コマンドの方法
デフォルトではこちらの方法になっています。aws-iam-authenticator
コマンドがインストール済みの方はこちらを使えば余計な手間がかからなくてよいでしょう。
kubeconfigの以下の記述の部分が該当箇所です。
args:
- token
- -i
- eks-sample
command: aws-iam-authenticator
aws eks get-token
コマンドの方法
aws-iam-authenticator
コマンドはないがaws
コマンドはある!という方はこちらの方法をとれば何もインストールしなくても大丈夫です。
上記のkubeconfig($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です。
# 以下は一例です
(略)
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用の設定記述も自動で消去されます。