このエントリーをはてなブックマークに追加

はじめに

今回は小ネタです。 Application Auto Scaling のスケジュールされたアクションの例 - Application Auto Scaling などでは aws-cli による Appliction Auco Scaling のスケジュール方法が示されていました。
しかし、CloudFormation によるものが Web 上でうまく見つけられなかったのでメモ程度に記しておきます。

要件

ECS サービス fuga/hoge において

  • 毎月、1日の3時から2日の18時まで ECS タスクが 5 つ稼働している
  • 上記の時刻以外では
    • ECS タスク数の制限は 最小: 0, 最大: 5 となる
    • 上記制限の間で任意の CloudWatch Alarm のメトリクスを指標としてスケールイン・スケールアウトする

CloudFormation テンプレート

AWSTemplateFormatVersion: "2010-09-09"
Description: 'Scheduled Auto Scaling'
Resources:
  ScalableTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    Properties:
      MaxCapacity: 5
      MinCapacity: 0
      ResourceId: service/fuga/hoge
      RoleARN: arn:aws:iam::XXXXXXXXXXXX:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService
      ScalableDimension: ecs:service:DesiredCount
      ServiceNamespace: ecs
      ScheduledActions:
        - ScheduledActionName: "start-monthly"
          ScalableTargetAction:
            MaxCapacity: 5
            MinCapacity: 5
          Schedule: "cron(0 3 1 * ? *)"
          Timezone: "Asia/Tokyo"
        - ScheduledActionName: "end-monthly"
          ScalableTargetAction:
            MaxCapacity: 5
            MinCapacity: 0
          Schedule: "cron(0 18 2 * ? *)"
          Timezone: "Asia/Tokyo"
  ScalingPolicy:
    Type : AWS::ApplicationAutoScaling::ScalingPolicy
    Properties:
      PolicyName: ScalePolicy
      PolicyType: StepScaling
      ScalingTargetId: !Ref ScalableTarget
      StepScalingPolicyConfiguration:
        AdjustmentType: ExactCapacity
        Cooldown: 300
        MetricAggregationType: Average
        StepAdjustments:
          - ScalingAdjustment: 0
            MetricIntervalLowerBound: 0
            MetricIntervalUpperBound: 1
          - ScalingAdjustment: 1
            MetricIntervalLowerBound: 1
            MetricIntervalUpperBound: 100
          - ScalingAdjustment: 2
            MetricIntervalLowerBound: 100
            MetricIntervalUpperBound: 200
          - ScalingAdjustment: 3
            MetricIntervalLowerBound: 200
            MetricIntervalUpperBound: 300
          - ScalingAdjustment: 4
            MetricIntervalLowerBound: 300
            MetricIntervalUpperBound: 400
          - ScalingAdjustment: 5
            MetricIntervalLowerBound: 400

(余談)Timezone について

Timezone にあるとおり、時刻の指定時にタイムゾーンを記すことができます。
スケジュールされた ECS タスク(スケジュールされたオートスケールではなく)だと スケジュールに従って実行する Amazon EventBridge ルールの作成 - Amazon EventBridge にあるようにタイムゾーンの指定は無く UTC 一択となっていますので、この設定属性は大変助かります。 なお、タイムゾーンの書式については

Valid values are the canonical names of the IANA time zones supported by Joda-Time (such as Etc/GMT+9 or Pacific/Tahiti). For more information, see https://www.joda.org/joda-time/timezones.html.

PutScheduledAction - Application Auto Scaling

とあるとおり、 Joda-Time – Java date and time API - Time Zones に記載の IANA のタイムゾーンID を使うこととなっています。
CloudFormation のドキュメントには直接記載がなく、関連 aws-cli の API リファレンスからたどることができました。AWS のドキュメントは複合的に見ていかないと要件が判然としないこともあるので慣れが必要ですね。

確認方法

