かなで技術日誌

プログラミングやエンジニアリング周りについて

主なアウトプットはScrapboxObsidianにまとめてます。

Eloquent Model FactoryでMaximum function nesting level of '256' reached, aborting!

Eloquentのfactoryでテストデータを用意してテストを実行した際、以下のようなエラーが突如発生した。

PHP Fatal error:  Maximum function nesting level of '256' reached, aborting! path/to/vendor ...

結論

依存関係が循環するfactoryを定義して実行しており、factoryの実行自体でエラーにならなかったため。

内容

これは大量の再帰呼び出しを行った際に発生するエラーです。 再帰呼び出しの回数が非常に多い場合、メモリにひたすら実行された処理の結果が積み重ねられ、スタックオーバーフローが発生する可能性があるため、PHPでは一定回数を超えるとエラーが発生します。

このエラーになる回数自体はXDebugの設定で変更できます。

teratail.com

ですが、今回テスト対象の実装には再帰呼び出しもなければ大量のループもありませんでした。 本当の原因はエラーの原因はfactoryの実装で以下のような実装になっていました。

factory(Room::class, function(Faker $faker){
    return [
        'name' => 'room name',
        'house_id' => factory(House::class)->create()->id
    ];
});
factory(House::class, function(Faker $faker){
    return [
        'name' => 'house name',
        'room_id' => factory(Room::class)->create()->id
    ];
});

RoomモデルとHouseモデルが循環しており、実質無限に再帰的に相互で呼び出しを行う処理が作られていました。
しかし、Houseモデルが参照しているhousesテーブルにはroom_idは存在しません。 存在しないカラムをfactoryで定義しているのでエラーになりそうですが、Eloquentのfactoryでは存在しないカラム自体ではエラーにならず、今回のエラーになるまでずっと処理が止まらなかったということです。

想定していなかった動作だったので原因の発見に少し時間がかかりました。 個人的には存在しないカラムを定義しているのであればエラーを発生させて欲しいですね。

社内勉強会でLTやった

www.slideshare.net

社内勉強会で乱数と擬似乱数について発表しました。

内容的にはそんなに深いことはしていないですが、難しかったと言う声が多かったのでちょっと意外でしたが、非エンジニアも見るのでそれはそうかもと言う感ある。

内容的には、CSPRNGと真の乱数生成器以外は暗号技術に使わないでねってことと、線形合同法はよく考えて使ってねって内容でした。

まだメルセンヌツイスターとXorshiftはアルゴリズムがよく分かっていないのでまたちゃんと勉強したいですね。

2019年読んだ本と2020年やること・やらないこと

あとで見返せるようにするのと、やることを宣言するために。

2019年読んだ本

言語系

フレームワーク

ネットワーク

OS

セキュリティ

他にも細かく色々あるがとりあえずこんなところ。

2020年やること

今年は計算機科学の基礎固めとエンジニアリングの両立で行きたいと思います。

エンジニアリング領域

上期はこっちをやる。

計算機科学基礎

下期はおそらくこっち。
アルゴリズムとデータ構造、コンピュータアーキテクチャが必須で、最後は読めたら。一年でざっと読了できればいいなぐらい。

アルゴリズムとデータ構造枠として

  • CTF
    • 正解率〜40%の問題は基本的に解けるようになる。(Reversingは微妙かも)
  • C, C++
    • CS周りの勉強やCTFで必要なので。
  • AtCoder
    • 緑には最低でもなる。

本当はサーバサイドKotlinとかRustとかもやりたいが、今年は上記をやっていくことを優先。

2020年やらないこと

エンジニアとして優先度が相対的に低いもの。多分来年にやることになりそう。

  • 英語
  • MLとかkaggleなど
  • 離散数学(まとめて時間は割かないの意で必要になったら勉強する)

去年は上期は特定のフレームワークとか言語ばかりだったので、ひたすら計算機科学とエンジニアリングの基礎を固めていくようにしたい。

フレームワークとかパブリッククラウドなどは必要な時に必要に応じてやる程度にしてあまり深入りしない方針にします。

追記 大切ぽいことが書いてあったので

nowokay.hatenablog.com

2019年

年越しそば食べたのとポケモンで盛大に順位溶かしたので。

1〜3月

前職で開発やってた。Spring BootとAngular1を2:8ぐらいの割合で書いてた。

4〜6月

