Kubernetes and Cloud Native Associate(KCNA)試験を受けたよ
Kubernetesにも少し慣れてきたので昨年12月末ごろに試験を受けました。
結果は合格でした!
e-Learning+試験(1回失敗しても再試験あり)のセットのコースを申し込んだのですが、勉強はe-Learningだけだと不十分との噂を耳にし、実際に受けてみてそう感じました。試験情報も少ない中で結構きつかったので、3年後の自分へのメモも兼ねて試験までの道のりでつまづいたところなど紹介していきます。
①間違えて英語版で申し込んでしまった
→ いきなりバカ丸出しなんですが。。。サポートに日本語切り替えできるか問い合わせたところ、日本語版申込をしてくださいとのことで英語版キャンセル、日本語版を再申し込みで解決した。
②e-Learningがわかりづらい
→ 英語をカタカナで日本語化したような用語が多く、途中でめげそうになった。(正直、英語版の方が分かりやすいかもとも思った)分からない用語など調べながら一通り読んで、章末問題も完璧にできるようになるまで繰り返した。3回くらい読みなおしをした。その上でインターネット上のCloud Nativeの思想に関するコラムや、Kubernetes道場を読み込んだ。
③模擬試験問題がない
→ e-Learningに模擬試験が付属してるのかと思いきや、章末問題のみ。UdemyのKCNAに関する問題を購入し実践トレーニングをしてみたが、勉強の方向性が合ってるかが分からず、とりあえず1回目の試験を受けることにした。
④試験場所が家
→ オンラインの試験で何もない部屋を用意する必要があった。仕事部屋は本やモノが多いので寝室に簡易テーブルを置いて対応した。机の周りからモノをどけろという指示が多くあんまり関係なかったかもしれない。
⑤試験開始前に完了後に出てくるはずのアンケートに答えてしまう
→ 専用ブラウザをインストールして受験するが、ブラウザインストール後にアンケート画面(英語)が表示された。これ答えた後に試験開始なの?質問事項が試験後っぽいけども、、、よくわかんない、と思いながら回答。正解は「専用ブラウザは手動で開く」だった。そして、「アンケートは試験後に答える」だった。先に答えても支障はなかった。
⑥試験官が外人
→ 試験開始前の準備(部屋をカメラで写したり)の指示が全て英語のチャットで送られてくる。。落ち着いて読めば対応できるかもしれないが、試験前でドキドキしてる中で対応を迫られてあんまり読めない。翻訳ツールも使えない。事前に先輩方(試験合格者)のブログに指示の内容が共有されていたのでだいぶ参考になった。
⑦試験の日本語和訳が微妙なところがある
→ 英語をカタカナで日本語化したような用語と思ってたら、日本語になってて逆に混乱した。試験中に問題を英語切り替えができるので、英語を読むしかない。やはりエンジニアに英語は必須要素らしい。
⑧試験の結果通知まで待ち遠しい
→ 全60問中45問(75%)が合格ライン。24時間以内にメールでお知らせするとのことだったが、4択式だから直ぐに結果がわかると思ってたから結果が待ち遠しかった。結果が分かったのは翌日。
6〜7割方がe-Learningに書かれていたことで、この部分を取りこぼさないことと、残りの部分に関しての知識をどう補っていくかが重要になると思います。
「残りの部分」がよく分からないのでe-Learningの学習を一通り終えた時点で模擬試験と思って1回目に挑戦して問題の雰囲気を掴む、2回目用に知らなかった用語などを覚えて持ち帰り、学習範囲を広げて2回目に臨んだらいいかと思いました。
⑨合格するとデジタルバッジがもらえるのだが・・・
→ 何故か姓名が逆に登録されており、認証ページで認証しようとすると、名前+IDで認証が通るような形に。。画面には姓+IDを入力と書いてあるのに。オーマイ!合格証書の氏名は姓名と正しい並び順なのだが、英語圏では名姓の並び順で入れ替わって登録されたのだろう。

