nokoのブログ

こちらは暫定のメモ置き場ですので悪しからず

Webアプリケーションのアーキテクチャについて考えたこと

はじめに

  • 自分でちょっとしたWebアプリケーションを開発するときに、構成をどうするか悩んだときのメモです。

考えたこと

Webアプリケーションフレームワーク

比較

f:id:noko_htn:20201022023948p:plain

Django

  • Djangoディレクトリ構成
    • cookie cutterを踏襲したいが、ファイルが増え過ぎるので、参考にしつつ自分で作るのが良いかも
    • DjangoのMTV
■モデル
・データベースと連携を取るところ
■ビュー
・他のフレームワークでいうコントローラ
・モデルで引っ張ってきた情報をどのようにみせるか(ロジックで加工する)
■テンプレート
・HTMLファイルとか。ダイレクトにスクリーンに反映されるコード
・モデルから直接情報を持ってこないこと

FastAPI

  • FastAPIの良いところ

その他

  • マイクロサービス化も考慮したい

インフラ構成

AWS or GCP

  • Google BigQuery使うならGCP、というケースが多い?
  • 逆にElastic Search使うならAWS?

AWS

  • AWS Elastic beanstalkからAmazon EKSへ移行する
    • AWS Elastic beanstalkの辛い点
    • CI/CDをどのように構築するか
  • lambda
    • ライブラリが必要で、ってなっていくと、意外と容量制限が早くてだんだん辛くなってコンテナにしたくなる
    • NFSはエンドポイントとかちょっとめんどくさい(LambdaもVPCだし)
    • zipを開いたら、直下のディレクトリに展開される
    • layerは、optにできる
    • パスが通っているところに置きたかったら、解凍したらpython配下になるようにする

GCP

ネットワークを復習してみた

はじめに

  • ネットワークを復習してみたときのメモです

参考

ネットワーク

1. Webブラウザがリクエストを作る

  • リクエストメッセージ
    • リクエストライン + ヘッダー + ボディ
      • リクエストライン: 要求方法 + 要求するURL
      • ヘッダー: Hostヘッダー、ブラウザの種類、Cookieヘッダーなど
      • ボディ: Postメソッドのとき。ヘッダーとボディの間は空行で区切る。クライアントからWebサーバに向けて送信するデータ。
  • リクエストメッセージ: 何を + どうして 欲しいのか
    • 何を = URI
    • どうして = メソッド
  • ドメイン名からIPアドレスを調べるとき、ブラウザはソケットライブラリのリゾルバを利用する
  • 1つのドメインの情報を分割して複数のサーバに登録することはできない。逆に、1台のDNSサーバに複数のドメインの情報を登録することはできる
  • DNSでは、ドメインの階層を上から辿っていく
    • まずルートドメイン
    • インターネットに存在するDNS全てに、ルートドメインDNSサーバを登録する
    • "com", "net" はVerisignというところが管理している
  • DNSサーバは一度調べた名前をキャッシュに登録しておく機能がある

2. TCP/IPのデータを電気信号にして送る

  • イーサネットのパケット - MACヘッダー = IPヘッダー
  • TCP
    • 特徴
      • シーケンスナンバーによる順序制御
      • データの再送
      • データの重複除去
  • UDP
  • コネクションとセッションの違い
    • コネクション: トランスポート層TCPコネクション。セッションでデータ転送を行うための論理的な回線
    • セッション: アプリケーション層、セッション層。通信の開始から終了までを管理する
  • ソケットライブラリ

