2024/06 CTOへの日報まとめ

日報




2024-6-3 ①

docker-composeで標準入力を使用する方法を知ることができました。

  • 前提として「docker-composeで標準入力を使用する」とは、コンテナが実行されている間に、ユーザーが直接そのコンテナに対してキーボードから入力を行うこと
  • これは、通常のターミナルセッションと同様に、コンテナ内のアプリケーションがユーザーからの入力を受け取り、それに応じて動作することを意味する

標準入力を使用することで、以下のことが可能になる

対話型操作:コンテナ内で実行される対話型シェル(bashやzsh等)にアクセスし、直接コマンドを入力して実行することができる

入力待ちのプログラム操作:コンテナ内で実行されるプログラムがユーザーからの入力を必要とする場合、その入力を直接コンテナに対して行うことができる

テスト・デバッグ:開発中やデバッグ中に、コンテナ内で動作するアプリケーションに対して直接入力を行うことで、その動作を確認したり、エラーを追跡したりすることが可能

これを実現するためには、docker-compose.ymlで以下のような設定を行う
version: ‘3.8’
services:
  app:
    image: myapp:latest
    stdin_open: true # 標準入力を開く
    tty: true # TTYを有効にする

stdin_open: true
標準入力を有効にするための設定。これにより、コンテナが標準入力を受け付けるようになる

tty: true
擬似ターミナル(TTY)を有効にする設定で、対話型セッションを可能にする

2024-6-3 ②

ブランチ名に特殊文字が含まれていて、シェルがそれを正しく認識できず、リモートからブランチを引っ張って来れない場合の対処法を知ることができました。ブランチ名をクォートで囲むと、シェルがブランチ名を正しく認識することができ、ローカルにpullできました。

git checkout -b “[新しいローカルブランチ名]” “origin/[リモートブランチ名]”

2024-6-4 ①

docker-composeで標準入力を使用するために必要なdocker-compose.ymlへの記述の1つである「tty: true」に関して、そもそもttyとは何か知らなかったので調べました。

  • TTYとは、「Teletype」の略
  • 元々は初期の電信端末(テレタイプ端末)を指していた
  • これらの端末は、遠隔地にあるコンピュータとやり取りするために使用され、キーボードとプリンターを組み合わせたようなものだった

しかし、現代のコンピュータシステムでは、TTYは以下のような意味合いで使用される

仮想端末

  • 現在のコンピュータやOSでは、物理的なテレタイプ端末の代わりに、仮想的な端末を指す
  • これにより、ユーザーはターミナルエミュレーター(例えば、Linuxの/dev/ttyデバイスファイル)を通じてシステムと対話することが可能

対話型セッション

  • 仮想端末は、ユーザーがシステムと対話するための手段を提供する
  • 対話型シェルを通じて、ユーザーはコマンドを入力し、その結果を即座に確認することができる

Dockerにおけるtty: true設定は、コンテナがこの仮想端末を使用できるようにし、対話型セッションを可能にする。これにより、コンテナ内で対話型の操作が行えるようになる。

まとめると、TTYは元々テレタイプ端末を指す略称でしたが、現代では仮想端末や対話型セッションの設定を意味することが分かりました。

2024-6-4 ②

FuelのCookieクラスのset関数の第3引数には、クッキーの有効期限(秒)を設定できると思いますが、ここに0を指定すると、セッションクッキーとして扱われることを知りました。

2024-6-5 ①

Docker Engineが内部的に3つのソフトウェアで構成されていることを知りました。

1. Docker Server(Docker Daemon)

  • コンテナ型仮想化技術を実現するための常駐アプリケーション(dockerdというアプリケーションが常駐している)
  • コンテナの実行や管理、イメージの管理、ネットワークの設定など、dockerの主要な機能を実行する中核となるソフトウェア

2. Docker Rest API

  • Docker Serverと通信するためのインターフェース
  • Docker Serverの直接操作は敷居が高いため、操作をRest API経由で代行している
  • Docker CLIや他のツールがこのAPIを使用して、Docker Daemonにリクエストを送信する

