Blogaomu

WEBアプリケーション開発とその周辺のメモをゆるふわに書いていきます。

Akka Streamsを利用するTCPクライアントのメモ

Akka Streamsを利用してTCP通信ができる簡易なコードが書けることを知ったのでメモ。

詳しくは以下のページを参照。

doc.akka.io

TCPクライアントのソースコード

package io.github.takayukiatkwsk.tcpclient

import akka.actor.ActorSystem
import akka.stream.scaladsl.{Flow, Keep, Sink, Source, Tcp}
import akka.util.ByteString

import scala.concurrent.Future

class TcpClient(implicit val system: ActorSystem) {
  private val connection = Tcp().outgoingConnection("127.0.0.1", 8080) // 接続先のホストとポート

  private val mapFlow = Flow.fromFunction[ByteString, String](b => b.utf8String)
  private val sink = Sink.headOption[String]

  def send(body: String): Future[Option[String]] = {
    Source
      .single(ByteString(body)) // TCPサーバーに送るデータ(StringからByteStringに変換)
      .via(connection)
      .via(mapFlow) // サーバーから返却されたデータをStringに変換
      .toMat(sink)(Keep.right) // 文字列を取り出す
      .run()
  }
}

object TcpClient extends App {
  implicit val actorSystem = ActorSystem("tcp-client")
  implicit val ec = actorSystem.dispatcher

  val tcpClient = new TcpClient()
  for {
    result <- tcpClient.send("Hello from TcpClient")
  } yield {
    println(result.getOrElse("Empty response"))
  }
}

今回TCPサーバーはnc(netcat)コマンドを利用してちゃちゃっと確認できるようにしました。

### 8080ポートでListenして、hello, worldを返す
$ echo -n 'hello world' | nc -l 8080

クライアントを実行してみます。

### ncコマンドとは別のターミナルで実行
$ sbt 'runMain io.github.takayukiatkwsk.tcpclient.TcpClient'

TCPサーバーに Hello from TcpClient というデータが送られてきて標準出力に出力されます。

$ echo -n 'hello world' | nc -l 8080
Hello from TcpClient

その後、TCPクライアントはTCPサーバーからの返信を受けて、ターミナルに hello world というデータが出力されます。

$ sbt 'runMain io.github.takayukiatkwsk.tcpclient.TcpClient'
...
hello world

感想としては、Akka Streamsのみで比較的シンプルにソースコードを書くことができ、レスポンスの扱いもFuture値を使うことができるのでハンドリングしやすいなと思いました。リソースのオープン・クローズも隠蔽されていて使う側としては非常に楽だなあと感じます。選択肢の一つとして覚えておくと良さげですね。

Kanazawa.rb meetup #89 に参加しました #kzrb

2020年になって最初の Kanazawa.rb です。今年もやっていきましょう。

kzrb.doorkeeper.jp

meetup.kzrb.org

もくもく進捗

久しぶりにWebアプリ作ってみようかなーとネタが浮んだのでユーザーストーリーの作成と必要になりそうな技術の調査を行いました。どういうお題かというとブログの最新記事を音声化して購読できるものです。アイデアとしてはよくあるものだとは思いますが自分が欲しいなあと少しでも思ったので作ってみることにしました。

ユーザーストーリーの作成は見様見まねでやってみました。最初はファイルに箇条書きで思いつくままに書いていたのですが、それぞれのユーザーストーリーの関連性や依存を洗い出したかったので付箋に書いてペタペタ貼って考えてみました。結果以下の画像のように並び替えました。

机のスペースが限られているので自分のみ分かる配置になっていますが左から右にストーリーが進んで目的が達成されるという並びにしています。縦方向は派生するパスを表しています。並べてみると最初はこの部分の一連のストーリーが実装できていればいいか、というようなことが決めやすいなという気付きがありました。

