Memento memo.

Today I Learned.

Docker の multi-stage build で golang の 軽量 image を作る

f:id:shotat_jp:20170805214939p:plain

Multi-Stage Build

docs.docker.com

Docker の 17.05 (ce) から multi-stage build 機能が追加されました。 multi-stage build とは、ベースイメージを複数利用し、多段階で build を行う機能です。

利用シーンはビルド環境とランタイム環境の分離が挙げられます。 ビルド環境とランタイム環境を分けるとランタイムがネイティブである Go のような言語では Docker image を劇的に小さくすることができます。

golang の ベースイメージは 数百 MB ありますが、alpine のような 軽量 Linux イメージ単体では 数 MB のため、効果としては 90 % 以上のダイエットに繋がります。

Docker image サイズを小さくするメリットや、一般的なプラクティスについてはページ下部のリンクが非常に参考になります。

Dockerfile の作成

multi-stage build を使って Go のコンテナアプリケーションを作成するテンプレートです。

github.com

Dockerfile の中身はこんな感じになります。

FROM golang:latest as builder

ENV CGO_ENABLED=0
ENV GOOS=linux
ENV GOARCH=amd64
WORKDIR /go/src/github.com/shotat/light-golang-container-template
COPY . .
RUN make

# runtime image
FROM alpine
RUN apk add --no-cache ca-certificates
COPY --from=builder /go/src/github.com/shotat/light-golang-container-template/app /app
EXPOSE 8080
ENTRYPOINT ["/app"]

Multi-stage build なので FROM が二回登場しています。 一行目の FROM では as xxx としてビルド環境に名前をつけています。 as で指定した名前は COPY --from=builder /go/src/github.com/shotat/light-golang-container-template/app /app で参照しています。

builder で GOOS GOARCH CGO_ENABLED環境変数を指定し、 alpine 用にビルドを行います。 amd64の場合 CGO_ENABLED=0 を指定しないとビルド環境依存が発生してしまうため指定は必須です。

RUN apk add --no-cache ca-certificatesSSL/TLS 通信を行うために必要で、ほとんどアプリケーションで利用するはずです。 通信の必要がなければ alpine ではなく busyboxscratch を使ってより小さなイメージが作れます。

Build する

ビルド自体は make だけで簡潔するようにしています。 Makefile の中身はこんな感じです。パッケージ管理には dep を使います。

# 適当に設定を書く
# NAME     := hello
# VERSION  := v1
# ...

all: setup build

setup:
    go get -u github.com/golang/dep/cmd/dep
    dep ensure

build:
    go build -o app
$ docker build . -t app:dev

アプリケーションの中身です。 適当に外部ライブラリを入れたかったので logger として logrus を使っています。

package main