3. Docker CLI

  • ユーザーがコマンドを入力してDockerを操作するためのツール
  • ユーザーからの入力を受け取り、それをDocker REST APIに送信し、最終的にDocker Daemonにリクエストを伝える

これら3つのソフトウェアで構成されるのが「Docker Engine」であり、シンプルに「Docker」と呼ばれることもある。

2024-6-5 ②

JSのfetch APIが提供しているfetchメソッドの返り値 Responceオブジェクトのokプロパティが、レスポンスが成功したか否か(ステータスコードは200番台か否か)を示す論理値を扱っていることを知りました。今後、fetchメソッドを使ってajax通信を行う際には、okプロパティに限らず、有用そうなプロパティは積極的に使用していければと思います

https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch
https://developer.mozilla.org/ja/docs/Web/API/Response

2024-6-6

PHPを用いた「クッキーに保存するjsonデータのサイズを小さくする方法」を1つ知ることができました。

  • gzcompress関数でjsonデータを圧縮する
  • 圧縮後データをbase64エンコードして、Cookieにセット可能な形式に変換する

単純にJSON形式のデータをBase64にエンコードした場合、サイズは小さくなるどころか、むしろ約33%サイズを増加させるみたいです。これは、Base64エンコードが3バイトのデータを4文字のASCII文字列に変換するためとのことです。

ただし、上記の方法では、gzcompress関数で圧縮したjsonデータをBase64エンコードしているため、base64エンコードによりデータサイズが増加しても、元の圧縮によるサイズ削減効果と比較して、全体としてはデータサイズが削減されることが期待されるみたいです。

他にもCookieに入れる値のサイズを削減する方法はあるかと思いますが、上記の方法を1つ頭に入れておきたいと思います。

2024-6-10 ①

dockerのポートフォワーディング機能により、ホスト側のポートとコンテナ側のポートをマッピングする際に、ホスト側のポートの指定を省略できることを知りました。

docker container run -d -p 8080 image

  • 省略した場合は、ホスト側のエフェメラルポート(一時的な通信のために自由に利用できるポート。多くのLinuxカーネルでは32768から61000が利用される)で空いているポートが自動的に割り当てられる
  • どのポートが割り当てられたかは、docker container lsの結果のPORTSで確認可能
2024-6-10 ②

ALBがターゲットグループ内のターゲットに対してヘルスチェックを行う際に、その健全性の判断基準として、ターゲットからの応答ステータスが1つ存在することを知りました。

2024-6-11

dockerイメージをビルドする際に、—pull=trueを付与してビルドすると、ローカルにベースイメージが存在している場合にも、毎度レジストリに最新のイメージを取得しにいくことが分かりました。これはイメージビルド時に、毎回最新のベースイメージを使用したい場合に指定すべきオプションとのことでした。

docker image build —pull=true .

2024-6-12

docker image tagコマンドについて知見を深めることができました

これまでは「タグを追加するコマンド」という認識だったため、名前空間とイメージ名を変更することでエイリアスを付与することはできないと認識していましたが、どうやらできるようです。

docker image tag oldnamespace/myapp1:latest newnamespace/myapp2:stable

Docker HubやGHCR(GitHub Container Registry)にイメージをpushする際には、それぞれイメージ名を規定の形式に修正しpushする必要がありますが、名前空間, イメージ名, タグ名全てに対してエイリアスを付与することができることを知り、レジストリ毎に別名でイメージ本体を複製する必要がなく、管理コストを低く抑えられることを知り、少し嬉しくなりました。

2024-6-13

docker container ls の結果で、特定の条件に一致するものだけを抽出するには、—filterオプションを利用すると良いことが分かりました。

docker container ls —filter “filter名=値”

(例)コンテナ名で抽出する場合
docker container ls —filter “name=containername1”

(例)イメージで抽出する場合
ancestorは「祖先」と表す英単語であり、その名の通り、コンテナにとっての祖先であるイメージを指定する
docker container ls —filter “ancestor=imagename1”

(例)状態で抽出する場合
docker container ls —filter “status=exited”

https://docs.docker.com/reference/cli/docker/container/ls/#filter

2024-6-14 ①

