scramble cadenza

技術ネタのガラクタ置き場

RDSのインスタンス作成、タイプ変更等の時間を調べた

イントロ

よくわかってなかったので自分で実験。
会社でアップグレードする機会があったので、その予行練習の記録をまとめたものです。

準備

0 の状態から最小限の構成で確認したかったので、以下のようになった

  • 管理画面から RDS を作成
  • 管理画面から ec2 を作成(ubuntu)
    • デフォルトの Security Group を attach する(RDS の Security Group がデフォルトの Security Group を許可する設定になっていたため)
  • 立ち上げた ec2 に mysql-client を install する
    • sudo apt-get update && sudo apt-get install mysql-client
  • 管理画面から RDS を変更
    • この結果を観察

インスタンスの作成

Time(utc+9) Event
Jul 9 9:37 AM Finished applying modification to convert to a Multi-AZ DB Instance
Jul 9 9:28 AM Applying modification to convert to a Multi-AZ DB Instance
Jul 9 9:28 AM DB instance created
Jul 9 9:28 AM DB instance restarted
  • クリックしてからイベント表示まで、3 ~ 5 分ほどかかる。
  • URL が表示されていれば、Status が modifying になっていても、接続は可能
    • Applying modification to convert to a Multi-AZ DB Instance のタイミングでは、もう URL は発行されている
  • Multi AZ 構成だと、インスタンス作成に少なくとも 10 分程度かかる模様。
  • m3.xlarge, m3.large, t2.small で検証したところ、インスタンス作成時間にインスタンスタイプは関係ないっぽい
    • 作成時間にほとんど差が無かった
    • 検証に偏りがあるかもしれない可能性は若干ある

インスタンスタイプの変更

AWS RDSのインスタンスタイプ変更やメンテ再起動にかかる時間は約1分〜5分 - Qiita

を拝借。sleep の時間を 5 秒に変えている

ubuntu@ip-172-31-25-166:~$ while true; do
>   mysqladmin ping -h test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com -u root -ppassword
>   date
>   sleep 5
>   echo "---------"
> done
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqld is alive
Sun Jul  9 01:30:17 UTC 2017
---------
...
---------
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqld is alive
Sun Jul  9 01:38:44 UTC 2017
---------
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqladmin: connect to server at 'test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com' failed
error: 'Can't connect to MySQL server on 'test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com' (111)'
Check that mysqld is running on test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com and that the port is 3306.
You can check this by doing 'telnet test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com 3306'
Sun Jul  9 01:38:49 UTC 2017
---------
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqladmin: connect to server at 'test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com' failed
error: 'Can't connect to MySQL server on 'test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com' (110)'
Check that mysqld is running on test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com and that the port is 3306.
You can check this by doing 'telnet test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com 3306'
Sun Jul  9 01:41:01 UTC 2017 # <= 5 秒おきに ping を飛ばすが、これだけ刺さってしまった
---------
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqld is alive
Sun Jul  9 01:41:06 UTC 2017
...
Time(utc+9) Event
Jul 9 11:06(?) AM インスタンスの status が modifying -> available になる —
Jul 9 11:04 AM Finished applying modification to allocated storage
Jul 9 10:47 AM Applying modification to allocated storage
Jul 9 10:47 AM Finished applying modification to DB instance class
Jul 9 10:41 AM ping が通るようになる —
Jul 9 10:40 AM Multi-AZ instance failover completed
Jul 9 10:39 AM DB instance restarted
Jul 9 10:39 AM Multi-AZ instance failover started
Jul 9 10:38 AM ping が通らなくなる —
Jul 9 10:31 AM Applying modification to database instance class
Jul 9 10:30 AM Performance Insights has been disabled
Jul 9 10:30 AM Monitoring Interval changed to 60

ストレージタイプの変更

General Purpose(SSD) -> Provisioned IOPS(SSD) に変更する。
この変更だとダウンタイムは発生しない。
mysqladmin ping も変更の間、全部通ったことを確認した。

なお「Magnetic からの変更」「Magnetic への変更」はダウンタイムが発生する模様。

ストレージタイプの使用 - Amazon Relational Database Service

Time(utc+9) Event
Jul 15 6:55 PM Finished applying modification to allocated storage
Jul 15 6:45 PM Applying modification to allocated storage
Jul 15 6:45 PM Finished preparing for modification of storage
Jul 15 6:44 PM Performance Insights has been disabled
Jul 15 6:44 PM Monitoring Interval changed to 60
  • ストレージの容量に比例して、変更の時間が長くなる可能性はありそう
  • リードレプリカを作成している場合は以下の点を考慮する必要がある
    • マスターと容量は合わせる必要がある
      • マスターが 100G ならば、レプリカは 100G 以上でないといけない
    • マスターとストレージタイプを合わせる必要は無い

mysql のバージョンアップ

5.6.35 -> 5.7.17 にしてみた。ダウンタイムが発生。
どうやらテーブルの型変換が強制的に行われるので、そのようなテーブルがあると結果が大きく変わりそう

MySQL DB エンジンのアップグレード - Amazon Relational Database Service

---------
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqld is alive
Sat Jul 15 10:10:21 UTC 2017
---------
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqladmin: connect to server at 'test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com' failed
error: 'Can't connect to MySQL server on 'test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com' (111)'
Check that mysqld is running on test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com and that the port is 3306.
You can check this by doing 'telnet test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com 3306'
Sat Jul 15 10:10:26 UTC 2017
...
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqladmin: connect to server at 'test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com' failed
error: 'Can't connect to MySQL server on 'test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com' (111)'
Check that mysqld is running on test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com and that the port is 3306.
You can check this by doing 'telnet test.celqgbwlf3q4.ap-northeast-1.rds.amazonaws.com 3306'
Sat Jul 15 10:13:43 UTC 2017
---------
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqld is alive
Sat Jul 15 10:13:48 UTC 2017
---------
Time(utc+9) Event
Jul 15 7:19 PM Finished DB Instance backup
Jul 15 7:16 PM Backing up DB instance
Jul 15 7:15 PM Database instance patched
Jul 15 7:15 PM Finished DB Instance backup
Jul 15 7:15 PM Updated to use DBParameterGroup default.mysql5.7
Jul 15 7:13 PM DB instance restarted
Jul 15 7:13 PM ping が通るようになる–
Jul 15 7:12 PM DB instance shutdown
Jul 15 7:10 PM ping が通らなくなる–
Jul 15 7:10 PM Backing up DB instance
Jul 15 7:10 PM DB instance shutdown
Jul 15 7:05 PM Performance Insights has been disabled
Jul 15 7:05 PM Monitoring Interval changed to 60