3. LANを通っていく

  • ネットワークは集約と切り替え(スイッチング)が大切
  • LANアダプタのPHY(MAU)回路で電気信号に変えられる
  • スイッチ
    • L2: スイッチングハブなど
      • 同じネットワークアドレスに所属する機器を集約する
      • イーサネットフレームという単位で通過が行われる
      • フレームの送信元のMACアドレス送信先MACアドレスの情報から転送する
      • MACアドレステーブルにのっていなければ、全ての物理ポートにフレームを送信する(フラッティング)
    • L3: ルータ機能のついたL2スイッチ
      • IPパケットを扱う
      • ルーティングテーブルで、ネットワークごとに転送させるべきゲートウェイIPアドレスと物理ポートの番号が記されている
      • 明記されていなければ、デフォルトゲートウェイ
      • ルーティングの交換は、手動だったり自動で他のルータとやり取りしたり
        • OSPF: RIPと異なり、ホップ数だけでなく、帯域幅も考慮して計算
    • L4/7: ロードバランサなど
  • ルータ = 中継部分 + ポート部分
    • 中継部分: パケットの中継先の判断を担当
    • ポート部分: パケット送受信の動作を担当
  • VLAN
    • スイッチ内を論理的に分割する機能
  • ポートVLAN
    • 1台のスイッチの物理ポートごとにVLAN番号を設定する
    • VLANを跨いで通信する場合は他のL3スイッチやルータを経由
  • タグVLAN
    • 複数台のスイッチにまたがってVLANを構築する
    • トランクポートを通じて他のスイッチと連結する
    • イーサネットフレームに4バイトのタグを付与
    • トランクポートを通る度にタグの付け外しを行う

4. プロバイダからインターネット内へ入っていく

  • インターネット = 家庭や会社のネットワークの規模が大きくなったもの
    • 例えば、インターネット内のルータには経路情報が10万件登録されていたり
  • PPPoE
    • PPPをイーサネット上で実現
      • 通信要求
      • 接続する資格を持ったユーザか確認
      • 通信条件について相談
      • 通信を開始
  • アクセス回線: プロバイダに接続する部分の回線
  • BAS(ブロードバンドアクセスサーバ)と普通のルータの違い
    • 本人確認機能、IPアドレスなどの設定情報をクライアントに通知する
  • IX(Internet eXchange): プロバイダ同士を一堂に集めて相互に接続する設備(実態は高性能なスイッチングハブ
  • ネットワーク機器の監視は、主にdiscard(パケットロス)とunbind(インターフェイスが外れた状態)
  • 切り分けコマンド
    • ping, traceroute, telnet, nslookup, dig
    • lsof: IPv6だけに見えても、IPv4互換アドレスで接続しても応答を返す
  • AWS)Internet Gateway = 自分のネットワークにインターネット回線を引き込むイメージ
  • EGP
    • AS番号をやり取りして、「どのネットワークの先に、どのネットワークが接続されているか」を大まかにやり取りする
    • 細かいところはIGPで

5. Webサーバにたどり着く

  • ファイアウォール
    • 今はパケットフィルタリング型が最も普及している
    • 動的ポートフィルタリング
      • 戻りの通信を考えないための機能
  • キャッシュサーバ
    • LBと同じように、Webサーバの代わりにDNSサーバに登録する。→キャッシュサーバにHTTPリクエストが届くようになる
    • コンテンツ配信サービスプロバイダは、主要なプロバイダと契約して、そこに多数のキャッシュサーバを設置する
  • SSLオフロード
    • 本来、Webサーバで行うSSL通信の処理をロードバランサ側に集約して行う機能
    • SSL処理はCPU負荷がかかる
    • ロードバランサはロードバランサで高価なので、他のシステムとの兼ね合いとかで、サーバ側でやるかとかは決める

6. レスポンスが作られ、Webブラウザに戻っていく

  • 1のリクエストメッセージ通り、WebサーバはURIで指定されたアプリケーションプログラムにリクエストメッセージ中にあるデータを渡して、処理が走る(レスポンスが作られる)
  • レスポンス
    • ステータスライン + ヘッダー + ボディ
    • Content-Typeヘッダー
      • ボディ部のコンテンツの種類を示す

Terraformのディレクトリ構成について考えたこと

前提

  • 小規模というほどではないが決して大規模ではない環境を組むときにちょうどいいTerraformのディレクトリ構成について悩んだときのメモです。
    • 環境ごとにVPCとかのネットワークを切って、それぞれにEC2(ECS)+RDS+S3とかとかを数台ずつ乗せていくくらい。

