SAMのサンプルプロジェクトを触ってみる

やってみたのでメモ。

AWS CloudShellで以下のコマンドを叩く。

sam init --runtime python3.7 --dependency-manager pip --app-template hello-world --name sample-sam

cd sample-sam

sam build

sam deploy --guided

SAMの公式ガイド:https://docs.aws.amazon.com/serverless-application-model/index.html

Lambda関数とかAPIGatewayとか、たったこれだけで全部できた。
削除もコマンドで簡単にできる。

sam delete

簡単すぎてすごすぎて何も書くことない。まじでただのメモ。
上のやつはUdemy講座のやつなんだけど、オプションとかよくわかんないのでAWS公式のチュートリアルやってみたいな。

AWSSystemManagerを触ってみた

やってみたのでメモ。

  1. IAMロールを作る
    AmazonEC2RoleforSSMというEC2インスタンス用のロールポリシーが既に用意されているので、これをアタッチしたロールを用意する。

  2. EC2インスタンスに1のロールをアタッチ

  3. AWS Systems Managerのメニューから左側のフリートマネージャーをクリック
    2でIAMロールをアタッチしたインスタンスが表示されていれば準備はOK。

  4. 左側メニューの共有リソース→ドキュメントをクリック
    仮想サーバーを操作するための、よく使う手順が既に用意されている。オートメーションのパッケージみたいな感じ。
    これを組み合わせて一連の操作を自動化できる。

  5. なんかやってみる。
    StopEC2インスタンスをやってみた。止まった。 起動するやつもある。STARTできた。

ついでによく使う機能

  1. 左メニューのノード管理→RunCommand
    WindowsUpdateなんかもある。 実行するインスタンスの割合を設定したり、結果を出力したりできる。

  2. 左メニューのノード管理→パッチマネージャー
    パッチの適用もスケジュール決めて自動化できる。便利ー。

Java女子部行ってきた。JJUG CCC recap イベント

4年ぶりにJava女子部行ってきました。
4年ぶりと行っても、4年前に初めて行ったきりなので、ほぼ初回みたいなもん。

今日やったトピック
①セッションの再演
 「金融系子会社でレガシーシステムしか作ったことないけど、モダン開発に挑戦してみた」
②セッションをみんなで見る
 Java21の新機能紹介みたいなやつをみた。

今日を経て、今後やりたいこと。
1.設計の勉強をちゃんとする。
  とにかく頭使って作らないことには身につかないと①のセッションで言ってて、そうだよな~と思ったので、なまけずに手を動かしていこうと思った。
  今日の①セッションで紹介されてた本、読む。
2.Javaのキャッチアップする
  現場に入ってるJava8の最新機能すら使えてないの、普通に勿体ない。
  Java21の最新機能をサラっと聞いただけでも、この処理書きにくいな~みたいなのを改善する努力みたいなのがすごくされているのがわかって、
  プログラミングの生産性上げる最短&最簡単ルート、案外これなのでは・・?と思ったレベルだった。

10月までは国連英検の勉強とAWS DVAの勉強がんばりたいので、できれば2023年中にやる。

以下、感想。

①について

「技術の壁」「文化の壁」がいまの現場と同じすぎて、共感しかなかった。
エンジニアやってて会社に勉強しろって言われたことない人いないレベルで勉強勉強言われるけど、
会社が望むような、その案件を受けられるプロを育てるには、
やっぱりきちんと時間割いて給料払って専任チーム作ってやらないと厳しいよなあと思った。
私個人もAWSの勉強してSAAとったけどじゃあAWSの案件いますぐ受けれますか?って言っても無理なわけで。
業務外でチマチマ勉強するだけでプロとして案件を受注できるようになる人材は、そもそもこんな給料でこんな会社にはいないのではと思う。
登壇者の汐月さんは、まともにモダン開発できるようになるまで3年はかかるって言ってて、
週5でフルコミットして3年なら、業務外の独学で1日2時間やったとしても4倍かかるわけだから12年?うーん。。
やっぱり組織レベルで変わりたければ投資って不可欠で、末端の人間にそこは結構どうしようもないんだけど、とはいえ会社が本腰入れたときに多少周囲をリードできるくらいの、それこそ机上の知識レベルのことを頭に入れておくのはちゃんとやっておきたいなと思った。
勉強したって案件ないし、虚無な気持ちになることもあるけど、とりあえず、本職バリバリの人が資格なんて意味ないって言うのを真に受けて勉強から逃げていたらダメ。自分のモチベーションをうまいことコントロールしながらコツコツできることから続けていきたいなーと思った。