注意事項

  • snapshot 作成中は instance を変更できない
    • snapshot の作成時間は、自動バックアップのイベントを見てれば大体予想がつく

まとめ

  • 自動バックアップの時間帯に注意
    • スナップショット作成中はインスタンスの変更ができない
    • メンテナンス時間をとる場合は、作業が後ろ倒しになってしまうので気をつける
  • インスタンスタイプ変更によるダウンタイムは 3 ~ 5 分程度
    • Multi-AZ のフェイルオーバーのタイミングでダウンタイムが発生する
    • この時間は DB にアクセス出来ないため、メンテナンス時間を設けることを推奨
    • 復旧後、管理画面上は modifying になっていたものの、接続は可能
      • インスタンスタイプ変更から、 available までの時間(= 全部完了の時間)は 約 35 分
  • mysql のバージョンアップによるダウンタイムは 3 分程度
  • リードレプリカの設定をしている場合、storage の設定に気をつける
    • マスターのアップグレードをするために、リードレプリカのアップグレードが必要になる場合がある
  • 検証は行ったけど、実際のデータが投入されてないと、実際の作業時間の見積もりは難しい
    • アップグレード予定の RDS からバックアップを作成し、予行練習するのが、一番確実で信頼できる結果になる
      • なんとも当たり前すぎる結論…
    • 参考までに、会社でデータベースの増強を行ったときは、作業時間が二時間程度だった
      • 作業内容は「リードレプリカのアップグレード」+ 「mysql 5.7 アップグレード」+ 「PIOPS への変更」+ 「インスタンスタイプの変更」の大盛り全部のせ
        • リードレプリカのアップグレード1時間 + マスターのアップグレード1時間という内訳
      • ストレージの容量は 40G ~ 60G 程度
  • 変更前に手動スナップショットは作成しておくと、万が一の事があっても安心
    • 無いとは思うが、いきなりインスタンスが消されても、手動スナップショットは残るため

参考

【AWS】RDSのインスタンスタイプ変更にかかる時間を調べてみた | Developers.IO AWS RDSのインスタンスタイプ変更やメンテ再起動にかかる時間は約1分〜5分 - Qiita

Docker と Vagrant で dotfiles をテストする

イントロ

mgi.hatenablog.com

で書いたとおり、最近は自分の dotfilesMItamae で管理している。

運用していく中で、ちょっと複雑な receipe を書いた時、「これ試したいけど、いきなりぶっこむのはちょっとなぁ…」と思うようになった。

そこで MItamae を試すだけ試して、満足したら廃棄できる環境を作ってみた。 ついでに、その sandbox 的環境をテストする serverspec も一緒に書いたので、まとめてみた。

準備

Dockerfile.serverspec

ローカルに serverspec をインストールするのは、ruby に依存してしまうのでナシ。

そこで serverspec を実行する Docker コンテナを用意した。

よく docker-api を使って serverspec でテストするやり方を見かけるけど、Docker コンテナの中で docker-api を使えないので、ssh 接続でテストする。

FROM ruby:2.4.1-alpine

RUN apk update && apk add openssh-client && echo -e 'Host *\nUseRoaming no' >> /etc/ssh/ssh_config

ENV SERVERSPEC_VERSION 2.39.1
RUN gem install serverspec -v ${SERVERSPEC_VERSION}

WORKDIR /serverspec

Vagrant

cookbook を試す環境は Vagrant で用意した。
最初はこれも Docker でコンテナを用意してやっていたけど、serverspec からアクセスするのはコンテナではなくイメージなので、テストが失敗する
(状況を見るとそのような挙動に見える)

# 以下は Docker で環境を作って、serverspec でテストするパターンを試したときのログ

# `ubuntu`(レシピ実行場所) で bash を起動
$ docker-compose run --rm ubuntu bash

# MItamae を install
root@a16bec43ae35:/home/root# bin/setup.sh

# レシピを実行する
root@a16bec43ae35:/home/root# bin/mitamae local cookbooks/git/default.rb
 INFO : Starting MItamae...
 INFO : Recipe: /home/root/cookbooks/git/default.rb
 INFO :   package[git] installed will change from 'false' to 'true'

# git が install された
root@a16bec43ae35:/home/root# which git
/usr/bin/git

# `serverspec` (serverspec 実行用コンテナ)で sh を起動
$ docker-compose run --rm serverspec sh

# spec を実行する。`docker-compose` を使うことで `ubuntu` で名前解決している
/serverspec # TARGET_HOST=ubuntu rspec spec/git_spec.rb

Package "git"
  should be installed (FAILED - 1)

Failures:

  1) Package "git" should be installed
     On host `ubuntu'
     Failure/Error: it { should be_installed }
       expected Package "git" to be installed
       /bin/sh -c dpkg-query\ -f\ \'\$\{Status\}\'\ -W\ git\ \|\ grep\ -E\ \'\^\(install\|hold\)\ ok\ installed\$\'

     # ./spec/git_spec.rb:4:in `block (2 levels) in <top (required)>'

Finished in 0.26349 seconds (files took 0.62787 seconds to load)
1 example, 1 failure

Failed examples:

# テストを実行しても、ssh で接続した世界には git が入っていない
rspec ./spec/git_spec.rb:4 # Package "git" should be installed

レシピを実行した後に docker commit して、もう一度 image を build して serverspec を走らせてもできたが、やってみてあまりに面倒に感じたので、スパッとやめてしまった。

ということで一周戻って Vagrant で環境を作ることになった。 現在のディレクトリを自動的に sync してくれるのも地味に助かった。

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"
  config.vm.network "private_network", ip: "192.168.33.10"
end

レシピ実行

昔なつかし、vagrant ssh からのレシピ実行

