Blueskyをメインのマイクロブログ型SNSにお引越し


X (旧Twitter)を、かれこれ14年くらい使ってきました。最初はよくわからずに使っていたものの、そのうちにフォロワーも徐々に増え、同じ趣味を持つ仲間との間でリアルなイベントを開くことができたりと、だいぶ楽しませてもらうことができました。

けれどもマイクロブログ型SNSとしての性格がだんだん変わってきたなと思うようになったのは、やはり2011年頃からですね。自分の旧Twitterに対するスタンスはあまり変わらなくて、本当にライフログ的に日々思ったことや起きた出来事などをゆるゆる投稿するスタイルだったのですが、周りの変化が激しすぎたというか、いつから言論プラットフォームになってしまったんだろう? という違和感を感じ始めていました。それが良いのか悪いのかは置いておいて。

ただテキストだけのやり取りで相手を論破しようとしたり罵倒しようとしたりする動きには正直賛同できなかったし、強いていえばそういうコンテンツを自身でコントロールすることができていたので、自分なりの使い方ができていたというのは確かにあるかもしれません。

それすらもコントロールできなくなってきたのが、2022年初頭にイーロン・マスクが実質的な経営権を握るようになってからの度重なる仕様変更(まぁ、改悪といってもいいでしょう)です。閲覧できる投稿のフィルタリングが難しくなり、一部機能の有料化によって目にしたくないコンテンツまで見なければいけなくなった辛さといったら。利便性と精神的な安心感を得るために一時的に有料アカウントにしていましたが、Blueskyの存在を知って、もはや旧Twitterに縋り付く必要は無くなったなと正直感じました。

Blueskyとは

いわゆる「分散型SNS」と言われているものであり、

1. データを個人で所有でき、サーバーをいつでも好きに引っ越しできる
2. おすすめアルゴリズムやフィードを自由に選択できる
3. 好きなサーバーに所属しながら1つの巨大なサービスにいるように使える