スケーリングポリシー ScalingPolicy は Web コンソールより当該 ECS サービスにアクセスすることで設定を確認することができます。
しかし、スケーラブルターゲットのスケジュールされたアクション ScalableTarget > ScheduledActions については Web コンソールから確認ができませんでした。
よって、以下のような aws-cli コマンドでリソースの状況を確認しました。
所望の状態になっているようです。大丈夫そうですね!

$ aws application-autoscaling describe-scheduled-actions --service-namespace ecs --resource-id service/fuga/hoge
{
    "ScheduledActions": [
        {
            "ScheduledActionName": "start-monthly",
            "ScheduledActionARN": "arn:aws:autoscaling:ap-northeast-1:XXXXXXXXXXXX:scheduledAction:YYYYYYYYYYYYYYYYYYYYY:resource/ecs/service/fuga/hoge:scheduledActionName/start-monthly",
            "ServiceNamespace": "ecs",
            "Schedule": "cron(0 3 1 * ? *)",
            "Timezone": "Asia/Tokyo",
            "ResourceId": "service/fuga/hoge",
            "ScalableDimension": "ecs:service:DesiredCount",
            "ScalableTargetAction": {
                "MinCapacity": 5,
                "MaxCapacity": 5
            },
            "CreationTime": "2022-07-26T07:23:27.962000+00:00"
        },
        {
            "ScheduledActionName": "end-monthly",
            "ScheduledActionARN": "arn:aws:autoscaling:ap-northeast-1:XXXXXXXXXXXX:scheduledAction:YYYYYYYYYYYYYYYYYYYYY:resource/ecs/service/fuga/hoge:scheduledActionName/end-monthly",
            "ServiceNamespace": "ecs",
            "Schedule": "cron(0 18 2 * ? *)",
            "Timezone": "Asia/Tokyo",
            "ResourceId": "service/fuga/hoge",
            "ScalableDimension": "ecs:service:DesiredCount",
            "ScalableTargetAction": {
                "MinCapacity": 0,
                "MaxCapacity": 5
            },
            "CreationTime": "2022-07-26T07:23:27.962000+00:00"
        }
    ]
}

まとめ

CloudFormation で Appliction Auco Scaling のスケジューリングをしてみました。Appliction Auco Scaling は ECS 以外にも Aurora などの各種サービスに対応しています。コストと可用性の最適化にはとてもよい選択肢だと思うので上手に使いこなしていけるようになりたいですね。


このエントリーをはてなブックマークに追加

こんにちは!伊神です!

今回はテスト駆動開発(Test-Driven Development: TDD)について記事にします!

テストを優先しながら実装を進める開発手法で、早期にバグを発見することができたり必要な機能を無駄なく開発することができます!

目次

テスト駆動開発(TDD)とは?

テスト駆動開発(TDD)とは、テストファーストな開発手法です。

はじめにテストコードから記述してき、次にそのテストを通過するようなプログラムを作り、最後にテストコードが成功する前提でリファクタリングを進めるということを繰り返し行います。

開発サイクル

テスト駆動開発のサイクルを大きく分けると3つのステップになります。

  • Red:テストの失敗
  • Green:テストの成功
  • Refactor:リファクタリングの実施

という順序で進められます。

※Red, Greenはテストツールによるテスト失敗が赤色、成功が緑色の表示になることからこう呼ばれています。

具体的なサイクルは下記になります。

  1. 作成したいコード目標を考える
  2. 目標を示すテストを書く
  3. 2.で書いたテストを実行して失敗させる(Red)
  4. 目的のコードを書く
  5. 2で書いたテストを通す(Green)
  6. テストが成功するのは前提条件でリファクタリングを行う(Refactor)

tdd

(例)FizzBuzz問題を解く際の書き方

目標を考えてコードを作成するためそれぞれの目標を細分化し、To-Doリストを作成する。

目標の決め手は下記2つ。

  1. 最も重要なものを最初に実施
  2. テスト容易性が高いものから実施

まずはTo-Doリストにしてみると下記のようになる。

要件

