EKS Loverな僕にしか思いつけない最強のおみくじアプリ

みなさん、おはこんばんちわ〜
ドコモサービスイノベーション部ビックデータ担当1年目の平山です。
いきなりですが、Kubernetesって好きですか？僕は大好きです。
この記事では、そんな愛の深い僕だからこそ思いついたんじゃないか（使いたくてたまらなかった）なものを紹介しています。
アイデア的には結構面白いと自分では思っていて一応自慢の子です。
ぱっと思いついてそのままのノリでやったので明日見直したらそうじゃなくなってるかも知れませんが・・・
技術的にはEKSについてが中心の記事となります。お楽しみくだされば幸いです。

## 1. 概要
![1.アプリ動作図3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893417/7704ebb8-88dd-7cac-e20d-f72f23363caf.png)
EKSLoverによるEKSLoverのためのEKSLoverが興奮する(?)Webおみくじアプリシステムの構築記事です。
手順は以下の通りとなります。
(1) CFn(CloudFormation)によりネットワークを展開。
(2) Operator環境としてAWS Cloud9を準備・環境構築。
(3) コンテナイメージの管理先としてECR(Elactic Contianer Registory)を展開。
(4) EKS(Elactic Kubernetes Service)クラスタを展開。
(5) EKS上で僕の考えた最強のおみくじアプリを展開。
このYAMLエンジニアめと言われそうなAWSサービスの羅列ですね。
コンテナ・IaC・CI/CDに魅せられたエンジニアの末路なのかなと若輩者ながら思う今日この頃です。

![1.アプリ動作図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893417/f0f16c4b-8782-9adf-668c-2532c6b71271.png)

