<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base='https://developers.crowd4u.org/'>
  <title>融合知能デザイン研究室 エンジニアブログ</title>
  <subtitle>融合知能デザイン研究室エンジニアブログはCrowd4UやNextCrowd4Uの開発している学生によるエンジニアブログです。私たちの研究室ではAI+人間+ビッグデータによる融合知能による研究を進め、その成果をクラウドソーシングプラットフォームなどの形で提供しています。その開発に挑戦するチームの裏話や技術共有メモ、ちょっと聞いて欲しいことなどを発信していきます。</subtitle>
  <link href="https://developers.crowd4u.org/feed.xml" rel="self"/>
  <link href='https://developers.crowd4u.org/' rel="alternate" type="text/html"/>
  <updated>2024-08-26T10:54:48Z</updated>
  <id>https://developers.crowd4u.org/</id>
  <author>
    <name>融合知能デザイン研究室</name>
    <uri>https://www.fusioncomplab.org/</uri>
  </author>
  
  <entry>
    <title>CeleryのBrokerとResultBackendをElastiCache(Redis)からSQS+DynamoDBに移行した話</title>
    <link href="https://developers.crowd4u.org/posts/2024-07-20-celery-sqs-dynamo/"/>
    <updated>2024-07-20T03:58:50Z</updated>
    <id>https://developers.crowd4u.org/posts/2024-07-20-celery-sqs-dynamo/</id>
    <author>
        <name>ギルド</name>
        <uri>https://developers.crowd4u.org/author/gild</uri>
    </author>
    <content xml:lang="ja" type="html">&lt;h2 id=&quot;%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB&quot; tabindex=&quot;-1&quot;&gt;はじめに&lt;/h2&gt;
&lt;p&gt;Crowd4u開発チームでは&lt;a href=&quot;https://developers.crowd4u.org/posts/2023-08-04-aws-cost-reduce/&quot;&gt;AWSのコストを$200減らすために行ったこと&lt;/a&gt; や&lt;a href=&quot;https://developers.crowd4u.org/posts/2022-12-24-advent_calendar/&quot;&gt;しがないエンジニアがレガシーコードに向き合った感想&lt;/a&gt; のとおり、AWSのコスト削減やレガシー改善への取り組みを続けています。&lt;/p&gt;
&lt;p&gt;今回は、AWSでElastiCache(Redis)が月30ドル以上かかっており、コスト削減の際に問題となったため、RedisをSQSに移行するまでは書いていきます。&lt;/p&gt;
&lt;h2 id=&quot;celery%E3%81%A8%E3%81%AF&quot; tabindex=&quot;-1&quot;&gt;Celeryとは&lt;/h2&gt;
&lt;p&gt;CeleryはPythonベースの分散タスクキューツールです。&lt;/p&gt;
&lt;p&gt;Pythonでの柔軟かつ信頼性の高いタスクキューとして、Pythonベースのウェブサービスを作成する際によく用いられています。&lt;/p&gt;
&lt;p&gt;Celeryにはタスクキューとして使う&lt;code&gt;CELERY_BROKER&lt;/code&gt;と必要に応じてタスクの結果を格納する&lt;code&gt;CELERY_RESULT_BACKEND&lt;/code&gt;を設定します。今回は、&lt;code&gt;CELERY_BROKER&lt;/code&gt;だけでなく、&lt;code&gt;RESULT_BACKEND&lt;/code&gt;も使用する必要がありました。&lt;/p&gt;
&lt;h2 id=&quot;aws%E3%81%A7%E3%81%AEcelery_broker%E3%81%AE%E9%81%B8%E6%8A%9E%E8%82%A2&quot; tabindex=&quot;-1&quot;&gt;AWSでのCELERY_BROKERの選択肢&lt;/h2&gt;
&lt;p&gt;AWS上でのCELERY_BROKERの主な選択肢として以下の3つがあります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ElastiCache&lt;/li&gt;
&lt;li&gt;Amazon MQ&lt;/li&gt;
&lt;li&gt;Amazon SQS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;現在、私たちが開発しているシステムではElastiCacheを使用しています。&lt;/p&gt;
&lt;p&gt;しかし、ElastiCacheやAmazon MQはインスタンスを建てるため時間単位で課金されます。そのため、常にリクエストが何万件も来るシステムならともかく、そうでない場合は必要以上のコストがかかります。&lt;/p&gt;
&lt;p&gt;一方で、Amazon SQSはリクエスト単位での課金かつ、毎月100万リクエストまでは無料のため大幅な節約となります。そのため、SQSへ移行することとしました。&lt;/p&gt;
&lt;h2 id=&quot;sqs%E3%81%A7%E3%81%AFresult_backend%E3%82%92%E5%8F%97%E3%81%91%E5%8F%96%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84%E2%80%A6&quot; tabindex=&quot;-1&quot;&gt;SQSではRESULT_BACKENDを受け取ることができない…&lt;/h2&gt;
&lt;p&gt;さて、以上のような理由でSQSへの移行を決めましたが、現在のシステムでは&lt;code&gt;RESULT_BACKEND&lt;/code&gt;を使用していますが、SQSでは&lt;code&gt;RESULT_BACKEND&lt;/code&gt;を受け取れないため別で用意する必要があります。&lt;/p&gt;
&lt;p&gt;こちらの主な選択肢としては&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;S3&lt;/li&gt;
&lt;li&gt;RDS(RDBMS)&lt;/li&gt;
&lt;li&gt;DynamoDB(noSQL)&lt;/li&gt;
&lt;li&gt;メモリ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;のような選択肢があります。今回はDynamoDBを選択しました。
理由としては、コスト面(RDSにも無料利用枠が存在しますが)、スケーリングの対応がしやすい点などが挙げられます。&lt;/p&gt;
&lt;h2 id=&quot;%E3%81%A9%E3%81%86%E5%AE%9F%E7%8F%BE%E3%81%99%E3%82%8B%E3%81%8B&quot; tabindex=&quot;-1&quot;&gt;どう実現するか&lt;/h2&gt;
&lt;p&gt;今回の構成の実現にあたり、以下のような&lt;code&gt;celeryconfig.py&lt;/code&gt;を作成し、Celeryに読み込ませるような形で実現をしています。環境変数は&lt;code&gt;.env&lt;/code&gt;や&lt;code&gt;Amazon Secret Manager&lt;/code&gt;などを使用して読み込んでください。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import os
from kombu.utils.url import safequote

broker_url = &amp;quot;sqs://{aws_access_key}:{aws_secret_key}@&amp;quot;.format(
	aws_access_key=safequote(os.environ.get[&amp;quot;AWS_ACCESS_KEY&amp;quot;]),
  aws_secret_key=safequote(os.environ.get[&amp;quot;AWS_SECRET_KEY&amp;quot;]),
)
		        
broker_transport_options = {
	&amp;quot;region&amp;quot;: os.environ.get(&amp;quot;AWS_REGION&amp;quot;, &amp;quot;your_region&amp;quot;),
  &amp;quot;visibility_timeout&amp;quot;: os.environ.get(&amp;quot;AWS_SQS_VISIBILITY_TIMEOUT&amp;quot;, 3600),
  &amp;quot;polling_interval&amp;quot;: os.environ.get(&amp;quot;AWS_SQS_POLLING_INTERVAL&amp;quot;, 1),
  &amp;quot;wait_time_seconds&amp;quot;: os.environ.get(&amp;quot;AWS_SQS_WAIT_TIME_SECONDS&amp;quot;, 15),
  &amp;quot;queue_name_prefix&amp;quot;: os.environ.get(&amp;quot;AWS_SQS_QUEUE_NAME_PREFIX&amp;quot;, &amp;quot;hoge-&amp;quot;),
}

# change dynamodb &#39;table name&#39;
result_backend = f&amp;quot;dynamodb://@{os.environ.get(&#39;AWS_REGION&#39;, &#39;your_region&#39;)}/table_name&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上のような設定により、BROKERにSQS、 RESULT_BACKENDにDynamoDBを設定することができます。ちなみに、SQSのキューに関しては特に何も設定しなかった場合は「&lt;code&gt;celery&lt;/code&gt;」という名前のキューが自動で生成されるみたいですね。今回の場合は&lt;code&gt;queue_name_prefix&lt;/code&gt;でPrefixを指定し、&lt;code&gt;hoge-celery&lt;/code&gt;というような名前のキューが作成されるようにしています。&lt;/p&gt;
&lt;p&gt;最後に、これらのコードを実行するIAMロールやIAMユーザーにSQSとDynamoDBのRead権限やWrite権限を与えるようなインラインポリシーを作成してアタッチして下さい(ここのポリシーは省略します)。&lt;/p&gt;
&lt;h2 id=&quot;%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB&quot; tabindex=&quot;-1&quot;&gt;おわりに&lt;/h2&gt;
&lt;p&gt;今回はSQSへの移行を行いましたが、当然環境によってはElastiCacheやAmazon MQが最適な選択肢となる場合は多く存在すると思います。たとえば、SQSを使用する場合はCeleryイベントやリモートコントロールコマンドに対応していないといった問題があり、SQSを使用する際には自分たちで監視・ヘルスチェックのための環境が必要と言えるでしょう(現在はこちらの対応はできていませんが、順次作成していかないとなとは思っています...)。&lt;/p&gt;
&lt;p&gt;以上のような理由により、CeleryのBrokerやBackendの適応先はサービスの運営状況と合わせて選定する必要なあるなと感じています。&lt;/p&gt;
&lt;p&gt;ここまで読んでいただきありがとうございました！&lt;/p&gt;
&lt;h2 id=&quot;%E5%87%BA%E5%85%B8&quot; tabindex=&quot;-1&quot;&gt;出典&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.celeryq.dev/en/stable/userguide/configuration.html#aws-dynamodb-backend-settings&quot;&gt;https://docs.celeryq.dev/en/stable/userguide/configuration.html#aws-dynamodb-backend-settings&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.celeryq.dev/en/stable/getting-started/backends-and-brokers/sqs.html&quot;&gt;https://docs.celeryq.dev/en/stable/getting-started/backends-and-brokers/sqs.html&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://qiita.com/hankehly/items/c3e0496eb04327a53ac4&quot;&gt;https://qiita.com/hankehly/items/c3e0496eb04327a53ac4&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  
  
  <entry>
    <title>令和5年に爆誕したオレオレFWの話</title>
    <link href="https://developers.crowd4u.org/posts/2023-12-24-oreore-fw/"/>
    <updated>2023-12-23T15:00:00Z</updated>
    <id>https://developers.crowd4u.org/posts/2023-12-24-oreore-fw/</id>
    <author>
        <name>notch_man</name>
        <uri>https://developers.crowd4u.org/author/notch_man</uri>
    </author>
    <content xml:lang="ja" type="html">&lt;p&gt;この記事は&lt;a href=&quot;https://adventar.org/calendars/8957&quot;&gt;klis Advent Calendar 2023の記事となります。&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;こんにちは、klis19（3編）のnotch_manです。今日はクリスマスイブですね。皆さんはいかがお過ごしでしょうか。私は仕事が多すぎてクリスマスどころか年末年始も消滅する見込みです。
今日はクリスマスイブにピッタリな一家に1つあると嬉しいオレオレFWの紹介をします。街中のカップルを眺めて心が寒くなった皆さん、愛を持ってオレオレFWを育てて豊かな人生を歩みましょう！&lt;/p&gt;
&lt;p&gt;※対象はWebアプリケーションフレームワークと思ってください&lt;/p&gt;
&lt;h1 id=&quot;%E5%85%8D%E8%B2%AC%E4%BA%8B%E9%A0%85&quot; tabindex=&quot;-1&quot;&gt;免責事項&lt;/h1&gt;
&lt;p&gt;この記事は主観がとても強いです。また、融合知能デザイン研究室で1年以上もPythonでWebを書くという苦行を経て辿り着いた1つの作法です。一般的な問題を解決するわけではないので、あまり真に受けないでください。オレオレを現場に持ち込んだ場合、同僚に嫌われます（経験談）&lt;/p&gt;
&lt;h1 id=&quot;%E3%82%AA%E3%83%AC%E3%82%AA%E3%83%ACfw%E3%81%A8%E3%81%AF&quot; tabindex=&quot;-1&quot;&gt;オレオレFWとは&lt;/h1&gt;
&lt;p&gt;オレオレFWとは自作のフレームワークのことを指します。巷で有名なものだとDjangoやFlask、LaravelやRailsなどがいわゆるWebアプリケーションフレームワークに分類されるものです。&lt;/p&gt;
&lt;p&gt;Webフレームワークは**「Webアプリケーションなどを開発するために必要な機能があらかじめ備えられている開発基盤」**を提供してくれます。これらはベタープラクティスな手法やテストされたライブラリを多数盛り込んでおり「&lt;strong&gt;誰でも&lt;/strong&gt;」一定の品質でアプリケーションを書くことが出来ます。つまり、高品質で生産性の高い開発体験を提供するのがフレームワークの役目です。これらのエコシステムは広く知られており、それらをベースにすることで私たちは事業やそのための開発に集中することができるようになります。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;これらの話を十分に理解した上でこの先を読んでください&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;オレオレFWについて知りたい人はとりあえずこの動画を視てください。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=TiyJh6QPork&quot;&gt;https://www.youtube.com/watch?v=TiyJh6QPork&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;その上での話ですが、オレオレFWは一般的なWebフレームワークの多くの利点を放棄します。生産性や品質なんて物は制作者に依存しますし、第3者から見ると理解不能な物体が誕生するだけです。知恵袋やStack OverflowでオレオレFWの質問をしても全く回答は返ってきませんし、ChatGPTもまともなことしか答えてくれません。&lt;/p&gt;
&lt;p&gt;つまり、令和5年においてオレオレFWを使うモチベーションはゼロです。というか使ってはいけません。Pythonな人ならstarletteやFastAPI、DjangoやFlaskを使うと良いでしょう。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;最初にstarletteを持ち出す時点で、私の思想はかなり歪んでいます&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;%E3%82%AA%E3%83%AC%E3%82%AA%E3%83%ACfw%E3%82%92%E4%BD%9C%E3%82%8B%E5%88%A9%E7%82%B9&quot; tabindex=&quot;-1&quot;&gt;オレオレFWを作る利点&lt;/h1&gt;
&lt;p&gt;オレオレFWは現場においてはほぼアンチパターンですが、趣味の範囲では良いことがたくさんあります。まず、フレームワークを作る中でアプリケーションの組み立て方を学ぶことができます。また、アプリケーションの設計を考えたり既存のフレームワークの目指したかった世界観が理解できるなど良いことがたくさんあります。また、既存フレームワークでは解決できない問題に直面したときに、それらを解決する手段を提案することができるようになります（コラコラ）。&lt;/p&gt;
&lt;p&gt;ともかく、Webフレームワークを十分に理解して使いこなすために一度自分で作ってみるというのはエンジニアとして良い経験になると思います。というわけでやっていこう！オレオレFW！&lt;/p&gt;
&lt;h1 id=&quot;%E3%82%AA%E3%83%AC%E3%82%AA%E3%83%ACfw%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%9F%E5%8B%95%E6%A9%9F&quot; tabindex=&quot;-1&quot;&gt;オレオレFWを作った動機&lt;/h1&gt;
&lt;p&gt;私は研究室でPythonを使ったWeb開発をしています。そもそも、PythonでWebを書くというのは例え既製品のフレームワークを使ったとしてもかなりの地獄が待っています。まず、Pythonはバージョンによってライブラリが壊れやすいことで有名です。Python2 → 3のジャンプを経験した人は色々苦しんだでしょうし、著名ライブラリでもランタイムのバージョンアップにライブラリが追従しない事があり気苦労が多いです。Python3系になってBCは減りましたがメモリ周りの最適化が進む中でJIT依存のライブラリが被害を受けたりは良くあります。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;特に機械学習系のライブラリでよく使われるnumbaのランタイムの追従が遅くて苦労した人が多いでしょう&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;これに対応するためのパッケージ管理手法も中々難しいところがあります。Pythonではnpmやcomposerに匹敵するその言語のパッケージマネージャというものは確立していない印象です。pipのrequiements.txtは管理が手間だし、poetryなど高級な物を使っても結局requirements.txtを吐いたり環境構築が辛いなどの問題があります。&lt;/p&gt;
&lt;p&gt;また、検索汚染もかなり深刻です。例えば、sqlalchemyでモデルのupdateを検索すると、ほとんどの例でselect → session commit()という手順を踏んでいます。この場合はDMLが2回走りますが、実際にはupdate1回で良いことは誰でも分かる事です。&lt;/p&gt;
&lt;p&gt;この状況の中で、研究室という特殊な環境で持続的な開発を行うためにはかなり気を遣わないといけません。これについては以前のブログでも述べているのでこちらもご覧ください。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.crowd4u.org/posts/2022-12-24-advent_calendar/&quot;&gt;https://developers.crowd4u.org/posts/2022-12-24-advent_calendar/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;よって、具体的には以下の点を考えて開発を行う必要があります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ちょっとの修正で3年は生き残るフレームワーク&lt;/li&gt;
&lt;li&gt;可能であればORMとかそういう物に頼らず、自分で面倒を見るぞという気になれる設計&lt;/li&gt;
&lt;li&gt;ライブラリやランタイムのBCの影響をなるべく受けない設計&lt;/li&gt;
&lt;li&gt;フレームワーク特有の魔法に頼らない素朴で分かりやすいコードや設計&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これらを考えるとデータベース周りでORMに頼りたくなかったり、ルーターとバリデーションがセットになったような&lt;code&gt;BaseModel&lt;/code&gt;にすらあまりロジックに入ってほしくない、脱出を見込んだ設計にするためにビジネスロジックは標準だけで簡潔させたい…お、これは既存フレームワークだけでは無理なのでは？よし！オレオレだ！という結論になるのは自明だと思います。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;典型的な言い訳なので、あなたの周りでこのようなことを言い出した人が居たら冷水をぶっかけましょう&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;%E3%81%A1%E3%82%87%E3%81%A3%E3%81%A8%E3%81%AE%E4%BF%AE%E6%AD%A3%E3%81%A7%E9%95%B7%E7%94%9F%E3%81%8D%E3%81%99%E3%82%8B%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%82%92%E7%9B%AE%E6%8C%87%E3%81%99%E7%90%86%E7%94%B1&quot; tabindex=&quot;-1&quot;&gt;ちょっとの修正で長生きするフレームワークを目指す理由&lt;/h3&gt;
&lt;p&gt;私たちは基本的に研究の片手間に開発をする必要があります。しかし、Pythonを取り巻く状況は進化のスピードが早くそれに追従し続けなければあっという間に負債になってしまいます。普段からライブラリやランタイムの更新は意識するようにしていますが、万一BCがあったとしても影響範囲を最小限にすることで工数を抑えることができると思っています。&lt;/p&gt;
&lt;p&gt;あと、素朴な設計のコードは案外長生きします。ここでいう「素朴」とはなるべく標準ライブラリに寄り添った実装を指します。
皆さんも機械学習ブームで盛り上がった2018~2020年頃のCNNで画像識別タスクをやるプログラムを作ったと思います。それを動かしてみてください。多分、動かないと思います（笑）。ベタに変化を急ぐライブラリに手を出すと数年経ったら全く動かなくなります。その一方で、最初に作ったHello Worldのプログラムを動かしてみてください。Python3系の時代に書いた物であれば今でも動くと思います。このような基本的な機能は年月が経っても基本的に壊れることはありません。&lt;/p&gt;
&lt;p&gt;つまり、こういう設計を心がけるとランタイムの更新にも追従しやすくなるのです。基本に忠実に基本の組み合わせによって複雑な物を表現したら、それが複雑でも長生きすると思っています。人類、バージョンアップを放棄したらあっという間にレガシーコードが爆誕します。そのために複雑だけど優れた魔法に頼らず、物理でゴリ押すことを選択しています。速度や性能の面で不利になりますが、あとで手を付けにくくなる事は回避したいのが狙いです。&lt;/p&gt;
&lt;p&gt;どうです？オレオレしてるでしょ？？？？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;さらに言うと、バージョンアップなどをノーコストに近い状態に持っていくことを目指しています。FWはメジャーアップデートするとミドルウェアが大きく変わって修正工数が増えるということがありますが、限られたリソースの中でそういう状況に持っていきたくはないです。パッケージを弄ってテストを回せばバージョンアップはいける！という状態にしたいです。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;orm%E3%82%92%E4%BD%BF%E3%81%84%E3%81%9F%E3%81%8F%E3%81%AA%E3%81%84&quot; tabindex=&quot;-1&quot;&gt;ORMを使いたくない&lt;/h3&gt;
&lt;p&gt;Pythonには有名なORMやその拡張としてマイグレーションツールが提供されています。しかし、以前マイグレーションツールを使ったところ偶然サーバーの瞬断が発生していまいテーブルの不整合が生じてしまいました。ALTERテーブルが走った場合は基本的に戻せないので、マイグレーションツールの世代管理周りのズレを素手で修正する事になってしまいました。この時は、こういった問題に対応できるメンバーが居たから何とかなりましたが研究室にこの先そういう人材が来るかは分かりません。その他にもORMはテーブルとモデルが1:1に密結合してしまうような雰囲気を与えたり、今のテーブル設計とユースケースにズレがありORM的な使い方が苦しいという課題があります。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;研究室では複数のサービスが運営されており将来的にこれらを統合することが計画されています。それらも考慮するとORMに頼らず自分達でドメイン設計をやっていく気合いを持つ必要があると考えています。そのため、一部ではORMが使われていましたが、一貫性の観点からも全て剥がし切りました。現在は、一部の複雑なクエリにはテンプレートエンジンを使いつつ生SQLを操作する選択を取っています。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%84%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%AE%E9%AD%94%E6%B3%95%E3%81%AB%E9%A0%BC%E3%82%89%E3%81%AA%E3%81%84&quot; tabindex=&quot;-1&quot;&gt;ライブラリやフレームワークの魔法に頼らない&lt;/h3&gt;
&lt;p&gt;個人的にはstarletteやFastAPIの設計が大好きです。これらはシンプルな設計を提供しつつミドルウェアを追加することで複雑な処理も出来るようになります。ただ、ミドルウェアを挟んでしまうと手続き型っぽく何処で何をやっているか追いにくくなってしまいます。大学生は基本的に手続き型プログラミングの世界で生きているので、なるべくその世界の感性で理解できるコードを実装したいと思います。てか、裏で魔法を使わずともif文とかでええやろ！なんで、かっこよくやりたいんや！！&lt;/p&gt;
&lt;h1 id=&quot;%E4%BB%8A%E5%9B%9E%E4%BD%9C%E3%81%A3%E3%81%9F%E3%82%AA%E3%83%AC%E3%82%AA%E3%83%ACfw%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6&quot; tabindex=&quot;-1&quot;&gt;今回作ったオレオレFWについて&lt;/h1&gt;
&lt;p&gt;今回作成したオレオレFWはこちらに公開しています。このオレオレFWはASGI互換のアプリケーションになっていて、これに対応した様々なワーカーを使って動作させることができます。今回はuvicornを使っています。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/notchman8600/ore-1&quot;&gt;https://github.com/notchman8600/ore-1&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;時間の都合上、POSTのサンプルを提供できていないと思うのでDBから値を取ってそれを表示するだけのシンプルなアプリケーションになっています。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;今回のフレームワークは以下のことを目標に設計しています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;放置しても2年は動くであろうライブラリ選定（実績有）&lt;/li&gt;
&lt;li&gt;メインアプリケーションの部分はライブラリに頼らない基本設計&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;1つの処理を一貫性のある作法に基づいて実装できる設計&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;闇雲に例外キャッチをしようとしたらコードレビューで叱れるエラーハンドリング設計&lt;/li&gt;
&lt;li&gt;APMなどの連携をある程度考えたカスタマイズしやすいロギング&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;静的解析やIDEに優しいソースコード&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;まあ、設計指針についてはほとんど上で述べた課題に対する答えになっているので特段触れませんがいくつか新しい話があるのでそれに触れておきます。&lt;/p&gt;
&lt;h3 id=&quot;1%E3%81%A4%E3%81%AE%E5%87%A6%E7%90%86%E3%82%92%E4%B8%80%E8%B2%AB%E6%80%A7%E3%81%AE%E3%81%82%E3%82%8B%E4%BD%9C%E6%B3%95%E3%81%AB%E5%9F%BA%E3%81%A5%E3%81%84%E3%81%A6%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B%E8%A8%AD%E8%A8%88&quot; tabindex=&quot;-1&quot;&gt;1つの処理を一貫性のある作法に基づいて実装する設計&lt;/h3&gt;
&lt;p&gt;フレームワークでは当たり前といえば当たり前ですが、ある種の一貫性のあるやり方で機能を追加できる設計を目指しています。これによって、いわゆるコピペプログラミングをやったとしても一定の制約の範囲に抑えることができて品質を保つことが出来ます。&lt;/p&gt;
&lt;p&gt;今回のオレオレFWでは以下の一貫性のある実装を行えるようにしています。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;期待したいレスポンスをControllerで定義&lt;/li&gt;
&lt;li&gt;そのレスポンスに必要なデータをRepositoryが作成する&lt;/li&gt;
&lt;li&gt;Repositoryは必要なデータを取得するために意味のある単位でモデル化されたStoreにフェッチする&lt;/li&gt;
&lt;li&gt;ストアは1つ以上のテーブルから意味のあるデータモデルのCRUDを行う&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;つまり、1機能を追加したいときはControllerに1メソッド、Repositoryに1メソッド、Storeにメソッドがあればそれを使いなければ追加すれば良いです。基本的にこれを繰り返すことで機能を増やしていける設計にすることでコピペプログラミングをやりやすくしています。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;コピペプログラミングができるということはCopilotやChatGPTにも優しい設計になっています。人間が理解不能なものはLLMも理解不能です。愚直だけど単純な方が後々幸せになれます。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;今回は実装上、他の概念も登場していますがそれは脱出を見込んだ話になっておりControllerからStoreまでは長期にわたって生き残らせる設計にしています。&lt;/p&gt;
&lt;h1 id=&quot;%E3%82%AA%E3%83%AC%E3%82%AA%E3%83%ACfw%E3%81%AE%E7%B4%B9%E4%BB%8B&quot; tabindex=&quot;-1&quot;&gt;オレオレFWの紹介&lt;/h1&gt;
&lt;p&gt;早速ディレクトリ構成を見ていきましょう。今回はこのような構成になっておりappがエントリポイントになっています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;app
&lt;ul&gt;
&lt;li&gt;actions/&lt;/li&gt;
&lt;li&gt;controllers/&lt;/li&gt;
&lt;li&gt;dispatchers/&lt;/li&gt;
&lt;li&gt;routers/&lt;/li&gt;
&lt;li&gt;model/&lt;/li&gt;
&lt;li&gt;stores/&lt;/li&gt;
&lt;li&gt;templates/&lt;/li&gt;
&lt;li&gt;main.py&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;db
&lt;ul&gt;
&lt;li&gt;DDLやテストデータの挿入用、気にしなくて良い&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;…（雑多なファイル）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;dispatcherはASGIレイヤーの処理をとなっており、HTTPリクエストを発火させてレスポンスを送信する役割を担っています。本来であればフレームワークに内包されていますが、今回はフレームワークお母さんが存在しないので自前で実装しています。&lt;/p&gt;
&lt;p&gt;基本的にはフレームワークお母さんが居ないので自前で作ったに過ぎないレイヤーですが、1点だけ重要な事があります。それは、HTTPのイベントに基づいて動いたアプリケーション処理の例外を全てハンドルする役割です。つまり、アプリケーションのロジックで例外が出た場合、最終的にこのレイヤーで全てハンドルできるようになっています。ここで、エラーログを取得してレスポンスを返せば絶対に白いエラー画面を返さずに済みます。エラーハンドリングを分かっていなくても事故ればログが出るので、何かあったときは後輩を救うことができます（とても大事）。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;async def dispatch_http_event(scope: HTTPScope, receive: ASGIReceiveCallable, send: ASGISendCallable) -&amp;gt; None:
    # dispatch errorを全体的に囲むことで白い画面を出さないようにする
    # 本当はAPIコールかHTMLコールかによって例外処理を考える必要がある
    # APIコールであればエラーオブジェクトのJSON、HTMLであればエラーページをレンダリングする必要がある
    try:
        content_type, encoded_res = await http_router(scope, receive)
        # レスポンスヘッダ
        headers = [
            (b&amp;quot;content-type&amp;quot;, content_type),
            (b&amp;quot;content-length&amp;quot;, str(len(encoded_res)).encode(encoding=&amp;quot;utf-8&amp;quot;)),
        ]
        await send({&amp;quot;type&amp;quot;: &amp;quot;http.response.start&amp;quot;, &amp;quot;status&amp;quot;: 200, &amp;quot;headers&amp;quot;: headers})
        await send({&amp;quot;type&amp;quot;: &amp;quot;http.response.body&amp;quot;, &amp;quot;body&amp;quot;: encoded_res})
    except Exception as e:
        # ここでアプリケーションから上がってくる例外は全てハンドリングする
        logger.error(f&amp;quot;error has occured in http event: {e},{traceback.format_exc()}&amp;quot;)

        # HTTPレスポンスの準備
        # ここではエラー起因の白い画面を出さないようにするためにエラーページをレンダリングする
        html_content = render_template(&amp;quot;error.jinja2.html&amp;quot;, {&amp;quot;message&amp;quot;: &amp;quot;hello, I am variable value.&amp;quot;})
        await send(
            {
                &amp;quot;type&amp;quot;: &amp;quot;http.response.start&amp;quot;,
                &amp;quot;status&amp;quot;: 500,
                &amp;quot;headers&amp;quot;: ((b&amp;quot;content-type&amp;quot;, b&amp;quot;text/html&amp;quot;),),
            }
        )
        await send({&amp;quot;body&amp;quot;: html_content.encode(&amp;quot;utf-8&amp;quot;), &amp;quot;type&amp;quot;: &amp;quot;http.response.body&amp;quot;})
    return
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;router%E5%B1%A4%EF%BC%88routers%2Bactions%EF%BC%89%E3%81%AE%E8%AA%AC%E6%98%8E&quot; tabindex=&quot;-1&quot;&gt;Router層（Routers+Actions）の説明&lt;/h2&gt;
&lt;p&gt;Router層ではリクエストを受け取りそれをバリデーションしてControllerに渡す役割を担います。多くのフレームワークではroutersとactionsを合わせたものが提供されています。例えばFastAPIであればエントリポイントの関数+pydanticを使うことで実現できます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# ルーターではリクエストを受け取ってアクションを実行しレスポンスを返却する。リクエストやレスポンスの内容はアクションに委譲する
async def http_router(scope: HTTPScope, receive: ASGIReceiveCallable) -&amp;gt; tuple[bytes, bytes]:
    path = scope[&amp;quot;path&amp;quot;]
    content_type: list[tuple[bytes, bytes]] = scope[&amp;quot;headers&amp;quot;]
    if path == &amp;quot;/&amp;quot;:
        hello_action = HelloAction()
        # リクエストボディを受け取る
        body = await receive_body(receive)
        # ヘッダーの解析
        headers = parse_headers(content_type)
        # ボディの中身とcontent-typeを渡す
        return hello_action.run(body=body, headers=headers)
    elif path == &amp;quot;/user&amp;quot; and scope[&amp;quot;method&amp;quot;] == &amp;quot;POST&amp;quot;:
        # リクエストボディを受け取る
        event = receive()
        return b&amp;quot;application/json&amp;quot;, json.dumps({&amp;quot;message&amp;quot;: {&amp;quot;message&amp;quot;: f&amp;quot;とりあえずPOSTはできたからいい加減な値を返す&amp;quot;}}).encode()
    else:
        # 本当に適当なレスポンスを作成したい時はこのように直接書くこともできる
        return b&amp;quot;application/json&amp;quot;, json.dumps({&amp;quot;message&amp;quot;: &amp;quot;Not Found&amp;quot;}).encode()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;いや～、if文でルーティングなんて誰の影響を受けたのでしょうか？（笑）この形にすれば存在しないエンドポイントを叩いた時に必ず404を返せるので良いでしょ？そして、とにかく分かりやすい（これ大事）。Router層ではリクエストデータやヘッダの取得を行いそれをActionに流しています。ここでは意味のあるデータの形にせず辞書形式のままにしておきます。パースエラーが起きた場合はここで止まる想定です。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;多くのフレームワークではrouter.add(”/someroute”,ハンドラ) のようにエンドポイントに対応するハンドラを登録してイベントを定義します。ただ、正直分かりにくくないですか？しかも、既製品だと存在しないパスにアクセスしたときはミドルウェアで例外作って404を返すようにしないと白い画面が出るなど使いづらいところが多いです。この形にすればそういう手間がかからない。しかもミドルウェアを使わないのでシンプル！どうです？オレオレしてますねwww&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;次にActionです。Actionではbodyやheaderの情報を渡してそれをバリデーションします。バリデーションしたオブジェクトはリクエストのDTOか生データに変換してControllerに渡します。&lt;/p&gt;
