OmniAICreator/Qiita-1.07M huggingface dataset:
【初心者】Proxmoxのqcow2フォーマットをVHDXに変換する


以前のエントリでVHDXをProxmoxにインポートしましたが、その逆をやってみます。

:::note info
結論
**qemu-img convert**コマンドを使うことで、気軽にHyper-VとProxmoxの相互運用が可能とわかりました。
:::

# 環境
ほぼ初期状態のWin11のイメージ（VM102）で、64GBのディスクを使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/603fa7e7-450a-6648-6799-8b409c54b80e.png)

# qcow2のサイズ確認

```.sh
cd /var/lib/vz/images/102
ls -lh
```
```
total 15G
-rw-r----- 1 root root 65G Jun 13 19:14 vm-102-disk-0.qcow2
```
ダイナミックディスクのため、65GBの容量のうち15GB使用中でした。

# 変換

```.sh
time qemu-img convert -p -f qcow2 -O vhdx vm-102-disk-0.qcow2 win11-onedrive.vhdx
```

31秒での変換となりました。とても早いです。
```
    (100.00/100%)
real    0m31.075s
user    0m1.266s
sys     0m10.909s
```

# VHDXをHyper-Vでマウント

VHDのタイプ、実使用サイズ、OS上のサイズをねんのため確認します。

```.ps1
Get-VHD -Path D:\win11-onedrive.vhdx |
    Select-Object -Property vhdtype,filesize,@{
        label='OS_Disk_Size'
        expression={($_.size/1GB).ToString('F2')}
    }
```
```
VhdType    FileSize OS_Disk_Size
-------    -------- ------------
Dynamic 14973665280 64.00
```

問題なくマウントと起動ができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/2193145f-089f-c1a5-800e-e9e7233be025.png)

# まとめ
- **qemu-img convert**コマンドを使うことで、気軽にHyper-VとProxmoxの相互運用が可能とわかりました。
- 今回は動的VHDXでしたが、固定にできるかはもう少し調査が必要でした。

# 参考にさせていただいたサイト

**イメージの固定 VHD 形式への変換**
https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/deploying_red_hat_enterprise_linux_8_on_public_cloud_platforms/convert-the-image_deploying-a-virtual-machine-on-microsoft-azure

**How to Convert Vhd/Vhdx to Qcow2 to Migrate Hyper-V to KVM?**
https://www.vinchin.com/en/blog/convert-vhd-vhdx-to-qcow2.html

**Get-VHD  Reference Module: Hyper-V**
https://learn.microsoft.com/ja-jp/powershell/module/hyper-v/get-vhd?view=windowsserver2022-ps
自宅サーバーのIPを秘匿しつつ、今話題のZeroTrustの恩恵にも預かりつつ、Minecraft(Bedrock)サーバーをなるべく安全に公開してみた

## おことわり

https://fujitsufjct.connpass.com/event/284370/

こちらのイベントのLT準備の思考整理用に慌てて作成した記事なので、めちゃめちゃ汚いです。申し訳ありません。
ご質問や、こここうした方が良いのでは？というご意見などは随時受け付けておりますので、是非コメントまで。

## 構成図

![Inked68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f31353835302f64393465653437322d313265322d316638652d393232352d3234653631616537353766662e706e67.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15850/789d1730-82f7-f0d2-e642-1203a9cbcdfa.jpeg)

## やったこと

- さくらのVPSを契約
  - リバースプロキシとWebアプリを1個動かすだけなので最小スペック（メモリ1G/ストレージ50GB/100Mbps）を月額990円で契約
  - 本当はWebARENA Indigoでストレージだけ20GBの同スペックVPSが349円（349円！？！？）で提供されてるのでこっちを使いたいが、プリペイド系クレジットカードとかデビッド弾く設定だったので断念（NTTPCさん...この歳までクレカ作ってない僕にも救いはありませんか...？）
- Cloudflareでドメインを取得
  - Proxyさせたときにアクセス元IPを拾う方法をまだ調べていないので、当座DNSOnlyモードでリバースプロキシのグローバルIPへ飛ばさせている（要改善）
  - VPS上で作成したFlaskアプリケーションをCloudflareTunnel経由で公開
- FlaskでTrustMasterアプリケーションを作成
  - ブラウザからawsのグローバルIPチェックにリクエストを送信し、グローバルIPを取得
  - 取得したグローバルIPをFlaskにGETで通知
  - 受け取ったFlaskはその内容をwhitelistに追記する
  - Flaskへのアクセスはコンテナ内に留めて、同じスタックのnginxから、かつ特定ドメインでのアクセス時のみアクセス許可する（後述するBedrockリバースプロキシ用のnginxとは処理が別）
- WireGuardで自宅サーバーへのVPNを構築
- nginxでUDPリバースプロキシを構築
  - nginxでUDPのプロキシを通すには専用のオプションを付けてビルドしないと行けないため、まずはそのコンテナ構築
  - 構築したUDP対応のnginxでwhitelistの内容に沿ったIPフィルタリングを行う

TrustMasterはCloudflareTunnel経由でVPS上のFlaskへ接続させ、nginx(BedrockServerへのアクセス)はCloudflareのDNS設定経由で直接接続させている。

bedrock用のドメインはDNSOnlyモードで公開しているためVPSのIPは秘匿出来ないことになるが、本来そのためのVPS。一応CloudflareTunnelがUDPに対応してくれるか、自分がCloudflareDNSのProxiedモードをちゃんと調べてアクセス元IPをnginxから拾えるように実装すればVPSも自宅サーバーも完全にIPを秘匿出来るようになる。（ちょっと安全過ぎる）

https://developers.cloudflare.com/support/other-languages/%E6%97%A5%E6%9C%AC%E8%AA%9E/%E5%85%83%E3%81%AE%E8%A8%AA%E5%95%8F%E8%80%85ip%E3%82%92%E5%BE%A9%E5%85%83%E3%81%99%E3%82%8B/

## 既知の問題

- nginxでconfigを動的に読み込むためには有償版のnginx plusを使わないといけないらしい
新しくユーザーが追加される度にnginx restartが必要となる。このシステム経由でログインしてるユーザーは、別のユーザーが新規にwhitelistの登録操作（に伴うnginx -s reload）したときに切断される
- PCからだとめっちゃラグい
何故かiOS版とか今回検証用に購入したAndroid版とかからだと全然遅延を感じずに遊べるのに、PCから遊ぶとラグい。（チェスト開いたりエリトラ中のロケット花火が実行されるまで1～2秒くらいかかるという典型的な遅延状態）それぞれ同じ回線から接続しても同様の事象が再現するので、クライアント依存っぽい。

## 参考資料

https://www.gwtcenter.com/docker-compose-wiregaurd

nginx UDP対応版のビルド

https://qiita.com/yt0f1/items/3dc562a8e51ab90c5de2

https://gakumon.tech/nginx/nginx_install_src_ubuntu.html

https://qiita.com/syoyo/items/f91b92e3cd1457821e0b

nginx UDPの構成

https://unchama.hatenablog.com/entry/2019/05/19/164159

WireGuard構築

https://vpslife.server-memo.net/ubuntu_wireguard_install/
【作業メモ】GitOpsでTerraformのコードを管理する(GCPチュートリアル)

# はじめに

IaCやるならコードはGitリポジトリで管理したいなあと思って以下ハンズオンにtry

https://cloud.google.com/docs/terraform/resource-management/managing-infrastructure-as-code?hl=ja


- devとprod環境のみ作成
- devまたはprod環境のブランチにpushしたらCloud Buildが走る
- GCP側の操作はCloud Shellで行う


# 1. APIを有効にする
GCPのCloud BuildとCompute EngineのAPIを有効化する。


# 2. GitHubのサンプルリポジトリをフォークする

リポジトリを見てみると、environmentsフォルダ配下にサブフォルダ(devとprod)がある。
フォルダでワークロードを分離していて、極力類似した構成にするのがよいけど、サブフォルダごとに設定変更をすることも可能。


# 3. 保存場所をCloud Storageバケットにする
バケットを作る。デプロイ履歴を残すためにオブジェクトのバージョニングを有効にする。

:::note warn
バージョニングを有効にするとストレージコストはその分増える
:::

terraform.tfvars ファイルと backend.tf ファイルのパスを書き換える(プレースホルダになっているところにプロジェクトIDを入れる)

# 4. Cloud Buildサービスアカウントに権限付与する

サービスアカウントはアプリケーションや仮想マシンに権限をつけるGoogleアカウント。
APIを呼び出すときはサービスアカウントを使用する。

Terraformスクリプトを実行できるようにするにはプロジェクトへのアクセス権限が必要。


# 5. Cloud BuildをGitHubリポジトリに接続

最初All repositoriesを選択してインストールしてしまったけど、GCPコンソールのCloud Build APIのダッシュボードから何も見えず...ハンズオンの説明だとリポジトリを選択しろとあったのでやり直し。リポジトリを選択したら接続できた。

# 6. 開発環境ブランチでファイアウォール設定を変更する

なんかビルド失敗した。マージ可能な状態ではあるけど、とりあえず次の手順をやってみる

