Programming

2016年12月 7日 (水)

CodeBuildでgo言語のプロジェクトをビルドする際の注意事項

こんにちは。エンジニアの島袋です。

前回に引き続き、AWS CodeBuildの話になります。

CodeBuildでgo言語のプロジェクトをビルドする場合のハマりポイントに気づいたので、そちらを紹介します。

要点から先に言うと、以下です。

  • CodeBuildは/tmp配下にリポジトリをチェックアウトするため、ビルド対象がGOPATH配下にない
  • GOPATH配下にないとvendoringが有効にならないため、go build時に「パッケージが見つからない」と言われる

go buildがコケる??

前回のエントリではbuildspec.ymlのphase名をtypoしたせいでドハマリしてたんですが、その後typoを修正し、再度ビルドを実行すると、今度はgo buildでコケました。

エラーメッセージには「cannot find package〜」と表示されています。

依存しているパッケージが見つからないということですが、そんなはずはありません。

installフェイズでglideを使い、依存パッケージは全てvendorディレクトリの中にダウンロード済みです。

では何が駄目だったのでしょうか?

ビルド対象のプロジェクトがGOPATH配下になかったため、vendoringが有効になっていなかった

GOPATHとvendoringについて簡単に説明しておくと、GOPATHはgo言語を利用する際に設定が必要になる環境変数で、ユーザが任意のパスを指定する必要があります。

go言語でbuildコマンドを叩くと、buildに必要な依存パッケージをGOPATH/src配下から走査します。

(go get "パッケージ名" とコマンドを叩くと、GOPATH/src配下にパッケージをダウンロードできます)

ところが、この方法ではプロジェクト毎に利用するパッケージのバージョンを管理することができません。

(go getコマンドは常に最新版を取得します)

そこで追加された機能がvendoringです。

vendoringは有効な状態では、GOPATH/src配下を走査する前に、プロジェクトのルートディレクトリ直下のvendorディレクトリからパッケージを走査します。

上述のglideはvendorディレクトリ内に依存パッケージをダウンロードするパッケージマネージャです。

(rubyにおけるbundlerのようなものですね)

そんな便利なvendoringなんですが、ビルド対象のプロジェクトディレクトリをGOPATH/src配下に配置していないと有効になりません。

ところが、CodeBuildはビルド対象として指定したリポジトリを"/tmp/src257031055/src"といった感じで/tmp配下にチェックアウトしてしまうんですね。

当該ディレクトリ内のvendorディレクトリ内に依存パッケージをダウンロードし、go buildコマンドを叩いても、vendoringが有効になっていないため、GOPATH/src配下した走査してくれず、「cannot find package〜」と表示されてしまうわけです。

筆者含め、go言語利用者はGOPATH/src配下にディレクトリを作ってコードを書き始めるのが半ば常識化しているため、vendoringが無効になっていることに気づくのに時間を要しました。

回避策

とりあえずのワークアラウンドなんですが、以下のようにしました。

...省略...
  pre_build:
    commands:
      - glide install
      - mkdir -p $GOPATH/src/github.com/stk132/tsg
      - mv * $GOPATH/src/github.com/stk132/tsg/.
  build:
    commands:
      - cd $GOPATH/src/github.com/stk132/tsg && go build

はい。pre_buildフェイズでGOPATH/src配下にディレクトリを切って、CodeBuildがチェックアウトしてきたリポジトリの中身を全てコピーしています。

(これで当該ディレクトリの中ではvendoringが有効になります。)

そしてbuildフェイズでは当該ディレクトリに移動して、go buildを実行します。

(cdコマンドとgo buildをチェインしているのは、別の行にわけて実行すると、元のワーキングディレクトリに戻っているためです。多分Dockerfileのビルド時と同じ挙動??)

これ、もうちょっとスマートな解決方法ないですかね。。。。?

GOPATH=$GOPATH:pwd

とかして、ワーキングディレクトリをGOPATHに追加する方法も考えたんですけど、それだとビルド対象プロジェクト内のサブパッケージが走査できなくなるんですよね。

終わりに

よくよく考えると、jenkins等も同様の問題をはらんでいる気がするのですが、もしかしてgo言語のプロジェクトのCIを回している人にとっては今回のようなのは常識だったりするのでしょうか?

筆者は専らローカルで利用するCLIコマンドばかり作っているため、ビルドもローカルマシン上で行なっていたため、まんまとハマってしまいました。

2016年12月 5日 (月)

AWS CodeBuildに入門失敗した話

こんにちは。エンジニアの島袋です。

今回のエントリ内容については表題の通りです。

typoで日曜の夜をふいにしました。

要約すると以下です。

  • CodeBuildはbuildspec.ymlのphase名をtypoしてもエラーにはならない
  • phase名をtypoすると、phasesが全て無視される?

AWS CodeBuildとは

re:invent 2016で発表されたAWSの新しいサービスの1つで、コンテナベースのフルマネージドなCI環境を提供する的なやつです。

(CodeBuild単体だとビルドに特化してるから、CIとは言わないかな?)

