うなすけとあれこれ

2019年05月11日

Itamae v1.10.4 をリリースしました

v1.10.3からのdiffはこちらです。

https://github.com/itamae-kitchen/itamae/compare/v1.10.3…v1.10.4

Changelogs

Suppress Ruby warnings

RUBYOPT=-w を指定したときに出る警告を修正するもの。

Run test cases correctly by pocke · Pull Request #289 · itamae-kitchen/itamae

これは v1.10.3 にmergeされている Add integration test with itamae local command で、test filesを列挙する正規表現が誤っていた問題を修正するもの。 これで 0 examples139 examples になりました。よかったですね。

このあたりはpull req authorのpockeさんがブログに書いてくれていますね。メンテナとして気付けなかった僕達のミスでもあります。ありがとうございます。

Itamaeのテストを壊してしまっていた話 - pockestrap

Refine itamae docker‘s created message by pocke · Pull Request #288 · itamae-kitchen/itamae

itamae docker コマンドに --tag オプションを指定した場合、成功したときのmessageにtagも表示するようにしたもの。

Add description to –tag option of docker subcommand by pocke · Pull Request #286 · itamae-kitchen/itamae

itamae docker コマンドのhelp messageに --tag オプションの説明を足したもの。

2019年05月11日
2019年04月30日

高専DJ部 #22 でした

CDJ

セトリ

  1. Flying Out (Extended Mix) - M.I.K.E. Push
  2. Black Mirror (Extended Mix) - James Dymond
  3. Kick Back (Original Mix) - Ashley Smith
  4. Fight My Way feat. Moses Keenan (Extended Mix) - Morgan Page, Moses Keenan
  5. With You feat. Emelie Cyreus (Progressive Extended Mix) - Magnificence, Venomenal, Emelie Cyreus
  6. Never Say Never (Extended Mix) - SICK INDIVIDUALS
  7. Turn Back Time (Original Mix) (Original Mix) - 3D Nation
  8. Burn (Original Mix) - DallasK, KSHMR
  9. We Are the Sound (Original Mix) - Betsie Larkin

徐々にテンションが上っていく感じの曲順になるようにやってみましたが、総評としてはパリピでしたね。精進します。

遅刻

イベント自体は3月末にあったので公開がとても遅れてしまいました。

今回、高専DJ部の開催スケジュール的に平成最後の開催となるので、IMAP++とのコラボを持ちかけたらなんと実現してしまいました。 Webサイトもmazcoさんに特別にデザインしてもらったりと、本当に記念となる回にできたかなと思います。

また、 #unasukefmのために購入したZOOM H4n Proで録音したものを内々に共有したりもしました。冒頭のmixcloudはそこから切り出したものになります。

2019年04月30日
2019年04月22日

Conference Week in Fukuoka 2019

RubyKaigi 2019

4/15からの1週間、福岡のConference Weekで何をしてきたのかという日記です。

#unasukefm 収録

カンファレンス開始前の15日には、GMOペパボで @udzura さんを相手に #unasukefm の収録を行いました。

Haconiwaについて聴いてる #unasukefm pic.twitter.com/gp67dELrVQ

— カルパス (@yoshi_hirano) 2019年4月15日

最新エピソードを配信しました。RubyKaigi 2019のLocal Organizerである @udzura さんにRubyKaigiの舞台裏、福岡移住の話、Haconiwaについて聴いています。ぜひ聴いて下さい - https://t.co/9JjFL6dOBv #4 - #kaigieffect (@udzura) by Railsdm Podcast #rubykaigi2019https://t.co/kjOtdiYJUO

— Railsdm (@railsdm) 2019年4月22日

ハッシュタグ #unasukefmの様子を見るに、皆さんに楽しんでいただけたようでなによりです。

実はudzuraさんを相手に収録をしたいというのは、Railsdm PodcastがunasukefmとしてSeason 2を開始したときからほとんど決まっていたようなものなのです。達成でき、HaconiwaやCloudNativeの話ができてよかったです。

CloudNative Days Fukuoka 2019

そして1番目のカンファレンス、CloudNative Days Fukuoka 2019に参加しました。

ここではKubernetesだけに留まらない、CloudNativeな技術に関する様々なトークを聞くことができました。特に僕が今課題を感じている点である、秘匿情報の管理についてVaultを使うケースを知れたのはよかったです。

懇親会でも、福岡でしか会えない方々をはじめ、Rubyコミュニティとはまた違うCloudNativeなコミュニティの方々とお話しすることができました。

RubyKaigi 2019

次に、1日挟んで迎えたRubyKaigi 2019です。挟んだとは言ったものの、僕は今回(も)helperとしてお手伝いする立場なので、17日も1日中設営作業をしていました。