Blueskyの始め方 (https://scrapbox.io/Bluesky/Blueskyの始め方)から引用

ことを目指して作られた、Twitter社からスピンオフして作られたプロジェクトであり、実際には現時点ではマイクロブログサービス(いわゆるSNS)として提供されています。この記事の執筆時点において全世界でのアカウント数は約130万アカウントですが、招待制のクローズドベータの状態が続いているものの、そのアカウント数は順調に伸びており、日本語を用いるユーザもかなりの勢いで増えてきています。

使い勝手はほとんどTwitterと同様です。タイムラインの仕組みが旧Twitterと少し異なりますが、その点についてはうまく”Feed”と呼ばれる絞り込み機能を用いて、自分と趣向の似ているユーザさんを探すことができます。

実際のユーザの方も、個性的かつ優しい方が多く、自身で関係性を構築していけばかなり気持ちの良いホームフィード(タイムラインと同等)を構築することができます。

Bluesky以外にも、さまざまなマイクロブログ型のSNSが乱立していてどこに引っ越せばいいの? という人も多いかもしれませんが、個人的なBlueskyの魅力は、そこにいる人の暖かさと、ちょっとした遊び心にあるのかなと思っています。やはり中にいる人の存在というのはSNSである以上は無視するわけにはいきませんから。

既存のBlueskyユーザさんのほとんどが招待コードを保持していると思いますので、もしも気になる方がいらっしゃったら、Blueskyユーザさんから招待コードを分けてもらって、是非Blueskyの世界に足を踏み入れてみてください。

私のBlueskyのアカウントは以下の通りです。

https://bsky.app/profile/vlayusuke.bsky.social

青い空が待ってくれています。

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

Amazon EventBridge + AWS Lambda + Amazon DynamoDBでBlueskyのbot機能を実装してみた


皆さんはBlueskyについてはご存知でしょうか? 旧Twitterのようなマイクロブログと呼ばれるテキスト系のSNSで、現在はクローズドベータ版として運用されているため、参加は招待制ですが、イーロン・マスクがオーナーとなって以来混迷を続けている旧Twitterと比較するとシンプルではあるものの、非常に使い勝手の良いUIとどちらかという中道な雰囲気のユーザー層が多いため、安心して利用することができます。

以前、同じ構成で旧Twitterへのbot機能を実装しましたが、日々のPostを徐々に旧TwitterからBlueskyへ移行しつつあるということもあり、同じ機能をBluesky向けにカスタマイズしてみました。

構成もAmazon EventBridge + AWS Lambda + Amazon DynamoDBという全く同じ構成にしていますが、一部構成を変更した箇所があるので、その辺りを中心に書いていきたいと思います。

Amazon DynamoDB

以前はDynamoDBテーブルの属性のうち、コンテンツに該当する部分はタイトルとURLを一緒に格納していたのですが、Bluesky向けにはAPIの仕様の関係もあり、一旦タイトルとURLを分割しました。そしてidをパーティーションキーに、dateをソートキーにしているすことで、この2つのキーで主キーにすることとしました。こうすることで逆に後のコーディングで苦労することになってしまったのですが。。。

AWS Lambda向けのPythonのコード

基本的にはAmazon DynamoDBのテーブルから必要なデータを取得してBlueskyの投稿用APIに対してpostするという仕組みには変わりがないのですが、旧Twitterの場合と違って幾つかの処理に差異があるので、 lambda_handler() 以外にも幾つかの関数を追加しています。

パスワードを取得するための関数 get_app_password()

Blueskyの投稿用APIに対して投稿する場合は、Blueskyにログインする必要があるため、SSMパラメータストアにBlueskyから提供されるサードパーティアプリケーション向けのApp Passwordを生成したものを格納しています。くれぐれもBlueskyユーザーのパスワードを使用するのはもしもの時のためにやめましょう。

DIDを取得するための関数 get_did()

Blueskyは分散型SNSとしての運用が考慮されているため、ユーザアカウント以外にユーザ固有のIDを識別するためのDIDを保持しています。そのDIDを取得するための関数になります。

API Keyを取得するための関数 get_api_key()

DIDとパスワードが取得できたら、その情報を元にしてBlueskyのAPIへのアクセス用のAPI Keyを取得する必要があります。このAPI Keyを取得するための関数になります。

BlueskyのAPIを叩いてスキートをPostするための関数 post_skeet()

これまでの情報と、Amazon DynamoDBのテーブルに格納されている情報を元に、スキートを行う関数になります。

これらを実際に実装した全体像が以下の通りになります。

import os
import boto3
import urllib3
import http
import json
import re
import logging
import traceback
import random
from datetime import datetime
from botocore.exceptions import ClientError

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

ids = int(os.environ.get('POST_ID'))

dynamodb = boto3.client('dynamodb')


def lambda_handler(event, context):
    
    text = ""
    url = ""
    id = str(random.randint(0, ids))

    try:
        response = dynamodb.query(
            ExpressionAttributeValues={
                ':v1': {
                    'S': id,
                },
            },
            KeyConditionExpression='id = :v1',
            TableName='BlueskyBotOldBlogPost',
        )
        
        logger.info("response is %s", response)

        if ('Items' in response):
            text = text + response['Items'][0]['description']['S']
            url = url + response['Items'][0]['url']['S']
        
        logger.info("description is %s", text)
        logger.info("url is %s", url)
        
        app_password = get_app_password()
        did = get_did()
        key = get_api_key(did, app_password)
        
        response = post_skeet(did, key, text, url)
        
        return response
        
    except Exception as e:
        logger.error(e)
        logger.error(traceback.format_exc())
        return {
            "statusCode": 500,
            "message": 'An error occured at skeet old Blog post.'
        }


def get_app_password():
    ssm = boto3.client("ssm")

    app_password = ssm.get_parameter(Name="bluesky_password", WithDecryption=False)
    app_password = app_password["Parameter"]["Value"]

    return app_password
    

def get_did():
    http = urllib3.PoolManager()

    HANDLE = "vlayusuke.bsky.social"
    DID_URL = "https://bsky.social/xrpc/com.atproto.identity.resolveHandle"

    did_resolve = http.request("GET", DID_URL, fields={"handle": HANDLE})
    did_resolve = json.loads(did_resolve.data)
    did = did_resolve["did"]

    return did


def get_api_key(did, app_password):
    http = urllib3.PoolManager()

    API_KEY_URL = "https://bsky.social/xrpc/com.atproto.server.createSession"

    post_data = {"identifier": did, "password": app_password}
    headers = {"Content-Type": "application/json"}
    api_key = http.request(
        "POST",
        API_KEY_URL,
        headers = headers,
        body = bytes(json.dumps(post_data), encoding="utf-8"),
    )
    api_key = json.loads(api_key.data)

    return api_key["accessJwt"]


def post_skeet(did, key, text, url):
    http = urllib3.PoolManager()
    now = datetime.today()
    
    text = text + "\n\n" + url
    
    found_uri = find_uri_position(text)
    
    if found_uri:
        uri, start_position, end_position = found_uri
    
    post_feed_url = "https://bsky.social/xrpc/com.atproto.repo.createRecord"

    post_record = {
        "collection": "app.bsky.feed.post",
        "repo": did,
        "record": {
            "text": f"{text}",
            "facets": [{
                "index": {
                    "byteStart": start_position,
                    "byteEnd": end_position + 1
                },
                "features": [
                    {
                        "$type": "app.bsky.richtext.facet#link",
                        "uri": uri
                    }
                ]
            }],
            "createdAt": now.strftime("%Y-%m-%dT%H:%M:%S"),
        }
    }

    post_request = http.request(
        "POST",
        post_feed_url,
        body = json.dumps(post_record),
        headers = {"Content-Type": "application/json", "Authorization": f"Bearer {key}"},
    )

    post_request = json.loads(post_request.data)

    return post_request

  
def find_uri_position(text):
    pattern = r'(https?://\S+)'
    match = re.search(pattern, text)

    if match:
        uri = match.group(0)
        start_position = len(text[:text.index(uri)].encode('utf-8'))
        end_position = start_position + len(uri.encode('utf-8')) - 1
        
        return (uri, start_position, end_position)
    else:
        return None

苦労したところ

実装例はいくつかあって参考にさせていただいたものも多いので、それほど苦労したところはなかったのですが、BlueskyのAPIの仕様を元にして、スキートに含まれるURLをきちんとリンクとしてPostする箇所の実装にはだいぶ苦労しました。旧Twitterと違ってBlueskyではURLもプレーンテキストとして認識するので、ここをリッチテキストとして認識させる必要があります。もちろんBlueskyのAPIにはその実装が行われているのですが、そこでだいぶ苦労をしました。

とはいえ、旧Twitterがこの先どういう運命を辿るかわからない中で、次のテキスト系のSNSを選んでいく中で、Blueskyの存在は見逃せないでしょう。APIの内容をもっと理解して、あ様々な使い方を見出していきたいと思います。

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

りきひさみねこヴァイオリン・ヴィオラ教室発表会 2023


今年も、りきひさみねこ先生の主宰するヴァイオリン・ヴィオラ教室の発表会の季節がやってきました。私にとっては去年に引き続き2回目の参加になりました。継続は力なり。

前回の記事の通り、私はJ.S.Bachの「無伴奏チェロ組曲ト長調 BWV1007」の「プレリュード」を、当たり前ですがピアノ伴奏なしの独奏で演奏することになりました。自分のヴィオラ人生の中で、オケや伴奏を伴わない独奏での演奏は初めてのことでした。

仕事の関係もあり、なかなか練習の時間をとることが難しかった中でなんとか日々少しずつ練習を重ねていって、いざ本番に挑んだのですが、本番前はこういうテンポで弾こうとか、ここの小節を弾く時にはポジション移動を忘れないようにしないととか、色々と考えていたところ、ステージに上がったところ、全て吹っ飛んでしまい、極度の緊張の中でとにかく目の前にある譜面をひたすらに追いかける形になってしまいました。指を間違えたり、一瞬演奏が止まってしまったところも、正直何箇所もありました。

惨敗、というか、やはり無伴奏は甘くなかった。

そんな無様な演奏の中でも、演奏自体を途中で諦めることなく、最後までなんとか弾き切るということだけは、先生のためだけではなく、同じ教室で習っている子どもさんを中心とした生徒の皆さんのためにも、どんな状態でも諦めちゃいけないということを見て欲しかったので、とにかくグダグダでもいいので弾き切りました。

ヴィオラという楽器だからというわけではないのですが、これまでパートという群れの中で弾くことが多かったので、自分の中にどこか、群れだからちょっと間違えても大丈夫だとか、そういう甘さがあったのかもしれません。それだけに独奏というのは、演奏技術をしっかり磨いていかなければいけないことはもちろん、ステージに立つ度胸も必要なんだなということを、改めて強く認識させてくれました。

いや今回ばかりは本当に悔しいですよ。心から悔しい。

けれども、悔しいからもう弾かないのではなく、まだまだ努力してレッスンを重ねていって、少しずつ上手くなって、来年の発表会ではもっと上手くなった姿を皆さんにお披露目できるようにしたいということだけは強く思ったのでした。

このままで終わるわけにはいかないですから。日々精進です。

なかなか上達しない自分を粘り強く見守って励ましてくださったみねこ先生と、暖かい拍手をくださった会場の皆さんには、本当に心から感謝したいと思います。ありがとうございます。

来年のこの場ではもっと上手くなった姿をお見せできるように、また基礎から練習に取り組んでいきたいと思います。負けませんから。

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

J.S.Bach 無伴奏チェロ組曲第1番「プレリュード」のレッスン その1


仕事の関係でちょっとだけお休みしていた、りきひさみねこ先生のヴィオラのレッスンを4月の末から本格的に再開し、7月末に行う発表会の曲目をどうするか話したところ、とんとん拍子でJ.S.Bachの無伴奏チェロ組曲第1番「プレリュード」を頑張ってみましょうかというお話になり、今日が実際に譜面を見ながらの初めてのレッスンでした。

最初はスラー無しでものすごくゆっくり音をとっていくところから開始です。録音を聴きながら譜面は眺めていたんですけれども、実際に弾いてみると指回りが結構大変なことに気づきました。普段は2の指でとっていたようなところを3の指でとったり、4の指でとっていたところをあえて開放限で弾いたり。正直頭がこんがらかりそうになりながらも、まずは全体の1/4までの音とりが完了。

先生曰く、前半と後半とでは前半の方が圧倒的に弾きやすいので、まずは前半をスラー無しでちゃんとさらっていき、全体をさらい終えたところで弾けないところをピックアップして重点的にレッスンしていきましょうとのこと。はい、承知しました。

この曲はJ.S.Bachの弦を使う曲の中でも好きなもののひとつなので、ゆっくりでもいいのでできるだけ丁寧に弾きたいなと考えています。目標にしているテンポは藤原真理さんの演奏している、割とゆったりしたものです。

可能であればこのテンポに近づけていきたいのですが、果たしてここまで近づけることができるかどうか。自分にとっては技術的にも自らかなりハードルを上げてしまった感がありますが、できる限りしっかりと弾くことができるように頑張っていきたいと思います。

ちなみにこの曲、先生によると原版はスラーがなくて、カザルスがスラー付きにしたとかなんとか。あとは1箇所だけ音の解釈(Hで弾くかBで弾くか)が異なるところがあるらしいです。初めて知りました。勉強になります。

レッスンの記録は自分自身の振り返りを含めて、発表会当日まで書き綴っていきます。

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

TerraformのtfstateファイルをTerraform Cloud上でお手軽に管理する方法


はじめに

IaC環境のひとつ、Terraformでコード管理しようとするとき、一般的にはバックエンドで生成される状態を管理するファイルである terraform.tfstate ファイルを、AWSではS3バケット上に格納することが多いですが、複数の環境のステートファイルをS3バケットで管理しようとするとなかなか煩雑になることが多々あるので、それを少し改良しようと、試しにTerraform Cloudで管理をしてみることにしました。

Terraform Cloudは、HashiCorp社が提供するTerraform向けのSaaS型のホスティングサービスで、本来はTerraformを組織の中で共同で利用するためのリポジトリを提供してくれたり、使いこなすことができれば、例えばGitHub上で管理しているTerraformのコードのプルリクエストがマージされた際に、GitHub Actionsのように、変更されたリソースの記述を環境上に反映してくれたりといったCI/CD的な機能を提供してくれたりと結構便利な機能が搭載されているのですが、そこまでのコントロールが必要でなくても、例えばステートファイルだけを管理することも可能です。

今回は、本当のスモールスタートで、ステートファイルだけをTerraform Cloud上に管理するところだけをスコープにしています。

設定手順

HashiCorp Cloud PlatformアカウントとOrganizationの作成

まずはHashiCorp Cloud Platformアカウントをトップページから作成してアカウントを取得後、Organizationの設定を行います。無料のプランであれば、5人まで組織の中にアカウントを紐づけることが可能です。

参考:

Projectとworkspaceの作成

Terraform Cloudは、Organizationの下に複数のProjectをぶら下げることができ、さらにその下に実際のステートファイルを管理するworkspaceを作成していくことができます。Terraform Cloudに作成したアカウントでログインすると、Projects & workspacesという画面に遷移するので、まずは右上の[New]ボタンをクリックしてProjectを作成します。

Projectの作成が完了したら、同じく[New]ボタンをクリックしてworkspaceを作成します。今回はコードの管理はGitHub上で行い、ローカルのターミナル環境を利用してTerraformの操作を行うので、Create a new Workspaceの画面上では[CLI-driven workflow]を選択します。すると次の画面でWorkspaceの名前と、Projectとの紐付けを行う画面に遷移するので、workspace名を入力して、先ほど作成したProjectを選択し、一旦[Create workspace]ボタンをクリックします。これでworkspaceまでが作成できています。

workspaceの設定

workspaceの設定まで完了したら、左ペインの[Settings]をクリックしてExecution Modeを”Local”に、Remote state sharingを”Share with all workspaces in this organization”に変更します。Execution Modeを”Remote”にすると、Terraformの実行に関する操作がすべてTerraform Cloud上にある仮想マシン上で実行されます。今回はステートファイルだけをTerraform Cloud上で管理したいため、”Local”に設定して、[Save settings]ボタンをクリックします。

Terraform側の設定

Terraform Cloudと連携できるようにするために、Terraformの実行環境を設定している .tf ファイルを以下のように記述します。(敢えてバックエンドをS3に設定していた時の設定をコメントアウトして残しています)

# ===============================================================================
# Terraform
# ===============================================================================
terraform {
  required_version = "1.4.6"
  #  backend "s3" {
  #    bucket = "terraform-bucket"
  #    key    = "terraform.tfstate"
  #    region = "ap-northeast-1"
  #  }

  cloud {
    organization = "vlayusuke"
    hostname     = "app.terraform.io"

    workspaces {
      name = "vlayusuke-poc-infrastructure-develop"
    }
  }

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "4.65.0"
    }
  }
}

