selmertsxの素振り日記

ひたすら日々の素振り内容を書き続けるだけの日記

parameter storeで環境変数管理したら便利だった

モチベーション

GitHubで管理できないパラメータの管理。めんどうですよね。昔は.envrc.localで管理していたんですけれども、チームで開発するときは、ちょっといじると周りに共有しなければならなくて手間でした。そうしなくて済む方法をawsparameter storeで作ってみました。

Parameter Storeってなんだ

AWS のサービスの一つで、パスワード、データベース文字列、ライセンスコードなどのデータをセキュアに扱うためのものです。

Systems Manager パラメータストア - Amazon EC2 Systems Manager

Parameter Storeへのデータ設定方法

下記のコマンドを叩いて、parameter storeにデータを突っ込んでいきます。

aws ssm put-parameter --name SlackToken --type String --value xxxxx 
aws ssm put-parameter --name IAMRole --type String --value xxxxx 

put-parameter — AWS CLI 1.14.16 Command Reference

コード

#!/bin/sh
SlackToken=(`aws ssm get-parameters --names SlackToken --query "Parameters[*].{Value: Value}"  --output text`)
IAMRole=(`aws ssm get-parameters --names IAMRole --query "Parameters[*].{Value: Value}"  --output text`)

echo "export SlackToken=${SlackToken}" >> .envrc.local
echo "export IAMRole=${IAMRole}" >> .envrc.local

ホントなら一回のリクエストで済ませたいのですが、複数パラメータを扱おうとすると、jsonを扱ったりする必要があって、ちょい手間なのでこんな感じですませちゃいました。

あと、.envrc.local を .gitignoreに追加しておきましょう。

実行

bin/setup.sh

を実行すると、下記のように .envrc.local が生成されます。

export SlackToken=xxxx
export IAMRole=xxxxx

という感じで、簡単にパスワード管理ができるようになりました。

おひとりさま開発基盤グループでの業務の進め方

はじめに

この記事は Speee Advent Calender の23日目の記事です。

こんにちは!ロボット大好き系エンジニア、開発基盤グループ所属の森岡です。 ガンダムF91とクロスボーン以外はほぼ全て視聴済み、フロントミッションも携帯ゲーム以外は全てコンプリート。フロントミッションアーマードコアの最新作をずっと待ち続けています。好きなMSはリックドムです。

さて、僕が所属している開発基盤グループですが、グループという名前が付いているものの、実際に所属しているのは現在森岡1人だけで、完全におひとりさまグループとなっております。この記事では、1人で開発基盤グループを進めていくためにやっていることについて、ご紹介させていただきます。

開発基盤グループのお仕事

開発基盤グループは Speee全社におけるwebサービスの技術的な品質向上 をミッションとして業務を行っています。具体的な業務としては、下記のようなものが挙げられます。

  • 全社的に利用されているDatadog, Sentry, Sidekiq Proなどのツールの導入・管理
  • 開発に関するガイドラインの作成
  • 全社のサービス品質を向上するための各種ツールの作成

tech.speee.jp ※ 1部、Speee Cafe Meetupでお話しさせていただきました

現在、このような業務を進めている開発基盤グループですが、過去は異なるミッションを持って仕事を進めていました。

"全社的な開発効率の向上"から"サービスの技術的な品質向上"へ

先月(2017年11月)まで、開発基盤グループのミッションは、全社的な開発効率の向上でした。 そのミッションに従って、僕たちはRevieeeという確認環境の自動構築ツールを作っていました。

tech.speee.jp

しかしながら、このツールは現在開発を停止しています。その理由は下記の通りです。

  • 人の異動などで、恩恵を受けられるプロダクトのエンジニアが少なくなった
  • AWSのインフラが大きな役割を持つwebサービス増え、Revieeeでそれらをカバーすることが難しくなった

また、弊社で採用している言語、フレームワークが多様化してきたこともあり、Revieeeのような Railsに特化した確認環境構築ツールは、現在のSpeeeにおいて 多くのエンジニアに価値があるとは言い難い ものになってしまいました。