https://photos.app.goo.gl/svaNw2jvBtzusJ259 ← 様子を軽くまとめました。

今回はネットワーク斑としてケーブルの敷設、同時通訳レシーバーの受け渡し、スピーカーへの同時通訳打ち合わせ案内などを主に担当しました。英語が咄嗟に出てこない&聞きとれないのがまだまだ課題として感じられる3日間でした。

なるほどね pic.twitter.com/jqoAAe4vng

— HolyGrail / 蜘蛛糸まな🕸️@新人VTuber (@HolyGrail) 2019年4月18日

2019年 pic.twitter.com/CCBO4FeLDX

— Yuki AKAMATSU (@ukstudio) 2019年4月21日

今になっても、「ああ、良かったなぁ」というふんわりとした感想しか出てきません。

2019年04月22日
2019年03月24日

未読のGitHub notificationを定期的に通知させる

botの様子

participating notification

皆さんは https://github.com/notifications/participating を普段どれだけ見ていますか。僕はこの機能をよく使うのですが、集中していたりすると見に行くのを忘れて、コメントされているのに返事をしそこねてしまったまま長い時間経ってしまうということがしばしばありました。

要件

push型の通知であること

こっちからアクセスしに行かなくても、「未読がこれだけあるよ」と教えてほしいわけです。

即時的でないこと

とはいっても、コメントされて数秒で通知が来る、という即時性は求めていません。なぜなら、例えば社のリポジトリに関することであれば、GitHubのコメントやmergeをSlackに流しているからです。コメントの応酬はそっちで見れます。

https://slack.github.com/

ユースケースとしては、「出社してまず見る」とか、「集中してて気づかなかったけどあのpull reqにコメントついてるっぽい」だとか、そういうのを求めていました。

GitHub notification reminderをつくった

https://github.com/unasuke/github-notification-reminder

これをheroku schedulerで定期的に叩くことによって、このように通知させています。

botの様子

Deploy to herokuボタンを作ったので同様の問題にお困りの方はご活用ください。

困っていること

GitHubのREST API v3でNotificationsを取得するendpointとresponseは以下URLの通りです。

https://developer.github.com/v3/activity/notifications/#list-your-notifications

ここで、通知の対象であるissueやpull requestの情報を見ようとすると、subject.url がそれっぽいなということになります。しかしよく見ると、domainが api.github.com になっています。例としてdocumentに載っている https://api.github.com/repos/octokit/octokit.rb/issues/123 ですが、ここにweb browserからaccessすると、JSONが返ってきます。この中に、html_url として、human accessableなURLが入っています。

これ、しんどくないですか。いわゆるGraphQLが解決しようとした、RESTによるN+1の実例じゃないか!となりました。そしてGraphQL API v4にはまだNotification Objectは来ていないのですね。

この件、supportに投げたのですが、僕の英語力が未熟なのか、「 https://developer.github.com/v3/pulls/#get-a-single-pull-request を使うといいよ」と返事が来ました。そういうことなのでしょう。

ただ、get-a-single-pull-requestしようにも、responseの中にissueやpull requestのnumberが単体では含まれないので、二進も三進も。(このscriptではgsubでhtmlを組み立てています)

こういう書き捨てのscriptのreadmeを頑張ることについて

よしいっちょブログに書くか〜となり、そんならREADME.mdを整備しておかないと「映え」ないなとなって、heroku appならdeploy to herokuボタン欲しいよなといろいろとmeta dataをつくっていて、正直面倒なんです。

じゃあなんで書くかというと、codeにcommentを書くように、あとで見る自分のためなんですね。未来の自分が環境構築するときに困らないように、という目的があるのかなと思いました。

2019年03月24日
2019年02月15日

unasuke.fm が始まりました

unasukefm

経緯

一言で表わすと、「ノリと勢い」で始まりました。

音質改善の足しに、とKyashで投げ銭をしたのをきっかけに、あれよあれよと話が進んで、いつのまにかRailsdm podcast Season 2として unasuke.fm をやることになりました。

Railsdm Podcastに投げ銭したい

— うなすけ (@yu_suke1994) 2018年12月24日

2019年からの Railsdm Podcast は、某うなすけさんが聞き手で、毎回ゲストを変えてトークしていくという内容でリニューアルしていく方向です。実質 https://t.co/Yosts9pvuRです。 #railsdm

— カルパス (@yoshi_hirano) 2018年12月24日

Season 2 episode 1

まずepisode 1として、onkさんをゲストに収録を行いました。以下から聞くことができます。

ジングル

KORG Gadget for iOSで作成しました。カルパスさんから頂いたふんわりとしたイメージをもとに作ってみました。

上のはイントロで使ったshort verで、アウトロで使用したlong verは以下です。

