ロジクラ入社して1年経ったのでエンジニア環境を内部評価してみた

自己紹介

家では子供3人の父親で、ロジクラでiOSエンジニア担当をしてる川上です。 2021年3月から入社し、iOSを全般的に担当しています。

ロジクラ社員による職場をガチ評価

今回は私がロジクラに入社して1年経過したため、ロジクラに興味を持った方に向けて、働いて分かったエンジニア目線によるエンジニア環境について内部評価してみます。

この記事を書こうと思った理由

転職活動を経験した人は分かると思いますが、「ホントのところ」が見えにくい会社が多いです。

募集要項のみでそれ以外の技術ブログや業務内容が見えなかったりと、情報に鮮度がなくもっと知りたいけれど、他にも見る企業は多いから諦めざる得ないといった経験は多いと思います。

【注意】個人の意見です

なお一個人として独断と偏見の混じった評価となります。 会社としての正式ではありません。

良い点

  • 時代先取り!? 我社はハイブリットワーク
  • コーディングに集中できる
  • Slack(テキストコミュ)の欠点をoVice(音声コミュ)で補っている
  • チーム内の問題解決に本気で取り組んでいる
  • ユーザーファーストが当たり前!
  • サービスに対してみんな意見できる雰囲気
  • 変だと感じたら変だと言っても奇っ怪な目で見られない
  • 上からの言いなりではなく、チームの方針を自分たちで話し合い提案できる
  • 社長が超話を聞いてくれる

我社はハイブリットワークにチャレンジ

私個人はリモートワーク歴は5年目になり、リモートワークの利点と欠点を味わってきました。

ロジクラでは元々はオフィスワーク中心でしたが、約2年ほど前にリモートワークに切り替えました。その後オフィスワークの利点も取り入れたくハイブリットワークを目指して働き方を改善を継続しています。

オフィスで仕事したい人は出社し、そうでない人は自宅や近くのノマドなどでリモートワークを選択できるようになっています。 私自身は今執筆時点で新型コロナ(オミクロン)が流行ってるので9割近く自宅作業です。

最近では借りていたオフィスを引き払い、指定の1曜日だけ借りれるオフィスに移りました。 もちろん流行りだからではなく、コスト面も考えた結果となります。

コーディングに集中できる

ロジクラは他社と比べるとわりかし会議体の少ない会社です。 1スプリント2週間のタイムボックスの中で私が参加している定例と呼べる会議体は合計7.5時間になります。

私個人ではさらに少ない職場で健全に回ってた経験があるのでまだ改善余地はありますが、それでも大手や縦割り組織から見ると少ない方かもしれません。

Slack(テキストコミュ)の欠点をoVice(音声コミュ)で補っている

リモートワークにとってSlackなどテキストコミュは必須かつ重要環境です。 しかしながら、テキストコミュは人によって得意/不得意があり、必ずしも最適解ではありません。 テキストに書き起こせる=自分で情報を整理・構築できている状態であり、 それ以外の例えば

  • 何がなんだか分からん状態
  • 完結するには不足点が多い状態
  • 情報よりも情緒・感情・熱量を伝えたい状態

などは音声コミュや直接話すことで情報齟齬は起きにくく効果的にコミュニケーションできます。 ロジクラではSlackに加えてoViceを使っており、それらを上手に活用しています。 会議体ではmeetやzoomを使っています。

またoViceに拘らず他にも類似ツールで良さそうなものがあれば「とりあえず試してみる」姿勢とそれを許容するマインドがあり、 最近だと月末のTGIFでは SpatialChat を使ってたりします。 こういった挑戦する文化が整っているのは成長促進する良い姿勢だと思います。

チーム内の問題解決に本気で取り組んでいる

私が入社してマインド共有とやり方を提案した経緯もありますが、 不要な会議体を減らしている一方で、チームが抱えている問題を深堀りする時間を2週間に1回2時間半使い問題の深堀りと解決策を出していっています。 これにより問題に向き合い続ける取り組みを維持できているので企業成長して問題が変わっても、同様に解決していける自己組織化された強いチームになると思っています。

ユーザーファーストが当たり前!

企業である以上利益追求は義務だと思います。 しかしながら利益には対価を払っても良いと思ってくれるユーザが存在することで成り立ちます。

ロジクラのプロダクト開発ではユーザーの意見を軽視せず、ユーザー目線の意見を出しても厄介者扱いしないことは、常にファンを獲得し続ける良い姿勢だと評価できます。

またエンジニアでも希望者はユーザーの倉庫へ訪問もでき、現場のオペレーションを教えてもらったり、抱えてる課題や苦悩・苦痛を教えてもらえることでユーザーの気持ちが理解しやすい点はユーザー目線を大事にしている行動だと思います。 訪問時は一人エンジニアが出向くのではなく、PMやCSと一緒に訪問し、基本的なやり取りを担ってくれて、部分的に質問できたりするので、訪問慣れていないエンジニアでも安心です。

サービスに対してみんな意見できる雰囲気

エンジニアでも業務知識を覚え、課題を知れば改善案を思い浮かびます。 しかし大手などでは職種偏見などによりエンジニアの提案は軽視される組織もあります。

