のほほんおじさんのアウトプット

インフラエンジニア。 マリノス・美術館・編集工学・AWS・ウイスキー・いぶりがっこ・タルタルが好きです。好き勝手やっていこうかなって気持ちです。

心機一転したい

なんだか変化を欲している

前厄の年齢となり、ちょっとこれまでの自分を振り返ってみたくなった。 そして、今とはまた違う場所に行きたくなってきた。

ということで、これまで自分がアウトプットしてきたくだらんことをまとめてみようと思い立った。

思い返せば

はてなダイアリーBloggerWordPress(さくらインターネットでのホスティング) → はてなBlog

というように気分転換して続いたり続かなかったりして今に至る。

で、改めて過去の自分の書いていることを振り返ってみると、とても興味深い。 自分にとってだけ興味深いってことだけなんだけど、それでもいいなって思った。

ということは、今思っていることをアウトプットしておくことは後々見返したときに、やっておいてよかったと思うって可能性が高いなあと思ったので、自分のためにだけアウトプットしておこうと決めた。

で、静的サイトジェネレーター

まあ今なら静的サイトジェネレーターかなと思って、しばらく前から気になっていたHugoに取り組んでみた。

何度か挫折したけど、今回はなんとか形になったのでこれでやっていこうと思う。

非公開にしてあった過去の記事をMarkdownに変更しつつ振り返りながらHugoにまとめていければいいな。

移行先はGithub Pages

10年前の自分は何を考えていたんだっけな?

今になってみたらくだらんって思えることも、10年前は本当に辛かったな。

10年前はラーメンばかり食べて体調崩したな。

また、10年後に同じような気持ちになるためにhttps://kothiba.github.io/tarutaru-life/に記録していこう。

転職して1年経ったので振り返ってみる

自分の記録として振り返り

不安だらけの転職でしたが1年経って、転職して良かったと思えています。

特にAWSを1年間触れ続けられたことはとても良い経験ができたと思っています。出社してAWSマネージドコンソールを立ち上げなかった日は一日もなかったと思います。

特に前職では営業的な動きとか社内外の調整や問い合わせだけで一日が終わることもあった中で、この1年でずっとAWSに触れ技術的なことに専念できたのは、転職したからこそかなって思っています。

IT系の情報サイトで資料をダウンロードするときに自分の職種や会社規模を選択する必要があるかと思いますが、そんな時にいつも自分の職種に「エンジニア」と素直に選択できない自分がいたのですが、今は以前と比べれば自信をもって「エンジニア」を選択できています。そんな些細なことですが、実はこれが一番大きな変化なのかもなってこれを書いてて感じました。

改めて、何をやったか記録として上げてみて、何が良かったか振り返ってみようと思います。

2019年4月

  • 監視基盤の構築
    • コンテナ(Fargate)で作ってねって言われて、Dockerの勉強から開始
    • シナリオ監視をJMeterでやってねってなる。
    • CloudFormationに落とし込むっていう縛りも、、、

FargateうんぬんよりもDockerfileをいかに賢く作成するかってことが重要でした。CloudFormationもyamlには縁遠い生活から一転して最低限読めるくらいにはなりました。

転職して初の案件がいきなりServerlessで、聞いたことしかないAWSサービスたちを利用する状況に目眩がしましたが、ここでなんとかやりきるベースができたことが良かったなあと感じています。

kothiba538.hatenablog.com

kothiba538.hatenablog.com

2019年5月

  • 監視基盤の構築
    • Fargateからの連携で、エラーを通知するLambdaの利用

Lambda?Python?まったく分からん、の状況でしたがこれは他のチームメンバーが作成してくれていたのでドキュメント作成を担当。実はここでPythonのコードの内容をしっかりと把握してなかったことが後に自分の首を締めることになるとは思いもよりませんでした、、、

2019年6月

  • 監視基盤構築
    • Fargateのエラーハンドリングって話になってどうしよう?ってなる

6月といえばちょうどAWS Summitが開催されている時期。初参加しつつ、これはチャンスと思い「Ask a Expert」でAWSの中の人にFargateのエラーハンドリングについて聞いてみたら、基本的な対応方法を教えてもらえて、さらにStepFunctionsでのエラーハンドリングも教えてもらった。

ここで聞いてた内容がのちに共有されていたので汎用的な悩みだったんだなと思うとともに、割と新しめなことに触れられていたんだなと分かって嬉しかったです。

2019年7月

  • 引き続き監視基盤構築
    • Lambdaの運用を引き継ぐことに、、、

Lambda?Python?まったく分からん、の状況は引き続きでしたが、業務的に引き継いで対応しないといけなくなったので、さらにドキドキが深まる。

少なくともこの時点で作成されていたPythonコードの内容をそれとなく理解できるようにはなってきた。でもこれはもっと根深い問題で、今でも色々と悩みの種を撒き散らしています、、、

2019年8月

  • 引き続き監視基盤構築
    • ずっと続いていた監視基盤構築プロジェクトも一旦リリースの目処が立ち、ドキュメントの作成に追われる。

仕様書とか書いたことなかったので、レビューでの指摘を受けて泣きそうなりながら修正を繰り返す。こんなドキュメントばかり作成するために転職したんじゃない!とイライラしたので、なんとか業務外で技術的なことに触れないと、と思いKnativeを少し調べだす。

2019年9月

  • 引き続き監視基盤構築
    • 本番環境にデプロイしていくために、スイッチロールとかAWSCLIとかを利用するようになる。こういのは実務でやらないと触れられないのでいい経験になる。また、運用として監視基盤を面倒みるということで、運用のツラミを再認識。このツラミをなくすためにも設計段階で色々と考慮すべきなんだなあって切実に感じられました。

2019年10月

  • 複数案件を対応
    • 監視基盤構築は継続
    • クラシックなAWS構成(EC2/ELB/RDS)
      • CloudFormationでの環境構築

最近では割と否定されがちなクラシックなAWS構成(EC2/ELB/RDS)ですが、最初のAWS体験がServerlessなこともあって、ここでは基礎的な構成をいかに構築していくかを学ぼうと愚直に対応しました。

既にAWSにある環境を移行したり、他社クラウドからの移行だったりとまちまちでしたが、既存環境とは別に新規でAWS環境を構築するって内容だったのでデータの移行とかまでは意識しなくてよかったのは楽ちんでした。で自分的には基礎的ないい経験ができたと思っています。

