CloudWatch Agentのインストールと設定手順に関する備忘録


はじめに

EC2インスタンスの標準メトリクスでは取得できないようなリソース監視であったり、EC2インスタンス上のプロセス監視を行う時には、CloudWatch Agentをインストールして使用しますが、なかなか普段構築する機会がないものの、今後も一定の需要はあるので、自分メモ的な感じで備忘録としてまとめています。

スピード優先だったので、これが正解とは言えないと思うのですが、まぁ自分メモですので。

CloudWatch Agentのインストール方法

本来は、「新しいCloudWatch Agentでメトリクスとログの収集が行なえます」の記事と同じような形で順を追ってインストールして行ったほうが理解としては確実になるのですが、今回はスピード感重視であるのと、新規に構築したEC2インスタンスに対してインストールを行わなければいけなかったので、AWS Systems Manager Quick Setupの機能を用いてSSM Agentと同時にインストールしてしまいます。

インストールの手順は至って簡単で、

  • AWS Systems Manager – 高速セットアップ(Quick Setup)を選択して、高速セットアップ(Quick Setup)のコンソールに移動
  • 右上の[Create]ボタンをクリック
  • ウィザードに飛ぶので、Host Managementの[Create]ボタンをクリック
  • Configuration optionsのセクションで、下の画像の通り、チェックボックスを全て選択して、SSM Agentと一緒にCloudWatch Agentも自動的にインストールできるようにする
  • Targetsのセクションで、インストール対象のEC2インスタンスがいるリージョン(基本的にはCurrent Regionで問題ないはず)で、Manualを選択して、インストールしたいEC2インスタンスを選択する
  • 右下の[Create]ボタンをクリックすれば、勝手にSystems Manager側で、SSM AgentのインストールとCloudWatch Agentのインストールまでやってくれる

追加のIAMポリシーの設定

Quick Setupの機能を用いてインストールを実行すると、デフォルトでEC2インスタンスに対して、AmazonSSMRoleForInstancesQuickSetupというIAMロールがアタッチされるのですが、これだけではCloudWatch Agentの初期設定ができないため、あらかじめ、このIAMロールに対して、CloudWatchAgentAdminPolicyというAWS管理ポリシーを追加でアタッチしてあげる必要があります。

自分の場合はここで思い切りひっかかってしまったので要注意。

これでCloudWatch Agentの初期設定は完了です。

CloudWatch Agentの初期設定

sshでEC2インスタンスに入り、

sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard

を実行するとウィザードが立ち上がるため、その内容に従って淡々と設定を進めていけば問題がないかなと思います。1箇所だけ、使い回しを利かせるため、以下の内容については”1″を選択しておいた方が便利です。

Do you want to store the config in the SSM parameter store?
1. yes
2. no
default choice: [1]:
1

で、最終的に以下のメッセージが出力されれば、初期設定は無事に完了になります。

Successfully put config to parameter store AmazonCloudWatch-linux.
Program exits now.

CloudWatch Agentは、運用上様々な機会で使用することが多いと思うので、できるだけ設定作業は簡素化しておきたいところ。今回は手作業で試してみましたが、今後のことも考えてコード管理できるようにしたいと思っています。

カテゴリー: AWS, Work | タグ: , , | コメントする

AWS認定SysOpsアドミニストレータ アソシエイト(AWS SOA-C02)に合格しての感想


昨年の夏にAWS認定ソリューションアーキテクト プロフェッショナルに合格してからかなり間が空いてしまったのですが、なんとか次の目標にしていたAWS認定SysOpsアドミニストレーター アソシエイト試験に774点で合格することができました。スコアとしてはもう少しとりたかったなというのが本当のところですが、これがいまの実力なので仕方がないでしょう。これでアソシエイトレベルの認定資格は全て取得することができました。AWS CLFを含めると5冠となります。

どんな風に勉強したのか

この記事を書いている2022/5/21時点では、参考書などは全く出版されていません。なのでどんな風に勉強を進めていけばいいのか最初は皆目見当がつきませんでした。しかも昨年の夏に試験のバージョンが上がってから難易度は確実に上がり、通常の選択式試験に加えてラボ試験が導入されたので、余計に困ってしまいました。

先に受験日を決めてしまう

この試験に限らずなのですが、まずは先に受験日を決めてしまいましょう。そこから逆算して勉強する計画を立てるのが一番いいです。というか受験日を決めておかないと、マイルストーンがぼやけてしまってついつい勉強をサボりがちになってしまうので、追い込みすぎない程度に追い込んでおくのが大事です。

自分の場合、結果的に半年コースになってしまったのですが、3ヶ月を一区切りとして勉強するように心がけていました。

出題範囲となるBlack Beltをとにかく読む

出題範囲は正直広いんですが、まずはBlack Beltと呼ばれる「サービス別資料 | AWSクラウドサービス活用資料集」から、運用に関連するサービスの資料を片っ端から読んでいき、サービスごとに要点を取りまとめていきました。要点を取りまとめるときにはDay OneというmacOS向けのジャーナルアプリケーションを活用しました。勉強を開始したのが転職の前で、InspectorやCloudTrailといったガバナンス系のサービスをなかなか使ってこなかったので、各サービスの特徴を理解してまとめるという流れは、頭の整理に結構役に立ったのではないかなと思います。

AWS SAPの参考書を読む

AWS SOAの試験範囲とは直接関係ないように思えますが、実は出題範囲の中にAWS SAPで問われる内容と結構被っているというのを、模擬試験を受けてみた中で強く感じたのと、ちょうど良いタイミングで「AWS認定資格試験テキスト&問題集 AWS認定ソリューションアーキテクト – プロフェッショナル」という、かなりまともな参考書が発売されたということもあり、この内容をざっと読みながら、やはり同じようにDay One上にまとめていきました。複雑な構成に関する問題もちょろちょろ出てくるので、結果としてですが、先にAWS SAPをとっておいてよかったなと感じています。

この本は試験対策以外にもAWS上でリソースを構成していくときの参考になるので、1冊持っておいても損ではないと思います。個人的にはおすすめ。

問題を解きながら間違えた箇所を復習していく