実際に動かしてみる

Terraformを実行するときには、ターミナル上でまず最初に terraform login してTerraform Cloudにログインします。このときにトークンの発行を求められるので、 yes を入力するとWebブラウザに遷移してトークン発行画面が表示されます。

トークンを発行したらターミナルコマンドに貼り付けるとTerraform Cloudのログイン画面に遷移するので、あとは普通に terraform init して terraform plan して terraform apply することで、ステートファイルをTerraform Cloud上に保持しながらTerraformの操作を実行することが可能になります。

そしてステートファイルは以下のようにTerraform Cloud上に保持されます。

今後進めたいこと

これで、バックエンドに terraform.tfstate ファイルを管理することなく、Terraformを用いた環境の構築と構成管理を進めていくことが可能になりました。

とはいえTerraform Cloudではまだまだ色々なことができそうなので、効率的に環境構築と構成管理をできるように、Terraform Cloudの機能を色々と試していきたいと思います。

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

MacBook Air (M2,2022)を購入して6か月経過


先代のMacBook Air (M1, 2020)は、ディスプレイの修理をしながらも快適に使用していたのですが、会社の上司がMacBook Air (M2, 2022)を購入して楽しそうに作業していたのを見て、これはやっぱり自分も買い替えるしかない! と勝手に思ってしまい、半年前につい買い替えをしてしまいました。自分の中ではMacに関する減価償却は3年と考えていて3年おきに買い替えをしていたのですが、異例の速さです。買ってしまったものは仕方がないのでもう使い倒すしかないですね。

