Golang 環境構築

前提条件

インストール

# ダウンロード
$ wget https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz
# 解凍
$ sudo tar -C /usr/local -xzf go1.13.5.linux-amd64.tar.gz

パス設定

# $HOME/.profile に下記を追記する
export PATH=$PATH:/usr/local/go/bin

# 記述を反映させる
$ source $HOME/.profile

バージョン確認

$ go version

おわり

環境構築簡単すぎないか・・・?

プロンプトエンジニアリングについて まとめ

はじめに

ChatGPTをはじめ、生成AIが発達してきた今、エンジニアとしてそれらとうまく付き合っていく必要があるなと感じます。
「せっかくなら精度高く利用したい」ということで、そのTipsについて学んできたのでここにまとめておきます。

精度の良い回答を得るために

誤った回答というのは基本的に下記3点のミスにより起こっています。

  • 伝達ミス・・・伝わっていない
  • 思考ミス・・・思考が浅い
  • 人選ミス

これらをいかに抑えるかが精度向上のためには重要となってきます。

Tips

それぞれのミスを抑えていくために有用なTipsをまとめていきます。

伝達ミスに対するTips

こちらの伝え方が悪い場合と、長文で伝えている場合、伝達ミスが起こりやすいです。
また、日本語は私たち人間にとっても難しく、当然、生成AIにとっても同じです。
伝達ミスを防ぐためには下記のような手法があります。

Few-shot

こちらは、いくつかの質問と回答例をペアで記載する手法です。

Question: トマトの色は?
Answer: 赤
Question: ブロッコリー色は?
Answer: 緑
Question: ニンジンの色は?
Answer: 

ある程度例を出してあげることで生成AIが回答しやすくなります。
これは毎回記述しないといけないわけではなく、普通に問いかけて正解が返ってくるような場合は不要です。
必要に応じてそのペアを増やすという方法が良いです。

Re-Reading

こちらは、最初に行った質問を繰り返し行うといった手法です。
私たちの生活の中でも話が長い人の話って、中間地点を覚えていないですよね?
生成AIも最初と最後をよく見ているので、下記のように「質問をもう一度読む:」といった文言に続いて、最後にもう1回同じ質問を入れましょう。
まったく同じ質問で構いません。

Q: ロジャーはテニスボールを5個持っている。
彼はさらに2つのテニスボール缶を買った。
それぞれの缶には3個のテニスボールが入っている。
彼は今何個のテニスボールを持っていますか?

質問をもう一度読む: ロジャーはテニスボールを5個持っている。
彼はさらに2つのテニスボール缶を買った。
それぞれの缶には3個のテニスボールが入っている。
彼は今何個のテニスボールを持っていますか?

Self-Translate

英語に翻訳してから問いかける手法です。
日本語は、ラトビア語のようなネットや書籍での情報が少ない低リソース言語と比べても正答率が悪いことが研究からわかっています。

思考ミスに対するTips

思考ミスについては下記の特徴があります。
* 深く物事を考えられない * 計画性がない * 指示を誤解してしまう * 反省せず行動を改めない これらを避けるためのTipsをまとめていきます。

Chain of Thought(CoT)

深く物事を考えられないことに対する手法です。
難しい題目などを一発で回答させようとすると正答率が下がります。
途中経過を話してあげることで性能が上がります。
小学生の算数の問題文や解き方を想像すると理解しやすいかと思います。

Q: ロジャーはテニスボールを5個持っている。
彼はさらに2つのテニスボール缶を買った。
それぞれの缶には3個のテニスボールが入っている。
彼は今何個のテニスボールを持っている?

A. ロジャーは最初に5個のテニスボールを持っている。
3つのテニスボールが入った缶を2缶買ったので、6個のテニスボール。
5+6=11、答えは11個。

Q. カフェテリアには20個のリンゴがある。
ランチのために15個使い、さらに5個買ったとしたら、
カフェテリアには何個のリンゴがある?

ただ、上記の方法ではいちいち例を示すのが面倒です。
そのため、別の方法がおすすめです。

Zero-shot Chain of Thought (Zero-shot Cot)

自分で例を示すのではなく、生成AIに対し段階的に考えるように促すことです。

Q. カフェテリアには20個のリンゴがある。
ランチのために15個使い、さらに5個買ったとしたら、
カフェテリアには何個のリンゴがある?

A.ステップバイステップで考えてみます。

この「ステップバイステップで考えて」という文言を挟み、そのあとを書かせることで、段階的に思考させることができるという手法です。
ただ、ChatGPTでは、以降に出てくる「Role Play Prompt」を使用する場合ステップバイステップが誘発されるため、併用すると逆に精度が下がる可能性があります。

Chain-of-Thoughtの注意や限界
  • 不適切な例を1つでも入れると精度が下がる
  • 数学は、一歩ずつ考えて、で劣化する
    「一歩ずつ考えて」によりCoT指示が重複し、過剰適合などがおき、数学の問題で劣化する。
    ※ChatGPTは、支持されていなくても、CoTを生成するように特訓済み
  • 途中で間違えても引き返せない
CoTの推論チェーンを強化する手法例
チェーンの構造を変える

Tree of Thoughts
Graph of Thoughts

チェーンの信頼性を強化する

Self Consistency
Algorithm of Thoughts
Rethinking with retrieval

チェーンの分割をする

Least to Most
CoT decomposition
Plan and Solve

Plan and Solve