問題集に関しては、幸いにしてUdemyや通称”koiwa”と呼ばれているWEB問題集があるので、それを片っ端から解いていきながら、間違えた箇所に関するAWSの公式ドキュメントを読んで復習するという流れを2回繰り返しました。この時はすでに転職後でNotionが使えるようになっていたので、受けてはNotionにまず転記して、間違えた箇所を復習という流れを繰り返していきました。

ラボ試験対策

ラボ試験対策と言っても、もともと実務でAWSマネージメントコンソールは頻繁に触っていたので、特別なことはしていないです。ただ、座学だけでは絶対にラボ試験はクリアできないというのは確かです。というのは、この試験が単純に知識だけを求めているものではないので。そういう意味では、個人的にはラボ試験が導入されたのは正解だったんじゃないかなと思っています。

ラボ試験の内容そのものはそれほど特別なことを求めているわけではないので、AWSマネージメントコンソールに触ったことがない人は、クレジットカードを人質にしてでも個人用のAWSアカウントを取得して、出題されている範囲のコンソールがどのようになっているのかだけでも確認したほうが絶対にいいです。

次の目標

もうここまできたのだから、あとはAWS認定DevOpsエンジニア プロフェッショナル(AWS DOP-C01)に合格して、6冠を達成することのみです。AWS SAPを受験した時のように、ちょっと特別体制を敷いて受ける必要が出てくるのですが、そこはきちんと計画を立てて準備を進めていこうと考えています。

カテゴリー: AWS | タグ: | コメントする

Violaレッスン6回目


Violaのレッスンを始めてからちょうど3か月が経過しました。習い始める前までは、楽器を弾くこと自体は楽しいのだけれども、色々姿勢が崩れていてものすごい力を入れないといけなかったところが、りきひさみねこ先生のはっと驚くようなご指導で、変な力を入れなくても楽に弾けるように、徐々になってきました。

前回は肩当てを外して、教本の練習をしたわけですが、今回からは左手の改造に本格的に着手を開始しました。

これまではどちらかというと左手をすぼめるような形で弦を押さえていたせいで、親指に負荷がかかってしまい、ポジション移動が結構大変だったり正しい位置に指をホールドできないという個人的な悩みがありました。まぁその辺って結局これまで半分自己流でやってきてしまったので、気がつかないうちに左手の使い方も変な風になってしまっていたんでしょうね。

ここで先生のご指導が入るわけです。

  1. まず左手の基本は、手のひらが自分の方向を向くようにすること
  2. 左手の手のひらを丸めるのではなくて、開くようにすること
  3. 指は傘の柄の部分のように、指板に指を引っ掛けるようにするようなイメージで置くこと

特に2番目の手のひらを開くようにすることは、これまで全然イメージをしてこなくて、ましてや、手の肉球をできるだけ押し出すようにしながら構えるというのが、初回だからということもあってかなり大変でした。これは先生に教えていただいたストレッチの方法を毎日続けながら、こわばっていた手と手首の柔軟性を少しずつ取り戻していくしかないですね。

けれどもこれを取り戻せば、今までよりも力を入れずにより自由に左手の指を抑えられるようになりそうなので、絶対にマスターしておきたいところです。ひとまずストレッチだけは毎日続けています。

こんな感じで、手の使い方、指の使い方の改造はまだまだ続きます。一通りの解像が終了したところで、自分の弾き方がどういう風に変わっていくのか、楽しみで仕方がありません。次回以降のレッスンも楽しみです。

それと、音階練習も始めていきましょうということで、カール・フィッシャーの音階教本を使うことになりました。一旦SITTは卒業です。音階練習、今まで積極的にやってこなかったので、こちらも先生のお力をお借りしながら強化していきたいところです。

カテゴリー: Private | タグ: , | コメントする

長期間スイッチロールしていないIAMユーザーを棚卸する仕組みを作ってみた


AWSアカウントやAWS IAM周りの管理って大変だ

AWSアカウントやAWS IAM周りの管理って、AWS Organizationsのようなサービスを使用していようがいなかろうが結構大変ですよね。特に何が大変かって、長期間ログインしていないIAMユーザーの棚卸から始まって、MFA認証の有無やらパスワードポリシーの管理やら何やら。

弊社では、マスターのAWSアカウント上でのみIAMユーザーを作成し、各プロジェクト用のAWSアカウントに対してはスイッチロールのみでアクセスできるようにしているのですけれども、やはりみんながみんな恒常的にAWSマネージメントコンソールにアクセスするとは限らず、プロジェクトが安定運用に入ってくると、アクセスするIAMユーザーの数も限られてきます。そうなると、やはり一定期間ごとに、そのAWSアカウントにアクセス可能なIAMユーザーの棚卸をすることがセキュリティ的にも必要になってくるわけです。

これを手作業でいちいち調べていると当然結構な労力になってくるわけで、その労力を軽減するためにも、とりあえず長期間スイッチロールそのものを行っていないIAMユーザーを探し出すための仕組みを考えて実装してみました。図でそのワークフローを書くとこんな感じになります。

ワークフロー

ここでは2本のワークフローを組みます。

  1. IAMユーザーがスイッチロールしたイベントはAWS CloudTrailで自動的に検知されるため、証跡ログがAmazon S3バケットに格納された時点で、PUTイベントをトリガーとしてAWS Lambda関数を呼び出して、Amazon DynamoDBにその履歴を格納するワークフロー
  2. Amazon EventBridgeのcron式をトリガーとしてAWS Lambda関数を呼び出して長期間スイッチロールしていないIAMユーザーを割り出し、Slack Incoming Webhookの仕組みを使用してSlackの特定のチャンネルにIAMユーザーのリストを通知するワークフロー

DynamoDBに作成するテーブルはこんな感じです。

aws dynamodb create-table \
    --table-name SwitchRoleHistory \  
    --attribute-definitions \
        AttributeName=eventID, AttributeType=S \ 
        AttributeName=eventTime, AttributeType=S \
    --key-schema AttributeName=eventID, KeyType=HASH AttributeName=eventTime, KeyType=RANGE \
    --provisioned-throughput ReadCapacityUnits=1, WriteCapacityUnits=1

少なくともeventTime属性はソートキーとして機能できるようにしておきます。eventID属性はあくまでもプライマリーキーとしてのみ機能するという感じです。ここではRCUとWCUの値を最小値にしていますが、スイッチロールが発生する頻度によって調整をかけた方がいいかもしれません。

1番目のワークフローのLambda関数