import (
    log "github.com/sirupsen/logrus"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    log.Info("Hello")
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

実際にビルドしてみます。

$ docker build . -t app:dev

Run !

動かします。

$ docker run --rm -p 8080:8080 app:dev

$ curl localhost:8080
# container output
time="2017-08-05T12:43:25Z" level=info msg=Hello

無事いい感じに動作しています。 デプロイサイクル短縮の鼓動を感じました。

参考書籍

Docker 実践ガイド (impress top gear)

Docker 実践ガイド (impress top gear)

WEB+DB PRESS Vol.99

WEB+DB PRESS Vol.99

  • 作者: ?橋健一,谷口禎英,井本大登,山崎勝平,大和田純,内村元樹,坂東昌哉,平田敏之,牧大輔,板敷康洋,大?浩崇,穴井宏幸,原口宗悟,久田真寛,ふしはらかん,のざきひろふみ,うらがみ,ひげぽん,池田拓司,はまちや2,竹原,片田雄樹,渋江一晃,WEB+DB PRESS編集部編
  • 出版社/メーカー: 技術評論社
  • 発売日: 2017/06/24
  • メディア: 大型本
  • この商品を含むブログを見る

みんなのGo言語【現場で使える実践テクニック】

みんなのGo言語【現場で使える実践テクニック】

参考資料

Rails 5.1 で webpack の設定を使う

Rails 5.1 がリリースされたので webpacker gem の機能を使う超最小限のメモ

いれる

# rails 5.1 系をいれる
$ gem install rails

# webpackオプションをつけてnew 
$ rails new hogehoge --webpack

# reactとか使いたかったらこう
$ rails new hogehoge --webpack=react

超基本的な使い方

build

# webpack で ビルド
$ bin/webpack
# or dev server を立てる
$ bin/webpack-dev-server

これで public/packs 以下に ビルド済の JavaScript が出力されます。

webpack の設定

config/webpack にいる

webpack に食わせる JavaScript たち

app/javascript/packs にいる

View から呼び出す

javascript_pack_tag ヘルパーを使う

    <%= javascript_pack_tag 'application' %>
    <%= javascript_pack_tag 'hogehoge' %>

ScalaのREPL環境 Ammonite-REPL を使ってみる

この記事を書いてる段階でScala歴5分くらいなのですが、デフォルトのREPL環境が微妙だったのでいい感じのScala REPLを探してきました。

シンタックスハイライトと補間が効くやつです。

Ammonite

日本語情報がほとんどないので流行っていないのかもしれない(知らない)

Install

$ brew install ammonite-repl

起動

$ amm

見た目

f:id:shotat_jp:20170320213423p:plain

いい感じに色がついて、型の補完とかも効きます。

イマイチなところ

素のREPLより動作がもっさりする。マシンパワーで殴るしかない。

まとめ

ScalaHello Worldができるようになりました。

読んでるなう

Guide to ScalaーScalaプログラミング入門

Guide to ScalaーScalaプログラミング入門

Scalaスケーラブルプログラミング第3版

Scalaスケーラブルプログラミング第3版

zshmarks と prezto と peco でディレクトリ移動をいい感じにする

N番煎じ感があるけど今まで設定してなかったのでやってみます。

zshmarks

ディレクトリをブックマークして移動しやすくするやつです

github.com

似たようなプラグインhttps://github.com/mollifier/cd-bookmark なんかもあるのですが、zshmarksの日本語情報が少ない & star数が多かったので 敢えてこっちを使ってみました。

使い方

基本的な使い方はこんな感じです。

# カレントディレクトリをブックマーク
$ bookmark hoge
# ブックマーク先に移動
$ jump hoge
# ブックマークを削除
$ deletemark hoge
# ブックマーク一覧を表示
$ showmarks hoge
# ブックマークのパスを表示
$ showmarks

このままでも便利なんですが、peco, fzfみたいなファジーファインダと組み合わせてエイリアス貼ると良さげです(後述)

install via prezto

# preztoのルートに移動
$ cd ~/.zprezto
# submoduleに登録
$ git submodule add https://github.com/jocelynmallon/zshmarks.git modules/zshmarks

~/.zpreztorc を修正

zstyle ':prezto:load' pmodule \
  'environment' \
  ...
  'zshmarks' # add this line

zshでログインし直すのを忘れずに

peco との連携

こんな感じの設定を ~/.zshrc に書いてみました。

# zshmarks
peco_change_directory() {
  directory_alias=$(showmarks | peco | awk '{print $1}')
  if test -n "${directory_alias}"; then
    jump "${directory_alias}"
    return 0
  fi
  return 1
}

peco_delete_bookmark() {
  directory_alias=$(showmarks | peco | awk '{print $1}')
  if test -n "${directory_alias}"; then
    deletemark "${directory_alias}"
    return 0
  fi
  return 1
}

alias cdd=peco_change_directory
alias dbm=peco_delete_bookmark
alias bm=bookmark

bm でブックマークを行い(zshmarksの機能)、 cdd dbm でpecoを利用して ディレクトリ移動、ブックマーク削除をできるようにしてみました。良さげです。

エイリアスはもうちょっといい感じの貼り方がある気がするのでいろいろ試したい。

ニーア オートマタ

ニーアオートマタ面白い

最強の Datadog の構成管理ツール Doggy を使う

Datadog 自体には構成管理機能はついていないため、モニタリングやダッシュボードの設定を誰かが吹っ飛ばした場合に復旧できない問題がありました。権限をつけることはできますが、それでもヒューマンエラーは防げません。「構成をコード化し、gitで管理できるようにしたい」という欲求が出てきます。

Datadog の構成管理ツールとして、モニタリングの設定には Barkdog 、ダッシュボードの設定には dashdog といったように、様々なツールが公開されています。色々触ってみた結果、モニタリングとダッシュボードの設定を同時にJSONで管理できる Doggy が最強でした(※個人の感想です)。

参考:

Barkdog

inokara.hateblo.jp

dashdog

blog.serverworks.co.jp

Doggy

github.com

使い方はREADMEを読めば分かるのですが、doggy push, doggy pull といったようにgitライクに使えます。

イチオシの機能は doggy edit です。編集したいダッシュボードやモニタリングのIDを指定すると、ブラウザが開き、編集が終わったタイミングでターミナルに戻ればそのままpullしてくれて構成管理のJSONに反映することが出来ます。

ニーア オートマタ

ニーア オートマタ超面白かった。

ここ一年くらいで読んだ技術書をまとめる(70冊くらい)

読んだというか積んでるだけの本もありますが、整理してみます。 途中で力尽きましたが超適当にコメントつけていきます。 あまりエッジの効いた本は読めてないので大体のプログラマ/エンジニアにはおすすめです。

ソフトスキル系

SOFT SKILLS

エンジニアの人生ガイドです。超おすすめ。

SOFT SKILLS ソフトウェア開発者の人生マニュアル

SOFT SKILLS ソフトウェア開発者の人生マニュアル

TeamGeek

文章が面白くてさらっと読める。超おすすめ。HRTが大事。

Team Geek ―Googleのギークたちはいかにしてチームを作るのか

Team Geek ―Googleのギークたちはいかにしてチームを作るのか

達人プログラマー

達人になれる。ソフトスキルから契約プログラミングみたいな話まで幅広い。

新装版 達人プログラマー 職人から名匠への道

新装版 達人プログラマー 職人から名匠への道

言語系

Ruby

パーフェクトRuby

網羅的にRubyを学べます。会社のメンターに貸している。

パーフェクトRuby (PERFECT SERIES 6)

パーフェクトRuby (PERFECT SERIES 6)

Effective Ruby

EffectiveにRubyが書ける。

Effective Ruby

Effective Ruby

メタプログラミングRuby

メタプログラミングを学べる。

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版

パーフェクトRails

ある程度Railsをわかっている前提で手元に置いておきたい本(超入門向けではない)

パーフェクト Ruby on Rails

パーフェクト Ruby on Rails

Go

Go言語によるWebアプリケーション開発

GoでWebアプリを作る。楽しい。

Go言語によるWebアプリケーション開発

Go言語によるWebアプリケーション開発

スターティングGo言語

Goライフをスタートすることができた。

スターティングGo言語

スターティングGo言語

Java

スッキリわかるJava入門 実践編

わかりやすい。 入門と書いてあるが 実践編の名を関していない本が同シリーズにあるので 真の入門者はそっちから読む必要がある。

スッキリわかる Java入門 実践編 第2版 (スッキリシリーズ)

スッキリわかる Java入門 実践編 第2版 (スッキリシリーズ)

はじめてのSpring Boot

わかりやすかった。

Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発

DIとかAOPの概念が分かりやすかった記憶。こっちの方が丁寧かも。

JavaScript

パーフェクトJavaScript

安定のパーフェクト

パーフェクトJavaScript (PERFECT SERIES 4)

パーフェクトJavaScript (PERFECT SERIES 4)

CSS

Web制作者のためのCSS設計の教科書

通称メロン本。仕事で全くCSSを書かないので細かいところは全部忘れたけど概念は分かりやすい。

Elixir

プログラミングElixir

最強の言語Elixirです。おすすめ。

プログラミングElixir

プログラミングElixir

Haskell

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

たのしい絵は登場するがたのしくは学べない。

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

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

Swift

詳解Swift

2版を読んでるうちに3版がでた。

詳解Swift 第3版

詳解Swift 第3版

Python

Python入門

Pythonは本がいっぱいあってどれが良いのかわからない。

Python入門[2&3対応]

Python入門[2&3対応]

設計

マイクロサービスアーキテクチャ

マイクロサービスの楽しい本です。楽しい。

マイクロサービスアーキテクチャ

マイクロサービスアーキテクチャ

Web API: The Good Parts

WebAPIについて深く考えることができます。楽しい。

Web API: The Good Parts

Web API: The Good Parts

エンタープライズアプリケーションアーキテクチャパターン

DDDとかと比べて圧倒的に読みやすい。おすすめ。

エンタープライズアプリケーションアーキテクチャパターン

エンタープライズアプリケーションアーキテクチャパターン

オブジェクト指向のこころ

オブジェクト指向のこころがわかる。デザインパターンとかも。

オブジェクト指向のこころ (SOFTWARE PATTERNS SERIES)

オブジェクト指向のこころ (SOFTWARE PATTERNS SERIES)

Head First デザインパターン

み○ず○苑っぽいけど分かりやすい。

Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本

Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本

ビヨンド ソフトウェア アーキテクチャ

セール時に買ったが実は読んでいない。

オブジェクト指向設計実践ガイド

Rubyで学べるOOP本。

エリック・エヴァンスドメイン駆動設計

全体の20%くらいで力尽きた。

エリック・エヴァンスのドメイン駆動設計

エリック・エヴァンスのドメイン駆動設計

実践ドメイン駆動設計

全体の20%くらいで力尽きた。

実践ドメイン駆動設計

実践ドメイン駆動設計

DB

SQL ゼロからはじめるデータベース操作

SQLあまり書いてないのでゼロから学んだ。学べた。

SQL 第2版 ゼロからはじめるデータベース操作 (プログラミング学習シリーズ)

SQL 第2版 ゼロからはじめるデータベース操作 (プログラミング学習シリーズ)

プログラマのためのSQL

すべてを知り尽くせます(進捗5%)

詳解MySQL 5.7

MySQLの新し目の本。入門本ではないので最低限の知識が必要。

プロとしてのOracleアーキテクチャ入門

プロなので読みました。

達人に学ぶ SQL徹底指南書

達人に学ぶ SQL徹底指南書

達人に学ぶ SQL徹底指南書

達人に学ぶDB設計 徹底指南書

達人に学ぶDB設計 徹底指南書

達人に学ぶDB設計 徹底指南書

機械学習アルゴリズム

データサイエンティスト養成読本 機械学習入門編

入門なのかわからないが前半にアルゴリズムがコンパクトにまとまっていて良い。

データサイエンティスト養成読本 機械学習入門編 (Software Design plus)

データサイエンティスト養成読本 機械学習入門編 (Software Design plus)

Chainerによる実践深層学習

Chainerをハンズオン的な。Webのチュートリアルで十分かも。

Chainerによる実践深層学習

Chainerによる実践深層学習

劣モジュラ最適化と機械学習

劣モジュラっていう響きがかっこいいので読んだ。

劣モジュラ最適化と機械学習 (機械学習プロフェッショナルシリーズ)

劣モジュラ最適化と機械学習 (機械学習プロフェッショナルシリーズ)

スパース性に基づく機械学習

スパース性がかっこいいので途中まで読んだ。

スパース性に基づく機械学習 (機械学習プロフェッショナルシリーズ)

スパース性に基づく機械学習 (機械学習プロフェッショナルシリーズ)

深層学習

ある程度の機械学習知識(NNW)があれば手っ取り早くDeepLearningが学べる。

深層学習 (機械学習プロフェッショナルシリーズ)

深層学習 (機械学習プロフェッショナルシリーズ)

言語処理のための機械学習入門

今日から読む。

言語処理のための機械学習入門 (自然言語処理シリーズ)

言語処理のための機械学習入門 (自然言語処理シリーズ)

データ解析のための統計モデリング入門

最近読み始めた。

岩波データサイエンス Vol.1

読み始めてからデータサイエンスの前提知識がないことに気づいた。

岩波データサイエンス Vol.1

岩波データサイエンス Vol.1

アルゴリズムクイックリファレンス

最近改訂版が出た。クイックリファレンスというイメージよりしっかり書いてあるのでおすすめ。

アルゴリズムクイックリファレンス 第2版

アルゴリズムクイックリファレンス 第2版

PRML

一回ちゃんと読まないといけない気がして気合で読んだ。ちゃんとは読めなかった。

パターン認識と機械学習 上

パターン認識と機械学習 上

PRML

一人で読むのはきつい。

パターン認識と機械学習 下 (ベイズ理論による統計的予測)

パターン認識と機械学習 下 (ベイズ理論による統計的予測)

人工知能は人間を超えるか

平和。

インフラ系

DevOps教科書

教科書感があって眠くなる。DevOpsに興味が出てから読むと面白かった。

DevOps教科書

DevOps教科書

入門Ansible

Ansibleの入門本。分かりやすかった。 最近はAnsible本も増えてきた気がするので他にもいい本があるかも。

入門Ansible

入門Ansible

絵で見てわかるOS/ストレージ/ネットワーク

わかりやすい。

DevOpsを支えるHashiCorpツール大全

大全というほどではない。Webのチュートリアルで十分感。

DevOpsを支えるHashiCorpツール大全 ThinkIT Books

DevOpsを支えるHashiCorpツール大全 ThinkIT Books

インフラエンジニアの教科書2 スキルアップに効く技術と知識

疲れたのでここからノーコメントでいきます

インフラエンジニアの教科書2 スキルアップに効く技術と知識

インフラエンジニアの教科書2 スキルアップに効く技術と知識

nginx実践入門

nginx実践入門 (WEB+DB PRESS plus)

nginx実践入門 (WEB+DB PRESS plus)

AWS実践入門

Amazon Web Services実践入門 (WEB+DB PRESS plus)

Amazon Web Services実践入門 (WEB+DB PRESS plus)

Amazon Web Services クラウドデザインパターン実装ガイド

Amazon Web Services クラウドデザインパターン実装ガイド 改訂版

Amazon Web Services クラウドデザインパターン実装ガイド 改訂版

Amazon Web Services クラウドデザインパターン設計ガイド

Amazon Web Services クラウドデザインパターン 設計ガイド

Amazon Web Services クラウドデザインパターン 設計ガイド

The DevOps 逆転だ!究極の継続的デリバリー

The DevOps 逆転だ!

The DevOps 逆転だ!

プログラマのためのDocker教科書

システムテスト自動化 標準ガイド

システムテスト自動化 標準ガイド CodeZine BOOKS

システムテスト自動化 標準ガイド CodeZine BOOKS

サーバ/インフラエンジニア養成読本 ログ収集〜可視化編

マスタリングTCP/IP 入門編 第5版

マスタリングTCP/IP 入門編 第5版

マスタリングTCP/IP 入門編 第5版

その他

自然言語処理 (放送大学教材)

自然言語処理 (放送大学教材)

自然言語処理 (放送大学教材)

プログラミングコンテストチャレンジブック

プログラミングコンテストチャレンジブック [第2版] ?問題解決のアルゴリズム活用力とコーディングテクニックを鍛える?

プログラミングコンテストチャレンジブック [第2版] ?問題解決のアルゴリズム活用力とコーディングテクニックを鍛える?

Code Complete 第2版 上

CODE COMPLETE 第2版 上 完全なプログラミングを目指して

CODE COMPLETE 第2版 上 完全なプログラミングを目指して

Code Complete 第2版 下

CODE COMPLETE 第2版 下 完全なプログラミングを目指して

CODE COMPLETE 第2版 下 完全なプログラミングを目指して

知識ゼロから学ぶソフトウェアテスト

エッセンシャル スクラム

エッセンシャル スクラム

エッセンシャル スクラム

エンジニアのためのGitの教科書[上級編] Git内部の仕組みを理解する

チャットボット AIとロボットの進化が変革する未来

チャットボット AIとロボットの進化が変革する未来

チャットボット AIとロボットの進化が変革する未来

まつもとゆきひろ 言語のしくみ

プログラム意味論

プログラム意味論 (情報数学講座)

プログラム意味論 (情報数学講座)

UNIXという考え方

UNIXという考え方―その設計思想と哲学

UNIXという考え方―その設計思想と哲学

アンダースタンディングコンピュテーション

実践Vim

Vimテクニックバイブル

Vimテクニックバイブル ?作業効率をカイゼンする150の技

Vimテクニックバイブル ?作業効率をカイゼンする150の技

Webを支える技術

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)