ユーザが自分で管理するjenkinsなどと比較して、勝手にスケールしてくれるので、ビルドキューが詰まって長時間ビルド完了待ちをすることがないというのがウリのようですね。

(あと根本的にサーバー管理の必要がない)

詳細については

あたりを参考にしてください。

早速使ってみようとしたところ。。。

CodeBuildではビルド手順をyaml形式でbuildspec.ymlというファイル名で保存し、ビルド対象のプロジェクトのルートディレクトリ直下に配置する必要があります。

今回は筆者が作ったslackにコメント投稿するためのgo言語製のCLIコマンドをビルドしました。

buildspec.ymlは以下のようになります。

version: 0.1

environment_variables:
  plaintext:
    GOPATH: "/go"

phases:
  install:
    commands:
      - mkdir -p $GOPATH/{bin,src}
      - curl https://glide.sh/get | sh
  pre-build:
    commands:
      - echo prebuildstart
      - PATH=$PATH:$GOPATH/bin
      - echo prebuildend
  build:
    commands:
      - echo buildstrat
      - glide install
      - go build
  post_build:
    commands:
      - echo Build completed on `date`


artifacts:
  type: zip
  files:
    - oshirase

一応buildspec.ymlの記法について説明すると、以下のようになります。

  • environment_variables : 環境変数の設定
  • phases : 実際のビルド手順
    • install : ビルドに必要なコマンドなどのインストール
    • pre_build : ビルド前の作業
    • build : ビルド作業
    • post_build : ビルド後の作業
  • artifacts : zipにパッケージングするビルド成果物の指定

はい。pre_buildとするところをpre-buildとしてしまったんですね。

これに気づくのに二時間近く費やしました。

なぜそれほど時間がかかったかというと、phasesの中に誤ったphase名を記述しても、エラーにならず、ビルド完了となってしまったためです。

ビルドは完了しているにも関わらず、artifactsに指定したビルド成果物が見つからないとうログが出力されていたためおかしいと思い、artifactsの内容について試行錯誤してたんですね。

(そもそもビルド成果物見つからないのであればエラーにしてほしいところなんですが)

今回の場合、typoしたpre_buildだけじゃなく、phasesを全て素通りしてartifactsの処理だけ走っていたようです。

(それが規定動作なのかは現状不明ですが)

終わりに

typoには気をつけましょう。

あと、codebuildはビルド対象のブランチを指定できないようですね。codebuild単体だとjenkins氏やCircleCI等のCIサービスにすぐにとってかわるということは無さそうです。(他のcode〜シリーズと組み合わせること前提ですかね?)

2016年10月24日 (月)

stoplight.ioを使ってみよう

こんにちは。エンジニアの島袋です。 先週末「君の名は」を見てきました。 おもしろかったです。(小学生並みの感想)

今回はstoplight.ioというwebサービスについて紹介します。

TL;DR

  • stoplight.ioを利用すると
  • swagger-specの作成がめちゃくちゃ捗る

本題に入る前に、swaggerについて

Swaggerとは

swaggerは大雑把にいうと、REST APIを仕様を記述するためのツール群です。 swaggerについては以下のスライドが詳しいです。

Swaggerで始めるモデルファーストなAPI開発

このswagger、何が嬉しいかといいますと、apiのレスポンスがswaggerで記述した仕様(swagger-spec)に準拠しているか検査できるんですね。

これを目視、手動でやるとか正直考えたくないです。

(筆者は前職で手動でやってました)

そもそもREST APIに仕様書いるのかっていう話なんですが、まあ必要ですよね。

API実装を外注に出す場合、あるいはこちらがAPIの実装を受託する場合など、特に必須になると思います。

その他、自社サービスのAPIをサードパーティ向けに公開する場合なども必要になってくるでしょう。

また、swagger-specはswagger-uiで人間が読むためのドキュメントとして表示できることも嬉しいポイントです。

(swagger-ui立ち上げるのが面倒くさい場合はbootprint-swaggerで静的htmlに落とします)

Swaggerのつらいところ

そんな便利なswaggerなんですが、swagger-specを記述するのはかなり大変です。

swagger-specはjsonかyamlで記述するのですが、レスポンス項目が5つしかない簡単なサンプルでも以下のような分量になります。

swagger: '2.0'
info:
  version: ''
  title: MySample
  description: sample for cloud blog
host: 'localhost:9944'
basePath: /api
schemes:
  - http
paths:
  '/student/{student_id}':
    parameters:
      - name: student_id
        in: path
        required: true
        type: string
    get:
      operationId: GET_student-student_id
      produces:
        - application/json
      responses:
        '200':
          description: ''
          schema:
            type: object
            properties:
              student_id:
                type: string
              grade:
                type: integer
                minimum: 1
                maximum: 6
              first_name:
                type: string
                minLength: 1
                maxLength: 20
              last_name:
                type: string
                minLength: 1
                maxLength: 20
              hobbies:
                type: array
                items:
                  type: string
                  minLength: 1
                  maxLength: 100
            required:
              - student_id
              - grade
              - first_name
              - last_name
              - hobbies
          examples:
            application/json:
              student_id: aliqua sint proident nostrud
              grade: 3
              first_name: id consect
              last_name: magna mol
              hobbies:
                - dolore Lorem ut elit dolor
                - ad nisi amet adipisicing
                - ipsum aliqua
                - consequat sunt
