OmniAICreator/Qiita-1.07M huggingface dataset:
Docker for AWSの永続ストレージプラグインCloudstorでEFSをマウント


Docker for AWS(以下D4a)はGAの際、永続化ストレージをサポートするCloudstorというプラグインを添付するようになりました。

https://docs.docker.com/docker-for-aws/persistent-data-volumes/

D4aの`17.06.0-ce`でどのように使うのか試して見たので使用感など。

## Cloudstorがサポートするストレージのタイプ

- EBSにデバイスを作成してマウント
  - k8sでのPersistent Volumesのような感じ。
  - 複数コンテナからの同時利用ははなし。
- EFSで任意のディレクトリをマウント
  - 対象のEFSはテンプレートパラメータで自動作成。
  - dockerdが環境変数`EFS_ID_REGULAR`, `EFS_ID_MAXIO`で対象を決めるので、一応既存のEFSも使えなくはない。
  - 複数コンテナからの同時利用OK。


## つかってみる


ドキュメントではswarmモード(service)での使い方しか出ていませんが、単発の`docker run`でも普通に機能します。

次のようにvolume-driverに`cloudstor:aws`を指定、デフォルトはEFS。

```bash
$ docker run -it --rm \
  --volume-driver=cloudstor:aws \
  -v sharedvol1:/shareddata \
  alpine:latest
```

これで、EFSに`sharedvol1`ディレクトリが作成され、Dockerコンテナからは`/shareddata`で参照できます。

EFS側に親ディレクトリがない場合もソース指定可能です。

```bash
$ docker run -it --rm \
  --volume-driver=cloudstor:aws \
  -v sharedvol5/1/2/3:/shareddata \
  alpine:latest touch /shareddata/touchfile
```

この場合、Dockerコンテナ起動時にEFSに対して`sharedvol5/1/2/3`というディレクトリを作ります。

なおマウント時にサブディレクトリを指定した場合も、VOLUME名はトップのディレクトリが使われます。

```bash
$ docker volume ls
DRIVER              VOLUME NAME
cloudstor:aws       sharedvol1
cloudstor:aws       sharedvol2
cloudstor:aws       sharedvol5
local               sshkey
```

挙動はこんな感じです。基本的には`docker service/volume`で作成し、全ノードで参照できるようにしておくとよいでしょう。

### このとき、EFSの中身

他のEC2インスタンスから`/mnt/efs`にEFSをマウントして、中の様子を見てみます。
VOLUME名のディレクトリが作られているだけでした。シンプルですね。

```bash
$ find /mnt/efs
/mnt/efs
/mnt/efs/sharedvol2
/mnt/efs/sharedvol1
/mnt/efs/sharedvol1/testfile
/mnt/efs/sharedvol5
/mnt/efs/sharedvol5/1
/mnt/efs/sharedvol5/1/2
/mnt/efs/sharedvol5/1/2/3/touchfile
/mnt/efs/fs-ef07xxxx:sharedvol1
```

一番下にあるやつは、EFSのID指定できないかなと考えたけど、とりあえずやってみて失敗に終わったディレクトリです。
Cloudstorはソースが非公開なので、`-v fs-ef07xxxx:sharedvol1:/shareddata`などと実行したらこんなことに。



### docker volume rm で、データも消える

volumeの詳細をチェック。

```bash
$ docker volume inspect sharedvol5
[
    {
        "Driver": "cloudstor:aws",
        "Labels": null,
        "Mountpoint": "/mnt/efs/reg/sharedvol5",
        "Name": "sharedvol5",
        "Options": {},
        "Scope": "local"
    }
]
```

いたって普通です。EFSレギュラー/MAXIO用の親ディレクトリを作り、そこにマウントと。
D4aではちょっとわからないけども、ホストから見るとEFSをマウントしっぱなのでしょうね。

最後に消したらどうなるのかと、`docker volume rm sharedvol5`してみました。
まずdocker管理下のvolumeは消えます、そしてEFS側も綺麗さっぱりです。


```bash
# 他インスタンスからEFSの中身を参照
$ find /mnt/efs
/mnt/efs
/mnt/efs/sharedvol2
/mnt/efs/sharedvol1
/mnt/efs/sharedvol1/testfile
/mnt/efs/fs-ef07xxxx:sharedvol1
```