ロジクラは誰であっても提案できる雰囲気となっています。 もちろん提案のために案を整理したり論点があってたり、ロジックが整ってる必要はあります。

実際私も何個も新機能や改善を提案していますが、みんな快く話を聞いてくれます。 ときには称賛してくれるほど心地よい雰囲気となっています。

変だと感じたら変だと言っても奇っ怪な目で見られない

過去経験した組織とは非効率なやり方をロジクラがやっていても不思議なことではありません。

その時に全体に対して「基本的な質問」「前提覆す本質ついた質問や意見」を投げかけてはいけないみたいな暗黙の了解はなく、 むしろロジクラでは最初に質問したことに感謝されることすらあります。

上からの言いなりではなく、チームの方針を自分たちで話し合い提案できる

組織によってはマネージャーや上長・経営層からの指示が絶対な会社は当然あります。

理由を聞いても「上が言ったから」と返ってきたり、直接聞くとめんどくさそうな顔されることに辟易している人は、ロジクラは良いと思えます。

なぜなら上へ直接聞くことも出来れば、出た組織方針を自分らで話し合いどう進むか提案することが許されています。 当然一方がすごく強いといった偏りではなく、双方の意見を交換できることが当たり前となっています。

社長が超話を聞いてくれる

会社によっては社長と話すには雲の上の存在でコネが必要だったり、人の話を全く聞かず持論ばかりを押し付ける社長だったりと、関わるだけ無駄な社長は残念ながら存在します。

ロジクラの社長では学生起業で右も左も分からないながらも失敗を繰り返し今までこれた実績があり、その挑戦する文化をすごく重要視しています。

まだこの規模ならではかもしれませんし、これからも継続できるかもしれませんが、 社員が個人として社長と話す機会もあり、社長も常にウェルカムになっており、社内からの情報を常に欲している状態です。

ロジクラに必要な改良点

一方で当然ながら良い点とは言えず、成長チャンスを隠し持った改良点もあります。

  • 資料がまだまとまっていない
  • 発言する人が偏ってる
  • (個人的に)まだ不必要な会議体が多い
  • 情報の交通整理が部署毎で稼働していない

資料がまだまとまっていない

スタートアップあるあるです。 初期スタートアップ時は本当にリソースが足りないため価値提供から離れた作業は優先順位が下がります。 ドキュメントも同じです。

しかし、弊社はそのフェイズは抜けエンジニアの数も当初よりも倍以上に増えました。 そこでお待ちかねの資料不足となります。

これは「なぜ資料がないんだ!」という感情的なものではなくただの通過儀礼的なものだと思っています。 選択と集中によりトレードオフされた結果であり、その結果が今も会社が継続していると分かっているためです。

もちろん「全然ない」ってことはなくある所はしっかりと整備された資料はあります。 しかし、人が増えてスケールアップしたことに対する伝達方法が口頭だった部分に関しては資料不足が出てきている状態です。

これに対して前述した問題解決に全力の会議で、問題となっている部分毎に対応を施す動きで解決へ進んでいます。

発言する人が偏ってる

これは問題が大きく3つあると思っています。

  1. 発言が不慣れな性格、話題、ついていくので精一杯のため発言が出来ない人
  2. 話題に興味が薄い、発言力に偏りを感じているため発言をしない人
  3. 発言したくても何らかの要因で発言できない人

これらは原因特定までは出来ていません、あくまで経験からくる仮定です。 もし2と3、特に3である場合は解決必須です。

これは個々の問題で難易度が高めで時間がかかってますが、メンバーに依頼して個人毎に話を聞いてもらってまずは特定をしている段階です。

(個人的に)まだ不必要な会議体が多い

私が入社してマインド共有と同時に会議体を減らした経緯もありますが、

  • 顔を合わせる必要がある
  • 同期的でなければならない
  • 対話性の高い議論となる

上記に当てはまらないなら会議体である必要はないと思っています。 例えば、議論や意見交換はされず、資料を会議体で読むだけの共有程度の会議体は集まる必要はなく、各自非同期に共有を確認し質問はSlackやoVice等でやれば良いと思ってます。 その質問・議論が長引くのであればそれは情報不足対話性不足関係者不足ということで後日それらを解決するためだけの会議体を開くほうが会議コストを抑えられます。

意思決定のない会議体は正味作業ではなく付帯作業であり、その時間が長いほどユーザーへの価値提供時間が遅くなります。

これに関しては引き続き形骸化してないかなど会議体の品質向上に努めていきたいです。

情報の交通整理が部署間で健全かつ安定して稼働していない

ソフトウェア開発、プロダクト開発、サービス開発、組織運営とどの粒度で見ても情報は流れるものであり、個から個もあればチーム(多)からチーム(多)もあります。 これらの情報の中身は他部署への質問・依頼であったり、共有だったりします。

  • 質問であればFAQや共有不足の改善で質問を減らしたり
  • 依頼であれば定期的に来るものは機能化(自動化やサービス化)で依頼を自己完結&高速化したり
  • 共有であれば最適なタイミングで、最低限の量で、必要とする人へ伝達し続けなければノイズ混じりの情報は見てもらえなくなります。