なるほどデザイン

まとめ

整理してみるとちゃんと読めてない本、あまり内容覚えてない本が結構あったのでちゃんと読みなおしたいと思いました。

NumPyの配列操作 - 条件に合う要素のみフィルタする処理の理解

積ん読消化中で、以前買った機械学習入門本を読んでいます。

データサイエンティスト養成読本 機械学習入門編 (Software Design plus)

データサイエンティスト養成読本 機械学習入門編 (Software Design plus)

前半部に基礎的な事項が簡潔にまとめられてて良い本です。 おかげで去年Courseraでやった内容を思い出せました。

本題

NumPy使っていて、以下の例での3行目あたりの処理で一体何が起きているのか分かってなかったのでメモ。

a = np.arange(10)
#=> array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
a[a<5]
#=> array([0, 1, 2, 3, 4]) <- !?!?

配列の indexing

配列のindexingは、配列に対してbool値でindexを指定すると True の要素だけ取り出せるやつです(本当はもうちょっと意味が広いです)。

a[np.array([True, False, True, False, True, False, True, False, True, False])]
#=> array([0, 2, 4, 6, 8])

配列の broadcasting

broadcastingは、配列の全ての要素に関数を適用するやつです。

a < 5
#=> array([ True,  True,  True,  True,  True, False, False, False, False, False], dtype=bool)