- 1〜100の数字を入力とする
- 数字が3の倍数なら「Fizz」と表示させる
- 数字が5の倍数なら「Buzz」と表示させる
- 数字が3と5の両方の倍数なら「FizzBuzz」と表示させる

ここから細分化しテスト容易性が低いものを省くためにTo-Doとして一覧で書き出してみる。

細分化の流れは省略しますが最終的には下記のようにします。

テスト容易性:高 重要度:高
- [ ] 数字を文字列に変換する
  - [ ] 1を渡すと文字列1を返す
  - [ ] 2を渡すと文字列2を返す

- [ ] 3の倍数のときは数の代わりに「Fizz」に変換する
  - [ ] 3を渡すと文字列"Fizz"を返す

- [ ] 5の倍数のときは数の代わりに「Buzz」に変換する
  - [ ] 5を渡すと文字列"Buzz"を返す

- [ ] 3と5両方の倍数のときは数の代わりに「FizzBuzz」に変換する

テスト容易性:低 重要度:低
- [ ] 1から100まで
- [ ] 表示する

こうすることによってテストコードを書きやすくする。

ここで「数字を文字列に変換するテスト」を2回行っているのは初めてのテストのため。

このことを三角測量といい想定と違う動きをしないか確認しておきます。

テストの書き方

基本は、「準備」「実行」「検証」のコードを書きます。

今回は、開発サイクルの3つの部分と最初のステップのみを記載します。

テストの失敗(Red)

文字列に変換せずにわざと失敗させる

src/FizzBuzz.php

public function _1を渡すと1を返す($num)
{
    return $num;
}

test/FizzBuzzTest.php

// 準備
$fizzbuzz = new FizzBuzz();
// 実行
// 検証
$this->assertSame('1', $fizzbuzz->_1を渡すと1を返す(1));

テストの成功(Green)

文字列に変換する処理を加えテストを成功させる

src/FizzBuzz.php

public function _1を渡すと文字列1を返す(int $num)
{
    $numStr = (string)$num;
    return $numStr;
}

test/FizzBuzzTest.php

// 準備
$fizzbuzz = new FizzBuzz();
// 実行
// 検証
$this->assertSame('1', $fizzbuzz->_1を渡すと文字列1を返す(1));

リファクタリング(Refactor)

src/FizzBuzz.php の関数名を「_1を渡すと文字列1を返す」から「convert」にリファクタリングし変更する。

src/FizzBuzz.php

public function convert(int $num)
{
     $numStr = (string)$num;
     return $numStr;
}

test/FizzBuzzTest.php

// 準備
$fizzbuzz = new FizzBuzz();
// 実行
// 検証
$this->assertSame('1', $fizzbuzz->convert(1));

今回は、関数名のリファクタリングでしたが、コードが複雑になった際コードの綺麗さやみやすさなどテストが成功する前提でリファクタリングを行います。

TDDを実践するメリット/デメリット

メリット

  • バグや手戻りが起きにくい

上記の開発サイクルにもあったように細かくサイクルを繰り返しているためバグを早期に検知・修正を行うことができます。

  • テスト対象のクラスの変更を助けることができる

テスト対象のクラスが変更されたことをテストコードが感知して、開発者に通知してくれます。

デメリット

  • 時間がかかる

実装しながらテストを書くためコーディングに時間がかかってしまいます。

  • テストが難しい場合がある

データベースやGUIなどテストが難しい場合もあり、モックやe2eテストというものが必要になることがあります。

参考

TDD Boot Camp 2020 Online #1 基調講演/ライブコーディング

テスト駆動開発

まとめ

今回は、テスト駆動開発(TDD)をご紹介させて頂きました。

開発に時間がかかってしまったり、慣れるまでに大変だったりとデメリットもありますが早期にバグを発見することができたり必要な機能を無駄なく開発できたりとメリットがたくさんあります!

他の開発手法にビヘイビア駆動開発(BDD)や受け入れテスト駆動開発(ATDD)などがあったりするので、随時調べ学習していきたいと思います!


このエントリーをはてなブックマークに追加

