【自由研究③】react-reduxのconnectについて調査してみた話
Reduxで実装する場合にReact部とRedux部の繋ぎ込みに react-redux が有効ということは、分かったのですがどうもconnect() に渡している引数や、戻り値として何が生成されるのか分からず 結果として、なんでconnect()使うと幸せになれるのか分からなかったため 調査してみました。
なぜ分からないのか?
なぜ分からないのか考えてみました。
- チュートリアル等で、手を動かしただけでドキュメントの読み込みが足りない
- そもそもReduxの理解が不足している
ドキュメントを再度読んでみる。
Usage with React · Redux を読んでまずは まとめてみることにしました。
Presentational / Container componentについて
再利用性を考慮して、Presentational(プレゼンテーショナル) / Container(コンテナ) component の2つに分離されるようです。 2つのコンポーネントの役割を簡単にまとめてみましたが、ざっくりいうと
- 見た目(表示)担当のPresentational component
- ロジック担当のContainer component
って感じのようです。
また、調べていたら良い記事を既に書いていらっしゃる方がいたので参考にさせて 頂きました。
Presentational Componentsとは
- 役割:見た目の実装
- データ取得時:propsより取得
- データ更新時:propsのcallback関数
- コンポーネントの作成:自分で作る
実際のイメージとしては、こんな感じです(こちらからの抜粋です)。
import React from 'react' export default React.createClass({ render() { const { text, onButtonClick, } = this.props return ( <div> <p>{text}</p> <button onClick={onButtonClick}>Click!</button> </div> ) } })
- {text}の表示は、props経由
- ボタンクリック時の{onButtonClick}の実装もprops経由
Container Componentsとは
- 役割:データフェッチやStateの更新
- データ取得時:reduxのstateにより取得
- データ更新時:reduxのactionにより更新
- コンポーネントの作成:react-reduxが生成
実際のイメージとしては、こんな感じです(こちらからの抜粋です)。
import { connect } from 'react-redux' import SomePresentationalComponent from 'some-presentational-compnent' const mapStateToProps = (state, ownProps) => { return { // blah blah blah } } const mapDispatchToProps = dispatch => { return { // blah blah blah } } export default connect( mapStateToProps, mapDispatchToProps )(SomePresentationalComponent)
はい、出てきましたー。connect() この辺りから、分からなくなってきます・・・。
こちらに以下のようなコメントが ありました。
mapStateToProps(state, ownProps)は,store.getState()の結果を第一引数に,このContainer component へ渡されたpropsを第二引数にして呼び出される関数で,これらのstateとpropsを使って子のPresentational componentにpropsとして渡す値を生成します. mapDispatchToProps(dispatch)は,store.dispatchを第一引数にして呼び出される関数で, 子のPresentational componentにpropsとして渡す値(というか,コールバック関数)を生成します. 通常,このコールバック関数では,引数として渡されているdispatchを呼び出し,子のPresentational Componentが StoreへActionを送信できるようにしておきます. ボタン押下時に呼ばれる処理などもコールバック関数として書いてPresentational componentにpropsとして 渡すと言いましたが,このコールバック関数を作るためのものと思っておけばいいと思います.
すごく丁寧な解説をして頂いているのですが、そもそも以下が良く分かっていないことが判明しました。 その結果として、connect()の引数から何が生成されるのかが分かっていないのだという結論に至りました。
- Store.getState()
- Store.dispatch()
以下のドキュメントで各メソッドの仕様を調査しました http://redux.js.org/docs/api/Store.html
getState()
- 要訳すると、現状のStoreが持つ最新のState郡を返す。
dispatch()
- 要訳すると、dispatchは、唯一Stateを変更する手段。
- 引数として、actionを渡す。actionとは、UIイベント、 networkのcallback等を指す。 actionのfieldには、"type"が必要
これを踏まえて
上記を踏まえて、connect()の解説を読むとより理解が深まりました。 要は、Presentational componentに対して、propsとして、stateと stateを更新するためのCallback関数として渡してるだけのようです。
変に、connect()の第1引数と、第2引数がなにかしらの対応づけがされているのでは と推察していたことがそもそもの間違いでした・・
ちゃんとドキュメント読めよと・・
以前作成した、以下のコードを「React Developer Tools」で確認(スクショ参照)すると、それが良く分かりました。CalculatorContainerコンテナ(Presentational component)に対して、propsとして引数に渡していることが 分かります。
import * as actions from '../actions'; const mapState = (state, ownProps) => ({ calculator: state.calculator, }); function mapDispatch(dispatch) { return { actions: bindActionCreators(actions, dispatch) } } export default connect(mapState, mapDispatch)(CalculatorContainer);
(上記のスクショが見えづらかったため、Reactコードを転記しました。)
<Provider store={dispatch: dispatch() subscribe: subscribe() getState: getState() replaceReducer: replaceReducer()}> <Connect(CalculatorContainer)> <CalculatorContainer calculator={inputValue: 0, resultValue: 0,showingResult: false} actions={onNumClick:fn(), onPlusClickfn():, onMinusClick:fn()}/> </Connect(CalculatorContainer)> </Provider>
そして再びconnet()へ
ここまでで、connet()の挙動に関しては、理解できました。 最後に謎なのが・・。
export default connect(mapState, mapDispatch)(CalculatorContainer);
という呼び出し時の記述です。connect(mapState, mapDispatch)の戻り値になんらかの関数が 戻ってきて、それに対してコンテナ(Presentational component)に引数に渡して、Class生成している ことが予想されます。
connect()の戻り値をドキュメントで確認すると
Returns
A higher-order React component class that passes state and action creators into your component derived from the supplied arguments. This is created by connectAdvanced, and details of this higher-order component are covered there.
とありますが、higher-order React component classというなんとも説明しづらい記述があったため 実際にreact-reduxの実装を見てみることにしました。
connect.jsを読む
src/connect/connect.jsが対象です。 ソース内のconnectHOC(selectorFactory)が ポイントのようです!。 connectAdvanced()の実装を確認します。
connectAdvanced.jsを読む
src/components/connectAdvanced.jsが対象です。 ソース を確認すると、Connectコンポーネントを作成し、それの子コンポーネントとして引数のコンテナ(Presentational component) を使用しているようです。
なので、connect()の戻りは、
「higher-order React component class」 == 「Connectコンポーネント」
ということになります。
まとめ
長々と調査してきましたが、自分なりの結論としては以下に至りました。
connect()とは
- 引数には、stateとstateを更新するCallback関数を渡す(引数のPresentational componentはpropsでのみ扱える)。
- Connectコンポーネントを返す(引数のPresentational componentを子コンポーネントして持つ)。
connect()のへの疑問から様々な箇所まで波及しましたが、そもそもReduxの理解不足という一言につきました・・。 ライブラリの内部実装を確認することは、いつも時間の無さにかまけて出来てなかったのですが 今回のconnect()の実装が腑に落ちなく、別のエンジニアに説明する材料が乏しかったため今回の確認作業を実施しました。
中々、ヘビーな作業でしたが、ライブラリのメリデメの理解は断然高まりますので、まとまった時間がある際に再度トライ してみたいです。
最後に
これを読まれている方で、ちょっと認識違うんじゃないかとか、この辺が分かりづらいというご意見ありましたら 是非、コメント頂ければと思います!
【自由研究②:react native】ExpoでDebug環境を整える
前回に引き続き、夏休み自由研究の第2段です! 今回は、前回導入したExpoのDebug環境を整えます。 (環境は、macを対象としています)
目次:
- シミュレータ/エミュレータを使用したDebug
- react-native-debuggerのインストール
- react-native-debugger-open のインストール
- react-native-debuggerの起動
- まとめ
シミュレータ/エミュレータを使用したDebug
まずは、シミュレータ/エミュレータを使用したDebug環境 を試してみました。
Expoの公式サイトのままやっています。 docs.expo.io
Android (Genymotionを使用した場合)
※Genymotionの導入については、別途まとめます。
Either press “Menu” button in Genymotion’s toolbar, or just hit Cmd-m. とあるので、エミュレータ上で、Cmd + mをします。
メニュー表示されるので、「Debug JS Remotely」を選択します。
Chromeが起動して以下のメッセージが表示されるので、それに従います。
こんな感じで、developer toolsが使用できるようになり、Debug出来るように なり、ログDebug等が可能になりましたが、JS記述のためやはり、ReactのDebug 環境としてはまだ厳しい感じです。
iOS
iOSシミュレータは、試せていませんが以下で可能なようです。
Hit Ctrl-Cmd-Z on a Mac in the emulator to simulate the shake gesture, or press Cmd+D.
react-native-debuggerのインストール
react-native-debuggerを使用すると、ReactのDebugが可能なようなので 導入してみます。また、reducerに手を加えることによってReduxのDebugも可能なようです。
$ brew update && brew cask install react-native-debugger
react-native-debugger-open のインストール
ドキュメントを確認すると、react-native-debugger-open も必要なようなので、インストールします。
https://github.com/jhen0409/react-native-debugger/tree/master/npm-package
インストール
$ npm i --save-dev react-native-debugger-open
package.jsonに追記
"scripts": { "postinstall": "rndebugger-open" }
npm run postinstall を実行し、getDevToolsMiddleware.js をreplaceする
$ npm run postinstall
react-native-debuggerの起動
前述しました、Androidエミュレータ場合、Cmd + m後に「Debug JS Remotely」 を選択すると、react-native-debuggerが起動します。
まとめ
react-native-debuggerは使用すると、react NativeのDebugが捗りそうです。 ただ、Androidエミュレータは「Genymotion」を使用することが推奨な感じです。 (SDK版では試していませんが、公式ドキュメントに記述がありませんでした) ReduxのDebugをする場合は、redux-devtools-extensionが別途必要そうですが こちらは、次回以降で検証したいと思います。
【自由研究①:react native】Expoを使って、react native環境を作ってみる
夏休み中ですが、休暇明けに「react native」 +「redux」の案件に携わるので 自由研究な感じで諸々やってみたメモをまとめてみようと思います。 今日は、Expoを触ってみた感想をまとめました。
目次:
- Expo.io
- create-react-native-appのインストール
- プロジェクト作成
- XDEのインストール
- Expoアプリのインストール
- XDEでプロジェクトを読み込み
- QRコードをExpoアプリでスキャンする
- まとめ
Expo.io
ちょっと前までは、「react native」はreact-native-cliを使用してアプリ構築する手段 しななかったようですが、xcodeやAndroid Stdioでビルドする必要があったりして、結構手間なようです。 また、実機テストが通常のネイティブアプリ開発のようにUSB接続が必要があります。
今回、Expo.ioというサービスを使用すると幸せになれるらしいということが分かったので こちらを使用してみることにします。react nativeの公式ページを見てもこれ使うサンプルが あるのでこちらを使用する方向になりそうです。
create-react-native-appのインストール
Expoを使用するために、create-react-native-appを使用してreact-nativeプロジェクトを 作成する必要があります。
npm install -g create-react-native-app
プロジェクト作成
プロジェクトを作成します。
create-react-native-app hogefuga
XDEのインストール
Desktop Development Tool: XDEをインストールします。
以下を参照しました。 Installation | Expo v15.0.0 documentation
Expoアプリのインストール
各デバイス向けのExpoアプリをGoogle Play/App Storeよりインストールします。 自分はとりあえず、Androidアプリをインストールしました。
以下を参照しました。 Installation | Expo v15.0.0 documentation
XDEでプロジェクトを読み込み
「Project」-「Open Project」でプロジェクトを読み込みます。
「Share」でQRコードを表示します。
QRコードをExpoアプリでスキャンする
Expoアプリ(今回は、Android)を起動して、「Scan QR Code」を選択し QRコードを読み込みます。
しばらくすると、作成したサンプルプロジェクトが起動します。
まとめ
Expoを使用したreact native環境は、非常にお手軽に実機テストまで出来て良い感じでした。 ビルド待ちが少し気になりましたが・・。
がっつりネイティブ依存の機能を作り込む場合は、「react-native-cli」+「各IDE」を使用して開発するのかもしれ ませんが現状は「Expo」+「create-react-native-app」で充分に思いました。
今日はここまで。
いまどきのJSプログラマーのための Node.jsとReactアプリケーション開発テクニック
- 作者: クジラ飛行机
- 出版社/メーカー: ソシム
- 発売日: 2017/07/26
- メディア: 単行本
- この商品を含むブログを見る
JavaScriptエンジニアが手っ取り早くReactの基礎を理解するための「超」入門書
- 作者: 天田士郎
- 発売日: 2017/02/05
- メディア: Kindle版
- この商品を含むブログを見る
Reactビギナーズガイド ―コンポーネントベースのフロントエンド開発入門
- 作者: Stoyan Stefanov,牧野聡
- 出版社/メーカー: オライリージャパン
- 発売日: 2017/03/11
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
入門 React ―コンポーネントベースのWebフロントエンド開発
- 作者: Frankie Bagnardi,Jonathan Beebe,Richard Feldman,Tom Hallett,Simon HØjberg,Karl Mikkelsen,宮崎空
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/04/03
- メディア: 大型本
- この商品を含むブログ (2件) を見る
WebデベロッパーのためのReact開発入門 JavaScript UIライブラリの基本と活用
- 作者: 柴田文彦
- 出版社/メーカー: インプレス
- 発売日: 2016/11/25
- メディア: Kindle版
- この商品を含むブログを見る
zappaが想定しないAWSアカウントにデプロイされて焦った話し
世間は、最近サーバレスが盛んで業務でlambdaを使用するメンバも増えてきましたが自分は なかなか使う機会がなかったのですが、PyConJPで弊社ブースを出すに当たってなにか展示物を作る 必要があったので、Zappa(GitHub - Miserlou/Zappa: Serverless Python Web Services)を使ってみようと思いました。
Zappaとは
Python/flaskベースのAWSの Serverless frameworkでAPI Gateway + lambda + S3を使用するようです。
テストアプリを作ってみた
zappa で hello world するまで - Qiitaを参考にさせてもらいながら もりもり作成して、デプロイしたわけです。
こんな感じで設定ファイルを書いて
{ "dev": { "app_function": "test.app", "aws_region": "ap-northeast-1", "profile_name": "default", "s3_bucket": "zappa-xyzxyzxyzxyz" } }
そしてデプロイしたわけです。
$ zappa deploy dev Calling deploy for stage dev.. Creating demo-zappa-dev-ZappaLambdaExecutionRole IAM Role.. Creating zappa-permissions policy on demo-zappa-dev-ZappaLambdaExecutionRole IAM Role. Warning! Your project and virtualenv have the same name! You may want to re-create your venv with a new name, or explicitly define a 'project_name', as this may cause errors. Downloading and installing dependencies.. Packaging project as zip.. Uploading demo-zappa-dev-1498726098.zip (7.6MiB).. 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 7.94M/7.94M [00:01<00:00, 4.31MB/s] Scheduling.. Scheduled demo-zappa-dev-zappa-keep-warm-handler.keep_warm_callback with expression rate(4 minutes)! Uploading demo-zappa-dev-template-1498726129.json (1.6KiB).. 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1.64K/1.64K [00:00<00:00, 5.09KB/s] Waiting for stack demo-zappa-dev to create (this can take a bit).. 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:15<00:00, 5.71s/res] Deploying API Gateway.. Deployment complete!: https://xxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev
すげーデプロイできた!!。そして、実行。
$ curl -l https://xxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev hello from Flask!
おお、楽すぎるーと感動しつつ・・。さて、マネージメントコンソールでどんな感じなのかなと 見てみました・・・。
何故か、該当するFunctionが見当たらない・・。
profileを確認してみる。
zappa_settings.jsonでは、defaultをしていました。
$ cat ~/.aws/config [default] region = ap-northeast-1 [profile xxxxxx] output = json region = ap-northeast-1 [profile yyyyyy] region = ap-northeast-1 output = json # @@@@は伏せ字 $ cat ~/.aws/credentials [xxxxxx] aws_access_key_id = @@@@ aws_secret_access_key = @@@@ [yyyyyy] aws_access_key_id = @@@@ aws_secret_access_key = @@@@
???。defaultのaccess_keyとsecret_access_key が定義されていないのに、なぜかデプロイされちゃってます・・・。
調査する。
$ aws lambda list-functions { "Functions": [ { "FunctionName": "demo-zappa-dev", "FunctionArn": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXX:function:demo-zappa-dev", "Runtime": "python3.6", "Role": "arn:aws:iam::[デプロイ先のAWSアカウント]:role/demo-zappa-dev-ZappaLambdaExecutionRole", "Handler": "handler.lambda_handler", "CodeSize": 7939825, "Description": "Zappa Deployment", "Timeout": 30, "MemorySize": 512, "LastModified": "2017-06-29T09:17:27.435+0000", "CodeSha256": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "Version": "$LATEST" } ] }
デプロイされたlambda functionのAWSアカウントが分かりました。 しかし、いまいち覚えがないアカウント・・・。
$ aws sts get-caller-identity { "UserId": [デプロイ先のAWSアカウント], "Account": [デプロイ先のAWSアカウント], "Arn": "arn:aws:iam::[デプロイ先のAWSアカウント]:root" }
うーん、これも同じ。~/.aws/configは使用していない模様。
$ aws configure list Name Value Type Location ---- ----- ---- -------- profile <not set> None None access_key ****************XXXX boto-config secret_key ****************4YT1 boto-config region ap-northeast-1 config-file ~/.aws/config
configの一覧が確認できるようなので、試すとboto-config というキーワードがあります。それで思い出しました。以前、botoを使用するために /etc/boto.cfgを作成したことを・・
試しにboto.cfgを削除して、デプロイすると見事にエラーとなりました。
$ zappa deploy dev Calling deploy for stage dev.. Oh no! An error occurred! :( ============== Traceback (most recent call last): ・・・ ============== Need help? Found a bug? Let us know! :D File bug reports on GitHub here: https://github.com/Miserlou/Zappa And join our Slack channel here: https://slack.zappa.io Love!, ~ Team Zappa!
まとめ
とりあえず、原因は分かりましたが、zappaのデプロイ時にzappa_settings.json に該当するprofileが無かった場合はせめてエラーメッセージ出力してデプロイしないで欲しいなあとも思ったのですが botoの挙動なのかもしれないので、ドキュメントを確認しました。
これ見る限り、 botoの仕様ぽいですね。良い勉強になりました。
The mechanism in which boto3 looks for credentials is to search through a list of possible locations and stop as soon as it finds credentials. The order in which Boto3 searches for credentials is: Passing credentials as parameters in the boto.client() method Passing credentials as parameters when creating a Session object Environment variables Shared credential file (~/.aws/credentials) AWS config file (~/.aws/config) Assume Role provider Boto2 config file (/etc/boto.cfg and ~/.boto) Instance metadata service on an Amazon EC2 instance that has an IAM role configured.
実践AWS Lambda ~「サーバレス」を実現する新しいアプリケーションのプラットフォーム~
- 作者: 西谷圭介
- 出版社/メーカー: マイナビ出版
- 発売日: 2017/06/09
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
サーバーレスシングルページアプリケーション ―S3、AWS Lambda、API Gateway、DynamoDB、Cognitoで構築するスケーラブルなWebサービス
- 作者: Ben Rady,吉田真吾,笹井崇司
- 出版社/メーカー: オライリージャパン
- 発売日: 2017/06/23
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
AWS Lambda: A Guide to Serverless Microservices (English Edition)
- 作者: Matthew Fuller
- 発売日: 2016/01/11
- メディア: Kindle版
- この商品を含むブログを見る
Amazon Web Services クラウドネイティブ・アプリケーション開発技法 一番大切な知識と技術が身につく
- 作者: 佐々木拓郎,佐藤瞬,石川修,高柳怜士,佐藤雄也,岸本勇貴
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2016/04/20
- メディア: Kindle版
- この商品を含むブログを見る
docker for mac + nginxのログをrsyslogコンテナで出力してみた
docker for mac + nginx + オレオレ証明書でローカルSSL環境を作ったメモ - katekichiのゆるブログ
前回の続きで、ログの集中管理する的なことをやってみたかったので rsyslogコンテナに吐き出してみることにしました。
ログの集中管理というと、「fluentd」(http://www.fluentd.org/)が主流だと思いますが 今回は、基本的なところを抑えたかったのでrsyslogにしました。
rsyslogサーバーを立てる
http://d.hatena.ne.jp/tmatsuu/20140603/1401811893 の記事を参考に立てました。
Dockerfile作成
FROM centos:latest RUN yum install -y rsyslog ADD remote.conf /etc/rsyslog.d/ EXPOSE 514 CMD ["/sbin/rsyslogd", "-n"]
remote.conf作成(rsyslog.conf)
$ModLoad imudp $UDPServerRun 514 $AllowedSender UDP, 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 local1.* /var/log/nginx_access_log local2.* /var/log/nginx_error_log
イメージビルド
$ docker build --tag simple-rsyslog .
コンテナ起動
$ docker run -d -p 514:514/udp -h "logserver" simple-rsyslog
テストしてみる
loggerコマンドを使用して、以前構築したnginxコンテナからログを送信してみます。
rsyslogコンテナのIPは、以下で調べました。
$ docker inspect -format="{{ .NetworkSettings.IPAddress }}" コンテナID
nginxコンテナからログ送信
$ docker exec -it [nginxのコンテナID] /bin/bash # コンテナに入る # logger -n[コンテナのIP] -p local1.info testtesttest # logger -n[コンテナのIP] -p local2.error errorerrorerrorerror
rsyslogコンテナでログの確認
$ docker exec -it [rsyslogのコンテナID] /bin/bash # コンテナに入る # tail -f /var/log/nginx_access_log Jun 27 10:07:16 172.17.0.3 <someone>: testtesttest # tail -f /var/log/nginx_error_log Jun 27 10:12:01 172.17.0.3 <someone>: errorerror12345678
ログフォーマット等の調整は必要そうですが、出力することはできました。
nginxイメージの修正
nginx.confのログ設定を変更します。
access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; ↓ access_log syslog:server=logserver,facility=local1 main; error_log syslog:server=logserver,facility=local2 notice;
イメージビルドと起動(--add-host
で「logserver」を指定)
$ docker build --tag webapp-syslog . $ docker run -d --name webapp-syslogp -p 443:443 -p 80:80 --add-host logserver:[rsyslogのIP] webapp-syslog
rsyslogコンテナでログ確認
$ docker exec -it [rsyslogのコンテナID] /bin/bash # コンテナに入る # tail -f /var/log/nginx_access_log Jun 27 11:08:12 f4809369ff7e nginx: 172.17.0.1 - - [27/Jun/2017:11:08:12 +0000] "GET / HTTP/1.1" 200 132 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36" "-" Jun 27 11:08:22 f4809369ff7e nginx: 172.17.0.1 - - [27/Jun/2017:11:08:22 +0000] "GET / HTTP/1.1" 200 132 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36" "-" Jun 27 11:08:24 f4809369ff7e nginx: 172.17.0.1 - - [27/Jun/2017:11:08:24 +0000] "GET / HTTP/1.1" 200 132 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36" "-"
まとめ
思いつきでやってみましたが、rsyslogの知見がそもそも無いのに、Docker上で構築するってのが 少し敷居が高かったです(そもそもDockerのオペレーションも怪しい・・)。
何事もステップ・バイ・ステップですね。良い勉強になりました。
記事読まれている方で、この辺り詳しい方、是非アドバイス頂けると嬉しいです。
- 作者: 鶴長鎮一
- 出版社/メーカー: 技術評論社
- 発売日: 2011/04/22
- メディア: 大型本
- 購入: 2人 クリック: 27回
- この商品を含むブログ (1件) を見る
docker for mac + nginx + オレオレ証明書でローカルSSL環境を作ったメモ
nginxのローカルSSL環境が必要になり、docker for macとオレオレ証明書で作ったので備忘録としてまとめました。
以下の記事を参考に構築してみました。
docker の nginx イメージの設定ファイルを眺めながら、独自ページを表示します。 - Qiita
nginxのdokcerイメージを取得
Docker Hubから最新のnginxイメージを取得する
詳細は、以下の記事を参照
$ docker pull nginx:latest
docker コマンド 基本のキ(nginx のコンテナを実行してみる) - Qiita
環境構築
$ mkdir nginx; cd $ _ $ touch Dockerfile $ mkdir html; cd $_ $ touch index.html $ tree . ├── Dockerfile └── html └── index.html
index.html
<html> <head> <meta charset="UTF-8"> <title>SSL Test</title> </head> <body> <h1>SSL Test</h1> </body> </html>
DockerFile作成
FROM nginx COPY ./html /usr/share/nginx/html
Dockerイメージの作成とコンテナ立ち上げ
$ docker build --tag ssl-nginx . Sending build context to Docker daemon 4.096 kB Step 1/2 : FROM nginx ---> cc1b61406712 Step 2/2 : COPY ./html /usr/share/nginx/html ---> Using cache ---> 2cc22a506a12 Successfully built 2cc22a506a12 $ docker run -d --name ssl-nginx-container -p 80:80 ssl-nginx 651eb9b5642d13274aed51f535d7014754bb001bdcfdf90d7e194bcd5c188b74
ブラウザで確認(http://localhost/)
SSL対応するためにオレオレ証明書の作成をします。 この辺りは、こちらを参考にしました。 DockerでNginxのコンテナを作成し、https化してWebページやサービスを公開する方法 | Black Everyday Company
$ brew list openssl $ mkdir keys; cd $ _ ### 秘密鍵を暗号化してしまうと、コンテナ起動時(Nginx起動時)に 「Enter PEM pass phrase」とパスワードが求められ起動できないようなので暗号化はしない。### $ openssl genrsa 2048 > server.key ### XXXXは適当に入力 ### $ openssl req -new -key server.key > server.csr You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:JP State or Province Name (full name) [Some-State]:XXXX Locality Name (eg, city) []:XXXX Organization Name (eg, company) [Internet Widgits Pty Ltd]:XXXX Organizational Unit Name (eg, section) []:XXXX Common Name (e.g. server FQDN or YOUR name) []:example.com Email Address []:hoge@example.com Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: <--- パスワードは空白でOK An optional company name []: # 有効期限はざっくり100日で $ openssl x509 -in server.csr -days 100 -req -signkey server.key > server.crt # 現状の構成 $ tree . ├── Dockerfile ├── html │ └── index.html └── keys ├── server.crt ├── server.csr └── server.key
nginx.confの設定
HTTP(ポート80)で受けたリクエストはHTTPS(ポート443)にリダイレクトする設定にしておきます。
server { listen 80; server_name localhost; return 301 https://$host$request_uri; } server { server_tokens off; access_log /var/logs/access.log; error_log /var/logs/error.log; listen 443; server_name localhost; ssl on; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_certificate /etc/nginx/server.crt; ssl_certificate_key /etc/nginx/server.key; location / { alias /var/www/; } }
DockerFileの変更
FROM nginx ADD ./html /var/www/html ADD ./app.conf /etc/nginx/conf.d/app.conf ADD ./keys/server.crt /etc/nginx/server.crt ADD ./keys/server.key /etc/nginx/server.key RUN chmod 755 -R /var RUN chmod 400 /etc/nginx/server.key EXPOSE 443 CMD ["nginx", "-g", "daemon off;"]
Dockerイメージの作成とコンテナ立ち上げ
$ docker build --tag webapp . Sending build context to Docker daemon 12.29 kB Step 1/9 : FROM nginx ---> cc1b61406712 Step 2/9 : ADD ./html /var/www/html ---> Using cache ---> 102243bc267d Step 3/9 : ADD ./app.conf /etc/nginx/conf.d/app.conf ---> Using cache ---> 00668c5641b7 Step 4/9 : ADD ./keys/server.crt /etc/nginx/server.crt ---> Using cache ---> 85ee3c0da89a Step 5/9 : ADD ./keys/server.key /etc/nginx/server.key ---> Using cache ---> f3095cadf8a3 Step 6/9 : RUN chmod 755 -R /var ---> Running in 8eab56aefa1c ---> 565d1f1e9f07 Removing intermediate container 8eab56aefa1c Step 7/9 : RUN chmod 400 /etc/nginx/server.key ---> Running in fe5c00f05409 ---> 02c65246caec Removing intermediate container fe5c00f05409 Step 8/9 : EXPOSE 443 ---> Running in 1e67a2a92994 ---> d95cc7a21aa0 Removing intermediate container 1e67a2a92994 Step 9/9 : CMD nginx -g daemon off; ---> Running in 0efbccec8be8 ---> 437f35b60db0 Removing intermediate container 0efbccec8be8 Successfully built 437f35b60db0 $ docker run -d --name webapp -p 443:443 -p 80:80 webapp 2e389797c84eb2162c0f67ab13eb865966ea70ecead1077a61055eadd869e952
ブラウザで確認(https://localhost/)
とりあえずできました。
最後にログは、コンテナ外に出したいのでその辺を試します。
ホストのディレクトリにマウントする
以下のように-v でホストのディレクトリにマウントするのが まずは、手っ取り早い方法かなと思いした。
$ docker run -d -p 443:443 -p 80:80 -v /tmp/sslnginx-log:/var/log/nginx webapp
Syslogで吐き出す方法もありますが、こちらは別の機会に試そうと思います。 nginx-1.7.1でsyslogにログを飛ばせるようになったので試してみた - Dマイナー志向
まとめ
最近、手を動かすことが少なくなってきているように感じたのでローカルの SSLテスト環境を作ろうと思ってやってみましたが、Dockerの理解がまだ浅かったので 良い勉強になりました。
あと普段の業務では、SSL証明書をAWSのELBに配置してしまっているため、直接nginxに 設定する良い機会になりました。
今回作成した環境は、githubにあります。 GitHub - y-nakazawa/docker-sslnginx
- 作者: 久保達彦,道井俊介
- 出版社/メーカー: 技術評論社
- 発売日: 2016/01/16
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (4件) を見る
nginx実践ガイド impress top gearシリーズ
- 作者: 渡辺高志
- 出版社/メーカー: インプレス
- 発売日: 2017/02/16
- メディア: Kindle版
- この商品を含むブログを見る
- 作者: Clement Nedelcu,長尾高弘
- 出版社/メーカー: アスキー・メディアワークス
- 発売日: 2011/04/21
- メディア: 大型本
- 購入: 2人 クリック: 714回
- この商品を含むブログ (23件) を見る
- 作者: Dimitri Aivaliotis,高橋基信
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/10/26
- メディア: 大型本
- この商品を含むブログ (7件) を見る
- 作者: 鶴長鎮一,馬場俊彰
- 出版社/メーカー: 技術評論社
- 発売日: 2015/09/26
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
「Electronではじめるアプリ開発」を写経してみた ⑥
またかなり間が空いてしまったのと、写経したものの期待通りの動きではなく調査しましたが 以下の理由からかなりハマってしまっていました。
Electron上でのReactのDebug方法を確立できていなかった。
ES2016と、Promiseの理解不足から原因の特定に時間が掛かった。
そもそもFirebaseのお作法が分かってなく、原因の特定に時間が掛かった。
※1.に関しては、以下を参考にしてDebug環境の構築をしたいと思います。 qiita.com
※2. 3.の課題については別途調査してまとめようと思います。
今回は、チャットルーム一覧の実装から
src/renderer/Rooms.jsx
import React from "react"; import { hashHistory } from "react-router"; import RoomItem from "./RoomItem"; import firebase from "firebase/firebase-browser"; const ICON_CHAT_STYLE = { fontSize: 120, color: "#DDD" }; const FORM_STYLE = { display: "flex" }; const BUTTON_STYLE = { marginLeft: 10 }; export default class Rooms extends React.Component { constructor(props) { super(props); this.state = { roomName: "", rooms: [] }; this.db = firebase.database(); this.handleOnChangeRootName = this.handleOnChangeRootName.bind(this); this.handleOnSubmit = this.handleOnSubmit.bind(this); } componentDidMount() { this.fetchRooms(); } handleOnChangeRootName(e) { this.setState({ roomName: e.target.value }); } handleOnSubmit(e) { const { roomName } = this.state; e.preventDefault(); if (!roomName.length) { return; } // Make new Chatroom for a Firebase const newRoomRef = this.db.ref("/chatrooms").push(); const newRoom = { description: roomName }; newRoomRef.update(newRoom).then(() => { this.setState({ roomName: "" }); return this.fetchRooms().then(() => { hashHistory.push(`/rooms/${newRoomRef.key}`); }); }); } fetchRooms() { return this.db.ref("/chatrooms").limitToLast(20).once("value").then(snapshot => { const rooms = []; snapshot.forEach(item => { rooms.push(Object.assign({ key: item.key }, item.val())); }); this.setState({ rooms }); }); } // Drawing the left pane renderRoomList() { const { roomId } = this.props.params; const { rooms, roomName } = this.state; return ( <div className="list-group"> { rooms.map(r => <RoomItem room={r} key={r.key} selected={r.key == roomId} />)} <div className="list-group-header"> <form style={FORM_STYLE} onSubmit={this.handleOnSubmit} > <input type="test" className="form-control" placeholder="New room" value={roomName} onChange={this.handleOnChangeRootName} /> <button className="btn btn-default" style={BUTTON_STYLE}> <span className="icon icon-plus" /> </button> </form> </div> </div> ); } // Drawing the right pane renderRoom() { if (this.props.children) { return this.props.children; } else { return ( <div className="text-center"> <div style={ICON_CHAT_STYLE}> <span className="icon icon-chat" /> </div> <p> Join a chat room from the sidebar or create your chat room. </p> </div> ); } } render() { return ( <div className="pane-group"> <div className="pane-sm slider">{this.renderRoomList()}</div> <div className="pane">{this.renderRoom()}</div> </div> ); } }
以下は、チャットルーム一覧の個々のアイテムの実装
src/renderer/RoomItem.jsx
import React from "react"; import { Link } from "react-router"; const LINK_STYLE = { color: "inherit", textDecoration: "none" }; export default function RoomItem(props) { const { selected } = props; const { description, key } = props.room; return( <div className={selected ? "list-group-item selected" : "list-group-item"}> <Link to={`/rooms/${key}`} style={LINK_STYLE}> <div className="media-body"> <strong>{description}</strong> </div> </Link> </div> ); }
ルームを追加すると一覧に登録されることが確認できました。
Firebaseのchatroomsにも登録されました。
今回はここまで!!
Electronではじめるアプリ開発 ~JavaScript/HTML/CSSでデスクトップアプリを作ろう
- 作者: 野口将人,倉見洋輔
- 出版社/メーカー: 技術評論社
- 発売日: 2017/03/28
- メディア: 大型本
- この商品を含むブログ (1件) を見る
Reactビギナーズガイド ―コンポーネントベースのフロントエンド開発入門
- 作者: Stoyan Stefanov,牧野聡
- 出版社/メーカー: オライリージャパン
- 発売日: 2017/03/11
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
入門 React ―コンポーネントベースのWebフロントエンド開発
- 作者: Frankie Bagnardi,Jonathan Beebe,Richard Feldman,Tom Hallett,Simon HØjberg,Karl Mikkelsen,宮崎空
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/04/03
- メディア: 大型本
- この商品を含むブログ (2件) を見る
- 作者: 掌田津耶乃
- 出版社/メーカー: 秀和システム
- 発売日: 2016/09/16
- メディア: 単行本
- この商品を含むブログを見る
JavaScriptエンジニアが手っ取り早くReactの基礎を理解するための「超」入門書
- 作者: 天田士郎
- 発売日: 2017/02/05
- メディア: Kindle版
- この商品を含むブログを見る