# 7. ブランチにルールを設定する
devとprodブランチでステータスチェックに成功しないとマージできないようにルール追加。
先ほどのpull requestに戻ってみると確かにマージできなくなっている。

こんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/9ae2b068-6976-1b57-75d8-a99bbaeeac34.png)

エラーログ見たらbucket doesn't existで言ってて、
3. の手順でterraform.tfvars ファイルと backend.tf ファイル内でバケットのパスをちゃんと書き換えられていなかった（プロジェクトIDが入るべきところがプレースホルダのままになっていた）ので、今回はもうGitHubから手動で書き換えた。→無事ビルドできた。

# 8. devに変更をマージ
ハンズオンの指示通りにGitHub操作

# 9. devブランチをprodブランチにマージ
同上

# 10. デプロイしたWebサーバにアクセス
ビルドログにIPが出るので、ブラウザからアクセスする。無事アクセスできた。

# 11. リソース削除
適宜お片付け

# おわりに
GitHubのセットアップしたり途中ビルドが上手くいかず結構時間かかった印象。4~5hくらいかかったかな。

Storageのパスがなんかうまく書き換わってなかったのは、Cloud ShellからGit pushしようとしたときのGitの認証でパスワード認証しようとしたからっぽい。
Shellのコンソールでも"Support for password authentication was removed on August 13, 2021."って怒られてたので、アクセストークンも取得して認証試したら通った。
(今回はGitHub側でGUIで操作しちゃったけど……)
"Build Your Own Docker" コースでの学びのメモ

CodeCrafters の "Build Your Own Docker" コースを Ruby で取り組んだ。

https://app.codecrafters.io/courses/docker?track=ruby

完成品は以下。

https://github.com/kudojp/codecrafters-docker-ruby

開発中に考えたことや新しく知ったことを雑にメモる。


## 1. コンテナ内でのプロセスをホストマシンのそれから分離 ([ref](https://github.com/kudojp/codecrafters-docker-ruby/blob/main/app/main.rb#L27))

Linuxの `unshare` コマンドにより、プロセスの名前空間を使い分離している。深掘って調べようとしたが断念。