そしてこれらの情報の発生源はユーザー起因だったり影響するものが多く、最終的にはユーザーへの価値提供速度やCXに影響します。

これらはまだまだ改善余地があると思っており、時間を作って現地現物することで一緒に改善していきたいです。

【まとめ】ロジクラは不慣れながらも頑張る意思は強く、互いを尊敬しあうことを当たり前にしている会社

どちらもまだまだ書ける項目はありますが、量が多くなるのでこの辺でまとめます。

良い点は継続すべきマインドや行動であり、改良点は素直に受け止めそこからどうするか考える成長ポイントだと考えています。

以上が1年働いてみて分かったロジクラの開発事情です。 ロジクラに興味を持ってる方にとって少しでもイメージできる情報となればと思います。

結構ぶっちゃけた内容だと思います。もしこの記事が投稿されれば、それを良しとする企業体質だと言うことになります。

最後に

ロジクラでは一緒に開発してくれるメンバーを募集しています!

興味がある方はぜひ こちら をご覧ください!

本番データを使った検証を手軽にできる仕組みの構築

ロジクラでエンジニアをしている高梨です!

前回のstagingDev環境構築に引き続き、DevOps周りの改善を紹介します! 普段開発を進めていると、本番のデータを利用したいと思うことがよくあったりしますよね?

例えばロジクラでは

  • 顧客からの不具合問い合わせの検証
  • 新機能の仕様検証
  • 新機能のパフォーマンス検証
  • ...etc

などなど、色々なタイミングで本番データを利用したいと思うタイミングがあります。

ただし手動でやろうとすると、本番環境のデータベース周りを触ることになるのでセキュリティ上のリスクや誤操作のリスクがかなり高く、なかなか気軽にできません。

ロジクラではこういった課題をクリアし、誰でも本番データを利用できるような仕組みを作ったので今回はそちらを紹介します!

仕組みの概要

冒頭にいったような、セキュリティ上のリスクや誤操作のリスクを回避し、手軽に本番データが利用できるようにするため、開発者が簡単なアクションをするだけで本番データをマスキングしたデータが入ったデータベース ( 社内ではcloneDB と呼んでいます) を立ち上げ、stagingDev環境からそれを確認できるようにすることをソリューションとしました。

処理の実行には、いつものように github actoinsを利用しています。 github actionsでは以下のようなスクリプトを叩いています。

DBの複製

#!/bin/sh
export AWS_PAGER=""

usage () { echo "Usage : $0 -c <CLONE_DB_CLUSTER_IDENTIFIER> -i <CLONE_DB_INSTANCE_IDENTIFIER> -P <MASTER_USER_PASSWORD>"; }
while getopts c:i:p: OPT
do
  case $OPT in
    c) CLONE_DB_CLUSTER_IDENTIFIER=$OPTARG
      ;;
    i) CLONE_DB_INSTANCE_IDENTIFIER=$OPTARG
      ;;
    p) MASTER_USER_PASSWORD=$OPTARG
      ;;
  esac
done

if [ ! "$CLONE_DB_CLUSTER_IDENTIFIER" ] || [ ! "$CLONE_DB_INSTANCE_IDENTIFIER" ] || [ ! "$MASTER_USER_PASSWORD" ]
then
  usage
  exit 1
fi

DB_SUBNET_GROUP_NAME=logikura-xxx
VPS_SECURITY_GROUP_ID=sg-xxx
SOURCE_DB_CLUSTER_IDENTIFIER=logikura-xxx

# DBClusterのclone
echo "cloning database..."
aws rds restore-db-cluster-to-point-in-time \
  --source-db-cluster-identifier $SOURCE_DB_CLUSTER_IDENTIFIER \
  --db-cluster-identifier $CLONE_DB_CLUSTER_IDENTIFIER \
  --restore-type copy-on-write \
  --use-latest-restorable-time \
  --db-subnet-group-name $DB_SUBNET_GROUP_NAME \
  --vpc-security-group-ids $VPS_SECURITY_GROUP_ID

## DBClusterにDBInstanceを作成
echo "creating database instance..."
aws rds create-db-instance \
  --db-instance-identifier $CLONE_DB_INSTANCE_IDENTIFIER \
  --db-cluster-identifier $CLONE_DB_CLUSTER_IDENTIFIER \
  --db-instance-class db.t3.medium \
  --engine aurora-postgresql
aws rds wait db-instance-available --db-instance-identifier $CLONE_DB_INSTANCE_IDENTIFIER
aws rds modify-db-cluster \
  --db-cluster-identifier $CLONE_DB_CLUSTER_IDENTIFIER \
  --master-user-password "${MASTER_USER_PASSWORD}" \
  --apply-immediately

すでにあるDBをリストアしています。コマンドの詳細は公式のドキュメント をご覧ください!

ただしこのままでは本番のデータがそのまま利用できてしまうのでセキュアではありません。問題に対応するため、隠しておきたいデータをマスキングする処理を実行しています。

マスキング

#!/usr/bin/env ruby
require 'optparse'
require 'securerandom'