残りの時間は技術的な課題を洗い出して一つずつ調査実装していきました(全ては終わっていない)。今回はサーバレスな基盤でRubyを使って実装してみようと考えてます。趣味アプリなので自分の使いたい技術(Rubyは最近触れてなかったので...)を使うというところと、音声変換や非同期処理などクラウド基盤やサーバレスアーキテクチャに乗せた方がもろもろ楽できるのかなというのが技術選定の背景です。調査実装してみて構成を変える可能性はありますが。

ギャラリー

こう見るとタイトル重要だなというのを再認識させられます。

懇親会

www.hotpepper.jp

最近オープンしたという「十三」にて一次会!炭火で鶏肉を焼きながらあれこれ喋ってました。レガシーリソースと新規開発、リモートワークとテクノロジー、ラジオで聴いた分身ロボットのカフェとセルフ介護の話、にいがた酒の陣、社内ルール、ガリベンガーVなどについて話しました。お腹いっぱいでチキン南蛮食べられなかった...。

二次会は安心の「だんまや水産」にて。細巻き美味しかった。トークはあんまり覚えてませんw

次回

決済プラットフォームのStripeミートアップやりますよ。もう使ってるよって方はぜひ事例を共有して、興味あるよって方は気になりポイントを聞いてみる絶好の機会です!

eventregist.com

2019年個人的振り返り

今年もこの季節です。

www.blogaomu.com

仕事

引き続きZOZOのお仕事を手伝っておりますが、去年(=2018年)から運用に入っていたサービスが終了し、次のプロダクトのリリースに向けてあれこれやっているという状況です。役割としては技術調査を行ったり開発効率を高めるためのツールを作ったりということがメインでした。初期実装は自分の方でざっくりやって続きは若者に委譲していくというパターンのやり方が多かったです。やっていく上では思想も含めて引き継いでいくというのが大事だと感じたし、同時に難しさも感じました。一年を通しては上手くいったなーということより上手くいかなかったなーということの方が多かった気がします。

そういうことを感じてかまたは無意識にかは分かりませんが、自身の方向性について考えようとする機会が増えて模索をしていた年でもありました。

  • スキルの棚卸し
    • いまできること、将来やっていたいことを明らかにするためマインドマップでまとめていた。それから先は進められていない。
  • AWS認定
    • どの程度やれているのかを把握するのに受験した。新しく知れたこともあったし、得意不得意について言語化しやすくなった。
  • 人に会って話を聞く
    • meetupやライトな会社訪問などで外の世界の話を聞く機会を作った。特にチームとして何か解決するみたいな部分。

コミュニティ活動

定常的なもの

  • Kanazawa.rb 9回
  • JAWS-UG金沢 7回
  • Code for Kanazawa Civic Hack Night 1回

Kanazawa.rbでは今期も運営メンバーに入ったり飛び込みLTしたりで参加してる感出せたなーと思います。JAWS-UG金沢ではあまり発表できなかったので来年は2回以上発表できるようにしたいです。Code for Kanazawaは初めて参加してみたものの、コミュニティとしての関わり方が他の2つと異なって少し難しさを感じました。一応 ha4go にはほんのり貢献できたのでよかったです。

大規模イベントや単発もの

  • JAWS DAYS 2019
  • HashiCorp Terraform & Vault Enterprise 勉強会 in 金沢
  • 突撃!!隣のアーキテクチャ
  • ScalaMatsuri 2019
  • Algolia 勉強会 in 金沢
  • Google Cloud Next '19 in Tokyo
  • AWS Community Day Kanazawa
  • 松本CTOが語るテックカンパニーとDMMの目指す先 DMM Meetup in 金沢

特にAWS Community Day Kanazawaは結果的に100名超が参加するイベントになりましたが、運営スタッフの一員として関われたのは経験として大きかったなと思います。詳しくは個別に記事を書く予定ですが、ゴールから必要なことを割り出してこれらを一つ一つ実行していく力が問われました。個人的には会の直前までふわふわしていましたが無事に開催できてよかったです。