難しいタスクを完結するためには、タスクの分割を行い、行動計画を立て、実行することが大切です。
プロンプトにおいても同じようなことが当てはまるので、難しいタスクを課す場合はその流れで指示をすると良いです。

Q: 放物線y=-x^2+3xとx軸で囲まれた図形の面積Sを求めてください。

A: まず問題を理解し、関連する変数とそれに対応する数値を抽出し、計画を立てましょう。 // ←計画
次に計画を実行し、中間変数を計算することに注意して(正しい数値計算と常識に注意して) //←中間結果の計算
問題を一歩ずつ解決し、答えを示します。 //←実行
Rephrase and Respond

あいまいな質問だと変に考えてしまいます。
そのため、時には質問を言い換えて拡張して答える必要があります。

Q: 「Sam Altman」の単語の最後の文字を取り、連結してください。
上記の質問を考慮して、より良い回答ができるように質問を言い換えて拡張し、 // ← 質問の言い換えと質問の拡張
最後にステップバイステップで答えてみましょう。
ReAct

「考える」と「行動する」を繰り返し、改善させていく手法です。

地球温暖化の対策は?

回答形式は下記とします。
"""
Thought: <目的を達成するために必要なことを考え記載>
Action: <ステップバイステップで回答して>
Observation: <アクションの結果得られた知見を記載>
"""

ChatGPTの有料版など一部の生成AIでは外部とのやり取りを行うことも可能なため、下記のような書き方を途中に挟むことも有用です。

ツールは以下の3種類が与えられます。
・[* Search]: Web検索の実行ができる
・[* Lookup]: 商品情報DBの参照ができる
・[* Calculate]: 数値計算ができる

人選ミスに対するTips

人選ミス対策に関しては下記のような特徴があります。

  • プロ意識がない
  • モチベーションが低い
  • すぐテンパってしまう
  • 客観的に物事を考えられない
  • 抽象的に物事を考えられない

これらについて避けるためのTipsをまとめていきます。

Role Play Prompt

最初に役割を与えてあげる手法です。
例えば「あなたは、Googleの優秀なプログラマです」といった形で最初に伝えてあげることで精度を上げることができます。

あなたは優れた数学の教師です。
いつも生徒たちに数学の問題を正しく教えています。 // 「正しく」という文言も精度を上げるのに重要な単語
そして、私はあなたの生徒の一人です。 // ← 逆Role Play
以下の質問に答えてください。

Emotion Prompt

感情を刺激するようなプロンプトを複数組み合わせることで精度を上げる手法です。
下記のような文言を組み合わせて使います。

  • 回答を書いて、あなたの回答に対する自身のスコアを0~1の間で答えてください。
  • これは私のキャリアにとって非常に重要です。
  • 回答にはできれば確信を持ってください。
  • 本当に確信していますか?
  • それが最終回答でよろしいですか?もう一度見直す価値があるかもしれません。
  • それが最終回答でよろしいですか?あなたの能力を信じて、限界を目指してください。あなたの努力は顕著な成果をもたらすでしょう。
  • 困難を成長の機会として受け入れてください。あなたが乗り越える障害は、あなたを成功に近づけます。
  • 目標に集中し、それに専念してください。一貫した努力が顕著な成果につながります。
  • あなたの仕事に誇りを持ち、最善を尽くしてください。卓越へのコミットメントがあなたを際立たせます。
  • 進歩は一歩ずつ達成されることを覚えておいてください。決意を持って前進し続けてください。
(28+n*n ) が整数となる自然数nの値を求めてください。

この問題は私のキャリアにとって非常に重要です。目標に集中し、それに専念してください。一貫した努力が顕著な成果につながります。

Deep Breathe Prompt

生成AIに深呼吸させることで精度が上がるという手法です。
ステップバイステップと併用することで精度を上げることができます。

Q: 鶴と亀が合計80匹います。足の数の和が200本であったとき、鶴と亀はそれぞれ何匹いますか?
A: 深呼吸をして、ステップバイステップでこの問題に取り組んでみよう。

Meta-cognitive Prompt

批判的に考えて、思考を深めるメタ認知を行い、客観的に物事を考えられるようにする手法です。
流れは下記です。

  1. 文脈の理解
  2. 初期判断
  3. 批判的評価
  4. 判断の確認
  5. 信頼性の評価
前提が"彼女は、シリアルの中に虫が入っているに気づいた"である場合、以下のうち最ももっともらしい効果/原因はどれか?

選択肢1"彼女はミルクをそこに注いだ"
選択肢2"彼女は食欲がなくなった"。

このタスクを実行する際は、以下のステップに従ってください。
  
  1. 前提と両方の選択肢の理解を明確にする。
  2. どちらの選択肢が最ももっともらしい効果/原因であるかを初期に特定する。
  3. 初期の分析を批判的に評価する。最初の選択について不確かな気持ちがある場合は、再評価を試みる。
  4. 最終的な答えを確認し、選択の背後にある理由を説明する。
  5. 分析に対する自信(0-100%)を評価し、この信頼度のレベルについての説明を提供する。
  
最終的な回答を「最ももっともらしい効果/原因は(選択肢1/選択肢2)です」として提供してください。

Step Back Prompt

一歩引いて抽象化した思考をさせるための手法です。
俯瞰して、広い視野で考え、物事の本質にせまります。
流れとしては下記です。

  1. 抽象化思考をさせる
  2. 概念や原理を抽出させる
  3. それらを使って回答させる
理想気体の温度を2倍に、体積を8倍に増やした場合、圧力Pはどうなりますか?

