akiyoko blog

akiyoko の IT技術系ブログです

macOS Sierra のクリーンインストールから Python 開発用の初期設定まで一挙公開

先日ついに、自宅の MacBook Pro の OS を Yosemite(!!)から一気に Sierra にアップデートしました。通常のアップデートだと動作が重くなるという記事 も見かけたので、今回はクリーンインストール(ハードディスクを初期化したまっさらな状態からのインストール)に初挑戦してみました。

そこで、macOS Sierra のクリーンインストールから Python 開発用の初期設定までを、今回は一挙公開してみることにします。

MacBook Pro のスペック

MacBook Pro Retinaディスプレイ MF840J/A(Retina, 13-inch, Early 2015)
液晶サイズ:13.3インチ
CPU:Core i5 / 2.7 GHz / 2コア
SSD:256 GB
メモリ:16 GB(8GBから増設)
重量:1.58 kg




macOS Sierra のクリーンインストールおよび初期設定の手順は以下となります。

以下、開発向けの初期設定手順です。

(なお、本記事の手順およびアプリのバージョン等は、2017年2月時点のものです。)

こちらも参考に。



 

1. macOS Sierra のインストール

1.1. Sierra への OSアップデート

クリーンインストールの前に、OS を一旦 Sierra にアップデートする必要があります。


[Appleメニュー] > [このMacについて] から、[ソフトウェア・アップデート]をクリックします。
f:id:akiyoko:20170209235949p:plain:w500

App Store が立ち上がるので、「無料アップグレード」をクリック。
f:id:akiyoko:20170210000014p:plain

あとは適宜インストールしていきます。
後で再度クリーンインストールするので、設定は適当で構いません。
f:id:akiyoko:20170210000119p:plain


インストールが完了したら、バックアップを適宜実施しておきます。


Apple AirMacTimeCapsule-3TB ME182J/A

Apple AirMacTimeCapsule-3TB ME182J/A




最後に、クリーンインストールをするためにシステム終了をします。



 

1.2. ハードディスクの初期化

「⌘ + r」を押したまま電源ボタンをクリックして、「macOSユーティリティ」を起動させます。

(参考)Mac の起動時のキーコンビネーション - Apple サポート



「ディスクユーティリティ」からハードディスクを初期化します。

f:id:akiyoko:20170210010308p:plain


左側のメニューから「Macintosh HD」を選択して「消去]をクリック。

f:id:akiyoko:20170210010431p:plain


「Mac OS Extended (Journaled)」形式でフォーマットします。

f:id:akiyoko:20170210010530p:plain

f:id:akiyoko:20170210010626p:plain


「☓」ボタンをクリックして、ユーティリティを終了します。

f:id:akiyoko:20170210010728p:plain



 

1.3. Sierra のクリーンインストール

続けて、macOS Sierra のクリーンインストールを開始していきます。


「⌘ + r」を押したまま電源ボタンを押して起動した「macOSユーティリティ」から、「macOSを再インストール」を選択します。

f:id:akiyoko:20170210010845p:plain


ここで大事なポイントが。
ここでまず、Wi-fi に繋がっていることを確認しておきます。

f:id:akiyoko:20170210011011p:plain


プロキシの影響などでここから進まない場合があるようですが、ここで「おかしいな」と諦めて電源を切ってしまってはいけません(私はそれで一度ハマりました・・ Internet リカバリ経由で Yosemite のインストールからやり直さなくてはいけない羽目になってしまいますのでご注意を)。Wi-fi の設定を見直すか、別のネットワークに接続すると解決できることがあります。

f:id:akiyoko:20170210011123p:plain

f:id:akiyoko:20170210011253p:plain

f:id:akiyoko:20170210011356p:plain


インストールにしばらく時間が掛かります。


インストールが完了したら、初期設定が始まります。

f:id:akiyoko:20170210011500p:plain

f:id:akiyoko:20170210011633p:plain

f:id:akiyoko:20170210011807p:plain


バックアップを取っていた場合は「Time Machineバックアップから」を選択してバックアップからデータを復元します。今回は特にバックアップからの復元はおこないませんでした。

f:id:akiyoko:20170210012144p:plain

f:id:akiyoko:20170210012326p:plain


これまで使っていた Apple ID でサインインします。

f:id:akiyoko:20170210012529p:plain

f:id:akiyoko:20170210012650p:plain


アカウントを作成します。
ちなみに、フルネーム、アカウント名、パスワードは後で変更可能です。

f:id:akiyoko:20170210012814p:plain

f:id:akiyoko:20170210012954p:plain

別のデバイスに承認要求が来たら、承認しておきます。


セキュリティのために暗号化は有効にしておきます。

f:id:akiyoko:20170210013114p:plain


書類とデスクトップのファイルを iCloud に保存する設定をしておきます。

f:id:akiyoko:20170210013318p:plain


データの自動送信はオフに。

f:id:akiyoko:20170210013458p:plain


Siri は後で設定オフにできるので、とりあえず入れておきます。

f:id:akiyoko:20170210013642p:plain

f:id:akiyoko:20170208234642j:plain


この後しばらくすると、macOS Sierra のデスクトップが表示されます。




2. 基本操作系の初期設定

2.1. トラックパッドの設定

[システム環境設定] > [トラックパッド]

とにかく全部チェックを入れておきます。

f:id:akiyoko:20170213213801p:plain
「軌跡の速さ」を少し速くしておきます。

f:id:akiyoko:20170210013923p:plain
f:id:akiyoko:20170210013939p:plain



2.2. Google 日本語入力を IME に設定

「Google 日本語入力」を 本家サイト からダウンロードしてインストールします。

f:id:akiyoko:20170210014045p:plain
f:id:akiyoko:20170210014056p:plain

Launchpad 上のアイコンはまとめておいて、名前を適当に付けておきます。

f:id:akiyoko:20170210014206p:plain



ここで、ConfigDialog を起動して、「¥キーで入力する文字」を「円記号(¥)」から「バックスラッシュ(\)」に変更しておきます。
f:id:akiyoko:20170308112233p:plain



次に、[システム環境設定] > [キーボード] > [入力ソース]から、「Google」以外のものを「−」で削除します。
f:id:akiyoko:20170210014126p:plain

またここで、「メニューバーに入力メニューを表示」のチェックを外しておきます。
f:id:akiyoko:20170210014231p:plain


(私は自宅の MacBook では日本語キーボードを使っているのですが)caps キーは全く使わないので、代わりに「control」キーを割り当てています。

[システム環境設定] > [キーボード]から[装飾キー]をクリックして、以下のように変更します。
f:id:akiyoko:20170210014455p:plain


ちなみに英語キーボードの場合は、以前は「Karabiner」を使ってスペースキー左右の ⌘キーを空打ちすることで英数/かなを切り替えていたのですが、2017年2月現時点で Karabiner がまだ Sierra に対応できていないようです。

いろいろ試行錯誤した結果、「英かな.app」というアプリをインストールすることで今のところ十分代替できています。
https://ei-kana.appspot.com




最後に、[システム環境設定] > [キーボード] > [ショートカット]から、[入力ソース]および[Spotlight]のチェックを全て外し、「^ スペース」「^ ⌥ スペース」「⌘ スペース」「⌥ ⌘ スペース」のホットキーを他の用途で使えるようにしておきます。
f:id:akiyoko:20170210014253p:plain
f:id:akiyoko:20170210014321p:plain


同じく[ショートカット]の[Mission Control]から「^ ←」「^ →」のショートカットの割り当てを外しておきます。
f:id:akiyoko:20170310215618p:plain



 

2.3. ファンクションキーの設定

「fn」キーを使わずとも、F7 でカタカナ、F10 で半角英数字に変換ができるようにします(Windows ライクな操作ができるように)。


[システム環境設定] > [キーボード]

f:id:akiyoko:20170210014521p:plain


2.4. Dock の設定

[システム環境設定] > [Dock]


以下の設定をおこないます。

  • Dock のアイコンサイズをやや小さめにして左側に配置
  • タイトルバーのダブルクリックでウィンドウを Dock にしまう
  • (ディスプレイが狭いので)Dock を自動的に隠す

f:id:akiyoko:20170210014544p:plain


3. セキュリティ系の設定

3.1. パスワードの設定

[システム環境設定] > [セキュリティとプライバシー]


必要に応じて変更しておきます。

f:id:akiyoko:20170210014612p:plain



3.2. スリープ/スクリーンセーバーの設定

Mac は一人でしか使わないし、めったに持ち出さないこともあり、ロック解除にパスワードは設定しない方向で。

f:id:akiyoko:20170210014636p:plain


[システム環境設定] > [デスクトップとスクリーンセーバ]


スクリーンセーバー起動までの時間を「開始しない」に設定し、スクリーンセーバーを起動しないように(すぐにディスプレイを切るように)しておきます。
f:id:akiyoko:20170210014702p:plain


3.3. ファイアウォールの設定

[システム環境設定] > [セキュリティとプライバシー] > [ファイアウォール]

f:id:akiyoko:20170210014721p:plain


3.4. 位置情報サービスの設定

[システム環境設定] > [セキュリティとプライバシー] > [プライバシー]

「Mac を探す」などで利用される位置情報サービスですが、(めったに外に持ち出さないこともあり)省エネのためにもオフにしておきます。

f:id:akiyoko:20170210014721p:plain


3.5. ディスプレイを切にするまでの時間設定

[システム環境設定] > [省エネルギー]

バッテリー使用時にディスプレイを切にするまでの時間を「2分」から「10分」に変更。

f:id:akiyoko:20170210014741p:plain

電源アダプタ使用時にディスプレイを切にするまでの時間を「10分」から「30分」に変更。

f:id:akiyoko:20170210014758p:plain


4. 見た目系の設定

Finder とメニューバーの設定をおこないます。

なお、Finder で隠しファイルを表示する際には、Sierra 以前では「Macで隠しファイルを表示する方法 - Qiita」で書かれているように、ターミナルで

$ defaults write com.apple.finder AppleShowAllFiles true
$ killall Finder

を実行していましたが、Sierra 以降では Finder 上で「⌘ + shift + . 」というショートカットで隠しファイルの表示/非表示を切り替えられるようになったので、今回は上記のコマンドは実行していません。

(参考)macOS Sierraでは隠しファイルを表示するショートカットキー「CMD+Shift+.」がFinderでも利用可能に。 | AAPL Ch.


 

4.1. デスクトップのアイコンをきれいに並べる

Finder の [表示] > [表示オプションを表示]から、

f:id:akiyoko:20170210014930p:plain


表示順序に「グリッドに沿う」を選択します。

f:id:akiyoko:20170210014947p:plain:w250


4.2. ウィンドウのサイドバーに表示される項目を選択

[Finder] >[環境設定] > [サイドバー] タブ

Finder のサイドバーに表示させたいものを適宜チェックしておきます。
f:id:akiyoko:20170210015024p:plain:w400


4.3. すべてのファイルの拡張子を表示

[Finder] > [環境設定] > [詳細] タブにて、「すべてのファイル名拡張子を表示」をチェックします。

f:id:akiyoko:20170210015042p:plain:w400


 

4.4. バッテリー残量の表示設定

メニューバー右上の電池マークをクリック -> 「割合(%)を表示」をチェック

f:id:akiyoko:20170210015054p:plain:w400


 

4.5. メニューバーの日付表示設定

[システム環境設定] > [日付と時刻] > [時計]

f:id:akiyoko:20170210015108p:plain
f:id:akiyoko:20170210015124p:plain


4.6. 音声アイコンを表示

[システム環境設定] > [サウンド]

f:id:akiyoko:20170210015144p:plain



5. その他の設定

5.1. ユーザのフルネームを変更

[システム環境設定] > [ユーザとグループ]

アカウント名やフルネームを変更する場合はこちらから。

f:id:akiyoko:20170210014814p:plain
f:id:akiyoko:20170210014831p:plain


5.2. コンピュータ名の変更

[システム環境設定] > [共有]


自動的にイマイチなコンピュータ名が付けられているので、ここでコンピュータ名を変更しておきます。

f:id:akiyoko:20170210014907p:plain



 

5.3. Siri をオフに設定

[システム環境設定] > [Siri] から、「Siriを有効にする」のチェックを外します。

f:id:akiyoko:20170210015207p:plain



5.4. スクリーンショット画像の保存場所を変更

ターミナル上で以下のコマンドを実行して、スクリーンショット画像の保存場所をデスクトップから変更しておきます。

$ defaults write com.apple.screencapture location ~/Pictures/
$ killall SystemUIServer

(参考)Mac - スクリーンショットで撮影した画像の保存場所を変更 - PC設定のカルマ




とりあえず、OS のインストールと初期設定は完了です。




6. 無いと困る系アプリ

次に、無いと困る系のアプリをインストールしていきます。

6.1. Google Chrome

パソコン版 Chrome からダウンロードしてインストール。


デフォルトのブラウザとして設定しておきます。
f:id:akiyoko:20170210015829p:plain
f:id:akiyoko:20170210015844p:plain:w400


たくさんタブを開くので、メモリ開放プラグインが必須です。
ちなみに「One Tab」を使っています。




 

6.2. Sublime Text 3

https://www.sublimetext.com/3
からダウンロードします。
f:id:akiyoko:20170210020751p:plain


以降、Sublime Text 3 の初期設定をしていきます。

(参考)[tips][Sublime Text] Sublime Text 3をインストールしたらまずやること


6.2.1. License

[Help] > [Enter License]

「----- BEGIN LICENSE -----」から「------ END LICENSE ------」までを貼り付け。

f:id:akiyoko:20170210020811p:plain:w500

6.2.2. Package Control のインストール