definitions: {}

はい。つらいですね。

それを解消できるサービスがstoplight.ioです。

stoplight.ioについて

stoplight.ioは簡単にいうと、REST APIをブラウザ上でモデリングし、swagger-specとしてエクスポートすることが可能なサービスです。

(ブラウザ上といいましたが、デスクトップアプリも提供されています。(恐らくElectron製))

個人で利用する分には無料のようなので、とりあえず使ってみましょう。

githubアカウントでもサインアップ可能です。

stoplightで簡単なAPIエンドポイントを作成してみる

上記で貼り付けたyamlの内容と同じものをstoplightで作成してみます。

/student/{student_id}にGETリクエストを投げると、該当する学生の情報をjsonで返却するものとします。

返却項目は以下とします。

  • student_id : 学生番号(必須, 文字列)
  • grade : 学年(必須, 数値1〜6の間)
  • first_name : 名(必須, 文字列, 1文字以上、20文字以内)
  • last_name : 姓(必須, 文字列, 1文字以上、20文字以内)
  • hobbies : 趣味(必須, 文字列配列, 無い場合は空の配列, 要素は1文字以上、100文字以内)

Stoplight_dashboard


まず、ダッシュボード画面から、New Endpointを選択します。

http methodや、endpointのパスなどの入力欄が表示されますので、下図のように入力します。

Endpoint_basic


次にレスポンス項目を設定していきます。

画面を下にスクロールしていくと、Responsesという項目があるので、そちらでレスポンス項目の設定を行っていきます。

「object」の横の+をクリックすると、レスポンス項目を追加していくことができます。

各レスポンス項目左側がプロパティ名、右側がプロパティの型となります。

プロパティ名は当然自分で入力する必要がありますが、型についてはjson-schemaで定義されている型が下図のように選択肢としてポップアップするので、そこから選択することができます。

Type_select


response項目にはバリデーション設定を加えることができます。

右端にあるvalidationsとrequiredというチェックボックスを見てください。

requiredにチェックを入れると、その項目は必須返却項目となります。

validationsをクリックすると、詳細なバリデーション設定項目が表示されます。

ここでは返却値の文字数制限(最小、最大)や、返却値の文字種の制限(正規表現)などの設定が可能です。

Validations_2


一通り入力し終えたら、JSON Schemaタブを開いてみます。

JSON Schemaタブにはeditorタブで入力した内容がJSON Schemaに変換されたjsonが表示されます。 どうでしょうか?

swagger-editorではここで表示されている内容を自分で入力する必要がありましたが、stoplightのeditorでは非常に簡単に入力することができたと思います。

Response_json_schema


次にexampleタブを開いてみます 。 最初は何も表示されていないと思います。

GENERATE EXAMPLE FROM SCHEMAをクリックしてみてください。

レスポンスのサンプルが表示されるはずです。

ここでGENERATE SCHEMA FROM EXAMPLEを押下すると、レスポンスサンプルからjson schemaが出力されます。

(なのでクリックしないでください。)

逆にいうと、「こんな感じの出力が欲しい」というサンプルをベースに仕様を起こすことが可能ということです。

Responses_example


ここまで完了したら、画面上部にあるsaveをクリックし、エンドポイントの作成を保存します。

最後にyamlでswagger-specとしてエクスポートしてみます。

画面右上のexportをクリックしてください。

エクスポート形式が復数表示されるので、ここでは

OAS(Swagger 2).yamlを選択します。

Export_select


するとブラウザの別タブが開き、yaml形式のswagger-specが表示されますので、右クリックから別名で保存でファイルに保存します。

stoplight.io便利なので使いましょう

如何でしたでしょうか?(大分雑な説明でしたが)

エクスポートしたyamlファイルの長さを見ると、swagger-editorで記述するのと比較し、stoplightでモデリングを行うのがとても楽だというのが分かると思います。

今回はstoplightのモデリング機能に絞って紹介しましたので、次回はstoplightのmock機能について紹介したいと思います。

(いつになるかわかりませんが)

2016年7月10日 (日)

Go言語のクエリビルダ−をタイプセーフに扱いたい

こんにちは。エンジニアの島袋です。

今回のテーマは表題の通りです。

TL;DR

  • クエリビルダにおけるテーブル名、カラム名の指定がタイプセーフではないため
  • 正確なテーブル名、カラム名を出力する関数を自動生成するコマンドを作り
  • クエリビルダをタイプセーフに扱えるようにした

Go言語におけるクエリビルダー

Go言語にはORMとしてメソッドチェインでSQLを組み立てるクエリビルダーと呼ばれるタイプのライブラリがいくつか存在します。

クエリビルダーでSQLを構築する場合、以下のようなコードになります。(dbr利用の場合)

クエリビルダは直感的にSQLが組み立てられるため、学習コストが低く扱い易いのですが、一つ問題があります。