1番目のワークフローのLambda関数はこんな感じで作成してみました。ちょっと一部だけ、実際に動作しているものからは改変しています。

# --------------------------------------------------
# function name:
#     saving-record-to-dynamodb-from-cloudtrail
# usecase:
#     detect to SwitchRole user from Amazon CloudTrail logs and
#     put into Amazon DynamoDB Table.

import json
from operator import truediv
import urllib.parse
import boto3
import zlib
import logging
import traceback
import os
import re


AWS_ACCOUNT = os.environ.get('AWS_ACCOUNT')
DYNAMODB_TABLE = os.environ.get('DYNAMODB_TABLE')
EVENT_NAME = os.environ.get('EVENT_NAME')
CROSS_ACCOUNT_ROLE_ARN = 'arn:aws:sts::' + AWS_ACCOUNT + ':assumed-role/'

logger = logging.getLogger()
logger.setLevel(logging.INFO)

s3Bucket = boto3.resource('s3')
db = boto3.resource('dynamodb')
tableName = db.Table(DYNAMODB_TABLE)


def parseS3CloudTrail(bucket_name, key):
    
    s3Object = s3Bucket.Object(bucket_name, key)

    # Decomplessing Objects in S3 Bucket
    payload_gz = s3Object.get()['Body'].read()
    payload = zlib.decompress(payload_gz, 16 + zlib.MAX_WBITS)
    payload_json = json.loads(payload)

    # To get AssumedRole information and put into DynamoDB 
    for record in payload_json['Records']:

        logger.info("[STEP2] eventName is: {}".format(record['eventName']))

        if EVENT_NAME == str(record['eventName']):

            logger.info("[STEP3-1] record is: {}".format(record))
            logger.info("[STEP3-2] arn: {}".format(record['userIdentity']['arn']))

            saveRecord(record)


def saveRecord(record):

    logger.info("[STEP4] Saving record to DynamoDB")

    arnFlg = False

    # Search Arn
    tempArn = record['userIdentity']['arn']

    # for master AWS Account
    findStr = r'_role/(.*)'
    responseArn = re.search(findStr, tempArn)
    arnFlg = True

    logger.info("[STEP4-1] detected responseArn is: {}".format(responseArn))

    # for master AWS Account
    if responseArn is None:

        findStr = r'user/(.*)'
        responseArn = re.search(findStr, tempArn)
        arnFlg = False

        logger.info("[STEP4-2] detected responseArn is: {}".format(responseArn))

    logger.info("[STEP5] responseArn: {}".format(responseArn))

    if responseArn is not None:
        responseArn = responseArn.group(1)

        if arnFlg == True:
            userName = responseArn
        else:
            userName = record['userIdentity']['userName']

        # Put into DynamoDB Table
        result = tableName.put_item(
            Item={
                'arn': responseArn,
                'userName': userName,
                'eventTime': record['eventTime'],
                'eventID': record['eventID'],
                'record': json.dumps(record)
            }
        )

    logger.info("[STEP6] Saved record to DynamoDB")


def lambda_handler(event, context):

    logger.info("[STEP1] Loading function")

    try:

        # Get current S3 Bucket Name and Key
        for record in event['Records']:
            bucket = record['s3']['bucket']['name']
            key = urllib.parse.unquote(record['s3']['object']['key'])

            logger.info("[STEP2] BucketName: {}".format(bucket))
            logger.info("[STEP2] Bucket Key: {}".format(key))

            # Get information and put into DynamoDB Table
            parseS3CloudTrail(bucket, key)

        logger.info("[STEP7] Finished Function")

        return {
            'StatusCode': 200,
            'message': 'Saving record to DynamoDB from CloudTrail is finished nomaly.'
        }

    except Exception as e:
        logger.error(e)
        logger.error(traceback.format_exc())
        
        return {
            'StatusCode': 500,
            'message': 'An error occured at Saving record to DynamoDB from CloudTrail.'
        }

なんか、logger.infoがやたら多くない? というツッコミはさておき、AWS CloudTrailからAmazon S3バケットへのPUTイベントが走った時点で、eventオブジェクトの中から、PUTされてきた圧縮済み証跡ファイルの情報を取得した後、それをまずは解凍してJSONからarnの情報を取得して、同じJSONの中にある他の情報と一緒に、最初に作成したDynamoDBのテーブルに突っ込む、という流れです。

arnの情報からのマッチングに、正規表現を用いています。マスターのAWSアカウント側では実はわざわざこんなことをする必要はないのですが、要件によって、スイッチロールされた側のAWSアカウント上でも同じ仕組みを入れたい、となった場合にも最低限のコード修正で対応できるように、わざわざarnの情報から正規表現を用いてマッチングをかけてIAMユーザー名を取得しています。

2番目のワークフローのLambda関数

2番目のワークフローのLambda関数はこんな感じで作成してみました。

# --------------------------------------------------
# function name:
#     check-long-time-switch-user-from-dynamodb
# usecase:
#     detect to long time SwitchRole user from Amazon DynamoDB and
#     send to Slack via Slack Incoming Webhook.

import json
import boto3
import logging
import traceback
import os
import operator
import datetime
import dateutil.parser
import urllib.request


DYNAMODB_TABLE = os.environ.get('DYNAMODB_TABLE')
TIME_DELTA = os.environ.get('TIME_DELTA')
SLACK_WEB_HOOK_URL = os.environ.get('SLACK_WEB_HOOK_URL')
SLACK_USER_NAME = os.environ.get('SLACK_USER_NAME')
MSG_OLD_ARN_IS_NONE = os.environ.get('MSG_OLD_ARN_IS_NONE')
MSG_OLD_ARN_IS_EXIST = os.environ.get('MSG_OLD_ARN_IS_EXIST')

logger = logging.getLogger()
logger.setLevel(logging.INFO)

db = boto3.resource('dynamodb')
table = db.Table(DYNAMODB_TABLE)

pjExp = "#id, eventTime, arn"
exAttName = {"#id": "eventId"}


def getValueFromKey(value, key):
    try:
        return str(value[key])

    except KeyError as e:
        return "None"


