MyMap

背景 地理、楽しいっすよね。めっちゃ好きです。 でも Google Maps って使うと、結局は Google って会社にお世話になってるわけですよ。 僕はこの「どっかの会社のお世話になっている状態」があまり好きではない。 だからサーバやデータセンターも自前運用するし、 Slack の代わりに Rocket.Chat を使うし、 JIRA ではなく Redmine を使うんです。 じゃあ Google Maps を自前で運用するにはどうするか? これが今回のテーマです。 地図アプリがどうやって動いているか 必要なものは大きく分けて二つ。 レンダリングエンジン(地図を描画するライブラリ) マップデータ(地図そのもののソース) OSSのレンダリングエンジン(ライブラリ) Leaflet OpenLayers MapLibre GL JS それぞれでベースマップを比較できるデモも作りました。 👉 比較サイト OSSのマップデータ 選択肢は色々あります。 OpenStreetMap (OSM) 世界中のボランティアが作る地図データ。ラスタ(画像タイル)も配布されているが、見た目は少し古臭い。 実際の中身はベクタだが、配信されている地図はあらかじめ描画されているラスタタイル。(元がベクタならそれを出せばいいじゃん、、とは思った) OpenMapTiles OSMデータをベクトルタイル化するOSS。 これを使えば、Google Maps 並みにきれいなベクトル地図を完全自前で運用できる。 ラスタはやっぱり綺麗に映らないですね。なのでベクトルマップが欲しいのですが、 ベクトルマップは自分で作るしかないっぽいです。(Maptilerって会社がOSMを所有しようとしているので気をつけて) 実際に OSM → Vector Map を自前ホストする流れ 1. OSMデータのダウンロード Geofabrik から地域ごとの .osm.pbf を落とす。 例: 日本全体 https://download.geofabrik.de/asia/japan.html 2. OpenMapTiles でベクトルタイル生成 まずは GitHub から OpenMapTiles を取得します。 ...

August 23, 2025 · 3 min · 492 words · Me

Create_RAG

背景 gmailの返答を自動化するプロジェクトを推進しています。 そこで、RAGですね。もうだいぶ時間が経っていますが、しっかりやっていきたいと思う。 RAG = 「Retrieval-Augmented Generation」 これを理解できればもう仕組みを理解したも同然です。 が一応説明しておきます。 RAGの仕組み めっちゃ簡単にいうと、 ベクトルデータベースに保存されたナレッジから 問い合わせと類似するナレッジを検索し引っ張り出し それを元にGEN AIに回答を作らせる という流れになります。 1でベクトルデータベースにナレッジを保存する時には、元の自然言語をベクトル表現に変換した物を格納する必要があります。 このベクトル変換器のことを「Embedding model」と言います。 x: 自然言語 f: Embedding model とすると y = f(x) で得られたy(とそれに対応するx)をベクトルDBに格納しておくわけですね。 x_q: 問い合わせが来た時 y_q = f(x_q) から得られるy_qと類似しているy_1, y_2, y_3, ,,, y_nを検索し、 それに対応するx_1, x_2, x_3, ,,, x_n を出力し、 ans = LLM(x_1, x_2, x_3, ,,, x_n) とう感じで回答を作らせるという流れです。 Embedding modelsの選定 こちらの記事に書いてある通り、Embedding modelの選定がRAG全体の回答生成性能に大きく影響する。 まあ、chatGPTに聞いた結果これが一番いいのかな。 BAAI/bge-base-en 今回RAGを構築するにあたり使うソフト vector DB: qdrant embedding model: BAAI/bge-base-en LLM: phi4 14b on ollama (@ rtx 3060 12g) Langchainなどの統合的なライブラリは使わない。なぜなら自由度が低いから。 vector DBでの検索も普通にREST APIでできるので問題なさそう。 ...

June 6, 2025 · 2 min · 265 words · Me

Googleapi Gmail Autoreply

