オイシックス_ら_大地におけるマイクロサービス高速開発に向けた取り組み.pdf

0d1076d3a26279d1a2fbc2d02d140210?s=47 川上徹
February 21, 2019

 オイシックス_ら_大地におけるマイクロサービス高速開発に向けた取り組み.pdf

0d1076d3a26279d1a2fbc2d02d140210?s=128

川上徹

February 21, 2019
Tweet

Transcript

  1. オイシックス・ラ・大地株式会社 川上徹 オイシックス・ら・大地における マイクロサービス高速開発 に向けた取り組み

  2. 1. 自己紹介 & 会社紹介 2. ECサイトのマイクロサービス化を行う背景 3. システム・アーキテクチャ概観 4. 開発環境構成

    5. 開発高速化へ向けた取り組み事例 6. Azure活用事例 7. エンジニア募集中! アジェンダ
  3. 川上 徹(かわかみ とおる) 自己紹介 金融系SIerにてエンジニア オイラ大地入社 システム本部 システム基盤部 基盤刷新セクション シニアエンジニア?

    ECサイトのマイクロサービス化推進担当 ・2006年〜 ・2018年10月
  4. 会社紹介

  5. None
  6. 今日はこのブランド のお話になります

  7. Oisixは2000年創業の 生鮮食品のネットショップ Oisixのサービス紹介

  8. 18年間サービス規模を拡大中 近年は年23.5%のペースで会員が増加 Oisixのサービス紹介

  9. Oisixのサービス紹介 定期宅配 おいしっくすくらぶ 毎週木曜日に カートを準備 その週の締切日まで サイト上で 自由にお買い物 カートの中身を 何度でも変更可能

    キャンセル自由 自社配送センターから 出荷 お届け日に ヤマト便で ご自宅にお届け
  10. 数人分の下ごしらえされた 食材・調味料・レシピのセット商品 近年は従来の生鮮食品単品ごとの販売から 「プレミアム時短」をキーワードにシフト より楽しい食生活をサポートする新商品や サービスの開発に注力 Kit Oisix (ミールキット) Oisixのサービス紹介

  11. 1. 自己紹介 & 会社紹介 2. ECサイトのマイクロサービス化を行う背景 3. システム・アーキテクチャ概観 4. 開発環境構成

    5. 開発高速化へ向けた取り組み事例 6. Azure活用事例 7. エンジニア募集中!
  12. Java web app since 2000 ECサイトのマイクロサービス化を行う背景 ・OisixのECサイトは2000年の  創業当時からJavaで作成 ・ソース上のコメントで確認できる  最古の日付は2000年6月1日

  13. 国内某 Cloud Data Center On-Premise ECサイトのマイクロサービス化を行う背景 Oracle RAC • 全てを司る神Oracle

    通称:Core DB • Monolithic Application • Stateful Application Server • Session Sticky ・ ・ Not Scalable & Low Testability
  14. ECサイトのマイクロサービス化を行う背景 ビジネスのスケールに対してシステムが ボトルネックになるわけにはいかない! ビジネスのスピードに対してシステムが 遅れを取るわけにはいかない! Microsoftさん、協力してくれるってよ

  15. 1. 自己紹介 & 会社紹介 2. ECサイトのマイクロサービス化を行う背景 3. システム・アーキテクチャ概観 4. 開発環境構成

    5. 開発高速化へ向けた取り組み事例 6. Azure活用事例 7. エンジニア募集中!
  16. マイクロサービス化へ向けたアプローチ ストラングラーパターン 機能の特定の部分を新しいアプリケーションやサービスに徐々に置き 換えることで、レガシーシステムを段階的に移行します。 レガシーシステムからの機能が置き換えられていくと、新しいシステム は最終的に古いシステムの機能すべてを置き換え、古いシステムを 抑圧して使用停止できるようにします。 https://docs.microsoft.com/ja-jp/azure/architecture/patterns/strangler より引用

  17. Existing EC Site システム・アーキテクチャ概観 ・ ・ CDN Azure API Management

    Azure Kubernetes Service Migrate Functions
  18. Azure API Management Azureが提供するフルマネージドのAPI ゲートウェイサービス • API呼び出しのバックエンドへのルーティング • JWT検証/証明書検証などの資格情報の検証 •

    流量制御 • リクエスト/レスポンスの変換 • 分析データの取得 参考:Azure API Management の概要と主な概念 https://docs.microsoft.com/ja-jp/azure/api-management/api-management-key-concepts
  19. Azure Kubernetes Service ▪Azure Kubernetes Service Azure が提供するフルマネージドの Kubernetesコンテナーオーケストレーションサービス ▪Kubernetes

    コンテナ化したアプリケーションのデプロイ/スケーリング および管理を行うためのオープンソースの コンテナオーケストレーションシステム 参考:Azure Kubernetes Service の概要 https://docs.microsoft.com/ja-jp/azure/aks/intro-kubernetes
  20. Spring Boot デファクトになりつつあるJavaフレームワーク Spring Bootを使用すると、スタンドアロンで実稼動グレードのSpringベー スのアプリケーションを簡単に実行できます (Google翻訳) • IoCコンテナ(DIコンテナ) •

    AOP を中心に高機能なJavaフレームワークである Spring Frameworkの機能を効率的に利用できるようにしたもの 参考:Spring Boot https://spring.io/projects/spring-boot
  21. 1. 自己紹介 & 会社紹介 2. ECサイトのマイクロサービス化を行う背景 3. システム・アーキテクチャ概観 4. 開発環境構成

    5. 開発高速化へ向けた取り組み事例 6. Azure活用事例 7. エンジニア募集中!
  22. 開発環境構成 ハイスペックMac使えます! ※Java/Kotlin開発者の話&ローカル環境は個人差があります ※Windowsは素晴らしいOSでVSCodeは素晴らしいIDE/エディタです ※Azure DevOps最高!! コードリポジトリ GitHub コンテナリポジトリ DockerHub

    パッケージ管理 Azure Artifacts CIツール CircleCI 2.0 ビルドツール Gradle IDE IntelliJ IDEA
  23. 1. 自己紹介 & 会社紹介 2. ECサイトのマイクロサービス化を行う背景 3. システム・アーキテクチャ概観 4. 開発環境構成

    5. 開発高速化へ向けた取り組み事例 6. Azure活用事例 7. エンジニア募集中!
  24. マイクロサービス(REST API)の大まかな開発プロセス Checkout (Clone & Build) Interface Design Coding/Testing Containerize

    Deploy Publish
  25. Checkout Checkout (Clone & Build) Interface Design Coding/Testing Containerize Deploy

    Publish
  26. Clone & Build

  27. ERROR!! 

  28. Build(Test)するには外部リソースが必要 • マネージドサービスをテスト用に立てる 「◦◦◦スキーマを使って今からJUnit流しまーす」 • インメモリDB(H2 etc)を使う 「細かい仕様が違う」 「外部リソースはDBだけじゃない」 •

    Dockerを使う 「docker pull docker pull docker pull」 「docker run docker run docker run」
  29. やってくれるライブラリがある Testcontainers(https://www.testcontainers.org) テストコードからDockerコンテナを起動出来るライブラリ テスト実行時にイメージのpull/run/stopを自動的に行える

  30. Spring BootでRedisを使う場合 public class ContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public

    void initialize(ConfigurableApplicationContext applicationContext) { GenericContainer redisContainer = new GenericContainer("redis:5.0.2").withExposedPorts(6379); redisContainer.start(); String containerIpAddress = redisContainer.getContainerIpAddress(); Integer mappedPort = redisContainer.getMappedPort(6379); TestPropertyValues values = TestPropertyValues.of("spring.redis.host=" + containerIpAddress, "spring.redis.port=" + mappedPort); values.applyTo(applicationContext); } } ApplicationContextInitializerの実装 @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ContextConfiguration(initializers = ContextInitializer.class) public class SampleControllerTest { // … } テストクラスでの利用
  31. Mockの活用 Dockerイメージでリソースを用意できない場合はMock化 • Storage キュー/Service Bus キュー • Blob Storage

    • 他サービスの呼び出し Spring BootではIoCコンテナ内の特定のインスタンスを Mock化することも容易 Mock化を考慮したクラス設計を @MockBean private AzureGetQueueService azureGetQueueService;
  32. Clone & Build

  33. SUCCESS!! 

  34. テストが重くなる/人権のあるPCを CircleCIのExecutor TypeをMachineに 野良Dockerイメージに注意 https://www.bleepingcomputer.com/news/security/runc-vulnerability-gives-attackers-root-access-on-docker-kubernetes-hosts/

  35. Interface Design Checkout (Clone & Build) Interface Design Coding/Testing Containerize

    Deploy Publish
  36. Swagger API定義の定番 Swagger(OpenAPI) API仕様を管理するOSSフレームワーク Swagger Specと呼ばれるYAML/JSONでREST APIの仕様を記述できる • Swagger Specを記述するためのSwagger

    Editor • プログラムコードを生成するSwagger Codegen • HTMLベースのAPI仕様書としてSwagger UI などのオープンソースのツール群として普及している 参考:swagger.io https://swagger.io/
  37. Swaggerを使った開発プロセス アプリケーションコードにSwagger Specを含める • IntelliJ Swagger Plugin https://plugins.jetbrains.com/plugin/8347-swagger IDE上でSwagger Specを記述する際のコード補完

    • gradle-swagger-generator-plugin https://github.com/int128/gradle-swagger-generator-plugin Swagger Codegen/Swagger UIのGradleタスク
  38. Swagger Codegen Swagger Specからエンドポイントとクライアントを生成 サーバ側コードとクライアントコードを同一リポジトリに client-projectからserver-projectのファイルを参照 client-project/build.gradle root ├ server-project

    │ └ swagger/v1-swagger-spec.yaml └ client-project inputFile = file('../server-project/swagger/v1-swagger-spec.yaml')
  39. Swagger UI CircleCIでのテスト時にSwagger UIを生成 .circleci/config.yml ビルドのArtifactsとしてSwagger UIを保存 GithubのPull Requestに紐付いて参照可能 -

    run: name: SwaggerUI command: ./gradlew :server-project:generateSwaggerUI - store_artifacts: path: ./server-project/build/swagger-ui-v1-server-project destination: swagger-ui-v1-server-project
  40. Swaggerを使った開発プロセス Write Swagger Spec Generate Swagger Code (Spring MVC Controller

    Interface) (RestTemplate Client) (And More) Write Business Logic Generate Swagger UI Code Review Publish Client Libraly Swagger UI
  41. Code Review with Swagger UI Typesafe Client Import to API

    Management
  42. Spring WebFlux/WebClient・・・? Kotlin・・・? OpenAPI Generator? https://github.com/OpenAPITools/openapi-generator

  43. Coding/Testing Checkout (Clone & Build) Interface Design Coding/Testing Containerize Deploy

    Publish
  44. Spring Boot Auto-configuration Spring Bootの主要機能「Auto-configuration」 Starterへの依存関係を追加するだけで諸々設定が行われる https://github.com/spring-projects/spring-boot/tree/master/spring-boot-project/spring-boot-starters • spring-boot-starter-web •

    spring-boot-starter-data-jdbc • spring-boot-starter-data-jpa • spring-boot-starter-security • spring-boot-starter-data-redis Starterは作れる! https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html
  45. Starterは作れる! @Configuration @ConditionalOnClass({AdjustDateGetter.class, AdjustDateFilter.class}) public class AdjustDateWebConfiguration { @Bean @ConditionalOnProperty(prefix

    = "oisix.common.web", value = "adjust-date", havingValue = "true", matchIfMissing = false) public FilterRegistrationBean businessDatetimeFilter() { FilterRegistrationBean bean = new FilterRegistrationBean(new AdjustDateFilter()); bean.addUrlPatterns("/*"); bean.setOrder(Integer.MAX_VALUE); return bean; } } Configuration Class org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ jp.co.oisixradaichi.oisixbootautoconfigure.web.AdjustDateWebConfiguration META-INF/spring.factories
  46. 共通的な実装はStarter化する • サービス間を跨いで共通的に利用する実装はStarter化 ◦ 利用するサービス側の実装負担を最小限に ◦ 機能の有効/無効化条件には注意を • そもそもの共通化効果を検討 ◦

    サービス毎に適切な実装があるはず ◦ ビジネス要件と照らし合わせて
  47. Containerize & Deploy Checkout (Clone & Build) Interface Design Coding/Testing

    Containerize Deploy Publish
  48. CI/CD flow Application Development Test & Code Review Build &

    Push Image Create k8s Manifest Code Review Apply Manifest Different Repository Image Tag with Commit Hash
  49. Spring Profile & Config Map Application Development Test & Code

    Review Build & Push Image Create k8s Manifest Code Review Apply Manifest Different Repository Image Tag with Commit Hash
  50. Spring Profile & Config Map ビルドしたコンテナイメージはどの環境でも動く必要がある • Spring Profile 環境変数やVMオプションなどでプロパティ等を切り替える仕組み

    • 環境変数の値を参照可能 プロダクトコードの環境差分値は環境変数で吸収 テスト時の設定等をSpring Profileで解決 application.yml application-production.yml application-staging.yml application-development.yml spring: redis: host: ${REDIS_HOST} port: ${REDIS_PORT} password: ${REDIS_PASSWORD}
  51. Spring Profile & Config Map • Config Map kubernetesで設定値を扱うためのリソース apiVersion:

    v1 kind: ConfigMap metadata: name: sample-service-config namespace: ns1 data: SPRING_PROFILES_ACTIVE: "production" apiVersion: apps/v1 kind: Deployment metadata: name: sample-service namespace: ns1 spec: template: spec: containers: - name: sample-service env: - name: SPRING_PROFILES_ACTIVE valueFrom: configMapKeyRef: name: sample-service-config key: SPRING_PROFILES_ACTIVE
  52. kustomize Application Development Test & Code Review Build & Push

    Image Create k8s Manifest Code Review Apply Manifest Different Repository Image Tag with Commit Hash
  53. kustomize 環境への反映はkubectl applyで行うが・・・ 各環境用のkubernetes manifestをフルセットで管理するのは面倒 kustomizeを使って基本設定 + 環境差分値に分割して管理 ├ base

    │ ├ kustomization.yaml │ ├ config.yaml │ ├ deployment.yaml │ ├ secret.yaml │ └ service.yaml ├ production-ns1 │ ├ kustomization.yaml │ ├ config.yaml │ ├ deployment.yaml │ ├ secret.yaml │ └ service.yaml # omit
  54. kustomize base/kustomization.yaml production-ns1/kustomization.yaml bases: - ../base patches: - config.yaml -

    secret.yaml - deployment.yaml - service.yaml resources: - config.yaml - secret.yaml - deployment.yaml - service.yaml
  55. kustomize base/config.yaml production-ns1/config.yaml kustomize build production apiVersion: v1 kind: ConfigMap

    metadata: name: sample-service-config namespace: ns1 data: SPRING_PROFILES_ACTIVE: "production" apiVersion: v1 kind: ConfigMap metadata: name: sample-service-config namespace: default data: SPRING_PROFILES_ACTIVE: "default" apiVersion: v1 kind: ConfigMap metadata: name: sample-service-config namespace: ns1 data: SPRING_PROFILES_ACTIVE: "production"
  56. configMapGenerator@kusotmize configmapリソースのみの更新の場合、podの再起動は行われない configmapの更新がSpring Bootアプリケーションに伝播しない configmapリソースのみの更新時にpodの再起動を行わせるには? configMapGeneratorを利用して deploymentリソースも更新する!

  57. configMapGenerator production-ns1/kustomization.yaml base/deployment.yaml namespace: ns1 bases: - ../base patches: -

    deployment.yaml - service.yaml configMapGenerator: - name: sample-service-config literals: - SPRING_PROFILES_ACTIVE=production env: - name: SPRING_PROFILES_ACTIVE valueFrom: configMapKeyRef: name: sample-service-config key: SPRING_PROFILES_ACTIVE
  58. kustomize kustomize build production ハッシュ値付きのconfigmapが生成される applyするとpod再起動がかかる! apiVersion: v1 kind: ConfigMap

    metadata: name: sample-service-config-tb9f7t5gh8 namespace: ns1 data: SPRING_PROFILES_ACTIVE: "production" --- apiVersion: v1 kind: Deployment # 省略 env: - name: SPRING_PROFILES_ACTIVE valueFrom: configMapKeyRef: name: sample-service-config-tb9f7t5gh8 key: SPRING_PROFILES_ACTIVE
  59. kustomize & kubectl on CircleCI CircleCIではビルド実行環境にDockerイメージを指定できる azule-cliのDockerイメージが配布されている https://hub.docker.com/r/microsoft/azure-cli/ kustomize &

    kubectlできるDockerイメージを作ろう! コンテナ上でサービスプリンシパル認証ログインすればOK FROM microsoft/azure-cli:latest RUN az aks install-cli && \ curl -sSL -o /usr/bin/kustomize $(curl -s https://api.github.com/repos/kubernetes-sigs/kustomize/releases/latest | \ jq -r '.assets[] | select(.name | test("linux_amd64")) | .browser_download_url') && \ chmod u+x /usr/bin/kustomize
  60. kustomize & kubectl on CircleCI CircleCIからのデプロイ対象判定 ▪前提 • 本番/ステージング/開発で別のAKSクラスタを構築 •

    ネームスペースでサイトを分離(Oisix以外のサイト/ブランドも有り) • リソースグループはクラスタと同名 ▪デプロイ対象判定の仕組み Githubのbranch名からデプロイ対象クラスタを判定 • production-ns1 -> productionクラスタのns1ネームスペース • develop-ns2 -> developクラスタのns2ネームスペース CircleCIの環境変数${CIRCLE_BRANCH}からクラスタ&ネームスペースを取得
  61. オペレーションイメージ master feature development-ns1 production-ns1 development-ns2 production-ns2 kustomize build →

    kubectl apply kustomize build HOLD kubectl apply ns1 ns1 ns2 ns2 development production PR PR
  62. Gitops(Operations by Pull Request) の考え方は踏襲できている気がする Fluxとかどうなのだろう

  63. Publish Checkout (Clone & Build) Interface Design Coding/Testing Containerize Deploy

    Publish
  64. Azure Artifacts ManagedなPrivate Maven Repositoryは意外と無い Sonatype Nexusを自前で立てるのも管理が辛い そこで Azure Artifacts

    Managed Maven Repository!!
  65. GradleからAzure ArtifactsにMaven Publish Maven Publish Pluginを使ってPublish build.gradle CircleCIで指定ブランチ時のみpublishMavenJavaPublicationToMavenRepositoryタスクを 実行させればOK apply

    plugin: 'maven-publish' publishing { publications { mavenJava(MavenPublication) { from components.java } } repositories { maven { url 'https://pkgs.dev.azure.com/xxxxxxxx/_packaging/xxxxxxxx/maven/v1' credentials { username "AZURE_ARTIFACTS" password ${AUTH_TOKEN} } } } }
  66. TOKENの期限はデフォルト90日/最長1年 更新を忘れそう

  67. 色々工夫してきたが・・・ アプリケーションコードの初期設定が煩雑 .circleci/config.yml build.gradle swagger spec setting.gradle testcontainers.properties build.sh kustomize

    .gitignore
  68. Lazybonesによるプロジェクトテンプレート 新規サービス開発時にスムーズに開始したい! MavenにはarchetypeがあるがGradleを使いたい! Spring Initializerだけでは不十分! ならば設定済みの雛形をコピーしてリネームしてもらう・・・ FIXME!! FIXME!! FIXME!! FIXME!!

    FIXME!! そこで Lazybones https://github.com/pledbrook/lazybones ERROR!! 
  69. Lazybonesによるプロジェクトテンプレート アーカイブの展開 & post-install scriptによるテンプレーティング Groovyによるスクリプトで柔軟な処理が記述可能 lazybones.groovy テンプレートエンジンによる置換処理/必要に応じてファイルのリネーム等を行う 参考:LINE Engineering

    https://engineering.linecorp.com/ja/blog/lazybones-project-generation-tool-for-micro-service/ @Grab(group="uk.co.cacoethes", module="groovy-handlebars-engine", version="0.2") import uk.co.cacoethes.handlebars.HandlebarsTemplateEngine registerDefaultEngine new groovy.text.SimpleTemplateEngine() String serviceName = ask("Input value for Service Name with kebab-case [default: blank-service]: ", "blank-service", "serviceName") def props = [:] props.serviceName = serviceName processTemplates "**/*.yaml", props processTemplates "**/*.gradle", props …
  70. 「今日から新サービスの開発を始める!」

  71. ??「40秒で支度しな!!」

  72. Yes ma'am!! 

  73. テンプレートのメンテナンスは必要 開発が始まったものは個別メンテ

  74. まとめ 開発を高速化するためには・・・ • ツール系OSSの積極利用 ◦ IDE/ビルドツール Plugin ◦ テスト系ライブラリ •

    マネージド・サービスの活用 ◦ CIツール ◦ Microsoft Azure!! • 開発者の負担を減らす仕組みづくり ◦ Starter ◦ プロジェクトテンプレート 開発プロセスにおけるボトルネックはどこか? それを改善できるツール/サービスを探そう!
  75. 1. 自己紹介 & 会社紹介 2. ECサイトのマイクロサービス化を行う背景 3. システム・アーキテクチャ概観 4. 開発環境構成

    5. 開発高速化へ向けた取り組み事例 6. Azure活用事例 7. エンジニア募集中!
  76. Existing EC Site APIの認証認可 ・ ・ CDN Azure API Management

    Azure Kubernetes Service
  77. JWT検証による認可制御 既存のECサイトにてJWT(Json Web Token)を払い出してAPIで検証 • API Managementのvalidate-jwtポリシーの利用を検討 ◦ 署名アルゴリズムにRS256を使用する場合、キーを Open

    ID 構成エンドポイントを介して指定する必要がある (≒ OpenID Connectのお作法に従う) ▪ APIの外部公開をしたいわけではない ▪ 大きなコストをかけられない ◦ ECサイトがStatefulすぎる ▪ scopeだけではAPI実行可否が判定できない JWT検証サービスを独自実装!
  78. APIの認証認可 Azure API Management Azure Kubernetes Service Inbound processing send-request

    Policy Backend JWT Validation & User State Check Sync User State
  79. API Managementリソースを コードベースで管理するには? https://docs.microsoft.com/ja-jp/azure/api-management/api-management-configuration-repository-git

  80. 1. 自己紹介 & 会社紹介 2. ECサイトのマイクロサービス化を行う背景 3. システム・アーキテクチャ概観 4. 開発環境構成

    5. 開発高速化へ向けた取り組み事例 6. Azure活用事例 7. エンジニア募集中!
  81. エンジニアリングで 食卓と畑を変える 仲間を募集中 エンジニア募集中!!