ちなみに以下のスペックのモデルを購入しました。

  • 8Core CPU
  • 8Core GPU
  • 16GB Memory
  • 512GB SSD

メモリは例によってデフォルト8GBのところを16GBに拡張しています。

期待通りのパフォーマンス

そんなわけで半年間使ってみてのインプレッションなのですが、パフォーマンスに関しては期待通りですね。普段使いだけではなく一部開発にも使用しているので、時たまDockerを立ち上げたりしているのですが、メモリを食いがちのDockerを立ち上げても他のアプリケーションの動作には影響しませんし、コードを書く際には同時にVisual Studio Codeを立ち上げているのですが、同時使用していても全く苦になりません。

それでいてMacBook Air (Mi, 2020)の重量よりもさらに軽い1.24kgまで軽くなっているので、どこにでも持っていきたい派の自分としては可搬性がさらに向上して嬉しい限りです。個人的に寂しいなと思っているのは、ディスプレイが大きくなったことで、”MacBook Air”のロゴがなくなってしまったことくらいですが、最近はそれも気にならなくなってきました。

ディスプレイのノッチに関しては、これはもう慣れですね。ちょうどメニューバーにかぶさるようにしてノッチが存在しているので、メニューバーに表示させているアプリケーションが一部見えなくなってしまっているのですが、そこは妥協点と言えば妥協点かもしれません。