上記の通り、カラム名やテーブル名を文字列で指定するため、タイプセーフではありません。

テーブル名やカラム名をtypoしてしまうと、クエリビルダによって生成されたSQLは実行時にエラーとなります。

これを解消するためには、正確なテーブル名、カラム名を出力してくれる関数があれば良さそうです。

そこで、データベースのメタデータからGo言語のソースコードを生成するCLIツール tsg を作ってみました。

(javaだとquerydslとかjooqとかコード生成機能付きのクエリビルダーがあるんですけどね)

tsg

tsgは以下のように使います。

(hostはlocalhost, portは5432がデフォルト)

  • u : データベースのユーザ名
  • pass : データベースのパスワード
  • d : コード生成対象のデータベース

コマンド実行時、以下のテーブルがあったとすると、

  • テーブル名 : users
    • カラム名 : id
    • カラム名 : name

以下のソースコードが出力されます。

このコードを利用すると、冒頭のクエリビルダ−のサンプルは以下のように修正できます。

上記のように、関数から正確なテーブル名、カラム名が取得できることが保証されていれば、Goのビルドが通った時点でテーブル名、カラム名について正確なSQLが発行されることが保証されます。

(SQLが意図通りかどうかは別)

さらに、エディタの自動補完機能を利用すると関数のtypo自体も防げます。

(文字列を直接指定する場合、エディタの自動補完機能に頼ることができません)

生成される構造体、関数の機能

下記のコードのコメントアウト部分のように、N()でテーブル名、カラム名が出力されます。 テーブルに別名を与える場合はA()を実行し、そのままメソッドチェインでつなげてN()を実行します。

未実装の機能

サポートしてるRDBが現状postgresqlのみです。

また、出力するソースコードにコメントがないため、golintを使っていると大量に警告が出ます。

加えて出力後のソースコードが整形されていないため、手動でgofmtをかける必要があります。

終わりに

今回のツールによりクエリビルダーをタイプセーフに扱うことができるようになりました。

(週末に作ったのでまだ実務で使っていませんが)

実はまだクエリビルダ−を便利に扱う上で課題がありまして、現状select文の結果をマッピングするための構造体は自分で手書きで作成する必要があります。

単一のテーブルに対応するだけでよければ、今回のように自動生成することも難しくはないのですが、問題はテーブルをjoinするケースです。

クエリビルダーにより出力されたSQLを見ないと、どのテーブルのどのカラムが必要なのか分からないため、事前にselect結果をマッピングする構造体を自動生成するということができません。

このあたり、何かうまいソリューションはないものでしょうか。。。。

2016年6月25日 (土)

Go言語でトランザクション制御のラッパー関数を作った話

こんにちは。エンジニアの島袋です。ブログ初投稿になります。

今回はGo言語でトランザクション制御のためのラッパー関数を作った話をします。

なぜラッパー関数が必要になったのか

最近はginを使ってAPIを実装しているのですが、ginにはトランザクション制御のためのミドルウェアがなく、自作する必要がありました。

(手動でbegin, commit( or rollback)を書くのはさすがにつらいので。。。)

ググったところ、違うWAFですがechoのサンプルリポジトリのeurie-inc / echo-sampleが参考になりそうでした。

以下はecho-sample/middleware/transaction.go からの抜粋です。

func TransactionHandler(db *dbr.Session) echo.MiddlewareFunc {

    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return echo.HandlerFunc(func(c echo.Context) error {

            tx, _ := db.Begin()

            c.Set(TxKey, tx)

            if err := next(c); err != nil {
                tx.Rollback()
                logrus.Debug("Transction Rollback: ", err)
                return err
            }
            logrus.Debug("Transaction Commit")
            tx.Commit()

            return nil
        })
    }
}

上記はミドルウェアの中でトランザクションを開始し、httpハンドラ本体からerrorが帰ってきたらrollbackを実行、errorが帰ってこなければcommitするという実装になっているようです。

これをパクって真似してginのミドルウェアに移植すればよさそうです。

(ところで、筆者はつい最近までSAStruts(のカスタム)で仕事をしていたのですが、GoのWAFでいうMiddlewareってSAStrutsでいうところのInterceptorなんですよね。やらせる仕事は同じなのに言語やフレームワークで用語が異なるのってちょっとめんどくさ混乱しますね。)

2,3行書き換えれば終わりじゃん、そう考えていた時期が私にもありました

ほぼコピペでいけるじゃないですかと、楽勝ムード漂っていたときにあることに気づきました。

type HandlerFunc func(*Context)

上記はginのHandlerFunc型の関数シグネチャです。

本体処理のハンドラも、middlewareも上記のシグネチャを満たす必要があります。

ええ、返り値がないんです。よって、errorをreturnしてmiddlewareになんらかの処理をさせるということができません。

/(^o^)\

error発生時にpanicにすればmiddlewareの中でrecoverで拾うことは可能ですが、panicを多用するのってgo言語の作法的にどうなんですかという問題があります。

ラッパー関数という手段

