社内SREコミュニティをやっている運営のひとり、VTRyoです。
今回は8月23日に主催するイベントについてご紹介です。
日時
【マネーフォワードSRE Meetup】多様化し続けるSREのLT会
- 時間 8/23 19:00~
- 場所 Youtube Live
当日は #sreforward のタグでTwitterにて感想をいただけると運営・登壇者一同励みになります!
社内SREコミュニティをやっている運営のひとり、VTRyoです。
今回は8月23日に主催するイベントについてご紹介です。
【マネーフォワードSRE Meetup】多様化し続けるSREのLT会
当日は #sreforward のタグでTwitterにて感想をいただけると運営・登壇者一同励みになります!
こんにちは!マネーフォワードのHR領域でQAエンジニアをしている森田です。
前回以来の半年ぶりのエンジニアブログ投稿になります。
不具合分析の取り組みを導入してから1年ほど試行錯誤してきましたが、同じような悩みや課題感を抱えて、不具合分析に取り組んでいる方もいらっしゃるのではないか思います。
そこで、導入編、運用試行錯誤編に分けて
何を考えて何を行ったのかと、その結果についてお話していきます。
導入編の今回は、不具合分析の取り組みをどう開始したのか、どう開発チームに取り入れていったのかについてお話します。
(次回の運用試行錯誤編では、どんな切り口で分析しているか、分析結果をどう開発チームに持ち帰っているのかについてお話する予定です!)
プロダクトによって運用方法が異なるところもありますが、大体下記の流れで運用しています。
※内容の詳細については次回です!
こんばんわ。
マネーフォワード クラウドBox というサービスを開発しています、Rails エンジニアの SAKAMOTO X です。
マネーフォワード クラウド Box (以下 MFCBox)はその名の通りストレージサービスです。
開発環境上や検証前の共用開発環境では、ファイルストレージとして S3 互換サービスの MinIO を使っています。
この記事では、弊社の検証前の共用開発環境 の Kubernetes Cluster 上で上記をどう構成しているかを説明していきます。
この記事でご説明する焦点は大きく分けて主に二つです。
読むと効果的な方は以下のような方を想定しています。
読み終わった頃には以下ができるようになることを目指します。
記事は少し長くなりますが、一つでも参考になる箇所があれば嬉しいです。
弊社の共用開発環境では、共用開発環境用の EKS でマネージされている Kubernetes Cluster 上に開発者が自由に Kubernetes の Namespace を作ることができ、その中でMFCBoxなどの検証したいサービスを必要なリソースの manifests を組み合わせて起動させることができます。
弊社の共用開発環境では、以下の理由で S3 等の各サービス個別に必要な AWS リソースは使わず、Kubernetesのリソースで表現するという方針になっています。
(EKS、 EBS は例外)
AWS 互換サービスは Localstack もあるのですが、以下の理由で MinIO を使っています。
* 欲しい AWS互換サービスは S3 のものだけであったこと
* Web UI が綺麗
* チームメンバーが使い慣れている
MFCBox の image のコンテナで起動している Pod と MinIO の image のコンテナで起動している Pod を分けて通信します。
ステートフルな Pod の実現方法と、共用開発環境上のコストやDB運用負荷からくる制約とどう折り合いをつけるかというところに焦点を置いています。
今回の構成を実現するに当たって重要な Kubernetes リソースは主に以下の5つです。
ファイルストレージコンテナには MinIO の image を使いますが、ステートフルなコンテナにするためには永続化領域が必要です。
永続化領域を確保するには、ネットワーク越しに Node にアタッチするタイプのディスクの機能として Kubernetes の Cluster レベルのリソースである PerisistentVolume を使い、PersistentVolumeClaim という Namespace レベルの Storage API を使って Pod から永続化領域を要求します。
ただし、Provisioner が設定された StorageClass というリソースを使うことによって、PersistentVolumeClaim が発行されてからクラウド環境の API を呼び出して動的に PersistentVolume の Provisioning を行うことにします。
このような Dynamic Provisioning では事前に開発者が PersistentVolume を作成しておく必要がなく、要求するリソースに対して割り当てる Volume 容量の無駄も生じさせないというメリットがあります。
参考: https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/
今回の例では Provisioner に kubernetes.io/aws-ebs
を使い Amazon EBS の領域を PersistentVolume に Provisioning します。
ステートフルなアプリケーションを管理するための Workload API には、一般的に使われている Statefulset を弊チームで使用することにしました。
Statefulset は以下の点で良い性質を持っているからです。
これらの性質によって、PersistentVolume を PersistentVolumeClaim と紐づけて Pod にマウントすると、 Pod ごとに PersistentVolume が割り当てられ、各 Pod の再作成時には喪失前と同じディスクを参照することができます。
つまり、次に紹介する Headless service と組み合わせれば Pod 名で Pod のIP アドレスを引けるようになるということも合わせると、サフィックスが 0 の Pod をマスターとしたリードレプリカ構成を実現しやすい仕組み になっています。
参考: https://kubernetes.io/ja/docs/tasks/run-application/run-replicated-stateful-application/
(※ ただし実際は Pod を 1 つしか起動しないのであれば Statefulset の恩恵はあまりなく、Deployment でも大丈夫です。今回の例でも 1つしか起動していませんが、今後は複数起動する可能性もあるので Statefulset を使っています。)
StatefulSetは現在、Podのネットワークアイデンティティーに責務をもつために Headless Service を要求する、という制限事項 があります。
したがって弊チームでも Pod の DNS を実現する Service API としてはPod の IP アドレスを直接返す Headless Service を作成します。
この制限の背景は、上述のように Statefulset は Pod によって割り当てられる PersistentVolume が異なり、他の Pod から Statefulset のどの Pod に接続するかで扱うデータが異なるので、リクエスト先の Pod の宛先ホスト名を明示して使用したい場合があります。
そうなると、各 Pod に対してネットワークアイデンティティを保たれていることが Podごとに固有のFQDNを払い出す Statefulset と相性が良いから、だと私は考察しています。
上記の構成を 表現したものが以下の manifests です。
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: gp3-ext4-ebs-sc reclaimPolicy: Delete volumeBindingMode: WaitForFirstConsumer provisioner: ebs.csi.aws.com parameters: type: gp3 fsType: ext4
apiVersion: v1 kind: PersistentVolumeClaim metadata: labels: create-snapshots: "true" annotations: argocd.argoproj.io/sync-options: Prune=false name: mfc-box-filestorage-pvc spec: storageClassName: gp3-ext4-ebs-sc accessModes: - ReadWriteOnce # アクセスを許可するノードを1つに限定する。EKS では ReadWriteMany をサポートしていないという背景もあっての選択。 resources: requests: storage: 100Gi
Statefulset では spec.volumeClaimTempletes で PersistentVolumeClaim に相当する項目を設定しても良い。
apiVersion: v1 kind: Service metadata: name: mfc-box-filestorage-service spec: clusterIP: None selector: app.kubernetes.io/name: mfc-box-filestorage ports: - name: minio protocol: TCP port: 9000 targetPort: minio-port
apiVersion: apps/v1 kind: StatefulSet metadata: name: mfc-box-filestorage spec: serviceName: mfc-box-filestorage-service # この Statefulset 特有のフィールドで Pod 側から Headless service を直接指定することで Pod 名で IP アドレスを引けるようになる。 selector: matchLabels: app.kubernetes.io/name: mfc-box-filestorage replicas: 1 updateStrategy: # Statefulset 特有のフィールド。RollingUpdate がデフォルトなので無くてもOk type: RollingUpdate template: metadata: labels: app.kubernetes.io/name: mfc-box-filestorage spec: containers: - name: set-buckets image: amazon/aws-cli:2.4.15 # run startup script command: ["sh"] args: ["/root/.aws/set-buckets.sh"] env: - name: AWS_DEFAULT_REGION value: ap-northeast-1 - name: AWS_ACCESS_KEY_ID value: hoge - name: AWS_SECRET_ACCESS_KEY value: fuga volumeMounts: - name: set-buckets mountPath: /root/.aws - name: mfc-box-filestorage image: minio/minio:RELEASE.2020-09-21T22-31-59Z imagePullPolicy: Always args: - server - /data env: - name: MINIO_ACCESS_KEY value: minioadmin - name: MINIO_SECRET_KEY value: minioadmin ports: - containerPort: 9000 name: minio-port resources: requests: memory: 128Mi cpu: 250m limits: memory: 256Mi cpu: 500m volumeMounts: - name: mfc-box-filestorage-pv mountPath: /data volumes: - name: mfc-box-filestorage-pv persistentVolumeClaim: claimName: mfc-box-filestorage-pvc - name: set-buckets configMap: name: minio-config apiVersion: v1 kind: ConfigMap metadata: name: minio-config data: set-buckets.sh: |- #!/bin/bash # 同じ Pod に含まれる container 同士はネットワークが隔離されておらず、IP アドレスを共有しているので、mfc-box-filestorage コンテナには localhost 宛で通信が可能。 set -e -x -u MINIO_ENDPOINT="http://localhost:9000" MFC_BOX_MINIO_ORIGINAL_BUCKET_NAME=mfc-box-original-data echo "Waiting for S3" # 使用しているブログで、 bash を不正なスクリプトとして判定されてしまうので `bash` としていますが、本来`(backtick)は不要です timeout 2m `bash` -c "until aws s3 ls --endpoint-url=$MINIO_ENDPOINT ; do sleep 5; done" echo "Minio is there" EXISTING_BUCKETS=`aws s3 ls --endpoint-url=$MINIO_ENDPOINT` if echo $EXISTING_BUCKETS | grep -q $MFC_BOX_MINIO_ORIGINAL_BUCKET_NAME; then echo "Bucket ${MFC_BOX_MINIO_ORIGINAL_BUCKET_NAME} already exists." else echo "Creating ${MFC_BOX_MINIO_ORIGINAL_BUCKET_NAME}" aws s3 mb s3://$MFC_BOX_MINIO_ORIGINAL_BUCKET_NAME --endpoint-url=$MINIO_ENDPOINT echo "Done" fi aws s3 ls --endpoint-url=$MINIO_ENDPOINT echo "wait forever to keep state clean.." sleep infinity
MinIO の image で起動した filestorage 用のコンテナとは別に bucket 作成用のサブコンテナをたてるサイドカーパターンにしました。
この bucket 作成用のコンテナが起動したら MinIO の bucket を作成する shell スクリプトを実行します。
これらの pod の起動順を initContainers
などを使って調整することもできるのですが、bucket 作成用のスクリプト上で MinIO のコンテナが起動し、aws s3 ls コマンドに応答するまで待つことで起動順を気にしなくてよい関係になっています。
※ この記事の MinIO image は Apache license 2.0 のやや昔のものです。最新の MinIOの場合、AGPL になっていたり、MINIO_ACCESS_KEY
や MINIO_SECRET_KEY
の部分等、異なる部分があると思うのでご注意ください。
※ Kubernetes は v1.21.3 です。API document: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/
上述のコードで示したファイルストレージ用のリソースは、結論から言うと アプリケーションコードのリソースとは別の Namespace に配置しています。
具体的には例えば以下のディレクトリツリーの shared-mfc-box-filestorage
の配置です。
(manifets 管理ツールには Kustomize(v3.6.1)を利用しています。)
. ├── namespaces │ ├── mfc-box-a │ │ ├── kustomization.yaml # services/mfc-box の内容を取り込む │ │ └── namespace.yaml │ ├── mfc-box-b │ │ ├── kustomization.yaml # services/mfc-box の内容を取り込む │ │ └── namespace.yaml │ ├── shared-mfc-box-filestorage │ │ ├── kustomization.yaml # services/mfc-box-filestorage の内容を取り込む │ │ └── namespace.yaml │ └── shared-db # services/mfc-box-db や他のサービスの DB の内容を取り込む │ ├── kustomization.yaml │ └── namespace.yaml └── services ├── mfc-box │ └── 略 ├── mfc-box-filestorage │ ├── base │ │ ├── kustomization.yaml │ │ ├── persistent-volume-claim.yaml │ │ ├── storage-class.yaml │ │ ├── service.yaml │ │ └── statefulset.yaml │ └── kustomization.yaml └── mfc-box-db └── 略
この構成にした背景は、またしても共用開発環境特有の制約が関係しています。
また、ファイルストレージはどのようにして使われるかから考えると導き出しやすいと思います。
弊社の共用開発環境上での ストレージのインフラ構成を踏まえながら、ツリーのような配置の理由ご説明します。
この共用開発環境上の Kubernetes Cluster では 各サービスの DB に関するリソースは、アプリケーションのリソースがある Namespace とは別れていて、共通の Namespace に置かれています。
アプリケーションのコンテナと違い、DBのコンテナは image が共通でもデータに違いがあるため、DBのコンテナが乱立した状態だと何か問題が生じたときに対応負荷が上がるからです。
MinIO のようにステートフルなファイルストレージは、同じくステートフルな DB とできるだけ寿命を一致させたいため、アプリケーションコードのリソースとは別の Namespace に配置します。
(例では shared-mfc-box-filestorage
という Namespace にしています。)
つまり共用開発環境では、検証したい Namespace のアプリケーションから共通のファイルストレージを読み書きします。
具体的には、ある人が立ち上げた MFCBox の Namespace(mfc-box-a
) のリソースと、他の人が立ち上げた MFCBox(mfc-box-b
) の Namespace のリソースから共通の MinIO の bucket のリソースを読み書きすることになります。
一方で、アンチパターンとして、ある人が立ち上げた MFCBox の Namespace に MinIO の bucket 用のリソースも入れてしまうとどうなるかというと、bucket の内容が 共通のDB と内容とずれてしまいますし、検証が終わって Namespace ごと削除した場合には 共通 DB に不要な内容が残ってしまいます。
MFCBox のアプリケーションコードの image で起動している同一クラスター内の Pod から、 上記でご紹介したファイルストレージの Pod に接続することを考えます。
Kubernetes の Service の仕組みによって Pod は
<Service名>.<Namespace名>.svc.cluster.local:
というドメインで名前解決されます。
つまり Service が listen する Port まで含めると http://mfc-box-filestorage-service.shared-mfc-box-filestorage.svc.cluster.local:9000
です。
もしくは、Service の中でも今回の構成では Headless Service を使い、かつそれを Statefulset から serviceName
で指定しているので以下のように Pod 名でも Pod の IP アドレスを引くことができます。
<Pod名>.<Service名>.<Namespace名>.svc.cluster.local:
したがって、
http://mfc-box-filesotrage-0.mfc-box-filestorage-service.shared-mfc-box-filestorage.svc.cluster.local:9000
という URI でも接続できます。
9000 は Headless service が listen している port 番号です。
上記のエンドポイントに向かってリクエストをすると mfc-box-filestorage コンテナの 9000 port へトラフィックを転送してくれます。
最後に以上の説明を踏まえて、
MFCBox のアプリケーションコードの Pod がある Namespace を mfc-box とすると、その中からファイルストレージ用の Pod に接続するインフラ構成図を表現すると、冒頭に示した図になります。
(DBのリソースは簡単化のため省略しています)
私たちが使える共用開発環境では以下のような条件(前提)や制約がありました。
このような状況下において、
ステートフルなファイルストレージ用のリソースは共有 Namespace に配置し、各々が検証したい image で起動した Namespace のステートレスなアプリケーション間で共有する
というようなリソース構成例をご紹介しました。何かしらご参考になれば嬉しいです。
こんにちは。クラウド勤怠でエンジニアをしていますkatuoです。今回は意外と知られていないVue.jsの「Render Function」を紹介します。
※ タイトルの「勤怠FEギルド」はクラウド勤怠のフロントエンドの技術的負債の解消や生産性向上のためのコード整備を行うチームのことを指しています。
皆さんお久しぶりです、初めてのかたははじめまして。
3年ぶりに祇園祭が開催されたり、また感染者数が上がっていたりとなかなか難しい情勢ですが、蒸し暑さで体調を崩さないようお気をつけください。
ぼくは先日夏風邪を引きました……。
今日はマネーフォワードで月イチで開催しているEngineering All Handsという社内イベントの運営裏話をしようと思います。
基本的に運営メンバーが限られているのであまり知る機会がない事柄ですし、社内メンバーも意外と知ってる人はいないと思います。
社内に限定する意味合いもないので、公開ドキュメントとしてエンジニアブログで現状のログとサマリーを残しておこうと思います。
この記事が、これから社内イベントを企画する、あるいはしているがうまくいっていないと感じられている方の参考になると嬉しいです。
余談ですが、技術広報は夏から秋にかけてカンファレンスの準備や企画で大変忙しい繁忙期にあたります。
自分で設定した「四半期に1回記事を投稿する」ノルマを早めに達成しておきたい…みたいな思惑があったりなかったりします。
この記事はエンジニアリング戦略室 エンジニアエンゲージメントグループ 技術広報の @luccafort の提供でお送りします。
それではいつもどおり長文ですが、旅のお供にどうぞ。
こんばんは。
いま僕は堂島川のほとりでこれを書いています。
闇夜の水面に、在りし日を思い浮かべながらこれを書いています。
夜を越えるのって難しいですよね。
そんな1022回目の夏、みなさんいかがお過ごしですか?
僕は夏より熱い春を越えたので夏が来る前から夏バテです。
さて、そんな熱い春にやったことを少しずつ紹介しようと思いますが、まずはStepFunctionsのカスタムメトリクスをつくった話を。
こんにちは。あるいはこんばんは <VTRyo>です。
2022年6月に一人Product SREだった僕もようやくメンバーが増え、チームとしてコミュニケーションを成立させねばという気持ちになってきました。
タスク管理ツールといったチームに閉じているものは基本的にチーム単位で選定できるのがマネーフォワードの良いところです。
我々マネーフォワードクラウドHRソリューションのSREグループ(以降SREグループと表記)では、GitHubを使って以下のことをしています。
今回は、社員の方からこんな声を頂いたことをきっかけに執筆しています!
マネーフォワードビジネスカンパニー クラウドERP本部 会計Plus開発部の西村です。
エンジニアリングマネジャーとして クラウド会計Plus の開発に携わっています。(執筆時)
本記事では ユニコーン企業のひみつ ―Spotifyで学んだソフトウェアづくりと働き方 を何度も読んだ私が toB 向けのプロダクト開発において経験し、考えたことを紹介します。
私は2021年1月にソフトウェアエンジニアとして入社し、グループリーダーを経て、エンジニアリングマネジャーとしてマネジメントに従事しているという立ち位置です。
もちろん1人でなしとげたことではなく、チームで考えて、学んで、成長してきた記録です。
https://www.oreilly.co.jp/books/9784873119465/
この本はインセプションデッキなどを紹介した アジャイルサムライ のジョナサン・ラスマセンの新作。著者がソフトウェアエンジニアとして働いた急成長中の Spotify でどのような取り組みをして、なぜうまくいったのかをユーモアたっぷりに紹介してくれる一冊です。Design It! や モノリスからマイクロサービスへ などの翻訳をされている 島田浩二 さんと、アジャイルサムライ や アジャイルな見積もりと計画づくり、そして最近だと Clean Agile の訳者としても知られる 角谷 信太郎 さんによる翻訳です。訳者あとがき はCCライセンスで公開されているので気になる方はこちらをまず読んでみてください。
Spotify では組織をスケールさせるためにどのようなことをしてきたのかが書かれているのですが、そのままやれば成功するテクニック集ではありません。
ちりばめられたヒントを拾い集めて、自分たちの組織をどうやってスケールさせるかを考えるきっかけにするという読み方が良いと私は思います。
こんにちは、関西開発拠点に所属し、マネーフォワード クラウド会計Plusでスクラムマスターをしています、堺です。
6/17-18にScrum Fest Osaka 2022が開催されました。
Osaka(大阪)と銘打たれていますが、全国から参加者が集まり、経験を伝え合い、交流していく「祭」になっています。
今年も
などなど、見どころ満載の2日間でした。
オンラインとオフラインのハイブリット開催でしたが、メインストリームはオンライン(コミュニケーションはdiscord、セッションはzoom)、オフラインの現地会場はホワイエ的な扱いで、登壇者も現地会場以外から発表するという形での開催でした。
(おかげで、遠方から無理に来ずとも同じ熱を感じられるという素晴らしい効果!)
マネーフォワードはスポンサーになっており、社内からは合計12名が参加、それぞれに学びを得てきました。
また、運営として参加している松尾浩志さんもいて、彼は同じチームでスクラムマスターとして在籍していたりします(自慢)
こんにちは、CTO室AI推進部アナリストグループの足立です。私たちアナリストグループは、主に「プロダクトの課題発見のためのデータ分析」に取り組んでいます。ユーザの皆さんがサービスをより利用しやすくなるよう、データ分析によって得られた知見は様々な場面で活用しています。
前回[1]は、多くのデータアナリストが参考にしているであろう、CRISP-DMと呼ばれるデータ分析のフレームワークを紹介しました。今回は、CRISP-DMを活用する上で注意することについて紹介します。
CRISP-DMは、データを中心に、ビジネス理解から始まり共有・展開に至るまで、6つの工程があります[1]。