PSequelとTeamSQLがお亡くなりなので、おれはTablePlusを使うことにするぜ

IntelliJ IDEA Database Tool

ここ一年くらいは IntelliJ 組み込みのデータベースクライアントでことを済まして来ました。対応するデータベースプロダクトも多いですし、専用のクライアントツールに引けを取らない機能は素晴らしいです。

qiita.com

その一方で、兼ねてより以下のような不満がありました。

  • データベースへの接続情報等をプロジェクトごとに持ってしまうので、複数プロジェクトにまたがる際に扱いづらい。
  • そう思って接続情報を保存するためだけのプロジェクトを作ってみたものの、ウィンドウが増えてくると Cmd + Tab の切り替えが面倒。
  • だからと言って All Product Pack や DataGrid 単体で買うのはコスパが悪い気がする。

TeamSQL

そう思って最初に検討したのが TeamSQL です。せっかくならモダンな UI で新しいプロダクトに乗り換えたいと思ったからです。

qiita.comwww.virment.com

意気揚々とアプリケーションのダウンロードのためにオフィシャルサイトを訪れた所、目に飛び込んできたのはメンテ終了のお知らせでした・・・。

TeamSQL has retired and is not available for download anymore.

PSequel

そう思って次に検討したのが PSequel です。IDEA 以前に使っていたツールになるので出戻りになります。PosgreSQL 専用のツールにはなりますし、決してモダンなツールではないものの、慣れた機能が使えれば十分かと思った次第です。

しかし、こんなブログ記事が。👀

tableplus.io

ほんまかいな、と思ってさらに探ってみたところ、メンテ状況に関して言及するコメントのついた Issue がいくつも放置されていることがわかりました。

github.comgithub.com

ああ、これはもう実質終了してますね・・。
もともとオープンソースでないプロダクトなので、作者の方が動いてくれない限りどうしようもないようです。

※とはいえ、無償でこういったプロダクトを提供し続けて頂いたことには感謝の気持ちしかないですね・・!:clap:

TablePlus

というわけで最終的に落ち着いたのが TablePlus です。

tableplus.io

正直はじめはピンと来ていなかったのですが、一般に提供される機能は揃っていますし、UI もモダンで更新状況も活発なので素晴らしいです。

ライセンス料金ですが 1台分が 1年間の更新付きで 59$ です。フリートライアルもありますし、1ヶ月あたりに換算すれば 5$程度なので、満足して購入する分にはぜんぜん払える額ですね!

補足

その他調べた中で得た知見として、2点補足します。

まず、DB クライアント GUI アプリを選ぶポイントとしては以下の6点です。

  • サポートするプラットフォーム:Windows ? MacOS ? Linux ? (or mobile ?)
  • サポートするDB:MySQL ? PostgreSQL ? SQLServer ? NoSQL ?
  • UI のモダンさ:気にならなければ大丈夫ですが古いアプリも多いので。
  • メンテ状況:アプリ自体は多いのですがその分停止してるものも多い印象でした。
  • 有償・無償:IDE などと違って有償でも高価なものは少ないです。
  • クライアント機能:スキーマのブラウズ機能やフィルタ機能といった一般的なものだけでなく、リードオンリーモードや ER 図のエクスポート機能など、どうしても欲しい特定の機能がある場合は要チェックです。

あと、私は一旦 TablePlus に落ち着きましたが DBeaver もかなり良さげでした。あまり触れていないので言及こそしませんでしたが、TablePlus よりサポートする DB が多いですし、無償で提供されているので使いやすいです。

qiita.comあと、ER 図のエクスポート機能もありますね。TablePlus でサポートされていない DB を今後扱うことになったり、サポートされていない機能が使いたくなることも出てくると思うので、二つを併用していけば良いかと考えてます。

ghq list の vcs オプションとその有効な使い方

f:id:msh5_h:20190515230652p:plain

先日 ghqv0.11.1 がリリースされました! このバージョンより ghq list コマンドに --vcs オプションが追加されています。🎉

$ ghq list --help
NAME:
    list - List local repositories

USAGE:
    ghq list [-p] [-e] [<query>]

(省略)

OPTIONS:
    --exact, -e      Perform an exact match
    --vcs value      Specify VCS backend for matching
    --full-path, -p  Print full paths
    --unique         Print unique subpaths

