🚴🏻‍♀️

【AWS AppMesh/AWS Cloud Map】AWSの基礎を学ぼう 特別編

2021/07/03に公開

最初に

途中で頓挫しています。
経過報告です。

概要

「AWSの基礎を学ぼう 特別編」で”AWS AppMesh と AWS Cloud Map”のハンズオンイベントに参加した感想ページです。

「AWS エバンジェリストシリーズ AWSの基礎を学ぼう」とは

AWS エバンジェリストシリーズ AWSの基礎を学ぼう

以下、Connpassページより引用

Amazon Web Services (AWS)は現在200を超えるサービスを提供し、日々サービスの拡充を続けています。
このAWS エバンンジェリストシリーズでは週次でAWSのサービスをひとつづつ取り上げながらその基礎を説明していく 初心者、中級者をターゲットとした講座です。午後の仕事前にスキルアップを一緒にしませんか?
注意点 登壇者による発表内容はアマゾン ウェブ サービス ジャパンとして主催しているものではなく、コミュニティ活動の一環として勉強会の主催を行っているものです。

毎週ありがとうございます!


AWS AppMesh と AWS Cloud Mapとは

AWS AppMesh

https://aws.amazon.com/jp/app-mesh

AWS App Mesh とは何ですか?
AWS App Mesh で、サービス間の通信のモニタリング、管理、デバッグが簡単になります。App Mesh は、マイクロサービスコンテナと同時にデプロイされるオープンソースのサービスメッシュプロキシである Envoy を使用します。App Mesh はモニタリングと追跡のために AWS のサービスと統合され、多くの一般的なサードパーティ製ツールとの使用が可能です。App Mesh は、Amazon ECS、Amazon EKS、AWS Fargate、AWS で実行する Kubernetes が管理するマイクロサービスコンテナ、および Amazon EC2 上で実行するサービスで使用できます。

EC2上でも動くのかー

AWS Cloud Map

https://aws.amazon.com/jp/cloud-map/

AWS Cloud Map とは
AWS Cloud Map は、クラウドリソース検出サービスです。Cloud Map ではアプリケーションリソースにカスタム名を付けることができ、これらの動的に変化するリソースの場所を自動的に更新します。アプリケーションがそのリソースの最新の場所を常に検出するため、アプリケーションの可用性が向上します。
モダンアプリケーションは一般的に、API を通じてアクセス可能な、特定の機能を実行する複数のサービスから構成されています。各サービスは、データベース、キュー、オブジェクトストア、カスタマー定義のマイクロサービスといったさまざまな他のリソースと対話をしますが、機能するためには、依存しているインフラストラクチャリソースすべての場所を見つけることができなければなりません。
Cloud Map により、データベース、キュー、マイクロサービス、カスタム名を持つその他クラウドリソースなどのアプリケーションリソースを登録できます。その後、Cloud Map はリソースの状態を継続的にチェックし、その場所が最新であることを確認します。そして、アプリケーションのリソースは、アプリケーションバージョンとデプロイ環境に基づき、必要な実際のリソースの場所をレジストリにクエリできます。


紐解いて行こう。

亀田さんのハンズオン資材
https://github.com/harunobukameda/AWS-App-Mesh-AWS-Cloud-Map

ハンズオンをやっていて何をしているかが理解できない点が多かったので、それを紐解いて行こう。
主にコマンドの紐解き

1.

rm -vf ${HOME}/.aws/credentials

意図/意味
Cloud9デフォルトでついているcredentialsを削除し、EC2ロールを有効にする。

2.

export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
export AWS_REGION=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | \
  grep region | cut -d\" -f4)

echo "export ACCOUNT_ID=${ACCOUNT_ID}" >> ~/.bash_profile
echo "export AWS_REGION=${AWS_REGION}" >> ~/.bash_profile
aws configure set default.region ${AWS_REGION}
aws configure get default.region

意図/意味
bash_profileにACCOUNT_ID、AWS_REGIONを設定する
aws configureにdefault.regionを設定する

3.

aws sts get-caller-identity

意図/意味
今使っているRoleがAppMesh-Workshop-Adminであるか確認する

4.

# create a folder for the scripts
mkdir ~/environment/scripts

# tools script
cat > ~/environment/scripts/install-tools <<-"EOF"

#!/bin/bash -ex

sudo yum install -y jq gettext bash-completion