機材

そもそも始めたばかりのpodcastでrebuild.fmレベルの環境・質を実現することは現実的ではありません。

Podcasting Guide 2017 – Tatsuhiko Miyagawa’s Blog

それでも、これまでの収録を聴いて「もっとよくできそうなんだけどな」という気持ちになっていたのも確かです。

今回の収録では、僕がそのとき持っていた機材だけで収録を行いました。それらを以下にまとめます。

録音編

録音にあたって、今回は以下の機材を使用しました。

これはさっちんさんに教えていただいたものです。

> https://t.co/pbAOT5yItE

マイクなら絶対これ。tsとかDiscordとかしてても設定ちゃんとすれば周りの音入らない。スピーカーでも全然OK
tsなら音楽流しながらでもボイスチャット出来る。

— 02/09 11:00 🍲 (@sachin21__) 2017年9月21日

購入してから自宅でのボイスチャットに使用していたものをそのまま持ち込んで録音しました。このマイクに関しては、普段の通話で問題になったことがなく(通話相手から何か言われたことがない、という意味)、それなりに安心して収録に臨めました。

収録ではマイクをUni(単一方向のみ集音)にして僕に向けていました。反対側にカルパスさんが居たのですが、カルパスさんの声が入ることがなく綺麗に音声を拾えていました。こちら側の音声についてはそれなりの質で収録できたのではないかなと思っています。

うなすけ君の編集後の音が全体的にかつてないほどクリアなので、クオリティ爆上がりしてる。それとともに今までスマンという気持ちがかなり溢れてる。

— カルパス (@yoshi_hirano) 2019年1月29日

今回onkさんが京都に居るという都合上、Hangout経由で収録しました。そのため、2人が同時に発声しているタイミングだとどちらか(どちらも?)の声が抑えられてしまい、何を話しているのか不明瞭な部分ができてしまいました。これはどうしようもないですね。

また今後ですが、既にある機材の他にもHolyGrailさんの環境を参考に、以下の機材を用意するつもりです。

ここ何ヶ月かかけていろいろ機材を揃えて環境はほぼ整ったので来年のなんらかの活動にご期待ください! pic.twitter.com/HzVtiqKrsk

— HolyGrail / 蜘蛛糸まな🕸️@新人VTuber (@HolyGrail) 2018年12月22日

機材に関してはキリがないので、あまり凝りすぎてもよくないでしょう。上記構成からさらに買い足していったり、上位機種への乗り換えなどはするつもりはありません。たぶんしないと思う。しないんじゃないかな。

自分はVTuberになるために機材に10万弱かけてきたがうなすけくんははたしてどうかな

— HolyGrail / 蜘蛛糸まな🕸️@新人VTuber (@HolyGrail) 2019年1月11日

機材構成に関しては、他のpodcastでの事例が探すと色々出てくるので、それらを参考にしてみるのもいいかもしれません。

podcastの運営に参加することになった結果、沼にはまっていっているように見えるかもしれませんが、オーディオインターフェースは既に所持していましたし、追加で欲しくなったのはPCMレコーダーくらいなものなので沼ではないです。

4月にどういう機材を手元に揃えているのかは誰にもわかりません。

ちょうど先日「Jonanは配信機材をRubyKaigiに持ち込んでいて現地から配信していた。ポッドキャストであれば映像も不要だしオーディオインターフェースとマイクだけで収録が可能なので実質ありとあらゆる場所で収録ができるようになるのでは?」という話をしたところなので期待ができそうですね。

— HolyGrail / 蜘蛛糸まな🕸️@新人VTuber (@HolyGrail) 2019年1月11日

編集編

録音した音声の編集は以下のような環境で行っています。

編集環境に関してはこれ以上のupdateはするつもりはありませんし、してもあまり意味がないかなと思っています。

ただ、マスタリングにあたって、低音域と高音域を削るイコライザーはかませましたが、ノイズ軽減などは今回はしていません。それについては改善の余地があると思います。

今後

今後お話ししてみたい方々をカルパスさんと話して十数人ほどリストアップしています。1年くらいは続くと思います。

#unasukefm の次回の配信は、来週 or 再来週の前半を予定しています。

— Railsdm (@railsdm) 2019年2月15日
2019年02月15日
2019年01月30日

[WIP]kibela2esa をつくりました

kibela esa migration params

HolyGrail/kibela2esa: Kibela 2 esa.io

りゆう

やっぱりWIPっていいですよね。

どういうことをしているのか

  1. exportされたmarkdownを読みこむ
  2. exportされた画像を読みこむ(pathと名前だけ持っておく)
  3. 画像をesaにアップロードして、S3のURLを持っておく
  4. markdown内の画像URLをesaのS3 URLに置換する
  5. frontmatterなどの処理や整形した結果のmarkdownをesaに投稿する