ともあれ、合格できて良かったです。
GKEにおけるコマンド操作のメモ
ちょっとしたオプションの違いでクラスターの立ち方が変わったりと慣れるのには使い込みが必要だなぁと思いました。 忘れそうなので備忘メモ。
クラスターを設置
クラスターとは、かたまりのことで、1クラスターでは1つのコントロールパネルを持つイメージです。 その実態はComputeEngineを束ねた形で1台のコンピュータとして扱う仮想化の技術が詰まっています。 ちょっと前に流行ったグリッドコンピューティングみたいな印象です。
さて、クラスターを設置していきます。
gcloud container clusters create test-cluster --region=asia-northeast1 --num-nodes=1
この書き方だと、各ゾーンに1台ずつComputeEngineが立ち、GKEのtest-clusterが1つ用意できました。
asia-northeast1-a、asia-northeast1-b、asia-northeast1-c それぞれに1台ずつのComputeEngineが立つので、そこそこのコストがかかります。
これは・・・本番運用向きですね。
上記コマンドのcreateをdeleteに変えてnum-nodesオプションを外して片づけます。
今回のケースでは検証だし、ComputeEngineを1台立てたいだけなので、オプションでゾーンの方を指定する
gcloud container clusters create test-cluster --zone="asia-northeast1-b" --num-nodes=1
ゾーンbに1台のComputeEngineが立ち、GKEのtest-clusterが1つ用意できました。 ちなみに、--num-nodes=3などの設定にすると、ゾーンbに3台立ちます。複数台立てるならマルチゾーン(リージョン指定する方)がおすすめです。
ポッドを設置
今日やりたいのはクラスターを立ち上げて、podを立ち上げたいので、nginxを立ててみることにします。 認証する
gcloud container clusters get-credentials test-cluster --zone=asia-northeast1-b
自環境の使えそうなテスト用のnginxイメージを探す(次回は作る予定)
gcloud container images list
使う。
kubectl run nginx --image=asia.gcr.io/[プロジェクト名]/nginx:v1 --port=80
podが1台立ち上がります。ここで、--replicasのオプションをつけて、3つpodを用意したいですが、指定できなかった。 cubectl scaleで増やしてみようとしたが、こちらも失敗。yamlで指定したほうがいいってこと?まだ検証が必要ですがタイムアップです。 やりたいことはいろいろとあるけど、今日はここまでにして、次回は続きからできるように。
お片付け
コストがかかる話なので、放置せずに削除する。
podを消す
kubectl delete pod nginx
autoscalingの設定を入れていないため1台のpodを消すだけで大丈夫。scalingの設定をしていれば異常を検知して1台新しいのが立ち上がるはず。この辺も設定次第なんですね。
クラスターを消す
gcloud container clusters delete test-cluster --zone="asia-northeast1-b"
きれいになりました。これでお休みの日に無駄な課金をされなくて済みます! それでは、みなさま、よい週末を!
codepipelineの承認をslack上で行う(その4)
確認してみる
デプロイが走るとこのようなメッセージが表示される

「承認」を選んだ場合

「キャンセル」を選んだ場合