このように開発効率の向上というミッションは、採用する技術や、そのときの人の配置に依存する部分が大きく、開発基盤グループが1人という現状では達成することが難しいものでした。そこで、2017年11月にミッションの見直しを開始しました。

新しく作るミッションは、人の入れ替わりや利用している技術に依存せずに価値を出せる必要があります。それが何かと考えたときに、弊社が運用している 全てのサービスの技術的な品質向上 が適切であると考えました。

このミッションを進めていく手順は下記のようになっています。

f:id:selmertsx:20171222204619p:plain

これだけでは具体的なイメージが湧かないと思うので、現在行っている業務内容を例に説明させていただきます。

画像圧縮の全自動化

現在、開発基盤グループでは、Speeeにおける全てのwebサービスにおいて、ユーザーにとって最適な画像を配信する ことを目的として、デザイナーやフロントエンドエンジニアの人たちと一緒に、その仕組みづくりを進めています。

なぜ 画像の最適化 を最初の施策として選んだのかと言うと、現状パフォーマンスの向上に最も寄与し、画像を用いる全てのサービスにおいて有効で、ページの表示速度を早くしたいという事業の意図とも合致するものであったからです。 画像の最適化 ができる仕組みを作るために、下記の内容で仕事を進めています。

  1. 画像取扱ガイドラインの作成
  2. サイトの評価ツールの作成
  3. 画像を自動圧縮するツールの作成
  4. CIでの未圧縮画像検知

画像取扱ガイドライン

ガイドラインの作成にあたっては、Googleの資料を参考にさせて頂いています。

Image Optimization  |  Web Fundamentals  |  Google Developers

ガイドラインには下記の項目を記載しています。

  • JPG、PNGSVG、GIFの選定基準
  • JPG、PNGの圧縮ツールおよび、圧縮レベル

Googleの資料においては、PNGではなく WebPの使用を勧めていますが、現状の対応ブラウザ状況を見ると、WebPへの完全移行を進めることは難しい状態にあります。そのあたりは僕たちで判断しガイドラインの作成をしております。その詳細については、またどこかのタイミングで発表させていただこうと思います。

未圧縮画像の検知ツールの作成

さて、ガイドラインが出来たら次に必要になってくるのは、僕たちのサイトの評価ツールです。 評価ツールは、slackのslash commandがインターフェースとなっており、下記のような方法で利用できます。

f:id:selmertsx:20171223184619p:plain

実行すると、slack上で改善点や、改善した場合にどの程度容量を圧縮できるか教えてくれます。 slackを利用した理由は、エンジニアだけでなく全ての職種の人に結果を見てほしかったからです。

f:id:selmertsx:20171223185232j:plain

この機能は AWS API GatewayAWS Lambdaを使って実現しています。 Lambdaの中で headless Chromeを使ってページを閲覧し、分析を行っています。 詳細については、また別の機会に説明させていただきます。 もう少し調整が出来たら、OSSとして公開しようかなと考えています。

その他、開発予定のツールについて

ここから先はまだ未着手のものですので、構想だけのお話しになります。 ここまでで、ガイドライン、そして評価ツールを作りました。 次に必要になってくるのは、手軽に画像を圧縮できるツールと、未圧縮の画像がコミットされたときに、それを検知できるCIツールです。rubocopもrspecも、CIで実行されるようにならなければ、きっとこれほど価値を出すことができなかったでしょう。

画像の圧縮については、Gitフックを使って実装しようと考えています。コミット時に自動で圧縮されるので、特に意識せずとも圧縮した画像をGitHubで扱えるようになるかと思います。

WE ARE HIRING

以上、「お一人様開発基盤グループでの業務の進め方」について記載させて頂きました。まだまだ歩き初めたばかりのミッションなので、荒削りな部分や修正するべき点などもあるかとは思います。人が増えて、ミッションの形も変わってくることもあるかもしれません。

