職場で上記の勉強会を企画してみることにしたので、参加者むけの手順書を兼ねてやり方を残しておきます。
はじめに
こんな人向けのハンズオンです。
- コンテナって興味あるけど自分で触ったことないからイメージわかない
- Kubernetesに入門してみたい
必要なものは以下です。
- PC:Win/Mac両対応。会社の業務OA端末(制限付きWindows)でも問題なし。
- AWSアカウントとIAMユーザー:社内向けにはわたしの方で用意しました。
その1:ブラウザーでAWSログインして操作端末を起動
まずはウェブブラウザー(ChromeもしくはEdge推奨)を起動し、AWSの管理画面(マネジメントコンソール)にアクセスしてIAMユーザーでログインします。
※ 社内向けにはログイン用のURLとIAMユーザー認証情報を別紙でお渡しします。
新しいコンソールを使うか聞かれたら、どちらでもハンズオンに支障ないので好きな方を選びましょう。
次に、ハンズオン用の操作端末として利用する「CloudShell」サービスを起動します。
AWSマネジメントコンソール画面上部の検索窓に cloudshell
と入力すると、サジェストが出てくるのでクリックしましょう。
黒い画面が出現し、数秒待つと最下部にプロンプトが点滅してコマンドを打てるようになります。
今回このCloudShellを操作端末として使うので、制約の多い会社PCでもブラウザーさえ使えればハンズオンできちゃうというわけです。
仮想ターミナルが準備完了し、カーソルがチカチカ点滅していることを確認したら、以下のコマンドをコピーしてCloudShellに貼り付け実行しましょう。
※ 環境準備はこちらの記事を参考にさせていただきました。
# ディレクトリ作成
mkdir -p $HOME/.local/bin
cd $HOME/.local/bin
# kubectl のインストール
curl -LO "https://dl.k8s.io/release/$(curl -LS https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
# eksctl のインストール
curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv /tmp/eksctl $HOME/.local/bin
# ホームディレクトリに戻る
cd $HOME
コピペすると「こんなにまとめて貼り付けていいの?」って聞かれるので「Paste」ボタンをクリック。
一連のコマンドがまとめて実行され、おそらく最後の行の cd $HOME
にカーソルが残っていると思うので、Enterを押してこいつも実行します。
これでCloudShell上に、Kubernetesのハンズオンに必要なツールをインストールできました。
次に以下のコマンドをCloudShellで実行して、EKSのクラスターを作成します。
※ 短いコマンドなので、できればコピペではなく自分で手入力してみましょう。その方が理解が深まります。
eksctl create cluster
これが完了まで20分ほどかかります。
裏ではCloudFormationというIaCツールが自動実行され、VPCやEC2などEKSクラスターに必要なインフラがもろもろ構築されていきます。
ここまで来たら、待ち時間のあいだに社内では「コンテナとは何ぞや?」的な紙芝居タイムに入ろうと思います。
※ 途中で詰まった方は講師がサポートしますので、遠慮なく発言したりチャット欄で教えてくださいね。
待ち時間:紙芝居タイム
コンテナって何?(Kubernetes入門)
これをやっている間に20分ぐらい経過してくれれば、EKSクラスターの作成が完了していると思います。
CloudShellに以下のような出力が表示され、プロンプトのカーソルが点滅して操作可能状態になっていればOKです。
2022-01-30 00:19:08 [✔] EKS cluster "attractive-party-1643500815" in "ap-northeast-1" region is ready
ここに表示されている自分のEKSクラスター名はぜひ見ておいてください。マニアックな英語圏の動物名など、いろいろランダムで設定されるようです。
(eksctlでクラスター作成時、オプションを何も指定していないのでクラスター名やノード台数などはおまかせで自動設定されています)
※ CloudShellは一定時間操作しないでいると切断されてしまいます。
が、Enterキーを入力すると数秒後にすぐ復帰しターミナル操作続行可能となります。
こいつをマネコンからも確認してみましょう。
CloudShellはそのままで、画面上部のAWSサービス検索ボックスに eks
と入力するとEKSコンソールへのリンクが表示されます。
これを右クリックして「新しいタブで開く」と、CloudShellを開いたままEKSコンソールに移動できるので便利です。
EKSコンソールの「クラスター」画面より、自分のクラスター名で検索してみるとちゃんと存在していることが分かると思います。
クラスター名をクリックしてみると、ワーカーノード(データプレーンの土台部分のEC2)が一覧表示され、2台いることが分かります。
ただし、AWSのマネコンからGUIで確認できる範囲はここまで!
ワーカーノード上で実際に動くコンテナの世界は、コマンドでしか見られないのでCloudShellに戻ります。
その2:いよいよK8sを触ってみる
サイボウズさんの新人研修資料が非常にわかりやすくて良かったので、今回テキストとして参考にさせていただきました。
上記資料のまんまMinikubeが使えると非常に楽なのですが、MinikubeはWindowsだと少々ややこしかったりPCの管理者権限も必要となるため、社内向けの勉強会では全員が同じ会社PC(制限付きWindows)でブラウザーから操作できるようAWSを活用してアレンジしてみたのが今回です。
この記事は以下出典のMITライセンスに基づいて公開しています。
Copyright (c) 2019 Cybozu
https://github.com/cybozu/introduction-to-kubernetes/blob/master/LICENSE
2-1. とりあえず現状を確認してみる
K8sの世界では「kubectl」というコマンドを使ってコンテナを操作します。
まずはデータプレーンのワーカーノードが何台いるのか確認してみましょう。
※ 短いコマンドなので、コピペではなく手打ちして理解を深めましょう。
kubectl get node
おそらく以下のような結果が出力されると思います。
さっきマネコンで見たとおり、EC2の名前が2台表示されてますね。
NAME STATUS ROLES AGE VERSION
ip-192-168-11-13.ap-northeast-1.compute.internal Ready <none> 3h6m v1.21.5-eks-9017834
ip-192-168-72-188.ap-northeast-1.compute.internal Ready <none> 3h6m v1.21.5-eks-9017834
さらに、Pod(コンテナ)はまだ何も作っていないはずですが、念のため一覧表示してみましょう。
kubectl get pod
「何もないよ」というメッセージだけが返ってきたと思います。
図にすると今こういう状態です。
2-2. ドキドキ!はじめてのPodをデプロイしてみる
それではいよいよ自分の好きなアプリを入れたPod(コンテナ)を作成してみます。
Pod作成の注文は複雑になる(どんな名前にして、どんなコンテナイメージを使い、どんなスペックで動かすか…等)ので、ファミレスのようにオーダーを注文書に書いてからコントロールプレーンに指示します。
注文書はYAMLという形式のテキストファイルに書くルールになっています。
注文書のことを「マニフェスト」といいます。
※ YAMLは慣れないとビビりますが、中身はただのテキスト箇条書きなのでサルでも分かります。
最初は「NGINX」という代表的なWebサーバー製品をコンテナとして動かしてみたいと思います。
まずは以下のテキストをメモ帳にコピペして、「nginx-pod.yaml」という名前で会社PCのデスクトップに保存してみましょう。
apiVersion: v1
kind: Pod
metadata:
name: my-nginx
labels:
component: nginx
spec:
containers:
- name: nginx
image: nginx:latest
これだけで注文書は完成です。
NGINXは人気のOSSなので、すでに他の人が作って公開してくれているコンテナイメージが存在するため、注文書のなかでそれを流用するよう指定しています。
こいつをCloudShellから参照できるよう、アップロードします。
CloudShell画面右上の「Actions」より「Upload file」を選び、PCのデスクトップに保存した「nginx-pod.yaml」をアップロードします。
アップロード完了したら、 ls
コマンドを実行してCloudShellのカレントディレクトリー内を一覧表示してみましょう。自分が作ったYAMLファイルが表示されていればOKです。
それでは、この注文書を使ってPodをデプロイしてみます。
コマンドは以下です。
kubectl apply -f nginx-pod.yaml
無事にPodが作成されたら、一覧表示して確認してみましょう。
先ほど打った以下コマンドをもう一度実行します。
kubectl get pod
すると以下のように、注文したとおり「my-nginx」というPodが1つ出現しています。
STATUSが「Running」なら、早くも起動終わって稼働中ということです。仮想マシン起動よりも断然早いのが分かります。
NAME READY STATUS RESTARTS AGE
my-nginx 1/1 Running 0 82s
ちなみにPodの詳細表示をするコマンドは以下です。
kubectl describe pod my-nginx
打ってみるとPod名やIPアドレス、イベントログなど色々出力されるのが分かります。
Podの調子が悪い際はこうやってログなど確認します。
2-3. Podにログインしてみよう
Podのガワを見ているだけだと実感が湧かないので、Podにログインしてみましょう。
Pod内でコマンド実行できる kubectl exec
を使ってbashを実行してみます。
kubectl exec -it my-nginx -- bash
これでPod内のOSをbashターミナルで操作できます。
図にするとこういう状態です。
本当にPod内に入れているのか怪しいので、Pod内で自分に向かってcurlコマンドを打ってみましょう。
curlは雑に言うと、任意のURLへのHTTPアクセスをコマンド上で再現してくれる機能です。
curl localhost
だっと長めのテキストが出力されると思います。
CLIに慣れていない人はピンと来づらいと思いますが、今やってるのはGUIでいうと結局こういうことです。
NGINXくんはWebサーバーなので、こういった画面を他の人が見られるように稼働してくれているわけですね。
curlの宛先にlocalhost(自分)を指定したらこのページが見られたので、ログインしているのは紛れもなくNGINX Podで正しかったということになります。
満足したら一旦 exit
コマンドでmy-nginx Podから抜けて、CloudShellに戻ります。
2-4. Podから別のPodにアクセスしてみる
次は「CloudShellから直接Podを覗く」だけでなく、「Podからさらに別のPodへアクセスする」ことをしてみます。ようはサーバー間通信がちゃんとできるかの確認です。
まずは既存のNGINX Podと別に、踏み台用のLinux Podをデプロイしてみます。
以下をコピペして「fumidai-pod.yaml」を会社PCのデスクトップに保存して、先ほどと同様CloudShellにアップロードしてみてください。
apiVersion: v1
kind: Pod
metadata:
name: fumidai
spec:
containers:
- name: bastion
image: debian:stable
command: ["sleep", "infinity"]
Debianという種類のLinuxがインストールされただけのシンプルなPodです。
こいつをkubectlで追加デプロイしてみましょう。
kubectl apply -f fumidai-pod.yaml
デプロイできたら現在のPod一覧を確認してみましょう。
コマンドはそろそろ覚えてきたでしょうか?
NAME READY STATUS RESTARTS AGE
fumidai 1/1 Running 0 12s
my-nginx 1/1 Running 0 115m
踏み台くんが元気に追加されていますね。
ここまでこういう状態です。
ただし正確に言うと、どのPodがどのノード(EC2)に乗っているかは不明です。
ノードとPodの管理はK8sにおまかせでよい世界で、あくまで利用者である僕らは「どんなPodが欲しいか」だけをオーダーしている状態です。
さて、踏み台Podにログインして、そこからNGINX Podへのアクセスを試してみたいのですが、その前に宛先となるNGINX PodのIPアドレスを確認しておく必要があります。
Pod一覧表示コマンドに -o wide
オプションをつけるとIPアドレスも分かります。
kubectl get pod -o wide
NGINXのIPアドレスは分かったでしょうか。
それではまず踏み台Podへ kubectl exec
でログインします。
kubectl exec -it fumidai -- bash
プロンプトが踏み台Linuxのrootに変わったら、お隣のmy-nginx PodにHTTPアクセスを試みましょう。
ただしcurlコマンドが未インストールのため、以下コマンドを実行して導入します。
apt update
apt install -y curl
無事にcurlがインストールできたら、先ほど確認したmy-nginx PodのIPアドレス宛てに打ってみましょう。
curl http://192.168.xx.xx
Welcome to nginx! ページは返ってきたでしょうか?
ここまで図にするとこういう状況です。
ここまで成功したら、また exit
コマンドで踏み台Podから抜けておきましょう。
2-5. 同用途のPodをたくさん増やして冗長化する
いまmy-nginx Podは1つだけですが、もしこいつに障害が起こったら誰も「Welcome to nginx!」ページを見られなくなってしまいます。
それは寂しくて困るので、このPodを常時3つ起動するよう注文書を書こうと思います。
先ほどまで書いていたYAMLは「Pod」という注文書ですが、Podを複数台セットで注文する場合は「レプリカセット」という別種の注文書を作る必要があります。
今回は「triple-nginx」という名前をレプリカセットに付けておきます。
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: triple-nginx
labels:
component: nginx
spec:
replicas: 3
selector:
matchLabels:
component: nginx
template:
metadata:
labels:
component: nginx
spec:
containers:
- name: nginx
image: nginx:latest
こいつを「nginx-replicaset.yaml」ファイルとしてローカルに保存し、CloudShellにアップロードしましょう。
この注文書をデプロイする前に、まず現状のPod一覧を確認します。
kubectl get pod
最初にPodマニフェストでデプロイしたmy-nginx Podが1台と、踏み台が1台の計2台が存在していますね。
それでは、上記のレプリカセットを指定してPodをデプロイしてみましょう。
kubectl apply -f nginx-replicaset.yaml
すでにNGINXのPodが1つ存在するところに、NGINXを3つ動かしたいという注文書をオーダーしました。結果どうなるのか、 kubectl get pod
コマンドで確認してみましょう。
NAME READY STATUS RESTARTS AGE
fumidai 1/1 Running 0 48m
my-nginx 1/1 Running 0 164m
triple-nginx-gt89t 0/1 ContainerCreating 0 3s
triple-nginx-xfdqc 0/1 ContainerCreating 0 3s
すると上記のように、「triple-nginx」レプリカセットから生成された自動命名Podが2つ、追加で起動準備しているのが分かります。
なぜ既存の1台を同用途と認識できているかというと、最初にデプロイした「Pod」用マニフェストと今回の「Replicaset」マニフェストの両方とも、 component: nginx
というラベルを仕込んでいました。
レプリカセットはこのラベルをもとにPodの稼働台数を見張る仕様になっているんですね。
(前略)
metadata:
(中略)
labels:
component: nginx
(後略)
では、イタズラしてみましょう。
NGINXのPodを1台、わざと削除してやるとどうなるでしょうか。
kubectl delete pod my-nginx
Pod削除に成功したら、すかさず kubectl get pod
でPod一覧を確認します。
すると、なんと「triple-nginx」レプリカセットから即座に1台、Podが追加生成されているのが分かります。
NAME READY STATUS RESTARTS AGE
fumidai 1/1 Running 0 65m
triple-nginx-gt89t 1/1 Running 0 16m
triple-nginx-x2k5x 1/1 Running 0 4s
triple-nginx-xfdqc 1/1 Running 0 16m
ということで、Pod障害時の自動修復も超早いことが体感できたと思います。
2-6. 複数Podへロードバランスする
せっかく同用途のPodを3台に冗長化したので、PodへのアクセスもIPアドレス決め打ちではなく名前でアクセスできるようにしましょう。
そのためには「サービス」という新種の注文書を作ります。今回「my-nginx」という名前のサービスにします。
apiVersion: v1
kind: Service
metadata:
name: my-nginx
spec:
selector:
component: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
実はここにも component: nginx
というラベルが仕込まれていることに気付いたでしょうか。
この「nginx-service.yaml」をまたCloudShellにアップロードして、デプロイしてみましょう。
kubectl apply -f nginx-service.yaml
この「サービス」もkubectlで一覧確認できます。
kubectl get service
してみると、以下のようにデフォルトの「kubernetes」というサービスに加えて、いまデプロイした「my-nginx」サービスが動いていることが分かります。
それでは試しに踏み台PodからIPアドレス直指定ではなく、「my-nginx」サービス宛てにcurlでアクセスしてみましょう。
kubectl exec -it fumidai -- bash
curl http://my-nginx
Welcome to nginx! ページは返ってきたでしょうか?
これでNGINX PodをIP直指定ではなく、サービス名でロードバランスさせて裏のどれか1台にリクエストを返却させる、ということが可能になりました。
図にするとこうなります。
ここまでできたら exit
で踏み台Podから抜けておきましょう。
2-7. 複数Podをローリングアップデートしてみる
最後はちょっと難しい操作にチャレンジします。
いま「triple-nginx」レプリカセットではPod 3台が稼働していますが、開発チームがNGINXコンテナを更新したため、新しいPodへ置き換えたくなりました。
しかし Welcome to nginx! は24時間アクセスが止まない人気サイトのため、なるべく顧客影響ないよう1 Podずつこっそり更新したいです。
このときレプリカセットを使ってローリングアップデートしようとすると大変面倒です。
なぜなら、現新バージョンのレプリカセットを2つ作成し、新レプリカセットのPod台数を1台ずつチマチマ増やしつつ、同時に現行レプリカセットのPod台数を1台ずつチマチマ減らしていくという、何回 kubectl apply
せなあかんねんという事態になります。
これを見越してK8sでは「デプロイメント」というレプリカセットの上位互換的な注文書も用意されています。(最初からそっち使えという話ですね)
「デプロイメント」では、同じ注文書のコンテナイメージ指定だけを新しいものに書き換えることで、勝手にPodを1台ずつ新パージョンのPodに置き換えてくれます。
それではまず、ややこしいので下位互換の「レプリカセット」はいったん削除してしまいます。
kubectl delete replicaset triple-nginx
代わりに「デプロイメント」用のYAMLマニフェストを作成します。
現新2バージョン作っておき、両方ともCloudShellにアップロードしましょう。
「nginx-deployment-v1.yaml」(現行バージョン)
こちらはNGINX 1.20のコンテナイメージを指定しています。
apiVersion: apps/v1
kind: Deployment
metadata:
name: triple-nginx
labels:
component: nginx
spec:
replicas: 3
selector:
matchLabels:
component: nginx
template:
metadata:
labels:
component: nginx
spec:
containers:
- name: nginx
image: nginx:1.20
「nginx-deployment-v2.yaml」(新バージョン)
こちらはNGINX 1.21のコンテナイメージを指定しています。
apiVersion: apps/v1
kind: Deployment
metadata:
name: triple-nginx
labels:
component: nginx
spec:
replicas: 3
selector:
matchLabels:
component: nginx
template:
metadata:
labels:
component: nginx
spec:
containers:
- name: nginx
image: nginx:1.21
これら2つをCloudShellにアップロードできたら、まずは現行バージョンのデプロイメントを使ってPodをデプロイします。
kubectl apply -f nginx-deployment-v1.yaml
kubectl get pod
してみると、NGINXのPodが3台起動していることが分かると思います。
この状態で新バージョンのデプロイメントを適用するとローリングアップデートが実施されます。
しかし、普通にやると経過がよく分からないので、CloudShellの別タブで踏み台Podにログインしておき、「my-nginx」サービスへNGINXのバージョン確認コマンドを0.1秒間隔で連打するよう仕込んでおきたいと思います。
まずCloudShell画面右上の「Action」ボタンより「New tab」をクリックします。
新しく開いたコンソールの方で、以下コマンドを実行し踏み台Podから確認コマンドを仕掛けます。
kubectl exec -it fumidai -- bash
while true; do echo "-------"; date; curl -s -i my-nginx | grep -E 'Server'; sleep 0.1; done
nginx 1.20.2
という記載が確認できると思います。
ちなみにCloudShellの2タブ目は、画面右側にドラッグすると2つのタブを横並びで表示できるので便利です。
このままCloudShellの1タブ目に戻り、新バージョン用のデプロイメントを適用してみましょう。
kubectl apply -f nginx-deployment-v2.yaml
右側のCloudShellタブ上で、NGINXバージョンが少しずつ 1.21.6
へ置き換えられていくのが分かるんじゃないかと思います。
通常、商用環境のK8sでPodをデプロイする際には、「Pod」や「レプリカセット」ではなくこの「デプロイメント」を利用するケースが一般的となるので覚えておきましょう。
(厳密に言うとデプロイメントを利用することにより、裏でよしなにレプリカセットがデプロイされ、さらにそのレプリカセットによりPodがデプロイされる…という風に下位オブジェクトをK8sが勝手にマネージしてくれます)
これであなたは、ローリング更新可能なNGINXコンテナを複数台、EKS環境上で稼働させる方法を習得できました!
おかたづけ
ムダなお金がかからないように、作ったクラスターを削除しましょう。
まずはクラスター名を確認して…
kubectl config get-clusters
最初のピリオドまでのカタマリがクラスター名です。
この部分を選択してコピーしたあと、以下の削除コマンドに埋め込んで投入しましょう。
eksctl delete cluster \
--name 消したいクラスター名 \
--wait
エラーでコケてしまった方は講師があとで消しておくので、諦めてそのまま放置でおkです。
まとめ
今回紹介した以下の「注文書(マニフェスト)」4種類によって定義されるものはそれぞれ、K8sの世界では「リソース」と表現します。
- Pod
- Replicaset
- Deployment
- Service
「リソース」はピンと来にくい表現なのですが、K8sの世界における「概念」のようなものです。
これらの「リソース」が実際に生成されたものを「オブジェクト」と言います。
私もまだまだKubernetes初心者なのですが、ここまで実機で触ってみていればコンテナ操作について何となくイメージが湧いてくるんじゃないかと思います。
こんなに分かりやすい新人研修資料を社外向けにも公開してくれたサイボウズさんには本当に感謝です!
※ 社内の勉強会においては、今回配布したIAMユーザーや作成いただいた各種AWSリソースは私のほうでまとめて削除しておきますのでご安心ください。