動かざることバグの如し

近づきたいよ 君の理想に

M1 lima環境でminikubeをインストールする方法

環境

  • M1 Macbook
  • minikube v1.26.1
  • lima 0.11.3

やりたいこと

使い捨てのKubernetes環境が欲しく、ローカルにKubernetesをインストールしようとした。

本番環境ならkubeadm一択なんだろうけど、サクッと構築するだけなら1台で完結するクラスタがいいなと思いminikubeがイケてそうだったので試したメモ。

ただ手元の環境がM1なのでamd64のイメージが動作しない。arm64のKubernetes環境作っても色々ハマりそうなので多少遅くなってもamd64クラスタがほしい。そこでDockerのバックエンドに使っているlimaに頼ることにした。

minikubeのインストール

公式サイトを見ましょう

minikube start | minikube

方法1: limaでDocker環境を構築しminikubeがそれを使う(失敗)

まずはlimaで公式のexamples/docker.yamlをインストールして、そことマウントされたDockerのSocketを利用してminikubeをインストールする方法。

minikube start --driver=docker --container-runtime=containerd

が、何回やっても失敗する。 Issue立てていたが仕様上無理とのこと。

github.com

ので諦めてオススメされた方法で試してみる。

limaで仮想VMを構築してその中にDockerとminikubeをインストール(成功)

  • limaでexamples/ubuntu.yamlUbuntuVMを起動
  • lima shell ubuntu で中に入り、普通にDockerとminikubeをインストール
  • 引き続きその中で minikube start してk8s環境を構築する

よさげ

❯ kubectl get pod -A
NAMESPACE     NAME                               READY   STATUS    RESTARTS   AGE
kube-system   coredns-6d4b75cb6d-667z4           1/1     Running   0          2m46s
kube-system   etcd-minikube                      1/1     Running   0          2m59s
kube-system   kindnet-j4pzm                      1/1     Running   0          2m46s
kube-system   kube-apiserver-minikube            1/1     Running   0          2m59s
kube-system   kube-controller-manager-minikube   1/1     Running   0          2m59s
kube-system   kube-proxy-lnvv2                   1/1     Running   0          2m46s
kube-system   kube-scheduler-minikube            1/1     Running   0          2m59s
kube-system   storage-provisioner                1/1     Running   0          2m57s

ログをもっと詳細に出したい

デフォルトだと最低限のログしか出ないのでエラー時に対応しづらい。

以下のようにするとめっちゃ細かくログを出力してくれる。

minikube start -v=1  --alsologtostderr

エラーで終了してしまった場合とかは minikube logs --file=error.txt で直近のログをファイルに保存できる。

初期化したい

minikube delete --all --purge

特定の文字から始まる文字だけマッチさせる正規表現

特定の文字から始まる文字だけマッチさせる正規表現を書きたい

「$」記号の後に整数が来る。例えば

¢100 makes $1

という例文があったとして、$1を抽出したい。

この場合は肯定後読み(Positive lookbehind)正規表現機能を使う必要がある。

let str = "¢100 makes $1.";
alert(str.match(/(?<=\$)\d+/))
// 結果: 1

(?<=Y)X は X の前に Y がある場合にのみマッチすることを意味する

逆に特定の文字から始まらない文字だけマッチさせる正規表現

否定後読み(Negative lookbehind)を使う。 (?<!Y)X は X の前に Y がない場合にのみマッチすることを意味する

let str = "¢100 makes $1.";
alert(str.match(/(?<!\$)\d+/))
// 結果: 100

参考リンク

Railsでmodelをフォルダ階層にしてきれいにする方法

環境

Railsのモデルディレクトリ荒れる問題

Railsはデフォルトだとapp/modelsにモデルファイルを生成する。

それはルールなので別にいいのだが、プロジェクトが成長するに従って10ならいいが50を超えてくるとかなりキツくなる。

そこでディレクトリをapp/models以下に作成して階層化したい。ディレクトリ別にグルーピングしたい

が、ググってもDB構造の階層化ばかりでフォルダを分ける記事が全然見当たらなかったのでメモ

サブディレクトリ化していない例

例えばこれは通常のPostモデル app/models/post.rb

class Post < ApplicationRecord
end

.model_name でそのモデルの命名規則などがわかる

irb(main):014:0> Post.model_name
=> 
<ActiveModel::Name:0x0000000114a6d2f8    
 @collection="posts",                     
 @element="post",                         
 @human="Post",                           
 @i18n_key=:post,                         
 @klass=Post (call 'Post.connection' to establish a connection),
 @name="Post",                            
 @param_key="post",                       
 @plural="posts",                         
 @route_key="posts",                      
 @singular="post",                        
 @singular_route_key="post",
 @uncountable=false>

まあそうなるわな

サブディレクトリ化する

まずは app/models/admin.rb を作成して以下

module Admin
end

で app/models/admin/post.rb を作成して以下

class Admin::Post < ApplicationRecord
end

これだけで後は Admin::Post でアクセス出来る

irb(main):021:0> Admin::Post.new
   (0.1ms)  SELECT sqlite_version(*)