GoogleChromeでアクセスした時の様子です。`${DomainName}:${Port}`によりアクセスします。
HTTP GETリクエストで動作する仕様にしているので、curlコマンドなどでも結果が表示されます(HTMLが整形されずそのまま返ってきますが・・・)。
![1.アプリ動作図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893417/475abce3-5e09-d520-4b54-2e2f68ce39c2.png)
４パターンのどれかが表示されます。
大吉（10%）中吉(20%）小吉（60%）凶（10%）の排出率にしてあります。
これで今日の運勢調べられるぞヤッター！

## 2. はじめに(はじめじゃない)
上記アーキテクチャ図を見てみなさんどう思いましたか？ちょっと不可解なところははないでしょうか？そうですそうです。ELB(Elactic Load Balancer)から伸びる矢印に**"おみくじを引く"**って書いているところです。コンテナ大好きインフラエンジニアの方ならこの時点で何してるかわかるんですかね？
　はじめに言っておきますが、少々トリッキーなEKSの使い方をしていると思います。アイデアとして個人的面白いかもと思ったのを趣味で実装してしまいました。

## 3. 環境構築

### 3.1 ネットワークの構築
![3.1.1ネットワーク図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893417/7756eee6-4c1b-4d88-aef0-eada1ed206a8.png)
CFnによりネットワークを構築します。
テンプレートに入力するパラメータは

* Stack name：omikuji-network
* Project Prefix：omikuji
* CIDR(VPC/Subnet * 2)

です。
この時CIDR[^1]はEKSの都合上多く取っておいてください。理由は後述します。

```yaml:cfn-base-resources.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: omikuji-base-environment

Metadata:
  AWS::CloudFormation::Interface :
    ParameterGroups:
      - 
        Label:
          default: "Project Name Prefix"
        Parameters:
          - PJPrefix
      - 
        Label:
          default: "Data Center"
        Parameters:
          - AvailabilityZone1
          - AvailabilityZone2
          - VPCCIDR
          - PublicSubnet1CIDR
          - PublicSubnet2CIDR

    ParameterLabels:
      PJPrefix:
        default: "Project Prefix"
      AvailabilityZone1:
        default: "Availability Zone 1"
      AvailabilityZone2:
        default: "Availability Zone 2"
      VPCCIDR:
        default: "VPC CIDR"
      PublicSubnet1CIDR:
        default: "Public Subnet 1 CIDR"
      PublicSubnet2CIDR:
        default: "Public Subnet 2 CIDR"

Parameters:
  PJPrefix:
    Type: String
    Default: omikuji
  AvailabilityZone1:
    Type: String
    Default: ap-northeast-1a
  AvailabilityZone2:
    Type: String
    Default: ap-northeast-1c
  VPCCIDR:
    Type: String
    Default: 10.10.0.0/16
    Description: X:0~255
  PublicSubnet1CIDR:
    Type: String
    Default: 10.10.1.0/24
    Description: "X:same as above, Y:0~255"
  PublicSubnet2CIDR:
    Type: String
    Default: 10.10.2.0/24
    Description: "X:same as above, Y:0~255"

Resources:

  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCIDR
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
      - Key: Name
        Value: !Sub ${PJPrefix}-vpc

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: !Sub ${PJPrefix}-igw
  
  AttachIGW:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Ref PublicSubnet1CIDR
      AvailabilityZone: !Ref AvailabilityZone1
      MapPublicIpOnLaunch: 'true'
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${PJPrefix}-public-subnet-1

  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Ref PublicSubnet2CIDR
      AvailabilityZone: !Ref AvailabilityZone2
      MapPublicIpOnLaunch: 'true'
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${PJPrefix}-public-subnet-2

  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${PJPrefix}-rt

  AssociateIGWwithRT:
    Type: AWS::EC2::Route
    DependsOn: AttachIGW
    Properties:
      RouteTableId: !Ref RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
        
  AssociateSubnet1withRT:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref RouteTable

  AssociateSubnet2withRT:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref RouteTable

Outputs:
  WorkerSubnets:
    Value: !Join
      - ","
      - [!Ref PublicSubnet1, !Ref PublicSubnet2]


```

[^1]: Classless Inter-Domain Routing。クラスを使わずにIPアドレスの仕組みです。細かいことは今はどうでもいいので、ここではそのネットワークで使えるIPアドレスの範囲を決めていると思えばいいです。

### 3.2 Operator環境の構築
![3.2Operator環境図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893417/b779860b-56f7-9a3f-2864-3e96b84fa5f8.png)
先ほど作成したネットワーク上にEKSを操作する環境を構築します。
読者のみなさんと環境揃えるためにAWS Cloud9という統合開発環境のサービスを利用します。
構築時の設定は

* Create a new EC2 instance for environment (direct access)
* t3.small (2 GiB RAM + 2 vCPU)
* Amazon Linux 2 (recommended)

です。
こうしてできた環境で、

1. EKSを操作するためのツールのインストール
2. AWS Clous9のクレデンシャルの設定

をおこないます。

#### 3.2.1 ツールのインストール
[AWS公式のEKSユーザーガイド](https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html)に沿っておこないます。

```sh: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 /usr/local/bin
eksctl version
```
```sh:kubectlのインストール
curl -o kubectl https://amazon-eks.s3.us-west-2.amazonaws.com/1.18.9/2020-11-02/bin/linux/amd64/kubectl
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin
kubectl version --short --client
```
#### 3.2.2 クレデンシャルの設定
AWS Cloud9では、起動したユーザーの権限と同じ範囲のAMTC(Amazon Managed Temporary Credentials)という一時的な認証情報が払い出されるため、普段使いではこのようなことはしません。
しかし、EKS構築時に時間がかかるコマンド(20分くらい?)を打つ関係上、15分で有効期限が切れ新しいものに切り替わるという仕様のAMTCでの認証では、途中で`Token expired`と怒られてしまいます。そのため、AWSマネジメントコンソールでIAMユーザーのアクセスキーを払い出し、それをCloud9に食わせます。

```sh:aws_credentialの設定
PROFILE_NAME=cluster-admin
aws configure --profile ${PROFILE_NAME}
export AWS_DEFAULT_PROFILE=${PROFILE_NAME} # defaultからcluster-adminのユーザーへのprofileの切り替え
```
`aws configure`をしたときに、AWSからせっかくAMTCあげてるのになんで自分でクレデンシャル塗り替えようとするの？って言われちゃいますが、EKSをコマンドで構築するためには仕方ありません。ゴリ押しましょう。以下は、怒られている様子です。
![3.2.2AMTC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893417/0804904d-5584-8dcc-03d8-5e5cc2e17fef.png)

今回は認証部分にこだわるのは本質的でない（僕はEKSをごにょごにょしたいだけなんだ！！）のでやりませんが、本来、IAMのアクセスキーを食わしたまま運用するのはセキュリティ的に非常によろしくないです。なので、商用サービスであったりするなら、必要な時だけ使ってそのフェーズが過ぎれば、他のクレデンシャルにかえます。本案件も構築が終わってからはAMTCに戻すべきです(操作するのも記事を書くのもやりたくないので今回はしないですが)。ボタンぽちぽちで戻せます。

また、Kubernetes（EKSの中で実際に動いているのはこれ）の設定でRoleの情報をもとに認証を行うRBAC(Role-Based Access Control)を扱うことでも認証は可能です。実際、私が業務においてEKSで運用しているシステムは、構築終了後はEKSを操作するリソースが持つIAM Roleに対して、このRBACの設定をして運用し、アクセスキーは消し去るようにしています。少なくとも私が所属するチームでは、商用システムのユーザーでアクセスキーが１日以上残り続けたことはきっとないはずです・・・知らないですが・・・

ここはインシデントに関わる重要なことなので少し丁寧めに書いてしまいました・・・良く分かんなかったら飛ばして結構です。僕がこの記事で共有したいのはここじゃないので！

#### 3.3 コンテナイメージの準備
ECRを構築し、そこに本システムで使うコンテナを格納します。
ここから、釣りタイトルの"僕にしか思いつけない"とした部分の実装の仕方がだんだん見えてきます。
今回用意したコンテナイメージは全部で４つです。

* great-fortune(大吉)
* middle-fortune(中吉)
* small-fortune（小吉）
* misfortune（凶）

です。以下のアセット類をビルドしたものになります。

```docker:Dockerfile
FROM node:15.1.0-stretch-slim
WORKDIR /app
COPY index.js /app/index.js
COPY package.json /app/package.json
RUN npm install
RUN useradd -m -u 1009 app
USER app
EXPOSE 8081
CMD ["npm", "start"]
```
ちなみにDockerfileの書き方がテキトーすぎてすみません。あまりにも急いでいたのでレイヤー構造など全く意識せずにテキトー実装してしまった[^2]ので、コレはひどい書き方ですね。

[^2]: レイヤー的に変更されにくい`npm install`などは前の方に持ってくるべきです。逆に`COPY`とかは後ろの方が良いですね。今のままだと例えば、COPY元のindex.jsに少し変更を加えると、それ以降のレイヤーが全て更新されます。index.jsに手を加える度に`npm install`が走ってしまうというわけですね・・・ぼっ僕はコレを伝えるために、嫌だなぁと思いながら敢えてこんな実装にしたんですよ、ええまぁ・・・（目は合わせられない）
```js:index.js
const express = require('express');
const os = require('os');
const app = express();

app.get('/', (req, res) => {
    const hostname = os.hostname();
    res.send(`<!DOCTYPE html>
    <html lang=“ja”>
    <head>
        <meta charset=“UTF-8”>
        <title>
            おみくじ
        </title>
        <style>
            p {font-size:20px;}
            .fortune {
                color:red;
                font-size: 30px;
                font-weight: bold;
            }
        </style>
    </head>
    <body>
        <h1></h1>
        <p>あなたの運勢は</p>
        <p class="fortune">大吉</p>
        <p>です</p>
    </body>
    </html>`);
});

app.listen(8081);
console.log('Example app listening on port 8081.');
```
```json:package.json
{
    "name": "app",
    "version": "1.0.0",
    "description": "Container App",
    "main": "index.js",
    "scripts": {
        "start": "node index.js"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
        "express": "^4.17.1"
    }
}
```
`index.js`に主にコンテナの挙動が書いてあります。
HTTP GETリクエストをポート：8081に受けると、HTML形式のメッセージを返す仕様です。
４つのコンテナの違いは、メッセージの表す内容が`大吉`・`中吉`・`小吉`・`凶`になっていることです。

コンテナ大好きインフラエンジニアの皆さん、今回の実装方法だんだんわかって来ましたかね？

## 4. EKSクラスタの作成

ここは非常に爽快です。たった一つのコマンド+パラメータでコンテナを管理する素晴らしいシステムが出来上がります。魔法で巨大建造物を作っているかのよう。顕現せよ、混沌に満ちた世界[^3]において、我に安定運用への光を示せ！！クーヴァネティス！！

```sh:create_cluster.sh
subnet=subnet-00xxxxxxxxxxxxx,subnet-00xxxxxxxxxxxxx

eksctl create cluster \
--name eks-handson-cluster \
--version 1.18 \
--region ap-northeast-1 \
--vpc-public-subnets ${subnets} \
--nodegroup-name omikuji-nodegroup \
--node-type t2.small \
--nodes 1 \
--nodes-min 1 \
--nodes-max 1000 \
--asg-access
```
この`eksctl create cluster`コマンドを実行するだけで、内部で２つのCFnが動き、必要なものを全て作ってくれます。ここで20分ほどかかるので、Cloud9でAMTCが使えなかったんですよね。
![4EKScluster.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893417/0ce3ecd4-84ee-0d7e-d450-21f3301a345d.png)
Cloud9のターミナルのログを見るとなにしてるかわかりますね。ここ見ながら、マネコンで実際何作られるかとか細かく見ていくと20分かかっても許せます。こんなに作ってくれてるのかと・・・
これで、いよいよコンテナをバシバシ走らせて管理していけますね！やっとです。現状は以下のような状態になっています。
![4EKSC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893417/943c4abd-9bad-e2ff-d7f6-b67366b45a35.png)

[^3]: この世の中、予測できない不都合なことは起こりますよね。突如トラフィックの謎スパイクが生じるとか良く聞きます。僕自身は若輩ゆえ、まだ経験したことないですが・・・

## 5. ぼくがかんがえたさいきょうのおみくじアプリの実装
ここまではAWSのプラットフォームでの構築でした。ここからは、AWSリソースで構築されてはいるもののKubernetesというプラットフォームでの構築になります。

Kubernetesではyaml形式で書かれたマニフェストファイルにより宣言的にAPIを呼ぶことで、あとはシステムがよしなにその望む状態のシステムを構築してくれます。そのメタ的に管理してくれる部分をコントロールプレーンノード（マスターノードともいう）と言い、対して実際にコンテナが動き仕事をする部分をワーカーノードと言います。なのでこれ以降の操作は非常に簡単で、マニフェストファイルを作成し、それを`kubectl apply`するだけです。
とても楽だなぁ。コンテナのオーケストレーションっていうのも納得です。
![5eksope.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893417/cd4801b4-10d4-2017-9350-ab06858fdedf.png)
さて、ようやく**"EKS Loverな僕にしか思いつけない最強の"**部分にきました(笑)。~~なんかここまできて、いや案外そういう実装あるのかもなとか思い始めて来たなぁ。でももう時間ないしコレでいくしかないんだ・・・~~
ここで、ネタバラシです。実装方法としては、おみくじにそのものに値するようなコンテナ（つまり大吉とか内容を表示するもの）を複数作り、その前にロードバランサのようなもの（正確にはKubernetesのService.LoadBalancerオブジェクト）をかませて、投げたGETリクエストのバランシングによりランダム性を持たせるというものです。システムの設計がオブジェクト指向に則ってます。ロードバランサがおみくじコンテナを引いている感じですね。

ランダム性は、今回はService.NordPortオブジェクトに依存しています。あれ？さっきService.LoadBalancerオブジェクトって言ってたやんって思った人、鋭いですね・・・（数行前の話ではありますが）。実はService.NordPortオブジェクトはService.LoadBalancerオプジェクト利用時に自動的に割り当てられるんです。なので、Service.LoadBalancerオプジェクトを使う時にService.NordPortオブジェクトが自動的に割り当てられ、その内部仕様はデフォルトではiptablesでのルーティングを採用していて、その設定はランダムに割り振るようになっているが故に、ランダムにおみくじを引いてくれるというわけです、ちょっとややこしいですかね。まぁとりあえずランダムなんです。

Kubernetesでの手順は以下の通りとなります。
(1)オートスケールできるようにするための設定
(2)各コンテナの展開(Deployment)
(3)ロードバランサの展開(Service:LoadBalancer)

### 5.1 オートスケールできるようにするための設定
これは、もしはじめから展開するコンテナ(正確にはPod)の数がわかっているなら、それに合わせてノードの数を指定して展開すればいいのですが、EKS使うからには、ノードの数の管理もKubernetesに任せたいが私の信条なので実装しました。オートスケールしてるのみると「おぉ、いいなぁ」ってなりますもんね！
`eksctl create cluster`で構築した時の設定では、ワーカーノードはASG(AutoScaling)で作られています。つまりAWSのプラットフォーム上ではノードはスケールすることができるようリソースが展開されています。しかし、EKSを使う場合、Kubernetesというプラットフォーム上でコンテナに関するリソースを管理していく事になります。そのため、KubernetesがASGに働きかけられるようにするために、Cluster Autoscalerというものを展開する必要があります。

[AWS公式](https://aws.amazon.com/jp/premiumsupport/knowledge-center/eks-cluster-autoscaler-setup/)がそのためのマニフェストファイルを用意してくれているので、それに従って、設定しましょう。
`cluster-autoscaler-autodiscover.yaml`がダウンロードできるので、それにクラスタ名を自分の環境のものにして、`kube apply`するだけです！

### 5.2 各コンテナの展開（おみくじの補充）
```yaml:deployment_great_fortune.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: great-fortune
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 2
      maxSurge: 4
  replicas: 1
  selector:
    matchLabels:
      sample-label: omikuji-app
  template:
    metadata:
      labels:
        sample-label: omikuji-app
    spec:
      containers:
        - name: omikuji-great-fortune
          image: 262403382529.dkr.ecr.ap-northeast-1.amazonaws.com/omikuji/fortune:great-fortune
          ports:
          - containerPort: 8081
```
上記のようなマニフェストファイルを`kubectl apply`します。
こちらはgreat_fortuneなので大吉のおみくじコンテナなのですが、`replicas: 1`となっているので、１つ展開されています。
今回、大吉（10%）中吉(20%）小吉（60%）凶（10%）の排出率するので

* 大吉 `replicas: 1`
* 中吉 `replicas: 2`
* 小吉 `replicas: 6`
* 凶 　`replicas: 1`

で展開して、おみくじ（コンテナ）を箱（ワーカーノード）に入れましょう！

そしてここで、CIDRを多めに切っておいてくださいといった先程のフラグを回収します。
ではなぜ多めにと言ったかというと、Pod１つ構築するたびににIPアドレスが１つ消費されるからです。従って、IPアドレスを絞りすぎた場合、IPアドレスの数の制限のせいでPodを起動できなくなることもあるので、ここは実運用でCIDRを切るときはその辺も考慮して設定をしなければいけません。今回は雑に多めに切りました。

### 5.3 ロードバランサの展開（おみくじを販売開始）
![ロードバランサ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893417/af918c02-3551-44b8-a862-efa530b5eec4.png)

今のままでは、展開したコンテナたちにはアクセスすることができません。エンドポイントがないからです。
それを実現するのがService APIの中のLoadBalacerオブジェクトです（コレはKubernetes側のオブジェクトなので注意）
今回EKSを用いているので、KubernetesのService.LoadBalancerオブジェクトを作成すると、実態としてはAWSのロードバランサであるELB(ClassicLoadBalancerの方)が構築されます。

細かい説明は置いておいて、とりあえず以下のマニフェストファイルを`kubectl apply`しましょう。

```yaml:service_loadbalancer.yaml
apiVersion: v1
kind: Service
metadata:
  name: loadbalancer
spec:
  type: LoadBalancer
  ports:
  - name: "http-port"
    protocol: "TCP"
    port: 8080
    nodePort: 30081
    targetPort: 8081
  selector:
    sample-label: omikuji-app
```
このマニフェストファイルでは、`selector`として`sample-label`を指定しています。すると、コントロールプレーンにより、同じ`sample-label`を持ち、その値が一致する（今回は`omikuji-app`）するPodがロードバランシングする先として登録されます。

上のELBの話と合わせて、頭がごっちゃになりかねないので、ここでトラフィックの通るルートをおさらいしておきましょう。
1. ユーザーからのGETリクエストがELBに
2. ELBからService.NordPortオブジェクト
3. Service.NodePortオブジェクトからランダムに選ばれたPod
といった具合です。詳しくはKubernetesの公式ドキュメントなどをみると内部実装などがわかると思います。

それではロードバランサのエンドポイントを確認したいので、実際にAWSのマネコンで見てみましょう。
![5_load.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893417/c58dbabe-023d-0afc-15ed-d75f7ffde4eb.png)
マニフェストファイル通りKubernetesがよしなにやってくれていることがわかりますね。それではここのDNS nameを使ってアクセスして動きを確認しましょう。Chromeのアクセスバーに`${Domain name}:${Port}`でポート番号を指定しながらURIにGETリクエストを投げることができます。今回ロードバランサのポートは`8080`を開けている（上記マニフェストファイルで指定）ので、`XXXX・・・.elb.amazonaws.com:8080`と入力します。

![アプリ動作４.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893417/eb20ae85-3975-da6e-1ba1-0644d58f89dd.png)

おみくじが無事引けました。何度か試行する場合、ブラウザからリクエストを送る方法だとどこかに何かがキャッシュ[^4]されているみたいでそのままの結果が返って来てしまいます（F5連打はうまくいかない）。そのためランダム性をすぐに確認したい場合は、curlリクエストですると毎回結果が変わって、バランシングのランダム性が体感できると思います。

```sh:curlにより、100回おみくじを引くコマンド
for itr in `seq 100`; do curl a9809XXXXXXXXXXXXXXXXXXXXXXXXXX.ap-northeast-1.elb.amazonaws.com:8080 --silent | grep -G "<p\sclass=\"fortune\">"; done
```

![くじ結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893417/e8a99cc9-7e84-6ba4-7782-6a4cc1bde01f.png)

[^4]: GoogleChromeの検証ツールでブラウザのキャッシュを切ってもF5連打でおみくじはすぐには変わらなかったです。ブラウザでも少し時間おけば違う結果返ってくるんですが、ちょっとこの辺よくわかってないです・・・有識者のどなたか、怪しい箇所思いついたらどうか教えてくださいませ・・・

最後にKubernetesのリソースたちを載せておきます。
![resources.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893417/48eccebf-6bad-0313-6f44-45398fb513b0.png)


## 6 まとめ
いかがだったでしょうか。記事を開く前にこの実装の仕方思いついた人いますか？
実用性や有用性はともかくタイトル詐欺にならず、ちょっと面白いなと思っていただけたら幸いです。ぱっと思いついてそのまま実装して、急いで執筆したのであまり色々は深く考えていないので、ツッコミどころもあるかと思いますが、共有していただけたら嬉しいです。

この記事で、EKS(というよりKubernetes)の基本は勉強できるかと思います。
ローカルPCの環境に依存しないように作ったので、興味が湧いた人はぜひ試してみてください。

それでは、終わりm...えっ？こんなのアプリのロジックはPythonやC＋＋で乱数使って一瞬で実装できるし、AWS使うならAPIゲートウェイやLambda使えば簡単に同じようなもの実装できるじゃないかって？？
うるさいです。なんでも`apply`でこういうようにしといてねって言ったらその通りにしてくれるEKSちゃん可愛いじゃないですか。結構愛で実装してしまいましたが、いずれ面白いもの作って見せますよ！！

ここまでみてくださった方、誠にありがとうございました。それではっ！！