sudo curl --silent --location "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/linux_64bit/session-manager-plugin.rpm" -o "session-manager-plugin.rpm"
sudo yum install -y session-manager-plugin.rpm

sudo curl --silent --location -o /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v1.16.8/bin/linux/amd64/kubectl
sudo chmod +x /usr/local/bin/kubectl
echo 'source <(kubectl completion bash)' >>~/.bashrc
source ~/.bashrc

curl --silent --location "https://github.com/weaveworks/eksctl/releases/download/latest_release/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv -v /tmp/eksctl /usr/local/bin

if ! [ -x "$(command -v jq)" ] || ! [ -x "$(command -v envsubst)" ] || ! [ -x "$(command -v kubectl)" ] || ! [ -x "$(command -v eksctl)" ] || ! [ -x "$(command -v ssm-cli)" ]; then
  echo 'ERROR: tools not installed.' >&2
  exit 1
fi

pip install awscli --upgrade --user

EOF

chmod +x ~/environment/scripts/install-tools

意図/意味
/environment/scripts/install-toolsというscriptを作成している。scriptでインストールしているのは以下

  • jq
    JSONから抽出、集計、整形する機能

  • gettext
    アプリケーションの国際化ができる機能。例えば日本からのアクセスには「ようこそ」、アメリカからのアクセスには「Welcome」と切り分ける

  • bash-completion
    コマンドのオプションで入力補完をしてくれる機能

  • session-manager-plugin
    AWS Session Managerのプラグイン

  • kubectl
    Kubernetesクラスターを制御するコマンドラインツール

  • eksctl
    Amazon EKSのクラスタとノードを制御するコマンドラインツール

  • aws cli
    Cloud9にもともとあるけどUPDATE

5.

~/environment/scripts/install-tools

意図/意味
4で作成したスクリプトを実行

6.

# clone the github repositories
cd ~/environment
git clone https://github.com/brentley/ecsdemo-frontend.git
git clone https://github.com/brentley/ecsdemo-nodejs.git
git clone https://github.com/brentley/ecsdemo-crystal.git

意図/意味
ハンズオンで必要な資材をgithubからclone

7.

cd ~/environment
curl -s https://raw.githubusercontent.com/brentley/appmeshworkshop/master/templates/appmesh-baseline.yml -o appmesh-baseline.yml

意図/意味
appmeshworkshopのCloudFormationテンプレートをGET
1000行もあるなぁ。。。

8.

# Define environment variable
IAM_ROLE=$(curl -s 169.254.169.254/latest/meta-data/iam/info | \
  jq -r '.InstanceProfileArn' | cut -d'/' -f2)

#Check if the template is already deployed. If not, deploy it
CFN_TEMPLATE=$(aws cloudformation list-stacks | jq -c '.StackSummaries[].StackName | select( . == "appmesh-workshop" )')

if [ -z "$CFN_TEMPLATE" ]
then
  echo "Deploying Cloudformation Template"
  aws cloudformation deploy \
    --template-file appmesh-baseline.yml \
    --stack-name appmesh-workshop \
    --capabilities CAPABILITY_IAM \
    --parameter-overrides Cloud9IAMRole=$IAM_ROLE
else
  echo "Template already deployed. Go ahead to the next chapter."
fi

意図/意味

  • curl -s 169.254.169.254/latest/meta-data/iam/info
    インスタンスプロファイルの情報を取得する。 これ知らんかった。

  • aws cloudformation list-stacks | jq -c '.StackSummaries[].StackName | select( . == "appmesh-workshop" )'
    appmesh-workshopというStackがあるか確認
    StatusがDELETE_COMPLETEのStackも取得できちゃうので、DELETE_COMPLETEであるなら実行してもええかな。

  • aws cloudformation deploy
    --template-file appmesh-baseline.yml
    --stack-name appmesh-workshop
    --capabilities CAPABILITY_IAM
    --parameter-overrides Cloud9IAMRole=$IAM_ROLE
    AWS CLIでcloudformation deployを実行

8補足 appmesh-baseline.yml

https://raw.githubusercontent.com/brentley/appmeshworkshop/master/templates/appmesh-baseline.yml

意図/意味
1000行ぐらいあるのでリンク先参照

  • Crystal
    ECR作成
  • NodeJS
    ECR作成
  • ECSCluster
    ECSクラスタ作成
  • CrystalTaskDefinition
    ECSタスク定義
  • ExternalLoadBalancerSecurityGroup
    LBのセキュリティグループ
  • ExternalLoadBalancer
    LB作成 紐付くパブリックサブネットは3つ
  • ExternalListener
    LBのリスナーはhttp
  • RubyTargetGroup