まとめ

a = np.arange(10)
#=> array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
a[a<5]
#=> array([0, 1, 2, 3, 4])
# 以下の2STEPに分解できる
bool_array = a < 5
#=> array([ True,  True,  True,  True,  True, False, False, False, False, False], dtype=bool)
a[bool_array]
#=> array([0, 1, 2, 3, 4])

データサイエンティスト養成読本 機械学習入門編 (Software Design plus)

データサイエンティスト養成読本 機械学習入門編 (Software Design plus)

「サーバ/インフラエンジニア養成読本 ログ収集〜可視化編」でFluentdがinstallできない問題

去年買って完全に積んでいた「サーバ/インフラエンジニア養成読本 ログ収集〜可視化編」を読み始めました。 最近お仕事で運用設計的なところ触ったりして興味湧いてきたのがきっかけです。

問題: 環境構築こける

fluentd周りでこける。Ruby古いとか言われる。

対応: td-agent2にした

td-agentはv1とv2があるらしいのでv2にしてみました。

本家側は書籍側とversion合わせる必要があると思うので適当にforkしました。

差分はこんな感じです。 github.com

対応方法これで合ってるのかよくわかりませんが、一旦動くところまで。

Neovimのpython_host_prog設定

新しくMBP買いました。Brewfile, dotfiles周りはある程度環境構築自動化できてるんですが、Neovim <=> python_host_progの連携とか何もしてないので少しずつscriptに落としていきたい。