さらにググったところ、stackoverflowのdatabase/sql Tx - detecting Commit or Rollbackという投稿が見つかりました。

以下は回答者のコードの抜粋です。

func Transact(db *sql.DB, txFunc func(*sql.Tx) error) (err error) {
    tx, err := db.Begin()
    if err != nil {
        return
    }
    defer func() {
        if p := recover(); p != nil {
            switch p := p.(type) {
            case error:
                err = p
            default:
                err = fmt.Errorf("%s", p)
            }
        }
        if err != nil {
            tx.Rollback()
            return
        }
        err = tx.Commit()
    }()
    return txFunc(tx)
}

実行したい関数を引数txFuncとして受け取り、トランザクションを開始してからtxFuncを実行。

txFuncの中でerror、もしくはpanicが起こったらdeferのところでrecoverを使って拾い、rollbackを実行、問題なければcommitという流れのようです。

これはいいですね。middlewareでトランザクション制御を行う場合、トランザクション境界がリクエストの開始〜終了となりますが、ラッパー関数方式の場合、トランザクション境界を柔軟に変更することが可能です。

gin用に書き換えてみた

func Transact(sess *dbr.Session, c *gin.Context, txFunc func(*dbr.Tx, *gin.Context) error) (err error) {
    tx, err := sess.Begin()
    if err != nil {
        return
    }
    defer func() {
        if p := recover(); p != nil {
            switch p := p.(type) {
            case error:
                err = p
            default:
                err = fmt.Errorf("%s", p)
            }
        }
        if err != nil {
            tx.Rollback()
            return
        }
        err = tx.Commit()
    }()
    return txFunc(tx, c)
}

txFuncの中でginの*Contextが参照できるように書き換えてみました。

また、筆者はORマッパーとしてdbrを利用しているので、database/sqlを使っている部分をdbr用に書き換えてあります。

(dbrの説明はdbr – Go 言語 O/R Mapper の紹介が詳しいです。)

このラッパー関数は以下のように使うことができます。

func hoge(c *gin.Context) {
   sess := conn.NewSession(nil)
   err := Transact(sess, c, func(tx *dbr.Tx, c *gin.Context) error {
              // なんらかの処理
              })

  if err != nil {
    //エラー処理
  }
}

とりあえずトランザクション制御できるようになった

上記のラッパーを使ってとりあえずはトランザクション制御ができるようになりました。

go言語、書いてて楽しいのですが、WAFは薄いものが多いため、結構足回りの機能を自作する必要がある感じです。

(まあ今回はほぼコピペだったわけですが。。。)

このあたりの知見がもっと共有されるようになると嬉しいですねー

2015年11月 2日 (月)

express実践入門ついての社内勉強会を開いてみた

みかんが美味しい季節になりましたね。エンジニアの小川(mitsuruog)です。
10/29にexpress実践入門というテーマで社内勉強会を開きましたので、その話を紹介します。

発表した資料はこちらです。

express実践入門 · GitHubexpress実践入門 · GitHub

なぜ、やろうとしたのか?

Expressは、node.jsのWebアプリケーションフレームワークです。
事実上、node.jsでのデファクトスタンダートとなっており、既に利用している方や、これから始めようと興味を持っている方も多いのではないかと思います。

弊社でも、IoTでの導入が最近増えてきました。

巷には、express入門用の記事が多くあるのですが、なぜ「express実践入門」という形で再整理したかと言うと、
私が実務でexpressを利用してきた上で、「express入門用の記事の多くに」次のような不満があったからです。

express3系で作成されている古い記事が多い

expressの最新バージョンは4です。expressの3系と4系ではAPIに互換性がない変更があります。
現在、日本語で読める記事や書籍のほとんどがexpress3系より前の内容で書かれています。

expressの「Getting started」をやってみただけの内容が多く、深みに欠ける

「express入門用の記事の多く」はexpressの「Getting started」と同様の内容であることが多いです。 やはり、Webアプリケーションを作成するためには、もう少し踏み込んだ内容が必要です。

Webアプリケーションを作成するという内容に欠ける

Webアプリケーションを作成する上でexpressの全機能を利用することはまずありません。
また、expressだけではなく、様々なnpmモジュール(認証、DBなど)を組み合わせてWebアプリケーションは構成されています。

時にexpressの細部に踏み込んだ内容が必要なケースはありますが、ほとんどの場合、expressをある程度使いこなした後に出くわす場合が多いのではないでしょうか。
立ち上がりとしては、「expressでWebアプリケーションを構築する」という立ち位置での、まとまったインプットが欲しかったりします。

このような中、初学者がexpressを攻略する上でのつまづくポイントと、中規模開発をターゲットにWebアプリケーションを構築する上でのベストプラクティスを私の経験ベースでまとめました。

実施風景

昼食の時間帯に行いました。
会社支給の弁当を片手に真剣にexpressについて学びます。

456a2398


当日は業務が忙しい中、20名程度の参加がありました。

456a2402


「よく噛むと“記憶力”がアップする!?」という話を信じているのかは定かではないのですが、弊社では隔週ペースでランチ勉強会を開催して、社員やパートナーの皆さんの技術力向上に努めています。