例えば、`$ unshare --pid --fork --mount-pro program_commandc` コマンドの引数として与えられたコマンドが実行されるプロセスの中では、自らのプロセスを参照すると 1 となる。オプションの詳細は、[こちら](https://blog.amedama.jp/entry/linux-pid-namespace_1)

この機能は OS X (Darwin) では提供されていないっぽい。

## 2. コンテナ内でのファイルシステムをホストマシンのそれから分離 ([ref](https://github.com/kudojp/codecrafters-docker-ruby/blob/main/app/main.rb#L28))

ホストマシンでコンテナがルートディレクトリとして使用するディレクトリを作る。そして、Linuxの`chroot` コマンドの引数として、コンテナのエントリポイントとなるコマンドを実行することにより、ファイルシステム内でアクセス可能な範囲をホストマシンのルートディレクトリの内部に制限できる。

例えば、`$ chroot /app/root_dir program_command`を走らせた場合、プログラムのルートディレクトリは`/root_dir`になる。

## 3. Dockerベースイメージの pull ([ref](https://github.com/kudojp/codecrafters-docker-ruby/blob/main/app/main.rb#L12-L16))

ホストマシンで、コンテナ内に置いておきたいコマンド(`/bin/ls`とか)の集合をtar.gz圧縮したファイルをDocker レジストリからダウンロードしてくる。そして、それを展開して、コンテナがルートディレクトリとして使用するディレクトリ内に置いている。

ダウンロードと展開の実装詳細は、[こちら](https://github.com/kudojp/codecrafters-docker-ruby/blob/main/app/base_image_reproducer.rb)を参照。

## 4. コンテナ内で走るプロセスのexit status, stdout, stderrをdockerコマンドの出力として吐き出す ([ref](https://github.com/kudojp/codecrafters-docker-ruby/blob/main/app/main.rb#L30-L33))

dockerコマンドのプロセスで、コンテナ内で走るプロセスを走らせて得られたexit status, stdout, stderrのそれぞれを、dockerrプロセスのそれとして書き出している。愚直だ。
Elixirの対話モードで内包表記の動作を確認してみた

# 概要
Elixirの対話モードで内包表記の動作を確認してみました。以下のページを参考にしました。

https://elixirschool.com/ja/lessons/basics/comprehensions

# 対話モードで実行
以下のコマンドを実行しました。
```
$ iex
Erlang/OTP 24 [erts-12.2.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit]

Interactive Elixir (1.12.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> list = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
iex(2)> for x <- list, do: x*x
[1, 4, 9, 16, 25]
iex(3)> for {_key, val} <- [one: 1, two: 2, three: 3], do: val
[1, 2, 3]
iex(4)> for {k, v} <- %{"a" => "A", "b" => "B"}, do: {k, v}
[{"a", "A"}, {"b", "B"}]
iex(5)> for <<c <- "hello">>, do: <<c>>
["h", "e", "l", "l", "o"]
iex(6)> for {:ok, val} <- [ok: "Hello", error: "Unknown", ok: "World"], do: val
["Hello", "World"]
iex(7)> list = [1, 2, 3, 4]
[1, 2, 3, 4]
iex(8)> for n <- list, times <- 1..n do
...(8)> String.duplicate("*", times)
...(8)> end
["*", "*", "**", "*", "**", "***", "*", "**", "***", "****"]
iex(9)> for n <- list, times <- 1..n, do: IO.puts "#{n} - #{times}"
1 - 1
2 - 1
2 - 2
3 - 1
3 - 2
3 - 3
4 - 1
4 - 2
4 - 3
4 - 4
[:ok, :ok, :ok, :ok, :ok, :ok, :ok, :ok, :ok, :ok]
iex(10)> import Integer
Integer
iex(11)> for x <- 1..10, is_even(x), do: x
[2, 4, 6, 8, 10]
iex(12)> for x <- 1..100,
...(12)> is_even(x),
...(12)> rem(x, 3) == 0, do: x
[6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96]
iex(13)> for {k, v} <- [one: 1, two: 2, three: 3], into: %{}, do: {k, v}
%{one: 1, three: 3, two: 2}
iex(14)> for c <- [72, 101, 108, 108, 111], into: "", do: <<c>>
"Hello"
iex(15)> 
```

# まとめ
何かの役に立てばと。
[AWS Q&A 365][ECS]AWSのよくある問題の毎日5選 #77

# 1. Q: タスク定義が起動に失敗する場合、どのようにトラブルシューティングを行いますか？
A: まず、タスク定義が有効で、必要なリソースが割り当てられていることを確認します。次に、コンテナイメージがアクセス可能で正しく設定されていることを確認します。また、エラーメッセージや失敗に関するログも確認できます。

# 2. Q: サービスが適切にスケーリングしない場合、どのようにトラブルシューティングを行いますか？
A: サービスの設定が正しく設定されていること、つまり希望する数とデプロイメントの設定を確認します。サービスのログでエラーや問題がないか確認します。さらに、サービスが正しいスケーリングポリシーを使用し、必要なリソースが利用可能であることを確認します。

# 3. Q: ロードバランサーとの接続問題をどのようにトラブルシューティングしますか？
A: ロードバランサーの設定が正しく設定されていること、つまりターゲットグループとリスナーを確認します。セキュリティグループとネットワークACLがロードバランサーへのトラフィックを許可していることを確認します。最後に、ログでエラーや問題がないか確認します。

# 4. Q: タスクのネットワーキングに問題がある場合、どのようにトラブルシューティングを行いますか？
A: タスク定義とサービス設定を確認し、ネットワーク設定が正しいことを確認します。セキュリティグループとネットワークACLがタスクへのトラフィックを許可していることを確認します。最後に、ネットワーキングに関連するエラーや問題がないかログを確認します。

# 5. Q: コンテナが起動に失敗する問題をどのようにトラブルシューティングを行いますか？
A: コンテナイメージがアクセス可能で正しく設定されていることを確認します。タスク定義を確認して、コンテナが適切に定義されており、必要なリソースが割り当てられていることを確認します。コンテナまたはその依存関係に関連するエラーや問題がないかログを確認します。
[AWS Q&A 365][ECS]Daily Five Common Questions #77

# 1. Q: How do I troubleshoot a task definition that is failing to start? 
A: First, check the task definition to make sure it is valid and has the necessary resources allocated. Then, check the container image to make sure it is accessible and correctly configured. You can also check the logs for any error messages or failures. 

# 2. Q: How do I troubleshoot a service that is not scaling properly? 
A: Check the service configuration to make sure it is set up correctly, including the desired count and deployment configuration. Check the service logs for any errors or issues. Additionally, make sure the service is using the correct scaling policy and that the necessary resources are available. 

# 3. Q: How do I troubleshoot connectivity issues with a load balancer? 
A: Check the load balancer configuration to make sure it is set up correctly, including the target groups and listeners. Check the security groups and network ACLs to make sure they allow traffic to and from the load balancer. Finally, check the logs for any errors or issues. 

# 4. Q: How do I troubleshoot an issue with task networking? 
A: Check the task definition and service configuration to make sure the network settings are correct. Check the security groups and network ACLs to make sure they allow traffic to and from the task. Finally, check the logs for any errors or issues related to networking. 

# 5. Q: How do I troubleshoot an issue with a container failing to start? 
A: Check the container image to make sure it is accessible and correctly configured. Check the task definition to make sure the container is properly defined and has the necessary resources allocated. Check the logs for any errors or issues related to the container or its dependencies.
Firebase上にゼロメンテナンスのサーバーレスアプリを構築するオープンソースライブラリ"Skeet"をリリース

![EffortlessServerlessSkeet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2780317/19cd3754-6d37-ab29-3f9b-5d55c80cfc7f.png)


Firebase 上にゼロメンテナンスのサーバーレスアプリを構築するオープンソースライブラリである"Skeet"のリリースしました。

Skeet: https://skeet.dev/

## もっと多くを少ないコストで

アプリの開発・運用コストを下げ、もっと多くのプランを実現させましょう。

Skeet はオープンソースのフルスタックアプリ開発ソリューションです。
すぐにアプリのロジックからスタートでき、インフラに関する心配は無用です。

### ハイパフォーマンス

Auth やストレージ等さまざまなイベントトリガーを活用した分散システムはより効率的に動作し、NoSQL データベースの Firestore は柔軟にスケール、リアルタイムストリームにも対応しています。

### エコフレンドリー

ユーザーの使用パターンに合わせてコンピューティングリソースが自動的にスケールするため、常に必要な分だけリソースを利用します。Skeet は経済的にも環境的にも優しい設計となっています。

### 優れた開発者体験

デプロイやサーバーインフラのメンテナンスの心配無しに、すぐにアプリロジックから書き始めることができます。TypeScript のサポートはアプリをより安全にし、さらに開発効率を向上させます。

### 堅牢なセキュリティ

SSL 対応はもちろん、Cloud Armor を活用した DDoS 攻撃及び bot 対策や、最小限のエンドポイント露出及び Pub/Sub メッセージングの活用が簡単に適用でき、Google 規模のセキュリティを実現できます。

## 新時代のアプリ開発プラットフォーム

![Skeet - Full-stack Serverless Framework](https://storage.googleapis.com/skeet-assets/animation/skeet-cli-create-latest.gif)

アプリの開発及び公開に必要なものはすべて揃っています。Skeet フレームワークのツール群は、分散し煩雑になりがちなサーバーレスアーキテクチャをできるだけ簡単に扱えるようにと設計されています。

### 高性能なモニター

Firebase Emulator Suite はバックエンドを可視化します。認証情報からデータベース、ストレージやすべてのログを一元管理できるため、開発のスピードが上がります。

### 強力な CLI ツール

Skeet CLI は Google Cloud のサーバーレス製品を組み合わせた分散システム構築をワンコマンドで完了させます。簡単な質問に答えるだけでアプリはデプロイ可能な状態に仕上がります。

### ゼロメンテナンス

GitHub Actions による自動デプロイ、そしてデプロイされたリソースは必要な分だけ自動的にスケールするため、サーバーインフラのメンテナンスは基本的に必要ありません。

## 新しいアプリ開発を体感してください

![Skeet with OpenAI](https://storage.googleapis.com/skeet-assets/animation/skeet-chat-latest.gif)

新しいアプリ開発を体感してください。
すぐに Web・iOS・Android アプリを開発し公開できます。

Skeet チュートリアルでは、OpenAI API を活用して AI チャットボットのアプリを開発し、実際にデプロイして利用できることを確認します。

Skeet チュートリアル: https://skeet.dev/ja/doc/backend/quickstart/
TKG2.2のMachineHealthCheckの設定を変更する

※8/31追記：TKG2.3では修正されている模様。変に悩みたくない方は2.3へアップグレードすることを推奨。

TKG2でClass-Basedでのクラスタ構築がサポートされ、かつデフォルトのクラスタ構築手順となったが、これを利用した場合、MachineHealthCheck(MHC)の設定変更が6/13時点の[マニュアル手順](https://docs.vmware.com/en/VMware-Tanzu-Kubernetes-Grid/2.2/using-tkg-22/workload-clusters-mhc.html)だと出来なくなる。

```
$ tanzu cluster machinehealthcheck node set test-cluster --node-startup-timeout 120m

Error: setting machine health checks on clusterclass based clusters is not supported
```

これを一時的に回避して強引にMachineHealthCheckの値を変えてみる。
なお、この方法はサポートされた方法ではないので、自己責任で利用し、かつ本番環境などでは利用しないこと。

Management Clusterにコンテキストを切替後、以下を実行してManagement Clusterの設定ファイルを抜き出す。
```sh
tanzu package installed get tkg-pkg -n tkg-system -f values.yml
```
MHCに関するパラメータを追加する。パラメータ一覧は[こちら](https://docs.vmware.com/jp/VMware-Tanzu-Kubernetes-Grid/2.2/tkg-deploy-mc-22/mgmt-deploy-config-ref.html#-8)。
ここでは`NODE_STARTUP_TIMEOUT`を変更して新規ノードがクラスタに参加するまでの待ち時間を伸ばしてみる。

現在の待ち時間は以下となっている。
```sh
$ kubectl get mhc -o yaml test-cluster-md-0-dvntw  | grep nodeStartupTimeout
  nodeStartupTimeout: 20m0s
```
先程抜き出した設定ファイル内の`NODE_STARTUP_TIMEOUT`を20分から120分に変更する。
```diff
$ diff -u values.yml.orig values.yml
--- values.yml.orig	2023-06-13 07:51:52.466136987 +0000
+++ values.yml	2023-06-13 10:33:02.780437280 +0000
@@ -83,7 +83,7 @@
     MHC_FALSE_STATUS_TIMEOUT: 12m
     MHC_UNKNOWN_STATUS_TIMEOUT: 5m
     NAMESPACE: tkg-system
-    NODE_STARTUP_TIMEOUT: 20m
+    NODE_STARTUP_TIMEOUT: 120m
     NSXT_ALLOW_UNVERIFIED_SSL: "false"
     NSXT_CLIENT_CERT_DATA: ""
     NSXT_CLIENT_CERT_KEY_DATA: ""
```
更新した設定ファイルを使ったtkg-pkgをupdateする。
```sh
tanzu package installed update tkg-pkg -n tkg-system -f values.yml
```
`kubectl get app -n tkg-system tkg-pkg`で`Reconcile succeeded`になっているのを確認できたら、再度`nodeStartupTimeout` を確認する。
```sh
$ kubectl get mhc -o yaml test-cluster-md-0-dvntw  | grep nodeStartupTimeout
  nodeStartupTimeout: 2h0m0s
```
変更できたようだ。
なお、この方法はClass-Basedでデプロイされたクラスタにしか効果がない。
またクラスタを選んで設定変更出来ない点も注意となる。
Intel Mac に Windows11 をクリーンインストールする

## まずはWindows10のISOダウンロード
https://www.microsoft.com/ja-jp/software-download/windows10ISO

![windows10_download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342/500d6347-d5da-9ce2-7934-7f570fa6f861.png)

## 次にWindows11のISOダウンロード
https://www.microsoft.com/ja-jp/software-download/windows11

![windows11_download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342/5677c0b1-2b13-3c30-6f4c-750dce2df22a.png)

## BootCampで使えるWindows11のISOファイルを作る
Windows10のISOを展開して別ディレクトリとしてコピーしておく。
Windows11のISOを展開して、sources/install.wim を抜き出しておく。
![copy_wim.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342/4e455f50-3661-b567-ce5d-5d320111c00e.png)
Windows11 のinstall.wimを Windows10のinstall.wimに上書きする。
別ディレクトリにコピーしておいた展開したISOｎディレクトリをもう一度ISOファイルに固める。
![setup_dir.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342/9687457c-a76e-fc16-7700-a9e4593e1b8a.png)
（ISOファイル作成するアプリを使う必要がある。MacOS標準搭載のツール「Disk Utility」ではうまくいかない）
※https://crystalidea.com/anytoiso コレを使った他のツールでも大丈夫だと思う
![bootcamp_setup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342/74220673-706c-6b2a-d308-97db2046abc8.png)
Bootcamp を立ち上げてWindows用のSetup.exeを保存しておく
「アクション」ー「Windowsサポートソフトウエアをダウンロード」を選択する。
（USBメモリに保存しておく）

## Windows11をインストール
あとは続けるボタンから普通にインストールしてOK
![windows_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342/a64b14b6-2016-e499-d816-ee73516afde2.png)
だけど、Windows11からオンラインインストールになっているのでオフラインインストールしなきゃいけない。。
そこで `Shift + F10` でコマンドプロンプトを起動します。
```
> cd oobe
> BypassNRO.cmd
```
でオフラインインストールできる。
![windows_setup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342/57abd61f-593a-14c1-8a3e-2689c09f590d.png)
これが終わったらUSBメモリに保存したSetup.exeを起動してドライバをインストールする。
あとは普通に使える。
IaC で作る さくらのクラウド デザインパターン②

## 前書き
Terraform や Ansible を使って、さくらのクラウド上でさまざまな構成を手軽に作成するためのデザインパターンをご紹介します。
前回[デザインパターン①](https://qiita.com/shztki/items/c36deaf889fbdfc095c1)の記事を書いたのですが、その続きとなります。
実行環境の準備などは、上記をまずご確認ください。

## デザインパターン 5
デザインパターン 1 をモデルに、自動バックアップとシンプル監視を設定するパターンです。
![sample_05.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532524/553929d9-85ff-0171-b3e3-4fec759da5e5.png)

詳細については、README で解説していますので、こちらをご確認ください。
https://github.com/shztki/sakura_design_pattern/blob/main/05_autobackup_simplemonitor/README.md

自動バックアップを使うと、 AWS でいえば EBS スナップショット(を DLM を使って自動化した場合)のように、ディスクに対して複数世代のバックアップ設定のスケジューリングができます。
できあがったものはアーカイブとして管理されるので、利用したいときはディスクに変換して、サーバを停止して付け替えるなどの作業が必要です。
また、毎回フルバックアップとなるようで、容量が大きいと時間もお金もかかります。
ご利用は計画的にどうぞです。

シンプル監視については、グローバルIP(または FQDN)に対する外形監視ができるだけ(内部監視はできません)ですが、なんと無料です！！！
FQDN利用/SSLの有効期限監視/外部グローバルIP アドレスへの監視、だと有料のようですが、さくらインターネットのサービスで利用しているグローバルIP に対する監視であれば、無料で利用できるようです。
これは太っ腹ですね。
ちなみに通知先に Webhook を利用可能なのですが、コントロールパネルで設定するときには `Webhook通知に含めるテキスト` という項目があったものの、 [Terraform の変数には該当のパラメータが無い](https://registry.terraform.io/providers/sacloud/sakuracloud/latest/docs/resources/simple_monitor)ようでした。
ここを設定したい場合、コントロールパネル上で手動で実施する必要があるので、ご注意ください。

## デザインパターン 6
さくらのクラウドで、エンハンスドロードバランサを使って、WEBサーバ 2台にアクセスを分散するための構成を作成するコードです。
![sample_06.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532524/da87ee36-077f-6a8f-21f9-d77c4400be86.png)

詳細については、README で解説していますので、こちらをご確認ください。
https://github.com/shztki/sakura_design_pattern/blob/main/06_enhanced_load_balancer/README.md

こちらは、「ルータ＋スイッチ」は必須ではありませんので、不要な場合は共有セグメントを使ってサーバを作成してもらって問題ありません。
パケットフィルタを使って HTTP へのアクセスをエンハンスドロードバランサに限定することを、循環参照にさせずに一回で実現するために、あえて利用してみただけとなります。
Terraform での実行後は、Ansible での Apache構築の自動化も可能ですので、あわせて実施してみてください。
エンハンスドロードバランサはプロキシ型ロードバランサのため、何もしないとアクセスログに記録されるリモートIPアドレスはすべてエンハンスドロードバランサ自身の IPアドレスになってしまいます。
きちんと正しいリクエスト元の IPアドレスが記録されるようにすることと、ヘルスチェックアクセスはログから除外する設定を、自動化しています。
なお設定していて思いましたが、 AWS の ELB の場合、[ヘルスチェック時](https://repost.aws/ja/knowledge-center/elb-fix-failing-health-checks-alb)には User-Agent に `ELB-HealthChecker/2.0` が入るので、さくらもこういう仕様にしてほしいですねー。
(さくらの場合、 User-Agent は空でした)
エンハンスドロードバランサのプロキシ元ネットワークを確認して、該当ネットワークからのヘルスチェックアクセスはログから除外する、という設定をするのは、毎回内容が変わって面倒ですし、User-Agent だと一律に設定できるので、管理も楽になるんですけどねー。

## 後書き
ということで、追加のデザインパターンをお届けしました。
またいずれ、DB や NFS のアプライアンスを利用した構成や、オートスケール、セキュアモバイルコネクトなど、あまりふだん使わないようなサービスを使ったデザインパターンなども公開してみたいなと思っています。
それでは、どうぞ楽しい IaC ライフを！！
7つのアクション

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/b41a923a-ac05-03d1-6b93-52bf193a1cef.png)

1:アクション
index:一覧表示
show:詳細表示
new:生成
create:保存
edit:編集
update:更新
destroy:削除
【ServiceNow】参照フィールドでの検索方法の設定

本記事では、参照フィールドでの検索方法に関する設定について紹介します。


はじめに
---
ServiceNowでは、検索ワードの前にアスタリスクを付ける（ワイルドカード）ことで部分一致が可能になりますが、参照フィールドではワイルドカードを使用しなくても部分一致が可能になっています。

このままでも検索自体は問題ありませんが、参照先の情報が多いときなどには大量の検索結果が表示されてしまうため、お客様によっては「前方一致検索と部分一致検索の使い分けができるようにしてほしい！」というご要望があります。

プロパティの設定を変更するだけで、前方一致検索と部分一致検索の使い分けが可能になるので、その設定方法についてご紹介します。

設定方法
---
１．フィルターナビゲーターに「sys_properties.list」と入力し、Enterキーを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335478/d6feaf41-d28a-abed-701d-138668ff9331.png)
２．Nameで「glide.ui.ref_ac.startswith」を検索します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335478/56d01a74-e573-a244-66f7-9e6f397b26fb.png)
３．Valueをfalseからtrueに変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335478/be1e034f-08af-6f29-2b6f-c6481aa99e65.png)

設定は以上です。

動作確認
---
sys_userテーブルを参照したフィールドで検索をします。

<strong>＜glide.ui.ref_ac.startswithプロパティの値がfalse＞</strong>
ワイルドカードを使用してもしなくても、部分一致検索となっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335478/cb5dd4c6-ff13-734e-e5e9-148f44cc4bcd.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335478/095ab706-ca44-7c50-eb98-5ef9df180ef5.png)

<strong>＜glide.ui.ref_ac.startswithプロパティの値がtrue＞</strong>
キーワードの前に何もつけない場合、前方一致検索となります。
ワイルドカードを使用したときだけ、部分一致検索となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335478/163b7fc3-ad69-a20a-d40d-3a22608b5ab3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335478/b396b175-55c0-c167-3443-82fb8dc316f9.png)

まとめ
---
本記事では、参照フィールドでの検索方法に関する設定について紹介しました。
プロパティの値を変更するだけで簡単に設定が可能なので、是非試してみてください。
　
　
　
　
　

参考情報
---
参照フィールドでの検索に関するいろいろな設定方法が記載されています。

https://docs.servicenow.com/ja-JP/bundle/tokyo-platform-administration/page/administer/field-administration/concept/c_AutoCompleteForReferenceFields.html#title_t_ImproveAutoCompleteQueries
1.GBDKの導入

**1.導入手順**

まずは導入（環境構築）手順からまとめていきます。

・目次
1.GBDKダウンロード
2.解凍&配置
　P1.フォルダ説明
　P2.READMEを読んでみる

<br>
<br>
<br>
<br>

**1.GBDKダウンロード**
　githubにてgbdk-2020をダウンロードする。（Ver4.1.1）
https://github.com/gbdk-2020/gbdk-2020

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476538/e82db084-91e7-7131-09ac-34af292ad539.png)
　お使いの端末に合わせてダウンロードしてください。
　（今回はWin10のためWINDOWS　64BITを選択）
<br>
<br>

**2.解凍&配置**
ダウンロードしたgbdk-win64.zipを解凍し、解凍後のフォルダをCドライブ直下に配置する。
　※C直下じゃなくてもOKだけど、よくわからない人はC直下で。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476538/8f166308-6fe2-ffc6-fa21-21e4f0b30716.png)
<br>
<br>
**P1.フォルダ説明**
　ポイントその１
　フォルダ構成を確認

C:\gbdk
└bin　　　　　　　GBDKプログラム類（SDCCコンパイラ？）
└examples　　　　　サンプルフォルダ（主にここで作業）
└include　　　　　C言語用ファイル類
└lib　　　　　　　　ライブラリ
└licenses　　　　　ライセンス
└ChangeLog　　　　　GBDKの更新履歴
└gbdk_manual.pdf　　GBDKのマニュアル
└README　　　　　　取説（テキストファイルなのでサクラエディタ等で開こう！）
└sdccman.pdf　　　　C言語構文等のユーザーガイド

<br>
<br>

**P2.READMEを読んでみる**
　ポイントその２
　READMEを読んでみる

　・「bin」サブディレクトリをシステムパスに追加
　　→必須ではなく基本バッチファイルで実行するため省略

　・「c:\gbdk\examples\gb」サブディレクトリで「compile.bat」を実行
　　→examples\gb配下の各フォルダ内にあるcompile.batをすべて実行するよう~~で、
　　　何に使われるのか分からないけどとりあえず実行しとく~~
　　examples\gb配下にはapa_image～wav_sampleの33個のサンプルプログラムがあり、
　　それらをまとめてコンパイルするバッチみたいなので実行不要
　　（サンプルプログラムを見てみたい方は是非）

<br>
<br>
<br>
<br>
<br>
<br>
以上で環境構築完了！
次回は実際にプログラムしてみよう！！
【備忘録】Lambda  AuthorizerにHeader情報が渡らない

## TLDR;

Lambda イベントペイロードタイプをTokenではなくて、Requestに変更する。

## 事象
SAMテンプレートを使用して、API Gatewayをリソースを作成した時にLambda Authorizerを設定した。いざ叩いてみると、LambdaにHeader情報が渡っていなかった。
Lambdaにログ出力を出して、Cloudwatchで確認すると何も出力されなかった。

```bash:Cloudwatch
2023/06/13 08:39:50 Headers: map[]
```

```yaml:SAMテンプレート.yaml
  MyApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: dev
      Auth:
        Authorizers:
          CognitoAuthorizer:
            UserPoolArn: !GetAtt MyUserPool.Arn
          MyLambdaAuthorizer:
            FunctionArn: !GetAtt LambdaAuthorizerFunction.Arn
            Identity:
              Headers: ["Id-Token"]
              ReauthorizeEvery: 300
      OpenApiVersion: 3.0.3
      EndpointConfiguration:
        Type: REGIONAL
```

## 解決方法
Lambda イベントペイロードをトークンではなくて、リクエストに設定することでHeaderを取得できました。

```diff_yaml:SAMテンプレート.yaml
  MyApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: dev
      Auth:
        Authorizers:
          CognitoAuthorizer:
            UserPoolArn: !GetAtt MyUserPool.Arn
          MyLambdaAuthorizer:
            FunctionArn: !GetAtt LambdaAuthorizerFunction.Arn
            Identity:
              Headers: ["Id-Token"]
              ReauthorizeEvery: 300
+           FunctionPayloadType: REQUEST
      OpenApiVersion: 3.0.3
      EndpointConfiguration:
        Type: REGIONAL
```

コンソール画面にて、API Gatewayの`オーソライザー`から設定を確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3089299/62b6dd5b-845a-d388-1f5f-ab1c35a5c72f.png)