&lt;p&gt;Actionは基底クラスを定義しています。Action.run()を実行することで実際の処理を開始する制約を与えています。派生クラスではrunだけ実装すれば良いのでとてもシンプルですね（主観）&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# Standard Library
from typing import Any

# Actionの基底クラスを定義する
# こいつはほぼ何の役にも立たないが、runを起動するという制約を与えることができるので実装している
class Action:
    def __init__(self, name: str, description: str, args: dict) -&amp;gt; None:
        self.name = name
        self.description = description
        self.args = args

    def run(self, *args: Any) -&amp;gt; tuple[bytes, bytes]:
        raise NotImplementedError(&amp;quot;Action.run() must be implemented by subclass&amp;quot;)

    def __str__(self) -&amp;gt; str:
        return self.name

    def __repr__(self) -&amp;gt; str:
        return self.name
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Actionは原則、1つのエンドポイントに1つ定義します。Actionとエンドポイントを1:1にすることでコードジャンプで追いやすくしています。例えば、GetHelloActionではあればHelloを取ってくるイベント、AddHelloActionであれば何らか”Hello”を追加するのだなと理解しやすくなります。もちろん、RESTに準拠すれば十分という話もありますがコードを読めば大体理解できるということが大事だと思っています。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Actionには名前が付けられるのでエラーログにAction名を叩き込むことでどの処理でエラーが出たか若干分かりやすくなるという話もあります&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Actionの実態はこんな感じです。Actionにはルーターでパースしたbodyの文字列が入ってきます。&lt;/p&gt;
&lt;p&gt;返り値がtupleですがこれはレスポンスのバイト列とcontent-typeのバイト列を想定しています。普通のFWだとResponseオブジェクトに辞書を入ればいい感じになるので、後述するControllerが返したレスポンスのDTOからバイトデータを作るようにしています。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class HelloAction(Action):
    def __init__(self) -&amp;gt; None:
        super().__init__(&amp;quot;hello&amp;quot;, &amp;quot;say hello&amp;quot;, {&amp;quot;name&amp;quot;: &amp;quot;string&amp;quot;})

    def run(self, headers: dict, body: dict) -&amp;gt; tuple[bytes, bytes]:
        # Actionでレスポンスを作成する
        res = controller.get_hello(1)
        content = render_template(
            &amp;quot;index.jinja2.html&amp;quot;,
            {
                &amp;quot;message&amp;quot;: &amp;quot;hello, I am variable value.&amp;quot;,
                &amp;quot;name&amp;quot;: res.user_name,
                &amp;quot;greeting&amp;quot;: res.greeting,
                &amp;quot;user_id&amp;quot;: res.user_id,
            },
        )
        return b&amp;quot;text/html&amp;quot;, content.encode()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ここまでのレイヤーは今のASGI準拠の自作FWに深く依存したところになっています。ただ、この辺りの実装は将来的に全て捨てて既製品（FastAPIなど）にスムーズに移行することが可能です。実際、今の現場ではPydanticのBaseModelをさらにDTOに詰め替えることでController以下の互換性を保っています。色々なエコシステムへの脱出が可能でありながら、素朴に実装できるのがオレオレFWのメリットです。引っ越しの際にはファイル数が多いかつActionが1つ1つバラバラになっているので、手間が増えますが1つ1つは単純作業なので工数は読みやすいです。工数の読みやすさは大事で、将来的に改修を依頼したときにスムーズに見積もりを出して発注に至ることもできます（コラコラ）。&lt;/p&gt;
&lt;h2 id=&quot;controller%E5%B1%A4%E3%81%AE%E8%AA%AC%E6%98%8E&quot; tabindex=&quot;-1&quot;&gt;Controller層の説明&lt;/h2&gt;
&lt;p&gt;ControllerではリクエストのDTOを受け取りService（今回は未実装）やRepositoryを呼び出し、レスポンスのDTOを返却する責務を担います。この設計はSpringFrameworkの設計を意識しています。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class UserGreetingDto:
    def __init__(self, user_id: int, user_name: str, greeting: str):
        self.user_id = user_id
        self.user_name = user_name
        self.greeting = greeting

class HelloController:
    def __init__(self) -&amp;gt; None:
        self.hello_repository = HelloRepository()
        pass

    def get_hello(self, user_id: int) -&amp;gt; UserGreetingDto:
        user_greeting = self.hello_repository.get_hello(user_id)
        return UserGreetingDto(user_greeting.user_id, user_greeting.name, user_greeting.greeting)

controller = HelloController()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;DTOクラスは全てPythonのclassを使っているので、ライブラリに依存しない世界を実現することができます。また、ランタイムのバージョンアップで壊れることもないので堅牢です。&lt;/p&gt;
&lt;p&gt;DTOの定義場所は別のフォルダでも良いですがファイルが散るとちょっとした加工をするときに面倒なのでControllerに置いています。困ったときは移せば良い。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;dataclassを使わない理由ですが、主にasdict()を使わせないためです。辞書は強力なデータ表現ですが、何でも入れることができるので可能性が爆発します。すると、せっかくDTOなどで受け渡しされるデータの透明性を確保したのに水の泡になります。強すぎる武器は時に毒になります、それを使わせるのはヤバい。それを防ぐために意図的に使っていません。いや～、オレオレですね（笑）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;repository%E5%B1%A4%E3%81%AE%E8%AA%AC%E6%98%8E&quot; tabindex=&quot;-1&quot;&gt;Repository層の説明&lt;/h2&gt;
&lt;p&gt;Repository層ではビジネスロジックにおいて意味のあるモデルを作成、取得する責務を担います。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class HelloRepository:
    def __init__(self) -&amp;gt; None:
        self.hello_store = HelloStore()
        self.user_store = UserStore()

    def get_hello(self, user_id: int) -&amp;gt; UserGreeting:
        user = self.user_store.fetch_user(user_id)
        greeting = self.hello_store.get_by_user_id(user_id)
        return UserGreeting(user.user_id, user.user_name, greeting.greeting)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この例ではユーザーの挨拶という1つのモデルをgreetingテーブルとuserテーブルから作成しています。データ作成の例が作れなかった（多分）のですが、登録時は&lt;code&gt;add_hello&lt;/code&gt;的な関数からそれぞれのstoreのsaveメソッド的なものを呼び出す想定になります。ここもサードパーティライブラリには依存していません。この辺りがFWに依存してしまうと本当に他所に脱出できなくなるので辛いんですよね。storeがインフラに依存してしまうのは仕方ないですが、そこまでで抑えてしまえばデータロジックの部分は崩さずにいけます。いや～、便利だな（笑）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;これは集約とか言われたりするかもですが、私はそういう高級な考え方は十分理解していません。あくまで複数のテーブルから意味のあるモデルを作るという理解で生きています。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;store%E5%B1%A4%E3%81%AE%E8%AA%AC%E6%98%8E&quot; tabindex=&quot;-1&quot;&gt;Store層の説明&lt;/h2&gt;
&lt;p&gt;Store層ではDMLを発行してデータの読み書きを行なうレイヤーになっています。基本的にはテーブルと1:1ですがJOINクエリを使う場合は複数テーブルを扱うこともあります。Store層の呼び出しも基本的にDTOを介して行います。DTOを介することでインフラやライブラリに影響されやすいDB操作を吸収し、他のレイヤーに影響しないようにしています。さらに、基底クラスにインフラ知識を寄せることで派生クラス（実態）についてはノータッチで移行が可能です。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class FetchUserDto:
    def __init__(self, user_id: int, user_name: str):
        self.user_id = user_id
        self.user_name = user_name

class UserStore(Store):
    def __init__(self):
        super().__init__()
        pass

    def fetch_user(self, user_id: int) -&amp;gt; FetchUserDto:
        query = f&amp;quot;SELECT id, user_name FROM users WHERE id = %s&amp;quot;
        row_data = self.fetch_data(query, user_id)
        # ここは本当はまともに例外を投げるべき
        assert row_data is not None and len(row_data) == 1, &amp;quot;error, row_data is None or len(row_data) &amp;gt; 1&amp;quot;
        logger.debug(f&amp;quot;row_data: {row_data}&amp;quot;)
        return FetchUserDto(row_data[0][0], row_data[0][1])

    def insert_user(self, user_id: int, user_name: str):
        query = f&amp;quot;INSERT INTO users (id, user_name) VALUES (%s, %s)&amp;quot;
        self.execute(query, (user_id, user_name))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ここでフェッチに失敗した時にAssertErrorの例外を出すようにしています。本当は例外が出たらRepository側で整合性を担保しないといけませんが、今はそのようなことは特にやっていません。そういうのは必要な時に追加すれば良いのです。&lt;/p&gt;
&lt;p&gt;Storeクラスはインフラ知識を吸収しつつfetchとexecuteに相当するメソッドを定義しています。基本的にfetchは読み込み、executeはその他を想定しています。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class DatabasePool:
    def __init__(self) -&amp;gt; None:
        # PooledDBを使用してコネクションプールを作成
        self.pool = PooledDB(
            creator=pymysql,  # 使用するDBAPI
            maxconnections=6,  # プールする最大接続数
            host=&amp;quot;mysql&amp;quot;,
            user=&amp;quot;root&amp;quot;,
            passwd=&amp;quot;password&amp;quot;,
            db=&amp;quot;ore&amp;quot;,
            charset=&amp;quot;utf8mb4&amp;quot;,
        )

    def get_connection(self):
        return self.pool.connection()

class Store(DatabasePool):
    def __init__(self):
        super().__init__()

    def fetch_data(self, query, data):
        with self.get_connection() as connection:
            with connection.cursor() as cursor:
                cursor.execute(query, data)
                return cursor.fetchall()

    def execute(self, query, data):
        with self.get_connection() as connection:
            with connection.cursor() as cursor:
                cursor.execute(query, data)
                connection.commit()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;インフラはPyMySQLに依存していますが、executeとqueryの形を維持しておけば派生クラスへの影響を無くすことができます。インフラ以降の際は基底クラスを弄るだけで済むので現実的な工数で載せ替えが可能になると思っています。いや～、楽ですね…。小噺ですが、今の現場ではライブラリ的に問題のあるライブラリを使っていますが、このような設計にするおかげでスムーズに移行ができそうです。&lt;strong&gt;このように常にライブラリから脱出することを見込んだ設計にするのはとても大事だなと実感しています。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;idは直で渡していますがこれは要はバランス…（笑）だって、これだけでDTO作るのはそれはそれで面倒でしょう。idで物を引くという極めて多いユースケースに至ってはDTOを使わないというルールを設けてしまえば最初に述べた一貫性も維持できますし、煩わしさも抑制できます。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;%E3%81%BE%E3%81%A8%E3%82%81%E3%81%B8&quot; tabindex=&quot;-1&quot;&gt;まとめへ&lt;/h1&gt;
