キャスレーコンサルティング 技術ブログ
  • HOME>
  • キャスレーコンサルティング 技術ブログ

こんにちは。
キャスレーコンサルティングのSI(システム・インテグレーション)部の杉光です。

今回はAWS Athenaの機能と特徴、性能検証結果について紹介させて頂きます。

AWS Athenaとは

Athenaは昨年度のRe:Inventで発表されたAWSの新しいインタラクティブクエリサービスです。
Google Big Queryの対抗サービスとも言われています。

現時点では、Management Console(GUI)及び、JDBC接続からクエリの実行ができ、主に以下の機能があります。

自動並列実行クエリサービス

  • 利用者がコンピューティングリソースを意識する必要がなく、サービス側が必要なリソースを計算し、
    クエリが自動的に並列実行される。大容量のデータも数秒〜数分で検索・集計できる。

S3のデータに対して標準SQLで分析可能

  • ANSI SQLに準拠したPrestoがベースとなっており、関数もPrestoでサポートされているものが利用可能です。
  • Schema-On-Readとなっていて、テーブルの作成、削除、変更、
    パーティションの追加・削除のDDLをApache Hiveで作成できる。
  • 実行したクエリに対してのみ料金が発生し、1TBあたり5USDの料金が発生する。(S3の料金は別途掛かる)

その他の特徴

利用の制限

  • クエリは検索のみ。UPDATE/INSERTなどはできない。
    Hadoop特有のORCやParquetなどのファイルフォーマットを用意する場合は、
    HiveやSparkなど他のエコシステムから作成する必要があります。
  • UDFやストアド・プロシージャが使えない。
  • データベースやテーブル単位での認可が行えない。(リソースはAWSアカウントで共有されてしまいます。)
    現状はDBやテーブル単位でのIAM制御が行えないので、S3バケットに対してのアクセス制御(IAM/バケットポリシー)となります。

コストの節約

  • パーティション化による節約
    S3に格納するデータをPrefixで区切り、パーティション化することで、データの参照を限定できる。
  • カラムナデータによる節約
    ORCやParquetのデータ・フォーマットにすることにより、特定のカラムの参照に限定することができる。

Athenaの使用例

続いて、Athenaの使用方法について簡単にご説明します。

事前作業として、Athenaを使用するIAMについては事前にAthenaを実行するために必要なポリシーを付与しています。
また、データを以下のようにS3のemrtest-dataバケットに配置しました。
気象庁の過去天気データを各都市毎に格納しています。

S3-dir

AthenaのConsole画面は以下のようになっています。
4つのタブがありますが、今回はクエリの編集や実行ができる「Query Editor」をご説明をします。
画面中央部がクエリの編集&実行部になり、その下で実行結果が確認できます。

athena_1

テーブルとパーティションの作成

DB、テーブル、パーティションの作成は「Catalog Manager」からでも行えますが、
今回はHiveクエリのDDLにて作成します。
尚、DBについてはDefaltを使用します。青枠のクエリにてweather_tsvテーブルを作成しました。
athena_2

パーティションの作成

続いて、先程作成したテーブルに対してパーティションを作成します。
該当データをS3パスにて/key=valueの形式で配置したので、動的にパーティションを認識することができます。
動的にパーティションを認識するには青枠のように”MSCK REPAIR TABLE table_name“を使います。
athena_3_msck

登録されているパーティションを確認します。
例では”city”をパーティションKeyとして認識していることが実行結果からわかると思います。
athena_4_showpartition

検索クエリの実行

作成したテーブルに対してクエリ(SELECT)を実行します。
各都市毎、年毎の平均気温を降順に表示しています。(那覇が上位を占めますね・・・)
クエリの実行結果はCSVファイルでダウロードすることもできます。

athena_5_execquery

Hive/Prestoとパフォーマンスを比較

最後にAthenaのパフォーマンスを確認します。
比較対象としてApache HiveとAthenaのベースとなっているPrestoを使用します。
PrestoはFacebook社が開発した分散クエリ実行エンジンです。

HiveとPrestoはAmazon EMRを用いて性能を計りたいと思います。
EMRはHive、Presto共にアプリーケーションサポートしているので、起動が簡単です。
リソースとバージョンは以下の通りです。

各検証サービスのバージョンとリソース

Hive:2.1.1 (Tez:0.8.4)
Presto:0.157.1(Hive catalogを使用)
EMRリソース
- Master Instance : m1.mededium
- Core Instance : m4.large

対象データ

Athenaの利用例にて少し触れましたが、気象庁が提供する過去の天気データを使用しました。
(http://www.data.jma.go.jp/gmd/risk/obsdl/index.php)
項目は、日付/日中平均気温/降水量の日合計/天気概状(昼)を選んでいます。
※ダウンロードしたデータの中には品質情報や、均質番号が含まれていますが、集計対象からは無視しています。

対象フォーマットと件数

対象データのフォーマットはTSV、Parquetとし、件数はそれぞれ、オリジナルのデータ件数、
オリジナルの10倍、オリジナルの100倍の件数を用意しました。
S3への配置方法は利用例と同様に各都市毎に約60年分のデータを置いています。

検証クエリ

まず、TSV、Parquetのデータに対して以下のようにテーブルを定義します。
HiveのDDLになりますが、PrestoとAthenaともに同じクエリを使うことができます。

TSVファイル

CREATE EXTERNAL TABLE weather_tsv(
dt STRING,
temperature FLOAT,
t_1 STRING,
t_2 STRING,
precipitation FLOAT,
p_1 STRING,
p_2 STRING,
p_3 STRING,
summary STRING,
s_1 STRING,
s_2 STRING
)
PARTITIONED BY( city STRING )
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
LINES TERMINATED BY '\n'
LOCATION 's3://emr-testdata/tsv3/';

Parquetファイル

CREATE EXTERNAL TABLE weather_parq(
dt STRING,
temperature FLOAT,
t_1 STRING,
t_2 STRING,
precipitation FLOAT,
p_1 STRING,
p_2 STRING,
p_3 STRING,
summary STRING,
s_1 STRING,
s_2 STRING
)
PARTITIONED BY( city STRING )
STORED AS PARQUET
LOCATION 's3://emr-testdata/parquet3/';

検索クエリ
計測対象のクエリは以下の2種類です。

検証クエリ①

--- 年毎に天気概状の中に"雪"が含まれている日を集計し、降順にソート
--- select tsv (ANSI)
select city,substr(dt,1,4),count(*) as cnt from weather_tsv where summary like '%雪%' group by city,substr(dt,1,4) order by cnt desc;
--- select parq (ANSI)
select city,substr(dt,1,4),count(*) as cnt from weather_parq where summary like '%雪%' group by city,substr(dt,1,4) order by cnt desc;

検証クエリ②

--- 年毎に平均気温を降順にソート
--- select tsv
select city,substr(dt,1,4),avg(temperature) as tmp from weather_tsv group by city,substr(dt,1,4) order by tmp desc limit 20;
--- select parq
select city,substr(dt,1,4),avg(temperature) as tmp from weather_parq group by city,substr(dt,1,4) order by tmp desc limit 20;

検証結果

各データ(フォーマット/件数/クエリ)に対する結果は以下の通りです。
(各々3回クエリを実行し、平均値を四捨五入したものになります)
※単位は秒です。

ファイル形式 TSV Parquet
件数 origin 10倍 100倍 origin 10倍 100倍
クエリ
Hive 30 35 44 45 58 83 23 19 43 45 59 84
Presto 7 3 11 7 34 43 7 3 11 7 34 44
Athena 1 2 3 3 4 3 2 1 5 3 3 3

今回の検証はEMRクラスタのリソースを小さめに設定しました。
HiveとPrestoは件数が増えるにつれて、処理時間が増していますが、Athenaは殆ど変わりないことが確認できました。
Prestoもインタラクティブクエリと言われているだけあり、データ容量とクラスタリソースの関係によっては、
Athenaに引けを取りませんでした。

さいごに

以上の結果からAthenaがリソースを意識することなくQueryを実行し、結果を得られることがわかりました。
今まではEMRでETLを行い、Redshiftで分析・集計を行うプロセスが多かったかと思いますが、
ETLを行うケースが減り、EMRのパフォーマンスに苦しんでいたエンジニアを助け、
分析官もより柔軟に素早くデータに触れる機会が増えることと思います。
現時点ではManagement ConsoleとJDBCからの利用に限られますが、
今後利用リージョンの拡充と他サービスとの連携が可能になり、利用用途も増えてくると思うので楽しみに待ちたいと思います。

参考文献

https://aws.amazon.com/jp/blogs/news/analyzing-data-in-s3-using-amazon-athena/
http://docs.aws.amazon.com/athena/latest/ug/what-is.html


こんにちは。
キャスレーコンサルティングSI(システム・インテグレーション)部の瀧下です。

電卓にメモリストが付いたAndroidアプリ開発で、
GUIコンポーネントの一つであるListViewを使ってみたので、説明していきたいと思います。

●メモ機能付き電卓アプリ機能
・計算処理
・メモリスト作成
・メモリストタップでメモ追加時の計算結果出力

※eclipseまたはAndroid Studio、Android SDKのインストール方法については
 ネットに多数記載されているので、割愛させていただきます。

ListViewとは?

Androidアプリ開発では、アプリのパーツを簡単に追加できる
GUIコンポーネントが数多く用意されていますが、ListViewはその中の一つです。

配列やデータをスクロール可能なリストで表示させることができ、
リスト行タップ時に、それに対応する動作を行うことができるため非常に便利です。

実装前にライフサイクルを知る

いざアプリ作成を始めようと、プロジェクト作成すると自動生成されたのが、
下のソースのonCreateメソッドです。

public class MainActivity extends Activity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 }
}

