Docker と ecs の連携を少し掘り下げる

Docker と AWS ECS の連携でハマって、公式サイト等含めて情報も少なかったので、久々に書いてみます。

 

まずは、下記のサイト(見たのは主にオリジナルの英語版)等を参考に、docker から ecs のデプロイを試してみました。

matsuand.github.io

 

docker context create ecs コンテキスト名

とやって、AWS の認証情報を設定し、

docker context use コンテキスト名

でコンテキストを切り替えて、

docker compose up

(※ ハイフンがないことに注意)とすると、ECS にクラスタやタスク、ロードバランサが自動で作られて、デプロイされます。

ここまでを前提に、ハマった点などをいくつか取り上げます。

IAMの権限がさらに追加で必要

上記サイトの「前提条件」にあるパーミッションをつけてみましたが、足りませんでした(その後更新等されているかもしれません)。手元にリストがないですが、事項すると足りない権限がエラーに表示されるので、一つずつ追加して、さらにEC2のフルの権限をつけて、ようやく利用できました。

VPCロードバランサーの指定について

VPC は x-aws-vpcロードバランサーはx-aws-loadbalancer で指定できるとあります。これで、独自に作ったVPCロードバランサーに、ECSのクラスタを紐づけることができます。(クラスタも同様に指定できるようですが、未検証)。

例がなくて、分かりにくかったのですが、docker-compose.yml のトップのレイヤー、つまり、下記のような階層になります。私がドキュメントを見たときは、VPCVPCのARNという表記が見えましたが、IDでした。

version: "3"

x-aws-vpc: VPCのID

x-aws-loadbalancer: ロードバランサーのARN

services:

  (略)

DooD (Docker out of Docker) 環境での実行について

 CI/CD まわりを整えようと、docker 内でビルドできるようにするために、さらにいくつかトピックがあります。DinDでも同様のことが必要かと思います。

linux での実行方法

Docker と AWS の連携は、compose-cli というのが実態のようですが、Window、Mac の Docker Desktopには含まれているものの、Docker Engine には含まれていないようで、別途インストールが必要です。手順は下記のページにありますが、注意点があるので次の項目に続きます。

github.com

docker イメージでの実行

上記リンク内に、install script がありますが、docker 公式イメージ(あるいはdocker/compose のイメージ)では動作しませんでした。compose-cliのインストールスクリプトは /usr/bin に docker がインストールされていることが前提ですが、公式イメージでは /usr/local/bin にインストールされているためです。

manual install の手順だとうまく動作しました。

私の場合、docker-linux-amd64.tar.gz を解凍したものを /docker ディレクトリに配置したのですが、

ln -s /usr/local/bin/docker /usr/local/bin/com.docker.cli

としたうえで、docker 実行時は、docker ではなく、 配置した方の /docker/docker で実行します。

docker context create を対話なしで実行

docker context create ecs コンテキスト名 --profile AWSのプロファイル名

とすることで、対話なしで実行できました(ドキュメントが見当たらず、github での提案をもとに試してみたらできた…)

話がそれますが、プロファイルは .aws をコンテナに取り込むことで利用できます。

 

以上、疲れました…

 

 

スキルの高いエンジニアを採用する方法について考えてみた

 いままでにエンジニア採用をしたり、逆に採用を受けたりしたので、「スキルの高いエンジニアを採る」という観点で、経験したことのある方法についてメリット・デメリットを走り書きでまとめてみたいと思います。
 方法は一つを用いるというよりは、組み合わせることもできるかと思います。

 媒体やエージェントを選んだ経験はないので、そのあたりによってもまた変わってくるかもしれません。

GitHubのアカウントを提出してもらう

 GitHubのアカウントを提出してもらって、書類審査などをする方法です。

  • メリット
    • コーディングのスタイルがよくわかり、エンジニアなら誰が見てもレベル感もなんとなく把握しやすい。
  • デメリット
    • GitHubをアクティブに更新している人が少ない
    • 使っていても、ちょっとしたプログラムしか置いてなかったりする
    • そのような人でも、バリバリ書ける人は結構いる

コーディング課題

 候補者の方に、コーディング課題を課す方法です。

  • メリット
    • 課題をこなすだけの具体的な能力があるかが把握できる
    • どのようなコードを書く人なのかを把握できる
    • コーディング課題を課す外部のサービスもある
  • デメリット
    • (時間的な制約を考えると、)十分な難しさの問題を出すことが難しい
      • 特に、設計の能力を確認することが難しい
    • 簡単な問題を課すと、十分な実務スキルがない人でも通ってしまう
    • 課題の作成に、スキルと手間がかかる
    • 候補者を時間的に拘束してしまう

 足切りとしては有効かもしれないと思っています。

