二度忘れた事を三度忘れないようにする

しがないフリーランスIT系エンジニア

shutdown時にスクリプトを動かす。

この記事を参考にpower.shに動かしたいシェルスクリプトを1行追加したところうまく動かなかったのでメモ。

dq310.com

原因は不明だけども、ログファイルをsftpする以下みたいなコマンドを書いたけどうまく動かなかった。

#shutdown.sh
sftp -b sftp.bat -i /home/user/.ssh/id_rsa user@192.168.0.0

手打ちすると動くけど、power.sh経由だとsuだなんだしても動かなかったので、以下の方式に変更したらとりあえず動いた。

www.sssg.org

GCP利用メモ

計算するの面倒なので実績値を記録していくメモ帳

・Cloud Storage
インスタンスにマウントさせて利用
このへんを参考にした
gsutil Tool  |  Cloud Storage Documentation  |  Google Cloud Platform

Cloud Storage FUSE  |  Cloud Storage Documentation  |  Google Cloud Platform

github.com

5,500ファイル系15GBをコピーした際
約2時間でコピー完了。
GCS API  30,500アクセス/11,500エラー
GCS JSON API 30,500アクセス/11,500エラー

料金 明日確認する。。。

自作キーボードのメモ

自作PCよりは自作ちっくな自作キーボード。
手順は参考URLに載ってるので割愛。