def sendSlack(sendText):
    try:

        # Generate Slack messages
        sendMessage = {
            "text": sendText,
            "username": SLACK_USER_NAME,
            "icon_emoji": ":dog:"
        }
        sendMessage = json.dumps(sendMessage)    

        # Send message to Slack channel via Incoming Webhook
        request = urllib.request.Request(
            SLACK_WEB_HOOK_URL,
            data=sendMessage.encode('utf-8'),
            method="POST"
        )

        with urllib.request.urlopen(request) as response:
            responseBody = response.read().decode('utf-8')

        logger.info("[STEP8] send to Slack finished.")
        return responseBody

    except Exception as e:
        logger.error(e)
        logger.error(traceback.format_exc())
        
        return {
            'StatusCode': 500,
            'message': 'An error occured at Check long time switch user from DynamoDB.'
        }


def lambda_handler(event, context):

    logger.info("[STEP1] Loading Function")

    try:
        count = 0

        # Get items from DynamoDB Table
        result = table.scan(
            ProjectionExpression=pjExp,
            ExpressionAttributeNames=exAttName
        )

        jsonDict = [""] * len(result["Items"])

        # Get arn and eventTime into JSON dictionary
        for item in result["Items"]:

            jsonDict[count] = {}
            jsonDict[count]["arn"] = getValueFromKey(item, "arn")
            jsonDict[count]["eventTime"] = getValueFromKey(item, "eventTime")

            count = count + 1

        logger.info("[STEP2] jsonDict: {}".format(jsonDict))

        # Sorting JSON dictionary Order By eventTime Desc
        sortDict = sorted(jsonDict, key=operator.itemgetter('eventTime'), reverse=True)

        logger.info("[STEP3] sort is completed. sortDict: {}".format(sortDict))

        # Srote in each array as arnList and lastLoginTimeList
        arnList = [arn.get('arn') for arn in sortDict]
        lastLoginTimeList = [lastLoginTime.get('eventTime') for lastLoginTime in sortDict]
        oldArnList = []
        evalFlg = 0

        if len(arnList) == len(lastLoginTimeList):
    
            # Generate old switchrole users into new array
            for tmpArn, tmpLastLoginTime in zip(arnList, lastLoginTimeList):

                timeDelta = datetime.timedelta(days=int(TIME_DELTA))
                evalDate = datetime.datetime.now() - timeDelta
                evalDate = evalDate.astimezone()

                logger.info("[STEP4-1] timeDelta is: {}".format(evalDate))

                tmpLastLoginTime = dateutil.parser.parse(tmpLastLoginTime)

                logger.info("[STEP4-2] tmpLastLoginTime is: {}".format(tmpLastLoginTime))

                if tmpLastLoginTime > evalDate:
                    evalFlg = 1
                    continue
                else:
                    evalFlg = 0
                    logger.info("[STEP5-1] Old Switched User is: {}".format(tmpArn))

                    for tmpArn2nd, tmpLastLoginTime2nd in zip(arnList, lastLoginTimeList):

                        tmpLastLoginTime2nd = dateutil.parser.parse(tmpLastLoginTime2nd)
                        
                        if tmpArn != tmpArn2nd:
                            continue
                        elif tmpArn == tmpArn2nd and tmpLastLoginTime < tmpLastLoginTime2nd:
                            evalFlg = 1
                            continue
                        else:
                            logger.info("[STEP5-1] evalFlg is: {}".format(evalFlg))

                            if evalFlg == 0:
                                oldArnList.append(tmpArn)
                                logger.info("[STEP5-2] appended list of tmpArn is: {}".format(tmpArn))

        oldArnList = list(set(oldArnList))
        logger.info("[STEP6] old IAM Users are this list: {}".format(oldArnList))

        # Generate Slack messages and old switch role user list
        if oldArnList == []:
            sendText = TIME_DELTA + MSG_OLD_ARN_IS_NONE
            logger.info("[STEP7-1] oldArnList is NOT Exist.")
        else:
            sendText = TIME_DELTA + MSG_OLD_ARN_IS_EXIST + ' ' + str(oldArnList)
            logger.info("[STEP7-2] oldArnList is Exist.")
        
        sendSlack(sendText)

        logger.info("[STEP9] Finished Function")

        return {
            'StatusCode': 200,
            'message': 'Check long time switch user from DynamoDB is finished nomaly.'
        }

    except Exception as e:
        logger.error(e)
        logger.error(traceback.format_exc())
        
        return {
            'StatusCode': 500,
            'message': 'An error occured at Check long time switch user from DynamoDB.'
        }

ここでまずは言い訳させてください。本当はSQL文で言うところの、SELECT DISTINCT文を使いたかったのですが、当然のことながら、Amazon DynamoDBにはそんな機能は兼ね備えていません。かといって、get_item()関数ではひとつのレコードしか返すことができないので、ちょっとコストはかかりますがscan()関数を実行してeventTimeとarnを引っ張り出しているという苦しいことをしております。Amazon DynamoDBって、リレーショナルにするまでもない情報を格納するにはお手軽だしコストもかからないので楽なのですが、こういうケースでちょっと痒いところに手が届きにくいというのがちょっと辛いですね。

なので、後半で、eventTimeでソートをかけた後に、リストの中身を二重にぐるぐる回して、最終のスイッチロール実行から一定期間が経過したIAMユーザーを探し出すという面倒臭いことをしています。この辺りのコードはもう少しエレガントになるように改良したいところ。

SlackのIncoming Webhookを使用してPostする関数は、教科書通りです。

実行結果

この2つのワークフローを回していくことによって、Amazon DynamoDBの中には少しずつスイッチロールしたIAMユーザーの履歴が溜め込まれていって、Slackには以下のようなイメージでメッセージが定期的に通知されてきます。

なかなかこの手の作業を自動化するのって面倒ではあるのですが、まずはこの実装をきっかけに、管理できていないところを可視化するという営みまでは実現することができました。この先の計画としては、可視化されたIAMユーザーから、該当するスイッチロール権限を自動的に削除する、というところまで行き着くところなのですが、この辺は運用も絡む世界なので、さらに整理しながら拡張を続けていきたいなと思っています。

カテゴリー: AWS, Work | タグ: , , , , , , | コメントする

Violaレッスン5回目


前回のレッスンの記録を書くのを忘れていましたが、りきひさみねこ先生のViolaのレッスンも5回目まできました。月に2回ちゃんと通っております。