単体試験(実際の内容は結合試験)をやってた。
この辺から転職するつもりで動く。
現職の面接の前に2社で面接があり落ちた。うち1社はエージェントの紹介で面接を受けた会社が、あまりにも酷い態度で憤慨した記憶があり、エージェントはダメだと思った。
確か6月半ばぐらいに内定通知が来て、そこから辞める話をしたら営業がゴネ始めて(社会人としてのマナーがどうとしか言ってなかった)とりあえず常駐先とは統合試験がある7月いっぱいまでで交渉。

Scala触ったりEffective Javaとか読んでた。

7〜9月

7月は統合試験があり残業時間が80こそ超えなかったものの70は超えてた気がする。(でも基本給低いので残業代はそこまで)
この時期エクセルスクショしかやってなかった上に忙しかったので最悪だった。
8月に転職し、9月まで社内向けでLaravelで改修やったり新規案件やってた。(新規案件は諸事情により凍結となった)

10〜12月

10月からすでに進行している案件のヘルプ?としてエンジニアが欲しいとのことでアサインされる。
だいたいLaravelとVueやってた。
技術書典にも行った。

12月は社内で使ってる稼働入力と日報管理アプリのリライトでRailsとNuxt触ったりDjangoAPI実装したりしてた。
年末はフロントエンドエンジニアの部署で燃えつつある案件のヘルプで非同期周りを書いて終わった。(DOMを返すAPI is 何)



今年は人権がある環境に転職できたので総じて良かったと言えるでしょう。
ただ、転職前はもっと色々やっていた気がするので、転職して充足感もありインプットはともかくアウトプットが微妙だったので、来年は社内外ともにアウトプットを意識していきたい。

ksnctf-Easy Cipher

最近の勉強はネットワーク、OS、セキュリティなどをやっており、そういった知識の確認と単純に面白そうということからCTFに興味があります。

最近SECCONのオンライン予選があり事前情報無でソロ参加しましたが簡単なcryptoの問題以外は解けなかったので過去問とか常設CTFをやっていきたいと思ったので記録として。

CTFの入門(?)としては定番らしいksnctfというサイトの問題をやっていきたいと思います。
ksnctf.sweetduet.info

以下解法を載せているので注意してください。

今回解いた問題はこちら
ksnctf.sweetduet.info


内容はただの暗号化されているぽい文章があるだけです。













[解法]

とりあえず簡単な暗号と聞いてパッと思いつくのがシーザー暗号です。
ja.wikipedia.org
換字式暗号ですが、アルゴリズムとしてはいたってシンプルなのでまずこれかなと思いました。

とりあえずrotでdecryptできるそれっぽいサイトがあったと思うので探してencryptしたらそれっぽい文章になったので、文章通りに入力したら正解。
www.xarg.org

ただこれだけではあれなので、自分でrotをdecryptするscriptをPythonで書いて、改めて合ってるか確認しました。
なおPythonにはrotのライブラリがありますが、あくまで自分で実装してみます。

line = input()
for i in range(25):
    print('rot{}'.format(i + 1))
    result = ''
    for j in range(len(line)):
        # アスキーコードに変換
        code = ord(line[j])
        # A-Z
        if 65 <= code <= 90:
            code = code + i + 1
            if code > 90:
                code = code - 26
        # a-z
        elif 97 <= code <= 122:
            code = code + i + 1
            if code > 122:
                code = code - 26
        result += chr(code)
    print(result)

a-zとA-Zでrot1〜rot26を一発でコマンド上に表示します。

こんな感じで自分でCTF用のライブラリを用意すると便利そう

GCEでGPUインスタンスを立ててJupyter Notebookを導入するまで

f:id:kana_kanade:20190430164935j:plain

Google Compute EngineでGPUインスタンスを立てて、SSH接続してJupyter Notebookを使えるようにする手順をまとめました。
深層学習やkaggleなどをやるに当たって手元の計算資源が心もとない場合は、クラウドのリソースを使うことになると思いますがその際の環境構築についてまとめました。

結果として参考にした記事を継ぎ接ぎしただけ感が否めませんが、また自分でググる時が面倒なので記事として上げておきます。

いつものように、誤りがあればご指摘いただければと思います。

一応ローカルの環境

$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.4
BuildVersion:	18E226

Ubuntu16.04LTSへのcudaのインストールについては以下の記事を参考にしました。
qiita.com
cudaやAnacondaインストール後のJupyter Notebookの設定周りについては以下の記事を参考にしました。
qiita.com


大まかな手順は以下の通りです。

  1. Google Cloud SDKをインストールして初期化する
  2. VMインスタンスを立ち上げてcudaをインストールする
  3. Anacondaをインストールする
  4. ネットワーク設定を変更してJupyter Notebookを開けるようにする