特に既存環境もCloudFormationで作成されていた環境については、具体的な引き継ぎやヒアリングの機会がなかったのですが、CloudFormationの内容を見ることで意図していることが理解できて、ほぼ同じ設計思想で新規環境を構築できたので、infrastructure as a codeって凄いなと実感しました。

2019年11月

  • 複数案件を対応
    • 監視基盤構築は本格運用開始
    • クラシックなAWS構成(EC2/ELB/RDS)

監視基盤案件はLambdaの本格運用が開始されて、そこでのちょっとしたバグとか本番リリースの対応でドキドキして蕁麻疹ができたりで大変でした、、、

でも、そこでさらにPythonを知らないといけなくなったので、否が応でも学ばねばならず、、、大変ではあるもののおかげで初めてプログラミング言語としては知っていると言ってもいいくらいのレベルにはなってきたのかなと感じました。こういうキッカケがないとPythonには触れなかったと思うのでこれはこれでとても良い経験になりました。

2019年12月

監視基盤はPythonだけではなく、Lambdaの知識もついていくし、テストを実施するためにCloudWatch周りの工夫も必要で、そういった周辺の知識がついていったのは良い経験でした。

またクラシックな構成の中にもCloudFrontやDirectConnectもあって、業務じゃないと触らないようなAWSサービスを一通り経験できたのは良かったです。

そして人生初のアドベントカレンダーに参加。Qiitaの投稿も初でした。やってみたいと思いつつ背中を押してくれる何かがないと一歩踏み出せない自分としては社内でアドベントカレンダーを企画してくれていたというのはとても嬉しいことでした。ここでも転職できてよかったなあと感じました。

qiita.com

2020年1月

  • 複数案件を対応
    • 監視基盤構築は本格運用開始
    • クラシックなAWS構成(EC2/ELB/RDS)
  • 社内もくもく会の講師を担当

監視基盤の本格運用で得た知識をもとに、別案件で1人でLambdaを構築し客先ではレビューしてPythonの説明もして、なんていう転職前では考えられなかったことも対応できて、少し自信がついてきました。

Pythonについてはチームメンバーによるテストコードの情報共有のおかげで当初よりは不安もなく対応できるようになったのは大きな収穫です。前職でもそうでしたが現職でもチームメンバーに色々と教えてもらえるのはとても有り難くて、環境には恵まれているなって本当に感じています。

あとは社内のもくもく会で講師をできたのもいい経験となりました。全然知らないKubernetesについての講師でしたが、詳しい方にサポートしてもらいつつ、基礎的な部分をさらっと説明しました。これはやはり講師としてアウトプットする立場の方がかなり知識がつくので、やれてよかったですね!

qiita.com

2020年2月

  • 複数案件を対応
    • 監視基盤構築は本格運用開始
    • クラシックなAWS構成(EC2/ELB/RDS)
  • 社内LTでPythonについて話す

クラシックな構成の案件では、知っているようで実作業はしたことがなかったEBSのリストアやEBSの暗号化を経験したり、EC2からメールを送信するためのAWSへの制限解除を英語メールでやり取りしたりと、業務でやってこその経験ができたので良かったです。

社内LTで話せたのも良い経験になりましたし、そこでプログラミング初心者おじさんの自分がPythonについて話すなんて転職前では想像もできないことでした。

それくらいPythonには慣れ親しんだと思っていたときに、監視基盤のトラブルシューティングPython全く理解できていないのではって事態に陥り、ガクッと自信をなくしました、、、

2020年3月

  • 複数案件を対応
    • 監視基盤構築は本格運用開始
    • クラシックなAWS構成(EC2/ELB/RDS)
  • CloudFormationのCI/CD
  • セキュリティ関連サービスの深堀り

案件的にはなんとか期間内にクローズできて、案件対応をこなせたという事実は大きな自信となりました。

一方で案件で利用したCloudFormationを案件特有の設定値をなくして他の案件でも使い回せるように汎用的にしていきたい想いがあって、それをGitLabのCI/CDを絡ませて形として残したいと思い試行錯誤していました。おかげでGitLabの使い方やその前段階のGitの使い方にも慣れてきたのは、これも転職前には思いもよらなかったので経験できてよかったことの1つになっています。

まとめ

改めて振り返ってみてコツコツとではありますがAWSの経験を積めていたことが実感できました。

ラクティショナーとアーキテクトの2つの資格は取得できて、今年度はアーキテクトプロフェッショナルの取得が業務命令なので大変ではありますが、資格勉強がプロフェッショナルで要求される範囲のAWSサービスに触れられるきっかけになるので、前向きに頑張っていきたいです。

こうしてAWSという取り組むべく軸ができたってことはとても嬉しいですし、これに取り組むことが仕事での成果に直結するのは分かりやすい指針になったので、こういった指針を持てる状況になったことが転職したことによる大きな成果かなと感じています。

そして2年目は、ちょうど会社としても本格的に取り組みだしたアジャイル開発に絡んでいきたいですね。

具体的には「認定スクラムマスター(CSM)」は取得しておきたいし、チームを作る、育てる、盛り上げる、機能させるみたいな動きはアラフォーの生存戦略として最低限できるようになりたいですね。

もちろんAWSの技術は追い続けて自分の手で構築できるようになるのは続けつつも、周りのメンバーや若手に色々教えてもらったりできる素直なおじさんになっていきたいです。そういうリーダー像(オーセンティック・リーダーシップ)も存在しているみたいですしね。

kothiba538.hatenablog.com

幸いなことに体が割と丈夫にできているので、2年目もまた発生するであろう苦しい状況もなんとか前向きに乗り切って、アウトプットを少しでも多く出せるように元気に頑張っていきたいです!!

再演!エンジニアのためのコーチング入門に行ってきました。

coaching.connpass.com

「エンジニアのためのコーチング入門」なるものに行ってきたので、自分用のメモです。

個人的なモチベーション

  • 数ヶ月前からメンターになったのだけど、どうしたら10歳以上年の離れた新人を育てられるのか知りたい
  • あと以前、すごい新人と一緒に仕事をしたことがあって、その子はすでに自分にとっての基準みたいのを明確に持っていた。それは会社内の技術じゃなくてもう少し大局観をもって自分のエンジニア人生のために目の前のことをやるかやらないかみたいな判断をするような基準で、そういう基準を自分で持つことができるように育てる方法はないのか気になっていた