できること

(まだ)できないこと

たいへんだったこと

まとめ

esaが好きです。

HolyGrail/kibela2esa: Kibela 2 esa.io

2019年01月30日
2018年12月24日

OCI Image Format Specification v1.0.1を読んで

skopeo

まえおき

発端

めちゃくちゃ興味あり〼 https://t.co/gHfAV5rbEU

— うなすけ (@yu_suke1994) 2018年11月30日

頭の中には設計があるんですが、…
OCIの仕様を再確認するところから…

— Uchio KONDO 🔫 (@udzura) 2018年11月30日

とりあえずブログかキータ

— Uchio KONDO 🔫 (@udzura) 2018年11月30日

という訳で、書きました。

OCI Image とは

Open Container Initiativeによって定められた、コンテナイメージフォーマットの標準仕様です。

詳しくは Open Container Initiativeによるコンテナランタイムとコンテナイメージの最初の標準化作業が完了、「OCI v1.0」発表 - Publickey にて。

OCI Imageを触ってみる

現在、OCI ImageをdownloadできるDocker HubのようなWebサイトは知る限りありません。なので、自分でOCI Imageを作成する必要があります。

まずはDocker Imageから

まず、Docker Hubにて公開されているDocker imageをdownloadするところからです。以下のコマンドで、Docker Hubからダウンロードしたimageをtarballとして扱うことができるようになります。

$ docker image pull ruby:2.5.3-slim-stretch
$ docker save ruby:2.5.3-slim-stretch --output ruby_253_slim_stretch.tar

このtarを展開してみましょう。

$ tar -xf ruby_253_slim_stretch

すると、manifest.json というファイルができているので、見てみます。

[
  {
    "Config":"b1c1603e80c648f3ab902b0259ab846a7779d0780124bf9e417dd4b8c3cea296.json",
    "RepoTags":[
      "ruby:2.5.3-slim-stretch"
    ],
    "Layers":[
      "aeff88bcdbbd12ea45c023c45f97b870492092899651c811b2ef26ae7fdf3120/layer.tar",
      "c61a4dce9ddcebd63027d09811998052c9b2cdb3a379c297277cf755dfcf1420/layer.tar",
      "de2944e57fc93c2f354420cb36210fd1181687a990ffd7123600fdaecba3ee83/layer.tar",
      "49c3631e8651776127d66adb995e78af1e2cfc52b7a10a20df0d92d837258419/layer.tar",
      "eb50b8a8210f1b43ff1571598e66b694844b2dcf6fbaa0691e8af6b7c80dcaa7/layer.tar"
    ]
  }
]

なるほど、これはOCI image specを読むとわかるのですが、OCIに定められている形式のJSONではありませんね。

skopeo

ここで、containers/skopeo というtoolを使用して、Docker imageをOCI imageに変換してみます。

$ skopeo copy docker://ruby:2.5.3-slim-stretch oci:ruby-oci:latest

すると、 ruby-oci/index.json というファイルができているので、見てみます。

{
  "schemaVersion":2,
  "manifests":[
    {
      "mediaType":"application/vnd.oci.image.manifest.v1+json",
      "digest":"sha256:a3843587af4f3e838f3e1a10649631144d4dcf4391980b64f3b902d81048057c",
      "size":976,
      "annotations":{
        "org.opencontainers.image.ref.name":"latest"
      },
      "platform":{
        "architecture":"amd64",
        "os":"linux"
      }
    }
  ]
}

なるほど、これは OCI Image Specに定められている image manifest fileですね。

以下、OCI Image Format Specification v1.0.1

自分なりに理解しようと翻訳したもののメモになります。 正確性の保証はないです。誤訳とかあります。最後のほう力尽きてます。


Open Container Initiative Image Format Specification v1.0.1

Overviewで語られていること

high level image manifest にはcontentsとdependencies of the image including the content-addressable(連想?) identity of one of more file system layer changeset archives、展開すると最終的に実行可能なファイルシステムになる

image configuration にはapplication arguments, environmentsなどの情報

image indexには high level manifest list of manifests and discriptorsのpointが含まれる

それらのmanifestsは異なるimageの実装 ←プラットフォームや他の属性によって変化することができる

一度作成されたOCI imageは名前によって探索(discovered)、ダウンロード、hashによる検証、署名による信頼、OCI Runtime Bundleへの展開ができる

(No Title)

Understanding the specification

components of the specは以下を含む

optional featureとしてSignaturesやNamingが仕様に含まれるかもしれない。

OCI Image Media Types

