OmniAICreator/Qiita-1.07M huggingface dataset:
ALBに自己署名証明書を導入

## 背景

システムとしてはAWSのPrivateCAを利用してプライベート認証機関を立て、証明書を発行して利用していました。しかし、AWSのPrivateCAは月額400ドルかかるため、コスト削減のために開発環境の一部ではPrivateCAの利用をやめ、自前でPrivateCAおよびサーバー証明書を発行することにしました。

更新作業を簡略化するため、証明書の有効期限は100年に設定しています。

---

## 証明書作成 & ACM登録シェル

以下は自己署名証明書を作成し、AWS ACMに登録するスクリプトの例です。

```bash
#!/bin/bash

domain=example.internal
cn=example-cn

# 各種鍵の保管場所作成
output_dir=output
mkdir -p ${output_dir}

# OpenSSLの設定ファイルを作成
openssl_ca_conf=${output_dir}/openssl_ca.cnf

cat <<EOF > ${openssl_ca_conf}
[ req ]
default_bits       = 2048
prompt             = no
default_md         = sha256
distinguished_name = dn
x509_extensions    = v3_ca

[ dn ]
C                  = JP
ST                 = Tokyo
L                  = Adachi
O                  = Example, Ltd.
OU                 = Example
CN                 = ${cn}

[ v3_ca ]
basicConstraints    = critical,CA:TRUE
subjectKeyIdentifier = hash
keyUsage            = critical,digitalSignature,keyCertSign,cRLSign
EOF

openssl_conf=${output_dir}/openssl.cnf
cat <<EOF > ${openssl_conf}
[ req ]
default_bits        = 2048
prompt              = no
default_md          = sha256
req_extensions      = req_ext
distinguished_name  = dn

[ dn ]
C                   = JP
ST                  = Tokyo
L                   = Adachi
O                   = Example, Ltd.
OU                  = Example
CN                  = *.${domain}

[ req_ext ]
subjectAltName      = @alt_names

[ alt_names ]
DNS.1               = *.${domain}
EOF

# プライベートルート証明書の作成
openssl genrsa -out ${output_dir}/rootca.key 2048
openssl req -new -x509 -key ${output_dir}/rootca.key -sha256 -days 36500 -out ${output_dir}/rootca.pem -config ${openssl_ca_conf} -extensions v3_ca

# サーバ証明書の作成
openssl genrsa -out ${output_dir}/server.key 2048
openssl req -new -key ${output_dir}/server.key -out ${output_dir}/server.csr -config ${openssl_conf}
openssl x509 -req -in ${output_dir}/server.csr -CA ${output_dir}/rootca.pem -CAkey ${output_dir}/rootca.key -set_serial 01 -days 36500 -out ${output_dir}/server.pem -extfile ${openssl_conf} -extensions req_ext

# CLIでAWS ACMへ証明書をインポート
aws acm import-certificate --certificate fileb://${output_dir}/server.pem --private-key fileb://${output_dir}/server.key --certificate-chain fileb://${output_dir}/rootca.pem
```

---

## ALBへの登録

作成した証明書をALBに登録する手順は以下の通りです。

1. HTTPSリスナーを開き、証明書タブから「デフォルトを変更」をクリックします。
2. ACMにインポートした証明書を選択し、デフォルトとして設定します。

---

## ルート証明書の信頼設定

ALBにアクセスする端末に対して、以下の設定を行います。

1. `rootca.pem` を端末にインストールします。
2. 各OSの手順に従ってルート証明書として信頼を追加します。

---

## アプリ側のトラストストアの更新

アプリケーションからALBにリクエストを送信する場合、アプリケーションのトラストストアにルート証明書を登録する必要があります。
以下はトラストストア作成のコマンド例です。JKS形式にしたかったため、keytoolを使用しています。
  ```
  keytool -import -alias example-truststore -keystore truststore.jks -file 
  rootca.pem
  ```
Xのだるまを回したら手元でもだるまを回す


以前バナナを回そうとして360度サーボの限界を知った時がありました。

https://x.com/n0bisuke/status/1837025046981672985

https://x.com/n0bisuke/status/1837018510691799408

ステッピングモーターを使うといいよという話

https://qiita.com/n0bisuke/items/1ec3227e1bfb586ae060

## Xのだるま コナミコマンドで回せるので拡張機能を作りました

> ![スクリーンショット 2024-12-27 21.36.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/5381e348-580c-ddcc-f3cb-e8d642be05b8.png)
istioのretry設定

OpenShift Service Mesh（2.5）管理下のアプリケーションへのリクエストが勝手にretryされていたので調査。  


## 1. デフォルト設定

istio-proxyの設定を確認。デフォルトのretry_policyだと5xxエラーなどの際に2回リトライする設定となっている。

```
$ oc rsh -c istio-proxy test-pod curl -s http://localhost:15000/config_dump |jq '.configs[] | select(.dynamic_route_configs) | .dynamic_route_configs[] | .route_config.virtual_hosts[] | select(.name == "test.hoge.svc.cluster.local:80")' 

{
  "name": "test.hoge.svc.cluster.local:80",
  "domains": [
    "test.hoge.svc.cluster.local",
    "172.21.24.200"
  ],
  "routes": [
    {
      "match": {
        "prefix": "/"
      },
      "route": {
        "cluster": "outbound|80||test.hoge.svc.cluster.local",
        "timeout": "0s",
        "retry_policy": {
          "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
          "num_retries": 2,
          "retry_host_predicate": [
            {
              "name": "envoy.retry_host_predicates.previous_hosts",
              "typed_config": {
                "@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"
              }
            }
          ],
          "host_selection_retry_max_attempts": "5",
          "retriable_status_codes": [
            503
          ]
        },
        "max_grpc_timeout": "0s"
      },
      "decorator": {
        "operation": "test.hoge.svc.cluster.local:80/*"
      },
      "name": "default"
    }
  ],
  "include_request_attempt_count": true
}
```

## 2. 設定方法

デフォルト設定を変更したい場合、`VirtualService`の`retries`フィールドを明示的に設定する。

### 設定例

```yaml
spec:
  hosts:
    - test
  http:
    - route:
        - destination:
            host: test
      retries:
        attempts: 5          
        perTryTimeout: 1s           
        retryOn: "5xx,connect-failure,refused-stream"
```

- **`attempts`**：リトライ回数。無効にしたい場合は0とする
- **`perTryTimeout`**：各リトライのタイムアウト時間。
- **`retryOn`**：リトライ条件。


### 参考
- [Istio](https://istio.io/latest/docs/concepts/traffic-management/#retries)  

- [Envoy](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#x-envoy-retry-on)
redux-toolkitでローカルストレージにデータを永続化する方法

## データを永続化したい理由
ブラウザをリロードするとグローバルStateが初期化されたくないとき。
バックエンドを使う場合はセッションを使うけど、フロントエンドだけで実現したいときはローカルストレージに保存する。

ローカルストレージに直接データを保存することもできるけど、Reduxを使う場合は`redux-persist`を使うのが良い。

### redux-persistのインストール
```bash
npm install redux-persist
```

### storeの定義
```ts:store.ts
import { configureStore } from '@reduxjs/toolkit';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage'; // ローカルストレージを使用
import userSlice from './userSlice'; // ユーザーデータを管理するスライス

const persistConfig = {
  key: 'root', // ストレージに保存するキー
  storage,
};

const persistedReducer = persistReducer(persistConfig, userSlice.reducer);

const store = configureStore({
  reducer: {
    user: persistedReducer,
  },
});

const persistor = persistStore(store);

export { store, persistor };
```

### Reduxプロバイダーを設定
redux-persistの`PersistGate`を使用して、ストアの再初期化が完了するまでUIを保留する。
```tsx:App.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { store, persistor } from './store';
import App from './App';

ReactDOM.render(
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <App />
    </PersistGate>
  </Provider>,
  document.getElementById('root')
);
```

### useSliceの作成
ここは普通のSlice作成
```ts:useSlice.ts
import { createSlice } from '@reduxjs/toolkit';

const userSlice = createSlice({
  name: 'user',
  initialState: {
    userId: null,
  },
  reducers: {
    setUserId: (state, action) => {
      state.userId = action.payload;
    },
    clearUserId: (state) => {
      state.userId = null;
    },
  },
});

export const { setUserId, clearUserId } = userSlice.actions;
export default userSlice;
```



## redux-persistとRedux-toolkitを一緒に使うと発生する可能性があるエラー

### A non-serializable value was detected in an actionエラー
和訳すると`アクションでシリアル化できない値が検出されました`であり、Reduxでシリアライズ不可なデータを使おうとするために発生するエラー

#### シリアライズ化とは
データ構造やオブジェクトを保存や送信が可能な形式（通常は文字列形式）に変換すること。

:::note info
シリアライズ化できるデータ
 - プリミティブ型
 - オブジェクト
 - 配列
:::

:::note warn
シリアライズ化できないデータ
 - 関数
 - 特殊なオブジェクト(Promise、Symbol、Map、Set)
 - クラスインスタンス
:::

### Reduxは関数やクラスインスタンス（シリアライズ不可データ）を使えないが、redux-persistはシリアライズ不可データをつかえる
Reduxはシリアライズ可能なデータのみを扱う設計になっているので、プリミティブ型やオブジェクト配列しか使えない。一方で、redux-persistはシリアライズ不可なデータ（関数やクラスインスタンス）も使える。


### 対応方法：Redux-toolkitのシリアライズデータチェックを除外する
Redux-toolkitの`configureStore`で格納するデータがシリアライズできるかどうかをチェックしており、その設定をミドルウェアで制御できる。
redux-persist由来のアクションはシリアライズできるかどうかのチェックを除外する。

```diff_tsx:
const persistedReducer = persistReducer(persistConfig, userReducer);

const store = configureStore({
  reducer: {
    flow: flowReducer,
    layout: layoutReducer,
    respondent: respondentReducer,
    user: persistedReducer,
    // user: userReducer,
  },
+  middleware: (getDefaultMiddleware) =>
+    getDefaultMiddleware({
+      serializableCheck: {
+        // 非シリアライズ可能な値を無視するための例外を指定
+        ignoredActions: [
+          "persist/PERSIST",
+        ],
+      },
    }),
});

```
【クソアプリ】LLMの気分になれるQAフォーラム作った

皆さんはLLMの仕組みをご存じでしょうか？
OpenAIのAPIを利用したことがある人はご存じだと思いますが、1トークンずつ文字列が決定されることを繰り返しています。

実はあれ、**中で小人が多数決で決定**しているんですよ...

今回はその小人側になれるクソアプリを突貫で作成しました。

クソアプリアドベントカレンダーでモチベが上がってしまったので仕方ないですね。


https://llm.primitive-ojisan.com/


## つかいかた

### 質問を作る

左上の鉛筆マークが新規作成です。
質問文、temperatureを設定してRunをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1977360/4d4f7bb3-b49c-3b52-ef8a-5650042c3e38.png)

### 質問に回答する

回答画面では10秒に1文字入力して投票でき、投票データが確認できます。
EOS（終了トークン）はボタンで入力できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1977360/fdcb5e8f-9310-00b8-4188-ee1e3f0dbe01.png)