②について
みんなで見たセッションはJava21の新機能紹介みたいなやつで、最初に書いた通りの学びがあったんだけど、
他にもおもしろそうなセッションあったからあとで見る。
JJUG CCCのセッション一覧
https://sessionize.com/api/v2/y7inyq6y/view/GridSmart

Youtube再生リスト
https://www.youtube.com/watch?v=klhjuOUOW98&list=PLy44EKO1L0eJD-QlTujqMJmR7bQ10WG6Z

youtube.com

飲み会も楽しかった。なんか新しい人との出会いが久しぶりだった。

次回のJJUGは参加したいな~。

VBAを勉強しました。~データを条件に従って分割~

仕事でどうしてもVBAから逃げられなくなってきたので、勉強した。
以下のようなマクロを作りました。

こういうデータがあったとき、

市町村名 子ども 登録日 項番
千代田区 100 200 10 2023/7/1 001
横浜市 100 200 10 2023/7/1 001
さいたま市 100 200 10 2023/7/1 001

こういうマスタを参照して、

県名 市町村名
東京都 千代田区
神奈川県 横浜市
埼玉県 さいたま市

それぞれ「千代田区」のデータなら「東京都のシートに、 「横浜市」のデータなら「神奈川県」のシートに、 「さいたま市」のデータなら「埼玉県」のシートに分割て出力します。

これまでマクロってちゃんと勉強せずにググりながら雰囲気で書いてたけど、やっぱりちゃんと勉強するほうが早いね。

できたマクロはこちら。

Sub データ分割()
    Application.ScreenUpdating = False
    
    'まずは生成したいシートが存在するかを確認しておく。存在する場合は確認
    Dim rc As Long
    If シート存在確認("東京都") Then
       rc = MsgBox("シート「東京都」は存在します。内容をすべて削除して更新してよろしいですか?", vbYesNo + vbQuestion)
       If rc = 7 Then
            MsgBox "処理を中止します"
            End
        End If
    End If
    
    If シート存在確認("埼玉県") Then
        rc = MsgBox("シート「埼玉県」は存在します。内容をすべて削除して更新してよろしいですか?", vbYesNo + vbQuestion)
        If rc = 7 Then
            MsgBox "処理を中止します"
            End
        End If
    End If
    
    If シート存在確認("神奈川県") Then
       rc = MsgBox("シート「神奈川県」は存在します。内容をすべて削除して更新してよろしいですか?", vbYesNo + vbQuestion)
       If rc = 7 Then
            MsgBox "処理を中止します"
            End
        End If
    End If
   
    'ここから処理開始
    Dim rownum As String
    rownum = Worksheets("データ").Range("A1").Row
    
    'データの最終行を取得しておく
    Dim dataLastRow As Integer
    dataLastRow = Worksheets("データ").Cells(Rows.Count, 1).End(xlUp).Row

    'マスタの最終行も取得
    Dim mstLastRow As Integer
    mstLastRow = Worksheets("マスタ").Cells(Rows.Count, 1).End(xlUp).Row
    
    '配列を定義
    ReDim tokyoArray(dataLastRow, 5) As String
    ReDim saitamaArray(dataLastRow, 5) As String
    ReDim kanagawaArray(dataLastRow, 5) As String
       
    '街名
    Dim targetTownName As String
    '県名
    Dim prefectureName As String
       
    'それぞれの配列の現在位置を定義
    Dim tokyoCellNum As Integer
    Dim saitamaCellNum As Integer
    Dim kanagawaCellNum As Integer
    '現在位置を初期化
    tokyoCellNum = 0
    saitamaCellNum = 0
    kanagawaCellNum = 0
    
