Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Prometheus入門から運用まで徹底解説

17,761 views

Published on

Tech Festa 2017で登壇した「Prometheus入門から運用まで徹底解説」のスライドです

Published in: Technology
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Prometheus入門から運用まで徹底解説

  1. 1. Prometheus 入門から運用まで徹底解説 株式会社ホワイトプラス システム開発Gマネジャー 大和屋貴仁 July Tech Festa 2017 googleエンジニアが作った監視システム
  2. 2. 大和屋貴仁 株式会社ホワイトプラス システム開発G マネジャー Microsoft MVP for Azure(2011-2018 Qiita : @t_Yamatoya http://sqlazure.jp/r
  3. 3. 富士フィルムイメージングシステムズと 共同開発したRFID検証。 宅配ネットクリーニングのリネット 会員数20万人突破! Golang、RFIDなど新しい技術や物を活用したサービス改善に興味のあるエンジニア募集!!
  4. 4. Prometheus の話をはじめる前に、 何故、Prometheusか?
  5. 5. SRE サイトリライアビリティエンジニアリング ――Googleの信頼性を支えるエンジニアリングチーム
  6. 6. Prometheusは、特にルール言語にBorgmonと多くの 類似点があります。 依然としてBorgmonはGoogle内部のものですが、ア ラートを発するためのデータソースとして時系列デー タを扱うという発想は、今日ではPrometheus、 Riemann、Heka、Bosunといったオープンソースの ツールを通じて広く受け入れられており… Prometheus と google SRE サイトリライアビリティエンジニアリング ――Googleの信頼性を支えるエンジニアリングチーム
  7. 7. Borgmanとは • 10年以上に渡って利用されているgoogle内部のモニタ リングツールの名称 • カスタムスクリプトを実行するのではなく、共通の データ公開フォーマットを利用する
  8. 8. データのフォーマット • 大量の収集を行うためにメトリックスのフォーマット は標準化されている必要がある • key/value式のプレーンテキストを返す • スキーマ―を持たないテキストベースのインターフェ イスが追加の障壁を低くする go_gc_duration_seconds{quantile="0"} 8.007600000000001e-05 go_gc_duration_seconds{quantile="0.25"} 0.000297585 go_gc_duration_seconds{quantile="0.5"} 0.00030774400000000004 go_gc_duration_seconds{quantile="0.75"} 0.000317933 go_gc_duration_seconds{quantile="1"} 0.004497566000000001 go_gc_duration_seconds_sum 1059.847743361 Borgmanと同様の仕様
  9. 9. ターゲットの発見 • データ収集をする対象を自動で検出する • サービスディスカバリ(Service Discovery)を利用すること でリストのメンテナンスコストを下げる Borgmanと同様の仕様
  10. 10. データの収集 • 指定された間隔でターゲットのURIからフェッチする • HTTP経由でターゲットをスクレイピングする • フェッチ時に合成変数も記録する • ホスト名とポート番号に解決できるか • レスポンスを返したか • 収集が完了した時刻 Borgmanと同様の仕様 % curl http://webserver:80/varz http_requests 37 errors_total 12
  11. 11. 時系列データベース • データをインメモリデータベースに格納し、定期的に チェックポイントで時系列データベース(ディスク) に書き出す • timestampとvalue形式で、時系列データに沿って格納 する Borgmanと同様の仕様 http_requests_total{status="200",method="GET"}@1434317560938 ⇒ 94355 http_requests_total{status="200",method="GET"}@1434317561287 ⇒ 94934 http_requests_total{status="200",method="GET"}@1434317562344 ⇒ 96483 http_requests_total{status="404",method="GET"}@1434317560938 ⇒ 38473 http_requests_total{status="404",method="GET"}@1434317561249 ⇒ 38544 http_requests_total{status="404",method="GET"}@1434317562588 ⇒ 38663 http_requests_total{status="200",method="POST"}@1434317560885 ⇒ 4748
  12. 12. ラベル ヒエラルキーのツリー構造ではなくラベルで表現する key/value形式のラベル集合で実装される ラベルは変数式と呼ばれる形式にまとめられる Borgmanと同様の仕様 {var=http_requests,job=webserver,service=web,zone=us-west}
  13. 13. クエリ式 • クエリを実行するとベクタ内で最新の値をもつ行が返 される • 期間を指定することができる Borgmanと同様の仕様 {var=http_requests,job=webserver,instance=host0:80,service=web,zone=us-west} 10 {var=http_requests,job=webserver,instance=host1:80,service=web,zone=us-west} 9 {var=http_requests,job=webserver,instance=host2:80,service=web,zone=us-west} 11 {var=http_requests,job=webserver,instance=host3:80,service=web,zone=us-west} 0 {var=http_requests,job=webserver,instance=host4:80,service=web,zone=us-west} 10 {var=http_requests,job=webserver,service=web,zone=us-west}[10m] {var=http_requests,job=webserver,instance=host0:80, ...} 0 1 2 3 4 5 6 7 8 9 10
  14. 14. アラート/Alertmanager • 真偽で判断し、真になるとアラート発行 • アラートには式が使え、瞬間的な閾値越えへの対処が容易 • Altermanagerは通知先へのアラート転送を担当する • アクティブになっているアラートに応じた他のアラート抑制 • 類似したラベルセットを持つ複数のアラートが発生した際の集約 Borgmanと同様の仕様
  15. 15. BorgmanとPrometheusの類似点まとめ • データフォーマット:プレーンテキストのkey/value • Target Discovery • データの収集方法:URLフェッチ、スクレイピング • 時系列データベース • ラベル • クエリ式 • アラート
  16. 16. Prometheus の歴史 2012秋 作成 2016/05 CNCF参加 2015/01 一般公開開始 2016/07 1.0 リリース 2017/08 2.0.0-beta.2 リリース
  17. 17. Prometheus の歴史 2012秋 作成 2016/05 CNCF参加 2015/01 一般公開開始 2016/07 1.0 リリース 2017/08 2.0.0-beta.2 リリース SoundCloudがモニタリング開発の検証開始 Matt ProudがSoundCloudに入社し、Prometheusの方向性を決める Matt Proud 2007/04-2012/08 google 2012/09-2013/09 Sound Cloud 2014/01- google
  18. 18. Prometheusの機能 • モニタリングシステムと時系列DB • 測定 • メトリックス収集と保存 • 保存したメトリックスに対するクエリ • アラート • ダッシュボード • 注力 • OSモニタリング • 動的クラウド環境
  19. 19. Prometheusがしないこと • 生ログ・イベントデータの収集 • リクエストトレース • 例外発見 • 長期保存ストレージ • 自動水平スケール • ユーザー/認証管理
  20. 20. Prometheusの利用企業 • AbemaTV / Cyberagent • Line • Yahoo!Japan • Freee • ホワイトプラス
  21. 21. Prometheus 入門
  22. 22. Prometheusの利用企業 Prometheus ServerExporterExporterExporter Pull Grafana/API/Web UI Altermanager Push
  23. 23. Prometheusの構築 • 構築は単一バイナリを動かすだけでOK • プリコンパイルされたバイナリをダウンロードして実行 • Sourceからmake • Dockerコンテナー docker run -p 9090:9090 -v /prometheus-data prom/prometheus -config.file=/prometheus-data/prometheus.yml
  24. 24. 設定:Prometheus.yml • Service Discoveryを利用する • EC2をTargetにしてみる • localhostも対象にしてみる global: scrape_interval: 5s scrape_configs: - job_name: 'prometheus' scrape_interval: 5s static_configs: - targets: ['localhost:9090'] - job_name: 'node' ec2_sd_configs: - region: ap-northeast-1 access_key: secret_key: port: 9100 relabel_configs: - source_labels: [__meta_ec2_tag_Name] target_label: instance
  25. 25. exporterを動かす • 監視対象でexporterを動かします • node_exporter: *NIXカーネル用のハードウェアとOSのメト リックス収集。 node_exporter -- collectors.enabled="conntrack,diskstats,entropy,filefd,filesystem,loadavg,mdadm,meminfo,netdev,netstat, stat,textfile,time,vmstat" >/dev/null 2>&1 &
  26. 26. 準備完了! • Prometheusサーバーの9090ポートにア クセスするとステータスページを確認 できる
  27. 27. ビジュアライズ表示 • 標準でもグラフ機能はある • けど、ダッシュボードなどのビジュアライズはGrafanaなどの ビジュアライズツールに委任する方針 • PrometheusにアクセスできるNW環境にGrafanaをイン ストール • Dockerで用意するのが簡単 # create /var/lib/grafana as persistent volume storage docker run -d -v /var/lib/grafana --name grafana-storage busybox:latest # start grafana docker run -d -p 3000:3000 --name=grafana --volumes-from grafana-storage grafana/grafana
  28. 28. ビジュアライズ表示2 • Data SourceでPrometheusを追加 • グラフでクエリを書けばOK
  29. 29. Service Discovery
  30. 30. 手動でtaarget設定する • 新しいサーバー追加するとConfig更新が必要 • バージョンミスマッチ • サーバーの喪失 scrape_configs: - job_name: microservice1 target_groups: - targets: [‘server1:8003’] - targets: [‘server2:8003’] - targets: [‘server3:8003’] - targets: [‘server4:8003’] - job_name: otherjob target_groups: - targets: [‘server3:8086’] - targets: [‘server4:8087’]
  31. 31. Service Discovery • DNS • Consul / Azure / ec2 • ファイル - job_name: pandora-exporter_nbg1 target_groups: dns_sd_configs: consul_sd_configs: - server: 127.0.0.1:8500 datacenter: nbg1 services: - pandora-exporter - job_name: service2-exporter_fra1 target_groups: dns_sd_configs: consul_sd_configs: - server: 127.0.0.1:8500 datacenter: fra1 services: - service2-exporter
  32. 32. 提供されているSD • azure • consul • dns • ec2 • file • gce • kubernetes • marathon • openstack • triton • zookeeper : nerve_sd_config / serverset_sd_config
  33. 33. relabelling
  34. 34. その1 ラベル何に使うの? node_load15{instance=“hoge-jp-green",job="node"} これがラベル 初期設定だと、IPがインスタンスに表示される ラベルにある項目でしかメトリックスは絞り込めない ラベルに無いものは使えない
  35. 35. relabeling とは? Targetsでラベルの上にマウス持っていくと いろんな情報が表示される 情報もってるのに、フィルターが反応しない!
  36. 36. relabeling とは? しれっと、Before relabeling と記載されている
  37. 37. relabeling とは? relabeling って? https://prometheus.io/docs/operating/configuration/#<relabel_config> いろんな情報あるけど、 最後には消しちゃうから 必要なものは定義して明示的に残してね
  38. 38. relabeling とは? __meta_ で始まるラベルは破棄されるので 必要であればrelabellingする
  39. 39. relabeling とは? 結果、こんな定義にしてEC2のタグ名を無事取得 scrape_configs: - job_name: 'node' ec2_sd_configs: - region: ap-northeast-1 access_key: secret_key: port: 9100 relabel_configs: - source_labels: [__meta_ec2_tag_Name] target_label: instance
  40. 40. Relabellingとは • Targetのメタデータを取り込み、収集する対象を選択 • 選択すると使用できる • 既定 • __address__ラベル • instanceラベル
  41. 41. Service Discoveryから Target選択 Configを元に __param_*ラベルを 設定する Job、__scheme__、 __metric_path__ラベルが設 定されていない場合は、 configを元に設定する __addres s_ラベル があるか どうか Targetを 破棄 relabel_configs Targetを 破棄 __address_ にポート番 号があるか どうか __scheme__がhttpか 空なら80、httpsなら 443を追加 __address_ に/が含ま れているか どうか __meta_から始まる すべてのラベルを削 除 Instanceラ ベルがある かどうか __address__ラベルを instanceラベルに copyする Targetを 破棄 Targetを作 成 無し 有り 含まれる 含まれ無い 有り 無し Drop/Keep Action無し 有り 出典:Robust Perception Life of a Label : https://www.robustperception.io/life-of-a-label/ Service Discoveryから Targetとラベルの生成
  42. 42. 対象の選択 • 目的のデータを選択する方法 • 構造が少ない場合は、正規表現 relabel_configs: - source_labels: ["__meta_consul_tags"] regex: ".*,production,.*” action: keep
  43. 43. 維持[keep]と破棄[drop] • もっとも単純なrelabellingの操作 • keepとdrop • Keep • 正規表現に合致すると、操作を継続します • 合致しない場合は、処理を終了し、次の対象に移ります • Drop • 正規表現に合致すると処理を終了し、次の対象に移ります
  44. 44. Service Discoveryから Target選択 Configを元に __param_*ラベルを 設定する Job、__scheme__、 __metric_path__ラベルが設 定されていない場合は、 configを元に設定する __addres s_ラベル があるか どうか Targetを 破棄 relabel_configs Targetを 破棄 __address_ にポート番 号があるか どうか __scheme__がhttpか 空なら80、httpsなら 443を追加 __address_ に/が含ま れているか どうか __meta_から始まる すべてのラベルを削 除 Instanceラ ベルがある かどうか __address__ラベルを instanceラベルに copyする Targetを 破棄 Targetを作 成 無し 有り 含まれる 含まれ無い 有り 無し Drop/Keep Action 無し 有り 出典:Robust Perception Life of a Label : https://www.robustperception.io/life-of-a-label/ Service Discoveryから Targetとラベルの生成
  45. 45. 2つのラベルに合致させたい場合 • source_labels • リストで好きなように複数のラベルを指定できる • 結果 • セミコロンで分割されて連結 • separator • セパレターを変更できます • 欠落しているラベルには空文字列が設定される • relabel_configs • リストで、好きなように複数のactionを設定できる • dropかkeepで処理が完了するまで、アクションが適用される
  46. 46. ラベルの変更 • Relabellingのメイン機能は、置換です • Source_lablesにregexを適用し、合致したものを replacementで置き換え、target_labelに結果を書き込み ます • 空ラベルはラベルが削除されたことを表します • __metaラベルはrelabellingの後、削除されます
  47. 47. EC2のNameタグをジョブ名にする例 relabel_configs: - source_labels: ["__meta_ec2_tag_Name"] regex: "(.*)” action: replace replacement: "${1}” target_label: "job"
  48. 48. デフォルトのものはもっとシンプルに書ける relabel_configs: - source_labels: ["__meta_ec2_tag_Name"] target_label: "job"
  49. 49. インスタンスラベル • サービスディスカバリー • __address__にhost:port を格納 • relabellingの終了までに、instanceラベルがない場合 • デフォルトで、__address__の値を設定 • EC2インスタンスIDやZookeeperのパスを設定可能 • 他のラベルを読みやすいインスタンス名として追加は 避ける
  50. 50. その他のラベル • schema、metrics_path、paramsはデフォルト • http/httpsかを確認できます • __param_* ラベル • 各URLパラメーターの最初の値が含まれる • 最初の値をrelabelできる
  51. 51. Query
  52. 52. ラベル {instance=“example1-com-green",job="node"} 0.005 {instance=“example2-com-green",job="node"} 0.005 {instance=“example3-com-green",job="node"} 0.005 node_load1{instance=~"(-hokan-jp|-batch|wh-plus-com|futon-jp|kutsu-jp|-jp)-green"} / count by(job, instance)(count by(job, instance, cpu) (node_cpu{instance=~"(-hokan-jp|-batch|wh-plus-com |futon-jp|kutsu-jp|-jp)-green"})) クエリ
  53. 53. node_load1[1m]の[1m]て何? irate(node_load1{instance=~".*-blue"}[1m]) 使い始めぐらいで、こういうクエリを見て フィーリングでクエリを書き始めてしまえる。あら、素敵。 クエリ書いてて、あれ?てなって、そーいえば[1m]の結果て何が返ってる??
  54. 54. node_load1[1m]の[1m]て何? Range vector って? https://prometheus.io/docs/querying/basics/#range-vector-selectors 現在から[ ]で指定した時間前までの 範囲内の値を すべて返す結果セット [5m]なら過去5分間 [1h]なら過去1時間 の記録全てを返すという意味
  55. 55. node_load1[1m]の[1m]て何? Range vector って? https://prometheus.io/docs/querying/basics/#range-vector-selectors 現在から[ ]で指定した時間前までの 範囲内の値を すべて返す結果セット [5m]なら過去5分間 [1h]なら過去1時間 の記録全てを返すという意味 5秒間隔で収集していると、1分範囲にすると約12個結果セットが返ってくる
  56. 56. node_load1[1m]の[1m]て何? Range vector って? https://prometheus.io/docs/querying/basics/#range-vector-selectors 現在から[ ]で指定した時間前までの範囲内 データ収集インターバールの指定時間が10秒にしてるときに [9s]とすると結果セットが1つ、2つとばらける
  57. 57. PromQL SQL: SELECT job, instance, method, status, path, rate(value, 5m) FROM api_http_requests_total PromQL: rate(api_http_requests_total[5m])
  58. 58. PromQL SQL: SELECT city, AVG(value) FROM temperature_Celsius WHERE country=”germany” GROUP BY city PromQL: avg by(city) (temperature_celsius{country=”germany”})
  59. 59. PromQL SQL: SELECT errors.job, errors.instance, […more labels…], errors.value / total.value FROM errors, total WHERE errors.job=”foo” AND total.job=”foo” JOIN […some more complicated stuff here…] PromQL: errors{job=”foo”} / total{job=”foo”}
  60. 60. Exporter
  61. 61. Exporter • 監視するサーバーにexporterという単一バイナリを配置 • daemonとして動かすだけ
  62. 62. 1プロセス1Exporter こっちのほうがデメリットが大きい ・1つのExporterだとボトルネックになりやすい ・単一障害ポイント ・値を選択して配信ができない ・監視が困難 ・メタデータの関連付けが難しい
  63. 63. exporterの種類 https://prometheus.io/docs/instrumenting/exporters/ に100以上リストアップされています
  64. 64. exporterのライブラリ https://prometheus.io/docs/instrumenting/exporters/ で提供されています Clojure Go Java/JVM Python-Django
  65. 65. node_exporterを利用したカスタムメトリック /var/log など特定ディレクトリを監視したい場合 -collector.textfile.directory で /var/lib/node_exporter/textfile_collector.を指定している その配下にCronなどでファイルを出力するとメトリックを追 加できる 形式:*.promで、指定フォーマットに従う必要がある
  66. 66. Grafana連携
  67. 67. Grafanaで項目名を指定する 項目名を明示しないとラベルがそのまま表示される。 {instance=“example.com”,job=“node”}
  68. 68. Grafanaで項目名を指定する 項目名を明示しないとラベルがそのまま表示される。 {instance=“example.com”,job=“node”} Legend format に {{instance}} のように表示したいラベルのkeyを「{{}}」でくくるといい。
  69. 69. GrafanaのSinglestatsでラベルの値を利用する Grafana 4.0 以上の利用を推奨 mysql_version_info{innodb_version="5.6.23", instance=“hoge.jp:9104",job="mysql", version="5.6.23-log",version_comment="MySQL Community Server (GPL)"} ラベルの値 をSinglestatで表示する
  70. 70. PromQLのデバッグ
  71. 71. PromQLのデバッグ ブラウザエクステンション https://github.com/weaveworks/weavecloud-browser-extension
  72. 72. Alerts
  73. 73. ALERT <alert name> IF <PromQL vector expression> FOR <duration> LABELS { ... } ANNOTATIONS { ... } <elem1> <val1> <elem2> <val2> <elem3> <val3> ... 結果毎に一つのアラート
  74. 74. ALERT EtcdNoLeader IF etcd_has_leader == 0 FOR 1m LABELS { severity=”page” } {job=”etcd”,alertname=”EtcdNoLeader”,severity=”page”,instance=”A”} {job=”etcd”,alertname=”EtcdNoLeader”,severity=”page”,instance=”B”} {job=”etcd”,instance=”A”} 0.0 {job=”etcd”,instance=”B”} 0.0 絞り込んだ結果 アラート発火
  75. 75. ALERT HighErrorRate IF sum rate(request_errors_total[5m])) > 500 {} 534 閾値が絶対値 サービスの成長に伴い閾値の調整が必要になる
  76. 76. ALERT HighErrorRate IF sum rate(request_errors_total[5m])) > 500 {} 534
  77. 77. ALERT HighErrorRate IF sum rate(request_errors_total[5m])) > 500 {} 534
  78. 78. ALERT HighErrorRate IF sum rate(request_errors_total[5m])) > 500 {} 534
  79. 79. ALERT HighErrorRate IF sum rate(request_errors_total[5m]) / sum rate(requests_total[5m]) * 100 > 1 {} 1.8354
  80. 80. ALERT HighErrorRate IF sum rate(request_errors_total[5m]) / sum rate(requests_total[5m]) * 100 > 1 {} 1.8354 高エラー/低トラフィック 低エラー/高トラフィック 合計
  81. 81. ALERT HighErrorRate IF sum by(instance, path) rate(request_errors_total[5m]) / sum by(instance, path) rate(requests_total[5m]) * 100 > 0.01 {instance=”web-2”, path=”/api/comments”} 2.435 {instance=”web-1”, path=”/api/comments”} 1.0055 {instance=”web-2”, path=”/api/profile”} 34.124 アラート発火
  82. 82. ALERT HighErrorRate IF sum by(instance, path) rate(request_errors_total[5m]) / sum by(instance, path) rate(requests_total[5m]) * 100 > 0.01 {instance=”web-2”, path=”/api/v1/comments”} 2.435 アラート発火 インスタンス1 インスタンス2~100
  83. 83. ALERT HighErrorRate IF sum without(instance) rate(request_errors_total[5m]) / sum without(instance) rate(requests_total[5m]) * 100 > 1 {method=”GET”, path=”/api/v1/comments”} 2.435 {method=”POST”, path=”/api/v1/comments”} 1.0055 {method=”POST”, path=”/api/v1/profile”} 34.124 アラート発火
  84. 84. Alertmanager
  85. 85. Alertmanagerの機能 • 同じアラート群をまとめる • 誰に送るかルーティングする • 再送やクローズのコントロール
  86. 86. アラートルール アラートルール アラートルール アラートルール 04:11 hey, HighLatency, service=”X”, zone=”eu-west”, path=/user/profile, method=GET 04:11 hey, HighLatency, service=”X”, zone=”eu-west”, path=/user/settings, method=GET 04:11 hey, HighLatency, service=”X”, zone=”eu-west”, path=/user/settings, method=GET 04:11 hey, HighErrorRate, service=”X”, zone=”eu-west”, path=/user/settings, method=POST 04:12 hey, HighErrorRate, service=”X”, zone=”eu-west”, path=/user/profile, method=GET 04:13 hey, HighLatency, service=”X”, zone=”eu-west”, path=/index, method=POST 04:13 hey, CacheServerSlow, service=”X”, zone=”eu-west”, path=/user/profile, method=POST . . . 04:15 hey, HighErrorRate, service=”X”, zone=”eu-west”, path=/comments, method=GET 04:15 hey, HighErrorRate, service=”X”, zone=”eu-west”, path=/user/profile, method=POST
  87. 87. アラートルール アラートルール アラートルール アラートルール Alert Manager zone eu-west でServiceXで15個のイ ンシデント発生 3x HighLatency 10x HighErrorRate 2x CacheServerSlow 個々のアラート: ... Chat JIRA PagerDuty
  88. 88. Alertmanagerのアラート群 route: receiver: infra # default receiver group_by: ['alertname', 'Service', 'Stage', 'Role'] group_wait: 30s # wait for aggregating alert group_interval: 5m # wait for alert (next time) repeat_interval: 3h # wait for alert (re-sending same one) 同じアラート、 同じサービス、 同じステージ、 同じロール が30秒以内に来たら、同じアラートとみなす 5分毎にアラート発砲する 次回送信されるのは、3時間後
  89. 89. Alertmanagerの送信先 メール・Webhook・Slack・Hipchat・PagerDuty・OpsGenie
  90. 90. {alertname=”DatacenterOnFire”, severity=”huge-page”, zone=”eu-west”} {alertname=”LatencyHigh”, severity=”page”, ..., zone=”eu-west”} ... {alertname=”LatencyHigh”, severity=”page”, ..., zone=”eu-west”} {alertname=”ErrorsHigh”, severity=”page”, ..., zone=”eu-west”} ... {alertname=”ServiceDown”, severity=”page”, ..., zone=”eu-west”} アクティブになったら、 同一リージョンのすべてのアラートをミュートにする
  91. 91. macOSのメニューバー • Line Engineerが書いたBitBarが便利 https://engineering.linecorp.com/ja/blog/detail/147
  92. 92. Prometheus というgoogleのノウハウが組み込まれた 監視ツールのお話しでした バージョン2の開発が進んでおり、間もなくリリースされる予定
  93. 93. http://sqlazure.jp/r/ コンタクト

×