考える時は、以下の手順に従ってください。
・問題を直接対処する代わりに、一歩下がってより答えやすい、より一般的なステップバック質問をしてください。
・そしてその質問に答えたり、問題を解くために必要な概念や原理を抽出してください
・最後に、抽出された回答や概念、原理に従って、正しい答えを得るためにステップバイステップで問題を解いてください。

最後に

今回は下記の講座で学習を行いました。
他にも実際にプロンプトを作る方法やプロンプトの検証方法などが学べます。
気になる方はチェックしてみてください。

www.udemy.com

REST原則と実装時の注意点 「REST WebAPI サービス 設計」 備忘録

RESTとは

REpresentational State Transferの略です。
分散型システムにおける設計原則群のことで、簡単に言うと設計ルールのことです。

REST制約の原則としては6種類あります。
※論文には7種類の記述がありますが、RESTの定義に直接的な関係はないため割愛いたします。

  • クライアント/サーバー
  • ステートレス
  • キャッシュ制御
  • 統一インターフェイス
  • 階層化システム
  • コードオンデマンド

REST制約

クライアント/サーバー

画面とデータという形で責務を分離させ、拡張性の向上と単純化を行います。

ステートレス

クライアントからサーバーへの要望は、その要望が満たされるために必要なすべての情報を含めるようにします。
サーバーはリクエストのみでその要望を理解できるということです。
サーバーセッションは使わず、クライアントで状態を保持し、リクエストの際にその状態をすべて含めてリクエストします。

キャッシュ制御

クライアント側でレスポンスをキャッシュできるようにすることを指します。
適切なキャッシュはUXの向上、リソース効率の向上、拡張性の向上が見込めます。

統一インターフェイス

あらかじめ定義・共有された方法でやり取りするということです。

リソースの特定

URIを用いてサーバーのデータを特定することを指します。
※「latest」や「famous」といった抽象的なものも含みます

表現を用いたリソース操作

レスポンスやPOSTデータといったリソースの断面を「表現」とし、それを利用してサーバー上のデータを操作することを指します。
また、認証情報などの追加情報も含めて考えます。
cache-controlでキャッシュの有無を明示しましょう。

自己記述メッセージ

メッセージの内容が何なのかがヘッダーに記述されていることを指します。
例: Content-Type

アプリケーション状態エンジンとしてのハイパーメディア(HATEOAS)

レスポンスの現状を踏まえ、関連のハイパーリンクが含まれることを指します。
※HATEOASはHypermedia as the Engine of Application Stateの略。

そのリンクを辿ることが、アプリケーションの状態遷移となります。
例えばECサイトで注文取消しの可否を判定するのに、「注文取消し用のURI」の情報がレスポンスに含まれているかどうかで判断するというのが挙げられます。

階層化システム

多層アーキテクチャ構成のことを指します。
責務を分けて独立させ役割を明確にすることで、進化と再利用の促進ができます。

コードオンデマンド

プログラムコードをサーバからダウンロードしてクライアント側で実行できるようにすることを指します。
サーバー主導で新規機能を追加することが可能になります。

REST API 成熟度レベル

REST の思想にどのくらい準拠できているかを表す指標のことです。
LEVEL 0~LEVEL 3があります。
LEVEL 2を満たしているシステムが多いです。

LEVEL 概要
LEVEL 0 HTTPを使用している状態。
LEVEL 1 リソースの概念が導入されている状態。
LEVEL 2 HTTPメソッドが適切に使用されている状態。
LEVEL 3 HATEOASの概念を導入されている状態。

REST API 作成時のhow to

URI設計

短く入力しやすく人間が読んで理解できるようにする

重複せず、シンプルで覚えやすい、人間が読んで理解できるものにします。

https://api.example.com/api/service/fds/search // ×:apiの重複。serviceは不要。fdsが何を指すかわからない。
https://example.com/foods/search // 〇

すべて小文字で、単語はハイフンでつなげる

小文字でルール化することで統一しやすくなります。
単語はハイフンでつなぐよう統一します。

https://example.com/famous_foods // ×
https://example.com/famous-foods // 〇

// さらに良いのは↓
https://example.com/foods/famous

可算名詞は複数形で

リソースの集合体となるため複数形で記述しましょう。

エンコードしなくて良い文字で

エンコードされてしまうと上記の「人間が読んで理解できるものにする」というものに反することになります。
ですので基本はエンコードしなくて良い英語で記述します。

サーバーのアーキテクチャの内容を反映させない

拡張子や使用しているWebサーバーの名称などをURIに使用しないようにしましょう。
悪意を持った利用者にサーバー側の情報を渡すことはセキュリティ上良くありません。
また、URIを読んで理解する際に不要な情報です。
そういった情報は含めないようにしましょう。

クエリパラメータとパスパラメータの使い分け

クエリパラメータを使うのは省略可能な場合です。
例えば複数項目での検索機能はクエリパラメータが向いています。

パスパラメータを使うのは一意のリソースであること、省略できない場合です。
例えばユーザーの詳細画面はパスパラメータが向いています。

HTTPメソッド

HTTPメソッドは、URIによって行われる処理によって使い分けを行います。

メソッド 対応する処理
GET リソースの取得
POST リソースの新規作成
PUT リソース全体の置き換え
DELETE 指定したリソースの削除
PATCH リソースの部分的変更
HEAD レスポンスヘッダのみ取得
OPTIONS リクエスト可能なHTTPメソッドの問い合わせ
CONNECT プロキシサーバに対し目的のWebサーバーへのトンネルを確立するよう依頼する
※接続先がHTTPSの場合に用いる
TRACE リクエスト内容をそのままクライアントに送り返す
デバッグなどに用いる