この機能は私が1年ほど前に提案したもので、最近になって開発者の方からフィードバックを頂いたことを受けて、PR を作成して master に取り込んで頂いたという流れになります。💪

Feature request: specify vcs argument on `ghq list` · Issue #94 · motemen/ghq · GitHub

例えば --vcs git といったように指定すると、Git の作業ディレクトリのみをリストアップすることができます。あなたが過去に SubVersionMercurial のプロジェクトを ghq get したことがあっても、その実行結果には含まれません。

vcs オプションの value として使用可能なキーワードは、README のこの辺りやソースコードのこの辺りを見ればいいんじゃないかと思います。

Accepted values are "git", "github" (an alias for "git"), "subversion", "svn" (an alias for "subversion"), "git-svn", "mercurial", "hg" (an alias for "mercurial"), and "darcs".

GitHub - motemen/ghq: Remote repository management made easy

var vcsRegistry = map[string]*VCSBackend{
    "git":        GitBackend,
    "github":     GitBackend,
    "svn":        SubversionBackend,
    "subversion": SubversionBackend,
    "git-svn":    GitsvnBackend,
    "hg":         MercurialBackend,
    "mercurial":  MercurialBackend,
    "darcs":      DarcsBackend,
    "fossil":     FossilBackend,
    "bzr":        BazaarBackend,
    "bazaar":     BazaarBackend,
}

ghq/vcs.go at v0.11.1 · motemen/ghq · GitHub

私はこの機能を使って、ローカルの全 Git リポジトリに対して git gc をかけてます。 この処理はワンライナーでこう書けます。

ghq list --full-path --vcs=git | xargs -I{} sh -c 'echo {}; git -C {} gc --aggressive'

これを cron に仕込むなどして日次実行するようにしておけば、ヒストリーの長いプロジェクトだったりブランチを多数抱えていても、最適化がかかってディスクサイズが減らせますし、Git 実行時に不定期で始まる GC もオフにできます。😎

※ 私はこういった意図しない処理が走って時間がかかる(Homebrew の自動アップデートとか)のが苦手なので、どちらかと言えば後者の目的でやってますね!

git config gc.auto 0

read コマンドはバックスラッシュをそのまま変数にセットしないので注意が必要

とある非業務なシェルスクリプトの編集中に、ShellCheck が見慣れないエラーを吐いていることに気づいた。🤔

f:id:msh5_h:20190506230808p:plain

help コマンドで -r オプションの意味を調べてみる。
read は bash のビルトインコマンドなので、man ではなく help を使うのが正しい。