今度こそは5年使えるかも

AppleCare+ for Macのラインナップの中にサブスクリプションプランが加わったのも大きいですね。これまで3年間隔で買い替えていたのは、買い切り型のAppleCareの期限が3年だったからというのもあったのですが、サブスクリプションプランであれば3年以上経過した場合でも保証が継続するので、ビンテージにならない限りは保証が効くというところが個人的にはかなり嬉しいところです。

これからもApple Siliconのチップは進化を続けるのでしょうけれども、相当な仕様変更がない限りは、今度こそはバッテリーの使用頻度を工夫しながらも5年くらい使っても期待通りのパフォーマンスは出続けてくれるのではないかなと思います。

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

山形交響楽団 第307回定期演奏会


山形交響楽団が、飯森範親さんの指揮で久しぶりにブルックナーの交響曲第7番を演奏するというので、山形新幹線に飛び乗って山形まで聴きに行ってきました。

本当に素晴らしい演奏を聴くと、感想を書くために何か言葉を紡ぎ出そうとしても、それが全て野暮になってしまうのではないかというほど、素晴らしい演奏でした。

芳醇な弦の響きと重厚感のある管楽器の響きが一体になって、まるで山形テルサホール全体が、ひとつの大きなオルガンのようになったのではないかという感覚を覚えてしまったほどでした。

その質感と音の世界が、いつまでも耳の奥に残ってなかなか抜けることができません。

