MUGIJIRU.JP

Webエンジニアの雑談ブログ

インフラの進歩すごいですね

最近、kubernetes(コンテナオーケストレーションシステム)の勉強をしているのですが
インフラの世界の進歩を改めてすごいなーと思っています。
自分が見てきた変化をざっくりまとめてみました。

サーバマシン組み立てて頑張る時代

オフィスにサーバマシン。これに一通りのミドルウェアを乗せて頑張っていた。

f:id:yast03:20181203182552j:plain

トラフィックやばい。台数増やさなきゃ時代

分散しなきゃやってられなくなって台数が増える。
メンテ超きつい。監視も頑張らないと。。。

f:id:yast03:20181203182556j:plain

マシンは複数台が当たり前だ。ラックにしよ時代

もはやサーバというものは積み上げるもの。
ラックマウントにして監視ツールとかも準備。
NASストレージ等も導入されるようになった。

f:id:yast03:20181203182614j:plain

オフィスじゃ管理しきれん。データセンター構えよう時代

時期的には上記ラックと同じ頃ですね。
膨大なマシンを運用する電力、セキュリティなど
諸々考えることを減らすためにデータセンターを借りて
物理的なケアはプロに任せることに。

f:id:yast03:20181203182634j:plain

なんかもうクラウドでええわ。サーバはVMに。IaaS時代

マシンを自分で抱える時代は終わった。
VMを上手に制御してくれるプロバイダに委任するように。

f:id:yast03:20181203182638j:plain

もはやサーバはコンテナに。スケーリングも自在。コンテナオーケストレーション時代

Cloud Native!
Immutable Infrastructure !
Immutable Deployments !

サーバを作ったり壊したりはもはやプロセスレベルに。サーバごとデプロイできたりするようになってきた。
(この辺まで到達している現場はリテラシーが高いと感じます)

f:id:yast03:20181203182643p:plain

(そろそろ)もうアプリのことに集中すればよろしやん。サーバレス時代

アプリの要件に応じてコードレベルで調整できる時代に(なりつつある?)
サーバーは必要なものが自動選択されるように。
(業界のえらい人はこの辺をかなり意識しているようです)

f:id:yast03:20181203182647j:plain

以上

時代の進歩は本当にすごいですね。
リソースの概念が複雑化していくこの先、よりシステムをオブジェクティブに捉えるスキルと
ビジネスで開発をするなら、コストの把握方法などをしっかり学んでいく必要があると感じました。
がんばろう。

マイクロサービスアーキテクチャ 用語メモ - 2

前回に引き続き、用語集です。
なかなか難しい単語も多かったです。

継続的インテグレーション

通称CI。ソフトウェア開発における習慣の名前。
エクストリーム・プログラミングのプラクティスの一つ。

プロジェクトメンバーがそれぞれ開発した結果を頻繁に結合して
定期的にビルドやテストを行うこと。
それによって、問題に早期に気づくことができる。
散々それぞれ作り尽くして、いざ結合してみたらいろんなバグや調整が必要になった
なんてことが起こらなくなる。

頻繁にビルドやテストを行うことになるので
できるだけ処理を自動化しようとしたくなる。そこで出てくるのがCIツール。
JenkinsとかCircleCIとか。

マイクロサービスだと相互の影響確認とかをする上で
全体でCIしないと大変そうだな、という印象で、なるほどーと思いました。

コンシューマ

消費者、需要者、購入者、などの意味を持つ英単語
業務用・企業向けと対比して消費者向け みたいな使い方をされる

マイクロサービスにおいて使う側と使われる側とかでよく出てくる気がします。

ビジネスロジック

業務ソフトウェアの中で、具体的な業務で扱う様々なエンティティ(実体)
たとえば担当者、商品、顧客、在庫などを表現し、
また、それらの関係や処理の方法、業務の流れなどをプログラムコードとして実装した部分。
アプリケーション固有の処理やルールを記述したもの。
フレームワークなどの記述以外はほぼ全部ビジネスロジックか?

サブスクライバ

通知メッセージを受け取るユーザ(この場合はサービスか)

パブリッシュ/サブスクライブ

イベントを発行する -> パブリッシュ
それを購読する -> サブスクライブ
みたいな捉え方で外れないんじゃないかと思ってます。