HTTP responseのContent-Typeで上の値を返すなどのように、typeを返すなにかしらの方法を実装してもよい(MAY)、また実装はmedia typeとdigestを期待してよい? 実装は返却されたmedia typeを尊重する必要がある(SHOULD)

Compatibility Matrix

前方・後方互換を可能な限り維持する必要がある。

似た、または関連するmedia typeは以下

Relations

Image indexは複数のImage manifestを持つ。Image manifestとImage JSON(config)は1対1。Image manifestはLayerのtar archiveを複数持つ。 Discriptorは全ての参照を持つ。

OCI Content Discriptors

properties

Digests

こういう形式

sha256とsha512がRegistered algorithmsとされている。sha256はMUSTでsha512はMAY。

Example

{
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "size": 7682,
  "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
  "urls": ["https://example.com/example-manifest" ]
}

OCI Image Layout Specification

OCI Image LayoutはOCI content-addressable blobs と location-addressable references のための directory構造を表す(?) Layoutではtarやzipなどのarchive formats、nfsなどの共有ファイルシステム、http、ftp、rsyncなどのネットワークによるファイル取得を使用してもよい。

あるimage layoutと参照は、manifestと指定された順序で適用されるfilesystem layerとOCI runtime specificationのconfig.jsonへ変換できるimage configurationがあればOCI Runtime Specification bundleを何らかのtoolによって作成できる。(?)

Content

Example

$ cd example.com/app/
$ find . -type f
./index.json
./oci-layout
./blobs/sha256/3588d02542238316759cbf24502f4344ffcc8a60c803870022f335d1390c13b4
./blobs/sha256/4b0bc1c4050b03c95ef2a8e36e25feac42fd31283e8c30b3ee5df6b043155d3c
./blobs/sha256/7968321274dc6b6171697c33df7815310468e694ac5be0ec03ff053bb135e768

$ shasum -a 256 ./blobs/sha256/afff3924849e458c5ef237db5f89539274d5e609db5db935ed3959c90f1f2d51
afff3924849e458c5ef237db5f89539274d5e609db5db935ed3959c90f1f2d51 ./blobs/sha256/afff3924849e458c5ef237db5f89539274d5e609db5db935ed3959c90f1f2d51

Blobs

Example

$ cat ./blobs/sha256/9b97579de92b1c195b85bb42a11011378ee549b02d7fe9c17bf2a6b35d5cb079 | jq
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7143,
      "digest": "sha256:afff3924849e458c5ef237db5f89539274d5e609db5db935ed3959c90f1f2d51",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      }
    },
...
$ cat ./blobs/sha256/afff3924849e458c5ef237db5f89539274d5e609db5db935ed3959c90f1f2d51 | jq
{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "size": 7023,
    "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 32654,
      "digest": "sha256:9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0"
    },
...
$ cat ./blobs/sha256/5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270 | jq
{
  "architecture": "amd64",
  "author": "Alyssa P. Hacker <alyspdev@example.com>",
  "config": {
    "Hostname": "8dfe43d80430",
    "Domainname": "",
    "User": "",
    "AttachStdin": false,
    "AttachStdout": false,
    "AttachStderr": false,
    "Tty": false,
    "OpenStdin": false,
    "StdinOnce": false,
    "Env": [
      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    ],
    "Cmd": null,
    "Image": "sha256:6986ae504bbf843512d680cc959484452034965db15f75ee8bdd1b107f61500b",
...
$ cat ./blobs/sha256/9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0
[gzipped tar stream]

oci-layout file

これ。

{
    "imageLayoutVersion": "1.0.0"
}

index.json

必須。image-layoutの参照、descriptorsのentry pointになる。/index.json に置かれる。 "org.opencontainers.image.ref.name" にイメージのtagが格納される?

{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.index.v1+json",
      "size": 7143,
      "digest": "sha256:0228f90e926ba6b96e4f39cf294b2586d38fbb5a1e385c05cd1ee40ea54fe7fd",
      "annotations": {
        "org.opencontainers.image.ref.name": "stable-release"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      },
      "annotations": {
        "org.opencontainers.image.ref.name": "v1.0"
      }
    },
    {
      "mediaType": "application/xml",
      "size": 7143,
      "digest": "sha256:b3d63d132d21c3ff4c35a061adf23cf43da8ae054247e32faa95494d904a007e",
      "annotations": {
        "org.freedesktop.specifications.metainfo.version": "1.0",
        "org.freedesktop.specifications.metainfo.type": "AppStream"
      }
    }
  ],
  "annotations": {
    "com.example.index.revision": "r124356"
  }
}

OCI Image Manifest Specification

imageと、そのコンポーネントのために生成された一意なIDからハッシュ可能なimageのconfigurationimage modelをサポートした参照可能なimageを作成すること、platform固有のmanifestを含んだ"fat manifest"による複数architecture対応のimageの実現、OCI Runtime Specificationへの変換の3つを目標にしている。