続きのデプロイも動くことが確認できた。やったー!
この対応を行うとともに、誰でも承認ボタンを押せる状態はよろしくないため、 slackのチャンネルをプライベートにして限定したメンバーのみ見れる状態にしておくなど 運用面での対応を考えておく必要がありそう。
それでもデプロイ通知チャンネル内で完結するのは非常に楽になったと思う。
codepipelineの承認をslack上で行う(その3)
その2までで、slackへの通知ができるようになった。その3ではslackから情報を受け取るところを作る。
4. ApiGatewayとpostのlambdaを作る
まずはLambda
import json
import urllib.request
import boto3
def lambda_handler(event, context):
client = boto3.client("codepipeline")
msg = event["body"] # httpでpostされたpayloadの格納
if not msg:
return {
'statusCode': 403,
'headers': {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
'body': "Forbidden"
}
msg_decode = urllib.parse.unquote(msg)
msg_replace = msg_decode.replace("payload=", '')
new_msg = json.loads(msg_replace)
token = new_msg["actions"][0]["value"]
approval_data = token.split(',')
pipeline_name = approval_data[1]
token = approval_data[0]
stage_name = "approval-stage"
action_name = "approval-action"
if new_msg["actions"][0]["name"] == "ok":
client.put_approval_result(
pipelineName=pipeline_name,
stageName=stage_name,
result={
'summary': 'Approval',
'status': 'Approved',
},
token=token,
actionName=action_name
)
return {
'statusCode': 200,
'headers': {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
'body': "```承認されました```"
}
else:
client.put_approval_result(
pipelineName=pipeline_name,
stageName=stage_name,
result={
'summary': 'Rejected',
'status': 'Rejected',
},
token=token,
actionName=action_name
)
return {
'statusCode': 200,
'headers': {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
'body': "```却下されました```"
}
Lambdaのデプロイは2.のやり方と一緒。 APIGatewayとの繋ぎはこのような雰囲気で。
## API Gateway
resource "aws_api_gateway_rest_api" "api" {
name = "${local.name}-rest_api-${local.environment}"
endpoint_configuration {
types = ["REGIONAL"]
}
}
# /approval-api
resource "aws_api_gateway_resource" "approval" {
path_part = "approval"
parent_id = aws_api_gateway_rest_api.api.root_resource_id
rest_api_id = aws_api_gateway_rest_api.api.id
}
resource "aws_api_gateway_method" "approval_method" {
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.approval.id
http_method = "POST"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "approval_integration" {
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.approval.id
http_method = aws_api_gateway_method.approval_method.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.handler.invoke_arn
}
# Lambda
resource "aws_lambda_permission" "apigw_lambda" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.handler.function_name
principal = "apigateway.amazonaws.com"
# More: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-control-access-using-iam-policies-to-invoke-api.html
source_arn = "arn:aws:execute-api:${local.region}:${data.aws_caller_identity.self.account_id}:${aws_api_gateway_rest_api.api.id}/*/${aws_api_gateway_method.approval_method.http_method}*/*"
}
resource "aws_api_gateway_deployment" "default" {
rest_api_id = aws_api_gateway_rest_api.api.id
stage_name = aws_api_gateway_stage.default.stage_name
triggers = {
redeployment = sha1(jsonencode(aws_api_gateway_rest_api.api.body))
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_api_gateway_stage" "default" {
deployment_id = aws_api_gateway_deployment.dummy.id
rest_api_id = aws_api_gateway_rest_api.api.id
stage_name = "v1"
xray_tracing_enabled = true
}
slackからの結果を受け取るAPIが作成できた。
5. ApiGatewayを呼び出すURLをslackアプリにセットする
このAPIをslackアプリへセットする
左メニューの Interactivity & Shortcuts でURLを設定する。

slackでの更新の準備ができた
(その4へ続く)
codepipelineの承認をslack上で行う(その2)
その1ではSlackアプリのベースを作った。その2ではそのtokenを使ってLambdaとそれを呼び出すSNSを作っていく。
2. SNSとpauseのLambdaを作る
今回のLambdaはpythonで書いた。 こんな感じ
import logging
import json
import os
import boto3
import traceback
from slack import WebClient
from slack.errors import SlackApiError
def lambda_handler(event, context):
token = os.environ["SLACK_API_TOKEN"]
channel_id = os.environ["channel_id"]
client = WebClient(token=token)
message = event["Records"][0]["Sns"]["Message"]
data = json.loads(message)
token = data["approval"]["token"]
codepipeline_name = data["approval"]["pipelineName"]
if codepipeline_name == "test-codepipeline-dev":
msg_text = "``` 承認してもよろしいでしょうか?```"
attachments_json = [
{
"fallback": "Upgrade your Slack client to use messages like these.",
"color": "#258ab5",
"attachment_type": "default",
"callback_id": "the_greatest_war",
"actions": [
{
"name": "ok",
"text": "承認",
"value": token + ',' + codepipeline_name,
"style": "primary",
"type": "button",
"confirm": {
"title": "承認しますか?",
"text": "本当によろしいですか?",
"ok_text": "OK",
"dismiss_text": "Cancel"
}
},
{
"name": "cancel",
"text": "キャンセル",
"style": "danger",
"value": token + ',' + codepipeline_name,
"type": "button"
}
]
}
]
try:
response = client.chat_postMessage(
channel=channel_id,
text=msg_text,
attachments=attachments_json
)
assert response["ok"]
except Exception:
print(traceback.format_exc())
SNSとLambdaのAWSへの作成はterraformにて実施
Lambdaを設置するところの例 Lambdaの環境変数にSlackアプリのtokenやチャンネルIDをセットする
# Function
resource "aws_lambda_function" "handler" {
function_name = "lambda-handler"
handler = "lambda_handler.lambda_handler"
filename = data.archive_file.function_zip.output_path
runtime = "python3.8"
role = aws_iam_role.lambda_iam_role.arn
source_code_hash = data.archive_file.function_zip.output_base64sha256
layers = ["${aws_lambda_layer_version.lambda_layer.arn}"]
timeout = 30
memory_size = 512
environment {
variables = {
ENVIRONMENT = "dev"
SLACK_API_TOKEN = [OAuth & Permissions の token]
VERIFICATION_TOKEN = [verification の token]
channel_id = [通知する slack の チャンネルID]
}
}
}
SNSトピックの作成とLambdaと繋ぐところの例
# SNS
resource "aws_sns_topic" "user_approval" {
name = "user_approval"
}
resource "aws_sns_topic_subscription" "user_approval_lambda_target" {
topic_arn = aws_sns_topic.user_approval.arn
protocol = "lambda"
endpoint = aws_lambda_function.handler.arn
}
resource "aws_lambda_permission" "default" {
statement_id = "AllowExecutionFromSNS"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.handler.function_name
principal = "sns.amazonaws.com"
source_arn = aws_sns_topic.user_approval.arn
}
実行するとこのように設置できる

あとはこれをCodePipeline側で呼び出すだけ。
3. CodePipelineのApprovalの設定にSNSをセットする

これでslackへ通知が届くようになる。
(その3へ続く)
codepipelineの承認をslack上で行う(その1)
CodePipelineを使うと便利!
だけど、変更検知してそのままデプロイまで走って欲しくない時が出てきたから手動承認のステージを追加したところ、デプロイが走るたびにAWSアカウントに2段階認証してサインインしてからの承認がとてもめんどくさい・・・
そこでslackアプリを使って承認/却下をslack上で行えるようにしてみた。
最終目標はこの流れ。

これを実現するために必要なこととして1〜5をやっていく
1. slackアプリを作る
2. SNSとpauseのlambdaを作る
3. CodePipelineのApprovalの設定にSNSをセットする
4. ApiGatewayとpostのlambdaを作る
5. ApiGatewayを呼び出すURLをslackアプリにセットする
1. slackアプリを作る


左メニューのOAuth & Permissions で権限を設定する。

設定するとtokenができる。これはLambdaで使うので控えておく。

左メニューのApp Home で表示名などを設定する。

インストールすると Basic Information の Building Apps for Slack にチェックマークがつく

App Credentials の Verification Token もLambdaで使うので控えておく。

このslackアプリは該当のチャンネルに参加させておく。
(その2へ続く)