個人開発

今年はプロダクトは作りませんでした。PCにsandboxというディレクトリがあるのですがその中で写経したりサンプルアプリを作ったりするぐらいでしたね。引き出しは少しずつ増えているはずなのでアイデアを形にするのを来年はできたらよいなと思います。

来年も上手い酒を飲めるように精進。

Kanazawa.rb meetup #88 に参加しました #kzrb

あっという間に2019年も12月を迎えていますね。金沢は冬らしくない気候が続いておりあまり年末という感じがしません。それでも12月のKanazawa.rbは年末LT大会。毎年の恒例行事です。

kzrb.doorkeeper.jp

meetup.kzrb.org

LT

発表に使用したスライドはこちらです。

speakerdeck.com

今年の後半において2つのAWS認定を取得したので動機や勉強方法、ふりかえりなどをさらっと発表しました。AWSイベントにおける認定ラウンジは人権、というパワーワードも出つつ折角なので来年のAWS Summit(もし参加できたら)ではラウンジ入ってみたいですね。発表に含めなかったところでは、先にデベロッパーアソシエイトに合格してそこそこの出来ですっかり油断し、次に受けたSysOpsアドミニストレーターアソシエイトでは合格ラインギリギリで通過してしまいもう少し勉強したかったなというのと運用に比較的関われてないというのが改めて分かったという印象でした。来年はソリューションアーキテクトアソシエイトと上級の認定を取れるように精進していきたいです。

LT大会全体としては、18名もの参加者が各々取り組んでいることや興味を持っていることを発表していて、改めてKanazawa.rbの懐の広さを感じました。

懇親会

meat-meet.owst.jp

最近武蔵にできた肉料理のお店で懇親会。食べるのと話すのに夢中で全然写真撮れませんでしたw 話題としては、機械学習のおすすめ入門本の紹介、Python初心者がJupyter Nootbookを最初から使うべきか問題、リモートワークのつらみ、オープンオフィスの生産性、チームをリードする立場になった話、勉強会と領域初心者の問題などなど個人的には盛り上がりました。残念ながら私は1次会で撤収。

来年もKanazawa.rb盛り上げていきます。

HTTP/2独習メモ2日目(nghttpxでTLS終端)

HTTP/2の学習シリーズ。本当はKanazawa.rb #87もくもく会でやっていたことなんですが参加エントリーを書かない内に次のKanazawa.rbが来そうなので学んだことだけ抜き出してメモっておこうというモチベーションです。

www.blogaomu.com

前回はクライアント-サーバー間のHTTP/2通信についてGoでサーバーを実装し確認するという内容でした。今回は間にリバースプロキシを挟んでTLS終端を担ってもらいます。よくありがちな構成をシンプルにしたものですね。

構成

nghttpx

まずは nghttpx の設定です。基本的な項目のみになっています。注目すべきは2行目の backend で proto=h2 で HTTP/2 プロトコルで接続するようになります。

frontend=0.0.0.0,8443
backend=server,8080;;proto=h2
private-key-file=/etc/nghttpx/cert/server.key
certificate-file=/etc/nghttpx/cert/server.crt

backend

h2cでリクエストを受け付けるように修正したBackendの実装は以下になります。h2c というパッケージが用意されているのでこれを利用します。

package main

import (
    "io"
    "log"
    "net/http"

    "golang.org/x/net/http2"
    "golang.org/x/net/http2/h2c"
)

func main() {
    handler := http.HandlerFunc(index)
    h2s := &http2.Server{}
    srv := &http.Server{
        Addr:    ":8080",
        Handler: h2c.NewHandler(handler, h2s),
    }

    log.Fatal(srv.ListenAndServe())
}

func index(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "Hello World! by HTTP/2")
}

オーケストレーション・リクエストの実行

前回サーバーはdocker化したので、docker-composeでリバースプロキシを含めて構成したいと思います。