まとめ

「私が考える最強のexpress実践入門」でした。
初めてexpressやる方で、この辺り悩んでいる方がいましたら、参考にしていただければ。。。

弊社でもIoT関連でどんどん実践投入していきます。

ではではー

2014年8月22日 (金)

サブディレクトリ型の複数サイトでtwitter cardを設定した

こころです。番外編ネタですが、フレクトで運営しているブログにtwitter cardを設定してみました。

1.twitter cardって?

twitter cardとは自分のサイトにmetaタグを数行埋め込むと、twitterのタイムラインにメディア(カード)を添付できるサービスです。

20140822_113413_2

こんな感じ。

カードは7種類。
Summary Card・・・標準のカード(タイトル、ディスクリプション、サムネイル、サイトのTwitterアカウント)
Summary Large Image Card・・・標準カードの画像大きめ
Photo Card・・・写真を表示するカード
Gallery Card・・・写真の収集を目立たせるカード
Player Card・・・Youtube動画や音楽プレーヤーを埋め込むカード
App Card・・・アプリケーションを紹介するカード
Product Card・・・商品を見せるカード

今回はよく見かけるSummary Large Image Card(標準のカードの画像大きめ)を設定してみます。

2.準備

用意するもの

  1. WebKitを用いたブラウザ(GoogleChromeかSafari)
  2. twitterアカウント
  3. カードを埋め込むWebサイト

twitter cardのプレビュー機能がWebKitを用いたブラウザでしか動作しないのです。
また、今回はサブディレクトリで複数サイトを運用している場合に対応しました。

フレクトでは、

フレクトのクラウドブログ> http://blog.flect.co.jp/cloud/
セールスフォースの豆知識> http://blog.flect.co.jp/salesforce/
フレクトのHeroku Lab> http://blog.flect.co.jp/labo/

と3つのブログをサブディレクトリ型で運用してるのです。

http://blog.flect.co.jp/***/を切り分けるときにちょっと詰まったので、あまり居ないと思いますが同じ様なサイト運用してる人のために書いてみようと思います。

3.設定

https://dev.twitter.com/docs/cards/validation/validator

から、作成したいカードのタイプを選択します。

20140822_120236今回はSummary Large Imageを選択

次のCard Validator画面では、サイトに貼付けるmetaタグを生成するためのオプションを入力します。

20140822_134800

赤枠・・・入力必須
黄色枠・・・任意項目(スマートフォン画面に最適化するためのオプション項目です)

必須プロパティについて

twitter:card・・・cardの種類
twitter:site・・・twitter cardを作成するサイトオーナーのユーザ名
twitter:title・・・カードのタイトル
twitter:description・・・カードのメッセージ(200文字以内)
twitter:creator・・・ページのコンテンツ作成者のTwitterアカウント(twitter:siteと同じでもOK)

twitter:image:src・・・画像のURL (Summary Large Image)の場合は最低280×150px必要です。

twitter:domain・・・サイトのドメイン ※サブディレクトリ型の場合は注意が必要で、http://blog.flect.co.jp/labo/* と、ワイルドカードを設定するのがポイントです。
Update Previewボタンを押すとmetaタグが生成されるので、サイトの共通headerに追加します。

4.確認方法
Card ValidatorのValidate&Applyタブに、追加したサイトのURLを入力し、Goボタンを押します。

20140822_113310

<Your domain name> approved と表示されます。

20140822_112750

以上で設定完了ですが、設定しようとするとけっこう細かいエラーが出るので、こちらのFAQを参考にしてください。特に、Twitterのクローラーはおよそ週1回の割合でサイトのメタデータを再度取得するので、twitter cardに修正を加えた場合は反映されるまで時間がかかるようです。

2012年1月22日 (日)

Panda Streamを使ってWEBサイトに動画アップロード&エンコード&再生機能を作る

こんにちは、大橋です。
ひさしぶりの記事投稿になります。

今日はPanda Streamという動画アップロード、エンコード(変換)および再生機能を自分のサイトに組み込めるサービスを使ってみたので記事にしてみます。Panda Streamの公式サイトは以下です、あわせて見てみてください。

http://www.pandastream.com/


<Panda Stream概要&特徴>

Panda Streamを使うと、Youtubeみたいに動画ファイルをブラウザからアップロードして、アップロードしたファイルをブラウザ上で再生できる機能を簡単に既存Webサイトに組み込むことができます。特徴は以下です。

  1. すべての機能がREST API形式で提供されていて、Ruby, Python, PHPなどAPIを簡単にコールするライブラリも用意されている
  2. ブラウザからのアップロードはpandaのサーバ側に直接送信されるので、自前のサーバで動画アップ用にインフラ/ミドルなど構築をしなくてよい
  3. データのストア先はAmazon S3を使うので、ストレージを自前で用意する必要なし
  4. アップロード、再生するときのJavaScript, HTML,などサンプルが一通り用意されている。
  5. 多様なInputフォーマットからFLV, MP4(H.264), WebMなど主要な再生環境向けへの変化に対応。