開発基盤グループでは、全社的なサービスの品質向上に一緒に取り組んでくれる、またはより良いミッションについて一緒に考えてくれる、そんなエンジニアを絶賛募集中です!!!ご興味を持って頂けた方がいましたら、ぜひwantedlytwitterなどで連絡をください!ご連絡お待ちしております!

www.wantedly.com https://twitter.com/

selmertsx (@selmertsx) | Twitter

24日目の記事は、iida-hayato より、「MercariARハッカソンで優勝したので技術的なまとめ」です!

おまけ

この取組みに関する考え方は SRE本の4章から拝借しました。

parcelでd3jsを扱うことが出来ない (v1.2.1 で解決済み)

エラー内容

➜  image-guideline git:(d3js) ✗ yarn parcel jpg_report.html
yarn run v1.3.2
$ /Users/shuhei_morioka/project/dev_opt/design-guideline/image-guideline/node_modules/.bin/parcel jpg_report.html
⏳  Building...
Server running at http://localhost:1234
🚨  /Users/shuhei_morioka/project/dev_opt/design-guideline/image-guideline/node_modules/xmlhttprequest/lib/XMLHttpRequest.js: Could not statically evaluate fs     at evaluate (/Users/shuhei_morioka/project/dev_opt/design-guideline/image-guideline/node_modules/parcel-bundler/src/visitors/fs.js:164:11)
    at CallExpression.path.get.map.arg (/Users/shuhei_morioka/project/dev_opt/design-guideline/image-guideline/node_modules/parcel-bundler/src/visitors/fs.js:31:21)

原因

🐛 d3 import: Could not statically evaluate fs call · Issue #298 · parcel-bundler/parcel · GitHub

npm libraries can specify alternative main fields in their package.json, we resolve the "module" and "jsnext:main" in priority of "browser" to get the full dependency tree. libraries like d3.js specifies node.js specific files in the "main" which breaks the build

対処方法

Resolver.jsを直接書き換えて回避した。

# parcel-builder/src/Resolver.js

    return resolver(filename, {
      filename: parent,
      paths: this.options.paths,
      modules: builtins,
      extensions: extensions,
      packageFilter(pkg, pkgfile) {
        // Expose the path to the package.json file
        pkg.pkgfile = pkgfile;

        // npm libraries can specify alternative main fields in their package.json,
        // we resolve the "module" and "jsnext:main" in priority of "browser" to get the full dependency tree.
        // libraries like d3.js specifies node.js specific files in the "main" which breaks the build
        const main = pkg.module || pkg['jsnext:main'] || pkg.browser;

        if (main) {
          pkg.main = main;
        }

        return pkg;
      }
    });

追記

現在では、下記のPRがマージされ、v1.2.1 でリリースされているので、問題無く扱うことができる。

Resolve "module", "jsnext:main" and "browser" before "main" by fathyb · Pull Request #299 · parcel-bundler/parcel · GitHub Comparing v1.2.1...master · parcel-bundler/parcel · GitHub

TypeScriptにおいて継承先のclassのプロパティはオブジェクトの生成時には利用できない

概要

表題のとおり。継承先のプロパティは、オブジェクトの生成時には利用できなかった。 このあたりの仕組みが気になる。やり方があれば、ご紹介頂けると幸いです!

確認のコード

class A {
  protected hoge = 3;
  constructor () {
    console.log(this.hoge);
    this.hoge;
  }

  hage() {
    return this.hoge;
  }
}

class B extends A {
  protected hoge = 4;
}
console.log("-------construct-----");
const a = new A();
const b = new B();
console.log("-------call function-----");
console.log(a.hage());
console.log(b.hage());
➜  typescript ts-node override.ts
-------construct-----
3
3
-------call function-----
3
4

ES6において strict modeになる条件

モチベーション

code reviewをして貰ったときに、「ES Modulesを使うと、暗黙的に 'use strict' 付きますよ」というコメントを頂いた。'use strict' になる条件を全然把握していないので、ちょっと資料を読んでみた。

読んだ資料

ECMAScript® 2015 Language Specification

http://www.ecma-international.org/ecma-262/6.0/#sec-strict-mode-code