APIを叩くと無事取れました。

```bash
2023/06/13 10:39:50 Headers: map[Id-Token:******]
```

## 後書き

LambdaイベントペイロードをTokenにしているとHeaderに`Authrization`がないと、Lambdaが起動すらしません。(起動しないのでCloudwatchにログが出力されない)
頑張ってSAMの設定ができたと思って、一生懸命API叩いてもUnauthrizedしか出てこず、Cloudwatchのログもないのでどこで詰まっているのかわからず、SAMの書き方が悪くてエンドポイントにLambda Authorizerが正しく設定できていないものだと永遠に思ってました。

公式ドキュメント、大事、、、

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html
OrgLimitsのキーリストの確認方法

Aepxで組織の制限を取得する場合、Salesforceの公開ドキュメントに取得方法が記載してありますが、
キーはどれぐらいがあるかは特に記載されていませんので、キーの確認方法を紹介します。

#### Apexで組織制限の情報を取得する場合、以下のリンクの方法で実現できます
https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_class_System_OrgLimits.htm

#### 以下のコマンドを実行すると、どの制限が取得できるかを確認できます。
```
sfdx force:limits:api:display
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496589/31d7f8ee-aa58-d628-1e74-3649a7330801.png)
BigQueryでtypeがRECORDでmodeがREPEATEDではないカラムを使うときは注意が必要

https://twitter.com/munaita_/status/1665579858288844800


少し詳細に説明します。

## RECORD mode のカラムの add と drop

以下のように、現在、typeがRECORDな場合、ネストされたカラムの drop も add もできません。結構不便です。ちなみにrenameもできません。

### drop

https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#details_16

> You cannot use this statement to drop the following:
> Partitioned columns
> Clustered columns
> Nested columns inside existing RECORD fields
> Columns in a table that has row access policies


### add

https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#details_13

> You cannot use this statement to create:
> Partitioned columns.
> Clustered columns.
> Nested columns inside existing RECORD fields.


## INSERTでRECORDの一部だけに向けたINSERTはできない

OK

```sql
INSERT INTO `pj.suzuki_test.nested` (id, record_column)
VALUES(111, STRUCT('value1' as column1, 'value2' as column2))
```

NG

```sql
INSERT INTO `pj.suzuki_test.nested` (id, record_column.column1)
VALUES(111, 'value1')
```


RECORDであり、REPEATEDな場合は色々とメリットはあると思いますが。一つの行内に複雑なデータ構造を格納し、データ冗長性を減らし、クエリの効率を向上させることができるからです。。

ただし、REPEATEDではない場合のRECORDタイプの定義を使う際は注意が必要です。
文字列

```java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.println("文字列:");
        String input = scanner.nextLine();

        String replaced = input.replace("\\", "/").replace("を", "に");

        System.out.println("文字列: " + replaced);
    }
}
```
Auto-GPT

勝手に Python でプログラミングを始めてしまうやばい Auto-GPT の話。

今話題の Chat GPT の欠点の一つに、学習した時点より新しい情報を答えられない事があります。それを解決するために様々な Plugin が開発されていますが、もっと意欲的な Chat GPT の拡張として Auto GPT という物がありっます。

Auto GPT は Chat GPT を支援してウェブへのアクセスはもちろん長期記憶や画像生成など何でもかんでも支援してしまおうという壮大な試みです。残念ながら私はまだ OpenAI GPT-4 が使えないので完全な機能は試せてませんが、何となく雰囲気はわかったので公式サイト https://docs.agpt.co/setup/ を元にメモします。

まず https://platform.openai.com/account/api-keys で OpenAI の API Key をもらっておく。

```shell
git clone -b stable https://github.com/Significant-Gravitas/Auto-GPT.git
cd Auto-GPT
cp .env.template .env
vi .env
...
OPENAI_API_KEY=xxx ### ここに OpenAI の API Key を書く
...
docker-compose build auto-gpt
docker-compose run --rm auto-gpt --gpt3only --continuous
```

ここで、最後のコマンドのオプションの意味は以下の通り、

* --gpt3only: gpt-3.5 だけを使う
* --continuous: 自動実行。料金に注意。最初に試す時はない方が良いと思います。

Auto-GTP が始まると質問されるので好きなお願いをします。

    I want Auto-GPT to: 日本車は全固体電池でテスラに勝てるか予測して?

すると、Auto-GPT は google などを駆使して情報を集め、できるだけ正確にお願いを叶えようとします。なんと最終的には Python コードを書いて何やら計算を始めました。これはやばい！！！

残念ながら GPT-3.5 がへぼいのか Auto-GPT に不備があるのか途中エラーで止まってしまいましたが、隔離した環境でやらないと Auto-GPT がパソコンを乗っ取ってしまいそうな勢いでした。
Cで二分探索

基本情報のお勉強の片手間に遊んでいたのをまとめる
## ソースコード
```C:serch.c
#include <stdio.h>
#include <string.h>