ここ連動して消えちゃうのか。。Docker視点ならそれでよいけど、コンフィグの共有とかでマウントするとかのNFSな視点だと違和感はあります。
マウントが外れるだけという選択肢があってもよいとおもって、[フォーラムに投稿](https://forums.docker.com/t/how-to-keep-files-on-efs-after-volume-rm/35234)しておきました。

## 終わりに

Docker Swarmが安定していればこの作りでも維持していけそうな気はしますが、なんだかんだでまだ不安定になったりします。
なにか手当をして回復するよりたいてい作り直した方が早いのですが、そういう運用ではvolumeのエントリーが巻き込まれてデータを消しそうな予感がしているため、(自分の用途では)フィードバックしつつもう少し様子見かなあ。


## EBSも少しだけ触って見る

volume-optに`backing=relocatable`を渡すと単体コンテナ用のEBSボリュームを作れます。

```bash
$ docker volume create -d cloudstor:aws \
  --opt backing=relocatable \
  --opt size=8 ebs1

=> ebs1
```

コマンド実行すると、ちゃんとEBSができていました。最初の紹介の通り、k8sのpvみたい。
こちらも`docker volume rm ebs1`などとすると、EBSごと消えます。
[DeepLearning] YOLOをiPad Pro 10.5で動かしてみた

YOLOを動かすコードがgithubにあがっていたので、自分のiPadでも動かしてみました。

ハマったこととしてXcodeの実行ボタンを押してもiPadがBusyって返ってきました。
⇒ iPadを２，3回リブートすることで解決

# コード
https://github.com/yuu/YOLO-CoreML-MPSNNGraph/tree/work です。
workブランチに今回動かすにあたっての修正を加えています。

# 環境
- iPad Pro 10.5
- iOS11.0 (15A5318g)
- Xcode 9 beta2 (9M137d)

# 動かす
`TinyYOLO-CoreML.xcodeproj` を開く
ターゲットをUSB接続しているiPadに変更
実行!

FPSは、24くらいでます。
iPad mini 2は、0.9当たりでした。
VisualStudio(Xamarin) iOSネイティブSDKをBindingで使用する時にDebugモード実行中に例外が起きる

VisualStudioを使ってiOSアプリを開発するときgoodAdの広告を表示させるためにネイティブSDKをBindingを使用して表示させる必要が出てきた。

他の方でもよく書かれているが
1.Binding用のプロジェクトを作成
2.Objective Sharpieを使用してhファイルからラッパークラス(実際はインターフェース)
を作成
3.iOSプロジェクトでBinding用プロジェクトを参照
4.iOSプロジェクトでBindingしたクラスを使用する

ざっくりとして、このような流れになった。

しかしここからが問題が起きた
goodAd広告特有なのかもしれないが下記の例外が起きる。
![スクリーンショット 2017-07-20 14.10.17.png](https://qiita-image-store.s3.amazonaws.com/0/142882/5b9b24ae-0236-516a-fa11-52f33b063c11.png)
***
System.ExecutionEngineException

Attempting to JIT compile method 'testBinding.goodAdSDK:.ctor (testBinding.GoodAdDelegate)' while running in aot-only mode. See https://developer.xamarin.com/guides/ios/advanced_topics/limitations/ for more information.
***

JITやらAOTやらどうやらコンパイルした時におかしいらしい。
デバッグモードでコンパイルした影響が出てるのかと思いリリースモードで実機に転送したら
この例外が出なかった。
Binding用のプロジェクトを右クリックしてオプションを開いて、デバッグとリリースでの違いを比べてみた。

ツリービューで[ビルド]-[コンパイラ]をクリック
構成をDebugとReleaseで変えてみて比較したらもっとも怪しかったのが<<最適化を有効にする>>のチェックボックスにチェックが"ある"か"ない"かの違いだった。

結論としては、Binding用のプロジェクトでデバッグモード時のコインパイラの設定で最適化を有効にすることでgoodAd広告は例外を出さずに表示されることができる。
このやり方は、たまたまなので理由を聞かれてもコンパイラじゃないですか？ってなるのでコメントもらっても返事できないかもしれないですがお困りの方いれば参考にしてみてください。

***
Binding用のプロジェクトでDebug時のコンパイラを下記のように設定

![スクリーンショット 2017-07-20 13.31.06.png](https://qiita-image-store.s3.amazonaws.com/0/142882/c72360d6-f8e7-6497-22f4-95cc121aec73.png)

例外を回避したのを確認する
![スクリーンショット 2017-07-20 14.11.26.png](https://qiita-image-store.s3.amazonaws.com/0/142882/aa6e3864-a91f-13ad-488c-305666cd72f5.png)
***
Oracleでカラム情報を調べる(USER_TAB_COLUMNS)

Oracleで、MySQLで言うSHOW CREATE TABLEみたいなことがしたいとき、
`DESC (テーブル名) `

とかで調べるが、これは権限がないと使えなかったりする。
カラムの型さえわかればいいみたいなときは

`SELECT * FROM USER_TAB_COLUMNS 
WHERE TABLE_NAME LIKE (テーブル名)`

とやるとカラム1行が1件となるデータが取得できる。

[USER_TAB_COLUMNS](https://docs.oracle.com/cd/E49329_01/server.121/b71292/refrn26277.htm)
[テーブルの列名を調べる：user_tab_columns ](http://sakochin3.exblog.jp/2500325/)
そろそろガバガバな命名規則を整理する

# そろそろガバガバな命名規則を整理する

## はじめに  
* 命名が糞適当なのでいい加減整理する  
* リーダブルコード読んだよ
* jsとcssの想定　フロントエンド寄り

## 命名の時のアレ

* キャメルケース : 複合語をひと綴りとして、要素語の最初を大文字で書き表すこと
例) gorillaGorilla  
  
* パスカルケース : 複合語の先頭を、大文字で書き始めるキャメルケースのこと。
例）GorillaGorilla

* スネークケース : アンダースコアを区切記号として単語をつなげる。
例）gorilla_gorilla  

* チェインケース：ハイフンを区切記号として単語をつなげる。
例）gorilla-gorilla

引用 
wikipedia
https://ja.wikipedia.org/wiki/キャメルケース

## それぞれの使用ケース
* キャメルケース : 関数名、オブジェクトのキー名

* パスカルケース : クラス名

* スネークケース : cssのクラス名、変数名

* チェインケース : 状態を表す時のcssのクラス名 is-activeとか

## 命名のポイント
* 明確な単語
* 汎用的な名前を避ける（使う状況を選ぶ）
* 具体的な名前を使う
* 接尾辞・接頭辞を使って情報を追加

### 明確な単語
「get」や「size」という単語は抽象的、「どこから」・「なんの」といった明確な情報を命名に含ませる

### 汎用的な名前を避ける
* いい名前は変数の目的や値を表す
* 一時的な保管を目的とした変数などは例外
* ループイテレータ(iとかjとかkとか)にも明確な名前を追加しておくとバグの発見が楽  
例) club_i ci

### 具体的な名前を使う
その変数や関数がなんの情報を持つかや、どんな仕事をするのかを具体的に名前に入れる（明確な単語）

### 接尾辞・接頭辞を使って情報を追加
* 値を計測できるものには単位も入れるといい。 ms（ミリ秒）とか
* 変数の意味を間違えるとバグになりそうな部分は属性を名前に追加する

### まとめ
命名むずい。アメリカ語わかんない。
命名に関してはリーダブルコードの2章と3章読むのが一番いいんじゃないですかね。
Pythonで複数のリストをforで処理する

メモ
以下のようにzipを使うと良いみたい

```python:
a = [1, 3, 5, 7]
b = [2, 4, 6]

for (a_num, b_num) in zip(a, b):
	print(a_num, b_num)
	
# 結果は以下の通り
# 1 2
# 3 4
# 5 6
```

lengthが短い方の処理が終わったら、そこで処理終了になるっぽい
さくらのVPSのnginxにSiteGuard Liteをインストールする

さくらインターネットが提供するさくらのVPSでは，セキュリティ対策としてジェイピー・セキュア社のSiteGuard LiteがさくらのVPSユーザ向けに**追加費用なし**で提供されています。バージョン3.20からついにnginxにも対応したのでインストールしてみました。

インストールマニュアルにはnginxのソースコードからコンパイルしろとありましたが，すでにyumでnginxをインストールして運用中なのでコンパイルなしでSiteGuard Liteをインストールを試みました。
**マニュアル通りのインストール方法ではないので自己責任**

## 環境
すでに以下のソフトウェアがインストール済という前提です
Centos7.3
nginx 1.12.1


## マニュアルの入手
・JP Secure SiteGuard Liteご利用ガイド （ダウンロードリンクとライセンスについての説明が書かれている)
http://support.sakura.ad.jp/resource/pdf/vps_doc/public/SAKURA_SiteGuard_Lite_Guide.pdf 
・SiteGuard Lite Nginx版 管理者用ガイド (SiteGuard Liteのインストール方法について書かれている)
https://cloud-news.sakura.ad.jp/wp-content/uploads/2017/06/siteguardlite-320-0_nginx.pdf

インストールマニュアルをダウンロードします。閲覧パスワードはさくらVPSのコンソールページの上部にSiteGuard利用というボタンがあるのでクリックして取得します。
管理者用ガイドの閲覧パスワードはご利用ガイドに書かれています。
![115000012602-0619.png](https://qiita-image-store.s3.amazonaws.com/0/95987/a3edd7d3-ab1a-ffa9-d5d9-0504b2485808.png)

## インストールファイルの取得
ご利用ガイドに「インストールファイルの取得について」という項目があるので，それに従ってnginx版をwgetします。

## SiteGuard Liteのインストール
SiteGuard Liteをダウンロードしたら，管理者用マニュアルのインストールの項目に従ってSiteGuard Liteをインストールをします。
make installするだけです。

## nginx用SiteGuard Liteモジュールのコピー
マニュアル内ではnginxコンパイルする際にオプションをつけてインストールするように書かれていますが，すでにyumでインストールしてあるのとコンパイルしたくないのでモジュールをコピーする方法をとりました。
/opt/jp-secure/siteguardlite/nginx 内のファイルを/etc/nginx/modules にコピーするだけです。

```bash
cp /opt/jp-secure/siteguardlite/nginx/* /etc/nginx/modules/
```

## SiteGuard Liteのセットアップ
管理者ガイドに従ってSiteGuard Liteのセットアップをします。

## ライセンスの登録
SiteGuard Liteのインストール直後は評価ライセンスになっているので，ライセンス登録をします。ご利用ガイドにライセンス情報についてという項目があるので，SiteGuard Liteを利用するサービスに会わせたライセンスキーを入力してください。

これで，インストールは完了です。あとはSiteGuard Liteのログインパスワードを変更したり，必要に応じてカスタムシグネチャを設定して誤作動を起こさないようにするだけです。マニュアル通りのインストール方法ではありませんが，今のところ問題なく動作しています。
EOL（サポート期限切れ）後のCiscoルーター・スイッチのIOSイメージがどうしても欲しいとき

#はじめに
「プログラミング」に関していないので怒られそうですが、私以外にも同じく悩まれる方がいるはず（と思う：QAサイトで散見）ことと、Qiitaさんのタグ指定に "Cisco" があった（！）ので投稿させていただきます。
本記事の内容を実践されるときは、**セキュリティリスクに注意してください！**

#本題
わが社では、相当古いCiscoルーター・スイッチが現役で稼働しています（冗長構成にしてある＆保守未加入、なうえ、Cisco機さんたちはなかなか壊れないことから買換え稟議がままらなず）
Ciscoルーター・スイッチのOSは [IOS](http://www.cisco.com/c/ja_jp/products/ios-nx-os-software/index.html)（Appleさんのとは違うもの）で、[Download Software](https://software.cisco.com/download/navigator.html) ページからバイナリイメージファイルを入手できます。その後、[サポート期限](http://www.infraexpert.com/study/tea4.html) が切れると、やがて掲載されなくなります。

前任がIOSのアップデートを行っていなかったため、とある有用な機能が使えないままサポート期限が切れてしまっている状況です。

##解決例
- ダウンロードURL（ずばりでなくとも、そのURLへのリンク元でも可）を探しだし、WebArchive（[Internet Archive: Wayback Machine](https://archive.org/web/) など）へ訪問して探してみる - お目当て（バイナリファイル）は残念ながらありませんでしたが、ドキュメントはいくつか見つかりました
- 同じ機種をの中古品を購入、あるいはレンタルをして吸いだす - IOSのバージョンを事前に確認する必要があります。セキュリティリスク対策としては、別々の店から購入して、複数個のバイナリを比較してみると安心？（レンタルの場合、IOS の権利関係は不明...）
- ネットワーク関連に強いSIerの営業さんなどに頼み込んで、あったら頂く
- EOL後もサポートをしてもらえる保守業者さんと契約する

他にもありましたら、ぜひご教示くださいませ！

##よくない例
- 野良サイトで公開されているバイナリを頂いてくる - サイト運営者に悪意がなくても、第三者からクラックされてバイナリが置き換えられているかも！

#さいごに
ネットワーク関係はセキュリティ問題に直結していますので、くれぐれも自己責任で！！**ウイルス（トロイ・盗聴機能など）が仕込まれているかもしれません。**バイナリのCRCやMD5のハッシュ値をどこかで調べられると安心ですね。

保守に入ること、サポート期限前に新機種に買い替えることがなによりです。セキュリティリスクに理解のある経営者・管理職・経理部門のある会社で働きましょう！
.htaccessであれこれ。（リダイレクトとBasic認証）

PCとスマホでディレクトリを分けたり、
httpをhttpsにリダイレクトしたりする、あれ。
それから、よく使うBasic認証のまとめです。

#PCとスマホのディレクトリを分ける場合
例えば以下のようなディレクトリで分ける場合。
PC: http://example.com
スマホ: http://example.com/sp/

PC: http://example.com/test/
スマホ: http://example.com/sp/test/

```.htaccesss:.htaccesss
RewriteEngine On

RewriteBase /
RewriteCond %{REQUEST_URI} !/sp/
RewriteCond %{HTTP_USER_AGENT} (iPod|iPhone|iPad|Android|Windows\ Phone)
RewriteRule ^(.*)$ /sp/$1 [R]

RewriteBase /
RewriteCond %{REQUEST_URI} /sp/
RewriteCond %{HTTP_USER_AGENT} !(iPod|iPhone|iPad|Android|Windows\ Phone)
RewriteRule ^sp/(.*)$ $1 [R]
```

それぞれの意味とメモ。

```.htaccesss:
RewriteEngine On
#Apacheのモジュール「mod_write」を有効にする。

RewriteBase /
#リダイレクトのベースとなるディレクトリ。転送後URL。

RewriteCond %{REQUEST_URI} !/sp/
#条件。URLに/sp/が含まれていなかったら。

RewriteCond %{HTTP_USER_AGENT} (iPod|iPhone|iPad|Android|Windows\ Phone)
#条件。ユーザーエージェントの判別。

RewriteRule ^(.*)$ /sp/$1 [R]
#URLを書き換える指示。^行頭。$行末。.*全て。
#()でグループ化した箇所を$1で部分置換する。

RewriteBase /
#リダイレクトのベースとなるディレクトリ。転送後URL。

RewriteCond %{REQUEST_URI} /sp/
#条件。URLに/sp/が含まれていたら。

RewriteCond %{HTTP_USER_AGENT} !(iPod|iPhone|iPad|Android|Windows\ Phone)
#条件。ユーザーエージェントの判別。

RewriteRule ^sp/(.*)$ $1 [R]
#書き換え指示。()でグループ化した箇所を$1で部分置換する。
```

#httpをhttpsにリダイレクトする場合


```.htaccesss:.htaccess
RewriteEngine On

RewriteBase /
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R]
```

それぞれの意味とメモ。

```.htaccesss:
RewriteEngine On
#Apacheのモジュール「mod_write」を有効にする。

RewriteBase /
#リダイレクトのベースとなるディレクトリ。転送後URL。

RewriteCond %{HTTPS} off
#条件。ssl(https)が無効の場合。

RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R]
#書き換え指示。全てをhttpsにリダイレクト。
#%{HTTP_HOST} httpリクエスト先のサーバー名
#%{REQUEST_URI} ページアクセスのURL
```
その他の環境変数確認に便利なページ。
[ルフトツールズ（LUFTTOOLS） Web便利ツール集 - 環境変数チェッカー](http://www.luft.co.jp/cgi/env.php)

#全てのページをトップにリダイレクトする場合

```.htaccesss:.htaccess
RewriteEngine On

RewriteBase /
RewriteCond %{REQUEST_URI} !(^/$)
RewriteRule ^(.*)$ / [R=301,L]
```

それぞれの意味とメモ。

```.htaccesss:
RewriteEngine On
#Apacheのモジュール「mod_write」を有効にする。

RewriteBase /
#リダイレクトのベースとなるディレクトリ。転送後URL。

RewriteCond %{REQUEST_URI} !(^/$)
#条件。トップページ以外のURLの場合。

RewriteRule ^(.*)$ / [R=301,L]
#書き換え指示。全てをトップにリダイレクト。
#301は恒久的な転送の場合。一時的な場合は302。
#Lは一旦処理を終了。
```

Lについて、こちらの記事が参考になりました。
[.htaccess に RewriteRule 書くときは、[L]フラグをつけてもそこで終了しないかもよ？って話。](http://qiita.com/ezaki/items/87c2dff8f7753ef048d2)


#Basic認証を設定する場合

・まずは、以下で生成された結果を.htpasswdで保存する。
[ルフトツールズ（LUFTTOOLS） Web便利ツール集 - .htaccess による認証用　パスワード暗号化ツール](http://www.luft.co.jp/cgi/htpasswd.php)
[TECH-UNLIMiTED｜すぐに使える便利なWEBツール - .htpasswd生成](http://tech-unlimited.com/makehtpasswd.html)

```.htaccesss:.htaccess
RewriteEngine On
AuthUserfile サーバーディレクトリ.htpasswd
AuthGroupfile /dev/null
AuthName "Please enter your ID and password"
AuthType Basic
require valid-user

<Files ~ "^\.ht">
    Order allow,deny
    Deny from all
    Satisfy All
</Files>
```


サーバーディレクトリは、フルパスを指定すること。
分からない場合は、適当にphpを作成して
以下のコードでフルパスを確認する。

```php:php
<?php
echo __FILE__;
?>
```

.htaccessと.htpasswdをルートディレクトリに置く。

おわり。
SASS/Compassでimage-urlやimage-widthを使ってみたがローカルに配置の画像しか読み取れない

image-widthは内部で「File.exists?(image_file)」を行っているため、
ローカル環境にある画像でしか画像幅を取得できない。


[GitHub Compass/compass](https://github.com/Compass/compass/blob/stable/core/lib/compass/core/sass_extensions/functions/image_size.rb)

58行目付近

```Ruby
  def image_path_for_size(image_file)
    if File.exists?(image_file)
      return image_file 
    end
    real_path(image_file)
  end
```

ない場合にはreal_pathで

```Ruby
  def real_path(image_file)
    # Compute the real path to the image on the file stystem if the images_dir is set.
    if Compass.configuration.images_path
      File.join(Compass.configuration.images_path, image_file)
    else
      File.join(Compass.configuration.project_path, image_file)
    end
  end
```

としているが、images_pathもproject_pathもローカルのディレクトリを指すものであって、URLを指定できない。
http://compass-style.org/help/documentation/configuration-reference/


したがってローカル環境に画像をおいておかないと自動で幅を取得するimage-widthは使えない。

SASSのコンパイルのたびに画像落としてこなければというのは効率よくないと思うのだが、良い方法ないかな...

サーバ上に配置されている画像も参照できるようになれば嬉しいなと思った次第でした。。。



追記）
結局画像も別のレポジトリですがGitで管理するようにし、
SASS/Compassでビルド時に自動で最新版を取得するような処理をかますことで対応しました。
【Electron連載】第５回 基本編－ウィンドウを作って表示

[↑【Electron連載（目次）】Electronでアプリ完成までのメモ](http://qiita.com/nullpointer_t/items/f74edc1f59cac42946ba)

んじゃ、今回から基本編のスタートやで。
というわけで、まずはWindowを作って表示までな。

ここのAPIドキュメントを参考に進めていくで。
https://electron.atom.io/docs/api/browser-window/

ソースコードはJavaScriptじゃなくて、TypeScriptで書いてるから注意な。

# ウィンドウの作成

```js:index.ts
const {BrowserWindow} = require('electron');

var win: Electron.BrowserWindow = new BrowserWindow({width: 800, height: 600});

//ウィンドウ内にリモートのページを表示する場合
win.loadURL('https://github.com');

//ウィンドウ内にローカルファイルのページを表示する場合
win.loadURL('file://' + __dirname + '/index.html');

//ウィンドウを閉じるイベント処理
win.on('closed', () => {
    win = null;
});
```

## BrowserWindow作成時のオプション
ウィンドウを作成する際のオプションで良く使いそうなのをピックアップしたで。
ここから抜粋したから、載ってないのはよく見てや。
https://electron.atom.io/docs/api/browser-window/#new-browserwindowoptions

| オプション名       | 型               | 省略時の値          | 説明 |             
|:-----------------|:------------------|:-------------------|:----|
| width | integer | 800 | ウィンドウの幅      |
| height | integer | 600 | ウィンドウの高さ   |
| x | integer | 画面中央 | ウィンドウの横位置（yとセットで使用） |
| y  | integer | 画面中央 | ウィンドウの縦位置（xとセットで使用） |
| resizable  | boolean | true | ウィンドウサイズの変更可否 |
| movable | boolean | true | ウィンドウ移動可否 |
| minimizable | boolean | true | 最小化可否 |
| maximizable | boolean | true  | 最大化可否 |
| closable | boolean | true | 閉じる可否 |
| alwaysOnTop| boolean | false | 常に最前面に表示 |

## イベント
ウィンドウのイベントを記述する方法や。
基本的にはonメソッドでイベント名を記載するみたいやけど、一部例外もあるっぽいな。
詳しくはこっち見てな。
https://electron.atom.io/docs/api/browser-window/#instance-events

引数があるapp-commandイベントの例やで。

```js:index.ts
const {BrowserWindow} = require('electron');
var win : Electron.BrowserWindow = new BrowserWindow();
//アプリのコマンドイベント
win.on('app-command', (e, cmd) => {
  //ブラウザの戻るを検知して処理
  if (cmd === 'browser-backward' && win.webContents.canGoBack()) {
    win.webContents.goBack();
  }
})
```
## メソッド
ウィンドウを操作する際にはメソッドを実行やな。
この辺は他のプログラムと変われへんな。
こっち書いてあるから参考にしてな。
https://electron.atom.io/docs/api/browser-window/#instance-methods

# 子ウィンドウ
子ウィンドウの作成方法や。
って言ってもBrowserWindowのインスタンス作成時にオプション指定するだけやけどな。

```js:index.ts
var top : Electron.BrowserWindow = new BrowserWindow();
var child : Electron.BrowserWindow = new BrowserWindow({parent:top});
child.show();
top.show();
```

子ウィンドウをモーダルにする場合はオプションで追加や。

```js:index.ts
var child : Electron.BrowserWindow = new BrowserWindow({parent: top, modal: true});
```

# フレームなしウィンドウや透明なウィンドウなど
ウィンドウの見た目を変えたり、ちょっと特殊なウィンドウを作る際はこっちを参照な。
https://electron.atom.io/docs/api/frameless-window/

とりあえず書かれている内容をかいつまんで書くと・・・

* フレームなしウィンドウはインスタンス作成時にframeオプションをfalseにする。
* メニューが表示されたままやから、titleBarStyleオプションをhiddenにしておけ。
* 透明なウィンドウはtransparentオプションをtrueにするとよい。
* マウスクリックを無効にする場合はwin.setIgnoreMouseEvents(true)を実行。
* ウィンドウを移動できるようにするにはbodyタグのstyleに"-webkit-app-region: drag"をつけておく。

てなところかな。
まちがってたらすんまへん。

＞　第６回　基本編－ダイアログ表示
透明感のあるVim環境をめざして

# はじめに
僕は透明感のある作業環境が好きです。
GVimでもiTermでもCygwinでも、なんでもすぐ背景を半透明化したがります。
なんとなく格好いいし、後ろにブラウザや資料を展開していると、いちいちウィンドウを切り替えることなく透かして見ることができて便利です。
しかしVimのカラースキームに背景色が設定されていると、ウィンドウの透過設定が無視され、せっかくの透明感が台無しになってしまいます。
そこで、`.vimrc`に設定を書き加え、カラースキームの良さはそのままに、背景だけを透明化します。


# .vimrc

`.vimrc`でカラースキームを設定している箇所

```vim:.vimrc
colorscheme XXXXX
```

よりも後の行に、以下を追記します。

```vim:.vimrc
highlight Normal ctermbg=NONE guibg=NONE
highlight NonText ctermbg=NONE guibg=NONE
highlight SpecialKey ctermbg=NONE guibg=NONE
highlight EndOfBuffer ctermbg=NONE guibg=NONE
```
非テキストの`ctermbg`と`guibg`に`NONE`を設定しています。


以下、それぞれのハイライトグループの意味です。

|グループ|意味|
|-------------|---|
|Normal|通常のテキスト部分|
|NonText|eol, extends, precedes|
|SpecialKey|nbsp, tab, trail|
|EndOfBuffer|バッファ外の行(行頭に`~`が表示されている行)|

※ `EndOfBuffer`のみ、Vim 7.4.2213 以降のバージョンで使用可能

これで、Vimに透明感を与えることができました。


# GUIパーツの非表示化

GVimやMacVimでは、ウィンドウ内にツールバーやスクロールバー等が表示されます。
これらのパーツはどうやっても半透明化できないので、非表示化してしまいましょう。
そもそもよく訓練されたVimmerであれば、マウスを使って操作するインターフェースなど必要ありません。

```vim:.vimrc
if has('gui')
  set guioptions-=T
  set guioptions-=m
  set guioptions-=r
  set guioptions-=R
  set guioptions-=l
  set guioptions-=L
  set guioptions-=b
endif
```

`has('gui')`で、GUIモードで動作しているかどうかを判別できます。
`guioptions-=X`では、GUI用のどのパーツを非表示化するかを指定しています。
オプションの意味については、[このあたり](http://vimwiki.net/?'guioptions')を参照ください。

GUI固有の設定なので、`.gvimrc`に書いても良いです。
その場合は`has('gui')`は不要です。

```vim:.gvimrc
set guioptions-=T
set guioptions-=m
set guioptions-=r
set guioptions-=R
set guioptions-=l
set guioptions-=L
set guioptions-=b
```

以上の設定で、テキストとタイトルバー以外の全てが透過された、とても美しい環境ができあがります。
Ruby on Railsでカート機能を実装するヒント。

昼飯食う前にメモです。

# どうやってカート機能を作ればいいか。
自分で考えたのはユーザー１人に対してカートは1つしか無いわけだから、以下のようなテーブルを作れば行けるのかなと考えていました。

###carts table
|id|item_id|number|user_id|
|:--:|:--:|:--:|:--:|
|1|1|3|4|

しかしこれだと、ログインしていなければカートに入れることができないというデメリットがあります。
そんな仕様だときっと売上にも影響しますね！

# そこで参考にしたサイト。
こちらのロジックでやってみることにしました。
http://qiita.com/DialBird/items/89ce3a0f25c9dfdb38c1

カートと商品の間に中間テーブルを設けることで解決です。
テーブル名は**carts - shoppings - items**という感じにしてみました。

# セッションについて。
上記のやり方だとセッションが必要になります。
標準のCookieStoreだとセキュリティに脆弱性がある為、activerecord-sessionというgemを使うと良いみたいです。

セッションについて参考にしたサイト。
http://shindolog.hatenablog.com/entry/2014/11/02/164118
WebSocketを利用してサーバからクライアントにイベントを通知するライブラリを作ってみた


とある案件で
・サーバからクライアントに対してイベントを通知して処理したい
・でも既存のシステムはそのまま使いたい
といった要望があったので、そのときの対応メモ

以前FCMを利用してServiceWorkerでやりとりしてもと思ったが、
対応ブラウザやPush配信の仕組みなど少し手間がかかりそうだったので、
Node.js/Socket.ioの組み合わせで通知の仕組みだけを構築してみることに...

FCMについての記事は下記をご参考ください。
[Firebase Cloud Messaging(FCM)を利用したWebPush通知の実装について](http://qiita.com/megadreams14/items/2f4221c3cc2a39663d7d)

# システム概要

イメージとしては添付画像のような形で、WebSocketにて通信する部分は別サーバで実施。
単純に通知のやりとりを行うハブとして独立させ、
クライアント側はイベントを受信したときに既存のシステムに対してイベントを通知して処理を行う。

![WebSocketNotification概要図.png](https://qiita-image-store.s3.amazonaws.com/0/17346/b60451d9-3d52-175b-fa07-c8c5077b1c9b.png)

こうすることで、既存のシステムを大きく改修しなくても
サーバからクライアントに対して任意のタイミングで任意の処理が出来るようになり、
イベントの購読という形でシンプルに実装可能なのかなと思い着手。


主にWebSocketで接続しているユーザの管理を既存のアプリケーションシステムで管理しているユーザで通知処理が行えるように（ユーザIDやグループの管理など）ライブラリ内で実装しています。

実際に構築したものは下記を参考に...
作りの部分とか改善可能なところが多いですが、一旦動くようにと...
https://github.com/brightvie/webSocketNotification

# 実装例

## サーバ

ExpressとSocket.ioのNode.jsサーバは用意する必要がありますが、
簡単に上記を実現するために組み込むことが出来ます。

```server.js
// ユーザ管理の仕組みの初期化も含めてここで実施
var WebSocketNotification = require('websocket-notification');
var webSocketNotification = new WebSocketNotification();

io.on('connection', function(socket){

  // WebSocket Notification Libraryで標準で用意されているメソッドを付与する
  // - ユーザ管理: login, disconnect イベントの設定
  // - 通知: POST /notification APIの実装
  webSocketNotification.setMethod(io, socket, app);
});
```

## クライアント

Webのクライアント側はBowerでライブラリを読み込み後
下記のように初期化するだけで機能実装は可能

```index.html
<script src="/components/socket.io-client/dist/socket.io.js"></script>
 <script src="/components/webSocketNotification/src/client/client.js "></script>
 <script>
   var obj = {
     systemName: '所属するシステムID' // 任意、指定がない場合はサーバ側でdefalutsが指定される
     userId: 'ユーザのIDを指定',     // 任意、指定がない場合はサーバ側でsocket.idが指定される
     groups: ['所属するグループIDなどを指定'] // 任意、指定がない場合は空配列となる
   };

   // WebSocket Clientの初期化
   var client = new WebSocketNotificationClient();
   // 任意のパラメータをセットする
   client.setOptions(obj);
   // WebSocketのコネクションを貼る
   var webSocket = client.getInstance(io);
   
   // getInstanceで返ってくる値は、socket.io-clientのインスタンスであるため、
   // 通常のsocket.on('イベント名', function(){}); といった書き方が可能である

   // サーバから通知を受け取ったときの処理を定義する。（デフォルトは、console.logでレスポンスが表示されるのみ）
   webSocket.on('notification', function(res) {
     // TODO: ここに処理を記載
     // - Angularなどのフレームワークに対してイベントを発火し、任意の処理を行うのもよし
     // - ここにべた書きで実施したい処理を行うのもよし
   });
 </script>
```

## 任意のイベントの発火

ここまで来るとあとは、既存のアプリケーションシステムからWebSocket経由で通知を送る部分です。
サーバ内部でのHTTPリクエストをイメージしており下記のようなリクエストを投げることで通知が行く仕組みです。

```
curl -X POST http://localhost:3000/notification -d '{"sentType": "user", "target": ["10001"], "message": {"statusCode": 200, "action": "alert"}}'
```

# まとめ

とりあえずWebSocket経由で既存システムを活かしたまま簡単にサーバからクライアントにイベントを送り、処理を行うことは出来たのですが、nodejsのプロセスが再起動するとユーザ情報が消えたり、インフラ側の整備がまだまだだったり、本番運用が想定された作りではないので今後改修をいれつつ、まずは小さい機能から組み込んで使い勝手を見ていければと思います。
yumのproxy設定とproxyを無効にする方法

一体何度proxyで困れば気が済むんだろう…

proxy経由でyumを使いたい場合は以下のようにyum.confにproxyの設定を行います。

```/etc/yum.conf
proxy=http://yourproxy:proxyPort
```

しかし、独自yumリポジトリなどがproxyを通したくない同一ネットワーク内に建っていて、そのリポジトリも見に行くようになっている場合、以下のようにproxyを経由して辿り着こうとするため503になってしまいます。

```
http://privateServer/yum/hoge/repodata/repomd.xml: [Errno 14] HTTP Error 503 - Service Unavailable
```

`no_proxy`が使えれば良いのですが、設定が無さそうなので、独自yumリポジトリの設定を修正します。
以下の通り、`/etc/yum.repos.d/xxx.repo`に`proxy=_none_`を追加します。

```
[hoge]
# こいつ↓
proxy=_none_
priority=1
name=hoge
baseurl=http://privateServer/yum/hoge
enabled=1
gpgcheck=0
```

`proxy=_none_`を設定することで、このリポジトリに対してproxyを通さずアクセスするようになります。

* [How to specify that Yum should not use proxy for specific domain \- Super User](https://superuser.com/questions/393099/how-to-specify-that-yum-should-not-use-proxy-for-specific-domain)
Google VR Viewで360度画像をhtmlに埋め込んでみた

この記事をメインにひとまずweb上で動かす。落ち着いたらiOSとかもやってみたい。
[Google VR Viewで360度コンテンツを表示する方法 - Qiita](http://qiita.com/kfunnytokyo/items/aabb3373ec79ef668f6e)
公式ドキュメント
[VR View for the Web  |  Google VR  |  Google Developers](https://developers.google.com/vr/concepts/vrview-web)

#vrview.jsの読み込み
jsファイルをビルドする。ビルドしたくなければ、飛ばして問題なし。

```
git clone https://github.com/googlevr/vrview.git
npm update -g npm
npm update -g
npm install
npm run build
npm run build-api 
npm run build-min
npm run build-dev
npm run build-api-min
npm run build-api-dev
```

buildフォルダ以下に色々できてる。


#画像の読み込み
読み込む元の画像を確認
https://2.bp.blogspot.com/-kvvsZ0F0kNE/V-PagLs7Y8I/AAAAAAABqfE/YTKbIkgE1n8yvsGI3Y2WDc3YXG-Ka5hiACKgB/s1600/nikon_keymission_360_still_image.jpg
https://cdn.glitch.com/fbc36a5e-0d0e-45d3-b6cb-0c1f01f6cd8e%2FMuttukadu%20Boat%20House(2K).jpg?149128341000

###注意
- サイズに気をつける
ちょっとでかいけど、サイズが決まってるみたい。ちっちゃいサイズのものを読み込んでもエラーで読み込めなかった。
- サーバー上に画像を配置すること
クロスドメイン不可


自分はflaskで動かしたけども
index.htmlファイルを直叩きでも動いた。

```html:index.html
<!DOCTYPE html>
<html>
<head>
    <title>Flask Demo</title>
</head>
<body>

    <div id="vrview"></div>

    <script>
      window.addEventListener('load', onVrViewLoad);
        function onVrViewLoad() {
        var vrView = new VRView.Player('#vrview', {
          image: 'https://2.bp.blogspot.com/-kvvsZ0F0kNE/V-PagLs7Y8I/AAAAAAABqfE/YTKbIkgE1n8yvsGI3Y2WDc3YXG-Ka5hiACKgB/s1600/nikon_keymission_360_still_image.jpg',
        });
      }
    </script>

    <script src="https://storage.googleapis.com/vrview/2.0/build/vrview.min.js"></script>
    <!-- <script type="text/javascript" src="static/js/vrview.js"></script>  -->
</body>    
</html>
```

マウスでクルクルできました。手軽過ぎました。

<img width="417" alt="スクリーンショット 2017-07-20 11.48.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/64334/27068706-b123-42ae-6b2f-c0248f46e6db.png">



#参考
https://github.com/googlevr/vrview
[Google VRの「VR View」で360度対応の画像や動画をWebサイトに埋め込んで表示させる | maesblog](https://mae.chab.in/archives/59789)
https://storage.googleapis.com/cardboard-camera-converter/index.html
Ruby基本文法引っかかりポイント


# mapとeachの違い
`map`はブロックを評価した結果を配列に格納して返してくれる。ブロックがない場合は`nil`が返る。
それに対し、`each`の戻り値はレシーバ自身であり、ブロックの処理を実行するのみ。

```rb:map
 [1,2,3].map {|n| n * 5 }
# => [5, 10, 15]
```

```rb:each
 [1,2,3].each {|n| n * 5 }
# => [1, 2, 3]
```

# ブロック内外で変数を参照できるかどうか
## for,while,until
構文の中に定義される変数は構文の外側でも参照可能

```rb:for
for i in 1..5 do
  sum = 0
  sum += i
end

puts sum

# => 5
```

## times,each,loop
ブロック内に定義した変数はブロックの外側で参照できない
ブロックを引数として受け取れる.

```rb:times
5.times do 
    sum = 0
    sum += 1
end

puts sum
# => Main.rb:6:in `<main>': undefined local variable or method `sum' for main:Object (NameError)

```



# 配列同士の計算
引き算 -> 重複した要素はすべて取り除かれる
足し算 -> 末尾に追加される

```rb:
[1,2,3,4]-[1,2]
# => [3, 4]

[1,2,3,4]+[1,2]
# => [1, 2, 3, 4, 1, 2]
```

# マッチング `=~`
正規表現オブジェクトと文字列オブジェクトのマッチを行う
`=~`演算子によるマッチングは、最初のマッチした文字の位置を返します。

```rb:
str = "Enjoy Programming!"
num = str =~ /P/

puts("Pは#{num}文字目にある")
# => Pは6文字目にある
```
check_version_conflict: can't activate xxxx, already activated xxxxx (Gem::LoadError)

# この記事について
* タイトルのようなエラーが出た際の対処方法について

# エラー内容
```
check_version_conflict: can't activate net-ssh-3.2.0, already activated net-ssh-2.9.2 (Gem::LoadError)
```

# 原因
* 既に複数のgemがInstallされていて、activateしたいGemがactivateできない為と思われます。

# 対処方法
* 私のケースでは、古いGemがactivatedされていたので、古いGemを削除しました。

```
# 例: bundle exec gem uninstall [対象のGem名]

$ bundle exec gem uninstall net-ssh

Select gem to uninstall:
 1. net-ssh-2.9.2
 2. net-ssh-2.9.4
 3. net-ssh-3.2.0
 4. All versions
> 1

```
* 上記のような感じで、1,2を削除しました。
Nagios check_pingでCRITICAL - Could not interpret output from ping command

# ***CRITICAL - Could not interpret output from ping command*** が出たときの話

>
- *check_ping*を実行して、以下のように出た人向け。
>
```
# /usr/local/nagios/libexec/check_ping -H x.x.x.x -w 3000.0,80% -c 5000.0,100% -p 1
ping
CRITICAL - Could not interpret output from ping command
```

---

- 調べたところ、大半の人はnagiosユーザーにping実行権限が無いから出るエラーとのこと。
  - https://www.thelinuxfaq.com/63-solved-critical-could-not-interpret-output-from-ping-in-nagios

>
- pingの実行権限見てみて、付いてなければ付与するだけ。
>
```
# ls -al /bin/ping
-rwxr-xr-x 1 root root 38520 3月 22 13:46 2017 /bin/ping
# chmod u+s /bin/ping
# ls -al /bin/ping
-rwsr-xr-x 1 root root 38520 3月 22 13:46 2017 /bin/ping
```

---

- 自分の環境では、↑では解決しなかったので備忘録として書いておきます。
- やったことは、***プラグインのバージョンを上げた***こと。

>
- バージョン確認。
>
```
# cd /usr/local/nagios/libexec
# ./check_ping -V
check_ping v1.5.x (nagios-plugins 1.5.x)
```
- (2017/07/20)時点での最新はv2.1.4らしいので、インストールする。
- コピーするときは旧バージョンのプラグインも念のため保持しておく。
>
```
# cd /usr/local/src
# wget https://nagios-plugins.org/download/nagios-plugins-2.1.4.tar.gz
# tar zxvf nagios-plugins-2.1.4.tar.gz 
# cd nagios-plugins-2.1.4
# ./configure && make && make install
# ./plugins/check_ping -V
check_ping v2.1.4 (nagios-plugins 2.1.4)
# cp ./plugins/check_ping /usr/local/nagios/libexec/check_ping2
```
- 動作確認して、check_pingが通ることを確認。ファイルを置き換えて終了 :ok_woman: 
>
```
# /usr/local/nagios/libexec/check_ping2 -H x.x.x.x -w 3000.0,80% -c 5000.0,100% -p 1
PING OK - Packet loss = 0%,...省略
# mv check_ping2 check_ping 
```
button要素をflexにすると位置指定が出来ない

button要素をflexにすると位置指定が出来ないバグっぽいものを見つけたっぽいので貼り付けておく
https://codepen.io/anon/pen/GEbbdE

## safariでの表示
![スクリーンショット 2017-07-20 11.23.55.png](https://qiita-image-store.s3.amazonaws.com/0/99270/2870f062-9b00-419a-5d4a-c78683412be5.png)
殆ど何も効かない、強制的に上下中央左寄せになる


## chrome, firefoxでの表示
![スクリーンショット 2017-07-20 11.24.45.png](https://qiita-image-store.s3.amazonaws.com/0/99270/686c890f-83af-77bb-01c1-6c8d9d7b9e2f.png)
強制的に上下中央になる


## 同じ人を見つけた

http://qiita.com/hashrock/items/189db03021b0f565ae27
buttonのレイアウト出来るようにdivを入れるといいみたい。ちょっと汚いHTMLになるから嫌だなぁ…。
(hoverとかイベントとかでアニメーションするのでもワンクッション必要になっちゃう)
アジャイル開発それでもガントチャート

スクラム開発ではチームのベロシティを測り、逆算して開発のゴールがいつくらいになるかを見積もります。
しかし、チームメンバーの変化の多いフェーズではチームのベロシティを測ることが難しいケースもあります。

特にチームの立ち上がりフェーズで、予定の人数を立ち上げ時には集めておらず、開発が進捗する度に１人ずつ増員する場合などがあります。
例えば１ヶ月に１人の増員が入るフェーズだと、３週間で３スプリント回して、平均的なベロシティが見えてきた時には１人増加することで、ベロシティの指標の値がリセットされてしまいます。
これは今までの人数プラス新しい人の分が増えると言う単純な話ではありません。
チームメンバの参加は既存チームのスキルトランスファーをする上で既存メンバーのスピードを落とすこともあります。また、通常チケットに上げづらいような細かな作業が得意なメンバかもしれません。
新しいメンバーの参加はチームに変化をもたらします。

ベロシティが測れない中で、どのように計画を立てたら良いのでしょうか？
バックログに全てに全員でポイントを付けるのはとても時間がかかりますし、スプリントプランニングを待っていたら計画を立てれません。
その作業こそがプロダクトオーナーの仕事なのですが、スケジュールという観点はスクラム開発では定義されていません。
タイムボックスとベロシティが指標になっています。


多くのプロジェクトではビジネス的な要件や契約などによってスケジュールを意識する必要があるケースが多くあります。
スケジュールに対する柔軟性を持っているならば、そのスケジュールに関する機能が柔軟性や堅牢性、保守性を重視すべきならばスケジュールの延長を提案しますが、技術的負債を抱えてもスケジュールを優先するべき重大な事案がビジネス要件としてある場合には、システムの機能は稼働すれば良いという水準にまで下がります。
どちらを選択すればよいかは状況に寄るため、スケジュールの延長が悪いことというのが悪いことであるかはわかりません。

この考えを前提に考えると、ベロシティとスプリントプランニングだけでは見積もりの見通しが悪くなります。

まとめると

- チームの変化が多い
- スケジュールに対して厳しい要望がある
- システムの技術的負債をコントロールする権限がある
- そして割り込み作業がほとんどない

これらの場合やチームがまだ無く見積もり時などの場合ではガントチャートは計画を非常に明確にしてくれます。


アジャイル開発は計画を立てないと言われがちですが、場合によってはしっかりとガントチャートを書いてはじめると良いケースも多いです。
ただし、チームが動き出すと計画はどんどん変わっていきます。
その場合にはガントチャートに縛られすぎず、スクラム開発用のツールやカンバンツールをメインに切り替え、
システムの価値がどこにあるのかを考え、それをコントロールする権限を手に入れ、良いシステムを作る方法を模索して下さい。
それこそがアジャイル開発です。
bash仮想ネットワークファイルのお相手としての StreamRelay.NET.exe

---
### bash仮想ネットワークファイルのお相手としての StreamRelay.NET.exe

bash には仮想ネットワークファイルというものがあって、

`bash < /dev/tcp/192.0.2.1/90 > /dev/tcp/192.0.2.1/91`

のように、192.0.2.1:90 を入力源、192.0.2.1:91 を出力先というように、ネットワークへリダイレクトする事ができる。
この入出力を一本化(一本のTCPコネクション)する事もできるが、この入力に一本、出力にも一本、合計2本の相手として、通常は、netcat(nc.exe)を2つ起動する事になると思うが、StreamRelay.NET.exe なら一つでよい。

---
### 具体例(bash と同等)

`bash < /dev/tcp/192.0.2.1/90 > /dev/tcp/192.0.2.1/91`
⇔
`StreamRelay.NET.exe -LocalPort -1 -LocalProgram cmd.exe -RemotePort 90 -RemoteHost 192.0.2.1 -RemoteHost2nd 192.0.2.1 -RemotePort2nd 91 -RemoteSeparateConnection`

cmd.exe の入力と出力が、2本の TCP コネクションに分かれるので、上記の bash の簡易リモートシェルと同等になる

---
### 具体例(bash の相手)

`bash < /dev/tcp/192.0.2.1/90 > /dev/tcp/192.0.2.1/91`
⇔
`StreamRelay.NET.exe -LocalPort -1 -LocalProgram cmd.exe -RemotePort 90 -RemoteHost 192.0.2.1 -RemoteHost2nd 192.0.2.1 -RemotePort2nd 91 -RemoteSeparateConnection`

の相手をするには↓な感じ

`StreamRelay.NET.exe -LocalPort 91 -LocalPort2nd 90 -RemotePort 0 -LocalProxy netstream:///?OutputLf`

無印(「-LocalPort」とか「-RemotePort」)が「Read/Input(読み取り)」。
2番目(「-LocalPort2nd」とか「-RemotePort2nd」)が「Write/Output(書き込み)」

さらに、bashへの入力時に「CrLf」→「Lf」に置換する必要があるだろう。

---
### UDPにも対応

対応してみたけど、↓のUDPの通信が発生しないのでダメかも知れない。

`bash < /dev/tcp/192.0.2.1/90 > /dev/udp/192.0.2.1/91`

なので、まず出力してUDP通信が発生するようにする

`(bash --version && bash)< /dev/tcp/192.0.2.1/90 > /dev/udp/192.0.2.1/91`
⇔
`StreamRelay.NET.exe -LocalPort -1 -LocalProgram cmd.exe -RemotePort 90 -RemoteHost 192.0.2.1 -RemoteHost2nd 192.0.2.1 -RemotePort2nd 91 -RemoteSeparateConnection -RemoteUdp2nd`

の相手をするには↓な感じ

`StreamRelay.NET.exe -LocalPort 91 -LocalPort2nd 90 -LocalUdp -RemotePort 0 -LocalProxy netstream:///?OutputLf`

---
### UDPにも対応(注意点)

UDP にも対応してみたが、UDP の場合は、一度通信が発生しないと接続しないので(つまり、相手から読み込むのに相手がデータを送信しない)、うまく接続できないかもしれない。

StreamRelay.NET.exe側としては、「Remote」と呼称される側の読み込み(RemotePortで指定される接続)がUDPの場合、適当なデータを送信して、接続を確立させる。

一方で、「Local」と呼称される側の書き込み(LocalPort2ndで指定される側の接続)がUDPの場合、相手側で適当なデータを送信してくれないと、接続が確立されない(つまり、書き込みできない)。
さらには、書き込み側の接続が確立できないので、読み込み側の接続だけで、読み書きの接続とするわけにはいかないので、結局「Local」と呼称される側の接続が確立されない、という事になるかもしれない。

---
### 具体例(bash の相手)(一つの待機サービスで、接続順で{読み込み→書き込み}で2本で、1本の入出力ストリーム)

`StreamRelay.NET.exe -LocalPort 90 -RemotePort 0 -LocalSeparateConnection`

一番最初の接続を入力、二番目の接続に出力、というようになる

---
### プラグインstdioTCP.dll

プラグイン stdioTCP.dll は、標準入出力のリダイレクト先をTCPにするので、プラグインstdioTCP.dllを使っても実現できる

#### 具体例(bash と同等)(プラグインstdioTCP.dll)

`bash < /dev/tcp/192.0.2.1/90 > /dev/tcp/192.0.2.1/91`
⇔
`StreamRelay.NET.exe -LocalPort -1 -LocalProgram cmd.exe  -RemotePort 0 -RemoteInputTCPHost 192.0.2.1 -RemoteInputTCPPort 90 -RemoteOutputTCPHost 192.0.2.1 -RemoteOutputTCPPort 91`

#### 具体例(bash の相手)(プラグインstdioTCP.dll)

`bash < /dev/tcp/192.0.2.1/90 > /dev/tcp/192.0.2.1/91`
⇔
`StreamRelay.NET.exe -LocalPort -1 -LocalProgram cmd.exe  -RemotePort 0 -RemoteInputTCPHost 192.0.2.1 -RemoteInputTCPPort 90 -RemoteOutputTCPHost 192.0.2.1 -RemoteOutputTCPPort 91`

の相手をするには↓な感じ

`StreamRelay.NET.exe -localport 0 -remoteport 0 -verbose -debugmode -LocalOutputTCPPort 90 -LocalInputTCPPort 91`

---
### 目次へ戻る

[目次というか最初の一歩](http://qiita.com/tomoki0sanaki/items/85f20cbe18378e33cd6f)
git logで特定の拡張子のファイルを含むコミット一覧が欲しい

例えば`.sql`ファイルを含むコミットの一覧が欲しくて考えた。

```
git log master..develop --no-merges --oneline *.sql
```

- mastetブランチとdevelopとの比較
- mergeコミットは除いて
- 1行で表示
- 対象はカレントディレクトリ以下のすべての拡張子が`.sql`のファイル

のログを表示。

```
git log --grep=<pattern>
```

とか

```
git log --name-only | grep <pattern>
```

とか試したけど、欲しいのはそれじゃない感。
そういえばディレクトリ指定できるな、ということを思い出して、ファイル指定すればいいと気づいて解決した。
Antターゲット覚え書き


実行可能なjarを作るために簡単なbuild.xmlを書いたので備忘録
version : 1.9.6


## クラスパス
lib配下の全てのjarを指定

```
<path id="classpath">
	<fileset dir="${lib.dir}" includes="*.jar" />
</path>
```

## コンパイル
クラスファイル置くディレクトリを削除

```
<target name="clean">
	<delete dir="${classes.dir}" />
</target>
```

上記のcleanをdependsに指定してるので、clean後にコンパイル


```
<target name="compile" depends="clean">
	<mkdir dir="${classes.dir}" />
	<!-- classpathrefを追加 -->
	<javac includeAntRuntime="false" srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath" encoding="utf-8"/>
</target>
```

| 項目 | 説明 |
|:-:|:-:|
| includeAntRuntime  | Antのライブラリを参照するか |
| srcdir  | ソース  |
| destdir | クラスファイルの保存先  |
| classpathref | 上記で指定したクラスパスのpath id を指定  |


## jarに入れる環境別の設定ファイルを指定
プロジェクトに設定ファイルを複数作る
今回は検品用と本番用を用意

antビルド実行時に引数を渡して、どのファイルをjarに入れるか指定する
以下、実行コマンド

検品
> ant -Dtarget=it

本番
> ant -Dtarget=prd

### コマンドの引数チェック
"it"か"prd"じゃない場合はエラーとなる

```
<target name="check">
	<fail message="target property is invalid">
			<condition>
				<not>
					<or>
						<equals arg1="${target}" arg2="it" />
						<equals arg1="${target}" arg2="prd" />
					</or>
				</not>
			</condition>
		</fail>

		<condition property="env.it" value="true">
			<equals arg1="${target}" arg2="it" />
		</condition>

		<condition property="env.prd" value="true">
			<equals arg1="${target}" arg2="prd" />
		</condition>
	</target>
```

### コマンドの引数に応じてjarに入れる設定ファイルのパスを決める

```
<target name="it-environment" if="env.it">
　　　　<!-- "it" の場合はitフォルダ配下のファイルをjarに入れる -->
	<property name="resources.dir" value="${src.dir}/resources/it" />
</target>

<target name="prd-environment" if="env.prd">
        <!-- "prd" の場合はprdフォルダ配下のファイルをjarに入れる -->
	<property name="resources.dir" value="${src.dir}/resources/prd" />
</target>
```

## jarを作る

```
<!-- dependsに指定してるターゲットを実行してからjar作成開始 -->
<target name="createjar" depends="check,it-environment,prd-environment,compile">
	<jar destfile="./project.jar" basedir="${build.dir}">
		<manifest>
			<attribute name="Main-Class" value="メインクラスのパス" />
		</manifest>
		<fileset dir="${resources.dir}" includes="**/*.*" />
	</jar>
</target>
```
| 項目 | 説明 |
|:-:|:-:|
| basedir| ビルド処理を行うベースディレクトリ  |
| destdir | jarの保存先  |
| manifest| attributeにメインクラスを指定  |
| fileset| 環境別の設定ファイルを、今回作成するjarの直下に入れる  

### 参考文献
http://www.techscore.com/tech/Java/ApacheJakarta/Ant/index/|
awsでgolangのサーバーを立てる & basic認証

ec2のインスタンスを立てるところとgolangのプログラムは割愛させていただきます。
主に、apacheの設定周りの説明をします

apacheの設定

```
sudo yum install httpd
sudo chkconfig httpd on
sudo /etc/init.d/httpd start
```

basic認証を設定する
- hogeの部分がbasic認証のユーザ名になります。
- パスワードを聞かれるので、任意のパスワードを設定します
- 複数のユーザを設定したい場合は、-cを抜いてhoge2みたいな感じでユーザを追加します
余談ですが、chromeでbasic認証のテストをする場合、cmd + shift + n でシークレットウィンドウを開いてテストすると楽です

```
sudo htpasswd -c /etc/httpd/.htpasswd hoge
```

確認はこちらでできます
```
cat .htpasswd 
```

- httpd.confを編集します。hoge.com部分はサーバ名が決まったらそれを
- ServerNameに指定してください。  
- golangはport8888で設定するものとします

 
※最低限の設定なので、最低限の設定としてご参照ください

```
sudo vim /etc/httpd/conf/httpd.conf 

////httpd.confの中身

//ServerNameを変更する。
ServerName hoge.com:80

//VirtualHostを追加する
<VirtualHost *:80>
  ProxyPreserveHost On
  ProxyRequests Off
  ServerName hoge.com:80
  ServerAlias hoge.com
  ProxyPass / http://localhost:8888/
  ProxyPassReverse / http://localhost:8888/
  <Proxy *>
   AllowOverride all
   Order deny,allow
   Allow from all
   AuthType Basic
   AuthUserFile /etc/httpd/.htpasswd
   AuthName "Please enter your ID and password"
   Require valid-user
  </Proxy>
</VirtualHost>
```

あとは、簡易なgolangのサーバを立てて、サーバをポート8888で設置して、
```
go run app.go 
```
を実行すればOKです。#app.goは任意です
平成27年度ソフトウエア開発派遣料金

[平成27年度 労働者派遣事業報告書の集計結果](http://www.mhlw.go.jp/stf/houdou/0000158549.html)
が出ていた(平成29年3月31日)ので賃金部分だけ抜粋。

#派遣料金
![派遣料金表.png](https://qiita-image-store.s3.amazonaws.com/0/170755/8f2e4d67-84c8-00c3-2cfc-8ca310be1f9a.png)
ソフトウエア開発(特定派遣)の平成27年の派遣料金は_29,447円/日(8H)_
１ヶ月ですと_588,940円/月(20日)_位ですかね。

##参考単価表
| 契約時間 | 平均単価 |
|:-----------|------------:|
| 160H | 588,940円 |
| 140H - 180H | 515,322円 - 662,557円 |
| 140H - 200H | 515,322円 - 736,175円 |

#賃金
![賃金表.png](https://qiita-image-store.s3.amazonaws.com/0/170755/84389cf3-1304-7b27-39ef-c5e1b877a577.png)
ソフトウエア開発(特定派遣)の平成27年の賃金は_18,022円/日(8H)_
１ヶ月ですと_36,0440円/月(20日)_位ですかね。
１年ですと_4,325,280円/年_位ですかね。

#マージン率
表は無かったと思うので、上記から計算すると
ソフトウエア開発(特定派遣)の平成27年のマージン率は_約39%_となります。

#その他
[人材派遣企業マージン率一覧（2017年1月時）](http://comlocation.net/wp/wp-content/uploads/2017/01/Margin-data-2017.pdf)
adbコマンドでAndroidをWiFiに接続設定する

## `adb connect` でAndroidに接続する

```bash
$ adb connect Android.local
connected to Android.local:5555
```

## `adb shell` で中に入る

```bash
$ adb shell
```

## WiFiのSSIDを登録する

```bash
$ am startservice \
    -n com.google.wifisetup/.WifiSetupService \
    -a WifiSetupService.Connect \
    -e ssid xxxxxxxxx \
    -e passphrase xxxxxxxxx
```

`xxxxxxxxx`にはそれぞれ、SSIDとパスフレーズを設定します。


## ipを確認

```bash
$ ip addr show
```
graphql-rubyのエラーレスポンスを拡張する

# やりたきこと

↓こんな感じのjsonを

```json
{
  "data": {
    ...
  },
  "errors": [
    {
      "message": "何かエラーがあったみたい",
      "locations": [
        {
          "line": 2,
          "column": 7
        }
      ],
      "path": [
        "hogehoge"
      ]
    }
  ]
}
```

↓こんな感じにしたい

```json
{
  "data": {
    ...
  },
  "errors": [
    {
      "code": "1234",
      "message": "何かエラーがあったみたい"
    }
  ]
}
```

codeとかkeyとかプログラマブルな何かが欲しい。

# やりかた
1. `GraphQL::ExecutionError` を継承したエラーclassを作る
1. `to_h`メソッドで欲しい情報を返す
1. resolveの中で作ったエラーをraiseする

```rb:my_error.rb
class MyError < GraphQL::ExecutionError
  attr_reader :code

  def initialize(code, message)
    @code = code
    super(message)
  end

  def to_h
    { 'code' => code, 'message' => message }
  end
end
```

```rb
resolve ->(_obj, _args, _ctx) { raise MyError.new('1234', '何かエラーがあったみたい') }
```

# 雑記
[ドキュメント](http://graphql-ruby.org/)には特に書いてないので公式的な方法じゃないのかも。良くない方法だったらご指摘ください。。。
読んだソースは[このへん](https://github.com/rmosolgo/graphql-ruby/blob/5268e368589410b37b934e9591639ea4b6c57356/lib/graphql/query/executor.rb#L17-L22)
[rescue_from](http://www.rubydoc.info/gems/graphql/1.6.6/GraphQL/Schema#rescue_from-instance_method)ってのも使えそうかも。[ブロックでreturnした値は`GraphQL::ExecutionError.new`に食われてしまう](https://github.com/rmosolgo/graphql-ruby/blob/924b92bc86a6da1b90a0528ac4f623af1a5b35bd/lib/graphql/schema/rescue_middleware.rb#L43-L51)けど、ブロック内でraiseしたら行けるんじゃないかな。試してない。。。
こんなかんじ？

```rb:app/graphql/my_schema.rb
MySchema = GraphQL::Schema.define do
  query QueryType
  mutation MutationType

  rescue_from KyacchiShitaiNannkaNoError do |err|
    raise MyError('1234', err.message)
  end
end
```

RESTfulに慣れていたのでエラー時にも200を返すのが気持ち悪く感じた。が、「RESTfulじゃないからここにいるのだ」と自分に言い聞かせた。
FluentdでS3にログをアップする

## Fluentdのインストール

```
$ sudo curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent2.sh | sh
$ sudo td-agent-gem install fluent-plugin-config-expander fluent-plugin-tail-asis fluent-plugin-file-alternative fluent-plugin-forest

$ sudo mkdir /var/log/td-agent/pos
$ sudo mkdir /var/log/td-agent/buffer
$ sudo chown -R td-agent. /var/log/td-agent
```

## conf設定

```
$ sudo vi /etc/td-agent/td-agent.conf
--------------------------------------------------------------------------------
<source>
  type config_expander
  <config>
    type tail
    format none
    path /var/log/ec2-user/XXXX.log
    pos_file /var/log/td-agent/pos/XXXX.pos
    tag api_${hostname}/XXXX
  </config>
</source>

<source>
  type config_expander
  <config>
    type tail
    format none
    path /var/log/ec2-user/XXXX.log
    pos_file /var/log/td-agent/pos/XXXX.pos
    tag api_${hostname}/XXXX
  </config>
</source>

<match *.**>
  type forest
  subtype s3
   <template>
    aws_key_id XXXX
    aws_sec_key XXXX
    s3_bucket XXXX
    s3_region ap-northeast-1

    path
    buffer_path /var/log/td-agent/buffer/${tag}
    time_slice_format %Y/%m/%d/${tag}/ec2-%Y-%m-%d-%H
    retry_wait 30s
    retry_limit 5
    flush_interval 1h
    flush_at_shutdown true
   </template>
</match>
```

## 起動と自動起動

```
$ sudo /etc/init.d/td-agent start
$ sudo chkconfig td-agent on
```

## リブート時のfuluentdの実行

```
$ sudo crontab -e
--------------------------------------------------------------------------------
@reboot /bin/bash -lc '/etc/init.d/td-agent restart 1>/tmp/td-agent-restart.log 2>&1'
```

## つまづいた事

fluent-plugin-tail-asis(type tail_asis)を使用していたが下記のようなエラーが発生し起動できない。解決策は見つからず。tailに変更した。

* uninitialized constant Fluent::TailInput (NameError)
* dry run failed: uninitialized constant Fluent::TailInput

## 未解決

@reboot時にfluentdがログをS3に飛ばしてくれない
[Rails] 本番サーバ上で必要なデータをCSVに出力し、ローカルに落としてくる

よくやる作業なのでメモしておく。

- Railsコンソールで実行
- Excelで開く時に文字化けしないようにBOMを追加する
- SCPでローカルに落とす

```ruby
# 本番サーバで Rails Console に入る
# ssh hogehoge
# cd /var/www/hogehoge/current
# RAILS_ENV=production bundle exec rails c

# file_name = "user_info.csv"
file_name = ""
# columns = %w(id name age address)
columns = %w()

# Excel で開くことができるように BOM を追加
File.open file_name, 'w' do |f|
  f.write "\uFEFF"
end

CSV.open file_name, 'a' do |csv|
  csv << columns
  # User.each do |uesr|
  #   csv << [user.id, user.name, user.age, user.address]
  # end
end

# ファイルをローカルへコピー ※ローカルで実行
# scp remote-hostname:/var/www/hogehoge/current/user_info.csv user_info.csv
scp remote-hostname:/remote/path/to/file /local/path/to/file

# csv ファイルを削除 ※本番サーバで実行
# rm user_info.csv
rm file_name
```
perlのDBIの、executeの戻り値

perlのDBIの、executeの戻り値について。

以下のようなときの話。

```perl
    # あらかじめ接続した$dbhがあるとして。
    my $sth = $dbh->prepare('実行したいクエリ');
    my $rv = $sth->execute();
    
    if (!defined($rv)) {
        printf("Failed to query.\n");
        printf("%s\n", $sth->errstr);
        return;
    }
    
```

|戻り値          |意味                               |
|:-------------:|:----------------------------------|
|undef          |クエリの失敗                        |
|-1             |影響があった（返ってきた）行が不明    |
|0E0            |影響があった（返ってきた）行が0       |
|数値           |影響があった（返ってきた）行数        |

影響があった行がない、または返ってきた行がない場合、0E0という文字列が返ってくる。

以下のように判断されるので、うまいこと使い分けることも、、、あるのかな？

```perl:a.pl
#! /usr/bin/perl -w

use strict;
use warnings;

my $a = '0E0';

printf("%s is %s\n", '$a', $a);

if ($a) {
    print('if ($a) ... true' . "\n");
} else {
    print('if ($a) ... false' . "\n");
}


if ($a > 0) {
    print('if ($a > 0) ... true' . "\n" );
} else {
    print('if ($a > 0) ... false' . "\n");
}
```

<pre>
[root@localhost ~]# ./a.pl
$a is 0E0
if ($a) ... true
if ($a > 0) ... false
[root@localhost ~]#
</pre>


-1が返ってくるパターンが良く分からない・・・。
クエリ自体は成功している、と考えていいのやら。
突然tigが使えなくなった解決方法


# 問題
突然こんなERRORがでて、tigコマンドが使えなくなったので、いろいろ調べたが、結構手間取ったので書いておく

```
tig
dyld: Library not loaded: /usr/local/opt/readline/lib/libreadline.6.dylib
  Referenced from: /usr/local/bin/tig
  Reason: image not found
```


# 解決策

最終的には、brew をUpgradeしたら治った。

```
brew upgrade
```

これでtigは使えるようになったが、tigを使うと毎回以下のWarningが出るようになった

```
tig warning: /usr/local/etc/tigrc:90: status-untracked-dirs has been renamed to status-show-untracked-dirs
tig warning: Errors while loading /usr/local/etc/tigrc.
```

これは次のコマンドで削除して解決した。`rm -f /usr/local/etc/tigrc`

# 試したもの

ほかにも`brew upgrade bash`でbashをupgradeすればよいと書いてあったのもあったので試したが、だめだった
備忘録：「オブジェクト指向でなぜつくるのか」３章

前回は「２章について書きます。」と言ったな。あれは嘘だ。

２章は「現実世界をオブジェクト指向の比喩として使うのは色々間違ってるよ。」という内容でした。
が、すでにわかってる内容しかなかったので割愛。

今回は３章をまとめます。
ちなみに、この本では「オブジェクト指向プログラミング」の略語として「OOP」を使っており、タイトルにも出てきますが、個人的にわかりづらいため僕の記事では「オブジェクト指向」として統一します。

#第３章 OOPを理解する近道はプログラミング言語の歴史にあり
要するに 

 - プログラミング言語改良の歴史（いままでのプログラムではなにができてなにができなかったのか）
 - なんのためにオブジェクト指向が発明されたのか
 
 
をまとめる章でした。
ひとつずついきましょう。

##プログラミング言語改良の歴史
プログラミングを学んだ方はご存知だと思いますが、そもそもコンピュータは２進数しか理解できません。
かと言ってコンピュータにわかるコードをそのまま書くと今度は人間が理解できない。

ということで人間がわかりやすい文法で書く仕組みを提供してくれるのが「プログラミング言語」であり、それで書いたコードをコンピュータが理解できるように直すのが「コンパイル」です。

つまり、プログラミング言語の進化とは「人間が理解しやすいように改良する」ことでもあります。
可読性が命な訳です。
初期のコンピュータが理解できる「機械語」

```
A10010
8B160210
01D0
A10410
```
から「アセンブリ言語」

```
MOV AX, X
MOV DX, Y
ADD AX, DX
MOV Z, AX
```

「高級言語」

```
Z=X+Y
```

へと改良され、現在見慣れたコードの形になっていきます。
そうすると今度は「一行一行はわかるけど全体の流れが複雑で理解できない」ということになります。
そこで「構造化プログラミング（＝GOTOレスプログラミング）」、「サブルーチンの独立性を高める」といったことが提唱されます。

構造化プログラミングについては「GOTO禁止」といえばわかると思います。
**大事なのは「独立性を高める」という部分です。**

これはどこからでもアクセスできる「グローバル変数」をなくし、思わぬ変更を防ぐためのものです。
また、作業効率をあげるために「再利用の促進」も同時期に生まれました。
これはサブルーチンを使用することで共通の部品を使えるようにするものです。
これらのために「ローカル変数」「値渡し」などの仕組みが生まれました。

この時点で進化の方向性は「独立性を高める」「再利用性を高める」方向にシフトしていきました。

##なんのためにオブジェクト指向が発明されたのか
ということでここまでで

- GOTO文の廃止
- サブルーチンによる再利用

は実現しました。
しかし、グローバル変数はどうしても共通の変数として持っておきたいときがあったり、サブルーチンだけだと再利用性が足りないという問題が残りました。
これらを解決するのがオブジェクト指向となります。

##まとめ
ということでこの章は、言語改良の歴史とオブジェクト指向の必要性の話でした。
実際に何をしたかったのかを意識することで理解がしやすくなったと感じます。
次回は４章をまとめます。
fluentdからkafkaにログ送信する際のパフォーマンスチューニング

# はじめに

fluentdからKafkaにデータ送信する際に、気にしておくべきfluentd側のチューニングポイントのまとめです。
fluent-plugin-kafkaのBufferedOutput前提です。
最初にデータが送信される際の流れを簡単に説明した後、チューニングポイントについて記載します。

###### 使用ソフトウェアとバージョン
- kafka 0.10
- fluentd v0.12
- fluent-kafka-plugin(BufferdOutput) v0.57 

# Kafkaへ送信される流れ

BufferedOutputプラグインではレコードはchunkと呼ばれる単位に区切られてバッファリングされます。
以下の記事にBufferedOutputの処理の流れが纏められており、参考になります。

http://qiita.com/tatsu-yam/items/bd7006e483f3b3c64309

本記事ではchunkがキューに入った後、Kafkaに送信されるにあたりどのような処理が行われるかについて追加で説明します。

1. chunk内の各レコードは1レコードづつ、Kafka送信用のキューにキューイングされます。
2. キューイングには`kafka_agg_max_bytes`というリミットが設けられています。送信キューが`kafka_agg_max_bytes`未満の間はレコードは順次キューイングされていきます。
3. キューイング後の合計サイズが`kafka_agg_max_bytes`を超える場合、キューイングの前にキューの中身をまとめてKafkaにバッチ転送します。
4. Kafkaへバッチ転送をした後の挙動は、`required_ack`というパラメータやKafkaのレプリケーション有無によって動作が異なってきます。ここではKafkaがレプリケーションされてる前提で説明しています。
  * `required_ack = -1`：Kafkaは受け取ったデータに対して、全レプリケーションフォロワまでの書き込みが完了したタイミングでackを返します。fluentdはackを待って、次の処理に移ります。
  * `required_ack = 1`：Kafkaは受け取ったデータに対して、レプリケーションリーダ（自分自身）への書き込みが完了したタイミングでackを返します。fluentdはackを待って、次の処理に移ります。※並行してレプリケーションの処理は行われます。
  * `required_ack = 0`：Kafkaはackを返さず、fluentdもackを待たずに次の処理に移ります。
 
上記の1~4までの流れをチャンク内のレコードが無くなるまで、処理するというのがfluent-plugin-kafkaのざっくばらんな処理の流れになります。

# チューニングポイント

上記を踏まえてfluentdのチューニングポイントです。

## `num_threads`を上げる

デフォルトの`num_threads`は1です。
上述した参考リンクに説明がありますが、`num_threads`を上げれば、chunkを複数スレッドで送信してくれます。
chunkの送信処理が詰まっていたなど、複数chunkがエンキューされていた状況であればスループットの上昇が期待できます。
ただし、複数チャンクが並列にKafkaに送信されるとレコードの順序性は保てなくなるので、その点で問題無いか注意が必要です。

## `kafka_agg_max_bytes`を上げる

これを上げるのがスループットに最も効果があります。
fluentd - > KafkaでのスループットのキモはKafkaへバッチ転送した後のack待ちをいかに減らすかだと思っています。
そのため、キューイングの上限サイズを上げてack待ちの頻度を減らす事が最も有効です。
デフォルトの`kafka_agg_max_bytes`は`4096`(byte)です。
これは結構少ない値だな、と個人的には思っており、レコードのサイズや件数が多い環境では`1m`あたりまで上げておくとスループットの上昇が期待できます。

## `required_ack`を変更する

デフォルトの`require_ack`は`-1`です。
ackを返すまでに必要な処理が多いため、ackが返ってくるまでの待ち時間は長めになり、待ち時間に引きづられる形で送信スループットも低下します。
このパラメータを`1`または`0`にすると、kafkaのack待ちの時間を減らす、あるいは0にする事ができるためスループットを上げる事ができます。
しかし、障害時などにログが欠損する可能性は上がるため、信頼度とスループットの兼ね合いで決めるのが良いでしょう。

# おわりに

fluent-plugin-kafkaのBufferdOutputのパフォーマンスチューニングについて色々と書きました。
が、結論とりあえず`kafka_agg_max_bytes`を上げとけば、スループット上限をデフォルトからかなり伸ばす事ができます。
という訳で、ログの流量に対してスループットが十分に出なくて困った場合、まずは`kafka_agg_max_bytes`を上げるのを検証してみるのが良いのではないかなと思います。
クラウドサーバでTensorFlow　その16

#概要
クラウドサーバでTensorFlowやってみた。
kaggleのdogs vs catsやってみた。
jsdoで動かしてみた。
ファイル選択にしてみた。

#環境
クラウド idcf
linux debian-8.11
tensorflow 1.2
gpu 無し

#写真
![](http://jsdo-static-contents.s3.amazonaws.com/images/capture/Y/6/A/Y6A0.jpg?t=1500507411)

#成果物

http://jsdo.it/ohisama1/Y6A0
クラウドサーバでTensorFlow　その15

#概要
クラウドサーバでTensorFlowやってみた。
kaggleのdogs vs catsやってみた。
jsdoで動かしてみた。

#環境
クラウド idcf
linux debian-8.11
tensorflow 1.2
gpu 無し

#写真
![](http://jsdo-static-contents.s3.amazonaws.com/images/capture/a/9/1/a91n.jpg?t=1500506746)

#成果物

http://jsdo.it/ohisama1/a91n