背景 受託開発先の方からLLMを使って問い合わせや予約業務の自動化ができないかと聞かれた。 もちろんできますよ、と僕は答えたわけです。 しかし、一回gmailの内容をローカルに持って来ないと始まりませんよね?? なので、gmailのメールをサーバに持ってくるためにgoogle apiを叩きます。 構成 webフロントエンド -> サーバ(アプリ) -> google api google apiの設定方法 1. プロジェクト作成 https://console.cloud.google.com/ にアクセス 以下の写真のgoogle cloudロゴの隣にあるボタンをクリック。この画像で言うと、gmail api access(ここから新しいプロジェクトを作る) プロジェクト名(例:gmail-auto-reply)を入力し、「作成」 2. Gmail API を有効にする google cloudロゴの隣にあるボタンから、先ほど作ったプロジェクトにプロジェクトを変更する 左側メニュー「APIとサービス」 → 「ライブラリ」を押す 検索ボックスに Gmail API と入力し、クリック。 enableボタンを押す 3. OAuth 同意画面を設定する 左側メニュー「APIとサービス」→「OAuth同意画面」 次のような画面が出てくる。get startedを押す 以下の情報を入力する ユーザータイプ 外部(基本はこれでOK) アプリ名 任意(例:Auto Mailer) サポートメール 自分のGmailでOK 開発者連絡先情報 自分のメールアドレス 4. 認証情報を作成(OAuth 2.0 クライアントID) 左メニュー「認証情報」→「+認証情報を作成」→「OAuth クライアントID」 以下を入力する アプリケーションの種類 → 「Web アプリ」 名前は何でもOK(例:auto-mail-backend) 「承認済みのリダイレクトURI」に以下を追加: http://localhost:3000/auth/google/callback https://yourdomain.com/auth/google/callback (実際に使うサーバ) 作成ボタンを押す。クライアントシークレットが表示されるのでめももしくはダウンロード 5 スコープを追加(必要なら) ここまでで設定が完了。次にやることは以下。 ...

May 10, 2025 · 2 min · 363 words · Me

自宅GenAIを公開するけど、誰でも触れるのはちょっと怖いのでBasic認証をかけた話

背景 自宅で動かしている Ollama + GenAI モデル(例えば phi4:14b)を、インターネット越しに使いたくなった。OpenVPN で穴を開けた上で、Nginx を使って自宅マシンへのプロキシを構成。 [ インターネット ] ↓ VPSのnginx (443) ↓ OpenVPN経由のプライベートIP (100.64.x.x) ↓ 自宅のGenAIサーバ (Ollama) 確かに便利ではあるが、「誰でも使える状態」にしてしまうのは少し怖くなった。 なので、Basic認証 を導入して、ユーザーとパスワードを知らないとアクセスできないようにした。 Nginx に Basic認証を追加する手順 認証用パスワードファイルを作成する まず、htpasswd コマンドを使って .htpasswd を作成。 sudo apt install apache2-utils # 初回のみ sudo htpasswd -c /etc/nginx/.ollama_htpasswd user1 2. Nginx の設定に認証を追加 /etc/nginx/nginx.conf server { listen 443 ssl; server_name hoge.ingenboy.com; ssl_certificate /etc/letsencrypt/live/ollama.ingenboy.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/ollama.ingenboy.com/privkey.pem; add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept'; location / { # Basic 認証を有効にする auth_basic "Restricted Area"; auth_basic_user_file /etc/nginx/.ollama_htpasswd; proxy_pass http://100.64.1.41:11434; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } 3. 反映 sudo nginx -t && sudo systemctl reload nginx 4. 確認 curl -v https://ollama.ingenboy.com → 401 Unauthorized ...

May 6, 2025 · 1 min · 128 words · Me

.envファイルは自動で読み込まれる?Composeでの勘違いと正しい理解

背景 最近、Docker Compose で .env を使っていて「あれ?なんで変数が読み込まれないんだ?」という問題に直面しました。結論から言うと、Docker Compose は .env を自動で読み込むが、コンテナの環境変数としては勝手に渡さないという、なかなか紛らわしい仕様によるものでした。 この記事では、自分がハマったポイントと、それによって得られた正しい理解を共有します。 結論だけ知りたい人向け .env ファイルは Compose ファイルで ${VAR} の形で参照されたときのみ自動で読み込まれる。 ただし、.env の内容は自動的にコンテナの環境変数として渡されない。 コンテナに渡したいなら、env_file: または environment: を明示的に使う必要がある。 ❌ 勘違いしていたこと 「.env は docker-compose.yaml と同じディレクトリに置けば、何もせずともコンテナに環境変数として渡される」 実際に .env に以下のような内容を記述していました: hogehoge=hoge hoge=foo ところが、Compose を起動してもコンテナ内にこれらの変数は存在しませんでした。 原因:.env はあくまで Compose ファイル用のテンプレート変数 例えば以下のように docker-compose.yaml 内で ${VAR} を使えば、それは .env から自動で補完されます: ✅ コンテナに環境変数を渡すには? 方法は2つ: env_file: を使う(.envファイルをそのまま指定) services: web: image: myapp env_file: - .env この場合、.env の中身がまるっとコンテナに渡されます。 environment: を使う(必要な変数だけ) services: web: image: myapp environment: - P_ARS=${P_ARS} - INET_ID=${INET_ID} このように書けば、Compose 内で .env の変数を参照して環境変数として渡せます。 ...

May 4, 2025 · 1 min · 88 words · Me

Encrypt_storage