$ vagrant up
$ vagrant ssh
ubuntu@ubuntu-xenial:~$ cd /vagrant
ubuntu@ubuntu-xenial:/vagrant$ bin/setup.sh
+ mitamae_version=1.3.2
+ mitamae_cache=mitamae-1.3.2
+ [ -f bin/mitamae-1.3.2 ]
+ uname
+ mitamae_bin=mitamae-x86_64-linux
+ wget -O bin/mitamae-x86_64-linux.tar.gz --max-redirect 3 -q https://github.com/k0kubun/mitamae/releases/download/v1.3.2/mitamae-x86_64-linux.tar.gz
+ tar xvzf bin/mitamae-x86_64-linux.tar.gz
mitamae-x86_64-linux
+ rm bin/mitamae-x86_64-linux.tar.gz
+ mv mitamae-x86_64-linux bin/mitamae-1.3.2
+ chmod +x bin/mitamae-1.3.2
+ ln -sf mitamae-1.3.2 bin/mitamae

ubuntu@ubuntu-xenial:/vagrant$ bin/mitamae local cookbooks/git/default.rb
 INFO : Starting MItamae...
 INFO : Recipe: /vagrant/cookbooks/git/default.rb

テスト

  • spec_helper.rbvagrantssh 接続する情報を書く
    • host は Vagrantfile に書いた private な ipaddress を指定
    • userkeyvagrant ssh-config の内容をそのまま使う
  • コンテナから serverspec を実行する
    • -v で現在のディレクトリをコンテナ内に mount する
    • --net=host で、ホストのネットワークを使う
      • serverspec 用コンテナの中から、Vagrant で立ち上げた VM を見つけるために、この指定
require 'serverspec'
require 'net/ssh'

set :backend, :ssh

if ENV['ASK_SUDO_PASSWORD']
  begin
    require 'highline/import'
  rescue LoadError
    fail "highline is not available. Try installing it."
  end
  set :sudo_password, ask("Enter sudo password: ") { |q| q.echo = false }
else
  set :sudo_password, ENV['SUDO_PASSWORD']
end

options = Net::SSH::Config.for(host)

options[:user] = 'ubuntu'
options[:keys] = '.vagrant/machines/default/virtualbox/private_key'

set :host,        ENV['TARGET_HOST'] || "192.168.33.10"
set :ssh_options, options

# Disable sudo
set :disable_sudo, true

Dir['./spec/shared/*'].each { |f| require f }
$ docker run --rm -it -v ${PWD}:/serverspec --net=host dotfiles_serverspec rspec spec/ubuntu_spec.rb

ubuntu
  behaves like git
    Package "git"
      should be installed

Finished in 1.15 seconds (files took 0.60525 seconds to load)
1 example, 0 failures

まとめ

ここまですると逆に面倒くさくなってきて、もはや趣味の領域に入りつつあります。

それと Mac 環境でのテストになってなくて、当初目標にしていた事が実現できたとは言い難い。

しかしながら、いつでも OS X を捨てられる準備をしておかないと、もし Apple がとんでもない改悪をしても、
それに耐え続けなければならなくなるし、私はそれを我慢し続けられるほど大人では無いという自覚がある。

ということで目的とはズレてしまったが、 Mac を捨てる準備を整えることができた、という副産物が得られたので良しとしたい。

それと、少しづつテストやレシピを補完していきたい。

webpack を使った Google Chrome Extension づくり

イントロ

気が向いたので Google Chrome Extension を作成してみたくなった。 そこで最低限 webpack を使って、開発環境を整備できる環境を作ってみた。

色々調べてみて、一先ずこんなところに落ち着いた、というメモを残します。

似たようなのあるやろ

はい。あります。

方針

この記事では webpack を使って Google Chrome Extension を作り始められるところをまとめている。

というのも、既存のを参考にしながら試した結果、結局テンプレートを使うより、小さく自作するのが一番良いと思ったからです。 (一度 yeoman 使って、後で破綻し、とてもゲンナリした)

以下のようなルールを決めて環境を作っている。

ルールも小さく保って、今後も守りやすいようにしているつもり。

ディレクトリ構成

src 以下のディレクトリ構成は好きにすれば良いと思うけど、今のところ以下のようにしている。

 % tree src/ -d
src/
├── _locales
├── images
├── scripts
├── styles
└── views # React 使う場合は `components` のほうがいいかも

ディレクトリ構成は、Github で色々と repository を見て回ったものの、共通点が少なく、皆自由にやってるんだなという印象を受けた。 故に各自でしっくりくるディレクトリ構成を選べば良いと思う。自分は上記になった、という紹介。

ただ、i18n については _locales 固定なので注意が必要。 chrome.i18n - Google Chrome

初め方

webpack + ES6 を使った構成を作ってみる

$ yarn init
$ yarn add -D webpack copy-webpack-plugin babel-loader babel-core babel-preset-es2015 
$ touch .babelrc

webpack

以下は一例。

$ touch webpack.config.babel.js
$ vi webpack.config.babel.js
import path from 'path';
import CopyWebpackPlugin from 'copy-webpack-plugin';

export default {
  entry: {
    contentScripts: path.join(__dirname, 'src', 'scripts', 'contentScripts.js'),
  },
  output: {
    path: path.join(__dirname, "dist"),
    filename: 'scripts/[name].bundle.js',
  },
  target: 'web',
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    port: 9000,
    hot: true,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: { loader: 'babel-loader' }
      },
    ]
  },
  plugins: [
    new CopyWebpackPlugin(
      [
        {
          from: path.join(__dirname, 'src', 'manifest.json'),
          to: path.join(__dirname, 'dist'),
        }
      ]
    )
  ]
};

補足

  • entry は複数持つ可能性が高いので、object で指定したほうが良い気がする
  • copy-webpack-pluginmanifest.jsondist/ にコピーするために入れている
    • manifest.json とは、Google Chrome Extension の設定ファイル
    • compile が不要なものは、まるっと dist にコピーする
      • manifest.json 以外でも、例えば html はべた書きで書いたりする場合は CopyWebpackPlugin の設定を足せばいい

js

$ touch src/scripts/contentScripts.js
$ vi src/scripts/contentScripts.js
% cat src/scripts/contentScripts.js
console.log("Hello");

