Scala で書いたマイクロサービスを Go で書き直した話

この記事はJX通信社 Advent Calendar 2019 2日目の記事です。
昨日は、たっちさんの「Kubernetes Admission Webhookでリソース作成を自在にコントロールする」でした。

f:id:nsmr_jx:20191129095710p:plain

こんにちは、サーバーサイドエンジニアの @kimihiro_n です。 今回は長年動かしてた Scala のマイクロサービスのリビルドを行った話をしようと思います。

TL;DR

  • 新しい言語を投入するのにマイクロサービスは便利
  • Scala で感じていた問題点を解消しつつ Go へ移行できた
  • 消費メモリが大きく減って安定稼働できるようになった 

予防線を貼っておきますと、Scala より Go のほうがいいよね、といった本旨ではありません。

Scala で書いたマイクロサービス

弊社のマイクロサービスの一つにカテゴリ分類専用のサービスが存在します。 カテゴリやキーワードを登録しておくとルールベースでカテゴリ分類を行うもので、自然言語やMLを使うほどでもない大雑把な分類を低コストで実現するためのサービスです。

持ってる機能としては

  • カテゴリやキーワードを REST 形式で CRUD 操作できる
  • テキストを分類エンドポイントへ POST するとカテゴリ分類が実行される

というシンプルなものです。2015年4月に作り始めたのでもう4年以上稼働しているシステムになります。

このシステムは最初 Scala で作り始めました。当時社内で Scala 勉強会というのが行われており、そこで学んだ知識の実践のために Scala を使って実装をはじめました。 独立したサービスなので新しい言語を持ち込むには最適のタイミングだったと思います。 また計算的な処理も多いのでメインで使っている Python よりもパフォーマンス出せるのではないかという期待もありました。

いろいろあったものの (本筋ではないので省略) 無事サービスとしてリリースでき、今日まで稼働してくれました。 ただ4年間動かす上でインフラ周りの大きな変遷がありました。

インフラの変遷

f:id:nsmr_jx:20191129100411p:plain:w120 f:id:nsmr_jx:20191129100459p:plain:w100

当初は専用の EC2 を建てて、そこに Ansible でデプロイするという形式で動かしてました。インスタンス自体の管理をしなくてはならないものの、サーバーのリソースを専有して動かせるので安定して動かす事ができていました。

その後、Docker が全社的に流行りだし、このサービスも Docker の上で走るよう変更しました。ビルドした war ファイルを Docker Image に乗っけるだけだったので移行はスムーズでした。

Docker の運用も最初は ElasitcBeanstalk を使って専有インスタンスで稼働していましたが、会社の ECS の基盤が整ってからは共有のサーバーの上で動くようになりました。

共用サーバーになって顕在化してきたのが Scala のメモリ使用量の問題です。 1タスクをメモリの割当量が256MBでは安定して動かすことが出来ず OOM が頻発してしまいました。512MBや1024MB消費することもあったので実装上のメモリリークも疑わしかったのですが、そもそも最低専有量が大きくて共用サーバーでは扱いづらいサービスとなっていました。 Docker の上に JVM を載せてその上にアプリケーションを載せる2階建ての構造になっているので、どうしてもオーバーヘッドが大きくなってしまうみたいです。 (最近は GraalVM のようなものが出てきて事情が変わりつつあります。)

その他 Scala で発生していた問題

またメモリ以外に Scala を扱う上で発生していた問題がいくつか。

コンパイルが遅い

開発のタイミングで一番の課題はこちらでした。インクリメンタルビルドがあるものの、多くの場合コンパイル待ち・テスト待ちで手が止まってしまっていました。フルにビルドが走ってしまうと10分くらい待たされることもありました。普段はインタプリタの Python メインで書いていたのでなおさら待ち時間への戸惑いを感じました。

書き方の統一が難しい

Scala はとても高機能な言語です。オブジェクト指向でありながら関数型のパラダイムを取りこんでいる特徴を持ってます。 それ故にプログラマの Scala 熟練度によって書き方が変わってしまう問題がありました。 Better Java 的な書き方から、より Scala らしい書き方、関数型を意識した書き方などいろいろな表現方法があります。 プログラムを書く上で「こういう書き方あるよ」と教えてもらってスキルアップできるのはとても楽しいですが、システムを保守する上でネックになりがちでした。 全社的に Scala の知見があって、コーディングの方針が統一できていればよかったのかもしれませんがそこまでの体制は作れませんでした。