=> #<Admin::Post:0x0000000115264830 id: nil, title: nil, date: nil, body: nil, created_at: nil, updated_at: nil>

だがこれだけではめでたしめでたしとはならない。form_withでフォームを作成するともれなくURLが想定ではなくなってバグるため。

もともとPostAdmin::Postディレクトリ作っただけなので、ルーティングやコントローラー周りは階層化するつもりはない。

しかし同様に .model_name すると route_key が「admin_posts」になってしまってるじゃないか。。。

irb(main):014:0> Post.model_name
=> 
<ActiveModel::Admin::Post:0x00000001141bd180    
@collection="admin/posts",                                            
@element="post",                                                      
@human="Post",                                                        
@i18n_key=:"admin/post",                                              
@klass=Admin::Post (call 'Admin::Post.connection' to establish a connection),
@name="Admin::Post",                                                  
@param_key="admin_post",                                              
@plural="admin_posts",                                                
@route_key="admin_posts",                                             
@singular="admin_post",                                               
@singular_route_key="admin_post",                                     
@uncountable=false>                                                   

解決方法

app/models/admin.rb にメソッドを追加する

module Admin
  def self.use_relative_model_naming?
    true
  end
end

Railsをリロードすると route_keyposts に戻っている!

irb(main):023:0> Admin::Post.model_name
=> 
<ActiveModel::Name:0x0000000117305ee0                                 
 @collection="admin/posts",                                            
 @element="post",                                                      
 @human="Post",                                                        
 @i18n_key=:"admin/post",                                              
 @klass=Admin::Post (call 'Admin::Post.connection' to establish a connection),
 @name="Admin::Post",                                                  
 @param_key="post",                                                    
 @plural="admin_posts",                                                
 @route_key="posts",                                                   
 @singular="admin_post",                                               
 @singular_route_key="post",                                           
 @uncountable=false,                                                   
 @unnamespaced="Post">                                                 

他にも色々見えないところで変わってるらしく、ただモデルの場所をフォルダ分け考えているなら全然あり

備考

ちなみにRailsのモデルを階層化しろって話はRails1.2時代から指摘されてたっぽい。ウケる

errtheblog.com

Switchゲーム映像をMacのDiscordで配信する方法

Discordで通話仲間に配信したい

ゲームしてると目の前の自分の画面を共有したいことがある。PCの画面だと画面共有押せばすぐに共有できるが、ニンテンドースイッチ自体にはその機能がないためできない。

そこでキャプチャボードを使ってPCを通すことで画面共有できるようになったのでやり方でメモ。

要件

  • ニンテンドースイッチのゲーム画面をDiscordの画面共有機能で共有できるようにする
    • ゲームの音声も共有する
    • 当たり前だが通話の音声は共有しない
  • あくまで画面共有だけで録画はしない
  • 今回はマイク(音声入力)はMacbook内蔵のマイクを使用するとする
  • PCはWindowsではなくMacbookなのでMacOSとなる

用意するもの

ニンテンドースイッチ

これがないと始まらない。自分は旧型を使用したが新型でも可能。当然映像出力機能のないスイッチライトは不可。

キャプチャボード

今回は録画機能とか要らないのでとりあえず安さ重視でコレを買った

Type-Cケーブルも(短いが)付属している点がGood

パソコン(Macbook)

これも必須。なぜならスイッチ本体に配信機能が無いため。

今回自分はM1 Macbookを使った。

アプリケーションとしては以下

  • Discord: 普段使ってるやつ
  • OBS: 配信するときの管理アプリ。Mac版も出てるしついこの間M1ネイティブ版もリリースされた

HDMIケーブル2本

普段は1本で十分だが、1本多く使うので注意

やり方

まずは構成図のように配線をする。

スクリーンショット 2022-08-08 8.47.41.png

次にOBSをダウンロードしてインストール。設定していく

OBSの設定

画面を開いたらウィザードみたいなのが表示されるが今回はOBS本体で配信、録画をしないのでキャンセルする

ここではOBSでゲームの映像、音声を扱えるようにするのがゴール。

OBS画面の左下の方にあるソースを右クリックして「映像キャプチャデバイス」を選択

1.png

そのまま「OK」をクリック

2.png

バイスをキャプチャボードのやつ(自分の場合はMiraBox Capture)にしてプリセットから「1280x720」を選択する。

これでOBSでスイッチの映像が扱えるようになった。

が、これだけだとゲームの音声が入らず無音配信になってしまうので音声も入れるようにする。

同様にソースを右クリックして「音声入力キャプチャ」をクリック

わかりやすいように「ゲームの音声」とかの名前にして「OK」クリック

バイスを同様にキャプチャボードのやつ(自分の場合はMiraBox Capture)にして「OK」をクリック

これでOBSでゲームの音声も扱えるようになった。が、イヤホンをパソコンにつけてもゲームの音が聞こえない。

これはデフォルトではゲームの音声が出力の対象外になっているため。配信では音が流れても自分が無音でゲームは流石にツラいため以下の設定を行う。