int compar(const void * n1, const void * n2){
    int i1, i2;
    i1 = *(int*)n1;
    i2 = *(int*)n2;
    if(i1>i2)return 1;
    if(i1<i2)return -1;
    return 0;
}

long tansaku_nibun_test(int* array, unsigned long min, unsigned long max, unsigned long value){
    unsigned long pivot = (min +max) / 2;
    if(array[pivot] == value){
        printf("探索成功(%ld)\n", pivot);
        return pivot;
    }
    if(min > max){
        printf("探索失敗\n");
        return -1;
    }
    if(array[pivot] < value){
        return tansaku_nibun_test(array, pivot + 1 , max, value);
    }
    if(array[pivot] > value){
        return tansaku_nibun_test(array, min, pivot - 1, value);
    }
    return 0;
}

unsigned long nibuntansaku(int* array, unsigned long size, unsigned long value){
    qsort(array, size, sizeof(int), compar);
    return tansaku_nibun_test(array, 0, size, value);
}
```

## 中身
標準ライブラリのqsortで中身を整頓して
そこから再帰で二分探索をしていくスタイル

## まとめ
これを使ってメモリを何ギガと使ったものからの探索も、それなりのスピードでこなせたので、やっぱり先人は偉大だと感じました　まる
VSCode+UnityC#でOpen C# Projectを押しても参照ジャンプが出来ない時の対処法

# 背景
いつもであればUnityの上部メニューバーからAssets>OpenC#Projectを実行することで関数の参照情報が復活出来ていました。
今回はその方法でも復活出来ず、別の方法で対処したため、その対処法を備忘録として残します。

# 対処法
VSCode上でCtrl+Jを押して、OUTPUTタグから拡張機能のログビューを出し、**OmniSharp Log**を選択
内容を見たところ、`server load timed out`でタイムアウトしていたためOmniSharpを再度実行すれば直りそうということが分かりました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141541/e607a690-44b5-d212-1597-dcc2c53b3813.png)

Ctrl+Shift+pでコマンド実行メニューを開き、`OmniSharp: Restart OmniSharp`を実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141541/dca30706-7b20-31fc-fb7a-5e9da8e34db2.png)
これで拡張機能が再起動されて、無事参照情報も表示されるようになりました。

# まとめ
もし同じ現象で困ってる人が居たら参考にしてください。
Smartyの変数をVueのコンポーネントに渡す時のまとめ

SmartyからVueに変数を渡すときは、PHPで`json_encode`をしてから、テンプレートに渡します。

:::note warn
`json_encode`をしないと、おかしな挙動をします。[※参考](#参考)
:::


```html:index.html
// v-bindを用いて、文字列のurlを渡します
// ダブルクオートだとバグります
// シングルクオートで渡しましょう
<Example :url='{$url}'></Example>
```
以下のようにして、Vueのコンポーネントでpropsとして受け取ります。

```typescript:example.vue
export default defineComponent({
    props: {
        url: { required: true, type: String },
    },
})
```

# 参考
PHPでjson_encodeをせず、Vueで変数を受け取って`console.log`すると、以下の結果になります。
- 文字列の場合
    `undefined`になります。

- 真偽値の場合
    - trueのとき、`1`になります。
    - falseのとき、`true`になります。

- nullの場合
    `空文字`になります。 

- int型の場合
    `number`型の数値になります。

- 配列の場合
    `ƒ Array() { [native code] }`になります。

- オブジェクトの場合
    ページが壊れます。
chartjs-subtitleを使ってみた

チャートに付加情報を書き込みたいと思って探したらありました。

https://github.com/jeredmasters/chartjs-subtitle

こんな感じになります。

![filename(25).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/be708106-2404-9cdb-a714-48c4f2e7eee1.png)

内容はjsonを読んで表示するようにしました。jsonは以下で作っています。

```sh
#!/bin/sh