開催にあたっての主催者の問題意識

  • 1on1って学ぶところ少なくない?
  • 1on1嫌がれるけど、これってやり方が悪いだけだよね。とはいえ1on1って学ぶところが少ないよね。
  • 1on1はエンジニアリングマネジメントの1つの手段
  • コーチングとの関係としては1部重なっている箇所があるって感じ
  • で、1on1やコーチングのやりかたも大事だけど、まずは「あり方」だよ

人と話すことが苦手な人の方が、コーチングは上手くなる / 三橋 新さん

今の世の中でコーチングが必要とされる理由、ティーチングとコーチングの違い、コーチングを実施する際のポイントなどをご紹介します。

speakerdeck.com

メモ

  • コーチングで1番大事なこと
    • Doing < Being
  • どんなBeingでいるべきか
    • 相手の可能性を信じる
  • それを体験から実践してみましょうということで2人1組で実践

    • 前提を指定された上で写真見てどんな感覚になるかを体験
      • この人はとてもイマイチだなと思って見る写真
      • この人は可能性に溢れていると思って見る写真
        • どういう前提で他人を見るか、可能性があると思って見ると良いところ探そうとするし、イマイチだと思っているとアラ探ししてしまう自分がいた、、、
  • 具体と抽象の話

    • 現実には3つのレベルがある
      • 合意的現実レベル
        • KPIのようなもの
      • ドリーミングレベル
        • 目標のようなもの
      • エッセンスレベル
        • 直感のようなもの
    • で、仕事ってわりと上の階層(合意的現実レベル)でやっているけど、コーチングではエッセンスレベルを活用すると良かったりする
      • 今の気分は何色?とか温度とか気分的な質問もするといい
    • あなたの強みを家電に例えると何ですか?
      • この質問の面白さは、話しているうちに話している方の頭が整理されていって、自分の強みがもっともらしく思えてくる点
  • まとめ
    • Being大事に!
    • 抽象的なことを考えて話して、そこで言語化していくうちにまとめていくみたいな流れがコーチングだったりする

エンジニアリング組織の1on1におけるコーチングスキルの活かし方 / 谷内 真裕

あなたは、どんな1on1を受けたいですか?1on1はコーチング・メンタリング・ティーチング・フィードバックの枠に収まりきらない「傾聴・対話・信頼・変化を生み出す時間」です。だからこそ、1on1を受ける側にも寄り添いたい。“エンジニアの1on1あるある”を織り交ぜながら、すぐに試したくなるコーチングスキルの活かし方についてお話します。

speakerdeck.com

メモ

  • 話す内容
    • ありかた
      • 自己探求と自己管理
      • 向き合い方
    • やりかた

自己探求と自己管理について

  • コーチしてるけどそれ自分ができてるの?なんて自分のことが気になってくると、他人に関われなくなる
    • 相手の全てに焦点をあてる(話だけでなく、全てに焦点をあてよう)
      • だめな例
        • あるある
          • こういうときって、相手の話を聞いてない、って状態だよ
        • どうして?
          • 誰もがBelifというメガネをかけている(自分のフィルターをかけている)
          • 上司が部下をコントロールしたい、せねば、全部教えてないと、救わないと、
          • 相手を変えるのが当たり前!それが1on1だ!とか、、、
            • これらはすべて「囚われ」だったりする → 凝り固まったBelifだよそれ
  • で、そんなときには自己探求して、暗黙的なBelifを探すこと!
    • とはいえBelifが悪者ってだけでもない、、、
  • コーチは自己管理をしないといけない
    • 自分の内なる声を横に置くこと
      • 置けるようになること、これは自己探求のたまものだったりする
        • で、結果「相手に注意を向けられる」ということになる!
          • そうすると相手も「よく聞いてもらえた」ってなる
  • 自己探求のためのリソース
    • スライド参照、いろんな本がある
  • まとめ
    • 囚われに気づくこと、そしてその囚われをなんとかコントロールすること

向き合い方について

  • 1on1はやるひとのあり方がおかしいと、相手に威圧的になっちゃう
  • でも完璧な人間である必要はない、それぞれの個性を活かせばいい
    • NCRW(people are Naturally Creative, Resourceful and Whole) 人はもともと創造力と才知に溢れ、かけるところのない存在なのだ
      • 相手の問題もその解決策も「すでにそこにあるもの」と、考えるとして、じゃあどうやって問えばそれを相手は気付けるのだろうか。
      • 好奇心をもち可能性を信じることが重要
        • そのためにも、まずは自分を信じること、相手を信じている自分を信じているところからスタートしないといけない
        • で、好奇心もって直感と遊び心をもって関わっていく
      • コーチングしていると、相手もそうだけど自分も変わっていくもの。先に自分が信じることが大事!

場作り

  • これができていないから、1on1が効果がでなくなる
  • それを回避するために、、、
    • 導入セッションをする
      • 目的、話すこと、約束(スライド参照)
  • 今、この瞬間から創る
    • 導入セッションとかしっかり準備していたとしても、そうじゃない雰囲気もある
      • そんなときには、その場をスタートとしてやってOK
    • そう思っていれば、導入セッションも大事だけど、カフェでも散歩でもいい
    • 1対1の人として遊び心をもって楽しく関係をつくっていきましょう

コーチングのスキル

  • スキルはいっぱいあるんだけど、コーチングの専門用語を暗記するのではなく、専門用語になっているものは、大体相手にインパクトをあたえるものを言語したものになっている
  • なので、用語とかよりは、そのインパクトに注目的すべき
  • でもスキルの前にまず関係がだいじ
    • その基礎が傾聴
      • 意識の焦点はどこにあたっている?
  • コーチングも練習が大事
    • 2人いて10分あれば練習ができる(練習方法はスライド参照)
    • 相手にあたえるインパクトがスキル
    • あとは遊び心
      • でもって練習するのだ大事
      • 普段の生活って大体決まりきっていて、その生活の中にはそこまで振れ幅みたいなのはない。でも人はちょっとサッカー観戦行ったり、ライブ見に行ったりすると普段とは違う振れ幅みたいなのに出会える。そういう振れ幅を重視しつつ遊び心で試してみるといい