1. Google Cloud SDKのインストール〜初期化するまで
まずはローカルでgcloudコマンドが使えるようにするため、Google Cloud SDKをインストールします。
基本的には以下の公式ドキュメントに従っていれば問題ないと思います。
cloud.google.com
セットアップが終わったら、gcloudコマンドが使えるか確認して、使えるようであれば次に進みます。

2. GPUインスタンスの立ち上げ〜cudaのインストール
上で上げた記事にも言及がありますが、コンソールからインスタンスを作成してもうまくいかないようで、一番最初はここで自分もはまりました。
なので、ローカルからgcloudコマンドでインスタンスを立ち上げる必要があります。
インスタンスを立ち上げる前にいくつか確認する項目がありますが、まずは参考までに以下が自分が叩いたコマンドです。{}内は適宜合わせてください。

$ gcloud beta compute instances create {INSTANCE_NAME} --machine-type n1-standard-4 --zone us-east1-d --boot-disk-size 20GB --accelerator type=nvidia-tesla-k80,count=1 --image-family ubuntu-1604-lts --image-project ubuntu-os-cloud --maintenance-policy TERMINATE --restart-on-failure --metadata startup-script='#!/bin/bash
# Install CUDA
if ! dpkg-query -W cuda; then
  curl -O http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/cuda-rep
o-ubuntu1604_8.0.61-1_amd64.deb
  dpkg -i ./cuda-repo-ubuntu1604_8.0.61-1_amd64.deb
  apt-get update
  apt-get install cuda -y
fi'

1 リージョンとGPU(?)の割り振りの確認
accelerator typeで指定しているGPU(?)はリージョンによって使える使えないがあります。
以下のコマンドで、どのリージョンでどのGPUが使えるのか確認できるので、それに合わせて設定してください。

$ gcloud beta compute accelerator-types list

2 APIの割り当ての確認
GPUの割り当てには上限があり、デフォルトでは1つなのでcount=1であれば大丈夫ですが、2以上積みたい場合はまず割り当てを確認し、足りない場合は申請する必要があります。
IAMの管理→割り当てから使うGPUでフィルタをかけて割り当て数を確認できます。
足りない場合は、チェックをして割り当てを編集から必要事項を入力してリクエストを送りましょう。

晴れてgcloudコマンドでインスタンスを立てたら、コンソールに移動してインスタンスが作成されていることを確認してください。
作成されていれば、SSHボタンを押すとコンソール画面が開きます。

開いたらcudaの環境設定をします。
(自分はsudoつけないとだめだった)

$ echo "export PATH=/usr/local/cuda-9.0/bin\${PATH:+:\${PATH}}" >> ~/.bashrc
$ source ~/.bashrc
$ sudo /usr/bin/nvidia-persistenced

終わったら、以下のコマンドを実行してドライバー情報が表示されたらインストール完了です。

$ nvidia-smi

もし表示されなかったら、インスタンス作成時にcudaがうまく入っていない可能性があるので、このコンソールで入れ直します。
sudoをつければうまくいくと思います。
cudaのインストールは結構時間がかかりますので気長に待ちましょう。

3. Pythonのインストール
Anacondaが安定だと思うのでインストールします。
www.anaconda.com
必要に応じてsudoをつけてください。
{USER_NAME}は置き換えてください。

$ wget https://repo.anaconda.com/archive/Anaconda3-2019.03-Linux-x86_64.sh
$ sh ./Anaconda3-2019.03-Linux-x86_64.sh
$ echo ". /home/{USER_NAME}/anaconda3/etc/profile.d/conda.sh" >> ~/.bashrc
$ source ~/.bashrc

終わったら仮想環境を作成します。
versionをpyhton3.6で指定して、仮想環境を作成し有効にします。

$ conda create -n {VENV_NAME} python=3.6
$ conda activate {VENV_NAME}

あとはconda install でTensorflowなりPyTorchなりをいれてください。

4. Jupyter Notebookの設定
Jupyter Notebookの設定を変更し、立ち上げるところまでやります。
Jupyter NotebookのConfigファイルを作成します。

$ cd ~
$ jupyter notebook --generate-config
$ cd .jupyter

SSL用の鍵を作成します。

openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mykey.key -out mycert.pem

色々聞かれますが、それっぽい内容を入力しましょう。以後この情報が必要になることは今の所ありません。

入力が終わったら、pythonインタプリタからハッシュを生成します。

$ python
>>> from notebook.auth import passwd; passwd()