JVM の知見が少ない

先程のメモリの話にも共通することですが、会社として JVM のサーバー運用に対して知見が溜まってなかったというのもあります。メインで使っている Python であればサーバーを動かすための知見が溜まっていて安定的に動かすことができたのですが、JVM のシステムは社内初だったため不安定になったり、問題発生時に適切な対処が取りづらかったです。

一時期新サービスは Scala で書いていこうみたいな勢いがありましたが、これらの問題のためか結局 Python メインに戻ってしまいました。

Go でリビルド

Python は個人的にもしっくりきていてとても扱いやすい言語だと思っているのですが、プロジェクトの規模が大きくなってくるにつれ「型」の重要性が増してきました。静的な型付けがあると、それ自身がコードのテストになり実行時の予期せぬエラーを減らしたり、型の情報を生かして IDE による効率的な開発が出来たりします。 Python にも Type Hints と呼ばれる型のサポートがありますが、エディタによってサポートのばらつきがあったり、間違った型付けをしていても mypy を通さない限り気づかなかったりしてイマイチです。

そこで目をつけたのが Go でした。 静的型付け言語の候補はいくつもあるのですが、コンパイルが速く Docker とも相性がよい言語を探してるうちに Go がよさそうなのでは、と思うようになってきました。 言語仕様もシンプルで書き手に左右されづらかったりと Scala のときに感じていた課題点を解消できそうな点もよさそうでした。あとコンパイルの速さとかコンパイルの速さなんかも大事ですね。 最終的に Go やろうと決め手になったのは社内で Go を利用しているプロジェクトがすでにある点でした。社内にすでに触れる人が複数名いるというのは非常に心強いです。

Tour of Go をこなして基本を覚えたあと、言語を覚えるには実践が一番ということで Go でかけそうなシステムを探してました。 そこで出てきたのが例の Scala のマイクロサービスです。独立したサービスで改修しやすく、APIの仕様も固まっており規模もそんなに大きくないという理由から、カテゴリ分類サービスを書き直してみることにしました。

やったこと

仕様が固まっていたので BDD (振る舞い駆動開発) を採用してテストコードを充実させながら進めていきました。

BDD のフレームワークを使うと

    シナリオ: カテゴリ一覧がみれる
    前提 insert_categories.sqlの中身がDBにセットされている
    前提 メソッドがGET
    もし /v1/categoriesへアクセスする
    ならば ステータス200が返ってくる
    かつ レスポンスのカテゴリが1件入っている

みたいな形で振る舞いを記述することができます。 この形式自体は Gherkin と呼ばれる形式で、プログラムの実装言語によらず共通して定義することができます。 日本語のドキュメントのような形なので非エンジニアでも何をしたいのかが明確になるメリットがあります。

Go の場合 DATA-DOG/godog gucumber/gucumber といった BDD のテストフレームワークが存在します( Python だと behave というものがあります)。 今回はDATA-DOG/godog を利用してテストを書いていきました。

実際テストを書くときは、上記のシナリオの 1 行 1 行(=step) に対応するコードを準備します。

たとえば「ステータス200が返ってくる」に対応するコードだと以下のようになります。

func (c *Context) CheckStatus(status int) error {
    if c.resp.Code != status {
        return fmt.Errorf("status_code %d is not %d", c.resp.Code, status)
    }
    return nil
}

200 の部分はパラメータとして利用できるので汎用的なテストコードを実現することができます。 BDD のフレームワークを利用すると嬉しいのが、振る舞いの記述を追加してもその分だけテストコードが増えるわけではないという点ですね。同じ名前の step は同じ関数が再利用できるので、開発が進むほどテストコードに割く時間が減ってくる特徴があります。

Web のフレームワークは Echo を使ってみました。Go の場合フレームワークを使わなくてもいいみたいな話を聞きましたが、REST API の場合、GET や POST, PUT... といったメソッドレベルでのルーティングが必要となってくるため、フレームワークを利用して見通しをよくするようにしました。