名前はなんだかかわいいですが、できることはけっこうすごいですね。なにかと難しい、動画アップ、変換、が簡単にできちゃいます。HerokuのAdd-onにもなっているので、Herokuを使うときにも便利(←一部の人だけの恩恵かもですが・・・)。

<動画機能を利用するための流れ>

早速使っている前に、大まかな手順を確認しましょう。Panda Streamを使ってWebサイトに動画アップロード&再生機能を埋め込む手順は以下のようになります。

  1. Amazon Web Services(AWS)にサインアップしてAmazon S3を利用できるようにしておきます。
  2. PandaにSign Upします。
  3. CloudというS3のバケットに1対1で対応したエンコーディングした動画を管理するグループを作成。
  4. Panda StreamのAPI Access用のキーとCloud IDを取得。
  5. ここから本番。実際にWebサイトに組み込むためのプログラミングをします。


<実際にWebサイトに組み込むための前準備>

手順(1)〜(4)のCloud作成までは簡単なのでさらっと説明します。

まず、Amazon S3を用意しましょう。AWSのアカウントがなければ以下から申し込んで、Amazon S3を使えるようにしましょう。アップロードした動画やエンコード後の動画、および動画から抽出されたサムネイル画像はAmazon S3上に保存されます。

http://aws.amazon.com/jp/

次にPanda Streamのサイトへいってサインアップしましょう。

http://www.pandastream.com/

サインアップしたら、Home画面のメニューに「API Access」という項目があるので、そのリンクをたどると以下のような画面になります。

Pandaapikey

ここで、
 ・Access Key
 ・Secret Key
 ・API URL(api.pandastream.com)

があることを確認しましょう。これはPanda StreamのAPIを使うときに必要になります。

次にメニューの「Clouds」というメニューがありますのでここをたどり、「Create new cloud」をクリックし、Cloudを作成します。Cloudの名前とパーミッションおよびS3のバケット、Access Key、Secret Keyを設定します。すると、以下の画面のようにCloudが作成されるので、作成後に左上に表示されているIDがCloud IDというもので、この後、APIアクセスで使うので控えておきましょう。

Cloud_id

いろいろサインアップするものがあって大変ですが、これで準備完了です。


<WEBサイトに動画アップロード機能を埋め込む>

Panda Streamのサイトにサンプルコードがアップされているので、それを見たり、動かしたりするのが入門にはちょうどよいです。

http://www.pandastream.com/docs/sample_apps

ここでは、その中のエッセンスだけ抜き出して、アップロードから再生までどんなコードを書けばよいか解説していきます。

まず、アップロード画面のHTML/JavaScript側を準備します。
HTMLは以下のように記述します。actionの遷移先は適当に変えてください。
最初のhiddenはこのあと説明するJavaScriptによりアップロードに成功後に動画のIDが埋め込まれ、次の画面に遷移するときのパラメータになります。

 

   

   

 

JavaScriptはPandaが提供しているJavaScriptライブラリ、jquery.panda-uploaderを使います。jQuery本体を先に読み込んで、Pandaのアップローダーライブラリを読み込みます。

  
  

  


アップローダーの読み込み後、前準備で取得したPanda Stream用のaccess_key、cloud_idなどともにsignatureを書く必要があります。これはサーバサイドで値を作成します。サーバサイド用にはいろいろな言語用にクライアントライブラリがあるので、言語ごとのPandaライブラリを使うことで、signatureを取得できます。ライブラリは以下にあります。

http://www.pandastream.com/docs/client_libs

Ruby, PHP, Pythonなどのライブラリがあります。環境が整っていてよいですが、Java版やPerl版がなかったりもします。
たとえば、Pythonならば以下のような処理でJSON形式でsignatureなど取得できますので、JavaScriptに埋めてください。

panda = Panda(
    api_host='api.pandastream.com',
    cloud_id='Cloud ID',
    access_key='Panda API用のAccess Key',
    secret_key='Panda API用のSecret Key',
    )
python_access_details = panda.signed_params('POST', '/videos.json')
# 以下のようなJSONが取得できます。
#{'access_key': 'xxxxxxxxxxxxxxx', 'timestamp': '2012-01-19T15:01:43.268760+00:00', 'cloud_id': 'xxxxxxxxxxxxxxxxxxxx', 'signature': 'U8OOrnBlYekV3FjyyAIZj1H0kVZlAATn8O/uI07mEMI='}

ここまでのHTML, JavaScript, サーバサイドの処理を組み合わせて、アップロード処理が作れます。説明したパーツを組み合わせると以下のようにプログレスバーが出てくる動画アップロード機能が作れます。

Upload_progress

 アップロードが終わるとコンテンツはS3に登録され、Panda Streamのサーバ側でエンコーディング処理が始まります。同時に、HTMLにhiddenでpanda_video_idとしたところに、動画再生のためのIDがセットされ、フォームのアクション先にpanda_video_idがパラメータとなり画面遷移します。

アップロードされた動画はPanda Streamの画面でも以下のように作成したCloudのDashboardでエンコード中の動画が確認できます。