sysinfo() {
# delete last piriod for jquery pasererror workaround
VAL=`ntpq -crv $1 | awk -F\" '/^v/{ver=$2};/^s/{sys=$2} END{print "{\"system\":\"" sys "\",\"version\":\"" ver "\"}"}'`
if [ -n "${VAL}" ]; then
  echo -n ${VAL} >> $2
fi
}

JSON="/root/htdocs/json/sysinfo.json"

echo -n "[" > ${JSON}
sysinfo 10.0.1.22 ${JSON}
echo -n "," >> ${JSON}
sysinfo 10.0.1.20 ${JSON}
echo -n "," >> ${JSON}
sysinfo 10.0.1.19 ${JSON}
echo -n "," >> ${JSON}
sysinfo 10.0.1.8 ${JSON}
echo -n "]" >> ${JSON}
```

chart.jsはv2を使っていて、新しいのは入っているのかもしれません。
pythonでdatetime.datetime同士を引き算する

# 結論
`datetime.timedelta`型が返ってきます。型の名前から想像できる通り、引き算した日時の差を表しています。
`datetime.timedelta`自体が日時に関するメンバ関数をいくつか持っているため差分計算に役に立ちます。

# 検証コード
```py
import datetime

# 1日と23時間の時間差
dt1 = datetime.datetime(2023, 6, 1, 0, 0, 0, 0)
dt2 = datetime.datetime(2023, 6, 2, 23, 0, 0, 0)

# datetime同士を引き算する
td21 = dt2 - dt1

# 型を調べる
print(f'td1の型  : {type(td21)}')
# datetime.timedeltaをそのまま出力
print(f'td21     : {td21}')
# 何日差かだけを取り出す
print(f'td21.days: {td21.days}')

# 日はまたぐが時間差は24時間以内
dt3 = datetime.datetime(2023, 6, 1, 23, 0, 0, 0)
dt4 = datetime.datetime(2023, 6, 2, 1, 0, 0, 0)

td43 = dt4 - dt3

# datetime.timedeltaをそのまま出力
print(f'td43     : {td43}')
# 日をまたいでいるが時間差が24時間以内のため日は0日差と出力される
print(f'td43.days: {td43.days}')
```


```
td1の型  : <class 'datetime.timedelta'>
td21     : 1 day, 23:00:00
td21.days: 1
td43     : 2:00:00
td43.days: 0
```

以上です。

# 参考

https://note.nkmk.me/python-datetime-timedelta-measure-time/

https://docs.python.org/ja/3/library/datetime.html
LaravelのWhereInメソッドでキーがない場合に対処する。

## 前提
Laravelの`WhereIn`メソッドでは通常キーを指定する。

```.php
$collection = collect([
    ['product' => 'Desk', 'price' => 200],
    ['product' => 'Chair', 'price' => 100],
    ['product' => 'Bookcase', 'price' => 150],
    ['product' => 'Door', 'price' => 100],
]);

$filtered = $collection->whereIn('price', [150, 200]);

$filtered->all();

/*
    [
        ['product' => 'Desk', 'price' => 200],
        ['product' => 'Bookcase', 'price' => 150],
    ]
*/
```

## キーがない場合

`[]`をキーの部分に設定する。

```.php
$collection = collect([1,2,3]);

$filtered = $collection->whereIn([], [1,2]);

$filtered->all();

/*
Illuminate\Support\Collection {#2460 ▼
  #items: array:1 [▼
    0 => "1"
    1 => "2"
  ]
*/
```

## 参考

https://readouble.com/laravel/8.x/ja/collections.html
LightGBMのパラメータエラー解消法：'verbosity should be of type int, got "False"

# はじめに
データ分析や機械学習において、PythonのライブラリLightGBMはその高速性と効率性から非常に人気があります。しかし、LightGBMを使用する際に、モデルのパラメータ設定で問題が生じることがあります。特に、モデルのパラメータ設定が不適切な値であった場合には、エラーメッセージが出力され、適切なパラメータ値に変更する必要があります。

この記事では、特にverbosityパラメータに関連するエラーについて、その原因と解消法について詳しく解説します。

# 問題の発生
例えば、以下のようにLightGBMを設定し、学習を実行したとします。

```
from lightgbm import LGBMClassifier

model = LGBMClassifier(verbosity="False")
model.fit(X_train, y_train)
```
この場合、次のようなエラーメッセージが表示されます。

```
LightGBMError: Parameter verbosity should be of type int, got "False"
```
このエラーメッセージは、verbosityパラメータが不正な値（この場合は文字列の"False"）であるため、LightGBMの学習が失敗したことを示しています。

# 原因の理解
verbosityパラメータは、LightGBMの学習中にどの程度のメッセージを出力するかを制御するためのものです。このパラメータは整数型で、-1, 0, 1, 2 のいずれかの値を取ります。各値の意味は次のとおりです。

-1: 最小の出力（エラーメッセージのみ）
0: エラーと情報
1: エラー、情報、警告
2: エラー、情報、警告、デバッグ情報
上記のコードでは、verbosityパラメータに文字列の"False"が設定されているため、エラーが発生しています。

# 解決方法
エラーを解消するためには、verbosityパラメータに適切な整数値を設定する必要があります。

例えば、メッセージの出力を最小限にしたい場合は、以下のように設定します。

```
model = LGBMClassifier(verbosity=-1)
model.fit(X_train, y_train)
```
こうすることで、エラーが解消し、LightGBMの学習が正常に行われるようになります。

# まとめ
LightGBMのパラメータ設定は、モデルの学習結果や実行速度、ログ出力の量などに直接影響します。パラメータの設定が不適切な場合には、エラーメッセージが出力され、適切な値に設定する必要があります。

特に、verbosityパラメータは出力メッセージの量を制御するため、混乱を避けるためにも適切な値を設定しておくことが重要です。

以上、LightGBMのパラメータverbosityに関連するエラーとその解決法について解説しました。これが皆さんのデータ分析や機械学習の作業に役立てば幸いです。
データの不均衡問題への対処法: アップサンプリングとSMOTE

# データの不均衡問題とその対処法
クラス分類の問題において、一部のクラスのデータが他のクラスに比べて大幅に少ない場合、データの不均衡という状況が生じます。不均衡なデータセットをそのまま使ってモデルを訓練すると、モデルは大多数のクラスを予測することで高い正確性を達成できますが、少数クラスの予測能力は低いままとなります。これに対処する一つの方法が、アップサンプリングという手法です。本記事では、Pythonを用いてアップサンプリングとその一つの手法であるSMOTEの例を紹介します。

## アップサンプリングとは
アップサンプリングは、少数クラスのデータを人工的に増やす（サンプリングアップする）ことで、クラス間のデータのバランスをとる方法です。アップサンプリングには、ランダムサンプリング、**SMOTE（Synthetic Minority Over-sampling Technique）**などの手法があります。

## ランダムサンプリング
ランダムサンプリングでは、少数クラスのデータからランダムにデータを選び、それをコピーしてデータセットに追加します。これにより、少数クラスのデータの数が増え、大多数クラスとのバランスが改善されます。しかし、単にデータをコピーするだけなので、モデルが新たなパターンを学ぶわけではありません。

Pythonを用いたランダムサンプリングの例を以下に示します。ここでは、imbalanced-learnパッケージを使用します。

```
from imblearn.over_sampling import RandomOverSampler
from sklearn.datasets import make_classification
import numpy as np

# 不均衡なデータセットの生成
X, y = make_classification(n_samples=1000, n_features=2, n_informative=2,
                           n_redundant=0, n_repeated=0, n_classes=2,
                           n_clusters_per_class=1,
                           weights=[0.99, 0.01],
                           class_sep=0.8, random_state=0)

# オリジナルデータのクラス分布
print('Original dataset shape %s' % np.bincount(y))

ros = RandomOverSampler(random_state=0)