version: "3.7"
services:
  server:
    build:
      context: server
  reverse-proxy:
    image: dit4c/nghttpx
    ports:
      - "8443:8443"
    volumes:
      - "./nghttpx:/etc/nghttpx"
      - "./cert:/etc/nghttpx/cert"
    depends_on:
      - server

プロジェクトは以下の構成です。

.
├── cert
│   ├── server.crt
│   └── server.key
├── docker-compose.yml
├── nghttpx
│   └── nghttpx.conf
└── server
      ├── Dockerfile
      └── main.go

これで全ての構成の準備ができたので docker-compose を起動します。

$ docker-compose up

別のターミナルでHTTPSリクエストを行います。

$ curl -v -k https://localhost:8443

*   Trying ::1:8443...
* TCP_NODELAY set
* Connected to localhost (::1) port 8443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /usr/local/etc/openssl@1.1/cert.pem
  CApath: /usr/local/etc/openssl@1.1/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=JP
*  start date: Dec 17 23:13:26 2019 GMT
*  expire date: Dec 16 23:13:26 2020 GMT
*  issuer: C=JP
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fe729822a00)
> GET / HTTP/2
> Host: localhost:8443
> user-agent: curl/7.67.0
> accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 200
< content-type: text/plain; charset=utf-8
< content-length: 22
< date: Thu, 19 Dec 2019 07:40:30 GMT
< server: nghttpx
< via: 2 nghttpx
<
* Connection #0 to host localhost left intact
Hello World! by HTTP/2

リバースプロキシを挟んでHTTP/2サーバーにリクエストし正常にレスポンスすることを確認できました。

また、dockerネットワークに接続して tcpdumpWireshark で確認してみます。(tcpdumpイメージは前回参照)

$ docker run \
    --net=container:$(docker ps -f "name=reverse-proxy" --format "{{.ID}}") \
    -v $(pwd)/tcpdump:/var/dump-data \
    tcpdump tcpdump -i eth0 -w /var/dump-data/eth0-2.pcap

https://i.gyazo.com/a8aac324b6fed700367dce17a6e30ef1.png

172.0.0.3 が reverse-proxy で 172.0.0.2 が backend server です。これらの間の通信もHTTP/2で行われていますね。よかったよかった。

まとめ

今回はnghttpxを利用してTLS終端およびバックエンドサーバーへのリバースプロキシを行いました。また、HTTP/2でのリクエスト・レスポンスを確認できました。余談ですが docker-compose.yml をさくっと書けたのは日頃の作業が身に付いてる感じがあって良かったです。

今回のソースコードはこちらにアップロードしています。 github.com

Kanazawa.rb meetup #86 に参加しました #kzrb

最近涼しくなっていよいよ秋だなあと感じるこの頃、みなさまいかがお過ごしでしょうか?今回のkzrbは意識高いもくもく会 @ DMM Games でした。

kzrb.doorkeeper.jp

ちなみに、お昼はビリヤニ(ヤギ)。

HTTP/2の学習

分かるようでよく分からないHTTP/2について以下の資料を見ながら学習していました。

tools.ietf.org

summerwind.jp

8pockets.booth.pm

HTTP/1.1には無かった概念がいくつか出てきていたのでその辺をふむふむ言いながら読んでいました。特にフレームという単位でデータを送受信するというのが特徴的でなるほどと思った部分でした。実際に手元でも確認したいなと思い、クライアント-サーバー間の通信を見てみようと思いtcpdumpで取得したデータをWiresharkで開いて確認するというのを進めていました。クライアントはHTTP/2対応のcurl、サーバーはGoで実装したものを利用しました。server.crt server.key はローカル環境で作成したものです。

package main

import (
    "io"
    "net/http"

    "golang.org/x/net/http2"
)

func main() {
    var srv http.Server
    srv.Addr = ":8080"

    http2.ConfigureServer(&srv, nil)
    http.HandleFunc("/", index)

    srv.ListenAndServeTLS("server.crt", "server.key")
}