Go を書いていて思うのはやはりコンパイルが速くて快適ということですね。テスト込みでも数秒で終わってしまうので TDD、BDD といった手法と相性がいいです。 あとは Go 言語は筋肉が要求されるというのが実感できました。例外処理もないので、愚直にエラーをチェックして返り値にセットするみたいなのを頻繁に書く必要があります。 ただ慣れてくると返り値を多値にして error を返すという仕様がとても自然に思えてくるようになってきました。 例外で横道から抜けられてしまうよりはちゃんと関数の結果として返ってくるほうが考慮漏れずに記述できる感じがします。 記述が長くなってしまうことについては「筋肉、筋肉…」と思うことで納得しています。

サービスリプレース

業務の合間の気分転換にちょっとづつ進めること約半年、ついにサービスが完成しました。 DB まわりのテストどうするか悩んだりきれいな設計になるようリファクタしてたりしてたら結構かかってしまいました。

Docker 化

FROM golang:latest as builder

ENV CGO_ENABLED=0 \
    GOOS=linux \
    GOARCH=amd64 \
    GO111MODULE=on

WORKDIR /opt/app
COPY . /opt/app
RUN go build

# runtime image
FROM alpine
COPY --from=builder /opt/app /opt/app

CMD /opt/app/basic_classifier

ECS に載せるため、Docker のイメージを作ったのですが Dockerfile がとてもシンプルでした。 バイナリを生成できてしまうと強いですね…。最終的なイメージサイズもかなり軽量です。 ECS だと起動のタイミングでイメージの Pull が走るので、イメージが軽いことは起動の高速化にも繋がります。

デプロイ

いよいよ実戦投入です。ECS で別タスクとして作っておき簡単にロールバックできるようにして切り替えました。

問題なく動いてる…と思いきや大きな問題が。 カテゴリ分類サービスを利用してるシステムの一部で予期せぬ挙動が起こってしまいました。 原因を精査してみたところ、API の仕様が変化しているのが元凶でした。 パラメータ名がスネークケースではなくキャメルケースという…。 サービス作成時に策定した仕様書に沿って実装していたのですが、Scala 版の実装のほうが間違っていて実装しており、利用側も実装を正に進めていたため、切り替えのタイミングでトラブルが起きてしまってました。

切り戻してシステムを改修したところ無事動くようになりました。

パフォーマンスの変化

実際 Scala から Go に置き換えてみてメモリ消費量がどうなったかというと

f:id:nsmr_jx:20191129101331p:plain

のようにリリースを境に大きくメモリ消費量を減らすことが出来ました。
(Scala 版右肩上がりなのでメモリリークも疑わしいです…) クラスタに余裕ができたので台数減らしたり、その分他のサービスを載せることができそうです。

一応 Apache Bench を使って簡易的なベンチマークもとってみました。

f:id:nsmr_jx:20191129101309p:plain