とりあえずNeovimでPython呼べるようにshellを書きます。

zcheeさんの以下のwikiを参考にしました 🙏

github.com

こんな感じです

#!/bin/sh
# requirements:
# - pyenv
# - pyenv-virtualenv

python2='2.7.13'
python3='3.6.0'

eval "$(pyenv init -)"

# python2
pyenv install "${python2}"
pyenv virtualenv "${python2}" neovim2
pyenv activate neovim2
pip install neovim

# python3
pyenv install "${python3}"
pyenv virtualenv "${python3}" neovim3
pyenv activate neovim3
pip install neovim

echo "NOTE: Add following lines to 'init.vim'"
echo "let g:python3_host_prog=\$HOME . '/.pyenv/versions/neovim3/bin/python'"
echo "let g:python_host_prog=\$HOME . '/.pyenv/versions/neovim2/bin/python'"

あとは上記scriptの通りに init.vim に設定追記すればokです。

Ruby: Hash -> Structの変換

こんな感じで書けます

  def deep_struct(hash)
    foo = hash.values.map do |v|
      case v
      when Hash
        deep_struct(v)
      when Array
        v.map { |x| deep_struct(x) }
      else
        v
      end
    end
    Struct.new(*hash.keys).new(*foo)
  end

OOPっぽく書きたければHash, Arrayに deep_struct のようなメソッドを拡張するのもアリです