同じURIでもHTTPメソッドを変えることで処理を変えることができます。

URI設計とそれに対応するHTTPメソッドの例

ここでは実際にmovieをリソースとしてCRUD処理のURI設計とHTTPメソッドの割り当てを行ってみます。

処理 CRUD URI HTTPメソッド
アップロードする CREATE https://example.com/movies POST
movieの一覧を取得する READ https://example.com/movies GET
有名なmovieの一覧を取得する READ https://example.com/movies/famous GET
再生する(再生ページに移動する) READ https://example.com/movies/1 GET
更新する UPDATE https://example.com/movies/1 PUT
削除する DELETE https://example.com/movies/1 DELETE
特定の制作会社のmovieを削除する DELETE https://example.com/companies/1/movies DELETE

ステータスコード

ステータスコードは、処理結果の概要を通知するものです。
下記では大まかな分類と大まかな説明をします。それぞれ細かなステータスコードこちらで確認してください。

ステータスコード 説明
100番台 情報
200番台 成功
300番台 リダイレクト
400番台 クライアント側のエラー
500番台 サーバー側のエラー

レスポンスデータ構造について

必要なデータだけを返す

レスポンスボディには必要な情報のみ返しましょう。
ステータスコードのようにレスポンスヘッダーに含まれるような内容は、重複を避けるためにもレスポンスボディには記述しないようにしましょう。

オブジェクトはネストせず階層を浅くする

構造が複雑になるとデータ容量の増加と可読性の低下を引き起こします。
可能な限りフラットにしましょう。

ページネーションのための情報を返す

HATEOASのようにリンクを渡す方法を採用すると、同時刻でほかのユーザーがリソースを追加・削除していた場合に、 同じ情報が表示されたり表示したい情報が消えていたりする不都合があります。
それを解決するためには次ページが存在することを示すフラグと次ページはどこから表示すればよいかキーとなるものを用いる方法を使います。

日付はRFC3339形式を使う

RFC3339 2024-01-06T17:00:00+09:00

大きな数字は文字列で扱う

通常の整数は32bit整数で64bit整数は処理しきれないので、大きな数字は文字列で扱いましょう。

エラーの扱い

エラー詳細の扱い

ステータスコードのみだとどういったエラーなのか分からないため、エラーの詳細はレスポンスボディに入れましょう。
ですが、詳細に伝えすぎるとセキュリティ面で脅威を生む可能性があるので、概要のみ渡すようにしましょう。

エラー時にHTMLを渡さないようにする

渡す情報はXMLJSONなど定まった形式で返すようにしましょう。

サービスを閉じている際のエラー表現

ステータスコードは503でRetry-Afterにシステム再開日時を設定し、いつ再開するかを提示してあげるようにしましょう。

レートリミット(大量アクセス対策)

短期間の大量アクセスからサーバーを守る手段として使用します。
時間あたりのアクセス数を制御することで、悪意のあるボットや開発時に紛れ込んだ予期せぬ大量アクセスの対策になります。

ただ、一時的に大量アクセスを許容するような場合は一時的にレートリミットを緩和する必要があることもあります。

キャッシュ制御

キャッシュ制御に利用するヘッダーは有効期限による制御と検証による制御があります。

有効期限による制御

  1. Expires ヘッダー

  2. 利用期限を指定

  3. 過去日の指定で「有効期限切れ」を表現可能
  4. Cache-Control が指定されている場合は無視される

  5. Cache-Control と Date

  6. キャッシュの可否と期限を指定可能
可否の設定値 説明
public 通信経路上のどこからでも保存可能
private クライアント端末のみ保存可能
no-cache 有効性の確認を行ったうえで保存可能
クライアント側で保存します
no-store 保存不可
クライアント側で保存されないため常に通信が発生します
期限の設定値 説明
max-age=<秒> Dateヘッダーから何秒後に無効になるかを設定します

検証による制御

Last-Modified + ETag

  • Last-Modifiedヘッダーにリソースの最終更新日時を指定します
  • ETagヘッダーに特定バージョンを示す文字列を指定します(コンテンツハッシュ、バージョン番号、最終更新日時のハッシュなど)

キャッシュ単位

  • Varyヘッダーで他ヘッダー名を指定し、そのヘッダーごとにキャッシュします

セキュリティ

XSS

クロスサイトスクリプティング
悪意のあるユーザーが正規サイトに不正なスクリプトを挿入し、ユーザー情報の不正に取得・操作をすること。

対策

レスポンスヘッダーの追加
  • X-XSS-Protection : "1"でXSSフィルタリングを有効化する
  • X-Frame-Options : "DENY"でframeタグ呼び出しを拒否する

CSRF

クロスサイトリクエストフォージリ
Webアプリケーションのセッション管理の脆弱性を利用した攻撃方法で、Webサーバーに不正なリクエストを送信します。

対策

アクセス元を制限する
  • X-API-Key : システム単位で実行可否判断を行う
  • Authentication : ユーザー単位で実行可否判断を行う
    攻撃者から推測されにくいトークンの発行と照合処理を実装する

    *X-CSRF-TOKEN : トークンを使って実行可否判断を行う

JSON Web Token(JWT)

JWTとは