9.

# Retrieve private key
aws ssm get-parameter \
  --name /appmeshworkshop/keypair/id_rsa \
  --with-decryption | jq .Parameter.Value --raw-output > ~/.ssh/id_rsa

# Set appropriate permission on private key
chmod 600 ~/.ssh/id_rsa

# Store public key separately from private key
ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub

意図/意味

  • aws ssm get-parameter…
    CloudFormationで作成されたパラメタストアにある値を復号化して~/.ssh配下に秘密鍵として出力
  • ssh-keygen…
    秘密鍵より公開鍵を作成して~/.ssh配下に出力

10.

# bootstrap script
cat > ~/environment/scripts/bootstrap <<-"EOF"

#!/bin/bash -ex

echo 'Fetching CloudFormation outputs'
~/environment/scripts/fetch-outputs
echo 'Building Docker Containers'
~/environment/scripts/build-containers
echo 'Creating the ECS Services'
~/environment/scripts/create-ecs-service
echo 'Creating the EKS Cluster'
~/environment/scripts/build-eks

EOF

# fetch-outputs script
cat > ~/environment/scripts/fetch-outputs <<-"EOF"

#!/bin/bash -ex

STACK_NAME=appmesh-workshop
aws cloudformation describe-stacks \
  --stack-name "$STACK_NAME" | \
jq -r '[.Stacks[0].Outputs[] | 
    {key: .OutputKey, value: .OutputValue}] | from_entries' > cfn-output.json

EOF

# Create EKS configuration file
cat > ~/environment/scripts/eks-configuration <<-"EOF"

#!/bin/bash -ex

STACK_NAME=appmesh-workshop
PRIVSUB1_ID=$(jq < cfn-output.json -r '.PrivateSubnetOne')
PRIVSUB1_AZ=$(aws ec2 describe-subnets --subnet-ids $PRIVSUB1_ID | jq -r '.Subnets[].AvailabilityZone')
PRIVSUB2_ID=$(jq < cfn-output.json -r '.PrivateSubnetTwo')
PRIVSUB2_AZ=$(aws ec2 describe-subnets --subnet-ids $PRIVSUB2_ID | jq -r '.Subnets[].AvailabilityZone')
PRIVSUB3_ID=$(jq < cfn-output.json -r '.PrivateSubnetThree')
PRIVSUB3_AZ=$(aws ec2 describe-subnets --subnet-ids $PRIVSUB3_ID | jq -r '.Subnets[].AvailabilityZone')
AWS_REGION=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | grep region | cut -d\" -f4)

cat > /tmp/eks-configuration.yml <<-EKS_CONF
  apiVersion: eksctl.io/v1alpha5
  kind: ClusterConfig
  metadata:
    name: $STACK_NAME
    region: $AWS_REGION
  vpc:
    subnets:
      private:
        $PRIVSUB1_AZ: { id: $PRIVSUB1_ID }
        $PRIVSUB2_AZ: { id: $PRIVSUB2_ID }
        $PRIVSUB3_AZ: { id: $PRIVSUB3_ID }
  nodeGroups:
    - name: appmesh-workshop-ng
      labels: { role: workers }
      instanceType: m5.large
      desiredCapacity: 3
      ssh: 
        allow: false
      privateNetworking: true
      iam:
        withAddonPolicies:
          imageBuilder: true
          albIngress: true
          autoScaler: true
          appMesh: true
          xRay: true
          cloudWatch: true
          externalDNS: true
EKS_CONF

EOF

# Create the EKS building script
cat > ~/environment/scripts/build-eks <<-"EOF"

#!/bin/bash -ex

EKS_CLUSTER_NAME=$(jq < cfn-output.json -r '.EKSClusterName')

if [ -z "$EKS_CLUSTER_NAME" ] || [ "$EKS_CLUSTER_NAME" == null ]
then
  
  if ! aws sts get-caller-identity --query Arn | \
    grep -q 'assumed-role/AppMesh-Workshop-Admin/i-'
  then
    echo "Your role is not set correctly for this instance"
    exit 1
  fi

  sh -c ~/environment/scripts/eks-configuration
  eksctl create cluster -f /tmp/eks-configuration.yml