func index(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "Hello World! by HTTP/2")
}

*1

先ほどのソースコードをdockerビルド時にビルドし、dockerコンテナで実行できるように。

FROM golang:1.13

WORKDIR /go/src/app
COPY . .

RUN go get -d -v .
RUN go install -v .

CMD ["app"]

これらを準備してひとまずcurlでリクエストできるか確認。

# サーバー起動
% docker run --rm -p 8080:8080 --name http2-server <image>:latest

# 別のターミナルウィンドウで
% curl -v --http2 https://localhost:8080 --insecure

*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=JP
*  start date: Oct 19 05:07:49 2019 GMT
*  expire date: Oct 18 05:07:49 2020 GMT
*  issuer: C=JP
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7f8e6c806800)
> GET / HTTP/2
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 200
< content-type: text/plain; charset=utf-8
< content-length: 22
< date: Mon, 21 Oct 2019 07:44:23 GMT
<
* Connection #0 to host localhost left intact
Hello World! by HTTP/2* Closing connection 0

良さげですね。で、今度はtcpdumpしてみました。以下の記事を参考にしながら。

medium.com

% docker build -t tcpdump - <<EOF 
FROM ubuntu 
RUN apt-get update && apt-get install -y tcpdump 
CMD tcpdump -i eth0 
EOF

% docker network create demo-net
# --nameを指定
% docker run --network demo-net --rm -p 8080:8080 --name http2-server server:latest
# tcpdumpで出力されるファイル置き場
% mkdir tcpdump
# tcpdump実行
% docker run --net=container:http2-server -v $(pwd)/tcpdump:/var/dump-data tcpdump tcpdump -i eth0 -w /var/dump-data/eth0.pcap

この流れで eth0.pcap ファイル生成されて、wiresharkで開くことができました。が、https://wiki.wireshark.org/HTTP2のような形式では表示されずヘッダー情報などが確認できませんでした。この辺はもう少し調べてみたいと思います。

懇親会

tabelog.com

tabelog.com

今回は5人と少人数でしたがその分濃い話で盛り上がりました。信頼の昇龍、お腹一杯であまり食べられなかったのが悔やまれます。某交差点から離れるほど良い店に当たる確率が高くなるという話を聞いてなるほどなーと思いました😇

*1:『はじめてのHTTP/2』41ページより引用

Googleフォーム・スプレッドシート・ドライブで立て替え金の管理を試している

11月20日に「AWS Community Day Kanazawa」というAWSユーザーグループ(JAWS-UG金沢)とAWSの共催で行われるイベントが開催されます。私はこの運営に携わっていてそのタスクの一つに立て替え金の管理というのがあります。今回はできるだけ 頑張らず に立て替え金の管理を行う試みを行っているのでその紹介をしようと思います。

頑張らない?

ここで言う「頑張らない」というのは、時間を掛けない、コストを掛けない、自動化させる、他の人でもできる、ということを心がける意味の言葉として捉えてもらえればと思います。コミュニティ主体のイベントという性質もあって、私個人としては仕事や家庭やプライベートなどから限られた時間やリソースを捻出する中でのスタッフ活動になりますのでできるだけこのような心がけで運営するというのが持続的に活動する上で大切だと考えています。もちろん、他のスタッフが支えてくれているからこそという側面もありますので関係各位には改めて感謝を申し上げます。

立て替え金管理のワークフロー

さて本題に戻って、立て替え金管理についてのワークフローを示します(細かいところは省略)。

  • イベント運営に必要な費用をスタッフが立て替えて支払い、領収書を受け取る
  • スタッフは会計担当スタッフ(私)に立て替えを申請する(1)
  • 会計担当スタッフは申請内容を記録し、領収書(スキャンしたPDF)を保存する(2)
  • 会計担当スタッフは立て替え費用を集計する
  • (スポンサー等から)費用が運営に入ってきたら、会計担当スタッフは立て替えてくれたスタッフに精算を行う