clone_db_host = nil
opt = OptionParser.new
OptionParser.new do |opt|
  opt.on('--clone-db-host VALUE', 'clone db host name') { |v| clone_db_host = v  }
  opt.parse(ARGV)
end
raise "required clone_db_host" if clone_db_host.blank?

settings = YAML.load_file("config/settings/masking.yml")

raise "saftiy" if Rails.env.production?
raise "saftiy" unless clone_db_host.match(/clone/)

ActiveRecord::Base.establish_connection(
  ActiveRecord::Base.connection_config.merge(host: clone_db_host, database: "logikura_production"),
)
settings["tables"].each do |table_name, column_names|
  sql = "UPDATE #{table_name} SET"
  update_terms = column_names.map do |column_name|
    "#{column_name} = uuid_generate_v4()"
  end
  ActiveRecord::Base.connection.execute("UPDATE #{table_name} SET #{update_terms.join(', ')}")
end

ymlにtableとcolumnの定義を買いてuuidで埋めてるシンプルな実装です。

以上を実行するとこで、本番相当のデータを安全に利用することができます!

最後にgithub actionでdbのエンドポイントをコメントするようにしているので、そのエンドポイントを利用して検証などを進めています 🎉

f:id:logikura:20211229133344p:plain

エンジニア以外での利用

前回の記事 で検証環境( 通称stagingDev環境 )をbranchごとに手軽に作る仕組みを紹介させて頂きましたが、staginDevの立ち上げ処理には環境変数を設定できるフックポイントがあります。

test -f staging_dev.env && export $(cat staging_dev.env)
if [ -e "./deploy/staging_dev_env/${STAGING_DEV_BRANCH}.env" ]; then
  export $(cat ./deploy/staging_dev_env/${STAGING_DEV_BRANCH}.env | grep -v ^# | xargs);
fi

これを利用して、 branch_name.env

DB_HOST=xxx

のように書いておくと、立ち上げた環境が接続するDBホストの値をoverrideして変更することができます。 検証環境で手軽に本番データを扱うことができるので、開発者だけではなくカスタマーサクセスのメンバーやセールスのメンバーも、マスキングされた安全なデータを問い合わせの対応や機能検証の際に気楽に使うことができます!

まとめ

本番とほぼ同じデータをいつでも利用できるようにしたことで、より正確な検証を行うことができ、顧客への対応や機能リリーススピードが改善しました 🙌 ローカルやstagingだとレコード数が少ないので常に高速に動いてる感じになりますが、実際にお客さんの環境を再現することでボトルネックに気づけることがあります。

また、ロジクラの開発チームでは週に1度パフォーマンス改善の取り組みをチームで行っているのですが、その際の検証環境としても本番環境のデータをcloneしたデータベースが施策の検証のために役立っています!

スタートアップのフェーズでは機能開発に追われがちですが、このような基盤の改善を進めることでさらに開発スピードをあげていき、より良いサービスの提供を進めていっています 👍

最後に

ロジクラでは一緒に開発してくれるメンバーを募集しています!

マスキングスクリプト実行に時間がかかるので、もっと良いソリューションある方 & ロジクラに興味ある方はぜひ こちら をご覧ください!

検証環境をbranch毎に作ってリリーススピードを改善した話

自己紹介

ロジクラでエンジニアをしている高梨です!

前回ロジクラのインフラ構成を紹介 したのに続き、今回もロジクラの開発を支えている環境周りに関して、リリーススピードを数倍にした例を紹介していきます!

以前ロジクラではチーム開発を進めていく中で、機能検証がスタックすることによって開発のスピードが停滞するという課題が発生していました。

具体的な問題としては、検証環境がstagingしか存在しておらず、複数の機能開発が同時に進んでいる場合に、PMなどに仕様を満たしているか確認してもらう順番待ちが発生したり、手直しがあった際に巻き戻しコストがかかりリリースが遅れるということがあります。

結果として、大きめの機能が入ってくるとリリース頻度が週に1度ほどになってしまうことがよくありました。

仕組みの概要

上記の問題を解消するため、変更した機能ごとに開発者以外が簡単に検証することができる stagingDev という環境を用意することをソリューションとしました。

branchごとにサブドメインの割り振りとECSのタスクを立ち上げ、今まで通りstagingのDBにアクセスできるようにして社内メンバーなら誰でも検証できるというものです。

処理の実行には github actoinsを利用しています。

以下に実際の設定を貼ります。

name: StagingDev作成君

on:
  pull_request:
    types: [opened, synchronize]
env: ...

jobs:
  build:
    if: contains(github.event.pull_request.labels.*.name, 'StagingDev')
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: kayac/ecspresso@v1
      with:
        version: latest
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with: ...
    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1
    - name: Setup Environment
      run: ... # 必要な環境変数の準備

    - name: Build Image
      run: |
        ./deploy/docker_build.sh staging ${{ secrets.IMAGE_REPOSITORY_URI }} $BRANCH_NAME ${{ secrets.SIDEKIQ_CREDENTIAL }} $BRANCH_NAME
        docker push ${{ secrets.IMAGE_REPOSITORY_URI }}:$BRANCH_NAME

    - name: Setup
      if: env.SETUP_COMPLETED == 'false'
      run: |
        # priorityが重複するとエラーになるので最大値を取得して重複しないようにする
        PRIORITY=$(aws elbv2 describe-rules --listener-arn ${arn} | jq -r '.Rules | map(select(.Priority == "default" | not) | .Priority | tonumber) | max + 1')
        # cloudformationでターゲットグループ, リスナールール, レコードセットの作成
        aws cloudformation create-stack \
          --stack-name $STACK_NAME \
          --template-body file://$(pwd)/deploy/cloudformation/staging_dev.yml \
          --parameters ParameterKey=BranchName,ParameterValue=$BRANCH_NAME ParameterKey=ListenerPriority,ParameterValue=$PRIORITY
        # 完了するまで待つ
        aws cloudformation wait stack-create-complete --stack-name $STACK_NAME

        TG_NAME=$STACK_NAME
        export TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups --query "TargetGroups[?TargetGroupName == '$TG_NAME'] | [0]" | jq -r .TargetGroupArn)
        export BRANCH_NAME=$BRANCH_NAME
        export IMAGE_TAG=$BRANCH_NAME
        export ENTRY_POINT='"./deploy/init.sh"'
        # ECS Serviceの作成
        ecspresso create --config ./deploy/ecs/logikura-web-staging-dev/logikura-web-staging-dev.yml
        ecspresso create --config ./deploy/ecs/logikura-worker-staging-dev/logikura-worker-staging-dev.yml

    - name: Deploy
      if: env.SETUP_COMPLETED == 'true'
      run: |
        TG_NAME=$STACK_NAME
        export TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups --query "TargetGroups[?TargetGroupName == '$TG_NAME'] | [0]" | jq -r .TargetGroupArn)
        export BRANCH_NAME=$BRANCH_NAME
        export IMAGE_TAG=$BRANCH_NAME
        export ENTRY_POINT='"./deploy/init.sh"'
        ecspresso deploy --config ./deploy/ecs/logikura-web-staging-dev/logikura-web-staging-dev.yml
        ecspresso deploy --config ./deploy/ecs/logikura-worker-staging-dev/logikura-worker-staging-dev.yml

    - ... # issueへのコメントやslack通知処理

