Fluentd meetup #2
-
Upload
tomohiro-ikeda -
Category
Documents
-
view
7.674 -
download
0
description
Transcript of Fluentd meetup #2
1
Fluentd & TreasureDataで
こっそり始めるログ集計
Fluentd Meetup #2
@mikeda
2
自己紹介
• @mikeda
– インフラエンジニア
– Fluentd歴3ヶ月
• 会社:CROOZ
– モバイルメインのWEBサービス
– ブログとかソーシャルゲームとか
– サーバ450台くらい
• 最近個人的に作ったもの
– 『ざびたんウィジェット』
– あんまり反響が(´・ω・`)
3
今日は
「Fluentd流行ってますよ。御社でも使ってみませんか?」
「でも導入たいへんなんじゃ・・・」
「だいじょうぶ、そんなことありません!!!」
という話と、
実際にどういうふうに使っているかについてです。
4
FluentdとTreasureDataについて
• Fluentd
– 構造化されたデータ
– プラグインによる高い自由度
– リアルタイム性
→データストリームを統合管理する基盤
• TreasureData
– TreasureData .inc
– クラウドなHadoop(Hive)
– Fluentdから簡単にデータを投入できる
5
今やってること グラフ&保存(検索/集計)
• アクセスログ:2億/day
– グラフ:PV、レスポンスタイム、遅延件数(アラート設定)
– 保存:TreasureData
• アプリケーションのエラーログ
– グラフ:Warning、Errorの件数
– 保存:とりあえず無し
• メールログ(試験中)
– グラフ:キャリアごとのメール受信数
– 保存:MongoDB
傾向分析と詳細調査の両方が簡単にできるように
6
集約サーバ集約サーバ
集約サーバ集約サーバ
Active/StandbyActive/Standby
WEBWEBサーバサーバ
WEBWEBサーバサーバ
構成概要
APPAPPサーバサーバ
tdtd--agentagent
tdtd--agentagent
WEBWEBサーバサーバ
WEBWEBサーバサーバ
メールサーバメールサーバ
tdtd--agentagent
WEBWEBサーバサーバ
WEBWEBサーバサーバ
その他その他
tdtd--agentagent
各サーバに各サーバにtdtd--agentagentをインストールをインストール
新規サーバ新規サーバ
7
集約サーバ集約サーバ集約サーバ集約サーバ
集約サーバ集約サーバ
Active/StandbyActive/Standby
WEBWEBサーバサーバ
WEBWEBサーバサーバ
構成概要
APPAPPサーバサーバ
tdtd--agentagent
tdtd--agentagent
MongoDBMongoDB
ZabbixZabbixサーバサーバ
ファイルファイル
アクセスログアクセスログ
TreasureDataTreasureData
WEBWEBサーバサーバ
WEBWEBサーバサーバ
メールサーバメールサーバ
tdtd--agentagent
メールログメールログ
WEBWEBサーバサーバ
WEBWEBサーバサーバ
その他その他
tdtd--agentagent
?????? ??????
集約サーバにログを集約、各種処理とアウトプット集約サーバにログを集約、各種処理とアウトプット
アプリログアプリログ
新規サーバ新規サーバ
8
グラフのイメージ
レスポンスコードごとのレスポンスコードごとのレスポンスコードごとのレスポンスコードごとのレスポンスコードごとのレスポンスコードごとのレスポンスコードごとのレスポンスコードごとのPVPVPVPVPVPVPVPV レスポンスタイムレスポンスタイムレスポンスタイムレスポンスタイムレスポンスタイムレスポンスタイムレスポンスタイムレスポンスタイム
レスポンス遅延レスポンス遅延レスポンス遅延レスポンス遅延レスポンス遅延レスポンス遅延レスポンス遅延レスポンス遅延
サービス単位でグラフ作成サービス単位でグラフ作成((ZabbixZabbix))
アプリケーションエラー件数アプリケーションエラー件数アプリケーションエラー件数アプリケーションエラー件数アプリケーションエラー件数アプリケーションエラー件数アプリケーションエラー件数アプリケーションエラー件数
9
導入に至った経緯
• 『おもしろそう』からスタート
– 渋谷の居酒屋でC社の@oraXXXさん、D社の@ryiXXさん
Fluentdおもしろいよー
syslog-ngでいいじゃん
syslog-ngオワコンじゃん
(´・ω・`)ショボーン
。。。(話を聞く)
おもしろそうだからとりあえず使ってみるわー
※もちろんその後に事前検証、メリット検討しました!
10
普通に導入しようとすると
「えー、ログとかそんながんばんなくていいでしょ」
となるかも
(どうしても裏側は後回しになりがち)
11
でもこのあたりを抑えれば
• 人と工数
• 費用(サーバ OR お金)
• 既存システムへの影響
ちょっとやってみようか、という雰囲気を作れる
実際どうだったか?
12
人と工数
• 人→ほぼ自分だけ
– 中堅どころのLinux力
– 初心者に毛が生えたくらいのRuby力
– 調整力&政治力・・・しがない1エンジニアです(´・ω・`)
• 工数→うろ覚えだけどきっとそんなにいらない・・・
– 導入、展開は簡単→td-agent(全部込みパッケージ)
1. yum install td-agent
2. コンフィグ編集 OR rsync
3. /etc/init.d/td-agent start
– 基本は設定ファイルベース
• ググってコピペ
13
費用(サーバ OR お金)
• Fluentd
– 最初は追加サーバ1台 →2億/dayのアクセスログ+α
– 障害発生 →冗長化して2台(片方はスタンバイ)
– マルチプロセス化などの負荷対策は特に無し
– もちろん規模によりけり
• 50億/Day、100,000msg/secな人は@tagomorisさんに
• TreasureData
– 500GBまで無料(圧縮サイズで)
– それ以上の容量、CPU予約したい場合は中の人に相談
14
費用(サーバ OR お金) 集約サーバ負荷:CPU
• 古めのサーバ(HP DL360G5)1台 →けっこうつらめ
– CPU:Xeon E5430 2.66GHz 4コア
→user 20%(1プロセスなのでけっこうギリギリ)
– メモリ:8G(平常運転で使用量1G)
– HDD:SAS 15krpm、ライトキャッシュ無し
→I/O wait 8%
• もうちょっと新しめのサーバにリプレース画策中
15
費用(サーバ OR お金) 集約サーバ負荷:トラフィック
• 気にするほどじゃない
– IN(各サーバからの転送):ピークで20Mbps
– OUT(TreasureDataへの転送):ピークで3Mbps
16
既存システムへの影響
• 既存サーバの負荷増加
– 無視できるレベル →APPサーバに導入(2000万/day)
• パッケージの依存関係
– ほぼなし →td-agent(専用のruby、gem、fluentd同梱)
• 既存環境の設定変更&アプリケーションの修正
– いったんいじらない →既存ログからデータ取得
ほぼ影響なし
→ダマで入れても絶対バレない!!!
17
というわけで
• ある程度までの規模であれば
• そんなに難しいことをしなくても簡単に導入できました
みなさんも気軽に導入してみましょう!
18
ここから・・・
実際の構成、設定について
19
その1-1 アクセスログ
Fluentd
20
使ってるプラグイン
• WEBサーバ側
– tail:アクセスログをtail
– forward:集約サーバに転送
• 集約サーバ側
– forward:WEBサーバからログ受け取り
– exec_filter:フィルタリング(また後で)
– copy:出力を分岐
– tdlog:TreasureDataにデータ送信
– datacounter:条件付きカウント(@tagomorisさんThx)
– zabbix:カウント結果をZabbixに送信(@fujiwaraさんThx)
21
プラグイン構成
22
グラフのイメージ
レスポンスコードごとのレスポンスコードごとのレスポンスコードごとのレスポンスコードごとのレスポンスコードごとのレスポンスコードごとのレスポンスコードごとのレスポンスコードごとのPVPVPVPVPVPVPVPV レスポンスタイムレスポンスタイムレスポンスタイムレスポンスタイムレスポンスタイムレスポンスタイムレスポンスタイムレスポンスタイム
レスポンス遅延レスポンス遅延レスポンス遅延レスポンス遅延レスポンス遅延レスポンス遅延レスポンス遅延レスポンス遅延
サービス単位でグラフ作成サービス単位でグラフ作成((ZabbixZabbix))
23
APPサーバ側設定
• 設定ファイルの構成
/etc/td-agent/
|-- conf.d
| |-- site1.conf
| `-- site2.conf
`-- td-agent.conf
• 1サービス1コンフィグ(ちょっと管理めんどう)
24
APPサーバ側設定 共通設定
• td-agent.conf
# サイトごとの設定をinclude
include conf.d/*.conf
# forward.**は集約サーバに転送
<match forward.**>
type forward
<server>
host 192.168.1.101
port 24224
standby
</server>
<server>
host 192.168.1.102
port 24224
</server>
buffer_type file
buffer_path /var/log/td-agent/buffer/forward
flush_interval 60s
</match>
# Fluentdのログ
<match fluent.**>
type file
path /var/log/td-agent/fluent.log
</match>
# マッチしなかったログ
<match **>
type file
path /var/log/td-agent/no_match.log
</match>
conf.dconf.d/*/*ををincludeinclude
集約サーバに集約サーバにforwardforward
FluentdFluentd本体のログ、本体のログ、
マッチしなかったログはファイルにマッチしなかったログはファイルに
25
APPサーバ側設定 サービスごとの設定
• conf.d/site01.conf
– アクセスログのtail
<source>
type tail
format format /^(?<host>[^ ]*) [^ ]* [^ ]* ¥[(?<time>[^¥]]*)¥]
"(?<method>¥S+) +(?<path>[^ ]+) +¥S*" (?<code>[^ ]*) (?<size>[^ ]*)
"(?<referer>[^¥"]*)" "(?<agent>[^¥"]*)" (?<restime>[^ ]*)/
time_format %d/%b/%Y:%H:%M:%S %z
path /var/log/httpd/site01-access_log.link
tag forward.access.site01
pos_file /var/log/td-agent/site01-access_log.pos
</source>
26
集約サーバ側設定
• 設定ファイルの構成
/etc/td-agent
|-- conf.d
| |-- site01.conf
| `-- site02.conf
|-- filter
| |-- mobile_access_log_filter.rb
| `-- social_access_log_filter.rb
`-- td-agent.conf
• 1サービス1コンフィグ(ちょっと管理めんどう)
• ログの種別ごとにフィルタプログラム
27
集約サーバ側設定 共通設定
• td-agent.conf
<source>
type forward
port 24224
bind 0.0.0.0
</source>
include conf.d/*.conf
<match fluent.**>
type file
path /var/log/td-agent/fluent.log
</match>
<match *.**>
type file
path /var/log/td-agent/no_match.log
</match>
conf.dconf.d/*/*ををincludeinclude
転送を受け付ける転送を受け付ける
FluentdFluentd本体のログ、本体のログ、
マッチしなかったログはファイルにマッチしなかったログはファイルに
28
集約サーバ側設定 サービスごとの設定1段目
• conf.d/game01.conf
• 1段目:exec_filterでフィルタリング
<match forward.access.game01>
type exec_filter
command /etc/td-agent/filter/mobile_access_log_filter.rb
in_keys host,method,path,code,size,referer,agent,restime,dcmuid,auuid,sbuid,spuid
out_keys host,method,path,code,size,referer,agent,restime,uid
tag filtered.access.game01
</match>
29
集約サーバ側設定 サービスごとの設定2段目
• 2段目:カウンティングとTreasureDataへのデータ送信
<match filtered.access.game01>
type copy
<store>
type tdlog
apikey XXXXXXXXXXXXXXXXXXXXXXX
buffer_type file
buffer_path /var/log/td-agent/buffer/game01/td
use_ssl true
auto_create_table
flush_interval 300s
</store>
<store>
type datacounter
count_key code
aggregate all
tag count.access.game01.code
count_interval 300
pattern1 2xx ^2¥d¥d$
pattern2 3xx ^3¥d¥d$
pattern3 4xx ^4¥d¥d$
pattern4 5xx ^5¥d¥d$
</store>
<store>
type datacounter
count_key restime
aggregate all
tag count.access.game01.restime
count_interval 300
pattern1 0to100ms ^¥d{1,5}$
pattern2 100to200ms ^1¥d{5}$
pattern3 200to500ms ^[2-4]¥d{5}$
pattern4 500to1000ms ^[5-9]¥d{5}$
pattern5 1000to4000ms ^[0-3]¥d{6}$
pattern6 over4000ms ^([4-9]¥d{6}|¥d{8,})$
</store>
</match>
レスポンスコードごとにカウントレスポンスコードごとにカウント
TreasureDataTreasureDataに送信に送信
レスポンスタイムごとにカウントレスポンスタイムごとにカウント
30
集約サーバ側設定 サービスごとの設定3段目
• 3段目:カウンティングした値をZabbixへ送信
<match count.access.game01.*>
type copy
<store>
type file
path /var/log/fluent/count.access.game01.log
</store>
<store>
type zabbix
zabbix_server 192.168.1.10
port 10051
host service_game01
name_key_pattern (xx_count|ms_percentage|4000ms_count)$
</store>
</match>
31
exec_filterについて
• 外部プログラムで自由にフィルタを作れるプラグイン
– データ構造をちょっといじる
• 各携帯キャリアごとのUID → 共通のUIDカラムに統合
• クエリストリングから情報を取得
– 個人情報の暗号化
– 不必要なデータを除外(静的ファイルのアクセスログなど)
– �
• 乱用するとカオス化しそう
– ログ自体をいじれるならそっちで解決しよう!
※TSVよりmsgpackのほうがいいかも
32
exec_filterの例
• モバイルサイトのアクセスログフィルタ
– キャリアごとのUID→統合UID、静的ファイルの除外
#!/usr/lib64/fluent/ruby/bin/ruby
require 'digest/sha1‘path_filter = Regexp.new "^/(img|swf|css|js|healthcheck)/"
while line = STDIN.gets
line.chomp!
host,method,path,code,size,referer,agent,dcmuid,auuid,sbuid,spuid = line.split("¥t")
next if path_filter =~ path
uid = ""
[dcmuid, auuid, sbuid,spuid].each do |id|
if id != "-"
uid = Digest::SHA1.hexdigest id
break
end
end
puts [host,method,path,code,size,referer,agent,uid].join("¥t")
end
33
その1-2 アクセスログ
TreasureData
34
TreasureData
• クラウドなHadoop
– Fluentdでデータを投入
– Hive:HiveQL(SQLっぽい言語)でデータ参照
• td-agentをインストールするだけでひと通り使える
– データの投入
• Fluentdプラグイン:tdlog
• CLI:td
– 各種操作(テーブル操作、クエリ発行、結果参照)
• CLI:td
• Rubyのライブラリ
※各種言語のライブラリもあります:Java、PHP
35
集約サーバ集約サーバ集約サーバ集約サーバ
集約サーバ集約サーバ
Active/StandbyActive/Standby
WEBWEBサーバサーバ
WEBWEBサーバサーバ
構成概要
APPAPPサーバサーバ
fluentdfluentd
fluentdfluentd
MongoDBMongoDB
ZabbixZabbixサーバサーバ
ファイルファイル
アクセスログアクセスログ
TreasureDataTreasureData
WEBWEBサーバサーバ
WEBWEBサーバサーバ
メールサーバメールサーバ
fluentdfluentd
メールログメールログ
WEBWEBサーバサーバ
WEBWEBサーバサーバ
その他その他
fluentdfluentd
?????? ??????
集約サーバからのアウトプット先の1つ集約サーバからのアウトプット先の1つ
アプリログアプリログ
36
データの投入
• Fluentdに設定を追記するだけ
<match filtered.access.game01>
type tdlog
apikey XXXXXXXXXXXXXX
buffer_type file
buffer_path /var/log/td-agent/buffer/game01/td
use_ssl true
auto_create_table
flush_interval 300s
</match>
37
CLIでデータ参照
• データ確認
$ td table:tail access game01
{“referer”:“-”,“time”:1345590016,“method”:“GET”,“code”:“200”,“size”:“234880”,“uid”:“XXXX”,“host”:“xx.xx.xx.xx”,“restime”:“95961”,“agent”:“Mozilla/5.0 �","path":"/mypage/"}
• 集計$ td query -w -d access
"SELECT COUNT(distinct uid), COUNT(*) ¥
FROM blog ¥
WHERE unix_timestamp()-60*60*24 < time“�+---------+----------+
| _c0 | _c1 |
+---------+----------+
| 1927724 | 70917556 |
+---------+----------+
たいてい数分から十数分で完了(クエリと契約CPU数に依存)
※ td schema:setを実行するとv[‘uid’]じゃなくuidと書けるようになる
38
どういうふうに使ってるか
いったんは定期集計ツールではなく、
『かゆいところに手が届く調査ツール』
として使ってます
• 簡単な管理画面を作成
– Ruby & Sinatra
– Ruby部分96行、テンプレート部分は合計99行
– プロトタイプ版の一部はブログにアップしてます
39
管理画面 Home
40
管理画面 Database/Table一覧
41
管理画面 クエリ(ジョブ)発行
42
管理画面 ジョブ一覧
43
管理画面 ジョブ詳細、結果確認
44
実際の集計例
45
TreasureDataの集計例
クエリ
SELECT split(path, '¥¥?')[0],
COUNT(1) AS numreq
FROM game01
WHERE
unix_timestamp()-1*60*60 <= time
AND restime > 3000000
GROUP BY split(path, '¥¥?')[0]
ORDER BY numreq DESC
※直近1時間で3秒以上かかっている件数
をパスごとに出力
レスポンス遅延が発生してるのはどのページ!?レスポンス遅延が発生してるのはどのページ!?
46
TreasureDataの集計例
クエリ
SELECT time, restime, path
FROM game01
WHERE
unix_timestamp()-24*60*60 < time
AND uid = XXXXXXX
ORDER BY time
※指定ユーザのアクセスパスと処理
時間を時系列で出力
問合せ調査問合せ調査 「タイムアウトばっかりでアクセスできません」「タイムアウトばっかりでアクセスできません」
マイページアクセスがマイページアクセスが55秒以上かかっている秒以上かかっている
→特定ユーザの場合のみ→特定ユーザの場合のみ
タイムアウトが発生するバグがあったタイムアウトが発生するバグがあった
47
TreasureDataの集計例
クエリ
SELECT
from_unixtime(CAST(time/(5*60) AS INT)*5*60) AS day,
COUNT(distinct uid),
COUNT(1)
FROM game01
WHERE
code = 200
AND unix_timestamp()-60*60 <= time
GROUP BY CAST(time/(5*60) AS INT) ORDER BY day
※※※※5555分分分分ごとのごとのごとのごとのUUUUUUUUととととPVPVPVPVをををを出力出力出力出力
※※※※特定特定特定特定ページのみといったページのみといったページのみといったページのみといった絞絞絞絞りこみもりこみもりこみもりこみも簡単簡単簡単簡単
リリース直後にリリース直後に55分ごとの分ごとのPVPVととUUUUを確認したい!を確認したい!
48
TreasureDataの集計例
クエリ
SELECT split(path, '¥¥?')[0],
COUNT(1) AS numreq
FROM game01
WHERE
unix_timestamp()-15*60 <= time
AND path LIKE '/tutorial/%'
GROUP BY split(path, '¥¥?')[0]
ORDER BY numreq DESC
※/tutorial/から始まるパスをアクセス数
順に出力
チュートリアルの離脱ポイントはどこ?チュートリアルの離脱ポイントはどこ?
49
TreasureDataの集計例
クエリ
SELECT path COUNT(1) AS numreq
FROM mikeda
WHERE
unix_timestamp()-60*60*24 <= time
AND referer = 'http://mikeda.jp/'
GROUP BY path
SORT BY numreq DESC LIMIT 10
※リファラーで絞り込んで遷移状況調査
サイトトップからの遷移状況が知りたい!サイトトップからの遷移状況が知りたい!
50
もっと高機能なのが欲しかったら
• TreasureDataが提供するダッシュボードもある(有料)
51
導入して
• 今まで
– 集計システムは集計チームが作りこみ
• 細かいことは対応しづらい
– 特別に知りたいことがあったら
• 集計チームに依頼する
• 自分のPerlワンライナーが炸裂
• 導入して
– 「知りたいと思った人」か「その隣の人」がすぐ集計できる
– 情報取得のコスト↓、意味のある情報を知るチャンス↑
– 草の根啓蒙中
52
アクセスログまわりの今後
• サイト単位の設定がめんどう
→forestプラグインで統合を検討中
• ログのフォーマットを整理したい
– 整理できればexec_filterいらないかな
53
その2 Postfixのmaillog
※試験中
54
メールログ解析
• モバイル系のサイトはメールが多い
– ブログ投稿、新規登録
– 不正も多い・・・
• グラフ化&検索できるようにしたい
※試験導入なので参考程度に。遅延時の挙動など未確認
55
Maillogのパース
• すごくたいへん
– 複数行で1セット&混ざりまくり
– 複数サービスで共有(to=<xx@yyy>で振り分けが必要)
• 単純にカウントするだけならto=だけ取得すればいいけど・・・
• もっとFluentdっぽくやりたい!
モザイクものモザイクもの
56
in_tailを拡張する
• 必要な情報を独自プラグインで組み立ててから転送
• 標準プラグインのin_tailを拡張
– ローテート対応、デーモン停止時の取りこぼし防止などはin_tail
にまかせる
– やっていいか迷ったけどすでにやってるのがあった
※追記)@frsyki『in_tailはそういう設計で作ってるのでどんどん継承
しちゃってください』
– 自作プラグインは/etc/td-agent/plugin/に置くだけで動く
– in_tailのコードは最初の100行くらい読めばいい
※コードのプロトタイプは@mikedaのGistにアップしてます
57
プラグイン構成
58
使っているプラグイン
• メールサーバ側
– maillog_tail:(in_tailの独自拡張)
– forward
• 集約サーバ側
– forward
– rewrite_tag_filter:サイト単位に振り分け
– forest:タグを見て動的にプラグインを複製
– mongo:mongodbにデータを出力
– datacounter:特定キーで件数カウント
– zabbix:カウント結果をzabbixに送信
59
メールサーバ側の設定、出力データ
• コンフィグ(基本的にin_tailと同じ)
<source>
type maillog_tail
path /var/log/maillog
tag maillog.test
pos_file /var/log/td-agent/maillog.pos
</source>
• 出力データ
2012-08-05T04:02:27+09:00 maillog.test {
"qid":"AF06E10339D7",
"from":"[email protected]",
"to":"[email protected]",
"orig_to":nill,
"size":"114364", "relay":"local", "delay":"1.5", "status":"sent"
}
60
集約サーバ側の設定 1段目
• 1段目:rewrite_tag_filterでサービス単位にバラす
→送信先ドメインでタグを付け替え
<match forward.mail.rcv>
type rewrite_tag_filter
rewriterule1 orig_to @blog¥.mikeda¥.jp$ filtered.mail.rcv.blog
rewriterule2 orig_to @wiki¥.mikeda¥.jp$ filtered.mail.rcv.wiki
�
</match>
61
集約サーバ側の設定 2段目
• 2段目:各種キーでカウンティング&mongodbにデータ投入
<match filtered.mail.rcv.*>
type copy
<store>
type forest
subtype datacounter
remove_prefix filtered.mail.rcv
<template>
count_key from
aggregate all
tag count.mail.rcv.__TAG__
count_interval 300
pattern1 docomo [@.]docomo¥.ne¥.jp$
pattern2 au [@.]ezweb¥.ne¥.jp$
pattern3 softbank [@.](softbank¥.ne¥.jp|vodafone¥.ne¥.jp)$
</template>
</store>
<store>
type mongo
�
62
集約サーバ側の設定 3段目
• 3段目:カウントした結果をZabbixに送信
<match count.mail.rcv.*>
type forest
subtype zabbix
remove_prefix count.mail.rcv
<template>
type zabbix
zabbix_server 192.168.1.10
port 10051
host service___TAG__
add_key_prefix mail.rcv
name_key_pattern count$
</template>
</match>
63
グラフできた!
64
気になったら集計
• 気になるところがあったらmongodbで集計
『業者さんや・・・』
65
その3 アプリケーションエラーログ
66
省略!
67
まとめ その1 Fluentd
• けっこう簡単に導入できる!
• 現状:アクセスログ、メールログ、アプリエラーログ
– まだまだ試験中な感じですみません
– 使わないとできないわけではないけど、使うとすごく楽
– in_tailはこっそり集計のキモ
• ちょっと手を加えたい時はexec_filterかin_tailいじるか?
• 検討、要望
– アプリチームを巻き込んで次の段階を検討中
→自由にログ出してもらってGrowthForecastで自動グラフ化
– プラグインの設定、内部状態ダンプ機能が欲しい
68
まとめ その2 TreasureData
• とにかく簡単にHadoopが使える
– Fluentdでデータ投入
– HiveでSQLっぽく集計
• 1エンジニアが勢いで最初の一歩を突破できる!
最初の一歩が大きな壁だった最初の一歩が大きな壁だった
コスト:コスト:HWHW、技術的コスト、技術的コスト
リスク:コストとメリットが不明確リスク:コストとメリットが不明確
((´́・・ωω・`・`))ショボーンショボーン
69
終わり!!!