今回も右手の修正を引き続きやる形になるのかなと思いきや、最初に開放弦でボウイングの練習をした後に、じゃあ左手もつけてみましょうということで、教本”SITT”の1番を少しずつ弾いていくことになりました。

肩当てなしでも弾けるらしい

これまでずっと肩当てをつけた状態で弾いてきたので、開放弦でのボウイングはともかくとして、左手をつけた状態で弾くことにはちょっとだけ不安があったのですが、実際に弾いてみると、音程はともかくとしても案外弾けるじゃないですか!

まずは楽器の1の指(人差し指)と3の指(薬指)を押さえる部分にテープを貼ってもらい、それぞれの指がそこから手前に行かないように目印にしてもらいました。

その上で、初めはスラー抜きで半分くらいを弾き、次にスラー付きで半分くらいを弾き、という流れに。音程のところで若干個人的に”?”(特に2の指と4の指)と思ってしまうところはあったのですが、それはともかくとしても肩当てなしでも曲がりなりに弾けることが分かった上に、これまで右手の修正とボウイングの練習を重ねてきたので、レッスンを始める以前よりもだいぶ弾きやすくなったという感覚です。弾いている自分自身が驚きました。

右手はC線を弾くときに手の甲の関節が曲がってしまうところだけはちゃんと気をつけなければいけないのですが、中指から小指にかけての弓の持ち方を修正したことと、親指の位置も修正したことがだいぶ幸いしたかもしれません。これまでは割と力技で弾いてきた記憶があるのですが、そこまで力を入れなくてもきちんとした音量で弾けるようになったのが嬉しいですね。

それから先生に指摘されて、おっと思ったのが、背筋が伸びてきたというところ、最初のレッスンの時はだいぶ猫背気味だったらしいんですよね。背の高い人が総じて肩が前のめりになってしまって猫背気味になってしまうらしいです。その傾向がだいぶ改善されてきたと。

肩が前のめりになる点、特に左肩が巻き肩っぽくなってしまう傾向は自分自身もなんとなく気になっていて、就寝前に肩を逆に反らせるようなストレッチをしているのですが、もしかしたらそれが功を奏したのかもしれません。

次回以降しばらくは、SITTの1番を弾きながら左手の修正に取り掛かる感じになるかなと思うのですが、まずは肩当てなしでも十分に楽器を弾くことができるんだということが結構嬉しかったです。

次回のレッスンも俄然楽しみになってきました。

カテゴリー: Private | タグ: , | コメントする

携帯電話回線をahamoへ切り替え


はじめに

総務省からの強い指導もあり、携帯電話キャリアの選択肢は大幅に広まりましたね。ちょっと前までは大手3社がひしめき合う構造でしたが、MVNO回線やら大手キャリアも格安のプランを提供するようになって、混戦状態が続く一方で、我々ユーザーにとっては自分の利用スタイルに応じて利用したいキャリアやサービスを自由に選択できる環境が少しずつ広がってきたと思います。

なぜキャリアの変更をしたのか

これまでは10年以上に渡ってとある大手の携帯キャリアを使用し続けてきたのですが、家族全体の利用料金だけでも、月によって若干の誤差はあるものの、家族3人で大体月あたり34,000円くらいかかっていました。これは固定費として考えると家計のうちの結構なボリュームを占めるのでかなり痛い。

もちろん適宜料金プランの見直しをかけてきたのですが、現在のようにリモートワーク化が進むと、データ通信量を案外使わないんですよね。自宅では基本的にWi-Fiがフル稼働しているからそっちに繋いでしまえばいいわけで、リアル出社時や休日にカフェで作業をするときにはテザリングでMacBook Airに繋ぎますが、それでも1か月に使用するデータ量は数GB程度。

しかも現在のキャリアにはいまだにテザリングするために手数料がかかるという。データ通信の中には当然移動中や移動先でのテザリングを使用する機会が少なくないと思うのですが、これにかかる年間のコストだけでも積み上げればそれなりなので、テザリングにかかる手数料もこの際削減してしまいたいなと。

あとは、もちろん家庭によって利用シーンはまちまちだと思うのですが、少なくとも自分に関しては、大容量プランを使用していてもコスト対効果が見合わないという実情がありました。

もちろん大手キャリアの場合には新型の端末を割賦によって購入できるという利点がありますが、これも色々とカラクリがあって、代理店側にはキャリアからちゃんとお金が流れていくような仕組みが整っているので、定期的に新しい端末に買い替えなければいけないという状況も出てきてしまいます。もちろん、この辺についてもそのうちに総務省の手が入るのではないかと思っているのですが。

そんなこんなで負荷をかけている携帯電話の固定費を削減するために、大手携帯電話キャリアが提供している格安のサービスから、通信エリアや安定性などを考慮した上で、自分の場合はahamoに乗り換えることにしたわけです。

契約申し込みはあっという間

今回はすでにiPhone12 miniを筆頭に複数のデバイスを所持しているということもあって、SIMだけの契約にしました。eSIMも考えたのですが、この場合はと使用できるデバイスが限られてしまうため、物理的なSIMのみを契約することに。申し込みの流れの画面から自分が持っているデバイスがahamoに対応しているかどうかをフローチャートに沿って確認してみるだけで概ねそのデバイスが対応しているかどうかの判断がつきます。この辺のUIはとても軽くて使いやすいですね。

今回はiPhone 12 miniと、何かの拍子で契約していたときにほぼほぼ無料で購入できたGalaxy A20で利用できるかどうか確認をしてみたのですが、どちらも対応。

もちろんキャリアメールなどは今時使用する頻度は完全と言っていいくらいないので選択肢からは当然外れます。Gmailアカウントひとつ持っていればいいだけなので。

そんなわけで申し込みの画面からサクサクと契約形態を選択していって、あっという間に契約手続き完了。MNPの場合にはもう少し煩雑な手続きが入りますが、その煩雑さを解消するのと、今回はテザリングでの使用が主目的だったので、新規電話番号を取得する形で契約を行いました。

SIMの配送から開通まで

SIMカードの到着までにかかった日数は概ね3日くらいでしょうか。eSIMを使用する場合はもっとリードタイムが短縮できるかもしれません。契約申し込み時に受付番号が発行されるので、受付番号でログインの画面から追うことで随時状況を確認することができます。