2回パスワードを入力した後にハッシュが表示されます。
パスワードはJupyter Notebookを開く際に、ハッシュはこの後設定ファイルを変更する際に必要となるので控えておきましょう。

次にConfigファイルを編集します。
編集方法は

cd .jupyter
$ vi jupyter_notebook_config.py

でファイルの中身が開きます。
キーボードのiを押すと編集可能になり、保存して閉じる場合は
1.Escapeキーを押す
2.「:wq」と入力する
と保存されます。

$ cat jupyter_notebook_config.py

でコンソールに表示して確認ができます。

Configファイルはだいぶ長いですが、上からc.NotebookAppのアルファベット順で以下のように並んでるので、コメントアウトを外して修正してください。
{HASH}には、先ほど控えておいたパスワードのハッシュをいれてください。

# OpenSSLで作ったファイルへのパス
c.NotebookApp.certfile = u'/home/username/.jupyter/mycert.pem'

# どのIPアドレスからのアクセスも受け入れる
c.NotebookApp.ip = '0.0.0.0'

# OpenSSLで作ったファイルへのパス
c.NotebookApp.keyfile  = u'/home/username/.jupyter/mykey.key'

# 勝手にブラウザを起動しない
c.NotebookApp.open_browser = False

# passwd()コマンドで作ったパスワードのハッシュを貼る
c.NotebookApp.password = u'{HASH}'

# 外部からアクセスするためのポート番号を指定する
c.NotebookApp.port = 8888

終わったらGCPのコンソールに移動します。
作成したインスタンスのページに移動し、インスタンス名をクリックするとインスタンスの情報が表示されます。
上の編集ボタンを押して、編集可能な状態にします。

ネックワークインターフェースのdefault → ネットワークのdefaultを選択してVPCネットワークの詳細を開きます。
「ファイアーウォール ルール」タブを選択し、ファイアーウォールルールを追加を押して、以下のように追加します。

ターゲットタグ: https-server
ソースタグ: 0.0.0.0/0
指定したプロトコルとポート: tcpにチェックして8888と入力

これで新たなファイアーウォールルールが追加されます。


追加したら編集画面まで戻り、先ほどのネットワークインターフェースの下に、ファイアーウォールの「HTTPSトラフィックを許可する」にチェックが付いているか確認し、なければチェックしてください。

チェックしたらVMインスタンスのページまで戻って、外部IPのアドレスが表示されていると思うのでクリックします。

クリックすると「このサイトにアクセスできません」と表示されると思います。(IPアドレスは隠しています)
f:id:kana_kanade:20190430162950p:plain

これは正しいポート番号を指定していないからです。
追加したファイアーウォールルールでポート番号を8888と指定しているので、IPアドレスの末尾に:8888と追加してアクセスしてみます。
f:id:kana_kanade:20190430163235p:plain

警告が出ていますが、これはSSL証明書が正規のものではないため(?)にこのような表示となっています。
(正直この辺は怪しいので理解があってるか微妙です。)
左下の詳細設定を押すと以下のように表示されます。
f:id:kana_kanade:20190430163900p:plain

アクセスしてみるとパスワード入力欄があるので、pythonでハッシュを生成する際に入力したパスワードを入力して、無事Jupyter Notebookが開くと思います。

終了する場合はCtrl+CでJupyter Notebookを終了し、VMインスタンスを停止して終了となります。(重要)

インスタンスは作っては壊してができますし、慣れるとそんなに時間もかからないと思います。

参考になれば幸いです。

PyCharmでPlease specify a different SDK nameの解決方法

結論:~/Library/Preferences/./options/jdk.table.xmlを削除orリネームしてPyCharmを再起動

OS:macOSX
PyCharm2019.1

PyCharmでProject Interpreterで仮想環境をApplyしようとしても、Please specify a different SDK name.とのエラーが表示されてしまい、仮想環境が保存できなくなりました。
仮想環境の名前の衝突がありそうで名前を変えてみましたが解決しませんでした。(これで解決した例もあるようです)

で以下のJetBrainのページに行き着きました。
intellij-support.jetbrains.com
①PyCharmを閉じる。

jdk.table.xmlを削除orリネーム
macOSXであれば、今回はPyCharm2019.1なので
~/Library/Preferences/PyCharm2019.1/optionsの下にあるようです。
Windowsであれば
intellij-support.jetbrains.com
から推測すると、Cドライブなら
C:\Users\\.PyCharm2019.1
の下の同様の場所にあるのでしょうか。(確認はしていないです。)

③PyCharmを再起動

この手順で無事仮想環境を保存できるようになりました。

色々探しましたが、やはり信頼できるのは公式ですね。