'「データ」シートから1行ずつ処理していく。
    Dim i As Integer
    For i = 0 To dataLastRow - 2
        rownum = rownum + 1
        '街名を取得
        targetTownName = Worksheets("データ").Range("A" & rownum).Value
        '街名を使用して、マスタから県名を探して取得
        prefectureName = WorksheetFunction.Index(Worksheets("マスタ").Range("B2:B" & mstLastRow), _
                                            WorksheetFunction.Match(targetTownName, Worksheets("マスタ").Range("A2:A" & mstLastRow), 0))
        
        'データを県名ごとの配列に格納
        If prefectureName = "東京都" Then
            Dim j As Integer
            For j = 0 To 5
                tokyoArray(tokyoCellNum, j) = Worksheets("データ").Cells(rownum, j + 1).Value
            Next
            tokyoCellNum = tokyoCellNum + 1
        ElseIf prefectureName = "埼玉県" Then
            Dim k As Integer
            For k = 0 To 5
                saitamaArray(saitamaCellNum, k) = Worksheets("データ").Cells(rownum, k + 1).Value
            Next
            saitamaCellNum = saitamaCellNum + 1
        ElseIf prefectureName = "神奈川県" Then
            Dim l As Integer
            For l = 0 To 5
                kanagawaArray(kanagawaCellNum, l) = Worksheets("データ").Cells(rownum, l + 1).Value
            Next
            kanagawaCellNum = kanagawaCellNum + 1
        End If
    Next
  
    '結果出力
    '東京都
    If Not シート存在確認("東京都") Then
        Worksheets("データ").Copy After:=Worksheets(Worksheets.Count)
        ActiveSheet.Name = "東京都"
    End If
    ActiveSheet.Range("A2 : E" & Cells(Rows.Count, 1).End(xlUp).Row).ClearContents
    
    Worksheets("東京都").Range("A2:E" & dataLastRow).Value = tokyoArray
    
    '埼玉県
    If Not シート存在確認("埼玉県") Then
        Worksheets("データ").Copy After:=Worksheets(Worksheets.Count)
        ActiveSheet.Name = "埼玉県"
    End If
    ActiveSheet.Range("B2 : D" & Cells(Rows.Count, 1).End(xlUp).Row).ClearContents
    
    Worksheets("埼玉県").Range("A2:E" & dataLastRow).Value = saitamaArray
    
    '神奈川県
    If Not シート存在確認("神奈川県") Then
        Worksheets("データ").Copy After:=Worksheets(Worksheets.Count)
        ActiveSheet.Name = "神奈川県"
    End If
    ActiveSheet.Range("A2 : E" & Cells(Rows.Count, 1).End(xlUp).Row).ClearContents
    
    Worksheets("神奈川県").Range("A2:E" & dataLastRow).Value = kanagawaArray
    
    '最後にデータ側の整形をする。集計に使う列は、表示形式を標準、データ型を数値にしておく
    Dim objSheet As Worksheet
    ' ブックの全シートを 1 つずつループして処理する
    For Each objSheet In ThisWorkbook.Worksheets
        If objSheet.Name <> "データ" And objSheet.Name <> "マスタ" _
                And objSheet.Name <> "操作パネル" Then
            '表示形式の変更
            objSheet.Columns(2).NumberFormatLocal = "G/標準"
            'データ型の変更
            objSheet.Columns(2).TextToColumns Comma:=True
            objSheet.Columns(3).NumberFormatLocal = "G/標準"
            objSheet.Columns(3).TextToColumns Comma:=True
            objSheet.Columns(4).NumberFormatLocal = "G/標準"
            objSheet.Columns(4).TextToColumns Comma:=True
        End If
    Next
    
    MsgBox "データ分割が完了しました"

End Sub

'シート存在確認の関数
Function シート存在確認(sheetName As String)

Dim ws As Worksheet
 
On Error Resume Next
Set ws = ThisWorkbook.Worksheets(sheetName)
On Error GoTo 0
 
シート存在確認 = Not ws Is Nothing
 
End Function

【Minecraft】1人でもプレイヤーが死亡したらワールドを再生成する

先日のこれを、以下の記事で書いている通りプラグインとの抱き合わせでちゃんと作った。

mstudy-diary.hatenablog.com

プレイヤー死亡時にspigotのサーバーログlatest.logにキーワードを出力するプラグインを設置し、
スクリプトでそれを拾って再生成操作を行う。