背景 サービスを利用するとき、提供者に悪意があることを前提にもの事を考えるべきである。 何かがあってからでは遅いからである。 クラウドサービスのストレージが暗号化されていないのが気に食わない。 万が一ストレージのデータが流出することに備え、ストレージを暗号化したい。 アプリケーションレイヤーではなく、fsレイヤーで暗号化したい。 さて、どうやるのかね。 透過的暗号 & 復号 gocryptfsという、透過的暗号化をディレクトリ単位でしてくれるツールがあります。 これが素晴らしい。ユーザランドで動く。すぐ動かせる。 インストール方法 sudo apt install gocryptfs # または公式のバイナリをダウンロード 暗号化されるディレクトリの作成方法 mkdir ~/secure_data.encrypted gocryptfs -init ~/secure_data.encrypted 復号されるディレクトリを暗号化されたディレクトリにマウント mkdir ~/decrypted_data gocryptfs ~/secure_data.encrypted ~/decrypted_data gocryptfs はシンプルで信頼性が高く、後付けでも運用しやすいです。 実際に、mongodbのデータを移植した方法がこちら # install sudo apt update sudo apt install gocryptfs # make directories mkdir -p /secure/mongodb.encrypted gocryptfs -init /secure/mongodb.encrypted # パスワードを設定 mkdir /secure/mongodb # mongodbのデータ領域の確認 docker volume inspect rocketchat_mongodb_data # コピーを/secure/mongodbに移動 (allow_otherでしないとmongodb(非rootユーザが読めなくなる)) gocryptfs -o allow_other /secure/mongodb.encrypted /secure/mongodb cp -a /var/lib/docker/volumes/rocketchat_mongodb_data/_data/* /secure/mongodb/ # バインドマウントからボリュームマウントに切り替える vim compose.yml volumes: - /secure/mongodb:/bitnami/mongodb # docker composeで再起動 docker compose up -d つまりポイント 権限回り。mongodbが実行するのですが、所有者と権限にはくれぐれもご注意。 1001をownerにしないとうまくいかないです。 ...

May 3, 2025 · 2 min · 353 words · Me

VPNがdefaultルートを書き換えずに全トラフィックを奪う方法とは?OpenVPNの0.0.0.0/1マジックを読み解く

背景 VPNを使ってトラフィックを中継する際、すべての通信をVPN経由にしたい──そんなときに現れるのが謎のルート 0.0.0.0/1 と 128.0.0.0/1。 一見すると「なんだこの中途半端なルートは?」と思われがちですが、これはOpenVPNが採用する非常に巧妙なテクニックです。本記事では、その目的、動作、実際のルーティングテーブルを紐解きながら、この仕組みの裏側を解説します。 ルーティングとは?ip route の基本 Linuxのルーティングテーブルは ip route で確認できます。このテーブルは、「どのIPアドレス宛の通信を、どのネットワークインターフェースから、どのゲートウェイ経由で送るか」という配送ルール一覧です。 $ ip route 192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100 default via 192.168.1.1 dev eth0 この場合、 192.168.1.0/24 に属する通信は eth0 から直接送られる それ以外(つまりインターネットなど)は 192.168.1.1(ゲートウェイ)を経由して送られる パケットを受信すると、カーネルはルートテーブルの上から順に一致するエントリを探し、一番一致度が高い(= プレフィックスが長い)ルートに従って出力先を決定します。 つまり、 どの通信もまずは「最も具体的な(最長一致の)ルート」を優先する これがVPNのルーティング操作にも大きく関わってきます。 defaultルートとは? default ルート(または 0.0.0.0/0)とは、どのルーティングエントリにも一致しない宛先のパケットをどこに送るかを決めるルールです。いわば「最後の砦」。 # 例: よくある default ルート default via 192.168.1.1 dev eth0 この例では、「どのルートにもマッチしなければ 192.168.1.1 に送れ」となっています。 通常、インターネットへの出口(ISPや上位ルータ)に向かうために使われます。 A (100.64.1.61) ↓ B (100.64.1.27) — VPN Client ↓ C (162.x.x.x) — VPN Server ↓ D (192.168.40.10) ↓ E (192.168.40.130) AからD・Eに通信を通したい。しかし同時に、Aのインターネット通信もVPN(サーバC)経由で出したい。 ...

May 2, 2025 · 2 min · 314 words · Me

Problem_site2site

