TECH PLAY

BASE株式会社

BASE株式会社 の技術ブログ

579

こんにちは、BASE BANK 株式会社 Dev Division でエンジニアとしてインターンをしている前川です。 今回、Amazon Elasticsearch Service(以下、Amazon ES)による、ECS/Fargate で稼働するアプリケーションのログデータの解析基盤を新規で構築することになったので、構築するにあたって調査した内容や関連する内容、実際におこなった構築方法についていくつか紹介します。 今回の構築の簡単な全体構成図は次のようになります。 今回は、 ECS/Fargate のログを S3 にルーティングする Amazon ES にログをルーティングする VPC アクセスの Amazon ES を構築し、Kibana を外部からアクセスできるようにする の3つの手順にわけて、構築方法や関連する内容について紹介していきたいと思います。 なお、この記事で取り扱っている各ツール・サービスのバージョンは次のとおりです。 Terraform: v0.12.5 Terraform provider.aws v3.6.0 SAM CLI: version 1.7.0 AWS CLI: aws-cli/2.0.61 Python/3.9.0 Darwin/19.3.0 source/x86_64 Fargate platform version: 1.4.0 FireLens で ECS/Fargate のログを S3 にルーティングする FireLens について Kinesis Data Firehose を用いて ECS/Fargate のログを S3 へのルーティングする ECS/Fargate のログを S3 に直接ルーティングする方法について Amazon Elasticsearch Service にログをルーティングする Amazon Elasticsearch Service の構築 Amazon Elasticsearch Service の Terraform による構築 ECS のリバースプロキシサーバーを使用した Kibana によるデータ解析環境構築 ECS デプロイツールについて おわりに FireLens で ECS/Fargate のログを S3 にルーティングする FireLens について ECS のコンテナの標準出力/標準エラー出力に出力されたログを S3 にルーティングするために、FireLens をログドライバーとして使用しました。 FireLens を使用することで、ECS で出力されたログを、サイドカーとして実行された Fluentd や Fluent Bit のログルーターコンテナにルーティングすることができます。 別のアプリケーションでは、S3 へのログのルーティングは、CloudWatch Logs 経由で行っていましたが、FilreLens を用いた方法によって、CloudWatch Logs のコストを抑えることや、カスタム設定を用いた柔軟なログの取り扱いができるようになりました。 Kinesis Data Firehose を用いて ECS/Fargate のログを S3 へのルーティングする 今回の構築では Kinesis Data Firehose 経由で S3 にログをルーティングしました。 まずはじめに、Kinesis Data Firehose と S3 の構築をします。 Terraform での定義例は次のようになります。 resource "aws_kinesis_firehose_delivery_stream" "sample" { destination = "s3" name = "sample" s3_configuration { bucket_arn = aws_s3_bucket.sample.arn role_arn = aws_iam_role.sample-kinesis-firehose-iam-role.arn prefix = "sample/" } } resource "aws_iam_role" "sample-kinesis-firehose-iam-role" { name = "sample-kinesis-firehose" assume_role_policy = jsonencode( { Version = "2012-10-17", Statement = [ { Sid = "", Effect = "Allow", Principal = { Service = "firehose.amazonaws.com" }, Action = "sts:AssumeRole" } ] } ) } resource "aws_iam_policy" "sample-kinesis-firehose-iam-policy" { name = "sample-kinesis-firehose-iam-policy" policy = jsonencode( { Version = "2012-10-17" Statement = [ { Sid = "" Effect = "Allow" Action = [ "s3:AbortMultipartUpload", "s3:GetBucketLocation", "s3:GetObject", "s3:ListBucket", "s3:ListBucketMultiPartUploads", "s3:PutObject", ] Resource = [ aws_s3_bucket.sample.arn, "${aws_s3_bucket.sample.arn}/*", ] } ] } ) } resource "aws_iam_role_policy_attachment" "sample-kinesis-firehose-iam-role-attach" { role = aws_iam_role.sample-kinesis-firehose-iam-role.name policy_arn = aws_iam_policy.sample-kinesis-firehose-iam-policy.arn } resource "aws_s3_bucket" "sample" { bucket = "sample" acl = "private" } Kinesis Data Firehose に対しては、データを送信する S3 の設定と、その S3 を操作するために割り当てる IAM ロールを作成しています。 次に、ECS のタスク内へ FireLens コンテナを追加し、サイドカー構成とするために、ECS のタスク定義を修正する必要があります。 ログを出力するアプリケーションコンテナにログドライバーを次のように設定します。 "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "firehose", "region": "ap-northeast-1", "delivery_stream": aws_kinesis_firehose_delivery_stream.sample.name } } delivery_stream のところには、先程構築した Kinesis Data Firehose の name で設定したものを使用します。 次に FireLens 設定を含むログルーターコンテナを追加します。 定義例は次のようになります。 { "name": "log-router", "image": "906394416424.dkr.ecr.ap-northeast-1.amazonaws.com/aws-for-fluent-bit:latest", "essential": true, "firelensConfiguration": { "type": "fluentbit" } } 最後に、タスクロールを修正し、FireLens コンテナが Kinesis Data Firehose へとストリームを送信できるようにします。 ポリシーの定義例は次の通りです。 { " Version ": " 2012-10-17 ", " Statement ": [ { " Effect ": " Allow ", " Action ": " firehose:PutRecordBatch ", " Resource ": " * " } ] } 以上の設定で、ECS のログを S3 に出力することができます。 ECS/Fargate のログを S3 に直接ルーティングする方法について また今回の構築では、S3 へのログの転送に Kinesis Data Firehose を使用しましたが、Fluent Bit が v1.6 より、コンテナログをルーティングする送信先として S3 をサポートし、Kinesis Data Firehose を経由せずに直接 S3 にログを転送することができるようになったようです。 aws.amazon.com ただし、デフォルトの設定である S3 のマルチパートアップロード API を使用し、この方法を使う場合は、Fluent Bit のインスタンスが、データのバッファリングのために永続的なディスクを必要とするので注意が必要です。 このディスクは、マルチパートアップロード API で送信するデータのチャンクをバッファリングするために使用され、もし Fluent Bit が不意に停止した場合に、同じディスクで再起動し、未完了のアップロード処理を完了するために必要だということです。 今回のような ECS/Fargate の環境で使用する場合は、Amazon EFS ファイルシステムを ECS タスクで使用することが推奨されているようでした。 また、永続的なディスクを使用しない場合に、S3 の PutObject API を使用し、データをバッファリングせずに頻繁に送信する方法によって、データの損失を抑える設定もあるようでした。 信頼性を重視する場合には、Kinesis Data Firehose を Fluent Bit と S3 間のバッファとして使用する方法が推奨されているようでしたので、S3 にログを転送する場合には、特別な理由がない限り Kinesis Data Firehose を経由する方法でおこなうのが良いのかなと思いました。 github.com Amazon Elasticsearch Service にログをルーティングする Kibana を用いたログの調査環境を構築するために、S3 にルーティングされたログを Amazon ES に転送する Lambda を作成しました。 Lambda は S3 の PUT をトリガーにして起動し、受け取った event の情報を用いて取得した S3 からの対象のログデータを加工し、Amazon ES に転送します。 BASE BANK チームでは、AWS SAM CLI を使用した Lambda の運用をおこなっており、今回の Lambda の環境構築にあたっても AWS SAM CLI を使用しました。 AWS SAM CLI を用いた Lambda の環境構築方法に関しては、同僚の永野が書いた下記エントリをご参照ください。 devblog.thebase.in AWS SAM CLI で Lambda を管理する場合は、AWS SAM のテンプレートファイルでトリガーとなるイベントの設定やロール、環境変数、VPC 周りの設定をおこないます。 BASE BANK チームでは、AWS リソースの管理を Terraform で行っているので、Lambda リソースの管理のみを SAM を用いて Cloud Formation でおこない、arn などを通してトリガーの対象となるリソースを参照し、テンプレートファイルを記述していました。 今回 Lambda のトリガーの対象となる ECS からのログが保存されている S3 についても、当初は Terraform で管理していたのですが、AWS SAM CLI で Lambda を構築し、トリガーの対象を S3 とする場合は、S3 を Lambda と同じテンプレートファイルで定義しなければならないという制約がありました。 github.com よって、Terraform で管理されていた S3 を、AWS SAM のテンプレートファイルで定義することで CloudFormation で管理するようにし、Terraform 側では、Data Resource として S3 を参照するように修正しました。 SAM テンプレートファイルのリソースセクションの作成例は次のようになります。 Resources : LogToEsFunction : Type : AWS::Serverless::Function Properties : CodeUri : log_to_es/ Handler : app.lambda_handler Runtime : python3.8 Events : BucketEvent : Type : S3 Properties : Bucket : !Ref LogBucket Events : s3:ObjectCreated:Put Filter : S3Key : Rules : - Name : prefix Value : log_prefix/ Role : !Ref ExecutionRole Environment : Variables : ES_DOMAIN_URL : !Ref EsDomainUrl VpcConfig : SecurityGroupIds : !Ref SecurityGroupIds SubnetIds : !Ref SubnetIds LogBucket : Type : AWS::S3::Bucket Properties : BucketName : !Ref S3Bucket S3 に保存された ECS のログを、Lambda を使用して Amazon ES に転送する方法を今回は使いましたが、ECS のログを Amazon ES にルーティングする方法として、FireLens を使用することで Fluent Bit が直接ログデータを Amazon ES にルーティングすることもできるようになったようです。 aws.amazon.com こちらの方法を使う場合は、Amazon ES へ送信するデータの加工についてや、ログデータが欠損した場合の考慮などを考える必要がありそうでしたが、手軽に Amazon ES にログデータをルーティングできるのは良さそうだと思いました。 Amazon Elasticsearch Service の構築 Amazon ES の VPC アクセスでの構築と、外部から Kibana にアクセスするための環境構築をおこないました。 Amazon ES の使い分けとして、 Lambda による、Elasticsearch API を用いたログデータの投入 Kibana によるログデータの解析のためのアクセス があります。 今回は、VPC アクセスによる構築により、Amazon ES に対するセキュリティの強化、Amazon ES と Lambda 間の安全な通信を実現することができましたが、一方で、Kibana に対しては適切なアクセス制限をした上で、データ解析のための外部からのアクセスをする必要があったので、ALB と ECS のリバースプロキシサーバーを使用した環境構築をおこないました。 Amazon Elasticsearch Service の Terraform による構築 今回 Amazon ES は、Terraform で構築しました。 Amazon ES を Terraform で構築する場合の、基本的な設定をした定義例は次のようになります。 resource "aws_elasticsearch_domain" "es" { domain_name = "sample" elasticsearch_version = "6.3" cluster_config { instance_type = "t2.small.elasticsearch" instance_count = 1 } vpc_options { subnet_ids = var.es_subnet_ids security_group_ids = var.es_security_group_ids } ebs_options { ebs_enabled = true volume_size = 10 } } この例では、Elastic Search の version や、インスタンスタイプの設定、サブネットやセキュリティグループの VPC 周りの設定をしています。 Amazon ES のセキュリティに関しては、 VPC アクセス設定によるネットワークに関するセキュリティレイヤー ドメインアクセスポリシーによる、リソースベースのセキュリティレイヤー Fine Grained Access Control による、ロールベースの細かいアクセスコントロールによるセキュリティレイヤー の 3 つの主要なセキュリティレイヤーがあり、このうち Terraform によるドメインアクセスポリシーの定義例は次のようになります。 resource "aws_elasticsearch_domain_policy" "es" { domain_name = aws_elasticsearch_domain.es.domain_name access_policies = jsonencode( { Version : "2012-10-17", Statement : [ { Effect : "Allow", Principal : { AWS : "*" }, Action : "es:*", Resource : "${aws_elasticsearch_domain.es.arn}/*" } ] } ) } ドメインアクセスポリシーの設定については、aws_elasticsearch_domain 内の access_policies でも設定することができますが、Terraform では、リソースの定義内で自らを参照することができないので、今回の場合は上記のように別リソースで定義しました。 BASE BANK チームでは、Terraform のセキュリティ静的解析ツールである tfsec を導入していて、今回の構築にあたって、tfsec の指摘により設定の検討をした項目がいくつかありました。 tfsec についてや、検討した項目に関しては、同僚の東口が書いた下記エントリをご参照ください。 devblog.thebase.in Amazon ES の構築にあたって注意すべき点として、既存のドメインには設定できず、新規で構築する際にしか設定できない項目や、インスタンスタイプや Elasticsearch のバージョンによっては設定できない項目が存在するというのがありました。 例えば、Audit Logs の有効化があります。 Amazon ES では、Audit Logs を使用することで、Elasticsearch へのすべてのリクエストのロギング、インデックスの変更、受信検索クエリの記録などのあらゆるユーザーアクティビティのログが記録できるようになりました。 aws.amazon.com しかし、この項目を設定するには、既存、または新規の Amazon ES で、Elasticsearch のバージョンが 6.7 以降であり、Fine Grained Access Control が有効になっている必要があります。 Fine Grained Access Control とは、ロールベースのアクセスコントロールによる、クラスターレベル、インデックスレベル、ドキュメントレベル、フィールドレベルの細かいアクセス許可や、Kibana マルチテナンシーの使用などができるようになるものです。 この設定を有効化するには、 保管時のデータの暗号化 と ノード間の暗号化 が有効になっており、ドメインへのすべてのトラフィックに HTTPS を要求する設定が有効になっていなければなりません。 このうち、保管時のデータの暗号化と、ノード間の暗号化、Elasticsearch のバージョンに関しては、既存のドメインに対して変更することができないので、変更したい場合はドメインを新規に作成する必要があります。 また、保管時のデータの暗号化に関しては、インスタンスタイプによっては設定できないので注意が必要です。 docs.aws.amazon.com ECS のリバースプロキシサーバーを使用した Kibana によるデータ解析環境構築 VPC アクセスによって構築された Amazon ES において、Kibana への外部からのアクセスをする方法はいくつかありますが、今回は ALB と ECS のリバースプロキシサーバーを使用した方法により環境構築しました。 ECS のリバースプロキシサーバーを経由せずに、ALB から直接アクセスすることもできますが、Amazon ES の Private IP の変更を定期的にメンテナンスする必要があり不便です。 ECS のリバースプロキシサーバーを経由することで、Amazon ES のホスト名を使って設定ができるので、定期的なメンテナンスが不要になり、より柔軟な設定をすることもできるかと思います。 今回の構築では、ECS のリバースプロキシサーバーは、nginx のイメージを使用し、設定ファイルを置き換えるだけの簡素な方法でおこないました。 ECS デプロイツールについて ECS のデプロイ方法についてはいくつか検討した上で、ECS CLI によるデプロイ環境を構築しました。 docs.aws.amazon.com ECS CLI は、ECS クラスターおよびタスクの作成、更新を、Docker Compose ファイルを利用しておこなえるツールです。 ECS CLI では、ALB やセキュリティグループなどのリソースの作成、管理ができませんが、今回は Terraform で ECS サービス、タスク以外の必要な AWS リソースが管理されていたので容易に導入することができました。 また、Docker Compose と Amazon ECS の統合により、Docker コマンドラインを使用し ECS へのアプリケーションのデプロイ ができるようになりました。 aws.amazon.com Docker Compose で構築された既存のアプリケーションの拡張や、Docker Compose を利用する ECS 開発者の開発体験の向上が図れるようになるようなので、機会があれば ECS CLI との違いについても含めて調査してみたいと思いました。 デプロイ方法を検討する中で、AWS Copilot CLI についても調査しました。 aws.github.io AWS Copilot CLI は、最低限 Dockerfile さえあれば、ECS クラスターやサービス、タスクに加えて、VPC やサブネット、ALB などのその他必要な AWS リソースを作成と、複数の AWS アカウントや複数の環境に対するデプロイのサポートまでおこなってくれる、かなり抽象度の高いツールです。 一方で、v0.3 から既存の VPC やサブネットの設定ができるようになりましたが、細かい設定についてはまだまだできない状況です。 aws.amazon.com 既存の ALB を設定するなど、その他の細かい設定ができない状況から今回は導入を見送りましたが、ECS でプロトタイプを動かしたり、技術検証をしたりすることが簡単にできる強力なツールだと思いますので、今後の進化に期待しつつ、使っていきたいと思いました。 おわりに Amazon ES を用いた、ECS/Fargate アプリケーションのログ解析基盤の構築例と、それに関連する内容について紹介させていただきました。 これから ECS/Fargate アプリケーションのログ解析基盤の環境構築をされる方々の助けになれば幸いです。 また今回の構築にあたっては、AWS のアップデートのサイクルの早さをとても感じました。 次々と新しい機能がリリースされるので、定期的にキャッチアップしたり、環境構築するたびに新しい技術の検証をしていかないとなと思いました。
アバター
BASE BANK 株式会社 Dev Division でSoftware Developer をしている清水( @budougumi0617 )です。 みなさんの開発現場でも社内ライブラリ・モジュールとして開発しているコード・GitHubリポジトリがあると思います。 そのようなリポジトリはパッケージ管理システムを経由して利用することがほとんどですが、そのためにはリリース作業を行う必要があるかと思います。 私のチームでは先日GitHubリポジトリのリリース作業をGitHub Actionsで自動化したので、本記事ではその内容を共有したいと思います。 TL;DR 今回はGitHub Actionsとrelease-it npmを使っています。 github.com www.npmjs.com 上記の技術を組み合わせることで次のような自動リリースのワークフローを構築しました。 (Pull Requestがマージされるなどで)mainブランチにコミットがプッシュされたらタグを打ち、GitHubリリースを作成する。 前回リリースとの差分で コミットメッセージのリリースノートを作成する 特定のファイルまたはディレクトリが更新されていたときだけ リリースする コミットメッセージに応じてセマンティックバージョンのパッチ/マイナー/メジャーアップデートを切り替える Actions上でしか使わないnpmパッケージなので、 リポジトリにpackage.jsonを置かない GitHub Actionsを使って自動化を行なうと、 コミット内容に応じた操作が簡単に実現 できます。 ただし、次の制約もありました。 プロテクトブランチを利用している場合はGitHub Actions上からコミットをプッシュすることはできない リリース用のPRをつくるといった迂回策が必要 そのため、今回の自動リリースでは「リポジトリ内の version 変数の値を更新してコミットしておく」のような操作は含んでいません。 なお、今すぐ試してみたい方は以下の2つのファイルを用意するだけで実現できます。 .release-it.json .github/workflows/release.yml .release-it.json の内容は次のとおりです。GitHubリポジトリのルートディレクトリに配置します。 https://github.com/budougumi0617/autorelease-by-release-it-on-actions/blob/main/.release-it.json { " requireUpstream ": false , " requireCleanWorkingDir ": false , " github ": { " release ": true } , " git ": { " commit ": false , " push ": false , " requireUpstream ": false , " requireCleanWorkingDir ": false } , " npm ": { " publish ": false , " ignoreVersion ": true } } .github/workflows/release.yml の内容は次のとおりです。 https://github.com/budougumi0617/autorelease-by-release-it-on-actions/blob/main/.github/workflows/release.yml name : auto release demo on : push : # mainブランチにコミットがpushされたときに限定 branches : - main # 上記条件に加えてgenディレクトリ配下が変更されたときのみという条件を追加 paths : - gen/** jobs : auto-release : runs-on : ubuntu-latest env : GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} RELEASE_IT_VERSION : 14.2.1 steps : - name : Check out codes uses : actions/checkout@v2 with : fetch-depth : 0 - name : Setup Node uses : actions/setup-node@v1 with : node-version : '12' - name : Set releaser settings run : | git config --global user.name release-machine git config --global user.email email@example.com - name : Major release id : major if : contains(toJSON(github.event.commits.*.message), 'bump up version major' ) run : npx release-it@${RELEASE_IT_VERSION} -- major --ci - name : Minor release id : minor # メジャーバージョンアップをしていないときマイナーバージョンアップを行なうか if : steps.major.conclusion == 'skipped' && contains(toJSON(github.event.commits.*.message), 'bump up version minor' ) run : npx release-it@${RELEASE_IT_VERSION} -- minor --ci - name : Patch release # コミットメッセージに特に指定がない場合はマイナーバージョンを更新する if : "!(steps.major.conclusion == 'success' || steps.minor.conclusion == 'success')" run : npx release-it@${RELEASE_IT_VERSION} -- patch --ci 今回のサンプルYAMLの場合はmain ブランチに gen ディレクトリ内への変更を含んだPRをマージすると自動でリリースが行なわれます。 また、 bump up version major といったメッセージが含まれていた場合はメジャーバージョンアップが行なわれます。 https://github.com/budougumi0617/autorelease-by-release-it-on-actions/releases サンプルリリースページ なお、本記事で利用している各ツールのバージョンは以下のとおりです。 ツール名 バージョン GitHub Actions v2 release-it npm 14.2.1 Node.js 12.X系 以下のURLは実際にGitHub Actionsで何回か自動リリースをしてみたサンプルリポジトリです。 https://github.com/budougumi0617/autorelease-by-release-it-on-actions リリース作業を自動化したい どんな言語を使っていても、業務で開発を行なっていると社内ライブラリを作成することがあると思います。 作成したライブラリはnpmやComposer、Go Modulesなどのパッケージ管理システムを経由して使うことになるのが大半だと思います。 そうなると一定の更新ごとにタグを設定し、バージョン管理する必要が出てきます。 とはいえ 「PRをマージしたら git tag コマンドを打って…」と各開発者が行なうのは億劫 です。 そのため、 mainブランチにPRがマージされたら(コミットがプッシュされたら)自動でタグ打ち、リリースする という自動化を試みました。 もちろんタグはリリースのたびにセマンティックバージョンがインクリメントされるようにします。 GitHub Actions上でrelease-it npmを実行してリリースをする https://www.npmjs.com/package/release-it release-it npmはよしなにセマンティックバージョンをインクリメントしながらリリースノートも作ってGitHubリリースを作成してくれるコマンドです。 たとえば、現時点のバージョンが 0.1.2 だったとき、次のように実行するとマイナーバージョンをインクリメントした 0.2.0 バージョンのリリースを作成してくれます。 $ npm run release -- minor --ci 次のリンクはrelease-it npmで作成されたリリースノートです。 https://github.com/budougumi0617/autorelease-by-release-it-on-actions/releases/tag/0.0.1 リリースノートのサンプル GitHub Actions上でNode.js環境を用意して実行するだけで終わりかと思いきや、いろいろ設定する必要があったので、ポイントを解説していきます。 GitHub Actions実行時にタグも取得しておく https://github.com/actions/checkout#checkout-v2 Set fetch-depth: 0 to fetch all history for all branches and tags. 今回構築する自動リリースのワークフローでは既存のタグからリリースするセマンティックバージョンを決定します。 そのため、GitHub Actions実行時に タグも一緒にチェックアウトしておく必要があります 。タグはGitHub Actionsを利用時にほぼ100% use されているであろう actions/checkout に対して fetch-depth: 0 オプションを渡すだけで取得可能です。 - name : Checkout codes uses : actions/checkout@v2 with : fetch-depth : 0 特定のパス配下が更新されたときのみリリースする 今回自動リリースしたいリポジトリはコードの自動生成を行なっていました。そのため、次のような事情がありました。 PRがマージされるたびのリリースは (どんどんバージョンが上がってしまうので)してほしくない 自動生成したコードが配置されている gen/ ディレクトリの内容が変更されたときだけ リリースしたい OpenAPIやgRPCなどを利用して同リポジトリ内でクライアントコードを自動生成したりしていると、同様のニーズが生まれると思います。 最初はCircleCIを利用して自動リリースを実現しようと思ったのですが、 パスを使ってCIを制御するのはGitHub Actionsのほうが簡単だったので 、GitHub Actionsで自動リリース作業を行なうことにしました。 GitHub Actionsのワークフローでは次のような制御をすることができます。 コミット内容を確認して特定ディレクトリに更新があったか確認する https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#onpushpull_requestpaths GitHub Actionsでは、 on.<push|pull_request>.paths を使うことで、特定ディレクトリに更新があったときだけにジョブの実行を制限できます 1 。 次のサンプルコードは以下の2つの条件を満たしたときのみ実行される設定です。 mainブランチにコミットがプッシュされた プッシュされたコミットの中に gen/ ディレクトリ内の更新が含まれていた on : push : branches : - main paths : - gen/** ワークフローの制御にコミットメッセージを利用する https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions このデータ構造は おそらく公式ドキュメントに明示的に載っていない のですが、GitHub Actionsでブランチにpushされた 一連のコミットの情報をジョブ実行中に利用可能 です。 ワークフロー実行時の情報は github context として参照できるのですが、この中の github.event でpushされたコミットの情報を持っています 2 。 この情報をパースするとコミットの内容をワークフロー中に使うことができます。 次のコードは「コミットのメッセージに 'bump up version major' があったら true になる」式です。 contains(toJSON(github.event.commits.*.message), 'bump up version major' ) https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsif これと、 jobs.<job_id>.steps.if 、を使うことで、「コミットメッセージによって実行されるstep」をワークフローに用意することができます。 jobs : sample : runs-on : ubuntu-latest steps : - name : teststep if : contains(toJSON(github.event.commits.*.message), 'コミットメッセージを確認' ) run : echo 'executed!!' https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions#steps-context また、contextの steps.<step id>.conclusion を用いることで前ステップの実行結果を利用して else if のような制御を行なうことも可能です。 jobs : ifelse-pattern : steps : - name : foo id : foo if : contains(toJSON(github.event.commits.*.message), 'foo' ) run : echo 'if step!' - name : bar id : bar # fooステップがスキップされた && コミットメッセージにbarを含む場合に実行する if : steps.foo.conclusion == 'skipped' && contains(toJSON(github.event.commits.*.message), 'bar' ) run : echo 'elseif step!' 次のリンクは実際にステップをいくつかスキップしているActionsの実行結果です。 https://github.com/budougumi0617/autorelease-by-release-it-on-actions/runs/1454817991 Actions実行画面 ここまではGitHub Actionsの設定方法でしたが、次はrelease-it npmをGitHub Actions上で使うコツです。 タグの設定とリリースはするが、コミットはしない release-it npmはGitHub Actions上で npx コマンドで実行しているので package.json は不要です。 が、release-it npm用の設定を用意する必要があります。 今回利用している設定は次のとおりです。 { " requireUpstream ": false , " requireCleanWorkingDir ": false , " github ": { " release ": true } , " git ": { " commit ": false , " push ": false , " requireUpstream ": false , " requireCleanWorkingDir ": false } , " npm ": { " publish ": false , " ignoreVersion ": true } } ざっくり説明すると、次のような設定です。 GitHubリリースを作成する gitのコミットは作成しない gitのpushはしない npmの公開はしない バージョンを決定するために package.json 内のバージョンを参照しない 鋭い方は「”pushしない”ってことはタグも公開されないんじゃないの?」と思うかもしれませんが、いいのか悪いのか、リリースを行なうときにタグはプッシュされるようです。 Actionsからプロテクトブランチにはコミットをpushできない https://github.community/t/how-to-push-to-protected-branches-in-a-github-action/16101/5 ここまで便利なGitHub Actionsでしたが、ひとつ制約があります。それは プロテクトブランチにコミットをプッシュすることができない 点です。抜け道がないか探していたのですがなさそうなので諦めました。 なので、先ほどのrelease-it npm用の設定ファイルは「タグの設定とリリースはするけどコミットはプッシュしない」という内容になります。 「自動リリースするときは package.json の中にある version も更新したい(コミットプッシュしたい)んだけど!」というようなニーズももちろんあると思います。 しかし、今回のユースケースではリリースタグでバージョンが管理されていればファイルとしてバージョンが参照できる必要はなかったので、こちらも妥協しました。 あとは開発するだけ! 以上の設定を行うと、特定条件のコミットを作るだけで自動でリリースされるようになります。 それぞれの設定は独立しているので、お好みでカスタマイズしていただけばと思います。 特定のディレクトリに限定する必要はないので on.<push|pull_request>.paths の設定は削除する マッチするコミットメッセージを変更する etc... name : auto release demo on : push : # mainブランチにコミットがpushされたときに限定 branches : - main # 上記条件に加えてgenディレクトリ配下が変更されたときのみという条件を追加 paths : - gen/** jobs : auto-release : runs-on : ubuntu-latest env : GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} RELEASE_IT_VERSION : 14.2.1 steps : - name : Check out codes uses : actions/checkout@v2 with : fetch-depth : 0 - name : Setup Node uses : actions/setup-node@v1 with : node-version : '12' - name : Set releaser settings run : | git config --global user.name release-machine git config --global user.email email@example.com - name : Major release id : major if : contains(toJSON(github.event.commits.*.message), 'bump up version major' ) run : npx release-it@${RELEASE_IT_VERSION} -- major --ci - name : Minor release id : minor # メジャーバージョンアップをしていないときマイナーバージョンアップを行なうか if : steps.major.conclusion == 'skipped' && contains(toJSON(github.event.commits.*.message), 'bump up version minor' ) run : npx release-it@${RELEASE_IT_VERSION} -- minor --ci - name : Patch release # コミットメッセージに特に指定がない場合はマイナーバージョンを更新する if : "!(steps.major.conclusion == 'success' || steps.minor.conclusion == 'success')" run : npx release-it@${RELEASE_IT_VERSION} -- patch --ci 終わりに 「PRマージしたぞー!」と思っても、そのあとにポチポチリリース作業をするのは億劫でした。 これで少しでも生産性があがるといいなと思っています。 なお、GitHub Actionsからプロテクトブランチへの直接コミットプッシュはできないのですが、renovateはPRを作成、Appで自動承認、自動マージという迂回をしてプロテクトブランチへのプッシュを実現しているようです。 GitHub Apps - renovate-approve · GitHub もっと突き詰めたくなったら同様の操作を実装してファイル更新も含めた自動リリースを実現したいなと思います。 最後に、BASE BANKでは新しくデザイナーとカスタマーサクセスの募集を開始したので、ぜひご応募お待ちしています。 www.wantedly.com www.wantedly.com 参考リンク https://github.com/budougumi0617/autorelease-by-release-it-on-actions https://www.npmjs.com/package/release-it https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions https://github.community/t/how-to-push-to-protected-branches-in-a-github-action/16101/5 「特定のディレクトリに更新があったときは無視する」という逆制御もできます。 ↩ contextをechoして無理やり確認しました。 ↩
アバター
BASE株式会社取締役 EVP of Developmentの藤川です。 世界中が新型コロナの影響で雇用の先行きが不透明な中、当社は引き続き成長を模索している状況で、マネージャ陣を中心に採用活動にも注力する毎日を送っています。 当社は正社員採用はもちろんのことですが、業務委託契約の方々にもお手伝いいただいておりますが、今回は業務委託契約にフォーカスした記事を書いてみたいと思います。 内製にこだわるチームを維持するための採用活動 私達はサービスを維持、成長させるために毎日ソースコードのメンテナンスをしています。我々はAWSやGCPなどのクラウドの環境とオープンソースのソフトウエアに恩恵を受けながら、独自のロジックは内製で開発しています。 他社の開発体制の事例として、スタートアップとして最初は内製で立ち上げたとしても、組織が大きくなりビジネスの成長が問われる中で、SIerさんに社内にがっつり入り込んでもらってSES契約などで開発力を補填したり、オフショアで海外に開発をお願いする事例をよく聞きます。 我々は創業以来、内製でソースコードを書いてきたチームですので、やはり内製での開発にはこだわりがあります。内製の重要性は、正社員がソースコードを書くか否かということではなく、ソースコードを書く行為とサービス運用が密接に結びついており、デプロイ後に何かあったら、ソースコードを書いた当人に即座にフィードバックして改善を求められる体制が維持されていることを示します。 これによりWebサービスを作る楽しさと技術者としての成長をリアルタイムに実現し、プロダクトクオリティに結びつけるということを大切にしています。スピーディな開発と改善を実現することが、持続的に成長するWebサービスの改善サイクルを支えています。開発メンバーにおいても、この開発サイクルを回し続けることが、複利的にエンジニアとしてのスキルを向上することに繋がります。 言い方を変えると「プロダクトを成長させることを前提とし、内製による開発体制を維持し続ける」ということもあります。プロダクトを連続的に成長させないのであれば、内製の開発体制にこだわる必要はなく、開発をアウトソースしフェーズごとに、僕らの預かり知らないソースコードを積み上げていって機能を付け足していくという選択もまたあるのでしょう。積極的にオフショアなどをやられている会社さんからすると、これはチームの覚悟の話と思われるのかもしれません。 現時点で思うこととして、もし受託契約やオフショアで開発し、納品されたソースコードによって深夜に不具合やパフォーマンス問題等が起きれば、いの一番に対応するのはSREチームやCTOである可能性が高く、誰が書いたのかの顔が見えないソースコードで不具合に対応し続けるのは酷な話だと僕は考えています。 サービスを運営し続けるのであれば、言い方は変ですが、不具合を出した仲間の顔が思い浮かぶ形、すなわち、自分たちチームの責任として納得行く形で不具合対応をしたりソースコードの改善をしていくことは仕事に対するモチベーション維持としても重要視しています。Webサービスにおいて人が携わる時間は、サーバを維持をするだけの活動ではなく、ショップオーナーさんの流通総額の実現を支え、サービスの改善のヒントを得る大切な活動なので、無駄な時間には使いたくないです。 つまり、内製組織を維持するための人件費や採用費に投資することは、それに携わる人の成長とプロダクトの成長を実現するためであり、結果として企業のポテンシャルを蓄積的に向上させていく手段ということになります。 ただし、これも成長圧力に対して、ちゃんと開発が追いついていればの話。内製にこだわり続けて、採用が滞ってしまって想定する成長を実現できなかったとしたら、もしかしたら内製にこだわる僕が経営責任を取る形で会社の様相が変わってしまったら、こういうこだわりを続けることはできなくなってしまうかもしれません。 そうならないためにもしっかり仲間を増やしていくことは開発チーム全員で携わっていく重要な仕事であると考えています。 内製にこだわるチームを維持するための業務委託 内製チームを実現するために雇用形態や国籍はこだわりません。同じチームで動いて改善サイクルを回せる状態を維持できていれば良いのだと思います。そのため携わるメンバーの契約形態は、現状は正社員としてジョインいただくか、業務委託契約でお願いしている状況です。 これまでBASEやPAYの開発においては、比較的少人数ではあるものの業務委託契約の方にもご活躍いただいてきました。我々が業務委託契約でお願いする方は「社員として登用できないハイスキルな方」と定義してきました。 つまり既に独立してフリーランスや自分の会社を持っていて独立心が高かったり、所属している会社の愛着があるからこそ、社員として来ていただくことが難しい方との契約形態として活用しています。 その代表例として、沖中さんのインタビューを動画で収録しました。もう2年半、BASEをお手伝いいただいていて、BASEの発展には欠かせない仲間です。 業務委託契約の方と社員とは多少の役割の差はあれど、労働環境や開発に必要な情報も含め、できる限り分け隔てなく、社員と同じように活躍を期待しています。技術ブログの執筆も普通にお願いしていたり社内勉強会で活躍いただくなど、情報のインもアウトも特に分け隔てなく業務としてこなしていただいております。 2021開発計画をお手伝いいただく業務委託契約の方を募集します! 現在、2021年の開発計画を整えていますが、とてもとても人が足りません。BASEというサービスをもっとよくして、ショップオーナーさんの成長を支えるための開発を一緒にやっていただける業務委託の人を増やしたいと思っています。もちろん、社員登用も積極的に行っております。 なおエンジニア採用向けの会社紹介資料を作ったので、よろしかったら是非見てみてください。 speakerdeck.com 役割別の募集要項はこちら! あらゆる役割で人材募集中! フロントエンドエンジニア open.talentio.com 開発プロジェクトにおけるフロントエンド実装と、BASEのフロントエンド実装におけるライブラリや実装技術の守り神を担います。 Webアプリケーションエンジニア open.talentio.com Webアプリケーションエンジニアは主体技術はバックエンド実装ですが、サービスを作る時にフロントエンドも書いています。 バックエンドエンジニア(アプリAPI開発) open.talentio.com BASEアプリのAPIの部分を開発するエンジニアになります。
アバター
フロントエンドチームの右京です。サービスの利用者向けには BASE U にて告知いたしましたが、2020 年 11 月 15 日をもって BASE は Internet Explorer 11 (以下 IE11) のサポートを終了しました。サポート終了と聞くと基本的にはネガティブな印象になりがちですが、ここでは主に開発者に向けて、サポートを終了することによって広がる新しい未来の話をしたいと思います。 ショップのデザインを進化させるであろう 2 つの技術 IE11 サポート終了後に新たに利用できる技術の中で、特に注目しているのは「Web Components」と「CSS カスタムプロパティ」です。 developer.mozilla.org developer.mozilla.org どちらも名前を聞くことが増えてきていて、すでにご存知の方も多いと思います。簡単に言えば前者は独自のタグを作って提供できる機能、後者は CSS で使える変数だと思ってもらえるとイメージしやすいのではないでしょうか。 HTML 編集とショップデザイン これらを活用できる背景には BASE が提供している「HTML編集」という App の存在があります。この機能はショップのデザインをかなり自由に編集できる機能で、 デザインマーケット のベースとなるものともなっており BASE の発展とは切っても切れない関係です。その自由度の高さから多くのオーナーズに利用していただいているのですが、一方で機能の動作保証や技術的サポートが難しいという問題がありました。 機能を提供し、保護するための Web Components HTML 編集が提供している、BASE のショップを構築するための条件や変数を BASE Template と呼びます。これについて詳しく知りたい場合は、以下のドキュメントを参照してください。 はじめに · Developers この中でも「商品の名前」を取得するための {ItemTitle} のようなタグで問題が起こることはほとんどありません。しかし例えば {AppsReviewTag} は、そのタグをレビュー機能を実現するための比較的大きめの HTML と JavaScript に置き換えて動作するタグとなっています。このタグはいわゆるコンポーネントのような扱いとなるのですが、これは現状多くのブラウザで動作をさせるため、必要な HTML、JavaScript、CSS に置き換えるような素朴な実装となっています。(例のコードはわかりやすくしたもので、実際に配信されるものではありません。) // HTML 編集で記述されたコード < div class = "review" > {AppsReviewTag} </ div > // 実際にショップとして配信されるコード < div class = "review" > < div class = "base-review-container" > < div ...> .... </ div > </ div > < script > .... </ script > </ div > これによって特に多く起こる問題が「意図しない破壊」です。スコープの存在しないものに BASE が開発したものを後付けするような形になるため、コードに予期せぬコンフリクトが発生するケースがあります。例えば、 BASE 側の改修によって HTML 編集で作成されたショップのデザインと CSS がコンフリクトしデザインが崩れてしまったり、他の箇所の JavaScript の実行エラーによって一部の機能が停止してしまったことがありました。結果としてオーナーズ、BASE 共に問い合わせやクレームに繋がってしまいます。 // 意図せず class が衝突してしまった! < div class = "review" > < style > .review { padding : 40px ; /* 二重に padding が適用されてしまい、デザイン崩れの原因に */ } </ style > < div class = "review" > < div ...> .... </ div > </ div > < script > document . querySelectorAll ( '.review' ) . ... // 本来想定していない要素も対象に </ script > </ div > これらの問題を解決する方法として Web Components、 特に Shadow DOM への期待が大きくなっています。Shadow DOM はカプセル化された DOM ツリーやスタイルを使用できるため、HTML 編集が書かれたコードと BASE が提供しているコードがお互いに干渉しづらくなります。これを利用することで、提供側利用側に関わらず安定した機能の開発、追加が実現できるようにしていきたいと思っています。 また、BASE のショップ以外への BASE の機能の追加のようなことも考えられます。将来的に以下のようなコードを埋め込むことで、どこからでも BASE の商品を購入できる機能が提供されるかもしれません。 < base -order itemId= "1234567890" ></ base -order > CSS カスタムプロパティによるデザインの変更 CSS カスタムプロパティについては 2 通りの活用方法を考えています。 1 つは先ほど紹介した Web Components での機能提供のデザイン面を補助するような形での利用です。Web Components に機能を隠蔽していくと安全性が高まる一方で、これまで比較的容易に行えていたスタイルの上書きによるデザイン調整が行いづらくなってしまいます。機能の安定性ももちろんですがショップやテーマにあったデザインを提供できる必要もあるため、この隙間を埋めるための方法として CSS カスタムプロパティ経由で多くのスタイルを変更可能にできないかと考えています。 2 つ目は実際に BASE のショップ運用している方でないと分かりづらいかもしれません。 デザインマーケット で販売されている多くの素敵なテーマは、さらに細部をカスタムできる「デザインオプション」を提供しています。 デザインオプション · Developers このデザインオプションを本当に細かく用意してくださるテーマデザイナー様も多く BASE としても非常に嬉しいのですが、どうしてもその数に圧倒されてしまうようなケースも存在します。そこで、こだわり度にあわせて段階的な設定を CSS カスタムプロパティをベースに導入できないかと考えています。例えばですが「デザインオプション」で設定できるものは大まかなカラーのみに絞り、細かなカラー調整については個々に CSS カスタムプロパティを設定できるような機能です。 他にもカラーセットのような機能も考えられます。現行のものでも一部のカラーについてはテーマ間で設定を引き継げるようになっているのですが、これをより汎用的な仕組みとすることでテイストを維持したまま様々なテーマを試せるなど。これらはもちろん BASE だけで実装できるものではなく、テーマデザイナーの協力も必須なものです。 まだまだできることはたくさん 今回は特に HTML 編集やショップデザインに大きく関わるものについて言及しましたが、もちろん他にも BASE がもっと使いやすくなるような改善も実施されていきます。 IE11 のために妥協していたユーザーインターフェイスの調整 管理画面のパフォーマンスの向上 そして、開発のスピードアップ IE11 のサポートを終了し、開発にブーストのかかった BASE の今後にご期待ください。そしてそんな開発を一緒にやってみたいメンバーも常に募集しております。 open.talentio.com
アバター
自己紹介 こんにちは。BASE株式会社のフロントエンドチームの谷口です。 本日は、BASEのフロントエンドで使用している日付ライブラリについてお話しします。 BASEの日付ライブラリについて BASEでは、frontendという領域が出来始めた当初、最もメジャーな日付ライブラリである moment.js を使用していました。 その後、 デザインコンポーネントの開発 など、frontend領域が成長していく中で より使い勝手の良い別の日付ライブラリが検討され、 date-fns が採用されました。 現時点で、ほぼ全てのコードがdate-fnsに移行済みです。 date-fnsについて date-fnsについて少し説明すると、公式にもありますが下記のような特徴が上げられます。 moment.jsや day.js がDateオブジェクトをラップして扱うのに対し、純粋な関数を必要な分だけ読み込んで使用することが出来ます。 こちらの ディレクトリ を見ると、180以上の機能が用意されており、全て関数をexportしている事がわかります。 date-fnsは値を不変に保つことが可能です。 これはmoment.jsと比較すると下記のような違いが見られます。 //moment.jsの場合、addを呼び出す度に元の値が変更され、同じ結果が得られません。 //コンソールに警告こそ表示されますが、この動作が予期せぬバグを生み出す可能性があります。 const today = new Date (); const momentToday = moment(today); momentToday.add( "day" , 3); console.log(momentToday.toDate()); // Sat Nov 07 2020 11:17:47 GMT+0900 (日本標準時) momentToday.add( "day" , 3); console.log(momentToday.toDate()); // Tue Nov 10 2020 11:17:47 GMT+0900 (日本標準時) //date-fnsの場合、元の値を変更することはありません。 const threeDaysTime = addDays(today, 3); console.log(threeDaysTime); // Sat Nov 07 2020 11:17:47 GMT+0900 (日本標準時) const sixDaysTime = addDays(threeDaysTime, 3); console.log(sixDaysTime); // Tue Nov 10 2020 11:17:47 GMT+0900 (日本標準時) また他のライブラリと依存しておらず、関数を呼び出す度に新しいDateオブジェクトが返ります。 関数自体にも副作用が無いため、テストツールや他のライブラリに組み込むことも容易です。 bundleサイズも使用する言語やwebpackの設定により異なりますが、比較的軽量です。 出典: https://bundlephobia.com/ TreeShakingをサポートしています。 TypeScriptとFlowどちらにも対応しています。 ドキュメント も充実しており、サンプルコードも豊富なので、使い方に困るということも少ないでしょう。 また近頃、moment.jsがメンテナンスモードになるという主旨の 記事 が公開されました。 その移行先候補の1つとしてdate-fnsも記載されており知名度も高いライブラリです。 出典: https://www.npmtrends.com/ date-fnsのバージョンアップ 上記の経緯で採用されたdate-fnsですが、BASEでは、実装当初のv1.30.1 からアップデートされていなかったため、今年の3月にv2.0.0にメジャーアップデートしました。 v2.0.0の開発にはおよそ 2年の歳月 がかけられており、 中には 破壊的変更 も含まれていたため、いくつか修正する必要がありました。 v1.30.1 -> v2.0.0の主な破壊的変更点 下記が関数名の変更などを除いた主な変更箇所です。 (サンプルコードは公式より抜粋しています) 主要な関数は文字列を許可していたが、日付のみ指定になった。 文字列を使用したい場合はparseISOで変換して使用する必要があります。 // Before v2.0.0 addDays( '2016-01-01' , 1) // v2.0.0 onward addDays(parseISO( '2016-01-01' ), 1) Unicodeのフォーマットが変わりました。 以前まではmoment.jsの仕様を模倣していた 経緯 がありましたが、その他の多くの言語に習って、より普遍的な物に変更されました。 後方互換用のオプションが用意されていますが、今後このオプションが削除される可能性も高いので、可能な限り変更した方が良いでしょう。 // Before v2.0.0 format( new Date (), 'YYYY-MM-DD' ) //=> 2018-10-283 // v2.0.0 onward format( new Date (), 'yyyy-MM-dd' ) //=> 2018-10-10 format( Date .now(), 'YY-MM-dd' , { awareOfUnicodeTokens: true } ) //=> '86-04-04' format関数は明示的にフォーマット形式を指定する必要があります。 // Before v2.0.0 format( new Date (2016, 0, 1)) // v2.0.0 onward format( new Date (2016, 0, 1), "yyyy-MM-dd'T'HH:mm:ss.SSSxxx" ) 日数の変換はparse関数に全て委任していましたが、parseは引数を必須とし、それぞれの用途に合わせた関数が用意されました。 // Before v2.0.0 parse( '2016-01-01' ) parse(1547005581366) parse( new Date ()) // Clone the date // v2.0.0 onward parse( '2016-01-01' , 'yyyy-MM-dd' , new Date ()) parseISO( '2016-01-01' ) toDate(1547005581366) toDate( new Date ()) // Clone the date 最後に 日付という性質上、変更箇所は多岐に渡りましたが、破壊的変更があったにも関わらず アップデートの難度はそこまで難しいものではありませんでした。 これは、date-fnsがDateを引数として渡すだけ、というシンプルな使い方であるため、修正すべきコードも比較的読みやすかったおかげだと感じています。 moment.jsがメンテナンスモードになる中、移行候補の1つとしてdate-fnsを検討してみていかがでしょうか。
アバター
こんにちは。BASE BANK 株式会社 Dev Division にて、 Software Developer をしている東口( @hgsgtk )です。 BASE BANK Dev での開発では、クラウドインフラの構成管理に、 Terraform を利用しています。 世の情報をたくさんキュレーションしている CTO の @dmnlk さんに、手軽に CI に組み込めそうなセキュリティチェックツールがあることを教えてもらったので、導入してみました。 CTO氏のキュレーションメディアで紹介された tfsec を早速試して良さそうだった https://t.co/bl67dlW2Ub https://t.co/vAkTOVagec — Kazuki Higashiguchi (@hgsgtk) August 21, 2020 このブログの公開日は 2020/10/30 ですので、導入してから約 2 ヶ月強経ちました。導入・運用をつづけた経験から、いろいろ tfsec 自体の変化や、運用したことで学べた AWS セキュリティプラクティスをご紹介します。 目次 目次 TL;DR tfsec とは 始め方 クラウドの認証キー・権限付与が必要ないため導入しやすい GitHub Actions で始める 既存の指摘は tfsec:ignore でいったん Fixme issue にする tfsec の指摘から AWS におけるセキュリティプラクティスを学ぶ Amazon CloudFront の推奨 SSL/TLS Policy Amazon ECR のイメージスキャン機能の有効化 Amazon S3 のバケットロギング AWS Key Management Service のキーローテーションの有効化 Amazon Elasticsearch Service のデータ保護 ノード間通信の暗号化 保管時の暗号化 Redis 用 Amazon ElastiCache のデータ保護 In-Transit Encryption (TLS) At-Rest Encryption その他 aws_security_group_rule' should include a description for auditing purposes aws_cloudfront_distribution' does not have a WAF in front of it おわりに TL;DR Terraform のセキュリティ静的解析 tfsec はクラウドの認証キー・権限付与が必要ないためすぐに試すことが出来る tfsec 自体の開発も頻繁にコミットされており日々進化を遂げている tfsec を使い続けることで学んだ AWS セキュリティプラクティスをいくつか紹介する tfsec とは tfsec は、Terraform ファイルを静的解析しセキュリティイシューとなるような点を指摘してくれるツールです。 A static analysis security scanner for your Terraform code. Discover problems with your infrastructure before hackers do. www.tfsec.dev AWS・Azure・GCP といったクラウドベンダーをサポートしています。また、秘匿情報の混入がないかといった特定ベンダーに関わらない一般的なセキュリティチェック事項も備えています。 開発はメンテナー・コアコミッターの方々によって頻繁に行われています。チェック項目の新規追加はもちろんのこと、先ほど紹介した https://www.tfsec.dev というウェブサイトを公開したり、tfsec 自体のパフォーマンスチューニングや カスタムチェック 作成のサポートなど、日々新機能が追加されています。また、筆者自身いくつか PR を提出していますがそれに対してもすぐに反応いただきメンテナー・コアコミッターの熱量の高さを感じます。 始め方 クラウドの認証キー・権限付与が必要ないため導入しやすい tfsec が始めやすい特徴として、当該クラウドの認証キー・権限などが不要な点です。たとえば、 terraform plan をするようなツールだと、AWS の場合 IAM を発行する必要があります。しかし、本ツールは *.tf ファイルの内容の静的解析を行うだけなので、これらがいりません。 AWS でのインフラ構成管理を Terraform でやる場合、 Terraform に対してそれなりに強い権限を付与するため、その権限管理をどこでやるかは論点になりがちです。それを考えなくていいのは、とても始めやすい利点だなと感じます。 GitHub Actions で始める 私の所属するチームでは、 GitHub Actions で導入をはじめました。 公式の GitHub では、 triat/terraform-security-scan が紹介されています。しかし今回は、GitHub の Pull request(PR) へのコメントがすぐに実現できる点で、 reviewdog が公開している reviewdog/action-tfsec を使用しました。 github.com 本アクションを使った設定方法は例えば次のような yml ファイルです。 name : tfsec on : [ pull_request ] jobs : tfsec : name : tfsec runs - on : ubuntu - latest steps : - name : Checkout uses : actions /checkout @v2 - name : Terraform security scan uses : reviewdog /action - tfsec@master with : github_token : $ {{ secrets.github_token }} reporter : github - pr - review # Change reporter fail_on_error : " true " # Fail action if errors are found filter_mode : " nofilter " # Check all files, not just the diff flags : "" # Optional これにより、次のように指摘内容が GitHub の PR のコメントで見れるようになります。 ReviewDogによるPRへのコメント ちなみに、最近 GitHub に Unchanged files with check annotations という Beta 版の機能がついて、PR での変更差分外のファイルに対するコメントも反映されるようになりました。 "Unchanged files with check annotations" によるPR差分外のコメント tfsec は、Release によって新たなチェック項目が増えたりします。チェック項目が増えた際に、それに引っかかる内容が Terraform の記述に含まれていないかを継続的にチェックするにあたって、便利な機能だなと個人的に感じています。 github.com 既存の指摘は tfsec:ignore でいったん Fixme issue にする GitHub Actions に入れると、多かれ少なかれ、 Error / Warning レベルの内容が指摘されるかもしれません。導入にあたりすべてを直してからというのも新規に生まれるセキュリティの穴を塞げなくてもったいないので、弊社の例では、最初に tfsec:ignore というマーキングで既存の指摘を無視する設定をしていきました。 resource " aws_ecr_repository " " hoge " { # tfsec : ignore : AWS023 # Fixme above it Fixme で残しつつ、随時解消していく方法をとっていきました。 tfsec の指摘から AWS におけるセキュリティプラクティスを学ぶ 実際に tfsec を使い続けてさまざまな指摘によって、 AWS におけるセキュリティプラクティスを学びました。それらの学びをおすそ分けします。具体的には次の内容を紹介します。 Amazon CloudFront の推奨 SSL/TLS Policy Amazon ECR のイメージスキャン機能の有効化 Amazon S3 のロギングバケット AWS Key Management Service のキーローテーションの有効化 Amazon Elasticsearch Service のデータ保護 Redis 用 Amazon ElastiCache のデータ保護 その他 ひとつひとつ見てみましょう。 Amazon CloudFront の推奨 SSL/TLS Policy tfsec では aws_cloudfront_distribution' defines outdated SSL/TLS policies (not using TLSv1.2_2018) という指摘項目があります。これは、以下の issue で追加された CloudFront のチェック観点です。 github.com この issue 当時のベストプラクティスでは、 TLSv1.2_2018 が推奨されておりました。しかし現在は、 AWS Console に表示されていますが、 TLSv1.2_2019 が推奨されるセキュリティポリシーとなっています。 「そもそも TLSv1.2_2019 はどう違うの」という疑問を持たれた方(かつての自分ですね)は、クラスメソッドさんの次のブログの解説がとてもわかりやすいのでおすすめです。 dev.classmethod.jp resource " aws_cloudfront_distribution " " example.com " { # (省略) viewer_certificate { acm_certificate_arn = " certificateのarn " cloudfront_default_certificate = false minimum_protocol_version = " TLSv1.2_2019 " ssl_support_method = " sni-only " } ) もともと、 TLSv1.2_2018 ではない場合 Error と指摘されていましたが、PR を提出しリリースいただきました。もし TLSv1.2_2019 にあげても Error になる場合は、tfsec のバージョンを 0.27.0 >= に上げてみてください。 github.com Amazon ECR のイメージスキャン機能の有効化 こちらは、同僚の前川さんに対応いただきました。 aws_ecr_repository' defines a disabled ECR image scan という指摘を受け対応したものです。ECR image scan は、2019 年 10 月 28 日に公開された ECR image に対するイメージスキャンの機能です。 aws.amazon.com 他のツールでは、 trivy や dockle などがあげられます。 ECR Image scan を有効にするには、 aws_ecr_repository.image_scanning_configuration を設定します。 image_scanning_configuration は、Terraform 2 系の場合は 2.34 から、 3 系の場合は 3.10 から使用可能です。 resource " aws_ecr_repository " " hoge " { # (省略) image_scanning_configuration { scan_on_push = true } } Amazon S3 のバケットロギング aws_s3_bucket does not have logging enabled と指摘された場合、S3 のバケットロギングの検討が必要です。 S3 では AWS開発者ガイド:Amazon S3 サーバーアクセスのログ記録 で解説がある通り、サーバーアクセスに対するログ記録機能が提供されています。このロギングを有効にしておくとセキュリティやアクセス監査の観点で役に立ちそうです。 resource "aws_s3_bucket" "public-usecase-bucket" { # (省略) logging { target_bucket = aws_s3_bucket.access-logs.id # logging bucketを指定 target_prefix = "logs/" } } resource "aws_s3_bucket" "access-logs" { # (省略) acl = "log-delivery-write" } S3 のアクセスログバケットを作成する際には、acl を log-delivery-write にします。 discuss.hashicorp.com log-delivery-write は、AWS があらかじめ定義した既定 ACL と呼ばれるものの 1 つです(既定 ACL については、 AWS開発者ガイド:アクセスコントロールリスト (ACL) の概要 をご参照ください)。 LogDelivery グループはバケットに対する WRITE および READ_ACP アクセス許可を取得します。 ロギングバケット自体の ACL を考える際にはこの既定 ACL を使用するのが便利です。 以前は、ロギングバケット自体にも aws_s3_bucket does not have logging enabled という指摘が入り tfsec:ignore マーキングで回避する必要がありました。しかし、tfsec に対して PR を提出し解消いたしました。S3 バケットのロギングバケットの場合、当該指摘をしないように修正されています。 github.com AWS Key Management Service のキーローテーションの有効化 これは、同僚の @budougumi0617 さんが新規 KMS Key を構築時に指摘され、対応いただいたものです。AWS Key Management Service(以降、AWS KMS と略します)には、 カスタマーマスターキー ローテーション という機能があります。 AWS KMS は自動的に有効にした日から CMK(Customer Master Key) を 365 日後にローテーションし、その後は 365 日ごとに実行します。 docs.aws.amazon.com resource "aws_kms_key" "sample_key" { description = "機密情報の暗号化に利用" enable_key_rotation = true # 自動キーローテーションを有効にする } 作業としては、 enable_key_rotation = true の設定だけです。その設定をしてから 365 日後に自動でローテーションされます。 Amazon Elasticsearch Service のデータ保護 こちらは、同僚の前川さんが新規 Amazon Elasticsearch Service (以降、Amazon ES と略します)構築時に指摘され対応いただいたものです。Amazon ES は、データ保護におけるセキュリティ上の機能として「ノード間通信の暗号化」・「保管時の暗号化」という2つのオプションを備えています。 ノード間通信の暗号化 Amazon ES のドメインは、デフォルトでは VPC 内のノード間トラフィックは暗号化されませんが、ノード間通信の暗号化を有効にすることで VPC 内のすべての通信で TLS 1.2 暗号化が有効になります。 docs.aws.amazon.com この設定をしていないと、 Resource 'aws_elasticsearch_domain.es' defines an Elasticsearch domain with plaintext traffic (missing node_to_node_encryption block). という指摘を受けます。 具体的には次のようなコードによってノード間通信の暗号化の設定が可能です。 resource "aws_elasticsearch_domain" "es" { elasticsearch_version = "6.3" # (省略) node_to_node_encryption { enabled = true # true にすることで有効化する } } しかし、 AWS開発者ガイド: Amazon Elasticsearch Service のノード間の暗号化 にある通り、これは既存ドメインに対しての設定のつけ外しはできないため、既存の Amazon ES ドメインに対しては別のドメインを作成する必要があります。 また、elasticsearch_version も 6.0 以降が求められます。既存リソースに対して作り直しのコストを書けるのはしんどいという判断したら、tfsec:ignore マーキングするとよいでしょう。 保管時の暗号化 Amazon ES ドメインでは、AWS KMS を使用してインデックスやログ・アプリケーションディレクトリのデータなどを保管時に暗号化する機能が提供されています。 docs.aws.amazon.com Terraform では encrypt_at_rest という記述で指定します。 resource "aws_elasticsearch_domain" "my_elasticsearch_domain" { domain_name = "domain-foo" encrypt_at_rest { enabled = true # 保管時のデータ暗号化を有効化 } } しかし、こちらも既存ドメインでは有効にできません。また、特定インスタンスタイプの場合はサポートしていないこともあります。 docs.aws.amazon.com R3 インスタンスタイプは、保管時のデータの暗号化またはきめ細かなアクセスコントロールをサポートしていません。 サポート対象外の場合 tfsec:ignore マーキングすることになりますが、このような暗号化の検討を指摘によって考えられるのは tfsec の良い点ですね。 Redis 用 Amazon ElastiCache のデータ保護 こちらも Amazon ES における「ノード間通信の暗号化」・「保管時の暗号化」と類似したセキュリティプラクティスが存在します。前者を「In-Transit Encryption (TLS)」、後者を「At-Rest Encryption」にて設定します。 In-Transit Encryption (TLS) tfsec からは Resource 'aws_elasticache_replication_group' defines an unencrypted Elasticache Replication Group (missing transit_encryption_enabled attribute というメッセージで指摘されます。 Amazon ElastiCache には、ネットワーク転送時の暗号化オプションが用意されています。 docs.aws.amazon.com 具体的には、 transit_encryption_enabled というオプションを true にしておくことで設定可能です。 resource "aws_elasticache_replication_group" "my-resource" { # (省略) transit_encryption_enabled = true } こちらの設定は、Redis の対応バージョン(3.2.6, 4.0.10 or later)や対応可能なノードタイプが限られるといった 制約条件 を事前に確認する必要があります。 また、暗号化の実装によるバックアップオペレーション・ノード同期オペレーションの実行パフォーマンスが低下する場合があることを 開発者ガイド にて示しています。 なお、既存のレプリケーショングループの場合は新しいレプリケーショングループを作成する必要があります。こちらの方法についても、 開発者ガイド にて具体的な手順が説明されています。実際に読者の中で気がついて検討をしたい方がいらっしゃれば、是非ご覧になってください。 At-Rest Encryption tfsec からは Resource 'aws_elasticache_replication_group.bb-prd-auth' defines an unencrypted Elasticache Replication Group (missing at_rest_encryption_enabled attribute) というメッセージで指摘されます。 Amazon ElastiCache には、データの暗号化オプションが用意されています。 docs.aws.amazon.com 具体的には、 at_rest_encryption_enabled というオプションを true にしておくことで設定可能です。 resource "aws_elasticache_replication_group" "my-resource" { # (省略) at_rest_encryption_enabled = true } こちらも、「In-Transit Encryption (TLS)」と同様の制約事項・考慮事項があります。 セキュリティ観点とパフォーマンス観点のバランシングは自身のアプリケーション要件に沿って検討が必要ですが、tfsec によってセキュリティ観点での指摘を純粋に受けられるのは設計上の利点でしょう。 その他 その他、少し細かいですが管理上大事な内容を紹介します。 aws_security_group_rule' should include a description for auditing purposes Security group の INBOUND / OUTBOUND ルールには、説明(description)を設定できますが、監査上は設定することが推奨されます。 resource "aws_security_group_rule" "huga" { # (省略) description = "BASE Office" } aws_cloudfront_distribution' does not have a WAF in front of it tfsec の v0.30.0 にて追加されたチェック項目です。 docs.aws.amazon.com WARNING レベルの指摘なので、要件上神経質になる必要がないユースケースでの CloudFront の利用であれば tfsec:ignore マーキングで対応可能です。 おわりに tfsec の導入と、その指摘項目から AWS のセキュリティプラクティスを紹介しました。 今回は紙面の都合上紹介しておりませんが、ちょうど 2020 年 10 月に一気に開発が進んでいるのが カスタムチェック です。JSON 形式でルールを指定することで自分たちにとってのルールを tfsec のチェック内に導入できます。こちらもまた別の機会に紹介いたします。 かんたんに使用開始できるので、ちょっとセキュリティに関心・不安があるけど何も出来ていないという方がいらっしゃれば、試してみてはいかがでしょうか。
アバター
BASE の Service Dev にて主に決済周りのバックエンド開発をしている翠川( @midori44 )です。 昨年は PayPal決済の導入 のプロジェクトでメインエンジニアとして携わらせていただきました。 今回は決済周りの開発をしていく中で、社内の開発環境を整えた話をします。 ローカル開発環境での課題 BASEでは現在、 BASEかんたん決済 として6つの決済方法を提供しています。 日々の機能開発をしていく中で、すべての決済方法において各機能が正しく動作するかを確認するために、ステージング環境や社内検証用のQA環境だけでなく開発者のローカル環境でも決済をテストできるようになっています。 新機能のリリース時にはもちろん本番環境で実際の決済を通して動作確認するわけですが、開発中のテストの度に本番相当の決済をするわけにはいかないので、各決済代行会社様のほうで用意していただいている検証用サーバーやsandbox環境に接続してテストすることになります。 基本的にはそれで問題ないのですが、キャリア決済に関してだけはちょっと他の決済とはフローが異なることから、ローカルでのテスト決済ができない状態になっていました。 キャリア決済のフロー キャリア決済では、購入者は一度カートを離れて決済代行サービスのほうで購入手続きを完了します。そのため、決済が確定したかどうかはwebhookで受け取る必要があるのですが、外部にある決済代行サービスは開発者のローカル環境にあるwebhookサーバーへアクセスできません。 もちろん、外部アクセスできるhttpサーバーを立ててリモートフォワードなどをすれば可能ですが、開発用の個人マシンでそこまでするのはリスクが高く現実的ではありません。 ということで、キャリア決済の動作確認をしたいときは共用の検証環境で確認するという方法で長らく運用されていましたが、やはり不便に感じる声が多かったのでDocker上で動く決済代行サービスのモックサーバーを作って解決しました。 外部サービスのモック化 弊社のローカル開発環境はDockerで構築されています。Docker Composeの設定ファイルはGitHubで管理しており、カートが動いているwebサーバー、ショッピングアプリ用のAPIサーバー、データベース用のDBサーバー……等々、各種サーバーをコマンド1つで立ち上げることができます。 今回は、その中の1つのコンテナとしてキャリア決済用のモックサーバーを立ち上げられるように準備します。 適当なwebサーバーを用意して、実際の決済代行サービスと同じレスポンスを返すモックサーバーを作ります。今回は購入中のリダイレクト先としてブラウザからアクセスするため、正常系だけでなく異常系もテストできるように、画面上の操作によって意図的にエラーを起こせるようにしておきました。 モックサーバーのサンプル画面 純粋なAPIサーバーの場合は、異常系のテストをしたいときは『特定のリクエストを渡された場合にはエラーを返す』という実装が一般的かと思います。 たとえば PayPal のsandbox環境では、リクエストヘッダーに PayPal-Mock-Response を渡すことで任意のエラーを受け取ることができるようになっています。 https://developer.paypal.com/docs/api-basics/sandbox/request-headers/ Docker Composeに定義を追加して、用意したモックサーバーをコンテナとして立ち上げられるようにします。今回のモックサーバー構築で注意したいのは、購入者に確認画面を表示させるためのブラウザ経由でアクセスと、裏でwebhookサーバーへ通知を飛ばすためのコンテナ間通信の2つの通信が発生することです。 弊社のDocker環境では本番と同じように任意のホスト名かつSSLでアクセスできるようDNSやリバースプロキシを通しているため、 docker-compose.yml で extra_hosts を設定してモックサーバーからwebhookサーバーへのコンテナ間通信のときにもリバースプロキシを通すようにする必要がありました。 最後に 外部サービスのサーバーを丸ごとモック化することで、一通りの動作確認をDockerネットワーク内で完結させることができました。 今回はローカルでの動作確認が最後までできないことからのモック化でしたが、外部サービスに対する動作確認(こちらからのリクエストに対して想定したレスポンスが返るかどうか)と、自サービス内の動作確認(正常レスポンスもしくはエラーを受け取ったとき正しく処理されるか)を分けて考えるためにも、適切なモック化は役に立つかと思います。 BASEでは開発環境の整備や中長期的な技術基盤の改善を担っていくエンジニア、および120万ショップの決済を支えていくエンジニアを募集しています。 binc.jp
アバター
この記事について コロナウイルスによる社会不安の影響でこの半年でリモートワークの機運が特に都心部を中心に大きく高まってきました。 つい先日ヤフー株式会社が全社テレワークへの移行を正式発表したことは記憶に新しいです。 BASEでもコロナウイルスの感染拡大に伴っていち早くリモートワークの制度を構築し(2020年2月には全社的にリモートワークを開始)、 その制度は現在でもまだ運用されています。 また、世の中の動向やニーズの高まりもあり、リモートワークへの関心の高まりは採用の場面でも感じられるようになってきています。 我々も各種求職者の紹介サービスを利用していますが、そういったサービスではリモートワーク可・不可という選択肢しか選べない場合が多く、 結局面接に来ていただいた方に、BASEでどのようなリモートワーク制度が取られているか、今後どうなっていく予定か、 そういった内容を毎回口頭でお伝えする形になっています。 そこで、毎回のように質問を受けるのであればいっそ記事にして公開し、 現場からみたBASEのリモートワークをお伝えしたい、というのがこの記事の趣旨になります。 あなたは誰 私はBASEのフロントエンドチームでエンジニアリングマネージャーをやっている松原( @simezi9 )です。 マネージャーになったのは2020年7月からです。それまではエンジニアとして働いていました。 なので、BASEがリモートワークを導入する上でどういう議論が行われていたかを直接耳にしたわけではありませんし、 当初は1メンバーとして会社の決定に従って行動する立場でもありました。 そして今ではその制度を運用する立場となってこの記事を書いています。 BASEでのリモートワークの位置付け まず最初にBASEのリモートワークに対して結論だけを述べると、 「BASEではリモートワーク(以下、WFH = Working From Home)は現状可能ですが、フルリモートで働きつづけていく可能性を積極的に模索することは現時点では想定していない。リモートとオフラインのバランスを探りつつ、サービス運営に最も最適な形を模索していく」 ということです。 これについては月並な表現かもしれませんが、対面でもしくは物理的に一緒に働くことで生まれる連帯感やコミュニケーション、文化といったものをBASEでは大事にしたいという思いがあるためです。 大前提としてBASEではよいプロダクトを送り出し、ユーザーの皆様をサポートするという点を第一に考えています。 そしてよりよいプロダクトを作る、品質を高めるという点において、中で働く人間の深いコミュニケーションや文化の醸成といったものが必要不可欠であると考えています。 そしてそういった関係を構築することは、WFHだけでは現時点では難しそうだという判断をしているためです。 コロナ禍におけるBASEの流れ 具体的にBASEにおいて、WFHがどのように運用されてきたか、またその中で自分が具体的にどのように振る舞ってきたかを紹介します。 WFH移行初期(2〜5月ころ) 全社的な動き BASEではコロナウイルスの感染拡大に伴い比較的早い段階でWFH制度が整備され、2月中旬〜下旬にはほぼ全社のメンバーがWFHを開始しました。 いままでは基本的にWFHのための制度はなく出社が前提であったBASEでこれだけ早期に移行できたのは情シスのメンバーの努力の賜物でした。 それらの仕組みの整備が進められていくとともに、社内的にコロナウイルスと働き方のあり方をどのように定義するのかという内部資料が用意され、 都の発表する警戒レベルなどと照らし合わせながら定期的に運用がアップデートされています。 社内で運用されているガイドライン 自分の動き この時期は自分はまだただのエンジニア職であったので基本的に家で作業をしていました。 3月頭と4月頭にチームに新メンバーが入社してきてくれたので、その顔合わせのために最初だけ2〜3日会社に行った以外は出社はありませんでした。 もともと家の作業環境を無意味に整えるのが大好きな性分だったので、WFH開始時には会社の情報共有ツールに謎のエントリを投下してはしゃいだりしていましたし 作業的には特に大きな問題はなく移行できたと思っています。 当時のハイテンションで書いた謎のエントリ群 ただ、フロントエンドエンジニアという仕事柄、作ったものを定期的にデザイナーやディレクターに直接見せて相談しつつ微調整をする、 といったことのコミュニケーションコストが高くなってしまったため仕事の効率という意味ではなんとも言えない部分があったことは否定できません。 WFH移行中期(6~8月頃) 全社的な動き 世の中的には少しずつ以前のような活動を取り戻す取り組みを始めた企業もあらわれはじめた時期だと思いますが、 BASEではWFHで一切出社しないメンバーが大半でした。 一部の、オフィスでないと仕事が難しいメンバーだけは出社していましたが、エンジニアは数名の方を除いて家で仕事をしていました。 オフィスの利用自体を禁止しているわけではなく、「オフィスを使うほうが都合が良ければ使って良い」というような柔軟な運用であったため、 育児などの家庭の都合で家だと作業が難しいようなエンジニアが出社をしていました。 業務都合で出社を要請されたエンジニアは基本的にはいなかった記憶があります。 自分の動き 私自身にも8月に自分が携わった比較的大きい機能のリリースがあったり、 7月にエンジニアからエンジニアリングマネージャーへの配置換えなどのイベントがありましたが、 リリースもマネージャー研修もzoomを利用してリモートで行われたため特に出社することはありませんでした。 現在(2020年9月〜10月) 全社的な動き 都の警戒レベルも少し下がってきたために、オフィスを深いコミュニケーションのできる場所・会社の文化を生む土壌として捉え、 適切なタイミングで活用していこうという機運が高まっています。 例として、一部のチームでは高度な業務知識が必要な場面でドメインモデリングのために週に一度出社して仕様を煮詰めていたり、 マネージャー陣が今後の作業のアサインや組織の構成を考えるために毎週出社してミーティングをする、といった具合です。 個人の事情は考慮されていますので、どうしても出社できないという方に強制したりはしていませんし、全社的に出社日があったりもしません。 ただチーム単位で出社日を決めるといった試みが見られるようになってきました。 また業務外のところでも、最近では新メンバーの歓迎会なども行われていました。 (BASEでは広いフリースペースがあり、そこに感染対策の衝立などが用意してあるためそれを使ってパーソナルスペースを確保した上で開催しています) 歓迎会の様子 家ではなんだか仕事がはかどらないというメンバーの自発的な出社も少しづつ増えてきています。 現在BASEでは社員が約140名ほど在籍していますが、20名前後がオフィスに出社しているようです。 自分の動き 私もマネージャー会議のために毎週金曜日は出社しています。 また、現在メンバーとの1on1を毎週30分行っていますが、BASEでは3ヶ月に一回OKRで個人の目標設定をする機会があるので、そのタイミングでの1on1ではメンバーに出社してもらって対面でちゃんと話すということを行いました。 リモートで満足なコミュニケーションを取り切れていなかったという批判はあるかとは思いますが、自分が実際にオフィスであって会話することによって得られる情報量や充実感というのは、オンラインミーティングだけでは補いきれないものであるなと感じました。 折角金曜に出社するのであればということでMtgなどの用事をなるべく金曜日に固めたりするなどの工夫をしています。 突発的な雑談の時間なども多くとっており、金曜は定常業務よりもコミュニケーションを優先する日、と自分の中ではちょっとした割り切りをしていたりします。 これから コロナウイルスの感染拡大は着地点が見えず、冬が近づきまた感染拡大が発生するのではないかという想定もあります。 そんな中でwithコロナという言葉が表しているように、リスクとメリットの落とし所を探る動きが続いていくものと思われます。 WFHへの考え方もそのうちの1つであると思います。 あくまで現時点での予測ですが、BASEでは急に「来月から全員出社必須」というようなことにはならなさそうではあります。 ただし流れとしては なるべく出社の機会を大切にしていく 、という路線であることだけはまず確実です。 出社することによる不便を極力なくすための整備も進められています。 例えばリモートでのミーティングが増えたことで会議室が枯渇気味になってしまい、急に出社するメンバーが増えると耐えられなくなってしまいそうなので zoomでの会議をするための個室スペースの導入が検討されていたりします。 最後に 長くなりましたが、コロナウイルスの流行は社会構造を良くも悪くも大きく変化させました。 また世の中の働き方の在り方も大きく再考されるきっかけにもなりました。 会社がオフィスを捨てフルリモートに移行する話や、個人的に東京を離れ近郊に移り住みリモートワークをするといったような話も珍しくはなくなってきました。 ワークライフバランスという意味では、自分自身の生活もリモートによってよくなった点がいくつもあります(通勤がなくなった時間で自炊を始めたり、ジムに行ってみたりと精神的な余裕が増えた) そうはいいながらも、BASEでの働き方は社会情勢を見極めつつ柔軟に運用されていくことが当面続きそうになっています。 それはオフィスを完全に離れるということはせず、同じ空間で働くことの価値を大切にしながらリスクとのバランスをとることで実現されていく、ということです ある意味でリモートでのコミュニケーションだけで充分だという人にはBASEという会社は合わないかもしれませんし、 リモート疲れしてきてそろそろ出社してちゃんとコミュニケーションとりながら仕事をしたいという方には合っているかもしれません。 もしBASEでのそんな働き方に興味がありましたらカジュアル面談という形で社内のお話をさせていただく機会を用意しているので、ぜひ応募いただければと思います。 採用情報 | BASE, Inc. 採用情報-フロントエンドエンジニア 採用情報-Webアプリケーションエンジニア
アバター
こんにちはBASE株式会社取締役EVP of Developmentのえふしん( @fshin2000 )です。 今回は、年末の給与改定から運用を開始する評価グレード制導入のお話を書いてみたいと思います。 これまで人材採用時の給与決定や社員の評価時には、マネージャ間で相談し役員承認の上で給与を決めていましたが、その基準や空気感は詳しく社内のメンバーに共有できていませんでした。理由として、中途主体の採用だとどうしても前職給与に影響され、人によって給与にばらつきがでてしまうため、体系だった形に整える機会がなかったのですが、今度、社内に評価グレード制というものを導入することになり、各給与レンジの方に求めるスキルや意識についてまとめたのでこちらで公開いたします。 評価グレード制というのは、一般的に等級と呼ばれるもので、一定サイズ以上の会社のご経験がある方なら、類する制度はどこでもあると思いますので詳細説明は割愛いたします。 (採用面接等で詳しくご説明差し上げられれば幸いです ^^ ) 以前からある当社の報酬体系で特徴的なのは、年収7〜800万円以上とそれ以下で給与の上がり方が変わるということが挙げられます。社員の評価は半期毎に行っているのですが、700万円以下の給与の人はスキル向上がそのまま役割向上につながるレンジと捉えていて、仕事を頑張る努力が昇給に反映されやすいのに対して、700万円以上の人はそのような概念をなくして、その代わり、 役割や期待値の変更 に対して大幅な給与アップを目指す。エンジニアリングマネージャもそれを前提として期待を設定していくという考え方があります。 700万円以上の人は、国内の転職市場においても経験豊富なハイスキル層とみなされると思います。マネジメント側の責務として、評価面談やOKRの設定を通じて、大きな期待をかけ、大きな成果をあげてもらい、大きな昇給を実現することが仕事です。エンジニアリングマネージャは、チームメンバーをプロデュースするという仕事の結果、自分自身の評価と昇給にもつながります。 この以前から取っていた給与の構造を踏襲し、グレードを定めました。これまでやっていた給与決定プロセスを言語化したというのが、作業にあたってやったことです。 評価グレード制に至るまでの過程 歴史ある大企業や老舗企業で働いている人たちは、等級制度、評価制度が当たり前に存在していると思いますので、あまり意識したことはないかもしれませんが、スタートアップ企業が評価グレード制を導入する動機には組織の拡大におけるマネージャへの権限移譲の文脈が存在します。 数年前まで昇給額を役員で決めてた。役員もマネージャから報告されるメンバーの活躍を感じられる組織サイズでもあったし、役員がマネージャも兼ねるケースが多かったので、一定の納得感は得られていたと思われる。 役割や期待に対する給与レンジや昇給額のような基準は、役員とマネージャ間には存在していて、採用活動や半年ごとに行われる評価会議を重ねるたびにブラッシュアップされていった 組織が大きくなりマネージャも増えるなか、その基準を言語化することで、どうしたら給与が上がるのかをフレームワーク化し、目標設定と評価をスケールするようにしたもの 一般にマネージャに給与の提案権限を移譲するにあたって、評価クオリティを維持し、評価への納得感と事業成長を実現するための取り組みとして導入されるものが評価制度や等級制度ということになります。 評価グレードの分類 評価グレードは、 A1・A2・Ex1・Ex2・Ex3・Ex4の6段階 に分類されます。 ※A(エー)=Associate、Ex(イーエックス)=Expert 新卒やキャリアが浅い方はA1から始まって、グレードが上がっていけば行くほど、サービスや会社に対する影響範囲は広く、未来を見据えた課題解決や課題設定力に対する期待が設定されます。 グレードに対しては報酬の下限額が設定されています。上記例に挙げた700万円のラインはEx2の下限値に設定されています。 また、皆さんに親しみやすい役割名としてはリード職がありますが、現在、当社ではテックリードと呼ばれる役割の人がいます。彼らはEx3の枠に該当します。当社ではさらにテックリード of テックリードとしてのプリンシパルテックリードが設定されており、それが最上位のEx4に位置づけられます。完全にBASEの未来を作る期待に対して設定した役割です。 各グレードに対する報酬の上限額は設定されていますが、グレードをまたいだ金額の被りは存在しています。 これは中途採用において、実績のある方はもちろん、期待値込みで若くても高い給与で採用することを想定し、入社後に当社のサービスの経験を積んでいただく中で、適切なグレードに合うようにマネジメントしていくための高速道路の合流車線的なバッファとして存在しています。この辺をあまり厳密にしすぎると採用活動に影響が出てしまうので柔軟性をもたせています。 といろいろ前置きを書きましたが、抽象的には以下のようにグレードに対する期待が設定されています。 A1 (新入社員や経験の浅い社員) 指示/指導を受けながら業務を遂行する 自らの業務領域について、業務改善や課題解決の提案をする A2 特定領域において自立した業務遂行をする 所属チームの業務について、業務改善や課題解決の提案および実行する Ex1 特定領域において、 所属チームをリード する水準の事業成果を生む Ex2 特定領域において、所属チームをリード する水準の事業成果を生む 所属Division全体の課題解決 に影響力を持つ 同職種における社内比較で 高い専門性や課題解決能力 を有する Ex3 (テックリードなどのリード職相当) 全社をリード する水準の事業成果を生む 全社の将来的な課題 を解決する事業成果を生む 同業種における 全社トップレベルの高い専門性や課題解決能力 を有する Ex4(プリンシパルテックリードなど) 全社をリードする水準の事業成果を生む 全社の将来的な課題を解決する事業成果を生む 全社の課題解決に影響力 を持つ 同業種における 業界トップレベルの高い専門性や課題解決能力 を有する Web系エンジニアの具体例 以下は、各グレードをWeb系エンジニアに比較的具体例として割り当てたものになります。すべてを満たさなくてはグレードが上がらないというわけではありません。その人の個性と期待する役割にあわせて評価しますが、グレードが上がれば上がるほど、 未来をつくることへのビジョン、プロダクトクオリティの実現、不確実性へのチャレンジを求める ことになるという部分は、共通して期待することになります。 A1 (新入社員や経験の浅い社員) 通常プロジェクトの開発メンバーとして参加し、開発、テストを任せることができる 比較的平易な難易度のお客様のお問い合わせや不具合対応の解決を任せることができる 周囲の助言を受けながら、所々の問題を解決できる A2 通常プロジェクトの開発者として参加し、一人でも開発、テスト、リリースを任せることができる お客様問い合わせや不具合について、解決を任せることができる 本番運用の安定性実現のための障害対応、高負荷対応のオペレーションに参加できる Ex1 通常プロジェクトの技術責任者としてメンバーをリードすることができる (技術責任者とは主にプロジェクトの設計レビューを主導する人) 当社にて求められているソースコード、プロダクト等の品質向上を自ら実現し、メンバーに広げることができる 本番運用の安定性実現のため、障害対応、高負荷問題等を率先して発見、対応し解決に導ける 経験の少ないメンバーに対する技術指導を日常的に任せられる Webサービスを運営するエンジニアとしてPJと日常的な改善を並行して行うために、適切な段取り、スピード、クオリティを実現できる Ex2 難易度の高い技術課題やプロジェクト(決済の追加や商品オプション機能、抽選販売など)の開発の設計、改善について、見積もり、スケジュール通りの実現を任せることができる 技術的負債の返済を自ら先導し、解決することができる 現状のソースコード、プロダクトについて、チームでの品質向上を率先してリードできる 常に稼働しているサービスに目を向けて、技術的な問題を解決し続ける 自動テストやデプロイ改善等を通じて、デプロイ後の問題発見や運用中のサービス改善をリードできる 一つの技術に依存せず、自分が関わる事業領域で求められる技術への適応力がある。開発言語、フロントエンドやサーバサイド、インフラなどの技術概念に囚われず、近接領域の技術を学び続けて問題解決に貢献できる システム改善が必要になる難易度の高い障害対応を率先してリードしクローズに導ける Ex3 (テックリードなどのリード職相当) 難易度の高いPJを実現するためにアーキテクチャ設計、作業段取り、見積もりを行い、メンバーを引っ張ることでスケジュールと品質を実現する 計画の有無に関わらず、短期的、中期的な事業成長を見据えながら、現状のサービス、技術の問題に目を向けて、改善提案および改善実施ができる 技術提案、サービスの問題提起、研究等を常に行っている 自分にとって未知の技術を現状にフィットするように取り入れ、サービスの成長を実現することができる 品質、機能、スケーラビリティ、セキュリティ等を改善する技術的課題を提起し、メンバーをアトラクトしながら改善をリードできる Ex4 (プリンシパルテックリードなど) 会社の中期目標を技術で実現するための技術方針や組織方針の立案および遂行ができる 開発組織に起きる問題点を発見し、技術力の底上げに対する抜本的な施策、生産性の改善を任せることができる セキュリティ、ビジネス、スケーラビリティ等のリスクに目を向け続け、改善提案を行い、組織計画、開発計画に結びつける 今後も役割に対する肩書や、金額構成、トップラインの給与の上限額などは変えていきたいと思っています。Webサービスを支えるエンジニアは一人一人の主体的な活躍がすべてです。非・労働集約的な役割の働き方に対する制度として、ここで決めた制度が今後も固定的であるわけではなく、流動的に見直していけるように業績を上げていきたいと思っています。 それこそEx3やEx4のグレードの人たちが活躍し、サービスクオリティ、業績への寄与を行い給与のトップラインを向上させることが、全エンジニアのスキル、社会的評価、給与水準の底上げにもつながると考えていますし、それがやりやすい組織構造を常に模索していきたいと思っています。 エンジニアリングマネージャのグレード設定について エンジニアリングマネージャにも評価グレードの基準が設定されていますが、こだわりポイントだけ抜粋しますと、以下の特徴があります。 ・採用候補者の適切な給与提案ができる ・新規ビジネス等の際に送り出せるマネージャ、リード候補の育成ができる ・ 組織拡大、分割を前提としたマネージャの育成ができる つまり、人を育てていき、新規事業や組織変更に伴って、一番できる部下を送り出すことを前提としたマネジメントをしてくださいということです。安定した自分の城を作るのではなく、常に不確実である前提で会社全体が成長するように採用とチームを作ることに専念してください。 マネージャは新たに人を採用し、成長をプロデュースして、新たに送り出すことでBASEというサービスのドメイン知識や哲学を兼ね備えた人材が、新規事業や新しい組織のマネージャとして新しい活躍をするというループを描いていきたいと思っています。特に新人マネージャは、既存メンバーのいい兄貴、チームリーダーとしては積み上げた経験が使えるのですが、採用となると経験のない新しい取り組みになるので、時間の使い方として主体的に動けるようになるには時間がかかります。採用に対して、自分事かつ主体的に動けるようになって初めてマネージャとして一人前とみなされます。それはまた全体視点で事業を捉えられるようになり、未来を見据えるようになったという意味でもあります。 そして、よく大企業の新規事業で言われることとして、エースは温存され、そうでない人材が新規事業にあてがわれるという話があります。これはこれで大企業において若手がチャンスを得るという構図になっていると思いますが、当社のように年功序列ではなく若手をガンガン重用していきたい組織においては、なんなら一番できる人が抜けてしまうことを前提としたマネジメントを求めています。 例えば子会社のBASE BANK社を支えるプロダクトマネージャとテックリードは、元々、BASEの決済チームのど真ん中を支えていた若手メンバーでした。新規事業に手を上げてくれて、彼らは今、BASE BANKチームでいきいきと仕事をしていますが、そこでのチャレンジをまたBASEのチームにフィードバックしてもらいたいと思っています。そういったことの再現を今後もやっていきたいと思っています。 現状のエンジニア組織の課題 現状、Web技術に関しては、CTOとプリンシパルテックリードの2トップがサービスの最先端の進化をリードしています。しかし、増えゆくGMVを支えながら、機能性向上、プロダクト品質の向上等を行っていくにはとてもとても手が足りません。 こちらの記事 にある通り、来年以降、CakePHP2.xをなくしていくことをサービスを維持、成長させながら行うという難しいプロジェクトにも挑みたいです。おそらくDDD等のノウハウを活用して再設計をすることになると思いますが、それらを支えていく各分野のエキスパートであるテックリード候補やハイスキルエンジニア層、組織の作り手であるエンジニアリングマネージャ候補を募集していくことになります。 上のグレード表現で言うと、エンジニア組織をリードできるEx2以上の人たちを増やしながら、若手の有望株であるEx1候補を採用して、組織としてスケールさせていきたいです。そのためにもありとあらゆる方策を取って、社内のエンジニアのスキル向上はもちろんのこと、採用を推進していきたいと考えています。 もし、BASEの未来を作ることに興味を持っていただける方がいらっしゃったら是非お話しましょう。エンジニアとしての成長をチャレンジできる環境を用意したいと思っていますし、人材投資をできる環境は整っていますので、一緒に仕事をしましょう! binc.jp
アバター
ServiceDev所属、サーバサイドエンジニアの栗田です。 現在私は、ServiceDevのチームに所属し、ネットショップ作成サービス「BASE」及びショッピングアプリ「BASE」の機能開発を担当しています。 BASEでは主にQ毎にプロジェクトチームを編成し、チームで主体となって機能開発を行っていきます。 チームメンバー構成はプロジェクトの特性や規模により様々ですが、多くの場合、同じグループから1〜2名がプロジェクトにアサインされます。 今回はとある長期間プロジェクトで感じた事や・実際にこのように進めたよというところをサーバサイドエンジニアの視点から紹介したいと思います。 どんなプロジェクトだったか 今回紹介するプロジェクトは「BASE」の拡張機能である「BASE Apps」の1つである「商品オプション App」を開発するプロジェクトでした。 「商品オプション App」は商品にギフトラッピングやオーダーメイドで使えるオプションを設定できる拡張機能です。詳細は下記からご覧いただければと思います。 baseu.jp プロジェクトメンバー構成 プロジェクトマネージャ(PM) & ディレクター 1人 フロントエンドエンジニア 1人 サーバサイドエンジニア 3人 (開始 1人, 最終的に3人) ネイティブアプリエンジニア 2人 デザイナー 1人 キックオフからリリースまでの期間 10ヶ月ぐらいほど 私の立ち位置 サーバサイドエンジニアのまとめ役的な立ち位置でした。 初めはサーバサイドエンジニアは1人でしたが、最終的に3人に増員し 私は開発ディレクションをやりつつ、他のメンバーと共に開発をしていきました。 エンジニアリングマネージャ(EM)から私へ期待されていた事は 「とにかく不確実性を下げて、プロジェクトをコントロールできる状態にして欲しい」でした。 具体的には 日々の開発の進捗を把握して、プロジェクトメンバーやEMに共有すること 不確実な領域をどんどん触っていき想定外なことを少なくしていくこと 不安要素を取り除くため、部分的な本番化をしていき、また各フェーズのリリースの計画を建てること リリースに向けた現実的な進捗を把握して、EMと人員計画の相談をすること などを行いながらプロジェクトを進めていきました。 どんな特色のあるプロジェクトだったか 商品オプションは、無料・有料の商品オプションを商品に複数個紐付けられるという仕様で、今まで「BASE」では注文の価格の演算の概念が増えるという事例はほぼなく、注文の価格はあらゆる箇所に影響するので、サーバサイドエンジニアの私からすると「システムの全体に関わりかなりの工数がかかりそうだな?どうしようかな?」というのがプロジェクトが始まった当初に思っていた事でした。 実際に影響があったアプリケーションは社内用の管理画面やバッチ処理なども含めて9つほどありました。 リリースについては複数のフェーズがある事が当初よりわかっており デザインテーマ向け(ネットショップ作成サービス「BASE」のデザインテーマを作成していただいているディベロッパー向け) ショッピングアプリ「BASE」向け ネットショップ作成サービス「BASE」向け の3つのフェーズがある事が見えておりました。 それぞれにクリティカルパスが存在したので、タスクのボトルネックが発生しないようにプロジェクトを進める必要がありました。3つのフェーズの進行については プロジェクト内で週次で定例MTGを行い、PMが中心になって進捗確認をこまめに行い進めていきました。 見積もり・設計フェーズについて 以前からこの機能をリリースしたいという想いは社内にあり、要件はある程度固まっていました。 ので要件定義フェーズはあまりなく、見積もり・設計が始まりました。 今思えば、私はプロジェクト開始当初、入社して1年も経っていなかったので サーバーサイドのシステムの全体像は正直わからない所もありましたが、とりあえずやってみる事にしました。 見積もりの叩きを作成したのですが、規模も大きく、怪しい箇所も多いので自分1人で進めるのは危険だと判断して、EMに相談して1人のシニアエンジニアに見積もり・設計のサポート役としてアサインしてもらうことになりました。 そして、見積もり・設計がある程度進んだ所でチームレビュー・CTOレビューへの流れになりました。 設計レビューについては、同じチームの宮村が記載した記事がありますので、そちらをご覧いただければと想います。 devblog.thebase.in 余談ですがシニアエンジニアは予定していた育休のタイミングと被ってしまい、実装には一切関わらないこととなりました。ピンチ!w 開発初期フェーズについて 初期は1人で進めていましたが、少なく見積もっても6ヶ月以上かかりそうと見えており、1人でやるのは難しいとEMと共有認識があったので、EMに2人目のエンジニアをアサインしてもらうように動いてもらいました。 (BASEでは、以前はプロジェクトにサーバサイドエンジニアが1人でアサインされる事もあったのですが、最近はサービスの成長もあり、システムが複雑化していってるので、2人以上のアサインになることが多いです) 2人目のエンジニアのアサインを確保できた時は、ある程度プロジェクトも進んできたので、リリースターゲット月を決める必要がありました。特にどのQに収めるのかどうかは、BASEの開発組織として大きな関心事でした。 見積もりは終わっていたので、タスク一覧表はできていましたが正直、精度や抜け漏れがあるかどうか不安だったので、新しくアサインされたエンジニアと2人で時間をかけて全量確認する事にしました。 長期なプロジェクトだったので、ここで2人の目線を合わせる事が後々に響いていってとても大切だったと感じました。 開発フェーズに感じた事や・実際にこのように進めたこと 他のチームが動きやすいように、データの生成の流れに沿って開発を進める事が効率的だった。 一連の流れで開発できた方が開発の進捗も実感できるしフロントエンド・ネイティブアプリの開発がしやすくなります。 全部リファクタする訳にはいかないので、部分的に改修して先に改修とテストだけデプロイした。 嬉しい悲鳴で他のプロジェクトがリファクタとConflictする事もあった。逆に更に発生しないように積極的に本番化した。 サーバサイドエンジニアチームで日時で相談会、MTGをして相談しやすい環境を作った。 プロジェクト途中でコロナ期間へ入ったのでより頻度の高いコミュニケーションを取るようにした。 必要に応じてリモートではなく会社に集まった。 リリースターゲットに開発を収める事が難しいとわかったので、ラスト3ヶ月はEMと連携して3人目のサーバサイドエンジニアをアサインしてもらった。 ※ 前提:BASEでは並行して色々なプロジェクトが走っていていたり、負債が残っているコードも存在します。 QAテスト・リリースフェーズに感じた事や・実際にこのように進めたこと 品質が保てたものから、既存の不具合がでないようにガンガンデプロイしていった 全部で9つアプリケーションあったがコアとなるカートや決済の処理を除いて、ほぼ先出しでリリース前に本番化する事に成功した リリースによってデータが破壊的にならないようにして、いつでもリバートできるような計画をした QAテスト3つのフェーズがあったので、それぞれで必要な最小限を作成してQAテストを進めていった QAテストで必要なデータ一式をPMが用意してくれた為、開発でバタバタしていたが、効率に進める事ができた 3つのフェーズに分かれていたので、段階的に品質が上がっていき、不具合があった場合でも早期鎮火をする事ができた CTOとの本番化についての交渉をした BASEでは様々な事情があり、ステージング環境で複数日検証する事を許可しておりません 今回はリリースの規模と天秤にかけて特例をいただき、複数日検証をする事によって安全なリリースを可能にした リスクを分散させる為に各フェーズの機能の本番デプロイの日とユーザー公開日を分けた まとめ 今回10ヶ月と長いスパンでしたが、上記でお伝えしたような事を考えながらチーム開発をしていきました。 BASEにおける長期プロジェクトのチーム開発の雰囲気は伝わりましたでしょうか? 最後に長期間プロジェクトとなり辛い時もありましたが この機能をリリースして、多くのショップオーナーさんから声をいただき、無事に機能を届ける事ができて本当に良かったと感じました。
アバター
はじめまして。 BASE株式会社 SRE Groupに所属している富塚( @tomy103rider )です。 先日、弊社CTOが 「もうさばき切れない」アクセスが激増したECプラットフォームにおける負荷対策 https://devblog.thebase.in/entry/bsucon という記事を公開しました。 社内ではこのアクセス激増をきっかけに「サービスの監視をどうしていくか」「サービス/システムのアラートに対してのアクションはどうあるべきか」といったような監視に関する話題も改めて盛り上がっています。 そんな中でふと1年くらい前にBASE BANK 株式会社の東口 ( @hgsgtk )が社内で主催した「入門 監視」輪読会に参加したことを思い出し、その輪読会がどういう会だったかなど、改めて輪読会を振り返ってみようと思います。 「入門 監視」輪読会の目的は何だった? この輪読会を開催するにあたって主催者がしっかりと残していた社内ドキュメントがあり、そこには以下の3つの目的が書かれていました。 - オーナーズに安定的なサービスを提供したい - インフラ監視を理解する素地を形成して関心を強める - アプリケーション監視の素地を形成して関心を強める 「オーナーズのみなさんに安定的なサービスを提供したい」という強い思いを持ち、SRE Groupが主に対応しがちな監視という分野に、いわゆるバックエンドエンジニアも関心を持つ機会にしたかったということが挙げられていました。 どういう形式の輪読会だった? 週に1回、1時間、参加は自由(業務優先)、参加者は開発メンバーやSREメンバーやマネージャーもふらっと参加したりと、わいわいとした雰囲気の会でした。 日時 毎週木曜日 15:00〜16:00 進め方 全12章(付録含む)を各回で2章くらいをもくもく読む タイムスケジュール 30分:当日の対象章を読み、気づき・疑問を共有ドキュメントに書き留める 25分: 書き留めた気づき・疑問を共有・議論 5分:まとめ・次回対象の章を決めて解散 書籍 『O'Reilly 入門 監視 ――モダンなモニタリングのためのデザインパターン』( https://www.oreilly.co.jp/books/9784873118642/ ) www.oreilly.co.jp ※当時の実際の様子(こんなこともあろうかと撮っておいた写真) もくもく読んでいる時間は主催者が心地よい音楽を流してくれていました。こういう雰囲気や環境作りも充実感に繋がる大事なポイントなのだなと感じたのを覚えています。 輪読会から1年以上が経過して・・・ さて、ここまでは輪読会の様子をお伝えしましたが、「輪読会に参加していた人たちがその後何か監視に対する気持ちや行動に変化があったか」という点が一番知りたくなり、 Q. 1年前に実施した入門監視本の輪読会に参加して、その後、監視に対して意識や行動に変化はありましたか? という質問を参加していた人たちに投げかけ、それぞれの回答をまとめると以下のようになりました。 行動に変化があった 関係ありそうなアラートに対して何かしらアクションをするように心掛けるようになりました。 アプリケーション監視のパターンとして Health エンドポイントパターンを知り活用しました。 https://devblog.thebase.in/entry/2019/03/06/110000 Mackerelでのビジネス監視(とある数値の変化をチェック)を実際に入れてみました。 意識に変化があった Zabbixで監視システムを構築してきた経験があるので、すぐ同様のものを構築してしまいそうになるが、果たしてそれが企業にとって本当にいいことなのかどうかより強く考えるようになりました。 構造化ログにする重要性は、Elasticsearch/Kibanaで可視化する、とか、そこまでいわゆるバックエンドのエンジニアも自分でやると必要性が身にしみてくるなと最近思います。 それほど変わってないですが、アプリケーションエラー見直しを考えている中でエラー追跡も監視に入るかなと思っているので参考になったところはあるかもしれないです。 変化はなかった その後も監視に対して特に強いアクションをとれていないと思います。 実務でこの本を意識することは今までなかったと思います。 輪読会を途中離脱してしまい今も積ん読状態です! その後、何かしらアクションまで起こしている人が3割、意識に変化があった人が4割、特に変化はなかったと思っている人が3割くらいという結果でした。 まとめ この結果を見ると、輪読会の当初の目的である「関心を強める」という点では半数以上の参加者が何かしらの変化があったと(前向きに)捉えると有意義な会だったのではないでしょうか。 また、私個人としては、過去に他社で運用していた日々のインフラ運用経験から監視について色々知ったつもりになっていましたが、改めて初心にかえって書籍や他のメンバーから学んだことが多かったり、特に「ユーザ視点での監視」の大切さの意識が強くなったのは収穫でした。 とはいえ、今後より良いサービスを提供していくためには、監視や運用についてもまだまだ改善する必要があるというのが現実の組織課題としてあります。BASEのエンジニア組織では監視や運用の面についても更に改善/強化していく計画があり、このあたりの話題は今後計画が進んでいった際に記事が上がるかと思いますのでお楽しみに!
アバター
こんにちは。BASE BANK株式会社 Dev Division にて、 Software Developer をしている永野( @glassmonekey )です。 弊社ではAWS Lambdaを活用する機会が増えまして、 最近メジャーアップデートのあった「AWS SAM CLI」を使ってリリースフローの改善にチャレンジしてみました。 そこで、samコマンドで作成したサンプルプロジェクトをローカルで実行しデプロイする方法を紹介します。それに加えて、現状BASE BANKチームで行っている代表的な運用設定をご紹介します。 今回記事作成に際して、 サンプルプログラム を用意しているのでもしよければ手元でご確認ください。 なお、今回LambdaにはGoを採用しました。検証に使用した環境は以下の通りです。 macOS: 10.15.x (Catalina) SAM CLI: version 1.2.0 AWS CLI: aws-cli/2.0.50 Python/3.7.4 Darwin/19.6.0 exe/x86_64 Go: go1.15.2 darwin/amd64 SAM CLIとは SAMとは Serverless Application Model の略称で、 AWS Lambda のデプロイ管理に使われるツールで、 ローカル上のデバッグ 、 ビルド 、 デプロイ をサポートします。 公式のアナウンス によると7月に バージョン1 がリリースされました。 画像は AWS SAM Local(ベータ版) – サーバーレスアプリケーションをローカルに構築してテストする より引用しました。 またこの愛らしいリスのキャラクターは 公式の説明 によると "SAM the Squirrel (リスの SAM)" について SAM the Squirrel は、サーバーレスアプリケーションで使用されるリソースを定義するモデルで、AWS Serverless Application Model (AWS SAM) から名付けられました。SAM は、AWS ユーザーがサーバーレスアプリケーションを効果的かつ簡単に構築できるよう、ツリー (木) の中で居心地のよい生活を後にしました。 とのことで、なんだか愛着が湧いてきますね。 環境構築 samコマンドを実行するために必要な設定をしておきます。 1. AWS SAM CLIのインストール 最初に AWS SAM を動かすためのCLIをインストールします。 macOSの場合は 公式 に紹介されている通り、 Homebrew を使って簡単にインストールすることができます。 $ brew tap aws/tap $ brew install aws-sam-cli または、 公式で提供されているpipパッケージ を利用する方法もあります。 CI環境などでbrewを入れたくない場合はこちらがおすすめです。 $ pip install aws-sam-cli sam --version でバージョンが返ってきたらインストール成功です。 $ sam --version SAM CLI, version 1.2.0 2. AWS CLIのインストール 直接SAMの実行には関係ありませんが、動作確認などで必要になってくるのでこちらも 公式 の方法に従って入れておきます。 $ curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg" $ sudo installer -pkg AWSCLIV2.pkg -target / こちらも aws --version で結果が返ってきたらインストール完了です。 $ aws --version aws-cli/2.0.50 Python/3.7.4 Darwin/19.6.0 exe/x86_64 3. sam実行ユーザーの作成 AWSコンソールなどからsamコマンドを実行するユーザーを追加しておきます。 必要なポリシーなどは後ほど解説するので、この段階では AdministratorAccess を設定しておきます。 また、cliからも触れるように設定しておきます。 aws configure 4. dockerをインストールしておく ローカル実行を行う場合のみ、docker daemonが動いてないといけないのでインストールをしておいてください。 docker for mac は こちら からインストールできます。 SAMを使った開発入門 まず最初に公式が提供してくれるHello, Worldなapiをsamを使ってデプロイすることをためしてみます。 1. プロジェクトを作成する sam init を行うとsam用のプロジェクトの初期化を行うことができます。今回はサンプルプログラムを流用するので対話型の設定を行いますが、既にディレクトリ構成などが完成している場合は不要なフローです。 sam init 今回は入門用のAPIをぱぱっと作るので1を選びます。 Which template source would you like to use? 1 - AWS Quick Start Templates 2 - Custom Template Location Goを使うので4を選びます。 PHPも選択肢に来ないかなー Which runtime would you like to use? 1 - nodejs12.x 2 - python3.8 3 - ruby2.7 4 - go1.x 5 - java11 6 - dotnetcore3.1 7 - nodejs10.x 8 - python3.7 9 - python3.6 10 - python2.7 11 - ruby2.5 12 - java8.al2 13 - java8 プロジェクト名はデフォルトの sam-app のままで行います。 Project name [sam-app]: 今回は単純なapiを作成するので1を選びます。 AWS quick start application templates: 1 - Hello World Example 2 - Step Functions Sample App (Stock Trader) すると プロジェクトテンプレートもデフォルトの https://github.com/aws/aws-sam-cli-app-templates を使用した形でプロジェクトが初期化されています。 ├── Makefile <-- Make to automate build ├── README.md <-- This instructions file ├── hello-world <-- Source code for a lambda function │ ├── main.go <-- Lambda function code │ └── main_test.go <-- Unit tests └── template.yaml AWS SAM としてLambdaの構成管理しているのは template.yml になります。 これはCloud Formationのラッパーとなっており、基本的な文法は Cloud Formation に準拠します。 また、内容に関しての詳細は SAMのドキュメント や Cloud Formationのテンプレートリファレンス を一読することをおすすめします。 2. ローカルで実行してみる。 2.1 ビルド実行 実行の前にビルドをしてみます。 $ sam build 実行後に下記のようにbundle含めてデプロイ用のファイル群が作成される。 ├── .aws-sam │   └── build │   ├── HelloWorldFunction │   │   └── hello-world(バイナリ) │   └── template.yaml 生成内容の設定に関してはtemplate.ymlに記述されている CodeUri: hello-world/ Handler: hello-world と対応しているので、生成したいhandler名やソースディレクトリを変更したい場合はここを変更します。 2.2 ローカル実行 次にローカルで実行を行う sam local invoke を実行します。 するとAWSリソースを介さずにローカルで完結して結果が返ってくるはずです。 $ sam local invoke {"statusCode":200,"headers":null,"multiValueHeaders":null,"body":"Hello, $実行環境IPアドレス\n"} また -eオプションをつけることでイベント情報のjsonを渡すことも可能です。 特にAPIコール以外(SQS経由など)で呼び出されるLambdaの場合だとデバッグ用のイベントを都度作成するのも手間だったりするので、イベント情報も極力Git管理しておくことをおすすめします。 副次的効果として、チーム内にどのようなイベントのやりとりが発生するかの共有にもつながります。 $ sam local invoke -e path/to/event.json` なお、雛形は sam local generate-event $リソース名 $API名 で作成することができます。 例えば、 SQSのreceiveMessage のイベントのJsonは下記のような形式となります。 $ sam local generate-event sqs receive-message { "Records": [ { "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78", "receiptHandle": "MessageReceiptHandle", "body": "Hello from SQS!", "attributes": { "ApproximateReceiveCount": "1", "SentTimestamp": "1523232000000", "SenderId": "123456789012", "ApproximateFirstReceiveTimestamp": "1523232000001" }, "messageAttributes": {}, "md5OfBody": "7b270e59b47ff90a553787216d55d91d", "eventSource": "aws:sqs", "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:MyQueue", "awsRegion": "us-east-1" } ] } 3. デプロイしてみる。 3.1 デプロイファイルの作成 初回デプロイの場合は --guided オプションを使用して対話形式でデプロイ設定ファイル samconfig.toml を作ります。 $ sam deploy --guided Stack Name [sam-app]: AWS Region [us-east-1]: ap-northeast-1 #Shows you resources changes to be deployed and require a 'Y' to initiate deploy Confirm changes before deploy [y/N]: n #SAM needs permission to be able to create roles to connect to the resources in your template Allow SAM CLI IAM role creation [Y/n]: Y HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y Save arguments to samconfig.toml [Y/n]: y 各項目に関してですが Stack Name … 内部的に使用するcloud formationのstack名。samを実行するユーザーはこのstack名に関しての権限が不足しているとデプロイに失敗するので、必要なので注意が必要です。最低限以下の権限が必要なことは確認しています。 "cloudformation:DescribeStackEvents", "cloudformation:CreateChangeSet", "cloudformation:DescribeChangeSet", "cloudformation:ExecuteChangeSet", "cloudformation:GetTemplateSummary", "cloudformation:DescribeStacks", Confirm changes before deploy: deploy前に確認を必要とするか。CI上で動かす想定なら n にしておくと良いです。 Allow SAM CLI IAM role creation: デプロイするlambdaに付与するロールを自動作成するか。自動作成する場合は余計な権限もつくことがあるので要注意です。 Save arguments to samconfig.toml: y ならこの対話型操作の設定内容を保存します。 すると以下のようなファイルが生成されます。次回以降の sam deploy 実行時はこの設定ファイルを見てくれるので、必要に応じて設定を書き換えると良いでしょう。 version = 0.1 [default] [default.deploy] [default.deploy.parameters] stack_name = "sam-app" s3_bucket = "aws-sam-cli-managed-default-samclisourcebucket-6qa86tkchc0g" s3_prefix = "sam-app" region = "ap-northeast-1" capabilities = "CAPABILITY_IAM" なお、 capabilities で指定できる値は以下のいずれかを設定することができます。 CAPABILITY_IAM CAPABILITY_NAMED_IAM CAPABILITY_RESOURCE_POLICY CAPABILITY_AUTO_EXPAND 詳細は AWS Serverless Application Repository開発者ガイド に記載されていますが、 template.yml 管理下のリソースの複雑さで設定の切り替えが必要なようです。Iamロールをtemplate.ymlで管理する場合などのケースでは要注意です。 今回はシンプルにlambdaのみのリソース管理化なのでデフォルトの CAPABILITY_IAM で良いようです。 3.2デプロイの実行 samconfig.toml が存在していれば sam deploy でデプロイされるはずです。 デプロイ結果のスクショ 無事にデプロイされました  👍 lambdaのコンソールを見に行くとAPI Gatewayの項目からエンドポイントが確認できるはずです。 コンソール画面を確認 確認できたエンドポイントを仮に https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/ とすると結果が返ってくれば成功です。 $ curl https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/ {"statusCode":200,"headers":null,"multiValueHeaders":null,"body":"Hello, $実行環境IPアドレス\n"} エンドポイントにcurlを投げてみるとローカル実行と同じようなレスポンスが返ってくるはずです。 しかし、ローカル実行時とは内部的にはネットワーク構成が異なっているので返ってくるIPは違います。 その他設定項目について 次に現状BASE BANKチームで行っている運用設定の一部をご紹介します。 環境ごとの設定の切り替え リソースのarnの切り替えなどを環境ごとに切り替えたい設定を外部から注入できるようにします。 今回はシンプルに APP_NAME と ENV を環境変数経由で受け取り表示するように書き換えてみます。 name := os.Getenv("APP_NAME") env := os.Getenv("ENV") return events.APIGatewayProxyResponse{ Body: fmt.Sprintf("%s@%s", name, env), StatusCode: 200, }, nil この環境変数を受け取るために、以下のようにトップレベルに Parameters を追加し、Functionリソース配下に Environment.Variables を追加します。 ここの意味としてはtemplateがパラメータとして受け取った値は環境変数としてFunctionリソースに受け渡すという形になります。 !Ref は Cloud Forrmationの関数 で、セクション間の参照を表します。 Parameters: ENV: Type: String AppName: Type: String Resources: HelloWorldFunction: ~~~ 省略 ~~~~ Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object Variables: ENV: !Ref ENV APP_NAME: !Ref AppName このようにしておくと実行時に Key=Value の形式でパラメータを渡すことができます。 $ sam local invoke \ --parameter-overrides 'ENV=local AppName=Hello' $ sam deploy \ --parameter-overrides 'ENV=dev AppName=Hello' 値を渡す方法は他にもあったりはしますが、ローカル実行とdeploy時の対照が取れているのところがおすすめなポイントです。 若干ハマったポイントとしてパラメータキーには _ などの記号を含むパラメータは読み込んでくれないようで APP_NAME ではなく AppName としています。 環境ごとのデプロイ設定 特にCI上ので実行を加味した場合、環境変数の注入用の parameter-overrides の他にも設定しておきたいパラメータがあります。 $ sam deploy \ --s3-bucket dev-sam-stacks \ --parameter-overrides 'ENV=dev AppName=Hello' \ --force-upload \ --no-fail-on-empty-changeset s3-bucket : cloud formationのアップロード先のバケット。基本的に環境ごとで、切り替えることになる思うので samconfig.toml から記述を消し、パラメータで受け取れるようにしておきます。 force-upload: ソースコードの変更を伴わないLambdaの設定値のみの変更の際に、正しくデプロイされないケースがあったのでフラグを追加しておきます。 no-fail-on-empty-changeset: 内容の変更がなくてもエラーにならないフラグです。CIで動かす場合を考えると、変更がないときも正常系と考えて良いはずなのでこのフラグを立てておきます。 Makefileの用意 以上を踏まえて環境ごとの設定に切り替えはあらかじめMakefile内に記述しておきます。 Makefileの用意を用意しておくと、チーム内にコマンドの共有ができるのと、CI 上で何かあったときに手元での検証がしやすくなるので便利です。 .PHONY: build test local-run local-up dev-deploy build: sam build # テストの実行 test: @cd ./hello-world/ && \ go test -v ./... # ローカルでlambdaを実行する local-run: build sam local invoke \ --parameter-overrides 'ENV=local AppName=Hello' # apiとして起動する場合 local-up: build sam local start-api \ --parameter-overrides 'ENV=local AppName=Hello' # デプロイする dev-deploy: build sam deploy \ --s3-bucket sam-app-stack \ --parameter-overrides 'ENV=dev AppName=Hello' \ --force-upload \ --no-fail-on-empty-changeset まとめ AWS SAM を使った入門と運用設定の一部を紹介しました。 もしこれから AWS SAM を使用される方々の助けになれれば幸いです。 従来の都度deployして確かめる方法だとどうしても1回の検証に時間がかかってしまい、Lambdaに手を出しづらい状況下だったので、今後の量産体制に無事に繋げられそうです。 バージョン1 になったことにより大分扱いやすくなった印象をうけました。 また、今回は時間の関係で導入まではできてないのですが local stack を使えばAWSリソース依存のテストもできそうなので、機会があればこちらも挑戦してみたいと思います。
アバター
こんにちは。BASE BANK 株式会社 Dev Division にて、 Software Developer をしている東口( @hgsgtk )です。 XP祭り2020 XP祭り2020 は、XPJUG(日本 XP ユーザーグループ)主催のベントです。2002 年から毎年行われていて、今年 2020 年は、9 月 19 日にオンライン開催されました。 xpjug.com 今回自分は初参加でした。LT 参加で申し込んでいたので、聴講者 && LT 発表の両方の視点で、参加レポートを書き連ねていきます。 TL;DR XP 祭り 2020 に参加しました 聴講レポート:『近代史とアジャイル』が面白かった 「時を超えたプログラミングの道 "XP はソーシャルチェンジである"」という発表をしました 聴講レポート:『近代史とアジャイル』が面白かった 聴講させていただいた内容、全て面白かったのですが、個人的に好きだった私の個人的ベストセッションが、Fumihiko Kinoshita さんの『近代史とアジャイル』というトークでした。 終盤のまとめスライドを引用しておりますが、2001 年のアジャイルマニュフェスト以前、にこれだけ大きな年表が出てくる話を聞いたことありませんでした。 『近代史とアジャイル』のスライドから引用 個人的な興味で、最近ソフトウェア自体ではなくて、哲学・社会心理学系っておもしろいな〜と浅く見回っていて、見回ってみると、ソフトウェア設計などの影にカント哲学やデカルト哲学が出てきます。 それらが歴史年表となって、アジャイルマニュフェストにいたる一枚のスライドに収められた光景に感動しました(そしてこの感動の末、もともと用意していた LT スライドをゴミ箱に入れて作り直し始めました)。 LT発表: 時を超えたプログラミングの道 "XPはソーシャルチェンジである" 当日の Lightning Talk 発表者は、 10 人で 9 番目でした。 実は「当日このテーマにしよう」と決め、事前に作っていた資料は捨て作り直しました。きっかけは、聴講レポートにあげたセッションを聞いて、昼休みに、改めて『Kent Beck, Cynthia Andres. エクストリームプログラミング』(以降、XP 白本と呼称します)を読み返したことでした。 www.amazon.co.jp XP まつりの熱を帯びた状態で読み返すと、「あぁ『XP はソーシャルチェンジである』というフレーズはこんな想いが込められていたのか...!!!」と、勝手に感動し、誰かに話したい気持ちが溢れた結果がこちらの資料でした。 "XP はソーシャルチェンジである" という言葉は 2 つの意思が凝縮されていると捉えました(それだけではないとも思っています)。 何からチェンジするのか = テイラー主義的組織構造からの脱却 どこを目指すのか = 生き生きと働くこと ひとつは、ソフトウェア開発におけるテイラー主義的社会構造からの脱却という意思。XP 白本には、「第 18 章テイラー主義とソフトウェア」という章があります。そこでは、テイラー主義がもたらす社会構造について次のように断言しています。 ソフトウェア開発で問題となるのは、テイラー主義に伴う仕事の社会構造だ。テイラーが提唱した「時間・動作研究」の儀式や道具こそなくなっているが、我々は無意識にこの社会構造を継承しているのである。 その社会構造は、ソフトウェア開発にまったく適していない。 そして、どこを目指したのか、という点についてですが、XP 白本には、「第 23 章時を超えたプログラミングの道」と言う章があります。「時を超えた」とくると、ソフトウェアエンジニアの頭によぎるのは、クリストファー・アレグザンダー氏の著書『時を超えた建設の道』です(主語が大きい自覚はちゃんとあります)。 www.amazon.co.jp これを読んだ Kent Beck 氏や Ward Cunniungham 氏たちは、クリストファー・アレグザンダー氏のパタン・ランゲージなどの考え方をソフトウェアを適用することを試み始め、現在のソフトウェア開発のコミュニティに馴染み深いデザイン・パターンや XP などにつながっていきます。 その過程で何を目指したのかは、この「第 23 章 時を超えたプログラミングの道」と「第 25 章結論」を読んでいくと、XP の目的・目指したいものが克明に書かれています。 XP白本 第25章 結論 から抜粋 "XP はソーシャルチェンジである" という何やらとらえどころの難しいフレーズも、XP をソフトウェア史上の「点」ではなく「線」で捉えると、その輪郭が浮き彫りになってくるような気がします。 ちなみに、すでに発表しようとして捨てた内容は、以前の開発チームブログに公開しているものでした。「抽象的な話より具体的な『今どうしたらいいのよが知りたいのよ』」派のあなたにおすすめです。 devblog.thebase.in また、最初は XP 白本の書評ブログとして書いていました。発表資料の内容 + 脱線(多め)になっています。 note.com 発表内容が、「だれかに話したい!」と言う衝動に駆られたものだったので、どれだけこの個人的な感動が伝わるだろうか、と思ったのですが、コミュニティが暖かく感謝感謝...。 【メモ】XP祭り2020 @hgsgtk さんのLT「時を超えたプログラミングの道 ”XPはソーシャルチェンジである” を読み直す」 とっても良かったです! https://t.co/vjyuaVFPRs #xpjug #ChistopherAlexander #Taylor pic.twitter.com/uND3V0A4Ti — Akapon (@Akapon2010) September 19, 2020 ペアデザインを始める過程で「XPはソーシャルチェンジである」という言葉を知って、ぼやっとしか分かっていなかったけれど、急にくっきりした感じある https://t.co/XBHjZHegf6 — Takahiro Hayashi / ぴーや (@taka_piya) September 19, 2020 この発表エモい。広島の河川景観は(原爆スラムを除けば)、まさに市民との対話によって形作られていったことを思い出す。計画プロセスの民主化によって生み出された芝生が広がる護岸は散歩しててとても気持ち良い場所になった。ソフトウェア開発もかくありたいよなと思う。 https://t.co/5IUwmvpNFq pic.twitter.com/P5XmzRdjap — hodagi (@hodahgi) September 19, 2020 建築の仕事からのアナロジーがソフトウェア開発に活きてくるの面白い🤔 https://t.co/5oTZP3oRz4 — Morito Ikeda (@_moricho_) September 19, 2020 XPまつり 2020 スタッフの方々へ謝辞 XP まつり 2020 開催ありがとうございました。昨今のコロナ禍で、Discord と Zoom を駆使したオンラインとなりましたが、Discord の盛り上がりもあって、XP 祭りの熱にしっかり 1 日巻き込まれて楽しい時間になりました。 この場を借りて御礼申し上げます。 おわりに そういえば、最近 @seike460 さんが配信されている podcast #46fm にて、関連話(デザイン・パターン起点ですが)をしていました。 #46fm Podcastを公開しました! 3回目は @hgsgtk さんをお招きしました! 46fm ep.3 「ISUCON10予選、パタン・ランゲージ、Go コーディングリーディング会、社内の小さなカイゼン @hgsgtk 」 https://t.co/XOUNKssLfq — せいけしろー (@seike460) September 19, 2020 そちらももし興味がありましたら(初めてラジオ形式で発音しているので日本語がカタコトですがご了承を...)。
アバター
前書き こんにちは、BASEのフロントエンドチームでエンジニアリングマネージャーをやっている松原( @simezi9 )です BASEではフロントエンドエンジニアの積極採用を行っています。 その過程で、面接を受けに来られた方によく「BASEはVueとTSを採用しているとのことですが、相性がいまいちじゃないですか?なんでVue+TSにしてるんですか?」 という感じの質問をいただくことがあります。 この記事は、そんなBASEのフロントエンドにおける、技術・・・というよりもVue.jsに対するスタンスについて嘘偽りなく答えてみよう、という記事になります なぜVueを採用したのか、その後 過去にも「 次世代の管理画面を作るフロントエンドの取り組み 」というエントリでVueを採用した経緯には軽くご紹介させていただきました。 それは端的に言えば「HTML/CSSを書いてきたデザイナー陣にも見た目がとっつきやすい」SFC、ひいてはVueの優れたインターフェイスによるものでした。 同時に静的検査による安定した開発体験を得られるTypeScriptを導入し、その力をフルに活かすためにVuexの採用はせず、 自前でstoreを用意する、という選択肢を取りました。 この路線は今に至るまで有効に機能していて、上記の記事で取り上げた「次世代管理画面」へのリプレースは主要な機能ではおおよそ完了し、 普段の開発業務でもVueもしくはTSのコードを書く時間が大半を占めるようになっています。 選択の評価 当時Vue.jsを採用したことは今でも正解であったと思っています。BASEでは二年前(2018年)時点では明確なフロントエンドを専任とする組織はなく、 フロントエンドエンジニアという肩書きを持ってその領域を専門的に担当するメンバーもほぼ不在でした。 技術をリプレースする際の心理的な抵抗感を最小限にして開発を前に進める、という目的は満たせましたし、機能的な不満点も現時点では特にありません。 そして、リプレースが進む中で独立したフロントエンドチームができ、 2018年度末には3名しかいなかったメンバーも今や9人、入社予定者を含めれば10人というチームに育ってきました。 実は、今のメンバーでもう一度リプレースを立ち上げるとしたらもしかしたらVue.jsは選ばないかもしれません。 それは何故かといいますと、「Vueを積極的に選ぶ理由が現時点ではなくなってしまったから」という一点によるものです。 最初にVue.jsを選定した際にはデザイナーが取り組みやすいことが大きな選定理由になりましたが、 BASEのフロントエンドチームが大きく成長し、きっちりとした設計(DDDやクリーンアーキテクチャのエッセンス・概念の導入・・・etc)、コードベースが積み上がっていくにつれて、フロントエンドのソースコードはデザイナーの手を離れフロントエンドエンジニアの持ち物に移行していきました。 それ自体はいいことでも悪いことでもなく、あくまでBASEの分業体制がそうなったという話です(デザイナーもコード書いていける方がいい、という話も勿論あります)。 ただ、そうなった場合に先述のSFCの親しみやすさはもはやそれほどメリットではありません。 むしろエンジニアがフロントエンドのコードを書くときによりベターな選択肢は何か?という点がポイントになります。 そうなったときにTSが半公式的にサポートしているReactは非常に有力な選択肢となります。 VueのTS及び型へのサポートは徐々に進化しており、来たるVue3へのメジャーバージョンアップで、最大の課題であったコンポーネントのpropsに型による制約を付けづらい問題もかなり解消されそうではありますが、 それでもReactとTSの親和性を10とすればVueとTSの現状の親和性は7か8(あくまで個人的な印象値)程度ではないかなと思います。 VueでJSXを採用することでより親和性を高めることも可能ですが、JSXでバリバリVueのコードを描くなら最初からReactを選んでおけばいい、という話でもあります。 またVue3へのアップデートの過程で、メジャーアップデートのアナウンスが成されてからもなかなかリリースが進展することなく、 結果的に多くの破壊的変更を伴うことが予測されるビッグバンリリースになりそうである、という点で今後のVueの開発サイクルに対して若干の不安がある面もあります。 ただし、Vueのよく練られたAPIや開発体験、NuxtやVue CLIなどのエコシステムは現時点でも十分に魅力的ですし、現時点ですでに本格的な採用に十分耐えられるものであるとも思っています。 少なくともあと数年は、Vueで書かれている部分をわざわざ別のフレームワークで書き直そうというような話にはならないと思っていますし、 アップデートに対する追従は積極的に行っていく予定です。 最後に 技術選定において判断軸はいろいろあるかと思いますが、BASEのフロントエンドにおいては特にVueに強くベットしていこうというわけではなく、 その時その時で社内環境、トレンドなどを総合的に判断して選定を行うことになると思います。 実際に社内ではコア機能のリプレースにWeb Componentsを活用したりNext.js(React)を利用した領域が生まれようとしています。 今後も新規プロダクトに関してはReact採用の機運はどんどんと高まっていくかと思います。 個人的な印象ではVueもReactも目に見えるUIを分解して、コンポーネントとして構築し直す感覚そのものは非常に近いものがあると思っていて、 両者のエッセンスの違いを理解してどちらも書けるということが、今後のフロントエンドエンジニアには当たり前のように求められていくのではないかと思っていますし、 BASEのフロントエンドエンジニアにおいても、どっちも書けて当たり前、という状態をちゃんと作っていきたいと思っています。 過去のBASEのフロントエンドの記事や登壇では基本的にVueの話をしてきましたので、 採用の場でも、React/Angularしか経験ないので〜というようなお話をいただく事も多々ありますが、 実のところ採用の場ではどのフレームワークが得意か、経験豊富かというのはそれほど重視していない点でもあります。 面接でもどちらかというとWebアプリケーション全体に対する知識(セキュリティ、ネットワークなども含む)や、プログラミングの基礎力・設計力などをお聞きしていることが多いです。 カジュアル面接という形で気軽に会社や現場の紹介をさせていただく場もご用意していますので、興味のある方はぜひ声をかけていただければと思います 採用情報はこちらから
アバター
基盤チーム所属の沖中( @okinaka )です。 「リファクタリング」という言葉、エンジニアのみなさんならご存知でしょう。 システムの振る舞いを変えずに内部を改善することを指す言葉です。 一般的に、コードの修正を指すことがほとんどですが、今回はデータベース設計のリファクタリングについてお話ししたいと思います。 絶版になってしまいましたが、 データベース・リファクタリング という書籍に様々な手法が紹介されていて参考になります。英語で良ければ 原書 はまだ入手可能ですね。 データベース・リファクタリング 作者: スコット W アンブラー , ピラモド・サダラージ 発売日: 2008/03/26 メディア: 単行本 Refactoring Databases: Evolutionary Database Design (Addison-Wesley Signature Series (Fowler)) (English Edition) 作者: Ambler, Scott W. , Sadalage, Pramod J. 発売日: 2006/03/03 メディア: Kindle版 商品並び替えの問題 弊社が運営する「 BASE 」というサービスは、無料でかんたんにネットショップを開設できるサービスです。Webサービスの他に、購入者向けショッピングアプリ「 BASE 」、ショップオーナー向けのショップ運営管理アプリ「BASE Creator」、外部システムとの連携のための BASE API なども提供しています。 ショップに商品を陳列する際の並び順は重要な情報です。並び順の情報は、 システムのいたるところで参照されています。 商品の並び順を管理している商品テーブルは、在庫管理などにも用いられるため頻繁に更新がかかるテーブルでした。 商品の並び順は、ショップごとに先頭を「1」として降順に番号がふられています。例えば、新規登録した商品を一番先頭に持っていきたい場合、他の商品の並び順も全て更新してあげる必要があります。 多くの商品をかかえるショップが管理画面などで商品を追加すると、大量の商品の並び順を更新する操作が行われるため、1つのテーブルに書き込みが集中してしまい、負荷が問題になっていました。 リファクタリングの方針 データベース・リファクタリング本で言うところの「テーブルの分割」を行いました。 並び順のデータを別テーブルで管理することで、書き込みを分散することができます。代わりにテーブルを JOIN するようにソースコードの修正が必要になります。 改善前 改善後 商品テーブル 商品id 商品名 : 並び順 商品テーブル 商品id 商品名 : 商品並び順テーブル 商品id 並び順 リファクタリングの流れ 以下の順で実施しました。 前準備 商品の並び順を管理する新しいテーブルを追加 商品テーブルと並び順のテーブルの値を同期 既存の並び順を新しいテーブルにインポート 修正したソースコードのリリース(複数回) 並び順を参照するコードを修正 並び順を更新するコードを修正 後処理 商品テーブルと並び順テーブルの同期を停止 移行期間中はトリガーで同期 ソースコードの修正は一人で作業するには広範囲で、全てを把握できているわけではありませんでした。 このまま一気に差し替えはリスクがあると判断し、サービス稼働中に無停止で段階的にリリースするために移行期間を設けました。 移行期間中は、商品テーブルと並び順のテーブルの値を同期する必要があり、 今回は、データベースのトリガーを利用することにしました。 トリガーとは、テーブルに対して INSERT / UPDATE / DELETE などを契機に処理を実行できるデータベースの機能です。環境や設定によっては利用できないこともあるのですが、幸い BASE のデータベースである Aurora は MySQL 互換ですので、利用することが可能でした。 以下にトリガーの例を示します。 商品テーブル (items) が INSERT された際に、並び順テーブル (item_list_orders) に内容を反映させています。 DELIMITER | CREATE TRIGGER item_list_orders_insert AFTER INSERT ON items FOR EACH ROW BEGIN IF NEW.list_order IS NOT NULL THEN INSERT INTO item_list_orders (item_id, list_order) VALUES (NEW.id, NEW.list_order); END IF ; END ; | DELIMITER ; 実際にやってみて データベースのエキスパートの方にも協力いただく必要はあったのですが、コードの修正作業は一人でできました。 移行期間を設けたことで、1つのリリースを影響範囲を極限まで絞り込んだことで動作確認しやすくなりました。 修正を一気にリリースしたい誘惑に抗いつつ、地道に作業をすすめていきました。 また、リファクタリングに欠かせない自動テストで動作に問題がないことを常に確認できました。 おかげさまで、大きなトラブルもなくリリースできました 🎉 (懸案だった更新のスループットも大幅に改善されたとのこと) おわりに データベースのリファクタリングは、正しい手順に沿って行えば、時間はかかりますが技術的には難しいことはありませんでした。 ミスした際の影響範囲の大きさから敬遠されがちですが、積極的にやる価値はあるのではないかと思いました。
アバター
BASE株式会社取締役 EVP of Development / PAY株式会社取締役 / BASE BANK株式会社マネージャのえふしんです。 新型コロナによる非常事態宣言下で起きたことについては、大変勉強をさせていただきました。 非常事態宣言下の巣ごもり消費、リアル店舗等でご活躍されている事業者様のEC利用が急増する中で、BASEにおいても例外ではなく、サービスに訪れるトラフィック急増、4月から5月の頭にかけては、サービスの安定性にも影響が出てしまうという状況が発生していました。 その中で起きていた一つの事象を解決した件が以下のCTOによる記事です。 devblog.thebase.in この記事はCTOやSREチームやWebアプリケーションのエンジニアの活躍で、負荷急増の問題解決をした一つの事例となります。 この対策を通じて、改めて認識したことがあります。 それは、 BASEに訪れるすべてのトラフィックを適切に受け止めて、適切に決済を完了させるという使命がこのサービスにはある ということでした。これがすべてのショップ、すべての購入者からの期待であり、その期待に応え続けることがサービスが成長するという意味です。 そんなのWebサービスなんだから当たり前だろ?と思うかもしれません。しかし、非常事態宣言下でのアクセス急増は、もし新型コロナがなかった場合に順調に成長した数年後の未来が突如やってきたというものでした。それだけ急速なDXが進んだという理解をしています。 その中で、ここから数年後の未来を迎えるために、今後、将来に渡ってサービスを改善し続けることの重要性を改めて感じることとなりました。そこには単純にAWSのインスタンス数をスケールアウトし、Auroraのインスタンススペックを上げるだけでは済まない未来が見えてしまったわけです。 PHPを使い続けていく意味 我々が提供しているBASEは、2012年に開発されたPHP + CakePHP2.x系で動いているコードを下地に機能追加と機能改善をし続けています。 僕がCTOだった時、そして、現CTOの川口に技術選定を委ねている現状においても、サービスの改善においてPHPから開発言語を変えるという選択は今のところ考えていません。 PHPを使う理由としては、 BASEの現状よりも高い成長を実現している他社実績を勘案するに、まだまだ成長に対するポテンシャルを兼ね備えている言語処理系である。 PHPという開発言語自身が、他の言語の良いところも取り入れて進化し続けている 少なくとも現状のWebのサーバサイド言語において、開発生産性も含めて、他の言語にまるっと切り替えてでも実現したい代替技術が見つかっていないというのが最大の理由です。 もちろん今後のスケーラビリティの実現に際してPHPでは無理ということになったら全然違う言語に変えるかもしれません。また、マイクロサービスとして新機能や新規事業において適材適所で様々な技術を追加採用していき、相対的にPHPの活用度が下がることはありえます。 BASE BANKチームはGo言語でサービスを開発していますし、データサイエンティストで構成されるData Strategyチームは機械学習で問題解決するために、Python、AWSやGCPを生かしています。 BASEの決済トランザクションを処理し、マイクロサービスの関係性になっているPAY.JPを運営するチームは、Pythonで書かれたWebサービスです。また、BASEチームにおいてもリリースはしていないもののGo言語で書かれたプロダクトも存在しています。BASEのフロントエンドは、Vue.jsやReactを活用したアーキテクチャへの変化も進んでいます。 会社全体としては特定の開発言語にこだわらない一方で、適材適所の技術選択にはこだわっていると表現するのが正しいでしょうか。 また、AWSやGCP、外部クラウドサービスとのAPI連携によって、相対的に、これまでPHPが担っていた機能や役割は少しずつ切り取られています。 これは時代の変化にあわせた適材適所化であり、サーバサイド言語はグルー(糊)としての役割が強くなります。フロントエンド、バックエンドに携わるエンジニアは、自動的に幅広い技術領域でのシステム構築力が求められていくと考えています。 最近はバックエンドのエンジニアにも、当然のようにフロントエンドのコードを書いてもらうための時間投資をしています。フロントエンドチームがきれいに整備したフロントエンド基盤をアプリケーションとして活かすコードを書くところは、当社のバックエンドエンジニアの当たり前のスキルだと考えています。逆にフロントエンドエンジニアにとってのサーバサイドもしかり、できる限り個々のエンジニアの心のなかにある聖域は無くしていきたいです。 雑に言えば、Webエンジニアであればフロントエンドからインフラまで全部見えるようになってという想いがあります。これは個人でWebサービスを作るなら当たり前のことですが、大規模開発においてWeb環境を取り巻く専門性と多様性の中で、何が良いかは議論は分かれるポイントです。ただ、CTOやプリンシパルテックリードは得意分野の振り幅はあれど、その辺、気にせず必要に応じて見てくれているので、僕はWebのエンジニアの理想像なのだと思います。ここから先は専門家に委ねるという引き際もまた技術の判断力ということでしょう。チームで解決すればいいので。いずれにせよ、その中の当社の技術スタックの一つにPHPが存在しているということです。 PHPからPHPへの移植 我々の差し迫った問題として、CakePHP2.xのライフサイクル終了を目前に、次のアーキテクチャへの移行が必要です。言うなれば、高齢化が進んでいるアーキテクチャを若返らせる必要があるということです。 既に、今年のプロジェクトとして重要機能のアーキテクチャ変更に手を付けていますが、更に、来年からは本格的に全体のアーキテクチャ変更をスケジューリングし、数年をかけてソフトウエア構造の若返りを実現して行こうと考えています。 また、もっと日常の問題として、PHP自身を使い続ける魅力として、PHP自身が開発言語のトレンドを取り入れて進化していると書きましたが、この考え方についていく難しさも存在しています。 BASEというプロダクトもリリースからなんだかんだと7年以上も経っています。新興のスタートアップ企業だと思っていましたが、当社のソースコードもそれなりに長い期間、競争力を維持している状態です。 まだまだ昔のソースコードが活躍している中、意識的に新しいPHPに対応したソースコードに書き換えて行かない限りは、PHPの進化というメリットを享受できません。 以前は、Ruby on Railsのように最先端への適応圧の高いフレームワークはまあまあリスクだと考えていましたが、当社のように成長、継続に時間とコストをかけられるWebサービスであれば、いずれにせよ最新の状態に変化をしていかないといけないので、必要経費として外的環境から変化を強制される方がまだ気持ちは楽なのかもしれません。 システム開発には「枯れたコードはいじるな」という定説が昔から存在しています。 既にリリースされているコードは十分安定しているから余計な修正はしてはいけないという考え方です。この考え方の元で教育されてきたエンジニアの人も多々いることでしょう。 後方互換性が維持されているPHPにおいては、この考え方に乗りやすいというメリットがあります。進化の歩みを止めたWebサービスを長く運営していくことについては、PHPの利用は向いていると考えることも可能です。 しかし、これは「Webシステムのコードはリリースした瞬間から劣化していく運命にあり、何もしないのは退化をもたらす」という概念と矛盾します。 誤解を恐れずに言うと、現状維持を許容する考え方が許されるのもPHPの強みであり、陥りやすい罠とも言えます。 自己責任でPHPの最新バージョンへの対応を意識してマネジメントしていかないと、どんどん最新環境への適応力が下がってしまい、働く人のスキルも含めて変化のコストが高くなっていってしまいます。 これを打破していく必要が我々にはあります。 エンジニアリングマネジメントの責務 このようなエンジニアリングプロセスの実現、意識の向上は、マネジメントサイドの仕事として意識的に行う必要があります。 どこかこれまで既存技術を良くしていくのは、意識も瞬発力も高いテックリード級の人がやる仕事だと考えていたところがありました。そのために日常のサービス開発をしているチームとは別に、基盤チームというビジネス主体の開発プロジェクトから切り離した技術チームを作り、その人達を中心に広げていければと思っていましたが、それだけでは望む状況にはなりませんでした。 そうではなく、当社で働くすべてのエンジニアが、毎日の仕事の中で、ソースコードをよくしていく、リファクタリングし変化をもたらせていく意識を持つ必要があるということにたどり着きました。 言い換えると、ビジネスの成長を実現する新機能のプロジェクト開発を進めるだけでは、事業継続性、サービス発展性の実現には足りないということです。 それ故に、エンジニアリングマネジメントとしては、チーム全員が変化を受け入れることを当たり前とするために、以下のことを意識的にやっていきます。 プロジェクトをこなす事以外に、日常的に既存コードをよくしていくための作業時間を確保する サービスを伸ばすことと、ソースコードやシステム全体をよくしていくことの両立ができ、高い生産性を実現するエンジニアを評価し、報酬に反映する。 そこでリーダーシップを発揮できる人をリード職やエンジニアリングマネージャに抜擢し、全体のレベルアップを図る このことに一緒に取り組んでくれるエンジニアの採用を強化する など。 このことをチーム全員で取り組んでいく必要があります。枯れたコードの安定性にサービス継続性を依存し、いつまで経ってもPHPの後方互換性に頼っているという状態では未来がありません。 事業継続性を実現するシステムズエンジニアリング このような事業継続を実現するための変化は、この業界においては式年遷宮と呼ばれる事が多いです。しかし、Webの場合は、サービス要件、セキュリティ要件、スケーラビリティ要件が刻一刻変わっていくため、個人的なイメージとしては、技術の伝承、現状維持を目的とした式年遷宮を例えにあげるのは、サービスの成長を前提とした概念としては少し物足りないという感覚を持っています。 個人的な好みとしてはこちらの例えで、2013年3月15日に行われた東急東横線の渋谷駅ホームの地下化に伴って行われた代官山駅の入れ替え工事がそれです。 渋谷という継続的に発展する都市を下支えするエンジニアリングの底力をこの映像から伺うことができます。 この工事は、一晩のうちに行われました。綿密な計画を練って、総勢、1200人で作業したというものです。チーム全体がこの役割を担うことで、サービス利用者になんら不便をかけずにサービスのアップデートを実現したこの映像をとてもリスペクトしています。 今後の発展のために このような前提の中で、BASEというサービスの現状は発展途上に他なりません。ここまで長々と書いてしまいましたが、直近における大きなtodoをまとめると、通常の事業成長のためのプロジェクト開発以外に、 来年からCakePHP2.x をやめるための計画を立てて開発計画を実行していく PHPの最新バージョンに適応するためのコードを書くことへの時間投資は常に意識する その他、思いきった新規アーキテクチャの導入など(実はもうやってるよ!) これらを実現し、技術力を底上げしていくための採用と組織構造を改良していくのがマネジメントの仕事になります。 BASE社で働くエンジニアは、今後、サービスを成長させ続けて流通総額を伸ばし続け、多くのショップオーナー様の成長を支えていくためにも、現状のままで良いという考え方は1mmも持たないで欲しいです。特に新型コロナの非常事態宣言下では、変化しないと先がないと言う未来が明らかに見えました。 当社のエンジニアは特定の言語を書きたい!みたいな価値観だけではなく(それはそれで否定しない)、成長するWebサービスを根幹から支えられる技術者として成長してほしいですし、そういう機会が目の前に転がっているのでCTOやテックリードだけにやらせずに、自らチャンスを拾っていって欲しいです。 そして、このサービスに携わる人達の技術力を下地に、勇気ある変革を実現していきたいと思っています。 そして一緒に手伝っていただけるエンジニアの方々を募集しております。やることは山程あるので助けてください! A-1.BASE_Webアプリケーションエンジニア/ショップオーナー向け開発 / BASE株式会社 また採用を一緒に手伝っていただけるVPoE候補も募集しております!とりあえずで結構ですので、皆さんのキャリアの選択肢の一つとしてZoomでお話しませんか? open.talentio.com
アバター
こんにちは。BASEでコミュニケーションデザイナーをしているszです。 この度リファインしたBASEのサービスロゴの制作過程を少しお話しできればと思い筆をとらせて頂きました。 目指したのはリブランディングではなくリファイン 創業から丸7年ほど、ネットショップ作成サービス「BASE」とショッピングアプリ「BASE」の顔となっていたサービスロゴ。今までのロゴはカラフルで愛らしくユーザーはもちろん社内のメンバーからもとても愛されていました。ロゴマークの部分である三角はアメリカ先住民族のテント「ティピ」で表していて、“インターネット上に小さな経済拠点をつくる”というプロダクトの想いを込めています。 そんなロゴですが、全く課題がないわけではありませんでした。 ティピの天辺部分の枝の処理が甘く少し視認性が弱かったり、ティピ全体の余白や角度が揃っていなかったりと細かく見ていくと、少し荒があったり、ロゴタイプの部分もカラフルな故に視認性が弱く、写真の上に載せたりすると文字の一部が溶けたり、白縁で処理して使う必要があったりとクリアしなければならない課題がありました。 しかしBASEというサービスのコアバリューは創業時から今も変わらず、アイデンティティの部分を大きく変えるのはまだ相応しいタイミングではないということもありました。 そこで目指したのが、サービスイメージを一新するのではなくリファイン(洗練)させることでした。 ロゴ制作者へのリスペクトを込めたインタビュー 今まで、BASEというサービスを支えてくれていたサービスロゴ。 創業当時にロゴを担当したデザイナーがどんな思いのもと作ったのか、ヒアリングするところからプロジェクトはスタートしました。改めてロゴのコンセプトや当時の制作の温度感やロゴタイプの選定プロセスをヒアリングしました。ロゴに紐づくグラフィック展開をみて、改めていいロゴだなぁと感じました。 デザインは必ず人が作っているものです。作った人を知るということで、次のデザインに向かうことができます。 ロゴ作成時のラフとグラフィック展開などです。 ティピの印象をどうするか リファインと言えど、どう作り上げるか様々な選択肢があります。 ティピもやり方次第では今と全く違った印象を持たせることができます。 そこで実施したのが、現BASEメンバーの非デザイナーの方々に、何も見ず記憶をたよりに5秒でロゴのティピを描いてもらい、そこからコアアイデンティティーが何かを探るとことをしました。 どのようにティピが印象付いているのかを確認することができ、リファインの判断基準が出来上がってきました。 ロゴタイプの選定 セリフ体やサンセリフ体さまざまなfontやキャップアンドローでBASEという文字を検証し、fontのバックグラウンドとBASEとの相性を確認していきました。 ある程度絞れてきたら、tipと組み合わせて検証もおこないました。 ここでポイントとなってきたのが、昔のロゴタイプ部分に使われていたfontがコンデンスドなfontであったということです。調べてみると20世紀初頭から中期の新聞の見出しから着想を得たものということがわかりました。コンデンスドはその特性上、情報量の多い文章との相性がよく文字数の少ないロゴとは少し相性が悪いところがあります。しかし全く印象を変えたfontも少し不安というメンバーの声もあり、コンデンスドのfontも候補に残しつつ、ティピとマージして調整に進みました。 カラー、ティピのディテールとロゴタイプのバランスを考える まずはティピを大雑把に絞り、ロゴのバランスを調整してバランスをみていきます。 ある程度ティピとロゴタイプが決まってきたらモックにはめて完成した時のインパクトを見極めていきました。 2案に絞り鶴岡さん(弊社代表取締役CEO)と話し、方向性の確認をして最終調整へ。 細かい修正を繰り返し完成したのがこちら! カラフルな印象だったロゴが少し大人びた印象に生まれ変わりました。 見慣れたロゴを変えるための準備 ロゴのリファインと言えども変えることにネガティブな意見がなかった訳ではありません。今までのロゴに対する愛着が、ユーザーにもメンバーにもありました。制作を開始した頃に「なぜ変えるの?」質問を受けることもありました。 その際には、視認性を含める課題を伝え、様々な意見を聞きつつプロジェクトを進めました。 プロジェクトメンバー間でも、プロダクトやサービスと同じようにサービスの顔であるロゴも進化し続けるべきだと認識を合わせて、リファインを進めました。 リモートと物理出社をうまく使い社内メンバーにチラ見せしながら進める 新型コロナウイルス感染症の影響で現在リモート推奨中のBASE社。しかし必要に応じてメンバーがオフィスに来ていたりもします。 ロゴリファイン担当のメンバーも必要に応じてオフィスに出社し、カラーの確認などをしていました。 そこで、制作途中のロゴを会議室の壁に張り出したままにして、あえてメンバーにも見えるようにしていました。これによりプロジェクトメンバー以外の人に「ロゴを変えるのかな」とあらかじめ変えることへの免疫をつけてもらっていました。また仮にネガティブな意見があるようであれば、はじめのうちに聞いておきたいという狙いもありました。制作途中もプロジェクトメンバー以外の意見も聞き、どう感じているかも常に意識して進めました。 これからもBASEのデザインは進化し続ける BASEのデザインはまだ進化の途中です。それはロゴも例外ではありません。ユーザーのためにMove Fastに新しい様々な機能提供してきたように時代に合わせてビジュアルアイデンティティーも変えて行く必要もあります。これからのBASEのデザインに是非ご期待ください!
アバター
こんにちは。BASE BANK 株式会社 Dev Division にて、 Software Developer をしている東口( @hgsgtk )です。 TL;DR GitHub Project でカンバン運用する際に、Issue 作成が少し面倒で、対応する GitHub Project を手動で指定しないといけない GitHub Actions の alex-page/github-project-automation-plus を用いて、Issue作成時に自動で GitHub Project に登録されるようにする ISSUE_TEMPLATE の作成・更新など、ささいなカイゼンを積極的にやる 背景 BASE BANK Dev Division での開発プロジェクトでは、GitHub Project でカンバン運用しています。 「 少人数でのアジャイル開発への取り組み実例 (一歩目の踏みだし方) | 詳説 | July Tech Festa 2020 登壇レポート 」にて、そのカンバンをちょっとだけチラ見せしておりました。 このカンバンには、Issueを作成して登録していくのですが、Issue を作成する際の ISSUE_TEMPLATE には、 GitHub Project は指定できません。そのため、Issue 作成時に対応するプロジェクトを指定していました。 これは、積もり積もると結構面倒な作業で、流れでポンポンIssueを作っていく際のリズムも途切れてしまいます。 ささいなカイゼンの重要性 最近、「 香川編集長 トヨタ生産方式 取材フル (生産量を「100倍」にしたトヨタ生産方式の秘密) | トヨタイムズ 」という YouTube の動画を見ました。老舗合羽メーカー「船橋」の医療用防護ガウンの生産をトヨタ社員がカイゼンして生産性を100倍にするという話です。 www.youtube.com そこで行われていたのは、「最新機器を導入しましょう」みたいな話ではなく、「ここにハサミをおいておいたら1秒早くなる」といった愚直なアイデアでした。 また、クレディセゾン常務取締役CTOの小野和俊さんの著書『 その仕事、全部やめてみよう――1%の本質をつかむ「シンプルな考え方」 』でも、毎日の「繰り返し」を、プログラマー的発想法で効率化することの重要性を説いています。 ちょっとしたことでも日常で行われる「繰り返し処理」をカイゼンしていくことは、チームの生産性・スループットを上げるために重要である、と学びました。 GitHub Actions で自動で登録する GitHub Actions で issue作成をフックに、指定した GitHub Project へ追加する、というものが、 Marketplace で公開されています。それが、 alex-page/github-project-automation-plus です。 github.com 設定方法はかんたんで、次のような yaml ファイルを .github/workflows/ 配下に入れるだけです。 name: 'Automatically add issue to basebankdev project' on: issues: types: [opened] jobs: automate-project-columns: runs-on: ubuntu-latest steps: - name: Move issues into pj column uses: alex-page/github-project-automation-plus@v0.2.4 with: project: githubprojet column: Backlog repo-token: ${{ secrets.AUTOMATION_TOKEN }} どのイベントをトリガーにするかはプロジェクトによりますが、本プロジェクトでは、 issues の opened のみをイベントトリガーに指定することで、作成時の issue を GitHub Project に登録するようにしています。 docs.github.com 注意点としては、レポジトリではなく、Organization 内プロジェクトボードにしている場合は、 secrets.GITHUB_TOKEN ではそのプロジェクトにアクセスできる権限を持っていません。 docs.github.com そのため、専用に admin:org を scope に含めた Personal Access Token を設定する必要があります。 これにより、Issue作成時の GitHub Project の手動指定と、作成したIssueを実際にプロジェクトのカラム(column)に追加する作業が不要になります。GitHub Project で Issue をトラッキングする運用における作業のリズムが改善されました。 ISSUE_TEMPLATEの使い分け その他にも、スプリント計画やレトロスペクティブの記録・アジェンダも、 GitHub Issue で行なっているため、複数の ISSUE_TEMPLATE を用意しています。 $ tree -L 1 .github/ISSUE_TEMPLATE .github/ISSUE_TEMPLATE ├── backlog_template.md ├── config.yml ├── retrospective_template.md └── sprint_planning_template.md ISSUE作成時は、下図のように複数のテンプレートから選択するようになります。 副次的な効果として、テンプレート化されたことで、それぞれのイベントの司会をローテーションできるようになっています。以前、「 朝会の司会はローテーションがオススメ 」というブログエントリーがありました。 www.agile-studio.jp その習慣イベントの継続性や当事者意識の高まりなどが期待されると言う話です。 実際に最近チームで、レトロスペクティブの司会・ファシリテーターをローテーションしていますが、それぞれの視点で様々なカイゼン点が見つかり、チーム運営が効率化される効果を感じています。 (例) Miro の振り返りテンプレートを自作して用意する ISSUE_TEMPLATE に毎回話しに上がる点を追加する おわりに ちょっとした GitHub Project 運用のカイゼン話でした。すぐにでも取り組める内容なので、似たような運用をされていたら、参考にいただければ幸いです。
アバター
はじめに CTOの川口 ( id:dmnlk ) です。 5月に オンラインmeetup をさせて頂きその中で「具体的な負荷対策に関しては開発ブログで!」と言っていた件ですが気づいたらもう9月になりかけていました。 コロナ禍においてネットショップ作成サービス「BASE」の利用者様が急増しました。 www.nikkei.com 5 月には 100 万ショップを超えるショップオーナー様にご利用していただいております。 今まで EC 事業を行っていなかった飲食店様や様々な業種の方が利用をはじめていただき、ショップオーナー様も購入者様共に短期の見通しでは想定をしていないアクセスが発生しました。 その途中でシステムとして対応しきれない面もあり、アクセス負荷によるサービスの不安定を招き皆様にはご不便や販売時間を変更していただくお願いなどをしてしまい大変申し訳ありませんでした。 現在では安定しておりますが、その中で一体 BASE にはどのような問題が発生していたのか、それをいかにして解決したのかを公開させていただこうと思います。 BASEのシステム構成について BASE は構成としてシンプルな Web アプリケーションになっており、オーナー様向け管理画面及び PC やスマートフォンでアクセスされるショップ画面を提供するアプリケーション、ショッピングアプリ「BASE」向け API アプリケーション、サードパーティ向け API アプリケーションなどがあり、データベースは単一のものをそれぞれが参照及び書き込みを行っています。ログ系データのみデータベースを別にしてあります。 アクセスが増えるとこのデータベースの負荷が高くなります。WebAP サーバーに関してはある程度スケールアウトやスケールアップが柔軟に行えるようになっておりますが、データベースに関してはそうはいきません。 データベースは Amazon Aurora MySQL を利用しています。 読み込みに関しては reader インスタンスを増やすことでスケールアウトが可能ですが書き込みに関しては容易には実現できません。 DBへの新規接続タイムアウト問題 コロナ禍において利用者が増えている最中、1 日の中でもアクセスが多い夜間帯でアプリケーションでエラーが多く発生する状態が観測され始めました。 そのエラーは DB への接続時に SQLSTATE[HY000] [2002] Connection timed out というものでした。 このとき、Aurora のメトリクスとして障害になりうるようなものは見当たりませんでした。 Connection 数が Max Connection の上限に達してしまっているならば、 Too many connections のエラーが出ているはずですが発生していません。 CPU や memory に関しても余裕がありました。 このエラーが発生しているとき、ブラウザ上でのアクセスも非常に待たされる状態です。 さらに、社内管理画面のようなアクセスが非常に少ないアプリケーションでも同様のエラーが発生していたので Apache などの WebAP が原因でないと推察していました。 原因が分からない状態ではありましたが、このまま放置することも出来ず利用者がまだまだ増えていく最中でしたので何かしらの対策を打つ必要がありました。 New Relic などのプロファイリングツールを用いて状態を見ていたところ、MySQL への接続に非常に時間がかかっていることはわかりました。 これはクエリの重さに依らず、非常にシンプルな SELECT でも起きていました。 設定でタイムアウトは 30 秒に設定していたので MySQL への接続それ自体に 30 秒かかってしまいアプリケーションとしてエラーになっているということです。 何が起きているかは分かるが、何故起きているが分からない状態で打てる手は多くありません。 アプリケーションで時間のかかっている処理などを少しでも改修するということは行っていましたが目に見える効果は大きくありませんでした。 インスタンスのスケールアップ あまり褒められるものではありませんが、とりあえず primary インスタンスのスケールアップを行うことにしました。 深夜に緊急メンテナンスを行い、primary インスタンスのスケールアップを行いました。 1段階上位のインスタンスへのスケールアップを行ったあと、状態は改善しました。 上記のエラーは夜間帯でも発生しなくなり、理由はわからないが何かしらの対策にはなったと胸を撫で下ろしました。 が、これから 2 週間ほど経ったあと同様のエラーが再発するようになってしまいました。 アクセスは更に増えていたので、また何かしらの性能限界に達してしまったのだということがわかりました。 インスタンスのスケールアップはまだ可能ではありましたが、延々と上げ続けるわけにもいきません。 コストパフォーマンスの面でも無駄が大きすぎますし、原因が分からない以上同様の問題にぶつかることは自明です。 事実、エラーが発生している状況でも Aurora の CPU 使用率は 10%程度しか上がっていません。 弊社では AWS のエンタープライズサポートに加入していますので、AWS の方にも調査を依頼しました。 各種メトリクスの提示や、エラー発生時のアプリケーションの状態などを AWS 側に提出しました。 back_logパラメータの変更 数日後 AWS の方から「back_log パラメータの変更を検討してはどうか」という回答を得ました。 AWS の観測によると当該時間帯、DB への新規接続が数万を超えており SYN の再送が起きていたということがわかりました。 これは Aurora 側で新規接続が受け付けられていない状態です。 新規接続が多くなりすぎて、Aurora の back_log が溢れ SYN/ACK が返却できないため SYN の再送が行われてしまっています。 back_log キューは「サーバアプリケーションが listen しているソケットが、accept していない確立済 TCP コネクションを保持するキュー」であり、これが溢れてしまうと Aurora(サーバー)がパケットを drop してしまうため SYN の再送が発生してしまうということになります。 https://blog.cloudflare.com/syn-packet-handling-in-the-wild/ http://u-kipedia.hateblo.jp/entry/2015/01/01/001135 Aurora にも back_log が設定されており、そのデフォルト値はインスタンスタイプによって自動的に引き上げられます。 スケールアップによって一時的に改善されたのは、この back_log パラメータが引き上げられたことによって一時的に受け入れられる新規接続が増えたことによるものでした。 度重なるアクセス増によって更に新規接続が増えたため、引き上げられた上限を超えたため再度 back_log から溢れてしまい接続が drop してしまったようです。 このパラメータの変更はデータベースの restart を伴うため、オンラインで行うことはできません。failover が行われるとはいえサービスの停止が必要です。 直近に深夜メンテナンスを行ったばかりで心苦しいところではありましたが、システムの安定化のために必要な措置として再度深夜メンテナンスを行い back_log パラメータを引き上げることにしました。 これはパラメータグループでいう back_log というパラメータになるので、ここを max_connections と同値に変更しました。 これによりシステムの安定度は大きく向上しました。 その他のデータストアへの対応 BASE では MySQL だけでなく Memcached や Redis を利用しています。 これらも自社で運用しているのではなくマネージドサービスの Amazon ElastiCache を利用しています。 データベース接続に関して問題がなくなったことで上記のデータストアへのアクセスに同様のエラーが発生していそうなことが分かってきました。 Amazon ElastiCache では Aurora のように back_log パラメータの変更が出来ません。 そこでアプリケーション側で対応することにしました。 PHPでの検索結果Persistent Connectionについて 新規接続を都度行うのではなくコネクションプーリングのように再利用することで新規接続を減らすことができます。 Memcached、Redis のコネクションオブジェクト作成時にそれぞれにオプションを指定することで可能です。 PHP ではこれを Persistent Connection、持続的接続と呼びます。 下記のドキュメントを参照し持続的接続を行うことで確立した接続を維持したまま、新たな接続を受け付けるようにしました。 https://www.php.net/manual/ja/memcached.construct.php https://github.com/phpredis/phpredis#pconnect-popen BASE のアプリケーションのデプロイでは Blue-Green deployment を利用しているため、デプロイ毎に新規インスタンス群が増えます。 待機系になったインスタンス群も、デプロイ後でも接続を保持してしまうので httpd の restart を行う必要がありました。 なお、MySQL への接続にも Persistent Connection を行うことは可能です。 PDO::ATTR_PERSISTENT によって管理されます。 有効にすることはフラグをオンにするだけなので実装コストがほぼ掛かりません。 https://www.php.net/manual/ja/features.persistent-connections.php ですが大きなデメリットがあります。 ドキュメントを参照すると 警告 持続的接続を使用する際にはまだいくつか心に留めておく必要がある注意 点があります。一つは持続的接続でテーブルをロックする場合にスクリプト が何らかの理由でロックを外し損ねると、それ以降に実行されるスクリプト がその接続を使用すると永久にブロックしつづけてしまい、ウェブサーバーか データベースサーバーを再起動しなければならなくなるということです。もう 一つはトランザクションを使用している場合に、トランザクションブロック が終了する前にスクリプトが終了してしまうと、そのブロックは次に同じ接続を使用して実行されるスクリプトに引き継がれる、ということです。 どちらの場合でも register_shutdown_function()を使用してテーブルの ロックを解除したりトランザクションをロールバックする簡単なクリーン アップ関数を登録することができます。しかしそれよりも良い方法は、テーブルロックやトランザクションを使用するスクリプトでは持続的接続を使用 せず、問題を完全に避けて通ることです(他の箇所で使用する分には問題あ りません)。 とあります。 これは接続が状態を持ってしまい、それぞれのケアをアプリケーション実装しなければいけないということです。 トランザクション処理のためにロックを確保している接続がエラーになってしまった場合などにその接続はロックを確保したままになってしまいます。 別のリクエストがその接続を利用とするとケアされていない場合ブロックしてしまうのは更なる障害を生んでしまいます。 トランザクションも同様です。 register_shutdown_function にロックやトランザクションを解除するような仕組みを作らなければなりません。 この処理が正しく動くことを検証することは相当量の検証が必要になるでしょう。 そのため、BASE では DB 接続へは Persistent Connection を利用することを採用しませんでした。 新規接続の限界 back_log の調整、他データストアの Persistent Connection によってしばらくは安定稼働させることができました。 しかし、この時の BASE のアクセス量の伸びは凄まじくこの構成でも接続エラーが発生するようになってしまいました。 ピーク時に秒間 2 万もの新規接続が primary インスタンスへ行われているといった状態です。 残された手段は primary のインスタンスに対しての接続数を如何にして減らすか、ということのみです。 前述のように PHP の Persistent Connection を使うことは新たなリスクを増やすことになります。 一般的に PHP+MySQL という構成だと ConnectionPool を用いることは稀です。なので利用できるミドルウェアもそこまで多くはありません。 ProxySQL や MaxScale というミドルウェアもありますが、これらの検証を行う時間的余裕がありません。これらを利用する場合、これ自体の可用性なども検証し運用する必要があります。 AWS からマネージドのコネクションプーリングとも呼べる RDS Proxy もありますがこの時点ではまだ GA になっておらずプロダクション環境に投入できません。 ここで出来ることアプリケーション側の接続をいかに reader インスタンスに行うか、ということです。 これはクエリを reader に向けるというよくある負荷対策ではなく、新規接続自体を reader に最初から向けるということが求められます。 弊社ではデータベースへのクエリ実行を primary や reader へ振り分けるのに弊社メンバーが作ったライブラリを利用しています。 どのようなライブラリなのか、というのは下記スライドを参照してください。 このライブラリの設定値の中で default_read というパラメータを true にすることで新規接続の時点で reader インスタンスへ接続を向けることが可能です。 Aurora はそのアーキテクチャ上、一般的に言われる primary と reader インスタンス間のデータ反映ラグがとても小さくなるようになっています。最大でも 100ms を超えることはありません。 ですが、全くのゼロでないのでそのラグが致命的な不具合になることがあります。ショップオーナーが変更した価格が反映されずに決済されてしまう、商品の在庫を超過して決済が行われるといった不具合だけは絶対に避けねばなりません。 なので default_read パラメータを有効にせず運用していました。 改めて、BASE というアプリケーションを見直すと主に 3 つの画面に分割されます。 ショップオーナー管理画面 ショップ情報や商品情報が表示され購入者が閲覧するショップ画面 決済処理を行うカート画面 これらはサブドメインで切り分けられていますが、コード及び稼働サーバーは同一です。 この中で 1 と 3 に関しては更新処理が多く行われており、この更新処理に不整合が起きると致命的な不具合となります。 ですが、2 のショップ画面に関しては更新処理はほとんどありません。あってもショップへのお問い合わせをする画面やチャット用の処理、くらいが主となります。 ここだけに絞るならば、default_read を true にするという選択肢は取れるのではないかと考えました。 最もリクエストが多いのもこの画面になるので効果は大きくなることも想定できます。 当初は 1 と 3 のインスタンス群と 2 のインスタンス群を別のサーバーとして振り分け、その場合のみ設定値を変更することも検討しましたが、新規接続をする前にリクエスト時のサブドメインによってこの設定値を可変にできることが判明したのでアプリケーションコードの変更を行いました。 結果として効果は覿面でした。 上記スライドにも上げられていますが、下記のように primary インスタンスへの接続は大幅に減少し reader への接続が増やすことができました。 connection count 上記の変更により圧倒的に BASE の安定度は向上し、ピーク帯においてもページが重かったり決済ができない、などといったことから解放されました。 余談とはなりますが CakePHP2 はデータソース毎に新規接続を作ります。 データベースインスタンスが別であればもちろんのこと、論理データベースが分割されていればその分データソースが分けられるので接続が増えることになります。インスタンスは同一でも、アカウント情報と注文情報などを論理データベースで分けるといったことはよくある構成ですが、これが全て別個の新規接続を生んでしまいます。 1 画面を表示する 1 リクエストにも関わらず複数の新規接続を生んでしまうのでここをうまく 1 つの接続を取り回せるような仕組みを開発することも新規接続を減らすために有効となります。 まとめ 今回 BASE において想定外であり未曾有の負荷であったことは言うまでもありません。 スケールアップだけでは対応できない、アプリケーションの単純なチューニングだけではどうにもならないという事象に初めて遭遇しました。 この中で、例えクラウドインフラを利用していても基礎的なミドルウェアや Linux の知識などが非常に重要であること、アプリケーションの構成を正しく理解しどのレイヤであれば思い切った対策を打てるかといったことを知っておくことの重要性を再認識しました。 合わせて、単純な DB の cpu/memory 使用率だけでなく新規接続の数など様々なメトリクスを監視することも重要になります。 なお、Aurora 1.19 にバージョンを上げると ConnectionAttempts というメトリクスが取得可能になるため、新規接続数をモニタリングすることが可能です(問題発生当時はこれより低いバージョンでした)。 AWS によりますと、Auroraのv2系にすることで更なるパフォーマンスの向上も見込めるということでしたのでアップデート計画を進めています。 今回は AWS の方のサポートが手厚かったことも大変助けになりました。 サポートケースの詳細を記すことはできませんが、弊社側でどのようなメトリクスを取得すれば調査に役立つかや具体的な検証用のソースコードの提供などもしていただきどれくらいの新規接続を行うと Aurora 側の限界値に達するかといった詳細情報まで頂きました。 このおかげで BASE としてどれくらいコネクションを減らすことが必要なのかの目算ができるようになりました。 もちろんこれでどんな時でも問題ないというわけではなく、新規接続をこれからも減らしたり負荷のかかる処理の分散、外部決済サービスへのリクエストの非同期化などまだまだやっていかなければなりません。 「BASE」というプラットフォームをご利用していただくショップオーナー様や購入者様の皆様がどれだけ増えても快適にご利用いただけるようこれからも改善を続けていきます。 弊社ではこのような大規模アプリケーションのインフラ運用やアプリケーション改善などを共に進めていってくれるメンバーを募集しています。 下記からご応募ください。 binc.jp いきなり応募ではなく、ちょっと話を聞いてみたいといった方でもお気軽にどうぞ。 TwitterへのDMも開放していますので是非。 twitter.com
アバター
こんにちは、Native Application Groupの大木です。2020/08/21に開催された TECH STAND #1 React Native にて、「最近のNative Modules開発について」というテーマで発表しました。 最近のNative Modules開発について React Nativeでネイティブアプリを開発していて、全くネイティブコードに触れなくてもいいかと言えば、実はそうではありません。 また、自分たちのユースケースに合うiOS/Androidのプラットフォーム機能にアクセスできるReact Nativeのライブラリが存在しない場合、自分たちでNative Modulesを開発する必要があります。 そのNative Modulesをどのように開発し、React Nativeで使えるようにしていくかについてお話しさせていただきました。 Native Modules開発で抑えるべきポイントはいくつかあるのですが、この発表では以下のポイントに絞って、説明をさせていただきました。 Bob というツールを使うとNative Modules開発のためのセットアップが簡単になる 知っておくべきアーキテクチャーについて SwiftなどNativeモダンな言語で開発する方法 react-native-commnity/Expo SDKは実装の参考になる 今後、Lean Coreでのコア機能のスリム化やRe-architectureなどの取り組みでReact Nativeが大きく改善していく中で、React Nativeへの理解をより深めるための助けになれば幸いです。 まとめ 今回登壇する機会を与えて下さったイベント運営スタッフの皆様に感謝いたします。また、React Native中級者〜上級者向けの実践的な内容ということで、他の方の発表からとても有用な知見を得ることが出来ました。
アバター