🦉

Docker+AWS Codebuild+Amazon S3による自動ドキュメンテーション機構

2024/08/22に公開

挨拶

株式会社ジンズのバックエンドエンジニア兼開発環境保全活動家の中島です。
今回は開発環境保全の取り組みとしてCI/CDの一機構、自動ドキュメンテーション機構を実装したためご紹介します。

背景

われわれJINS、日本国内だけでも既に約500店舗出店しているのですが、さらに海外にも積極的に店舗を展開していっています。これに加えてECサイトの強化・トランザクション管理システムの見直しなど陰に陽にシステムの改善を進めています。

こういった小売業の展開にあたって重視されるのがITシステムの「アジリティ」、すなわち "改善・横展開をスピーディーに行えること"。これが事業を拡大するにあたり重要な要素となります。これを踏まえて現在の社内システムの構築にあたっては マイクロサービスアーキテクチャ を積極的に採用しています。

マイクロサービスアーキテクチャとは、システムを"多数の独立したサービスの連携"で実現する設計方式です。機能ごとに独立して開発できるため、モノリシックなシステムに比べ部分的な改善・拡張が容易に実現できるというメリットがあります。

一方で、当然ながら 機能間の連携が開発者内で適切に共有されている必要があります。お隣のシステムに「どう問い合わせれば」「どういった応答を返してくれるのか」を正しく把握できていない中で開発を進めると、マイクロサービスアーキテクチャはバグの温床になるばかりです。

この情報共有が難しい。

今回はここの改善を狙い

  • マイクロサービスの機能仕様のドキュメントが
  • 常に最新の状態に更新され
  • 関係する開発者全員がアクセスできる

状態を常に維持するべく、自動ドキュメンテーションの機構を構築しました。

構成図

構成図

JINSのバックエンドで動くシステムのCI/CDは
bitbucketにリポジトリをpush → AmazonECSとしてサービス立ち上げ
が比較的一般的な構成となっています。
今回はここにドキュメンテーションのためのフロー(赤枠)を追加します。

自動ドキュメンテーションにあたってはタイトルの通り下記3つのツール・サービスをコアとしています。

  • Docker:サービスの定義に追加してドキュメント生成ステージを定義
  • AWS Codebuild:サービスのビルドとともにドキュメントを生成
  • Amazon S3:静的ホスティングを利用しドキュメントを公開

実装

Docker

DockerFileではマルチステージビルドを利用しドキュメント生成用のdocステージを構成しておきます。
つまり、アプリケーションサイドとインフラサイドとの間に docステージの/docsディレクトリにドキュメントが生成される」というお約束 のみを設け、開発上での結合を疎にします。
設計としてシステム構成のみならず開発プロセスの面でもマイクロサービスアーキテクチャ的疎結合の思想に倣います。素敵。

FROM node:latest as doc
...
RUN ["bash", "-c", "<ドキュメント自動生成コマンド> --output=/docs/index.html"]

ドキュメントの自動生成コマンドはredocなりdoxygenなりsphinxなり何でも…index.htmlが生成されればokです。

今回はインフラ開発者(私)とアプリケーション開発者(ベンダーさん)でそれぞれ別々に開発を進めましたが、この切り分けが予想以上に上手く機能して満足。

AWS Codebuild

JINSではTerraformを利用したIaCを導入しています。
今回のAWS側のインフラ定義もすべてTerraformで実装します。

AWS Codebuild側ではDockerFileの定義に従ってシステムをビルドするのに加え、コンテナ内で生成されたドキュメントをS3に渡す処理を実装します。

resource "aws_codebuild_project" "main" {
  name      = "codebuild_project_name"
  buildspec = templatefile("build_spec.tpl", {})
  artifacts {
    type                = "S3"
    location            = data.aws_s3_bucket.documents.bucket
    encryption_disabled = true
  }
...
}

aws_codebuild_projectリソース内でartifactsを定義し、locationとしてAmazonS3バケットを指定します。
これに合わせてcodebuildのビルド仕様build_spec.ymlに下記を記載します。

version: 0.2

phases:
  ...
  build:
    commands:
      ...
      - docker build --target doc -t documentation-image .              # ドキュメント用ステージをターゲットにビルド
      - docker cp $(docker create documentation-image):/docs documents  # 生成されるファイルをdocumentsディレクトリにコピー
      ...
  ...

artifacts:
  files:
    - "**/*"
  base-directory: documents

これにより、docステージでビルドされるコンテナ内で/docsに生成される全てのファイルは指定したS3バケット内の<codebuild_project_name>ディレクトリ以下にコピーされます。

AmazonS3

ドキュメントを配置するAmazonS3バケットを作成します。
開発者限定でアクセスできるようにしたいため、バケットへのパブリックアクセスは制限しAmazonCloudFront経由でのアクセスのみ許可します。
今回は割愛しますがAmazonCloudFrontでは社内のVPNからのみアクセスできるような制限を設けています。

// S3バケット定義
resource "aws_s3_bucket" "documents" {
  bucket = "document_bucket_name"
}

// 静的ホスティングの定義
resource "aws_s3_bucket_website_configuration" "documents" {
  bucket = aws_s3_bucket.documents.bucket
  index_document {
    suffix = "index.html"
  }
}

// パブリックアクセスのブロック
resource "aws_s3_bucket_public_access_block" "documents" {
  bucket                  = aws_s3_bucket.documents.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

// CloudFront経由のアクセスのみ許可するポリシー定義
data "aws_iam_policy_document" "documents" {
  statement {
    effect    = "Allow"
    principals {
      type        = "Service"
      identifiers = ["cloudfront.amazonaws.com"]
    }
    actions   = ["s3:GetObject"]
    resources = ["${aws_s3_bucket.documents.arn}/*"]
    condition {
      test        = "StringEquals"
      variable    = "aws:SourceArn"
      values      = [aws_cloudfront_distribution.documents.arn]
    }
  }
}

// バケットとポリシーの紐づけ
resource "aws_s3_bucket_policy" "documents" {
  bucket = aws_s3_bucket.documents.id
  policy = data.aws_iam_policy_document.documents.json
}

// アクセス制限+https通信のためのCloudFront定義
resource "aws_cloudfront_distribution" "documents" {
  origin {
    domain_name = aws_s3_bucket.documents.bucket_regional_domain_name
    origin_id   = aws_s3_bucket.documents.id
    ...
  }
  default_root_object = "index.html"
  ...
}

上記を一式Terraformでapplyすることで<CloudFrontのURL>/<codebuild_project_name>/index.htmlに常に最新のドキュメントが反映されるようになります。

まとめの所感

アジャイル寄りの開発におけるドキュメント保守運用は人間の仕事ではないので徹底的に自動化しましょう。

最後に

我々株式会社ジンズITデジタル部、社内では比較的若い部署ではありますが、少しずつ需要も規模も拡大していっています。そのため新たなエンジニアを随時募集しています。

  • 大局を見ながらプロジェクトを運営してビジネスを動かしたい!という方
  • 実際に動くシステムをバリバリ実装しながらベンダーさんと殴り合いたい!という方

どちらの志向でも活躍できる土壌があります。
興味ある方はぜひ、下記募集ページをご覧ください!
https://hrmos.co/pages/jins/jobs/1829091030515531842

JINSテックブログ

Discussion