観点

  • [効率]環境ごとの差異を比較しやすいこと
    • dev→stg→prodとデプロイしていくとして、差分反映・確認がしやすいこと
  • [安定]安全にデプロイしやすいこと
    • 変更頻度や影響範囲を考慮

ディレクトリ構成

  • はじめに結論
├── env
│   └── stg01
│       |── network
│       |   └── main.tf
│       └── service-aaa
│           └── main.tf
└── modules
    └── vpc
        └── main.tf

ファイル例

  • env/network/stg01
#----------
# Terraform
#----------
terraform {
  required_version = "0.13.2"
  backend "s3" {
    bucket  = "system-stg01-deploy-tf-999999999"
    region  = "ap-northeast-1"
    key     = "tfstate"
    encrypt = true
  }
}

#----------
# Provider
#----------
provider "aws" {
  region = "ap-northeast-1"
}


#----------
# Remote State
#----------
data "terraform_remote_state" "XXX" {
  backend = "s3"

  config = {
    region = "ap-northeast-1"
    bucket = "system-stg01-deploy-tf-999999999"
    key    = "tfstate.XXX"
  }
}

#----------
# Local Values
#----------
locals {
  env = "stg01"
}

#----------
# Resource - Network
#----------
# vpc
module "vpc" {
  source = "../../modules/vpc"

  vpc_cidr_block = "192.168.10.0/24"
  vpc_tags_Name  = "system-${local.env}-vpc"
}

# public subnet
module "public-subnet01" {
  source = "../../modules/subnet"

  subnet_vpc_id                  = module.vpc.vpc_id
  subnet_cidr_block              = "192.168.10.0/26"
  subnet_map_public_ip_on_launch = "true"
  subnet_tags_Name               = "system-${local.env}-public-subnet01"
}

module "public-subnet02" {
  source = "../../modules/subnet"

  subnet_vpc_id                  = module.vpc.vpc_id
  subnet_cidr_block              = "192.168.1.64/26"
  subnet_map_public_ip_on_launch = "true"
  subnet_tags_Name               = "system-${local.env}-public-subnet02"
}
  • modules/vpc/main.tf
variable "vpc_cidr_block" {}
variable "vpc_tags_Name" {}

resource "aws_vpc" "this" {
  cidr_block           = var.vpc_cidr_block
  instance_tenancy     = "default"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = var.vpc_tags_Name
  }
}

output "vpc_id" {
  description = "The ID of the VPC"
  value       = concat(aws_vpc.this.*.id, [""])[0]
}

実装方針

  • モジュール切り出し+環境分離+コンポーネント分割
  • 変数の値は4箇所に集約(全て環境ファイルに)
    • 環境ファイルに直書き
    • 同一環境ファイルの別モジュールの値(output)
    • 環境ファイルのLocal Values
    • コンポーネントの値(outputをRemote Stateとして拾う)
  • 全てmain.tfに寄せる
    • 環境ファイルとmoduleファイル
    • variable.tfとか切り出さない

検討経緯

モジュール切り出し+環境分離+コンポーネント分割

  • 推奨されてそうなのでモジュール切り出しにして見た
  • 今後のことを考えて受け身が取れるように
  • モジュール切り出しは、ダイナミックとかロジック入れるときに役立つ?
  • ただ、ロジックは極力使わずベタ書きしている
    • EC2のcountとかあるが、tagのインデックスくらいならいいがEIPとか絡むとややこしくなる。だったら環境ファイル側でベタ書きする。
  • →が、環境ごとに下手書きで十分な気がする(モジュールを繰り返し利用したところで、記述量が減らない)
    • 結局、コードとは違う。設定値の羅列で、ロジックがないから。ベタ描きで十分 or 思い切ってWorkspace?
      • Workspaceも、比較しやすい?
  • moduleはあくまでパーツ。システム側の都合を考慮せず、疎結合に組み合わせられるように実装する
  • tf.stateの分割
    • 変更が多い/少ない
    • 非機能要件の違い

変数の値は4箇所に集約(全て環境ファイルに)

  • 同一環境ファイルの別モジュールの値(output)
