LCL Engineers' Blog

夜行バス比較なび(高速バス比較)・格安移動・バスとりっぷを運営する LCLの開発者ブログ

LCLはITエンジニアが「成長できる」環境か

モバイルアプリエンジニアの山下です。
去年の8月に入社してから早くも半年が経とうとしています。

techblog.lclco.com

先日、開発チームで ITエンジニア採用に欠かせない原則とは の記事が話題に上がりました。
そこで、LCLはITエンジニアが「成長できる」環境なのかこの半年間と共に振り返ってみました。

(下記は記事中の『ITエンジニアが「成長できる」環境と「成長できない」環境』の項目を挙げています。)

触るシステムの種類

LCLでは現時点で大きく分けて3つのサービスを運営しており、各メンバーがメイン分野を軸にサービスを横断して着手しています。そのため、触るシステムの種類は絞られていません。

私の場合はモバイルアプリエンジニアとしてiOS, Androidの開発している傍ら、APIやPUSH通知などを扱う管理画面などの開発も進めています。

テーマの変化

カスタマー向けの自社サービスを運営している会社なので同じ課題が発生することは少なく、次々と新しい機能追加・改善が求められる環境です。

どのサービスも成長段階のため、サービスの成長に合わせてアーキテクチャ見直し、インフラ強化、パフォーマンス・安定性の向上などが必要です。

また、チーム全体で開発手順の効率化や業務改善に取り組んでいるため、目先のタスクのみを消化するのではなく、先の運用コストをみて改善タスクを優先したりと優劣も相談しあって進めています。

扱う技術の種類

各メンバーは自分がメインとする分野を軸に関連する技術に対して制限なく扱っています

私の場合は、前述の通りモバイルアプリとバックエンドの開発を日々取り組んでいるため、それらに関連する技術とCI周りや業務改善に伴うHubotやChrome拡張機能開発を扱っています。

これらは個人のモチベーション次第でメイン分野を超えて誰でも着手できる環境にあります。

扱う技術の新しさ

新しく運用を始めるシステムは暫定の新しいバージョンで始め、現行のサービスは段階を経て追いつくようにしています。また、言語に限らずOSSや新しいサービスも良いものはまず試して採用を検討できる環境です。

モバイルアプリでは、iOSはSwiftの最新のバージョンを追っており、AndroidもKotlinへ移行を始めています。

扱う技術の汎用性

基本的に技術や設計はその分野のデファクトスタンダードを採用しています。

仕事の範囲・大きさ

LCLでは各サービスにエンジニアとは別にPOが就いており、POとエンジニアが相談して仕様やスケジュールを決定する仕組みができています。そのため、エンジニアは裁量を持ちつつ開発に専念できます。

アーキテクチャの選定・設計などはエンジニアチーム全員で話し合い、立場関係なく質疑を出しベストな選択ができるようにしています。

エンジニア全員がリリースまで担当でき、常に全体が見れる環境で業務が行なえます。

仕事の難易度

タスクによっては見積もりよりも工数が掛かることがありますが、どうしようもなく難しいということはありません。
行き詰った際はエンジニアチームのチャットで相談したり、別の解決方法を試したり柔軟に進めることができます。

仲間

プルリクのレビュー、設計の議論など、チームとして技術力向上の機会が多くあります。 チャット文化なこともあり業務中に口頭で喋る機会は少ないですが、チャット上でも技術のことから世間話まで日々楽しくコミュニケーションがとれています。

社外活動

勉強会の参加を推奨しており、業務時間として参加することも可能です。
参加した際は内容を共有して業務に取り込んだりしています。

最近の例ではこれにより週1でモブプロを行う文化が生まれました。

インターネット

個人ブログやSNSなどの制限はありません。業務で使うアカウント(例えば GitHub)についても個人アカウントを利用することができます。

技術的な業務の割合

社内に事務や営業部署などの仕組みができているため、エンジニアがメールを作成したりExcel業務をすることは一切ありません。エンジニアは開発に専念することができます。

私自身この半年で1通もメールを作成していません。

おわりに

LCLは分野や裁量に縛られず業務したいエンジニアにとっては成長ができる環境だと思っています。
とはいえ、色々と手を付けすぎてメイン分野の工数が減ってしまっているという点も気をつけなければいけないです。

エンジニア募集しています