&lt;p&gt;いや～、オレオレしていたでしょ？とりあえず、このFWの考え方に従えば今の現場における私の課題は解決することが出来ています（大事）。あとはフレームワークを作る中で、既製品の考え方も再確認出来てstarletteは最高！の気分になっています。あと、私はフルスタックフレームワークが余り好きではないので素朴な既製品がより素朴になったなと思うとニヤニヤが止まらないですね。&lt;/p&gt;
&lt;p&gt;正直、これをそのまま現場に持ち込んでも同僚の反発は必至です。また、これが大規模になったり、5年後生き残るのか？とか言われたらそれな…という話になります。ただ、随所でクリティカルなケースを考えてそれの脱出を見込んだ設計を心がけてるあたり、そういう気配りとシンプルを積み重ねた物があればある程度は何とかなるのではないでしょうか？&lt;/p&gt;
&lt;h2 id=&quot;%E3%82%AA%E3%83%AC%E3%82%AA%E3%83%AC%E3%82%92%E7%8F%BE%E5%A0%B4%E3%81%AB%E6%8A%95%E5%85%A5%E3%81%97%E3%81%9F%E3%81%84%E6%96%B9%E3%81%B8&quot; tabindex=&quot;-1&quot;&gt;オレオレを現場に投入したい方へ&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;それは家庭菜園でやれ！、以上！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;まあ、世の中綺麗事では通らないこともあるからな…。ただ、実戦テストを通じていない物を投入すると本当にヤバいので止めた方が良いと思います。今回のオレオレFWも自分が今まで様々に開発したり現場に投入して2年近く…（フィクションです）使った実績を元に、今作るならこうするという考えを述べているものです。私は経験の浅いエンジニアですが、それでも数年間取り組んだ成果として半年くらいは何とかなるオレオレFWになっています。&lt;/p&gt;
&lt;p&gt;まあ、その上で用量用法を守って同僚を酒や食事で口説き落として部分的にオレオレを使って成果を出して納得していただく…というのが個人的にオススメのオレオレの普及方法です。オレオレFWが現場で機能したら1人のエンジニアとしてちょっと嬉しいですよね（笑）&lt;/p&gt;
&lt;p&gt;ただ、最後は何で作るかより何を作るかですよ？手段に捕らわれ過ぎるのは良くない。私たちは事業課題を捉えてそれを技術で解決するプロなのですから…&lt;/p&gt;
&lt;h1 id=&quot;%E7%9C%9F%E5%AE%9F%E3%81%AE%E3%81%BE%E3%81%A8%E3%82%81&quot; tabindex=&quot;-1&quot;&gt;真実のまとめ&lt;/h1&gt;
&lt;p&gt;いや～、最近はとある現場に思う事が多々ありすぎまして…。まあ、世の中は様々に様々ですが皆さん強く生きていきましょう。現実を捉えて課題を解決する地道な積み重ねをしていけばきっと人生は良き物になると思います。そう信じて、私はけもの道を歩み続けます…。&lt;/p&gt;
&lt;p&gt;来年のクリスマスイブはせめて1人温泉でゆっくり…なんて事を思っています。では、外は寒いですが独りぼっちのクリスマスイブを強く生きてください。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1 id=&quot;%E3%81%93%E3%81%93%E3%81%8B%E3%82%89%E5%85%88%E3%81%AF%E7%9C%9F%E5%AE%9F%E3%82%92%E3%81%8A%E4%BC%9D%E3%81%88%E3%81%97%E3%81%BE%E3%81%99&quot; tabindex=&quot;-1&quot;&gt;ここから先は真実をお伝えします&lt;/h1&gt;
&lt;p&gt;今年のSOHO祭（架空のイベント）に合わせて爆誕した真実のサイトを研究室のメンバーと作成しました。このサイトはとにかく軽量で必要な情報をミニマムに提供することを目指しています。機能は「ブース一覧」「エリア別フィルタ」「ステージスケジュールのカレンダー（ical互換）」の3つです。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sohosai23.basyauma.jp/&quot;&gt;シン・SOHO祭 TOP&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ちなみにロゴの色ですが#FF7E1Cと#00ACD1を使っています。これには明確な意図がある色で、決してとあるサイトのロゴを参考にしている物ではありません。これが分かる人は私と仲良くなれると思うのでDMをください（笑）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ソースコードはこちらで公開しています。見て分かると思いますが、HTML,CSS,JSのシンプルな作りになっています。何で作るのではなく、何を作るかが大事ですよね？必要な人に必要な機能が提供できれば、技術スタックなんてどうでもいいんですよ。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/halca-crowd/soho-Illuminati&quot;&gt;https://github.com/halca-crowd/soho-Illuminati&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;なお、PageSpeed Insightのスコアは何処かのサイトと比べて遙かに好成績をマークしています。GAを入れたせいで若干数値が悪くなっていますが、これを抜くとオール100に近いスコアをマークしました。来年以降も気が向いたら真実をお伝えする予定なので、こちらのアカウントもよろしくお願いします。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/soho_illuminati?s=20&quot;&gt;soho-Illuminati (@soho_illuminati) on X&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;このサイト制作ではスクラムを実践した開発を行ない、なんと8時間で0からサイトを立ち上げることが出来ました。このサイト制作で培われたスクラムのノウハウについては、いずれ何処かでお話しようと思います。以上、SOHO祭の真実でした。皆さん、よいお年をお迎えください。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;マチアプの今年の戦績ですが13戦0勝、3サービスBANでした。対戦ありがとうございました。これさ、本気でそういう出会いを求めるときにオレオレFW以上のけもの道になるのでは？まず、手段が断たれているので詰みなんだよね（笑）以上、notch_manの真実をお伝えしました。私は一人イブの夜の街に消えます。&lt;/p&gt;
</content>
  </entry>
  
  
  <entry>
    <title>VPC PeeringとALBのバーチャルホストで環境の切り分けとコスト削減を両立させる</title>
    <link href="https://developers.crowd4u.org/posts/2023-10-30-vpc-peer-alb/"/>
    <updated>2023-10-30T10:05:00Z</updated>
    <id>https://developers.crowd4u.org/posts/2023-10-30-vpc-peer-alb/</id>
    <author>
        <name>ギルド</name>
        <uri>https://developers.crowd4u.org/author/gild</uri>
    </author>
    <content xml:lang="ja" type="html">&lt;h2 id=&quot;%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB&quot; tabindex=&quot;-1&quot;&gt;はじめに&lt;/h2&gt;