そうこうしているうちにSIMカード到着。開通の儀に入ります。が、この日は定期メンテナンス日で朝になるまで開通手続きが行えません。docomoの場合は、毎週火曜日から水曜日にかけての深夜帯がメンテナンス日に該当するので夜中に開通手続きをするためには注意が必要ですね。

そんなわけで、メンテナンスが終了する時間を待って、いよいよ開通の儀に入ります。SIMカードの発送が行われた時点で、実は受付番号でログインの画面から開通手続きをすることができるのですが、その時点で開通の手続きができてしまい、完了した時点から料金が発生するので、ここはグッと待ちました。

「開通の儀」なんて恭しく言ったところで、なんてこない、まずはデバイスにSIMカードを挿して、Webサイト上から開通手続きをしてあげるだけです。そこから実際に回線が使えるようになるまでの時間は概ね1分程度でした。これで晴れてahamo生活スタートです。

契約までの敷居がかなり低くなった

具体的にはこれから使っていくことで、実際にどれくらいのスピードやトラフィックで通信ができるのかを徐々に検証していこうと思うのですが、少なくとも自身でデバイスのSIMの抜き差しさえできるのであれば(eSIMの場合はその必要すらありませんが)、契約の手続きに関しては、UIの秀逸さもあってか、かなりスムーズでした。

面倒臭いとすれば、MNPで転出するときに、前のキャリアがいちいち引き留め工作に出ようとするところくらいでしょうか。諸々メリットが書かれているんですけれども、逆にキャリアがメリットと考えている部分が我々にとってはデメリットだったりすることもあるんですよね。もちろん囲い込み戦略とかあるんでしょうけれども、MNPの転出手続きに至る流れももう少し簡素化してほしいものです。

実際の使用感についてはすでにさまざまな方がさまざまな記事で書かれていると思うのですが、自分なりの使い方で、前のキャリアと比較してどのように変わったのかは後日追記していきたいと思います。

少なくとも固定費削減のための足がかりは掴めたということで、今のところは文句なしですね。

カテゴリー: Private | タグ: , | コメントする

今更ながらCloudMapperをローカルに構築してみた


CloudMapperとは

普段Amazon Web Services(AWS)の設計や構築を行うにあたって、AWSリソースをどのように構成するか、そのネットワーク構成がどうなっているのかを紐解くために、draw.ioCacooを使って構成図を書くことがちょいちょいあると思います。

設計の段階では確かにこれらのツールを使用してどのような構成にするか、どのようなAWSリソースの配置を行うかを検討するのにはとても有用なのですが、どのAWSリソース(例えばEC2インスタンスとかECSサービスとか)にどういうSecurity Groupがアタッチされているの? とか、Internet Gatewayから目的のAWSリソースに対してちゃんと論理的に通信できる状態になっているの? といった内容を構成図上で読み取るのはなかなか大変ですし、例えば想定通りの通信ができないといった事象に対してマネージメントコンソールやVPCフローログだけで解析を行うのにはそれなりに苦労をするのもまた実情だと思います。

そこを補完して、AWSリソース群を実際のAWS環境から収集して、ネットワーク構成図として落とし込んでくれるのが、CloudMapperというツールです。

ローカルの環境に構築してみる

CloudMapper自体の構築手順は、さまざまなWebサイト上で解説されているのですが、ここではローカルの環境であるMacBook Pro (16-inch, 2019, Intel / macOS Monterey 12.2.1)上に構築してレポート出力していく手順を、できるだけ詳細に記述していこうと思います。

前提条件

まずは、ネットワーク構成図を作成したい対象のAWS環境に、以下のIAMポリシーが少なくともアタッチされているIAMユーザーを作成している必要があります。

  • ViewOnlyAccess (ReadOnlyAccessでも可能)
  • SecurityAudit

また、ローカルの環境上に、少なくともPythyon3.8以上がインストール済みである必要があります。README.mdではPython3.7でも動く的なことが書いてあるのですが、3.8にはなっていたほうがいいのかなと思います。

さらにこのIAMユーザーから、あらかじめSecret KeyとSecret Access Keyを取得しておく必要があります。ご存知の通り、Secret KeyとSecret Access Keyは漏れ出すと大変なことになるので管理には十分に気をつけてください。

CloudMapperをインストールしていく

ここではホームディレクトリ上にインストールしていくことを前提としています。以下のコマンドを順次実行してインストールしていきます。

% git clone https://github.com/auto-labs/cloudmapper.git
% brew install autoconf automake libtool jq
% python3 -m venv foobar
% source foobar/bin/activate
(foobar) % pip install --prefer-binary -r requirements.txt
  1. GitHubからCloudMapperのソースコード一式をcloneしてくる
  2. Homebrewを使用して、動作させるために必要なライブラリをインストールする
  3. pythonの仮想環境を構成する(ここでは仮想環境としてfoobarという名称にしています)
  4. 仮想環境上で、CloudMapperの動作に必要な追加モジュールが記述されている”requirements.txt”の内容をバイナリでインストールする

AWS CLIの環境設定をしていく

ここでは前提条件の中で書き記したIAMユーザーのSecret KeyとSecret Access Keyの情報を

% aws configure

で登録していきます。ここは詳細は省きますが、リージョンは”ap-northeast-1″(東京リージョン)で、出力形式は”json”で問題ありません。

ここまででCloudMapperの一連のインストールと事前準備は完了です。

CloudMapperを実行するための事前準備をする

CloudMapperは”config.json”というJSONファイルの中に初期設定情報を持ちます。この中身は、どういったプロファイル名で、どこのAWSアカウントから取得したい情報を参照するかが記述されています。この初期設定情報をあらかじめ取得するために、以下の情報を、Pythonの仮想環境上でコマンド実行して設定していきます。

(foobar) % python3 cloudmapper.py configure add-account --config-file config.json --name (プロファイル名) --id (取得対象のAWS Account) --default DEFAULT
(foobar) % python3 cloudmapper.py configure add-account --config-file config.json --name (別のプロファイル名) --id (取得対象のAWS Account)

デフォルトでは1行目で設定した項目が使用されるので、別のプロファイルに切り替えたいときは2行目を使用する、といったイメージです。

AWS環境のリソース情報を収集する