$ help read
read: read [-ers] [-u fd] [-t timeout] [-p prompt] [-a array] [-n nchars] [-d delim] [name ...]
    ...(省略)
    If the -r option is given, this signifies `raw' input, and
    backslash escaping is disabled. 
    ...(省略)

この説明だけだともう一つピンと来ないので、さらにググって調べてみる。

※-rオプションを指定しなかった場合、入力の末尾にあるバックスラッシュは行の継続と見なされる。バックスラッシュを入力するにはそれ自体をバックスラッシュでエスケープする必要がある。
readコマンドについてメモ | OpenGroove

どうやら read コマンドはバックスラッシュをメタキャラクタの一つとして扱っていて、特に末尾に置かれる場合には行継続として扱われる仕様とのこと。確かにバックスラッシュを標準入力として与えてみても、セットされる文字列にはバックスラッシュとして含まれない。

# バックスラッシュが先頭にあるケース(無視される)
$ read test
\foobar
$ echo $test
foobar

# バックスラッシュが途中にあるケース(無視される)
$ read test
foo\bar
$ echo $test
foobar

# バックスラッシュが末尾にあるケース(行継続される)
$ read test
foobar\
> baz 
$ echo $test
foobarbaz

# バックスラッシュが2文字連続するケース(エスケープされる)
$ read test
foo\\bar
$ echo $test
foo\bar

そこで r オプションを与えると、バックスラッシュもセットされる文字列に含まれるようになる。

$ read -r test
\foobar
$ echo $test
\foobar

$ read -r test
foo\bar
$ echo $test
foo\bar

$ read -r test
foobar\
$ echo $test
foobar\

今回 ShellCheck で引っかかったのは、シェルスクリプト中でパスワードの入力を求める箇所だった。ここに関してはバックスラッシュを入力として与えたときこれがセットされないのは不適切なので、r オプションをつけるように修正した(つまり ShellCheck は今回も神でした)👍

SQLite3 のオンラインバックアップ

どのような点で優れているか?

SQLite ロック機構に則ってデータベースの複製が行われるので、オンラインのデータベースに対して安全に処理を実行できる。 例えば、単にバックアップを取るだけならファイルコピーでも可能だが、もしデータベースが書き込み中であった場合、バックアップは壊れている恐れがある。 ちなみに、オンラインバックアップの対象はデータベース単位である。 特定のテーブルのみ取り出したいといった場合は、ユースケースに合わないので愚直に ATTACH と INSERT クエリでコピーすべし。

実施手順

オンラインバックアップの実施は非常に簡単で、sqlite3 コマンドで .backup というメタコマンドを実行する。

以下に実行例を示す。 ここでは、sample.db という SQLite データベースのバックアップを sample.db.backup として取っている。 ちなみに、sample.db.backup というファイルが既に存在する場合、上書きされてしまうので注意。

$ sqlite3 sample.db "SELECT * FROM members;"
1|Smith
2|Alisa
$ sqlite3 sample.db ".backup sample.db.backup"
$ sqlite3 sample.db.backup "SELECT * FROM members;"
1|Smith
2|Alisa

また、バックアップ元およびバックアップ先としてカレントディレクトリ以外を指定したい場合には、 絶対パスもしくは相対パスを用いて指定すればよい。

$ sqlite3 sample.db ".backup /var/tmp/sample.db.backup"
$ sqlite3 sample.db.backup "SELECT * FROM members;"
1|Smith
2|Alisa

ちなみに、アタッチしているデータベースのバックアップを取りたい場合は、 コマンドの第1引数に、バックアップを取りたいデータベース名を指定する。

$ sqlite3
sqlite> ATTACH DATABASE "sample.db" as sample;
sqlite> .backup sample sample.db.backup
sqlite> .quit

$ sqlite3 sample.db.backup "SELECT * FROM members;"
1|Smith
2|Alisa

インメモリデータベースの書き出し

オンラインバックアック機能を使えば、インメモリデータベースの書き出すことが可能である。

$ sqlite3
SQLite version 3.7.17 2013-05-20 00:56:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> CREATE TABLE members (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);
sqlite> INSERT INTO members (name) VALUES ('Smith');
sqlite> INSERT INTO members (name) VALUES ('Alisa');
sqlite> SELECT * FROM members;
1|Smith
2|Alisa
sqlite> .backup sample.db
sqlite> .quit

$ sqlite3 sample.db 
SQLite version 3.7.17 2013-05-20 00:56:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> SELECT * FROM members;
1|Smith
2|Alisa

インメモリデータベースへの読み込み

.backup コマンドと逆の操作を実施する .restore コマンドがある。 これを利用すれば、ファイルに書き出したデータベースをインメモリデータベースとして読み込むことが可能である。 ちなみに、この操作を実施した時点で既にインメモリ上にデータベースが開かれていた場合、 そのデータベースは上書きされて消えてしまうので非常に注意。

$ sqlite3
SQLite version 3.7.17 2013-05-20 00:56:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .restore sample.db
sqlite> SELECT * FROM members;
1|Smith
2|Alisa

ライブラリによるサポート

オンラインバックアップは、C ライブラリから利用することも可能である。 詳細は割愛するが、C ライブラリではより細かくバックアップ挙動を指定することが可能である。 ちなみに、Python の sqlite3 モジュールは未対応。

参考

Web 上で公開されているシェルスクリプトをローカルで直接実行したい

Gist 上で自分で公開しているシェルスクリプトを直接実行したかった。

パターン1: 単に実行したい

curl -sSL {URL} | sh

curl でシェルスクリプトを取得しつつ、パイプ経由で sh に流している。もっとも簡単かつポピュラーなパターンだと思う。

curl のフラグ指定は -sSL としていて、今回のユースケースにおいて最も汎用性の高い指定を選んでいるつもり。

フラグ 機能
-sS 出力を抑える。ただしエラーの時は出力を出す。
-L HTTP リダイレクトに従う。

パターン2: オプションをつけて実行したい

実は、パターン1 の方法ではオプションを渡した実行ができない。 なのでシェルスクリプトがオプションを取らない場合は良いが、シェルスクリプトがオプションを取る場合は使えない。 対処法としては、sh に第1引数に /dev/stdin を指定すれば良い。*1

curl -sSL {URL} | sh /dev/stdin foo bar

補足: Gist 上のシェルスクリプトの実行

Gist 上にアップロードしたシェルスクリプトを実行するには、シェルスクリプトを raw で得る必要がある。 Gist の UI 上で Raw ボタンを押すと、こういった URL に飛ぶ。

https://gist.githubusercontent.com/msh5/ad8c7e0af6134c0ec5c1d918a4544188/raw/077c817418717406779fa8aad992eda83e1bc4b7/mk-apache20-license.sh

// 省略版
https://gist.githubusercontent.com/{ユーザ名}/{ハッシュ A}/raw/{ハッシュ B}/{ファイル名}

注意したいのは、ハッシュ B の方は Commit ハッシュなので、この URL では最新の変更に追従できないこと。 例えば Gist 上で変更を行っても、変更が反映されていない元のファイルにアクセスしてしまう。 これが意図する動作であればそれに越したことはないが、多くの場合は最新のシェルスクリプトにアクセスしたいのではなかろうか。 実はハッシュ B の部分を消してしまえば、最新の変更に追従できるようにできる。

// 最新の変更に追従してくれる URL
https://gist.githubusercontent.com/{ユーザ名}/{ハッシュ A}/raw/{ファイル名}

補足: この方法の危険性

「悪意のある人によって公開されているものは危ないよね」という話と、 「ネットワークが不安定で中途半端なスクリプトが実行されてしまうと意図しない挙動になるよね」という話。 アドホック性の高い分こういう危険性もあるので、安全なところに置いてあるスクリプトにだけ使ってくれたまへ。

rcmdnk.com

*1:sh はオプション指定が何もないときは空気を読んで /dev/stdin から読んでくれるが、 オプション指定があるとそれが機能しないため、シェルスクリプトの実行さえままならなくなるようだ。

はじめての Haskell

Haskell をちょくちょく触ってみていて、メモが溜まってきたのでこれをまとめた。

(2018-07-19)「Haskell の学び」を変更・追記した。

Haskell とは?

www.haskell.org

  • 関数型言語
  • 「ハスケル・カリー」さんから名前をとっている(だけど開発者は全く別)
  • 最新の言語仕様は Haskell 2010
  • モナドで有名だけど圏論の知識は必須というわけではない(らしい)

Haskell の学び

チュートリアルのオススメはこの二つ。

モナドの説明など概念的な説明は日本語の方が嬉しいと思うが、それにはここがわかりやすい。 基本的に「日本語でおk」な説明のサイトが多いので、読んでいて意味不明だったらすぐに違うソースを探した方がいい。

www.shido.info

入門書籍はいまのところ使っていないが、この辺りがテッパンらしい。 ちなみに、2番目の「すごいHaskellたのしく学ぼう!」は上で紹介した「Learn You a Haskell for Great Good!」の邦訳版である。 個人的に3番目を読んでみたいのだが、電子書籍版がないため躊躇している。

プログラミングHaskell

プログラミングHaskell

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

  • 作者: 青木峰郎,山下伸夫
  • 出版社/メーカー: SBクリエイティブ
  • 発売日: 2014/10/05
  • メディア: オンデマンド (ペーパーバック)
  • この商品を含むブログを見る

インストール

macOS 環境なら Haskell Platform の利用が推奨されている。 Haskell Platform には以下のツールが入っていて、これをインストールするだけですぐに Haskelll 開発が始められる。

  • GHC(コンパイラ)
  • Cabal(パッケージ管理)
  • Stack(ビルド自動化)
  • プロファイリング、カバレッジ測定ツール
  • 35 のコアパッケージとその他メジャーなもの

Haskell Platform は Homebrew-Cask 経由で簡単にインストールできる。

brew cask install haskell-platform

エコシステム

エコシステム 概要 他の言語での対応
GHC デファクトスタンダートなコンパイラ実装。 gcc (C)
Hackage パッケージリポジトリ。 PyPI (Python)
Cabal パッケージ管理ツール。基本的には Hacakge から取ってくる。 pip (Python), npm (Node.js)
Stackage Stable Hackage の略。依存関係でエラーがでないように調整されたパッケージセットのこと。 該当なし(他の言語ではパッケージ開発者が決めている)
Stack ビルド管理ツール。 Gradle (Java), Bundler (Ruby)
HLint コードスタイルチェックツール。 pylint (Python)
Hoogle リファレンス検索サービス。記号類も検索しやすい。 該当なし

開発エディタ

github.com

Language Server 実装の一つで VSCode などで使える。 VSCode の Market Place で検索すれば Haskell Syntax Highlighting に次いで一番上に出てくるので、普通に見れば間違えることはないかと。 Language Server 実装はいくつかあるものの、これが一番実用レベルでよろしい。(参考

plugins.jetbrains.com

IntelliJ の Haskell サポート用プラグインがある。
IDEA でインストールできることを確認している。

gcovr を動かしてみる

インストール

gcovr は Python で実装されていて、PyPI で配布されている。

# pip install gcovr

実行手順

  1. カバレッジ測定を行うプログラムのソースを -fprofile-arcs -ftest-coverage-lgcov パラメータをつけて gcc コンパイルする
  2. .gcda, .gcno ファイルが生成されていることを確認する
  3. 1 でコンパイルして出来たプログラムを実行して、.gcda ファイルが更新されることを確認する
  4. 結果を出力する
# gcovr -r .
------------------------------------------------------------------------------
                           GCC Code Coverage Report
Directory: .
------------------------------------------------------------------------------
File                                       Lines    Exec  Cover   Missing
------------------------------------------------------------------------------
hoge.c                                        42      36    85%   51,55,102-103,107-108
------------------------------------------------------------------------------
TOTAL                                         42      36    85%
------------------------------------------------------------------------------

参考

fzf / peco でカレントディレクトリ以下のファイルを選択して消す

# fzf だとこんな感じ
ls -f | fzf -m | xargs rm
# Tab もしくは Shift+Tab で選択して、Enter で決定

fzf はデフォルトで複数指定できない仕様なので、複数指定用のオプション -m を与えて有効化しているのがポイント。

# peco だとこんな感じ
ls -f | peco | xargs rm
# Ctrl+Space で選択して、Enter で決定

peco はデフォルトで複数指定できるのでその点は気にしなくて良いが、キーマップが厄介。 選択指定に Ctrl+Space というかぶりやすいキーアサインがされているので、他のキーアサインとかぶっている場合はマッピングを替えてやる必要がある。

ちなみに、ワイルドカード展開が効かないので、一気に消すにはおもったより便利じゃなかった。 一方で、壊れたファイル名で出来ちゃったファイル(名前指定が難しい)を消すのには便利。

Pipenv スクリプトを書く

名称として合っているのかはわからないが、npm スクリプト的なことを Pipenv でやるための方法について。

方法は wiki に書いてある

Home · pypa/pipenv Wiki

Pipenv 的には隠し機能らしく、Pipenv の wiki の Hidden Features に記載されている。 どうして隠しているのかは定かではないが、とにかくここに書かれているようにすればできる。 Pipfile の scripts セクションに、スクリプト名をキーにしてコマンドを記述する。 コマンドはきちんとセミコロンで囲った方が確実なので、そのようにするのがオススメである。

[scripts]
reverse = "pyreverse -o png -p ...

実践してみる

pipenv run スクリプト名 で実行する。 ここではスクリプト名を reverse としたので、そのように指定する。

$ pipenv run reverse
...
(コマンド実行)

なんてことはなかった。

資格取得に取り組んでみようとおもう

どうしてそういう気持ちになったか?

転職活動をして気になるのが、自分の能力が正しく伝わっているかということです。 準備として職務経歴書を作って提出し、面談では自身のキャリアを口頭で伝えるわけですが、 頑張ってアピールしているつもりでも担当者の方のリアクションが非常に涼しかったり、 逆にじぶんのレベルよりも遥かに低い質問があったりします。

自分の営業能力の低さもあいまって、そういった質問が来てしまうと、 「職務経歴書をどの程度読んでくれているのか?」 「一通り目を通して頂いているとして、自分の文章がどう受け取られ、その結果どういった評価をされているか?」 といったことが気になってくるのです。 誰かにレビューを頼むわけにもいかないので、どうしたものかと考えるわけですが、 そこでふと、資格でもとってみようかと思いました。

資格なんてとっても無駄じゃない?

職業能力というのは二つの要素によって構成されていると思います。 一つ目は知識、二つ目は業務経験です。 資格で証明可能なのは知識だけなので、そういった意味では十分ではないと思います。 しかし、先の体験を通じて、職業能力の一部を証明できるだけでも価値が高いと考えました。

あと、自分がレベルの高い資格を持ってる人に対してスゴいとおもっていることに気づきました。 といっても、自分の近くに仕事がデキてかつレベルの高い資格を持っている人がいるというわけではないのですが、 想像で「プロマネとネスペを持っていて TOEIC 900 点以上」みたいな人がいたら、 「この人スゲェな・・・」と思うと思います。 なので、自分もそういったレベルの高い資格の取得に取り組むことで、スゴい存在を目指そうと思いました。

どういった資格の取得を目指すの?

最初に目指そうとおもったのは、プロマネネスペです。 国家資格という点でテッパンだろうというのと、知名度も難易度も十分なものということでこれを選びました。 ネスぺは今年10月、プロマネは来年4月の試験での取得を目指そうと思います。

ただ、この二つはたった今から資格取得を目指すには試験までの期間が長く、 このままではモチベーションを持て余してしまいます。 そこで、LPICAWS 認定試験 から受験することにしました。 民間資格は受験システムが柔軟で、予約さえできれば任意の日時に受験が可能です。 というわけで早速、一番レベルの低いものを再来週に受験してこようと思います。 ついでに言うと、この二つはおなじ受験会場を選択できたので、1日に連続で二つの試験を受験するように予約できました。 ちなみに、どうしてこの二つを選んだかと言うと、 もともと Linux は業務で慣れているので LPIC を取得することでその経験値を証明できると思ったためです。 AWS 認定の方は、逆に AWS は業務での経験はまったくないですが、利用経験を問われることが非常に多く、 AWS の社会的需要の高さを感じてこの機会に勉強してみようと思ったためです。

その他に、IT 関連では JSTQB、それ以外では TOEIC 900 点以上を目指そうと考えています。 JSTQB は、ソフトウェアテスト技術者の能力を判定するための資格です。 業務で最近ソフトウェアテストに関して体系的に調査する機会があったのですが、 ソフトウェアテスト自体に面白さに感じたので、 これを契機に資格もとりたいと思った次第です。 TOEIC は 4 年ほど前に受験して以来受験していません。 当時のスコアは 650 点で、ここからスコアを上げるには多くの勉強時間が必要なイメージがあったのと、 正直言ってここからスコアを上げていける自信がなかったので、半ば諦めて距離を置いていました。 リスニングが非常に苦手でここでスコアがとれていないという感じなので、 ここを頑張って強化できれば十分900点を取れる可能性もあると信じ、 とりあえずスコアが上がる手応えを得るために 100点アップから目指してやってみようと思います。

runc を動かしてみる

次の業務で runc が使えそうだったので、使用感を得るために試してみた。

※ runc について一部勘違いしていたので、公開時の記載より該当箇所を修正しております。ブコメにてご指摘頂いた @mapk0y さん、ありがとうございました。

runc とは?

github.com

runc とは、OCI に準拠したコンテナランタイムである。 コンテナと聞いて一番最初に思い浮かぶのは Docker だが、Docker も containerd 経由で runc を呼んでいる。

runc は、ワンタイム実行可能なコマンドラインプログラムとして実装されている。 runc はシェルコマンド経由で実行可能で、コンテナは runc のサブプロセスとして動作する。 そのため、コンテナを動かすための準備も不要だし、Docker で指摘されることが多いセキュリティ面での問題も runc レベルでは該当しない。

runcとは、OCI に準拠したコンテナランタイムである。 コンテナと聞いて一番最初に思い浮かぶのは Docker だが、Docker は一つの統合プラットフォームである。 比較するのであれば、Docker の標準ライタイムとして採用されている containerd と比較するのがレイヤ的に正しい

containerd との違いは、デーモンプログラムでない点である。 Docker では、コンテナランタイムとして containerd をデーモンプロセスとして走らせ、そのサブプロセスとしてコンテナを動作する。 一方の runc は、ワンタイム実行可能なコマンドラインプログラムとして実装されている。 runc はシェルコマンド経由で実行可能で、コンテナは runc のサブプロセスとして動作する。 そのため、コンテナを動かすための準備も不要だし、Docker で指摘されることが多いセキュリティ面での問題も該当しない。

ちなみに、rkt も runc と同じくワンタイム実行可能なコンテナランタイムとして知られているが、 rkt はランタイム機能に加えて、コンテナのライフスタイル管理やイメージ管理といった統合プラットフォームとしての機能も備えている点で違っている。

runc でコンテナを動かすにはどうするか?

ざっと以下の手順をとる。

  • runc バイナリのビルド
  • イメージの準備
  • config.json の作成
  • コンテナの実行

runc コミュニティは生成済みのバイナリを配布していないので、自分でビルドする必要がある。 自分でビルドすると聞くと「コンパイルエラーが出たらどうしよう」と身構えてしまうが、 go build の1行を実行するだけなので大したことはない。 ちなみに、libseccomp がシステムにインストールされていないとエラーがでるが、 特に seccomp の統合機能が必要なければ、単にこの機能を外してしまえばよい。 この機能を外すには、環境変数として BUILDTAGS を空文字列で渡すことで実現できる。

runc ではコンテナイメージとして、ファイルシステムのルートとしたいディレクトリを指定する。 特定の Docker イメージを runc で動かしたい場合は、export して tar 展開すればよい。 ディレクトリパスはデフォルトで ./rootfs だが、後述する config.json で任意のパスを指定することもできる。

// busybox イメージを変換する例
docker export $(docker create busybox) | tar -C rootfs -xvf -

config.json は、イメージのメタデータを指定するためのものである。 runc spec コマンドでデフォルト設定のものを生成できるので、そこから始めて必要な設定だけ変更するようにすればよい。 ちなみに Docker と違って、環境変数の指定やコマンドパラメータの指定も config.json で行う。 コンテナ実行時にアドホックに渡すようなことは出来ないので気をつけること。 若干不便なので、頻繁に使うようであればラップしたコマンドでサポートするなど、なんとかした方がいい気がする。

systemd との連携

systemd をスーパバイザとして使う方法がよく紹介されるので、これについて補足する。 Docker や rkt と違って、runc 単体ではライフサイクルが管理できず機能不足である。 そこで systemd サービスの一つとして runc を実行するコマンドを用意しておくことで、 任意のコンテナアプリをサービスとして振る舞わせることができる。

Windows 仕様の Zip ファイルに含まれる各ファイルのファイル名を MacOS / Linux 環境で文字化けなしで確認する

Zip は仕様として、ファイル名のエンコーディング形式を指定する方法が用意されていないので、ASCII 以外では安全にファイル名を指定できない。 一方で、日本語を始めとする 非 ASCII 文字を含んだ名前のファイルというものはよくよくあって、実際には何らかのエンコーディング形式でもってファイル名を指定する。 このために一般的に使用されているエンコーディング形式は2つで、UTF-8(MacOS / Linux)と Shift_JIS (Windows)である。 ただ、MacOS / Linux と Windows 間でエンコーディング形式が違っているので、この OS 間で Zip ファイルの受け渡しを行うとファイル名の文字化けが頻発する。

Windows 以外の OS を常用している人なら誰しも経験したことがあるはずだが、ファイル名が文字化けしてしまうのは控えめに言って不便である。 なにか解決方法がないかと思い調べてみると、Windows 仕様の Zip 形式に対応するためのパッチが unzip コマンドで用意されているようだった。 それを使ってみても良かったのだけど、ファイル名をのぞくだけならサクッと Python で書けるだろうとおもったので5分で作ってみた。

gist.github.com

Gist に投稿した Python スクリプトでも、このように curl 経由で直接実行できる。

$ curl -sS https://gist.githubusercontent.com/msh5/cda7439eb1de26bc8e643b0372edb89b/raw/list_winzipped_filenames.py | python - ~/Downloads/xxx.zip
○×契約書.pdf
△□提案書.pdf

はい、便利!

Pipenv を使ってみたら最高だった

最近 Qiita でバズってるこの記事に影響されて Pipenv を使い始めているんだけど、 「おれたちが待ってたのはコレだ」感をスゴく感じられてオススメ。

github.com

特徴は以下の三つ。

  • pyenv + virtualenv + pip のスイートツール
  • bundler, npm ライクな依存管理
  • Python.org 推奨

プロジェクトを始めるときに pyenv, virtualenv, pip を機械的に叩かないといけなかったりだとか、 requirements.txt の依存関係管理が弱かったりだとか、 Pipenv を使うことでそういった問題をまとめて解決できる。

インストールは pip install pipenv すれば良い。 Homebrew にも対応しているので、MacOS なら brew install pipenv でもOK。

pip install pipenv

# Mac OS
brew install pipenv

pipenv をプロジェクトで始めるには、まず使用するインタプリタを決定する。 指定可能なインタプリタは pyenv install --list で確認できる。 ちなみに、新しいバージョンのインタプリタが出てこない場合は、 brew upgrade pyenv もしくは pyenv update すると、 ローカルの pyenv リポジトリが更新されて見えるようになる。

$ pipenv --python 3.6.5
Creating a virtualenv for this project…
Using /usr/local/bin/python3.6m (3.6.5) to create virtualenv…
...
Creating a Pipfile for this project…

$ pipenv run python --version
Python 3.6.5

pipenv --python <インタプリタ名> を実行すると、以下の3つの処理が内部的に行われている。

  • 利用するインタプリタのインストール(未インストールであれば):= pyenv install <インタプリタ名>
  • 利用するインタプリタの設定 := pyenv local <インタプリタ名>
  • 利用する仮想環境の作成と設定 := virtualenv <環境名> && source <環境名>/bin/activate

なので、指定するインタプリタの仮想環境が一発で作れるというわけ。 さらに、もうひとつの処理が内部的に行われている。

  • Pipfile の作成

作成される Pipfile はデフォルトでこういったものである。

[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]

[dev-packages]

[requires]
python_version = "3.6"

source は、pip install 時に参照する PyPI リポジトリの指定となっている。 公式の PyPI リポジトリ以外を使う人はあまりいないはずなので、基本的にここを触る必要はない。

requires はインタプリタの指定、packages, dev-packages は依存パッケージの指定である。 依存パッケージをインストールすると、自動的に packages, dev-packages に足される。 依存パッケージのインストールは pipenv install で行う。

$ pipenv install requests
Installing requests…
...

$ cat Pipfile
...
[packages]
requests = "*"
...

バージョンを指定していないので * になっているが、そこまで気にしなくて良い。 なぜなら、おもむろに生成されている Pipfile.lock 内でインストールされた厳密なバージョンが記録されているし、 他のマシンでおなじ環境を再現するときに参照されるのも Pipfile.lock の方だからである。

$ cat Pipfile.lock
...
        "requests": {
            "hashes": [
                "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b",
                "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e"
            ],
            "index": "pypi",
            "version": "==2.18.4"
        },
...

そして、他のマシン上で同じ環境を再現するために、Pipfile と Pipfile.lock はバージョン管理しておく。 他のマシン上で同じ環境を再現するには、リポジトリを clone した上で pipenv sync を実行する。

$ pipenv sync
Creating a virtualenv for this project…
...

エディタの話を付け加えておくと、VisualStudio Code が既に Pipenv をサポート済みである。 VSCode は内部的に pylint や isort といったコマンドを実行するが、 アプリを立ち上げると Pipenv を自動で検出して、 こういったコマンド類を仮想環境にインストールされたものから使ってくれる。 さらに、VSCode はコマンド類が見つからなかった場合、 (ユーザに対して確認を求めた上で)インストールコマンドを実行するが、 このコマンド類のインストールについても仮想環境に対して行ってくれる。 なので、VSCode ユーザにとっては至れり尽くせりといったところである。