manifest.json

$ touch src/manifest.json
{
  "manifest_version": 2,
  "name": "name",
  "version": "0.0.1",
  "description": "",
  "author": "mgi166",
  "content_scripts": [
    {
      "matches": ["http://*/*", "https://*/*"],
      "js": ["scripts/contentScripts.bundle.js"]
    }
  ],
}

いざ開発

  • まずは「デベロッパーモード」にチェックを付ける
  • その後パッケージ化されていない拡張機能を読み込む、というボタンを押して、dist/ ディレクトリを読み込むようにする
    • 一度は webpack コマンドを実行し、dist/ を作成する必要がある

screenshot_2017-05-28_20_36_53.png (153.6 kB)

これで自作する拡張機能が登録できたと思う。

後は

$ webpack --watch

で変更を監視して、後は src/ 以下のファイルをいじれば ok compile が終わったソースを読み込む場合は、chrome://extensions/ の拡張機能で、リロードをするのを忘れないようにする。(これがめんどいのだが…)

screenshot_2017-05-28_20_36_53.png (156.3 kB)

まとめ

webpack を通して Google Chrome Extension を開発するまでの流れを書いてみた。 後はやりたいことに応じて、好きなようにできると思う。

Wercker で build API を叩いてもテストが始まらない件

イントロ

最近 Wercker を使っています。 Wercker でも CircleCI と同じように build を行う API があるのですが、どうしても build の API が叩けなくてハマったのでメモ。

現象

build API を使って build を行うと、Invalid stack というエラーが出る。
エラーメッセージの意味がよくわからない。

$ jq . -c <<JSON | curl -s -d @- -H 'Authorization: Bearer token' -H 'Content-type: application/json' 'https://app.wercker.com/api/v3/builds' | jq .
{
  "applicationId": "applicationid",
  "branch":"develop",
  "envVars":[
    {"key":"HOGE_FOO","value":"true"}
  ]
}
JSON

{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Invalid stack, only stack 1 and 5 are supported for builds"
}

結論

build API を使うのではなく、Runs API を使う。

現在の Wercker は二つの環境が同居していて、その環境のことを Stack と呼ぶ。
古い環境を Classic stack と呼び、新しい環境を Docker Workflows Stack というらしい。

今は新しく環境を作ると、全て Docker Workflows Stack になるようで、build API は古い stack 用の API で、Runs API は新しい stack 用の API となっている。
どちらも叩くと CI の build が走る。

したがって最近作った CI の場合、runs API を叩いて、build を走らせる必要がある、というネタでした。

なお、必須パラメータが Build API とは若干異なり、Runs API では、pipelineId という値が必須になる。
pipelineId というのは、repository を選択して、Workflows タブから下の方にある、pipeline 名をクリックしたときのURL から取得する。(app.wercker.com/{user}/{reponame}/workflows/pipeline/{pipelineId})
API 経由でもちょっと面倒くさいけど頑張れば取れる。

f:id:mgi:20170309230422p:plain f:id:mgi:20170309230605p:plain

curl で叩く場合、以下のようになる。

jq . <<JSON | curl -X POST -s -d @- -H "Authorization: Bearer token" -H 'Content-type: application/json' https://app.wercker.com/api/v3/runs | jq .
{
  "pipelineId": "pipeline_id",
  "branch": "develop",
  "envVars": [
    {
      "key": "HOGE_FUGA",
      "value": "true"
    }
  ]
}
JSON

おわり

Runs ってなんだよ… 「走る」かよ…
隅々まで document を読まないと、こういう恥をかきます。

こんなつまらないことでハマって、初 StackOverflow デビューしました。
速攻で英語の添削が入りました。おわり。

wercker - What does “Invalid stack, only stack 1 and 5 are supported for builds” means? - Stack Overflow

初めての人がなんとなく Go で CLI を作ってみた時のまとめ

イントロ

ちょっと昔に 「みんなのGo言語」が良かったので、自分のためだけのCLIツールを作ってみた - えいのうにっき を読んで、なんとなく何か作りたい気分になっていた。

Go 言語の知識が皆無の状態から 自作 CLI 作ったので、その時のメモをまとめてみました。
効率が良いとは思いませんが、こういう学習パスを通ればものは作れるぞ、という一例として参考になれば。

まず最初に読む

Go 言語で CLI 作るときの知見

「Go」 で「CLI」というと、過去に良い記事があったような記憶があったので、
探し出して、読み直してみた。

どちらもすごい良い記事で、更に気分が高まる。

package を探す

Go で CLI 支援パッケージ絶対あるやろ、と思ったらやっぱりあった。
めんどくさがりな自分でもできそうな気がしてきて、更に気分が高まる。

開発環境

作業ディレクトリ(Workspace って言うらしい)

Goコードの書き方 - The Go Programming Language GO:Workspace と GOPATH - Web系開発メモ

多くの Go言語プログラマは、1つの GOPATH(1つの Workspace)で、複数(or 全て)の ソース・パッケージ・リポジトリ・依存性を管理しているようです。

好きなディレクトリで作業できないのはちょっと意外。
回避方法はあると思うが、深くは追わず Go ってそういうものなんだ、で一旦飲み込んだ。 経験者の意見を伺いたいところ。

GitHub で公開することを想定すると、$GOPATH/src/github.com/{repo} みたいなところで作業をすることになる。

Emacs

go-mode + go-autocomplete を導入。
先人の知恵は素晴らしい。

学習

A Tour of Go

一通り流した。なんとなくイメージが見えてきた。

A Tour of Go

CLI 支援パッケージまわり

早速調べてあったパッケージを使い始める。
私が触ったのは以下の 2 つ。

  • spf13/cobra
  • urfave/cli

spf13/cobra

spf13/cobra: A Commander for modern Go CLI interactions

有名コマンドも採用実績があり、サブコマンドを作りやすいのが嬉しいところ。 gif 見ればなんとなく使い方がわかる、というところも良いと思った。

が、作っていくうちに「今回のやりたい内容を考えると、サブコマンド要らないのでは」と思い直し、方向転換した。 残念ながら軽く触る程度で終わってしまったが、次のチャンスがあればまた触ってみたい。

