読者です 読者をやめる 読者になる 読者になる

hako oneshotで何度でもDigdagのsession情報を保持する

以前こんな記事を書きました

hatappi.hateblo.jp

この記事ではDigdagをDocker上で動かす際にsession情報をDataVolumeにおくことでコンテナを立ち上げ直した時もsession情報を参照できるようにするというものでした
これはECS上でも実現することは出来る データボリュームについてかかれたドキュメント

画面上から作成することも可能だが最近Hakoを使っているのでこれを使って実現したい

今回実現したいこと

コンテナインスタンスにボリュームを追加してコンテナにてマウントポイントを指定したい

Hakoの設定

scheduler:
  type: ecs
  region: ap-northeast-1
  cluster: test-cluster
app:
  image: digdag-sample
  memory: 3000
  mount_points:
    - source_volume: digdag_session
      container_path: /digdag_session
      read_only: false
volumes:
  digdag_session:
    source_path: /mnt/digdag

こんな感じに設定をするとコンテナインスタンス側の/mnt/digdagとコンテナ側の/digdag_sessionがマウントされるのでコンテナ側でdigdagをrunする時は下記のようなコマンドで実現できる

$ hako oneshot sample.yml -- digdag run sample.dig --save /digdag_session

Hakoをコンテナインスタンス0の状態からoneshotする

hatappi.hateblo.jp

昨日はHakoで狙ったコンテナインスタンスにたいしてoneshotする記事を書きました

このoneshotを僕が使う時はバッチ目的として使用するのでサービスの用にコンテナインスタンスを立て続けるわけではありません
もちろん起動し続けたままでも良いのですが、バッチが半日に1回の場合など使用しない時間が長い場合はお金がもったいないです

そのためバッチを使用する時にコンテナインスタンスを立てたかったのです
aws cliaws sdkで自分の好きな方法でインスタンスをたててからhako oneshotを使用しても良いのですが
Hakoにはリソースが足らない時にスケールアウトしてくれる素敵な機能がついています

下記のブログのオートスケーリングにも記載があるのですがautoscaling_group_for_oneshotにリソースが足らない時に
スケールアウトするAutoScalingグループ名を設定します
つまりこの素敵な機能を使うにはAutoScalingグループにてコンテナインスタンスを管理していないといけないようです

techlife.cookpad.com

コード的にはここらへん 中身も難しいことはしてなくてSDKを使ってAWSAPIを叩きつつ現在のコンテナインスタンスの数やリソース状況をみて足りない場合にスケールアウトしている

ただv1.3.1だと指定クラスタ内にコンテナインスタンスが0だと例外が出てスケールアウトされなかったのでPRを下記で送り

github.com

無事マージしていただきました 2017/05/03時点でv1.3.3ですが、このバージョンにて取り込まれています :tada:

hako | RubyGems.org | your community gem host

コード

scale_out.yml

scheduler:
  type: ecs
  region: ap-northeast-1
  cluster: test-cluster
  autoscaling_group_for_oneshot: test-auto-scaling-group
app:
  image: busybox
  memory: 1000