軽めのテキストの分類
Scala: Requests per second:    97.24 [#/sec] (mean)
Go: Requests per second:    118.89 [#/sec] (mean)

重めのテキストの分類 
Scala: Requests per second:    20.37 [#/sec] (mean)
Go: Requests per second:    29.34 [#/sec] (mean)

Go 書くときパフォーマンスはあまり意識せず書いていたので、Scala より性能が上がるのは嬉しい誤算でした。 分類結果は一致しているので大きくロジックを変えたつもりはないですが、コード的に完全移植したわけではないのでこのベンチマークは参考程度に見てもらえると。 Scala 強い人が書いたら十分逆転もありえそうです。

おわりに

1機能が独立して動くマイクロサービスみたいな構造だとこういった置き換えが気軽にできていいですね。 サービスまるまる実装してみて Go への理解がだいぶ進んだように思います。 パフォーマンスや使い勝手も悪くないのでこれから Go どんどん触っていきたいです。

明日は@shinyorkeさんの「データをいい感じに活用するためのアジャイルな言語化とその取り組み」です。

Kubernetes Admission Webhookでリソース作成を自在にコントロールする

この記事はJX通信社 Advent Calendar 2019の1日目の記事です。

こんにちは、SREのたっち(TatchNicolas)です。

先日開催されたKubeCon 2019でもセッションで紹介されていた、Admission Webhooksについて書きます。

Admission WebhooksとはKubernetesリソースを操作(CREATE/UPDATE)する時に、作成や変更の内容をチェックしたり、書き換えたりすることができる機能です。

TL;DR

  • Admission Webhooksを使うと、あらゆるKubernetesリソースの操作をトリガーに 「チェック(Validation)」「変更(Mutation)」 を行える
    • 身近なところでは、Istioでサイドカーのauto-injectionで使われています
  • どの種類のリソースにどんな操作をするときにWebhookを呼ぶかは細かく条件指定ができる
  • 最低限自分で実装すべきは非常にシンプルなWebサーバのみで、簡単に試すことができる
  • 実運用するには冪等性、可用性、適用範囲など考えることが色々

サンプルコードは以下に置いてあります。

github.com

やってみる

f:id:TatchNicolas:20191129002303p:plain
Kubernetes Admission Webhooksの位置付け

今回は、以下の二種類のAdmission Webhooksを作ってみます。

  • 「Pod作成時にenv ラベルにdev,stg,prdのいずれかが付与されているかチェックする、なかったらリソース作成を拒否する」 = ValidatingWebhook
  • 「Pod作成時にリソースの名前にある接頭辞を付与する」 = MutatingWebhook

Kubernetes環境はAdmission Webhooksのbetaが取れている v1.16を使います。今回はMinikube v1.4.0を利用しました。*1

Webhookを書いてServiceとして動かす

では早速、Webhookを実装しましょう。要は、kube-apiserverからJSONを受け取ってJSONを返すことができれば良いので言語やフレームワークは何でも良いです。 前述の通り今回は手軽さ最優先でFlaskを使います。

最低限のValidationは非常にシンプルで、以下のようなレスポンスを返すだけです。allowedtrue であればValidationは成功し、false であれば失敗します。

{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": false
  }
}

実際に書いたコードはこちら。今回はstatus フィールドにメッセージを入れてみました。たとえばkubectlによるPod作成時にValidationに失敗した場合、このメッセージが返ってきます

これをDeploymentとしてデプロイし、Serviceとして叩けるようにします。*2

今回は同じクラスタ内から利用するのでClusterIPだけで十分です。 真面目にやるなら、目的の異なるWebhookは別にデプロイしたほうが良いと思いますが、今回はお試しということで一つのアプリケーションとして書いてしまいました。

Admission WebhooksはHTTPS必須なので、Podを作る前にまずcfsslを使ってオレオレ証明書を作成します。

$ cat <<EOF | cfssl genkey - | cfssljson -bare server
{
  "hosts": [
    "sample-admission-webhook.default.svc"
  ],
  "CN": "sample-admission-webhook.default.svc",
  "key": {
    "algo": "ecdsa",
    "size": 256
  }
}
EOF
2019/11/27 18:28:38 [INFO] generate received request
2019/11/27 18:28:38 [INFO] received CSR
2019/11/27 18:28:38 [INFO] generating key: ecdsa-256
2019/11/27 18:28:38 [INFO] encoded CSR

server-key.pemserver.csr が作成されます。続いて、Webhookに持たせるための証明書を作ります。

$ cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
  name: sample-admission-webhook.default
spec:
  request: $(cat server.csr | base64 | tr -d '\n')
  usages:
  - digital signature
  - key encipherment
  - server auth
EOF
certificatesigningrequest.certificates.k8s.io/sample-admission-webhook.default created
$ kubectl certificate approve sample-admission-webhook.default
certificatesigningrequest.certificates.k8s.io/sample-admission-webhook.default approved

base64エンコードしてダウンロードし、そこからSecretリソースを作ります。

$ kubectl get csr sample-admission-webhook.default -o jsonpath='{.status.certificate}' | base64 --decode > server.crt
$ kubectl create secret tls --save-config sample-admission-webhook-secret --key server-key.pem --cert server.crt
secret/sample-admission-webhook-secret created

最後に、このSecretを使ってDeploymentを作成し、それに紐づくServiceも作成します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-admission-webhook
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sample-admission-webhook
  template:
    metadata:
      labels:
        app: sample-admission-webhook
    spec:
      containers:
      - name: app
        image: tatchnicolas/sample-admission-webhook:0.10
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: tls
          mountPath: /tls
        command: ["gunicorn"]
        args: 
        - "main:app"
        - "--bind"
        - "0.0.0.0:8080"
        - "--access-logfile"
        - "-"
        - "--certfile"
        - "/tls/tls.crt"
        - "--keyfile"
        - "/tls/tls.key"
      volumes:
      - name: tls
        secret:
          secretName: sample-admission-webhook-secret
---
apiVersion: v1
kind: Service
metadata:
  name: sample-admission-webhook
spec:
  selector:
    app: sample-admission-webhook
  ports:
  - port: 443
    protocol: TCP
    targetPort: 8080

Webhookの設定を登録する

さて、それではいよいよAdmission Webhooksの設定を登録します。必要なManifestは至ってシンプルです。

caBundle フィールドにてクライアント(=kube-apiserver)がWebhookへリクエストを送る際に信用する証明書を指定することができます。

まず、ValidatingWebhookのほうから登録してみましょう。

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: "sample-validating-webhook"
webhooks:
- name: "sample-validating-webhook.hoge.fuga.local"
  failurePolicy: Fail
  rules:
  - apiGroups: [""]
    operations: ["CREATE"]
    apiVersions: ["v1"]
    resources: ["pods"]
    scope: "Namespaced"
  clientConfig:
    caBundle: <server.crtの中身をbase64エンコードして貼る>
    service:
      namespace: default
      name: sample-admission-webhook
      path: /validate
  admissionReviewVersions: ["v1", "v1beta1"]
  timeoutSeconds: 5
  sideEffects: None

続いてMutatingWebhookも同様に

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: "sample-mutating-webhook"
webhooks:
- name: "sample-mutating-webhook.hoge.fuga.local"
  failurePolicy: Fail
  rules:
  - apiGroups: [""]
    operations: ["CREATE"]
    apiVersions: ["v1"]
    resources: ["pods"]
    scope: "Namespaced"
  clientConfig:
    caBundle: <server.crtの中身をbase64エンコードして貼る>
    service:
      namespace: default
      name: sample-admission-webhook
      path: /mutate
  admissionReviewVersions: ["v1", "v1beta1"]
  timeoutSeconds: 5
  sideEffects: None

Manifestを適用したら、リソースが作成されたことを確認しましょう

$ kubectl get validatingwebhookconfigurations
NAME                        CREATED AT
sample-validating-webhook   2019-11-28T05:58:28Z
$ kubectl get mutatingwebhookconfigurations
NAME                      CREATED AT
sample-mutating-webhook   2019-11-28T05:39:28Z

動きを確認する

では、実際に動作確認してみましょう。

apiVersion: v1
kind: Pod
metadata:
  name: webhook-demo
  labels:
    env: stg
spec:
  containers:
  - name: nginx
    image: nginx

こんなManifestからPodを作ってみましょう。

$ kubectl apply -f pod.yaml
pod/dummy-prefix-webhook-demo created
$ kubectl get pod
NAME                                        READY   STATUS              RESTARTS   AGE
dummy-suffix-webhook-demo                   0/1     ContainerCreating   0          2s
sample-admission-webhook-7ddfc8c784-6pf7k   1/1     Running             0          6m6s

Pythonで書いた処理の通り、名前が書き換わっています。

Validationのほうもチェックするために、env ラベルが dev,stg,prd のいずれにも一致しないPodを作ってみましょう。

apiVersion: v1
kind: Pod
metadata:
  name: validation-demo
  labels:
    env: willberejected
spec:
  containers:
  - name: nginx
    image: nginx

設定した通りのエラーメッセージが返ってきており、Podも作られていないことが確認できます。

$ kubectl apply -f invalid_pod.yaml
Error from server: error when creating "invalid_pod.yaml": admission webhook "sample-validating-webhook.hoge.fuga.local"
denied the request: Required label env is not set or its value is invalid

はまったところ

  • k8s v1.14.8で利用できるバージョン(admissionregistration.k8s.io/v1beta)では、リクエスト先 portオプションを指定するとUnknown fieldって怒られる(サンプルには書いてあるのに...)
  • Podに対してWebhookを書くと、DeploymentやReplicaset経由で作られるPodについてもValidation/Mutationの対象となってしまうので、Webhookを開発中にそのDeploymentを更新したらWebhook用のPod自体がValidationに引っかかってコケてしまった
    • あとは kube-system ネームスペースのPodに対して悪さをされると困るので、GitHubのサンプルコードでは namespaceSelector objectSelector を使って対象を絞っています

感想

オレオレ証明書の準備等の手間はかかりましたが、Webhook自体は非常にシンプルに作ることができました。Kubernetes自体にコンパイルされるAdmission controllerを作るのは非常にハードルが高いですが、こうして疎結合に機能を拡張できるのはとても魅力的ですね。

Webhook自体の可用性が低いと条件に合致するリソースの操作全体に影響してしまったり、MutatingWebhookConfigurationのほうは「変更を加える」という性質上、複数回呼ばれてしまった場合に備えて処理を冪等にする必要があったり、本番運用の際は慎重になる必要があります。

しかし、HelmやKustomize等を使うのに比べてより根っこに近いところでガバナンスを効かせることができるので、Admission Webhooksは非常に強力な機能だと思います。上手に使って、リリースや運用の負荷を下げていきましょう。

参考資料

*1:執筆時点で最新のMinikubeにはregistry-credsのaddonに不具合があり、Docker Desktopではk8sバージョンがv1.16いなかったため

*2:ServiceのほかにURLによる指定でクラスタ外のWebhookを呼ぶこともできます

potatotips #66 (iOS/Android開発Tips共有会) に登壇してきました

f:id:numatch-jx:20191120000627j:plainこんにちは、Androidエンジニアのぬまっちです。(@nuMatch)
半年ほど前からFlutterエンジニアとしてもデビューしてるので、今はAndroid/Flutter (iOS) エンジニアという感じでしょうか。

あっという間に冬が近づいてきました。 夏にはFlutter布教の為、社内でFlutterモブプログラミング回をしたのが懐かしくなります。 tech.jxpress.net

今回はyappliさんで開催されたpotatotips #66 (iOS/Android開発Tips共有会)にてLTしてきましたので、その紹介をさせて下さい。

potatotips.connpass.com

登壇前

今回、自分がLTで紹介したかったメインテーマは「怖くないよFlutter」でした。
iOS/Android開発におけるTipsがメインの勉強会なので、
「Flutterって最近良く聞くけど、まだ片方のプラットフォームしか触ったことないな」
って方を対象に、Androidエンジニア視点でFlutterに入門したらクロスプラットフォームが身近になった、という紹介をしたいと思いました。

という事でタイトルは
「Android エンジニアが Flutterに入門して驚いたこと3点」
です!

今回、久しぶりの登壇(JXエンジニアになってからは初)という事もあって準備・練習の段階から社内エンジニアに手伝って頂きました。
この場を借りて感謝!です!

f:id:numatch-jx:20191120000627j:plain

LT

実際に登壇で使った資料はこちらになります。

speakerdeck.com

怖くないよ flutter doctor

Flutter入門する方がほぼ必ず叩くだろうコマンド「$ flutter doctor」について紹介しました。

FlutterSDKのインストールが完了し、PATHが通っていれば flutter doctor コマンドを実行することが出来ます。
flutter doctor コマンドは、開発環境がFlutterで開発できるか診断してくれる便利な機能です。

で、試しに叩いてみるとかなり怒られると思います。
AndroidまたはiOSどちらかのプラットフォームのみの開発者なら、Android Studio / Xcodeが片方しか入ってない事も多いかと思いますが、両方揃えないといけません。
特にAndroidエンジニアの方はiOSの開発環境が整っていないと思います。
HomebrewやCocoaPodsも足りないと怒られますが、ちゃんと順番に何をすればいいのか指示してくれるので怖がる必要はありません。

言われた通りにコマンドを叩いていくとFlutter開発の為の環境が作られるはずです。
自分はAndroid StudioでiOSのシミュレータが立ち上がるのを体験した時、軽く感動を覚えました。

入れやすいよ Plugin

自分が声を大にして言いたい、

「FlutterにおけるPluginの導入にしやすさ」

について語らせて頂きました!
何故ここまでピックアップするかというと、理由が大きく2つあります。

  1. 検索のしやすさ

使いたいPluginを探したい時は、まずpub.dev/flutterで検索しましょう。( Flutter packages

LTではAndroidにおける値の永続化の入門であるSharedPreferenceを例にとって紹介しました。

(紹介したPluginはこちらになります↓)

shared_preferences | Flutter Package

検索窓でSharedPreferenceと入力すれば、現在導入可能なSharedPreference用Pluginの一覧が出てくると思います。
似たような名前のPluginが沢山あると思いますが、選ぶポイントとしては

  • 「flutter.dev」の公式であるかどうか
  • 「performance score」が100、または100または100に近いかどうか

が大事かと。

  1. クロスプラットフォームのしやすさ

導入したいPluginが決まったら「pubspec.yaml」に追加記述しましょう。
記述したらflutter pub getコマンドを叩く事をお忘れなく。

基本的にはこれだけです。

PluginによりますがSharedPreference用Pluginではプラットフォーム固有の設定は不要で、導入の準備が完了になります。
AndroidのManifestファイルへの記述や、iOSのPodfileへの書き込みいらずになる事が多いのは大変ありがたいです。
「pubspec.yaml」で両プラットフォーム対応が完結するのはFlutterの大きな強みになると思います。

Flutter と Firebaseの相性の良さ

クロスプラットフォーム入門としてFlutterとFirebaseは抜群の相性を誇ります。
Flutter公式で専用のPluginを用意されていますし、
(他にも各FrebasePackage用のPluginが用意されています。)

ドキュメントは日本語が用意されています。
Flutter アプリに Firebase を追加する  |  Firebase

今回のLTでは時間が足りずに詳細は語れませんでしたが、

  • Firebase Authentication
  • Firebase Cloud Messaging

については後日、JX通信社のアドベントカレンダーにて紹介したいなと思っています!

qiita.com

@nuMatchの担当する回をお待ち頂ければ幸いです!

登壇してみて

振り返ってみると、今回のLTでFlutterのTipsを紹介していたのが自分だけだったのが印象的でした。 また、Twitterで参加者の方の感想で
「聞きやすかった」、「Flutterはまだ入門したことないので、定期的に紹介されるのは嬉しい」
との声を見かけたので、準備した甲斐があったなと嬉しく思いました。

今回はFlutterにおけるクロスプラットフォーム入門についての登壇でしたが、次は実際にプロトアプリ開発で出会った事例を元にした内容でLT出来ればと思います!

開発合宿@箱根湯本

f:id:jxpress:20191008062416j:plain

はじめに

こんにちは!CTOの柳です。今回のブログでは、9月某日に行われたJX通信社恒例の開発合宿について紹介したいと思います。

これまで、JX通信社では年1回のペースで開発合宿を実施してきました。

tech.jxpress.net

www.wantedly.com

合宿のテーマ

毎年、弊社の開発合宿では、さまざまなテーマに取り組んできました。社内環境・ツールの整備、CI/CDの導入、去年はビジネスサイドのメンバーを巻き込んだデータ分析講習会や普段できない業務改善について取り組みました。

今年はCTOである私が幹事を引き受けたので、職権乱用メンバーを説得し、より尖ったテーマに取り組みました。ずばり「rebuild」です!

組織とプロダクトが大きくなっていく中、普段の業務では慣れた技術スタックばかり使うようになってしまいます。世の中に面白い技術が続々と登場しても、中々触れる機会がありません。

そこで、合宿では「新しい技術スタックで、既存プロダクト1つを0から作り直すこと」(= rebuild)にチャレンジしました。

合宿までの準備

実りのある合宿にするために、準備は怠りません。

合宿参加者を開発領域(フロントエンド、バックエンド、アプリ、etc..)ごとにチームを分け、それぞれの領域の技術選定や設計をしてもらいました。

また、定期的に、合宿参加者が全員集まり、

  • 目指すべきMVP(Minimum Viable Product)はどういったものか

  • 合宿中に取り組むPBI(Product Backlog Item)は何か

について話し、意識のすり合わせを行いました。

箱根温泉「ホテルおかだ」

今年は箱根湯本にある「ホテルおかだ」さんにお邪魔しました。宿を決めるにあたり、株式会社Loco Partnersさんと株式会社FiNCさんのブログを参考にしました!この場を借りて御礼申し上げます。

relux 開発合宿 in 箱根湯本 - wootan's diary

FiNC 開発合宿 in 箱根! | FiNC Developers Blog

合宿の様子

f:id:jxpress:20191008062706j:plain

ロマンスカーに乗っていざ出発。

f:id:jxpress:20191008063003j:plain f:id:jxpress:20191008063015j:plain f:id:jxpress:20191008063027j:plain

宿到着。

f:id:jxpress:20191008063158j:plain

到着後、インフラチームから、デプロイ方法について説明を受けました。

弊社では、コード管理にGitLab、デプロイにGitLab CIを、普段は使っていますが、今回はGitHub Teamと、発表されたばかりのGitHub Actionsを使って、CI/CDのパイプラインを組んでみました。

f:id:jxpress:20191008145143j:plain

開発中。

f:id:jxpress:20191008063721j:plain

開発中。

f:id:jxpress:20191008145154j:plain

開発中。

f:id:jxpress:20191008063744j:plain

開発中。

f:id:jxpress:20191008063800j:plain

開発中。

f:id:jxpress:20191008145131j:plain

開発中。

f:id:jxpress:20191008063908j:plain

夕食・朝食は共にバイキング。種類豊富でした。

f:id:jxpress:20191008064133j:plain

ご飯から戻ってまた開発。深夜遅くまで続きました。

f:id:jxpress:20191008064148j:plain

開発中。

f:id:jxpress:20191008064212j:plain

そろそろ限界。

f:id:jxpress:20191008064313j:plain

合宿最終日は会議室を借りて、成果発表会を開きました。

f:id:jxpress:20191008145121j:plain

合宿の成果

最終日ギリギリまで粘ってなんとか、バックエンドからWebフロント・アプリまで疎通でき、3泊4日の日程内にプロダクトのrebuildができました!

WebフロントエンドはTypeScriptを、アプリはFlutterを使い、BFFとしてGraphQLのAPIを作りました。 バックエンドは、普段使ってるAWSではなくGCPを採用し、GCPの各種サービス(GAE、Cloud Functions、Cloud Pub/Sub)やKubernetes / Knativeにチャレンジしました。

まとめ

今回の合宿では、新しい技術にガッツリ取り組めました。また、3泊4日という期限内に、全員で1つのプロダクトの0→1開発に取り組めたことは、チームとして得難い経験になったと思います。

最後に、我々を快く受け入れてくれた「ホテルおかだ」さんに感謝!

www.hotel-okada.co.jp

JX通信社は「PyConJP 2019」にゴールドスポンサーとして協賛しています

VPoE の小笠原です。今年の PyConJP 2019 、JX通信社はゴールドスポンサーとして協賛しています! f:id:yamitzky:20190916100113j:plain

9/16(月)-9/17(火)の2日間、スポンサーブースに出展しています。ブースでは、Python で作った AI ピンポンゲーム大会を開催しています。

参加方法は、GitHub からフォークしてゲーム AI を実装するだけです! 高得点者には素敵な景品もありますので、ぜひ遊びに来てください! f:id:yamitzky:20190916101006j:plain

github.com

キャンペーン参加方法

PyConJP 2019 JX通信社ブースにてどなたでもご参加いただけます.
参加者は右上のボタンからこのレポジトリを Fork してください.
Fork したレポジトリに下記の手順で Pong チームを実装してください.
実装が終わったら弊社ブースにお越しください.
GitHub のアカウント名でエントリーできます.
プログラムでなく人力での参戦も可能です。ゲームの腕に自身がある方はぜひ参戦ください.
キャンペーンの終了は PyConJP のスポンサーブース終了時間に準じます.

こちらのピンポンゲームは社内勉強会で作ったものです。エンジニアブログもあわせてよろしくお願いいたします!

tech.jxpress.net