Dashboard


<WEBサイトで動画を再生する>

さて、アップロードされた動画はエンコードに多少時間がかかります。エンコードのフォーマットはPandaに作成したCloudごとにプロファイルを作成できます。デフォルトはmp4/h.264です。PandaのAPIを使ってエンコードの進捗が何パーセントかも取得できますが、それについては今日は省きます。

動画のエンコードが終わったら、PandaStreamのDashboard上でも再生できますが、アップロード時に取得できたpanda_video_idをパラメータにencodings APIを呼び出し、エンコードされたS3上のファイルパスを取得します。エンコーディングのプロファイルごとにファイルパスや拡張子、スクリーンショットのパスが取得できます。Pythonだと以下のようなコードで取得できます。

    # panda_video_idはアップロード時に取得したID。pandaはPandaクラスのインスタンス
    panda_encodings = json.loads(panda.get("/videos/%s/encodings.json" % panda_video_id))

    encoding = None
 
 # エンコーディングのプロファイルごとに取得できる
    for panda_encoding in panda_encodings:
        if panda_encoding['extname'] == '.mp4' and panda_encoding['status'] == 'success':
            encoding = {
                'id'     : panda_encoding['id'],
                'width'  : panda_encoding['width'],
                'height' : panda_encoding['height'],
                # 以下のようにすると動画のURLやスクリーンショットのjpegのURLが取得できる
                'url'    : "http://%s.s3.amazonaws.com/%s%s" % (’S3のバケット名', panda_encoding['id'], panda_encoding['extname']),
                'screenshot_url' : "http://%s.s3.amazonaws.com/%s_4.jpg" % ('S3のバケット名', panda_encoding['id']),
            }

取得したurlやscreenshot_urlをHTML上で以下のように埋め込みます。ここではHTML5で再生する例を示します。

サーバ側で取得したスクリーンショットのURLや動画ファイルのURLをそれぞれ埋め込めば再生できると思います。もちろん、エンコードが終わってからですが。

かんたんな使い方は以上になります。Panda StreamのDocumentにいろいろな情報があるので、ここから先はそちらを見るようお願いします。


<気になる価格体系>

そういえば、価格について触れないといけないですね。以下のページに記載がありますが、同時に処理できるエンコーダの数により価格が決まります。

http://www.pandastream.com/pricing_and_signup

フリー版はエンコーダは他の人とシェア、ファイルサイズの上限が10MBです。有料版は1つの専用のエンコーダ(dedicated encoder)あたり月間99ドルです。たとえば、3つのエンコーダならば397ドルです。エンコーダの数は同時にエンコードできる動画の数と考えてください。実際には占有できるCPU coreのようです。あと有料版はファイルサイズの上限が5GBのファイルの動画までエンコードできます。


<PaaS上でのプログラミング>

今回はPanda Streamという動画アップロード、エンコード、再生を実現するためのサービスの紹介をしましたが、こういった特定の機能を提供するサービスは動画のエンコードだけでもZencoder(http://zencoder.com/ )など他のサービスがあります。他にも画像変換、メール、PDF生成など、スクラッチで作ると手がかかる機能の実現が最近は外部サービスを使うことで可能になってきているようですね。

Heroku、DotCloudなどのPaaSにより、簡単にアプリケーションをクラウド上にデプロイできるようになりましたが、動画処理やサムネイル生成など特別な機能の実現をしようというときにこれまでと同じやり方ではできないものもあり、少し頭を悩ます部分です。

そういった問題への解決策がPanda Streamのようにアプリケーションの特定の機能を提供するサービス群なのかな、と思います。

クラウド環境でのプログラミングは主にWEB、アプリケーションサーバ用のCPU/DBを提供するHerokuなどのPaaSとその周辺を補強するサービス群を使いこなすことがポイントになるんだろうと思い始めています。

PaaSを使ったアプリケーション構築スタイルをこれから追求しないといけない分野ですね。


<まとめ>

Panda Streamという動画アップロード、エンコード、再生機能を簡単にWebサイトに組み込めます。

こういったサービスは他にもたくさんあるので、今後もたくさん試してみてこのブログ上で共有していきたいと思います。

採用情報

株式会社フレクトでは、事業拡大のため、
・Salesforce/Force.comのアプリケーション開発
・HerokuやAWSなどのクラウドプラットフォーム上での
Webアプリケーション開発
エンジニア、マネージャーを募集中です。

未経験でも、これからクラウドをやってみたい方、
是非ご応募下さい。

フレクト採用ページへ

会社紹介

株式会社フレクトは、
認定コンサルタント
認定上級デベロッパー
認定デベロッパー
が在籍している、セールスフォースパートナーです。
また、heroku partnersにも登録されています。
herokuパートナー
株式会社フレクトのSalesforce/Force.com
導入支援サービス
弊社の認定プロフェッショナルが支援致します。
・Visualforce/Apexによるアプリ開発
・Salesforceと連携するWebアプリ開発
も承っております。
セールスフォースご検討の際は、
お気軽にお問合せください。

2024年4月

  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30        
ブログ powered by TypePad