urfave/cli

urfave/cli: A simple, fast, and fun package for building command line apps in Go

cobra を止めた後に選んだのはこちら。 star 数と README のわかりやすさが決め手。

他のパッケージも眺めてみたが、大体これと似ていたので、どれでもいいなら star 数で選ぶというミーハーっぷりを発揮。

依存関係の解決

自分の GOPATH には既に別のパッケージが散乱していて、汚れまくっていたので、rubybundler 的なやつが欲しかった。

そこで Big Sky :: golang オフィシャル謹製のパッケージ依存解決ツール「dep」 を発見。

なんとなくの気分で使ってみるが、まだ使いこなせていない。 まだ Go 言語に慣れてなくて、dep の全体を把握するのが難しいのもある。

それと、この時に Go の依存関係解決についてよく理解していないことに気づき、以下の資料を見て学んだ。

Goと依存パッケージ管理

それ以外の学習

後は思いついた疑問を赴くままにググりまった。効率が悪いがしょうがない。
以下はまだ読んでないけど、時間がある時にまとめて読みたい。

感想

  • 恥ずかしながら型言語を触って何かを作ったのは初めての経験。
    • 戻り値の型がわかるとコードが読みやすい。
    • 書き方がオブジェクト指向っぽくはならず、右往左往している。
  • すぐ panic になってしまう Go は、ちょっとかわいい。
  • 配列やマップの扱いが難しかった。もっとこう… 何かあるだろ、と思う場面が多い。
    • 慣れてないだけかも。
    • こういう時、型が無い方がいいなーと思う。
  • わかっていないことは非常に多い
    • 依存関係の解決
      • dep で解決できるのは、開発環境で使用するパッケージの指定。
      • リリースして go get すると失敗する可能性は残されている。
    • reflect パッケージ周り
      • まだ腹落ちしてなくて、もうちょっと慣れが必要
    • 作業ディレクト
      • 今は $GOPATH/src/github.com/{repo} で開発してる
    • ソースコードのファイル分割と import まわり
  • 結局は A Tour of Go を一通りやって、後は勢いで押し切っただけ
    • そういう意味では「何でもいいから作りたいものがあるかどうか」が一番大事で、難しいのだよなーと思った
    • 日頃の仕事を不便だと感じる気付きが重要となる

できあがったもの

完全に自分用ですが、terminal からコードレビューを依頼する CLI を作った。

仕事環境が変わり、レビュアーが曜日ごとに変わるようになってしまったので、レビュアーを自動特定できるようにした。
「今日は誰にレビュー依頼するのだっけ」「その管理シートどこにあるのだっけ」みたいなことをコードレビューのたびにやっていて、スーパー非効率だったのを改善できて良かった。
(レビュー体制をなんとかしたほうがよいのでは、という論点はあるのだが…)

mgi166/review-request: Request code review in terminal.

とはいえ、毎回固定のメッセージで依頼を投げてしまって bot っぽい。
バレないようにするためにも、ランダムでメッセージを変えるとか、人間っぽさを出していきたい。

terraform quick start 的なメモ

イントロ

terraform の初期化を行うのはプライベートでは二回目。
以前まとめていた自分用メモを、少し整形して公開してみる。

terraform はなんぞや、というところはすっ飛ばし、もう一度手元で terraform を使う環境を構築する人向けです。
内容は今更感がありまくりで、目新しいことは何もなし。

(なお、個人的な趣味により、provider は aws で、backend は s3 にしていますし、その前提で記事を書いています。)

terraform を始める

まず terraform init コマンドを実行する。

実行するとカレントディレクトリに .terraform というディレクトリが掘られていて、ここに terraform.tfstate というファイルが作成される。

この terraform.tfstateterraform が管理するリソースを json 形式で書いたもの。
commit するのは注意が必要。
Command: init - Terraform by HashiCorp

terraform init -backend=s3 \
-backend-config="bucket=terraform-bucket" \
-backend-config="region=ap-northeast-1" \
-backend-config="key=terraform.tfstate" \
-backend-config="profile=profile_name" .

terraform init コマンドの補足

backend とは

tfstate 置き場の事。
-backend=s3terraform.tfstate の置き場所を s3 にする、という設定。

tfstate の管理について

terraform はローカルにある terraform.tfstate を元にコマンドが実行される。 したがって、複数人で開発する時、各個人のローカルで持っている terraform.tfstate の状態に差があると、terraform の実行結果に差が出てしまう。

ファイルの状態によっては、A さんは EC2 が作られていて、B さんは EC2 が作られていない、という矛盾した状況にもなり得る。こうなると、どちらが正しいかわからなくて困ってしまう。

現在実際に構成されている AWS のリソースが常に正であり、その状態は常に tfstate は反映されてなければならない。

なので、複数人で開発する場合は、全員が最新の terraform.tfstate を参照する必要がある。

State - Terraform by HashiCorp

tfstate を s3 で管理する意味

全員が最新の tfstate を参照する方法の一つとして、s3 に terraform.tfstate を置く、というやり方がある。

A さんが AWS の状態が更新したら terraform.tfstate をローカルで上書きして、s3 に保存する。
B さんは s3 の terraform.tfstate をローカルに反映させて、terraform.terraform を実行する、というみたいな仕組み。(多分)

なお、terraform init 以外でも、terraform remote config コマンドを実行することで terraform.tfstate を s3 で管理する指定ができる。

Remote State Backend: s3 - Terraform by HashiCorp

terraform remote config \
    -backend=s3 \
    -backend-config="bucket=terraform-bucket" \
    -backend-config="key=terraform.tfstate" \
    -backend-config="region=ap-northeast-1"

provider の設定

Provider: AWS - Terraform by HashiCorp

どちらかを選択する

  • access_key, secret_key セットする。
  • profile をセットする

access_key をセットする

provider "aws" {
   access_key = "${var.aws_access_key}"
   secret_key = "${var.aws_secret_key}"
   region = "us-east-1"
}
  • access_key or 環境変数 AWS_ACCESS_KEY_ID をセットする
  • secret_key or AWS_SECRET_ACCESS_KEY をセットする
  • region or AWS_DEFAULT_REGION をセットする

profile をセットする