#背景 VPNを使って拠点間通信(Site-to-Site Routing)を構成していると、 思ったようにパケットが届かないケースがあります。今回は以下の構成で発生した通信不通トラブルと、その解決方法を共有します。 🔧 ネットワーク構成 [A] 100.64.1.61 (100.64.1.0/22) ↓ [B] 100.64.1.27 (OpenVPNクライアント) → VPN → [C] OpenVPNサーバー → [D] 192.168.1.4 (192.168.1.0/24) 目標:AからD(192.168.1.4)への通信を通すこと。 ❗ 問題の症状 BからDには通信可能(VPN経由で192.168.1.4が見える) AからDへの通信は届かない しかしDからAへの通信(返信)は可能 🧠 原因 Aは 192.168.1.0/24 サブネットに属していないため、この宛先へのパケットをデフォルトゲートウェイに送ってしまっていた。 つまり、「行きのルート」が不在だったわけです。 ✅ 解決策:Aに静的ルートを追加 一時的にルートを追加する場合は以下のコマンドを実行: sudo ip route add 192.168.1.0/24 via 100.64.1.27 これで、Aは目的地 192.168.1.4 へのパケットをVPN中継ノードBに送るようになります。 UbuntuでNetplanを使用している場合、/etc/netplan/xxx.yaml を以下のように修正します: network: version: 2 renderer: networkd ethernets: enp3s0: dhcp4: false dhcp6: false addresses: [100.64.1.61/22] routes: - to: default via: 100.64.1.1 - to: 192.168.1.0/24 via: 100.64.1.27 nameservers: addresses: [100.64.1.1, 8.8.8.8, 8.8.4.4] ちなみに、netplan applyをやって疎通できなくなると困ることってありますよね。 そんな時のためにこれがあります ...

May 2, 2025 · 3 min · 619 words · Me

Make back-up mechanism of self-hosted drive using Rsync and nginx

背景 自宅でfilebrowserというOSSを使ってwebからアクセス可能なドライブを作ったのですよ。 ただね、なんとこの間、5年間動かし続けた自作PCが壊れてしまい、直すまでドライブが使えないという問題が発生した。 Mean Time Between Failureをなるべく短くするために以下の二つの手段でドライブを冗長化する。 (しかも、リージョンが分かれているという。自宅で停電が発生しても、実家のドライブにアクセスするようになります。) filebrowserの下準備 メインのノードとは別にバックアップノードを用意しておき、rsyncで定期的にファイルを同期する filebrowserの前にnginxが動いているが、ここでHCを導入。メインがぶっ壊れた時にはバックアップにLBする。 0. filebrowserの下準備 Details filebrowserの立て方を簡単に教えますね。 バイナリをビルドするかダウンロードする(https://github.com/filebrowser/filebrowser) /usr/local/binに移動する。 パスワードとかゴニョゴニョする。パスワードのハッシュを生成。そして、–passwordの引数で渡す filebrowser hash mypassword ユニットファイルを作る。 例: [Unit] Description=FileBrowser Web UI After=network.target [Service] ExecStart=/usr/local/bin/filebrowser -a 0.0.0.0 -p 10001 -r /home/ray/hdd/filebrowser -d /etc/filebrowser/filebrowser.db --username hogehoge --password hogehoge --log /var/log/filebrowser.log WorkingDirectory=/etc/filebrowser Restart=always User=root Group=root [Install] WantedBy=multi-user.target sudo systemctl daemon-reload する sudo systemctl hogehogeが出力されたら、それを–passwordで渡す。 以上 1. rsyncで定期的にファイルを同期する 以下のコマンドを実行するcronジョブを作っておきましょう。毎時間ファイルを同期してくれます。 rsync -avz -e ssh ray@evn:~/hdd/filebrowser/ /opt/filebrowser_drive/ cronジョブの作り方は簡単 sudo crontab -e これで開いて、以下のジョブを登録する */1 * * * * rsync -avz -e "ssh -i /home/ray/.ssh/id_ed25519" ray@100.64.1.61:~/hdd/filebrowser/ /opt/filebrowser_drive/ cronはrootで実行されるので、鍵を指定しないと/home/root/.ssh の鍵を使いますが、めんどいので自分のを使うようにする。 ...

May 2, 2025 · 1 min · 186 words · Me

Grafana_variables

事始め いやね、だんだんと管理するインスタンス数が増えてくるわけですよ。そうすると一辺に全部のメトリクスが出てきてわかりずらいわけです。 それを解消するためにラベルを張って、どれがどれくらいか見たいよね、というお話です。 grafanaでラベルを変数化する方法 dashboard > setting > variablesから変数を用意できます。 変数にも種類があり、customやtextがあります。 おすすめはcustomですね。自分が好きな名前で候補を登録できます。 例えば以下のように evn : 100.64.1.61:9100, jhonny : localhost:9100, これ、カンマ区切りでkey:valueになってる。そして最後もカンマが必要ってのが大事。 そして、promqlでは以下のように書くことで変数を自分で選べるようになります。 rate(node_disk_written_bytes_total{instance=~"$host"}[1m])/1024/1024

March 27, 2025 · 1 min · 21 words · Me