このフローでは具体的な方法について書かれていませんがこれからどうやってやろうかという観点で読んでもらえればと思います。まず、(1)の立て替えたことを会計担当スタッフに申請する部分ではどのような方法を利用することが考えられるでしょうか。メールで申請する、専用のフォームを用意してここから入力してもらう等ありそうです。次に(2)の申請内容を記録・保存する部分についてはどうでしょうか。集計のことを考えるとExcelGoogleスプレッドシートに記録していくのが良さそうですね。

今回の要件としては、管理に必要な項目が決まっている、できるだけオープンに管理する、というポイントがありさらに頑張らないことを加味するとGoogleフォームとGoogleスプレッドシートおよびGoogleドライブを利用することにしました。

ツール 利点
Googleフォーム 入力フォーマットの統一、Submit後Googleスプレッドシートへ自動的に反映、ファイルもアップロード可、自ら実装およびホスティングしなくてよい、GASでカスタマイズ可能(後述)
Googleスプレッドシート 共有可能
Googleドライブ 領収書ファイルの置き場として共有可能

特にフォームについては様々な機能が利用できる上に自分で実装とホスティングしなくてよいというのが嬉しいポイントです。フォームを作成するときもブラウザでポチポチするだけなので他の人にも引き継ぎやすいと思います。

Googleフォーム・スプレッドシート・ドライブを使ったワークフロー

Googleフォーム、スプレッドシート、ドライブを使うワークフローは以下になります。赤字の部分が影響範囲になります。これで会計担当スタッフ(私)が関与する部分がだいぶ減りました。

  • イベント運営に必要な費用をスタッフが立て替えて支払い、領収書を受け取る
  • スタッフはGoogleフォームに必要な項目(日時、担当者名、金額、用途、領収書をスキャンしたPDF)を入力し申請を行う
  • Googleスプレッドシートに入力された内容が同期され、領収書PDFは特定のGoogleドライブフォルダに保存される
  • 会計担当スタッフはGoogleスプレッドシート上で立て替え費用を集計する
  • (スポンサー等から)費用が運営に入ってきたら、会計担当スタッフは立て替えてくれたスタッフに精算を行う

プラスα

これで運用の負担はだいぶ減ったのですが、フォームに申請があっても私にしか通知が届かないという課題としてありました。課題と感じた理由としては、私のメールにのみ届くため見落とす可能性がある、また申請者や他のスタッフにも通知がある方がオープンになって良いという点です。

そこでGAS(Google Apps Script)を使ってJAWS-UG金沢のSlackに通知する機能を実装しました。実装方法については以下のリンクが詳しいですのでこちらを参照ください。(「google form slack」でGoogleするといろいろ参考になる記事がでてきます)

qiita.com

うまく連携できるとSlackには以下のような通知が来るようになります(アイコンはそれっぽい絵文字を指定した)。フォームに入力された値を使って通知メッセージを生成しています。

Image from Gyazo

Slackの通知を含めると最終的にはこのような仕組みとなりました。*1

f:id:TAKAyuki_atkwsk:20191017232035p:plain

おわりに

これまで見てきた仕組みによって要件を満たしつつ運用としてはだいぶ楽になりました。その分仕組みを考えるときや作るときには多少頑張りました。今回は立て替え金管理というフローで利用しましたが応用できる業務フローは他にもあると思います。このような仕組みで頑張らない運用を考えるのも面白いですね。

最後に宣伝ですが、11月20日に金沢でAWS Community Dayが開催されますのでAWSクラウド、IoT、ビッグデータにご興味のある方はぜひお越しください。お待ちしております。

awscommunityday2019.jaws-ug.jp

*1:アイコンはこれらを利用しています Googleフォーム新しいロゴ icon by Icons8, Googleスプレッドシート icon by Icons8, Google ドライブ icon by Icons8