onCreate()メソッドとは何なのか?

早速、onCreate()メソッドの調査から始めました。

onCreateメソッドは、ライフサイクル(状態遷移)のフローの一つのようです。
では次にライフサイクルとは何なのか?

ライフサイクルとは、アプリケーション起動、実行、終了という
アクティビティの一連の動作をライフサイクルと呼びます。

メモ機能付き電卓アプリについて.xlsx

上の図は、アクティビティのライフサイクル図ですが、
一番上に存在していることから分かるように、起動時にはonCreateメソッドから呼び出されます。

以下で、各メソッドの呼ばれるタイミングについて説明します。

・onStart()、onResume()・・・onCreate()から順に呼ばれて、「実行中」状態に遷移する。
・onPause()・・・別アクティビティの開始時。
・onStop()・・・アクティビティが非表示になった場合。
・onDestroy()・・・アクティビティの終了時。

AndroidアプリはonCreate()がないことには始まらないのですね。。。
onCreate()メソッドに電卓機能を実装していきます。

電卓機能で実装漏れしていた箇所

アプリ作成後に実装漏れがあったことに気付きました。
演算子の連続入力です。

btnPlus.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 // 既に演算子が含まれている場合は計算処理を行う
 if (mFormulaFlg) {
 number = rs.equal(number);
 rs.calculation(number, 1);
 formula = number + "+";
 number = "";
 counter_text.setText(formula + number); // 数値をストリングに変換して表示

上のソースは、電卓の「+」タップ時に呼び出される処理です。

既に「9+7」を入力していた場合、「+」タップ時に「16+」と電卓に表示させるように
実装していますが、もし「16+」が入力されているところに「+」を入力すると、
「16++」で加算処理を行ってしまうので、アプリが落ちてしまいます。

実機動作確認でアプリが落ちるときは、アラームが鳴るので、
あの音を聞くと結構力が抜けるのは、Android開発あるあるではないでしょうか。

対応としては、substringで計算式の末尾文字を取得して条件分岐すれば、
連続で演算子が入力された場合、スルーさせたり演算子の置換等ができますね。

動作確認がタップで簡単なので、今後はランダムテスト的に行うように注意します。

メモ機能の実装

ではメモ機能の実装について説明させていただきます。

①「メモ追加」ボタンをレイアウトに追加するため、xmlファイルにボタンタグを追加します。

<Button
 android:id="@+id/btnMemo"
 style="?android:attr/buttonStyleSmall"
 android:layout_width="80dp"
 android:layout_height="65dp"
 android:layout_marginLeft="510dp"
 android:layout_marginTop="15dp"
 android:background="@drawable/tegaki" />

エミュレーターでは、以下のボタンにあたります。

キャプチャ

※ボタンは背景画像をつけています。

②①のボタン押下時にメモダイアログを表示させます。

③ダイアログを表示させるために、AlertDialogを使用します。

AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);

④テキストボックスを作成するEditTextを準備します。
setTitleでダイアログのタイトル部に表示させたい文字列、setViewでViewをセットできるので、今回はテキストボックスをセットします。

final EditText editView = new EditText(MainActivity.this);
dialog.setTitle("Memo");
dialog.setView(editView);

AlertDialog、EditText、どちらもGUIコンポーネントです。

⑤ダイアログ内にボタンを設置します。
そのためにBuilderクラスで用意されているsetPositiveButtonメソッドを使います。

メソッド内で、計算結果と入力テキストをデータクラスに格納しています。

dialog.setPositiveButton("追加", new DialogInterface.OnClickListener() {
 public void onClick(DialogInterface dialog, int whichButton) {
 // データ格納クラスのインスタンス生成
 Memo mm = new Memo();

 // 追加ボタンをタップ時の処理
 String text = editView.getText().toString();

 // メモと数値をセット
 mm.setMemo(text);
 mm.setNum(number);

 // リストにメモと数値を格納
 memoList.add(mm);

 // アダプターにテキストをセット
 adapter.add(editView.getText().toString());
 }
});

上の画像は、アダプターに入力テキストを格納していますが、
アダプターは、ListView実装時に重要なものになるので、後述でListViewと併せて説明させていただきます。

// アダプターにテキストをセット
adapter.add(editView.getText().toString());

アダプターは事前にメンバ変数で定義しておきます。

// ArrayAdapterオブジェクト生成
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);

アダプターにも種類がありますが、今回はSDKが標準で用意しているArrayAdapterを使いました。

⑥最後にshowメソッドでダイアログ表示です。

dialog.show();

次は、いよいよListViewについて説明していきます。

ListViewを使ってみる

①xml内でListViewを定義します。

<ListView
 android:id="@+id/list_view"
 android:layout_width="wrap_content"
 android:layout_height="match_parent"
 android:layout_alignParentTop="true"
 android:layout_marginLeft="450dp"
 android:layout_marginTop="125dp" >
</ListView>

②xmlで定義したListViewをJavaで使う準備です。

// ListViewオブジェクトの取得
ListView listView = (ListView) findViewById(R.id.list_view);

③ListViewのアイテムクリック時の処理を記述します。
実装も簡単で、ボリュームは以下の通りです。

// ListViewのアイテムクリック時処理
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
 public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {

 // 取得した数値を表示
 counter_text.setText(memoList.get(position).getNum());
 }
});

ここでは、「リスト表示されるアイテムクリック時のイベント」を定義しますが、
それを実現するインタフェースが、onItemClickメソッドです。

onItemClick(AdapterView<?> adapterView, View view, int position, long id)

上記のように、このように引数が多いので、具体的な役割について以下で説明します。

adapterView・・・クリックした項目のAdapterViewです。今回はListViewになります。
view・・・クリックアイテムのViewです。
position・・・クリックした項目のListView内でのpositionです。Indexで0から始まっています。
id・・・クリックした項目にセットされているIDです。

ListViewの実装時に、疑問に思ったのは
「項目にメモリストを表示したいのに、その実装はどうやって行うのか?」という事です。

ListViewに登録データを項目として表示させたい。
そんな時に利用するのが、アダプターです。

Adapterとはデータを担当するListと、UIを担当するListViewの橋渡しをするコンポーネントです。

そして、実際に関連付けが行われるのがsetAdapterメソッドです。

// ListViewとAdapterの紐付け
listView.setAdapter(adapter);

これで、ListViewにメモリストを表示させる準備は完了です。

つまり、メモ機能で実装しているアダプターにセットさせるものは、
ListViewの項目で表示させたいデータという事になります。

// アダプターにテキストをセット
adapter.add(editView.getText().toString());

④選択アイテム取得処理です。

// ListViewのアイテムクリック時処理
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
 public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {

 // 取得した数値を表示
 counter_text.setText(memoList.get(position).getNum());
 }
});

クリックして取得したいのは、メモに関連付いた計算結果なので、

setTextメソッドにメモデータ格納クラスのオブジェクトをセットしていた
memoListから選択位置であるposition(index)を利用して、クリックアイテムに紐付く計算結果を取得します。

自分のアプリでは利用しませんでしたが、選択アイテムを取得するために、
ListViewにはgetItemAtPositionというメソッドが用意されています。

記述すると、こんな感じです。

// 選択アイテムを取得
ListView listView = (ListView) adapterView;
String selectItem = (String) listView.getItemAtPosition(position);

getItemAtPositionではListの表示内容を取得することができるため、
本来はデータ格納クラスのオブジェクトを用意せずに、選択項目のデータ取得ができます。

完成イメージ(エミュレーター画面)

最後に

今回は、GUIコンポーネントのListViewをピックアップして説明させていただきましたが、
上記のアプリ紹介の説明だけでも、アラートダイアログやエディットテキストなどのGUIコンポーネントが登場しています。

他にも様々な種類のGUIコンポーネントが存在しており、
パレットが用意されていることで、配置もドラッグ&ドロップでレイアウト作成が可能なので
画面レイアウトを見ながら、実装できることもandroid開発の魅力の一つです。

comp

私は、iphoneなのでエミュレーターで動作確認しましたが、

androidをお持ちの方は、ぜひ実機確認でandroid開発を楽しんでみてください。

ご一読頂き、ありがとうございました。


皆さま、こんにちは。 キャスレーコンサルティングのSI(システム・インテグレーション)部所属の青木(和)と申します。
今回、「ブルーグリーンデプロイメント」(Blue Green Deployment)というデプロイ手法を紹介させていただきます。

開発工程に占めるリリース作業の負荷は、思いの外大きいものです。
リリース準備のために手順を整備したり、事前にリハーサルを行ったり。
また、本番環境のサーバーを停止させる必要が有るなどの制約がある場合、休日や深夜にリリースする必要があります。
ブルーグリーンデプロイメントは、こうしたリリースの作業負荷を低減させることができるデプロイ手法です。

ブルーグリーンデプロイメントとは