リモートプロシージャコール

RPC。
ローカルで呼び出しをして別のリモートサーバで実行をする。
ローカルで関数呼び出ししていると見せかけて実は実行しているサーバはリモートであるとか。

実装上は意識しないで済むけど、どこでやっているかは隠蔽されるので
扱いかたを間違えると地獄という話が腑に落ちました。

ペイロード

伝送されるデータ全体のうち、伝送処理のための管理情報(ヘッダやメタデータなど)を除いたものにあたる

リクエスペイロード、という意味が超よくわかりました

整列化 マーシャリング

異なる2つのシステム間で、データを交換できるようにデータを操作する処理
違う言語同士の変換とか。
PHPの変数-> json に、みたいなイメージ?

非整列化 アンマーシャリング

上記の逆で、json -> PHPの変数に戻す みたいな話

ハイパーメディア

コンテンツに様々なデータ=テキスト/画像/動画/音声などのリンクが含まれる概念

ハイパーメディアコントロール、なるほどなるほどって感じ。

セマンティクス

「意味」「意味論」
非常に捉え方が難しい概念。
実装において「これが正しい」と判断するための基準。
シンタックスと対で利用されることが多い?
protected function を定義したとして
シンタックス的には誤字脱字はないから正しい、としても
そのメソッドがprotected ではなく public である / privateであるべき
といった判断がされる場合は、セマンティクス的にはダメ。みたいなイメージか。

セマンティックバージョニング

バージョンの付け方。
1.2.3というバージョンがあるとして
左から順番にメジャー、マイナー、パッチバージョンを表している。
APIの変更に互換性のない場合はメジャーバージョンを、
後方互換性があり機能性を追加した場合はマイナーバージョンを、
後方互換性を伴うバグ修正をした場合はパッチバージョンを上げる。


以上です。
マイクロサービスだと、やっぱりパブリッシュ / サブスクライブ
とか、セマンティクスとかの用語が体感に落ちていないと文脈がわかりづらいということがよくわかりました。
(多分、この概念がわかっていないとメッセージブローカーとか言われてもハァ!?ってなる)

だいたい読めるようになってきたので
次は続かないかもしれません。

マイクロサービスアーキテクチャ 用語メモ - 1

https://www.amazon.co.jp/dp/4873117607/ref=cm_sw_r_tw_dp_U_x_VhHRBb8BENJZCwww.amazon.co.jp


最近お勉強でこの本「マイクロサービスアーキテクチャ」を読んでいるのですが
横文字になかなかついていけなくて、evernoteにメモって一つ一つググりながら進めています。
今回はそのメモの一部を紹介します。

これらは、何言ってるのかサッパリわからないところから
ざっくりイメージを捉えるためにさっさとググってメモったものなので、間違っているかもしれません・・・

モノリス / モノリシック

一枚岩

コンウェイの法則

組織構造と設計は似てきちゃう。グダグダな組織構造だと設計もグダグダになりがち。
最適な設計を目指すなら組織構造も最適に。
(ここもっと理解したい)

チェックインをリリース候補として~

チェックイン = 要するにチェックアウトの逆、VCSに反映する内容。

コンシューマを変更することなく~

需要者 購入者 消費者 を変更することなく

レジリエンス

回復性 復元力 弾力性
不整合とかに強いってことか?

オンデマンドプロビジョニングシステム

リソースの調達を一括でやってくれるやつ AWS的な

サービスのセマンティクス

利用されている要素が正しく動作するかを判断する基準

フレーミング

枠づけ

メトリック

相手のところまでたどり着くのにどれだけ大変か (測量とかが語源)

エンドポイントのバージョニング

端末 拠点のバージョニング

サーキットブレイカ

良くない事象(つまり障害)が頻発すると「あ、これはやばいから一旦この導線をオフにしていこう」
という仕組み?

コードを介したガバナンス

コードを介した統治 / 支配

サイドカーサービス

親サービスと同じライフサイクルを共有し、親サービスと共に作成され、終了するサービス
アプリケーションのコンポーネントを別のプロセスまたはコンテナーにデプロイして
分離性とカプセル化を実現

デリバリチーム

作るチーム?

ドメインモデル貧血症