プラグインはこちら。

github.com

スクリプトはこちら。パスはご自由に。

#!/bin/sh

# ------------設定箇所-------------

# screenの名前
SCREEN_NAME=Minecraft

# ワールド名
WORLD_NAME=HARDCORE_WORLD

#ワールドパス
WORLD_PASS=minecraft-spigot
#バックアップワールドパス
BK_WORLD_PASS=bk_minecraft-spigot

#スクリプトログパス
SCRIPT_LOG=logs/shellresult.log
SCRIPT_LOG_NUMBER=numberOfPlayears.log

#マイクラログパス
SPIGOT_LOG=${WORLD_PASS}/logs/latest.log

# ---------------------------------

echo START >> ${SCRIPT_LOG}

#二重起動チェック
pid=`pgrep -ox newWorld.sh`
mypid="$$"

if [ ${mypid} != ${pid} ]; then

    echo notMyPID >> ${SCRIPT_LOG}
    exit

fi

echo shellStart >> ${SCRIPT_LOG}

#死亡確認。これがプラグインから吐かれるキーワード
isDeath=`grep -q "PLAYERDEATH\!" ${SPIGOT_LOG} && echo OK`

#サーバー停止中かどうか。
#サーバーを再起動するごとにlatest.logはリフレッシュされるが、タイミングによってリフレッシュ前に当シェルが動いてしまわないため
isNotStopping=`grep -q "Stopping the server" ${SPIGOT_LOG} && echo NG`

#死亡したプレイヤーが存在してサーバー停止中じゃない場合
if [ "${isDeath}" = "OK" ] && [ "${isNotStopping}" != "NG" ]; then
    
  #人数チェック
  echo checkPlayears >> ${SCRIPT_LOG}
    screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "list\015"'
    grep 'There are . of' ${SPIGOT_LOG} | tail -n 1 > ${SCRIPT_LOG_NUMBER}
    isNoPlayears=`grep -q 'There are 0 of' ${SCRIPT_LOG_NUMBER} && echo OK`
    
    if [ "${isNoPlayears}" = "OK" ]; then
    #0人だったらワールド再生成
  #人数チェックをしないと、1人死んだあと他のメンバーが遊んでいても問答無用でサーバーが止められて余韻がないので・・
    echo createNewWorld >> ${SCRIPT_LOG}

    screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "stop\015"'
    
    mv ${SCRIPT_LOG_NUMBER} ${SCRIPT_LOG_NUMBER}.`date "+%Y%m%d_%H%M%S"`

    sleep 30
    
  #ワールドディレクトリを無理矢理削除してしまうので、丸ごとコピーしておく
    cp -r ${WORLD_PASS} ${BK_WORLD_PASS}

    rm -rf ${WORLD_PASS}/${WORLD_NAME}
    rm -rf ${WORLD_PASS}/${WORLD_NAME}_nether
    rm -rf ${WORLD_PASS}/${WORLD_NAME}_the_end

    #削除後に再起動
    screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "cd minecraft-spigot"\015'
    screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "./Minecraft_start.sh\015"'

  #コピーしたバックアップデータをzip化しておく。容量がきついので
    zip ${BK_WORLD_PASS}.`date "+%Y%m%d_%H%M%S"`.zip ${BK_WORLD_PASS}
    rm -rf ${BK_WORLD_PASS}

    echo success >> ${SCRIPT_LOG}

fi
fi

【Minecraft】ログを取得してワールドを自動的に再生成するスクリプト

ハードコアを複数人でクリア、1人でも死んだら初めからやり直しという企画を前やったときに、
手動でワールドを再生成するのが面倒だったのでスクリプト化してみた。
まだまだスマートじゃなくてアレだけどとりあえず動いたので記録しておく。

スクリプトは、ワールドから人がいなくなったら再生成という形にしているが、
よく考えたら死亡ではなく今日はここまで!って解散したときもワールドが削除されちゃうのでアカンですね。
プラグインとの抱き合わせで、プレイヤーの死亡カウントをログに出力する形にして、
そのログをチェックして実行するやり方に直したい。
(あと誤作動したときのためにワールドをバックアップとかもしたいけど、サーバーの容量の問題もあるので要検討)