module.vpc.vpc_id
  • 環境ファイルのLocal Values

    • 比較しやすくなるように、環境変数(dev, prodなど)はLocal Valuesとして一度だけ定義する
  • コンポーネントの値(outputをRemote Stateとして拾う)

    • 呼び出し先のmoduleではなく、呼び出し元でoutputとして定義する必要あり
data.terraform_remote_state.network.outputs.vpc_vpc_id

全てmain.tfに寄せる

  • 「観点」記載の通り、環境ごとの差分を比較しやすくしたいので、分けることにあまり意味がない
    • それぞれのファイルの量も少ない

参考

共通コードのディレクトリにはclassの定義が、 環境ごとのディレクトリにはclassのインスタンスを作る際に渡す実際の値が定義されていると イメージすると分かりやすいと思います。
# コンポーネント分割例
* network
  * vpc,subnet
* routing
  * route53,alb,acm
* services
  * ecsのサービス単位(ecs cluster,taskdifinition,service,それに付随するec2系リソース ecr pipeline系)
* opsserver
  * session manager,ec2
* events
  * やりたい処理単位(lambda cloudwatchEvents ここはApex+Terraform)
  * モニタリングとかもここに
* state machine
  * step function
* storage
  * s3
* datastore
  * elasticache,RDS
  * elasticache,RDSを別々に
* iam user
# コンポーネント分割例
* network
  * ネットワーク系の設定VPC、Subnet、NAT、Route Tables等の設定を管理
* securitygroup
  * セキュリティグループの管理
* iam
  * IAMの管理
* s3
  * S3回りの管理
* backend
  * DB等のデータストア関連のリソースを管理
* webservers
  * webサーバやALB等の管理
* opsservers
  * 運用系のサーバの管理

Terraformについて調べたことメモ

はじめに

  • Terraformまわりについて復習したときのメモ

メモ

ディレクトリ構成案(最小構成)

.
├── README.md
└── stg01
    ├── ec2.tf
    ├── provider.tf
    └── versions.tf

ファイル例

  • ec2.tf
resource "aws_instance" "<pj>-stg01-app-01" {
  ami           = "ami-XXXXX"
  instance_type = "t2.medium"
  count = 1
  ebs_block_device {
    device_name    = "/dev/sda1"
    volume_type = "gp2"
    volume_size = 20
  }
  tags = {
    Name = "<pj>-stg01-app-01"
    Project = "<pj>"
  }
  key_name = "XXXXX-key"
  vpc_security_group_ids = ["sg-XXXXX"]
  subnet_id = "subnet-XXXXX"
}
  • provider.tf
provider "aws" {
  region  = "ap-northeast-1"
}

Terraformインストール手順(@デプロイ用マシン)

  • Terraformインストール
wget https://releases.hashicorp.com/terraform/0.13.2/terraform_0.13.2_linux_amd64.zip
unzip terraform_0.13.2_linux_amd64.zip
# PATHが通っているディレクトリにコピー
sudo cp terraform /usr/local/bin/
terraform -version
  • AWS環境情報設定
aws configure
# ~/.bashrc に記載して、 source ~/.bashrc でも可
# export AWS_ACCESS_KEY_ID=***
# export AWS_SECRET_ACCESS_KEY=***

Terraform実行手順(@デプロイ用マシン)

cd terraform/stg01
terraform init
terraform plan
terraform apply
terraform show

補足

  • リソースを破棄するコマンド
# 注意!
terraform destroy

MLOps勉強会Tokyo(Online)#1参加レポート

はじめに

メモ

MLOpsコミュニティの発足にあたり

  • DataRobot シバタさん
  • アンケート
  • 可愛そうなモデルを根絶したい
  • MLOpsは新しい分野。何が求められているのかをディスカッションしたりもしたい。
    • MLOpsに必要とされているものとは