provider "aws" {
  region  = "ap-northeast-1"
  profile = "profile名"
}

最小限の構成

環境変数を適切にセットしている前提であれば

provider "aws" {}

に落ち着く。

これからどうするの?

後は普通にドキュメント見ながら resource を定義していくだけで OK。
aws の resource はこちらにまとめられている。

Provider: AWS - Terraform by HashiCorp

terraform import 手順

terraform には、現在 AWS に構築されている環境を terraform.tfstate に落とし込む機能がある。
それを terraform import と呼ぶらしい。

Import: Usage - Terraform by HashiCorp

terraform import resource名.名前 {id}

というコマンドを叩く。
resource 名は import 対象の resource。名前は terraform で管理したい名前。{id} は resource によって異なる。
{id} は勘でなんとかなる気がします。

例えば aws_route53_zone という resource の場合は、以下のコマンドで import できた。

terraform import -backup=backup aws_route53_zone.mgi166 {zone_id}

このコマンドで何が起こるかというと、ローカルの terraform.tfstate に、aws_route53 の設定内容が反映される。

後は、自前で resource を定義しながら、terraform plan との差分が無くなるまで、 tf ファイルを試行錯誤すれば良い。
というのも、差分が無くなるということは、import コマンドを叩いた後の terraform.tfstate と、tf ファイルに書いたコードの内容が同じであることを意味するからです。

しかし、terraform import は完全ではないようで、差分をなくすのは難しい場合がある。
具体的には一部の属性が変わったりしてしまう。
私はどうしようもないと諦めてしまったけど、このあたりの良い解決方法があれば教えていただきたいです。

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but
will not be persisted to local or remote state storage.

aws_route53_zone.mgi166: Refreshing state... (ID: xxxx)
aws_route53_record.www: Refreshing state... (ID: xxxxx CNAME)

The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.

Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.


# force_destroy が `""` だったのが、"false" になっている
~ aws_route53_zone.mgi166
    comment:       "HostedZone created by Route53 Registrar" => "Managed by Terraform"
    force_destroy: "" => "false"

まとめ

  • terraform init or terraform remote config で backend の設定を作る
  • 必要に応じて terraform import で既存のリソースを import する
  • 後は document を読んで頑張る

参考

ローカル環境構築を MItamae で自動化した話

イントロ

お仕事用 MacMac book pro に変わってしまった。
環境構築をやるのも久々だったのだけれども、dotfiles のボリュームが増えたのと、実はコード化出来ていない部分が多かったりして、かなり面倒だった。

ちゃんともう一度やり直そう、と思って選んだのが MItamae

  • ansible は活用例が多いっぽいけど、yaml だと表現力が気になる(食わず嫌い)
  • Itamae は良さそうだけど、gem をインストールすることで、環境汚すのはちょっと嫌だ。ruby に依存するし。
  • となると chef も同様
  • puppet は思った以上にデカかった
  • boxen はへんじをしない。ただのしかばねのようだ
  • go で環境構築したら、Mac 以外の環境構築も楽になるのでは説を思いつく
  • itamae-go を発見
  • そういえば mrubyItamae ってなかったっけ
  • Itamaeのmruby実装「MItamae」が大体いい感じになった話 - k0kubun’s blog を発見
  • MItamae にしてみるか

みたいな流れで行き着きました。

今まではどうしていたか

rake で色々やってた。
自前で rake task を定義していて、rake コマンド叩くことで各種 dotfile の配置が完了する。
shell よりは書きやすいと思ったのと、ruby の勉強のために rake を使っていた、という経緯がある。

実際にやってみてどうだったか

mruby がほんの僅かに理解できた

結論から言うと MItamae を使うだけなら、mruby の学習自体は殆ど要らなかった。
ただし mruby でいう gem の取扱いは、CRuby と全然違うので、ここを知らないと大分混乱する。

mruby と mgem と rbenv と - Qiita

MItamae の install は簡単

Mitamae のバイナリが github で配布されているので、curl でダウンロードでおk。

ここが mruby のすごいところで、CRuby とは違う点。
今までは gem を install する時には、ruby を入れて、gem install コマンドを実行していたけど、mruby の gem は、gem 自体それぞれ binary になっているようだ。
(正しい表現かはわからない)

ディレクトリ構成は chef っぽく落ち着く

シンプルな Chef なので、書き方は自由。
だが、試行錯誤していくうちに、結局 Chef みたいな構成に落ち着いていった。
Itamaewiki にも(Chef みたいな構成とは書いてないが)ベストプラクティスが載っている。

Best Practice · itamae-kitchen/itamae Wiki

レシピの実行順序は ruby で担保する

chef で言う run_list 的なやつは MItamae には存在しないっぽい。

どうやるのが良いかわかってないが、今のところ mitamae local xxx.rb コマンドで渡す ruby のコードの中で、include_recipe を順番に並べて管理している。

MItamae で使っている gem を見る

mitamae の DSL の中で、どの程度自由にやれるのかというと、以下のファイルを見れば良いっぽい。

mitamae/mrbgem.rake at master · k0kubun/mitamae

add_dependency に追加されている gem は使える。
DSL の中からやりたいこと、という意味では大体揃っている。

結果

dotfile がすごいすっきりした。嬉しい。
.emacs.d とか .bundle 等の実体は cookbooks の中に押し込んでいる。

mgi166/dotfiles: dotfiles

おわり

k0kubun/dotfiles: Configuration management of development environment がよく出来すぎていて、大体まねっこになってしまった。

MItamaemruby もすごい便利です。

参考

esa.io クライアントを kotori から Quail に乗り換えた

イントロ

職業柄、自分のフィーリングとマッチしているというのもあるのだと思うけど、esa.io を一年運用していて全く困らないし、本当に助かってる。

しかし一点だけ、esa.io というより、OSXesa.io クライアントに不満があった。

それは kotori というクライアントを使っていたのだけど、「リンクをクリックするとクライアント内で画面遷移し、二度と戻れない」という凶悪な仕様だ。

kotori で書いた記事の中でリンクをクリックするの図はこちらです。

f:id:mgi:20170106183808g:plain

これが超辛い。