コードレビュー課題

 候補者の方に、コードをお渡しして、コードのレビューをする課題を課す方法です。
 コードにはあえてツッコみどころを入れておき、それをツッコんでもらうようなイメージです。

  • メリット
    • 開発や設計に関する知識や思考力が具体的に把握できる
    • コーディング課題よりも実施に時間が掛からない
  • デメリット
    • 課題の作成に、工夫と手間がかかる
    • 回答されたレビューを見て判断するのに、一定のスキルが必要

 結構いいかもしれないと、思いはじめています。

面接

 面接を通して、スキルを把握する。(面接の目的は他にもありますが、ここではスキルの把握に絞って書きます)

  • メリット
    • 意外と、面接だけでもスキルは把握できる
    • 設計の能力や、開発におけるコミュニケーションスキルをはかることもできる
  • デメリット
    • 候補者のスキルがどれだけ把握できるかが、面接の担当者のスキルに大きく左右される
    • 漫然と話すだけでは、開発力は全く把握できない。

 質問すべき項目を蓄積しておき、面接前にある程度決めておくことで、かなり精度を上げることはでき、質問事項をブラッシュアップしていくことができます。

 他の方法でもそうかと思いますが、面接の担当者と採用の責任者で、採用する人に必要なスキル感の認識が合っていないと、ひどいことになります。

対面でのコーディング・設計課題

 事前に課題を課すのではなく、直接お会いして、その場で課題をこなしてもらう方法です。

  • メリット
    • 課題をこなすだけの能力があるかが把握できる
    • 課題の結果だけではなく、そのプロセスを見ることができる
    • (課題の設計によっては)設計の能力を見ることもできる
  • デメリット
    • 非常に短い時間で解ける課題しか課すことができない
    • ディスカッションが必要な課題になると、面接の担当者のスキルに結果が大きく左右される

 目の前でFizzBuzzを問いてもらった経験がありますが、今考えるとあまり精度の高い方法ではなかったと思います。簡単な課題をやってもらうのは時間がもったいないと思いますが、設計課題をディスカッションしながら行うのは、設計力が見れていいかもしれないです。

作ったアプリ、サービスなどを見せてもらう

  • メリット
    • スキルが、最終的なアウトプットとして見ることができる
    • 作られたアプリやサービスの複雑さや斬新さによっても、能力をある程度把握することができる
    • (他社案件ではなく、個人制作の場合) アプリやサービスをまるっと作れるだけの広い(≠深い)知識や経験を持っていることがわかる
  • デメリット
    • (個人制作の場合、)他人が要件定義をした場合に、スキル的に要求水準が満たせない場合がある
    • (他社案件の場合、)見せてもらった画面や機能が、他の人が担当していた箇所だったり、一部修正しただけというパターンもあるので、確認が必要
    • 守秘義務や、サービス期間の関係で、必ずしも見せられるものがあるとは限らない

 昔は、ハズレがない方法と思っていましたが、意外と落とし穴があります・・・

変数の型の宣言は必要なのか?

巷で型の宣言の話で盛り上がっているようなので、自分の妄想というか、最近思っていることをひっそりと書きまとめてみたいと思います。書いてる途中で障害が発生したりして、公開しそびれていましたが、せっかく書いたので公開。

「変数」の「型の宣言」は「必要」なのか? のそれぞれのキーワードごとに話を進めていきます。

変数

そもそも、変数とは何なのかというお話です。変数とは、何かしらのひとまとまりのデータの格納してある場所を指し示すものだと、私は思っています。

a = 1;
b = "Hello";
c = ["a", "b", "d"];
char a = 1;
char *b = "Hello";
char c[] = {'a', 'b', 'c'};

上はJavaScript、下はC言語のつもりです。上の2つの例で、a には 数字1 を代入しています。メモリ上のどこかに、例えば「00000001」という値が書き込まれ、変数aはその「00000001」の格納されている場所を指し示しています。b には「hello」を代入していますが、これもメモリ上のどこかに、例えば「010010000110010001101100011011000100111100000000」のような値が書き込まれ(あるいは既に存在して)、bはその「0100...」のメモリ上の位置を指し示すようになります。
配列やオブジェクト、関数のようなものであっても、表現が複雑になるだけで、やってることは同じだと思います。

型の宣言

C言語では、変数はメモリ上のポインタを直接扱っており、char 型は 1byte、int 型は例えば4byteというように、型によって大きさが異なっていたりするので、コンパイルをする際に型の情報を見ないと、何バイト分処理していいのかすら分からなかったりしたのだろうと思います。つまり、型がないとやっていけなかったわけです。(図はイメージ図であり、適当です。)

では、型が必須なのかというとそういうことはなくて、そのメモリの場所に何の型なのかを書いておけば、コンパイル時(インタプリタでもいいですが)に型を解決する必要はなくて、型から開放されることができそうですね。