振る舞いとデータが分かれてしまっており、手続型の設計・実装になってしまう状態。
(ここもっと理解したい)

スタブ化

外部プログラムとの細かなインターフェース制御を引き受けるプログラム。



次回、続くかもしれません。

Macの.DS_Store、リソースフォーク(._で始まるファイル)を削除するオリジナルシェルコマンド

Macで共有フォルダをマウントして開発をしていると

.DS_Store
._ ファイル

が邪魔だなーと思うことがよくあります。

DS_Storeに関しては、あらかじめ作成しない方法があります。
下記でまとめてくださっている方がいらっしゃるので紹介
.DS_Storeの仕組みと削除&作成しないよう設定する方法 | UX MILK


でも、リソースフォーク(._ファイル)はFinderでマウントしているとどうしても作られてしまうことがあって、邪魔です。

そこで「rmrcforks (rm resource forksの略)」というコマンド一発で
current directory以下に存在する._ ファイルと .DS_Storeファイルを削除できるようにしてみました。

設定

# .bash_profile でパスが通っていること
PATH=$PATH:$HOME/bin
#!/bin/bash
# ~/bin/rmrcforks (755で作成)

OPT=${1:-0}

if [ ${OPT} = "-t" ] ; then
    find ./ \( -name ".DS_Store" -or -name "._*" \)
else
    find ./ \( -name ".DS_Store" -or -name "._*" \) -print -exec rm {} ";"
fi

実行

# 実際に消す
$ rmrcforks
# リストアップだけして消さない
$ rmrcforks -t

MySQL集計関数の小技(GROUP_CONCAT)

MySQLには「GROUP_CONCAT」という関数があります。
これには、複数のレコードを1行の文字列にカンマ区切りでつなげる能力があって
調査とか分析などでもたまに利用します。

どういう関数なのかは下記などよくまとめてくださっている方の記事をご参考に・・・
https://qiita.com/kyuu1999/items/93b02128f07c577b3e48

小技の紹介

この結果セットがカンマ区切りで文字として取れるのは周知のことと思いますが
実はこの関数、このまとまった文字列に対してソートとか重複排除ができたりします。

# 結果セットをidを基準に昇順で並べる
GROUP_CONCAT(bind_value ORDER BY id ASC)

# 結果セットを重複排除してユニークにする
GROUP_CONCAT(DISTINCT bind_value)

# 結果セットをユニークにしてidを基準に降順にする
GROUP_CONCAT(DISTINCT bind_value ORDER BY id DESC)

ちなみに、DISTINCTはCOUNTなどでも利用できます。

便利ですね!

Laravel5.3 以降 バージョンアップ MIddreware ConvertEmptyStringsToNullと日付系Validation

表題の件です。

アップグレードガイド 5.3 Laravel

配列、論理型、整数、数値、文字列をバリデートする場合、新しいnullableルールを指定していない限り、有効な数値として判断されなくなりました。

結論から言うと
Laravel5.3以降はバリデーションルールのrequireの逆は指定なしではなくnullable
という話です。

Laravel5.5のデフォルトのグローバルミドルウェア構成

下記のとおりです。

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
    ];
...

ConvertEmptyStringsToNull とは何をしているミドルウェアなのか

読んだところ、リクエストパラメータが文字列かつカラ文字だったらnullに変換する処理ということがわかります。

<?php

namespace Illuminate\Foundation\Http\Middleware;

class ConvertEmptyStringsToNull extends TransformsRequest
{
    /**
     * Transform the given value.
     *
     * @param  string  $key
     * @param  mixed  $value
     * @return mixed
     */
    protected function transform($key, $value)
    {
        return is_string($value) && $value === '' ? null : $value;
    }
}

Validationにおいて何が起こるのか

現在確認できている限り、日付系のバリデーションルールにおいて
常にfalseを返す動きを取るようになってしまうことが分かっています。

他のルールでも影響があるかもしれませんが全ては追っていません。

日付の範囲をフォームから受け取る、こんなバリデーションルールがあったとします。

<?php
$rules = [
    'date-from' => 'required|date',
    'date-to'   => 'date|after:date-from'
];

本来であれば

  • date_fromというパラメータは必須で、日付形式である必要がある。
  • date_toというパラメータは省略可能であるが、入力がある場合は日付形式かつdate_from以降である必要がある。