ここで実際にCloudMapperを実行し、ネットワーク構成図を出力するのに必要なAWS環境のリソース情報を取得していきます。注意点としては、仮にap-northeast-1上のAWSリソースだけを取得したくても、ありとあらゆるAWSリソースを収集しにかかるので、グローバルリソースも含めて、全リージョンのAWSリソースの情報収集を行うようです。そのため、環境にもよりますが、初回実行には手元の計測ではコマンドの実行が終わるまでに大体30分程度かかりました。Pythonの仮想環境上で、以下のコマンドを実行します。

(foobar) % python3 cloudmapper.py collect --config config.json --account (プロファイル名)

レポート出力の準備を行う

上記のコマンドでAWS環境のリソース情報を収集し終えたら、レポート出力の準備のために、Pythonの仮想環境上で、以下のコマンドを実行します。この準備自体は、構築されているAWSリソースが軽いものであればあっという間に実行が完了します。

(foobar) % python3 cloudmapper.py prepare --config config.json --account (プロファイル名)

レポートを出力してみる

レポート出力の準備が完了したら、CloudMapperが内部的に持っているWebサーバ機能を利用してレポート出力をするために、Pythonの仮想環境上で以下のコマンドを実行します。

(foobar) % python3 cloudmapper.py webserver

ここまで完了したら、http://127.0.0.1:8000/ に、Webブラウザ上でアクセスします。すると、こんな感じのレポートが出力されると思います。(以下はあくまでも一例です)

無事にネットワーク構成図が出力されました。ちょっと形が不恰好ですが、それぞれのAWSリソースアイコンや、矢印の部分をクリックすると、どんな設定でAWSリソースが構成されていて、どういったネットワーク構成になっているかがjson形式で表示されます。

所感

こんなわけで、ネットワーク構成図を出力することができるのですが、例えばVPC外に構成されるAWS LambdaやAmazon S3といった情報はどうもスタンドアロンでは出力することができないようです。ただし、Amazon API Gatewayに紐づけられたVPC Lambdaや、VPC Endpoint経由で紐づけられたS3バケットなどは出力される模様(具体的な検証はまだできていませんが、サンプルを見る限りではどのように見受けられます)。

なので、用途としては、ネットワーク構成を紐解いたり、トラブルシューティングにあたってSecurity Groupが正しく対象となるAWSリソースにアタッチされていて通信可能なのかを解析する用途には非常に有用なのではないかと思います。

ローカル環境への構築自体は非常に簡単で、待ち時間を含めて2時間程度で完了してしまうので、構成図だけではネットワーク構成をうまく可視化できないなという悩みを持たれている方には、非常に良いツールなのではないかと思いました。

カテゴリー: AWS, Work | タグ: , | コメントする

転職のご報告


はじめにご挨拶

2022/2/28をもって、約3年半勤めた前職を退職し、2022/3/1から新しい会社でのキャリアをスタートしました。

前職のうち、特に某大手携帯電話会社の案件では本当に様々な方にお世話になっただけではなく、案件を通じてAmazon Web Servicesだけでなく、ネットワーク領域を中心とする様々な知識とナレッジを蓄積することができました。関係者の皆様にはこの場を借りて改めて心から御礼申し上げます。

2022/3/1からは渋谷にある某スタートアップ企業で働き始めました。初日はオリエンテーションと環境の整備が中心でしたが、2日目からは早速具体的な業務に関する相談を受けつつも、これまでに携わったことのない領域、特にリリースの自動化に関する領域や、どちらかというとSRE的な役割に近い領域の改善に早速取り組み始めています。

初めてのスタートアップ系の企業ということで、今まで働いてきた企業とのカルチャーの違いに戸惑いつつも、少しずつそこに慣れていくためにも、当面の間は出社勤務とリモートワークとのハイブリッドな形でバランスをとりながら、徐々に馴染んでいこうとして最初の1週間が経過したところです。

なぜ転職に踏み切ったのか

元々私はクライアントワークというよりも、SESに近い形でのキャリアをここ数年間は歩んできました。もちろんSESという立ち位置を否定するつもりはないのですが、この立ち位置を重ねていくうちに、クライアントと働いている立場(会社)との越えられない垣根を越えて、よりダイレクトにお客様とのつながりを持ちたいということだけではなく、自分が得てきた経験値をベースにしながら、そのナレッジを自社の資産として蓄積して自分もチームの人もさらにボトムアップしていきたいと考えたのがまず1点。

それから、この年齢に差し掛かってきて、そろそろ若い人を積極的に育てていって、組織としてプロ集団を形成できたらいいなということを強く思ったのがもう1点。

それらを総合的に考えながら、少しでもキャリアアップできる環境を求めて、かれこれ半年間あまり、忙しい業務の合間を縫って少しずつ転職活動を進めていき、今回ご縁があって転職に至りました。

まず1週間で感じたことと、進めていきたいこと

まず1週間で感じたことは、立ち上がりの速さをとにかく求められているんだろうなということと同時に、いろいろな意味で自己管理を求められているのだろうなということです。それは、勤務時間の管理だったり、その勤務時間の中でどういうことを学び、アウトプットとして出していくかというところだったり、様々です。比較的自由度の高い組織であるからこそ、逆にそういった自己管理が自然と求められてくるのではないかと思っています。

それを踏まえつつ、現状を少しずつ見ていく中で、これから前向きに改善していきたい課題がいくつか見えてきているので、まずは具体的な業務をしっかりと進めつつも、改善していきたい課題を積極的に可視化して、解決への糸口を掴んでいく、そしてそれを具体的な形にしていくということを、この先少なくとも3か月間の目標にしていきたいと思います。

とはいえ、性格上何かとスタートダッシュをハードにしすぎる傾向があるので、そこは自分自身も大切にしながら、バランスよく進めていきたいと思います。

このBlogは自体は、お客様との間で結んだNDAを厳守しながらも、技術的なTipsも含めて色々な気付きを一般化して翻訳しながら、変わらず継続していきます。一般化することで、普遍的な集合知が出来上がってくるので、その一助になればと考えています。

今後ともよろしくお願いいたします。

カテゴリー: Work | タグ: | コメントする

Violaレッスン3回目


りきひさみねこ先生のViolaレッスンも早いものでもう3回目となりました。前回はオンラインレッスンだったのですが、今回からは3回目のワクチンも接種済みということもあって、対面でのレッスンになりました。