上記の処理の流れを簡単に説明します!

  1. PRに StagingDev ラベルを貼り、そのPRに変更をプッシュすることによってActionがトリガ
  2. 該当ブランチで利用する環境変数をセット
  3. 該当ブランチのイメージを生成
  4. 用意しているcloudformationの設定を利用し、リソースを構築
    • Route53
    • LB (target groupの設定など)
  5. タスクのデプロイ
  6. 完了したらurlをPRにコメント

f:id:logikura:20211129120805p:plain

最後にslack通知を行います🚀

f:id:logikura:20211129120831p:plain

ちなみに設定は紹介しませんが、PRをクローズすると環境が消されるactionも用意しており、環境が作られっぱなしになることが内容になっています 👌

リソースの構築について

ざっくりとした環境構築までの流れは理解いただけたかなと思うので、ここからさらにstagingDev環境を具体的にどう構築しているかを記載します。

前回の記事でも紹介したようにロジクラはAWS上でECSを利用して運用されています。

実際に検証できる環境を作るためには、branchごとの変更がdeployされたECSの新しいタスクに対して独自のurlからアクセスできるようにリソースを作らないといけません。

CFでTGとRecordSetの作成

まず設定から記載します。

AWSTemplateFormatVersion: "2010-09-09"
Description: ""
Parameters:
  BranchName:
    Type: String
  VpcId:
    Type: String
    Default: 
  AlbArn:
    Type: String
    Default:
  Listener443Arn:
    Type: String
    Default: 
  ListenerPriority:
    Type: Number
Resources:
  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckIntervalSeconds: 10
      HealthCheckPath: "/"
      Port: 80
      Protocol: "HTTP"
      HealthCheckPort: "traffic-port"
      HealthCheckProtocol: "HTTP"
      HealthCheckTimeoutSeconds: 5
      UnhealthyThresholdCount: 2
      TargetType: "ip"
      Matcher:
        HttpCode: "200"
      HealthyThresholdCount: 5
      VpcId:
        Fn::ImportValue:
          !Sub "${VPCStack}-VPC"
      Name: !Sub "logikura-tg-ecs-${Environment}"
      HealthCheckEnabled: true
      Name: !Sub "staging-dev-${BranchName}"
      Port: 443
      Protocol: HTTP
      TargetType: ip
      VpcId: !Ref VpcId
  ListenerRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - Type: forward
          TargetGroupArn: !Ref TargetGroup
      Conditions:
        - Field: host-header
          HostHeaderConfig:
            Values:
              - !Sub "dev-${BranchName}.com"
      ListenerArn: !Ref Listener443Arn
      Priority: !Ref ListenerPriority
  RecordSet:
    Type: AWS::Route53::RecordSetGroup
    Properties:
      HostedZoneName: logikura.com.
      RecordSets:
      - Name: !Sub "dev-${BranchName}.com"
        Type: CNAME
        TTL: '60'
        ResourceRecords:
        - ***.elb.amazonaws.com