はじめまして! 2022年7月に入社しました伊神 誠人と申します!

前職もWebマーケティングを行っている会社でその経験も活かしながら頑張って行きます! 配属はPHPチームで現在研修を受けています。

今回は「PHPの名前空間」についてご紹介したいと思います!

プログラム内においてクラスや関数などの名前が重複し衝突をしないようにするため名前空間を利用しグループ化する必要があります。 PHPのnamespaceやuseなどについて基本的なことから使い方まで紹介します。

  • namespace(名前空間)とは?
  • namespaceの使い方
    • 名前の重複による衝突(エラー)例
    • namespaceを利用した衝突(エラー)を回避する例
  • useとは?
  • useの使い方
    • 名前空間のインポート(取り込み)例
    • 名前空間のエイリアス(別名)をつける例
  • 補足
  • まとめ

namespace(名前空間)とは?

namespace(名前空間)とは、クラスや関数などをグループ化することをいいます。 通常は同じファイル内に同じ名前のクラスや関数を設定する事はできませんが、名前空間を使用することにって、同じ名前のクラス名や関数名を使用することができます。 なので外部のライブラリを利用してもnamespaceを利用することで衝突することを避けることができます。

namespaceの使い方

名前空間を定義する際は、「namespace 名前空間名;」とPHPファイルの一番先頭に記述します。

名前の重複による衝突(エラー)例

person.php:

<?php

function getName(){
    return "山田太郎";
}

function getName(){
    return "伊藤太郎";
}

?>

call.php

<?php
 
require_once 'person.php';

echo getName();
 
?>

実行結果

PHP Fatal error:  Cannot redeclare getName() (previously declared in ...

関数getNameを再宣言できないよとエラーが発生します。

※PhpStormだと事前に検知してくれました。

namespaceを利用した衝突(エラー)を回避する例

person.php:

<?php

namespace namespace_1; //追加

function getName(){
    return "山田太郎";
}

namespace namespace_2; //追加

function getName(){
    return "伊藤太郎";
}

?>

call.php

<?php
 
require_once 'person.php';

echo namespace_1\getName();
echo "\n";
echo namespace_2\getName();
 
?>

実行結果

山田太郎
伊藤太郎

namespaceを利用することで関数名が同じでも呼び出すことができました。 今回は、namespace_1のgetName関数とnamespace_2のgetName関数でそれぞれグループ化しています。

useとは?

useを使用することで、名前空間のインポート(取り込み)、名前空間のエイリアス(別名)をつけて使用することができます。

useの使い方

名前空間のインポート(取り込み)例

nagoya.php

<?php

namespace asia\japan\aichi\nagoya;

class pref {
    public function getPref() {
        return "nagoya";
    }
}

?>

call.php

<?php

require_once 'nagoya.php';

use asia\japan\aichi\nagoya\pref;

$sample = new pref;
echo $sample->getPref();

?>

call.phpに use asia\japan\aichi\nagoya\pref; と記述することで、prefクラスをインポートしています。

※useの箇所をuse functionにすることで関数、use constにすると定数もインポート可能です。

名前空間のエイリアス(別名)をつける例

nagoya.php

<?php

namespace asia\japan\aichi\nagoya;

class pref {
    public function getPref() {
        return "nagoya";
    }
}

?>

call.php

<?php

require_once 'nagoya.php';

use asia\japan\aichi\nagoya\pref as name;

$sample = new name;
echo $sample->getPref();

?>

useでasia\japan\aichi\nagoya\prefをname(別名)にしています。 こうすることによってパスを短縮して同名のクラスや関数をかんたんに呼び出すことができます。

参考

https://www.php.net/manual/ja/language.namespaces.importing.php

まとめ

名前空間を定義するnamespaceについてや名前空間のインポートやエイリアスをつけるuseの基本的な情報から使い方を紹介しました。

プロジェクトが大きくなると名前の衝突が起きてしまうため名前をわざと長くしたりせず名前空間をうまく利用しながら活用していきたいですね。