esa.io では Qiita みたいなリアルタイムプレビュー機能がついていて、右側で markdown の変換結果が見れる。
ところが記事書いている途中にうっかりリンクをクリックしたらもうゲームオーバーで、編集画面には二度と戻れない。

じゃあどうするかというと kotori を強制終了してやり直すしかない。

長文を途中保存していないと全て消えてしまう。これで何回か枕を濡らした。

今までの暫定策

とはいえ、それ以外の kotori に不満は一切なくて、運用でカバーしてた。

  • ⌘ + S をなるべく頻繁にタイプする
    • このショートカットで一次保存できる(素晴らしい)
  • スクロールする時、リンクのそばにカーソルを動かさない

とか。
kotori 先輩に鍛えられたおかげで、「これは危ない」という操作が直感でわかるようになってきて、かなり誤操作は少なくなったきた。

半分あきらめてたけど、最近知ったこと

help/サードパーティーesaツール - docs.esa.io を今更見つけて読んだ。
そこで 1000ch/quail というアプリがあるようなので、試してみたのがきっかけ。

インストール方法

1000ch/quail に書いてあるとおり。 zip 落として Applications にコピーするだけ。

electron 製ですね。

使ってみた感想

リンクをクリックすると、ブラウザで開いてくれる。

f:id:mgi:20170106184427g:plain

涙が出るほど嬉しい。初めて触った時嬉しくて声が出ました。

正直 kotori のほうが軽い気がするし、画面遷移も早い。
それに Quail でボタンを押した時に一瞬白くなるのは気になる。
けど、この機能には代え難い。

終わりに

私しかこの件で枕を濡らしてないのかもしれないけど、長年辛かったことが解決した事が本当に嬉しいので、記録しておく。

昔は kotori しか存在しなかった気がしたけど、今は iOS 版もあるみたいだし、みんな徐々に esa.io の良さに気づき始めているのでしょうか。
インターネットのすみっこから応援しています。

しかし、Quail になった今でも、リンクをクリックするのはドキドキするという。。

serverless + webpack + babel で AWS Lambda をナウく書く

イントロ

最近 AWS Lamdba を仕事で触っています。
serverless やら Apex なり色々あるわけですが、沢山ありすぎてよくわかりません。

今までは Apex の手軽さに甘えていましたが、そろそろ serverless が本気を出してきたという噂を聞き、serverless を使い始めています。

どうせ serverless を使うなら、もう全部使い倒してやろうということで、babel も意識高く使っていきたいわけです。

この記事では serverless + babel を使うところまでをまとめました。

環境

  • serverless@1.1.0
  • serverless-webpack@1.0.0-rc.2
  • webpack@1.13.3

serverless

まずは雛形作成

# template 指定は必須
# `sls` は `serverless` コマンドの alias 
$ sls create -t aws-nodejs

serverless.yml というファイルが設定ファイル。
これを早速いじっていく。コメントや公式のドキュメントがわかりやすい。

# serverless.yml を編集していく
$ emacs serverless.yml
$ git diff serverless.yml
 provider:
   name: aws
   runtime: nodejs4.3
+  region: ${env:AWS_REGION}
 functions:
-  hello:
-    handler: handler.hello
+  imageResizer:
+    handler: index.handle
# service の deploy
$ sls deploy

# AWS Lambda 関数の deploy
$ sls deploy function -f imageResizer

# AWS Lambda 関数の実行
$ sls invoke -f imageResizer

# Log の tail
$ sls logs -f imageResizer -t

まず最初に sls deploy で Service の deploy を行う。
この Service というのは serverless 用語の一つで、AWS Lambda を実行するための AWS インフラ全体を指す名称。

この Service を管理するために serverless は CloudFormation を暗黙的に使用している。
うっかり region 指定を忘れると、sls deploy を実行した時、serverless デフォルトの region である us-east-1 に、 CloudFormation の Stack が出来上がってしまうので要注意。

region はコマンドラインの option で渡すか serverless.ymlprovider で指定する。

serverless-webpack

serverless を使って deploy はできるようになった。後は書いたコードを babel を使っていく。
serverless-webpack プラグインがあって、これを使えば OK。

elastic-coders/serverless-webpack: Serverless plugin to bundle your lambdas with Webpack

外部の npm パッケージを使う場合

serverless-webpack を使用している場合、任意の package を handler の中で require しようとすると、 webpack コマンドで失敗してしまう。

$ git diff
--- a/index.js
+++ b/index.js
@@ -1,5 +1,6 @@
 'use strict';

+import gm from 'gm';

# sls コマンドは serverless コマンドの alias
$ sls webpack —out .webpack
...
ERROR in ./~/thread-sleep/~/node-pre-gyp/package.json
Module parse failed: /Users/argerich/dev/serverless-image-resizer/node_modules/thread-sleep/node_modules/node-pre-gyp/package.json Unexpected token (2:8)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected token (2:8)
    at Parser.pp$4.raise (/Users/argerich/dev/serverless-image-resizer/node_modules/acorn/dist/acorn.js:2221:15)

この場合、webpack-node-externals を使って、設定ファイルを調整する必要がある。
具体的には以下のような修正をする。

# webpack.config.js
+const nodeExternals = require('webpack-node-externals');
+
 module.exports = {
   entry: './index.js',
   target: 'node',
@@ -5,5 +7,6 @@ module.exports = {
     libraryTarget: 'commonjs',
     path: '.webpack',
     filename: 'index.js'
-  }
+  },
+  externals: [nodeExternals()]
 };
# serverless.yml

 custom:
   webpack: webpack.config.js
+  webpackIncludeModules: true

webpack を通した deploy は serverless deploy function -f では行われない。
serverless deploy で行う。

$ sls deploy

Serverless: Deprecation Notice: Starting with the next update, we will drop support for Lambda to implicitly create LogGroups. Please remove your log groups and set "provider.cfLogs: true", for CloudFormation to explicitly create them for you.
Serverless: Bundling with Webpack...
Time: 578ms
   Asset     Size  Chunks             Chunk Names
index.js  2.17 kB       0  [emitted]  main
Serverless: Packing external modules: babel-polyfill@^6.16.0, gm@^1.23.0
Serverless: Packaging service…
Serverless: Uploading CloudFormation file to S3…
Serverless: Uploading service .zip file to S3…
Serverless: Updating Stack…
Serverless: Checking Stack update progress…
.....
Serverless: Stack update finished…
Serverless: Removing old service versions…