X_resampled, y_resampled = ros.fit_resample(X, y)

# サンプリング後のクラス分布
print('Resampled dataset shape %s' % np.bincount(y_resampled))
```
## SMOTE
SMOTEはより高度なアップサンプリングの手法で、新たな少数クラスのデータを合成します。SMOTEは以下のステップで動作します：

少数クラスの各サンプルについて、k最近傍法（k-NN）を使って最も近い「隣人」を見つけます。
これらの「隣人」の間でランダムに一つを選びます。
選ばれた「隣人」と元のサンプルとの間に新しいサンプルを生成します。これは、選ばれた「隣人」から元のサンプルに向かう線分上の点をランダムに選ぶことで実現されます。
これにより、新しいサンプルが生成され、元のデータに追加されます。これらの新しいサンプルは元のデータに基づいていますが、元のデータとは異なるため、モデルが新たなパターンを学ぶことが可能となります。

Pythonを用いたSMOTEの例を以下に示します。ここでも、imbalanced-learnパッケージを使用します。

```
from imblearn.over_sampling import SMOTE

sm = SMOTE(random_state=42)

X_resampled, y_resampled = sm.fit_resample(X, y)

# サンプリング後のクラス分布
print('Resampled dataset shape %s' % np.bincount(y_resampled))
```
ただし、SMOTEは単にデータを増やすだけでなく、新しいデータが元のデータの特徴空間内である程度均一に分布するように合成するため、適用する際には注意が必要です。特に、少数クラスのデータが大多数クラスのデータと重なる領域でSMOTEを適用すると、新しく合成されたデータが大多数クラスの領域に侵入する可能性があり、ノイズの多いモデルを生み出す可能性があります。そのため、SMOTEを適用する際には、その効果を確認しながら適切なパラメータを設定することが重要です。
【メモ】HTMLの新機能、popover属性

#はじめに
World Pressでアウトプット用として公開しています。が、小学生の‘感想‘になっています。ご了承ください。
[World Press](http://nira37.jp)
#私は本当に未経験で実務もないので自分用のメモなどで活用していきたいと思います。間違いなどあるとおもいますので、その際はアドバイスお願いいたします。
***

↓題名の関連記事↓

https://coliss.com/articles/build-websites/operation/css/about-popover-api.html

***
↓実践↓
```index.html
    <button popovertarget="my-popover">
            Open Popover </button>
<div id="my-popover" popover>
    <p>これだけで表示/非表示が可能。
        <br>JavaScriptは不要なのか？<p>
</div>
```
```style.css
body{
    background: rgb(242, 250, 228);
    display: flex;
    justify-content: center;
}

#my-popover{
    background: aqua;
    color: black;
    position: absolute;
    top: 300px;
    margin: 0 auto;
}
```
cssが無くても動作を確認できました。なのでHTMLの記述のみで大丈夫。
***
`popovertargetaction`を使用して明示的なポップオーバーを実装できる。
以下`popovertargetaction`のオプション
`popovertargetaction="show`: ポップオーバーを表示します。
`popovertargetaction="hide`: ポップオーバーを非表示します。

結果→ポップオーバー内に×ボタンが追加されたのを確認。

この状態ではページ内において他の箇所を適当にクリックすると勝手に消えてしまう。
***
`popover="manual"` ***手動ポップオーバー***を使う。
この記述により
* 他のポップオーバーを閉じない。
* ライトディスミス不可能。トグルやクローズアクションで閉じる。
上記記述により解決。
***
さらにスタイリング機能もある。```::backdrop```詳細は記事を参照。

https://coliss.com/articles/build-websites/operation/css/about-popover-api.html

記事内ではまだ、私には難易度が高く、自分なりに簡単に実装してみました。

###完成

```index.html
<body>
    <button popovertarget="my-popover">
            Open Popover </button>
    <div id="my-popover" popover="manual">
        <button class="close-btn" 
                popovertarget="my-popover" popovertargetaction="hide">
            <span aria-hide=”true”>❌</span>
            <span class="sr-only">Close</span>
        </button>
    <p>これだけで表示/非表示が可能。
        <br>JavaScriptは不要なのか？
        </p>
    </div>
    <img class="back" src="img/back.jpg" alt="">
</body>
```
```style.css
body{
    background: rgba(38, 255, 0, 0.58);
    width: 100%;
}

#my-popover::backdrop{
    background: rgba(190, 190, 190, 0.5);
    color: black;
    position: absolute;
}
.back{
    max-width: 80%;
    display: flex;
    justify-content: space-between;
    margin: 50px auto;
}
```
ざっくりと記述したので見づらいと思います。ご了承ください。
↓結果↓
![result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3372034/9ff4b954-b4d4-65ee-2e1a-2f9b0c51327f.png)


***
#最後に
ご指摘などあればコメントなどしてくれますと幸いです。
BigQuery 日本語カラムを触ってみた

# BigQuery 日本語カラムを触ってみた

## はじめに
2023/3/22 から、BigQuery のテーブルのカラムに日本語を使えるようになりました！

https://cloud.google.com/bigquery/docs/release-notes#March_22_2023

リリース直後コンソール画面から日本語が設定できなかったので BigQuery Storage API でしか使えないと勘違いしていたのですが、反映が遅かっただけでコンソール画面から設定できるようになっていました。
公式ドキュメントは以下です。

https://cloud.google.com/bigquery/docs/schemas?hl=ja#flexible-column-names

この記事では日本語カラムを使ってみた感想をまとめます。

## 設定方法
コンソールの「テーブルを作成」パネルの「スキーマ」で指定できます。
全角数字や半角カタカナも指定可能でした。
以前は日本語をカラム名に入力するとエラーメッセージが表示されていたと思います。

![テーブル作成_手動.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426544/6c664ba3-b071-1142-fdc5-6ae42c2fdd77.png)


実際テーブルを作るときはスキーマの自動検出をよく使ってますが、こちらはまだプライベートプレビュー中でした。

## クエリ実行
クエリ実行時に日本語カラムを指定する場合は `'` でカラム名を囲む必要がありました。

![クエリ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426544/64c1ca1d-f3a2-dec9-aae6-f6ebc92b5268.png)


## テーブルのエクスポート
GCS、スプレッドシートへのエクスポートでは、正常にカラム名が表示されました。

![スプレッドシート.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426544/8cecf259-ef90-2f04-1e86-594b70e3e97f.png)


Looker Studio ではビュー部分でエラーが発生しました。INTEGER 型カラムでも同様のエラーが発生したため、日本語カラムに対応していないと思われます。

![LookerStudio.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426544/b5084e36-974f-7f04-b478-3f784745afe0.png)


## 最後に
Datastream for BigQuery のリリースなど他システムからデータ取り込みができる機能が増えている印象ですが、
日本語カラム対応で連携がより簡単になりますね！

以上です。
Ruby2.1技術者認定試験 Silver (例題解説).002

- ## 始めに
  週1回以上を目標に、この例題解説記事を継続していきます。
  書き慣れてきたら、別の題材の記事にも挑戦していきます。
  <br>

- ## 例題
  **Q .下記の実行結果を得るためのコードを選択肢の中から選べ。**

  ```ruby
  id = [1, 2, 3, 4, 5]
  [コード実行]
  => [3, 4]
  ```
  
  ① p id.slice(3..4)
  ② p id.slice(-3, 3)
  ③ p id[-3..3]
  ④ p id[2, 3]
  <br>

  :::note alert
  **解説をみる前に答えを予想してみて下さい。**
  
  ↓下に解説があります。↓

<br><br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>