$ hako oneshot scale_out.yml -- echo "Hello"
~~
I, [2017-05-23T20:36:18.938312 #8533]  INFO -- : Increment desired_capacity of test-auto-scaling-group from 0 to 1
~~

実行するとdesired_capacityを0から1にしてインスタンスの希望台数を1にしていることが分かる
後はしばらく待つといつも通り処理を開始してくれる

Hakoで狙ったコンテナインスタンスにoneshotしたい

1週間くらい前からHakoに入門している @hatappiです

hatappi.hateblo.jp

以前hako oneshotを使用してサンプルのようなものを動かしました

今回は少しECSのタスク配置を掘り下げてそれをHakoで設定しようというものです

ECSにはクラスター内でタスクを起動する際にCPUやMemoryなどに基づいてタスクを配置していきます
これらはカスタマイズできてECS内ではタスク配置戦略, タスク配置の制約事項という2種類の定義があります

それぞれを公式のドキュメントから引用すると

タスク配置戦略

タスク配置戦略は、タスク配置またはタスクの終了でインスタンスを選択するためのアルゴリズムです。
たとえば、Amazon ECS でランダムにインスタンスを選択することも、インスタンスのグループ間に均等に分散されているタスクを持つインスタンスを選択することもできます。

タスク配置の制約事項

タスク配置の制約事項は、タスク配置中に考慮されるルールです。
たとえば、アベイラビリティーゾーンやインスタンスタイプに基づいたタスク配置の制約を使用できます。
名前と値のペアである属性をコンテナインスタンスに関連付け、属性に基づいて制約事項を使用しタスクを配置できます。

となっております
なるほど???
例としてはタスク配置戦略として複数のアベイラビリティーゾーンでタスクを均等に分散してタスク配置の制約事項としてインスタンスタイプがt2.mediumなものに配置といった感じでしょうか

今回に関しては後者のタスク配置の制約事項を使用していきます

やりたいこと

1つのクラスター内に6台のコンテナインスタンスが起動しているとする
ただこの内の1台だけはworkerAとして利用して他の5台でworkerBを動かしたいとする

やったこと

コンテナインスタンスには属性と呼ばれるカスタムメタデータがありECS側ではAMIのIDやインスタンスタイプなどが提供されています
この属性はユーザーがカスタム属性として追加することも出来るようになっています

コンテナインスタンスへのカスタム属性の追加に関しては下記の公式ドキュメントが参考になります
Amazon ECS タスク配置の制約事項 - Amazon EC2 Container Service

今回はコンテナインスタンスを起動したり削除したりを繰り返すので都度手動で設定するのは面倒だったのでAMIを作成する時に
/etc/ecs/ecs.configECS_INSTANCE_ATTRIBUTES={"role": "workerA"}のようにecsの設定ファイルに追記しています
※ 元となるAMIに関してはECS用に最適化されたものを使用しています 参考

今回ですとコンテナインスタンスrole=workerAという属性をもたせたので下記コマンドでフィルタリングできると思います

$ aws ecs list-container-instances --cluster my-cluster --filter 'attribute:role == workerA'
{
    "containerInstanceArns": [
        "arn:aws:ecs:ap-northeast-1:1111111:container-instance/aaaaaaaa"
    ]
}

最後にHakoの設定していきます
ところがここで1つ問題が発生しました
AWSRunTaskApi側にはタスクの配置戦略が設定出来るのですが
hako oneshotではそれが設定できませんでした

ここにきて出来ませんは残念な結果になってしまうので本体にPRを作成しました
そして無事マージしていただいたで安心して使用できます

github.com

設定はファイルは前回使用したものを編集して

test.yml

scheduler:
  type: ecs
  region: ap-northeast-1
  cluster: test-cluster
  placement_constraints:
    - type: 'memberOf'
      expression: 'attribute:role == workerA'
app:
  image: busybox
  log_configuration:
    log_driver: awslogs
    options:
      awslogs-group: test
      awslogs-region: ap-northeast-1

重要なところは

  placement_constraints:
    - type: 'memberOf'
      expression: 'attribute:role == workerA'

というところでこれによりカスタム属性として role=workerAと追加したインスタンスにたいしてタスクが配置できるようになります
後はこれをworkerBにも設定してあげれば hako oneshotで狙ったインスタンスにタスクを配置することが出来ます

最後に

Hako楽しい

Rails+webpackerでAngularをさわってみる

出遅れた感はありますが今回はRails5.1からrailsで使用できるようになったwebpackerを使用してみようと思います
※ 2017/05/20時点の情報をもとにかいてます

webpackerとは?

rails/webpackerはRails5.1からRails配下に入ったyarnでパッケージを管理してくれたりjsでビルドをしてくれる 現状はAngular, React, Vue.jsを指定してはじめられる

準備

Rails5.1からnew時に--webpackを指定するとyarn installや--webpack=[angular|react|vue]でそれぞれに必要なパッケージを追加してインストールなど開発に必要なものを用意してくれるようになりました

$ rails -v
Rails 5.1.1
# 今回はAngularを使いたいのでオプションで指定
$ rails new --webpack=angular sample

ディレクトリの中身を覗いてみる

Gemfileには gem 'webpacker' が追加されていて
app/javascript配下に下記のようなものが展開されている app/javascript/packs配下のものがエントリーポイントとしてデフォルトとして指定されている

.
├── hello_angular
│   ├── app
│   │   ├── app.component.ts
│   │   └── app.module.ts
│   ├── index.ts
│   └── polyfills.ts
└── packs
    ├── application.js
    └── hello_angular.js

とりあえず動かす

まずは適当な画面を作成する

$ rails g controller home index
$ open http://localhost:3000/home/index

f:id:hatappi1225:20170520180145p:plain

次に

application.html.erb内にjavascript_include_tagのかわりでも良いですし追加で<%= javascript_pack_tag 'application' %>を記載します
そしてrails serverとは別にコマンドラインから./bin/webpack-dev-serverでwebpack-dev-serverを起動

これでアクセスをすると画面上は変更はないのですがブラウザのコンソール上には

f:id:hatappi1225:20170520180211p:plain

と出ていると思います

これはapp/javascript/packs/application.jsがwebpackによってビルドされたものが見えており
このファイルを編集してあげるとwebpack-dev-serverによって自動でビルドしてブラウザもリロードしてくれます :tada:

次にAngularのプロジェクトを読み込んでみます
rails new --webpack=angularした時にapp/javascript/packs/hello_angular.jsにサンプルがおかれているのでこれを使います

例えば先程作成した home#indexのviewに<%= javascript_pack_tag 'hello_angular' %>を追加してあげて
タグとしては<hello-angular>Loading...</hello-angular>を追加します

<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

<hello-angular>Loading...</hello-angular>

<%= javascript_pack_tag 'hello_angular' %>
<!-- タグよりも上にjavascript_pack_tagをおくとERROR Error: The selector "hello-angular" did not match any elementsとエラーが出ます --> 

今回はviewをいじっただけなのでwebpack-dev-serverによって自動ではリロードされないので自分でブラウザをリロードします

f:id:hatappi1225:20170520180415p:plain

でた!!

コンポーネントを追加してみる

テキストをうけとったらそれでButtonを作成してくれるコンポーネントを追加してみます

まずapp/javascript/hello_angular/app/text-button.component.tsを作成して下記のように記載します

import { Component, Input } from '@angular/core';

@Component({
  selector: 'text-button',
  template: `<button>{{text}}</button>`
})
export class TextButtonComponent {
  @Input() text: string;
}

次にapp/javascript/hello_angular/app/app.module.tsに先程のTextButtonComponentを追加してあげます

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { TextButtonComponent } from './text-button.component';

@NgModule({
  declarations: [
    AppComponent,
    TextButtonComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

そして最後にapp/javascript/hello_angular/app/app.component.tsのtemplateに下記のように<text-button text="WORLD!"></text-button>に追加したら終わりです

import { Component } from '@angular/core';

@Component({
  selector: 'hello-angular',
  template: `
    <h1>Hello {{name}}</h1>
    <text-button text="WORLD!"></text-button>
  `
})
export class AppComponent {
  name = 'Angular!';
}

そうすると画面は自動でリロードされ

f:id:hatappi1225:20170520180547p:plain

と表示される!!

最後に

今回はRailsにwebpacker gemを追加してAngularを使ってみました

ここからは個人的な思いになるのですが
Angularは公式サイトにもかかれている通り

One framework

とかかれているので本来はhttp clientやrouterがAngular側で提供されているのでそれを使ってアプリケーションを作成していきます
今回のようにRailsのviewの一部のコンポーネントとして使用するイメージがあまりわきませんでした

個人的にはAngularをやるなら下記を使用していくと思います

github.com