【Golang備忘録①】 - OS固有処理への対応 -

Goでは標準パッケージだけで、ほぼほぼOSの区分なしに実装できる。 しかしそれでもWindows/Linux固有の実装というのは付きまとう。

Goでは2つの対処方法がある。1つ目はrntime.GOOSによるOS判定による方法。
2つ目はBuild Constraintsを利用したビルド時にOS振り分けを行う方法。

runtime.GOOSを使う

runtime.GOOSには実行されるOS名が格納されている。

OSに依存した処理を分けて書きたい場合、パッケージの依存やOS特有のAPIなどが現れない場合に限りruntime.GOOSを判定して処理を分けることができる。

// 例えば、bat or shellを実行するコードの場合

var cmd exec.Cmd

if runtime.GOOS == "windows" {
    cmd = exec.Command("cmd", "/c", "myapp.bat")
} else {
    cmd = exec.Command("/bin/sh", "-c", "myapp.sh")
}

err := cmd.Run()
if err != nil {
    log.Fatal(err)
}

GOOSで取得できる主な値の一覧

OS
Windows windows
Linux linux
Android android

solarisとかfreebsdとかあるけど割愛。(WindowsLinuxあれば大体なんとかなるでしょ。)

Build Contraints を使う

Build Contraints とはGoのソースコードをビルドする際に指定できる条件識別、またはそれを使用したビルドの手順を指す。

Goでは次の2つの規則を使用して各環境においてビルドに含まれるソースコードを明示できる。

  • ファイル名による指定
  • +buildコメントによる指定

ファイル名による指定

次のファイルが置かれているディレクトリで go build を実行すると、Windowsではcommand.goとcommand_windows.goが、Linuxではcommand.goとcommand_linux.goがコンパイルされる。

runtime.GOOSで取得できる値で指定している。

OS識別以外にCPUアーキテクチャによる指定もできるがまず使わないと思う。

+buildコメントによる指定

ファイル単位で+buildコメントを使用して条件を書く方法。

各OSやアーキテクチャに従ってコンパイル対象となるソースファイルを定義または、コンパイル対象から除外するといったことができる。

// +build[タグ]

package main

+build コメントの下は必ず1行空ける。

// 使い方1 Windowsであること
// +build windows

// 使い方2 Linux(とAndroid)ではないこと !はNOTの意味
// +build !linux

// 使い方3 LinuxまたはWindowsであること , 区切りで複数条件
// +build linux,windows

linuxを指定するとAndroidも含まれてしまう。

Androidは含みたくない場合は以下のように明示的にする。

// +build linux,!android

コンパイル対象から外す場合は ignore を付与する。

この場合、go run 以外できなくなる。

// +build ignore

VS Code ショートカット チートシート

VS Codeでよく使うショートカットまとめ

去年は狂ったように使っていたが、IntelliJ IDEAに浮気してショートカットをわすれてしまった。。。昔使っていたチートシートが見つかったのでブログに残しておく。

基本操作

キー 動作
⌘X 行の切り取り (未選択時)
⌘C 行のコピー (未選択時)
⌘Enter 下に行追加
⇧⌘Enter 上に行追加
⌘D 対象行を追加
⌘↓ 行の先頭に移動
⌘↑ 行の末尾に移動
⌘Home ファイルの末尾に移動
⌘End ファイルの先頭に移動
⌥⌘F 置換
⌘F 検索

ナビゲーション

キー 動作
⌘P 指定ファイルに移動
⇧⌘O 指定シンボルに移動
⇧⌘M エラーと警告を表示
F8 次のエラーと警告に移動
⇧F8 前のエラーと警告に移動

エディター/ウィンドウ操作

キー 動作
⇧⌘N 新しいウィンドウを開く
⇧⌘W ウィンドウを閉じる
⌘1 左のメニューにフォーカス
⌘2 右のエディターにフォーカス
⌘3 左メニュー(検索)にフォーカス
⌥⌘← 左隣のエディターにフォーカス
⌥⌘→ 右隣のエディターにフォーカス

ファイル操作

キー 動作
⌘N 新しいファイル
⌘S 保存
⇧⌘S 名前を付けて保存
⌘W ファイルを閉じる
⌘K Enter 作業中ファイルに追加
⌘K ↓ 次の作業中ファイルを開く
⌘K ↑ 前の作業中ファイルを開く
⌘K P アクティブなファイルのパスをコピー
⌘K R アクティブなファイルをエクスプローラーで表示
⌘K O アクティブなファイルを新しいウィンドウで表示