2023/6/12 追記
完成版はこちら。
mstudy-diary.hatenablog.com

追記おわり

ファイルを指定している個所は適宜絶対パスにするといいと思います。

#!/bin/sh

# ------------設定箇所-------------

# screenの名前
SCREEN_NAME=Minecraft

# ---------------------------------

#二重起動チェック
pid=`pgrep -ox newWorld.sh`
mypid="$$"

if [ ${mypid} != ${pid} ]; then

#cronで10秒おきに起動しているので、二重起動になっていたら処理を終了
exit

fi

#1人以上入室したあとかどうかを取得したいので、listコマンドを実行
screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "list"\015'

#Spigotログの中から、listコマンドの結果行だけを抜粋
grep 'There are . of' latest.log > tmp11.txt
#sedコマンドでログを編集。確認用に毎度ファイル出力してるので汚い。綺麗にしたい
#空白削除
sed 's/ //g' tmp11.txt > tmp12.txt
#[TIMESTAMP]There are X of players~とログが出るので、XだけほしいのでまずX以前を削除 
sed 's/^.*Thereare//g' tmp12.txt > tmp13.txt
#X以降を削除
sed 's/ofamaxof.*//g' tmp13.txt > tmp14.txt
#結果を確認。0以外だったら(1人以上が入出している記録があったら)、変数にOKが返る
numOfPlayers=`grep -q -v 0 tmp14.txt && echo OK`

if [ "${numOfPlayers}" = "OK" ]; then
#1人以上入室していた場合
#1人以上入室した記録があり、かつ現在の人数が0人だったらワールドを再生成したいので、またログをチェック
#listコマンドの結果のうち最終行を取得(同じようなことさっきもやってますね。要修正。)
grep 'There are . of' latest.log | tail -n 1 > tmp21.txt
#さっきと同じく、空白を削除して、人数以外の文字列を削除
sed 's/ //g' tmp21.txt > tmp22.txt
sed 's/^.*Thereare//g' tmp22.txt >tmp23.txt
sed 's/ofamaxof.*//g' tmp23.txt > tmp24.txt

#最終人数が0人だったら再生成OK
numOfPlayers=`grep -q 0 tmp24.txt && echo OK`

#ワールド再生成
if [ "${numOfPlayers}" = "OK" ]; then

#サーバーをとめて、
screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "stop\015"'

sleep 30

#worldデータを削除
#spigotのserver.propertiesのlevel-nameでワールドディレクトリが作成される。今回はHARDCORE WORLDにしてるのでそれを指定。
rm -rf 'HARDCORE WORLD'
rm -rf 'HARDCORE WORLD_nether'
rm -rf 'HARDCORE WORLD_the_end'

#削除後に再起動
screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "cd minecraft-spigot"\015'
screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "./Minecraft_start.sh\015"'

fi

fi

cronの登録内容はこんな感じ。10秒おきに実行します。

* * * * * for i in `seq 0 10 59`;do (sleep ${i}; cd /~/シェルがおいてあるパス && ./newWorld.sh) & done;

cronについてはこちらをどうぞ。

mstudy-diary.hatenablog.com

CloudWatchAgentでメモリ使用率を見たい

掲題の通り。
メモリ使用率は、カスタムメトリクスを設定しないとできないので、それをやってみた。

qiita.com

ほぼこちらの手順通りなんだけど、
違うのは取得するログにSpigotのlatest.logを追加したくらい。

        "agent": {
                "metrics_collection_interval": 60,
                "run_as_user": "root"
        },
        "logs": {
                "logs_collected": {
                        "files": {
                                "collect_list": [
                                        {
                                                "file_path": "/var/log/syslog",
                                                "log_group_name": "syslog",
                                                "log_stream_name": "syslog"
                                        },
# ここからSpigotログ
                                        {
                                                "file_path": "Spigotのログのフルパス/latest.log",
                                                "log_group_name": "latest.log",
                                                "log_stream_name": "latest.log"
                                        }
# ここまで
                                ]
                        }
                }
        },
        "metrics": {
# (以下省略)

これで無事、SSH接続しなくてもマイクラのサーバーログを見れるようになった。
本番環境?の操作、ドキドキするからなるべくやりたくないね・・