エンジニアリングマネージャー育成におけるコーチング / 安西 剛

1on1は、現場のメンバーだけでなく、マネージャー育成にも有効です。ただ、現場のエンジニアとマネージャーだと、目的や業務などが違うため、違うアプローチを求められます。全体を俯瞰し、リーダーとは何か、マネージャーとは何か、自分の在り方など、多面的に考え行動を続ける必要があります。今回は、その基本的な考え方をお話します。リーダーやマネージャーとして活動している方々ご自身にも参考になるような内容にします。

メモ

  • エンジニアリングマネジャーの話
    • EMのための話をする
    • 自分がチームリーダーになったとしたらどういう振る舞いをしたらいい?
  • エンジニアリングマネージャーとして成長するために必要なことは?
    • 大体、会社って中間層が課題
      • 中間層=マネージャー
    • マネージャーになると、より「あり方」が大事になる
      • エンジニアの間は「やり方」
      • どうしてかといえば、あり方がチーム内に広まる、なので「あり方」をよくしていかないとってことになる
  • オーセンティックであるか?

  • このオーセンティックはかなり重要な概念(欧米では良いリーダーにはこれが必要となっている)
    • 「自分らしさ」を貫いていること=オーセンティック
      • ステレオタイプのグイグイ引っ張っていくようなリーダーシップはもう多様性についていけなくて、あまり成果が上がっていないのが実情
        • つまり、マネージャーだから、リーダーだからとかってことでリーダーシップを発揮するということは考える必要がもうないってこと
  • だって、リーダーらしく振る舞っているけど、それって無理してない?
    • その無理、バレバレだよ、、、
    • 無理して嘘ついて分かった振りをして、それが嘘だってバレたときの信用の落ち込み度合いはとんでもないものになる、、、
  • オーセンティックであるための3つのポイント
    • セルフアウェアネス
      • 自己認識を高める
        • 自分とも向き合う(成果や結果だけでなく自分が成長していますか?ってことを考える)
        • フィードバックを受け入れる

セルフ・アウェアネス (ハーバード・ビジネス・レビュー [EIシリーズ])

セルフ・アウェアネス (ハーバード・ビジネス・レビュー [EIシリーズ])

  • セルフコンパッション
    • 弱さを認める
      • 弱い自分を認められている?
      • 弱点がわかれば、そこを補う対策をとることも可能
    • 弱さや恥を他人に話せているか?さらけ出せている?
      • 自分の弱点を部下に見せれている?
        • これができれば、みんな助けてくれるかもしれない
    • 結果、ありのままでいいんだよってこと
      • 強いリーダーシップがありのままならそれでいいし、弱ければ弱い自分を見せていこう
  • まとめ
    • オーセンティックであることで
      • 学び続けられる
      • 覚悟を持っていられる
        • 正解はない、自分にとってのオーセンティックを継続すべき

で、今回の話を聞いての個人的な感想

  • コーチングや1on1ってもっと仕事にフォーカスするものかと思っていたらそういうものではなく、より全般的にその人に対する興味とか、相手を1人の人間として見るという部分にフォーカスする行為だった。なので、必ずしもコーチングや1on1だけで職場で起きている全ての問題が解決されるわけではないということは、今まで自分が思っていたコーチング観とは違っていてそれを知れて良かったと感じた。コーチングや1on1が銀の弾丸と思っていたので、、、
  • で、そこからさらに相手を1人の人間としてどう見るかというときに、コーチする側の「あり方」が大事だということを学べた。個人的な理解としては、まずスタートが人間として相手に興味をもつことであり、そこで作られた人間的な関係性の延長線上に職場での関係性(仕事)があって、日々のコミュニケーションの中で仕事についての問題も話されていき、そこで問題が明確になって情報を共有して一緒に解決策を考えた結果、仕事上の問題がクリアされることもある、そんな感じなんだなと思いました。
  • とはいえ、人間的な関係性の構築がなあなあな関係になっては本末転倒だなとも感じました。その辺りのさじ加減がとても難しいんだろうな、、、

Fargateでのコンテナの上書きで大いにハマってしまった、、、

Fargateでのコンテナの上書きについて

  • Fargateでコンテナの上書きをしようと思うと以下の2つができます。

f:id:kothiba:20190520223542p:plain

  • で、ちょっとコンテナの上書きをすることで解決したいなって問題があったので、いろんな事例を調べてみました。結果、Dockerfileの中身を上書きできるんだろうなって結論に至ったわけですが、どうやらそこで今回とんちんかんな認識をしてしまった結果、大いにハマってしまったという経験をしました。
  • 参考にしたのは以下など

解決したい問題

  • Fargateで作成したJMeterコンテナがあって、こいつはシナリオファイルを引数にとるのですが、引数を環境変数として規定して、環境変数を上書きすることで引数にとるシナリオファイルを変更する(違う種類のシナリオを実行する)ようにしたい、というのが今回解決したい問題でした。

    解決前の状況

  • FargateでJMeterコンテナが実行します。
  • Dockerfileには、以下を記載しています。
    • ENTRYPOINTでjmeter.shを実行する
    • CMDで-n -t シナリオファイル
  • 結果的に、jmeter.sh -n -t シナリオファイルというコマンドを実行するコンテナとなり、シナリオファイルに沿った内容のWEBチェックを実行します
FROM alpine:3.9

ARG JMETER_VERSION="5.1.1"
ARG PLUGIN_VERSION="1.4.0" 
ENV JMETER_HOME /opt/apache-jmeter-${JMETER_VERSION}
ENV JMETER_BIN  ${JMETER_HOME}/bin
ENV JMETER_DOWNLOAD_URL  https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-${JMETER_VERSION}.tgz
ENV PLUGIN_DOWNLOAD_URL  https://jmeter-plugins.org/downloads/file/JMeterPlugins-Standard-${PLUGIN_VERSION}.zip
ENV SCENARIO_DOWNLOAD_URL [S3に配置したJMeterのシナリオファイル.jmx]