画面表示

キー 動作
^⌘F フルスクリーン表示のトグル
⌘B サイドバー表示のトグル
⇧⌘D デバッグ画面の表示
⇧⌘E エクスプローラーの表示
^⇧G Git画面の表示
⇧⌘F 検索画面の表示
⇧⌘J 詳細検索のトグル
⇧⌘C 新しいコマンドプロンプトを開く
⇧⌘U アウトプット画面を開く
⌃` 統合ターミナルの表示をトグル

設定 Preferences

キー 動作
⌘, ユーザー設定を開く

デバッグ

キー 動作
F9 ブレークポイントのトグル
F5 コンティニュー
F5 ポーズ
F11 ステップイン
⇧F11 ステップアウト
F10 ステップオーバー
⇧F5 ストップ

タスク

キー 動作
⇧⌘B ビルドタスクの実行
⇧⌘T ランタスクの実行

【AWS メモ⑧】Elastic Beanstalk 自分用にまとめてみた

この記事はQiitaの記事をエクスポートしたものです。内容が古くなっている可能性があります。

Elastic Beanstalk とは

Webアプリケーションをアップロードすると、運用に必要な環境の作成、デプロイ、ロードバランシングまで自動で構築できるPaaSである。 環境は、ウェブサーバー環境、ワーカー環境の2種類が用意されている。(公式サイト - Elastic Beanstalk 概念)

また、複数コンテナを動作させるDocker環境の構築も可能。コンテナの管理はECSらしい。(公式サイト - 複数コンテナの Docker 環境)

使ってみる

Elastic Beanstalk をターミナルから使用するためのツールをインストールする。

# とりあえず brew をアップデート
$ brew update

# ツールをインストール
$ brew install awsebcli

HelloWorldしてみる

Elastic Beanstalk はアプリケーションと環境に分かれている。 アプリケーションには、アプリケーション名、キーペア名、ランタイムバージョン、リージョンを設定する。 環境には、オートスケーリング、ELBの設定をする。 アプリケーションという枠の中にWebアプリケーションを実行する環境が複数存在するイメージ。

アプリケーションの作成

$ mkdir HelloWorld
$ cd HelloWorld

# アプリケーションを作成する(profileは適宜変更)
$ eb init --profile eb-demo

eb init を実行して以下の設定値で作成

  • リージョン     :us-west-2
  • アプリケーション名 :(デフォルト)
  • ランタイム      :PHP
  • PHPバージョン    :7.2
  • キーペア      :eb-demo

設定値が完了すると、HelloWorldディレクトリの中に以下のファイル、ディレクトリが作成される。

./
├── .gitignore
└── .elasticbeanstalk
    └── config.yml

HelloWorld/.elasticbeanstalk/config.ymlの中身には、アプリケーションの情報(アプリケーション名、キーペア名、ランタイムバージョン、リージョン)が設定されている。

branch-defaults:
  default:
    environment: null
    group_suffix: null
global:
  application_name: HelloWorld
  branch: null
  default_ec2_keyname: aws-eb
  default_platform: PHP 7.2
  default_region: us-west-2
  include_git_submodules: true
  instance_profile: null
  platform_name: null
  platform_version: null
  profile: null
  repository: null
  sc: null
  workspace_type: Application

環境の作成

環境を作成する。 コマンド実行後、しばらくすると環境が立ち上がる。 デフォルトでは、オートスケーリンググループ、EC2、ELB、セキュリティグループが作成される。

# ebの作成
# 静的なページを返すだけの設定で作成する
$ echo "Hello World" > index.html

# dev-env という名前で環境を作成する
$ eb create dev-env

ステータスの確認

環境のステータスはeb statusで確認できる。

$ eb status

Environment details for: dev-env
  Application name: HelloWorld
  Region: us-west-2
  Deployed Version: app-200503_044529
  Environment ID: e-snihahmjcb
  Platform: arn:aws:elasticbeanstalk:us-west-2::platform/PHP 7.2 running on 64bit Amazon Linux 2/3.0.0
  Tier: WebServer-Standard-1.0
  CNAME: dev-env.eba-ijnmvfx5.us-west-2.elasticbeanstalk.com
  Updated: 2020-05-02 19:47:39.052000+00:00
  Status: Ready
  Health: Green

ヘルスの確認

eb healthで環境のヘルスチェックもできる。

スクリーンショット 2020-05-03 4.49.18.png

ログの確認

eb logsで環境ないのログを確認できる。

$ eb logs

============= i-087965f9152f39898 ==============
----------------------------------------
/var/log/eb-engine.log
----------------------------------------
2020/05/02 19:47:24.930642 [INFO] Running command /bin/sh -c systemctl stop php-fpm.service
2020/05/02 19:47:24.935046 [INFO] Executing instruction: FlipApplication
2020/05/02 19:47:24.936947 [INFO] create soft link from /var/app/current/ to /var/www/html
2020/05/02 19:47:24.936981 [INFO] Executing instruction: start X-Ray
2020/05/02 19:47:24.936986 [INFO] X-Ray is not enabled.
2020/05/02 19:47:24.936990 [INFO] Executing instruction: start php-fpm
2020/05/02 19:47:24.936997 [INFO] No plugin in cfn metadata.
2020/05/02 19:47:24.937076 [INFO] Running command /bin/sh -c systemctl show -p PartOf php-fpm.service
2020/05/02 19:47:24.942972 [INFO] Running command /bin/sh -c systemctl daemon-reload
2020/05/02 19:47:25.012288 [INFO] Running command /bin/sh -c systemctl reset-failed
2020/05/02 19:47:25.016430 [INFO] Running command /bin/sh -c systemctl show -p PartOf php-fpm.service
2020/05/02 19:47:25.021426 [INFO] Running command /bin/sh -c systemctl is-active php-fpm.service
2020/05/02 19:47:25.024860 [INFO] Running command /bin/sh -c systemctl start php-fpm.service
2020/05/02 19:47:26.366513 [INFO] Executing instruction: start proxy with new configuration
2020/05/02 19:47:26.366550 [INFO] Running command /bin/sh -c /usr/sbin/nginx -t -c /var/proxy/staging/nginx/nginx.conf
2020/05/02 19:47:26.398655 [ERROR] nginx: the configuration file /var/proxy/staging/nginx/nginx.conf syntax is ok
nginx: configuration file /var/proxy/staging/nginx/nginx.conf test is successful

・
・
・

----------------------------------------
/var/log/nginx/access.log
----------------------------------------
172.31.36.8 - - [02/May/2020:19:50:05 +0000] "GET / HTTP/1.1" 200 12 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36" "180.56.80.253"
172.31.36.8 - - [02/May/2020:19:50:06 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://dev-env.eba-ijnmvfx5.us-west-2.elasticbeanstalk.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36" "180.56.80.253"


----------------------------------------
/var/log/nginx/error.log
----------------------------------------
2020/05/02 19:50:06 [error] 3758#0: *26 open() "/var/www/html/favicon.ico" failed (2: No such file or directory), client: 172.31.36.8, server: , request: "GET /favicon.ico HTTP/1.1", host: "dev-env.eba-ijnmvfx5.us-west-2.elasticbeanstalk.com", referrer: "http://dev-env.eba-ijnmvfx5.us-west-2.elasticbeanstalk.com/"

ファイルを更新してデプロイしてみる

index.htmlを内容を書き換えてデプロイしてみる。 eb deployで環境にデプロイできる。

ファイルを書き換える
$ echo "Hello World 2" > index.html

# デフォルトだと、1回にすべてのインスタンスに対してデプロイを実行する(All at once)
# All at once だとダウンタイムが発生するので注意
$ eb deploy

ちなみに作成、デプロイ時に使用したソースコードと設定ファイルは、s3に保管される。 バケット名はデフォルトだと、elasticbeanstalk-リージョン名-ランダムな文字列となる。

環境変数を加えてみる

eb setenv key=name環境変数を設定できる。

$ eb setenv ENABLE_COOL_NEW_FEATURE=true

アプリケーションを終了する

eb terminateで環境を終了できる。(環境に関する全てのサービスを削除する)

# このコマンドで終了できる
# コマンドを実行後、環境名を入力してエンター(今回の例では dev-env)
$ eb terminate

設定ファイルの生成とカスタマイズ

起動しているアプリケーションの、設定ファイルを保存することができる。

$ eb config save dev-env --cfg prod

コマンドを実行すると、HelloWorld/.elasticbeanstalk/saved_configs/prod.cfg.ymlが作成される。 中身は、ELBの設定やオートスケーリングの設定等。

EnvironmentConfigurationMetadata:
  Description: Configuration created from the EB CLI using "eb config save".
  DateCreated: '1588449138000'
  DateModified: '1588449138000'
Platform:
  PlatformArn: arn:aws:elasticbeanstalk:us-west-2::platform/PHP 7.2 running on 64bit Amazon Linux 2/3.0.0
OptionSettings:
  aws:elasticbeanstalk:command:
    BatchSize: '30'
    BatchSizeType: Percentage
  aws:elb:policies:
    ConnectionDrainingEnabled: true
  aws:elb:loadbalancer:
    CrossZone: true
  aws:elasticbeanstalk:environment:
    ServiceRole: aws-elasticbeanstalk-service-role
  aws:elasticbeanstalk:healthreporting:system:
    SystemType: enhanced
  aws:autoscaling:launchconfiguration:
    IamInstanceProfile: aws-elasticbeanstalk-ec2-role
    EC2KeyName: eb-demo
  aws:autoscaling:updatepolicy:rollingupdate:
    RollingUpdateType: Health
    RollingUpdateEnabled: true
EnvironmentTier:
  Type: Standard
  Name: WebServer
AWSConfigurationTemplateVersion: 1.1.0.0

設定ファイルから環境を起動してみる

eb putで設定ファイルから環境を起動できる。

# prod はファイル名の.cfgの前の部分
$ eb config put prod

設定ファイルに設定を追加する

HelloWorld/.elasticbeanstalk/saved_configs/prod.cfg.ymlにオートスケーリングの設定を追加する。

# OptionSettingsの下に追加
  AWSEBAutoScalingScaleUpPolicy.aws:autoscaling:trigger:
    UpperBreachScaleIncrement: '2'
  AWSEBCloudwatchAlarmLow.aws:autoscaling:trigger:
    LowerThreshold: '20'
    MeasureName: CPUUtilization
    Unit: Percent
  AWSEBCloudwatchAlarmHigh.aws:autoscaling:trigger:
    UpperThreshold: '50'

設定を反映して起動してみる。 デプロイが完了するとオートスケーリンググループのスケーリングポリシーが更新される。

$ eb config put prod
$ eb config dev-env --cfg prod

デプロイのタイミングで任意の操作を実行

アプリケーションデプロイのタイミングで、インスタンス上で任意のコマンドを実行する仕組みがある。Beanstalkのアプリケーションバンドル直下に.ebextensionsというディレクトリを作り、その中に拡張子.configが付いた設定ファイルを配置することで制御できる。 (内部では、cloudformationを実行して各種設定を反映している。、AWSコンソール画面上からも確認できる。)

今回の例では、こんなかんじ。

HelloWorld
├── .ebextensions
├── .elasticbeanstalk
│   ├── config.yml
│   └── saved_configs
│       ├── prod.cfg.yml
│       └── initial-configuration.cfg.yml
├── index.html
└── .gitignore

オートスケーリングの設定を制御してみる

autoscaling.configという名前で、オートスケーリンググループのconfigを作成する。

設定は以下の通り。 option_settingsにオートスケーリングの設定等追加できる。

# docs 1 https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options.html#configuration-options-precedence
# docs 2 https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/ebextensions-optionsettings.html

option_settings:
  # docs 3 https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options-general.html#command-options-general-autoscalingasg
  aws:autoscaling:asg:
    # 最小 1台
    MinSize: 1
    # 最大 10台
    MaxSize: 10
    # スケーリングのクールダウン 240秒
    Cooldown: 240

  # docs 4 https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options-general.html#command-options-general-autoscalinglaunchconfiguration
  aws:autoscaling:launchconfiguration:
    InstanceType: t2.micro
    # ImageId: ...
    # SecurityGroups: ...
    # InstanceIamProfile: ....

以下のコマンドでデプロイする。

# 初回はこちら
$ eb create prod-env --cfg prod

# すでにアプリケーションが起動中ならこちら
$ eb deploy

DynamoDBの作成と接続情報をデプロイ時に設定してみる

dynamodb.configという名前で、DB作成から接続情報を設定するconfigを作成する。

設定は以下の通り。 ResourcesOutputsを使用して他のサービスの作成ができる。(CloudFormationと同じ構文) Resourcesで作成したリソースの情報をoptions_settingsで設定している。

Resources:
  DynamoDBTable:
    Type: AWS::DynamoDB::Table
    Properties:
      KeySchema:
         HashKeyElement:
           AttributeName: id
           AttributeType: S
      # create a table with the least available rd and wr throughput
      ProvisionedThroughput:
         ReadCapacityUnits: 1
         WriteCapacityUnits: 1

  NotificationTopic:
    Type: AWS::SNS::Topic

Outputs:
  NotificationTopicArn:
    Description: Notification topic ARN
    Value: { "Ref" : "NotificationTopic" }

option_settings:
  aws:elasticbeanstalk:application:environment:
    # デプロイ時に設定を環境変数に登録
    NOTIFICATION_TOPIC: '`{"Ref" : "NotificationTopic"}`'
    DYNAMODB_TABLE: '`{"Ref" : "DynamoDBTable"}`'
    AWS_REGION: '`{"Ref" : "AWS::Region"}`'

設定を反映して起動してみる。

$ eb deploy

デプロイ後、サーバー内で以下のコマンドを実行すると環境変数に接続情報が設定されていることが確認できる。

$ /opt/elasticbeanstalk/bin/get-config environment -k NOTIFICATION_TOPIC

$ /opt/elasticbeanstalk/bin/get-config environment -k DYNAMODB_TABLE

$ /opt/elasticbeanstalk/bin/get-config optionsettings

デプロイ時にコマンドを実行してみる

commands.configという名前で、configを作成する。 commandscontainer_commandsの違いとしては、前者はアプリケーションのデプロイ前なのでアプリケーション内のファイルに変更を加えることができない。 後者は、アプリケーションがデプロイされたあとなのでアプリケーション内のファイルに変更を加えることができる。

# docs 1 https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html#linux-commands 
commands:
  create_hello_world_file:
    command: touch hello-world.txt
    cwd: /home/ec2-user

# docs 2 https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html#linux-container-commands
container_commands:
  modify_index_html:
    # アプリケーション内のファイルを触れるので、中身を書き換えてみる
    command: 'echo " - modified" >> index.html'

  database_migration:
    command: 'echo "TODO: database migration"'
    # 以下の設定で他のcontainer_commandsよりも優先しての実行ができる
    leader_only: true

設定を反映して起動してみる。

$ eb deploy

動作を確認してみる。

$ curl http://prod-env.hogehogefugafuga.us-west-2.elasticbeanstalk.com
Hello World
 - modified

デプロイポリシーについて

アップデート方法は以下の5通りある。(公式サイト - Elastic Beanstalk 環境へのアプリケーションのデプロイ

文章だけだとわかりづらいので図説してみる。

All at once

デフォルト設定ではこの方式をとる。 起動中のアプリケーションに一括でデプロイを行う。 デプロイ完了までの時間が短いが、ダウンタイムが発生する。 スクリーンショット 2020-05-05 19.30.13.png

デプロイに失敗した場合、自動ロールバックにより古いアプリケーションを再度デプロイする。 スクリーンショット 2020-05-05 19.30.20.png

ローリング(Rolling)

バッチサイズ分のインスタンスをELBからデタッチして、デプロイを行う。 デプロイが完了後、アタッチする。これを全てのインスタンスが新しいアプリケーションになるまで繰り返す。 注意点としては、デタッチするインスタンス分のリソースが環境から減ることになるので、一時的にインスタンス一台あたりの負荷が上昇する点。 また、古いアプリケーションと新しいアプリケーションが混在する時間が存在する。 スクリーンショット 2020-05-04 20.02.05.png

デプロイに失敗した場合、初回バッチの場合は、自動ロールバックにより古いアプリケーションを再度デプロイするだけでロールバックが完了するが、 初回バッチ以外で失敗した場合、した図のようにバージョンが混在した状態になってしまうので手動による再デプロイが必要になる。 スクリーンショット 2020-05-04 20.02.17.png

追加バッチとローリング(Rolling with additional batch)

ローリングと違い、バッチサイズ分のインスタンスを追加するので、リクエストを受け付けるインスタンスの数は変わらない。 バッチサイズ分のインスタンスを追加して、新しいアプリケーションをデプロイする。デプロイ成功後、ELBから新しいアプリケーションをELBにアタッチして、デプロイしたインスタンス分、古いアプリケーションが起動しているインスタンスをデタッチ、終了する。 注意点としては、ローリングと同じく古いアプリケーションと新しいアプリケーションが混在する時間が存在する点。

スクリーンショット 2020-05-05 19.43.01.png

デプロイに失敗した場合、ローリングの場合と同じく初回バッチの場合は、自動ロールバックにより古いアプリケーションを再度デプロイするだけでロールバックが完了するが、初回バッチ以外で失敗した場合、した図のようにバージョンが混在した状態になってしまうので手動による再デプロイが必要になる。 スクリーンショット 2020-05-05 19.43.09.png

変更不可(Immutable)

新しいアプリケーションをデプロイするための Auto Scaling Group を作成して、動作の確認が取れたインスタンスから古いアプリケーションと同じ Auto Scaling Group に追加する。全てのインスタンスの追加完了後、デプロイ用に作成した Auto Scaling Group の削除と古いアプリケーションが起動しているインスタンスの終了を行う。 注意点としては、古いアプリケーションと新しいアプリケーションが混在する時間が存在する点。

スクリーンショット 2020-05-05 19.54.29.png

デプロイに失敗した場合、新しいアプリケーションが起動しているインスタンスの終了とデプロイ用に作成した Auto Scaling Group の削除を行う。

スクリーンショット 2020-05-05 19.53.41.png

ブルーグリーンデプロイ(Blue/Green)

Route53を用意すれば、ブルーグリーンデプロイもサポートしている。(もはやElastic Beanstalkの機能では無い気がするけど) トラフィックの重み付け等、設定が可能。(カナリアデプロイが利用できる)

下の図はカナリアデプロイの場合

スクリーンショット 2020-05-05 20.00.40.png

デプロイに失敗した場合、環境の破棄とトラフィックの重み付けの変更を行うことでロールバックが可能。

スクリーンショット 2020-05-05 20.00.46.png

解決できなかったこと

東京リージョン(とかオハイオとか)では以下のコマンド実行時にエラーが発生した。AWS何もわからない。

$ eb config put prod
ERROR: NotFoundError - Elastic Beanstalk can't find a platform version that matches "PHP 7.4 running on 64bit Amazon Linux 2".

Terraformのstateをリモートで管理する

この記事はQiitaの記事をエクスポートしたものです。内容が古くなっている可能性があります。

CloudFormationでは、スタックの状態(state)はAWS側で管理されているが、Terraformの場合、デフォルトでは管理されません。

状態管理を設定せずにterraform applyコマンドを実行すると、ローカル端末に状態管理ファイルが生成されます。(terraform.tfstateという名で作成されます。 )

状態管理のメリット

メリットは以下の通りです。

  • リモートに状態を保存と状態のロックができるため、状態の破損を防ぐことができる
  • リモートに状態を保存することで、複数人で同じ状態を共有できる

ローカルで管理していたら、PCが壊れてインフラ管理できなくなったり、状態管理ファイルを紛失したりするリスクがなくなります。

実装してみる

状態管理用のスタックを用意する

Terraformでは、状態管理にS3とDynamoDBを使用できます。

S3に状態管理ファイルを保管して、DynamoDBでコンフリクトを防ぐ構成となっています。

また、状態管理用スタックについて公式では以下のように管理することを推奨しているので、別管理した方が良さそうです。

以下引用 :

Terraform is an administrative tool that manages your infrastructure, and so ideally the infrastructure that is used by Terraform should exist outside of the infrastructure that Terraform manages.

引用元 : 公式サイト - Multi-account AWS Architecture

ここでは、面倒なので以下のスタックを実行して構築します。

テーブルはLockIDという名前のプライマリーキーを持つ必要があるのでその設定だけは入れましょう。

/**
 * state管理用のS3 & DynamoDB 作成用スタック
 * stack ディレクトリ内のtf実行前にこちらを先に実行する
*/

terraform {
  required_version = "0.12.5"
}

provider "aws" {
  region = "ap-northeast-1"
}

resource "aws_s3_bucket" "terraform_state" {
  // ダメだったら別の名前にする
  bucket = "terraform-state-kento75"
  versioning {
    enabled = true
  }
}

resource "aws_dynamodb_table" "terraform_state_lock" {
  name           = "terraform_state_lock"
  read_capacity  = 1
  write_capacity = 1
  hash_key       = "LockID"

  attribute {
   // 必須
    name = "LockID"
    type = "S"
  }
}

簡単なスタックを作成する

backendを設定することで先ほど作成したS3とDynamoDBによる状態管理を実装できます。

terraform {
  required_version = "0.12.5"

  // state lock 設定
  // state管理用 S3, DynamoDB は別管理
  backend "s3" {
    bucket         = "terraform-state-kento75" # state管理用バケット
    region         = "ap-northeast-1"
    key            = "terraform.tfstate"
    encrypt        = true
    dynamodb_table = "terraform_state_lock"
  }
}

その他に実装したスタックはこちら を参照

terraform initコマンドを実行すると、backendがS3になっていることが確認できます。

$ terraform init

Initializing the backend...

Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

スタックを作成してみます。

$ terraform apply

実行に成功すると、S3バケット内にterraform.tfstateが作成されます。 スクリーンショット 2020-02-29 19.40.26.png

DynamoDBにも追加されます。

スクリーンショット 2020-02-29 19.42.52.png

本当にロックされるのか試す

別セッションで同時にterraform planを実行した時にどのような挙動をするのかを確認してみます。

先に実行した方は正常に動作しましたが、後から実行した方は以下のようにエラーとなります。

$ terraform plan

Error: Error locking state: Error acquiring the state lock: ConditionalCheckFailedException: The conditional request failed
        status code: 400, request id: 86SS1900A1DIKG5ORN24PRLDG3VV4KQNSO5AEMVJF66Q9ASUAAJG
Lock Info:
  ID:        aff55b1a-0cbf-d759-7e3b-e778a391925a
  Path:      terraform-state-kento75/terraform.tfstate
  Operation: OperationTypePlan
  Who:       kento@KentonoMacBook-Pro.local
  Version:   0.12.5
  Created:   2020-02-29 10:46:17.720234 +0000 UTC
  Info:      


Terraform acquires a state lock to protect the state from being written
by multiple users at the same time. Please resolve the issue above and try
again. For most commands, you can disable locking with the "-lock=false"
flag, but this is not recommended.

まとめ

Terraformのbackend周りの実装を行うことで、複数人での運用時の事故を防ぐことができるようになるので、なるべくなら実装した方が良いと思います。

【備忘録】ポモドーロ・テクニックについて

ポモドーロ・テクニックとは


25分の作業+5分の休憩を1ポモドーロとして、4ポモドーロ毎に30分の休憩をとる勉強法のこと。

ポモドーロに合わせて、佑を細かく区切ることで作業時間の見積もりがしやすくなる。



4つのポイント



時間管理を徹底する


タイマーなどを利用して、作業時間を順守する。



休憩時間のルールを決める


5分の休憩中に作業のことは考えない。

また、メールやSNSのチェックも避ける。



やらないことをリスト化する


例えば、SNS閲覧、テレビ視聴、優先度の低いタスクなど。



タスクをリスト化する


タスクはなるべく細かく分割して、リスト化する。



使用するツール


タイマー


時間を知らせるために使う。(スマホアプリなど)



イシューシート


タスクをすべて書き込んだ管理用のシート。

タスク毎に期限、優先度、見積もり時間(何ポモドーロ)をメモする。



TODOシート


その日に行うタスクを管理するシート。

見積もり時間と優先順位をリストアップする。



レコーディングシート


1日の終わりに、タスクごとの見積もり時間と実測時間を記録する。

見積もりが適正かを判断するための振り返りシート。

集中力を高めるには

集中と作業効率を高めるために、書籍等で調べたことの備忘録。

刺激を集中スイッチにする

集中力を高めるために嗅覚刺激を使うことは古くから行われていたらしい。

最近は持ち運びができるアロマの道具がある。匂いの選び方に関しては個人差はある。

以下3種類用意する。


  • 一気に集中したいときのもの
  • 落ち着きたいときのもの
  • スイッチを入れ替えたいときのもの



ガムを噛んで、作業効率を回復させる

作業を長時間続けていると、だんだんと集中が続かなくなり、作業効率が落ちる。

作業時は、ワーキングメモリの機能が低下するらしい。

噛むことがワーキングメモリの回復に奴立つと言われている。

また、噛むことでストレス、疲労、不注意などが低下すると言われている。



なぜやるのかを事前に考え切る

取り組むべき仕事の目的を自分の言葉で腹に落としてから始めることで、ゴールがはっきりとし、迷いが消える。

また、やらないこともはっきりするので、余計な工数がかからない。



後悔と不安は考えない

後悔と不安は集中力を阻害するもの。

不安なことがあっても気にしない。周りの環境が心理的安全性が担保されているとなお良い。



やることとやらないことをはっきり

タスクに優先順位をつける。

タスクの時間を決める。後回しのタスクはTODOリストに入れて今は忘れておく。

小さな意思決定は、機械的に判断できるようにする。(無駄な思考を使わない)



目標設定は小さな目標を立てる

なるべく、定量化できる。自身の能力の115%ほどの目標を立てる。



ポモドーロテクニックを使う

25分の作業+5分の休憩を1ポモドーロとして、4ポモドーロごとに30分間の休憩をとる。

実行したことを振り返る。

JavaSE8以降のインタフェースについて

JavaSE7以前のインタフェースとJavaSE8以降のインタフェースの使用がだいぶ違うのでメモ。


JavaSE7までのインタフェースの特徴

  • 複数個の抽象メソッドが設定可能
  • 継承で利用されるのが一般的
  • 定数はpublic static 扱いになる
  • オブジェクトになれない



JavaSE8のインタフェースの特徴

  • 複数個の静的メソッドが設定可能
  • 複数個のdefaultメソッドが設定可能
  • 複数個の抽象メソッドが設定可能
  • new 演算子によるオブジェクト生成が可能



関数型インタフェースの特徴

  • 抽象メソッド数は1個のみ
  • インタフェースの名称と抽象メソッドの名称が1対1で対応している
  • シグニチャ(戻り値と引数)によって処理内容の概要が決定される
  • 関数型インタフェースのオブジェクト化を抽象メソッドを実装する方法で実現可能

関数型インタフェースはJavaSE8インタフェースの形態の一つ。\ 関数型インタフェースと内部の抽象メソッドは1対1で対応している。



インタフェースまとめ

SE7インタフェース SE8インタフェース SE8関数型インタフェース
抽象メソッド 複数設定可 複数設定可 1個のみ
静的メソッド 設定不可 複数設定可 複数設定可
defaultメソッド 設定不可 複数設定可 複数設定可
オブジェクト生成 生成不可 生成可 生成可



SE8のインタフェース実装例

package sample;

public interface SampleInterface {

  static void printStatic() {
    System.out.println("静的メソッド");
  }

  default void printDefault() {
    System.out.println("Defaultメソッド");
  }

  // 抽象メソッド
  abstract void printAbstract(String inStr);
}


SE8のインタフェースは、以下のようにインスタンス化できる。

package sample;

public class Sample {
  public static void main(String[] args) {

    // 静的メソッドの実行
    SampleInterface.printStatic();

    // 抽象メソッドの実装とインスタンス化(匿名クラスを new してる)
    // → インターフェースを継承した名前の無いクラスを new
    SampleInterface si = new SampleInterface() {
      @Override
      public void printAbstract(String inStr) {
        System.out.println(inStr);
      }
    };

    // オーバーライドしたメソッドの実行
    si.printAbstract("オーバーライドされた抽象メソッド");

    // defaultメソッドの実行
    si.printDefault();
  }
}



関数型インタフェース

package sample2;

@FunctionalInterface
public interface CheckWonLottery {

    // 抽象メソッド
    abstract public String check(Integer inNumber, Integer winNumber);
}
package sample2;

public class Sample {

    public static void main(String[] args) {

        // n1 : くじの番号、n2 : 当選番号
        // 匿名クラスの作成
        CheckWonLottery cw = new CheckWonLottery() {

            @Override
            public String check(Integer n1, Integer n2) {
                if (n1 == n2) {
                    return "的中";
                } else {
                    return "ハズレ";
                }
            }
        };

        String result = cw.check(105555, 1055559);
        System.out.println(result);
    }
}



関数型インタフェースの一覧(よく使うものだけ)

関数型インタフェース名 抽象メソッド構文 概要
Function<T, R> R apply(T t) 引数:T
戻り値:R
Consumer<T> void accept(T t) 引数:T
戻り値:なし
Predicate<T> boolean test(T t) 引数:T
戻り値:boolean
Supplier<T> T get() 引数:なし
戻り値:T
UnaryOperator<T> T apply(T t) 引数:T
戻り値:T
BinaryOperator<T> T apply(T t1, T t2) 引数:T(2個)
戻り値:T
BiFunction<T, U, R> R apply(T t, U u) 引数:T、U
戻り値:R


既に定義されているものは後で定義してはならない。 というより、たくさんあるので使用用途に当てはまるものがあることが多く、自分で作ることはない。

Java API ドキュメント - 関数型インタフェースの項 - 参照