分かったこと

  • Global codeが、Strict Directive ( 'use strict' のこと )を含むDirective Prologueで始まるなら、strict mode codeである。
  • Module codeはいつでも strict mode codeである。
  • ClassDeclaration、またはClassExpressionの全ての部分は、strict mode codeである。
  • strict directive を含む場合、evalの中身も strict modeになる。
  • 関連する FunctionDeclaration, FunctionExpression, GeneratorDeclaration, GeneratorExpression, MethodDefinition, ArrowFunctionがstrict mode codeを含んでいるならば、functionのcodeも strict modeなcodeになる。
  • 引数にcode strictされているfunctionがある場合、それを受け取るfunctionもcode strictになる

TypeScriptをVSCodeでデバックする

概要

  • TypeScriptのデバック効率を上げたい
  • フロントエンドのコードでは無いのでchromeデバッグツールが使えない
  • VSCodeのデバックツールを試してみた
  • 5分くらいで設定できた

VSCodeでのデバック画面

f:id:selmertsx:20171130165908p:plain

こんな感じの画面でデバックができます。

設定方法

VSCodeにおけるTypeScriptデバックの流れとしては、下記の通りです

  1. launch.jsonを読み込む
  2. launch.jsonのpreLaunchTaskにて、実行するタスクを指定する
  3. 指定されたタスクを tasks.jsonから探して実行する
  4. デバッガが動く
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debugging TypeScript",
      "program": "${file}", 
      // tasks.json のlabelで指定
      "preLaunchTask": "Compile TypeScript", 
      "cwd": "${workspaceFolder}", 
      "outFiles": [
        "${workspaceFolder}/build/**/*.js"
      ]
    }
  ]
}
// ${file}: 今開いているコード
// ${workspaceFolder} : VS Codeを開いたフォルダ
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Compile TypeScript",
      "type": "shell",
      "command": "yarn build",
      "problemMatcher": []
    }
  ]
}

デバックの実行

fn + F5で、デバックが開始されます。

f:id:selmertsx:20171130165922p:plain

node.js version could not be determined というログが出ている原因は、これから見ていきます :pray:

参考資料

https://code.visualstudio.com/docs/editor/debugging https://code.visualstudio.com/docs/editor/tasks#vscode

TypeScriptで書かれた AWS Lambdaのコードで AWS SAM Localを使う

やりたいこと

  • TypeScript で書かれている AWS Lambda のコードを AWS SAM Local で動かしたい
  • TypeScriptのコードとbuild後のコードは別ディレクトリで保持したい
  • AWS SAM LocalのREADMEではそこの説明が無いので、自分でなんとか動く方法を模索する必要がある

結論

CodeUri プロパティはcloudformation.ymlからの相対パスを指定する必要がある

※ Lambdaと AWS SAM の初心者です。間違ったことを言ってる。もしくはもっと良い方法を見つけられていない可能性があります。なにかありましたら、コメント頂けますと幸いです!

AWS SAM Localとは

https://github.com/awslabs/aws-sam-local#advanced

AWS Lambda関数をローカルで実行しテストができるようになるツール。 S3, DynamoDB, Kinesis 等のリソースからのイベントをエミュレートすることもできる。 API Gatewayをlocalで動かし、Lambdaコードの変更をhot reloadすることもできる。

AWS SAM のinstall

npm install -g aws-sam-local
sam --version # installできているか確認する

上記コマンドだけで installが可能。Dockerも必要なので気をつけること。

設定内容

AWS SAMの説明とcloudformation.yml の書き方についてはここに書いてあるので、ここを見ながら設定を行う。 https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction

yamlの設定内容

# cloudformation.yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Simple Lambda functions

Resources:
  HelloWorld:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: build/src
      Handler: index.post
      Runtime: nodejs6.10
      Events:
        GetResource:
          Type: Api
          Properties:
            Path: /chat-bot
            Method: post

上記の設定ファイルが SAM Localを使う上で、自分が設定した CloudFormationのymlである。CloudFormationに Transform: AWS::Serverless-2016-10-31 という値を追加することで、SAMの定義をCloudFormationで行うことが可能となる。