先駆者に学ぶ MLOpsの実際

  • システム開発に置いて、データサイエンティストがやらないこと全て
    • 環境整備・成果物管理
    • 本番データでのモデル推定
    • 推論結果のシステムへの組み込み
    • 効果モニタリング
  • Walmart
    • MLプロジェクトの60-80%がプロダクションにならずに頓挫している
    • 本番化する計画は明瞭か
      • -> 企業にとって利益が出る(意味がある)システムにまで組み込み切る必要がある
  • データサイエンティスト並にデータエンジニアの求人が多い
  • Netflix
    • データの探索 2w
    • プロトタイピング 6-8w
    • 本番化 12-14w
    • リリース後のモデル更新 8w
  • MLOps NYC2019注目セッションまとめがある
  • SageMakerを使っている
  • エラーハンドリングもデータエンジニアが実装
  • とにかくデータサイエンティストの負担を軽減してあげる
  • マルチクラウドのメリット
    • AWS + GCP
    • GCPのBQ, SageMakerを使いたかった
  • コーディングフローの整備までやっているか?
    • コーディングルールとかはレビュー

DiscoveryDataScienceMeetup(DsDS)#0参加レポート

はじめに

メモ

広告文自動生成プロダクトでDataflowを導入した話

  • ダイレクトコピーの自動生成
  • Cloud Runでマイクロサービスを組んでいる
  • 分析はCompute Engineでやっている
  • pandas on GCEだとデータ量的に辛い
    • データ整形1週間とか
  • →Dataflowを使っている
  • デバッグのために、最初はローカルで行うべき。runnerオプションの切り替えだけでいい。
  • 実行環境の切り分け→Makefileとか、Dockerで包んだり
  • Dataflowでは非Pypiパッケージのインストールがかなり面倒   * 運用で使用しているマイクロサービスAPIを流用
  • Cloud Functionだとpipパッケージのインストールはできる一方、apt-get系のインストールができないんですよねCloud RunだとDockerレベルで環境を構築できるので、apt-getが必要な形態素解析パッケージをいれるためにCloud Runを採用しています!

TensorflowのモデルをGCPでサービングしてきた話

  • 予測APIの提供
  • 入力は画像andテーブル
  • なぜGCPか?→BQのため
    • クラウドの列指向DBに比べて 機能が豊富・性能が高い・使いやすい

  • AI Platform
    • GCSにモデルをおくだけで、APIを提供してくれる
  • 「結局前処理サーバーは必要だった」
  • AI Platform Custom Prediction Routine
    • ツラミ多し
    • モデルの容量制限とか
    • 廃止が決まっている
  • →結局GKE
  • 現時点だと saved model と custom prediction routine 共に Pytorch は選べないようです!

SageMakerで試行錯誤する推論パイプライン

  • 歴史
    • 最初は生EC2だった
      • ステップが密結合になりがち、一番リソースを食う処理に合わせてインスタンス選択、ライブラリバージョンが固定し辛い(1枚のpythonファイルなので)
    • →SageMaker Batch TransforをAirflowからキック
      • AirflowはECS上に自前→結構辛い
    • →Batch Transfor
      • バッチと言いつつ…。
    • →Processingを使うように
    • AWS Batchを使っていいのでは?
    • →training jobで推論までやっちゃう。スポットインスタンスのため。
      • モデルの推論も
  • SageMakerは、今までベタ書きだったコードを書き換えないといけない
  • SageMakerのlocalモードはまだできないことがある *スポットインスタンスのために、airflowは東京リージョンなのにクロスリージョンでus-east-1を
  • リトライはairflowで実装
  • 一応awsのstep functionsも検討に上がりましたが、当時はあまり使いやすそうじゃなかったのでほぼ迷わずairflow

Cloud Composerで組む機械学習パイプライン

  • CloudComposerで前処理からデプロイまで
  • クラスタ管理やログ管理がマネージド
  • GKE Pod Operator
    • Composerとは別のクラスタのPod
    • 基本方針は、全部これで(pythonとかBQやらない)
      • pythonは環境汚染。Airflow自体との競合
  • MLのリポジトリとパイプラインのリポジトリは別
    • masterにマージされたら、CircleCIでビルド、更新