「docker container logsコンテナIDまたはコンテナ名」で、特定のコンテナのログ(標準ストリーム出力、つまり標準出力と標準エラー出力)を取得&表示できますが、このコマンドに-fオプションをつけると、tail -fのように、ログをリアルタイムで取得し表示し続けることができることを知りました。

docker container logs -f コンテナIDまたはコンテナ名

2024-6-14 ②

JSからvalueの値を取るときに、$(‘[name=”job_fot_exists”]:checked’).val() のようなコードにより、選択されている要素のvalueを取ることが通常は多いですが、仮に $(‘[name=”job_fot_exists”]’).val() といった形で :checked を外した場合、かつname = “job_fot_exists” の要素が複数存在する場合は、name属性がjob_fot_existsの要素のうち、1番目の要素のvalueが取得されることを知りました。

2024-6-17

dockerコンテナにクレデンシャル情報を渡す場合(例、MySQLコンテナにrootユーザーのパスワードを渡す場合)、下記2つの方法があるかと思います。

1、コマンドライン引数で渡す
 docker container run -d —rm —name mysql \
 -e “MYSQL_ROOT_PASSWORD=root_password” \
 -e “MYSQL_DATABASE=test” \
 mysql:8.0.33

2、環境変数に設定して渡す(docker-compose.yml)

version: “3.9”
services:
  mysql:
    container_name: mysql
    image: mysql:8.0.33
    environment:
      MYSQL_ROOT_PASSWORD: root_password
      MYSQL_DATABASE: test

ただし、コマンドライン引数で渡す場合、コマンド実行時に毎回入力が増えて面倒です。また環境変数に設定して渡す場合、このdocker-compose.ymlをGitHub等のバージョン管理システムにおいておくことで、いくらプライベートリポジトリであったとしても、人為的なミスによるパブリックな場所への公開や、マルウェアによる漏洩、またリポジトリへのアクセス権限を与えたサードパーティ製ツールやサービスからの漏洩など、一定数脅威は存在します。

そこで、コンテナの外からクレデンシャルを安全に渡すためのsecretsという仕組みについて知ることができました。

# docker-compose.yml
version: “3.9”
services:
  mysql:
    container_name: mysql
    image: mysql:8.0.33
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password ## ①
      MYSQL_DATABASE: test
    secrets: ## ②
      – mysql_root_password
secrets: ## ③
  mysql_root_password: ## 任意のシークレット名
    file: ./mysql_root_password

トップレベルで定義されたsecrets要素(③)では、コンテナの外のファイルシステム(つまりホスト環境)からクレデンシャルを読み取ることができる。

②にて、③で定義したシークレットをコンテナにマウント。ここでマウントされたシークレットは、コンテナ内部では/run/secretsディレクトリに配置される。

①にて、MYSQL_ROOT_PASSWORD_FILE環境変数を使用し、ホストから取得したクレデンシャルが記載されたファイルを指定する

ホストの./mysql_root_passwordをgitの管理外にすることで、バージョン管理システムに置く必要がなくなる

クレデンシャルを直接取り回すのではなく、ファイルパスベースで扱うことで、よりセキュアに機密情報を扱えることを知りました。

2024-6-18 ①

.dockerignoreファイルが活きる場面を知ることができました。

  • COPY [ホスト側のディレクトリ] [コンテナ側のディレクトリ] で、ホスト側のディレクトリを丸ごとコンテナにコピーする際に、余分なファイルも含めてしまいがち
  • このとき、余分なファイルを取り除くために、.dockerignoreファイルを使用すると良い
  • 例えば、ホストのディレクトリにdockerfileが含まれている場合、dockerfileはコンテナ内では不要なため、.dockerignoreに記述することで対象から外す
  • .dockerignoreは、ワイルドカードの記述は可能だが、.gitignoreのような正規表現の記述はできないため、その点は注意
2024-6-18 ②