awscliで作っても良いのですが、削除が簡単にできるようにCFを採用しています。 terraformを利用している会社ではterraformで書き換えてもらってもよいかと思います!

最終的にbranch毎に割り振られたurlにアクセスすると、以下のようにbranch毎の変更が反映されたリソースにアクセスできます!

f:id:logikura:20211129120900p:plain

リソースの削除について

削除はCFで作ってるので簡単です。

githubactionsでPRが閉じた際にECSとELB等をまとめて消してます。

ecspresso delete --config ./deploy/ecs/logikura-web-staging-dev/logikura-web-staging-dev.yml --force
ecspresso delete --config ./deploy/ecs/logikura-worker-staging-dev/logikura-worker-staging-dev.yml --force

aws cloudformation delete-stack --stack-name $STACK_NAME
aws cloudformation wait stack-delete-complete --stack-name $STACK_NAME

一つ注意としてはPRの削除のたびに実行するとエラーになるので、stackが存在するかチェックする必要があります。

aws cloudformation describe-stacks --stack-name $STACK_NAME

注意事項

cloudformationのstackNameの制約で、 [a-zA-Z][-a-zA-Z0-9] にbranch名を限定しないとリソースの作成が失敗してしまうという問題があります。

branch名のルールが決まっていれば問題ないのであえて解決していませんが、もし命名に異なるルールがあるチームの場合は注意してください!

まとめ

ブランチごとに開発者以外が動作検証できる環境を作ったことで、それぞれの環境で仕様確認が同時並行で行うことができ、リリースがスタックすることがなくなりました 🙌

結果的に開発からユーザーに利用してもらうまでのスピードが数倍早くなり、顧客への提供スピードが上がっただけではなく、誰でも自由に検証できる環境があることで事前の社内周知やガイド作成など会社全体とし業務スピードをあげることにも成功しました!

今後も開発がスケールする仕組みを作っていき、さらに顧客へ価値を届けることができるようにしていきたいです 👍

最後に

強引にシェル芸で解決してる部分もあるのですが、もっとスマートにしていきたいので、 ロジクラでは一緒に開発してくれるメンバーを募集しています!

興味がある方はぜひ こちら をご覧ください!

現状のインフラ構成とワーカーのオートスケール

現状のインフラ構成とワーカーのオートスケール

ロジクラでテックリードをしている高梨です! サービスが公式に公開される前から技術選定だったり、実際にコード書いたり、インフラやSRE的なことまで色々やってます。 最近は愛犬と遊ぶのにはまってます。

f:id:logikura:20211124171041j:plain

概要

ロジクラは今年、AzureからAWSへとインフラの移行を行いました 🎉

当初ロジクラはAzureを利用していました。利用していたAppServiceは十分要件を満たすことができ、Microsoftさんからのサポートも手厚かったことが主な理由で利用していたのですが、会社のフェーズが進み、今後さらに事業拡大し開発組織を大きくしようと考えたとき、リファレンスが充実しており、経験者が多いAWSに移行して採用につなげることを主な目的にインフラ移行を決定しました。 ここでは現状のロジクラのインフラ構成と、移行時に工夫した点などを紹介していきます!

今回移行したものはメインサービスである在庫管理のSaaSのロジクラです。

必要な仕組みとして大まかに説明すると、

  • メインのサービスを乗せているサーバー・DB
  • 非同期処理を行うためのサーバー
  • 非同期処理を行うためキューを貯めるRedis

というシンプルな構成になっています。

管理やスケールの容易さから今回メインのサービスを載せるサーバーについては、ECS・Fargateを利用することにしました。

構成紹介

載せきれない部分もあるのですが、大まかな構成としては以下のようなシンプルな構成になっています。

f:id:logikura:20211110165801p:plain

次にこの構成を作っていった中で、特に工夫していった点を紹介していきます。

デプロイについて

デプロイに関してはgithub actionをトリガーにしています。stagingとproductionでトリガーは異なるのですが、deploy処理に関しては以下のgithub actionの設定ファイル(不要な部分はカットしています)のように、buildしたimageを一度ECRに置き、そのimageを利用してdbのmigrationを行った後にdeployする流れとなります。

...
jobs:
  ...  
  build_image:
    runs-on: ubuntu-latest
    steps:
    - uses: nelonoel/branch-name@v1.0.1
    - name: Check out code
      id: checkout
      uses: actions/checkout@v2

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ${{ secrets.AWS_REGION }}
    - name: Set environment
      id: set_environment
      run: {{環境変数を設定する処理}}

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1
    - name: Build Image
      run: {{deployするimageをbuild}}

  deploy:
    needs: [build_image]
    runs-on: ubuntu-latest
    env:
      IMAGE_TAG: ${{ needs.build_image.outputs.image_tag }}
    steps:
    - uses: kayac/ecspresso@v1
      with:
        version: latest
    - name: Check out code
      id: checkout
      uses: actions/checkout@v2
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ${{ secrets.AWS_REGION }}

    - name: Database Migration
      run: {{DBのmigrate処理}}

    - name: Quiet Sidekiq Process
      run: {{Sidekiqプロセスの停止}}

    - name: deploy
      run: |
        ./deploy/ecs_deploy.sh -t web -e staging -i $IMAGE_TAG
        ./deploy/ecs_deploy.sh -t worker -e staging -i $IMAGE_TAG