- ## 解説
  
  各選択肢毎に解説し、最後にまとめて結果を表示する。
  
  - **事前説明**
    **[1\*]**`配列.slice(n)`は、指定されたインデックス(配列番号n番目)の要素を戻り値として返すメソッド。配列に直接インデックスを指定する方法と同じ結果になる(下記参照)。
    
    ```ruby
    id = [1, 2, 3, 4, 5]
    p id.slice(0)  # silceメソッド使用
    => [1]
    p id[0]  # 直接指定
    => [1]
    ```
  
  - **①**
    配列のインデックス指定方法として`(a, b)`とした場合、`a 番目から b 個分`の要素を選択できます。
    今回は`(3..4)`の為、`3番目から4番目まで` **(コンピューター上では 0 から数えるので、人間的な数え方としては 4～5 番目の意味)** のインデックスの要素が選ばれるので、結果は『[4, 5]』です。
  
  - **②**
    インデックスの指定方法として`(-a, b)`とした場合、**後ろから数えて**`a 番目から b 個分`の要素を選択できます **(※)**。
    今回は`(-3, 3)`なので、`後ろから3番目から3個分`が表示され、結果は『[3, 4, 5]』となります。
      ```ruby
      # (※)... インデックスの指定が後ろからの場合は0 からではなく-1 から数える。
      
      id = [1, 2, 3, 4, 5]
    
      # と定義した場合、↓の様にインデックスが指定される。
      
      #	   虚数配列(存在しない) ← | → 本来の配列
      #		[ 1,  2,  3,  4,  5] [ 1,  2,  3,  4,  5]
      #		  ↑   ↑   ↑   ↑   ↑    ↑   ↑   ↑   ↑   ↑
      #		 -5, -4, -3, -2, -1,   0,  1,  2,  3,  4 ..... インデックス(配列番号)
      ```
  
  - **③**
    今回は`[-3..3]`の為インデックス`-3～3`の要素 **([1\*]、選択肢①参照)** となりますが、`id[-3,-2,…,2,3]`の7つを表示するわけではありません。
    **インデックスがマイナス**の場合は本来のインデックスに戻してからその間の要素を返す為、`[-3..3] = [2..3]`**(選択肢②の(※)参照)** となり結果は『[3, 4]』になります。
  
  - **④**
    今回は`[2, 3]`の為インデックス`3から3個分`の要素 **([1\*]、選択肢②参照)** が表示されるので、結果は『[3, 4, 5]』になります。

    ```ruby
    irb(main):004:0> id = [1, 2, 3, 4, 5]
    => [1, 2, 3, 4, 5]
    irb(main):005:0> p id.slice(3..4)     # 1
    [4, 5]
    => [4, 5]
    irb(main):006:0> p id.slice(-3, 3)     # 2
    [3, 4, 5]
    => [3, 4, 5]
    irb(main):007:0> p id[-3..3]     # 3
    [3, 4]
    => [3, 4]
    irb(main):008:0> p id[2, 3]     # 4
    [3, 4, 5]
    => [3, 4, 5]
    ```
  
  よって答えは『**③ p id[-3..3]**』
  <br>
  <br>
  - **参照ページ**
    *[Array#slice (Ruby 3.2 リファレンスマニュアル)](https://docs.ruby-lang.org/ja/latest/method/Array/i/slice.html "Array#slice (Ruby 3.2 リファレンスマニュアル)")*

<br>
<br>

- ## 最後に
  
  確認として`id = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]`を定義して、上と同じ条件の出力や上と同じ出力になる条件を探して色々遊んでみて下さい。
  
  「こんな条件、どこで使うん？」と思うものも、勉強していく上で使える場面が見つかることもあります。
(個人的には、今回のsilceメソッドや③の指定方法等)
  
  同じ結果になる色々な条件やメソッドを知り、クライアントの求めている条件に合った方法の条件式が書けるよう、一緒に頑張っていきましょう。
  
  <br><br>
  最後まで読んでいただき、ありがとうございました。
【PHP】スネークケースからパスカルケースへの変換

## 書いたコード

```php
<?php
declare(strict_types=1);

class Common 
{
    public static function toPascal(string $string): string
    {
        return str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
    }
}
?>
```

## 実行結果
```php
echo Common::toPascal('hello_world') // => HelloWorld
```
このようにして、スネークケースからパスカルケースへの変換が出来ました。

:::note 
実行結果を確認したい場合
この[サイト](https://paiza.io/ja/projects/new)を使うと手っ取り早いです。
:::


## コードの解説
- **`declare(strict_types=1);`** とは？
    - 厳格な型付けを行っています。
- **`public`** とは？
    - どの範囲からでもアクセス可能であることを示しています。
    - **`public`** をつけることによって、他のクラスやファイルから呼び出すことができるようになります。
- **`static`** とは？
    - 簡単に言うと、クラスのインスタンスを生成することなしに、利用できるプロパティやメソッドのことです。
    - すなわち、staticを用いるとインスタンスを生成しなくてもクラスのオブジェクトのアクセスが可能になります。
- **`str_replace`** とは？
    - 検索文字列に一致したすべての文字列を置換します。
- **`ucwords`** とは？
    - 文字列の各単語の最初の文字を大文字にします。

そして、
```php
return str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
```
の部分でどこから実行されるのかが分かりにくいと思うので、そこについて説明します。

結論、指定されたコードの実行の順序は、**内側から外側へ** と進みます。
 以下に、一行のコードの実行の手順を説明します。

1. **`str_replace('_', ' ', $string):`**
    - まず、与えられた文字列 $string 内のアンダースコア _ を空白文字に置換します。


2. **`ucwords(str_replace('_', ' ', $string)):`** 
    - アンダースコアが空白文字に置換された結果の文字列に対して、ucwords 関数を適用します。これにより、文字列内の各単語の先頭文字が大文字に変換されます。

3. **`str_replace(' ', '', ucwords(str_replace('_', ' ', $string))):`**
    - 先ほどの結果の文字列内の空白文字を削除します。



## 参考サイト・記事一覧

- [厳格な型付けについて](https://www.php.net/manual/ja/language.types.declarations.php#language.types.declarations.strict)
- [【PHP入門】staticプロパティとstaticメソッド](https://medium-company.com/php-static/)
- [PHP: str_replace - Manual](https://www.php.net/manual/ja/function.str-replace.php)
- [PHP: ucwords - Manual](https://www.php.net/manual/ja/function.ucwords.php)
RustのCORS設定でハマったので記録。まさか "/"が問題だったとは。

RustでAxumを使ってAPIサーバーを作っていたのですが、適切にCORS設定をしているのにも関わらずCORSエラーが発生する事案が発生しました。その報告とエラー解消を記録します。

# 状況

Axumを使ってAPIサーバーを建てていました。以下のようなイメージです（Axumのサンプルを改変）。

```rs
use axum::{
    routing::get,
    Router,
};
use tower_http::cors::CorsLayer;

#[tokio::main]
async fn main() {
    let origins = [
        "http://aaa.com".parse::<HeaderValue>().unwrap(),
        "http://bbb.com/".parse::<HeaderValue>().unwrap()
    ];

    let app = Router::new()
        .route("/", get(|| async { "Hello, World!" }));
        .layer(
            CorsLayer::new()
                .allow_origin(origins)
        );

    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}
```

ポイントとしては以下の点になります。

1. 複数のオリジンに対しアクセス許可をするため、`origins`を`HeaderValue`のListとしている
2. 2つ目のoriginが`http://bbb.com/`のように最後に`/`が挿入されている

上記のようなシチュエーションで、`http://aaa.com`からはリソースにアクセスできるのに、`http://bbb.com`からはCORSエラーが発生し、リソースにアクセスできない、という問題が発生していました。

# 原因と解決策

解決策ですが、オリジン設定の際に`http://bbb.com/`ではなく、`http://bbb.com`のように **`/`を削除して設定を行うことが必要**です。

つまり、以下のようにorigin変数を束縛します。

```rs
// Error
let origins = [
    "http://aaa.com".parse::<HeaderValue>().unwrap(),
    "http://bbb.com/".parse::<HeaderValue>().unwrap()
];

// OK
let origins = [
    "http://aaa.com".parse::<HeaderValue>().unwrap(),
    "http://bbb.com".parse::<HeaderValue>().unwrap()
];
```

自分的には、`http://bbb.com`でも`http://bbb.com/`でもオリジンは同じなので問題ないのでは？と考えており、上記解決策になかなか気付けずハマってしまいました。

原因としては`tower-http`ライブラリの実装に起因します。

CORSの設定・処理を行う`CorsLayer`は`tower-http`ライブラリからインポートして使用しているのですが、その中でAllow-Contrl-Allow-Originの設定を行う`AllowOrigin`の実装を見ると、以下のようにヘッダーを作っています。

```rs
// ~ 略
pub(super) fn to_header(
    &self,
    origin: Option<&HeaderValue>,
    parts: &RequestParts,
) -> Option<(HeaderName, HeaderValue)> {
    let allow_origin = match &self.0 {
        OriginInner::Const(v) => v.clone(),
        OriginInner::List(l) => origin.filter(|o| l.contains(o))?.clone(),
        OriginInner::Predicate(c) => origin.filter(|origin| c(origin, parts))?.clone(),
    };

    Some((header::ACCESS_CONTROL_ALLOW_ORIGIN, allow_origin))
}
// ~ 略
```

https://github.com/tower-rs/tower-http/blob/master/tower-http/src/cors/allow_origin.rs

紛らわしいかもしれませんが、`to_header`メソッドの第一引数`&self`はアクセスを許可するオリジン、第二引数`origin`はリクエストが送られてきたオリジンになります（`to_header`メソッドを使っている[こちら](https://github.com/tower-rs/tower-http/blob/master/tower-http/src/cors/mod.rs)も参照）。

複数のオリジンを指定した場合、`origin.filter(|o| l.contains(o))?.clone()`の部分を見れば分かる通り、 **リクエストのオリジンと指定したアクセス許可するオリジンのリストを比較し、そのリストの中にリクエストのオリジンが含まれていれば、そのオリジンをAccess-Control-Allow-Originヘッダに設定しています**。

そのため、エラーが発生した原因としては、

- リクエストのオリジンは`http://bbb.com`である
- アクセスを許可は`http://bbb.com/`と設定してる
- `http://bbb.com`は`http://bbb.com/`と一致していないと判断され、Allow-Control-Allow-Origin設定が反映されていない（ように見える）

となります。

また、上記のコードの通り、単一のオリジンのみにアクセスを許可する場合はリクエストのオリジンとの比較を行っていないため、`http://bbb.com/`でも問題なさそうです。

スラッシュを削除するだけなのでエラー解消自体は簡単ですが、中々原因に気付けずハマってしまいました、、、