「control + ` (shift + @)」で Console を表示して、Installation - Package Control の「SUBLIME TEXT 3」タブに記載されているコマンド

import urllib.request,os,hashlib; h = 'df21e130d211cfc94d9b0905775a7c0f' + '1e3d39e33b79698005270310898eea76'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)

をコピペして Enter(上記コマンドは定期的に変更されるので、適宜公式ページを参照すること)。

f:id:akiyoko:20170210020854p:plain


6.2.3. 文字コード対応

「⌘ + shift + p」で Package Control を開き、Install Package を選択して、
f:id:akiyoko:20170210021008p:plain

「ConvertToUTF8」を検索してインストールします。
f:id:akiyoko:20170210021052p:plain

6.2.4. 全角スペースの表示

Package Control で「TrailingSpaces」を検索してインストールします。

f:id:akiyoko:20170210021109p:plain

全角スペースが認識できていないような。。

(参考)Sublime Text 3でスペース(半角・全角)やタブを可視化する方法



たまに発生する文字化けは「Codecs33」をインストールすることで解消できます。

f:id:akiyoko:20170210021156p:plain


(参考)MacでSublimeText3を使う際にShift_JISを文字化けしないようにする - Qiita


6.2.5. ユーザ設定

[Preference] > [Settings - User]

{
    "default_encoding": "UTF-8",  // デフォルトのエンコーディングの文字コード
    "draw_white_space": "all",  // タブやスペースなどの不過視文字を表示
    "fallback_encoding": "UTF-8",  // 文字コードが不明なファイルのエンコーディングの文字コード
    //"font_face": "",
    "font_size": 10,
    "highlight_line": true,  // 現在の選択行をハイライト表示
    "ignored_packages":
    [
        "Vintage"
    ],
    "line_padding_top": 2,  // 行間
    "show_encoding": true,  // エンコーディングの文字コードを右下のステータスバーに表示
    "tab_size": 4,
    "trailing_spaces_highlight_color": "comment",  // コメントの色でハイライト
    "trailing_spaces_regexp": " |[ \t]+",  // 全角スペースを正規表現に追加
    "translate_tabs_to_spaces": true,  // タブをスペースに変換
    //"trim_trailing_white_space_on_save": true,  // 空白の削除
    "word_wrap": true  // 自動改行
}

 

6.2.6. 再起動

最後に、Sublime Text 3 を再起動します。



6.3. HyperSwitch

タスクの切り替えを Windows風にするアプリです。


本家サイト からダウンロードしてインストールします。
何年も前からずっと β です。。

f:id:akiyoko:20170210021425p:plain

macOS Sierra 以降、環境設定でダウンロードした全てのアプリの実行を許可することができなくなったので、インストール時にアプリへのアクセス許可を一つ一つ設定する必要があります。
f:id:akiyoko:20170210021446p:plain:w400
f:id:akiyoko:20170210021457p:plain


起動後に、以下の設定をおこないます。

  • 「Run HyperSwitch in the background」にチェック
  • ウィンドウの移動を「⌘ + Tab」に変更
  • 「Include windows from other screens」にチェック(複数モニタを使っている場合)
  • 「Use shift to cycle backwards」にチェック

f:id:akiyoko:20170210021515p:plain

Dock にもメニューバーにも表示しないように設定。
f:id:akiyoko:20170210021529p:plain

以下のチェックは外しておきます。
f:id:akiyoko:20170210021541p:plain



 

6.4. Dropbox

https://www.dropbox.com/downloading?src=index
からアプリをダウンロードしてインストールします。

f:id:akiyoko:20170210225111p:plain:w350

起動したら Dropbox アカウントでログインしてファイルを同期します。
f:id:akiyoko:20170210225132p:plain:w350
f:id:akiyoko:20170210225146p:plain:w350

アプリへのアクセスを許可します。
f:id:akiyoko:20170210225202p:plain:w500
f:id:akiyoko:20170210225217p:plain


 

6.5. Alfred

App Store からインストールするとバージョン1系のものしかインストールできないので、公式サイト からダウンロードしてインストールします。

f:id:akiyoko:20170210021641p:plain


最後に、Alfred のホットキーを「⌘ ダブルタップ」に変更しておきます。
f:id:akiyoko:20170210021702p:plain


(参考)Alfredを起動するショートカットは⌘キー2回押しで - Qiita


 

6.6. Memory Clean

App Store からインストールします。

f:id:akiyoko:20170210015511p:plain


以下の設定をしておきます。

  • 「Icon Visible」をオフ
  • 「Auto Clean」にチェック

f:id:akiyoko:20170210015529p:plain:w400
「Auto Clean」をオンにします。
f:id:akiyoko:20170210015540p:plain:w400


 

6.7. Stuffit Expander

解凍ツール。App Store からインストールします。

f:id:akiyoko:20170210015558p:plain





以下、開発向けの初期設定です。


7. Xcode

7.1. Xcode

Xcode はいわば通過儀礼です。
App Store からインストールします。現時点の最新バージョンは 8.2.1 でした。

f:id:akiyoko:20170210015336p:plain


Xcode はサイズが 4GB 以上もある巨大アプリです。
もしここで、ダウンロードが全然進まなくて「Xcode をダウンロードできませんでした」というエラーが出てしまう場合は、Google Public DNS を使うように設定すればすんなりとダウンロードできる場合があります。


設定方法は、[システム環境設定] > [ネットワーク] から以下の設定をおこないます。
f:id:akiyoko:20150919030402p:plain
f:id:akiyoko:20150919030416p:plain

(参考)[Mac]Mac でGoogle Public DNS を設定する方法


起動すると追加のコンポーネントのインストールを要求されるので、インストールをしておきます。
f:id:akiyoko:20170210230709p:plain:w500


7.2. Command Line Tools

次に、Command Line Tools をインストールします。
Command Line Tools をインストールすると、合わせて Git などが自動的にインストールされます。

以下のコマンドを実行します。

$ xcode-select --install


ダイアログが出てくるので、「インストール」をクリックします。
f:id:akiyoko:20170210015356p:plain:w500
f:id:akiyoko:20170210015410p:plain
f:id:akiyoko:20170210015426p:plain



後で Homebrew をインストールするのにライセンスに同意する必要があるので、先に済ませておきます。

$ sudo xcodebuild -license


f:id:akiyoko:20170210015441p:plain




 

8. Homebrew

8.1. Homebrew

Homebrew は macOS 用パッケージマネージャーです。Python や Ansible などのパッケージをコマンド一発でインストールすることができます。

なお、Homebrew Cask という派生のパッケージマネージャーを使うと Macアプリのパッケージ管理もできるのですが、アプリの更新が遅かったり、Cask 自体の不具合があったり、アプリの管理がややこしくなってしまったという前回の反省から、今回は Homebrew Cask を使うのはやめました。


今回の初期設定の方針としては、基本的にアプリは Webサイトからダウンロードしてインストールすることとし、機能的に過不足がないものが App Store からも入手できる場合のみ App Store からインストールすることにしています。



ターミナルで以下のコマンドを実行して、Homebrew をインストールします。

$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

念のため、コマンドは 公式サイト をチェックしておいた方がよさげです(記事によっては公式サイトとは異なるコマンドが記載されている場合があるようです)。

$ brew --version
Homebrew 1.1.9
Homebrew/homebrew-core (git revision d96b; last commit 2017-02-08)


Homebrew は「/usr/local/」以下にファイルをインストールするので「/usr/local/bin」にパスを通しておく必要があるのですが、最初から「/usr/local/bin」にパスが通っていたので、今回は特に何もしませんでした。

$ env $PATH
env: /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin: No such file or directory

もしパスが通っていなければ、以下を実行。

$ echo "export PATH=/usr/local/bin:\$PATH" >> ~/.bash_profile
$ source ~/.bash_profile


最後に、Homebrew の診断をしておきます。

$ brew doctor
Your system is ready to brew.


 

9. 開発系パッケージ

9.1. Git

Xcode の Command Line Tools でインストールされたものをそのまま使います。

$ git --version
git version 2.10.1 (Apple Git-78)
$ which git
/usr/bin/git


初期設定をしておきます。

$ git config --global user.name "akiyoko"
$ git config --global user.email "akiyoko@users.noreply.github.com"
$ git config --global color.ui auto


GitHub への接続設定をします。

~/.netrc

machine github.com
login akiyoko
password <パスワード>

なお、.netrc のパーミッションは 600 にしなければ動作しません。

$ chmod 600 ~/.netrc

 

9.2. Python

macOS にはデフォルトで Python 2.7 が入っています。

$ python --version
Python 2.7.10
$ which python
/usr/bin/python


ここで改めて、Homebrew で pyenv をインストールし、pyenv から Anaconda をインストールして、Python系の周辺パッケージのセッティングをまとめて済ませます。


<過去記事>
akiyoko.hatenablog.jp


### Homebrew で pyenv をインストール
$ brew install pyenv

$ pyenv --version
pyenv 1.0.7

$ cat << EOF >> ~/.bash_profile
export PYENV_ROOT=\${HOME}/.pyenv
export PATH=\${PYENV_ROOT}/bin:\$PATH
eval "\$(pyenv init -)"
EOF

$ source ~/.bash_profile


### Anaconda をインストール
$ pyenv install -l
$ pyenv install anaconda-2.4.0
$ pyenv global anaconda-2.4.0


$ python --version
Python 2.7.10 :: Anaconda 2.4.0 (x86_64)
$ which python
/Users/akiyoko/.pyenv/shims/python


また、

  • condaは仮想環境管理にも使えます。: virtualenv/venvの代わり
    • 仮想環境下でcondaやpipでパッケージをインストールできます。
    • condaで作る仮想環境はpythonのバージョン違いまで吸収できるため、virtualenvの上位互換と言えます。
    • 事実、anacondaでvirtualenvを使おうとすると、condaで環境を作るように警告が出ます。(virtualenvが作れはします。)


データサイエンティストを目指す人のpython環境構築 2016 - Qiita」より


ということなので、virtualenv および virtualenvwrapper はインストール不要です。


 

9.3. pip

pip は Anaconda に含まれているので、インストールは不要です。

$ pip --version
pip 7.1.2 from /Users/akiyoko/.pyenv/versions/anaconda-2.4.0/lib/python2.7/site-packages (python 2.7)


 

9.4. tree

tree は macOS にデフォルトでインストールされていないので、インストールします。

$ brew install tree

 

9.5. wget

wget も macOS にデフォルトでインストールされていないので、インストールします。

$ brew install wget

 

9.6. Ansible

$ brew install ansible

$ ansible --version
ansible 2.2.1.0
  config file =
  configured module search path = Default w/o overrides


後でインストールする Vagrant で起動した VM(192.168.33.10 で起動中と仮定)に対して、疎通テストをしてみます。


まずは、インベントリファイルを、とりあえず ~/hosts あたりに作っておきます。

$ cat << EOF > ~/hosts
[webservers]
192.168.33.10

[all:vars]
ansible_ssh_user=vagrant
EOF

インベントリファイルを探索するルールについては、以下を参照のこと。
osx - Where to store Ansible host file on Mac OS X - Stack Overflow


実行結果。

$ ansible -i ~/hosts all -m ping -k
SSH password:
192.168.33.10 | success >> {
    "changed": false,
    "ping": "pong"
}




 

10. 開発系アプリ

10.1. iTerm2

公式ページ から最新版をダウンロードします。

f:id:akiyoko:20170210021920p:plain


iTerm を立ち上げて、Preferences から適宜設定を変更します。

f:id:akiyoko:20170210021942p:plain


[Profiles] > [Text]

とりあえず、フォントを 11pt くらいに変更。
f:id:akiyoko:20170210022000p:plain

[Profiles] > [Window]

背景の透明度を上げます。
f:id:akiyoko:20170210022019p:plain

[Profiles] > [Terminal]

「Unlimited scrollback」にチェックを入れます。
f:id:akiyoko:20170213211353p:plain



10.2. PyCharm

Professional Edition をインストールします。

f:id:akiyoko:20170212224535p:plain

【2017/3/10 追記】
PyCharm の環境設定について記事にまとめました。
akiyoko.hatenablog.jp


10.3. Vagrant & VirtualBox

Vagrant をインストールします。

Download - Vagrant by HashiCorp
f:id:akiyoko:20170212224402p:plain
f:id:akiyoko:20170213000516p:plain

$ vagrant --version
Vagrant 1.9.1


続けて、VirtualBox をインストールします。

Downloads – Oracle VM VirtualBox
f:id:akiyoko:20170212224559p:plain

「OS X hosts」からダウンロードします。
f:id:akiyoko:20170212224626p:plain
f:id:akiyoko:20170213000844p:plain

バージョンは 5.1.14 r112924 (Qt5.6.2) でした。




ちなみに以前は、Vagrantbox.es というサイトから Box を検索して「vagrant box add」で URL を登録していましたが、現在は Vagrant を開発している本家 HashiCorp が Box を配布するようになっています。

Discover Vagrant Boxes | Atlas by HashiCorp


なので、「Boxを追加する」という作業も不要になりました。
「vagrant init」で名前を指定した後、初回の「vagrant up」実行時にダウンロードが始まります。

$ mkdir -p ~/dev/vagrant/ubuntu14 && cd $_
$ vagrant init ubuntu/trusty64
$ vagrant up

 

10.4. Sequel Pro

MySQL クライアントには「Sequel Pro」を使っています。

https://sequelpro.com/download
からダウンロードします。

f:id:akiyoko:20170213232725p:plain


例えば、Vagrant(IPアドレスを「192.168.33.10」と想定)上の MySQL への接続設定はこちら。
f:id:akiyoko:20170213234528p:plain

EC2インスタンス(IPアドレス「52.xxx.xxx.xxx」、公開鍵認証)上の MySQL への接続設定はこちら。
f:id:akiyoko:20170213234640p:plain




 

11. その他系アプリ

11.3. Skitch

矢印をお絵かきするアプリ

f:id:akiyoko:20170210015644p:plain


「【eLV勉強会】Dockerを触ってみよう 〜 初心者向けDockerハンズオン 〜」に参加してきました

会場

CO-CreationLABO
東京都千代田区平河町1-4-3 平河町伏見ビル 2F

全体の感想など

仮想化技術と言うと、開発環境構築のために VMware や VirtualBox、Vagrant を利用することはあったのですが、Docker は今回が初体験でした。Docker については何となく知っていたのですが、やはり実際に触ってみると感覚として理解できますね。


たまたま積ん読になっていた Docker本があったので、勉強会の前にざっと目を通しておこうと思ったのですが、(良いことなのですが)詳しく書かれ過ぎていて結局核心の部分までたどり着かず。。


ついでに言うと、(「さくらのVPS」は以前に使っていましたが)「さくらのクラウド」も初、Docker 専用のホスティングサーバ「Arukas」も初体験でした。ちなみに、Arukas はさくらのインターネットが運営をしていて、今は無償公開中だそうです。



 

Dockerの基本的概念

横田真俊氏(さくらのインターネット)


  • Docker は PyCon 2013 で発表された
  • コンテナと仮想化の違い
  • コンテナはOSの代わりに各コンテナがアプリを稼働させる。ホストマシンの処理負荷が低い
  • コンテナのメリット
    • 処理速度が速い
    • メモリ・ディスクの消費量を抑えられる
    • 一番大きなメリットはポータビリティ


確かに、VirtualBox + Vagrant などの仮想化ツールで開発環境を構築するスタイルだとポータビリティ性に少し難があるように感じますし、仮想マシンの構成を管理するための Infrastructure as Code にしても「Infrastructure as Code の弊害 | Stay Creative !」で言及されているように実態とコードとの乖離が問題となります。そういった意味でも、Docker のポータビリティ容易性は大きな魅力です。

(参考)VagrantとDockerについて名前しか知らなかったので試した - Qiita




 

Dockerハンズオン

ハンズオン資料
qiita.com

  • インストールハンズオン
  • 基礎的なコマンド
  • アプリケーションをインストール
  • Dockerfileを書いてみよう
  • 応用編


 

さくらのクラウドで CentOS7 をインストール

さくらのクラウド にログインして「さくらのクラウド(IaaS)」をクリックします。

f:id:akiyoko:20170121020221p:plain

サービスご利用の流れ| さくらのクラウド」のフローに記載されているように、さくらインターネット会員ID(さくらのVPS を利用していれば既に持っているはず)があればコントロールパネルにログイン可能です。その後、「さくらのクラウド」アカウント(「さくらのクラウド」ユーザーも同時に作成可能)を作成して、クレジットカードを登録すれば利用を開始することができるようになります。


まずはリージョンを「東京第1ゾーン」に変更して、「追加」ボタンをクリック。
f:id:akiyoko:20170121021639p:plain

最小構成で CentOS サーバを作成します。
なお今回は、公開鍵の設定はせずにパスワード認証の設定を行います。
f:id:akiyoko:20170121021704p:plain


全てのステータスが「成功」になると、インスタンスの起動は完了です。
f:id:akiyoko:20170121022504p:plain


接続する IPアドレスは、サーバ一覧の画面から確認することができます。
f:id:akiyoko:20170208080557p:plain


クライアントのターミナルから、

$ ssh root@27.133.xxx.xxx

でログインすることができます(パスワードは作成時に入力したもの)。



コントロールパネルの[詳細]>[コンソール]から Webコンソールを操作することも可能ですが、動作がモッサリしているのでちょっと使い物にならない印象です。。
f:id:akiyoko:20170121022711p:plain
f:id:akiyoko:20170121022728p:plain


CentOS7 上で Docker をインストール

CentOS に Docker をインストールします。

### パッケージ情報を最新化
yum -y update

### docker-engine パッケージをセットアップ
curl -fsSL https://get.docker.com/ | sh

### サービスを有効化
systemctl enable docker.service

### docker デーモンを起動
systemctl start docker

### docker のバージョン確認
docker version


<実際のコマンド>

[root@akiyoko ~]# curl -fsSL https://get.docker.com/ | sh
+ sh -c 'sleep 3; yum -y -q install docker-engine'
warning: /var/cache/yum/x86_64/7/docker-main-repo/packages/docker-engine-selinux-1.12.6-1.el7.centos.noarch.rpm: Header V4 RSA/SHA512 Signature, key ID 2c52609d: NOKEY
docker-engine-selinux-1.12.6-1.el7.centos.noarch.rpm の公開鍵がインストールされていません
Importing GPG key 0x2C52609D:
 Userid     : "Docker Release Tool (releasedocker) <docker@docker.com>"
 Fingerprint: 5811 8e89 f3a9 1289 7c07 0adb f762 2157 2c52 609d
 From       : https://yum.dockerproject.org/gpg
setsebool:  SELinux is disabled.
Re-declaration of boolean virt_sandbox_use_fusefs
Failed to create node
Bad boolean declaration at /etc/selinux/targeted/tmp/modules/100/virt/cil:159
/usr/sbin/semodule:  Failed!
libsemanage.semanage_direct_install_info: Overriding docker module at lower priority 100 with module at priority 400.

If you would like to use Docker as a non-root user, you should now consider
adding your user to the "docker" group with something like:

  sudo usermod -aG docker your-user

Remember that you will have to log out and back in for this to take effect!

[root@akiyoko ~]# yum -y update
読み込んだプラグイン:fastestmirror, priorities
Loading mirror speeds from cached hostfile
No packages marked for update
[root@akiyoko ~]# systemctl enable docker.service
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.

[root@akiyoko ~]# systemctl start docker
[root@akiyoko ~]# docker version
Client:
 Version:      1.12.6
 API version:  1.24
 Go version:   go1.6.4
 Git commit:   78d1802
 Built:        Tue Jan 10 20:20:01 2017
 OS/Arch:      linux/amd64

Server:
 Version:      1.12.6
 API version:  1.24
 Go version:   go1.6.4
 Git commit:   78d1802
 Built:        Tue Jan 10 20:20:01 2017
 OS/Arch:      linux/amd64


 

コンテナを起動する(Docker run)

CentOS 上で Ubuntu を利用できるようにします。

### hello-world を起動
docker run hello-world

### Ubuntu コンテナを起動して Ubuntuを操作
docker run -it ubuntu bash

### Ubuntu バージョンを確認
cat /etc/issue


<実際のコマンド>

[root@akiyoko ~]# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
78445dd45222: Pull complete
Digest: sha256:c5515758d4c5e1e838e9cd307f6c6a0d620b5e07e6f927b07d05f6d12a1ac8d7
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://cloud.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/

[root@akiyoko ~]# docker run -it ubuntu bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
b3e1c725a85f: Pull complete
4daad8bdde31: Pull complete
63fe8c0068a8: Pull complete
4a70713c436f: Pull complete
bd842a2105a8: Pull complete
Digest: sha256:7a64bc9c8843b0a8c8b8a7e4715b7615e4e1b0d8ca3c7e7a76ec8250899c397a
Status: Downloaded newer image for ubuntu:latest

root@030555c1014f:/# cat /etc/issue
Ubuntu 16.04.1 LTS \n \l


 

起動した Ubuntu で curl を利用できるようにする

### Ubuntu のアップデート
apt-get -y update

### curl をインストール
apt-get -y install curl

curl ifconfig.me/ip

### exit でコンテナから抜けられる
exit

### 再度コンテナを動かしてみる
docker run -it ubuntu bash

### 再び IPアドレスを入れようとしても動かない
curl ifconfig.me/ip

exit すると、環境が消えてしまいます。

バックグランド(デタッチドモード)でコンテナを動かす

そこで、今度はバックグラウンドで動かしてみます。

### 今の時刻を毎秒表示するコンテナを起動する
docker run jpetazzo/clock

### バックグランド(デタッチドモード)でコンテナを動かす
docker run -d jpetazzo/clock
  • d オプションを付けると、バックグラウンドで動作させることができます。


 

基礎的なDockerコマンド

### docker ps で今動いているコンテナの確認
docker ps

### docker ps -a で今まで起動したコンテナを確認
docker ps -a

### 「docker ps -l」で直近に操作したコンテナを表示し「docker ps -q」でコンテナのショートIDが表示される。
docker ps -l
docker ps -q


<実際のコマンド>

[root@akiyoko ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
6f89ec594aa2        jpetazzo/clock      "/bin/sh -c 'while da"   5 minutes ago       Up 5 minutes                            suspicious_lalande
[root@akiyoko ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                       PORTS               NAMES
6f89ec594aa2        jpetazzo/clock      "/bin/sh -c 'while da"   5 minutes ago       Up 5 minutes                                     suspicious_lalande
00a52497540f        jpetazzo/clock      "/bin/sh -c 'while da"   6 minutes ago       Exited (0) 5 minutes ago                         admiring_volhard
2ec9602f7828        ubuntu              "bash"                   8 minutes ago       Exited (127) 6 minutes ago                       angry_engelbart
030555c1014f        ubuntu              "bash"                   14 minutes ago      Exited (130) 8 minutes ago                       mad_noether
83b477b7eb2a        hello-world         "/hello"                 16 minutes ago      Exited (0) 16 minutes ago                        reverent_booth
[root@akiyoko ~]# docker ps -l
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
6f89ec594aa2        jpetazzo/clock      "/bin/sh -c 'while da"   5 minutes ago       Up 5 minutes                            suspicious_lalande
[root@akiyoko ~]# docker ps -q
6f89ec594aa2


 

docker logs でコンテナのログを確認する

### コンテナのログを確認
docker logs 6f89ec594aa2

### 指定された数行だけログを確認
docker logs --tail 3 6f89ec594aa2

### リアルタイムでログを確認
docker logs --tail 1 --follow 6f89ec594aa2


 

docker kill、docker stopでコンテナを止める

### デタッチモードで起動したサーバを停止させる
docker kill 6f89ec594aa2
docker stop 6f89ec594aa2

kill よりも stop の方が安全に停止できるそうです。


 

Docker上で Ghost をインストールする

次は、簡易ブログツール「Ghost」を Docker を利用してインストールしてみます。
Ghost は、コマンド一発でインストール可能です。

### -p でポートマッピング可能(Ghost は 2368番ポートで動作する)
docker run -p 80:2368 -d ghost

### ブラウザに作成したサーバのIPアドレスを入力してアクセスができるか確認し、アクセスができるようであればコンテナを停止
docker kill (コンテナのID)


ブラウザから Ghost が動作しているのを確認することができます。
http://27.133.xxx.xxx/
http://27.133.xxx.xxx/admin

f:id:akiyoko:20170207233546p:plain

 

nginx コンテナを立ち上げてページを編集

下記コマンドで nginx を起動します。

docker run -itd -p 80:80 nginx:latest

起動後にブラウザから IPアドレスを入力して nginx の画面を表示し、正常に nginx が起動しているか確認をします。
f:id:akiyoko:20170207233636p:plain

 

nginx コンテナ上にページを作成

### docker psでnginxのコンテナIDを確認
docker ps

### docker exec コマンドで bash の追加プロセスを実行
docker exec -it $(docker ps -ql) /bin/bash

Ubuntu に入れた!

### ps -ef コマンドで nginx コンテナの中での操作を確認
ps -ef

### nginx のドキュメント・ルートにある index.html を書き換える
echo 'hello world' > /usr/share/nginx/html/index.html
cat /usr/share/nginx/html/index.html


ブラウザで確認します。

http://27.133.xxx.xxx/index.html
f:id:akiyoko:20170207233657p:plain


bashを終了します。

exit


 

nginx イメージの作成

### docker commit コマンドで mynginx:1.0 イメージを作成
docker commit $(docker ps -ql) mynginx:1.0

### docker images コマンドで、イメージが作成されたのを確認
docker images

### 作成した mynginx:1.0 イメージを使って、新しいコンテナを実行
docker run -d -p 8080:80 mynginx:1.0

### curl コマンドで、コマンドライン上でポート 8080 を開く
curl http://localhost:8080


<実際のコマンド>

[root@akiyoko ~]# docker commit $(docker ps -ql) mynginx:1.0
sha256:dde66d6cd18fc6d0ead8c88b460d9a54e4668471a44a66b281e37600192053c4
[root@akiyoko ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
mynginx             1.0                 dde66d6cd18f        About a minute ago   181.6 MB
ghost               latest              783cee09899e        11 hours ago         325.8 MB
nginx               latest              a39777a1a4a6        17 hours ago         181.6 MB
hello-world         latest              48b5124b2768        4 days ago           1.84 kB
ubuntu              latest              104bec311bcd        4 weeks ago          128.9 MB
jpetazzo/clock      latest              12068b93616f        23 months ago        2.43 MB

[root@akiyoko ~]# docker run -d -p 8080:80 mynginx:1.0
1d4f406be21b686906f8b68adc283660f3dc0aa762f7113708515c153e78ce49

[root@akiyoko ~]# curl http://localhost:8080
hello world


 

コンテナの停止と削除

### docker ps でコンテナの状態を確認
docker ps

### コンテナを停止するため docker kill コマンドを実行
docker kill $(docker ps -q)

### docker ps では実行中のプロセスはない
docker ps
### docker ps -a でコンテナが停止中であることを確認
docker ps -a

### docker rm コマンドでコンテナ(コンテナ用のイメージ・レイヤとメタ情報)を削除
docker rm $(docker ps -aq)
docker ps -a


 

Dockerfile でイメージの自動構築

### 作業用ディレクトリ mynginx を作成
mkdir mynginx
cd mynginx

### Dockerfile を作成
tee ./Dockerfile <<-'EOF'
FROM nginx:latest
RUN echo "hello world<br />$(date)" > /usr/share/nginx/html/index.html
EOF

### docker build コマンドで mynginx:1.1 イメージを自動作成
docker build -t mynginx:1.1 .

### イメージが作成されたことを確認
docker images

<実際のコマンド>

[root@akiyoko mynginx]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
mynginx             1.1                 b9f4ec36b1d3        17 seconds ago      181.6 MB
mynginx             1.0                 dde66d6cd18f        7 minutes ago       181.6 MB
ghost               latest              783cee09899e        11 hours ago        325.8 MB
nginx               latest              a39777a1a4a6        17 hours ago        181.6 MB
hello-world         latest              48b5124b2768        4 days ago          1.84 kB
ubuntu              latest              104bec311bcd        4 weeks ago         128.9 MB
jpetazzo/clock      latest              12068b93616f        23 months ago       2.43 MB
### 作成した mynginx:1.1 イメージを使って、新しいコンテナを実行
docker run -d -p 8888:80 mynginx:1.1

### curl コマンドで、コマンドライン上でポート 8888 を開く
curl http://localhost:8888

<実際のコマンド>

[root@akiyoko mynginx]# docker run -d -p 8888:80 mynginx:1.1
e3566e13cc33662496a82d13e598a04834b1b5f907ac502d55f6510e5c985107
[root@akiyoko mynginx]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                           NAMES
e3566e13cc33        mynginx:1.1         "nginx -g 'daemon off"   11 seconds ago      Up 10 seconds       443/tcp, 0.0.0.0:8888->80/tcp   elated_davinci
[root@akiyoko mynginx]# curl http://localhost:8888
hello world<br />Wed Jan 18 11:47:30 UTC 2017


 

Docker Hub にログイン

アカウント作成
https://hub.docker.com/ から登録し、ログインします。

f:id:akiyoko:20170207233923p:plain


 

サーバから Docker Hub にログイン

docker login コマンドを実行します。

<実際のコマンド>

$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: <自分のDockerHubID>
Password: <パスワード>
Login Succeeded

 

Docker イメージの登録と公開

イメージをタグ付けします。

### docker tags コマンドで mynginx:1.1 イメージに Docker Hub ID の情報をタグ付け
docker tag mynginx:1.1 <自分のID名>/mynginx:1.1

### docker images で確認
docker images

<実際のコマンド>

[root@akiyoko mynginx]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
mynginx             1.1                 b9f4ec36b1d3        7 minutes ago       181.6 MB
akiyoko/mynginx     1.1                 b9f4ec36b1d3        7 minutes ago       181.6 MB
mynginx             1.0                 dde66d6cd18f        15 minutes ago      181.6 MB
ghost               latest              783cee09899e        11 hours ago        325.8 MB
nginx               latest              a39777a1a4a6        17 hours ago        181.6 MB
hello-world         latest              48b5124b2768        4 days ago          1.84 kB
ubuntu              latest              104bec311bcd        4 weeks ago         128.9 MB
jpetazzo/clock      latest              12068b93616f        23 months ago       2.43 MB


 

docker push でイメージ送信

DockerHub に作成したイメージを送信します。

### docker push コマンドでイメージを送信
docker push <自分のID名>/mynginx:1.1


<実際のコマンド>

[root@akiyoko mynginx]# docker push akiyoko/mynginx:1.1
The push refers to a repository [docker.io/akiyoko/mynginx]
1022adbe77fc: Pushed
a03d7e02b0d4: Mounted from library/nginx
e04b871e18d3: Mounted from library/nginx
a2ae92ffcd29: Mounted from library/ghost
1.1: digest: sha256:df231d9fd41379c5565d1c529921bdb616fb3ec946f5b02d321bde0cd83d2059 size: 1155


Docker Hub 上にある自分のページを表示し、リポジトリを確認します。

f:id:akiyoko:20170207234126p:plain


不要なイメージの削除

### docker images -q を実行すると、ローカルにあるイメージ ID を表示
docker images -q

### docker rmi で全てのイメージを削除
docker rmi -f $(docker images -aq)


 

Arukas にログイン

DockerHub に登録したイメージを利用して、Arukas 上でアプリケーションを作成します。


https://app.arukas.io/
f:id:akiyoko:20170207234242p:plain

f:id:akiyoko:20170207234320p:plain


Arukas 上でアプリケーションを作成

ダッシュボードにて、「新しいアプリケーションを作成」ボタンをクリック。

f:id:akiyoko:20170207234354p:plain

アプリ作成に必要な各種情報を入力します。

App Name myapp
Image <DockerHubId>/mynginx:1.1(1.1 のタグを忘れずに!)
Port 80

アプリケーションを作成 ボタンをクリック。

f:id:akiyoko:20170207234415p:plain

一覧画面に作成されたアプリケーションが表示されます。


アプリの 起動 ボタンをクリック後、OK をクリックすると、 アプリが起動します。

f:id:akiyoko:20170207234434p:plain

起動後、 Endpoint の URL を開きます。

f:id:akiyoko:20170207234453p:plain

f:id:akiyoko:20170207234518p:plain

「ビットコインとか勉強会#4」に参加してきました

会場

株式会社ミクシィ 休憩スペース
東京都渋谷区東1-2-20(住友不動産渋谷ファーストタワー7F)

Twitter

twitter.com


仮想通貨と言えば・・。
Coincheck様、いつもお世話になっています。


ビットコイン取引高日本一の仮想通貨取引所 coincheck bitcoin
ビットコイン取引高日本一の仮想通貨取引所 coincheck bitcoin



 

全体の感想など

この年始にまた 急騰&暴落 した今アツアツのビットコインについての勉強会に初めて参加してきたのですが、新しく耳にする話ばかりですごく刺激的でした。

(参考)Bitcoin Price Index - Real-time Bitcoin Price Charts


さすがに事前情報無しでは全く着いていけないなと思ったので、事前に一冊読んでおきました。初歩の初歩から分かりやすく解説されていて、内容も非常に面白かったです。ビットコインの概要、メリット・デメリットなどがバッチリ理解できました。気軽に読めるページ量(75ページ)だったのも+1。



ちょうど勉強会の前日にアップされていたホットエントリーの記事です。こちらも非常に勉強になりました。

blog.clock-up.jp



あと、こちらの記事も分かりやすかったです。

bitcoin.peryaudo.org

Bitcoinは、

  • 電子署名を使い、コインを取引のかたまりとして表現し、
  • 二重譲渡をブロックチェーンと仕事の証明により防ぎ、
  • ブロックの生成者には報酬が与えられる。(マイニング)

うーん、実に簡潔で分かりやすいです。



ところで、「ビットコイン解説本」でも勉強会でも話に挙がったように、ビットコインは前途洋々、完全無欠の仮想通貨というわけではないようです。cameong 氏が懸念していたのは(例えば中国の)一国集中。半減期が来るなどして difficulty が上がって採掘に掛かるマシンパワーのコストに対して報酬がペイしなくなってくると、中国のチームだけがマイナーとして残っていくと予想され、そうなったときに例えば当局がグレートファイアーウォールで干渉したりすると、支払いがいつまで経っても承認されないという事態も起こり得るとのこと。

あるいは、2100万BTC が掘り尽くされた後の世界はどうなるのか。ビットコインが今後どうなっていくのか、どのように世界の経済を変えていくのか、まだまだ何が起こるか予測不可能な未来が待っているのだなぁと感じました。


あと、この暗号通貨読書会グループで「CoinTip」という Twitter 上で使える Bitcoin 投げ銭アプリを作ったらしいです。



 

ビットコイン 超入門概論(仮)

Kawagoe Koji 氏


  • 仕組み
    • ウォレット・・・ユーザがビットコインを管理するためのアプリ
    • トランザクション・・・ウォレットから他者への送金処理
    • マイニング・・・送金を承認し、新たなブロックを台帳(ブロックチェーン)に繋ぐ作業
  • 何が革新的か?
    • PoW(Proof of Work)と非中央集権

 

コマンドラインで色んな暗号通貨を送金してみよう

cameong


  • 今回紹介された仮想通貨
    • Bitcoin・・・王道。ブロック生成時間は10分と長め
    • NEM・・・PoI。Bitcoin 2.0(ユーザ独自通貨発行 etc)
    • Zcash・・・ゼロ知識証明。匿名性高い
  • 用語
    • Full node・・・全トランザクションを保有するノード。Bitcoin の全トランザクションは現時点で 100GBくらいある
    • SPV node・・・いわゆるライトウォレット。送金に特化したノード

2016年の akiyoko blog 振り返り

f:id:akiyoko:20170103161118j:plain:w500

明けましておめでとうございます。今年も小網神社に初詣に行ってきました。

というわけで新年明けまして少し経ちましたが、昨年(2016年)の akiyoko blog を振り返っておきたいと思います。


ちなみに 2015年の振り返りはこんな感じでした。

<過去記事>
akiyoko.hatenablog.jp



2016年の akiyoko blog 振り返り

2012年6月から開始したこのブログですが、昨年12月に累計 100万PVを突破していました。
ありがとうございます!


ところで、昨年一年間で作成した記事は合計 44本でした。

ブログを開始して以来初めて週一本ペースがキープできませんでした。しかしながら、実戦に力を入れなければいけなかった一年でブログをやり過ぎないように多少気をつけていたので、まあ仕方ないところでしょう。


全記事 44本中、Python 関連の記事が 18本で全体の 4割以上(そのうち Django 関連が 13本、Mezzanine 関連が 7本)、決済関連が 8本でした。Django や Mezzanine の記事を書いてもアクセスが全然跳ねないのは重々承知なのですが 他にネタもないので 書かないわけにはいきません。今後もどんどん書いていきますよー!


また昨年は、社外の勉強会に11回参加し、その内 8本を記事としてアップしましたが(とあるワークショップに参加した3回分については記事を書いていません)、こちらもアクセスは全然でした。まあ、こちらも想定内ですが。。


そして前回に引き続き、各記事ごとのアクセス数ランキングです。
昨年のアクセス数(*1)ランキング上位 30本をリストアップしてみたのですが、昨年の記事 44本中 30位以内に入ったのはわずか 6本でした。 *2


ブログ記事ごとのアクセス数ランキング(akiyoko blog 2016年)

# 昨年比 タイトル 作成日 ポイント
1 「プロジェクトマネージャ試験」に一発合格するための三か条 - akiyoko blog 2014/10/26 169.5
2 AppStore 登録前の iOSアプリを Ad-Hoc で配布してインストールする方法 - akiyoko blog 2014/08/23 87.4
3 Git で コミットを無かったことにする方法 (git revert の使い方) - akiyoko blog 2014/08/21 65.5
4 Video.js を使って HLS形式の動画をストリーミング再生する - akiyoko blog 2015/08/11 65.2
5 Python でリストのソートまとめ - akiyoko blog 2014/09/26 49.5
6 日本統計学会認定「統計検定2級」に合格しました - akiyoko blog 2014/12/30 39.1
7 「道は開ける」を読んだ - akiyoko blog 2014/04/13 36.2
8 「Python 3 エンジニア認定基礎試験」に合格しました! - akiyoko blog 2016/12/21 36.0
9 Windows で R(統計解析ツール)を使う - akiyoko blog 2014/11/07 33.3
10 初心者がプルリクまでに覚えるべきたった 9つの厳選 Gitコマンド - akiyoko blog 2014/12/02 29.5
11 Python で Selenium WebDriver を使ったブラウザテストをする方法 - akiyoko blog 2014/04/29 27.1
12 Backbone.js を基礎からやってみよう - akiyoko blog 2015/08/07 26.1
13 nodebrew で Mac の Node.js 環境をスッキリさせた - akiyoko blog 2015/06/20 21.8
14 Open BroadCaster Software (OBS) で YouTube ライブストリーミングを使った動画配信をする方法 - akiyoko blog 2016/01/26 20.4
15 jQuery で Ajax を使ってみる - akiyoko blog 2014/07/21 19.5
16 Ansible 初心者なら、まずは Ansible Galaxy から始めてみよう - akiyoko blog 2015/12/06 18.5
17 MongoDB の使い方まとめ - akiyoko blog 2014/08/01 18.3
18 知的財産管理技能検定2級を受験してきました - akiyoko blog 2012/11/25 17.4
19 Pythonでヒストグラム - akiyoko blog 2013/06/07 16.7
20 Apple Developer Program の有効期限が切れてしまったときの対処方法 - akiyoko blog 2015/11/13 16.3
21 iMovie の設定あれこれ - akiyoko blog 2016/02/01 15.5
22 今年の流行語大賞「JPAP」(Jupyter / Python / Anaconda / Pyenv)〜 ゼロからはじめる Jupyter Notebook 〜 - akiyoko blog 2016/12/06 15.3
23 iPad上でアプリ内課金 (In-App Purchase) の実機テストをする方法メモ - akiyoko blog 2013/11/09 15.0
24 初めてのTOEIC受験で800点オーバーを取るための勉強法 - akiyoko blog 2013/09/05 14.7
25 ベスト・オブ・Django本! - akiyoko blog 2016/12/05 14.6
26 Python, Django 界隈の単体テスト事情(unittest / nose / django-nose) - akiyoko blog 2015/01/01 13.9
27 【ポケモンGO】富士山頂でポケモン獲ったどーー! 頂上でゲットしたポケモンは一体何だった?? - akiyoko blog 2016/08/10 13.5
28 「AWS認定 ソリューションアーキテクト - アソシエイト試験」に合格しました - akiyoko blog 2015/06/09 13.3
29 Pythonのself - akiyoko blog 2012/06/27 12.6
30 見よ!これが Python製の WordPress風フルスタックCMSフレームワーク「Mezzanine(メザニン)」だ! - akiyoko blog 2015/12/23 11.2



#27 の「【ポケモンGO】富士山頂でポケモン獲ったどーー! 頂上でゲットしたポケモンは一体何だった?? - akiyoko blog」は、これまでないテイスト(技術的な事柄には一切触れていません)で実験的に書いた記事だったのですが、思いのほか伸びませんでした。。アクセスを集めるのは本当に難しいですね。

akiyoko.hatenablog.jp



そのほか、個人的に頑張って書いたのにアクセスが全然だったと感じている記事のワースト3は以下になります。

PayPal や Django に関する記事のニーズが単純に少ないというだけかもしれませんし、いずれも 10,000字を超える超長文記事だったので Google 神にスパムと見なされてしまったのかもしれません。。

暇を持て余している方は是非お読みになってくださいませ。

akiyoko.hatenablog.jp
akiyoko.hatenablog.jp
akiyoko.hatenablog.jp



今年の目標

ユーザーの満足度を示す指標値と巷で言われている「ページ/セッション」は「1.18」でした。前回「1.19」だったのでほとんど変わらずですが、昨年終盤に Google Analytics の数値がスパムに汚されてしまったので、信頼性は若干低いかもしれません。


昨年は長文で重めの記事が多かったので、今年はもう少し的を絞った記事を書いていきたいと思います。

あと、今年は仕事が忙しくなりそうなので投稿数は少なめになるかもしれません。
悪しからず。


 

*1:純粋な PV数ではなく、作成日からの日数で割ったポイントで算出しました。

*2:作成日のところを黄色く塗っています。

2016年に読んだ本のリスト

年末ということで今年の読書歴を振り返ってみたのですが、2016年は、雑誌やマンガ、技術系の本を除いて11冊の本を読んでいました。

今年はなるべく、流行りの本よりも多くの人に読まれている古典系の本を読みたいと考えていたのですが、本を読むのが本当に遅いので月にほぼ一冊ペースになってしまいました。来年はもう少しペースを上げて読んでいきたいと思います。


 

ビジネス系

1.ビジョナリー・カンパニー

オススメ度:★★★★★


今年のはじめに「リーン・スタートアップ ムダのない起業プロセスでイノベーションを生みだす」に続けて読んだ本。 「リーン・スタートアップ」がスタートアップ(*1)が成功するためのビジネス開発手法を説く本であるのに対して、「ビジョナリー・カンパニー」は時代を超えて繁栄する偉大な企業の「真理」を描き出した、1995年発刊の経営書です。

原題は「Built to Last: Successful Habits of Visionary Companies」で、永遠に続く企業の秘訣、という意味でしょうか。この本の一番の特徴は、厳しい条件から選び出した18社の偉大なトップ企業とそのライバル企業(前者を金メダリストとすると銀・銅メダリストクラス)を比較して、ビジョナリー・カンパニーをビジョナリー・カンパニーたらしめている要素をあぶり出すというその分析手法にあります。なお、紹介されている 36社のうち日本企業はソニーくらいで、あまり馴染みのない企業ばかりという印象が否めませんでした。


本書を貫く主題として、ビジョナリー・カンパニーの「ビジョン」は、

ビジョン=基本理念(長期に渡って維持される基本理念)+進歩(将来の理想に向けた進歩)

であり、基本理念を熱心に維持すると同時に、進歩を促すための具体的でしっかりした仕組みを持った組織をつくることが、ビジョナリー・カンパニーの基本とされています。またここで、基本理念は「我々が何者で、何のために存在し、何をやっているのかを示すもの」であり、

基本理念=基本的価値観(組織にとって不可欠で不変の主義)+目的(単なるカネ儲けを超えた会社の根本的な存在理由)

と書かれています。
なお、進歩については、ビジョナリー・カンパニーは組織に活力をみなぎらせるためにきわめて大胆で興奮を呼び起こす「BHAG(ビーハグ:社運を賭けた大胆な目標)」を掲げる傾向があるとし、さらに BHAG に続く第二の種類の進歩として「進化による進歩(枝分かれと剪定)」を積極的に促すとしています。


最後に、本書から気になった一節を抜粋。

ビジョナリー・カンパニーの創業者はどこまでも粘り抜き、「絶対に、絶対に、絶対にあきらめない」を座右の銘としている。しかし、何を粘り抜くのか。答えは会社である。アイデアはあきらめたり、変えたり、発展させることはあるが、会社は絶対にあきらめない。会社の成功とは、あるアイデアの成功だと考える起業家や経営幹部が多いが、(中略)善し悪しは別にして、ひとつのアイデアにこだわることなく、長く続くすばらしい組織をつくり上げることを目指して、粘り抜くことができる。

後に述べる「GRIT」にも繋がっている話で、非常に興味深い一節だと思いました。


 

2.ビジョナリー・カンパニー2

オススメ度:★★★★★


原題「Good to Great: Why Some Companies Make the Leap...And Others Don't」(良好から偉大へ)が示す通り、偉大な企業へ飛躍を遂げた企業の転換点に着目して分析し、前著「ビジョナリー・カンパニー」では取り上げられなかった「どうすれば偉大な企業になれるのか」「良好な企業から抜け出せない病を治療するにはどうすればよいのか」といったテーマを取り上げた、2001年発刊のベストセラー経営書です。

「ビジョナリー・カンパニー」シリーズは、2010年発刊の三作目「ビジョナリーカンパニー3 衰退の五段階」までが特に有名ですが(*2)、三作目は衰退した企業の特徴がテーマということで、まずは成功の秘訣を知りたいという経営者や起業家の興味から外れてしまい、一作目と二作目だけを読むという人も多いようです。あと、本の内容から二作目を読んでから一作目を読む方がよいと言う人も中にはいるようですが、私はそのまま一作目を読んでから二作目を読むというので全然問題ないと思いました。


内容をひと言で言えば、良い組織を偉大な実績を持続できる組織に変える法則として、

第五水準の指導者がいて、適切な人をバスに乗せ、厳しい現実を直視する規律を持ち、真実に耳を傾ける社風を作り出し、評議会を作って三つの円が重なる部分で活動し、すべての決定を単純明快な針鼠の概念に従って下し、虚勢ではなく現実の理解に基づいて行動すればよい。

と書かれています。

「ビジョナリー・カンパニー」シリーズを通して言えますが、独特な比喩や言い回しやあって、実際に本を読んでいない人には全然ピンと来ません。逆に、一度理解してしまえば長期間記憶が定着するのでしょう。


私が一番好きなフレーズは、針鼠の概念(本質を見極めて単純化した概念)で、

  • 自社が世界一になれるもの
  • 経済的原動力になるもの
  • 情熱を持って取り組めるもの

の交点を徹底的に見極め、それに矛盾しない社運を賭けた大胆な目標(BHAG)を立ち上げ、そこに向かっていくことがビジョナリー・カンパニーへの道だということです。

f:id:akiyoko:20170101112722p:plain:w500

そのほか全体を通して、規律の文化(規律のある人材・規律のある考え・規律のある行動)をいかに作り上げるか、が組織にとって非常に重要とのことです。


最後に、「ストックデールの逆説」と著者が呼んでいる一節があるのですが、

どれほどの困難にぶつかっても、最後には必ず勝つという確信を失ってはならない。そして同時にそれがどんなものであれ、自分が置かれている現実の中でもっとも厳しい事実を直視しなければならない。

これも GRIT に通じる考え方なのかな、と思いました。



 

3.ゼロ・トゥ・ワン

オススメ度:★★★★★


PayPal 創業者で、最近ではドナルド・トランプの政権移行チームのメンバーにもなったという起業家で投資家の ピーター・ティール の本です。ビジョナリー・カンパニー1・2の二冊とまとめて一気読みしました。

著者のピーター・ティールは採用面接で「賛成する人がほとんどいない、大切な真実とは何か?」と必ず訊くと言います。その理由は、本当に社会のためになる新しいものを生み出す(ゼロから1を生み出す)ためにはこれまでと「違う」ものに着目しなければならず、それが新たな市場の独占を可能にし、企業に利益をもたらすからだと説きます。

要するに「常識を疑え」ということなのですが、未来はどうなるか分からないという「常識」さえも疑ってしまえ、というのが彼の考え方です。

2014年発刊のこの本は、2000年4月に崩壊したドットコム・バブルの反省から生まれた「リーン」(計画せずに製品を柔軟に改良するという開発手法)の考え方は現代のスタートアップビジネスには合わないとし、そのアンチパターンとして、

  • 大胆に賭ける
  • 成功を実現するための計画をする(出来が悪くても無いよりはマシ)
  • 競争せず独占する
  • 製品だけでなく販売にも力を入れる

を良しとしています。

f:id:akiyoko:20170101185655p:plain

また最後に、優れたビジネスプランは、次の七つの質問にしっかりと答えを出すことが求められると書いています。

  • エンジニアリング(ブレイクスルーとなる技術を開発できるか?)
  • タイミング(今がこのビジネスを始めるのに適切なタイミングか?)
  • 独占(大きなシェアが取れるような小さな市場から始めているか?)
  • 人材(正しいチーム作りができているか?)
  • 販売(プロダクトを届ける方法があるか?)
  • 永続性(この先10年、20年と生き残れるポジショニングができているか?)
  • 隠れた真実(他社が気付いていない独自のチャンスを見つけているか?)


「ビジョナリー・カンパニー」「リーン・スタートアップ」を読んで、この「ゼロ・トゥ・ワン」までを一気に読むと、主張の違いや同じ考え方の部分が明確になって非常に面白かったです。


 

4.フリー

オススメ度:★★★☆☆

フリー(無料)経済を事例を交えて丁寧に解き明かした本。年末に読んだのですが、想像以上にボリュームがあって読むのにすごく時間が掛かってしまいました。

著者の クリス・アンダーソン は、米「WIRED」紙の元編集長で、「ロングテール」の名付け親として有名です。


まず、著者はフリー経済を以下の四つに大別しています。

  • ① 直接的内部相互補助 ・・・フリーでない他のものでフリーを補填する
  • ② 三者間市場 ・・・第三者がスポンサーとしてお金を払う
  • ③ フリーミアム ・・・少数の有料利用者が無料利用者を支えるモデル
  • ④ 非貨幣市場 ・・・評判や関心を動機とした贈与経済や無償の労働など

さらに、

潤沢な情報は無料になりたがる(逆に、稀少な情報は高価になりたがる)

という「フリーの万有引力」が働くため、低い限界費用で複製・伝達できる情報を扱う場合はこれまでとは違った新しいビジネスの仕組みを考える必要があり、例えばフリーによって得た評判や注目をどのように金銭に変えるかを創造的に考えなくてはならない、としています。


なおこの本自体も、出版された当初に期間限定でデジタル版が無料公開されていたそうです。


 

教養系

2016年の中旬は、教養とは何か?というのをふと考えていました。

5.ビジネスに効く最強の「読書」

オススメ度:★★★★★

著者の 出口治明 氏は、ライフネット生命の代表取締役会長兼CEOで、無類の読書家で旅行好きとしても知られています。ビジネス本も多数執筆していますが、何冊か教養についての本を出していてその中の一冊がこの本です。


本書では、教養については、

人間が賢くなる方法は、人に会い、本を読み、世界を旅すること以外にない

と書かれていて、読書については、

読書は知識を得るためではなく、自分の頭で考える材料を得るためにある

と書かれています。なるほど、「本の中に答えはない。本を材料にして自分自身で考えよ」ということでしょうか。読書で考え方の幅を広げる訓練をすることで教養が磨かれるということであれば、読書は「量」というよりも「質」が物を言うのかもしれません(もちろんある程度の量は必要という前提で)。


なお本書は、目次が

  • Part 1:リーダーシップを磨くうえで役に立つ本
  • Part 2:人間力を高めたいと思うあなたに相応しい本
  • Part 3:仕事上の意思決定に悩んだ時に背中を押してくれる本
  • Part 4:自分の頭で未来を予測する時にヒントになる本
  • Part 5:複雑な現在をひもとくために不可欠な本
  • Part 6:国家と政治を理解するために押さえるべき本
  • Part 7:グローバリゼーションに対する理解を深めてくれる本
  • Part 8:老いを実感したあなたが勇気づけられる本
  • Part 9:生きることに迷った時に傍らに置く本
  • Part 10:新たな人生に旅立つあなたに捧げる本

といった章立てになっていて目的別にカテゴリ分けされて本が紹介されているため、興味のあるところからどこからでも読み始めることもできます。

いろいろなジャンルから108冊の本が紹介されていますが、歴史書や小説が意外と多い印象を受けました。歴史書については、

様々な角度から「歴史」を知って、他人と自分の「今」を俯瞰的に眺め、さらに時間軸、空間軸から自分の立ち位置を読み取る。そうした感覚を研ぎ澄ませることは、ビジネスの世界を生きるうえでも大きな武器になるに違いありません。歴史は間違いなくビジネスに効くのです。

と言っています。なお、古典については、

ビジネスを進める上で本当に役立つ本は、圧倒的に古典に多いと思います。なぜなら、そこには様々な人間がリアルに描かれているからです。つまり、古典を読めば人間力(人間に対する観察力)が高まるのです。ビジネス書を10冊読むよりも、古典1冊を読んだ方がよほど役に立つ。

と考えており、「韓非子」「ブッデンブローク家の人びと」「王書」の三冊を紹介しています。


最後に、紹介されていた108冊から私が個人的に気になった本をいくつかピックアップ。


 

6.おとなの教養

オススメ度:★★★☆☆

テレビでお馴染みの池上彰さんの本。

「私たちはどこから来て、どこへ行くのか?」という問いかけに答えるための真摯な取り組みが「リベラルアーツ」という古来からある学問であり、様々な偏見や束縛から逃れ、自由な発想や思考を展開していくことができる教養を学べるものとしています。

また教養とは、「進歩の速い世の中にあっても陳腐化しないスキル」であり、

すぐに役に立つことは、世の中に出て、すぐ役に立たなくなる。すぐには役に立たないことが、実は長い目で見ると、役に立つ。

ということが書かれています。「長い人生を生きていく上で、自分を支える基盤になるもの」というのが教養である、という考え方です。

先の「ビジネスに効く最強の「読書」」のように本の紹介をするというスタイルではなく、宗教、宇宙、人類の起源、免疫学、経済学、歴史、日本の起源などの各ジャンルごとに池上さんが分かりやすい説明をしていくというスタイルの本です。


7.孫子・戦略・クラウゼヴィッツ

オススメ度:★★★★☆

「孫子」と「戦争論」という二大戦略書を対比させながら分かりやすく解説した本。タイトルに惹かれて買ったのですが、たまたま今年の4月頃に gacco の「中国古典に見る指導者の条件」というオンライン講座を受講していたのですが、この本の著者である 守屋淳 氏が件のオンライン講座の講師をされていた 守屋洋 教授のご子息だったのはちょっとした偶然でした。

以前、会社の勉強会で「孫子 (戦略論大系)」を輪読していたことがあり孫子は少し囓っていたのですが、クラウゼヴィッツは初(と思ったら、マンガ版「戦争論 ─まんがで読破─」を読んだことがありました。面白くないのでオススメしませんが・・)。曰く、軍人目線、一対一で戦争を考えるクラウゼヴィッツに対して、政治家目線で複数のライバルを視野に入れる孫武ということで、経営者や起業家であれば孫子の兵法の方が合っているように感じました。しかしながら、政治家や君主によって開戦を決定づけられた後に表舞台に立たされるというクラウゼヴィッツの立場を考えると、プロジェクトマネージャーであれば戦争論の方がしっくり来るのかもしれません。


本書では「王道のクラウゼヴィッツ」に対して「詭道の孫子」とも書かれていますが、どちらの戦略書が勝っているかというよりは、両者の置かれた背景に大きな違いがあるため一概に優劣はつけられません。繰り返し同じ相手と戦うことがあり負けてもある程度やり直しが利くナポレオン時代のプロイセンと、周囲をライバルに囲まれ不敗が求められた戦国時代の呉ではあまりにも状況が違い過ぎます。またここで筆者が、戦争論が野球選手や囲碁・将棋の棋士たちの状況に当てはまるとして、落合博満氏の「勝負の方程式」や羽生善治氏の「決断力 (角川新書)」から考え方を引いているのが斬新でした。


なお教養については、筆者は、

教養とは、新たな視点、異なる立場の宝庫に他ならない

と言っています。
いろいろな視点や立場を比較して俯瞰することで、教養が磨かれていくのでしょう。



 

GRIT(グリット)系

8.やり抜く力

オススメ度:★★★★★

教育業界で話題になっている「GRIT(グリット)」(*3)の本。ひと言で言えば、「努力と才能のどちらが大事か?」というテーマについて書かれた本でしょうか。

著者は心理学者の アンジェラ・ダックワース 教授です。専門は心理学者ということですが、本の内容はデータ重視で説得力があります。この本を読んでから、TED の「アンジェラ・リー・ダックワース 『成功のカギは、やり抜く力』」を見ると、動画の内容がさらによく理解できます。


www.ted.com


本書では、GRIT(やり抜く力)とは「情熱」+「粘り強さ」とされ、両者は一見似ていますが、

  • 「情熱」・・・自分の最も重要な目標に対して興味を持ち続け、ひたむきに取り組むこと
  • 「粘り強さ」・・・困難や挫折を味わってもあきらめずに努力を続けること

として明確に区別しています。またさらに情熱は、「興味」と「目的」に支えられているとしています。


才能がなくても一万時間練習を続ければその道のエキスパートになれるというマルコム・グラッドウェルが提唱する「一万時間の法則」というものがありますが、本書では、エキスパートたちはただ何千時間もの練習を積み重ねているだけではなく、「意図的な練習」(並外れた努力を要する極めて過酷な練習)をおこなっており、楽な練習はいくら続けても意味がないと言います。なるほど言われてみれば確かにそうで、単に惰性で続けていても得られるものは少ないですよね。小学校の頃に何年もスイミングスクールに通っていたのに、嫌々続けていたからか全然上手くならなかったつらい過去を思い出しました。。


また成功の秘訣は、「GRIT」に加えて「成長思考のマインドセット」としていて、子どもの頃の「褒められ方」が一生を左右するとも述べています(才能ではなく努力を褒める方がよい)。

以前に読んだ「「学力」の経済学」でも、具体的な努力を褒めた場合は子どもの成績が伸びたのに対して子どもの元々の能力を称賛した場合は成績が落ちたということが実験結果から実証されており、本書の主張とも合致します。


他にもいろいろな教訓や示唆があり、大変興味深かったです。教育関係の人は必読かも。


 

9.Think Simple

オススメ度:★★★☆☆

GRIT の本に続けて、スティーブ・ジョブズの本を読みました。

これまでスティーブ・ジョブズの本としては、「スティーブ・ジョブズ I」「スティーブ・ジョブズ II」「スティーブ・ジョブズ 驚異のプレゼン」を読んできましたが、スティーブ・ジョブズの特徴を表す「現実歪曲フィールド」と双極をなす「シンプルの杖」(シンプルに対する熱狂的哲学)をフィーチャーした本です。

シンプルであることは、複雑であることよりも難しい。
物事をシンプルにするためには、懸命に努力して思考を明瞭にしなければならないからだ。
だが、それだけの価値はある。
なぜなら、ひとたびそこに到達できれば、山をも動かせるからだ。

とスティーブ・ジョブズが言ったように、彼は不可能を疑い、逆境を逆境と思わず、自分が信じるものに対しては最後までやり抜くという「GRIT」を具備した人物であると感じました。


 

10.志高く 孫正義正伝

オススメ度:★★★☆☆

続けて、スティーブ・ジョブズとも交流があった、ソフトバンクグループ代表取締役社長の 孫正義 氏の半生を描いた伝記を読みました。生い立ちから少年時代、トタン屋根の会社で始めたソフトバンクを売上高9兆円の一大グループ企業まで育て上げた 2015年までの、地道な努力を惜しまず本質を見極めて素早く対処する「行動力」、人に喜んでもらえる仕事がしたいという「情熱」、志を絶対に成し遂げるという「意思」について、孫氏の類稀なる非凡なエピソードが描かれていました。


ここまで「やり抜く力」「Think Simple」「志高く 孫正義正伝」と続けて読んできて、この三冊が私の中で「GRIT」という点で繋がりました。

スティーブ・ジョブズと孫正義氏は両氏とも、青年時代の成功体験によって世界は変えられると信じた「成長思考」の持ち主であり、自分が信じるものに対しては最後までやり抜く「GRIT」の精神を兼ね備えた人物です。結果論かもしれませんが、GRIT と成功の繋がりを強く感じることができました。



その他

11.究極のセールスレター

オススメ度:★★★★☆

ライティングのコツを身につけるために、ライティングのレジェンドと言われる ダン・ケネディ の本を今年の半ばに読んでみました。具体的なライティングのコツについて書かれた、超実践的な本です。

2006年に出版された少し古い本で、元々はセールスレター向けに書かれたものですが、DM や SEO にも応用が効くと思います。

  • セールス術とは技術であり、慣れである
  • 買うのは感情(買ってから自分の選択を理屈で正当化する)
  • 送り手の関心事ではなく、受け取り手の関心事に単刀直入に的を絞れ
  • オファーの不利な点を認めて正直に伝えると、信頼できると評価してもらえる
  • AIDA の法則
  • 喜んで受け取ってもらうには、重要で、知る価値があって、ためになると思ってもらえることを伝えればよい
  • 価格の件が曖昧になるような比較をする
  • 問題を誇張し、まったくひどい災難だと思わせる
  • 焦らせる、投資収益率を説明する、自尊心に訴える、しっかり保証する
  • 見込み客がどんな反応をするかをしっかり理解する。疑問や反論の起こりそうなあらゆる可能性に用心深く対処する
  • 買ってくれそうな人のために書く
  • 何度でも言う。単刀直入に、例を挙げて、実話で、証言・証明で、お客・専門家・その他の代弁者の言葉で、番号を振った要約で
  • 簡単に「うん」とうなづいてもらえるような質問を次々にすることで同意する癖をつけさせる

これらを思い浮かべながら深夜のテレビショッピングを見ていると、実際これらのテクニックを意識して作られているなぁと恐ろしいほど実感することができます。

*1:本書では「とてつもなく不確実な状態で新しい製品やサービスを創り出さなければならない人的組織」を指すとされる。ベンチャー企業とスタートアップの違いについては、ベンチャー企業とスタートアップの違い | freshtrax | デザイン会社 btrax ブログ も参照

*2:2012年発刊の第四弾「ビジョナリーカンパニー4 自分の意志で偉大になる」もあります。

*3:GRIT は、Guts(度胸)、Resilience(復元力)、Initiative(自発性)、Tenacity(執念)の頭文字とされています。

Stripe 決済の最新事情 〜 Django と Stripe と私 〜

この投稿は 「Django Advent Calendar 2016 - Qiita」 の 4日目の記事です。



今日は クリスマス Advent Calendar の最終日ですが、4日目の記事を書いています。
というのも、自分が担当した 12/5 の「ベスト・オブ・Django本! - akiyoko blog」の前日だけ何故かずーっと空いていて、気持ち悪かったので。。

f:id:akiyoko:20161224150324p:plain



この記事では、「Django で Stripe 決済」を実装・検証します。


Django Advent Calendar 22日目に書いた「Django と Paypal と私(主に PayPal 決済の最新事情について) - akiyoko blog」のスピンオフ的な位置付けです。


<過去記事>
akiyoko.hatenablog.jp



 

Stripe とは

まず、「Stripe とは何ぞや?」から説明します。

Stripe は、2016年10月に日本で本番サービスイン した、新しい決済代行サービスです。後発サービスだけに、導入が簡単だったり、API が洗練されていて使いやすかったり、管理画面(ダッシュボード)がスッキリしていて直感的に分かりやすかったりと、なかなかイケてるサービスなのです。

なお、決済サービスの接続方式は「直接決済方式」で、他のサイトへの画面遷移を伴わず、離脱が少ない決済フローが実現できるのも特徴です。 *1


本社は米国カリフォルニア州サンフランシスコで、立ち上げが 2011年という比較的新しいスタートアップですが、今や世界中で数千社、年間数十億ドルの支払いを処理しており、飛ぶ鳥を落とす勢いの一大企業にまで成長しています。 *2


現時点で 世界25ヶ国でサービスを展開 しており、日本企業であれば、世界中のあらゆる国のエンドユーザから利用してもらうことが可能です。ちなみに私は、これを「世界25ヶ国でしか使えない」と勘違いしていたのですが、気になって問い合わせてみたところ、

「25 ヶ国に展開」という意味につきましては、Stripeのアカウントを作成し、オンラインで決済を受けることができる国数が25か国、という意味となります。


御社のサービス・プロダクトを購入されるエンドユーザに関しては、国の制限なく利用可能です。御社のターゲットとなるお客様に合わせた通貨を指定することで、その通貨でエンドユーザは決済を行うことができ、購入体験を損なうことなく決済が完結いたします。そして、日本以外の通貨でお支払いを受けた際には、Stripeが自動で円換算を行い、御社の銀行口座へ毎週お振込みいたします。


という丁寧な返事がすぐに返ってきました。
私の勘違いで危うく Stripe を候補から外してしまうところでした。。 念のため問い合わせしておいて良かったぁ。俺、グッジョブ!!



なお、私が必要としている決済サービスとしては、

  • 海外からの決済が可能(JPY以外の通貨が扱える) *3

というのを最低条件としていて、調査前は PayPal を第一候補、Stripe を第二候補と考えていたのですが、実際に両者を触って検討してみた現在の率直な感想では、Stripe の方が使い勝手が良さそうな印象です。


PayPal の最新事情については、以下の過去記事を参考にしてください。


<過去記事>
akiyoko.hatenablog.jp



Stripe については、以下の公式サイトのほか、


以下のサイトを参考にしました(本番サービスイン前の少し古い情報もあるのでご注意を)。


 

Stripe のメリットとデメリットは?

私が思いつく限りの、Stripe のメリット(プラス面)とデメリット(マイナス面)を挙げてみます。

メリット(プラス面)
  • 固定費用なし(決済成立ごとに手数料 3.6%) *4
  • 導入(≒実装)がめちゃくちゃ簡単
  • 直接決済方式(画面遷移がない)のため、離脱が少ない
  • クレジットカード情報の入力フォームを用意しなくても、checkout.js や stripe.js でセキュア(PCI-DSS に準拠)なフォームを簡単に作成できる
    • フォームの自動ローカライズにも対応 *5
  • 管理画面(ダッシュボード)が見やすい
  • 三井住友カードが全面バックアップしている(らしい)ので安心 *6
  • 売上金の円建て振り込みが可能(手数料は週一回の振り込みまで無料)
  • 定期支払い(継続課金)も利用可能
  • Stripe Radar, CVC, ZIP code などによる不正使用対策が充実

 

デメリット(マイナス面)
  • 現時点で JCB カードが利用不可 *7, *8
  • 銀行振込み・コンビニ支払いは利用不可(クレジットカードのみ)


現状、大きなデメリットは JCBカードが使えないというほぼ一点のみかと思われます。
それを許容できるのであれば、Stripe は決済サービスの筆頭候補になり得るはずです。


(参考)


 

Stripe 決済の仕組みは?

Stripe が推奨しているオンライン即時決済フローは、下図のようなものになります。
f:id:akiyoko:20161224150344p:plain

① ショッピングカート画面(「Stripe で支払う」ボタン)
    ↓
② 決済モーダルウィンドウ(「支払う」ボタン)
    ↓
③ 決済完了画面


 
すごくシンプルですよね。

Stripe 決済の接続方式は「直接決済方式」で、Stripe サイト等にリダイレクトされることなく決済を完了させることができます。つまり、エンドユーザが「Stripe」を意識することがないので、ユーザの離脱防止に効果が期待できます。




次に、Stripe がユーザのクレジット情報を処理するまでの詳細な仕組みについて、順を追って説明します。

Step 1:Securely collecting payment information

(参考)Card Payments Quickstart

  1. フォームに Stripe 謹製の「checkout.js」を設置しておく。
  2. ユーザがボタンをクリックすると、クレジットカード(およびその他の)情報を入力するためのモーダルウィンドウが立ち上がる。
  3. モーダルウィンドウ内でクレジットカード情報を入力して決済ボタンをクリックすると、checkout.js が裏側で Stripe の API とやり取りをおこない、数分間だけ有効になるトークンを生成してから、自サーバのアクションに POSTリクエストを実行する(トークンを「stripeToken」というパラメータで送信)。

 

Step 2:Creating Charges

(参考)Card Payments Quickstart

  1. 自サーバ側でトークンを受け取り、Stripe の API を利用して決済処理を実行する(トークンも渡す)


たったこれだけ。

フォームに埋め込むスクリプトもほんの数行だけですし、サーバ側の処理もリクエストからトークンを受け取って Stripe API を使って即時決済を実行するだけです。


ユーザが入力したクレジットカード情報は、Stripe API によって自動的にトークンとして自前に暗号化され、自サーバ側には流れてこないので安全 です。ただし、Stripe API とのやり取りをセキュアに保つためには、フォームを置くサイトを SSL化しておく必要があります。



なお、Step 1 でトークンを生成する手段は、

  • Checkout.js(簡単標準フォーム)
  • Stripe.js(カスタマイズ可能なフォーム)
  • Mobile SDKs

の 3種類が用意されており、ECサイトのオンライン決済では上の二つのいずれかを利用すればよいでしょう。

なお、checkout.js のパラメータをいろいろとカスタマイズできるようになっているので、ほとんどの場合は標準フォーム(checkout.js)の方で事足りるのではないでしょうか。

(参考)Checkout Reference



 

実装

購入する商品と合計金額が表示されたカート画面から Stripe 決済をおこなうことを想定した、「shop」アプリケーションを作ってみます。

/opt/webapps/myproject/
├── config
│   ├── __init__.py
│   ├── local_settings.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
├── shop
│   ├── apps.py
│   ├── __init__.py
│   ├── urls.py
│   └── views.py
└── templates
    ├── base.html
    ├── error.html
    └── shop
        ├── base_shop.html
        ├── cart.html
        └── complete.html


動作確認をおこなった Python および Django のバージョンは以下の通りです。

  • Python 2.7.6
  • Django 1.10



ソースコードは GitHub に置きました。
github.com



 

Settings

conf/settings.py (抜粋)

# Application definition

INSTALLED_APPS = [
    ...
    'shop',
]
...
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        ...
        },
    },
]
...
# LOCAL SETTINGS
PROJECT_APP_PATH = os.path.dirname(os.path.abspath(__file__))
PROJECT_APP = os.path.basename(PROJECT_APP_PATH)
f = os.path.join(PROJECT_APP_PATH, 'local_settings.py')
if os.path.exists(f):
    import sys
    import imp
    module_name = '%s.local_settings' % PROJECT_APP
    module = imp.new_module(module_name)
    module.__file__ = f
    sys.modules[module_name] = module
    exec (open(f, 'rb').read())


conf/local_settings.py (抜粋)

DEBUG = True

##############
# STRIPE KEY #
##############
STRIPE_API_KEY = '<stripe-api-key>'
STRIPE_PUBLISHABLE_KEY = '<stripe-publishable-key>'


上記 2つのキーには、管理画面(ダッシュボード)の[Your account]>[Account settings]>[API Keys]で確認できる、「Test Secret Key」と「Test Publishable Key」をそれぞれ設定します。

https://dashboard.stripe.com/account/apikeys
f:id:akiyoko:20161224194651p:plain



 

URLConfs

shop/urls.py

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^cart$', views.ShowCartView.as_view(), name='cart'),
    url(r'^checkout$', views.CheckoutView.as_view(), name='checkout'),
]

 

Views

shop/views.py

import logging

from django.conf import settings
from django.contrib import messages
from django.shortcuts import render
from django.views.generic import View
import stripe

logger = logging.getLogger(__name__)


class ShowCartView(View):
    def get(self, request, *args, **kwargs):
        return render(request, 'shop/cart.html', {
            'data_key': settings.STRIPE_PUBLISHABLE_KEY,
            'data_amount': 500,  # Amount in cents
            'data_name': 'akiyoko blog',
            'data_description': 'TEST',
        })


class CheckoutView(View):
    def post(self, request, *args, **kwargs):
        # Set your secret key: remember to change this to your live secret key in production
        # See your keys here: https://dashboard.stripe.com/account/apikeys
        stripe.api_key = settings.STRIPE_API_KEY

        # Get the credit card details submitted by the form
        token = request.POST['stripeToken']

        # Create a charge: this will charge the user's card
        try:
            charge = stripe.Charge.create(
                amount=500,  # Amount in cents
                currency='usd',
                source=token,
                description='This is a test.',
            )
        except stripe.error.CardError as e:
            # The card has been declined
            return render(request, 'error.html', {
                'message': "Your payment cannot be completed. The card has been declined.",
            })

        logger.info("Charge[{}] created successfully.".format(charge.id))
        messages.info(request, "Your payment has been completed successfully.")
        return render(request, 'shop/complete.html', {
            'charge': charge,
        })

 
バックエンドの実装は、Card Payments Quickstart のサンプルのほぼコピペです。



なお、Stripe API を利用するための Python パッケージが必要となるので、pip でインストールしておきます。

$ pip install stripe

 

Templates

shop/cart.html

{% extends "./base_shop.html" %}

{% block title %}Cart{% endblock title %}

{% block content %}
{{ block.super }}
<form action="{% url 'shop:checkout' %}" method="POST">
    <script
        src="https://checkout.stripe.com/checkout.js" class="stripe-button"
        data-key="{{ data_key }}"
        data-amount="{{ data_amount }}"
        data-name="{{ data_name }}"
        data-description="{{ data_description }}"
        data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
        data-locale="auto">
    </script>
    {% csrf_token %}
</form>
{% endblock content %}


POST送信時に「csrfmiddlewaretoken」というパラメータを付与しないと 403エラーになるのは、Django に詳しい皆さんには釈迦に説法ですよね。



shop/complete.html

{% extends "./base_shop.html" %}

{% block title %}Complete{% endblock title %}

{% block content %}
<span style="font-size: 0.7rem;">{{ charge }}</span>
{% endblock content %}


 

動作確認

最後に、実際に検証環境で画面を動かしながら、動作を確認してみます。


ブラウザで「http://localhost:8000/shop/cart」にアクセスし、Stripe のチェックアウトボタン(今回は公式サンプルと同じく「Pay with Card」のまま)をクリックします。
f:id:akiyoko:20161224210831p:plain

モーダルウィンドウが起動して、クレジットカードの入力フォームが表示されるので、テスト用のクレジットカード情報(*9)を入力して、支払いボタンをクリックします。(なお、テスト用クレジットカード番号を入力した場合は、カードの有効期限、CVC は適当で構いません。)
f:id:akiyoko:20161224210853p:plain

自サーバ側で Stripe API の即時決済が実行され、shop/complete.html に charge オブジェクトの中身がダンプされました。
f:id:akiyoko:20161224210908p:plain



 

Django ベースの ECパッケージとの連携

これまで、クレジットカードの入力フォームを用意していない場合に Stripe の標準フォーム(checkout.js あるいは stripe.js)を利用する方法について解説してきました。

Django Oscar*10)や Cartridge*11)などの Django ベースの ECパッケージを使ってクレジットカード情報の入力フォームを利用している場合は、さらに簡単に Stripe API を使うことができます。


例えば、このような感じで(Stripe の標準フォームを使わなくても)サーバ側の処理のみで決済を完結させることができます。


cartridge/shop/payment/stripe_api.py

def process(request, order_form, order):
    """
    Payment handler for the stripe API.
    """
    data = {
        "amount": int((order.total * 100).to_integral()),
        "currency": getattr(settings, "STRIPE_CURRENCY", "usd"),
        "card": {
            'number': request.POST["card_number"].strip(),
            'exp_month': request.POST["card_expiry_month"].strip(),
            'exp_year': request.POST["card_expiry_year"][2:].strip(),
            'cvc': request.POST["card_ccv"].strip(),
            'address_line1': request.POST['billing_detail_street'],
            'address_city': request.POST['billing_detail_city'],
            'address_state': request.POST['billing_detail_state'],
            'address_zip': request.POST['billing_detail_postcode'],
            'country': request.POST['billing_detail_country'],
        },
    }
    try:
        response = stripe.Charge.create(**data)
    except stripe.CardError:
        raise CheckoutError(_("Transaction declined"))
    except Exception as e:
        raise CheckoutError(_("A general error occured: ") + str(e))
    return response.id

https://github.com/stephenmcd/cartridge/blob/0.12.0/cartridge/shop/payment/stripe_api.py#L24-L49




ほかにも、dj-stripe というパッケージは、「Two Scoops of Django: Best Practices for Django 1.8」の著者として有名な Daniel Greenfeld もプロジェクトに参加しているため、比較的信頼できるプロダクトになっていると推測されます(私自身は全然使ったことはありませんが)。




 

まとめ

Stripe を使ってみた印象をひと言で言うと、「超絶シンプル」です。これ以上簡単に使える決済サービスが果たしてあるのか?と思ってしまうくらいシンプルです。


PayPal や GMOペイメントゲートウェイなど他の決済代行サービスと比較して、面倒な実装が削ぎ落とされていて最低限の実装だけで決済 API を利用することができたり、管理画面(ダッシュボード)が直感的に使えたりするなど、「開発者に優しい決済サービス」になっている思います。




うぉぉぉぉ!! これが噂の Stripe ってやつか!
熱いぜ! 熱いぜぇぇぇぇぇ!

f:id:akiyoko:20161221075723p:plain:w350



Stripe、今後日本ですごく流行る予感がします。



オススメ Django 本

最後に、Django のベストプラクティス本の紹介です。

Two Scoops of Django: Best Practices for Django 1.8

Two Scoops of Django: Best Practices for Django 1.8


記事を書きましたので、是非ご参考に。


<過去記事>
akiyoko.hatenablog.jp

*1:決済サービスの接続方式については、過去記事「Django と Paypal と私(主に PayPal 決済の最新事情について) - akiyoko blog」を参考のこと

*2:Stripe: Press resources より

*3:2017年4月末でのサービス終了が宣言された「WebPay」では、海外向けでの販売のみを前提とした利用はできないと規定されていました。 https://webpay.jp/faq#constraints

*4:Stripe: Pricing を参照

*5:「data-locale」パラメータを「auto」に設定することで実現可能。パラメータの詳細については Checkout Reference を参照

*6:Stripeが日本で正式ローンチ、三井住友カードが資本参加を発表 | TechCrunch Japan より

*7:Which cards and payment types can I accept with Stripe? : Stripe: Help & Support より

*8:【徹底比較】話題の5大オンライン決済サービスPayPal、SPIKE、Stripe、WebPay、Yahoo!ウォレット FastPay、を比べてみた(比較表有り) には「順次対応予定」とは書いてありましたが。。

*9:https://stripe.com/docs/testing#cards にテストで使用できるクレジットカード情報がリストアップされています

*10:過去記事「ゼロからはじめる Django で ECサイト構築(その3:Django Oscar の機能を調べる) - akiyoko blog」を参照

*11:Cartridge は、Mezzanine 専用に作られた、Mezzanine に ECサイト機能を搭載するためのアプリケーションです。Mezzanine 公式ページには「Ecommerce / Shopping cart module」と紹介されています。

PayPal 決済の最新事情 〜 Django と PayPal と私 〜

この投稿は 「Django Advent Calendar 2016 - Qiita」 の 22日目の記事です。



この記事では、「Django と PayPal REST API で In-Context Window による PayPal 決済フロー」を実装・検証します。

はじめに謝罪しておきますが、「Django Advent Calendar」にも関わらず、結果として Django はオマケ程度にしか扱っていません。。


 

はじめに

オンライン決済の仕組み

ECサイトの決済処理の仕組みを簡単に図解すると、以下のようになります。

f:id:akiyoko:20160515172525p:plain
10 Safe and Popular Gateways for Online Payment Processing | InstantShift を参考に作成)


決済業者と直接契約するのはいろいろと面倒なので、代わりに決済代行業者と契約することで、Visa や Master Card などの各種クレジットカード(決済代行サービスによっては銀行振込やコンビニ決済も)が ECサイトで利用できるようになります。



少し細かい話になりますが、決済代行サービスごとに、決済処理システムとのやり取りの方式が異なります。

決済処理システムとの接続方式には、大きく分けて次の二種類があります。

接続方式 説明
リンク(画面遷移)方式 決済代行サービスのサイトに一旦遷移してクレジットカード情報(あるいは決済代行サービスのアカウント)などを入力させる方式
直接決済方式 画面遷移をおこなわず、バックエンドで直接決済処理をおこなう方式。実現方式や実装方法によってさらに細分化される。 *1


現在のところ、PayPal の接続方式は「リンク方式」のみ となっています。PayPal には「Direct Credit Card Payments」という直接決済方式の決済サービスもあるのですが、残念ながら(利用できるのは UKのみで)日本では利用できません。 *2

なお、GMOペイメントゲートウェイゼウス などの決済代行業者ではそれぞれの方式の決済代行サービスが各種取り揃えられています。また、最近本番サービスインとなった話題の Stripe は「直接決済方式」となっています。


なぜ PayPal?

私が必要としている決済サービスとしては、

  • 海外からの決済が可能(JPY以外の通貨が扱える) *3

というのを最低条件としていました。

そのほか、

  • PayPal に慣れている
  • PayPal のビジネスアカウントを既に持っている

という理由から、PayPal を第一候補に考えています。


PayPal の ECサイト用オンライン決済にもいろいろと実装方式(使用する API の種類など)があるのですが、

  • Braintree v.zero がプロダクション利用できるのは 2017年以降(2018年?)
  • Classic API よりも REST API を使いたい
  • In-Context Window を使ったフローの方が離脱が少ない

という事情を勘案して、「In-Context Window による決済フローを PayPal REST API で実装」するのがベストな選択肢であるという結論に達しました。


 

In-Context Window とは?

今回検証した PayPal のオンライン決済パターンは、下図のようなものになります。

f:id:akiyoko:20161222132204p:plain
NVP/SOAP Integration - PayPal Developer の図を元に作成。緑枠:自サイト、青枠:PayPal サイト)

① ショッピングカート画面(「PayPal で支払う」ボタン)
    ↓
② (In-Context Window 内)ログイン画面(「ログイン」ボタン)
    ↓
③ (In-Context Window 内)支払承認画面(「支払いに同意」ボタン)
    ↓
④ 決済完了画面


In-Context Window は、過去記事 の「5. ポップアップウィンドウ型(In-Context Window)」(小さなポップアップを立ち上げてその内部で PayPal サイトを表示させる新しいタイプの画面遷移パターン)に該当する決済フローです。

これまでの Express Checkout と違って、全画面が PayPal 決済ページにリダイレクトされることなく、小さなポップアップが立ち上がってその中で PayPal 決済ページを表示するというのが最大の特徴です。


<過去記事>
akiyoko.hatenablog.jp



これが、PayPal の数あるオンライン決済パターンの中で一番シンプルで最も新しいパターンになるかと思います。


また PayPal の公式ページでも、

PayPal no longer recommends full-page redirects to PayPal for Express Checkout. Instead, Express Checkout only requires a payment ID generated by the REST Payments API, or a payment token generated by the legacy NVP/SOAP APIs, to initiate and finalize a payment.


https://developer.paypal.com/docs/integration/direct/express-checkout/integration-jsv4/other-integrations/

ということで、全画面をリダイレクトするような画面遷移は今後はオススメしないということなので、今後の PayPal 決済では In-Context Window 型のフローが増えてくると思われます。



In-Context Window 型の通常決済(Checkout)を利用するには、フロント側に「checkout.js」という PayPal 謹製の JSライブラリを読み込ませて(*4)、あとは決まったやり方に則って実装するだけで OK です。

checkout.js は少し前まで V 3.5.0 だったのですが、現在の最新バージョンは V.4.0.0 となって、実装方法が若干変更されています。 *5


今回、V 4.0.0 で検証する前に V 3.5.0 でも実装・検証してみたのですが、より簡単に、よりセキュアに実装できるようになったという印象です。

具体的には、V 4.0.0 になって、

  • PayPal ボタン生成のときに、PAYPAL_CLIENT_ID を画面に晒さなくてよくなった
  • Create Payment のときに、生成した Payment から「redirect_url」を取り出してリダイレクトしなくてよくなった *6

などのうれしい変更点がありました。


 

PayPal REST API とは?

PayPal REST API は、PayPal が提供する様々な決済サービスを利用することができる RESTful API です。現時点で、PayPal が実現できるほぼ全ての決済サービスを網羅しているようです。 *7


なお、PayPal REST API は OAuth 2.0 プロトコルによる認可システムを採用しており、PayPal Developer サイトで作成した売り手アカウントの Credential(Client ID および Secret)を使用して各 API 呼び出しに必要なトークンを払い出します。 *8


Credential の作成方法については、ここでは説明を省略します。 *9



先に述べたように、PayPal REST API には様々な API の種類がありますが、オンライン決済処理ではその中から「Payments API」を使用すれば事足りるでしょう。 *10


PayPal REST API を便利に利用するためのライブラリとして、Python であれば PayPal Python SDK が 本家 PayPal から提供されていますので(Python のほかにも Java, PHP, .NET, Ruby, Node.js など各種言語向けの SDK が揃っています)、 pip でインストールして使います。


 

Django パッケージは使わないの?

Django Packages : Payment ProcessingDjango Packages : django SHOP plugins などで、PayPal に対応している決済パッケージをチェックしてみたのですが、

  • Django 1.10
  • PayPal REST API
  • In-Context Window(checkout.js V 4.0.0)

に対応しているものは今のところ見当たりません。


スターの多い順に確認してみると、django-merchant は「PayPal Website Payments Pro」のみ対応ということで日本では利用不可、django-lfs は「PayPal Payments Standard」のみ対応ということで API が古くて NG、django-paypal も「PayPal Payments Standard」または「PayPal Website Payments Pro」のみ対応ということで先の二つと同じでした。


そもそも「PayPal REST API で In-Context Window 決済」は自前で実装してもそんなに大変ではなくて、Django パッケージをわざわざ使うまでもないといった印象です。




ということで、前置きがずいぶん長くなってしまいましたが、「Django と PayPal REST API で In-Context Window による PayPal 決済フロー」を実際に試していきます。



実装前には、以下のドキュメントをざっと読んでおくことをお勧めします。


 

実装

購入する商品と合計金額が表示されたカート画面から PayPal 決済をおこなうことを想定した、「shop」アプリケーションを作ってみます。

/opt/webapps/myproject/
├── config
│   ├── __init__.py
│   ├── local_settings.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
├── shop
│   ├── apps.py
│   ├── __init__.py
│   ├── urls.py
│   └── views.py
└── templates
    ├── base.html
    ├── error.html
    └── shop
        ├── base_shop.html
        ├── cart.html
        └── complete.html


動作確認をおこなった Python および Django のバージョンは以下の通りです。

  • Python 2.7.6
  • Django 1.10



ソースコードは GitHub に置きました。
github.com



 

Settings

conf/settings.py (抜粋)

# Application definition

INSTALLED_APPS = [
    ...
    'shop',
]
...
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        ...
        },
    },
]
...
# LOCAL SETTINGS
PROJECT_APP_PATH = os.path.dirname(os.path.abspath(__file__))
PROJECT_APP = os.path.basename(PROJECT_APP_PATH)
f = os.path.join(PROJECT_APP_PATH, 'local_settings.py')
if os.path.exists(f):
    import sys
    import imp
    module_name = '%s.local_settings' % PROJECT_APP
    module = imp.new_module(module_name)
    module.__file__ = f
    sys.modules[module_name] = module
    exec (open(f, 'rb').read())


conf/local_settings.py (抜粋)

DEBUG = True

# PayPal
PAYPAL_MODE = '<paypal-mode>'  # 'sandbox' or 'live'
PAYPAL_CLIENT_ID = '<paypal-client-id>'
PAYPAL_CLIENT_SECRET = '<paypal-client-secret>'

 

URLConfs

shop/urls.py

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^cart$', views.ShowCartView.as_view(), name='cart'),
    url(r'^create-payment$', views.CreatePaymentView.as_view(), name='create-payment'),
    url(r'^execute-payment$', views.ExecutePaymentView.as_view(), name='execute-payment'),
]

 

Views

shop/views.py

import logging

from django.conf import settings
from django.contrib import messages
from django.http import JsonResponse, Http404
from django.shortcuts import render, reverse
from django.views.generic import View
import paypalrestsdk

logger = logging.getLogger(__name__)


class ShowCartView(View):
    def get(self, request, *args, **kwargs):
        return render(request, 'shop/cart.html', {
            'paypal_mode': settings.PAYPAL_MODE,
        })


class CreatePaymentView(View):
    def post(self, request, *args, **kwargs):
        paypalrestsdk.configure({
            'mode': settings.PAYPAL_MODE,
            'client_id': settings.PAYPAL_CLIENT_ID,
            'client_secret': settings.PAYPAL_CLIENT_SECRET,
        })

        payment = paypalrestsdk.Payment({
            'intent': 'sale',

            # Payer
            'payer': {
                'payment_method': 'paypal',
            },

            # Redirect URLs
            'redirect_urls': {
                'return_url': request.build_absolute_uri(reverse('shop:execute-payment')),
                'cancel_url': request.build_absolute_uri(reverse('shop:cart')),
            },

            # Transaction
            # Note: This is dummy. If production, transaction should be created with reference to cart items.
            'transactions': [{
                # Item List
                'item_list': {
                    'items': [{
                        'name': 'item',
                        'sku': 'item',
                        'price': '5.00',
                        'currency': 'USD',
                        'quantity': 1,
                    }]
                },
                # Amount
                'amount': {
                    'total': '5.00',
                    'currency': 'USD',
                },
                'description': 'This is the payment transaction description.',
            }]
        })

        # Create Payment
        if payment.create():
            logger.info("Payment[{}] created successfully.".format(payment.id))
            return JsonResponse({'success': True, 'paymentId': payment.id})
        else:
            logger.error("Payment failed to create. {}".format(payment.error))
            return JsonResponse({'success': False, 'error': "Error occurred while creating your payment."}, status=500)


class ExecutePaymentView(View):
    def get(self, request, *args, **kwargs):
        # Query strings are always in request.GET
        payment_id = request.GET.get('paymentId', None)
        payer_id = request.GET.get('PayerID', None)

        try:
            payment = paypalrestsdk.Payment.find(payment_id)
        except paypalrestsdk.ResourceNotFound as err:
            logger.error("Payment[{}] was not found.".format(payment_id))
            return Http404

        # Execute Payment
        if payment.execute({'payer_id': payer_id}):
            logger.info("Payment[{}] executed successfully.".format(payment.id))
            messages.info(request, "Your payment has been completed successfully.")
            return render(request, 'shop/complete.html', {
                'payment': payment,
            })
        else:
            logger.error("Payment[{}] failed to execute.".format(payment.id))
            messages.error(request, "Error occurred while executing your payment.")
            return render(request, 'error.html')

Create Payment は
PayPal-Python-SDK/create_with_paypal.py at master · paypal/PayPal-Python-SDK · GitHub
を参考に、Execute Payment は
PayPal-Python-SDK/execute.py at master · paypal/PayPal-Python-SDK · GitHub
を参考にしました。


なお、例外処理は全然ケアしていないので、本番で使う場合には要注意です。


 

Templates

shop/cart.html

{% extends "./base_shop.html" %}

{% block title %}Cart{% endblock title %}

{% block content %}
{{ block.super }}
<div id="paypal-button"></div>

<script src="https://www.paypalobjects.com/api/checkout.js" data-version-4></script>
<script>
    paypal.Button.render({
        env: '{{ paypal_mode }}',
        payment: function (resolve, reject) {
            paypal.request.post('{% url "shop:create-payment" %}', {csrfmiddlewaretoken: '{{ csrf_token }}'})
                .then(function (data) {
                    console.log("data=", data);
                    if (data.success) {
                        resolve(data.paymentId);
                    } else {
                        reject(data.error);
                    }
                })
                .catch(function (err) {
                    console.log("err=", err);
                    reject(err);
                });
        },
        onAuthorize: function (data, actions) {
            return actions.redirect();
        },
        onCancel: function (data, actions) {
            return actions.redirect();
        },
        onError: function (err) {
            // Show an error page here, when an error occurs
        }
    }, '#paypal-button');
</script>
{% endblock content %}

フロントのスクリプトは、

などを参考にしていただければ。


また、POST送信時に「csrfmiddlewaretoken」というパラメータを付与しないと 403エラーになるのは、Django に詳しい皆さんにはお馴染みですよね。



shop/complete.html

{% extends "./base_shop.html" %}

{% block title %}Complete{% endblock title %}

{% block content %}
<span style="font-size: 0.7rem;">{{ payment }}</span>
{% endblock content %}

 

処理概要

ここで、In-Context Window の処理フローを少し解説すると以下のようになります。

  1. paypal.Button.render で PayPal ボタンを表示
  2. ユーザがボタンをクリックすると、paypal.requet.post() で '/shop/create-payment' にリクエストを POST する
  3. サーバ側で商品情報や合計金額、およびPayPal からリダイレクトさせる URL を含めた Payment を create して、payment.id を JSON で返す
  4. checkout.js が勝手に PayPalサイトにリダイレクトしてくれる
  5. In-Conetxt Window 内でユーザが決済を承認して同意すると、onAuthorize、キャンセルすると onCancel がコールバックされるので、それぞれのコールバック関数の中で適宜リダイレクトをする(ここでは勝手にリダイレクトされない)
  6. 「/shop/execute-payment?paymentId=xxx&token=yyy&PayerID=zzz」という URL でリクエストされるので、サーバ側で paymentId, PayerID を取得して Payment を execute して、決済完了画面を表示させる


実装を見ながら、処理の流れをチェックすると分かりやすいかと思います。


 

動作確認

最後に、実際に検証環境で画面を動かしながら、動作を確認してみます。


ブラウザで「http://localhost:8000/shop/cart」にアクセスし、PayPal のチェックアウトボタンをクリックします。
f:id:akiyoko:20161221135159p:plain

In-Context Window(モーダルウィンドウみたいなもの)が起動して、PayPal サイト(サンドボックス)のログイン画面が表示されます。
PayPal の買い手アカウント情報を入力してログインし、
f:id:akiyoko:20161221145000p:plain

「同意して続行」をクリックします。
f:id:akiyoko:20161221145025p:plain

onAuthorize がコールバックされ、return_url の「/shop/execute-payment」に redirect() され、shop/complete.html に payment オブジェクトの中身がダンプされました。
f:id:akiyoko:20161221145046p:plain



 

まとめ

Django で PayPal 決済を検討しているのであれば、今回紹介したように、フロントは「In-Context Window の checkout.js V 4.0.0」、バックエンドは「PayPal REST API 」という現時点での最新スタイルがオススメです。

ドキュメントや記事がまだ少ないのが欠点ですが、PayPal の日本チームもそこには今後力を入れていくそうなので期待しましょう。

PayPal の決済フローもドキュメントも、現在進行形でどんどん進化していますよ!!


最後にひと言。

PayPal REST API で In-Context Window 決済はいいぞ!


ご拝読ありがとうございました。「Django と Paypal と私」でした。




明日は、luizs81 さんの 23日目の記事です。よろしくお願いします。


オススメ Django 本

Django のベストプラクティス本です。
全編英語ですが絶対オススメです。

Two Scoops of Django: Best Practices for Django 1.8

Two Scoops of Django: Best Practices for Django 1.8


記事を書きましたので、是非ご参考に。


<過去記事>
akiyoko.hatenablog.jp

*1:決済代行業者によって呼び方も異なる。例えば、GMOペイメントゲートウェイ では「プロトコルタイプ」「モジュールタイプ」、ゼウス では「トークン(JavaScript)型」「データ伝送(API)型」などと呼ばれるが、いずれも画面遷移を伴わない直接決済方式である。

*2:https://developer.paypal.com/docs/classic/api/#website-payments-pro

*3:2017年4月末でのサービス終了が宣言された「WebPay」では、海外向けでの販売のみを前提とした利用はできないと規定されていました。 https://webpay.jp/faq#constraints

*4:常に最新版のものを利用するために、PayPal の CDN を利用することが推奨されています。

*5:V 3.5.0 から V 4.0.0 への移行方法については、「Upgrade checkout.js to V4.0.0 - PayPal Developer」を参照

*6:payment_id を JSON形式で返すだけで、後は checkout.js が勝手にリダイレクトしてくれるようになりました。

*7:利用できる API の種類については「REST API reference - PayPal Developer」を参照

*8:詳しい仕組みについては「How PayPal uses OAuth 2.0 - PayPal Developer」を参照

*9:https://www.paypal-knowledge.com/infocenter/index?page=content&id=FAQ1949 が新しくて参考になりそうです。

*10:本番では「Payment Experience API」を組み合わせて使うこともありますが今回は利用しません。