file_get_contents(‘php://input’) に関して

file_get_contentsは、実際にはファイルから内容を読み取る関数だと思いますが、特殊な「仮想ファイル」である php://input を指定することで、HTTPリクエストのボディを直接取得できることを知りました。また取得の際には、HTTPリクエストボディの生データ(raw data)を取得できることも知りました。

2024-6-19

MonorepoとPolyrepoについて知ることができました。

MonorepoとPolyrepoとは、ソフトウェア開発におけるリポジトリ管理のアプローチの一つ。

Monorepo
Monorepo(モノレポ)とは、すべてのプロジェクトやコードベースを単一のリポジトリにまとめて管理するアプローチ

特徴

  • 単一のリポジトリにすべてのコードが存在する
  • プロジェクト間の依存関係が明確で、共通コードの再利用が容易
  • リポジトリ全体を通じて一貫したバージョン管理が可能

利点

  • 統一された環境:開発者は単一のリポジトリをクローンするだけで、すべてのプロジェクトにアクセスできる
  • 変更のトラッキングが簡単:コードベース全体にわたる変更を一元管理できる
  • CI/CDの効率化:一つのCI/CDパイプラインで複数のプロジェクトをビルド・テストできる

欠点

  • スケーラビリティの問題:リポジトリが大規模になると、クローンやビルドの時間が増加する
  • コンフリクトの増加:多くの開発者が同じリポジトリで作業するため、変更の競合が発生しやすい
  • 権限管理の複雑化:すべてのプロジェクトが一つのリポジトリにあるため、権限の管理が難しくなる場合がある

Polyrepo
Polyrepo(ポリレポ)とは、各プロジェクトやコンポーネントを個別のリポジトリに分けて管理するアプローチ

特徴

  • 複数のリポジトリにプロジェクトが分散している
  • 各プロジェクトが独立してバージョン管理される
  • プロジェクト間の依存関係は外部リポジトリとして管理されることが多い

利点

  • スケーラビリティ:各リポジトリが独立しているため、大規模なコードベースでも効率的に管理できる
  • 権限管理の簡素化:プロジェクトごとに異なるアクセス権を設定できる
  • 独立したリリースサイクル:各プロジェクトが独立してリリースされるため、他のプロジェクトの影響を受けにくい

欠点

  • 依存関係の管理が難しい:プロジェクト間の依存関係を管理するのが複雑になる
  • 統一されたビューがない:全体の変更履歴や状況を一目で把握するのが難しい
  • CI/CDの設定が複雑:複数のリポジトリごとにCI/CDパイプラインを設定する必要がある
2024-6-20 ①

アーティファクトという用語について知ることができました。

  • アーティファクトとは、アプリケーションがビルドされ、実行, 配布可能な状態になったもの
  • 実行ファイルやアーカイブされたzipファイル、広義の意味ではコンテナイメージもアーティファクトに該当する
2024-6-20 ②

今更かもしれませんが、vscodeの正規表現を使った置換機能により、指定した文字の前で一括改行する方法を知ることができました。

{の前で改行する例
・検索文字列:\{
・置換文字列:\n{

2024-6-21 ①

GitHub Actionsで定義したワークフローのトリガー条件として、pull_requestを指定することができますが、ここから更にトリガー条件を絞れることを知りました。下記のように定義することで、PRが作成または再オープンされたときのみワークフローをトリガーし、PRの更新時はトリガーしないようにすることができるみたいです。特定条件下に限って実行したいワークフローがある場合には、このように条件を絞れば良いことを知りました。

on:
  pull_request:
    types: [opened, reopened]

2024-6-21 ②

本日改めて、PHPのショートサーキット評価を意識したコーディングをする必要性を感じました。

下記のような順番で条件を定義していたため、$headers内にX-Signatureキーが存在しない場合に、Undefined indexが出力されました。

if ($signature != $headers[‘X-Signature’] || empty($headers[‘X-Signature’])) :

そこで、条件の定義順を反対にしました。これにより、1つ目の条件が成立した場合には、2つ目の条件の判定処理すら走らず分岐の中に入る「PHPのショートサーキット評価の挙動」により、Undefined indexの出力を防ぎつつ、実現したかった条件を実現することができました。

if (empty($headers[‘X-Signature’]) || $signature != $headers[‘X-Signature’]) :

今後、or条件を用いる際には、各条件の定義順序に気を付けたいと思います

2024-6-24

GitHub Actionsにて、任意の再利用可能なカスタムアクションを定義し、そのカスタムアクションをワークフローから呼び出す方法を知りました。

  • ワークフロー定義ファイルは、リポジトリ内の .github/workflows 以下にyamlで配置する
  • これと同じように、カスタムアクションに関しても、.github/actions 以下に、アクションが定義されたyamlを含むディレクトリとして配置する

ディレクトリ構造例

.github/
├── workflows/
│ └── main.yml
└── actions/
└── my-action/ (カスタムアクション毎にディレクトリを作成)
├── action.yml
└── script.sh(必要に応じて追加)

action.ymlで、カスタムアクションを登録

(省略)
steps:
- run: echo "Hello from custom action with input ${{ inputs.my-input }}"
shell: bash
(省略)

ワークフロー定義ファイルから、カスタムアクションを呼び出す

(省略)
- name: Use custom action
uses: ./.github/actions/my-action (プロジェクトのルートディレクトリからの相対パスで指定)
with:
my-input: "Hello World"
(省略)

出力:Hello from custom action with input Hello World

2024-6-25

CIでは、ビルドやテスト、セキュリティ(脆弱性の有無)や構文(コーディング規約の遵守)の確認が行われ、マージ前の品質の保証が行われると思います。またCDでは、ビルドとテスト、そしてテストに合格した場合は自動デプロイが行われると思います。

そこで、両者で行うテストでは、それぞれどのようなテストを行うことが多いのか、また良いとされているのか、気になったので調べてみました。

  • CIとCDの両者で行われるテストでは、それぞれ内容や対象範囲が異なることが多い

CIでは、主に以下のようなテストが行われる

1. ユニットテスト

  • 各モジュールや関数単位でのテスト
  • 個々のコンポーネントが正しく機能するかを確認

2. インテグレーションテスト

  • 複数のモジュールが連携して正しく動作するかを確認
  • 変更が他の部分に悪影響を与えていないかをチェック

3. 静的解析とリンティング

  • コードの品質とコーディング規約の遵守を確認
  • 潜在的なバグやパフォーマンス問題を検出

CDでは、デプロイ前により広範なテストが行われる

1. エンドツーエンド(E2E)テスト

  • 実際のユーザーの操作をシミュレートし、アプリケーション全体が期待通りに動作するかを確認
  • フロントエンドとバックエンドの統合を含むシナリオテスト

2. パフォーマンステスト

  • アプリケーションの応答時間やスループットを評価
  • 負荷テストやストレステストを行い、システムの限界を確認

3. セキュリティテスト

  • 脆弱性スキャンやペネトレーションテストを実施
  • デプロイ後のセキュリティリスクを最小化

4. 受け入れテスト

  • 最終的なビジネス要件や仕様に基づいたテスト
  • ユーザーストーリーに基づいたテストケースを実行

CIとCDのプロセスで行うテストが重複することもあるが、以下の戦略を取ることで効率的かつ効果的にテストを実施することができる

1. テストのスコープの分離

  • CIでは、変更のあったコード部分や影響範囲を重点的にテスト
  • CDでは、リポジトリ全体を対象とした広範なテストを実施

2. テストの種類の分離

  • CIでは、ユニットテストやインテグレーションテストなど、小規模かつ高速なテストを中心に行う
  • CDでは、E2Eテストやパフォーマンステストなど、大規模で時間のかかるテストを中心に行う

3. テスト結果のキャッシュと再利用

  • CIで実行したテスト結果をキャッシュし、CDでも再利用可能な部分は再利用する
  • これにより、重複するテストの実行時間を削減

CIとCDの両方でビルドとテストを行うことで、コードの品質を多角的に保証することが可能。CIは開発サイクル中の頻繁な変更に対する迅速なフィードバックを提供し、CDはリリース前に広範な検証を行うことで、本番環境へのデプロイの信頼性を高める。それぞれのプロセスで適切なテストを実行し、効率と効果を最大化することが重要。

2024-6-26 ①

GitHub Actions で定義したワークフローにて、機密情報を扱う方法の1つとして、GitHubのシークレットを使用した方法について知ることができました。

  • GitHubのリポジトリ設定ページで「シークレット」と呼ばれる機密情報を登録する
  • シークレットとは、APIトークン、パスワード、認証情報などの機密データを安全に管理するためのもの

詳細

  • GitHubリポジトリのトップページ > Settings > Secrets and variables > Actions > New repository secret > Add secret
  • 下記のように、GitHub Actions ワークフローから呼び出す(下記はCDのワークフロー例)


name: Deploy Workflow

on:
push:
branches:
- main

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Install dependencies
run: npm install
- name: Build project
run: npm run build
- name: Deploy
run: ./deploy.sh
env:
API_TOKEN: ${{ secrets.API_TOKEN }}

非常にシンプルに、GitHub Actions ワークフロー内で機密情報を使用することができる方法であり、とても良い機能だと思いました。

2024-6-26 ②

クリティカルセクションという用語について知ることができました。
https://e-words.jp/w/%E3%82%AF%E3%83%AA%E3%83%86%E3%82%A3%E3%82%AB%E3%83%AB%E3%82%BB%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3.html

2024-6-27 ①

DockerfileとDockerfile.slimの違いを理解することができました。

DockerfileとDockerfile.slimの違いは、主にイメージサイズや依存関係に関するもの

Dockerfile
Dockerfileは、Dockerイメージをビルドするための標準的な設定ファイル。通常、このファイルにはアプリケーションを動作させるために必要なすべての依存関係やツールが含まれている。

特徴

  • 完全な環境:アプリケーションを実行するために必要なすべてのツールや依存関係が含まれる
  • 開発とデバッグが容易:フルスタックの環境が提供されるため、開発者は開発とデバッグを容易に行える
  • サイズが大きい:多くのツールや依存関係が含まれるため、イメージサイズが大きくなる傾向がある

使用例(dockerfile)

FROM python:3.8
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
CMD ["python", "app.py"]

Dockerfile.slim
Dockerfile.slimは、通常のDockerfileから不要な部分を削減した、より軽量なバージョン。このファイルは本番環境での利用を念頭に置いており、最低限必要な依存関係とツールだけを含むように設計されている

特徴

  • 最小限の環境:不要なツールや依存関係を省き、必要最低限のものだけを含む
  • サイズが小さい:イメージサイズが小さいため、ダウンロードやデプロイが高速
  • セキュリティの向上:不要なツールやライブラリが含まれないため、攻撃対象が減少する
  • 開発やデバッグが難しい:必要最低限の環境しか含まれないため、デバッグが難しくなることがある

使用例(dockerfile)

FROM python:3.8-slim
COPY . /app
WORKDIR /app
RUN pip install --no-cache-dir -r requirements.txt
CMD ["python", "app.py"]

主な違い
サイズ

  • Dockerfile:フルスタックの環境であるため、イメージサイズが大きくなる
  • Dockerfile.slim:不要な部分を削減しているため、イメージサイズが小さい

依存関係とツール

  • Dockerfile:アプリケーションの開発やデバッグに必要なすべてのツールやライブラリを含む
  • Dockerfile.slim:プロダクション環境で必要最低限のツールやライブラリのみを含む

パフォーマンスとセキュリティ

  • Dockerfile:開発の便宜を図るために多くのツールが含まれ、その結果セキュリティリスクも高まる可能性がある
  • Dockerfile.slim:不要なツールを排除することで、セキュリティリスクが低減し、デプロイメントが高速になる

両者の選択は、開発のフェーズや目的に応じて異なる。開発やテスト環境ではフルスタックのDockerfileを使用し、プロダクション環境では軽量なDockerfile.slimを使用するのが一般的なアプローチである。

2024-6-27 ②

画像の種類を判別できるPHPのexif_imagetype関数について知ることができました

2024-6-28 ①

dockerfileに記述するCOPYインストラクションとADDインストラクションは、両者ともにホストからコンテナにファイルをコピーする命令ですが、両者の違いについて知ることができました。

  • COPYは単純なコピーであるのに対して、ADDではコピーに加えて、コピーしたアーカイブをファイルシステム上に展開まで行える
  • つまり、展開が必要なファイルをコンテナ内に持っていく際には、COPYではなくADDを使うべき
2024-6-28 ②

PHPのarray_key_first関数とarray_key_last関数について知ることができました。