必要なのか?

ということは、プログラム言語の都合という観点でみると、もはや型は必須ではないんだなということになります。必要ではないです。

すると、もはや宗教の世界になります。

PHPのように型を意識しなくても書けちゃったりする言語もあるのですが、そういう言語を触っていると、数値が欲しいのに文字列が入っているとか、関数にDateを渡すのか、文字列の状態で渡すのか悩むとか、いろいろあって、型があると便利だなと思ったりすることもあります。Rubyで、「ダックタイピングが便利!」と思ったのも、Javaのインターフェースの仕組みと利用方法を理解してからだったりしましたし、単に好みとかスタイルの問題かなと思います。

まとめ

個人的には、「変数に型がついていなくても生きていけるけど、型がついてるともうちょっと幸せになれる。」そんな感覚です。

おまけ

こんな記事書いておいてなんですが、Objective-CJava でいうジェネリクスが使えなくてつらいです。うまく扱う方法を知っている型がいらっしゃいましたら、こっそり教えて下さいm(_ _)m。id型は却下です。

各プログラミング言語の印象

これまで使ったことがある言語の印象を、二言で述べたいと思います。

Ruby 楽しい, 自由
JavaScript シンプル, パワフル
Java 堅実, 安心
Objective-C 柔軟, キメラ
PHP 簡単, カオス

個人的には、RubyJavaScriptがオススメです☆
プログラミングをちょっとだけ触ってみたい人には、とっても簡単なPHPもオススメだったりします(が、きちんと勉強したい人には、他の言語をお勧めします)。

結論としては、どの言語もそれぞれ素晴らしいです。

JavaScriptとして動作するfizzbuzz.rb

LL Decadeで、Polyglotの話を聞いて面白かったので、私もやってみました。

そこで、FizzBuzzを書いてみました。
fizzbuzz.rbですが、JavaScriptとしても動作します。Rubyのバージョンは1.9です。

'use strict';
"#{define_singleton_method(:var){|*v|}||";/*"}"
# */ var puts=console.log; /*
# */ Number.prototype.to_s = function(){return this;};
var max=100, a=0, b=0; "#{";/*"}"
(1..max).each { |i| # */ for(var i=1;i<=max;i++){
  a = i % 3 < 1;
  b = i % 5 < 1;
  puts((a ? 'fizz' : '') + (b ? 'buzz' : '') + (!a && !b ? i.to_s() : ''));
}

JavaScriptでのシンタックスハイライト

'use strict';
"#{define_singleton_method(:var){|*v|}||";/*"}"
# */ var puts=console.log; /*
# */ Number.prototype.to_s = function(){return this;};
var max=100, a=0, b=0; "#{";/*"}"
(1..max).each { |i| # */ for(var i=1;i<=max;i++){
  a = i % 3 < 1;
  b = i % 5 < 1;
  puts((a ? 'fizz' : '') + (b ? 'buzz' : '') + (!a && !b ? i.to_s() : ''));
}

解説