コード変更を行うとテスト、ビルド、デプロイなど、リリース準備までの工程を自動化してくれる「継続的デリバリー」(Continuous Delivery)という考え方があります。
ブルーグリーンデプロイメントは継続的デリバリーから派生したデプロイの方法の事で、次のことを目標にします。

  • デプロイの自動化
  • サーバーのダウンタイムゼロ

クラシックなデプロイメントは既存のサーバーに対して新しい資源を配置したり設定を更新することで行われていました。
ブルーグリーンデプロイでは環境を2つ用意し、リクエストの向き先を切り替えることでデプロイを行います。
大まかな手順は以下の通りです。

①本番環境(以下、ブルー環境と呼ぶ)に対し、アップデート環境(以下、グリーン環境と呼ぶ)を新規作成する。
②グリーン環境をルータにアタッチする。
③ルータの向き先を切り替え、アプリケーションへのリクエストの向き先をブルー→グリーンに変更する。
④グリーンの動作が安定したら完了。問題が発生した場合、向き先をグリーン→ブルーに変更し、ロールバックする。
⑤次回のリリースではグリーンを本番、ブルーをテスト環境とし、①〜④の手順でリリースを実施する。

bg_xxx

検証概要

AWSを利用して検証を行いました。
検証環境の構成は以下の図に示す通りです。

BGDeploy-Architecture

図にあるenv1(ブルー環境)が既存環境で、ユーザがURLでアクセスするとAmazon Route53(DNSサーバーの役割をするAWSサービス)によってブルー環境にリクエストが振られるようにルーティングされています。
これからenv2(グリーン環境)を追加し、設定を変更することでグリーン環境に移行を行うというのが本検証の目的です。

ブルー、グリーン各環境の構成、管理には「AWS Elastic Beanstalk」というサービスを使用します。
各環境の内部は、「ロードバランサー > ターゲットグループ > EC2インスタンス」という若干複雑な構成になっています。
Elastic Beanstalkを使用することで内部の構成をあまり意識せず、環境単位で構成管理を行うことができます。
Elastic BeanstalkにはコンソールとCLI(Command Line Interface)2つの操作方法がありますが、
検証ではCLI(ver3.7.8)を使用しています。

Elastic Beanstalk(CLI)の導入方法、使用方法については公式ドキュメントをご参照ください。
AWS Elastic Beanstalk (CLI)公式ドキュメント

リクエストの向き先を切り替える方法はいくつか考えられますが、今回は次の2つを実践しました。

  • 検証① ⇒ 2つの環境に割り振られたドメインを入れ替える方法(CNAME_SWAPによるデプロイ)
  • 検証② ⇒ DNS(Route53)のルーティング設定を変更する方法(DNSカットオーバーによるデプロイ)

検証環境作成

検証を行うのに使用した環境構築方法を以下に示します。
手順①~④はElastic Beanstalkで、手順⑤はAmazon Route53コンソールから操作しています。

①アプリケーション作成
以下のコマンドでアプリケーションを作成します。

$eb init

対話式の質問が途中で表示されるので、以下のように設定します。

  • アプリケーション名 = BGDeploy
  • アプリケーション種類 = PHP

②ブルー環境作成
①で作成したアプリケーションに環境を作成します。この環境が今動いている既存の環境と考えてください。
今回の検証では、トップページが1枚のシンプルな構成となっています。

$eb create env1

ブルー環境には以下のhtmlをデプロイします。

<html>
<head>
<title>ver1.0</title>
</head>
<body bgcolor="blue">
<font color="white">this is ver1.0</font>
</body>
</html>

デプロイ。

$eb use env1
$eb deploy

③グリーン環境作成
①で作成したアプリケーションにもう一つ環境を追加します。この環境が移行後の環境であると考えてください。

$eb create env2

グリーン環境には以下のhtmlをデプロイします。

<html>
<head>
<title>ver1.1</title>
</head>
<body bgcolor="green">
<font color="white">this is ver1.1</font>
</body>
</html>

デプロイ。

$eb use env2
$eb deploy

④確認
手順②③で作成した環境にwebブラウザからアクセスすると以下のようになります。

  • ブルー環境
$eb use env1
$eb open

bgdeploy_blue

  • グリーン環境
$eb use env2
$eb open

bgdeploy_green

⑤Route53にアプリケーションを関連づけ
手順①〜③で作成したアプリケーションを「AWS Route53」に関連づけることでDNSによるルーティングを可能とします。
Route53とはAWSのサービスの一つで、DNSサーバーの役割をします。
事前に取得した以下のドメインが、②で作成したブルー環境のエイリアスとなるように関連付けをします。
ユーザーはこのURLを叩いてアプリケーションにアクセスすると考えてください。

  • ドメイン名 : aokixyz.top

bgdeploy_route53

webブラウザからアクセスすると、ドメインがブルー環境に紐付いていることが確認できます。
bgdeploy_domain1

検証①CNAME_SWAPによるブルーグリーンデプロイメント

では、ブルー環境からグリーン環境へと移行を行います。
現在、アプリケーションへのリクエストは以下のように処理されます。

①ユーザがURLを叩く。
②Route53が名前解決を行い、ブルー環境にリクエストを振る。
③ロードバランサーがポリシーに則り、アプリケーションサーバー(EC2)へリクエストを振り、処理が行われる。

検証環境構築の中でブルー、グリーン各環境(正確には各環境のロードバランサー)に自動的にドメインが振られています。
2つのドメイン(CNAME)を交換することで、DNSで名前解決するサーバーをグリーン環境に変更し、向き先を変更します。
Elastic Beanstalkはドメイン交換の機能を持っており、以下のコマンドで簡単に行うことが可能です。

$eb use env2
$eb swap env1

以下のコマンドを実行すると、ドメインが入れ替わっていることを確認できます。
(CNAMEの項目がドメインに該当します。env1で使用していたドメインがenv2に割り振られています。)

$eb status
Environment details for: env2
  Application name: BGDeploy
  Region: ap-northeast-1
  Deployed Version: app-170104_132005
  Environment ID: e-xpjfywsf6r
  Platform: 64bit Amazon Linux 2016.09 v2.3.0 running PHP 5.4
  Tier: WebServer-Standard
  CNAME: env1.3kjeufmpnp.ap-northeast-1.elasticbeanstalk.com
  Updated: 2017-01-04 04:29:02.981000+00:00
  Status: Ready
  Health: Green

これで移行完了です。
サーバーを停止させることなく移行するという目標が達成されました。
アプリケーションのURLを叩くと新環境に移行できていることが確認できます。

bgdeploy_cswap_newapp

移行後の環境で問題が発生した場合、以下のコマンドで元のブルー環境に向き先を変更してロールバックします。

$eb use env1
$eb swap env2

ブラウザからURLを叩くと元の環境に戻っていることが確認できます。
bgdeploy_domain1

検証②DNSカットオーバーによるブルーグリーンデプロイメント

検証①ではドメイン交換することで移行を行いましたが、DNSの設定を変更することでも同じことが可能です。
Route53では「トラフィックポリシー」というものを作成することができ、これによってルーティングの向き先や重みづけなどの制御をすることができます。

今回作成するトラフィックポリシーは「加重ルーティングポリシー」と呼ばれるポリシーです。
1つのDNS名(aokixyz.top)に対して複数のリソースを関連づけ、指定した比率でルーティングすることが可能です。
例えば「ブルー100, グリーン0」であればアプリケーションへのリクエストは全てブルーにルーティングされます。
「ブルー1, グリーン1」であればブルーとグリーンに均一に分散されることとなります。

下の表のように優先度を設定し、移行前はブルー環境に、移行後はグリーン環境に向くようにします。

環境 優先度(移行前) 優先度(移行後)
ブルー 100 0
グリーン 0 100

以下の手順でトラフィックポリシーを作成します。

①Route53マネージメントコンソールから「Create policy records」を押下。
bgdeploy_route53_2

②ポリシーに名前をつけ、「Next」を押下。
bgdeploy_route53_3

③endpointに「Elastic Beanstalk environment」を選択し、ブルー環境とグリーン環境を指定する。
ルールに「Weighted Rule」を選択し、ブルー環境 = 「100」, グリーン環境 = 「0」の優先度を指定する。
その後、「Create Traffic Policy」を押下。
bgdeploy_x1

④トラフィックポリシーを作成した後、ポリシーレコードを作成する。
bgdeploy_route53_6

⑤StatusがAppliedになれば、ポリシーの作成は完了です。
bgdeploy_route53_7

ブラウザからURLを叩くとブルー環境にルーティングされていることを確認できます。
bgdeploy_route53_8

続いてトラフィックポリシーを編集して、優先度を変更します。(移行後の状態となります。)

トラフィックポリシーを編集して新しいバージョンとして保存。
bgdeploy_x2

ポリシーレコードのバージョンを新しいものに変更。
bgdeploy_x3

ブラウザからURLを叩くとグリーン環境に切り替わっていることを確認できます。
bgdeploy_route53_13

移行後の環境で問題が発生した場合、移行前のトラフィックポリシーにバージョンを戻すことでロールバックすることができます。

まとめ

長くなりましたが、いかがでしたでしょうか。

リリース時に新しい環境を作成して古い環境は使い捨てていくというのは、クラウド時代らしいデプロイ方法だと思います。
この発想は「Immutable Infrastracture」(一度サーバーを構成したら変更しないという考え方)とリンクしています。

リリースがより効率的に行われたらいいな、と思います。
ここまでお読みいただき、ありがとうございました。

参考