とりあえず必要なもの(今回買ったもの)
・キーボード基盤(GH60 satan)
・キースイッチ(Gateron クリア?白?軸)
・キーキャップ(黒色PCBのダブルショット
・LED(色の変化スピードが遅いのと速いの)
・プレート(キースイッチと基盤の間?にはめるやつ 銀色)
・ケース(透明)
・スタビライザー(スペースとかの長いキーを安定させるやつ)
・半田ごて(amazonでセットになってるやつ)
・USBケーブル(ヨドバシで適当に)

プレートはなくてもいいかなーと思ったり。実際、半田した後に スタビライザーの針金が外れて直すのに手こずった。。。

参考URLと利用時のメモ

キーボードのレイアウトエディター
Keyboard Layout Editor

ファームに書き込める形式に変換
TMK Keymap Generator

・スペースは「SPACE」としないと認識しなかった。
・LEDは両方チェックいれると純粋なOFFは実装出来ない?

ほぼ同じ構成

SATAN BUILD LOG - Everything you need to know as a beginner to build a keyboard
imgur.com

GCPのネットワーク設計・序

東京リージョンが来たのでGCPを全力で触ってる途中ですが、ネットワーク、ファイアウォール(FW)の仕様がちょっと変わっていたのでメモ。 (追記事項があり次第更新予定) まだまだ勉強中なので誤りがあればご指摘ください!

ネットワーク設計する際の注意点

  • 「ネットワーク」同士のローカルIPによる繋ぎこみは出来ない。
     VPN使えばいけるだろうけど、全体の管理者とかでなければ考えなくていいかと。

  • FWルールに「Deny」は無い
     許可のルールだけ書いてあとは全てDenyという非レガシーな考え方。らしい。

  • FWルールのdestは何かしらのインスタンス指定方法は「全て」か「タグ」
     グローバルIP持ってるインスタンスはタグ付け必須。サブネットとか関係無い仕様。
  • FWルールにインターネットへのアウトバンド制御不能。
     上述のとおり、destはインスタンスしか指定できません。
  • AWSでいうプライベートサブネットは無い。
     後でオレオレセキュアネットワーク設計を書く予定。
  • CloudSQLはグローバルIPでしかアクセスできないので、GCEは静的IPアドレスが必要。  アクセス制御の方法がCloudSQLで設定できるネットワーク(IPアドレス)依存なので、要注意。なんかCloudSQLProxyてのもあるみたいだけど、あんまりメリットというか違いがわからなかった。とりあえずMySQL Proxyのようなモノかと思ったけど、そうでもないっぽいのでちょっと気持ちがふわふわする感じ。。。
  • ルートで設定できるネクストホップはネットワークで定義しているサブネットかつ、存在するIP転送設定しているインスタンスIPアドレスしか指定できない。
     存在しないIPアドレスとか自動生成されるデフォゲIP、リンクローカルアドレスのような特殊IPアドレスは指定できませんでした。

今の所、AWSに慣れてるとクセに感じる事があったりするけど、オンプレ経験のほうが長い身としてはGCPのほうが直感的な感じではある。専用用語が少ないってのもあるけど、ベンダが誰の立場となってサービス作ってるか、という視点の違いが大きいんだろうな。

しかし、安くて速いのでこれからAWSではなくてGCP採用が増加しそうですね。お仕事待ってます。

CentOS 7 でswap領域を追加する

AWSで小さいインスタンスを使っていると、updateなり一時的作業なりでメモリ不足のエラーが出ることがままあるのでそういう時用に。

参考

dev.classmethod.jp

#!/bin/sh

SWAPFILENAME=/swap.img
MEMSIZE=`cat /proc/meminfo | grep MemTotal | awk '{print $2}'`

if [ $MEMSIZE -lt 2097152 ]; then
        COUNT=$[${MEMSIZE} / 512]
elif [ $MEMSIZE -lt 8388608 ]; then
        COUNT=$[${MEMSIZE} / 1024]
elif [ $MEMSIZE -lt 67108864 ]; then
        COUNT=$[${MEMSIZE} / 2048]
else
        COUNT=4096
fi

dd if=/dev/zero of=${SWAPFILENAME} bs=1M count=${COUNT} && chmod 600 ${SWAPFILENAME} && mkswap ${SWAPFILENAME} && swapon ${SWAPFILENAME}

CentOS7の場合、ddコマンドでファイルを作る必要がるので、その辺を弄ったスクリプト担ってます。

Rails4のリレーションを使い分けたい

個人的にはSQL文を組み立てるのは苦手ではありつつも、SQL文をある程度書けるようになるべきだと思っています。とはいいつつも、様々なフレームワークで実装されているモデルの機能というのは素晴らしいと思っています。特にRails4のhas_one,has_manyといったリレーション機能は非常に便利だと思います。

今回、Rails4でリレーションの設定をしたモデルを使っていて、発行されているクエリを見ていると非常に効率の悪いクエリが発生しているのに気付きました。

ざっくりいうと、SNSのようにユーザ一覧ページを表示した時に、一覧表示に必要のない参照先のテーブル情報まで引っ張っていました。一覧で100人表示したら不要なSELECTのクエリが100回(リレーション数によっては200,300となります)発生する動作をしていたので、流石に無いなと思って調べてました。

DB設計がなってない、と言われればそこまでなんですがそこは置いておいて。。。Rails4ではリレーションの設定をした場合、find_by_sqlとかなんとか使っても参照先のテーブルから情報を引っ張るようです。

あまりスマートではないですが、modelファイルをもう一個作って今回は回避しました。

class Users < ActiveRecord::Base
  has_one :profiles
  has_many :followers
# 略
end
class UsersNonRelational < ActiveRecord::Base
  self.table_name = 'users'
end

親テーブルの一覧が欲しいだけなんだけど、、、って時の対応でした。もっとスマートな方法・機能があれば教えてください。。。

Phalconを使う際に最初にすることリスト

どのようなサービスであっても個人的にほぼ必須と思っている設定をつらつら書き留める記事です。
こんなのもあるといいYOってのがあれば教えてください。

環境
CentOS 7
Phalcon 3.0.1
PHP 7.0.10

名前空間(Namespace)

使わない人はとことん使わないイメージですが、個人的にはあまりデメリットを感じないので必ず利用するようにしてます。

// app/config/loader.php
<?php

$loader = new \Phalcon\Loader();

$loader->registerNamespaces([
        'Appname\Controllers' => $config->application->controllersDir,
        'Appname\Models' => $config->application->modelsDir
]);

$loader->register();

ローダに名前空間ディレクトリの関係を定義します。「'Appname' => 'app/'」みたいにするとコントローラとかモデルも同的に定義・読み込みするらしいのですが、少々面倒でも細かく定義するようにしています。サブモジュールとかで階層重なるのを防げたりするので。

// app/config/services.php
// 以下を追記(ルーティング設定により不要
$di->set('dispatcher', function () {
    $dispatcher = new Dispatcher();
    $dispatcher->setDefaultNamespace('Appname\Controllers');
    return $dispatcher;
});

デフォルトのnamespaceをここで指定すると、次のルーティング設定時にnamespaceの指定を省略できます。逆に異なるnamespaceを多用するのであればあまりメリットはでないかもしれません。

ルーティング

ルーティングの実装方法はいくつか用意されていますが、極力読みやすく厳しく設定できると思っている方法を使ってます。といっても公式で紹介されている方法ですが。あと正規表現は適当です。

// app/config/router.php
<?php

use Phalcon\Mvc\Router;
use Phalcon\Mvc\Router\Group as RouterGroup;

// デフォルトの動作を無効化
$router = new Router(false);
// 末尾のスラッシュを自動的に取り除く
$router->removeExtraSlashes(true);

// デフォルトルート
$router->add('/', ["controller" => "index", "action" => "index"]);
// 404のパス設定
$router->notFound(["controller" => "index", "action" => "route404"]);

// ここから
$blog = new RouterGroup(['controller' => 'blog']);
$blog->setPrefix('/blog');
$blog->addGet('', ['action' => 'index']);
$blog->addGet('/edit/{id}', ['action' => 'edit']);
$blog->addPost('/edit', ['action' => 'create']);
$router->mount($blog);
// ここまでを1セット

return $router;
// app/config/services.php
// 以下を追記
$di->setShared('router', function () {
    $config = $this->getConfig();
    require $config->application->routerPath; // configにパスを書いています
    return $router;
});

Groupを使った時のルート指定は「''」とすることで定義可能。
なお、わざと1行で書くようにして見通しをよくしてます。コーディング規約的におかしいとか言われた事もありますが、ルーティング定義はPHPで書かれているとはいえ、個人的には見易さを重視します。
あと、書き方が冗長だ、こうすれば動的に定義できてスッキリする、というのはわかっていますが、明示的に設定した以外の動作を取って欲しくない(特にチームで開発している時)ので、パスやメソッドとか細かく書いてます。

Viewの無効化(View不要時)

APIとか作る際にはViewを無効にするだけで、標準のMVCを使うようにしています。これは学習コストの問題だけなので効率的か、といわれると効率的ではないでしょう。

// app/controllers/ControllerBase.php
    public function initialize()
    {
        $this->view->disable();
    }

Viewに処理が到達するまでに実行されればいいので、どこに記述しても問題はないです。APIを作っている時は初期化時に実行させれば記述漏れとかなくなるので、親コントローラのinitializeに記述していることが多いです。

ログ出力

とりあえず実装して、足りない部分はそれぞれのコントローラやモデルで実装します。

// app/config/services.php
// 以下を追記
use Phalcon\Logger;
use Phalcon\Logger\Adapter\File as FileAdapter;
$di->setShared('logger', function () {
    $config = $this->getConfig();
    $logger = new FileAdapter($config->application->applogPath, ['mode' => 'w']);
    return $logger;
});

手っ取り早いのでファイル出力のロガーを実装。syslogやらもあるので必要に応じて変更する。

Exception

エラーはExceptionで処理する事が多かったので、どこのExceptionでも一箇所でコントロール出来るようにします。個人的には設計しやすいのでよく使います。(主要部分のみコード記載)

// app/config/services.php
// 以下のように編集
use Phalcon\Mvc\Dispatcher as MvcDispatcher;
use Phalcon\Dispatcher;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Mvc\Dispatcher\Exception as DispatchException;
use Appname\Controllers\ExceptionModule;
$di->setShared('dispatcher', function () {
    $dispatcher = new MvcDispatcher();
    $dispatcher->setDefaultNamespace('Appname\Controllers');

    $eventsManager = new EventsManager();

    // Attach a listener
    $eventsManager->attach("dispatch:beforeException", new ExceptionModule());

    $dispatcher->setEventsManager($eventsManager);

    return $dispatcher;
});
// app/controllers/ExceptionModule.php
<?php
namespace Appname\Controllers;

use Phalcon\Events\Event;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Mvc\Dispatcher\Exception as DispatchException;

class ExceptionModule
{
    public function beforeException(Event $event, Dispatcher $dispatcher, $exception)
    {
        // Handle 404 exceptions
        if ($exception instanceof DispatchException) {
            $dispatcher->forward(array(
                'controller' => 'index',
                'action'     => 'show404'
            ));
            return false;
        }

        // Handle other exceptions
        $dispatcher->forward(array(
            'controller' => 'index',
            'action'     => 'show503'
        ));

        return false;
    }
}

DIのところでbeforeExceptionを独自のものを指定する。いちおう公式に載っている方法ですが、Exception処理部分を別ファイルにして管理しやすいようにしています。(以前、docでもnewして外部ファイルで管理する方法をもうちょっと細かくコード付きで書いてたような記憶がある)
基本的にはinstanceofのif文を必要なだけ追記してException毎に処理を分けていきます。開発時は最初にログ出力処理を書いてデバッグしやすくしたりしてます。