Image Manifest

image indexはarchitectureやOSごとに展開可能なそれぞれのimageの情報を持つが、image manifestは特定のarchitecture、OSに対する単一のcontainer imageにおけるconfigurationとlayerの集合を提供する。

Image Manifest Property Descriptions

Example

{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "size": 7023,
    "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 32654,
      "digest": "sha256:9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0"
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 16724,
      "digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 73109,
      "digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
    }
  ],
    "annotations": {
    "com.example.key1": "value1",
    "com.example.key2": "value2"
  }
}

OCI Image Index Specification

Image Index Property Descriptions

Example

{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7682,
      "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    }
  ],
  "annotations": {
    "com.example.key1": "value1",
    "com.example.key2": "value2"
  }
}

Image Layer Filesystem Changeset

change types

file types

File attributes

hardlinks

Platform-specific attributes

Createing

rootfs-c9d-v1/
    etc/
        my-app-config
    bin/
        my-app-binary
        my-app-tools

色々とfilesystemについての解説が続く

Non-Distributable Layers

OCI Image Configuration

用語

properties

Example

{
    "created": "2015-10-31T22:22:56.015925234Z",
    "author": "Alyssa P. Hacker <alyspdev@example.com>",
    "architecture": "amd64",
    "os": "linux",
    "config": {
        "User": "alice",
        "ExposedPorts": {
            "8080/tcp": {}
        },
        "Env": [
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
            "FOO=oci_is_a",
            "BAR=well_written_spec"
        ],
        "Entrypoint": [
            "/bin/my-app-binary"
        ],
        "Cmd": [
            "--foreground",
            "--config",
            "/etc/my-app.d/default.cfg"
        ],
        "Volumes": {
            "/var/job-result-data": {},
            "/var/log/my-app-logs": {}
        },
        "WorkingDir": "/home/alice",
        "Labels": {
            "com.example.project.git.url": "https://example.com/project.git",
            "com.example.project.git.commit": "45a939b2999782a3f005621a8d0f29aa387e1d6b"
        }
    },
    "rootfs": {
      "diff_ids": [
        "sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1",
        "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
      ],
      "type": "layers"
    },
    "history": [
      {
        "created": "2015-10-31T22:22:54.690851953Z",
        "created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /"
      },
      {
        "created": "2015-10-31T22:22:55.613815829Z",
        "created_by": "/bin/sh -c #(nop) CMD [\"sh\"]",
        "empty_layer": true
      }
    ]
}

Annotations

Rules

Pre-Defined Annotation Keys

Back-compatibility with Label Schema

org.opencontainers.image org.label-schema Compatibility notes
created build-date Compatible
url url Compatible
source vcs-url Compatible
version version Compatible
revision vcs-ref Compatible
vendor vendor Compatible
title name Compatible
description description Compatible
documentation usage URLの場合にはCompatible
authors Label Schemaにはない要素
licenses Label Schemaにはない要素
ref.name Label Schemaにはない要素
schema-version OCI Image Specにはない要素
docker.*, rkt.* OCI Image Specにはない要素

Conversion to OCI Runtime Configuration

Verbatim Fields

annotation fields

Parsed Fields

Optional Fields

Annotations

Considerations

Extensibility

Canonicalization

JSON

Extended Backus-Naur Form

2018年12月24日
2018年11月30日

Railsのテストを実行する環境をdockerで構築したが使いみちがない

docker-compose ps ※ Rails アプリのテストではなく、Rails本体のテストについての話です。

僕がそうだったように、皆さんにもふと「Rails本体のテストを実行してみたいな〜」と思うことがあるでしょう。

ところが、Rails本体のテストというのは大変です。ActiveRecordひとつ取ってみても、網羅するためにはMySQL、PostgreSQL、MariaDB、SQLiteの4つのRDBを用意しなければいけません。

もちろん、公式でそのような環境を構築するためのものは用意されています。

https://github.com/rails/rails-dev-box

rails-dev-boxを使うと、Vagrantを用いて、Rails本体の開発環境を構築できます。(yahondaさんありがとうございます)

ですが、Dockerが仮想環境の主流となっている今、VagrantでなくDockerを使いたい、そう思うのもおかしくはないでしょう。ないですよね?

なので、docker-compose を使用して、Railsのテストを実行する環境を構築してみました。以下、それに伴って行なったことの解説になります。

Dockerfile

FROM ruby:2.5.3-stretch