LCLではエンジニア積極的に募集中です。
興味のある方はお気軽にご連絡よろしくお願いいたします。
インターンも募集しています。

https://www.lclco.com/recruit/

AWS WAFの概要まとめ

年末年始にかけて、AWS関連の最新情報を整理していたところ、AWS WAFがかなり使いやすいレベルにアップデートされていました。 セキュリティ関連のサービスは、日頃はそれほど最新の情報を追えていないので、これを機会に簡単にまとめたいと思います。

AWS WAFとは

AWS WAF は、CloudFrontまたは、ALBへのHTTP/HTTPSリクエストをモニタリングできるWeb Application Firewallです。 詳細については、各所で記事がありますので割愛しますが、「従量課金で低コスト」「専用のインスタンス不要」「マネージドルール」という点が、他のWAF製品との違いになると思います。

概念

まず、AWS WAFの概念について整理します。基本的には、一般的なACLの考え方と同じです。

Condition

Conditionは、AWS WAFがモニタリングするリクエストの条件を定義します。

例として以下のような条件を定義できます。

  • IPアドレス
  • リクエスト元の地域
  • User-Agent等のHTTP Header
  • URI

Conditionでは、あくまでもモニタリングする条件のみを定義し、この条件に対する振る舞いは定義しません。

Rule

Ruleは、Conditionを組み合わせて、アクションを発動するための条件を定義します。(アクションについては、後述します)

例えば、下記を一つのRuleとして定義し、このRuleに対しての振る舞いを設定できます。

  • リクエスト元のIPアドレスが 192.xx.xx.xxでない
  • リクエスト元の User-Agent ヘッダーに XxxBot が含まれる
  • 特定の地域からのリクエストである

また、レートベースのRuleも定義可能です。レートベースのRuleでは通常のRuleに加え、リクエスト数がレートリミット(最小値2,000 / 5分)を超えた場合にアクションを発動します。

Web ACL

Web ACLは、各Ruleのアクションとデフォルトアクションを定義します。

アクションの種類

アクションには、3種類あります。

  • Allow
    • 条件に一致するリクエストを許可します。
  • Block
    • 条件に一致するリクエストを拒否します。( ステータスコード403を返します)
  • Count
    • 条件に一致するリクエストをカウントします。主にACLのテストに利用するようです。

各Ruleのアクション

各Ruleに対してアクションを定義します。Ruleに定義したすべての条件に一致した場合の振る舞いを定義します。

デフォルトアクション

デフォルトアクションは、Web ACLのすべてのRuleの条件に一致しないリクエストの振る舞いを定義します。

例えば、「特定のIPアドレスのリクエストのみ許可」という場合には、

  1. 特定のIPアドレスのRuleを定義し、アクションはAllow
  2. デフォルトアクションはBlock

という定義をします。

以上で、AWS WAFの基本的な概念について説明しました。

マネージドルール

マネージドルールは、AWS re:Invent 2017 で発表された新機能です。

AWS WAFではルールを柔軟に定義できるものの、適切なルールをメンテナンスし続けるには運用負荷が高いことがネックでした。

マネージドルールでは、セキュリティベンダーが事前設定済みのルールを利用することができます。 さらに、ルールはセキュリティベンダーによって自動的に更新されるため、ルールの運用から開放されます。

マネージドルールの料金は、利用するルール毎に異なりますが、現在見る限りでは、月額 数ドルから数十ドルと比較的低コストで導入できます。

ALBへ設定例

AWS ConsoleからALBに対しての設定も紹介します。例として、「特定のIPからのリクエストは拒否する」というルールを定義します。

Web ACLの基本情報を入力

ACLの名前と、対象とするALBを設定します。対象とするALBは、ACL作成後も変更できます。

f:id:lcl-engineer:20180103012730p:plain

Conditionの作成

拒否対象のIPアドレスを定義します。

f:id:lcl-engineer:20180103012740p:plain

Ruleの作成

作成したConditionを元に、Ruleを定義します。ここでは、「IPアドレスが一致」を条件としています。

f:id:lcl-engineer:20180103013327p:plain

ACLにRuleを追加

ACLにRuleを追加します。上記で作成したRuleに対しては、ActionをBlockと設定します。その他、デフォルトのActionはAllowを設定します。

f:id:lcl-engineer:20180103013546p:plain

以上で完了です。このように簡単な条件なら、すぐに設定できます。