画面下の方の「音声ミキサー」ウィンドウよりゲームの音声のサブメニューボタン「︙」をクリックして「オーディオの詳細プロパティ」をクリック

すると「オーディオの詳細プロパティ」ウィンドウが表示されるので「ゲームの音声」の音声モニタリングを「モニターと出力」にする。するとゲームの音声が聞こえるはず。

確認ポイント

最終的なOBSのウィンドウは以下のようになるはず。

  • プレビュー画面にゲームの画面(例だとアオリちゃん ホタルちゃんのかわいい画像)が映っていること
  • 自分がゲームの音声を聴けてること
  • なんか喋った時に音声ミキサーのマイクの音声ゲージが動くこと

これを満たしていればOBSの設定は完璧

Discordの設定

次はDiscordの設定

悲しいことにMacOSのセキュリティ上そのままではゲームの音声を画面共有で流すことができない。そこでDiscordの拡張機能をインストールする必要がある。

まずはDiscordの設定を開き「音声・ビデオ」を選択

画面共有の欄にインストールボタンがあるのでインストール。

が、最近のMacOSだとさらにセキュリティが厳しくなっており、「現在のセキュリティ設定では、システム機能拡張のインストールは許可されていません」と言われてそのままではインストールできない。そこで以下の記事を参考にセキュリティレベルを一旦下げてインストール出来るようにする。

note.com

gori.me

最終的に以下のように「アプリケーションから音声をキャプチャする準備が整いました」と表示されればインストール完了である。

ちなみにDiscord自体の音声設定はこんな感じでOK。いつもどおりで特に画面共有するからと言って変えることは他にない。

確認ポイント

  • 画面共有の拡張機能のインストールが完了している
  • OBS同様に画面共有の権限がOKになっている

いざ配信

これでようやく配信できるが、このままOBSの画面を配信しても編集画面が映ってしまう。

MacOSの場合はWindowsみたいにOBSとDiscordが連携していいように画面を加工してくれないので、自分でゲーム画面だけのウィンドウを作成し、それをDiscordに画面共有させる必要がある。

まずはMac画面の右上のマニューバーからOBSのアイコンをクリックし、「全画面プロジェクター(番組)」を選択しディスプレイをクリック

すると全画面でゲームの画面が映し出される。が、Discordは全画面のウィンドウを画面共有できないので、ウィンドウの緑ボタンをクリックして全画面表示を解除する

で、DiscordをOBSと同じディスプレイスペースに置いた状態でDiscordの画面共有ボタンをクリックし全画面表示させたゲーム映像だけのウィンドウを選択する

「Go Live」をクリックすると晴れて配信が開始される。やったね!

が、このままだとMacのウィンドウのタイトルバーも同時に映ってしまう。なんかダサい。

そこで一度縮小化したゲームのウィンドウを再度全画面化する。こうしても1度画面共有されていれば解除されることはない。

これで相手からはゲームの映像だけが画面共有される。

はてなブログのsitemap.xmlをCIで自動送信するようにした

はてなブログのsitemap.xmlひどすぎ問題

これは昔から続いている問題なんだが、はてなブログだとインデックスが上手くいかないケースが有る。

先人の人たちが腐るほどに記事にしてくれている

karupoimou.hatenablog.com

chateau-vulpes.com

www.imaichido.com

kanaxx.hatenablog.jp

記事は消されているけど、はてな公式ですら問題を認識している。ならはよ直せよ。

GoogleSearchConsoleからのサイトマップ送信に失敗する問題について

どうもGoogle公式のAPIでインデックスの送信ができるのを知ったので自動化した。

成果物

ググったらコードあった。感謝

github.com

クローンしたのが以下

github.com

Github Actionsを以下のように作成

name: Github Action CI

on:
  push:
  schedule:
    - cron:  '57 22 * * *'

jobs:
  execute:
    name: Submit sitemap to Google
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: "Install compose packages"
        run: composer install

      - name: "Execute PHP"
        env:
          GCP_CREDENTIALS: ${{ secrets.GCP_CREDENTIALS }}
        run: |-
          echo $GCP_CREDENTIALS | base64 -di > credential.json
          php publish_sitemap_to_indexing_api.php "https://thr3a.hatenablog.com/sitemap_periodical.xml?year=$(date +%Y)&month=$(date +%-m)"

サービスアカウントの作成

ここが結構面倒くさい

  • GCPの管理画面から「Indexing API」「Google Search Console API」のAPIを有効化する
  • API とサービス/認証情報より「サービスアカウント」を新規作成
    • ロールは「閲覧者」でOK
  • 作成したサービスアカウントを選んで認証キーを生成、JSONを保存する
  • サーチコンソールからからユーザー管理を選択
  • さっき作ったサービスアカウントのメールアドレスをユーザー追加
    • 必ず権限は「オーナー」にする

GitHub ActionsのCI設定

ダウンロードしたJSONbase64に変換してSecretsに登録する。CIからは環境変数で渡る

cat credential.json | base64

CI回して全部200ならOK

スクリーンショット 2022-08-07 16.42.26.png

スケージュール入れたので毎日自動送信されるはず