実際のdpeloyに何しては、カヤック製のecspressoを利用しており、以下コマンドで $SERVICE_NAMEごとの設定(productionやstagingなどを入れています)を読み込んでいます。

ecspresso deploy --config ./deploy/ecs/${SERVICE_NAME}/${SERVICE_NAME}.yml --rollback-events DEPLOYMENT_FAILURE
ecspresso wait --config ./deploy/ecs/${SERVICE_NAME}/${SERVICE_NAME}.yml

詳しくはecspressoのレポジトリをご覧ください!

タスクに関してもgithub actionから実行したいコマンドと環境を指定して実行するよう設定しています。

以下がecspressoでの実行コマンドになります。

ecspresso --config ./deploy/ecs/${SERVICE_NAME}/${SERVICE_NAME}.yml run --overrides="{\"containerOverrides\":[{\"name\":\"logikura-ecs-container-web-${APP_ENV}\", \"command\":[\"./deploy/run_command.sh ${COMMAND}\"]}]}"

最後にdeployとタスク実行ともに、完了するとslack通知が流れてきます。

f:id:logikura:20211110171022p:plain

以上が基本的なdeploy周りの設定となります!

オートスケールについて

構成の中で工夫した点としては、workerのオートスケールの仕組みを導入したことです。

ロジクラは、利用者の性質上、例えば商品登録や出荷データの登録など、大量のデータを一度に処理したいと言う要望が多く、非同期処理を行うことが必要になっています。

ただ、処理が集中した場合jobが詰まってしまうと言うことが発生し得ます。顧客も増えている ⇒ 安定的に全てのお客さんにシステムを使ってもらうために、job数に応じてオートスケールする仕組みを取り入れています。