という動きをするはずです。

ですが、このConvertEmptyStringsToNullというミドルウェアが挟まると
afterのルールが入力の有無に関わらず常にfalseとなってしまいます!

なぜか

このConvertEmptyStringToNullミドルウェアを通ると

?date-from=2018-06-20&date-to=

というパラメータが

<?php
[
    'date-from' => '2018-06-20',
    'date-to'   => ''
];

ではなく

<?php
[
    'date-from' => '2018-06-20',
    'date-to'   => NULL
];

に変換されます。

すると、日付形式のバリデーション「compareDates」を利用するルールにおいては
値にNullが入っていると常にfalseを返すようになってしまいます。

<?php
# Illuminate/Validation/Concerns/ValidatesAttributes
...
    /**
     * Compare a given date against another using an operator.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @param  array  $parameters
     * @param  string  $operator
     * @return bool
     */
    protected function compareDates($attribute, $value, $parameters, $operator)
    {
        if (! is_string($value) && ! is_numeric($value) && ! $value instanceof DateTimeInterface) {
            return false;
        }

        if ($format = $this->getDateFormat($attribute)) {
            return $this->checkDateTimeOrder(
                $format, $value, $this->getValue($parameters[0]) ?: $parameters[0], $operator
            );
        }

        if (! $date = $this->getDateTimestamp($parameters[0])) {
            $date = $this->getDateTimestamp($this->getValue($parameters[0]));
        }

        return $this->compare($this->getDateTimestamp($value), $date, $operator);
    }

Laravel5.3以降は、このミドルウェアによって入力のないパラメータはNullとなり
DB更新と親和性が高まります。
パラメータの存在有無は、従来通りhasで確認することになるでしょう。
その代わり、requiredの逆にnullableを指定しないと、一部のルールが常にfalseを返すようになります。

Laravel5.2以前の動きに戻したい場合、このミドルウェアコメントアウトすれば元の挙動に戻ります。

バージョンアップ中に当現象に遭遇し、ソースを読んで変な時間を取ることになったので
似たケースで困る方の助けになれば幸いです。

csvファイルの処理

csvファイルの処理はシステム開発ではポピュラーなものですが
Microsoft Excelなどで吐き出されるような

・セル内改行を含んでいたり
・ダブルクォートで値が囲まれていたり

といったデータの処理については、初見ではどうすればいいのか悩むものでした。

私はいつも使う関数を持っているのですが、これが随分と信頼性があり
業務システムにおいてこの関数を介して処理したデータでトラブルが起きたことがありません。

fgetcsv, splfileobject, explodeによるお手製処理など
選択肢はいろいろありますが、どれも何らかトラブルが起きることが多い中で
この関数は安定感があるようです。

貼っておきます。

<?php
/**
 * CSVファイルの1行を配列で取得
 * 複数行対応
 *
 * @param object $fh fopen()などで得られたファイルハンドラ
 * @param string $file_encode 処理するファイルの文字コード
 */
function ml_fgetcsv(&$fh, $file_encode = 'UTF-8')
{
    $values = array();

    if (feof($fh)) {
        return false;
    }

    while ($values === array()) {
        $csv = '';

        while (!feof($fh)) {
            $csv .= fgets($fh);

            if (((preg_match_all('/"/', $csv, $matches))%2) == 0) {
                break;
            }
        }

        if (mb_strlen(trim($csv)) === 0) {
            continue;
        }

        $temp = preg_replace('/(?:\x0D\x0A|[\x0D\x0A])?$/', ',', trim($csv), 1);

        preg_match_all('/("[^"]*(?:""[^"]*)*"|[^,]*),/', $temp, $matches);

        for ($i = 0 ; $i < count($matches[1]); $i ++) {
            if (preg_match('/^"(.*)"$/s', $matches[1][$i], $m)){
                $matches[1][$i] = preg_replace('/""/', '"', $m[1]);
            }

            if (strtoupper($file_encode) !== 'UTF-8') {
                $values[] = mb_convert_encoding($matches[1][$i], 'UTF-8', $file_encode);
            } else {
                $values[] = $matches[1][$i];
            }
        }
    }

    return $values;
}