先生のレッスンを開始して以降、右手の改造に取り組んでいるのですが、これがなかなかうまくいかず。先生曰く、今まで相当無理のかかるような弓の持ち方をしていたらしく、本当は右手のMP関節を平らにしたいところなのだけれども、僕の場合は完全にここが立ってしまっているので、全体的に弓を持つ右手全体(特に小指)に無駄な力がかかっているのだと。

そんなわけで、MP関節が平らになるように弓の持ち方全体を、先週から引き続き改造しています。中指と薬指は、弓が第2関節にきっちりとくっつくくらいにして、その分だけ右手全体を弓の先端方法に傾けます。そして小指は添えるだけ。この右手の形でひたすらボウイングの練習です。最初は弓を十分に使い切るように弾いて、その次は弦が最大限振動する位置でダウンボウの繰り返し。もう教本どころの騒ぎではなくて、とにかく基礎中の基礎からたたき直している状態。

けれども、大学を卒業して以降の20年間くらいが完全に自己流だったので、元々の目的である基礎を見直すという意味では、本当に良い練習になっています。

思い切り自分の顔が写り込んでしまっているけれども、その時々の状態を先生が撮影してくれてレッスン直後に送ってくださるので、載せてしまおう。こんな感じです。

肩当てが消える

そうやってボウイングの練習を繰り返していたのですが、私のどちらかというと横向きに楽器を構えている姿勢に気がついた先生、身体の体幹に対して真っ直ぐ楽器がくるように姿勢を修正したいということで、

肩当てをあえて外しましょうか!

ということで、先生、私の楽器から肩当て(楽器がきちんと構えられるように肩と楽器の間に挟む器具)を外してしまいました。そして顎を乗せる位置を若干楽器の中心にくるように修正し、という感じで、楽器の構え方についても大改造を開始。これも一朝一夕ではなかなか治らないかもしれませんが、こうして構え方を変えてもらうだけでも、出る音がだいぶ変わってくることに自分でも気がつきました。いやこれはすごい。

そんな感じで、楽器を弾く基本となる楽器の構え方と弓の持ち方から根本的に見直しているレッスンなのですが、やはりきちんとプロの先生に見ていただくことって大切ですねぇ。今更何を言っているんだって感じかもしれませんが、これは本当です。

そんな感じであれこれやっている間にあっという間にレッスンの時間は終了。先生のおかげで今回も楽しく楽器を弾くことができました。

カテゴリー: Private | タグ: , | コメントする

日帰りで福岡往復


有給休暇が大量に余っていたので消化期間に充てているのですが、1日中ずっと家にこもっているのはなかなかしんどいし、生産性も落ちてくるので、ちょっくら飛行機に乗るのを楽しみつつ、ちょっとしたリゾートワークまがいなことをしてくるか、ということで、日帰りで大好きな福岡に行ってくることに。航空券をポチッとしました。

フライトログ

  • 2022/2/25 NH243 HND → FUK 788 JA838A 5K
  • 2022/2/25 NH256 FUK → HND 788 JA831A 6A

天神のエンジニアカフェでリゾートワーク的な作業をする

福岡空港の良いところは、なんと言っても地下鉄が真下に乗り入れていて、博多まで約5分、天神まで約10分で出られるところ。今回は預け入れ手荷物は全くなく、気軽にトートバッグにMacBook Airとちょっとしたガジェットだけを入れて往路便に搭乗したので、飛行機を降りてから地下鉄に乗り込むまでわずか20分かからないくらい。ちょうど乗り継ぎが良かったので天神駅に着いたのが飛行機が到着してからわずか30分後。これは本当に魅力です。

天神ではひとりで出かけた際には仕事がらみでいつもお世話になっているエンジニアカフェへ。ちょうど仕掛かり始めていた個人開発のツールのコーディングとデバッグをみっちり90分くらいしてきました。普段とは異なる環境での作業はとにかく捗ります。作業環境を必要に応じて変えるのって気持ちの切り替えの意味でもとにかく良いですね。エンジニアカフェ、現在は無料の会員登録が必要ですが、会員登録してしまえばオープンなコワーキングスペースだけでなく、無料で集中スペースなども借りることができ、しかも個人利用であれば無料なのが素晴らしい。こういった形のコワーキングスペースって、東京圏だとドロップインで何かしらのお金がかかるので、とてもありがたいです。

作業をして十分にお腹を空かせてから、天神4丁目にあるラーメン屋さんでしっかりと豚骨ラーメンをいただいてきました。やっぱり地元で食べるラーメンは美味しい。ごちそうさまでした。

その後、アクロス福岡の中をちょっとぶらついてから天神駅に向かい、福岡空港行きの地下鉄に乗り込みます。天神滞在時間3時間というとんでもなくわずかな時間ですが、もうこれだけでも個人的には十分福岡の空気を堪能です。

帰路のNH256で小確幸

あっという間に福岡空港に戻ってきて、ラウンジでちょっと休憩したあと、復路となるNH256に搭乗。限定運用とはいえ国際線機材の78Mに乗れるのはちょっと嬉しかったりします。機体番号見てみたら、先月に娘と福岡旅行した時と同じ機体でした。そんな偶然もあるものなんですね。

一昨年くらいから、飛行機に搭乗した際には、CAさんにお願いをして搭乗証明書をいただいているのですが、そこでちょっとした嬉しい出来事が。いつもと違うデザインの、シール型の搭乗証明書に加えて、ANAのCAさんが手作りしたのではないかと思われる、手書き風のFUKステッカーを添えていただきました。

この存在、半年くらい前から知っていて、なかなかレアな存在であるという噂を聞いていたのですが、まさかここでいただけることになるとは! なんだか小格好という感じでとても嬉しかったです。CAさんに感謝です。本当にいつも何かしらの心遣いをいただけてありがたいですね。

そんな感じで無事に羽田空港に到着し、ちょうど良いタイミングでリムジンバスの接続があったので日が暮れる前に帰宅。

ショートトリップにしては若干やりすぎ感は否めないのですが、空港から市内までが近いという利点を活かせることを考えると、日帰り福岡往復って普通にできてしまうんですね。そして街並みが自分好みでどこでもクリエイティブな作業ができてしまう。これが当たり前になる時代に、もっともっとなってほしいと思います。

だいぶリフレッシュできました。これで3月からの仕事も頑張れそう。

カテゴリー: Private | タグ: , , | コメントする