else

  NODES_IAM_ROLE=$(jq < cfn-output.json -r '.NodeInstanceRole')

  aws eks --region $AWS_REGION update-kubeconfig --name $EKS_CLUSTER_NAME
  cat > /tmp/aws-auth-cm.yml <<-EKS_AUTH
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: aws-auth
      namespace: kube-system
    data:
      mapRoles: |
        - rolearn: $NODES_IAM_ROLE 
          username: system:node:{{EC2PrivateDNSName}}
          groups:
            - system:bootstrappers
            - system:nodes
EKS_AUTH
  kubectl apply -f /tmp/aws-auth-cm.yml
fi

EOF

# build-containers script
cat > ~/environment/scripts/build-containers <<-"EOF"

#!/bin/bash -ex

CRYSTAL_ECR_REPO=$(jq < cfn-output.json -r '.CrystalEcrRepo')
NODEJS_ECR_REPO=$(jq < cfn-output.json -r '.NodeJSEcrRepo')

$(aws ecr get-login --no-include-email)

docker build -t crystal-service ecsdemo-crystal
docker tag crystal-service:latest $CRYSTAL_ECR_REPO:vanilla
docker push $CRYSTAL_ECR_REPO:vanilla

docker build -t nodejs-service ecsdemo-nodejs
docker tag nodejs-service:latest $NODEJS_ECR_REPO:latest
docker push $NODEJS_ECR_REPO:latest

EOF

# create-ecs-service script
cat > ~/environment/scripts/create-ecs-service <<-"EOF"

#!/bin/bash -ex

CLUSTER=$(jq < cfn-output.json -r '.EcsClusterName')
TASK_DEF=$(jq < cfn-output.json -r '.CrystalTaskDefinition')
TARGET_GROUP=$(jq < cfn-output.json -r '.CrystalTargetGroupArn')
SUBNET_ONE=$(jq < cfn-output.json -r '.PrivateSubnetOne')
SUBNET_TWO=$(jq < cfn-output.json -r '.PrivateSubnetTwo')
SUBNET_THREE=$(jq < cfn-output.json -r '.PrivateSubnetThree')
SECURITY_GROUP=$(jq < cfn-output.json -r '.ContainerSecurityGroup')

aws ecs create-service \
  --cluster $CLUSTER \
  --service-name crystal-service-lb \
  --task-definition $TASK_DEF \
  --load-balancer targetGroupArn=$TARGET_GROUP,containerName=crystal-service,containerPort=3000 \
  --desired-count 3 \
  --launch-type FARGATE \
  --network-configuration \
      "awsvpcConfiguration={
        subnets=[$SUBNET_ONE,$SUBNET_TWO,$SUBNET_THREE],
        securityGroups=[$SECURITY_GROUP],
        assignPublicIp=DISABLED}"

EOF

chmod +x ~/environment/scripts/*

意図/意味
/environment/scripts/bootstrapというscriptを作成している。設定内容は以下

  • bootstrap
    単純な起動Shellで「fetch-outputs」、「build-containers」、「create-ecs-service」、「build-eks」を順番に起動している

  • fetch-outputs
    CloudFormationのOutputsにある情報をcfn-output.jsonに出力する

  • build-eks
    eksの設定ファイルを作成する

    • cfn-output.jsonからPRIVSUB1_ID,PRIVSUB2_ID,PRIVSUB3_IDを取得
    • SUBNET IDを利用してAWS CLIでAZを取得する
    • eks-configuration.ymlを作ってEKSの設定処理を実行 EKSはよく分からんのでPASS
    • aws-auth-cm.ymlを作って認証処理を実行 EKSはよく分からんのでPASS
  • build-containers

    • cfn-output.jsonからCRYSTAL_ECR_REPO,NODEJS_ECR_REPOを取得
    • docker buildしてリポジトリにPUSH
  • create-ecs-service

    • cfn-output.jsonからCLUSTER,TASK_DEF,TARGET_GROUP,SUBNET_ONE,SUBNET_TWO,SUBNET_THREE,SECURITY_GROUPを取得
    • aws cliでECSのサービス作成を行う

11.

~/environment/scripts/bootstrap

意図/意味
10で作成したbootstrapを実行
ERROR

Error: timed out (after 25m0s) waiting for at least 3 nodes to join the cluster and become ready in "appmesh-workshop-ng"

これが理由なのか、これ以降でもエラーを吐くので、先週の土曜日と合わせて2回目の中断。
アカウント変えてやるか。

Discussion