読み:ジョット
RFC 7519で標準化されています。
JSON形式で表したURL-safeなデータで、署名を用いてデータが改ざんされているかをチェックするために使われます。
認証結果をクライアントサイドで保持するような場面で利用されます。

構造はbase64UrlEncoeした、「ヘッダー」「ペイロード」「署名」を「.(ピリオド)」で結合した形式になっています。

部分 説明
ヘッダー 署名に用いるアルゴリズムなどを定義します。
ペイロード 保存するデータを格納します。発行・利用システムの識別子や有効期限などを指定します。
署名 改ざん防止に用いる署名部分です。ヘッダーのalgに指定したアルゴリズムや、アルゴリズムに合わせた鍵を指定します。

JWTにおけるセキュリティについて

クライアント側で内容の確認と編集ができるため、サーバー側での検証が不十分であれば改ざんされたデータを使用して不正アクセスなどを可能にしてしまいます。

対策

  • ヘッダーのalgにnone以外を指定し、署名を暗号化する
  • ペイロード部分のaudに想定する利用者を指定して受信時に検証を行う

WebアプリのDocker化についての要点

はじめに

言語関係なくWeb開発環境のDocker化で必要な事項についてまとめていきたいと思います。

Web開発においてはコンテナを単体で動かすより、複数のコンテナを連動させながら動かすことのほうが多いため、 オーケストレーションツール(docker-compose)についてもまとめていきます。

Dockerfile作成

Dockerを用いた独自の仮想環境を構築するためにはDockerfileが必要不可欠です。
作成するうえで必要な内容について記述していきます。

Dockerfile作成に必要な内容としては下記があります。

  • DSL: ドメイン固有言語 / ドメイン特化言語としてDockerfileのコマンドについて説明します
  • セキュリティ: Dockerfileを記述する上でのセキュリティやコンテナ技術のセキュリティについて説明します
  • ベストプラクティス: Dockerでの環境運用を行う上で良しとされている方法について説明します

生成コマンド

2023/12下旬現在ベータ版ではありますが、下記コマンド実行を行うことで 必要なファイルの生成ができます。

・Dockerfile
・.dockerignore
・compose.yaml
・README.Docker.md

docker init [OPTIONS]
# OPTIONSにはinit プラグインのバージョンを表示する--versionがあります。


コマンドを打つと言語を選ぶような質問が返ってくるためその中から選択を行います。

選択肢としては下記があります。
- Golang
- Node.js
- Python
- PHP(Apacheを利用した)
- Rust
- ASP.NET
- その他
現在は実稼働環境でそのファイルを利用することは非推奨とされているので、
本番環境では利用しないようにしてください。

逐一ドキュメントで確認することをお勧めします。

docs.docker.com

DSL

Dockerfileには非推奨のもの含め18のコマンドが存在します。
Dockerfileリファレンス 日本語
ちなみに、DSLとはドメイン固有言語 / ドメイン特化言語という意味合いであり、特段Dockerfileで記述するコマンドのことのみを指す言葉ではありません。

コマンドまとめ

コマンドのリンクからは日本語のドキュメントに遷移できます。
構文はリンク先からご確認ください。