### 感想
作っていたらこっくりさんとか、2chの〇〇が完成したらのノリを思い出しました。

※ 予告なくメンテナンス・終了する可能性があります。


### インフラの選定

#### ~~Aurora Serverless v2~~
今回のアプリケーションの要件として、投票の集計がありました。
個人開発御用達のNoSQLではこの機能が用意されておらず、DynamoDBでやろうとするとデータをすべてAPサーバー内のRAMに読み込む必要があり、投票が多い場合にメモリ不足を引き起こす可能性がありました。
また、一つのドキュメントサイズ制限も厳しいため、トランザクションで投票結果を都度集計するなど工夫してもスケールしないと考えました。

これらの理由からRDBを採用する必要がありましたが**クソアプリにお金はかけられない**のでAurora Serverless v2を採用し、休眠期間中の請求をカットしました。
~~個人的にはSupabaseとの二択にできると考えています。~~
supabaseのほうが安いので移行しました

#### GCP Cloud Run
サーバーレスのコンテナ実行環境で、AWS App Runnerと異なりゼロスケールが可能です。

AWSでまとめることができればよかったのですが、lambdaではinit durationの上限に達し実行できませんでした。
Remixでフルスタックアプリにしていることの弱点ですが、手軽に作れてSSR対応できるのは大きいです。

### バグ修正とか

#### prismaがタイムアウトする
aurora serverless v2のコールドスタートが長すぎてタイムアウトしました。
pool_timeout=60を接続設定に追加することで解決した...はずですがインフラ周りは検証がむずかしいですね。

### データベース接続パスワードがローテーションされる
GCPからAuroraを利用している時に起こりました。
AWSで完結していないので、シークレットマネージャーのパスワードローテーションに追随できませんでした。
ローテーションは無しにすることで事なきを得ました。
IoT デバイスに必要な軽量な OS である ZephyrRTOS は Linuxとどう違うか  (IoT の o)

# OS と エッジデバイス

## I と T のあいだ
 

 
- 「I」nternet 
- 「o」f 
- 「T」hings

の 「o」 は 「I」nternet と「T」hings の中間。
リアル世界では、「I」nternet と「T」hings の間にあるものはエッジデバイスとなる。



特にエッジデバイスのソフトウェアとして、簡単に作るなら例えば Arduino IDE などでファームウェアを作ることになる。


Arduino 開発環境はコンピュータソフトウェア開発という観点からはライブラリ集ぐらいの簡単なものだけれど、
リアルタム性とか、TCP/IP を使って立て込んだようなことをしようとしていると  もうちょっと OS 的なものが欲しくなる。

## IoT の OS

RaspberryPi などだと Linux が使われる。しかしながら Linux を動かすとなると メモリ64M とストレージ、仮想メモリ、ブートローダーやユーザ管理などをお膳立てしてあげないといけない。
エッジ側はなるべくライトウェイトな構成にしたいので、フルスペックの Linux は大げさすぎることが多い。
IoT エッジデバイスにおいての OS は、少ないリソースで動くものを使いたい。

## RTOS
そういったもののうち、リアルタイム性能が優秀なものは RealTimeOS 、RTOS というジャンルのものがある。 リアルタイム性能は、逆に Linux では実現が難しかったりするのでそういったものを重視する場合には特に、RTOS が使われる。


# お仕事と RTOS

RTOS としてはいろんなものがあり、多くはお仕事の道具という感じ。

- FreeRTOS
- Micrium μC/OS
- TI RTOS
- GreenHills Integrity
- WindRiver VxWorks
- ITRON

# Zephyr RTOS

今回、 Zephyr RTOS を試しに使ってみて、紹介してみます。

上記に示したように RTOS はお仕事の道具的なニュアンスがありますが、Zephyr はホビーとしても、お気楽に使える感じです。
フリーでありある程度いろんなものにポートされていて、また作例も多く使いでがありそうです。


## Zephyr の特徴

- オープンソース
- 軽量  簡単なマイコンの小さなフットプリントで動作
- 通信に強い
- POSIX 対応
- HAL  がある
- Apatch 2.0 License
- Linux ファウンデーションの派生プロジェクトとして活動
- 割と大きな各企業が参加 https://www.zephyrproject.org/project-members/
- intel Atom プロセッサ内のリアルタイム管理の仕組みにも使用
- コンパレータのAPIがあるぞ

# Zephyr RTOS と Linux の類似性

Linux ファウンデーションの派生という背景があるためか、Linux と似た概念がある。
- HALがある
- ドライバーの管理が容易
- 通信に強い
- OSS
- 多くのアーキテクチャに対応 (x86, ARM, RISC-V, 他)

などなど。
-
# Zephyr RTOS と Linux の違い

しかしながら Linux とは何が違う? ということはあまり話題にはならない。今回は具体的な違いを。

## リソース

Linux に必要なリソースは、最低限のメモリは数十MByte ぐらい。MMU が必要。ストレージとして数十MBytesは必要。
ZephyrRTOS は例として WCH 社の CH32V003 でも動作している。CH32V003 は RAM 2kBytes、ストレージに相当するプログラムメモリーは16KBytes。 MMUは必要ない。
https://qiita.com/nanbuwks/items/22680849ee7674d66a89

## ハードウェアコスト

先のリソースに関連して、最小のハードウェアで実現できるもののお値段。

Linux：ワンチップでLinuxが動作できるような、プロセッサ、メモリ、フラッシュ内蔵もので、以前Linux名刺などで話題になった F1C100S SOCだと500円ぐらい。たくさん買うと300円ぐらい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/26b7e2fa-49cd-df08-ff06-367acff52c25.png)



https://qiita.com/nanbuwks/items/d6a37924e3c21ed3449b

ZephyrRTOS：先に挙げた CH32V003 は50円。たくさん買ったら15円ぐらい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/6d81f0e7-3d27-1090-97d1-6cd6fa13146a.png)



## OS を構成するファイル

- Linux はカーネル、ドライバ、シェル、アプリなど様々なファイルから成る。
- ZphyrRTOS は単一のバイナリのみで動作。

## ブートローダー

- Linux は その他の GRUB や U-Boot などから呼び出される必要がある。
- ZphyrRTOS にはブートローダーが組み込まれている。

## ユーザ

- Linux: マルチユーザ
- ZphyrRTOS: Linux と同じようなユーザの概念はなく、せいぜいセキュリティモデルによる区別ぐらい

## ライセンス

- Linux: GPLv2
- ZphyrRTOS: Apache

## ユーザーインターフェース

- Linux: GUIとしてデスクトップがあったり、CUIとしてログオンできたりする。
- ZphyrRTOS： ユーザ操作のためのUIはあるが、OSを操作するためのUIは無いことが普通。


## 用途