会心の演奏だったと思います。この忙しい日々の中で素晴らしい音楽を聴くことができることがどれだけ素晴らしいことなのかということを、今もって改めて痛感させられました。そして自分なりに継続して音楽に触れていきたいと思わせてくれる演奏会でした。

飯森範親さんと山形交響楽団の皆さんに感謝です。

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

AWS認定ソリューションアーキテクト アソシエイト(AWS SAA-C03)に合格しての感想(C01からの再認定編)


これまで、AWS認定ソリューションアーキテクト プロフェッショナル(AWS SAP-C01)AWS認定DevOpsエンジニア プロフェッショナル(AWS DOP-C01)に合格してきたので、本来は再認定の日までにこの2つの試験を再受験すればいいのですが、AWS認定ソリューションアーキテクト アソシエイトのバージョンがC03まで上がっているということや、ここ最近サーバレスやコンテナのようなモダンアプリケーションを構築するお仕事に関わることが多くなってきたので、それらの経験で得た知識がどこまで付いているのかを効果測定するためにも、受けてみることにしました。

結果は777点で合格。上位資格に受かっていることを考えると、本当は800点以上のスコアを叩き出したかったのですが、出題範囲や分野が前回受けた内容と異なることを考えると、これはこれで納得のいくスコアなのかなぁと思います。

ついでに再認定扱いになったので2024年7月までだった再認定までの猶予期間も2026年2月まで延長させることができました。まずは一安心です。

どのようにして勉強したのか

今回この試験を受けるにあたって何をどのようにして勉強したのかというと、実はほとんど試験対策のような勉強の仕方はしてきませんでした。なぜかというと、前述の通り、前回の受験からの実務経験が本当に身についているかどうかの効果測定が目的だったからです。この試験を受けること自体が完全に手段化していました。なので試験ガイドの内容を確認した程度で、他に試験勉強をしたというのがなかったのです。これまでの経験と知識全てで勝負したという感じで。

強いて勉強らしいことをしたとすれば、「AWSで実現するモダンアプリケーション入門(落水恭介 吉田慶章著/技術評論社)」を3周くらい読みました。この本はAWSが提唱するモダンアプリケーションという考え方に沿って、既存のモノリシックなAWSの構成をいかにして改善させることができるかという考え方について、事例を交えながらわかりやすく書いてくれている良書だと思いますし、試験で出題される分野にかなり近いところを題材として採用しています。自分にとってはこれで十分だったかなと思っています。

ここでは具体的にどのような問題が出たのかについては、行動規範に抵触する可能性があるので言及はしません。ただ強いていうのであれば、だいぶ出題範囲が広くなっているのと同時に、モダンな方向にシフトしているなぁ、という感想は持ちました。

現時点で市販されているAWS SAA向けの試験参考書でC03のバージョンに対応しているのはごく僅かなので、初めてAWS SAAを受ける人にとっては結構大変かもしれませんが、座学だけではなく、実際にハンズオンをして試してみた方が理解は早いかもしれません。実務と紐づいてこその資格なので、手を動かしてみるほうが周り道に見えて実は近道なんじゃないかなと思います。

アソシエイト試験はその内容から、プロフェッショナル試験とは違った難しさがあるのですが、それは相変わらずでしたね。特に初めて挑戦する人は、十分に準備期間を設けてチャレンジしてみるほうがいいのかなと思います。

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

PritunlとAmazon DocumentDBを使用したVPNの仕組みを構築しようとして失敗した話


はじめに

社内のVPNの仕組みも社員数が増えてくるにつれてそれなりに管理がしやすいような方がいいよね、ということで、仕組み自体を更改するにあたり、VPNサーバを簡単に構築 & 管理することのできるPritunlと、ここが一番肝心なのですが、どうせならPritunlが使用するデータソースをAWS上で構築したいよねということで、Amazon DocumentDBを使用しようという話になり、ちょっとPoCをしてみましたというのがこの記事の趣旨になります。

まぁ最終的にはPritunlのデータソースとしてAmazon DocumentDBが使えない! という結論に至ったのですが、その理由は後述します。

構成

ざっくりと構成図書くとこんな感じになります。他にも社内のルータとかが登場するのですが、それは流石に出せないので、あくまでもVPNサーバとAmazon DocumentDBの関係性だけを記述しています。

Amazon DocumentDBの前段には、Public Subnetにルータとしての機能を持つEC2インスタンスを構築します。可用性はあまり求められないのでシングルAZ(AZ-a)にしていますが、Amazon DocumentDB側でサブネットグループの構築が必須となるため、AZ-cを空っぽの状態で構成しています。

VPS上に構築するVPNサーバは、今回はUbuntu 22.04 LTSを使用しています。