WORKDIR /rails
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - \
  && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
  && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
  && apt update && apt install --assume-yes \
    ffmpeg \
    sqlite3 \
    imagemagick \
    mupdf \
    mupdf-tools \
    poppler-utils \
    libmariadbclient-dev \
    libsqlite3-dev \
    postgresql-contrib \
    libpq-dev \
    libxml2 \
    libxml2-dev \
    libxslt1-dev \
    libncurses5-dev \
    mysql-client \
    git \
    make \
    nodejs \
    yarn \
 && apt-get clean \
 && rm -rf /var/lib/apt/lists/*

COPY . .
RUN bundle install --jobs=4
RUN npm install
RUN cd actionview && npm install

FROM から WORKDIR の指定までは飛ばして、aptによるパッケージのインストールを見てみます。 ここで指定しているパッケージ郡ですが、 https://github.com/rails/rails-dev-box/blob/master/bootstrap.sh を参考にしました。

続く COPY . . で、 rails/rails のファイルを一気にimageに追加しています。いわゆるDocker image buildのお作法としては、Gemfile、Gemfile.lock 、package.json などのファイルをCOPYして、bundle installなりnpm installなりを行ってからアプリケーションのコードをCOPYしてくるのが一般的です。しかしRailsのしかも開発環境では、Rails gem自体がActiveRecordやActiveModelなどの複数のgemに依存しており、それらがmonorepo構成で rails/rails に含まれているため、このようにしないと bundle installが実行できません。

あとは bundle installとnpm installを実行しているだけです。

docker-compose.yml

version: '3'
services:
  rails:
    build: .
    command: /bin/bash
    environment:
      - MYSQL_HOST=mariadb
      - PGHOST=postgres
      - PGUSER=postgres
      - REDIS_URL=redis://redis:6379
      - MEMCACHE_SERVERS=memcached
    tty: true
    volumes:
      - ".:/rails"
    links:
      - mysql
      # - mariadb
      - postgres
      - redis
      - memcached
      - rabbitmq
  mysql:
    image: mysql:5.7
    environment:
      - MYSQL_ALLOW_EMPTY_PASSWORD=yes
  postgres:
    image: postgres:11.1-alpine
  mariadb:
    image: mariadb:10.4.0-bionic
    environment:
      - MYSQL_ALLOW_EMPTY_PASSWORD=yes
  redis:
    image: redis:5.0.1-alpine
  memcached:
    image: memcached:1.5.12-alpine
  rabbitmq:
    image: rabbitmq:3.7.8-alpine

Rails本体のテストを行うにあたり、必要な関連サービスはMySQL PostgreSQL MariaDB Redis Memcached RabbitMQ の6つになります。SQLiteについてはrails service内に含まれています。

testの実行

`$ docker-compose up -d rails でrails serviceが立ち上がります。そしたら、

$ docker-compose exec rails bash

でcontainerに入り、testを実行します。

とはいえ、test suiteはTravis CIで実行されることに最適化されているため、以下のようなpatchを当てる必要がありました。

testそのものは、travis.ymlを参考に、このように実行します。

$ GEM=am,amo,as,av,aj,ast ci/travis.rb

このコマンドでは、ActionMailer、ActiveModel、ActiveSupport、ActionView、ActiveJob、ActiveStorageのtestを実行します。

例えばActiveRecordの、SQLite3でのテストの実行結果は以下のようになりました。

root:/rails# time GEM=ar:sqlite3 ci/travis.rb

[Travis CI] activerecord with sqlite3
Running command: bundle exec rake sqlite3:test

...snip...

Rails build finished successfully

real    63m17.484s
user    12m22.931s
sys     6m50.865s
root@ebdba3a4da12:/rails#

あきらめたやつ

multi stage buildによる Node.js ランタイムの構築

tootsuite/mastodon などで採用されているテクニックですが、multi stage buildを用いてNode.js、npm、yarnのバイナリを main imageに追加することにより、パッケージマネージャやバイナリの直接ダウンロードと比較して高速に別言語のランタイムを構築することができます。今回もmsatodonと同様にNode.js、npm、yarn を COPY --from node で持ってこようとしたのですが、 cannot find module'../lib/utils/unsupported.js' というエラーが発生し、解決策として出てくるのがNode.jsのサイインストールばかりだったので、aptからインストールすることにしました

で、これどうするのか

どうすればいいんでしょうね。

他の皆さんはどうしているのか

気になったのでTwitterで聞いてみたところ、Travis, VPS, localなどがあるようですね。確かにlocalhostに直にインストールしてしまうのが一番楽だと思いました。

皆さんRailsのテストってどうやってますかね。あ、RailsアプリのテストではなくRails本体のテストのことです。

— うなすけ (@yu_suke1994) 2018年11月21日

VPS借りてUbuntu 18.04入れて回してます。期待する答えじゃないかもしれませんが、何か困ってますかね。

— Yasuo Honda (@yahonda) 2018年11月21日

homebrewある環境だったらMySQLとPostgreSQL bottleで入れてActiveRecordのtestに必要なdatabaseやuser作るのは1分ぐらいあればできるので僕はローカルにインストールしてますね。

— Ryuta Kamizono (@kamipo) 2018年11月21日

すごい手抜きなやり方だとRailsをforkした自分のリポジトリに対してtravis設定して雑にpushしながらテストするとかですかね?

— sue445 (@sue445) 2018年11月21日
2018年11月30日
2018年10月11日

Itamaeのメンテナになりました

invited

きっかけ

@ryot_a_rai こんどitamaeの話をしたいので飲みに行きましょう!!!! CC @sue445

— うなすけ (@yu_suke1994) 2018年10月7日

僕が以前書いた、落ちていたitamaeのintegration specを直しました から、@sue445 さんもtravis-ciに移行させるpull reqを出したりと、停滞していた開発が再開しそうな流れになっていました。

しかし、作者の @ryotarai さんに時間がないのか、なかなかmergeまで持っていくことができていませんでした。 そこで冒頭のtweetがあり、3人で集まった結果、 僕とsue445さんが itamae-kitchen organization にMemberとして追加されることになりました。

【速報】.@yu_suke1994 さんが .@ryot_a_rai さんを詰めた結果(違)、Itamaeのコミッタになりました! pic.twitter.com/gSo2oTrhz1

— sue445@10/8技術書典5 か75 (@sue445) 2018年10月10日

(今気づいたのですが、現時点で僕のcommitはItamaeには入っていないんですね)

Itamae について思っていること

近年のWebアプリケーションインフラ界隈は、Docker、Kubernetesなどによって Cloud Native になっていく流れが主流となっているように感じています。今所属している会社でも、Rails on Docker on Kubernetes with GKE という構成になっており、いわゆる「モダン」と呼ばれる構成からはインスタンスのプロビジョニングという概念が消えつつあります。

Itamaeに限らず、ChefやAnsibleなど書くことになる機会も減っていくでしょう。

ただ、世間の潮流がどれだけ Cloud Native になったとしても、インスタンスの構築という作業が消えてなくなることは絶対にありません。それに、個人レベルで生のマシンの構築を行なったりするという概念もなくなりはしないでしょう。

そのような場合において、Itamaeを選択肢のひとつとして十分に検討対象であるという状況を続けていきたいと考えています。何故なら僕はRubyistですし、構成管理ツールの中ではItamaeが最も好きだからです。

今後のItamaeにおいて、後方互換を失う規模の変更が入るということはないと思いますが、ryotaraiさん、sue445さんと話していてまだまだ改善する部分はあるという認識は共通しています。細かい改善をしながら粛々とメンテナンスが行なわれていくことになるでしょう。

今後よろしくお願いします。

2018年10月11日
2018年10月08日

第51回 情報科学若手の会に参加しました

もらった寿司

幹事のどくぴーや、前年参加のなっちゃん、puhitakuにオススメされたので情報科学若手の会に参加することにしました。

発表の内容は想像以上にアカデミックであり濃く深いものばかりで、正直なところ着いていくのがやっと、いや着いていけないというのが現実でした。

ただ、それは参加を後悔しているという訳ではなく、自分の知らなかった分野、知っていたつもりでもそれは表層だけでまだまだ深淵がある分野についてその存在を知ることができたということで、参加できたことはとても価値があることだと感じました。

僕は初参加だったのですが、ショート発表を1つ、LTを1つ行ないました。初対面の人が多いイベントでは、発表する側に回ったほうが認知が上がること、参加枠も発表者としてだと倍率が低くなることなどの利点があるためです。(定員問題はなさそうでしたが)

今回のショート発表については外部公開はしませんが、LTについては過去の表参道.rbでのmail gemの話をしました。参加者にRubyistが少ないので知らない人が大半だと考えたためです。発表自体はそれなりに盛り上がったようで良かったです。

懇親会でのカスタムキャスト将棋(2人で交互にカスタムキャストのパラメータをいじって好みのアバターを作成する競技)はとても楽しかったです。オタクが終わっていく様子は面白いですね。僕は終わりました。

僕と @ntddk さんで作った限界美少女です #wakate2018 pic.twitter.com/9vlkenzVQo

— うなすけ (@yu_suke1994) 2018年10月7日

アッアッアーーーーー #wakate2018 pic.twitter.com/kXHZ2L7nnT

— うなすけ (@yu_suke1994) 2018年10月7日

来年も参加したいです。もっというなら、他のアカデミックな発表に負けないような濃い内容の発表を持っていきたいと思いますが、さてそんなことができるかな……

2018年10月08日
新しい投稿
古い投稿