- Linux：割と高機能。画面表示やサーバー機能などが必要で Linux を用いるとメリットがある。センサ1つをクラウドに接続するだけなどの用途であれば Linux でもできるが、高い mcu を使うともったいなくなる。
- ZphyrRTOS：高機能にもできるけれども、例えば温度計1つをネットにつなぐだけ、というような用途でも安い mcu との組み合わせで威力を発揮。


## 開発コミュニティのコミュニケーションチャネル

- Linux：メーリングリスト
- ZphyrRTOS： GitHub や Discord

## マスコット

- Linux：ペンギン
- ZphyrRTOS：凧


ほかに気づいていけば書き足していきます。
Google認定資格 [Associate Cloud Engineer] の取得までにやったこと

本日、勤務時間中にAssociate Cloud Engineer資格試験にのぞみ、無事合格をしました。
Google Cloud未経験から3カ月で本資格を取得できたので、1つの成功体験として留めておこうと思います。

# 目次
1. この資格で証明できること
1. 教材と勉強方法
1. 所感

## 1.この資格で証明できること
今回取得したAssociate Cloud Engineerの取得によって、どんな技能が証明できるのか？
[公式のサイト](https://cloud.google.com/learn/certification/cloud-engineer?hl=ja)には以下のとおり記載されています。
>Associate Cloud Engineer 試験では、以下のことを行う能力が評価されます。
>クラウド ソリューション環境の設定
>クラウド ソリューションの計画と構成
>クラウド ソリューションのデプロイと実装
>クラウド ソリューションの正常な運用
>アクセスとセキュリティの構成

実際に受験をした感想として、上記を行うまでの技能はついていないという感覚です。
問題の傾向として、あるユースケースに対してどうするのがよいか という問いが多いので、
リソースをどのように選ぶか までの理解は深まりますが、どう実装するか までの理解にまでは至っていません。
実経験を積む中でこの辺りは理解が深まると思うのでそのための土台ができたかなという感覚です。

## 2. 教材と勉強方法
私は2つの教材を使用しました。
* [Google Cloud Skill Boost](https://www.cloudskillsboost.google/course_templates/77?catalog_rank=%7B%22rank%22%3A2%2C%22num_filters%22%3A0%2C%22has_search%22%3Atrue%7D&search_id=40629595)
* [模擬試験](https://it-concepts-japan.com/qsm_quiz/google-cloud-associate-cloud-engineer-%e6%a8%a1%e6%93%ac%e8%a9%a6%e9%a8%932)

Google Cloud Skill Boostについては一通り受講をしましたが、あまり参考にならなかった印象です。（コスパが悪い）
Google特有の絶妙に読みずらい日本語訳で、受講自体が苦痛でした。ただ、リソースの種類や大まかな役割についてはここで把握できたと思います。
模擬試験について、勉強のほとんどの時間を割きました。プレミアム問題集を使用し、3周しました。
回答を見ても理解できないものは、ChatGPTを使用して理解を深めました。
以下のフォーマットのプロンプトを使用していました。
```
以下の問題文と回答の選択肢があります。
正しい答えは選択肢のうちどれですか。またその理由を教えてください。
[問題文]
[選択肢A]
[選択肢B]
[選択肢C]
[選択肢D]
```

## 3. 所感
今回、Associate Cloud Engineerを取得しました。
このおかげで、実務で扱っている環境を見ても抵抗感なく作業ができるようになりました。
インフラ設計はまだまだできないので、実経験を積んでいけたらと思います。
VSCode の Jupyter Notebook で実行中の ipynb ファイルのファイル名を取得する

## はじめに
前に調べてすっかり忘れてしまっていたので備忘録として残します．
ググっても情報がほとんどないので，以前の自分はどうやって調べたのか不明すぎる．

## 方法
たったこれだけ．
```python
print(__vsc_ipynb_file__)
# >>> home/path/to/file.ipynb # linux は homeからのパス
# >>> c:\Users\username\path\to\file.ipynb # Windows はcドライブからの絶対パス
```
なお string オブジェクトです．
```Python
print(type(run_file))
# >>> <class 'str'>
```

## 参考文献
* https://stackoverflow.com/questions/70219603/get-jupyter-notebook-name-while-using-vscode
自宅K8sサーバーへMetallbを導入

# 環境
- K8s v1.31.2
- CNI Flannel
- Master×1の単一ノード

# やりたいこと

- MetallbのL2モードをK8s環境に入れたい

# 作業

### ①Helmでmetallbをインストール(metallb-systemというnsで実行)

https://metallb.universe.tf/installation/

```bash
helm repo add metallb https://metallb.github.io/metallb
helm install metallb metallb/metallb
```

### ②L2モードの設定を入れる(IPAddressPool,L2Advertisement)

https://metallb.universe.tf/configuration/

```yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: default
  namespace: metallb-system
spec:
  addresses:
  - 192.168.10.201-192.168.10.250
  autoAssign: true
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: default
  namespace: metallb-system
spec:
  ipAddressPools:
  - default
```

LBに割り当てるIPはホストのネットワークの範囲。今回は192.168.10.200~192.168.10.250。

他の古いサイトとかでは`configmap` を使用しているが、現在はCRDによる構成設定が可能になったため使用できない。

### ③サンプルサイトをデプロイ

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.27.3
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: default
  labels:
    app: nginx
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
  selector:
    app: nginx
```

serviceでExternal IPが先ほど指定したIPプールの範囲で割り当てられていることを確認。

### ④単一ノードの`node.kubernetes.io/exclude-from-external-load-balancers`の削除(環境が単一ノードの場合に限る)

https://metallb.io/troubleshooting/#using-wifi-and-can-t-reach-the-service

どうやら、単一ノードの場合デフォルトでノードに`node.kubernetes.io/exclude-from-external-load-balancers`というラベルが付いているので

そのラベルのためmetallbはノードからサービスをアドバタイズしない。

解決法

- `node.kubernetes.io/exclude-from-external-load-balancers`ラベルの削除
- MetalLBのスピーカー設定で`--ignore-exclude-lb`フラグを有効にする

今回はラベルの削除を行った。

```yaml
kubectl label node <ノード名> node.kubernetes.io/exclude-from-external-load-balancers-
```

これをしないと、ホストからはアクセスできるがホストのlanにあるPCからはアクセスできず、arpコマンドを打っても反応しない。

ラベルを削除したら、PCから先ほどのExternal IPにアクセスをして通信できるか確認。

`arp -a` でExternal IPとホストのIPのMACアドレスが同じことを確認したらmetallbのL2モードが正常に動いていることが確認できる。

# CNIとの互換性

https://metallb.universe.tf/installation/network-addons/

FlannelはサポートされているがCalicoなどの一部はサポートされていないが一応動くらしい。

お好みで、、
何も考えずに dockerでは起動するがdocker-compose経由だとコンテナが起動失敗する Exited (255) ERROR 事象に対応する

`docker` だとコンテナが起動するが、`docker-compose`経由だと`docker`のコンテナが起動せずに `Exited (255) ERROR` が出てしまって起動に失敗するパタンに自分がすごくよく遭遇する。

とりあえず、なにも考えずに以下の様に対応。

* 起動失敗パタン
```
version: '3'
services:
  app:
    build:
      context: .
      dockerfile: "Dockerfile"
    image: "app"
    container_name: "app"
    ports:
      - 8001:8001
    command: "/sbin/init"
```

* 起動成功
```
version: '3'
services:
  app:
    build:
      context: .
      dockerfile: "Dockerfile"
    image: "app"
    container_name: "app"
    privileged: true　# ここ
    ports:
      - 8001:8001
    command: "/sbin/init"
```
```
privileged: true
```
を付与することで起動する。

一応、注意事項として`privileged: true`はセキュリティ的にリスクを伴うため、なるべく最小限の権限で動作させる方法を検討するのがよいとのこと。
haystack?? needle??　なにその命名

# あらまし
haystackとneedleという命名を見て何のことだろうと思い，調べてみたところ，「find a needle in a haystack」（干し草の中から一本の針を探す）という慣用句に由来することがわかった．

# 状況
LeetCodeで28. Find the Index of the First Occurrence in a Stringを解いていた時のこと，問題文に，
‘‘‘
Given two strings needle and haystack, return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.
‘‘‘
とあった．

# 解説
「haystack」は「干し草」の意味で，「needle」は「針」．
調べてみると，「find a needle in a haystack」という慣用句があるらしい．意味は，「干し草の中から一本の針を探し出す」であるが，比喩的に，「膨大な数の中から小さなものを見つけ出すくらい難しいこと」とのこと．

ChatGPTに例文を出力してもらったところ

"Trying to find my lost earring in the park is like finding a needle in a haystack."
（公園で失くしたイヤリングを見つけるのは、干し草の山で針を探すようなものだ。）

"Debugging this software bug feels like finding a needle in a haystack."
（このソフトウェアのバグを見つけるのは干し草の山で針を探すような気分だ。）

このように使うらしい．
（「干し草の中の針」として日本語の文脈で使っている人もいるらしい）

要するに，**大量のデータの中から特定のデータを見つける時に慣例的に使われている変数**らしい．
今後使っていこう．

因みに，PHPではstr_contain()という間数のドキュメントに記載があるらしい．
私はPHPには疎いのでわからなかったが，界隈によっては一般的なのかもしれない．
私の周りでは膾炙していない．
EKSでアプリ開発プラットフォームを作ろう！

## 概要
インフラ管理をできるだけアプリ開発者から分離して、アプリ開発者が気軽にアプリをデプロイできるような構成を作成しました。
- アプリ開発者がインフラと独立したrepositoryで開発できる
- インフラ開発者が単一のrepositoryでアプリを一元管理できる
- 複数アプリのネットワークや認証認可を共通できる。
- ECRにpushするだけでアプリがインターネットにデプロイできる
![](https://storage.googleapis.com/zenn-user-upload/ed98e3ac3ce9-20241216.png)

リポジトリはこちら
https://github.com/yuu-misaki/eks-argocd-platform
https://github.com/yuu-misaki/eks-argocd-app



### インフラ管理者
- プラットフォームリポジトリを用いて、EKSの管理（ALBもingressで定義したものです）をします。
- argocdを用いることで、各アプリケーションの一元管理が可能です。
- アプリアクセスを共通ALBにすることで、ネットワーク制限や監視もしやすくなります。
- 認証認可が必要ならALBにcognitoを紐づけることも可能です。
![argocd画面](https://storage.googleapis.com/zenn-user-upload/7cf3205b8e56-20241216.png)

### アプリ開発者
- 好きなリポジトリを立てて、開発できます。
- ECRにイメージをpushすれば、自動的にargocd image updaterによってイメージが更新されます。
- デフォルトではlatestタグがついている、最新のイメージが2分ごとに確認され、新しくなっていれば更新されます。

## 背景
fastapiやstreamlitをデプロイする際、なんだかんだ毎回terraformコードを作成し、ECRにpushした後連携してECS, Lambdaをデプロイするようなgithub actions or デプロイコマンドを作成し...とやっているのですが、アプリ開発者の負担が大きいなと思うようになりました。
インフラエンジニアから見ればこの順でデプロイコマンド打たなければいけないとかすぐわかるんですが、アプリ開発者にも求めているといつまでもスケールしないなと。
また、それぞれ認証認可やネットワークを作成するのもしんどいので、一元化できないかな〜と考えていました。

そんな中でk8sとargocdの話を聞き、CI/CDの仕組みがめちゃくちゃ良さそう！と感じました。
さらに追い風にEKS autoモードが発表され、これはやるしかないと、プライベートで取り敢えず考えついたものを作成しました。

## 実行方法
展開方法は以下リポジトリのREADMEに記載してあります。主にplatformリポジトリを参考いただければわかるかと思います。
インフラ管理者はplatformリポジトリ、アプリ開発者はappリポジトリで作業します。
https://github.com/yuu-misaki/eks-argocd-platform
https://github.com/yuu-misaki/eks-argocd-app

なおEKS展開にあたっては、AWS利用料金がかかるので注意ください！

## 結果
インフラ管理者は稼働アプリやネットワークを一元管理でき、アプリ開発者は自由な開発環境と迅速なデプロイが簡単にできてハッピー！（になるはず）

## 参考にさせていただいたリポジトリや記事
EKSのterraformコード
https://www.beex-inc.com/blog/eks-automode-hybrid-nodes-raspberrypi-deploy

argo image updater
https://techblog.zozo.com/entry/measure-argocd-image-updater
https://techtekt.persol-career.co.jp/entry/tech/240726_01

## 今後の展望
- 本リポジトリを作成するにあたっての技術解説記事作成
- ALBへcognitoの連携
- image updaterのdigest戦略以外の検討
- streamlit, fastapi, mlflowをデプロイしてMLプラットフォーム化
undefined method `session_path' for an instance of

## 概要
会員登録機能実装時に「undefined method `session_path'..」エラーが発生。
機能実装の流れも含めて記載します。

## 会員登録機能 実装手順
1.Gemfileに「devise」を追加して、「bundle install」コマンドを実施。
```rb
gem 'devise' #Gemfileに記載
```

2.Deviseをインストール
```rb
rails generate devise:install
```

3.Userモデルを生成
```rb
rails generate devise User
```

4.マイグレーションを実行
```rb
rails db:migrate
```

5.会員登録用のビューファイルなどを作成
```
rails generate devise:views users
```

6.app/views/devise/registrations/new.html.erbファイルを編集。
下記のように英語表記を日本語に修正とnameフォーム追加など実施。
```rb
  <div>
    <%= f.label :名前 %>  
    <%= f.text_field :name %>
  </div>
  
  <div class="field">
    <%= f.label :email %><br />  # :emailをメールアドレスなど
    <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
  </div>
  ```

7.app/controllers/application_controller.rbにユーザー認証を追加
```rb
 before_action :authenticate_user!
```

## エラー発生経由と解決概要
users/sign_in ページにアクセスした際「undefined method `session_path' for an instance of」エラー発生。
下記記事を見つけ、「docker compose down」「docker compose up -d」コマンドを実施。Railsサーバの再起動が必要だった模様です。

・参照記事
https://stackoverflow.com/questions/24420942/undefined-method-session-path

## まとめ
エラーの対処方法と今回実装しようとした会員登録機能の手順も同等の機能実装の際に役に立つのではと思い、一緒にまとめました。
DMiMEの導入　windows10版

## 経緯
医学用語の変換が鬱陶しかったため有名な変換辞書のDMiMEを導入しようと考えたが、OSDN経由では不可能だったため既存の方法が使えなくなった。（2024/12/27現在アクセス不可）
## 必要なソフト

・DMiME
・google 日本語入力
## DMiMEのダウンロード
[DMiME 医学医療用語変換辞書](https://x.com/DMiMEjp)
DMiMEの制作者様が運営しているXのアカウントにアクセスし、2023年12月24日投稿の緊急避難用google driveリンクよりダウンロードする
https://x.com/DMiMEjp/status/1738761806439665673
## Google 日本語入力のダウンロード
他の解説記事ではwindowsの場合は必要ないとのことだったが、インストール時に辞書ツールが出てこず、その後の作業が進まなかったためダウンロードした
[Google 日本語入力](https://www.google.co.jp/ime/)
![スクリーンショット 2024-12-27 193917.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2841017/5a701a9b-db73-18bc-5ffb-ec23bb1ed19c.png)

windows版をダウンロードする

![スクリーンショット 2024-12-27 193950.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2841017/c76768c2-2f6f-97ba-a32d-b61212a08850.png)

「同意してインストール」をクリックしてインストール
## DMiMEのインストール
google日本語入力をインストールすると右下のIME変換表示が白くなるので右クリックする

![スクリーンショット 2024-12-27 194322.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2841017/171e13f9-18bc-ebbb-0daf-797365d92190.png)

出てきたリストから「辞書ツール」を選択する

![スクリーンショット 2024-12-27 194547.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2841017/4f6a42b8-65e2-8ea8-3904-b1288f250acf.png)

辞書ツールのウィンドウが開くので左上の管理メニューを開いて「新規辞書にインポート」をクリック
![スクリーンショット 2024-12-27 202846.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2841017/ae69be24-7468-a7a2-709f-4ec46ca91120.png)

クリックするとインポート画面が出てくるため「ファイルを選択」を押し、あらかじめ解凍しておいたDMiMEフォルダからDMiMEテキストファイルを選択し任意の辞書名をつけインポートする。
これで医学用語の変換にイライラすることがなくなった。
## 参考
https://x.com/DMiMEjp
[DMiMEのインストールから使い方解説｜医学用語変換辞書](http://xn--1-19t205kpxao3y8re6uu3x0f.xyz/2018/11/24/dmimeinstall/)
【app-ads.txt の配置】 Firebase Hosting × Functions でプラットフォーム別に動的に設定する

## 概要

`app-ads.txt` をデベロッパーサイトに配置する件でFirebase Hostingを用いて配置する記事をいくつか参考にさせていただきました

- https://zenn.dev/keitaan0822/articles/155c69fc617d44
- https://qiita.com/masaibar/items/c378b4f01b707ac2506a
- https://zenn.dev/k9i/articles/8b91ee1e7dcc53

表示されるページを違和感ないようにするには各アプリで各アプリページにリダイレクトする必要があります。

各アプリでプラットフォームごとにこのサイトを作ろうとするとアプリ数×2 の数のサイトが必要になってしまします。
これではアプリ数が多い場合、プラットフォームごとにサイトを作成する必要があり、管理が複雑になります。

そこで、1つのWebページでユーザーのプラットフォームに応じて適切なリダイレクト先に動的に遷移させる方法を採用し、アプリ数×2のWebページ作成を回避します。


## 使用する技術

- **Firebase Hosting**: ホスティングサービス→サイトの本体
- **Firebase Cloud Functions**: サーバーレスなバックエンドサービス→プラットフォームｔごとにリダイレクト判定
- **Node.js**: Cloud Functions 用の JavaScript 実行環境

---

## 手順

### 1. Firebase プロジェクトのセットアップ

Firebase CLI がインストールされていない場合、以下を実行してインストールしてください：

```bash
npm install -g firebase-tools
```

Firebase プロジェクトをセットアップします：

```bash
firebase login
firebase init hosting functions
```

---

### 2. Cloud Functions のコードを作成

以下のコードを`functions/index.js`に追加します。

```javascript
const functions = require("firebase-functions");

// 環境変数でリダイレクト先を管理
const iosUrl =
  "https://apps.apple.com/jp/developer/XXX"; // 自身のAppStore のWebページ
const androidUrl =
  "https://play.google.com/store/apps/developer?id=XXX"; // 自身のAndroid のWebページ

exports.redirectBasedOnDevice = functions.https.onRequest((req, res) => {
  const userAgent = req.headers["user-agent"] || "";

  let destination;
  if (/iPhone|iPad|iPod/i.test(userAgent)) {
    destination = iosUrl; // iOS用URL
  } else if (/Android/i.test(userAgent)) {
    destination = androidUrl; // Android用URL
  } else {
    destination =
      "https://XXX"; // デフォルトURL。私は力を入れているAppStoreの方を配置しました
  }

  res.redirect(301, destination);
});
```


---

### 3. Cloud Functions をデプロイ

Cloud Functions を Firebase にデプロイします：

```bash
firebase deploy --only functions
```

---

### 4. Firebase Hosting 設定の更新

`firebase.json`に以下を追加して、リクエストを Cloud Functions にリダイレクトします：

```json
{
  "hosting": {
    "public": "public", 
    "rewrites": [
      {
        "source": "/",
        "function": "redirectBasedOnDevice"
      }
    ]
  }
}
```

---

### 5. app-ads.txt をpublic/ に設置

Firebase Hosting の公開ディレクトリ（デフォルトでは `public/` ディレクトリ）に `app-ads.txt` ファイルを配置します。

``` bash
# AdMob のサイトから app-ads.txt をコピー
$ echo "コピーしたコードスニペット" > public/app-ads.txt
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262578/34abbceb-ac67-3342-8c7d-0d60dcef1a62.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262578/6e95b976-6102-3de5-cd47-7758d8b54cd0.png)




要件: https://support.google.com/admob/answer/9363762?hl=ja#step2

### 6. Firebase Hosting にデプロイ

```bash
firebase deploy --only hosting
```

---

## 動作確認

1. **iOS デバイス**からアクセスした場合：\
   → App Store の Web ページにリダイレクト

2. **Android デバイス**からアクセスした場合：\
   → Google Play の Web ページにリダイレクト

3. **その他のデバイス**（PC など）からアクセスした場合：\
   → デフォルトのページにリダイレクト

---

## 注意事項

- Cloud Functions のコストがかかる
    - 一般的に誤差の範囲内だと思いますが
- User-Agent の信頼性
    - 一部のブラウザやデバイスで User-Agent が正確に取得できない場合があります。その際はデフォルト URL にフォールバックします
Node-RED の LINE用ノードをまた使ってみた：LINE Notify がわりにメッセージのブロードキャスト【IoTLT-2】

この記事は、「[IoTLT Advent Calendar 2024](https://qiita.com/advent-calendar/2024/iotlt)」に後付けで登録した 21日目の記事です。そして、IoTLT のアドベントカレンダーに登録した記事としては 2つ目となります。

IoTLT のアドベントカレンダーを振り返るイベントが 12/27 に行われている中、以下のような話が出てきていたので、自分もイベント中に「内容決め 〜 記事執筆・公開」をやってみました。

https://x.com/n0bisuke/status/1872591516994314611

余談ですが、このイベントの配信開場にいて、そこで書きました。

https://x.com/Zakkiea/status/1872583019024040130

## はじめに
この記事では、以下のアドベントカレンダー 25日目で扱った内容のお試しをやってみます。

●Node-RED の LINE用ノードの最近のアップデートについて調べてみた【Node-RED】 - Qiita
　https://qiita.com/youtoy/items/71ebc6d29e8fbb7c80d4

試すものは、以下の Node-RED用ノードです。

●node-red-contrib-line-messaging-api - npm
　https://www.npmjs.com/package/node-red-contrib-line-messaging-api

ちなみに以前、上記のノードを使った際には以下の記事を書いていたりしました。

●LINE Messagin API を利用できる Node-RED のノードの新機能（Bot の返信で画像を利用）を enebular 上で試す - Qiita
　https://qiita.com/youtoy/items/287a0782fdd185fc2004

### Node-RED関連の環境構築
自分のローカル環境には、Node-RED と古いバージョンの node-red-contrib-line-messaging-api は入っていました。

そのため、node-red-contrib-line-messaging-api の最新版へのアップデートのみ行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/63def780-c05a-6d13-a358-2c213769ed4b.png)

### LINE Bot用の設定・今回の内容
LINE Bot の環境は、今月のアドベントカレンダーで以下の記事を書く時に作ったので、それを流用します。

●ライブラリ「LINE Bot Notify」を使った LINE Bot への Push通知【LINEDC】 - Qiita
　https://qiita.com/youtoy/items/b70a2847d5b7158aa9d9

今回実施する内容は、上記と同じ LINE Bot へのプッシュになります。

### Node-RED でフローを作成して実行
Node-RED で、node-red-contrib-line-messaging-api を使ったフローを作って、それを実行してみます。

#### 作成したフロー
上記のアップデートをしたので、利用可能なノードが以下のようになっていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/db7c1e63-279e-c31d-7e60-814e5a695252.png)

そして作成するフローについて、今回やることはとりあえずプッシュをするだけなので、以下のシンプルなものとしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/86704cd5-4c1a-e160-4c8d-bbcea079119d.png)

また、フローの最初 2つのノードの設定は以下のとおりです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/ef2ff222-3746-21c9-c5bf-28d3120bf687.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/448b3f91-6933-4001-de7f-a9ec1db579d2.png)

#### フローの実行
上記のフローを実行して、スマホの LINEアプリがどうなっているかを確認してみます。

確認してみたところ、以下のようにメッセージのプッシュが無事に行えていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/154b5efa-27ca-a129-c450-79792f569115.png)
Pythonからsqliteを操作

# 1 インポート
Pythonは標準ライブラリにsqlite3が含まれているので、importするだけで使うことができる。
```python
import sqlite3
```
# 2 データベースに接続、カーソルを取得
データベースファイルの名前を指定して接続し、接続からカーソルを取得する。
<strong>ファイルが存在しない場合は新規作成される</strong>ので、既存のファイルに接続する場合は、別途ファイルの存在チェックをしたほうが良いかもしれない。
```python
con = sqlite3.connect("sample.db")
cur = con.cursor()
```
特殊な使い方として、データベース名に":memory:"を指定すると、メモリ上にデータベースを作成できる。
```python
con = sqlite3.connect(":memory:")
```
# 3 SQLを実行
## 3.1 基本的な実行方法
カーソルのメソッド<code>execute()</code>に、文字列でSQLを渡すことでSQLを実行できる。
```python
cur.execute("CREATE TABLE people ( id INTEGER PRIMARY KEY, name TEXT, score INTEGER)")
```
接続したファイルが正常なsqliteファイルでなかった場合、<strong>接続する段階ではエラーが出ず、SQLを実行するタイミングでエラーが投げられる</strong>仕様になっているようなので、エラーハンドリングする際には注意すること。
## 3.2 変数を使用したいとき
SQL内で変数を使いたい場合、変数を挿入する箇所を?にする。引数にタプルを渡すと、タプルの要素をSQLに挿入してから実行してくれる。
```python
name_sample = "Bob"
score_sample = 80
cur.execute("INSERT INTO people (name,score) VALUES (?,?)",(name_sample, score_sample))
```
文字列のエスケープなども適切に行ってくれるので、変数を使いたいときは、単純に文字列連結するのではなくこの方法を使ったほうがよい。

## 3.3 複数のデータに対してまとめてSQLを実行したいとき
<code>executemany()</code>を使用することで、複数のデータに対して同じ処理を一括で行うことができる。
```python
datalist = [ ("Katou",60) , ("Suzuki",75) , ("Yamada",80) ]
cur.executemany("INSERT INTO people (name,score) VALUES (?,?)" , datalist)
```
## 3.4 SELECT文の結果を取得したいとき
<code>execute()</code>の戻り値を受け取り、<code>fetchall()</code>を実行することでSELECT文の実行結果を取得できる。
行列はタプルのリストとして表現される。
```python
response = cur.execute("SELECT * FROM people")
matrix = response.fetchall()
print(matrix)
```
```
[(1, 'Bob', 80), (2, 'Katou', 60), (3, 'Suzuki', 75), (4, 'Yamada', 80)]
```
<code>fetchall()</code>の代わりに<code>fetchone()</code>を使うと、データをタプルとして一行ごとに取得できる。
```python
response = cur.execute("SELECT * FROM people")
line1 = response.fetchone()
line2 = response.fetchone()
print(line1)
print(line2)
```
```
(1, 'Bob', 80)
(2, 'Katou', 60)
```
# 4 コミット、終了
pythonのsqlite3モジュールの場合、デフォルトではコミットするまで操作が反映されない。
更新した場合は終了前にコミットが必要になる。
```python
con.commit()
con.close()
```
コードの途中でコミットしていない更新を破棄したい場合、<code>rollback()</code>を実行する。
```python
con.rollback()
```
# 参考リンク
https://docs.python.org/3/library/sqlite3.html#sqlite3.connect
Viteの環境変数に型をつける方法

## Viteの環境変数
.envに記述した環境変数をReactコンポーネントから取得する際、any型となる。

```:.env
VITE_APP_TITLE="APPLE"
```

```tsx:Reactコンポーネント
// (index) ImportMetaEnv[string]: any
console.log(import.meta.env.VITE_APP_TITLE)
```


### 環境変数に型をつける
毎回型アサーションでも問題ないが、`vite-env.d.ts`で型を指定することができる。
vite-env.d.tsファイルがなければ、srcフォルダ直下に作成する
```diff_tsx:vite-env.d.ts
/// <reference types="vite/client" />
+ interface ImportMetaEnv {
+   readonly VITE_APP_TITLE: string;
+ }
```

```tsx:Reactコンポーネント
// (property) ImportMetaEnv.VITE_APP_TITLE: string
console.log(import.meta.env.VITE_APP_TITLE)
```
【M1 Mac】Go言語のインストール方法

## Goの公式にアクセス
https://go.dev/dl/

## CPUに対応するパッケージを選択
私はM1なのでARM64を選択しました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523103/bec1fb6d-9c47-e049-33d3-c1add81b17c0.png)

## DLしたファイルを解凍し、手順に沿ってインストール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523103/b09011e9-d6f4-656a-49c7-aec5996a73f1.png)

## ターミナルでバージョンを確認
```
go version
```
```
go version go1.23.4 darwin/arm64
```

バージョンが表示されればOK
AWS CloudTrail Lake が包括的なダッシュボード機能の提供を開始

# 初めに
本記事では、AWS CloudTrail Lakeの「ハイライト」ダッシュボードのAIを活用したインサイトとさまざまなユースケースに対応する事前構築済みダッシュボードの提供について説明します。

https://aws.amazon.com/jp/about-aws/whats-new/2024/11/aws-cloudtrail-lake-analytics-cross-account-data-access/

# ■ハイライトダッシュボードの AI を活用したインサイト
CloudTrail Lake管理でキャプチャされたデータと、イベントデータストアに保存されたデータイベントの概要を簡単に表示できます。このダッシュボードを使用すると、最も失敗したAPI呼び出し、失敗したログイン試行の傾向、リソース作成の急増など、重要な分析情報をすばやく特定して理解しやすくなります。データ内の異常や傾向を一目で分かるのはとても便利だと思います。
（現在AIを活用したインサイトはプレビュー版です。）

###### 【設定手順】
1.AWS CloudTrailコンソールに移動し、ナビゲーションペインの[Lake]の下の[ダッシュボード]を選択して、ハイライトダッシュボードを確認します。[Agree and enable Highlights]からイベントデータストアを選択してハイライトダッシュボードを有効にします。

![2024-12-27_18h48_32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934307/7712a29a-1afb-1ab6-7578-b47b8d44e015.png)

2.データが取り込まれたら、ハイライト ダッシュボードを確認します。

# ■さまざまなユースケースに対応する事前構築済みダッシュボード
新しいダッシュボード機能には、14種類の事前構築ダッシュボードが追加されました。これらは異なるユーザーや用途に応じて設計されています。例えば、セキュリティ用ダッシュボードは主要なセキュリティ指標を追跡し、オペレーショナルモニタリング用ダッシュボードはエラーや可用性の問題を可視化します。また、Amazon EC2やAmazon DynamoDBなど特定のAWSサービスに特化したものもあり、これらのサービス内のリスクや問題を特定するのに役立ちます。

###### 【設定手順】
1.カスタムダッシュボードを最初から作成するには、ナビゲーションペインの[Lake]の下の[ダッシュボード]に移動し、[Build my own dashboard]を選択します。

![2024-12-27_19h02_52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934307/a5513980-7e29-67b8-3495-b37517ad5b68.png)

2.[Enter a name for the dashboard]フィールドに名前を入力し、[Permissions]の下の[event data store]を選択してイベントを視覚化します。次に、[Create dashboard]を選択します。

![2024-12-27_19h01_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934307/c07571ed-6ce1-8769-dd3a-531ae14a612d.png)

これで、ダッシュボードにウィジェットを追加できるようになりました。ダッシュボードは柔軟にカスタマイズできます。ウィジェットの追加を使用して、事前に構築されたサンプル ウィジェットのリストから選択することも、新しいウィジェットの作成 を使用して独自のカスタム ウィジェットを作成することもできます。ウィジェットごとに、折れ線グラフ、棒グラフ、その他のオプションなどを選択できます。

# まとめ
これらの機能は、AWS CloudTrail Lake がサポートされているすべての AWS リージョンで利用できます。ただし、ハイライトダッシュボードの AI を活用したインサイトについては、現在、バージニア北部リージョン、オレゴンリージョン、東京リージョンでプレビュー版として提供されています。
これらの機能を活用することでより効率的にデータの分析ができます。
Unity制作メモ：2D Spriteに影をつけるだけ　

1. プロジェクトの目標

2Dスプライトに影を付けるシェーダーを作成する。
Unityの Universal Render Pipeline (URP) を使用して、スプライトにリアルタイムライティングと影の効果を適用。
参考にした動画：
https://www.youtube.com/watch?v=flu2PNRUAso
1. カスタムシェーダー制作開始

作業内容
Unityプロジェクトにて Create > Shader > Unlit Shader を選択し、新しいシェーダーファイルを作成。
シェーダーファイルの名前を SpriteShader に変更。
目的: スプライトにリアルタイムライティングと影を適用できるカスタムシェーダーを実装すること。
2. 初期シェーダーコード記述と設定

作業内容
影とライティングに対応する以下のカスタムシェーダーコードを記述：
ShadowCaster パス: 影生成用。
ForwardLit パス: ライティングとテクスチャの描画用。
書いたコードをシェーダーファイルに適用し、マテリアルに設定。

3. エラー発生と修正
事象の内容：
影はつくが、スプライトがピンク色になる。
原因はHRPとURPの競合っぽい。
詳細は以下に続く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396775/8a8cb0ce-487b-4926-2f93-e30b6dd4aa9f.png)

発生エラー1: SAMPLE_TEXTURE2D の引数不足
エラー内容:
'SAMPLE_TEXTURE2D': Too few arguments to a macro call. というエラーが、ShadowCaster パスと ForwardLit パスの両方で発生。
原因: SAMPLE_TEXTURE2D マクロの引数に不足があった。
解決方法:
SAMPLE_TEXTURE2D マクロを以下のように修正：
SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv);
必要な3つの引数（テクスチャ、サンプラー、UV座標）を正しく指定。

発生エラー2: TransformObjectToHClip の型変換警告
警告内容:
implicit truncation of vector type というWarningが発生。
原因: TransformObjectToHClip の出力型が float3 ではなく float4 であるため。
解決方法:
型変換を正確に指定：
OUT.positionHCS = TransformObjectToHClip(float4(IN.positionOS.xyz, 1.0));
必要な w 成分を追加して、型を float4 に統一。

4. HDRP関連のエラー発生

エラーの詳細
エラー内容: 'GetInverseCurrentExposureMultiplier': undeclared identifier。
原因:
Unityプロジェクトが Universal Render Pipeline (URP) を使用しているにもかかわらず、HDRP関連のシェーダーコードが含まれていた。
HDRP専用のシェーダーノードや関数がプロジェクトに影響を与えていた。
解決方法:
HDRPパッケージの削除:
Unityの Package Manager を開き、High Definition RP パッケージを削除。
プロジェクトディレクトリの Library/PackageCache を削除してUnityを再起動。
URPアセットの確認:
プロジェクト設定 (Project Settings > Graphics) で、Default Render Pipeline Asset がURPアセットに設定されていることを確認。
5. シェーダーの再テストと最終修正

テスト内容
シーン内のスプライトオブジェクトにカスタムシェーダーを適用。
確認ポイント:
スプライトに影が適切に付くか。
ライティングの影響を受けて明暗が変化するか。
結果
シェーダーが期待通り動作。
HDRP関連のエラー・警告はすべて解消。
最終的にURP環境で影付きスプライトが正常に描画。

まとめ
作業: カスタムシェーダー作成、エラー修正、HDRP削除。
発生エラー:
SAMPLE_TEXTURE2D 引数不足。
型変換警告。
HDRP専用関数未定義エラー。
解決方法: 正しいシェーダーコードへの修正と、HDRPパッケージの削除。
ローカルLLM＆日本語テキストによるGraphRAGのテスト（失敗）

## 概要
ローカルLLM＋GraphRAGで日本語テキストデータのインデックス作成を試みたが失敗したので、その途中までの作業記録を残す（GraphRAGの多言語対応が進んだらリトライしてみよう…）

1. 仮想環境の作成とGraphRAGのインストール
2. プロジェクトの初期化と`input`フォルダへのデータ配置
3. `settings.yaml`の修正
4. 環境変数でのAPI_BASE設定
5. プロンプトチューニング（`prompt-tune`）
6. インデックス化の試行と課題

- [GraphRAG公式ドキュメント](https://microsoft.github.io/graphrag/)

## 1. 仮想環境の作成とGraphRAGのインストール

```bash
# 仮想環境の作成とアクティベート
$ python3 -m venv graph-venv
$ source graph-venv/bin/activate

# pipでGraphRAGをインストール
(graph-venv) $ pip install graphrag
```

## 2. プロジェクトの初期化と`input`フォルダへのデータ配置

```bash
(graph-venv) $ graphrag init --root ./ragtest
```

プロジェクト初期化後に、以下のフォルダ構造が生成される。

```
ragtest/
├── input/  # テキストデータを配置するフォルダ
├── prompts/  # プロンプト設定を管理するフォルダ
└── settings.yaml  # プロジェクト全体の設定
```

`input`フォルダに解析対象の日本語テキストファイルを配置。
2025/12/26時点ではtxtとcsvのみ対応であるらしい。

```
ragtest/input/sample.txt
```

`sample.txt`の内容（例）：

```plaintext
メロスは激怒した。必ず、かの邪智暴虐の王を除かなければならぬと決意した。
```

## 3. `settings.yaml`の修正
`settings.yaml`ファイルを以下のように修正。

```yaml
# settings.yamlの抜粋（修正後）
encoding_model: cl100k_base
llm:
  api_key: sk-2d76xxxxxxxxxxxxxxda5e24
  type: openai_chat
  model: jean-luc/tiger-gemma:9B-v1a-Q4_K_M
  model_supports_json: true
  api_base: http://localhost:3000/api

embeddings:
  llm:
    api_key: sk-2dxxxxxxxxxxx24
    type: openai_embedding
    model: nomic-embed-text:latest
    api_base: http://localhost:3000/api
```

修正点：
- `llm.api_base` と `embeddings.llm.api_base` にローカル環境のAPIエンドポイント (`http://localhost:3000/api`) を指定。
- 使用するモデル名を適切に設定。
- これらのパラメータはopen webuiのopenai互換API利用を前提としている（Ollamaでやっている事例のほうが世の中には多い模様。まあopen webuiもバックエンドはOllamaだから似たようなもんか…）

## 4. 環境変数でのAPI_BASE設定
GraphRAGでは、`settings.yaml` で `api_base` を設定しても、環境変数 `OPENAI_BASE_URL` を別途設定しないと動作しないバグ、あるいはガイド不足がある。[^1]


```bash
(graph-venv) $ export OPENAI_BASE_URL=http://localhost:3000/api
```

## 5. プロンプトチューニング（`prompt-tune`）
`graphrag prompt-tune`コマンドを使用してプロンプトをチューニング。

```bash
(graph-venv) $ graphrag prompt-tune --root ./ragtest --config ./ragtest/settings.yaml --language Japanese
```

このコマンドにより、`prompts/` フォルダにエンティティ抽出や関係性抽出に使用するプロンプトが生成される。


## 6. インデックス化の試行と課題
`graphrag index` コマンドを実行してインデックス作成を試みたが、異常終了。

```bash
(graph-venv) $ graphrag index --root ./ragtest --config ./ragtest/settings.yaml
Logging enabled at /mnt/NVMe01TB01/graphrag/ragtest/logs/indexing-engine.log
❌ extract_graph
None
```

### 発生したエラー

```plaintext
09:59:41,961 graphrag.index.graph.extractors.graph.graph_extractor ERROR error extracting graph
ValueError: Entity Extraction failed. No entities detected during extraction.
```

このエラーは解決できなかった。以下の可能性が想定される。
1. モデルが日本語テキストに適していない。
2. プロンプトの設計が不十分。
3. 入力データがエンティティ抽出に適していない。

[^1]:[GitHub Issue: API_BASEに関する問題](https://github.com/microsoft/graphrag/issues/1481)
XserverにSSHで接続する方法

今まで、FTPを使ってXserverのリポジトリにデータを移行していましたが、1月以降の案件でAWSのコンテナへターミナルから操作するらしいのです。AWSはお金がかかるので、研修で使わせていただいているXserverで、gitの操作ができることを目標にして検証します。（もっと効率のいいやり方があるかもしれません。あくまで私の学習記録としてご覧ください。間違いなどございましたらご教示いただけますと幸いです:bow_tone1:）

## SSH接続
1 .公開鍵の作成
![スクリーンショット 2024-12-27 9.50.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882527/9300d65b-0893-5884-c0cc-78b5ce8cf720.png)
2 .ダウンロードした秘密鍵を~/.ssh/ディレクトリに保存
```
cd ~/.ssh/
mv ~/Downloads/your_private_key ~/.ssh/
chmod 600 ~/.ssh/your_private_key （権限強化）
```
3 .~/.ssh/configファイルに以下の設定を追加（vimなどで記述）
```
Host xserver
HostName サーバID.xsrv.jp
User サーバID
Port 10022
IdentityFile ~/.ssh/your_key_file

このおかげで
ssh xserver
と入力すればSSH接続が可能となります。
```

## gitとの連携
今回は、すでにgitがインストールされている想定でいきます。
1 .リポジトリをクローン
```
cd /path/to/your/public_html
git clone git@github.com:your-username/your-repository.git .
```
2 .ローカルで公開鍵を生成
```
ssh-keygen -t rsa -b 4096 -C "your-email@example.com"
この時
Enter passphrase (empty for no passphrase):
が表示されたら、パスワードを設定できます。（設定しておくと安心）

セッション中にはパスワードを一回のみの入力で済ませられるコードもあります。
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa
```
3 .GitHubに公開鍵を登録

Settings＝＞SSH and GPG keys
![スクリーンショット 2024-12-27 12.29.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882527/69c5d90b-68fd-5f55-6bc3-fab962b779f5.png)
4 .接続をテスト
```
ssh -T git@github.com
Enter passphrase for key '/home/*****/.ssh/github': 

Hi username! You've successfully authenticated, but GitHub does not provide shell access.
（この文字が出たら成功）
```

これでターミナル上でのgitの操作ができます！
例えば
```
git pull origin main
Enter passphrase for key '/home/******/.ssh/github': 
From github.com:your-username/your-repository
 * branch            main       -> FETCH_HEAD
Already up to date.
```
リモートリポジトリの変更をサーバーへ反映させることができます（今までFTPでやっていたこと）

## 感想
よりエンジニアらしいことができるようになったと感じ、自己肯定感が上がりました:arrow_heading_up::arrow_heading_up:
あと、Warpすごい！（コードの予測をしてくれるので）

今年はエンジニアという未経験の分野に挑戦できた素晴らしい年でした。
この場を借りてお世話になった方々へお礼申し上げます:bow_tone1:
ICPC2024 Yokohama 参加記

この記事はKCS Advent Calendar 2024の24日目の記事です。
（よかったら他の記事も読んでね）

2024年 12/21, 22 に開催された ICPC 2024 Asia Yokohama Regional にチーム egg_raspberry_choco として参加してきました。

この記事はその大会の参加記です！
ちなみに、順位は 54位/55チームでした…。

# チーム紹介

Lazz : この記事を書いている人。B1, Atcoder青。
にたまごマホロア : 構築が得意。B1, Atcoder緑。
ストーンチョコ : チームで唯一英語が読める人。B2, Atcoder茶。

# 1日目

1日目は始まるのが遅かったので、とりあえず中華街に行きました。
フカヒレ小籠包、おいしかった😋

![IMG20241221122903.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950851/e992c957-bda7-25b5-9061-7b4537a74992.jpeg)

会場に着きました！

![IMG20241221135306~2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950851/d7b86ef2-2736-9fd5-c4d9-973ec0495b80.jpeg)

競プロのオフラインイベント自体初めて参加したので、会場のいたるところが面白かったです。

広い！！
Tシャツ！？！？
お菓子多い！！！！！！
など。

周りのチームの人たちも、案内してくれたスタッフの人たちも競プロが強い人ばかりで、ずっと「すご！！」って感じでした。
諸々の説明の後、リハーサルのコンテストがありました。
キーボードが知らない配列で驚いていたら、これはUS配列というんだよと教えてもらいました。ちゃんと打てるようになるまで、まあまあ苦戦しました。

解散後は夜のみなとみらい観光をしました。
クリスマスの時期のみなとみらい、街中の任意の場所にイルミネーションがあって感動。
みなとみらい、素晴らしい街だね。

・山下公園から空に伸びる光（マイクラのビーコンみたい）

![IMG20241221173053.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950851/5f016794-37ba-74af-0fee-eff5cfe06cdf.jpeg)

・みなとみらいの夜景

![IMG20241221173408.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950851/13499549-dea7-707f-6e2b-1e7a8436e5cd.jpeg)

・街のイルミネーションにつられて光ってしまったトラックのタイヤ

![IMG20241221173847.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950851/38993134-2ec7-3c66-2f6d-8f581240755a.jpeg)

# 2日目

### コンテスト

自分がA, Bを解き、その間ストーンチョコさんとにたまごマホロアはほかの問題の読解をするという作戦でした。が、A問題を見てみるとそこにはめちゃ長英語問題文が書いてあります。白旗を挙げて、ストーンチョコさんにA問題を読んでもらいます。
B問題を見ると、解法がわかったので実装します → バグります……
にたまごマホロアがKが解けたというので渡します → TLEします……
A問題の概要をストーンチョコさんに聞き、解けたので実装します → 初めてのAC (0:54)
C問題を見ると、解けたので実装します → AC (1:13)

B問題がなぜ落ちてるかわからず苦労していたところ、2人がEが解けたというのでパソコンを渡します。結論から言うと、残りの時間はこれの実装とバグ取りで終わります。（最終的にギリギリバグは取れなかったらしいです）

Bがなぜ落ちているかがわかったので、途中でPCを使わせてもらいます → AC (3:42)

2人が実装している間考察をしていて色々生えましたが、実装レベルまで詰め切れずコンテストが終わりました。
結果はA, B, Cの3完、54位/55チームでした。

### 反省

チーム戦の意識が足りなかった。（特に自分）
Bはバグった時点でチームメイトに見てもらったらもっと早くバグが取れていたと思うし、
Eは自分が実装を代われば実装できていたと思います。
ちゃんとチームワークが取れていれば、4完以上も目指せた。

### そのあと

表彰式の会場に移動。
Yes/No、リアルで見るのは初めてでめっちゃワクワクしました。
でも、Yes/Noの結果E問題を解いていない唯一のチームになってしまって悲しかったです。
先輩方のチームであるAnonymsが8完10位でプレイオフを確定させていて嬉しかったです。

すべてが終わったあと、横浜イルミネーションツアーDay2をして、家に帰りました。

![IMG20241222202029.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950851/814fccf8-fdf0-378f-e8ee-1ee21d5b5ead.jpeg)

# 感想

結果はだいぶ反省なんですが、初めてのオフライン競プロイベントは楽しかったです！
来年はもっと強くなって、勝ちたいです。
まず黄色になるぞ！！
JavaScriptでのフォルダサイズ取得について

# はじめに
こんにちはプログラマー2年目の塚田と申します。
今回はJavaScriptでのフォルダサイズ取得について書きたいと思います。

フォルダサイズを取得するにあたってありがちなのがこの書き方

```javascript
var files = 取得したフォルダ;
var folderSize = 0;

for(int i=0;i<files.length;i++){
    folderSize = folderSize + files[i].size;  
}

console.log(folderSize);
```
以上の書き方でフォルダ配下の合計ファイルサイズが求められると思います。**file.size**でファイルサイズは取得できるのに**folder.size**でフォルダサイズが取得できないのはなんでだろ。。。私、気になります。

それはそうとfor文を使わないで書けないかなと思い、調べてヒットしたのがこちら

```javascript
var files = 取得したフォルダ;

int folderSize = 
Array.from(files).reduce((acc,files) => acc + files.size,0)

console.log(folderSize);
```
まず**Array.from**で配列風のオブジェクトを**実際の配列**にします。
実際の配列にすることで受け取ったフォルダを操作しやすい配列に変換します。

次に **.reduce()** とは、配列の各要素に対して累積計算を行います。
今回の場合、各fileのsizeプロパティを累積的に加算します。

そして**acc**はその累積値を保持します。初期値は0。
つまり**acc**に現在のファイルサイズを加算します。

最後に書いてある「,0」は.reduce()の初期値を設定しています。
この0というのが最初のaccに格納されます。

# おわりに
どうでしょうか。つい先日私も実装してみたばかりですが、for文無しでも一文で書けることに感動しました。
今後もコードの書き方についても投稿していきたいと思います。
以上、塚田でした。
CSSフレームワーク Open Props の紹介

# Open Propsとは？

Open Props はCSSフレームワークの一種で、たくさんのCSSカスタムプロパティ（変数）を提供します。極端な言い方をすると、ただの定数の集合体です。これをフレームワークにカテゴライズしても良いのかな？と疑問に思うぐらいにシンプルな発想です。

https://open-props.style/

例えば、Open Props には、”--font-size-5”というCSSカスタムプロティが定義されています。このプロパティの値は”2rem”となっています。

```html
<p style="font-size: var(--font-size-5);">こんにちは</p>
```

このように記述すると2remの大きさで「こんにちは」と表示されます。

```html
 <p style="font-size: 2rem;">こんにちは</p>
```

Open Props を使わずにこのように記述しているのと同じです。

# どういったところが嬉しいのか？

デザインを同じ色やサイズに統一するのが簡単でブレにくい。開発する時にCSSを追加するだけで使い始めることができます。全体に適用しなくても部分に適用していくことも可能です。

シンプルな定数の集合なので、FOCSS や  BEM などの他のCSS設計や自前のCSS設計に適用することも可能です。

# CSSカスタムプロパティ（変数）とは？

CSSカスタムプロパティはCSSの標準機能で Open Props はこの機能を利用しています。


```:MDNからの引用
複雑なウェブサイトには膨大な量の CSS が含まれており、
その結果、CSS の値が重複することがよくあります。
例えば、スタイルシートの異なる場所で同じ色が使用されていることはよくあります。
多くの場所で重複している色を変更するには、
すべてのルールと CSS ファイルを検索して置換する必要があります。
カスタムプロパティを使用すると、値を 1 か所で定義し、
他の複数の場所で参照できるため、作業が容易になります。
また、可読性と意味づけも利点のひとつです。
例えば、
--main-text-color は 16 進数表記のカラーコード #00ff00 よりも理解しやすく、
特にその色が様々なコンテキストで使用される場合にはその傾向が顕著です。
```

https://developer.mozilla.org/ja/docs/Web/CSS/Using_CSS_custom_properties

# サンプルコード

## OpenPropsの例

```html
<html>
<head>
  <link rel="stylesheet" href="https://unpkg.com/open-props">
  <link rel="stylesheet" href="https://unpkg.com/open-props/normalize.min.css">
  <style>
    .open-props-sample {
      font-size: var(--font-size-5);
      text-decoration: underline;
      text-decoration-color: var(--blue-5);
      text-decoration-thickness: var(--border-size-2);
    }
  </style>
</head>
<body>
  <p class="open-props-sample">
    Open Propsでスタイルを適用した例です。
  </p>
</body>
</html>
```
![スクリーンショット 2024-12-06 16.46.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261675/46834861-1a40-9fa5-1555-8fe57270a1a1.png)



Open Propsを使ってフォントサイズ・下線・色・線の太さを指定しています。

## TailWindの例

別のCSSフレームワークであるTailWindを使って、先ほどのOpen Propsを使った例と同じような表現をしてみます。

```html
<html>
<head>
  <link href="./output.css" rel="stylesheet">
  <script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
  <p class="text-3xl underline decoration-blue-500 decoration-2">
    Tailwind CSSでスタイルを適用した例です。
  </p>
</body>
</html>
```

![スクリーンショット 2024-12-06 16.41.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261675/e789ad2d-e0ec-3a0c-fea8-1ddc3b5a7cfe.png)


TailWindを使ってフォントサイズ・下線・色・線の太さを指定しています。

TailWindなどのCSSフレームワークでは用意されたクラスを設定することでスタイルを表現しますが、Open Props はクラスではなくCSS カスタムプロパティ（変数）が用意されており、それを使ってスタイルを表現します。

# OpenPropsとTailWindの比較

|  | OpenProps | TailWind |
| --- | --- | --- |
| 記述スタイル | フレームワークが用意したCSS変数をプロパティの値に指定する | フレームワークが用意したCSSクラスをHTMLタグのクラスに指定する |
| 学習 | 標準CSSについての知識が必要 | どんなクラスが用意されているか、ある程度知っておく必要がある |
| 部品化 | 別途CSS設計が必要 | apply機能でクラスをまとめることができる |
| メリット | 他のCSS設計と親和性が高い | 迅速にスタイルを適用できる |
| デメリット | 迅速にスタイルを適用するのは苦手 | 部品化しないとHTMLタグが長くなりがち |

# 最後に

[State of CSS 2023](https://2023.stateofcss.com/ja-JP/css-frameworks/) の満足度1位になっていたので気になっていました。2024年ではどうなっているでしょうか。

Open Props はデザインの統一感を保ちながらも自由にカスタマイズできるので、フレームワークに慣れていない、あるいは特定のフレームワークに大きく依存したくないといった状況下でも導入しやすいのではないかと思いました。