検索システムのMLRモデル更新の自動化

  • Solrのメンテナンス
  • 機械学習モデルのCI/CD
  • 元々はオブジェクトストレージに置いていたが、GitLFSでおけるようになった
  • モデルのバリデーション
  • 今後は、ベンチマーク、A/Bテスト、モデル選択も自動化したい
  • Cloud ComposerにてGKEPodOperatorで全て行うのであればArgoを使ってもよいのかなと感じたのですが

  • 実験でArgoベースのKubeflow Pipelineを使っていますね。 比較すると、Airflowはやや成熟してきており色々と高機能で安定感がある

  • 一方でkubeflow pipelineは我々が検討していた段階では色々と足りない機能(失敗したrunを途中から実行など)があり、鋭意開発中感があったので本番運用に使うのは怖かったです。

  • 素のargoは使ったことはないのですが、今だとGCPでkubeflowベースのAI Platform Pipelinesも出ています

全社共通レコメンドプラットフォームへのKubernetes/Airflow導入

  • オンプレk8s
  • ジョブ管理はAirflow
  • トラフィックは10k req/s
  • Pod operator
  • なぜAirflow
  • 特徴量管理
    • BQ?
  • 実験管理
    • Kedro + MLflow tracking
    • Jupyterの相性がいい
    • kedroはドキュメントが丁寧・使いやすさなどがいい点としてあげられますね。GUIの点では定常実行を考えるとAirflowがいいですが、実験の上では十分かなと。

スクラム開発でやっていること

はじめに

スクラム開発とは

アジャイル開発のやり方の一つ
「事前に全てを正確に予測し、計画することはできない」ということが前提となるプロジェクトにおいて、
・おおよその全体像はちゃんと明らかにしつつ、
・優先度(重要度が高い、リスクが高い)を常に整理しておいて、
・もちろん直近の作業は詳細化して取り組みつつ、
・こまめに期間を区切って継続的な関係者のフィードバックを得ながら計画を修正していくことで、
要求の分析や設計などに必要以上の時間をかけないように進めていく。

やっていること

1. 『プロダクトバックログ』のベース作成(by PO)@PJのはじめ

  • やること
    • 実現したい要求をリストにして並び替える
    • ストーリー/デモ手順(完了要件)/見積もり/スプリント
      • 見積もり/スプリントについては「スプリント計画ミーティング」にて記載する
  • ポイント
    • 優先度順をきっちり決めておく

2. 「スプリント計画ミーティング」(by PO/SM/Devチーム)@スプリントのはじめ

  • やること
    • 『プロダクトバックログ』を見ながら、次のスプリントで実施する項目を選択する
    • タスクに分解+見積もり+アサインする
    • -> スプリントバックログ(Issue)に
  • ポイント
    • 見積もり工数はフィボナッチ数で(不確実性を吸収)

3. デイリースクラム(by SM/Devチーム)@毎日

  • やることとポイント
# 目的
* スプリントのゴールを達成できるか検査する
* 残作業の追跡

# アジェンダ
* PJ概況
* 前回のデイリースクラムからやっていたこと
* 次回のデイリースクラムまでにやること
* 問題点

# 備考
* 15分厳守。問題点の話し合いは別途枠を設ける。
* Issueをメンテする。
* 「他の人に共有すべきこと」に重点を置く。

4. 開発(by Devチーム)@毎日

5. スプリントレビュー(by PO/Devチーム)@随時

  • やること
    • デモを実施し、完了要件を満たしているか確認する

6. スプリントレトロスペクティブ(by SM/Devチーム)@スプリントのおわり

  • やること
    • 振り返り
      • 予定と実績
      • 予定と実績の乖離を元に、KPT振り返り
  • ポイント
    • 次のスプリントをよりよく進めるための振り返り

補足

ロール

  • PO(プロダクトオーナー)
  • SM(スクラムオーナー)
    • 開発プロセスが上手く回るようにする
    • 妨害を排除し、支援と奉仕をする
  • Devチーム(開発チーム)
    • リリース判断可能なプロダクトを作る