今回の記事の執筆にあたり、以下のサイトを参考にさせていただきました。
Martin Fowler氏のblog


キャスレーコンサルティングSI(システム・インテグレーション)部の鈴木です。
今回は鈴木チーム全員でLinking APIを使用してIoT技術を体験した模様をお伝えします。

メンバーは以下の3名です。
鈴木(要件定義担当、アプリ開発リーダー、ブログ執筆担当)
田中(孝)さん(アプリ開発メンバー、社内勉強会講師担当)
谷本さん(アプリ開発メンバー)

Linking対応アプリを作るきっかけ

世間では以前からIoT(Internet of Things)という単語が飛び交っていましたが、
ここ最近、自動運転技術のレベルが進展したりスマートデバイスが登場して
より一層IoTが注目を集めるようになってきました。

しかし、チームのメンバーはみんな業務アプリケーション開発技術者であり、IoTに興味はあっても、
なかなかIoTの技術に触れる機会がありませんでした。

そんなとき、NTT DoCoMoがProject Linkingというプロジェクトを展開していることを知りました。
(https://linkingiot.com/)

Linkingプロジェクトでは、デバイスとアプリの連携をサポートするAPIを提供しています。
・デバイス開発者はLinking APIに準拠したデバイスを製作します。
・アプリ開発者はLinking APIに従ってアプリとデバイスのI/Fを記述します。

Linking
※図はProject Linkingのサイトより

Linkingによって、デバイス開発者は自分が作成したデバイスを専用アプリ「のみ」ではなく、
いろんなアプリからデバイスを使ってもらうことができ、アプリ開発者は専用デバイス「のみで」使える技術ではなく、
異なるデバイスを扱うときもLinking API対応のデバイスであれば、習得した技術を生かすことができます。

「専用」「のみ」といったキーワードが少なく、「いろんな」「どんな」といったキーワードが増えるほど
その技術は技術者にとってもユーザにとっても身近になります。
つまり、LinkingはIoTを身近な技術にすることに挑戦するプロジェクトだということです。
※鈴木個人の見解です

今までIoTに疎遠だった自分たちにぴったり!
さっそくアプリ開発を体験してみることになりました。

Linkingで何つくる?

どんなデバイスを使ってどんなアプリを開発するか。
IoTの肝といえる部分。

物がただ単にインターネットに繋がっているだけではそれほど意味がなく、
物から得られる情報をどう扱うかでIoTの価値が決まります。

デバイスで出来ること:温度取得、湿度取得、位置情報取得、LED点灯・点滅
※デバイスによってどれが出来るか異なる
デバイスとスマホ間の通信:LinkingはBluetooth Low Energy(BLE)でスマホとデバイスの通信を行う

議論を重ねた結果、
温度センサー、湿度センサー、LEDを搭載し、クリップ留めができる「Tukeru」というデバイスを使用して、
ペットのケージ(飼育スペース)内の温度、湿度を監視するアプリ「ペット快適生活」を開発することになりました。

ケージに「Tukeru」を取り付けて、温度、湿度を監視し、大切なペットを熱射病や低体温から守ります。
飼い主とペット、共に嬉しいアプリだと思います。

快適な環境ではなくなった場合、アプリにメッセージを表示するとともに、デバイスのLEDを点灯させ、
ユーザにお知らせします。

◆「ペット快適生活」の搭載機能
画面設計
1.不適切環境と判断した際のアラートを表示する領域
2.飼育に適切とされる温度、湿度を設定する領域
3.現時点の温度、湿度表示

デバイスとのI/F
4.デバイスから温度、湿度情報を取得する機能
5.不適切環境と判断した際に、デバイスのLEDを点灯させる機能

業務処理
A.一定間隔でデバイスから情報を取得
B.デバイスから取得した温度、湿度が快適な温度、湿度範囲内にあるか判定
C.範囲外の場合に画面にアラートメッセージを表示し、デバイスのLEDを点灯する

ここからはアプリ開発の肝である、デバイスとのI/F機能(上記4.と5.)の実装について、
他ではあまり触れられていない部分について紹介します。
なお、正式なAPI仕様についてはProject Linkingの開発者用ページをご覧ください。

LinkingAPIを使った実装

今回はすべてMainActivity.java内に記述しました。

デバイスからセンサーデータを取得する

onCreate()にデバイスのセンサーデータを取得するControlSenSorDataオブジェクトを生成します。

mySensorDataInterface = new MySensorDataInterface();
mSensorData = new ControlSensorData(this, mySensorDataInterface);

onClick()の見守り開始ボタン押下時の処理として、まずは接続デバイス情報を取得します。

// 接続デバイス情報の取得
GetDeviceInformation getDeviceInformation = new GetDeviceInformation(this);
List<DeviceInfo> deviceInfos = getDeviceInformation.getInformation();

if (deviceInfos.isEmpty()) {
    Log.e(TAG, "デバイス情報が取得できませんでした");
    return;
}

// 複数のデバイス情報を取得可能(今回はデバイスは一つなので引数は0固定)
DeviceInfo info = deviceInfos.get(0);

デバイス情報を取得できたら、以下のようにセンサーデータに情報を設定し、センサーデータ取得を開始します。
通知間隔と通知継続時間は単位が違うので注意が必要です。

// センサーデータ通知間隔(ミリ秒)
mSensorData.setInterval(10*1000);
// センサーデータ通知継続時間(秒)
mSensorData.setDuration(600);
// センサータイプ情報
mSensorData.setType(4);
// デバイスのBDアドレス("00:11:22:AA:BB:CC"形式)
mSensorData.setBDaddress(info.getBdaddress());
// デバイスへセンサーデータの取得開始依頼
mSensorData.start();

センサータイプの設定値は以下の通りです。
setType表

センサーデータを取得

センサーからデータを取得した時の処理はMySensorDataInterface.onSendorData()に記述します。
originalDataには拡張センサーのデータが格納されています。
データは指数表現で記述されており、比較や画面表示をする際には10進数に変換する必要があります。
今回はお試しということで、変換処理はLinkingAPIダウンロードファイルに同梱されているサンプルプログラムから
流用しました。

@Override
public void onSensorData(String bd, int type,float x,float y,float z, byte[] originalData, long time) {
    float fVal;
    BigDecimal bigD;

    Log.d(TAG, bd + ":センサーデータを受信しました。");

    if(type == 4) {
        // 指数表現→10進数への変換
        // (LinkingAPIダウンロードファイルに同梱されているサンプルプログラムよりPairingUtilを流用)
        fVal = PairingUtil.convertOriginalData(4, originalData);
        // 小数第二位を四捨五入
        bigD = new BigDecimal(String.valueOf(fVal));
        fCurTemp = bigD.setScale(1, BigDecimal.ROUND_HALF_UP).floatValue();
        // 画面項目curTempに表示
        curTemp.setText(String.valueOf(fCurTemp));
    }
    Log.d(TAG, "現在の温度:" + fCurTemp);

実際に取得した値はこのように表示されます。
after_getTemp_a

デバイスのLEDを点灯させる機能

取得した温度、湿度がアプリで設定した快適温度と快適湿度の範囲内にあるか判定します。
範囲内にあれば「快適」なので何もせずに監視を継続しますが、範囲外である場合はアプリにメッセージを表示し、
デバイスのLEDを点灯させます。
ではLEDの点灯を行う処理を見ていきます。

onCreate()内に拡張センサーの通知用クラスSendOtherのオブジェクトを生成します。

// 拡張センサー情報の通知用
sendOther = new SendOther(this);

デバイスに拡張センサー情報を通知するときは以下のように記述します。

// デバイスあてにLED点灯指示を送る
sendOther.setIllumination(
    new byte[]{
        LINKING_IF_PATTERN_ID,
        blink_pattern,
        LINKING_IF_COLOR_ID,
        color
    });
sendOther.send();

引数blink_patternにはLED点灯パターンを設定します。
LED点灯パターン表

引数colorにはLED点灯色を設定します。
LED色表

Tukeruのデバイス仕様書は現時点で公表されていないので、実際にデバイスを作動させて上の表を作りました。
※参考にした結果につきましては一切責任を負いません

通知を受けるとデバイスのLEDが点灯します。
DSC_1268
ちなみにこのときの画面です。
after_alert_a

おわりに

そこまで遠くない将来、AIの発達などでプログラマーが不要になるという意見があります。
この意見は、アプリのアイデア出しをするためにIoT、AIやビッグデータなど最新技術を調べていて見つけたのですが、
読んでいるうちに遥か昔の記憶がフラッシュバックしてきました。

それは、社内SEをしていた頃、業務効率化のためのシステムを提案すると、現場から
「システム部はリストラを助長するだけだ!」
「我々の仕事を奪うのか!」
と直球の意見を受けた経験です。

「自分のやっていることは間違ってないはずのに。。」
と寂しい思いをしましたが、今度は自分の仕事が奪われる立場になりました。

しかし、あまり危機感を抱いていません。
コーディングは機械に任せて、社会的問題に役立つシステムや顕在化していないニーズを呼び起こすシステムを考える、
より上流の仕事に専念できる!
そう考えるとむしろ歓迎すべきことだと思います。
新しいITサービスを提供するためのクリエイター的発想を今のうちからどんどん磨いていきましょう!

また、IoTは私たちが望んでいる未来を実現する有力な手段であることが実感できました。
Project Linking自体も本格的なIoTプロジェクトとなるべく、デバイスとアプリがインターネット接続できるように
進化するかもしれません。

今後もIT業界の最新動向についてアンテナを高くする必要があると認識した、
非常に意義のあるLinkingアプリ体験でした。
(了)


こんにちは、キャスレーコンサルティング SI(システム・インテグレーション)部の清水(誠)です。

今回は、シンプルかつメンテナンス性の高いコードを書くために重要な概念のひとつである
「モジュール結合度」について取り上げます。

そういえば情報処理試験を受けた時に覚えはしたけど、実務ではあまり意識する機会がなかったなあ……
という方もいらっしゃるのではないでしょうか。

他ならぬ筆者がそうだったので、今回改めておさらいし、具体例と合わせてご紹介してみようと思います。
今後の実務で活用していただたくきっかけとなれば幸いです。

なお、ここで言うモジュールとは関数や変数を一定の単位でまとめたものを指し、
一般的なオブジェクト指向型言語での「クラス」に置き換えていただいても差し支えありません。
また、サンプルコードはC#にて記述します。

モジュール結合度とは?

モジュール結合度とは、モジュール同士の密接さを表す尺度です。
6段階に分類され、結合度が低いほど独立性が高くなり、良いモジュールとされています。
以下、結合度の高い順にご説明します。

1.内容結合

あるモジュールが、別のモジュールの外部宣言していない内部状態を直接参照したり、
命令の一部を共有したりしている状態を指します。
内容結合では、一方のモジュールの変更が他方のモジュールに影響を及ぼすので
障害につながる確率が高くなります。

アセンブラ等の低水準言語では陥りがちですが、高水準言語では一般的には発生しないためサンプルコードは省略します。

2.共通結合

共通域に定義したデータをいくつかのモジュールが直接使用するような状態を指します。
共通域の定義データとはいわゆるグローバル変数、JavaやC#で言うところのstatic変数です。

・共通域のデータは他のモジュールで書き換えられてしまう可能性がある
・共通域のデータ定義の変更がそれを使用しているモジュール全てに影響する

等、デメリットが多くあります。

サンプルコード

using System;

public class CommonParameter
{
    // 共通域のデータ
    public static string Name;
    public static int Number;
    public static string Place;
}

public class CommonCoupling {
    public string GetMessage(){
        var message = CommonParameter.Name + "が" + CommonParameter.Number + "匹います。";
        return message;
        // CommonParameter.Placeは使用しない
    }
}

public class TestCommon
{
    public static void Main() {
        CommonParameter.Name = "犬";
        CommonParameter.Number = 2;
        CommonParameter.Place = "庭";
        var data = new CommonCoupling();
        Console.WriteLine(data.GetMessage());
    }
}

実行結果
 犬が2匹います。

3.外部結合

外部宣言したデータ(= public static変数)を共有している状態を指します。
データを共有するという点では共通結合と似ていますが、必要なデータのみを外部宣言するため
共通結合よりも結合度は弱くなります。

サンプルコード

using System;

public class ExternalCoupling {

    // 必要なパラメータをstatic化
    public static string Name;
    public static int Number;

    public string GetMessage(){
        var message = Name + "が" + Number + "匹います。";
        return message;
    }
}

public class TestExternal
{
    public static void Main() {
        ExternalCoupling.Name = "猫";
        ExternalCoupling.Number = 3;
        var ext = new ExternalCoupling();
        Console.WriteLine(ext.GetMessage());
    }
}

実行結果
 猫が3匹います。

4.制御結合

呼び出し側のモジュールが、呼び出されるモジュールの制御を指示するデータ(いわゆるフラグ)を
パラメータとして渡す状態を指します。呼び出し側は相手のモジュールのロジックを知っている必要があるため、
相手をブラックボックス扱いできず結合度が強くなります。

サンプルコード

using System;

public class ControlCoupling {
    public string GetMessage(string name, int number, string type){
        string message = string.Empty;

        // 引数typeによって処理を分岐
        if (type == "哺乳類")
        {
            message = name + "が" + number + "匹います。";
        }
        else if (type == "鳥類"){
            message = name + "が" + number + "羽います。";
        }
        return message;
    }
}

public class TestControl
{
    public static void Main() {
        var ctrl = new ControlCoupling();
        Console.WriteLine(ctrl.GetMessage("ハムスター", 4, "哺乳類"));
        Console.WriteLine(ctrl.GetMessage("ハト", 5, "鳥類"));
    }
}

実行結果
 ハムスターが4匹います。
 ハトが5羽います。

5.スタンプ結合

共通域にないデータ構造を、2つのモジュール間で受け渡す状態を指します。
結合度は比較的弱いですが、スタンプ結合の場合は受け渡すデータ構造の一部を使用しないことがあります。

サンプルコード

using System;

public class StampCoupling {
    public string GetMessage(StampParameter p){
        var message = p.Name + "が" + p.Number + "匹います。";
        return message;
        // p.Placeは使用しない
    }
}
public class StampParameter
{
    // 受け渡し用データ(インスタンス変数なので共通域ではない)
    public string Name;
    public int Number;
    public string Place;

}

public class TestStamp
{
    public static void Main() {
        var p = new StampParameter() ;
        p.Name = "アリ";
        p.Number = 6;
        p.Place = "地中";
        var stmp = new StampCoupling();
        Console.WriteLine(stmp.GetMessage(p));
    }
}

実行結果
 アリが6匹います。

6.データ結合

単純なデータだけをパラメータとして受け渡す状態を指します。
相手モジュールをブラックボックス化できるので、結合度は一番弱くなります。

サンプルコード

using System;

public class DataCoupling {
    public string GetMessage(string name, int number){
        var message = name + "が" + number + "匹います。";
        return message;
    }
}

public class TestData
{
    public static void Main() {
        var data = new DataCoupling();
        Console.WriteLine(data.GetMessage("コツメカワウソ", 7));
    }
}

実行結果
 コツメカワウソが7匹います。

おわりに

一般的に、モジュール同士の結合度が低いほどコードの可読性・再利用性が高まり、
障害が発生した場合も対応がしやすくなります。

プロダクト毎のアーキテクチャやコーディング規約の許す範囲で、
可能な限り結合度を弱く保つコーディングを心掛けましょう。

最後までお読みいただきありがとうございました。


こんにちは。キャスレーコンサルティングSI(システム・インテグレーション)部の森外です。
今回はElixirとPhoenix Frameworkを使用したWebアプリケーションの作成について紹介します。

Elixirについて
Elixirとは、拡張性しやすくメンテナンスしやすいアプリケーションを構築できるよう設計されている関数型言語です。
Erlang VM上で動作します。(JavaとScalaの関係に似ています)
http://elixir-lang.org/

Phoenix Frameworkについて
サーバサイドMVCパターンを実装するためのElixirで作成されたフレームワークです。
http://www.phoenixframework.org/

作成するWebアプリケーションについて
ブラウザからデータの登録・参照・更新・削除をできることを目的とします。
これから紹介する手順はすべてターミナル上で行っています。

事前準備

以下の環境を準備します。

  • macOS (10.12.1)
  • Elixir (1.3.4)
  • Phoenix Framework (1.2.0)
  • PostgreSQL (9.6.1)
  • npm (3.10.9)

※ PostgreSQLとnpmのインストールについては省略します。

Elixir のインストール

Homebrew※を使用してインストールを行います。
※ macOS用のパッケージマネージャーです。http://brew.sh/

$ brew install elixir

バージョンの確認を行ってみます。

$ elixir -v
Erlang/OTP 19 [erts-8.1] 1 [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Elixir 1.3.4

macOS以外のインストール方法はインストールガイドを参照してください。
http://elixir-lang.org/install.html

Phoenix Frameworkのインストール

はじめに、パッケージマネージャのHexをmixコマンドでインストールします。
※ mixはElixirのプロジェクトを管理するコマンドラインユーティリティです。
Elixirのインストールと同時にインストールされています。

$ mix local.hex

つづいて、Phoenix Frameworkをインストールします。

$ mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez

プロジェクトの作成

任意のディレクトリでmixコマンドを実行し、プロジェクトを作成します。
引数の player_phoenix がプロジェクト名になります。

$ mix phoenix.new player_phoenix

プロジェクト作成時にPostgreSQLが起動していないとエラーが発生します。
また、Phoenixはデフォルトでpostgresユーザを利用するように設定されています。

データベースの作成

アプリケーションが利用するデータベースを作成します。
player_phoenix ディレクトリに移動し、mixコマンドを実行します。

$ cd player_phoenix
$ mix ecto.create

psqlコマンドでデータベースが作成されていることを確認します。

$ psql -l
                                   List of databases
       Name         |  Owner   | Encoding |   Collate   |    Ctype    | Access privileges
--------------------+----------+----------+-------------+-------------+-------------------
 player_phoenix_dev | postgres | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 |

Phoenixの起動

Phoenixが起動することを確認してみます。
mixコマンドでPhoenixを起動します。

$ mix phoenix.server

ブラウザで http://localhost:4000 にアクセスして以下の画面が表示されることを確認します。
hello_elixir

Phoenixの停止

ctrl-c を二回実行することでPhoenixを停止させることができます。

ジェネレータの実行

Phoenix Frameworkには、MVCパターンのファイルを自動生成するジェネレータ機能があります。
mixコマンドで自動生成させます。

$ mix phoenix.gen.html Player players name:string team:string position:string
* creating web/controllers/player_controller.ex
* creating web/templates/player/edit.html.eex
* creating web/templates/player/form.html.eex
* creating web/templates/player/index.html.eex
* creating web/templates/player/new.html.eex
* creating web/templates/player/show.html.eex
* creating web/views/player_view.ex
* creating test/controllers/player_controller_test.exs
* creating web/models/player.ex
* creating test/models/player_test.exs
* creating priv/repo/migrations/20161207134319_create_player.exs

各引数は以下の意味を持ちます。

  • Player ・・・ モデル名
  • players ・・・ リソース名
  • name:string以降 ・・・ テーブルのカラム名とその属性

ルーターファイルの編集

自動生成されたリソースにブラウザからアクセスできるようにするため、ルーターファイル(web/router.ex)を編集します。
20行目に resources “/players”, PlayerController を追加します。

  defmodule PlayerPhoenix.Router do
     use PlayerPhoenix.Web, :router

    pipeline :browser do
      plug :accepts, ["html"]
      plug :fetch_session
      plug :fetch_flash
      plug :protect_from_forgery
      plug :put_secure_browser_headers
    end

    pipeline :api do
      plug :accepts, ["json"]
    end

    scope "/", PlayerPhoenix do
      pipe_through :browser # Use the default browser stack

      get "/", PageController, :index
      resources "/players", PlayerController # <- ここを追加
  end

    # Other scopes may use custom stacks.
    # scope "/api", PlayerPhoenix do

マイグレーションの実行

mixコマンドでマイグレーションを実行します。

$ mix ecto.migrate

マイグレーションの内容は priv/repo/migrations/xxx_create_player.exs に定義してあります。
ジェネレータで指定したテーブルとカラムが定義されているのが分かります。(6〜9行目)

$ cat priv/repo/migrations/20161207134319_create_player.exs
defmodule Bar.Repo.Migrations.CreatePlayer do
  use Ecto.Migration

  def change do
    create table(:players) do
      add :name, :string
      add :team, :string
      add :position, :string

      timestamps()
    end

  end
end

psqlコマンドでテーブルが作成されていることを確認します。

$ psql -U postgres player_phoenix_dev
# \d
                List of relations
 Schema |       Name        |   Type   |  Owner
--------+-------------------+----------+----------
 public | players           | table    | postgres
 public | players_id_seq    | sequence | postgres
 public | schema_migrations | table    | postgres
(3 rows)

SQLを実行し、playersテーブルのレコードを確認してみます。

# SELECT * FROM players;
 id | name | team | position | inserted_at | updated_at
----+------+------+----------+-------------+------------
(0 rows)

Phoenixを起動

ここまで問題なく進めたならPhoenixを起動してみましょう。

$ mix phoenix.server

一覧画面の表示

先ほどルーターファイルに追記したコントローラにブラウザからアクセスすると、一覧画面が表示されます。
http://localhost:4000/players

players

データの登録

データの登録を行ってみます。
new

playersテーブルのレコードを確認してみます。
無事登録されていることが分かります。

# SELECT * FROM players;
 id |        name        |     team     | position |     inserted_at     |     updated_at
----+--------------------+--------------+----------+---------------------+---------------------
  1 | クレイ・トンプソン       | ウォリアーズ     | G        | 2016-12-07 14:16:27 | 2016-12-07 14:16:27
(1 row)

データの参照・編集

登録したデータの参照と編集を行ってみます。
編集ではチーム名を変更します。
※ いくつかデータを追加してあります。

show_edit

playersテーブルのレコードを確認してみます。
3行目のレコードが更新されていることが分かります。

# SELECT * FROM players;
 id |        name        |     team     | position |     inserted_at     |     updated_at
----+--------------------+--------------+----------+---------------------+---------------------
  1 | クレイ・トンプソン       | ウォリアーズ     | G        | 2016-12-07 14:16:27 | 2016-12-07 14:16:27
  2 | ティム・ダンカン        | スパーズ       | C        | 2016-12-07 14:33:02 | 2016-12-07 14:33:02
  3 | ケビン・デュラント       | ウォリアーズ     | F        | 2016-12-07 14:33:26 | 2016-12-07 14:36:51
(3 rows)

データの削除

登録したデータを削除してみます。
delete

playersテーブルのレコードを確認してみます。
削除したプレイヤーのレコードが削除されていることが分かります。

# SELECT * FROM players;
 id |        name        |     team     | position |     inserted_at     |     updated_at
----+--------------------+--------------+----------+---------------------+---------------------
  1 | クレイ・トンプソン       | ウォリアーズ     | G        | 2016-12-07 14:16:27 | 2016-12-07 14:16:27
  3 | ケビン・デュラント       | ウォリアーズ     | F        | 2016-12-07 14:33:26 | 2016-12-07 14:36:51
(2 rows)

まとめ

気づかれた方もいらっしゃると思いますが、今回プログラミングしたのはルーターファイルの編集で追記した1行だけです。
Phoenix Frameworkを使うことで、簡単なWebアプリケーションを短時間で作成することができます。

Elixirが気になっている方や勉強したい方は、今回のようにWebアプリケーションを作成して、
自動生成されたファイルを読んだり拡張したりすることで勉強になると思います。


こんにちは。
キャスレーコンサルティングのSD(システムデザイン)部の 小松 です。

社内でミドルウェアのチューニングやシステム基盤の設計等を担当しています。
今回は、Go言語で書かれたインテリジェントな負荷分散を実現する Traefik を試した話を紹介します。 (続きを読む…)


こんにちは、キャスレーコンサルティングSD(システム・デザイン)部の青木です。
今回はFontAwesomeを使ってWebページにアイコンを表示してみようと思います。

FontAwesomeとは

FontAwesomeは商用でも無料で使用することができ、様々なユニークなアイコンを画像ではなく、
テキストとしてWebページ上に表示することができるWebアイコンフォントです。

テキストでの表示となるため、cssで自由に大きさや色を変更することができます。
また、FontAwesome自体に多数のアイコン表示機能が組み込まれており、
現時点で600種類以上のアイコンを簡単にWebページに表示することができます。

フォントのライセンスはSIL OFL 1.1、コードのライセンスはMIT Licenseとなっています。
http://scripts.sil.org/OFL
http://opensource.org/licenses/mit-license.html

FontAwesomeのダウンロードと準備

【FontAwesomeをダウンロード】

下記リンク先からFontAwesomeをダウンロードします。
http://fontawesome.io/

ダウンロードの際は
「No thanks, just download Font Awesome」を選択します
download

【必要ファイルの準備】

ダウンロードしたzipファイルを解凍し、cssフォルダ内のファイルとfontsフォルダ内のファイルを
対象サイトのcss,fontsフォルダにコピーしてください。
※cssとして使用するのはfont-awesome.min.cssなのでfont-awesome.cssはコピーする必要はありません。
※自分のcssフォルダとfontsフォルダもFontawesomeのzipと同じように、同じ階層に配置するようにしてください。

[例]
root
  ┗css
  ┗fonts

FontawesomeFileCopy

【FontAwesomeをHTMLにインポート】

HTMLに下記を記載しfont-awesome.min.cssをインポートします。
※ファイルのパスはカレントディレクトリによって異なります。

<link rel="stylesheet" href="./css/font-awesome.min.css">

FontAwesomeアイコンの表示

FontAwesomeのサイトトップ画面から「icons」タグをクリックしアイコンの一覧から使用したいアイコンを選択します。
表示される<i>タグをコピーしHTMLソース上にペーストすることでアイコンを表示することができます。
iconsBtn

getIconCode

【アイコンの大きさを変える】

<i>タグのclass属性内に下記の記述を追加することでアイコンの大きさを変えることができます。

  • fa-lg
  • fa-2x
  • fa-3x
  • fa-4x
  • fa-5x
<i class="fa fa-rocket" aria-hidden="true"></i>
<i class="fa fa-rocket fa-lg" aria-hidden="true"></i>fa-lg
<i class="fa fa-rocket fa-2x" aria-hidden="true"></i>fa-2x
<i class="fa fa-rocket fa-3x" aria-hidden="true"></i>fa-3x
<i class="fa fa-rocket fa-4x" aria-hidden="true"></i>fa-4x
<i class="fa fa-rocket fa-5x" aria-hidden="true"></i>fa-5x

例えば、上記のように記述した場合は下記のように定義されたスタイルが適用されます。

.fa-lg {
  font-size: 1.33333333em;
  line-height: 0.75em;
  vertical-align: -15%;
}
.fa-2x {
  font-size: 2em;
}
.fa-3x {
  font-size: 3em;
}
.fa-4x {
  font-size: 4em;
}
.fa-5x {
  font-size: 5em;
}


fa-lg
fa-2x
fa-3x
fa-4x
fa-5x

【アイコンの角度を変える】

<i>タグのclass属性内に下記の記述を追加することでアイコンの角度を変えることができます

  • fa-rotate-90       時計回りに90度回転
  • fa-rotate-180      時計回りに180度回転
  • fa-rotate-270      時計回りに270度回転
  • fa-flip-horizontal    水平方向に反転
  • fa-flip-vertical      垂直方向に反転

下記のスタイルが適用されます。

.fa-rotate-90 {
  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
  -webkit-transform: rotate(90deg);
  -ms-transform: rotate(90deg);
  transform: rotate(90deg);
}
.fa-rotate-180 {
  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
  -webkit-transform: rotate(180deg);
  -ms-transform: rotate(180deg);
  transform: rotate(180deg);
}
.fa-rotate-270 {
  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
  -webkit-transform: rotate(270deg);
  -ms-transform: rotate(270deg);
  transform: rotate(270deg);
}
.fa-flip-horizontal {
  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
  -webkit-transform: scale(-1, 1);
  -ms-transform: scale(-1, 1);
  transform: scale(-1, 1);
}
.fa-flip-vertical {
  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
  -webkit-transform: scale(1, -1);
  -ms-transform: scale(1, -1);
  transform: scale(1, -1);
}
:root .fa-rotate-90,
:root .fa-rotate-180,
:root .fa-rotate-270,
:root .fa-flip-horizontal,
:root .fa-flip-vertical {
  filter: none;
}

normal
fa-rotate-90
fa-rotate-180
fa-rotate-270
fa-flip-horizontal
fa-flip-vertical

【アイコンを回転させる】

<i>タグのclass属性内に下記の記述を追加することでアイコンの回転させることができます

  • fa-spin   2秒で1回転させる(時計回り)
  • fa-pulse  1秒で1回転させる(時計回り)※1回転を8ステップに分割して1ステップずつ動作する

下記のスタイルが適用されます。

.fa-spin {
  -webkit-animation: fa-spin 2s infinite linear;
  animation: fa-spin 2s infinite linear;
}
.fa-pulse {
  -webkit-animation: fa-spin 1s infinite steps(8);
  animation: fa-spin 1s infinite steps(8);
}
@-webkit-keyframes fa-spin {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(359deg);
    transform: rotate(359deg);
  }
}
@keyframes fa-spin {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(359deg);
    transform: rotate(359deg);
  }
}



【アイコンを重ねて表示する】

アイコンのクラスに下記を追加することでアイコンを重ねて表示することができます

  • アイコンの親タグ
    • fa-stack
  • アイコンを表示するのタグ
    • fa-stack-1x  アイコンの大きさを指定(親のと同じ大きさ)
    • fa-stack-2x  アイコンの大きさを指定(親の2倍の大きさ)
    • fa-inverse   アイコンの色を白にする

下記のスタイルが適用されます。

.fa-stack {
  position: relative;
  display: inline-block;
  width: 2em;
  height: 2em;
  line-height: 2em;
  vertical-align: middle;
}
.fa-stack-1x,
.fa-stack-2x {
  position: absolute;
  left: 0;
  width: 100%;
  text-align: center;
}
.fa-stack-1x {
  line-height: inherit;
}
.fa-stack-2x {
  font-size: 2em;
}
.fa-inverse {
  color: $fff;
}

fa-ban on fa-camera

アイコンを疑似要素で表示する

ここまではHTMLにタグを埋め込むことでアイコンを表示してきましたが、
今度はHTMLにタグを埋め込まずに、CSSの疑似要素を使ってアイコンを表示してみます。

【HTML】

<p class="iine">いいね</p>

【CSS】

.iine:before {
  font-family: 'FontAwesome';
  content: "\f087";
  margin-right: 5px;
}

 いいね

疑似要素のcontent属性に指定する値は下記を参照してください。
(アイコン名の横に表示されているUnicodeの値です)
http://fontawesome.io/cheatsheet/

※上記リンク内のUnicodeの値にある「&#x」を「\」(バックスラッシュ)に置き換えて使用してください
※また、アイコンをHTMLに直接埋め込む場合に「&#x」のまま使用します

またはfontawesomeのzipを解凍したcssフォルダ内のfont-awesome.cssを参照することでも
content属性に指定する値を確認することができます。

.fa-thumbs-o-up:before {
  content: "\f087";
}

Sass(Scss)でも表示してみる

※Sassの環境準備は下記に記載しています

【必要ファイルの準備】

FontAwesomeのzipファイル内のscssフォルダ内のファイルを任意のフォルダにコピーします。

FontAwesomeScssCopy

「style.scss」ファイルを新規に作成し、コピーしてきた「font-awesome.scss」をimportします。

createScss

※importの際拡張子の省略が可能なので「.scss」を省略しています

【style.scss】

@import "./font-awesome";

.iine:before {
  @include fa-icon;
  content: $fa-var-thumbs-o-up;
  margin-right: 5px;
}

content属性には表示したいアイコンのUnicodeの値を格納した変数を指定します。
(変数は_variables.scssファイル内に定義されています。)

【Scssファイルのコンパイル】

コマンドプロンプトで、作成したscssファイルのあるディレクトリに移動し、
下記コマンドでscssファイルをコンパイルし、cssファイルを作成します。
※「style.scss」部分にコンパイルしたいscssファイル名を指定
※「../css/scssStyle.css」部分に作成したいcssファイルのパスとファイル名を指定

$ sass --style expanded style.scss:../css/scssStyle.css

※下記コマンドでコンパイルすると今後scssファイルを保存した際に自動でコンパイルしてくれるので便利です

$ sass --style expanded --watch style.scss:../css/scssStyle.css

【HTMLで表示する】

コンパイルして作成されたcssファイルをHTMLに読み込みます。

<link rel="stylesheet" href="./css/scssStyle.css">

※コンパイル前のscssファイルでfont-awesomeを読み込んでいるため、
HTMLにfont-awesome.min.cssを読み込む必要はありません。

以上でScssでもFontAwesomeのアイコンを表示することができます。

補足:【Sassの環境準備】

[Rubyのインストール]

SassをダウンロードするのにRubyが必要なので、まず下記リンク先からRubyをダウンロードし、インストールします。
http://rubyinstaller.org/downloads/

※インスト―ルの際に下記にチェックを入れてインストールしてください
・「Rubyの実行ファイルへ環境変数PATHを設定する」
・「.rbと.rbwファイルをRubyに関連づける」

下記コマンドでRubyがインストールされたことを確認します。

$ ruby -v

Rubyのバージョン確認ができればインストールは成功しています。

[Sassのインストール]

まず下記コマンドでSassのインストール時に使う「Rubygems」を最新状態にします

$ gem update --system

下記コマンドでSassをインストールします

$ gem install sass

下記コマンドでSassがインスト―ルされたことを確認します

$ sass -v

Sassのバージョンが確認できればインストールは成功しています。

以上でSassの環境準備は完了です。

おわりに

今回はFontAwesomeを使いアイコンを表示してみましたが、アイコンを表示させるライブラリは
FontAwesome以外にも下記のようなものもあるので、お気に入りのアイコンがあるライブラリや、
自分にとって使いやすいライブラリを探してみるのも面白いかもしれません。


  • Entypo
    Entypo

  • WebHostingHub
    WebHostingHub Glyphs

  • FoundationIcon
    Foundation Icon Fonts 3

  • Genericons
    Genericons

  • flatIcon
    flaticon

  • iconMoon
    IcoMoon

今回の内容は以上となります。
最後までお読み頂き、ありがとうございました。


お疲れ様です!
キャスレーコンサルティングのSI(システム・インテグレーション)部の栗田です。

ここ最近、何となくウェブサーバーを立ててみたいと思い、今回のテーマを思いつきました。
(ざっくりすぎですが…。)

ただ立てるだけでは味気なく思い、方向音痴な自分へのプレゼントも兼ねて、
「今回は地図上の最短ルートを検索するサイトも作成してみよう!」
と思います。

map
(こんな感じのよくあるものです。)

「前編:サーバー構築編」では、Webサーバー構築からpgRouting用の地図データのインポートまでの説明をさせて頂きたいと思います。

環境は、
OS:Ubuntu16.04 LTS
DB:postgresql9.5、PostGIS、pgRouting
言語:PHP7.0
といった内容の構築となります。
(サーバー接続用のWindows端末も必要となります。)

※Ubuntuのインストール手順は省略させて頂きます。既にインストール済の前提で進めさせて頂ければと思います。

前準備

1. サーバー接続用のWindows端末を用意し、TeraTermをインストールして下さい。
2. TeraTermでサーバーに接続し、下記のコマンドを実行します。
パッケージのリストをサーバーから入手したり、インストール済のパッケージの最新化などを行います。

sudo apt-get update
sudo apt-get check
sudo apt-get -s upgrade
sudo apt-get upgrade

テキストエディタをインストールします。

sudo apt-get install vim

これで前準備はokです。

セキュリティ対策

今時は色々な輩がここぞとばかりにサーバーを乗っ取ろうと試みてくるようです。
怖い世の中ですね…。
それではセキュリティ面の設定に関する説明をさせて頂きます。

♦SSH接続の設定
SSHで接続する際は鍵認証にしたいと思います。ペアとなる鍵がなければSSH接続できない仕組みとなります。

1. TeraTermを起動し、「新しい接続」ダイアログを閉じます。
2. 設定(S) → SSH鍵生成(N)で下記ダイアログを表示します。

tera1

3.「生成」ボタンをクリックします。
4.「鍵のパスフレーズ」を入力後に「公開鍵の保存」「秘密鍵の保存」をクリックします。
5. TeraTermでサーバーに接続し、TeraTermに公開鍵のファイルをドラッグドロップします。
6.「SCP」ボタンをクリックするとホームディレクトリにアップロードされます。

tera2
7. TeraTermで以下のコマンドを実行して、鍵を登録します。

cd ~
mkdir .ssh
chmod 700 .ssh
cat id_rsa.pub > .ssh/authorized_keys
chmod 600 .ssh/authorized_keys
rm -f id_rsa.pub

8. Rootになり、以下のファイルを編集します。

sudo su -
vim /etc/ssh/sshd_config

【変更箇所】

# Port22を開放したままは危険なので、違うポート番号にして下さい。(1024以上が良いと思います。)
#Port22 → コメントを外し、Port12345 に変更(もちろん12345以外でも可能です)

# rootユーザでのログインを無効にして下さい。
#PermitRootLogin yes → コメントを外し、PermitRootLogin no に変更

# パスワード認証を無効にして鍵認証にして下さい。
PasswordAuthentication yes → PasswordAuthentication no に変更

# X11Protcolを遮断して下さい。
#X11Forwarding yes
#X11DisplayOffset 10
X11Forwarding no

9. SSHをリロードして設定を反映して下さい。

service ssh reload

♦iptablesの設定
決められたIPアドレス、ポートの接続のみを許可します。

1.適当なディレクトリを作成し、iptables設定用のスクリプトファイルを作成します。
スクリプトファイルは/etc/network/iptables/set_iptablesで新規作成します。

sudo mkdir /etc/network/iptables/
sudo vim /etc/network/iptables/set_iptables

【ファイルの内容】
ファイルの内容は状況によって変更して下さい。

/sbin/iptables -F
/sbin/iptables -X
/sbin/iptables -P INPUT DROP
/sbin/iptables -P OUTPUT ACCEPT
/sbin/iptables -P FORWARD DROP
/sbin/iptables -A INPUT -i lo -j ACCEPT
/sbin/iptables -A OUTPUT -o lo -j ACCEPT

#下記のようなプライベートIPアドレスはアクセス禁止
/sbin/iptables -A INPUT -s 10.0.0.0/8 -j DROP
/sbin/iptables -A INPUT -s 172.16.0.0/12 -j DROP
/sbin/iptables -A INPUT -s 192.168.0.0/16 -j DROP

#PING応答許可
/sbin/iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT

#下記ポート有効(12345は♦SSH接続の設定の8.で決めたポート番号です)
/sbin/iptables -A INPUT -p tcp --dport 12345 -j ACCEPT
/sbin/iptables -A INPUT -p tcp --dport 80 -j ACCEPT
/sbin/iptables -A INPUT -p tcp --dport 5432 -j ACCEPT

/sbin/iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
/sbin/iptables-save > /etc/network/iptables/iptables.db

2. 作成したファイルに実行権限を与えて実行して下さい。実行するとiptables.dbが作成されます。

sudo chmod +x /etc/network/iptables/set_iptables
sudo /etc/network/iptables/set_iptables
ls /etc/network/iptables/

3. ネットワークの起動時にiptablesの設定を反映するシェルスクリプトを作成します。
/etc/network/if-pre-up.d/ディレクトリにシェルスクリプトファイルを設置すると、ネットワーク起動時に自動でスクリプトを実行します。
シェルスクリプトファイルは/etc/network/if-pre-up.d/load_iptablesで新規作成して下さい。

sudo vim /etc/network/if-pre-up.d/load_iptables

【ファイルの内容】

#!/bin/sh
/sbin/iptables-restore < /etc/network/iptables/iptables.db

4. 実行権限を付与します。すぐに反映したい場合はシェルスクリプトを実行して下さい。

sudo chmod +x /etc/network/if-pre-up.d/load_iptables
sudo /etc/network/if-pre-up.d/load_iptables

次は、Webサーバーの導入に入りたいと思います。

ApacheとPHPを導入

1. Apacheをインストールします。

sudo apt-get install apache2

2. PHPと共に、Apacheとpostgresqlで使用するためのモジュールをインストールします。

sudo apt install -y php
sudo apt install libapache2-mod-php
sudo apt-get install php7.0-pgsql

3. /var/www/htmlにinfo.phpというファイルを作成します。
【ファイルの内容】

<?php
 phpinfo();
?>

4. Apacheを再起動します。

sudo systemctl restart apache2.service

5. Windows端末もしくはスマホなどから、http://(サーバーのIPアドレス)でアクセスし、
「It Works!」が表示されればApacheが正常に起動しています。
apache1

6. Windows端末もしくはスマホなどから、http://(サーバーのIPアドレス)/info.phpでアクセスし、
「phpinfo」が表示されればPHPが正常に起動しています。
php1

postgresqlを導入

次にルート検索の肝となるデータベース(postgresql)のインストールを行いたいと思います。
あと絶対に必要ではありませんが、Windows端末にpgAdminをインストールしておくと便利ですので、ご検討下さい。

1. pgRouting絡みのパッケージをインストールするために、aptのリポジトリに以下を追加します。
あとはadd-apt-repositoryを使用できるようにします。

sudo apt-get install apt-file
sudo apt-file update
sudo apt-file search add-apt-repository
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:georepublic/pgrouting-unstable
sudo apt-get update

2. postgresqlとPostGISをインストールします。

sudo apt-get install postgresql postgis proj-bin gdal-bin
sudo /etc/init.d/postgresql start

3. pgRoutingとOpenStreetMapのデータをpgRoutingで使うためのパッケージをインストールします。

sudo apt-get install postgresql-9.5-pgrouting
sudo apt-get install osm2pgrouting

4. postgresユーザのパスワードを設定して下さい。

sudo passwd postgres

5. OpenStreetMap用テーブルを作成します。

sudo -u postgres createdb --encoding=UTF8 osm;

6. posgtesユーザーでログインし、PostGISとpgRouting拡張を適用します。

su - postgres
psql --username=postgres --dbname=osm -c "CREATE EXTENSION postgis;"
psql --username=postgres --dbname=osm -c "CREATE EXTENSION pgrouting;"

7. postgresqlの設定を変更します。まずはpostgresql.confを変更し、外部からpostgreSQLに接続できるようにします。

sudo vim /etc/postgresql/9.5/main/postgresql.conf

【変更箇所】

#listen_addresses = 'localhost' を listen_addresses = '*'に変更して下さい。

7. 次にpg_hba.confを変更し、こちらも外部からpostgreSQLに接続できるようにします。

sudo vim /etc/postgresql/9.5/main/pg_hba.conf

【変更箇所】

host all all 0.0.0.0/0 trust の行を追加します。
local all postgres peer を local all postgres trust に変更します。

8. 変更後にpostgresqlを再起動します。

sudo /etc/init.d/postgresql start

openStreetMapデータの取り込み

1. http://download.geofabrik.de/に日本の部分を効率よく切り取ってくれたデータがありましたので、これを利用します。
OSMの地図データ(XML)を圧縮したデータがpbfデータとなります。ホームディレクトリにダウンロードします。

cd ~
wget -c http://download.geofabrik.de/asia/japan-latest.osm.pbf

2. *.pbfを*.osm(xml)に変換します。変換後はサイズが25GBほどになりますので、ご注意下さい。

osmconvert japan-latest.osm.pbf > map.osm

3. postgresqlにOSMデータをインポートします。
ただし物凄く時間がかかってしまい、ここから先は後編で説明させて頂きたいと思います。
コマンドとしては以下で実行できると思います。

osm2pgrouting -f map.osm -c /usr/share/osm2pgrouting/mapconfig.xml -d osm -U postgres --clean

※物凄くスワップ領域を使うので、途中でOOMKillerに”Killed”される可能性があります。
その場合はスワップ領域を増やして下さい。とりあえず一時的に20GBまで増やしてしまいました…。

# 管理者権限になる
sudo su -

# スワップ用フォルダの作成
mkdir /var/swap

# 2Gのスワップ用のファイル作成
dd if=/dev/zero of=/var/swap/swap0 bs=1M count=20480

# パーミッションの設定
chmod 600 /var/swap/swap0

# スワップ割り当て
mkswap /var/swap/swap0
swapon /var/swap/swap0

スワップ領域を削除する場合は以下のコマンドを実行して下さい。

# スワップ停止
swapoff /var/swap/swap0

#スワップ用ファイルの削除
rm /var/swap/swap0

最後に

次回はフロント側(JQuery+Ajax、ハイブリッドアプリ)もしくはAndroidアプリ(LocationManagerなどでGPSを使用するなど)で、現在位置から最も近いルートをサーバーから取得しながら、目的地へ向かってみるなど出来たら良いな…と考えております。
地図を扱うと世界旅行した気分になれますね(笑)
発想次第では色々な可能性を秘めていると思いますので、地図に関連する開発も勉強して頂けますと夢が膨らむと思います!

以上となります。
最後まで読んで頂き、大変感謝です!


こんにちは。
キャスレーコンサルティングのSI(システム・インテグレーション)部のKanemakiと申します。

皆さん、GoogleAppsScriptは活用されていますか?

これまでも技術ブログでは
 Google Apps Script入門
 Google Apps Scriptで、心温まる年賀状を作ろう!
と過去に取り扱っていますが、今回はGoogleAppsScript(以下GAS)とラベルを活用したメール整理術をご紹介します。

(続きを読む…)


12345...10...

  • Profile
    キャスレーコンサルティングの技術ブログです。
    当社エンジニアが技術面でのTips、技術系イベント等についてご紹介いたします。
  • CSV社長ブログ
  • チーム・キャスレーブログ