&lt;p&gt;NextCrowd4U(N4U)では様々なタスクの実行を可能にするためにOpenAIのAPIやGoogleSearchAPIを使用する機能の提供をしています。&lt;/p&gt;
&lt;p&gt;これらの機能はN4U本体とは独立したもののためN4Uとは別のリソース(EC2やECSだけでなくVPCなども別です)として立てています。&lt;/p&gt;
&lt;p&gt;今回はALBが高くて新しく立てたくないという話と、ALBが存在しないVPCに新規サーバーを建てる際にVPCのためピアリングを使用して、ALBに導線をつなぎバーチャルホストとして立てたという内容を書いていきます。&lt;/p&gt;
&lt;h2 id=&quot;%E4%BB%8A%E5%9B%9E%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B%E7%92%B0%E5%A2%83&quot; tabindex=&quot;-1&quot;&gt;今回作成する環境&lt;/h2&gt;
&lt;p&gt;まず、今回開発したものについて触れておきます。ザックリ言うと、OpenAIのAPIをたたき、N4U本体に返すような踏み台的な環境を作成しました。イメージとしては下の図のようなものになっております。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://developers.crowd4u.org/posts/2023-10-30-vpc-peer-alb/ecs-vhost.png&quot; alt=&quot;作成する構成&quot; /&gt;&lt;/p&gt;
&lt;p&gt;以下の様な構成で、VPCピアリングを使用し、別のVPCで使用しているALBをバーチャルホストで使用する、という形での実装を今しています。&lt;/p&gt;
&lt;p&gt;API Gatewayを使用しなかった理由としては、Streamをするような設定が面倒だったためですね。。。&lt;/p&gt;
&lt;p&gt;このような面倒なことをした理由を書いた上で、具体的な実装についてTerraformを使用して紹介していきます。&lt;/p&gt;
&lt;h2 id=&quot;%E7%90%86%E7%94%B1%EF%BC%9Aelb%2C%E9%AB%98%E3%81%84%E3%83%BB%E3%83%BB%E3%83%BB&quot; tabindex=&quot;-1&quot;&gt;理由：ELB,高い・・・&lt;/h2&gt;
&lt;p&gt;私たちの開発チームは毎月80USD程度のコストをELBに使用しています。（ELBの料金体系に関しては&lt;a href=&quot;https://aws.amazon.com/jp/elasticloadbalancing/pricing/&quot;&gt;こちらの公式ページ&lt;/a&gt;を参照してください。）&lt;/p&gt;
&lt;p&gt;合計で5つのELBが立っていますが、そのうち2つはほとんどトラフィックがないNLB（そのため金額はほぼ掛かっていない）で、残りの3つのALBがこのコストのほとんどの割合を占めています。&lt;/p&gt;
&lt;p&gt;LLMAPIも今後研究の実験で何度もトラフィックが発生することが分かっているため、ALBの料金が当然ネックになってきます。&lt;/p&gt;
&lt;p&gt;一つのALBで可能な限り処理すれば、少なくとも時間当たりの料金が新しく作成するよりも削減することが出来ると踏み、今回の構成にしました。&lt;/p&gt;
&lt;h2 id=&quot;%E5%AE%9F%E8%A3%85&quot; tabindex=&quot;-1&quot;&gt;実装&lt;/h2&gt;
&lt;p&gt;実際に今回作成したIaCの一部を紹介していきます(このままコピー＆ペーストしても動きませんので注意してください)。各リソースは直書きにしていますが、適宜tfvarsで管理したりSecrets Managerを使用したものにするなどして対応してください。&lt;/p&gt;
&lt;h3 id=&quot;vpc-%E3%83%94%E3%82%A2%E3%83%AA%E3%83%B3%E3%82%B0&quot; tabindex=&quot;-1&quot;&gt;VPC ピアリング&lt;/h3&gt;
&lt;p&gt;今回のケースとして、「本番VPCとその上にVPCが作成されており、新しくFargateを立てるVPCは存在しない」というものだったため、新規でVPCを作成し、ピアリングの設定を行います。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VPCの作成
&lt;ul&gt;
&lt;li&gt;CIDRブロックは適当に書いていますので環境に応じて書き換えてください。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Terraform&quot;&gt;resource &amp;quot;aws_vpc&amp;quot; &amp;quot;hoge-vpc&amp;quot; {
  tags = {
    Name = &amp;quot;hoge-vpc&amp;quot;
  }
  cidr_block = &amp;quot;0.0.0.0/16&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;VPC上にパブリックサブネットを2つ作成します。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Terraform&quot;&gt;resource &amp;quot;aws_subnet&amp;quot; &amp;quot;hoge-subnet-1&amp;quot;{
    vpc_id = aws_vpc.hoge-vpc.id
    cidr_block = &amp;quot;0.0.0.0/24&amp;quot;
    availability_zone = &amp;quot;ap-northeast-1a&amp;quot;
    map_public_ip_on_launch = true
}

resource &amp;quot;aws_subnet&amp;quot; &amp;quot;hoge-subnet-2&amp;quot;{
    vpc_id = aws_vpc.hoge-vpc.id
    cidr_block = &amp;quot;0.0.1.0/24&amp;quot;
    availability_zone = &amp;quot;ap-northeast-1c&amp;quot;
    map_public_ip_on_launch = true
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;インターネットゲートウェイも続けて作成します。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Terraform&quot;&gt;resource &amp;quot;aws_internet_gateway&amp;quot; &amp;quot;hoge-internet-gateway&amp;quot; {
  vpc_id = aws_vpc.hoge-vpc.id
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最後に、ピアリングのためにルートテーブルとルートを作成します。&lt;/p&gt;
&lt;p&gt;prod-vpc → hoge-vpcとhoge-vpc → prod-vpcの両方について接続の設定をしていきます。&lt;/p&gt;
&lt;p&gt;ルートテーブルの設定を間違えると接続がうまく行きませんので、よく確認しながら設定を行ってください。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Terraform&quot;&gt;# ルートテーブルの作成
resource &amp;quot;aws_route_table&amp;quot; &amp;quot;hoge_to_prod-route-table&amp;quot; {
  vpc_id = aws_vpc.hoge-vpc.id

  tags = {
    Name = &amp;quot;hoge_to_prod-route-table&amp;quot;
  }
}

# ルートの作成
resource &amp;quot;aws_route&amp;quot; &amp;quot;hoge_to_prod_route&amp;quot; {
  route_table_id = aws_route_table.hoge_to_prod-route-table.id
  destination_cidr_block = data.aws_vpc.prod-vpc.cidr_block
  vpc_peering_connection_id = aws_vpc_peering_connection.hoge_to_prod.id
}

resource &amp;quot;aws_route&amp;quot; &amp;quot;prod_to_hoge_route&amp;quot; {
  route_table_id = data.aws_route_table.prod-route-table.id
  destination_cidr_block = aws_vpc.hoge-vpc.cidr_block
  vpc_peering_connection_id = aws_vpc_peering_connection.hoge_to_prod.id
}

resource &amp;quot;aws_route&amp;quot; &amp;quot;hoge_to_internet_route&amp;quot; {
  route_table_id = aws_route_table.hoge_to_prod-route-table.id
  destination_cidr_block = &amp;quot;0.0.0.0/0&amp;quot;
  gateway_id = aws_internet_gateway.hoge-internet-gateway.id
}

# ルートテーブルの関連付け
resource &amp;quot;aws_route_table_association&amp;quot; &amp;quot;hoge-route-table-association-1&amp;quot; {
  subnet_id = aws_subnet.hoge-subnet-1.id
  route_table_id = aws_route_table.hoge_to_prod-route-table.id

  depends_on = [ aws_route_table.hoge_to_prod-route-table ]
}

resource &amp;quot;aws_route_table_association&amp;quot; &amp;quot;hoge-route-table-association-2&amp;quot; {
  subnet_id = aws_subnet.hoge-subnet-2.id
  route_table_id = aws_route_table.hoge_to_prod-route-table.id
  
  depends_on = [ aws_route_table.n4u_hoge_to_prod-route-table ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-Terraform&quot;&gt;resource &amp;quot;aws_vpc_peering_connection&amp;quot; &amp;quot;external_to_prod&amp;quot; {
  vpc_id = aws_vpc.hoge-vpc.id
  peer_vpc_id = data.aws_vpc.huga-vpc.id
  auto_accept = true

  tags = {
    Name = &amp;quot;hogehuga-peer&amp;quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;fargate-spot%E3%81%AE%E4%BD%9C%E6%88%90&quot; tabindex=&quot;-1&quot;&gt;Fargate SPOTの作成&lt;/h3&gt;
&lt;p&gt;次に、ALBがないVPC側でFargateを構築します。今回はSPOTインスタンスを使用するため、capacity_providerに関しても設定の必要があります。&lt;/p&gt;
&lt;p&gt;Fargateのタスク定義は省略しますが、環境に応じて作成してください。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;クラスタの作成&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Terraform&quot;&gt;resource &amp;quot;aws_ecs_cluster&amp;quot; &amp;quot;hoge-cluster&amp;quot; {
  name = &amp;quot;hoge&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;サービスの作成
&lt;ul&gt;
&lt;li&gt;コンテナのポートに関しても環境に応じて書き換えてください。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Terraform&quot;&gt;resource &amp;quot;aws_ecs_service&amp;quot; &amp;quot;hoge-api&amp;quot; {
  name = &amp;quot;hoge-api&amp;quot;
  cluster = var.ecs_cluster
  task_definition = aws_ecs_task_definition.hoge-api-aws_ecs_task_definition.arn
  desired_count = 1

  capacity_provider_strategy {
    capacity_provider = &amp;quot;FARGATE_SPOT&amp;quot;
    weight = 1
  }

  network_configuration {
    assign_public_ip = true
    subnets = [&amp;quot;subnet-hogehuga&amp;quot;, &amp;quot;subnet-piyohoge&amp;quot;]
    security_groups = [&amp;quot;sg-hogehugapiyo&amp;quot;]
  }

  load_balancer {
    target_group_arn = aws_lb_target_group.hoge-api-tg.arn
    container_name = &amp;quot;hoge&amp;quot;
    container_port = &amp;lt;your port&amp;gt;
  }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最後に、お好みでタスクを作成するコードを書いていただければ完成です。&lt;/p&gt;
&lt;p&gt;以上のコードを&lt;code&gt;terraform apply&lt;/code&gt;することで、VPCピアリングをして他のVPC上のALBに接続することが出来ます。&lt;/p&gt;
&lt;h2 id=&quot;%E6%9C%80%E5%BE%8C%E3%81%AB&quot; tabindex=&quot;-1&quot;&gt;最後に&lt;/h2&gt;
&lt;p&gt;作成時に困った点としては、ルートテーブルの知見が少なく、正しくルートが設定されていないことに起因して接続がうまく行かないことでした。もしVPCピアリングを使用してインフラ構築をするときに接続がうまく行かない場合、ルート周りから調べていくと良いのかなと今回の経験を通じて感じました。&lt;/p&gt;
&lt;p&gt;今回はほぼIaCの紹介という形になってしまいましたが、ここまでお付き合いいただきありがとうございました！&lt;/p&gt;
</content>
  </entry>
  
  
  <entry>
    <title>ﾜﾀｼ、ﾗﾑﾀﾞ、ﾜｶﾗﾅｲ</title>
    <link href="https://developers.crowd4u.org/posts/2023-08-10-lambda-api/"/>
    <updated>2023-08-10T04:09:24Z</updated>
    <id>https://developers.crowd4u.org/posts/2023-08-10-lambda-api/</id>
    <author>
        <name>ギルド</name>
        <uri>https://developers.crowd4u.org/author/gild</uri>
    </author>
    <content xml:lang="ja" type="html">&lt;h2 id=&quot;%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB&quot; tabindex=&quot;-1&quot;&gt;はじめに&lt;/h2&gt;
&lt;p&gt;ふざけたタイトルで始めましたが、とあるシステムを移植する際に発生した問題、結局どうしたかについて書いていきます。最初に感想を話すと、「知見がなさすぎる、ﾂﾗｲ」です(もっと勉強しないとなぁ…)。&lt;/p&gt;
&lt;p&gt;さて、事の発端は現行のNextCrowd4U(N4U)に以下のような状況が発生していることです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;N4Uに依存している訳ではないが、N4Uの環境に共存しているシステムが存在している&lt;/li&gt;
&lt;li&gt;現状は問題ないが、今後の機能実装の際に本番環境のリソースを圧迫する可能性がある&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以上の理由から当該システムをN4Uから切り離すこと、また当然ながら研究費は限られていますからできる限り低コストで移行する必要がありました。&lt;/p&gt;
&lt;p&gt;すぐにできそうかなと思っていたのですが、結果何もわからず1か月くらい格闘することになったので書いていこうと思います。&lt;/p&gt;
&lt;p&gt;図が大雑把なのはご愛敬と思いながら読んでいただけると幸いです。&lt;/p&gt;
&lt;h2 id=&quot;%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6&quot; tabindex=&quot;-1&quot;&gt;前提条件&lt;/h2&gt;
&lt;p&gt;切り離しの際にいくつかの前提条件がありましたんので最初にまとめておきます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Goで実装されたAPI&lt;/li&gt;
&lt;li&gt;システムの内部処理で外部API(Google Search API)を使用している&lt;/li&gt;
&lt;li&gt;Search APIのリソース削減のため、Memcachedを使用している
&lt;ul&gt;
&lt;li&gt;検索結果をキャッシュして同一の検索クエリに対してAPIを使用せずに結果を返すようにしている&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これに加え、コストをできるだけ低くするという制約が存在しています。&lt;/p&gt;
&lt;h2 id=&quot;%E2%91%A0%E3%81%8A%E3%82%8C%E3%81%AE%E3%81%8B%E3%82%93%E3%81%8C%E3%81%88%E3%81%9F%E6%9C%80%E5%BC%B7%E3%81%AE%E6%A7%8B%E6%88%90&quot; tabindex=&quot;-1&quot;&gt;①おれのかんがえた最強の構成&lt;/h2&gt;
&lt;p&gt;最初に考えたのは以下のような構成です。&lt;/p&gt;
&lt;p&gt;ElastiCache(Memcached)と接続するためにLambdaをVPC内に配置しています。&lt;/p&gt;
&lt;p&gt;LambdaはGoに対応しているので、Lambdaでシステムを実行し、APIGatewayでAPIを立てるのが一番良いのかなという話です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://developers.crowd4u.org/posts/2023-08-10-lambda-api/lambda.png&quot; alt=&quot;最初の構成&quot; /&gt;&lt;/p&gt;
&lt;p&gt;しかし、この構成には問題点が存在し破綻しました。&lt;/p&gt;
&lt;h3 id=&quot;lambda%E3%81%8C%E5%A4%96%E9%83%A8%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%81%A8%E9%80%9A%E4%BF%A1%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84&quot; tabindex=&quot;-1&quot;&gt;Lambdaが外部サービスと通信できない&lt;/h3&gt;
&lt;p&gt;一か月ほど格闘した主な理由はこれに気づくのに時間がかかりすぎた事です。&lt;/p&gt;
&lt;p&gt;そもそもGoのソースをLambdaで使えるように変えていなかったみたいなゴタゴタがありながらも、デプロイがうまく行かなかった際に主にうまく行かなかった理由は以下の2点でした。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ElastiCacheとからレスポンスが返ってこない&lt;/li&gt;
&lt;li&gt;Search APIにリクエストを投げることが出来ない&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;1に関してはアウトバウンドルールの設定が間違っていたことが原因で解決にそこまで時間はかかりませんでした。&lt;/p&gt;
&lt;p&gt;問題は2で、セキュリティグループですべての通信を許可するように設定しても通信が上手くいかないところまでは分かっていたのですが、だからこそIAM Roleやソースコード側に原因があると思い込み、先輩に指摘されるまでVPCはじめネットワーク周りの調査を全然してませんでした。&lt;/p&gt;
&lt;p&gt;先輩の指摘後、改めて調査すると以下の記事を見つけました。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.classmethod.jp/articles/lambda-vpc-with-global-address/&quot;&gt;VPC LambdaはIGWからインターネットに出られないわけではなかったという話 | DevelopersIO&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;なんと、VPC LambdaはパブリックIPを持たないらしい。。。&lt;/p&gt;
&lt;p&gt;自分でも検証してみましたが「な か っ た」。外部と通信できないのはそれはそうという感じですね()&lt;/p&gt;
&lt;p&gt;つまり以下の現象が本構成でシステムを実装する際に発生しています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ElastiCacheと接続するためにLambdaはVPC内に配置する必要がある&lt;/li&gt;
&lt;li&gt;しかし、VPC LambdaはパブリックIPを持たないため外部と通信できない&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これの対応方法として、&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;NAT Gatewayを利用する&lt;/li&gt;
&lt;li&gt;Lambdaをあきらめる&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;という2つの方法が存在します。実際に採用した構成を次節で紹介します。&lt;/p&gt;
&lt;h2 id=&quot;%E2%91%A1%E5%AE%9F%E9%9A%9B%E3%81%AB%E6%8E%A1%E7%94%A8%E3%81%97%E3%81%9F%E6%A7%8B%E6%88%90&quot; tabindex=&quot;-1&quot;&gt;②実際に採用した構成&lt;/h2&gt;
&lt;p&gt;最終的に、1のNAT Gatewayを利用するコストと2のECS(Elastic  Container Service)やElastic BeanStalkなどを利用するコストを検討した結果、以下の4つの理由から後者の選択肢(ECS Fargate)を選びました&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;STGの際に使用したことがある&lt;/li&gt;
&lt;li&gt;常に使うシステムではないため使わない時は停止しておきたい&lt;/li&gt;
&lt;li&gt;軽いシステムなので大してvCPUやメモリを消費しない(最小設定で実行可能)&lt;/li&gt;
&lt;li&gt;2・3の理由からNATを使用するよりコストは抑えられる可能性が高い&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://developers.crowd4u.org/posts/2023-08-10-lambda-api/fargate.png&quot; alt=&quot;Fargate構成&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ここからはSTG環境で使用したタスク定義ファイルを一部使いまわしながら、サクッと実装することが出来ました。&lt;/p&gt;
&lt;p&gt;また、&lt;a href=&quot;https://developers.crowd4u.org/posts/2023-08-04-aws-cost-reduce/&quot;&gt;AWSのコストを$200減らすために行ったこと&lt;/a&gt; でも紹介した通り、ECRのライフサイクルポリシーの設定やSpotインスタンスの採用を行っています。&lt;/p&gt;
&lt;p&gt;出来ました！で終わってしまうのもテックブログとして微妙ですが何はともあれ以上の構成で無事にシステムの移行を行うことで出来ました。&lt;/p&gt;
&lt;h2 id=&quot;%E7%B5%82%E3%82%8F%E3%82%8A%E3%81%AB&quot; tabindex=&quot;-1&quot;&gt;終わりに&lt;/h2&gt;
&lt;p&gt;今回のタスクをこなす際の感想は最初に書いた通り、AWSやインフラ全般に対する経験や知識量の不足から詰まる事が多かったなという印象です。&lt;/p&gt;
&lt;p&gt;純粋な力不足から何もない所で転びがちだったと思うので、知識経験ともに増やしていきたいなと強く思った次第です。&lt;/p&gt;
</content>
  </entry>
  
  
  <entry>
    <title>AWSのコストを$200減らすために行ったこと</title>
    <link href="https://developers.crowd4u.org/posts/2023-08-04-aws-cost-reduce/"/>
    <updated>2023-08-04T05:59:26Z</updated>
    <id>https://developers.crowd4u.org/posts/2023-08-04-aws-cost-reduce/</id>
    <author>
        <name>ギルド</name>
        <uri>https://developers.crowd4u.org/author/gild</uri>
    </author>
    <content xml:lang="ja" type="html">&lt;h2 id=&quot;%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB&quot; tabindex=&quot;-1&quot;&gt;はじめに&lt;/h2&gt;
&lt;p&gt;NextCrowd4uの開発に当たり、STAGING環境の整備や為替レートの上昇に伴い、AWSの利用料が4月時点で700USDを越えてしまいました。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://developers.crowd4u.org/posts/2023-08-04-aws-cost-reduce/chat.png&quot; alt=&quot;教員から来たメッセージ&quot; /&gt;&lt;/p&gt;
&lt;p&gt;主にSTAGING環境の調整を行い400～500USDまで下げたので行ったことについて紹介していきます。&lt;/p&gt;
&lt;h2 id=&quot;1.-%E4%BD%99%E8%A8%88%E3%81%AA%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%AE%E5%89%8A%E9%99%A4&quot; tabindex=&quot;-1&quot;&gt;1. 余計なリソースの削除&lt;/h2&gt;
&lt;p&gt;定番ですが、余計なリソースの削除です。以下の不要なリソースを削除していきました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用していないEC2インスタンス&lt;/li&gt;
&lt;li&gt;1つでいいのになぜか2つ立っていたECSタスク&lt;/li&gt;
&lt;li&gt;たまりまくったECRのイメージ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ECRイメージに関しては、使用していない5日以上作成してから立っているコンテナを削除するようなライフサイクルポリシーを作成し、定期的にクリーニングするようにしました。&lt;/p&gt;
&lt;h2 id=&quot;2.-staging%E7%92%B0%E5%A2%83%E3%81%AE%E6%94%B9%E5%96%84&quot; tabindex=&quot;-1&quot;&gt;2. STAGING環境の改善&lt;/h2&gt;
&lt;h3 id=&quot;%E2%91%A0fargate%E3%81%AEspot%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E5%8C%96&quot; tabindex=&quot;-1&quot;&gt;①FargateのSpotインスタンス化&lt;/h3&gt;
&lt;p&gt;Fargate Spotとは&lt;a href=&quot;https://aws.amazon.com/jp/blogs/news/aws-fargate-spot-now-generally-available/&quot;&gt;AWS公式ブログ&lt;/a&gt;(7/27参照)では以下のような説明をされています。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Fargate Spotは&lt;a href=&quot;https://aws.amazon.com/fargate/&quot;&gt;AWS Fargate&lt;/a&gt;の新しい機能です。中断に強い&lt;a href=&quot;https://aws.amazon.com/ecs/&quot;&gt;Amazon Elastic Container Service (Amazon ECS)&lt;/a&gt;タスクに最適であり、Fargate価格から最大70%割引で提供します。
(中略)
Fargate Spotは、AWSクラウドの空きキャパシティを活用してタスクを実行します。Fargate Spotが空きキャパシティを確保できるかぎり、ユーザーは指定したタスクを起動することができます。AWSにキャパシティが必要になったとき、Fargate Spotで稼働するタスクは2分前の通知とともに中断されることになります。Fargate Spot用のキャパシティが使用できなくなると、Fargateは稼働中の通常のタスクを保持しながら、Fargate Spotで稼働するタスクをスケールダウンします。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;AWSの空きリソースを使用することで安く利用することが出来るとのことです。実際に金額を比較してみると(2023/07/27時点)、&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Fargate&lt;/th&gt;
&lt;th&gt;Fargate Spot&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;vCPU(1時間当たりのUSD)&lt;/td&gt;
&lt;td&gt;0.05056&lt;/td&gt;
&lt;td&gt;0.015168&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory(1時間当たりのUSD)&lt;/td&gt;
&lt;td&gt;0.00553&lt;/td&gt;
&lt;td&gt;0.001659&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;とかなり安いことが分かります。&lt;/p&gt;
&lt;p&gt;STAGING環境であるという点や、Spotインスタンスが使えなくなった場合、通常のFargateに移行するような設定も可能なことからSpotインスタンスの導入を行いました。&lt;/p&gt;
&lt;h3 id=&quot;%E2%91%A1%E3%82%BF%E3%82%A4%E3%83%A0%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%AE%E5%B0%8E%E5%85%A5&quot; tabindex=&quot;-1&quot;&gt;②タイムスケーリングの導入&lt;/h3&gt;
&lt;p&gt;当然、STAGING環境は誰も使用しないような時間が存在します。&lt;/p&gt;
&lt;p&gt;そのため開発メンバーが作業しない時間帯はFargateの実行タスクを0にすることでダウンさせるようにしました。&lt;/p&gt;
&lt;p&gt;実現の方法としてAWS CLIで設定する方法や、EventBridgeとLambdaで実行する方法がありますが、今回はAWS CLIを使用する方法で実行しました。（AWSマネジメントコンソールで確認できないのつらい。。。）&lt;/p&gt;
&lt;p&gt;以下のようなCronで、実行タスクを0にする設定と実行タスクを1以上にする設定をCLIで仕込むことでタイムスケーリングの設定をすることが可能です。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;aws application-autoscaling put-scheduled-action --service-namespace ecs --resource-id service/{クラスタ名}/{サービス名} --scheduled-action-name {スケジュール名(schedule-onなど)} --schedule &amp;quot;cron({Scaling Time})&amp;quot; --scalable-dimension ecs:service:DesiredCount --scalable-target-action MinCapacity=0,MaxCapacity=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;設定した時間を確認したい場合は以下のコマンドで実行できます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;aws application-autoscaling describe-scheduled-actions --service-namespace ecs --resource-id service/{クラスタ名}/{サービス名}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;削除する場合はリソースとスケジュール名を入力することで実行することが可能です。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;aws application-autoscaling delete-scheduled-action --service-namespace ecs --resource-id service/{クラスタ}/{サービス名} --scheduled-action-name {スケジュール名} --scalable-dimension ecs:service:DesiredCount
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB&quot; tabindex=&quot;-1&quot;&gt;おわりに&lt;/h2&gt;
&lt;p&gt;まだ考えられるコスト軽減策はあるかと思いますが、今回は主にFargateのタイムスケールについて書いてきました。&lt;/p&gt;
&lt;p&gt;実際に以上の対策の実行後、一日当たりのFargateのコストが明らかに減少しました。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://developers.crowd4u.org/posts/2023-08-04-aws-cost-reduce/graph.png&quot; alt=&quot;減ったAWSコスト&quot; /&gt;&lt;/p&gt;
&lt;p&gt;今後、Fargateは更に活用していくことになると思うので、コスト面では気をつけつつ、使いこなしていきたいなと思っています！&lt;/p&gt;
&lt;p&gt;ここまで読んでいただきありがとうございました。&lt;/p&gt;
</content>
  </entry>
  
  
  <entry>
    <title>M1とWSL両方で動作するJupyterLabのDockerファイルを作った話</title>
    <link href="https://developers.crowd4u.org/posts/2023-02-16-jupyterdocker/"/>
    <updated>2023-02-16T07:32:38Z</updated>
    <id>https://developers.crowd4u.org/posts/2023-02-16-jupyterdocker/</id>
    <author>
        <name>ギルド</name>
        <uri>https://developers.crowd4u.org/author/gild</uri>
    </author>
    <content xml:lang="ja" type="html">&lt;h2 id=&quot;%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB&quot; tabindex=&quot;-1&quot;&gt;はじめに&lt;/h2&gt;
&lt;p&gt;融合知能デザイン研究室でもB3向けのプレゼミが始まり、先生や先輩も環境を再現しやすくするためにDockerでコンテナ立てるかーと思い至りました。&lt;/p&gt;
&lt;p&gt;そのため、研究室のPC(M1)と自分のPC(Ubuntu on WSL2)の両方でPython, 特にJupyterLabが動作するようなコンテナを作ったので紹介していきます。&lt;/p&gt;
&lt;h3 id=&quot;%E3%81%93%E3%81%AE%E8%A8%98%E4%BA%8B%E3%81%A7%E6%89%B1%E3%81%86%E3%82%82%E3%81%AE&quot; tabindex=&quot;-1&quot;&gt;この記事で扱うもの&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;DockerコンテナのPython3.10イメージでJupyterLabをインストールする方法&lt;/li&gt;
&lt;li&gt;Dockerで立ち上げたJupyterLabをブラウザで見る方法&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;%E3%81%93%E3%81%AE%E8%A8%98%E4%BA%8B%E3%81%A7%E6%89%B1%E3%82%8F%E3%81%AA%E3%81%84%E3%82%82%E3%81%AE&quot; tabindex=&quot;-1&quot;&gt;この記事で扱わないもの&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;JupyterLabイメージをはじめから使用してコンテナを立ち上げる方法&lt;/li&gt;
&lt;li&gt;JupyterLabの設定ファイルを最初から適応する方法&lt;/li&gt;
&lt;li&gt;PyTorchなどの機械学習ライブラリやGPUをDockerで動かす方法&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E6%A7%8B%E6%88%90&quot; tabindex=&quot;-1&quot;&gt;ファイル構成&lt;/h2&gt;
&lt;p&gt;以下のようなファイル構成で作成を行います。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.
│  docker-compose.yml
│  Dockerfile
│  requirements.txt
│
└─app
    └─src
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;requirements.txt&quot; tabindex=&quot;-1&quot;&gt;requirements.txt&lt;/h2&gt;
&lt;p&gt;JupyterLabだけ記述していきます。&lt;/p&gt;
&lt;p&gt;これだけならDockerfileに直接書けばいいのですが、実際は他にもライブラリを入れるので。。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;jupyterlab
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;dockerfile&quot; tabindex=&quot;-1&quot;&gt;Dockerfile&lt;/h2&gt;
&lt;p&gt;早速Dockerfileを記述していきます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Dockerfile&quot;&gt;FROM python:3.10-slim

ENV PYTHONBURRERED=1

WORKDIR /usr/src/app

# python &amp;amp; gcc settings
RUN apt update &amp;amp;&amp;amp; apt install -y &#92;
    tzdata &#92;
&amp;amp;&amp;amp; apt-get install -y curl &#92;
&amp;amp;&amp;amp; apt-get install -y gcc &#92;
&amp;amp;&amp;amp;  ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime &#92;
&amp;amp;&amp;amp;  apt-get clean &#92;
&amp;amp;&amp;amp;  rm -rf /var/lib/apt/lists/*

COPY requirements.txt .

RUN pip install --upgrade pip
RUN pip install --upgrade setuptools

# cargo settings
ENV RUST_HOME /usr/local/lib/rust
ENV RUSTUP_HOME ${RUST_HOME}/rustup
ENV CARGO_HOME ${RUST_HOME}/cargo
RUN mkdir /usr/local/lib/rust &amp;amp;&amp;amp; &#92;
    chmod 0755 $RUST_HOME
RUN curl --proto &#39;=https&#39; --tlsv1.2 -sSf https://sh.rustup.rs &amp;gt; ${RUST_HOME}/rustup.sh &#92;
    &amp;amp;&amp;amp; chmod +x ${RUST_HOME}/rustup.sh &#92;
    &amp;amp;&amp;amp; ${RUST_HOME}/rustup.sh -y --default-toolchain nightly --no-modify-path
ENV PATH $PATH:$CARGO_HOME/bin

RUN pip install -r requirements.txt

# 8000番ポートでトークンなしでJupyterLabを起動する
CMD [&amp;quot;jupyter&amp;quot;, &amp;quot;lab&amp;quot;, &amp;quot;--port&amp;quot;, &amp;quot;8000&amp;quot;, &amp;quot;--allow-root&amp;quot;, &amp;quot;--ip=0.0.0.0&amp;quot;, &amp;quot;--LabApp.token=&#39;&#39;&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;M1やWSLでjupyterlabの起動を行うため、以下の設定を行っています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;gccのインストール&lt;/li&gt;
&lt;li&gt;cargoのインストール
&lt;ul&gt;
&lt;li&gt;そのためのcurlのインストール&lt;/li&gt;
&lt;li&gt;cargoのパスを通すための設定&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以上の処理を行った後、8000番ポートでjupyterlabを起動しています。&lt;/p&gt;
&lt;p&gt;トークンを発行するとブラウザからアクセスが出来なかったので、トークンなしでの起動をしています。&lt;/p&gt;
&lt;h2 id=&quot;docker-compose.yml&quot; tabindex=&quot;-1&quot;&gt;docker-compose.yml&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;version: &amp;quot;3.9&amp;quot;

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 8000:8000
    tty: true
    volumes:
      - ./app:/usr/src/app
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;docker-composeでは先ほど作成したDockerfileを指定して、8000番ポートを使うようにしているだけです。&lt;/p&gt;
&lt;h2 id=&quot;%E8%B5%B7%E5%8B%95&quot; tabindex=&quot;-1&quot;&gt;起動&lt;/h2&gt;
&lt;p&gt;お馴染みのコマンドを入力して起動します。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker-compose build
docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;localhost:8000&lt;/code&gt;にブラウザでアクセスするとJupyterLabにアクセスできます！&lt;/p&gt;
&lt;h2 id=&quot;%E5%8F%82%E8%80%83&quot; tabindex=&quot;-1&quot;&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://c-a-p-engineer.github.io/tech/2022/09/29/docker-rust-install/&quot;&gt;https://c-a-p-engineer.github.io/tech/2022/09/29/docker-rust-install/&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  
  
  <entry>
    <title>Deta.shでサクッとウェブアプリをデプロイしてみる</title>
    <link href="https://developers.crowd4u.org/posts/2023-01-01-start-deta/"/>
    <updated>2022-12-31T15:00:00Z</updated>
    <id>https://developers.crowd4u.org/posts/2023-01-01-start-deta/</id>
    <author>
        <name>ギルド</name>
        <uri>https://developers.crowd4u.org/author/gild</uri>
    </author>
    <content xml:lang="ja" type="html">&lt;h1 id=&quot;%E6%B3%A8%E6%84%8F&quot; tabindex=&quot;-1&quot;&gt;注意&lt;/h1&gt;
&lt;p&gt;Deta.shは2023年5月末でサービスを終了し、&lt;a href=&quot;https://deta.space/&quot;&gt;DetaSpace&lt;/a&gt;への移行を行いました。
これにより、本記事と同様の方法でのデプロイは出来なくなり、利用者もDeta Spaceへのアカウントを持たないとサービスへアクセスができない仕様になりましたので注意してください。&lt;/p&gt;
&lt;h2 id=&quot;%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB&quot; tabindex=&quot;-1&quot;&gt;はじめに&lt;/h2&gt;
&lt;p&gt;昨年、今までアプリケーションの運用ができるPaaS(Platform as a Service)の一つであるHerokuが無料プランを廃止し、代替手段を探すエンジニア(特に学生)が続出しました。
私もそのうちの一人なわけですが、代替サービスでたまに聞くようになったDeta.shが面白そうだったので、サクッとアプリ作成からデプロイまでやってみようと思います。&lt;/p&gt;
&lt;h2 id=&quot;deta.sh%E3%81%A8%E3%81%AF&quot; tabindex=&quot;-1&quot;&gt;Deta.shとは&lt;/h2&gt;
&lt;p&gt;Deta.sh(&lt;a href=&quot;https://www.deta.sh/&quot;&gt;https://www.deta.sh/&lt;/a&gt;)は完全無料のホスティングサービスの一つで、以下の3つのサービスを提供しています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deta Micros: デプロイサービス&lt;/li&gt;
&lt;li&gt;Deta Base: NoSQLデータベース&lt;/li&gt;
&lt;li&gt;Deta Drive: ファイルストレージ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Deta Microsに関してはPythonとNode.jsで書かれたアプリのデプロイができるようですね。&lt;/p&gt;
&lt;h2 id=&quot;%E6%97%A9%E9%80%9F%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;早速アプリを作ってみる&lt;/h2&gt;
&lt;p&gt;というわけで、早速Deta Microsを使ってアプリをデプロイしていこうと思います。
Detaアカウントは作成済という前提で説明していきます。&lt;/p&gt;
&lt;h3 id=&quot;deta-cli%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB&quot; tabindex=&quot;-1&quot;&gt;Deta CLIのインストール&lt;/h3&gt;
&lt;p&gt;Detaアカウントを作成したら、ローカルからDeta Microsへデプロイを行ったりMicro上のプログラムのcron等が出来るDeta CLIをインストールしていきます。&lt;/p&gt;
&lt;p&gt;WindowsであればPowershellで、以下のコマンドを入力します。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;iwr https://get.deta.dev/cli.ps1 -useb | iex
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mac, Linuxであればターミナルで、以下のコマンドを実行するとインストールされます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -fsSL https://get.deta.dev/cli.sh | sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;インストールが終了したら、自身の持つユーザーアカウントにデプロイを行うために、ターミナルからログインコマンドを入力します。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;deta login
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;コマンド入力後、ブラウザが自動で開きログインを求められます。
detaコマンドがないと言われた場合は、ターミナルの再起動やパスの設定を適宜行ってください。&lt;/p&gt;
&lt;h3 id=&quot;%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E4%BD%9C%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;アプリケーションを作る&lt;/h3&gt;
&lt;p&gt;デプロイするためのアプリケーションを作っていきます。
正月ということで、運勢を返すだけのおみくじアプリでも作ってみましょうかね。。。&lt;/p&gt;
&lt;p&gt;今回は以下の環境でアプリを作っていきます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python3.9
&lt;ul&gt;
&lt;li&gt;FastAPI&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HTML
&lt;ul&gt;
&lt;li&gt;Jinja2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CSS
&lt;ul&gt;
&lt;li&gt;Bootstrap5&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Deta.shがFastAPIのスポンサーらしく、FastAPIに&lt;a href=&quot;https://fastapi.tiangolo.com/ja/deployment/deta/&quot;&gt;チュートリアル&lt;/a&gt;が存在しますので参考にしていきます。
最終的なディレクトリ構成は以下の様になりました。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;.
├── main.py
├── requirements.txt
└── template
    ├── index.html
    └── result.html
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;まずは作業ディレクトリをつくり、&lt;code&gt;template&lt;/code&gt;フォルダを作り、その中にトップページの&lt;code&gt;index.html&lt;/code&gt;とおみくじの結果を表示する&lt;code&gt;result.html&lt;/code&gt;を作ります。&lt;/p&gt;
&lt;p&gt;HTMLは以下のように作成しました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;index.html: トップページ&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-HTML&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;link href=&amp;quot;https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; integrity=&amp;quot;sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC&amp;quot; crossorigin=&amp;quot;anonymous&amp;quot;&amp;gt;
        &amp;lt;script src=&amp;quot;https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js&amp;quot; integrity=&amp;quot;sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM&amp;quot; crossorigin=&amp;quot;anonymous&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;title&amp;gt;Deta.sh-Tryout&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1 class=&amp;quot;bg-danger text-white&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;/&amp;quot; class=&amp;quot;text-decoration-none text-white&amp;quot;&amp;gt;おみくじ&amp;lt;/a&amp;gt;&amp;lt;/h1&amp;gt;
            &amp;lt;div class=&amp;quot;card mx-auto&amp;quot; style=&amp;quot;width: 75%; height: 50%;&amp;quot;&amp;gt;
            &amp;lt;div class=&amp;quot;card-body&amp;quot;&amp;gt;
                &amp;lt;form  action=&amp;quot;result&amp;quot; method=&amp;quot;POST&amp;quot;&amp;gt;
                    &amp;lt;button type=&amp;quot;submit&amp;quot; style=&amp;quot;width: 80%; height: 18rem&amp;quot; class=&amp;quot;btn btn-danger mx-auto d-flex justify-content-center&amp;quot;&amp;gt;&amp;lt;h2&amp;gt;おみくじを引く！&amp;lt;/h2&amp;gt;&amp;lt;/button&amp;gt;
                &amp;lt;/form&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;result.html: 結果ページ&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-HTML&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;link href=&amp;quot;https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; integrity=&amp;quot;sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC&amp;quot; crossorigin=&amp;quot;anonymous&amp;quot;&amp;gt;
        &amp;lt;script src=&amp;quot;https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js&amp;quot; integrity=&amp;quot;sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM&amp;quot; crossorigin=&amp;quot;anonymous&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;title&amp;gt;あなたの運勢&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1 class=&amp;quot;bg-danger text-white&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;/&amp;quot; class=&amp;quot;text-decoration-none text-white&amp;quot;&amp;gt;おみくじ&amp;lt;/a&amp;gt;&amp;lt;/h1&amp;gt;
            &amp;lt;div class=&amp;quot;card mx-auto&amp;quot; style=&amp;quot;width: 75%; height: 50%;&amp;quot;&amp;gt;
            &amp;lt;div class=&amp;quot;card-body&amp;quot;&amp;gt;
                &amp;lt;p&amp;gt;ボタンをもう一度クリックするともう一度実行するよ！&amp;lt;/p&amp;gt;
                &amp;lt;form  action=&amp;quot;/result&amp;quot; method=&amp;quot;POST&amp;quot;&amp;gt;
                    &amp;lt;button type=&amp;quot;submit&amp;quot; data-html=&amp;quot;true&amp;quot; style=&amp;quot;width: 80%; height: 18rem&amp;quot; class=&amp;quot;btn btn-danger mx-auto d-flex justify-content-center text-nowrap&amp;quot;&amp;gt;
                        &amp;lt;h2&amp;gt;&amp;lt;/h2&amp;gt;
                    &amp;lt;/button&amp;gt;
                &amp;lt;/form&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;result.html&lt;/code&gt;の15行目、``にはFastAPIから渡された文字列が挿入されます。&lt;/p&gt;
&lt;p&gt;次に、FastAPIでサーバーを立てるための&lt;code&gt;main.py&lt;/code&gt;を作業ディレクトリ直下に作成し、以下のようにします。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
import random

# FastAPIの呼び出し
app = FastAPI()
# Jinja2テンプレートを使用するためにテンプレートディレクトリを指定して呼び出し
templates = Jinja2Templates(directory=&amp;quot;template&amp;quot;)

# Topページのテンプレートを返す
@app.get(&amp;quot;/&amp;quot;, response_class=HTMLResponse)
async def index(request:Request):
    return templates.TemplateResponse(&amp;quot;index.html&amp;quot;, {&amp;quot;request&amp;quot;:request})

# ボタンが押された際に結果画面に遷移する
@app.post(&amp;quot;/result&amp;quot;, response_class=HTMLResponse)
async def result(request:Request):
    # おみくじの運勢リストを作成
    lack_list = [&amp;quot;大吉&amp;quot;, &amp;quot;吉&amp;quot;, &amp;quot;中吉&amp;quot;, &amp;quot;小吉&amp;quot;, &amp;quot;末吉&amp;quot;, &amp;quot;凶&amp;quot;, &amp;quot;大凶&amp;quot;]
    # リストの要素をシャッフルする
    random.shuffle(lack_list)
    # シャッフル後に0番目の要素をおみくじの運勢として返す
    lack = lack_list[0]
    return templates.TemplateResponse(&amp;quot;result.html&amp;quot;, {&amp;quot;request&amp;quot;:request, &amp;quot;lack&amp;quot;:lack})
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;deta%E3%81%AB%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4&quot; tabindex=&quot;-1&quot;&gt;Detaにデプロイ&lt;/h3&gt;
&lt;p&gt;ここまで書けたらDetaにデプロイしてみましょう！&lt;br /&gt;
以下のコマンドを順番に入力していきます。必ずターミナルでは作業ディレクトリを開いた状態にしておいてください。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;deta上でインストールするパッケージを指定するために作業ディレクトリ直下に&lt;code&gt;requirements.txt&lt;/code&gt;を作成する&lt;/p&gt;
&lt;p&gt;今回は&lt;code&gt;requirements.txt&lt;/code&gt;を以下のようにしました。
Numpyなどの他のライブラリも、テキストファイル上に記載することでインストールできそうですね。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;fastapi == 0.85.1
jinja2 == 3.1.2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このコマンドを打った後、以下のような出力が表示されます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
    &amp;quot;name&amp;quot;: {アプリケーションの名前},
    &amp;quot;id&amp;quot;: {Deta MicrosのID},
    &amp;quot;project&amp;quot;: {プロジェクトID},
    &amp;quot;runtime&amp;quot;: &amp;quot;python3.9&amp;quot;,
    &amp;quot;endpoint&amp;quot;: {アプリケーションのURL},
    &amp;quot;region&amp;quot;: &amp;quot;ap-southeast-1&amp;quot;,
    &amp;quot;visor&amp;quot;: &amp;quot;disabled&amp;quot;,
    &amp;quot;http_auth&amp;quot;: &amp;quot;disabled&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これが作成したアプリケーションの情報で、&lt;code&gt;deta details&lt;/code&gt;コマンドでも確認することが出来ます。&lt;/p&gt;
&lt;p&gt;この出力の&lt;code&gt;endpoint&lt;/code&gt;の欄に記載されているのが、デプロイしたアプリケーションのURLとなります。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;アプリケーションをDeta上に作成する&lt;/p&gt;
&lt;p&gt;以下の様にコマンドを入力します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;deta new
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;detaのvisorを有効にします&lt;/p&gt;
&lt;p&gt;アプリケーションの作成した後だと、HTTPS通信のためのポート開放が行われておらず、アプリケーションにアクセスしてもエラーを返すことがあります。&lt;/p&gt;
&lt;p&gt;Detaでは、visorと呼ばれる機能を有効化することで、アプリにアクセスできるようにしてくれるようです。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;deta visor enable
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Detaにデプロイする&lt;/p&gt;
&lt;p&gt;ここまで来たらついにデプロイです！&lt;/p&gt;
&lt;p&gt;オンラインダッシュボード上での煩雑な設定とかもなしに3行でデプロイできてしまいます。すごく便利ですね。。。&lt;/p&gt;
&lt;p&gt;捕捉ですが、バグや新機能の実装後もこのコマンドを実行することで最新のアプリケーションをデプロイすることが出来ます。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;アプリにアクセスして確認する&lt;/p&gt;
&lt;p&gt;早速、1で確認したURLにアクセスして結果を確認してみましょう！&lt;/p&gt;
&lt;p&gt;私の場合は以下のリンクにデプロイされています。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://y0rcw0.deta.dev/&quot;&gt;https://y0rcw0.deta.dev&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;無事デプロイできていそうです！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://developers.crowd4u.org/posts/2023-01-01-start-deta/result.png&quot; alt=&quot;デプロイ結果&quot; /&gt;&lt;/p&gt;
&lt;p&gt;以上がDeta.shを使ってウェブアプリをデプロイする方法です。&lt;/p&gt;
&lt;p&gt;ちなみにDeta.shはウェブダッシュボードでサブドメインを設定することが出来るので、使われていなければ自分のアプリに合ったドメインを設定することも可能です。&lt;/p&gt;
&lt;p&gt;今回作ったアプリケーションは以下のURLからも確認することが出来ます。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://omikuji-n4u.deta.dev/&quot;&gt;https://omikuji-n4u.deta.dev&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB&quot; tabindex=&quot;-1&quot;&gt;おわりに&lt;/h2&gt;
&lt;p&gt;サクッとと言いつつ、少々長くなってしまった気もしますが、ここまで読んでいただきありがとうございました！&lt;/p&gt;
&lt;p&gt;今回紹介したDeta.shは細かい機能不足などはあると思いますが、個人で制作した簡単なアプリを公開するにはかなり便利に感じました。&lt;/p&gt;
&lt;p&gt;Deta.shのサイトを読んでいると、今後も無料でサービスを提供するらしいので、もしかしたら個人的に長いお付き合いになるかもしれません(笑)&lt;/p&gt;
&lt;h2 id=&quot;%E3%81%8A%E3%81%BE%E3%81%91&quot; tabindex=&quot;-1&quot;&gt;おまけ&lt;/h2&gt;
&lt;p&gt;幣開発チームのリーダーが今回作成したアプリを試してくれたらしいのですが、このようなスクショが送られてきました笑&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://developers.crowd4u.org/posts/2023-01-01-start-deta/omake.png&quot; alt=&quot;おまけ&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ま、まぁこれ以上悪くならないという意味でもあるので。。。(震え)&lt;/p&gt;
&lt;h2 id=&quot;%E5%8F%82%E8%80%83&quot; tabindex=&quot;-1&quot;&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;Deta Docs&amp;quot;：&lt;a href=&quot;https://docs.deta.sh/docs/home/&quot;&gt;https://docs.deta.sh/docs/home/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&amp;quot;Detaにデプロイ&amp;quot;: &lt;a href=&quot;https://fastapi.tiangolo.com/ja/deployment/deta/?h=deta&quot;&gt;https://fastapi.tiangolo.com/ja/deployment/deta/?h=deta&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  
  
  <entry>
    <title>GitHub ActionsではじめてのCI/CD</title>
    <link href="https://developers.crowd4u.org/posts/2022-12-13-github-actions/"/>
    <updated>2022-12-31T01:00:41Z</updated>
    <id>https://developers.crowd4u.org/posts/2022-12-13-github-actions/</id>
    <author>
        <name>あけい</name>
        <uri>https://developers.crowd4u.org/author/23akei</uri>
    </author>
    <content xml:lang="ja" type="html">&lt;h2 id=&quot;introduction&quot; tabindex=&quot;-1&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;GitHub Actions ってよく聞くけどわかってないので使ってみたいと思っていました．また，CI/CDについても同様でした．そこで，ポートフォリオサイトのデプロイを題材にGitHub Actionsに触れてみようと考えました．その過程で得た情報を記事にします．&lt;/p&gt;
&lt;h2 id=&quot;github-actions%E3%81%A7%E5%87%BA%E6%9D%A5%E3%82%8B%E3%81%93%E3%81%A8%2F%E3%82%84%E3%82%8A%E3%81%9F%E3%81%84%E3%81%93%E3%81%A8&quot; tabindex=&quot;-1&quot;&gt;GitHub Actionsで出来ること/やりたいこと&lt;/h2&gt;
&lt;p&gt;GitHub Actionsとは何か？GitHub Actionsのドキュメントの冒頭が分かりやすく説明してくれていましたのでそのまま引用します．&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;GitHub Actions は、ビルド、テスト、デプロイのパイプラインを自動化できる継続的インテグレーションと継続的デリバリー (CI/CD) のプラットフォームです。 リポジトリに対するすべての pull request をビルドしてテストしたり、マージされた pull request を運用環境にデプロイしたりするワークフローを作成できます。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://docs.github.com/ja/actions/learn-github-actions/understanding-github-actions#overview&quot;&gt;GitHub Actionsを理解する - GitHub Docs&lt;/a&gt;より&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;今回，GitHub Actionsで行いたいことは以下の２つです．&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Hugoのビルド&lt;/li&gt;
&lt;li&gt;ビルドしたポートフォリオサイトのデプロイ&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;手元にHugoを利用したポートフォリオサイトのプロジェクトがあります．（ポートフォリオサイトの詳細はこちらの記事に：&lt;a href=&quot;https://crowd4u.github.io/posts/2022-12-28-hugo_portfolio/&quot;&gt;Hugoでポートフォリオサイトを作成する際の備忘録&lt;/a&gt;）&lt;/p&gt;
&lt;p&gt;これをローカルでビルドして，出力されたものをGitHubのリモートレポジトリにpushするだけでGitHub Pagesとして公開することは可能です．しかし，その方法だとサイト更新のたびにコミットログが大量の生成ファイルで埋め尽くされてしまい，見づらくなってしまいます．手元でビルド専用のブランチを切って作業することも可能ですが，面倒なのでこのプロセスを自動化したいと思いました．&lt;/p&gt;
&lt;p&gt;というわけで，Hugoのビルド，デプロイをGitHub Actionsで自動化したいと思います．次節からGitHub Actionsの説明，設定を順に行います．&lt;/p&gt;
&lt;h2 id=&quot;workflow%E3%81%AE%E5%AE%9A%E7%BE%A9&quot; tabindex=&quot;-1&quot;&gt;Workflowの定義&lt;/h2&gt;
&lt;p&gt;WorkflowはGitHub Actionsにおいて実行するジョブの集合で，ブランチのプッシュなどのイベントに応じて発火させることができます．Workflowはレポジトリ内の&lt;code&gt;.github/workflows&lt;/code&gt; ディレクトリにYAMLファイルで記述します．&lt;/p&gt;
&lt;p&gt;今回は&lt;code&gt;.github/workflows/hugo-deploy.yml&lt;/code&gt; を作成し， &lt;code&gt;name&lt;/code&gt; を設定ました．これは任意で，無かったら自動的に決まるそうです．&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;name: hugo-deploy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;もう１つ，任意の要素として &lt;code&gt;run-name&lt;/code&gt; があります．例えとして不適切かもしれませんが， &lt;code&gt;name&lt;/code&gt; と &lt;code&gt;run-name&lt;/code&gt; はDockerにおけるイメージ名とコンテナ名に対応する概念です． &lt;code&gt;run-name&lt;/code&gt; は式を含むことが可能で，動的に設定することが可能みたいです．（コンテキストの話になるのでこの記事では省略）&lt;/p&gt;
&lt;h2 id=&quot;workflow%E7%99%BA%E7%81%AB%E6%9D%A1%E4%BB%B6%E3%81%AE%E5%AE%9A%E7%BE%A9&quot; tabindex=&quot;-1&quot;&gt;Workflow発火条件の定義&lt;/h2&gt;
&lt;p&gt;Workflow内の &lt;code&gt;on&lt;/code&gt; 要素にWorkflowの発火条件を記述できます．今回は &lt;code&gt;master&lt;/code&gt; ブランチにpushした際に発火させたいので，以下のように記述します．&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;on:
  push:
  	branches:
  	  - master
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;branches&lt;/code&gt; 以下を省略すれば，全てのブランチにpushされた際に発火させることができます．&lt;/p&gt;
&lt;p&gt;また，push時以外にも発火させることができます．（参考：&lt;a href=&quot;https://docs.github.com/ja/actions/using-workflows/events-that-trigger-workflows&quot;&gt;ワークフローをトリガーするイベント - GitHub Docs&lt;/a&gt;）&lt;/p&gt;
&lt;p&gt;複数のイベントを条件に発火させることもできるようです．具体的には，複数のイベントが同時に発火した場合，それぞれにWorkflowが実行されるらしいです．&lt;/p&gt;
&lt;h2 id=&quot;job%E3%81%AE%E5%AE%9A%E7%BE%A9&quot; tabindex=&quot;-1&quot;&gt;Jobの定義&lt;/h2&gt;
&lt;p&gt;今回実行したい以下の２つの一連の流れをjobとして定義します．&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Hugoのビルド&lt;/li&gt;
&lt;li&gt;ビルドしたポートフォリオサイトのデプロイ&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;上記２つのコマンドについて記述する前にjob全体の定義で設定すべきことがいくつかあります．&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;jobs:
  deploy-hugo:
  	runs-on: ubuntu-latest
  	steps:
  	  - uses: actions/checkout@v3
  	  	with:
  	  	  submodules: true
  	  	  fetch-depatch: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;jobs&lt;/code&gt; 内に &lt;code&gt;deploy-hugo&lt;/code&gt; という名前のjobを定義しました．　&lt;code&gt;runs-on&lt;/code&gt; でjobを実行する仮想マシン（ランナー）の種類を選びます．ubuntu以外にもwindows serverやmacosが選べるみたいです．&lt;/p&gt;
&lt;p&gt;&lt;code&gt;steps&lt;/code&gt; に実行するコマンドを順に記述していきます．&lt;/p&gt;
&lt;p&gt;最初に  &lt;code&gt;uses: actions/checkout@v3&lt;/code&gt; でランナーにリポジトリのコードをチェックアウトします．ランナー上でリポジトリのコードを使用する際にはこれを必ず実行する必要があります．今回はランナー上でHugoのビルドを行うために必要です．&lt;/p&gt;
&lt;p&gt;今回のレポジトリはHugoのThemeをサブモジュールとして含んでいるので &lt;code&gt;submodules: true&lt;/code&gt; でサブモジュールもチェックアウトするようにします．&lt;/p&gt;
&lt;p&gt;また， &lt;code&gt;fetch-depth: 0&lt;/code&gt; を設定することで全てのブランチとタグの全ての履歴をチェックアウトします．&lt;em&gt;.GitInfoや.Lastmodに必要らしいですが，ここではそれらの説明は省きます．&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;1.-hugo%E3%81%AE%E3%83%93%E3%83%AB%E3%83%89&quot; tabindex=&quot;-1&quot;&gt;1. Hugoのビルド&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;- name: setup hugo
  uses: peaceiris/actions-hugo@v2
  with:
    hugo-version: latest

- name: build
  run: hugo --minify
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;まず，ランナー上にhugoをインストールします. その後, hugoのビルドを実行します．&lt;/p&gt;
&lt;p&gt;&lt;code&gt;name: setup hugo&lt;/code&gt; のステップで &lt;code&gt;uses: peaceiris/actions-hugo@v2&lt;/code&gt; でhugoのセットアップを行います．&lt;/p&gt;
&lt;p&gt;次に，ビルドを実行します． &lt;code&gt;name: build&lt;/code&gt; のステップで，&lt;code&gt;run: hugo --minify&lt;/code&gt; でビルドを実行します． &lt;code&gt;--minify&lt;/code&gt; オプションで出力されるファイルを圧縮します．&lt;/p&gt;
&lt;h2 id=&quot;2.-%E3%83%93%E3%83%AB%E3%83%89%E3%81%97%E3%81%9F%E3%83%9D%E3%83%BC%E3%83%88%E3%83%95%E3%82%A9%E3%83%AA%E3%82%AA%E3%82%B5%E3%82%A4%E3%83%88%E3%81%AE%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4&quot; tabindex=&quot;-1&quot;&gt;2. ビルドしたポートフォリオサイトのデプロイ&lt;/h2&gt;
&lt;p&gt;今回はGitHub Pagesにデプロイします．便利な既存のActionsを利用します．&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;- name: deploy
  uses: peaceiris/actions-gh-pages@v3
  with:
    github_token: $
    publish_dir: ./public
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;パラメータとして渡している &lt;code&gt;github_token&lt;/code&gt; の &lt;code&gt;secrets.GITHUB_TOKEN&lt;/code&gt; は何かトークンを別で取得して与えなければいけないというものではなく，ランナーによって自動的に生成されるトークンなので設定する必要は無いです．&lt;/p&gt;
&lt;p&gt;&lt;code&gt;publish_dir&lt;/code&gt; はそのまま公開するディレクトリです．今回は他に特にパラメータを設定していないので，同一レポジトリの &lt;code&gt;gh-pages&lt;/code&gt; ブランチにランナー上の  &lt;code&gt;./public&lt;/code&gt; に出力されたビルド結果がコミットされます．&lt;/p&gt;
&lt;p&gt;ここまでの &lt;code&gt;hugo-deploy.yml&lt;/code&gt; の全体を掲載します．&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;name: hugo-deploy

on:
  push:
    branches:
      - master

jobs:
  deploy-hugo:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          submodules: true
          fetch-depth: 0

      - name: setup hugo
        uses: peaceiris/actions-hugo@v2
        with:
          hugo-version: latest

      - name: build
        run: hugo --minify

      - name: deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: $
          publish_dir: ./public
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%E3%83%AC%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E5%81%B4%E3%81%AE%E8%A8%AD%E5%AE%9A&quot; tabindex=&quot;-1&quot;&gt;レポジトリ側の設定&lt;/h2&gt;
&lt;p&gt;レポジトリ側で &lt;code&gt;gh-pages&lt;/code&gt; ブランチを公開するように設定する．GithubのレポジトリのSettings→Pagesの”Build and deployment”の”Source”で”Deploy from a branch”を選択し，”Branch”で &lt;code&gt;gh-pages&lt;/code&gt; を選択して”Save”を押下．&lt;/p&gt;
&lt;h2 id=&quot;%E3%82%B5%E3%82%A4%E3%83%88%E3%81%AE%E5%BE%AE%E4%BF%AE%E6%AD%A3&quot; tabindex=&quot;-1&quot;&gt;サイトの微修正&lt;/h2&gt;
&lt;p&gt;今回自分はドメイン直下のURLではない位置(&lt;code&gt;example.com/hoge/&lt;/code&gt; みたいな位置)にデプロイしたため，そのままではページが正常に表示されませんでした．&lt;/p&gt;
&lt;p&gt;そこで，Hugoのコンフィグファイルの以下の２点を修正しました．&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;baseURL: https://example.com/hoge # 修正前： https://example.com
canonifyURL: true # 追加
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;canonifyURL&lt;/code&gt; を設定することで絶対パスを有効化します．&lt;/p&gt;
&lt;p&gt;実際にデプロイしたものがこちらになります．&lt;a href=&quot;https://23akei.github.io/myportfolio/&quot;&gt;https://23akei.github.io/myportfolio/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;そのリポジトリはこちら：&lt;a href=&quot;https://github.com/23akei/myportfolio&quot;&gt;https://github.com/23akei/myportfolio&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;以上です． &lt;em&gt;Thank you for reading!&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;参考&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gohugo.io/hosting-and-deployment/hosting-on-github/&quot;&gt;https://gohugo.io/hosting-and-deployment/hosting-on-github/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gohugo.io/getting-started/configuration/&quot;&gt;https://gohugo.io/getting-started/configuration/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.github.com/ja/pages/getting-started-with-github-pages/creating-a-github-pages-site&quot;&gt;https://docs.github.com/ja/pages/getting-started-with-github-pages/creating-a-github-pages-site&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.github.com/ja/actions/learn-github-actions/contexts#github-context&quot;&gt;https://docs.github.com/ja/actions/learn-github-actions/contexts#github-context&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.github.com/ja/actions/using-workflows/workflow-syntax-for-github-actions&quot;&gt;https://docs.github.com/ja/actions/using-workflows/workflow-syntax-for-github-actions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.github.com/ja/actions/learn-github-actions/understanding-github-actions&quot;&gt;https://docs.github.com/ja/actions/learn-github-actions/understanding-github-actions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/marketplace/actions/github-pages-action&quot;&gt;https://github.com/marketplace/actions/github-pages-action&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/marketplace/actions/hugo-setup&quot;&gt;https://github.com/marketplace/actions/hugo-setup&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zenn.dev/nikaera/articles/hugo-github-actions-for-github-pages&quot;&gt;https://zenn.dev/nikaera/articles/hugo-github-actions-for-github-pages&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  
  
  <entry>
    <title>Hugoでポートフォリオサイトを作成する際の備忘録</title>
    <link href="https://developers.crowd4u.org/posts/2022-12-28-hugo_portfolio/"/>
    <updated>2022-12-28T06:20:37Z</updated>
    <id>https://developers.crowd4u.org/posts/2022-12-28-hugo_portfolio/</id>
    <author>
        <name>あけい</name>
        <uri>https://developers.crowd4u.org/author/23akei</uri>
    </author>
    <content xml:lang="ja" type="html">&lt;h2 id=&quot;introduction&quot; tabindex=&quot;-1&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;この記事は筆者が１年放置したポートフォリオサイトの更新に際して，でHugoでポートフォリオサイトを作る際の手順をまとめておこうと思い，執筆するものです．&lt;/p&gt;
&lt;h2 id=&quot;what%E2%80%99s%2Fwhy-hugo%3F&quot; tabindex=&quot;-1&quot;&gt;What’s/Why Hugo?&lt;/h2&gt;
&lt;h3 id=&quot;what%E2%80%99s%3F&quot; tabindex=&quot;-1&quot;&gt;What’s?&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Hugo is a static HTML and CSS website generator written in &lt;a href=&quot;https://go.dev/&quot;&gt;Go&lt;/a&gt;. It is optimized for speed, ease of use, and configurability. Hugo takes a directory with content and templates and renders them into a full HTML website.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;（&lt;a href=&quot;https://github.com/gohugoio/hugo/blob/master/README.md&quot;&gt;hugo README.md&lt;/a&gt;より）&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;HugoはGolang（プログラミング言語Go）によって記述された静的ウェブサイトジェネレーターです．HTML+CSSで構成された静的サイトを生成するのに便利です．&lt;/p&gt;
&lt;h3 id=&quot;why%3F&quot; tabindex=&quot;-1&quot;&gt;Why?&lt;/h3&gt;
&lt;p&gt;筆者は今までに何度かHugoでブログや静的サイトを構築したことがあります．他の静的サイトジェネレーターの利用経験が無いため比較はできないのですが，Hugoには以下の良い点があると思います．&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Markdownで記事が書けるという点がとても楽で良い．必要に応じてHTMLをMarkdown内に埋め込むことができる．&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://themes.gohugo.io/&quot;&gt;https://themes.gohugo.io/&lt;/a&gt;等で公開されているthemeを利用することで，いい感じのデザインのページを簡単に生成できる．&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;hugo%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB&quot; tabindex=&quot;-1&quot;&gt;Hugoのインストール&lt;/h2&gt;
&lt;p&gt;LinuxやMacOSの環境の方はパッケージマネージャーを利用してインストールするのが楽でいいと思います．例えばUbuntu等apt環境だとこんな感じ．&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ sudo apt install hugo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Windows用ソフトウェアでよくあるインストーラパッケージは用意されておらず，Chocolatey等のパッケージマネージャーを利用する必要があったり，Dockerのイメージを利用する必要があります．個人的には面倒な気がするのでWSL上のLinuxにインストールして利用するのがいいと思います．&lt;/p&gt;
&lt;p&gt;オープンソースなのでgoの開発環境が有る方は自前でビルドするのもいいかもしれません．&lt;/p&gt;
&lt;p&gt;注意すべき点として，Hugoにはstandardとextendedの２つのエディションがあることが挙げられます．一部のThemeはextendedの機能を要求するため，基本的にはextendedをインストールするのがよいでしょう．以下に示す参考ページに具体的なインストール方法が示されていますが，パッケージマネージャーによってextendedのパッケージ名が異なるため注意してください．&lt;/p&gt;
&lt;p&gt;参考：&lt;a href=&quot;https://gohugo.io/installation/&quot;&gt;https://gohugo.io/installation/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;%E3%81%AF%E3%81%98%E3%82%81%E3%81%AE%EF%BC%91%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89&quot; tabindex=&quot;-1&quot;&gt;はじめの１コマンド&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;hugo new site&lt;/code&gt; コマンドで新しいプロジェクトが生えます．&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ hugo new site myportfolio
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例えば上記コマンドでカレントディレクトリの &lt;code&gt;myportfolio&lt;/code&gt; 以下にhugoのプロジェクトが生えます．こんな感じに．&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ cd myportfolio/
$ ls
archetypes  assets  config.toml  content  data  layouts  public  static  themes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;デフォルトのconfig.tomlはこんな感じです．こんな感じなんだ～（小並感）．&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;baseURL = &#39;http://example.org/&#39;
languageCode = &#39;en-us&#39;
title = &#39;My New Hugo Site&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考：&lt;a href=&quot;https://gohugo.io/commands/hugo_new_site/&quot;&gt;https://gohugo.io/commands/hugo_new_site/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;gitでプロジェクトのソースを管理してる人は &lt;code&gt;git init&lt;/code&gt; しておくと良いと思います．&lt;/p&gt;
&lt;h2 id=&quot;%E3%83%86%E3%83%BC%E3%83%9E%E3%82%92%E9%81%A9%E7%94%A8%E3%81%99%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;テーマを適用する&lt;/h2&gt;
&lt;p&gt;今回私は&lt;a href=&quot;https://themes.gohugo.io/themes/hugo-profile/&quot;&gt;Hugo Profile&lt;/a&gt; を使います．画像少なめのポートフォリオサイト用Themeということで選びました．適当に選んだのでこの後変えるかも．（ちなみに前回Hugoでポートフォリオサイト作った時もこんな気持ちで，その一年後が今です．本当はThemeの入れ替えだけならわざわざプロジェクトを作り直す必要は無いのですが，記事のためにやりなおしています．）&lt;/p&gt;
&lt;p&gt;テーマの使い方は各テーマのReadmeとかに書いてあるのでそれぞれ参照してください．以下，この記事は&lt;a href=&quot;https://themes.gohugo.io/themes/hugo-profile/&quot;&gt;Hugo Profile&lt;/a&gt;を使った説明になります．&lt;/p&gt;
&lt;p&gt;とりあえず，submoduleとして &lt;code&gt;themes&lt;/code&gt; 内にテーマを持ってきます．&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;myportfolio:$ git submodule add git@github.com:gurusabarish/hugo-profile.git themes/hugo-profile
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%E3%83%86%E3%83%BC%E3%83%9E%E3%82%92%E7%B7%A8%E9%9B%86%E3%81%99%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;テーマを編集する&lt;/h2&gt;
&lt;p&gt;Hugoのポートフォリオテーマははポートフォリオ機能だけなら，configファイルだけで完結してしまうことが多く（筆者個人の感想．n_themes=2），今回のThemeもそうでした．ですので，Hugoを紹介しているサイトとかでよくある &lt;code&gt;hugo new&lt;/code&gt; コマンドを用いた新規記事の作成の説明は省きます．&lt;/p&gt;
&lt;p&gt;ここでHugo ProfileのReadmeを読むと &lt;code&gt;hugo new site&lt;/code&gt; の際に &lt;code&gt;-f=yaml&lt;/code&gt; オプションをつけろと書いてありますね．デフォルトではconfigファイルはTOMLですが， yamlも指定できるらしい．Hugoのプロジェクトを立ち上げるときには，まず最初に利用するテーマを決めて，そのReadmeのUsageを確認することをおすすめします．&lt;/p&gt;
&lt;p&gt;今回私はすでにオプションを指定せずに&lt;code&gt;hugo new site&lt;/code&gt; を実行して &lt;code&gt;config.toml&lt;/code&gt;を作ってしまいましたが， &lt;code&gt;config.toml&lt;/code&gt; を削除して &lt;code&gt;config.yaml&lt;/code&gt; をテーマの &lt;code&gt;exampleSite&lt;/code&gt; 内から持ってきたら解決しました．&lt;/p&gt;
&lt;p&gt;yamlファイルを適当に編集して「とりあえず出せそうな感じになったな」と思ったら内容が激薄になっていました．&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://developers.crowd4u.org/posts/2022-12-28-hugo_portfolio/portfoliosite.png&quot; alt=&quot;screenshot_of_my_portfolio_site&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ローカルでのテストは &lt;code&gt;hugo server&lt;/code&gt; コマンドでできます． &lt;code&gt;localhost:1313&lt;/code&gt; でテストサーバーにアクセスできます．実行中にファイルを更新すると自動でリビルドが走って更新されるので便利です．&lt;/p&gt;
&lt;p&gt;デプロイしたサイトはこちらです．&lt;a href=&quot;https://23akei.github.io/myportfolio/&quot;&gt;https://23akei.github.io/myportfolio/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;以上です．実際のデプロイやCI/CDについては別記事で． &lt;em&gt;Thank you for reading!&lt;/em&gt;&lt;/p&gt;
</content>
  </entry>
  
  
  <entry>
    <title>しがない大学生がレガシーコードに向き合った感想</title>
    <link href="https://developers.crowd4u.org/posts/2022-12-24-advent_calendar/"/>
    <updated>2022-12-23T15:00:00Z</updated>
    <id>https://developers.crowd4u.org/posts/2022-12-24-advent_calendar/</id>
    <author>
        <name>notch_man</name>
        <uri>https://developers.crowd4u.org/author/notch_man</uri>
    </author>
    <content xml:lang="ja" type="html">&lt;p&gt;&lt;a href=&quot;https://adventar.org/calendars/8181&quot;&gt;これはklis Advent Calendar 2022 24日目の記事です。&lt;/a&gt;
こんにちは、klis19（3編）のnotch_manです。今日はクリスマスイブですね。皆さんはいかがお過ごしでしょうか。巷では熱々ムードが漂っていますが、私に関しては表題にあるように”レガシーコード”と相まみえています。そんな私がこの1年間で向き合ってきたレガシーコードに向き合った感想を綴ろうと思います。&lt;/p&gt;
&lt;h2 id=&quot;%E7%B5%90%E8%AB%96&quot; tabindex=&quot;-1&quot;&gt;結論&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;レガシーコードは向き合い続ければ必ず克服できる&lt;/li&gt;
&lt;li&gt;ソースコードはメンヘラなので手を加え続けよう（手から離れたソースコードはその瞬間にレガシーコードになる）&lt;/li&gt;
&lt;li&gt;レガシーと戦いやすい実装を心がける&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB&quot; tabindex=&quot;-1&quot;&gt;はじめに&lt;/h2&gt;
&lt;p&gt;私は現在で大学での短期雇用、2社の長期インターン、また業務委託でソフトウェア開発を行なっています。ほとんどの現場のコードベースは長い年月を経て作られたものです。
私のインターンでの主な業務は、いにしえのソースコードを解読し現世人が解読出来るものに復元することです。
今日はそのうち1社と大学での業務でレガシーコード改善を行なった感想を綴ろうと思います。&lt;/p&gt;
&lt;h2 id=&quot;10%E5%B9%B4%E4%BB%A5%E4%B8%8A%E3%81%AE%E6%AD%B4%E5%8F%B2%E3%81%AE%E3%81%82%E3%82%8B%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AE%E6%A0%B9%E5%B9%B9%E3%81%AB%E3%83%A1%E3%82%B9%E3%82%92%E5%85%A5%E3%82%8C%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;10年以上の歴史のあるシステムの根幹にメスを入れる&lt;/h2&gt;
&lt;p&gt;PRTIMESでは現在PHPを始め、各種コードベースのリファクタリングやバージョンアップを進めています。最初は細かいライブラリのリファクタリングを行なっていき、現在は大規模な修正範囲を伴う作業を進めています。
一般に、影響範囲の広いものほど放置される傾向にあります。しかし、その間にもサービスは成長を続けるので負債もさらに増加していく一方です。だから、誰かがいつかは手を付けなければいけません。私はこの1年で、そんなレガシーコードに向き合う精神と技術力を養うことが出来ました。&lt;/p&gt;
&lt;h3 id=&quot;%E3%83%AC%E3%82%AC%E3%82%B7%E3%83%BC%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E6%94%B9%E5%96%84%E3%81%AF%E8%87%B3%E3%81%A3%E3%81%A6%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB&quot; tabindex=&quot;-1&quot;&gt;レガシーコードの改善は至ってシンプル&lt;/h3&gt;
&lt;p&gt;レガシーコード改善と名前は格好いいですが、技術的にやることは以下の2つだけでシンプルです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ライブラリのリプレイス・バージョンアップを行なう&lt;/li&gt;
&lt;li&gt;以前と同じ挙動を担保する&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ただし、実際に手を加えると以下の様なことを考えなければなりません。そして、これには途方もない労力が必要になります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;メジャーアップデートで以前の互換が切られている可能性&lt;/li&gt;
&lt;li&gt;将来のバージョンアップを見据えたライブラリ選定&lt;/li&gt;
&lt;li&gt;本当に以前と同等の挙動をしているか確認する（これが一番難しい）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;そして、技術以外に必要な物もシンプルです。最早、精神論ですが…（笑）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;くじけない心&lt;/li&gt;
&lt;li&gt;粘り強く調査→修正のサイクルを繰り返す忍耐力&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;はい、最後は精神論です。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;「レガシーコードの改善は技術力だけでなく，それよりもむしろ精神力が要求されます．チーム内でレガシーコード問題の深刻さを共有し，精力的に取り組みましょう」&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;技術で解決出来る事は技術で殴れば解決できます。しかし、レガシーコードリファクタリング緻密に調査を積み重ねる、泥臭く1つ1つを修正していくなど技術以外の能力が要求されます。だから鍛錬を積み重ねて、これらの作業を行なう精神力を養っていく必要があると考えています。&lt;/p&gt;
&lt;p&gt;これらを兼ね備えてやるべきことをやっていけば必ずレガシーを克服することが出来ます。今年、様々な現場でレガシーを解消してきた私が言うので多分間違いないと思います（笑）&lt;/p&gt;
&lt;h3 id=&quot;%E7%B5%90%E8%AB%961%EF%BC%9A%E3%83%AC%E3%82%AC%E3%82%B7%E3%83%BC%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AF%E5%90%91%E3%81%8D%E5%90%88%E3%81%84%E7%B6%9A%E3%81%91%E3%82%8C%E3%81%B0%E5%BF%85%E3%81%9A%E5%85%8B%E6%9C%8D%E3%81%A7%E3%81%8D%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;結論1：レガシーコードは向き合い続ければ必ず克服できる&lt;/h3&gt;
&lt;p&gt;結論、何が起きても勇敢に向き合う腕力、忍耐力、技術力があればいつかは必ず克服できます。そして、究極のテストである「以前と同じ動作を保証する」テストを終えるだけで実績がそのまま評価に繋げられます。皆さんも臆すること無くレガシーコードに向き合い続けましょう。その力はソフトウェア開発以外でも必ず活かせます。&lt;/p&gt;
&lt;h2 id=&quot;%E3%81%9F%E3%81%A3%E3%81%9F%E6%95%B0%E3%83%B6%E6%9C%88%E3%81%A7%E8%85%90%E6%95%97%E3%81%97%E3%81%9F%E3%82%B3%E3%83%BC%E3%83%89%E3%83%99%E3%83%BC%E3%82%B9%E3%82%92%E6%89%8B%E7%9B%B4%E3%81%97%E3%81%99%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;たった数ヶ月で腐敗したコードベースを手直しする&lt;/h2&gt;
&lt;p&gt;続いての事例は融合知能デザイン研究室で開発中のシステム「NextCrowd4u」です（以下N4U）。N4Uは融合知能デザイン研究室でサービスを運営しているCrowd4u（以下C4U）の後継システムとして現在開発中のシステムです。こちらは前任者から引き継ぎ現在も開発を続けています。&lt;/p&gt;
&lt;h3 id=&quot;%E4%BA%8B%E4%BB%B6%E3%81%AF%E5%94%90%E7%AA%81%E3%81%AB%E8%B5%B7%E3%81%8D%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;事件は唐突に起きる&lt;/h3&gt;
&lt;p&gt;私は4月から開発業務に関わり始めましたが、早速トラブルが発生しました。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://developers.crowd4u.org/posts/2022-12-24-advent_calendar/advent-1.png&quot; alt=&quot;障害対応&quot; /&gt;&lt;/p&gt;
&lt;p&gt;と言うわけで、復旧作業をしようと思ったのですがまず本番環境への接続方法を確認するのに手間取りました。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://developers.crowd4u.org/posts/2022-12-24-advent_calendar/advent-2.png&quot; alt=&quot;障害対応&quot; /&gt;&lt;/p&gt;
&lt;p&gt;結局、過去のチャットログから接続方法を確認しログイン出来ました。これ以外にもトラブルが多発している状態で、当時の現場はとても混乱してました。コードベースも前任者からの引き継ぎが十分でなく、ゼロからコードリーディングをして何とかデバッグするという状況でした。&lt;/p&gt;
&lt;h3 id=&quot;%E7%B5%90%E8%AB%962%3A%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AF%E3%83%A1%E3%83%B3%E3%83%98%E3%83%A9%E3%81%AA%E3%81%AE%E3%81%A7%E6%89%8B%E3%82%92%E5%8A%A0%E3%81%88%E7%B6%9A%E3%81%91%E3%82%88%E3%81%86&quot; tabindex=&quot;-1&quot;&gt;結論2:ソースコードはメンヘラなので手を加え続けよう&lt;/h3&gt;
&lt;p&gt;ソースコード、特にPythonのようなドラスティックな変化をする言語は手を加え続ける必要があります。数ヶ月コードベースに手を付けられなかったら、&lt;code&gt;pip install -r requirements.txt&lt;/code&gt;すら通らなくなるでしょう。まるでメンヘラですね、手がかかって本当に困ります。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;エンジニアはメンヘラと付き合いがちという俗説をよく耳にします。確かに、プログラムがメンヘラみたいなものなのでそれに扱い慣れてたり類似性を感じるからでしょうかね。
個人的にはPythonとかすぐに壊れがちの言語をメインに扱っている人ほどメンヘラと付き合ってるのでは？と思ってしまいます。言語別で統計を取ったら面白そうですね（笑）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;このように、開発陣でコントロール出来ないシステムを運用するのはリスクであり、早急に開発体制を立て直す必要がありました。&lt;/p&gt;
&lt;h3 id=&quot;step1&quot; tabindex=&quot;-1&quot;&gt;STEP1&lt;/h3&gt;
&lt;p&gt;まず、システムをコントロール下に置くために私がシステムやコードベースの全容の把握を行なう必要があります。しかし、稼働中のシステムの運用を止める訳にもいかないので、時間稼ぎのためオペレーション部分から立て直しを進めていきました。最初は私1人で作業を行ない、その手順を全て示すことで他の人も同じ作業が出来るように障害対応と全体のレベルアップ施策を当時に行なっていました。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://developers.crowd4u.org/posts/2022-12-24-advent_calendar/advent-3.png&quot; alt=&quot;障害対応&quot; /&gt;&lt;/p&gt;
&lt;p&gt;深夜の復旧作業は中々辛かったですが、復旧後は1つ1つの手順を説明し着実にレベルアップを図っていきました。
&lt;img src=&quot;https://developers.crowd4u.org/posts/2022-12-24-advent_calendar/advent-4.png&quot; alt=&quot;障害対応&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;step2&quot; tabindex=&quot;-1&quot;&gt;STEP2&lt;/h3&gt;
&lt;p&gt;6月頃から本格的にコードベースの復旧に取りかかります。しかし、この時には4～5月のシステム障害対応の段階でシステム開発の手が止まってしまい担当者もいなくなったことで完全にレガシーシステムと化してしまいました。
この時の問題点は概ね以下の通りです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python3.7で動いている&lt;/li&gt;
&lt;li&gt;ライブラリのメジャーアップデートなどで互換の維持が出来ない&lt;/li&gt;
&lt;li&gt;アーキテクチャの問題で耐障害性が低くシステムダウンが頻発&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;4月時点では右も左も分からない状態だった開発陣もシステム障害に向き合う中でコードベースへの理解が深まっていました。そこで私はマネジメントやコードレビューに徹し、さらにメンバーのスキルの底上げを図っていきました。
レガシー改善では以下のワークフローで開発を進めました。これにより、手を動かしながら私のノウハウも伝えることができメンバーの開発力を大幅に向上させることに成功しました。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;設計に関してはDesignDocを作成&lt;/li&gt;
&lt;li&gt;DesignDocでソフトウェア設計に関してnotch_manの経験も踏まえつつアドバイス・改善&lt;/li&gt;
&lt;li&gt;GitHubでタスクチケット（Issue）を作り開発着手&lt;/li&gt;
&lt;li&gt;一旦、自力で実装する&lt;/li&gt;
&lt;li&gt;コードレビューで設計面でnotch_manのノウハウを伝授&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;これらの手順に沿ってまずライブラリのアップデートや明らかなデッドコードの削除を進めていきました。そして、それが終わった後にソフトウェアアーキテクチャに問題になっていた箇所の大規模リファクタリングを実施しました。（これについては開発者ブログの記事をご覧下さい）。
最後は以下のPRによって一旦のレガシー改善は完了としました。4月にはてんやわんやしていたチームが見事大規模なレガシー改善を達成した瞬間です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://developers.crowd4u.org/posts/2022-12-24-advent_calendar/advent-5.png&quot; alt=&quot;障害対応&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;step3&quot; tabindex=&quot;-1&quot;&gt;STEP3&lt;/h3&gt;
&lt;p&gt;これまではレガシーの改善に尽力しましたが、今後はサービスグロースを進めながらレガシーと戦う必要があります。
n4uのレガシー改善としては主に以下の項目が残っています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;テストコードの実装&lt;/li&gt;
&lt;li&gt;CRUDロジックを新設計の実装にフルリプレイス&lt;/li&gt;
&lt;li&gt;各種ライブラリの見直し&lt;/li&gt;
&lt;li&gt;テストを含めたCI/CDの構築&lt;/li&gt;
&lt;li&gt;ログ基盤の改善&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;今後、n4uの開発チームは新機能開発を積極的に行なっていき、これまで以上のリリース速度で研究室が目指すサービスの実現に向けて歩みを進めていきます。その一方で、過去の負債やこれから生まれる負債についても戦略的に向き合う体制を構築していきます。&lt;/p&gt;
&lt;h3 id=&quot;%E7%B5%90%E8%AB%963%3A-%E3%83%AC%E3%82%AC%E3%82%B7%E3%83%BC%E3%81%A8%E6%88%A6%E3%81%84%E3%82%84%E3%81%99%E3%81%84%E5%AE%9F%E8%A3%85%E3%82%92%E5%BF%83%E3%81%8C%E3%81%91%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;結論3: レガシーと戦いやすい実装を心がける&lt;/h3&gt;
&lt;p&gt;これは今後のレガシーコードの向き合い方の話です。と言いつついきなり残念な話ですがですが、システムというのは誕生した瞬間に腐り始めるものです。つまり、作ったシステムはその瞬間にレガシーとなるのです。そのため、レガシーを生まないことはほぼ不可能です。だから、システムを開発するときにはコードベースが腐ることを前提として実装を行なう必要があります。
少し前に技術負債の話が話題になりましたが、私のこれまでの経験を踏まえると以下の点を気をつける必要があると思います。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;SOLID原則を守る&lt;/li&gt;
&lt;li&gt;手に負えなくなった機能は置き換えやすいように実装を行なう&lt;/li&gt;
&lt;li&gt;テストコードを実装する（ユニットテストだけでなくE2Eテストも用意する）&lt;/li&gt;
&lt;li&gt;CI/CDを整えてテストやコード規約を強制的に機能させる仕組みを用意する。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;まず、1つ目について触れます。ソフトウェアの開発原則には様々な物がありますが、Web開発ではSOLID原則を意識するだけでかなり改善すると考えています。特に単一責任の原則と依存性逆転の原則は非常に大事だと思っているので、この辺りの実装についてはコードレビューで品質を保つように努めていきます。&lt;/p&gt;
&lt;p&gt;2つ目ですが、時の担当者がメンテナンスをしなければならない機能に手を付けられないとそれが放置されてしまい負債という形でレガシーが引き継がれてしまいます。だから、困ったときには捨てることが出来るシステム設計を心がける必要があります。関心の分離はソフトウェアの設計で大事ですが、特にメーラーなどの外部ライブラリを使う際の変更容易性には注意して開発を行ないます。&lt;/p&gt;
&lt;p&gt;3つ目と4つ目は当たり前の話ですね？ただ、テストコードや静的解析をツールを頑張って導入しても使われなかったら意味が無いです。そのためCI/CDの中でそれらを強制的に動かす仕組みを整えていきます。こうすることで、テストや静的解析の効果を最大限発揮し将来に渡って一定のコード品質を保つことが出来ます。&lt;/p&gt;
&lt;p&gt;このようにレガシーを生み出さない、あるいは改善するためには普段からどうすれば戦いやすいか？という視点に立つ必要があるなと痛感しました。今後、N4Uの開発の中でこうした知見が貯まっていったら積極的に紹介していきます。&lt;/p&gt;
&lt;h2 id=&quot;%E9%9B%91%E3%81%AB%E3%81%93%E3%81%AE1%E5%B9%B4%E3%82%92%E6%8C%AF%E3%82%8A%E8%BF%94%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;雑にこの1年を振り返る&lt;/h2&gt;
&lt;p&gt;この1年で超ド級の謎物体やら小学生もビックリするような限界状態の中で前に進み続ける腕力、精神力や技術力を身に着けることが出来ました。来年は攻めの姿勢で構えながら、さらなる深淵へと歩みを進めていきたいと思います。&lt;/p&gt;
&lt;p&gt;そして、今年はめっちゃ頑張った。その結果、レガシーコードリファクタリング業（みならい）は名乗れる程度に成長したのでは？と思ってます。研究室に眠るレガシーシステムの改善のご用命は融合知能デザイン研究室へ（笑）&lt;/p&gt;
&lt;h2 id=&quot;%E6%9C%80%E5%BE%8C%E3%81%AB&quot; tabindex=&quot;-1&quot;&gt;最後に&lt;/h2&gt;
&lt;p&gt;キラキラ光る街並み、その裏側には多くのエンジニアの汗と涙が詰まっています。2人の愛の育みも大事ですが、そういった人達の光を浴びぬ物語を時々で良いから思い出してあげてください。皆様、素敵なクリスマスをお過ごしください。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;（私のクリスマス？おっと、何処かで案件の燃えてる香ばしい匂いがするぞ…）&lt;/p&gt;
&lt;/blockquote&gt;
</content>
  </entry>
  
  
  <entry>
    <title>簡単に始める自動デプロイ生活</title>
    <link href="https://developers.crowd4u.org/posts/2022-12-20-rsync-deploy/"/>
    <updated>2022-12-20T13:00:00Z</updated>
    <id>https://developers.crowd4u.org/posts/2022-12-20-rsync-deploy/</id>
    <author>
        <name>notch_man</name>
        <uri>https://developers.crowd4u.org/author/notch_man</uri>
    </author>
    <content xml:lang="ja" type="html">&lt;p&gt;こんにちは、notch_manです。とある講義のメンターをやっていて色々思うところがあったので、今日は簡単に自動デプロイを実現する方法を紹介したいと思います。&lt;/p&gt;
&lt;h2 id=&quot;%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%81%A3%E3%81%A6%E9%9B%A3%E3%81%97%E3%81%9D%E3%81%86&quot; tabindex=&quot;-1&quot;&gt;デプロイって難しそう&lt;/h2&gt;
&lt;p&gt;デプロイって皆さんに聞くと大体こんなワードが返ってきます（私の観測範囲）。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;GitHub Actionsでいろいろ書くんでしょ？難しそう&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;CircleCIで自動デプロイはやったことありますが、個人だとお金がかかるし…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;友人にプロが居るのでいつも丸投げです&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;確かに、複雑なデプロイや環境毎に変数値が変わるなどの事情があればこれらのサービスを使った方がいろいろな事が出来ます。ただ、デプロイってそんな難しいものなのでしょうか？ここでデプロイという単語を辞書で見てみましょう。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;デプロイ&lt;/strong&gt;とは、配備する、配置する、展開する、配置につく、などの意味を持つ英単語。ITの分野では開発したソフトウェアを実際の運用環境に配置・展開して実用に供することを指す場合が多い。（出典：&lt;a href=&quot;https://e-words.jp/w/%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4.html&quot;&gt;https://e-words.jp/w/デプロイ.html&lt;/a&gt;）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;これらをまとめると、みなさんが難しいと思う自動デプロイはたった3つの条件を満たせばどんな方法で実現しても良いことが分かります。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;何かを実行したらソースコード・成果物が所定の位置に配置される&lt;/li&gt;
&lt;li&gt;所定の位置に配置されたソースコード・成果物が実用的な動作をする&lt;/li&gt;
&lt;li&gt;それらがなるべく人の手を介さずに処理される&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;というわけで、これらを満たすとても簡単なデプロイスクリプトを実装してみます。&lt;/p&gt;
&lt;h2 id=&quot;rsync%E3%81%A7%E5%A7%8B%E3%82%81%E3%82%8B%E8%87%AA%E5%8B%95%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4&quot; tabindex=&quot;-1&quot;&gt;rsyncで始める自動デプロイ&lt;/h2&gt;
&lt;p&gt;初心者がデプロイ組むときにサーバーのURLとかいろいろ考えるの難しいですよね？でも、そんな状態でも大体”&lt;strong&gt;公開鍵認証&lt;/strong&gt;”でSSHは通じているはずです。となれば、この経路を使ってファイルを置くことが出来そうですね。というわけで、SSH経由で自動デプロイをしてみましょう。これを実現する方法にrsyncというものがあります。rsyncは差分ファイル転送を行なうツールになっています。差分ファイル転送を雑に説明すると、あるファイルに更新があった場合に別のサーバーなどの対応するファイルと変更を同期する機能のことです。rsyncについては&lt;a href=&quot;https://wiki.archlinux.jp/index.php/Rsync&quot;&gt;こちらのサイト&lt;/a&gt; がとても参考になります。&lt;/p&gt;
&lt;p&gt;では、rsyncを使ってデプロイスクリプト（deploy.sh）を書いてみましょう。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rsync -r -a &amp;lt;転送したいファイルを含んだフォルダ&amp;gt;/* &amp;lt;remote server&amp;gt;:&amp;lt;配置したいディレクトリ&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;さらに~/ssh/configを編集して鍵情報などを登録しておきます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;Host &amp;lt;remote server&amp;gt;
  HostName &amp;lt;ip address or server name&amp;gt;
  User example
  IdentityFile ~/.ssh/example_key
  ServerAliveInterval 60
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上でデプロイスクリプトの完成です。あとは&lt;code&gt;bash [deploy.sh](http://deploy.sh)&lt;/code&gt; を実行すればデプロイが走ります。&lt;/p&gt;
&lt;h2 id=&quot;%E3%81%BE%E3%81%A8%E3%82%81&quot; tabindex=&quot;-1&quot;&gt;まとめ&lt;/h2&gt;
&lt;p&gt;自動デプロイとか一見すると難しいワードですが、以下を満たせば手段は何でも良いです。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;何かを実行したらソースコード・成果物が所定の位置に配置される&lt;/li&gt;
&lt;li&gt;所定の位置に配置されたソースコード・成果物が実用的な動作をする&lt;/li&gt;
&lt;li&gt;それらがなるべく人の手を介さずに処理される&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;特に講義で頻繁にデプロイをする方々はぜひ参考にしてみてください。最後になりますが、技術で解決出来る事はさっさと技術で解決して本質にしっかり向き合っていきましょう！&lt;/p&gt;
</content>
  </entry>
  
  
  <entry>
    <title>NextCrowd4u概説</title>
    <link href="https://developers.crowd4u.org/posts/2022-12-08_what_is_n4u/"/>
    <updated>2022-12-08T01:26:23Z</updated>
    <id>https://developers.crowd4u.org/posts/2022-12-08_what_is_n4u/</id>
    <author>
        <name>あけい</name>
        <uri>https://developers.crowd4u.org/author/23akei</uri>
    </author>
    <content xml:lang="ja" type="html">&lt;p&gt;融合知能デザイン研究室のあけいです．
我々が現在開発しているWebサービスであるNextCrowd4uについて，その概略を説明します．&lt;/p&gt;
&lt;h2 id=&quot;crowd4u%E6%A6%82%E8%AA%AC&quot; tabindex=&quot;-1&quot;&gt;Crowd4u概説&lt;/h2&gt;
&lt;p&gt;まず，&amp;quot;Next&amp;quot;以前の「Crowd4u」について簡単に説明します．
一言で言ってしまえば，Crowd4uはクラウドソーシングプラットフォームです．&lt;br /&gt;
&lt;a href=&quot;https://crowd4u.org/&quot;&gt;Corwd4u&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E3%82%BD%E3%83%BC%E3%82%B7%E3%83%B3%E3%82%B0%E3%81%A8%E3%81%AF&quot; tabindex=&quot;-1&quot;&gt;クラウドソーシングとは&lt;/h3&gt;
&lt;p&gt;「クラウドソーシング」をご存知でない方も結構いると思われるので，まずクラウドソーシングについて簡単に説明します．
クラウドソーシングの「クラウド」は&amp;quot;Crowd&amp;quot;すなわち&amp;quot;群衆&amp;quot;を意味します．（&amp;quot;Cloud&amp;quot;すなわち&amp;quot;雲&amp;quot;，ではないです！）
それと，外注すなわち&amp;quot;アウトソーシング&amp;quot;を組み合わせた言葉，それがクラウドソーシングです．
つまりクラウドソーシングは，不特定多数の労働者に対するアウトソーシングです．
クラウドソーシングを利用した具体的なサービスを以下に示します．&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mturk.com/&quot;&gt;Amazon Mechanical Turk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://crowdworks.jp/&quot;&gt;CrowdWorks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lancers.jp/&quot;&gt;Lancers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ubereats.com/&quot;&gt;Uber Eats&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;etc...&lt;/p&gt;
&lt;p&gt;Crowd4uもここに挙げたようなクラウドソーシングプラットフォームの１つです．&lt;br /&gt;
クラウドソーシングは不特定多数の労働者に仕事を依頼できるため，&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大量のタスクの早期完了&lt;/li&gt;
&lt;li&gt;不特定多数の人間の知見の収集（＝集合知の形成）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;などが可能になります．&lt;/p&gt;
&lt;h3 id=&quot;%E5%A4%A7%E5%AD%A6%E3%81%AE%E7%A0%94%E7%A9%B6%E5%AE%A4%E3%81%AB%E3%82%88%E3%82%8B%E9%81%8B%E5%96%B6&quot; tabindex=&quot;-1&quot;&gt;大学の研究室による運営&lt;/h3&gt;
&lt;p&gt;Crowd4uは融合知能デザイン研究室が運営するクラウドソーシングプラットフォームで，主にクラウドソーシングの学術研究のために利用されています．
他の大学の研究にも利用されており，それらの研究の要求に応じて機能を実装するなど，学術研究との結びつきの強いプラットフォームです．&lt;/p&gt;
&lt;p&gt;ちなみに，Crowd4uに決済機能は無いため，あくまでもボランティアでタスクを請け負ってもらうという仕組みになっています．&lt;/p&gt;
&lt;h2 id=&quot;n4u%E6%A6%82%E8%AA%AC1---human%2Bai&quot; tabindex=&quot;-1&quot;&gt;N4U概説1 - Human+AI&lt;/h2&gt;
&lt;p&gt;「NextCrowd4u」次期Crowd4uシステムについて説明します．&amp;quot;Next&amp;quot;Crowd4uには今までのCrowd4u，ひいては既存のクラウドソーシングプラットフォームと大きく異なる点があります．それはすなわち&amp;quot;AI&amp;quot;です．
近年急速に発展する機械学習，それらAIの能力は一部領域では人間を凌駕するまでに発展しました．今後AIがより多くの場面で利用されると予想されます．&lt;/p&gt;
&lt;p&gt;NextCrowd4uではAIと我々がより密接に関わるであろう将来に向けて，AIと人間のよりよい関わり方を研究するために，AIに対するクラウドソーシングを行う機能を実装します．&lt;/p&gt;
&lt;h3 id=&quot;ai%E3%81%AB%E5%AF%BE%E3%81%99%E3%82%8B%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E3%82%BD%E3%83%BC%E3%82%B7%E3%83%B3%E3%82%B0&quot; tabindex=&quot;-1&quot;&gt;AIに対するクラウドソーシング&lt;/h3&gt;
&lt;p&gt;クラウドソーシングというのは&amp;quot;人間&amp;quot;にタスクをこなしてもらうというのが一般的ですが，現在では人間にその対象は人間に留まりません．&lt;br /&gt;
AIの性能を競うコンペティションサイトとして&lt;a href=&quot;https://www.kaggle.com/&quot;&gt;Kaggle&lt;/a&gt;等があります．これらのコンペサイトでは，コンペのお題に対して不特定多数のAI開発者が各々のAIの性能を競い，コンペの主催者はAIやAIによって得られたお題の結果を得ます．
これは不特定多数のAI（とその開発者）に対するクラウドソーシングといえます．
従来のクラウドソーシングのタスク依頼対象である人間ワーカに対して，AIによるクラウドソーシングにおいて「AI（とその開発者）」を一体とみなしてAIワーカと考えます．&lt;/p&gt;
&lt;h3 id=&quot;%E4%BA%BA%E9%96%93%E3%81%A8ai%E3%81%AE%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E3%82%BD%E3%83%BC%E3%82%B7%E3%83%B3%E3%82%B0%E3%81%AE%E9%81%95%E3%81%84&quot; tabindex=&quot;-1&quot;&gt;人間とAIのクラウドソーシングの違い&lt;/h3&gt;
&lt;p&gt;AIに対するクラウドソーシングは人間に対するそれとは異なる点があります．
人間ワーカに対するクラウドソーシングでは，（悪意のあるものを除けば）基本的に高品質なタスク結果を期待できます．しかし，人間ワーカはタスクの実行に時間がかかります．
一方，AIワーカは大量のタスクを素早く実行できます．しかし，特に教師データを必要とするAIにおいては，教師データが無い，もしくは少ない状態では十分な品質のタスク結果を得ることができません．
そもそも，機械学習モデルの学習に用いられる教師データは主に人間のワーカによるラベル付けで作成されています．そのため，AIの学習を行う前にはたいてい人間ワーカによる仕事があります．&lt;/p&gt;
&lt;h3 id=&quot;human%2Bai-crowd&quot; tabindex=&quot;-1&quot;&gt;Human+AI Crowd&lt;/h3&gt;
&lt;p&gt;NextCrowd4uでは前述の背景を踏まえ，AIに対してもクラウドソーシングが可能なプラットフォームとします．つまり，NextCrowd4uは&amp;quot;不特定多数の人間&amp;quot;と&amp;quot;不特定多数のAI&amp;quot;：Human+AI Crowdに対するクラウドソーシングを行うプラットフォームです．人間へのタスク依頼とAIワーカへのタスク依頼を柔軟に組み合わせることで，より迅速により高品質なタスク結果が期待できます．&lt;/p&gt;
&lt;h2 id=&quot;n4u%E6%A6%82%E8%AB%96-2---%E3%83%A1%E3%82%BF%E3%83%97%E3%83%A9%E3%83%83%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0&quot; tabindex=&quot;-1&quot;&gt;N4U概論 2 - メタプラットフォーム&lt;/h2&gt;
&lt;p&gt;上でいくつかのクラウドソーシングプラットフォームを挙げましたが，それらのプラットフォームにはそれぞれ特徴があり，状況やタスクの目的に応じてそれらを使い分けることが出来れば，より効率的により良い結果を得ることが期待できます．
そこで，NextCrowd4uはクラウドソーシングタスクを直接扱うのではなく，複数の既存プラットフォームを扱う「メタプラットフォーム」を目指しています．
例えば&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;APIなどを用いて既存のクラウドソーシングサービス上でタスクを発行．&lt;/li&gt;
&lt;li&gt;タスクの結果を得たら，それをNextCrowd4u上で統合，分析する&lt;/li&gt;
&lt;li&gt;結果に応じて動的に各プラットフォームでタスクを発行する&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;...という流れを繰り返すことを想定しています．&lt;br /&gt;
このように１つの目的に対して状況に応じて柔軟に複数のサービスを用いることが可能なプラットフォームを目指しています．&lt;/p&gt;
&lt;h2 id=&quot;%E3%81%BE%E3%81%A8%E3%82%81&quot; tabindex=&quot;-1&quot;&gt;まとめ&lt;/h2&gt;
&lt;p&gt;NextCrowd4uは&lt;br /&gt;
&lt;strong&gt;Human+AI Crowdに対するクラウドソーシングを実現するメタプラットフォーム&lt;/strong&gt;&lt;br /&gt;
です．&lt;/p&gt;
&lt;p&gt;今後我々N4U開発チームは最新の研究の成果などを取り入れながらNextCrowd4uの開発を行い，AIと人間がより高度に融合した社会の基盤づくりを進めていきます．&lt;/p&gt;
</content>
  </entry>
  
  
  <entry>
    <title>RDS上のDBのデータをローカルで同期できる環境を作った話</title>
    <link href="https://developers.crowd4u.org/posts/2022-12-07-rds-restore/"/>
    <updated>2022-12-01T05:54:39Z</updated>
    <id>https://developers.crowd4u.org/posts/2022-12-07-rds-restore/</id>
    <author>
        <name>ギルド</name>
        <uri>https://developers.crowd4u.org/author/gild</uri>
    </author>
    <content xml:lang="ja" type="html">&lt;p&gt;初めまして、融合知能デザイン研究室に仮配属中のギルドです。(留年しなければ「仮」がとれるはず。。。)&lt;br /&gt;
融合知能デザイン研究室で開発しているNextCrowd4uの開発チームに最近加わったため、加入後に初めて行ったことについて書いていきたいと思います。&lt;/p&gt;
&lt;h2 id=&quot;nextcrowd4u(n4u)%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6&quot; tabindex=&quot;-1&quot;&gt;NextCrowd4u(N4U)について&lt;/h2&gt;
&lt;p&gt;まだブログも創設から間もないという事で、NextCrowd4Uについてザックリ説明していこうと思います。
NextCrowd4u(以下N4U)は、「世界をより良い場所にするためのマイクロタスクを実行するための非営利プラットフォーム」として2008年ごろから稼働している&lt;a href=&quot;https://crowd4u.org/ja/&quot;&gt;Crowd4u&lt;/a&gt;の後継システムです。
N4U開発チームで具体的にどんなことを行ってきたかについては、&lt;a href=&quot;https://developers.crowd4u.org/posts/2022-11-30-intro/&quot;&gt;こちらの記事&lt;/a&gt;をご覧ください。&lt;/p&gt;
&lt;h2 id=&quot;n4u%E3%81%AE%E9%96%8B%E7%99%BA%E7%8F%BE%E5%A0%B4%E3%81%A7%E7%99%BA%E7%94%9F%E3%81%97%E3%81%A6%E3%81%84%E3%81%9F%EF%BE%81%EF%BD%AE%EF%BD%AF%EF%BE%84%E9%9D%A2%E5%80%92%E3%81%AA%E4%BA%8B&quot; tabindex=&quot;-1&quot;&gt;N4Uの開発現場で発生していたﾁｮｯﾄ面倒な事&lt;/h2&gt;
&lt;p&gt;さて、実際の開発ではサーバー上のコードを直接編集。。。するわけでは当然なく、基本はローカルの環境で開発して、GitHubでコミットやらレビューやらしてからデプロイするわけですが、そうなると開発やテストが行いやすいため、実際のサービスのデータをローカルでも扱いたいというニーズが出てきます。
N4UはAWSをサーバーのインフラとして使用しており、DBもAmazon RDS for MySQLを使っています。
これまでは、RDS上にある本番環境のデータをローカル環境で使用するために、踏み台サーバーを2回通してSCPコマンドを使ってデータを取得する必要がありました。
このような環境はテストや開発において支障がでていたとのことで、もう少し手軽に本番とローカルのデータを同期したいというニーズが生まれました。&lt;/p&gt;
&lt;h2 id=&quot;%E3%82%84%E3%81%A3%E3%81%9F%E3%81%93%E3%81%A8&quot; tabindex=&quot;-1&quot;&gt;やったこと&lt;/h2&gt;
&lt;p&gt;やったことは非常にシンプルで、N4Uを稼働しているサーバーから、1日1回DBをダンプして踏み台サーバーに送るような環境を作成します。(下図)
そして、ローカルでリストア用のスクリプトを実行することで、踏み台サーバーにアクセスし、ダンプされたDBをダウンロードして、ローカルのデータベースにリストアするようにしました。&#92;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://developers.crowd4u.org/posts/2022-12-07-rds-restore/n4u-db-restore.drawio.png&quot; alt=&quot;図1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;これを実現するためにやったこととしては&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;N4Uサーバー
&lt;ol&gt;
&lt;li&gt;RDSのDBをダンプし、踏み台に送信するシェルスクリプトを作成する(dump.sh)&lt;/li&gt;
&lt;li&gt;上記のスクリプトを定期的に実行する(cron)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;踏み台サーバー
&lt;ol&gt;
&lt;li&gt;AWSでEC2の環境を新しく作成する&lt;/li&gt;
&lt;li&gt;nginxを導入し、ベーシック認証ができるようにする&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;ローカル環境 &lt;br /&gt;
以下のようなシェルスクリプトを作成する。(restore.sh)
&lt;ol&gt;
&lt;li&gt;踏み台サーバーからDBをダウンロードする&lt;/li&gt;
&lt;li&gt;ローカルのDBにリストアする&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;踏み台サーバーの設定に関しては、私が書くよりも分かりやすい記事がたくさんあるかと思います。&lt;br /&gt;
そのため、ここからはDBのダンプスクリプトとリストアスクリプトについて書いていきます。&lt;/p&gt;
&lt;h3 id=&quot;n4u%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%EF%BC%9Adb%E3%82%92%E5%AE%9A%E6%9C%9F%E7%9A%84%E3%81%AB%E3%83%80%E3%83%B3%E3%83%97%E3%81%99%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;N4Uサーバー：DBを定期的にダンプする&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. DBをダンプする&lt;code&gt;dump.sh&lt;/code&gt;を作成する&lt;/strong&gt;&lt;br /&gt;
というわけで、以下のようなダンプスクリプトを作成しました。データベースの容量がある程度大きいことが想定されますのでgzipを使用して圧縮をしています。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/bin/bash

# RDSからDBをダンプする(dump.sqlを作成)
mysqldump -h {RDSのホスト名} -P {ポート番号} -u {ユーザー名} -p{パスワード} -p {DB名} &amp;gt; dump.sql

# gzipに圧縮
gzip dump.sql

# scpコマンドで踏み台に送信する
scp -i {秘密鍵のパス} dump.sql.gz {踏み台のホスト名}:{踏み台のダウンロード先のパス}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;パス等が多いという事もあり、文字だらけですが以上のようなスクリプトを作成しました。&lt;br /&gt;
注意する点としては、パスワードは&lt;code&gt;-p&lt;/code&gt;の後に空白を入れずに入力するという事です。空白を入れてしまうとDB名として認識されてしまいます。&lt;br /&gt;
また、私の環境での話ですが、パスワードをDB名より先に入力しないと&lt;code&gt;-p {DB名}&lt;/code&gt;の部分がパスワードとして認識されてしまい、ダンプできないという現象が発生しました。&lt;br /&gt;
そのため、パスワードを先に記述してエラーを回避しています。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;dump.sh&lt;/code&gt;を定期的に実行する&lt;/strong&gt;&lt;br /&gt;
次にこのスクリプトを定期的に実行していきます。&lt;br /&gt;
AWSということで、Amazon EventBridge！と行きたいところですが、AWSを使用した開発経験が過去に皆無だったこともあり、無難にcronで定期実行することにしました。&lt;br /&gt;
コマンドラインで以下をたたき、crontabを開きます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;crontab -e
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;次にcronのスクリプトを書いていきます。&lt;br /&gt;
以下は毎日01時00分にシェルスクリプトを実行するスクリプトです。
パスは適宜書き換えてください(ex. &lt;code&gt;~/tools/dump.sh&lt;/code&gt;)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;0 01 * * * sh dump.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ちなみにcronの時間指定は&lt;code&gt;{分} {時} {日} {月} {曜日}&lt;/code&gt;で、指定しない項目には&lt;code&gt;*&lt;/code&gt;を指定します。&lt;br /&gt;
以上の設定でN4Uサーバー上で毎日DBをダンプする環境が出来上がりました！&lt;/p&gt;
&lt;h3 id=&quot;%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E7%92%B0%E5%A2%83%EF%BC%9Adb%E3%82%92%E8%B8%8F%E3%81%BF%E5%8F%B0%E3%81%8B%E3%82%89%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89%E3%81%97%E3%81%A6%E3%83%AA%E3%82%B9%E3%83%88%E3%82%A2%E3%81%99%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;ローカル環境：DBを踏み台からダウンロードしてリストアする&lt;/h3&gt;
&lt;p&gt;N4UサーバーからDBをダンプして踏み台に送信できるようになったので、ローカル環境でリストアするスクリプトを書いていきます。
ちなみにダウンロードには&lt;code&gt;wget&lt;/code&gt;コマンドを使用して行っていきます。
また、先述した通り踏み台サーバーはベーシック認証を設定しているため、wgetのオプションで指定し、ローカル環境としてDockerを使用しているためDockerコンテナのMySQLにリストアしていきます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/bin/bash

# DBをwgetでベーシック認証を挟んでダウンロードする
wget -P {ダウンロード先のパス} --http-user={ユーザー名} --http-passwd={パスワード} http://{踏み台のIPアドレス}:{ポート番号}/dump.sql.gz
# gzip化されているので解凍する
gunzip -f dump.sql.gz
# Dockerコンテナ内のSQLにリストアする
cat dump.sql | docker-compose -T {コンテナ名} mysql -u {ユーザー名} -p{パスワード} -p {DB名}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上で&lt;code&gt;restore.sh&lt;/code&gt;も完成しました！&lt;br /&gt;
以上のスクリプトをローカル環境で実行することで(&lt;code&gt;bash restore.sh&lt;/code&gt;)、ローカル環境に本番環境のデータが同期されるようになりました！&lt;/p&gt;
&lt;h2 id=&quot;%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB&quot; tabindex=&quot;-1&quot;&gt;おわりに&lt;/h2&gt;
&lt;p&gt;以上が私がN4Uの開発チームに入ってからの初めての仕事になります。今後、更に開発に携わっていくことになるかと思いますので、このブログへのアウトプットも含めて頑張っていきたいです。
ここまで拙文にお付き合いいただきありがとうございました！&lt;/p&gt;
</content>
  </entry>
  
  
  <entry>
    <title>融合知能デザイン研究室でエンジニアブログを始めました</title>
    <link href="https://developers.crowd4u.org/posts/2022-11-30-intro/"/>
    <updated>2022-11-27T15:25:32Z</updated>
    <id>https://developers.crowd4u.org/posts/2022-11-30-intro/</id>
    <author>
        <name>notch_man</name>
        <uri>https://developers.crowd4u.org/author/notch_man</uri>
    </author>
    <content xml:lang="ja" type="html">&lt;p&gt;初めまして。融合知能デザイン研究に所属する現在B4のnotch_manです。
融合知能デザイン研究室（fusioncomp lab）は2017年に立ち上げたAI+人間+ビッグデータによる融合知能の研究を進める研究室です。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fusioncomplab.org/&quot;&gt;融合知能デザイン研究室のHPはこちら&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;今回は融知研のエンジニアブログの記念すべき初投稿として、エンジニアリング視点で融知研の目指す方向、エンジニアブログを開設した理由などについて書いていこうと思います。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- 目次を良い感じに作る --&amp;gt;&lt;/p&gt;
&lt;h2 id=&quot;%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%83%96%E3%83%AD%E3%82%B0%E3%82%92%E9%96%8B%E8%A8%AD%E3%81%97%E3%81%9F%E7%90%86%E7%94%B1&quot; tabindex=&quot;-1&quot;&gt;エンジニアブログを開設した理由&lt;/h2&gt;
&lt;p&gt;私たちがエンジニアブログを開設した理由は、自分達の持つ知見を外部に発信することでエンジニア界隈全体に少しでも貢献したいと考えたからです。&lt;/p&gt;
&lt;p&gt;エンジニアブログは様々なIT企業が技術的知見を外部に発信する場として積極的に運用されています（所感）。例えば、PRTIMES様もエンジニアブログを公開しており、業務内外で得た知見を外部に共有する文化が広まりつつあります。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.prtimes.jp/2022/11/04/benefits-of-blog-posts-for-engineers/&quot;&gt;PRTIMES 開発者ブログへ&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;また、私たちにもいろいろなメリットがあります。&lt;/p&gt;
&lt;h3 id=&quot;%E3%83%96%E3%83%AD%E3%82%B0%E3%82%92%E6%9B%B8%E3%81%8F%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88&quot; tabindex=&quot;-1&quot;&gt;ブログを書くメリット&lt;/h3&gt;
&lt;p&gt;ブログを書くメリットは以下の様なものがあると考えています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;自分がやってきたことの記録になる&lt;/li&gt;
&lt;li&gt;記事を書くことで身に着けた知識が定着する&lt;/li&gt;
&lt;li&gt;ポートフォリオとして活用出来る&lt;/li&gt;
&lt;li&gt;研究室での開発という特異な環境の知見自体に価値がある&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;1つ1つ説明していきます。&lt;/p&gt;
&lt;h4 id=&quot;%E8%87%AA%E5%88%86%E3%81%8C%E3%82%84%E3%81%A3%E3%81%A6%E3%81%8D%E3%81%9F%E3%81%93%E3%81%A8%E3%81%AE%E8%A8%98%E9%8C%B2%E3%81%AB%E3%81%AA%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;自分がやってきたことの記録になる&lt;/h4&gt;
&lt;p&gt;研究では手探りでいろいろなことを進めていきます。すると、過去に自分がやったことを忘れて同じようなことを検索したり、車輪の再発明をしたりと無駄な時間が増えてしまいます。これまでの自分のログを文章としてまとめることでそれらを振り返ることが出来るようになります。&lt;/p&gt;
&lt;h4 id=&quot;%E8%A8%98%E4%BA%8B%E3%82%92%E6%9B%B8%E3%81%8F%E3%81%93%E3%81%A8%E3%81%A7%E8%BA%AB%E3%81%AB%E7%9D%80%E3%81%91%E3%81%9F%E7%9F%A5%E8%AD%98%E3%81%8C%E5%AE%9A%E7%9D%80%E3%81%99%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;記事を書くことで身に着けた知識が定着する&lt;/h4&gt;
&lt;p&gt;例えば、開発中に何かのバグに遭遇したときに大抵の場合は手探りで解決してしまうと思います。しかし、バグが何故生まれたのか？それを防ぐことは出来ないのか？あるいは技術的な限界があるのか？それらについて深い洞察を行なうことはあまり無いと思います。
これらをブログ記事を書くことをきっかけに確かな知識として定着することが出来るようになります。何故なら、第3者に自分が直面した課題、その解決策を伝える時に原因から解決策まで完結に、明確に伝える必要があるからです。何となく弄ったらバグが治りました！なんて記事は何の知識にもならないですし、他の人に伝わる内容にはなりません。他に人に伝えるには何となくで済ましたことを1つ1つを言語化し、それを理解する必要があります。それらの活動を通じて、本物の知識を身につけることが出来ますし、ついでに文章執筆スキルも向上させられます。&lt;/p&gt;
&lt;h4 id=&quot;%E3%83%9D%E3%83%BC%E3%83%88%E3%83%95%E3%82%A9%E3%83%AA%E3%82%AA%E3%81%A8%E3%81%97%E3%81%A6%E6%B4%BB%E7%94%A8%E5%87%BA%E6%9D%A5%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;ポートフォリオとして活用出来る&lt;/h4&gt;
&lt;p&gt;最近のIT関連の就活ではこれまでの実績を要求されがちです。ここで、顕著な実績（学会やコンテストでの受賞歴など）があれば問題無いですが、私たちのような研究室での開発は公に出来ない事も多いので不利になりがちです。しかし、何はどうであれ開発していることに変わりはなく、適切なプロセスを踏んでいればIT業界で働くための基礎体力は身に付いてます。エンジニアブログで執筆する内容はそのような基礎体力を証明する明確なアウトプットだと考えています。さらに、これらを地道に積み上げていき融知研のエンジニアブログが一定の知名度を獲得すれば、その記事を書いているという事実が実績にもなります。訳の分からないコンテスト・ハッカソンに参加したり奇抜なポートフォリオサイトを作るのも良いですが、ブログ記事を通じた地道なアウトプットも1つの戦略として有効です。&lt;/p&gt;
&lt;h4 id=&quot;%E7%A0%94%E7%A9%B6%E5%AE%A4%E3%81%A7%E3%81%AE%E9%96%8B%E7%99%BA%E3%81%A8%E3%81%84%E3%81%86%E7%89%B9%E7%95%B0%E3%81%AA%E7%92%B0%E5%A2%83%E3%81%AE%E7%9F%A5%E8%A6%8B%E8%87%AA%E4%BD%93%E3%81%AB%E4%BE%A1%E5%80%A4%E3%81%8C%E3%81%82%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;研究室での開発という特異な環境の知見自体に価値がある&lt;/h4&gt;
&lt;p&gt;私たちは研究室でシステム開発を行なうという稀な環境に属しています。この特異性として以下が考えられます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;人が2～3年スパンで総入れ替わりする&lt;/li&gt;
&lt;li&gt;研究がメインであるためシステム開発は副業的感覚&lt;/li&gt;
&lt;li&gt;専任のエンジニアが居らず、属人化が進む&lt;/li&gt;
&lt;li&gt;たまたまプログラミング得意な人が居れば何とかなるが、居ない場合はとても苦労する&lt;/li&gt;
&lt;li&gt;いずれの状況に関わらずサービスはユーザーに価値を提供し続ける&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;私が開発に関わり始めたのは2022年の5月頃ですが、前年度まで居られた優秀な方は全員卒業してしまいゼロからの再スタートになってしまいました。
幸い、前任者ほどでは無いですが私にもある程度の知見がありギリギリのところでサービスを提供し続けられています。
しかし、そちらを優先しているが故に研究室内のネットワークの管理に手が回らないなどの弊害も生じています。
これらは徐々に解消しつつあります（これについては別途記事を作成します）が、このような特殊な環境での開発知見はそれ自体一定の需要があるのではないかと考えています。
私たちはそのような知見を積極的に外部に公開することで、ソフトウェア開発全般に対して微力ながら貢献できれば良いなと考えています。&lt;/p&gt;
&lt;h2 id=&quot;%E8%9E%8D%E5%90%88%E7%9F%A5%E8%83%BD%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E7%A0%94%E7%A9%B6%E5%AE%A4%E3%81%AE%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%AE%E7%9B%AE%E6%8C%87%E3%81%99%E6%96%B9%E5%90%91&quot; tabindex=&quot;-1&quot;&gt;融合知能デザイン研究室のエンジニアリングの目指す方向&lt;/h2&gt;
&lt;p&gt;私たちが今後目指すのは次の3つです。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Crowd4uのサービス維持&lt;/li&gt;
&lt;li&gt;NextCrowd4uのサービス提供を目指す&lt;/li&gt;
&lt;li&gt;挑戦できる環境を整える&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;crowd4u%E3%81%AE%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E7%B6%AD%E6%8C%81&quot; tabindex=&quot;-1&quot;&gt;Crowd4uのサービス維持&lt;/h3&gt;
&lt;p&gt;Crowd4uは2008年頃から稼働している「世界をより良い場所にするためのマイクロタスクを実行するための非営利プラットフォーム」です。
こちらでは延べ200万回以上のマイクロタスクが実行され、多くのプロジェクトが行なわれてきました。
また、私たち融合知能デザイン研究室の研究の要に位置づけられるサービスであり今後もサービスの提供を持続していく必要があります。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://crowd4u.org/ja/&quot;&gt;Crowd4uはこちらからアクセスできます&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ただ、現在Crowd4uのサービス維持に当たって以下の様な課題が山積しています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;各種バージョンが古くセキュリティ上の課題が深刻である&lt;/li&gt;
&lt;li&gt;モノリスなEC2を抱えている&lt;/li&gt;
&lt;li&gt;前任者からの引き継ぎが十分ではない&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ただ、我々はこの1年でこれらに立ち向かうための基礎体力を身につけてきました。
新機能開発とは異なり、このような課題は適切に進めていけばいつかは必ず終わる作業です。
その事実は後述するNextCrowd4u開発で証明しました。
多くの課題があり、時間もかかりますが1つ1つ確実に前に進んでいきます。&lt;/p&gt;
&lt;h3 id=&quot;nextcrowd4u%E3%81%AE%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E6%8F%90%E4%BE%9B%E3%82%92%E7%9B%AE%E6%8C%87%E3%81%99&quot; tabindex=&quot;-1&quot;&gt;NextCrowd4uのサービス提供を目指す&lt;/h3&gt;
&lt;p&gt;NextCrowd4u（N4U）はCrowd4uの後継システムとして昨年から開発が本格化したプロジェクトになっています。
ただ、昨年から開発と言っても4月で担当が総入れ替わりし、ほぼ0の状態からスタートしたプロジェクトです。
N4Uは3月下旬から研究目的でテストリリースが始まっていました。
しかし、これまでとは全く違う言語・フレームワーク・アーキテクチャで開発していたが故にいくつものトラブルが発生しました。
これらを解消するには多くの作業が必要で、半年近く経った11月末にそれらの機能改修が終了しシステムの安定性を大きく向上させることに成功しました。&lt;/p&gt;
&lt;p&gt;私たちは知識も経験も無い中、手探りですが課題を洗い出しそれらをタスクチケットに起票して1つ1つ進めていきました。
並行していた新機能開発も含めてですが、このプロジェクトだけで実に100以上のタスクチケットが立てられました。
これらを進めるのは途方もない作業で、負担も大きかったと思います。
しかし、私たちは途方もない量でも1つ1つ進めていけば前に進むことを実感しました。
そして、やり続ければいつかはやり遂げられることを経験しました。&lt;/p&gt;
&lt;p&gt;今後は新機能開発やセキュリティ面の改修を進めていき、来年のリリースに向けて攻めの姿勢で開発を続けていきます。&lt;/p&gt;
&lt;h3 id=&quot;%E6%8C%91%E6%88%A6%E5%87%BA%E6%9D%A5%E3%82%8B%E7%92%B0%E5%A2%83%E3%82%92%E5%89%B5%E3%82%8B&quot; tabindex=&quot;-1&quot;&gt;挑戦出来る環境を創る&lt;/h3&gt;
&lt;p&gt;私は&lt;strong&gt;挑戦するマインド&lt;/strong&gt;がとても大事だと思っています。
新しいサービスを作るにしても、レガシーなシステムを直すにしても多くの壁があります。
時にこの壁はとても厚く、越えるのを躊躇ってしまうレベルの物かもしれません。
しかし、誰かがこの壁を越えることに取り組まなければ私たちは前に進むことは出来ません。&lt;/p&gt;
&lt;p&gt;これまでも、私を中心に以下のような新しいことに取り組んで来ました（これらについては追々ブログとして公開する予定です）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;レガシーシステムのバージョンアッププロジェクト&lt;/li&gt;
&lt;li&gt;レイヤーアーキテクチャの採用によるAPIのリファクタリング&lt;/li&gt;
&lt;li&gt;ローカルと本番環境のデータベースの同期&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;その他にも研究室の取り組みとして以下のようなことをやりました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;サーバー構築を組み立てから実施&lt;/li&gt;
&lt;li&gt;オープンラボの運営を従来と大きく変え、多くの見学者を動員&lt;/li&gt;
&lt;li&gt;エンジニアブログの立ち上げ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;今年、行なった様々なことは主に私を中心に取り組んだ内容になります。
1つ1つの壁は厚かったですが、皆が力を合わせ助け合うことでそれらを越えることが出来ました。
この成功は私たち全員の&lt;strong&gt;挑戦するマインド&lt;/strong&gt;が高まり、積極的な行動があったからこそ成功したものです。&lt;/p&gt;
&lt;p&gt;私は、このような&lt;strong&gt;挑戦するマインド&lt;/strong&gt;を持った人達を増やせれば研究も開発もさらに大きく進んでいくのではないかと考えています。
そして、私をはじめこの1年プロジェクトに関わった方々には多くの成功や失敗を経験し、たくさんのノウハウを貯めています。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;今のラボにはそういった頼もしいメンバーがたくさん居るので、何かやりたい事があったら積極的に手を挙げてドンドン取り組んでいきましょう&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;既に多くのメンバーが自分のやりたい事を積極的に行動する雰囲気が出来つつあり、今後も多くのことに取り組んでいく予定です。
もちろん、本業は研究なのでそれを疎かにしてはいけないです。
しかし、せっかくの機会なので学生時代にしか出来ない活動をたくさんやっていきましょう。&lt;/p&gt;
&lt;p&gt;私は、そのような雰囲気作りを今後も積極的に行なっていくつもりです。&lt;/p&gt;
&lt;h2 id=&quot;%E6%9C%80%E5%BE%8C%E3%81%AB&quot; tabindex=&quot;-1&quot;&gt;最後に&lt;/h2&gt;
&lt;p&gt;このブログでは今後、研究や開発で詰まったことの知見共有、チーム開発のノウハウなどを積極的に発信していきます。
引き続きよろしくお願い申し上げます。&lt;/p&gt;
</content>
  </entry>
  
</feed>