EC2インスタンスからAmazon DocumentDBへの接続確認

構築したEC2インスタンスにmongoDBをインストールして、内部的にAmazon DocumentDBへ接続が可能かどうかを確認します。当然のことながら、EC2インスタンスにはAmazon DocumentDBに対する接続を許可するIAMロールの設定をして、Amazon DocumentDBに対してはインバウンドでEC2インスタンスからのtcp/27017の通信を許可するSecurity Groupのインバウンドルールを設定しています。

まずはEC2インスタンスにmongoDBをインストール。

sudo echo -e "[mongodb-org-6.0] \nname=MongoDB Repository\nbaseurl=https://repo.mongodb.org/yum/amazon/2/mongodb-org/6.0/aarch64/\ngpgcheck=1 \nenabled=1 \ngpgkey=https://www.mongodb.org/static/pgp/server-6.0.asc" | sudo tee /etc/yum.repos.d/mongodb-org-6.0.repo
sudo yum install -y mongodb-org -y

これが完了したら、Amazon DocumentDBに接続するための証明書を取得してきます。

wget https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem

そして、以下のコマンドで接続確認を実行します。

sudo mongosh --tls --host <cluster name>.cluster-<cluster identifier>.ap-northeast-1.docdb.amazonaws.com:27017 --tlsCAFile rds-combined-ca-bundle.pem --username <username> --password <password>

実行後、以下のように出力されれば接続は成功です。

Current Mongosh Log ID: <Log ID>
Connecting to: mongosh://<credentials>@<cluster name>.cluster-<cluster identifier>.ap-northeast.docdb.amazonaws.com:27017/?directConnection=true&tls=true&tlsCAFile=rds-combined-ca-bundle.pem&appName=mongosh+1.6.2
Using MongoDB: 4.0.0
Using Mongosh: 1.6.2

(snip...)

rs0 [direct : primary] test>

VPNサーバの構築

AWS側の構築と設定が完了したら、次にVPS上のVPNサーバを構築していきます。

まずはAWS CLIのインストールから。

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

次にPritunlをインストールしていきます。

# まずは既存でインストールされているパッケージを最新バージョンに更新
sudo apt update && sudo apt upgrade

# PritunlとMongoDBをインストール
sudo apt update && sudo apt install pritunl mongodb-org

# インストールしたPritunlとMongoDBを起動
sudo systemctl start pritunl mongod
sudo systemctl enable pritunl mongod

ここまで終われば、あとはPritunlとローカルのMongoDBの起動状態を確認すればOKです。

Pritunlの設定

Pritunlを初期セットアップするためには、セットアップキーの取得が必要なため、以下のコマンドを実行します。

sudo pritunl setup-key

実行すると16進数のセットアップキーが表示されるので、それを控えておきます。

次に、https://<VPNサーバのIP Address>/setup をブラウザで開き、表示されたダイアログにセットアップキーを入力します。

[Save]ボタンを入力すると、管理者のユーザ名とパスワードを作成しろというダイアログが表示されるので、コンソールに戻り、以下のコマンドを実行して管理者のユーザ名とパスワードを作成します。

sudo pritunl default-password

ここで出力されたユーザ名とパスワードを控えておきます。

もしもVPNサーバのローカルに構築されたMongoDBを使用してPritunlを使用する場合は、ここで構築は完了なのですが、ここで、一旦VPNサーバからDocumentDBに接続が可能かどうかを、EC2インスタンスから接続確認した時と同じ手順で接続を確認します。

sudo mongosh --tls --host <cluster name>.cluster-<cluster identifier>.ap-northeast-1.docdb.amazonaws.com:27017 --tlsCAFile rds-combined-ca-bundle.pem --username <username> --password <password> --tlsAllowInvalidHostnames

ここまでは普通に成功するはずです。

PritunlのデータソースとしてAmazon DocumentDBが使えないことが発覚

ここで、/etc/mongod.confのデータベース接続文字列を修正して、Amazon DocumentDBを向くように設定したのですが、ここでInternal Server Errorが発生。/var/log/pritunl.logに以下のような大量のエラーが吐かれます。