Sampled requestsの確認

設定がうまくいっているかどうかは、Web ACLの画面の「Sampled requests」から確認ができます。

Block条件に該当するリクエストが発生していた場合は、リクエストの情報・該当するRuleが表示されます。

f:id:lcl-engineer:20180103013814p:plain

まとめ

AWS WAFの最初のリリース時は、ALBには対応しておらずCloud Frontのみの利用可能でした。その後、ALB対応・レートベースのRule・マネージドルールなど、ユーザが本当に必要な機能を着実に追加しているのは、さすがAWSです。

特に、マネージドルールの登場で、セキュリティ専任者が不在のチームにもAWS WAFの導入の敷居が大幅に下がったと思います。今後もマネージドルールの拡充などが予定されているので、アップデート情報は随時追っていきたいと思います。

Redashで1つのカラムに保存されたJSONデータを取り扱う

Redashでデータを加工する際に、1つのカラムに含まれるJSONの中身を取り扱う必要がありました。しかし、単純にクエリを叩くだけではJSONの中身を参照できません。

そこで、Redashの機能の一つであるPython Data Sourceを利用してJSONを解析し、各データを1つのカラムで取得・加工できるようにしてみました。

準備

Python Scriptを有効化するために/opt/redash/.envを修正

export REDASH_ADDITIONAL_QUERY_RUNNERS=redash.query_runner.python

設定を反映

$ sudo supervisorctl restart all

Data Sourceを追加

f:id:lcl-engineer:20171231005613p:plain

以上でPython Data Sourceを選択できるようになります。

使い方

Python Data Sourceでは以下の記述でデータの取得・表示を行います。

データの取得

Redashで既に定義済みのクエリを利用する場合

例: https://localhost/queries/123

get_query_result(123)

直接クエリを叩く場合

query='select * from queries'
execute_query('<DATA_SOURCE_NAME>', query)

行の定義

add_result_row(result, {
  '<COLUMN_NAME_1>': '値', 
  '<COLUMN_NAME_2>': '値'
})

列の定義

add_result_column(result, 'COLUMN_NAME_1', '', 'string')
add_result_column(result, 'COLUMN_NAME_2', '', 'integer')

JSONの解析

それでは、本題のJSONの解析をします。

今回は例として予約時の日付とブラウザの種類を取得します。

元データ

ID ACTION DETAIL
1 book {"date": "20180124", "browser": "safari"}
2 book {"date": "20180201", "browser": "webview"}

クエリ

Data Sourceに「Python」を選択します。

f:id:lcl-engineer:20171231005601p:plain

下記のように記述にして実行します。

import json

# 既に定義済みのクエリの結果を参照
q_res = get_query_result(123)

result = {}
for row in q_res["rows"]:
    detail = json.loads(row["detail"])
    
    add_result_row(result, {
        "id": row["id"],
        "action": row["action"],
        "date": detail["date"],
        "browser": detail["browser"]
    })

add_result_column(result, 'id', '', 'integer')
add_result_column(result, 'action', '', 'string')
add_result_column(result, 'date', '', 'string')
add_result_column(result, 'browser', '', 'string')

結果

ID ACTION DATE BROWSER
1 book 20180124 safari
2 book 20180201 webview

(おまけ)割合を計算

上記ではJSONを解析しましたが、Pythonを書いてデータの加工も可能です。

from collections import Counter

q_res = get_query_result(123)

result = {}
browserArr = []

for row in q_res["rows"]:
    browserArr.append(row["browser"])

counter = Counter(browserArr)
for key, value in counter.most_common():
    add_result_row(result, {
        "browser": key,
        "total": value,
        "割合": str(float(value) / len(browserArr) * 100) + "%"
    })

add_result_column(result, 'browser', '', 'string')
add_result_column(result, 'total', '', 'integer')
add_result_column(result, '割合', '', 'string')

結果

BROWSER TOTAL 割合
webview 1 50.0%
safari 1 50.0%

最後に

本来であれば事前に加工し別のDBやテーブルに保存しておくのが理想ですが、急ぎの対応であったり基盤を作る前であればこの方法で補えると思います。

エンジニアを募集しています

LCLではエンジニア積極的に募集中です。
興味のある方はお気軽にご連絡よろしくお願いいたします。
インターンも募集しています。

https://www.lclco.com/recruit/