Service Information
service: serverless-image-resizer
stage: dev
region: ap-northeast-1
api keys:
  None
endpoints:
  None
functions:
  serverless-image-resizer-dev-imageResizer: arn:aws:lambda:ap-northeast-1:xxxxxx:function:serverless-image-resizer-dev-imageResizer

✨  Done in 27.52s.

まとめ

  • region のデフォルトは us-east-1 なので、指定を忘れないこと。
    • serverless.yml に region を書いておく(ドキュメントの隅っこ にサラッと書いてある)
  • serverless-webpack で外部 package 使う場合は、webpack-node-externals を使う
  • deploy は serverless deploy function ではなく、serverless deploy を使う

という話でした。

Apex と Serverless 両方使ってみての感想

個人的には Apex のほうが学習コスト少なくて、機能もシンプルなので好き。

Serverless はもれなく CloudFormation が付いてきて、管理運用の面で色々思うところがある。AWS Lamda だけ触りたいんや! と思ってる人にとって、いきなり CloudFormation が出てくるとびっくりする。

けどその分 serverless は設定ファイルを柔軟に書けるし、プラグインは充実している。Apex で微妙だと思った環境変数周りを、うまくカバーしているところは好印象。

一方で Apex でしか出来ないことは Terraform との連携だけ。この機能にどれだけ期待するかが、一つの採用基準でもあると思う。

しかし、ただ単に AWS Lambda を使いたい、というだけであれば Apex で十分だし、落とし穴は少ない。 Apex で不満に思うことが増えてきたら serverless を検討する、で良いのかな。

と、当たり前の結論に至りました。

参考になる

おまけ

$ sls webpack —out .

とやると、現在のディレクトリが全て消されて途方にくれるので、 良い子のみんなは絶対に真似をしないでください

ファイル名で使えない文字

イントロ

web アプリケーションで、ユーザーにファイルをダウンロードさせる機能を追加したいとする。
例えばダウンロードさせるファイルの名前をアプリ側で自動決定する場合、その名前にはどのような制限があるのだろうか。

何回か調べてる気がするので、まとめてみる。

前提

そもそも、ファイル名の制限は、OS ではなく、基本的にはファイルシステムで決まる。

ファイルシステム上の限界

ファイルシステムの限界はどこまでなのかというと Comparison of file systems - Wikipedia, the free encyclopedia にまとめられている。

古いファイルシステムでは、ファイル名は 8 文字、拡張子が 3 文字、ドットも含めて合計 12 文字までしかダメ、という時代もあったらしい。8.3形式 と呼ばれているのがそれだ。

有名どころのファイルシステム(NTFS, FAT系, ext4, xfs)では

  • ファイル名の長さは 255 バイト or 文字まで
  • NUL だけがファイル名として使えない(一部のファイルシステムでは / も禁止されている)

という仕様が多いようだ。

Windows で禁止されている予約語

ただし Windows は例外がある。
ファイルシステムだけでなく、OS レベルで予約されている文字があり、これらはファイル名として使えない。

Naming Files, Paths, and Namespaces (Windows) によると、以下の文字が予約されている。

< (less than)
> (greater than)
: (colon)
" (double quote)
/ (forward slash)
\ (backslash)
| (vertical bar or pipe)
? (question mark)
* (asterisk)

手元に win 機が無かったので、残念ながら検証はしていない。
本当にダメなのか試してみたい。

結論

対応するファイルシステムのカバー範囲により、ファイル名に使える文字は異なる。
だが、現実的な落とし所として

  • windows を視野にいれるのであれば、windows で禁止されている予約語は使わないほうが良いと思う。
  • NULL文字も使えないファイルシステムが多いため、止めたほうが無難。
  • 長過ぎるファイル名は NG なので、そのあたりも調整する。
    • マルチバイト文字のときが面倒くさいが、より小さい方に合わせ 255 byte まで のほうががいいのかな。

ぐらいを考えておけば良さそうだろうか。

おまけ

手元の MacファイルシステムHFS+ だった。

f:id:mgi:20161010132555p:plain

https://en.wikipedia.org/wiki/Comparison_of_file_systems によると、制限は以下の通り

File system Maximum filename length Allowable characters in directory entries Maximum pathname length Maximum file size Maximum volume size
HFS Plus 255 UTF-16 characters Any valid Unicode Unlimited slightly less than 8 EiB slightly less than 8 EiB
# 制御文字のファイル名は作れる
`touch "\a"`
=> ""

# null 文字はダメだった
`touch "\x0"`
#  ArgumentError: string contains null byte
#        from (irb):1:in ``'
#        from (irb):1
#        from /Users/argerich/.rbenv/versions/2.3.0/bin/irb:11:in `<main>'

# 256 byte は長すぎる
`touch #{"a" * 256}`
touch: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: File name too long

# 255 byte なら OK
`touch #{"a" * 255}`
=> ""

# 日本語で 255 文字はアウト
`touch #{"" * 255}`
touch: あああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああ: File name too long

>> "".bytesize
=> 3

# 3 * 85 = 255 なので、85 文字まで OK
>> `touch #{"" * 85}`
=> ""

# 86 文字目からアウト
>> `touch #{"" * 85 + "a"}`
touch: あああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああa: File name too long

# UTF-16 が OK ならば、絵文字でもいけるはず。
`touch #{"\u{26C4}" * 85}`
=> ""

f:id:mgi:20161010132646p:plain

絵文字ファイル名で作れているっぽい。ls すると ? になるけど。

f:id:mgi:20161010132710p:plain

それにしてもWikipedia 先生。ファイル名の長さ、255 byte までなのでは?
Any valid Unicode の中に null 文字は入ってないのかな?

一部の文字は互換性維持のため、OS の API で制御しているとの噂もあるけど… うーむ。
情報が少なすぎて、これ以上調べるのは難しそうだった。一次情報どこにあるの。

試してみたものの、Wikipedia を引用してまとめたので、すごい微妙になってきた…
この記事は用法用量を守って、正しくお使いください。

参考