ポイントは、2行目の"#{define_singleton_method(:var){|*v|}||";/*"}"です。
Rubyでは#{}が式展開されますが、JavaScriptでは展開されません。これを利用して、JavaScriptでのみコメントを開始しています。

この状態で、rubyのコードを書くと、JavaScriptではコメントアウトされているので、Rubyのみで実行することができます。
さらに、 #*/ (JavaScriptのコード) /* とやると、Rubyではコメントアウトされたまま、JavaScriptのみでコメントを解除できるので、JavaScriptのみで動かすことができます。

これにを使えば、RubyJavaScriptそれぞれのコードを含めることができますが、それでは面白くないので、fizzbuzzのロジック部分は共通化しました。

FizzBuzz部分

まず、varを使って変数宣言をします。怖い人に睨まれないように、JavaScriptで'use strict'を使っているので、varでの変数宣言が必要です。一方、Rubyにはvarキーワードがないので、define_singleton_methodでvarメソッドを定義しています。(2行目)

次に、1から100までループします。ここはうまく共通化できなかったので、先ほどの要領でJavaScriptRubyで書き分けています。ループの変数は共通してiです。

fizzbuzzでは一般的には分岐が必要ですが、if文がうまく使えなかったので、三項演算子を使いました。

Rubyでは、文字列と数値は+では結合できません。なので、to_sで文字列に変換することにしました。そのため、JavaScriptでも動くようにNumberのprototypeにto_sを追加しています(4行目)。

最後に、各行をputsで出力しています。JavaScriptでputsを使うために、putsにconsole.logを定義しています。そのため、実行のためにはconsole.logが定義されている必要があります。

元ネタ

記号でPolyglotプログラミング♪: http://developer.cybozu.co.jp/.s/takesako/2009/07/polyglotrejectk.html

ビーフストロガノフのドキュメンテーション

はじめに二言だけ。

定食屋「CodeIsDoc」

あなたはCodeIsDocという名前の定食屋さんに行きました。
メニューを見たら、写真が張ってありました。

*1

「見たら分かるでしょ?」ということのようです。

「分かりません><」

Cafe de Matz

翌日、今度はカフェに行きました。
メニューを見たら、食べ物の名前と共に、写真が張ってありました。

ビーフストロガノフ
*2

「名前重要ですね。」

レストラン Plenty

さらに次の日、レストラン「Plenty」というところに行きました。メニューを見てみると

ビーフストロガノフ
*3
〜ロシアの代表的な料理です。ハッシュドビーフにも似ていますが、サワークリームも使っています〜

「なるほど、よく分かりました。」

まとめ

ドキュメントが大事というのがよく分かるのではないかと思います。

ところで、いろいろな文献を参考に、適当にビーフストロガノフの説明文を書きましたが、実際にはどういう料理なんでしょうか?

Railsで規約に従わないテーブル名を使う

Ruby On Railsでデータベースを扱う時は、Railsの規約に従ってモデル名、テーブル名等を決定します。しかし、古いシステムからDBを引き継ぐ、他のプログラムとDBを共有する等、場合によっては、規約から外れた名前が必要になることがあります。

今回は、そんな場合にどうするかの内容です。

なお、Rails単体で新しくアプリを作る場合は、規約にきちんと従うことを強くお勧めします。

この内容は、Yokohama.rbのLT大会で話した内容をまとめ直したものです。(初めてのLTだったので、スライドを晒す勇気がなかったので、日記にまとめて書くことにしました。)

テーブル名を規約から変える

例えば、ユーザのテーブルを作る場合、モデル名は大文字始まりの単数形(例:User)、テーブル名は小文字始まりの複数形(例:users)を使うのがRailsの規約です。Userという名前のモデルクラスがある場合、自動的にusersテーブルを読みに行くようになっています。

これを変えたい場合、Rails3.0の場合、

def User < ActiveRecord::Base
  set_table_name 'myuser'

end

のように、set_table_nameを使います。が、Rails3.2に上げたら、deprecation warningが出ました。Rails3.2では、

def User < ActiveRecord::Base
  self.table_name = 'myuser'

end

と、self.table_name に代入します。
(※Rails3.1でどうだったかは試してないです確か、Rails3.1からの変更だったと思います。定かではないので、間違っていたらごめんなさい。)

単数形を使いたい場合も同じようにできます。
(※単純に複数形が不規則なだけの場合は、/config/initializers/inflections.rb で設定しましょう。)

テーブル名を規約から変えた時のテスト

テストでは、set_fixture_classを使うことで、フィクスチャの名前と、モデルクラス名を対応付けすることができます。まず、フィクスチャは、テーブル名に従って名前を付けます(例: myuser.yml)。そして、テストでは、fixturesで利用するフィクスチャを定義する前に、set_fixture_classでフィクスチャとモデルクラスの結びつきを定義します。

describe User do
  set_fixture_class :myuser=> User
  fixtures :myuser #, :follows 等と、必要なものを挙げる

end

主キーをidから変える

主キーをidから変えたい場合は、set_primary_keyを使うことができますが、

def User < ActiveRecord::Base
  set_primary_key :username

end

Rails3.2では、同様にprimary_key=を使うようです。多分。

def User < ActiveRecord::Base
  primary_key = :username

end

複合主キー

Railsでは基本的にサロゲートキー(代理キー)を使って、idが主キーになりますが、場合によっては複合主キーが使いたいこともあります。
そんなときは、composite_primary_keysというgemを使います。

まず、Gemfileに

gem composite_primary_keys

を追加して、bundle installをします。

次に、set_primary_keys(さっきと違って複数形です)で主キーを設定します。

def Follow < ActiveRecord::Base
  set_primary_keys = :user_id, :following_user_id
end

Rails3.2に対応したバージョンのものでは、Railsの変更に合わせて、primary_keys=になっています。

def User < ActiveRecord::Base
  primary_keys = :user_id, :following_user_id

end

まとめ

このあたりまで出来れば、大分いろいろなテーブル構造に対応できるとおもいます。

RailsというとCoC(設定より規約を)によって規約に従わないといけないイメージがありますが、規約に従えば設定をしなくてもいいだけで、細かい設定はいろいろすることができます。

Railsは柔軟であるということが、よくお分かりいただけるのではないかと思います。

あと、最後に言っておきますが、規約に従って差し支えない時は、積極的に規約に従ったほうがシンプルになって簡単です。