# Install extra packages
ARG TZ="Asia/Tokyo"
RUN    apk update \
    && apk upgrade \
    && apk add ca-certificates \
    && update-ca-certificates \
    && apk add --update openjdk8-jre tzdata curl unzip bash \
    && apk add --no-cache nss \
    && rm -rf /var/cache/apk/* \
    && mkdir -p /tmp/dependencies  \
    && curl -L --silent ${JMETER_DOWNLOAD_URL} >  /tmp/dependencies/apache-jmeter-${JMETER_VERSION}.tgz  \
    && curl -L --silent ${PLUGIN_DOWNLOAD_URL} >  /tmp/dependencies/JMeterPlugins-${PLUGIN_VERSION}.zip  \
    && curl -L --silent ${SCENARIO_DOWNLOAD_URL} >  /tmp/test-plan.jmx  \
    && mkdir -p /opt  \
    && tar -xzf /tmp/dependencies/apache-jmeter-${JMETER_VERSION}.tgz -C /opt  \
    && unzip -oq "/tmp/dependencies/JMeterPlugins-${PLUGIN_VERSION}.zip" -d $JMETER_HOME  \
    && rm -rf /tmp/dependencies  

ENV PATH $PATH:$JMETER_BIN

WORKDIR ${JMETER_HOME}

ENTRYPOINT [ "jmeter.sh" ]
CMD [ "-n" , "-t" , "/tmp/test-plan.jmx" ] 
  • で、シナリオファイルを差し替えて実行したい場合は、上記のDockerfileに記載した内容のENV JMETER_DOWNLOAD_URLをFargateの環境変数の上書きで上書きすればいいだけだと考えたのです。

全然うまくいかないよ、、、

  • でも全然うまくいかない。これはFargateのせいかと考えて色々と調べてみた結果、Fargateの問題ではなく、そもそもDockerfileの書き方が良くないんじゃないかという事に、思い至りました。
  • というか、、、ENV JMETER_DOWNLOAD_URL自体はちゃんと上書きされていた、、、
  • でもそうなると、なぜうまくいかないのか?と思いましたが、以下のように考えると納得できました。
    • ENV JMETER_DOWNLOAD_URLの値自体が上書きされても、DockerRun時に、その次の行以下に記載されているRUN内容が再実行することってないよね?
    • うん。一度Buildされているから、DockerRunするたびにイメージをBuildするなんて意味不明な動きをDockerがすることはないよな。と、、、

一度に2度の作業をするコンテナを作りたい

  • で、そうなってくると今回作ったJMeterのコンテナって、以下の2つのことを一度に実行してくれないと困るってことになります。
    • jmeter.shを実行すること
    • シナリオファイルをダウンロードすること

ならば簡単だ!ENTRYPOINTCMDを複数書けばいい!

  • が、話はそんなに簡単ではありませんでした。
  • まず、CMDはENTRYPOINTの引数のような形で記載しないといけない
    • なので、ENTRYPOINTでjmeter.shを実行しているのに、CMDでcurlを実行しようとするとjmeter.shにはcurlなんてオプションはありませんよって言われる、、、
    • であれば、ENTRYPOINTは使わない!CMDを2個書いてやる!となるのですが、CMDは複数書いても最後に書いた内容しか実行されない、、、
  • やばい、、、どうすればいいんだ??

シェルスクリプト

  • で、このシェルスクリプトをどう書くかっていうのがセンスだよなあって痛感しました。自分はここで相当悩み、助け舟を出してもらい解決策を見出しました。こいうところでサクッとスクリプトを書けるような人間になりたいなあ!!
#!/bin/bash
curl -L --silent $1 > /tmp/test-plan.jmx
jmeter.sh -n -t /tmp/test-plan.jmx
  • $1で第一引数をcurlしてシナリオファイルとして保存します
  • 保存したシナリオファイルをjmeter.sh -n -t /tmp/test-plan.jmxで実行する
  • そして、Dockerfileに以下のように記載してCMDに第一引数となるシナリオファイルの保存先を指定する
COPY run.sh /tmp/
ENTRYPOINT [ "sh" , "/tmp/run.sh" ]
CMD [ "S3上のシナリオファイル" ] 
  • こうすることで、コンテナの上書きであるCMDの上書きを利用してシナリオファイルの引数をいかようにも書き換えられるということになります。

ということで課題クリア!

  • シナリオファイルが複数になった場合の対応は、上記の対応策で解決です。
  • また、JMeterのバージョン変更とか大きな変更のときはビルドし直す必要がありますが、そんなときはイメージにつけるタグで管理します

まとめ

  • Fargateはあくまで実行環境で、実行の大元となるDockerfileを正しく理解することがコンテナを利用する際には必要なんだと感じました。
  • Dockerfileのコマンドの意味が理解できてくると、Fargateってこんなに便利なんだなって理解できるようになりました。
    • 例えば今回のFargateのコンテナの上書き機能として利用したCMDの上書きも、docker上でいうdocker run image名 [CMDの上書き]コマンドと同じ意味で、Dockerで使える機能がAWS上で気軽に使えるっていうのは凄いことなんだなって思いました。

ECS(Fargate)でJMeterを構築してみる

ECS(Fargate)でJMeterを構築するっていうタスクに従事中

  • タスクを振られたのでなんとか対応せねばなりません
  • しかも、以下のような縛りがあります
    • JMeterでシナリオ監視をせよ
    • ECS、しかもFargateで構築せよ
  • ちなみに、ECSやったことない、、、一応AWSのコンテナサービスだってことくらいは知っているくらい
  • あと、JMeterもあのWEBサイトに試験的に負荷をかけるアレでしょ、程度の知識しかない、、、

とりあえずググる

  • まず出てきたのは以下の記事

tkaaad97.hatenablog.com

  • ドンピシャかと思いきやFargateではなくEC2でクラスタを構築するとのこと、、、
  • でも、上記の記事はとても参考になりました。50回くらいは読み込みました!

必死にググることで得たこと

  • ECSではFargateEC2を選ぶことができる
  • FargateEC2クラスタを構築してコンテナを動かすといった管理は不要でコンテナを実行できる
    • 上記のような基礎的なことはこちらに完璧にまとまっています

dev.classmethod.jp

  • ちょうどこのタスクを始めて1週間たった時期の記事で、タイムリーでとても助かりました!
  • コンテナでJMeterを動かす、という視点が重要だってこと
    • Dockefileを用意しないといけない
    • Dockerfileの読み書きくらいはできるようにならないといけない

ではコンテナでJMeterを動かさなくては

  • DockerfileはDockerHubから拝借します
    • 参考にさせてもらったのは以下

hub.docker.com

  • とくにENTRYPOINT ["/entrypoint.sh"]理解できるまでとても難しくて、この参考例の場合はあくまで負荷検証ツールとしてリソースをどこまで割り当ててJMeterを起動させるかを考えた上でのENTRYPONTになっていた
    • 自分がやりたいのは、単純にシナリオが1回動けばいいだけ。
    • これは、スケジュールが来たらシナリオ監視が1回起動して、シナリオ監視を終えたら停止するコンテナを作ればいいんだという発想になれたことで、先に進めました。バッチ処理みたいにコンテナの利用方法をすれば良いんだなということ。

JMeterでのシナリオ監視結果をCloudWatchに連携しないといけない

  • そしてJMeterでの監視結果をCloudWatchに連携しないといけない
    • これはFargateのログは標準出力に吐き出されたものをCloudWatchと連携できるということなので、シナリオ監視の結果を書き出すことで対応
  • 参考は以下。めちゃくちゃ参考になりました!(もちろんGoogle翻訳した)

www.concurrencylabs.com

ということで参考までに以下の成果物を

FROM alpine:3.9

ARG JMETER_VERSION="5.1.1"
ARG PLUGIN_VERSION="1.4.0" 
ENV JMETER_HOME /opt/apache-jmeter-${JMETER_VERSION}
ENV JMETER_BIN  ${JMETER_HOME}/bin
ENV JMETER_DOWNLOAD_URL  https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-${JMETER_VERSION}.tgz
ENV PLUGIN_DOWNLOAD_URL  https://jmeter-plugins.org/downloads/file/JMeterPlugins-Standard-${PLUGIN_VERSION}.zip
ENV SCENARIO_DOWNLOAD_URL [S3に配置したJMeterのシナリオファイル.jmx]

# Install extra packages
ARG TZ="Asia/Tokyo"
RUN    apk update \
    && apk upgrade \
    && apk add ca-certificates \
    && update-ca-certificates \
    && apk add --update openjdk8-jre tzdata curl unzip bash \
    && apk add --no-cache nss \
    && rm -rf /var/cache/apk/* \
    && mkdir -p /tmp/dependencies  \
    && curl -L --silent ${JMETER_DOWNLOAD_URL} >  /tmp/dependencies/apache-jmeter-${JMETER_VERSION}.tgz  \
    && curl -L --silent ${PLUGIN_DOWNLOAD_URL} >  /tmp/dependencies/JMeterPlugins-${PLUGIN_VERSION}.zip  \
    && curl -L --silent ${SCENARIO_DOWNLOAD_URL} >  /tmp/test-plan.jmx  \
    && mkdir -p /opt  \
    && tar -xzf /tmp/dependencies/apache-jmeter-${JMETER_VERSION}.tgz -C /opt  \
    && unzip -oq "/tmp/dependencies/JMeterPlugins-${PLUGIN_VERSION}.zip" -d $JMETER_HOME  \
    && rm -rf /tmp/dependencies  

ENV PATH $PATH:$JMETER_BIN

WORKDIR ${JMETER_HOME}

ENTRYPOINT [ "jmeter.sh" ]
CMD [ "-n" , "-t" , "/tmp/test-plan.jmx" ] 
  • FargateのCFn
AWSTemplateFormatVersion: "2010-09-09"
Description:
  jmeter-fargate-yml
 
# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------# 
Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "Project Name Prefix"
        Parameters:
          - ProjectName
      - Label:
          default: "Fargate for ECS Configuration"
        Parameters:
          - ECSClusterName
          - ECSTaskName
          - ECSTaskCPUUnit
          - ECSTaskMemory
          - ECSContainerName
          - ECSImageName
          - ECSServiceName
          - ECSTaskScheduleEventsName
          - ECSTaskDesiredCount
      - Label:
          default: "Netowork Configuration"
        Parameters:
          - VpcId
          - ECSSecurityGroupId
          - ECSSubnetId

    ParameterLabels:
      ECSClusterName:
        default: "ECSClusterName"
      ECSTaskName:
        default: "ECSTaskName"
      ECSTaskCPUUnit:
        default: "ECSTaskCPUUnit"
      ECSTaskMemory:
        default: "ECSTaskMemory"
      ECSContainerName:
        default: "ECSContainerName"
      ECSImageName:
        default: "ECSImageName"
      ECSServiceName:
        default: "ECSServiceName"
      ECSTaskScheduleEventsName:
        default: "ECSTaskScheduleEventsName"
      ECSTaskDesiredCount:
        default: "ECSTaskDesiredCount"
 
# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  ProjectName:
    Default: jmeter-test
    Type: String
 
#VPCID
  VpcId:
    Description : "VPC ID"
    Type: AWS::EC2::VPC::Id
 
#ECSSecurity Group
  ECSSecurityGroupId:
    Type: AWS::EC2::SecurityGroup::Id
 
#ECSSubnet
  ECSSubnetId:
    Description : "ECS Subnet"
    Type : AWS::EC2::Subnet::Id
 
#ECSClusterName
  ECSClusterName:
    Type: String
    Default: "cluster"
 
#ECSTaskName
  ECSTaskName:
    Type: String
    Default: "task"
 
#ECSTaskCPUUnit
  ECSTaskCPUUnit:
    AllowedValues: [ 256, 512, 1024, 2048, 4096  ]
    Type: String
    Default: "256"
 
#ECSTaskMemory
  ECSTaskMemory:
    AllowedValues: [ 512, 1024, 2048, 4096  ]
    Type: String
    Default: "512"
 
#ECSContainerName
  ECSContainerName:
    Type: String
    Default: "container"
 
#ECSImageName
  ECSImageName:
    Type: String
    Default: "ECRのURI"
 
#ECSServiceName
  ECSServiceName:
    Type: String
    Default: "service"

#ECSTaskScheduleEventsName
  ECSTaskScheduleEventsName:
    Type: String
    Default: "TaskScheduleEvents"

#ECSTaskSchedule-Expression
  ScheduleExpression:
    Type: String
    Description: Cron settings
    Default: 'cron(0 * * * ? *)' #rate(1 hour)
 
#ECSTaskDesiredCount
  ECSTaskDesiredCount:
    Type: Number
    Default: 0

Resources:

# ------------------------------------------------------------#
# ECS Cluster
# ------------------------------------------------------------#
  ECSCluster:
    Type: "AWS::ECS::Cluster"
    Properties:
      ClusterName: !Sub "${ProjectName}-${ECSClusterName}"
 
# ------------------------------------------------------------#
#  ECS LogGroup
# ------------------------------------------------------------#
  ECSLogGroup:
    Type: "AWS::Logs::LogGroup"
    Properties:
      LogGroupName: !Sub "/ecs/logs/${ProjectName}-ecs-group"
 
# ------------------------------------------------------------#
#  ECS Task Execution Role
# ------------------------------------------------------------#
  ECSTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${ProjectName}-ECSTaskExecutionRolePolicy"
      Path: /
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
 
# ------------------------------------------------------------#
#  ECS TaskDefinition
# ------------------------------------------------------------#
  ECSTaskDefinition:
    Type: "AWS::ECS::TaskDefinition"
    Properties:
      Cpu: !Ref ECSTaskCPUUnit
      ExecutionRoleArn: !Ref ECSTaskExecutionRole
      Family: !Sub "${ProjectName}-${ECSTaskName}"
      Memory: !Ref ECSTaskMemory
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE
 
# ------------------------------------------------------------#
#ContainerDefinitions
# ------------------------------------------------------------#
      ContainerDefinitions:
        - Name: !Sub "${ProjectName}-${ECSContainerName}"
          Image: !Ref ECSImageName 
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref ECSLogGroup
              awslogs-region: !Ref "AWS::Region"
              awslogs-stream-prefix: !Ref ProjectName
          MemoryReservation: 128
          PortMappings:
            - HostPort: 4445
              Protocol: tcp
              ContainerPort: 4445
 
# ------------------------------------------------------------#
#  ECS Service
# ------------------------------------------------------------#
  ECSService:
    Type: AWS::ECS::Service
    Properties:
      Cluster: !Ref ECSCluster
      DesiredCount: !Ref ECSTaskDesiredCount
      LaunchType: FARGATE
      NetworkConfiguration:
       AwsvpcConfiguration:
           AssignPublicIp: ENABLED 
           SecurityGroups:
             - !Ref ECSSecurityGroupId
           Subnets:
             - !Ref ECSSubnetId
      ServiceName: !Sub "${ProjectName}-${ECSServiceName}"
      TaskDefinition: !Ref ECSTaskDefinition

# ------------------------------------------------------------#
#  ECS TaskSchedule # ここ以下をCFnで指定するとエラーになる
# ------------------------------------------------------------#
  # TaskScheduleEvents:
    # Type: AWS::Events::Rule
    # Properties:
      # Name: !Ref ECSTaskScheduleEventsName
      # ScheduleExpression: !Ref ScheduleExpression
      # State: ENABLED
      # Targets:
        # - Id: 1
          # Arn: !GetAtt ECSCluster.Arn
          # RoleArn: !GetAtt ECSTaskExecutionRole.Arn
          # EcsParameters:
            # TaskDefinitionArn: !Ref ECSTaskDefinition
            # TaskCount: 1
            # LaunchType: FARGATE
            # NetworkConfiguration:
            #   AwsvpcConfiguration:
            #     AssignPublicIp: ENABLED #本番はdisable
            #     SecurityGroups:
            #       - !Ref ECSSecurityGroupId
            #     Subnets:
            #       - !Ref ECSSubnetId
      # TaskSchedulerRole:
      #   Type: AWS::IAM::Role
      #   Properties:
      #     AssumeRolePolicyDocument:
      #       Version: "2012-10-17"
      #       Statement:
      #         -
      #           Effect: "Allow"
      #           Principal:
      #             Service:
      #               - "events.amazonaws.com"
      #           Action:
      #             - "sts:AssumeRole"
      #     Path: /
      #     Policies:
      #       - PolicyDocument:
      #           Statement:
      #             - Effect: "Allow"
      #               Condition:
      #                 ArnEquals:
      #                   ecs:cluster: !Ref ECSClusterArn
      #               Action: "ecs:RunTask"
      #               Resource: "*"
      #             - Effect: "Allow"
      #               Condition:
      #                 ArnEquals:
      #                   ecs:cluster: !Ref ECSClusterArn
      #               Action:
      #                 - "iam:ListInstanceProfiles"
      #                 - "iam:ListRoles"
      #                 - "iam:PassRole"
      #               Resource: "*"
      #         PolicyName: "TaskSchedulerPolicy"

注意点

  • で、上記のファイルを利用するにあたっての注意点が2つ
    • Fargateでスケジュール起動のタスクを作成しようよしても失敗する

qiita.com

  • ECRにDockerfileを登録しておく必要がある

把握しないといけないこと多い、、、

  • FargateでJMeter構築ってだけかと思ったら、色々と付随して把握しないといけないことがでてきて大変。でも今はこうして1つ1つクリアしていくことが楽しいです!
  • あとFargateは元になるDockerfileがポイントだなって感じました。ここでセンスの良し悪しが決まると思いました。

CloudFormation 最低限読み書きできますレベルになるまでの話

CloudFormationは納品物に丁度いい

  • 転職して1ヶ月。AWSを利用する仕事になった今、CloudFormationは納品物として非常にわかりやすいものなので、とりあえず何でもかんでもCloudFormation化することを考える必要が出てきました。
  • 右も左もわからない中、CloudFormationを最低限読み書きできるようにはなったかなと思えたので、それまでの試行錯誤で学んだことをここに記録しておきます。

俺的基本方針

  • テンプレート作成の方針を決める
    • CloudFormationを実行したら全てが出来上がっている、というのはまずないと思った
    • 実務上、既存のネットワークがあった上でそこにCloudFormationで追加していくみたいな流れが自然だと思った
    • なので、まずは既存環境にリソースを追加するためのCloudFormationってことでテンプレートを作っていくことにした

既存環境にリソースを追加するためには

  • どうやらCloudFormationには、自分でネットワークを指定したり元になるAMI(AmazonMachineImage)を指定したりできるみたい
    • この指定するっていうのが、項目があって空欄があって、という時の空欄のことで、その空欄をいかに出すかを理解しようと決めたことでCloudFormationの理解がだいぶ進んだ気がした

f:id:kothiba:20190502233113p:plain

いかにリソースを指定するか

  • で、上記のような見た目にするために必要なのは以下の2つでした
    • parameter
    • metadata

parameter

  • これは項目をつくるもの
    AWSTemplateFormatVersion: 2010-09-09
    Parameters:
      PJPrefix:
        Type: String
      VpcId:
        Type: AWS::EC2::VPC::Id
        Default: ""
      SubnetId:
        Type: AWS::EC2::Subnet::Id
        Default: ""
      KeyPairName:
        Type: AWS::EC2::KeyPair::KeyName
        Default: ""
      EC2InstanceName:
        Type: String
        Default: ""
      EC2InstanceAMI:
        Type: String
        Default: ""
      EC2InstanceInstanceType:
        Type: String
        Default: "t2.micro"
      EC2InstanceVolumeType:
        Type: String
        Default: "gp2"
      EC2InstanceVolumeSize:
        Type: String
        Default: "30"
      SSHAccessSourceIP:
        Type: String

metadata

  • これはparameterとセットなのかなくらいにしか意識していなかったですが、よくよく調べてみると、このmetadataの順序でparameterが表示されるという仕組みでした。metadata大事!
   Metadata:
   "AWS::CloudFormation::Interface":
     ParameterGroups:
       - Label:
           default: "Project Name Prefix"
         Parameters:
           - PJPrefix
       - Label:
           default: "Network Configuration"
         Parameters:
           - VpcId
           - SubnetId
       - Label:
           default: "EC2Instance Configuration"
         Parameters:
           - KeyPairName
           - EC2InstanceName
           - EC2InstanceAMI
           - EC2InstanceInstanceType
           - EC2InstanceVolumeType
           - EC2InstanceVolumeSize
           - SSHAccessSourceIP
     ParameterLabels:
       VpcId:
         default: "VpcId"
       SubnetId:
         default: "SubnetId"
       KeyPairName:
         default: "KeyPairName"
       EC2InstanceName:
         default: "EC2 Name"
       EC2InstanceAMI:
         default: "EC2 AMI"
       EC2InstanceInstanceType:
         default: "EC2 InstanceType"
       EC2InstanceVolumeType:
         default: "EC2 VolumeType"
       EC2InstanceVolumeSize:
         default: "EC2 VolumeSize"
       SSHAccessSourceIP:
         default: "SSH AccessSourceIP"

必要なリソースができたらあとは定義していくのみ

  • 個人的に作る必要はあったのは、ECSでした
    • しかもFargateだった

その他、組み込み関数について

Fn::Join

  • 特定の区切り文字で区切って文字列をつなげる
  • AWSのリソースはいい感じに文字を繋げないと定義できないことがあるのでそういった時に利用する
  • 個人的な感覚としては、Outputsでエクスポートした値を利用するときに以下のようにImportValueと一緒に使うことが多い気がする
  - Fn::Join:
  - ""
  - - Fn::ImportValue: !Sub s3-${Env}-DataBucketArn
    - /*

Fn::Sub

  • 変数と普通の文字列を接続する
  • これはCloudFormationで作成したリソースを一意の名前にしたいときに変数を頭に付けるみたいな手法がよく使われていて、その際に利用する
    Profile:
        Type: AWS::IAM::InstanceProfile
        Properties:
          Path: "/"
          Roles: 
            - !Ref IAMRole  
          InstanceProfileName: !Sub "${PJPrefix}-${EC2InstanceName}-profile"
  • {PJPrefix}と${EC2InstanceName}に入れた値をここでprofileという文字列に繋げている

Fn::Ref

  • 値を参照する
  • これは一番わかり易いものかもしれない
    PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
        SubnetId: !Ref PublicSubnet
        RouteTableId: !Ref PublicRouteTable
  • subnetのidはpublicsubnetとして定義したサブネットのidだよってことで!Ref(Fn::Refの短縮形)を利用している

とりあえずは

  • これくらいの知識でも十分にCloudFormationを読み書きできるようになったと感じています。
  • というか一ヶ月丸々と調査と勉強の時間を与えてもらえたのが大きい。そして業務の一環としてやっていると妥協もできないので、良い緊張感となってより身についた
  • あとはアウトプットとして、1日の成果をWikiにアップするという自分の中での縛りを作っていたのでアウトプットまでを一連の流れに組み込めたのはとても良かった

『Rich Life×いのうえのうえん×nichinichi』@新百合ヶ丘nichinichiに行ってきました!

贅沢な時間

とある事情でお時間余りあるご身分のため、ここぞとばかりに贅沢な時間を過ごすために新百合ヶ丘まで行ってきました。

nichinichi.shop

f:id:kothiba:20190311143924j:plain
『Rich Life×いのうえのうえん×nichinichi』のコラボレーション

f:id:kothiba:20190311143947j:plain
新百合ヶ丘nichinichiにて開催。壁がカワイイ!

お料理の内容

◎料理内容◎
●いのうえのうえんガーデンサラダ
●レバーパテ
カポナータ
●ウフマヨネーズ
●BeBeのチーズ
●ピッツァ数種類
●パン数種類
等々 他にも有り…

f:id:kothiba:20190311144814j:plain
さっそくお野菜てんこもり!

f:id:kothiba:20190311144936j:plain
レバーパテ最高!一人でもくもく食べました。

f:id:kothiba:20190311154409j:plain
もちろんパンもあります。

f:id:kothiba:20190311154419j:plain
普段出していないパンも今日の料理に合わせて提供してくれました!

そしてメイン

f:id:kothiba:20190311153031j:plain
鎌倉の名店BeBeのブッラータチーズを使ったピッツァ!

f:id:kothiba:20190311154512j:plain
ピッツァ!2枚目。ブロッコリーの存在感とハムの味最高!

f:id:kothiba:20190311160451j:plain
アレッタ、ケール、豚肉の入ったピッツァ!

普段聞いたことない食材の名前が聞こえる空間

f:id:kothiba:20190311161427j:plain
野菜にしてもチーズにしてもパンにしても知らない用語が飛び交っていて刺激的!

f:id:kothiba:20190311162236j:plain
食を支える人たちってカッコイイ!!でも皆さんシャイなので視線はくれない。

また行きたい!

とても贅沢な時間でしたし、参加している皆さんがとても楽しそうで良い雰囲気でした。そして何より作っている人たちも楽しそうに自信をもって料理を提供してくれていたのが、とても心地よかったです。また行きたいです!!