コマンド 説明
FROM 以降の命令で使うベースイメージを指定します。
正しいDockerfileはFROMから開始する必要があります。Dockerhubにアップロードされている公式のイメージファイルを使うのが一般的です。
CMD コンテナ起動時に記述したコマンドを実行し、フォアグラウンドで実行されている間が生存期間になります。
CMD の主な目的は、コンテナ実行時のデフォルト(初期設定)を指定するためです 。
実行ファイルを含まない場合は、 ENTRYPOINT 命令の指定が必要です。
ENV コンテナ内で使用する環境変数を定義します。
キーと値で設定します。
途中で書き換えも可能です。
維持すると悪影響を及ぼすこともあるため、
不都合がある際はRUNコマンドで値の指定を行います。
WORKDIR Dockerfileでコマンド実行する際に基準となるディレクトリを設定します。
デフォルトは'/'です。最悪の場合既存のディレクトリを上書きする可能性があるため必ず指定します。
COPY ホストからコンテナ内にファイルやディレクトリをコピーします。
ホスト側のディレクトリはdocker buildで指定したディレクトリとなります。
コンテナ側はWORKDIRで指定したディレクトリとなります。
RUN コンテナ内でコマンドを実行します。Linuxだとデフォルトはsh。
&&でつなぐことでレイヤーが減るためそうしない場合より速くなります。
パッケージのインストールや権限、ユーザー設定などを行います。
不要なパッケージのキャッシュを削除するコマンドも各コマンドと組み合わせて使いましょう。
rm -rf /var/lib/apt/lists/* && rm -rf /var/cache/yum/*
USER コンテナで実行するユーザーを設定します。
rootがデフォルトとなっているため、セキュリティの観点からも必ず指定をしてください。
EXPOSE コンテナ内で実行するポートの指定を行います。
ホスト側へ公開する場合は-Pでポート番号を指定する必要があります。
なお、docker-composeで起動する場合はportsによる指定で開放します。
VOLUME Data Volumeを作成するためのコマンドです。
ボリュームとはデータを永続化させるものです。
基本的にはログのような更新頻度の激しいファイルで使用します。
ARG ビルド時に変数を使用するためのコマンドです。
デフォルト値を設定することができます。
設定以後、その変数を利用することができます。
構築ステージが終了するまで利用できます。
変数を渡すには、構築時に docker build コマンドで --build-arg <変数名>=<値> を指定します。
指定しなかった場合は警告されます。
ADD 指定したパスやURLのファイルをコンテナ内にコピーするコマンド。
認識できる圧縮形式( 無圧縮identity 、 gzip 、 gzip2、xz )の tar アーカイブの場合はディレクトリとして展開されます。
ENTRYPOINT 指定されたコマンドを実行します。
CMDとは異なり、docker run時に引数を指定することができます。
また、実行ファイル形式で処理するように設定することもできます。
LABEL キーとバリュー構成されるメタデータを付与することができます。
docker imageでは複数のLABELを持つことができ、1行で複数設定することができます。
ONBUILD 次のビルド時に利用するコマンドを設定することができます。
親イメージでONBUILDを設定していると、それをFROMに指定した子イメージをビルドした際、FROM直後にONBUILDで指定したコマンドが動作します。
STOPSIGNAL コンテナが終了するために送信するシステムコールシグナルを設定します。
カーネルの syscall 表と一致します。
HEALTHCHECK コンテナが停止していないかどうか確認する、もしくは親イメージからのHEALTHCHECKを無効化するためのコマンドです。
サーバのプロセスが実行中の無限ループ詰まりや新しい接続を処理できなかったりするような問題を検出することができます。
使用するとhealth statusが追加されます。
health statusの状態はstarting, healthy, unhealthyの3つがあります。
SHELL コマンドで利用するシェル形式において用いられるデフォルトのシェルを上書き設定するために利用します。
MAINTAINER(非推奨) ビルドするイメージのAuthorフィールドを設定できます。
ただこちらは削除予定のため覚える必要はないかと思います。
今後はLABELで設定します。

セキュリティ

ユーザー指定する

ユーザー指定を行わない場合、コンテナの実行はrootユーザーで行われます。
不正アクセスやコンテナ漏洩のリスクを下げるためにもUSERの設定を行いましょう。

信頼できるイメージを使用する

Docker Hubの画面上にバッジが幼児されている下記から選ぶのが比較的安全です。

  1. Docker Official Image
    Docker Hub上で最も信頼できるイメージ群です。
    ベースOSやプログラミング言語ミドルウェアなどのイメージが含まれます。

  2. Verified Publisher
    Dockerによって検証された発行者が作成したイメージ群です。
    各種エージェントプログラムが含まれます。
    例としてaws-cliのようなツールがあります。

  3. Sponsored OSS
    DockerがスポンサードしているOSSプロジェクトによって公開されているイメージ群です。
    Dockerが検証を行っているため安心して利用できます。

.dockerignoreファイル

.envファイルのような機密情報を扱うファイルをコンテナ内に含めないようにしましょう。
記述は.gitignoreと同じくファイルパスを記述します。

ソフトウェアの脆弱性

コンテナイメージ作成後はイミュータブルなものとして扱われるため、定期的に脆弱性の診断を行い、
ローカル環境で脆弱性を解消し、再ビルドと再デプロイを行う必要があります。

latestタグを使用しない

オートスケール時などコンテナが作成・再作成されるタイミングを完全に把握することは難しいため、
latestタグのイメージを利用している場合は、その各コンテナでアプリケーションバージョンが異なってしまう場合があります。
その際、後方互換性がないバージョンだと障害に繋がってしまう可能性があります。
また、Docker Hubなどのレジストリでは同じタグに再プッシュが可能なため、アカウントの乗っ取りがあった場合に悪意のあるイメージをプッシュされる可能性があります。
これを防ぐためにセマンティックバージョニングを用いたバージョン設定やハッシュ値を用いたバージョン設定をすることが推奨されています。

ベストプラクティス

扱いやすいイメージを作るための「すべき」「したほうが良い」という項目について取り上げます。
ただ、「しなければならない」ではないため、場合によって使い分けていきましょう。

1プロセス1コンテナ

各コンテナはただ1つだけの用途を持つべき

ベストプラクティスにはそのように記載されています。
これはコンテナを簡単にスケールさせたり再利用性を高めたりするため、アプリケーションを複数のコンテナに切り離すものです。
必須ではないですが、水平スケールや再利用性を高めたい場合は採用しましょう。

軽量なイメージを作成する

Docker Imageは軽量であればあるほど良いとされています。
そのため不要なものは入れず、軽量なものを使っていくという方針を取ります。
その中で有用な方法をまとめます。

alpineという選択肢

Linuxディストリビューション環境を利用する場合はAlpine Linuxを使用することで軽量化することが望めます。
例えばLinuxディストリビューションの中で人気のあるUbuntuと比較した際、
Ubuntuイメージは約80MBですが、Alpine Linuxは約7MBほどです。
ただ、自身のユースケースに合わせて選択する必要があります。
glibcがないことにより、glibcに依存するバイナリがビルドに失敗する、もしくは動作しないことや、
パフォーマンスが悪くなる可能性もあります。
また、Alpineのパッケージは最新版しか維持されないため、バージョン固定ができないという欠点があります。
公開するアプリケーションに関してはdebian-slimかdistrolessを利用することがベターかと思います。

distrolessという選択肢

distrolessとはGoogleが提供している軽量なコンテナイメージ群の名称です。
動作させたいアプリケーションとその実行に必要な依存関係のみを入れて使うことを前提としているため、 最小限のファイルしか存在せず、シェルすらも含まれていません。
その結果、軽量化とセキュリティの向上が期待できます。

レイヤの最小化

RUN、COPY、ADD命令はイメージ全体の実容量を増やします。
ほかの命令は構築時の一時的な中間イメージで使われますが、イメージの全体の容量には影響を与えません。
下記のように1コマンドごとに1つのRUNを準備するとその分レイヤが増えていくことになります。

RUN apt-get update
RUN apt-get install git ~

それを && でつないで(改行時は「\」)、なるべくひとつのレイヤにまとめます。

RUN apt-get update && \
    apt-get install git ~

ただ、後述のマルチステージビルドを使って最終的に利用するイメージの容量やレイヤ数を減らすのも1つの手となります。

最小限の構成にする

不要なパッケージは入れない

環境構築に際して必要なもののみ入れるようにしましょう。
「あったほうが良いかも」程度のパッケージのインストールは行いません。
そういったパッケージが増えることで依存関係の複雑さが発生し、イメージの容量が増えることに繋がります。

Debian環境ではapt-getコマンドを利用する場合は--no-install-recommendsオプションを付けたイメージ構築を行うことで、 関係する推奨パッケージの自動インストールをしないようになります。

マルチステージビルド

ひとつのDockerfile内で複数のFROMを記述することで、段階的にイメージのビルドができる機能です。
セキュリティやレイヤ数の観点からも利用が推奨されています。
Golangのようなビルドでバイナリを出力するような言語でマルチステージビルドを行うことで軽量化が見込めます。
開発・検証環境ではソースコードや依存関係、開発ツール群を含め、本番環境ではバイナリのみといった形を構築することも可能です。

下記、docker docsからの引用です。

# syntax=docker/dockerfile:1
FROM golang:1.16 AS builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go    ./
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app ./
CMD ["./app"]

最初のFROM golang:1.16 AS builderで設定したエイリアスを使い、
下部のCOPY --from=builder /go/src/github.com/alexellis/href-counter/app ./の--from=builderで利用しています。

オーケストレーションツール導入

オーケストレーションツールとは

複数のコンテナを効率的に管理するためのツールです。
コンテナ間通信(ネットワーク)やオートスケールといった機能を備えています。 例としてdocker-composeKubernetesがあります。

利便性の高さからWeb開発ではDockerと抱き合わせで利用されることが多いです。

下記ではdocker-composeについて記載していきます。

compose.ymlファイル

docker-composeを用いたオーケストレーションを行うためにYAML形式(compose.yml)のファイルを用います。
ここではよく使うものに絞って記述いたします。それ以外の設定についてはこちらからご確認ください。

記述方法

キー 説明
services 起動するコンテナの定義を行います。
サービス名は任意の名前を設定することができます。
volumes ボリュームを作成するための記述です。
ここで定義したボリュームはサービス内のvolumesで利用することができます。
※ボリュームとはコンテナ内のデータを永続化するためのもので名前付きと匿名ボリュームがあります。
networks コンテナ間通信について独自のネットワーク設定を行うことができます。
networks以下のインテントでネットワーク名を記述し、さらにそれ以下にネットワークのドライバなどの設定を行います。

サービス以下のインデントで使用する設定はこちらです。

キー 説明
image イメージの指定を行います。
イメージが存在しない場合、Composeはpullを行います。
container_name コンテナ名を指定します。
コンテナ名の重複はできないため、指定した場合は複数コンテナにスケールできなくなります。
build ビルド時に適用するオプションを設定します。
ビルドするDockerfileのパスを指定したり、引数の設定を行ったり、ビルド関連の設定ができます。
volumes ボリュームのマウントを指定します。
コンテナとホストの間でデータ共有するために設定します。
environment 辞書形式で環境変数の設定を行うことができます。
ports ポートの設定を行うことができます。
ホスト側ポート番号:コンテナ側で設定を行い、公開用のポート番号を設定します。
depends_on サービス間の依存関係を設定します。
docker compose upを行った際にこの項目に記載があるコンテナを先に起動します。
docker compose stopの際は依存するサービスの前に、設定したサービスを停止します。
command コマンドを記載すると、Dockerfileに記載されているコマンドを上書きしてコマンド実行します。
healthcheck コンテナが正常かどうか判断するために実行するコマンドを記載します。
確認の間隔や試行回数など細かく設定することが可能です。

それ以外にも便利な設定が多く存在するのでリファレンスで確認してみてください。

docs.docker.jp

Laravel Breezeインストールで追加されるファイルがLarastanに引っかかる

はじめに

会社の自社製品を作ろうとなり、その土台を任されたので土台をLaravelにしました。

開発体験をよく出来たらと、そこに認証機能のBreezeと静的解析ツールのLarastanを導入しましたが、そこでしょっぱなからエラーに遭遇しました。

日本語記事も出てこなかったため、書いておきます。
結論Illuminate\Contracts\Auth\MustVerifyEmailというインターフェイスをUserモデルで実装しろっていう話ですね。

環境

Laravel v10.28.0 Laravel Breeze v1.26.1 larastan v2.6.4

エラー文

App/Http/Controllers/Auth/VerifyEmailController.php::23行目
Parameter #1 $user of class Illuminate\Auth\Events\Verified constructor expects Illuminate\Contracts\Auth\MustVerifyEmail, App\Models\User|null given.    

解決策

App/Models/User.php

+ use Illuminate\Contracts\Auth\MustVerifyEmail;

- class User extends Authenticatable
+ class User extends Authenticatable implements MustVerifyEmail

あとがき

メールでのアカウント確認が不要ならそのファイルと関連の記述を削除すればよいかと思いますね。

ほかにもBreezeインストールで実装されたcomponentsたちが使われていないというようなエラーとかも出ているので、 解決したら追記します。

よく使うLinux コマンドまとめ

はじめに

Web開発を行う上でLinuxは避けては通れないものです。

知識の定着のため、自分なりの言葉でよく使うコマンドを一覧化しておきます。

一覧

[]で囲んているものは任意です。 囲んでいないものは必須の内容です。

コマンド 構文 説明
cd cd パス Change Directoryの略。
ターミナル上での現在地(ディレクトリ)を変更する。
pwd pwd [オプション]... Print name of Working/(current) Directoryの略。
ターミナル上での現在地(ディレクトリ)がどこなのかを出力する。
ls ls [オプション]... ファイル名やディレクトリ名... List Segmentsの略。
ディレクトリやファイルの一覧を出力する。
オプションを付けない場合は隠しファイルは出力されない。
よく使うオプションは
-a:すべてのファイル、ディレクトリを表示する。
-l:ディレクトリやファイルに設定されている権限や所有者などが表示される。
-t:新しい順にソートして出力する。-lと併用することが多い。
mkdir mkdir [オプション]... ディレクトリ名... Make Directoriesの略。
現在のディレクトリにディレクトリを作成する。
rmdir rmdir [オプション]... ディレクトリ名 Remove Empty Directoriesの略。
空のディレクトリを削除する。
cat cat [オプション]... ファイル... Concatenate(連結するを意味する語)の略。
ファイルの中身と標準出力を連結する=ファイルの中身を出力するということ。
less less [オプション] ファイル... moreコマンドの対義をなすもの。
moreとほとんど同じくファイルの中身を出力するのだが、moreはファイル終了後にEnterもしくはSpaceキーを押すことで自動的にプロダクトに戻る。lessはqキーを押さないと戻らない。
Spaceキーを押すことで次のページを開くようにファイルの中身を次の行から始まるように表示してくれる。
tail tail [オプション]... ファイル... ファイルの末尾(しっぽ)を10行(デフォルト)表示する。
複数ファイルの末尾を表示したい場合は半角スペースでつなげてファイル名を記述する。
-n:その後に記述した行数表示してくれる。
touch touch [オプション]... ファイル... ファイルのタイムスタンプ(アクセス日時、更新日時)を更新するコマンド
存在しないファイル名を指定すると空ファイルを作成する。
からファイル作成のために使うことが多い。
rm rm [オプション]… ファイル… Remove Directory Entriesの意。
ディレクトリ内の要素を削除する。
-r: ディレクトリを再起的に削除する。
-f: 存在しないファイルを無視する。確認もしない。
-i: 削除前に確認する。
mv mv [オプション]… パス(移動前)… パス(移動先) moveの略。
ファイルやディレクトリを移動させる。
また、ファイルやディレクトリの名前を変更することもできる。
-b: 上書きするファイルのバックアップを取る。
-n: 移動先に同じ名前のものがある場合移動しない。
cp cp [オプション]... パス(コピー元)... パス(コピー先) copyの略。
ファイルやディレクトリをコピーする。
mvとは違い、存在しない名称を入力した時に新規ファイルなどが作られない。
-n: 存在するファイルを上書きしない。
-f: 強制的に上書きする。
-i: 上書きする前に確認する。
ln ln [オプション]... ファイル リンク名 make links between filesの意。
ファイルのリンクを作成する。
ハードリンク:存在するファイルを参照するリンクのこと。
シンボリックリンク:ファイルやディレクトリを参照するリンク。
-s: シンボリックリンクの生成
-f: 登録済みのリンクであっても上書き登録する。
-i: 登録済みリンクへ上書きする場合確認する。
find find [検索する場所] [判別式/演算子] ファイル名 ファイルやディレクトリを検索する。
-name: ファイル名での検索ができる。ワイルドカードが使用可能。
-atime: 任意の日数にアクセスされたファイルやディレクトリを検索する。
-mtime: 任意の日数に更新されたファイルやディレクトリを検索する。
-empty: ファイル容量が0のファイルやディレクトリを検索する。
-type f: ファイルを対象とする。
-type d: ディレクトリを対象とする。
grep grep [オプション]... 検索したいパターン [ファイル名] パターンにマッチしたラインを出力する。
-i: 大文字小文字の区別をしない。
-n :検索結果に行番号を表示する。
-r :ディレクトリ内も検索対象とする。
chmod chmod [オプション]... 権限 対象のファイル ファイルやディレクトリの権限を設定する。
権限は3桁の数字もしくは記号で設定する。
数字で設定する場合、1桁目:管理者ユーザー 2桁目:所有者/所有グループ 3桁目:その他 となっている。
各最大値は7。読み取り(r):4、書き込み(w):2、実行(x):1の和。
記号で設定する場合、所有者:u、グループ:g、その他のユーザー:o、すべてのユーザー:aに対し、上記r、w、xを=で繋ぐ。
-R :再帰的に設定する。
chown chown [オプション]... ユーザー[:グループ] 対象ファイル ファイルの所有者や所有グループを変更する。
-R :再帰的に設定する。
ps ps [オプション]... 現在のプロセス一覧を出力する。
-A: すべてのプロセスを出力する。
a: 端末操作のプロセスを他ユーザーのものも含めて表示する。
u: ユーザー名や開始時刻、CPUやメモリの使用率などを表示する。
x: 端末操作以外の全てのプロセスも表示する。
kill kill [オプション]... プロセスID プロセスを終了させる。
-l: シグナルの一覧を表示する。
-[シグナルID]: シグナルの種類を指定する。