bundle installされたgemのソースコードも含めてptで検索する

$ pt 'def logged_in?' . $(bundle show --paths) --color --group | less -R

ptpt PATTERN [PATH1] [PATH2] ... と複数パスを指定できるので、カレントディレクト.bundle show --paths で得たgemのパス一覧を渡すことで検索できました。

bundle show --paths 便利。

ということで忘れないようにメモエントリ。

helm-ag-project-root-with-bundle-paths

"helm" といったらクラウドネイティブだったりしますが、使っていたhelm-agのコマンドにptを設定しているので、そのままbundle installされたgemのソースコードも含めてプロジェクトのソースを検索できる関数をしたためておきました。

(require 'helm-ag)

(custom-set-variables
 '(helm-ag-base-command "pt -e --nocolor --nogroup"))

(defun helm-ag-project-root-with-bundle-paths ()
  "helm-ag-project-root with bundle paths."
  (interactive)
  (helm-ag--init-state)
  (let ((rootdir (helm-ag--project-root))
        (bundle-paths (split-string (shell-command-to-string "bundle show --paths") "\n")))
    (let ((helm-ag--default-directory default-directory)
          (helm-ag--default-target (add-to-list 'bundle-paths rootdir)))
      (helm-ag--query)
      (helm-attrset 'search-this-file nil helm-ag-source)
      (helm-attrset 'name (helm-ag--helm-header helm-ag--default-directory) helm-ag-source)
      (helm :sources '(helm-ag-source) :buffer "*helm-ag-project-root-with-bundle-paths*" :keymap helm-ag-map
            :history 'helm-ag--helm-history))))

以上です

Mackerel plugin (mackerel-plugin-prometheus-exporter) を書いてみた #mackerelio

この記事はMackerelアドベントカレンダー23日目の記事です。

22日はshiimaxxさんのMackerelで負荷テスト中のリソースモニタリングを行うでした!

そして今日はMackerel Day #2ですね!

Mackerel 5周年おめでとうございます! 🎉🎉🎉🎉🎉

Mackerel pluginを書いたことがない

実は私、Mackerelアンバサダーになっていたのでした(公ではじめて言った)*1

ただ、まだMackerel pluginを書いたことがない のでした。

そして今回 id:a-know さんにたまたまツイートを拾ってもらいアドベントカレンダーにお誘いを受けたので、「とても良い機会!」ということで書いてみました。

Mackerel pluginの作り方は公式マニュアルに完璧にまとめられていた

f:id:k1LoW:20191221152806p:plain
プラグインの作り方を調べてまとめようと思っていた

Mackerel pluginの作り方ってどんな感じなのかなーと調べたら公式マニュアルにサンプルコードも含めて完璧にまとまっていました ので、当初考えていたエントリはここで終了となりました。

「終了」だけだとあれなので、プラグインの開発からリリースまでに見た方が良さそうなマニュアルの箇所だけ列挙しておきます。

完璧なマニュアルでした!!

Mackerel pluginを書いてみる

ではMackerel pluginを書いていきます。

お題選定

お題はこれです。

選定の理由は「そーだいさんが求めていたから」というのもありますが、2018年のMackerel Meetup #11でも実装が検討されていたようなので、お題で書くとはいえMackerelユーザの一定の割合の人が興味あるかもと思ったからです。

また、私は普段の業務でMackerelだけでなくPrometheusを触ることも多く、マニアックなExporterも書いていたりするので、いけるんじゃないかなと。

Mackerel pluginは標準出力にメトリクスを出力する、シンプルなコマンドラインツール

公式マニュアルにもあるようにmackerel-agentとMackerel pluginは標準出力を通じて連携しています。

ようは、取得したメトリクスをMackerelが指定しているフォーマットで標準出力に出力すれば良いわけですね。

Goで書くのであれば、github.com/mackerelio/go-mackerel-plugin パッケージを活用すれば、提供されているインターフェースを満たせば終わりです。そしてそのサンプルコードは公式マニュアルにあります

私はコマンドラインツール作成用ライブラリは、github.com/spf13/cobraが好きなので、これで雛形を生成します。

$ cobra init mackerel-plugin-prometheus-exporter --pkg-name github.com/k1LoW/mackerel-plugin-prometheus-exporter --viper=false

あとは cmd/root.go に、公式マニュアルのサンプルコードを参考に実装を書いていくだけです。

Prometheus ExporterはHTTPリクエストに対して text/plain でメトリクスを出力する、シンプルなWebアプリケーション

Prometheus ExporterもMackerel pluginとほとんど同じで、Prometheusが読めるフォーマットでテキストを出力しているだけです。ただそれが標準出力ではなく、HTTPリクエストに対するレスポンスというだけです。

ようは、ExporterにHTTPリクエストして受け取ったテキストをパースすれば良いわけですね。

そして、Prometheusのエコシステムの中で、この「ExporterにHTTPリクエストをしてテキストをパースしている」のはPrometheusになります。

たまたまPrometheusもGoで書かれていて読めそうなので見にいってみます。

github.com

実際にパースしているのは github.com/prometheus/prometheus/pkg/textparse パッケージのようなので、テストケースなどを参考に使い方を把握します。

mackerel-plugin-prometheus-exporter

ここまでのまとめです。

  1. mackerel-agentからMackerel pluginが実行される
  2. github.com/prometheus/prometheus/pkg/textparse パッケージを使って任意のExporterからメトリクスを取得する
  3. 取得したメトリクスを github.com/mackerelio/go-mackerel-plugin パッケージのルールに合わせて詰め直す
  4. mackerel-agentが標準出力経由でメトリクスを取得してMackerelに送信する

. . .

できました!

github.com

具体的には以下のようなアーキテクチャになります。

f:id:k1LoW:20191221172030p:plain
mackerel-plugin-prometheus-exporterのメトリクス収集アーキテクチャ

使ってみる

使ってみます。

まず mackerel-plugin-prometheus-exporter をインストールします。

$ sudo mkr plugin install k1LoW/mackerel-plugin-prometheus-exporter

node_exporterを起動しておきます。

$ node_exporter # brew install node_exporter
INFO[0000] Starting node_exporter (version=0.18.1, branch=, revision=)  source="node_exporter.go:156"
INFO[0000] Build context (go=go1.13.4, user=Homebrew, date=)  source="node_exporter.go:157"
INFO[0000] Enabled collectors:                           source="node_exporter.go:97"
INFO[0000]  - boottime                                   source="node_exporter.go:104"
INFO[0000]  - cpu                                        source="node_exporter.go:104"
INFO[0000]  - diskstats                                  source="node_exporter.go:104"
INFO[0000]  - filesystem                                 source="node_exporter.go:104"
INFO[0000]  - loadavg                                    source="node_exporter.go:104"
INFO[0000]  - meminfo                                    source="node_exporter.go:104"
INFO[0000]  - netdev                                     source="node_exporter.go:104"
INFO[0000]  - textfile                                   source="node_exporter.go:104"
INFO[0000]  - time                                       source="node_exporter.go:104"
INFO[0000] Listening on :9100                            source="node_exporter.go:170"

mackerel-agent.confに設定を追記します。

[plugin.metrics.prometheus-exporter]
command = ["/opt/mackerel-agent/plugins/bin/mackerel-plugin-prometheus-exporter", "--target", "http://:9100/metrics"]

mackerel-agentを再起動します。

f:id:k1LoW:20191222221106p:plain
メトリクス数が多すぎて見えない

node_exporterのメトリクスがMackerelに取り込まれました!

もし利用するときは exclude_pattern の設定は必須ですね。

MackerelとPrometheus

MackerelとPrometheusは様々な点で設計思想が異なっています。

取得できたメトリクスを見てみるとわかるように、mackerel-plugin-prometheus-exporter経由でExporterから取得したメトリクスをMackerelで有用なグラフにするには exclude_pattern や、カスタムグラフなどを活用してグラフを整形する必要がありそうです。

もしPrometheusプロセスがどこかにいるのであれば、fujiwara さんが作成されたmackerel-plugin-prometheus-query でPrometheusを経由してPromQLで整形後にMackerelに送信するのがMackerelの思想的にも良さそうです(今日のMackerel Day #2でmackerel-plugin-prometheus-queryも含めた発表があるみたいです!楽しみですね!)。

もしくはMackerelのQueringの機能(具体的にはカスタムグラフ作成時に使用可能な関数)がさらに充実してくると、 mackerel-plugin-prometheus-exporter が使える範囲も広がってくるかもです。

というわけでMackerel pluginを作ってみたエントリでした!

明日は setoazusa さんです!

*1:Mackerelとも連携するmetrの作成が評価されたそうです! https://k1low.hatenablog.com/entry/2019/08/23/090831

Knativeワークショップに参加したあとKnative on kindを構築してみた #serverlessdays #serverlessfukuoka

ServerlessDays Fukuoka 2019にワークショップから本編、懇親会まで参加して楽しんできました!

fukuoka.serverlessdays.io

本編ではサーバレスな最新事例から新しく追加されたエコシステムまでキャッチアップできました。Amazon RDS Proxyとか「埋めてきたなー」という印象。

また、我々が運用しているコンテナランタイムやクラスタも負けてないぞと思って、いろんなタイミングで話してた気がします。主に懇親会で。

で、ServerlessDays Fukuoka 2019の個人的な一番の目当ては前日のワークショップでした。

Knativeで作るDIY FaaS

私が参加したのは "Knativeで作るDIY FaaS" で、Kubernetesクラスタ(GKE)上にKnativeを使ってFaaS環境をつくるというものです。

ワークショップのリポジトリはこちら

github.com

参加前から、かなり濃いだろうと予想していて、「1日がかりのワークショップだろうなー」と勝手に思っていたら午後から開始でした。まさかの濃縮。

実はこのワークショップはServerlessDays Tokyo 2019でも開催されたらしくワークショップとしては v1.1.0 とマイナーバージョンが上がっていました。ただの再演ではないわけですね。

講師の @toshi0607 さんのエントリからもわかるように考え抜かれた内容で、インプットの多い数時間でした。

印象的だったのがワークショップの内容が「ワークショップで持ち帰ってほしいところがはっきりしていること」「それ以外は限りなく時間がかからないようにしていること」です。

その分エンジニアにありがちな「このコマンドは何?」と気にはなったのですが、ドキュメントはあるし参考リンクはあるしで、「それはあとで」なんとかなるようになっていました。

短時間で綺麗に完結させているワークショップだと思いました。参加して良かった!

Knative on kind

というわけで、復習タイムというかやりたくなったのが「ローカルにKnative環境を構築する」です。

Kubernetesクラスタには使ってみたかった kind を選択。Knative on kindです。

最初は何も考えずに kubectl apply をしまくった結果ローカル環境のリソースが足りずクラスタを壊してたりしたのですが、落ち着いてドキュメント読むなどをしてなんとかサンプルアプリが動くところまで行くことができました。

最終的にうまくいった手順の記録です。

必要なツールをインストール

helmはv2を使うので helm@2 を指定します

$ brew install kind kubernetes-cli helm@2

kindでKubernetesクラスタを作成

control-planeとworkerの1つづつNodeを作成します。

# kind-config.yaml
kind: Cluster
apiVersion: kind.sigs.k8s.io/v1alpha3
nodes:
- role: control-plane
- role: worker
$ export K8S_VERSION=v1.16.3
$ kind create cluster --name knative --image kindest/node:${K8S_VERSION} --config kind-config.yaml
Creating cluster "knative" ...
 ✓ Ensuring node image (kindest/node:v1.16.3) 🖼
 ✓ Preparing nodes 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
 ✓ Joining worker nodes 🚜
Set kubectl context to "kind-knative"
You can now use your cluster with:

kubectl cluster-info --context kind-knative

Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
$ kubectl config use-context kind-knative
$ kubectl config current-context
kind-knative
$ kubectl cluster-info --context kind-knative
Kubernetes master is running at https://127.0.0.1:61330
KubeDNS is running at https://127.0.0.1:61330/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

MetalLBをインストール

Istioの istio-ingressgatewayService type:LoadBalancer を必要とするのですが、パブリッククラウドではないので Service type:LoadBalancer が標準では用意されていません。そのため MetalLB をインストールしてService type:LoadBalancer を用意します。

# metallb-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 192.168.1.240-192.168.1.250
$ export METALLB_VERSION=v0.8.3
$ kubectl apply -f https://raw.githubusercontent.com/google/metallb/${METALLB_VERSION}/manifests/metallb.yaml
$ kubectl apply -f metallb-config.yaml

Istioをインストール

Knativeが利用するIngress/GatewayとしてIstioをインストールします。

ただ、フルパッケージを入れるとホスト(Macbook)のリソースが厳しいので、Knativeのドキュメントを参考に最低限をインストールします。

# istio-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
 name: istio-system
 labels:
   istio-injection: disabled
$ export ISTIO_VERSION=1.4.2
$ curl -L https://istio.io/downloadIstio | sh -
$ for i in istio-${ISTIO_VERSION}/install/kubernetes/helm/istio-init/files/crd*yaml; do kubectl apply -f $i; done
$ kubectl apply -f istio-namespace.yaml
$ helm template --namespace=istio-system \
  --set prometheus.enabled=false \
  --set mixer.enabled=false \
  --set mixer.policy.enabled=false \
  --set mixer.telemetry.enabled=false \
  --set pilot.sidecar=false \
  --set pilot.resources.requests.memory=128Mi \
  --set galley.enabled=false \
  --set global.useMCP=false \
  --set security.enabled=false \
  --set global.disablePolicyChecks=true \
  --set sidecarInjectorWebhook.enabled=false \
  --set global.proxy.autoInject=disabled \
  --set global.omitSidecarInjectorConfigMap=true \
  --set gateways.istio-ingressgateway.autoscaleMin=1 \
  --set gateways.istio-ingressgateway.autoscaleMax=2 \
  --set pilot.traceSampling=100 \
  istio-${ISTIO_VERSION}/install/kubernetes/helm/istio \
  > ./istio-lean.yaml
$ kubectl apply -f istio-lean.yaml
$ kubectl get pods -n istio-system
NAME                                    READY   STATUS    RESTARTS   AGE
istio-ingressgateway-6b699467f5-bxzjs   1/1     Running   0          42s
istio-pilot-7957c5468f-q67zl            1/1     Running   0          42s
$ kubectl get svc -n istio-system
NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)
        AGE
istio-ingressgateway   LoadBalancer   10.99.44.136    192.168.1.240   15020:30715/TCP,80:31380/TCP,443:31390/TCP,31400:31400/TCP,15029:31512/TCP,15030:30136/TCP,15031:30290/TCP,15032:31941/TCP,15443:31026/TCP   45s
istio-pilot            ClusterIP      10.100.11.226   <none>          15010/TCP,15011/TCP,8080/TCP,15014/TCP
        45s

Knative (Serving) をインストール

Knative (Serving) をインストールします

$ kubectl apply --filename https://github.com/knative/serving/releases/download/v0.11.0/serving.yaml
$ kubectl get pods -n knative-serving
NAME                                READY   STATUS    RESTARTS   AGE
activator-7db6679666-rm724          1/1     Running   0          41s
autoscaler-ffc9f79b4-nkd6n          1/1     Running   0          41s
autoscaler-hpa-5994dfdb67-j9vnv     1/1     Running   0          41s
controller-6797f99458-86vnl         1/1     Running   0          40s
networking-istio-85484dc749-qdcnh   1/1     Running   0          39s
webhook-6f97457cbf-wbzwq            1/1     Running   0          39s

Deploy app

Knativeのドキュメントにあるサンプルアプリをデプロイしてみます。

apiVersion: serving.knative.dev/v1 # Current version of Knative
kind: Service
metadata:
  name: helloworld-go # The name of the app
  namespace: default # The namespace the app will use
spec:
  template:
    spec:
      containers:
        - image: gcr.io/knative-samples/helloworld-go # The URL to the image of the app
          env:
            - name: TARGET # The environment variable printed out by the sample app
              value: "Go Sample v1"
$ kubectl apply -f service.yaml
$ kubectl get ksvc helloworld-go
NAME            URL                                        LATESTCREATED         LATESTREADY           READY   REASON
helloworld-go   http://helloworld-go.default.example.com   helloworld-go-k2c9b   helloworld-go-k2c9b   True

ポートフォワーディング

istio-ingressgateway とホストをポートフォワーディングで繋げます。

$ kubectl port-forward svc/istio-ingressgateway -n istio-system 8880:80

Hello World

curlで叩くとリクエスト契機でアプリが立ち上がってレスポンスを返します。

$ curl -H "Host: helloworld-go.default.example.com" http://127.0.0.1:8880
Hello Go Sample v1!

今回もコードでまとめています

PostgreSQLの運用練習と同じように今回も動くコードでまとめています。

github.com

ServerlessDays Fukuoka 2019楽しかった!

運営の皆さん、発表者/ワークショップ講師の皆さん、ありがとうございましたー!

色つけツール colr に、逆に受け取った標準入力から色を消す --erase オプションを追加した

地味に普段から重宝している colr--erase オプションを追加しました。

colr についての紹介エントリは以下をご覧ください。

k1low.hatenablog.com

色をつけたいことがある。そして色を消したい時がある

colrによってカジュアルに色をつけた標準出力を得ることができるようになったのですが、一方で色を消したいときもあることに気づきました。具体的にはエスケープコードを外したい。

標準出力をファイルやクリップボードなどに保存するとき、ツールによっては擬似端末かどうかに関わらず色つけ用のエスケープコードを強制的に出力するものがあります。

colrもそうです(理由についてはエントリを参照)。

意外に消したいタイミングは多くその度にそのコマンドの色消しオブションだったりsedなどで対応していたのですが、毎回ググるのもアレなので実装することにしました。

別コマンドにするか迷いましたが、あまりに小さい機能だったのでcolr にオプションとして追加しました。

使い方は簡単で colr --erase をパイプに繋げるだけです。

$ any-colorful-command | colr --erase

具体的な実装ですが、 mattn/go-colorableNonColorable をそのまま活用しています。便利!

v1.0.0

他に追加の機能が思いつかなかったので v1.0.0 としました。安定板。

このまま枯れたコマンドとして生きながらえてくれるといいなと思います。

filtに tail -Fなストリームだけでなくcat /path/to/access.log のような標準出力に対しても何度でもコマンドを試行錯誤できる機能を追加した

"トライアルアンドエラーパイプ"ことfiltに、tail -F /path/to/access.log なストリームだけでなく cat /path/to/access.log な標準出力に対しても何度でもコマンドをパイプで繋げて試行錯誤できる機能を追加しました。

k1low.hatenablog.com

今回新たに追加された機能もあわせて、使い方を紹介します。

使い方

filt v0.5.0の時点で使い方は2種類あります。

ストリームデータ(従来の使い方)

連続してデータが流れ続けてくるような標準出力には、パイプで filt を繋げます。

$ tail -F /path/to/access.log | filt

このときのユースケースとしては、

今リアルタイムに流れているデータに対して 何度でもコマンドを試行錯誤したい」

というものになると思います。

filtの標準入力にはそのまま途切れずにデータが流れてきます。

オプションなしのデフォルトの filt のとき、filtが提供するプロンプトでパイプで繋げるコマンドを入力すると、filtはそのコマンドに都度 その時流れてきたデータから 流します。つまり、コマンドを入力するまでのデータは保持せず捨てます。

これによりリアルタイム性を保持したまま、ストリームに対して「何度でもコマンドを繋げて試行錯誤する」ということを実現しています。

終端がわかっているデータ(新しい使い方)

保存済みのアクセスログの検索や整形など、データの終端がわかっているような標準出力には、--buffered オプションをつけてパイプで繋げます。

$ cat /path/to/access.log | filt --buffered

このときのユースケースとしては、

/path/to/access.logの先頭から終端までに対して 何度でもコマンドを試行錯誤したい」

というものになると思います。

filt --buffered のとき、filtはまずデータの終端までバッファに読み込みます。そして、filtが提供するプロンプトでパイプで繋げるコマンドを入力すると、filtはそのコマンドに、都度 バッファに保持したデータを最初から 流します。

これにより、標準入力で受け取ったデータに対して「何度でもコマンドを繋げて試行錯誤する」ということを実現しています。

v1.0.0に向けて

これで、ほとんどの標準出力に対してとりあえずパイプでfiltを繋げてコマンドを試行錯誤するということができるようになったと思います。

filtは私自身日常的に使っているツールです。しばらく使って使い勝手に問題がなければ、微調整後 v1.0.0 にして出そうと思っています。

「とりあえずパイプでfilt」

で開発体験がより良くなれば幸いです。

PostgreSQLの運用練習の成果をコードで残していっている

RDBMSレプリケーションなど、なんとか調べてギリギリ理解して組んで、その後新規レプリケーションを組むか障害が発生したとき、また一から調べている気がするので、動く環境として記録していくことにしました。

github.com

これはなに

今のところ、PostgreSQLストリーミングレプリケーションの構築->フェイルオーバー->再レプリケーション までをひと通りMakefileにまとめたものです。

2019年10月時点では、非同期と同期のストリーミングレプリケーションを1種類づつ作っています。

コードでまとめることで良かったこと

何度も試せる

まず何度も試せるのが良いです。設定変更の検証などもすぐです。

私は今回試行錯誤した結果、2桁はレプリケーションを組んだので、レプリケーションがあまり怖くなくなりました。

設定に多くのパターンがあることがわかった

実際に手を動かすと頭に入ってくるということだと思います。知らないことが多すぎた。

レプリケーションの種類でも2種類(ストリーミング、ロジカル)、ストリーミングレプリケーションのほうだけでも、同期非同期、トランザクションにも数種類、マスタ昇格の方法、スタンバイ方法、リカバリ方法、etc...

興味があるところだけ作ってみましたが、再現可能なコードになっているので他の設定も試しやすいです。

CIが利用できる

CIを利用して、複数の環境で検証することが楽になります。

その結果、PostgreSQLのバージョンをまたいで同じ設定が使えるかどうかなどがわかります。

とりあえず、PostgreSQL12ではrecovery.confを使ったリカバリはサポートされていなさそうだということを知りました。

他の人に共有できる

再現可能なコードで残っているため他の人に共有がしやすいです。

なんなら(作っている範囲であれば)ハンズオンも簡単です。

有識者から指摘を受けられる可能性がでてくる

コードなので、設定も想定運用も全てコードで見えます。

そうするともしかしたらインターネット上の有識者から「pg_rewindを使ったほうがいい」「そのパラメーターは少し間違っている。こっちのほうがいい。なぜなら〜」などの指摘をもらえる可能性があります。

これからしたいこと

このリポジトリは完全に自分の練習の成果を残すもので、これからもいじっていく予定です。永遠のWIP。

Dockerの環境でできる範囲でですが、いろいろなパターンも試してみたいと思っています。

また、今後現場でも体験するであろうPostgreSQLのバージョンアップなどもコードで残していきたいと思います。

第5回Web System Architecture研究会に参加した #wsa研

第5回がまた福岡で開催されるということで、第2回参加から久しぶりでしたが、参加・発表してきました。

発表内容

予稿

github.com

発表資料

発表内容は、「関係する(コンポーネント間で通信をしている)コンポーネントのログの流量の間には相関があるはずで、その相関をみることで異常や異常箇所特定の足がかりにできるのではないか」というものです。

発表してみて

先行研究の発見

着眼した個所は多少違うのですが、「複数の関係する時系列データの相関を見ることで異常個所を特定する」というアイデアが、 matsumotoryさんの学部時代の研究と全く同じだったのは驚きでした。もしサーベイするならmatsumotoryさんの学部卒業論文からですね。 あれ?ブログからかな?

この私のログの研究?は、要はシステムにおいて経験値が大きいエンジニアと経験値が小さいエンジニアの差を埋めるのでは?と思って行なっています。研究者ではないのでそこまで時間は取れませんが、その分肩肘張らずにやっていきたいと思います。

こういうことを教えてもらえるのも研究者が参加しているWSA研の良いところです。

多くのフィードバック

たった15分の発表ですが、今回も多くのフィードバックをもらうことができ、頂いたアイデアやフィードバックに知識インデックスがはられました。

WSA研では、質疑応答にも発表時間と同じ15分の時間がとられていることと、(今のところ)発表者=参加者なので、発表者-参加者間の距離が小さいことが良い効果を発揮しているのかも?と思っています。

幅広い、そして興味深い発表

Web System Architecture 研究会のサイトにも予稿やスライドがアップされていますので是非みていただきたいのですが、どれも興味深く聴くことができました。

残念ながら最後のセッション4は子供の体調不良で途中退席せざるを得なかったのが本当に残念でした。


今回も楽しく参加できました。ありがとうございました!