In order to include objects defined by AWS SAM within a CloudFormation template, the template must include a Transform section in the document root with a value of AWS::Serverless-2016-10-31.

ここで重要なのは HelloWorldリソースのCodeUri プロパティで、こいつはjsファイルのディレクトリを指す。気をつけなければならないのは、指定するディレクトリパスは絶対パスか、cloudformation.ymlからの相対パスを指定しなければならないということ。よって、このときのディレクトリ構成は下記のようになる。

➜  speee_pf_checker git:(aws_saml) ✗ tree -I node_modules
.
├── build
│   └── src
│       ├── index.js
 |          ...
│       └── index.js.map
├── cloudformation.yml
├── package.json
├── src
│   ├── application.ts
│   ├── index.ts
 |      ...
│   └── pc_environment.ts
├── cloudformation.yml
├── tsconfig.json
├── tslint.json
└── yarn.lock

ちなみにドキュメントでは下記のように記載されている。

For example, if your AWS Lambda function source code is in the /home/user/code/lambdafunction/ folder, specify CodeUri: /home/user/code/lambdafunction for the AWS::Serverless::Function resource. The command returns a template and replaces the local path with the S3 location: CodeUri: s3://mybucket/lambdafunction.zip.

ということで、絶対パスじゃないといけないかと思ったら、絶対パス指定して実行したら下記のエラーとなった。

➜  speee_pf_checker git:(aws_saml) ✗ echo '{"body": "chiba"}' |  sam local invoke HelloWorld --template cloudformation.yml
2017/11/27 19:22:28 Successfully parsed cloudformation.yml
2017/11/27 19:22:28 Connected to Docker 1.34
2017/11/27 19:22:28 Fetching lambci/lambda:nodejs6.10 image for nodejs6.10 runtime...
nodejs6.10: Pulling from lambci/lambda
Digest: sha256:0721cee9614fe0c995c2eb0f52b9803a23d1c2da3007d46cb54b745d970850a0
Status: Image is up to date for lambci/lambda:nodejs6.10
2017/11/27 19:22:31 Reading invoke payload from stdin (you can also pass it from file with --event)
2017/11/27 19:22:31 Invoking index.post (nodejs6.10)
START RequestId: 08f01507-747c-1789-1312-b3368c5a26ad Version: $LATEST
Unable to import module 'index': Error
    at Function.Module._resolveFilename (module.js:469:15)
    at Function.Module._load (module.js:417:25)
    at Module.require (module.js:497:17)
    at require (internal/module.js:20:19)
END RequestId: 08f01507-747c-1789-1312-b3368c5a26ad
REPORT RequestId: 08f01507-747c-1789-1312-b3368c5a26ad  Duration: 13.31 ms  Billed Duration: 0 ms   Memory Size: 0 MB   Max Memory Used: 29 MB

{"errorMessage":"Cannot find module '/var/task/index'","errorType":"Error","stackTrace":["Function.Module._load (module.js:417:25)","Module.require (module.js:497:17)","require (internal/module.js:20:19)"]}

JavaScriptのコード

// build/src/index.js
'use strict';
console.log('Loading function');

exports.post = (event, context, callback) => {
    callback(null, {
        statusCode: 200,
        headers: { "x-custom-header" : "my custom header value" },
        body: "Hello " + event.body
    });
};

確認用のシンプルなJSコード。

動作確認

➜  speee_pf_checker git:(aws_saml) yarn build
yarn run v1.3.2
$ tsc
✨  Done in 3.25s.
➜  lambda git:(aws_saml) ✗ echo '{"body": "chiba"}' |  sam local invoke HelloWorld --template cloudformation.yml
2017/11/27 18:04:54 Successfully parsed cloudformation.yml
...
{"statusCode":200,"headers":{"x-custom-header":"my custom header value"},"body":"Hello chiba"}

上記コマンドで確認した結果、aws sam localが動いていることが確認できた。

後程確認すること

  • node_modulesを AWS SAM Localで読み込む
  • API GatewayをLocalで使う