[undefined][2023-01-20 09:38:17,828][INFO] Starting setup server
[undefined][2023-01-20 09:38:18,153][INFO] Generating setup server ssl cert
[undefined][2023-01-20 09:38:22,654][ERROR] Pritunl setup failed
Traceback (most recent call last):
  File "/usr/lib/pritunl/lib/python3.10/site-packages/pritunl/setup/__init__.py", line 43, in setup_all
    setup_mongo()
  File "/usr/lib/pritunl/lib/python3.10/site-packages/pritunl/setup/mongo.py", line 446, in setup_mongo
    mongo.secondary_database.create_collection(
  File "/usr/lib/pritunl/lib/python3.10/site-packages/pymongo/database.py", line 425, in create_collection
    return Collection(self, name, True, codec_options,
  File "/usr/lib/pritunl/lib/python3.10/site-packages/pymongo/collection.py", line 187, in __init__
    self.__create(kwargs, collation, session)
  File "/usr/lib/pritunl/lib/python3.10/site-packages/pymongo/collection.py", line 264, in __create
    self._command(
  File "/usr/lib/pritunl/lib/python3.10/site-packages/pymongo/collection.py", line 238, in _command
    return sock_info.command(
  File "/usr/lib/pritunl/lib/python3.10/site-packages/pymongo/pool.py", line 710, in command
    return command(self, dbname, spec, secondary_ok,
  File "/usr/lib/pritunl/lib/python3.10/site-packages/pymongo/network.py", line 161, in command
    helpers._check_command_response(
  File "/usr/lib/pritunl/lib/python3.10/site-packages/pymongo/helpers.py", line 167, in _check_command_response
    raise OperationFailure(errmsg, code, response, max_wire_version)
pymongo.errors.OperationFailure: Feature not supported: capped:true, full error: {'ok': 0.0, 'code': 303, 'errmsg': 'Feature not sup
ported: capped:true', 'operationTime': Timestamp(1674175102, 1)}
[undefined][2023-01-20 09:38:22,658][INFO] Stopping server

該当するコードを調べてみたのですが、Pritunl側では、セットアップ時のmongoDBに対する設定として、上限付きコレクションが Trueになっているのですが、Amazon DocumentDBの仕様ではFalseになっており、変更不可であるため、セットアップそのものができない状態になってしまっているのでした。万事休す。

というわけで、PoCはここで泣く泣く終了になってしまいました。この辺のPoCに関してはなかなかまとまったドキュメントがないので、ここに至るまでに結構時間を溶かしてしまいました。Pritunl側のUpdateを待つか、Amazon DocumentDB側で上限付きコレクションの設定がサポートされるかどうか、今はちょっと待ちの状態ですね。

ただ、どちらかがサポートされれば、AWSのリソースで効率的にVPNサーバの管理ができるようになるかもしれません。

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

2022年のフライトログとBronzeステイタス継続


はじめに

去年からANA Bronzeのステイタスに到達したのですが、今年もそれを継続すべく、まぁ飛行機にはよく乗りました。多分年間7往復したのは初めてなんじゃないかなと思います。

というわけで簡単なフライトログをば。

フライトログ

  • 2022/1/15 NH245 HND → FUK (B788 / JA802A)
  • 2022/1/16 NH258 FUK → HND (B788 / JA831A)
  • 2022/2/25 NH243 HND → FUK (B788 / JA838A)
  • 2022/2/26 NH256 FUK → HND (B788 / JA831A)
  • 2022/6/17 NH019 HND → ITM (B788 / JA840A)
  • 2022/6/19 NH024 ITM → HND (B788 / JA815A)
  • 2022/7/9 NH677 HND → HIJ (A321 / JA139A)
  • 2022/7/9 NH684 HIJ → HND (A321 / JA151A)
  • 2022/10/17 NH055 HND → CTS (B763 / JA608A / 鬼滅弐号機)
  • 2022/10/19 NH070 CTS → HND (B788 / JA810A)
  • 2022/11/12 NH395 HND → SYO (B738 / JA63AN)
  • 2022/11/12 NH400 SYO → HND (B738 / JA72AN)
  • 2022/12/3 NH245 HND → FUK (B772 / JA745A / 鬼滅参号機)
  • 2022/12/4 NH252 FUK → HND (B788 / JA818A)

相変わらずB788には乗っていますね。もうすでにANAの主力機になっているので幹線に搭乗しようとすると大概にしてB788に当たりますが、好きな飛行機なので全然問題ありません。まさかのまさかだったのが、3機就航している「鬼滅の刃」コラボ機に期せずして2機登場してしまったこと。ラッキーと言えばラッキーなのかもしれませんが、あの機内アナウンスはちょっと恥ずかしいですね。でも個人的には楽しめたのでよしとします。

そういうわけで2年目のANA Bronzeステータスに突入

そんな感じで乗りまくったので、年末には今回もANA Bronzeのステータスを獲得することができました。2年目になるので来年はマイルの積算率が上がりますね。欲を言えばDiamondまでいきたいところですが、そこを目指そうとすると海外に行くことも視野にいればければいけないので、ご時世柄このままBronzeを継続していければいいのかなと思っています。あまり欲は出しません。

なんやかんや言っても飛行機の旅は大好きなので、来年も無理のない範囲で乗りまくる所存であります。

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