credentials = Aws::Credentials.new(ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_KEY"])
client = Aws::CloudWatch::Client.new(region: EMV["AWS_REGION"], credentials: credentials)
client.put_metric_data({
    namespace: "SidekiqJobCount",
  metric_data: [
    {
       metric_name: "job_count",
       dimensions: [
          {
            name: "app_env",
            value: ENV["RAILS_ENV"],
          },
        ],
        value: Sidekiq::Stats.new.enqueued,
        unit: "Count",
    },
  ],
})

落ち着いたらスケールインしたくなりますが、処理の途中で落としてしまうと設計によっては問題が生じる場合があるかと思います。

https://github.com/mperham/sidekiq/wiki/Signals#tstp

SidekiqはTSTPシグナルを送ると新しいジョブの取得を停止し、処理が終わると停止します。

この仕組を利用して、TERMをtrapしてSidekiqを安全に停止するようにしました。

function trap_term() {
  SIDEKIQ_PID=`ps -ef  | grep -v grep | grep sidekiq | awk '{ print $2 }'`
  kill -TSTP $SIDEKIQ_PID
  while :
  do
    SIDEKIQ_STATUS=`ps axu  | grep -v grep | grep sidekiq`
    echo $SIDEKIQ_STATUS
    if [[ $SIDEKIQ_STATUS =~ stopping ]]; then
      echo "stopped sidekiq"
      exit
    else
      sleep 1
    fi
  done
}

trap trap_term TERM

これで解決したように思えたのですが、処理に時間のかかるjobの場合stopTimeout(最大120秒)を超えるとKILLされてしまう問題が起こりました。

stopTimeoutはFargate環境では現状これ以上伸ばせないので、結局スケールインはできていません。

何か良い方法あれば教えて下さい 🙏

参考

https://aws.amazon.com/jp/blogs/news/graceful-shutdowns-with-ecs/

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_definition_parameters.html

まとめ

toBのサービスだと同じような課題を抱えている会社は多いと思います。ぜひ参考にしてみてください!

ロジクラではこの他にもインフラ面で工夫している点がいくつもあるので、また別の記事でも紹介していきます!

最後に

ロジクラでは一緒に開発してくれるメンバーを募集しています!

興味がある方はぜひ こちら をご覧ください!

開発チームのマネジメント層を置かないことに決めた話

こんにちは!ロジクラでプロダクトチームの統括をしている榊間です。
初記事なのでまずは会社紹介から、、

僕が所属している株式会社ロジクラはSaasの在庫管理アプリ「ロジクラ」を提供しており、モノを扱う全ての企業が持つ在庫問題の解決を通して、お客様の事業の基盤・インフラとなる存在になるために事業を展開しています!

 

会社組織としては拡大中で、僕が入った当初の数人しかいなかった時期から仲間も増え、会社らしくなってきたなというふうに感じてます笑

そんなロジクラですが、外部で手伝っていただいている方を含めると開発チームは現在7名で、主にロジクラのiOSアプリとWEBアプリを開発しています(各メンバーの紹介はこちら)

 

通常このくらいの規模になるとマネージャー層を作って組織マネジメントの準備を考える会社が多いと思いますが、ロジクラの開発チームではあえてマネジメント層を置かないという判断をしました。

この記事ではその背景と運用に関して紹介します!

 

背景

まずそもそもマネジメントがなぜ必要か考えた時、理由の一部を挙げると以下になります

  • チームの目標と方向性決め、全社目標とのすり合わせ
  • メンバーの管理
  • メンバーの評価

...などなど(他にもたくさんある)

 

マネジメント職が担う役割は多岐に渡り、その機能は会社を動かしていく上でどれも必要です。

ただ、機能は必要だとしてもそれを個人が担う必要はあるでしょうか。

 

会社の成長のボトルネックになる

マネジメント層をつくるデメリットとして、意思決定がマネージャーに集中します。もちろんメンバーに裁量持たせてチームをうまく回せるマネージャーであれば問題ないですが、個人のスキルに依存するので、構造的に意思決定の集中を誘発しやすいです。

全ての意思決定のたびに許可が必要になると、多数の意思決定を行わなければならないスタートアップではボトルネックになることがあります。

 

主体性の抑制

意思決定の集中により、

  • マネージャー個人で方針が合わないと却下され、多様な意見が取り入れにくくなる
  • メンバーが意思決定に関わる場が設けられない

といった現象も起こります。

その結果、メンバーそれぞれが意思決定か関わるアイデアを考えたり、行動をそもそも起こさず、マネージャーから与えられた役割をこなすことに収束し、主体性の抑制が起きてしまいます。

 

業務環境の変化によりマネジメントがさらに難しくなった

コロナの影響でロジクラも全社員フルリモートになりました。

同じ悩みを持っている会社も多いと思いますが、それぞれのメンバーとコミュニケーションをとったり、パフォーマンスを管理するということ自体が以前より難しくなったの感じています。

このような状況下において、あえて難易度の高いマネジメントをするよりは、メンバーそれぞれがある種セルフマネジメントができるような環境を作っていくことが重要だと考えています。

 

組織の方針を決めれたきっかけ

実際このような方針に転換できたのは、もともといる開発メンバーがセルフマネジメントを行えていたという部分が大きいです。

会社としても、ロジクラでは全社の数値や方針が公開・説明されているので、全員が共通認識を持ちやすく、会社の目標達成のために個人、チームでどのような意思決定を行うべきか考える機会が多い文化がありました。

採用活動においてもセルフマネジメントの部分は強調しており、実際以前の会社でマネジメント職を行っていた方が共感して入社を決断してくださるなど、同様の課題感を抱えている開発者は多いのを感じています。

 

運用上の問題点とその解決策

マネージャーが担うはずの機能は誰がやるの?

まず評価制度構築、組織事務管理、技術支援など、チームで必要だが本来マネジャーが担うような機能を洗い出し、それぞれの機能に担当者を設定しました。

こうすることで意思決定のスピードを担保しつつ、意思決定の機能が個人に集中するのを防ぐことができます。

ただ、各機能が本当に必要か、うまくワークしてるかなどは確認が必要で、チームとしてガバナンスミーティングというものを設定し、役割交代も含め定期的に見直しをはかっています。

ガバナンスミーティングとは

役割を新たにつくったり、修正したり、なくしたりする必要があると感じている人(「提案者」と呼ばれる)は、だれでも議題に自分の案を加えることができる。このようなチームのガバナンスに関わる問題が順に一つずつ取り上げられ、次のプロセスに従って採決に至る。
1提案が発表される
2問題点の明確化
3反応ラウンド
4修正と明確化
5異議申し立てラウンド
6統合ラウンド

*1

みんな平等ということ?

違います。各メンバーが担う機能の重みや量などはバラバラなので、評価もそれに合わせて変えています。

あくまで主体性の抑制をしないことが目的なので、全員同等の評価をするということではありません。

 

意思決定が遅くなるのでは? 

この取り組みをメンバーの権限を完全に平等にすることだと捉えると意思決定のたびに全員の承認が必要なのではないかという発想になり、意思決定のスピードが下がると思われる方もいると思います。

ただ、あくまで意思決定は各機能の担当者が行い、必要なのは関係者に対する事前の共有と相談のみです。

現状は人数も少なく、開発チームに関する決定はデイリースクラムなどで全体に共有できるのですが、規模が大きくなった場合はティール組織にある助言プロセスを導入しようと計画中です。(助言プロセスについては詳しく説明しませんが、ティール組織ぜひ読んでみてください!)

 

今後も自己成長を継続

以上が今ロジクラの開発チームで行っている取り組みです。

チームの規模によって問題も変わってくると思いますが、今後うまくいった・いかなかったことも随時公開していければと思ってます!

 

最後に、、

ロジクラでは一緒に開発、組織づくり手伝ってくれる人を募集してます!

ぜひカジュアルに組織・技術のことを話しましょう!

=> 応募はこちら 

*1:引用: フレデリック・ラルー ティール組織 - マネジメントの常識を覆す次世代型組織の出現