次の記事が分かりやすく纏まっている。
GitHub Actionsでワークフローと同じリポジトリにあるComposite Actionを利用する
はじめに
GitHub Actionのワークフローの一部を再利用したくて、いくつかのステップをComposite Actionとして利用できるように切り出した。
その際に、uses
の指定方法を見つけるのに苦労したので、メモしておく。
uses
の記載方法
ドキュメントの以下に記載があった。
以下にドキュメントからYAMLの記述例を抜粋する。
jobs: my_first_job: steps: - name: Check out repository uses: actions/checkout@v3 - name: Use local my-action uses: ./.github/actions/my-action
参考
アクションの配置場所
ドキュメントには明確には記載されていないが、YAMLの記述例を参考にすると.github/actions
に配置するのがよさそう。
PHPのデバッグ
PHPは殆ど触れたことがないが、諸事情でデバッグすることになった。 PHPに標準的なログ出力の機能があるのかどうか(そして当該Webアプリで使っているのかどうか)は、よく分からない。 とにかく、エラーが発生したコンテキストが分かればいい。
デバッグ対象のWebアプリはException
を握りつぶすような実装になっているので、catch句のところにfile_put_contents()
の呼び出しを加えて、例外の情報を独自にファイル出力するようにした。
catch(Exception $e) {
file_put_contents('/tmp/log.txt', $e, FILE_APPEND | LOCK_EX);
}
systemdの管理下にあるApache HTTPDでファイル出力したためか、ログは/tmp/log.txt
には出力されず、/tmp/systemd-private-f7be22cd34b84aeea9431ded8ca53541-php-fpm.service-8WLt2f/tmp/log.txt
というファイルに出力されていた。
このファイルの内容を確認して、何とか原因の特定に成功した。
ちなみに、Exception
には__toString()
というメソッドが定義されていて、これはJavaでいうtoString()
やPythonでいう__str__()
にあたるもののように見えた。
なので、文字列が期待される場所で例外を引き渡すと、自動的に文字列化してくれるのではと推測する。
参考
Docker Composeの仕様
Docker Composeの仕様は、Compose Specとして以下の場所で標準化されている。
Compose Specの実装はDocker Compose、Kompose、Nerdctlなど幾つかあり、その中でもDocker ComposeはReference Implementation (参照実装) という位置づけのようだ。
Docker ComposeのマニュアルはSwarm modeなどの拡張にも言及していて、素のDocker Composeを使いたいときはマニュアルとしては少し読みづらいので、そういった場合はCompose Specを参照するのがよいのかも。
コンテナで簡易NFSサーバを構築
はじめに
評価用にNFSサーバが必要になったため、コンテナで構築できないか調べてみた。
環境
$ cat /etc/os-release | grep PRETTY_NAME PRETTY_NAME="Ubuntu 22.04 LTS" $ uname -srvm Linux 5.15.0-25-generic #25-Ubuntu SMP Wed Mar 30 15:54:22 UTC 2022 x86_64 $ docker version Client: Docker Engine - Community Version: 20.10.14 (略) Server: Docker Engine - Community Engine: Version: 20.10.14 (略)
利用したコンテナイメージ
DockerHubで公開されている以下を利用。
更新が2019年と古いが、NFSは枯れた技術なので数年前のものでも機能的には問題ないはず。
NFSサーバの構築
(1) ディレクトリの準備
まず、今回の調査の作業用として、ホスト側に以下のディレクトリを準備した。
/ `-- nfs `-- nfs-root
ホスト側の/nfs/nfs-root
を、コンテナ側の/export
にバインドマウントし、これをNFSで共有する。
ホスト側の/nfs
は、後述するdocker-compose.yml
の配置場所にしている。
ディレクトリの作成のために、以下のコマンドを実行する。
$ sudo mkdir -p /nfs/nfs-root
また、後ほどNFSマウントができたかどうかを確認するために、/nfs/nfs-root
の中にファイルを一つ作成しておく。
$ sudo echo "hello, world!" > /nfs/nfs-root/greeting.txt
(2) docker-compose.ymlの作成
以下の内容でdocker-compose.yml
を作成し、/nfs
に配置しておく。
一部ドキュメントどおりでは動かなかった部分があり手を加えているが、詳細は後述する。
version: "3.8" services: "nfs-server": image: erichough/nfs-server privileged: true # cap_add: # - SYS_ADMIN # - SYS_MODULE ports: - "2049:2049" environment: NFS_EXPORT_0: "/export *(rw,sync,all_squash,no_subtree_check,fsid=0)" volumes: - /nfs/nfs-root:/export - /lib/modules:/lib/modules:ro
(3) NFSサーバの起動
以下のコマンドを実行して、NFSサーバのコンテナを起動する。
$ cd /nfs $ docker-compose up -d
動作確認
(1) ホストからマウント
適当なディレクトリ/nfs-test
を作成して、マウントを試してみる。
$ sudo apt update $ sudo apt install nfs-common $ sudo mkdir /nfs-test $ sudo mount -v -t nfs4 [ホストマシンのIP]:/ /nfs-test
マウントに成功すると、以下のようにgreeting.txt
を読み込むことができる。
$ cat /nfs-test/greeting.txt hello, world!
(2) コンテナからマウント
まずはUbuntuのコンテナを起動する。
ネットワークはDocker Composeが作成したnfs_default
を利用する。
$ docker run -it --privileged --net nfs_default ubuntu:latest /bin/bash
コンテナ起動後、コンテナの中に先ほどと同様に適当なディレクトリ/nfs-test
を作成して、マウントを試してみる。
$ apt update $ apt install nfs-common $ mkdir /nfs-test $ mount -v -t nfs4 nfs-server:/ /nfs-test
マウントに成功すると、以下のようにgreeting.txt
を読み込むことができる。
$ cat /nfs-test/greeting.txt hello, world!
ちなみに、コンテナ起動時にオプション--privileged
を付与しておかないと、以下のようなエラーが出てマウントに失敗する。
$ mount -t nfs4 nfs-server:/ /mnt mount.nfs4: Operation not permitted
参考
ドキュメントどおりでは動かなかった部分
コンテナイメージerichough/nfs-server
のドキュメントは、ドキュメント内のリンクの都合上、DockerHubよりもGitHubの方を見たほうがよい。
主に次の2つのドキュメントを参照した。
ここで、コンテナに付与する権限について--privileged
ではなく--cap-add
を推奨しているが、ドキュメントどおりにSYS_ADMIN
とSYS_MODULE
を付与しても権限が足りずに起動に失敗する。
他に何が必要なのかを調べる時間がないので、--privileged
を利用した。
クライアント側でも--privileged
がないとマウントに失敗するので、NFSはLinuxカーネルと密結合しているように思う。
NFSのバージョンについて
NFS v3はRPCやロックなどの様々なサービス(デーモン)を動的に組み合わせて実現しているため、設定が複雑で単にコンテナのポートをホスト側で公開するだけでは動かない。 そのため、コンテナでのNFS v3サーバの構築はハードルが高い。
一方、NFS v4はその辺りが改善されていて、ポート2049にアクセスできればNFSの利用が可能であり、コンテナでのNFS v4サーバの構築は比較的ハードルが低い。
とはいえ、--privileged
等を利用した権限付与が必要であり、コンテナを運用する際のセキュリティポリシー次第では、NFS v4は利用できない可能性がある。
複数のGitのユーザを使い分ける
はじめに
複数のGitサーバ/サービスを利用する場合、Gitのユーザの切り替えが必要になる。 適切に切り替えを行わないと、誤ったユーザでコミットしてしまって、履歴を汚してしまう。 できるだけこの失敗を防ぐために、やっていることのメモを残しておく。
ユーザをグローバルに設定しない
あえてグローバルにユーザを設定せず、リポジトリごとにローカルにユーザを設定する。 これで、「デフォルト(=グローバルな設定)のユーザでコミットしてしまった」というミスは防げる。
示すまでもないが、各リポジトリ内で以下を設定することになる。
$ git config --local user.name "jaybanuan" $ git config --local user.email ”jaybanuan@example.com”
Bashのプロンプトにユーザ名を表示する
カレントディレクトリのリポジトリにどのユーザが設定されているかを、Bashのプロンプトに表示しておく。 これで、「想定とは違うユーザでコミットしてしまった」というミスを減らすことができる。
やりたいことを具体的に説明すると、通常は以下のようなプロンプトだが、
jaybanuan@devpc:~/src $
以下のようにgit clone
したリポジトリに移動すると、
jaybanuan@devpc:~/src $ git clone https://host/path/to/myapp.git jaybanuan@devpc:~/src $ cd myapp
以下のように、末尾にgitのユーザ名@ブランチ名
が付加されたプロンプトに変える。
jaybanuan@devpc:~/src/myapp (jaybanuan@main)$
ユーザ名が設定されていない場合は以下のようにNO-USER-NAME
と表示し、git config
でユーザ名を設定すると上記と同様にユーザ名が表示されるようになる。
jaybanuan@devpc:~/src/myapp (NO-USER-NAME@main)$ jaybanuan@devpc:~/src/myapp (NO-USER-NAME@main)$ git config --local user.name jaybanuan jaybanuan@devpc:~/src/myapp (jaybanuan@main)$
これを実現するために、以下のスクリプトを"~/.bashrc"に付け加えておく。
get_git_info_for_ps1() { local GIT_INFO=$(__git_ps1 "%s") if [ -n "$GIT_INFO" ]; then local GIT_USER_NAME=$(git config --get user.name) if [ -z "$GIT_USER_NAME" ]; then USERNAME="NO-USER-NAME" fi echo " ($USERNAME@$GIT_INFO)" fi } PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\#033[00m\]\[\033[36m\]$(get_git_info_for_ps1)\[\033[00m\]\$ '
複数のユーザを管理できるCredential Helperを使う
gitのパスワード管理にはCredential Helperを利用すると入力の手間が省けて楽である。 しかしながら、Credential Helperの設計思想的には、ある1人のユーザが複数のGitサーバを使い分けるという一対多の関係を想定していると思われる。 現実にはプロジェクトやポジションによってユーザを使い分けることがあり、その場合は多対多の関係になる。
サードパーティ製(GitHub製)のCredential Helperにgit-credential-manager-core
というものがあり、名前空間という独自機能をもっている。
この名前空間を利用してユーザごとに管理領域を分けることで、多対多の管理を実現することができる。
具体的には、git-credential-manager-core
をインストールした後、cloneしたgitのリポジトリに入って以下のようなコマンドを実行すれば良い。
$ git config --local credential.credentialStore secretservice $ git config --local credential.namespace jaybanuan
参考
リポジトリをcloneするたびに適切にgit config
を実行するのが面倒だったので、スクリプト化した。
スクリプトはgitのサブコマンドの仕様に沿ったファイル名にしてあるので、パスの通っているディレクトリに配置しておけば、以下のように1コマンドで設定ができる。
$ git user jaybanuan
Minikubeを構築して、ローカルのコンテナイメージをデプロイする
はじめに
コンテナの開発環境を構築する際のメモ。 Minikubeを構築して、ローカルのコンテナイメージをデプロイするまでの手順を残しておく。
環境
$ cat /etc/os-release | grep PRETTY_NAME PRETTY_NAME="Ubuntu 20.04.4 LTS" $ uname -srvm Linux 5.13.0-39-generic #44~20.04.1-Ubuntu SMP Thu Mar 24 16:43:35 UTC 2022 x86_64 $ docker version Client: Docker Engine - Community Version: 20.10.14 (略) Server: Docker Engine - Community Engine: Version: 20.10.14 (略)
手順
(1) Minikubeのインストール
インストール方法はいくつかあるが、今回はDebian packageを利用する。 以下のコマンドを実行。
$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_latest_amd64.deb $ sudo dpkg -i minikube_latest_amd64.deb
詳細はこちら。
(2) kubectlのインストール
インストール方法はいくつかあるが、今回はsnapを利用する。 以下のコマンドを実行。
$ sudo snap install kubectl --classic
詳細はこちら。
(3) Minikubeの起動
以下のコマンドを実行。
$ minikube start 😄 Ubuntu 20.04 上の minikube v1.25.2 ✨ docker ドライバーが自動的に選択されました。他の選択肢: kvm2, ssh, none 👍 minikube クラスター中のコントロールプレーンの minikube ノードを起動しています 🚜 ベースイメージを取得しています... 💾 Kubernetes v1.23.3 のダウンロードの準備をしています > gcr.io/k8s-minikube/kicbase: 379.06 MiB / 379.06 MiB 100.00% 169.59 KiB > preloaded-images-k8s-v17-v1...: 505.68 MiB / 505.68 MiB 100.00% 220.72 K 🔥 docker container (CPUs=2, Memory=3900MB) を作成しています... 🐳 Docker 20.10.12 で Kubernetes v1.23.3 を準備しています... ▪ kubelet.housekeeping-interval=5m ▪ 証明書と鍵を作成しています... ▪ コントロールプレーンを起動しています... ▪ RBAC のルールを設定中です... 🔎 Kubernetes コンポーネントを検証しています... ▪ gcr.io/k8s-minikube/storage-provisioner:v5 イメージを使用しています 🌟 有効なアドオン: storage-provisioner, default-storageclass 🏄 完了しました! kubectl が「"minikube"」クラスタと「"default"」ネームスペースを使用するよう構成されました
(4) ダッシュボードで動作確認
以下のコマンドを実行。
$ minikube dashboard 🔌 ダッシュボードを有効化しています... ▪ kubernetesui/metrics-scraper:v1.0.7 イメージを使用しています ▪ kubernetesui/dashboard:v2.3.1 イメージを使用しています 🤔 ダッシュボードの状態を検証しています... 🚀 プロキシーを起動しています... 🤔 プロキシーの状態を検証しています... 🎉 デフォルトブラウザーで http://127.0.0.1:35159/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ を開いています... 既存のブラウザ セッションで開いています。
デフォルトのブラウザが自動的に起動されて、以下のようにダッシュボードが表示される。 まだ何もデプロイしていないので「表示するものがありません」とメッセージが出ている。
(5) テスト用のWebサーバのイメージを作成
まず、作成するコンテナイメージをMinikubeが認識できるようにしなければならない。 その方法はいくつかあるが、今回は「docker-env command」という方法を採用する。 以下のコマンドを実行する。
$ eval $(minikube docker-env)
詳細はこちら。
次に、以下の内容でDockerfileを作成する。 Ningxのドキュメントルートに、hello, world! を表示するだけのHTMLを配置している。
FROM nginx:1.21 RUN echo "<!DOCTYPE html><html><body>hello, world!</body></html>" > /usr/share/nginx/html/index.html
そして、以下のコマンドを実行して、コンテナイメージを作成する。
$ docker build --tag test-web:1.0.0 .
(6) Minikubeにデプロイ
以下の内容でmanifest.yml
を作成する。
--- apiVersion: v1 kind: Service metadata: name: test-web-service spec: type: NodePort selector: app: test-web ports: - protocol: TCP port: 80 targetPort: 80 --- apiVersion: apps/v1 kind: Deployment metadata: name: test-web-deployment labels: app: test-web spec: replicas: 1 selector: matchLabels: app: test-web template: metadata: labels: app: test-web spec: containers: - name: test-web image: test-web:1.0.0 ports: - containerPort: 80
このマニフェストファイルをMinikubeに適用する。
$ kubectl apply -f manifest.yml
(7) 動作確認
以下のコマンドを実行して、アクセス用のURLを取得する。
$ minikube service test-web-service --url http://192.168.49.2:32219
WebブラウザでこのURLにアクセスすると、以下のようにhello, world!が表示される。
ダッシュボードを確認すると、デプロイメント、ポッド、レプリカセットが作成されていることを確認できる。