- 投稿日:2019-03-30T19:53:20+09:00
SpringBoot+Vueなアプリケーションを構築する
目標
- サーバーサイドはSpring(Java)
- フロントはVue
- npmの恩恵を受けたい
- アウトプットはJarだけ
リポジトリ
https://github.com/iwanagat85/spring-boot-vue
構築
ディレクトリ構成
- Spring Initializr等で出来たSpringプロジェクト直下に
webディレクトリを作成- そこにvue-cliを使ってvueのプロジェクトを作成する
. ├── .gitignore ├── build.gradle ├── gradle ├── gradle.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── src | ├── main | └── test └── web ├── public ├── src ├── .gitignore ├── babel.config.js └── package.jsonVueProjectの設定
- デフォルトのポートがSpringと被るので
8081へ変更build時の出力先を/src/main/resources/static/に指定package.json"scripts": { "serve": "vue-cli-service serve --port 8081", "build": "vue-cli-service build --dest ../src/main/resources/static/", "lint": "vue-cli-service lint" }SpringProjectの設定
- 画面のリソースはVue側で自動生成するので
staticディレクトリを.gitignoreにまるっと登録.gitignoresrc/main/resources/static/
- リソース生成時にvue側の
buildを実行する--prefixでnpmを実行するディレクトリを直接指定するbuild.gradletask npmRunBuild() { if (!file("${rootDir}/web/node_modules").exists()) { "npm --prefix ${rootDir}/web install ${rootDir}/web" .execute() .waitForProcessOutput(System.out, System.err) } "npm --prefix ${rootDir}/web run build" .execute() .waitForProcessOutput(System.out, System.err) } processResources { dependsOn npmRunBuild }実行
$ ./gradlew bootRunその他
- npmのビルドが毎回走るから起動が物凄く遅い、、、
- フロントエンド作成時は
npm run serveしながら- APIでのやりとりはSpring側とportが違うから
CORSで怒られる
localhost:8081を許可しなきゃいけないけど、、、
- 投稿日:2019-03-30T18:39:11+09:00
はてなブログの歴代一位をまとめて見れるサイトを作った
はじめに
はてなブログのテクノロジーカテゴリをよく見ています。
今までの歴代一位がどのようなものだったか気になって一覧で見れるサイトを作りました。https://hatena-hotentry.netlify.com/
使った技術
- serverlessFramework
- go
- goquery
- RealtimeDatabase
- lambda
- vue
バッチ
はてなブログのデータを取得するスクリプトをバッチで動かしています。
はてなブログをスクレイピングするコード(go)を書き、ServerlessFrameworkを使ってlambdaにアップロードしています。
ServerlessFrameworkはserverless.ymlにcronを定義しておけば、cloudWatchを使ったlambdaのスケジュール実行を設定してくれるのでお手軽にバッチ処理が作れます。参考:https://dev.classmethod.jp/etc/serverless-framework-lambda-cron-execute/
スクレイピングしたデータを保存する場所としてはfirebaseのRealtimeDatabaseを使いました。
初めはCloudFirestoreの方に保存していたのですが、保存しているドキュメント数が5000近くになり、無料プランでの一日のドキュメント読み取り数が50000だったため、10回全件取得すると、その日はデータ取得ができなくなる状態になりました。
そのため、RealtimeDatabaseに乗り換えましたが、NoSQLにデータを保存する際の設計がわかっていれば、CloudFirestoreでもうまくできたような気がします。フロント
vueを使いました。vue-cliで雛形を作り、それをいじって作っています。
デプロイにはnetlifyを使っています。最後に
goとvueを勉強するために作りましたが、こういう小さいアプリケーションを色々作っていきたい気持ちです。
- 投稿日:2019-03-30T18:08:23+09:00
v-cardをクリックしたらv-dialogが開くサンプル
Vuetifyの公式サイトには、v-btnをクリックしてv-dialogを開くサンプルが掲載されています。1
ここでは、v-cardをクリックしたときにv-dialogを開くサンプルを作成してみました。
長い文章を小さめのカードで表示するとき、v-cardでは省略形で表示しておき、そこをクリックしたら全文がv-dialogで表示されるようになっています。
サンプル
CodePenで動くサンプルを作ってみました。
なお、元は単一ファイルコンポーネントとして「CardTest」を定義し、それをApp.vueで複数並べる使い方をしていました。
しかし、CodePenではそのままだとうまく動きませんので、propsで定義していたtitleとcontentをdata()に移動させてあります。See the Pen Vuetify Sample - click card, open dialog by shozzy (@shozzy) on CodePen.
ポイント
data()で定義されているdialogの値が、v-dialog要素のv-model属性にバインディングしてあります。
デフォルトではfalseなのでv-dialogは非表示です。
v-cardの方でクリックイベントを拾い dialog = true にすると、v-dialogが表示される仕掛けです。単一ファイルコンポーネントのインスタンスごとに状態を持っているので、App内に複数のカードを並べてあっても、正しく各カードの状態を保持できます。
<template> <div class="cardtest"> <v-card min-width="200px" height="100%" @click.stop="dialog = true"> <v-card-title>{{ title }}</v-card-title> <v-divider></v-divider> <v-card-text>{{ shortenedContent }}</v-card-text> </v-card> <v-dialog v-model="dialog" scrollable max-width="80%"> <v-card> <v-card-title>{{ title }}</v-card-title> <v-divider></v-divider> <v-card-text height="300px">{{ content }}</v-card-text> </v-card> </v-dialog> </div> </template> <script> export default { name: 'CardTest', props: { title: String, content: String }, data(){ return { dialog: false } }, computed: { shortenedContent: function(){ let maxlength = 100; if(this.content.length <= maxlength){ return this.content; } else{ return this.content.substr(0,maxlength-10)+'...(続きを読む)' } } } } </script>
- 投稿日:2019-03-30T15:00:03+09:00
Vuejsでのプロトタイピングを簡単にできるアプリが欲しかったので作っている話
はじめに
タイトルの通り作っているものがあるが、公開することで既にこれできるよみたいな情報も得られると思ったので公開しようと思った。
本当はもう少し作りこんでから公開したかった。
ただ、残業が多すぎてアプリケーションが作れないから転職しようとしていて、エージェントから公開しているGitHubとかQiitaのアカウントありますかと聞かれたからROM専から卒業しようと思った。とりあえず動くもの
https://sterashima78.github.io/vue-webpage-builder
とりあえず動いている絵
作っている動機
基本的には READMEに書いておいたが、書きなぐりました感が強いのでもう少し丁寧に書く。
似ていたもの
既存のもので十分な人もいると思うので、作る前に調べていた時に触ったものを書いておく。
ほしかったもの
- 実用的なソースが出力される
- プロトタイピングに使ったあとで、じゃあこれで行こうとなった時に全部書き直すよりも一部直せば済むほうが嬉しい
- Vueggはコンポーネントの配置が絶対座標だった。絵を作るのには適していたのだけど、出力をそのまま使う気にはならなかった
- 任意のコンポーネントを利用できる
- Vuetifyや、Elementなどたくさんコンポーネントフレームワークがあるので、任意のものが使えてほしい
- 自分が作ったコンポーネントも利用できるようになっていてほしい
- Vueggは単一のコンポーネントフレームワークしか利用できなかった (Issueにもなっている)
どうするか
任意のコンポーネントを利用するという点はアプリケーションを構築に利用しているVueインスタンスと、アプリケーション内で構築されるプロトタイプで利用するVueインスタンスが同一であると絶対に解決できないと考えた。
初めはインスタンスを複数作って Shadow DOMを使えば何とかなるとも考えたけど素直にiframeを使うことにした。
- iframe要素内でscriptタグでvuejsを読み込み、アプリケーションからiframe内のVueインスタンスを参照する
- メインのアプリケーション部分でvnodeに相当するツリーを構築する
- iframe内のvueに渡して renderメソッド で描画
これができれば、iframe内で任意のスクリプトを読ませる機能を作ることで、『ほしかったもの』に書いた、任意のコンポーネントが利用できるの要件を満たせる。
面倒だったこと
Vuex Store は複数のインスタンスで共有できない
アプリケーションで構築するvnodeデータをVuexで管理して、そのデータをiframe内のvueでも共有して renderでstateを参照できれば一番シンプルだと考えて、Vuexベースで作ってた。(入門したてのTypeScriptでstoreに型つけるにはどうすればいいんだとか調べてまぁまぁ時間かかった)。
いざ描画しようと思ったら、できないということを知って困った困った。結局 Rx を併用して無理やりやっている感がある。
Vueに登録されている全コンポーネントが知りたい
アプリケーションの性質的に利用できるコンポーネントがリストされていて、選択できないといけないが、それを取得するためのインターフェースが見つからなかった。
devtoolsとか見ていたら方法がありそうだったので調べたら、
Vue.options.componentsに入っていた。おわりに
Vuejsでのプロトタイピングを簡単にできるアプリを作っているという話でした。
もしも、上に書いた『ほしかったもの』をカバーするサービスやアプリケーションがあれば教えていただけると嬉しいです。以下が、今後やりたいこと。
- 追加したい機能
- 任意のコンポーネントフレームワークを使えるようにする (いまはvuetifyのみだけど仕組み的には可能なのですぐやりたい)
- 複数ページへの対応 (vue-routerで)
- 作成途中の保存・読み込み
- 機能以外で対応したいこと
- リファクタリング
- CI・CDの導入
- ある程度機能がそろったら
- 残業が減ってアプリケーションを作れるようになる
- 投稿日:2019-03-30T14:16:41+09:00
Google Books APIとVue.jsを使ってシンプルなBook Finder appを作る
このアプリを作ろうと思ったキッカケ
2月末にChingu Voyageという海外のプロジェクトに参加し、見事に制限時間内に課題のGoogle APIを使ったBook Finder アプリを作ることができずプロジェクトから落ちました。(笑)
というのも私はずっとrailsでしか勉強をしていなくてAPIはなんとなくJSONだったりaxiosが必要だということは分かっていたのですが、Javascriptを本格的に勉強したことがなく、プロジェクトでJavascriptが必要と分かった瞬間必死でネット上の教材を探したのですが、なかなか希望するものがなく・・・
いや写経してもなんの意味もないなと気づいたので、最近勉強をしているVue.jsを使って自分で作ってみようと思いました。こんなアプリができます
↓こんなアプリが最終的にできます。Vue.jsだと直感的にコードが理解しやすく好きです。
Google books api app
※モバイルでみるとボタンの文字がおかしいですがまた調整します。3 SHOW DETAILボタンで、googlebooksのサイトに遷移し、本の詳細を見る
ソースコードと解説
main.jsconst vm = new Vue({ el: '#app', data() { return{ query:'', items:[], #apiから取得したデータを格納 } }, methods:{ getResult(query){ axios.get("https://www.googleapis.com/books/v1/volumes?q=search" + query).then(response => { console.log(response.data); this.items = response.data.items; }); } } });index.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Book Finder</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css"> <link rel="stylesheet" href="main.css"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> </head> <body> <div class="container" id="app"> <h3 class="text-center">Book Finder</h3> <p> Search:<form v-on:submit.prevent="getResult(query)"> <input type="text" placeholder="Type in your search" v-model="query" /></p> #フォーム入力したものをv-modelでバインド <div class="book_wrapper"> <div class="book_card" v-for="item in items"> <div class="row no-gutters"> <div class="col-md-4"> <img class="card-img" v-bind:src="item.volumeInfo.imageLinks.thumbnail" /> </div> <div class="col-md-8"> <div class="card-body"> <h5 class="card-title">{{item.volumeInfo.title}}</h5> <p class="card-text">{{item.volumeInfo.authors}}</p> <p class="card-text"><small class="text-muted">{{item.volumeInfo.publisher}}</small></p> </div> <div class="book_button"> <a v-bind:href="item.volumeInfo.previewLink" class="waves-effect waves-light btn" target="_blank">show detail</a> </div> </div> </div> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> #vue.jsのインストール <script src="main.js"></script> #jsファイルを読み込む <script src="https://cdn.jsdelivr.net/npm/axios@0.17.1/dist/axios.min.js"></script> #axiosをインストール </body> </html>main.css.book_wrapper{ width:100%; } .book_card{ margin:10px; float:left; box-sizing: border-box; -webkit-box-sizing: border-box; width:40%; } .book_button{ text-align:center; }参考知識
利用したVue.jsやaxiosについて下に参考メモを記入しておきます。
Vue.jsのインストール
CDNで用意されているので、index.htmlに貼り付けましょう。
詳しくはインストールを参照してください。JSONデータとaxiosでgetしたデータについて
今回はGooglebooksAPIを利用してアプリを作りました。
ドキュメントはこちら。例えばURLに
https://www.googleapis.com/books/v1/volumes?q=search+harryこのようにいれると、harryと名前がつくものを検索することができます。
これを検索欄に入力すると画面いっぱいに以下のようなJSONデータが表示されます。{ "kind": "books#volumes", //books#volumesの種類で検索 "totalItems": 561, //561個のデータがある "items": [ //そのうちのitemsは以下のとおり { "kind": "books#volume", "id": "8VEJo-StWrkC", "etag": "n0F3h00LHMo", "selfLink": "https://www.googleapis.com/books/v1/volumes/8VEJo-StWrkC", "volumeInfo": { "title": "The Search for Harry C", //タイトル "authors": [ "Mort Altshuler & Irv Susson" //著者 ], "publisher": "Xlibris Corporation", //発刊者 "publishedDate": "2001-05-14", //発刊日 "description": "Harry Connors is at Penn State and in love with Sarah, a fellow student. They marry shortly after they are graduated. He gets a job with a contract agency of the Atomic Energy Commission involved in research programs related to atom bomb radiation effects, trigger devices and other secret projects. Harry´s work takes him to New Mexico, Philadelphia Naval Air Station and Indian Springs Air Force Base, Nevada. On the day that Harry disappears, a test atom bomb is detonated, an unmanned plane flown near the mushroom cloud to collect data explodes and a top-secret plan goes missing. Harry is never found and the government´s investigation circumstantially determines that Harry was an agent of the Soviet Union. Sarah, pregnant, returns to Pennsylvania. Four decades later, Harry´s son, Adam, sets out on an intriguing mission to find out what happened to his father. Along the way, with the help of his fiancee and two of his father´s fraternity brothers, he finds an aged member of the Russian spy machine, ex coworkers and a host of some very odd folks.", //本の説明 "industryIdentifiers": [ { "type": "ISBN_13", "identifier": "9781465315236" }, { "type": "ISBN_10", "identifier": "1465315233" } ], "readingModes": { "text": true, "image": true }, "pageCount": 211, "printType": "BOOK", "categories": [ "Fiction" ], "maturityRating": "NOT_MATURE", "allowAnonLogging": false, "contentVersion": "0.1.0.0.preview.3", "panelizationSummary": { "containsEpubBubbles": false, "containsImageBubbles": false }, "imageLinks": { //smallThumbnailとthumbnailはどちらも画像を表示する "smallThumbnail": "http://books.google.com/books/content?id=8VEJo-StWrkC&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api", "thumbnail": "http://books.google.com/books/content?id=8VEJo-StWrkC&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api" }, "language": "en", "previewLink": "http://books.google.co.jp/books?id=8VEJo-StWrkC&pg=PA69&dq=search+harry&hl=&cd=1&source=gbs_api", //今回は詳細ページをこちらにします。 "infoLink": "https://play.google.com/store/books/details?id=8VEJo-StWrkC&source=gbs_api", "canonicalVolumeLink": "https://market.android.com/details?id=book-8VEJo-StWrkC" }, //ここから下はこのアプリでは使わない情報です。 "saleInfo": { "country": "JP", "saleability": "FOR_SALE", "isEbook": true, "listPrice": { "amount": 1404.0, "currencyCode": "JPY" }, "retailPrice": { "amount": 1264.0, "currencyCode": "JPY" }, "buyLink": "https://play.google.com/store/books/details?id=8VEJo-StWrkC&rdid=book-8VEJo-StWrkC&rdot=1&source=gbs_api", "offers": [ { "finskyOfferType": 1, "listPrice": { "amountInMicros": 1.404E9, "currencyCode": "JPY" }, "retailPrice": { "amountInMicros": 1.264E9, "currencyCode": "JPY" } } ] }, "accessInfo": { "country": "JP", "viewability": "PARTIAL", "embeddable": true, "publicDomain": false, "textToSpeechPermission": "ALLOWED", "epub": { "isAvailable": true, "acsTokenLink": "http://books.google.co.jp/books/download/The_Search_for_Harry_C-sample-epub.acsm?id=8VEJo-StWrkC&format=epub&output=acs4_fulfillment_token&dl_type=sample&source=gbs_api" }, "pdf": { "isAvailable": true, "acsTokenLink": "http://books.google.co.jp/books/download/The_Search_for_Harry_C-sample-pdf.acsm?id=8VEJo-StWrkC&format=pdf&output=acs4_fulfillment_token&dl_type=sample&source=gbs_api" }, "webReaderLink": "http://play.google.com/books/reader?id=8VEJo-StWrkC&hl=&printsec=frontcover&source=gbs_api", "accessViewStatus": "SAMPLE", "quoteSharingAllowed": false }, "searchInfo": { "textSnippet": "They did \u003cb\u003efind\u003c/b\u003e a small cave at Spectre Mountain about three hundred feet up. It \u003cbr\u003e\nwas only about four feet high and about seven feet deep and it looked as though \u003cbr\u003e\nit may have been a burial site. It was obvious that it had been searched and \u003cbr\u003e\nruined ..." } },v-modelで検索フォームで入力した文字をデータとして渡す
基本的な使い方としては以下のような形があります。入力や選択をすると、即座にデータ、そしてDOMに反映されます。今回はこちらを利用して検索フォームに入力した言葉をmain.jsのmethodsに渡しています。
(参考)
html<p>{{message}}</p> <input v-model="message">javascriptvar app = new Vue({ el: '#app' data:{ message:'メッセージ' } })レスポンシブ対応
【CSS】box-sizing:border-boxの使い方|効かない時は?
以前からレスポンシブ対応に苦手意識がありましたが、今回box-sizingを利用して非常に楽に設定できるようになりました。デザイン
materializecssとBootstrapが混在してしまいました。
が、まあいっかという感じです。
このようなデザインフレームワークを使うと自動でレスポンシブになるので好きです。足りないところをプラスすればいいです。疑問点・理解しきれていないこと
検索結果がすべて表示されないこと
APIから取得したデータはかなり多いはじなのに、1ページしか表示されません。なぜかまだ調べてみます。
var app と const vm の違い
Vue.jsを表面上しか分かった気になっていないため、このような違いに混乱します。
違いについて検索してみると、var appもあればconst vmもあり、どっちも使えるが、constは読み取り専用となり、再代入ができない等の理由がある・・・とのこと。まだ理解しておりません。v-on と v-modelの違い
公式リファレンス:イベントハンドリングとフォーム入力バインディングを参照してみると、v-onの場合はクリックしたりラジオボタンを押したりすることで、イベントを発生させる場合に使用する、v-modelの場合は、フォームに入力した文字等を非同期で反映させたいときに使用するという使い分けかなと思います。
今回参考にしたサイト
Vue.jsでQiita検索APIを使ってみる
Vue.jsとAxiosなら驚くほど簡単に作れる!外部APIを使ったWebアプリの実例
TypeScriptでNuxtアプリを作るチュートリアル【書籍検索システム】間違っている記述やもっとこうしたほうがいいとのご意見等ありましたらコメントください。
- 投稿日:2019-03-30T13:27:00+09:00
Vue.js + Vuex + TypeScriptを使ってTodoListを作る
Vuexを使っての状態管理のやり方をいまいちイメージできていなかったので、実際に動かしながら作ってみた。
環境はvue-cli 3を使って構築しました。
詳しくは公式サイトを参照ください。
環境
node: v10.15.1
npm: 6.8.0
vue: 2.5.22
vuex: 3.0.1
typescript: 3.0.0成果物のpackage.jsonに環境周りが書いてあるので、ご参考までに
そもそも、vuexって何?
vuexの公式サイトには、こう書かれています。
Vuex は Vue.js アプリケーションのための 状態管理パターン + ライブラリです。
詳しくは公式サイトに詳しく載っています。
ハマったポイント
1つコンポーネントを作成して、2つ目のコンポーネントを作った時に、片方のコンポーネントしか表示されないなぁって思いました。
原因が、componentsフォルダに入っているコンポーネントファイルたちには、
@Componentをつけないと、コンポーネントとして認識してくれない・・・それで少しハマりました。src/component/TodoList.vue<template> <div> <h3>Todo List</h3> <ul> <li v-for="todo in todos" :key="todo.id"> <p>ID:{{todo.id}}</p> <p>Text:{{todo.text}}</p> </li> </ul> </div> </template> <script lang="ts"> import { Component, Vue, Prop } from 'vue-property-decorator'; import store from "@/stores/toDo"; @Component//←これをつけないと「コンポーネント」として、認識されない・・・ export default class TodoList extends Vue { get todos() { return store.getters.todos; } } </script>少し考えたところ
今回storeファイルをインポートする形式にしたのは、今後ページを増やす際に、store自体のファイルを増やす可能性が高かったので、storesフォルダを作成して、ページごとにファイルを分けることにしました。
まとめ
ある程度vuexの流れがわかってきたので、今度はfirebaseを使って、データベース管理をどういう風にやるかを考えて、実装して、記事にしようと思います。
参考
- 投稿日:2019-03-30T06:28:58+09:00
KubernetesとNode.jsでマイクロサービスを作成する 5/8 Dockerを使ったサービス構築
第5章 Dockerを使ったサービス構築
第4章までで、Twitterライクなマイクロサービスを構成する、3つのサービスを作成してきました。
次はいよいよ、これらのサービスをマイクロサービスとして構築するのですが、いきなりKubernetesを利用する前にワンクッションはさみます。本章では、各サービスのDockerイメージを作成し、以下のような構成で、ローカルのDocker環境でTwitterライクなサービスを起動します。
完成版のリポジトリはこちらにあるので、実装がうまくいかない場合は比較してみてください。
reireias/microservice-sample-integrationチュートリアル全体
- 第1章 概要
- 第2章 Tweetサービス
- 第3章 Userサービス
- 第4章 Webサービス
- 第5章 Dockerを使ったサービス構築
- 第6章 Kubernetes with minikube
- 第7章 Kubernetes with GCP
- 第8章 Kubernetes with AWS
構成
dockerとdocker-compose
まずはローカルに
dockerとdocker-composeがインストールされていることを確認しましょう。docker version Client: Version: 18.06.1-ce API version: 1.38 Go version: go1.10.4 Git commit: e68fc7a Built: Fri Jan 25 14:33:54 2019 OS/Arch: linux/amd64 Experimental: false Server: Engine: Version: 18.06.1-ce API version: 1.38 (minimum version 1.12) Go version: go1.10.4 Git commit: e68fc7a Built: Thu Jan 24 10:56:33 2019 OS/Arch: linux/amd64 Experimental: falsedocker-compose version docker-compose version 1.23.2, build 1110ad0 docker-py version: 3.7.0 CPython version: 3.7.2 OpenSSL version: OpenSSL 1.0.2g 1 Mar 2016microservice-sample-integrationリポジトリの作成
ここからは、各サービスのリポジトリ内のコードを利用して、DockerイメージやKubernetesの設定を作成していくことになります。
そのためには、各サービスのコードが参照できる構成が望ましいです。今回は下記のディレクトリ構成をとるような、
microservice-sample-integrationリポジトリを作成します。microservice-sample-integration └── services ├── microservice-sample-tweet ├── microservice-sample-user └── microservice-sample-web実際の開発現場では、このリポジトリの管理をSREチームが主導して行ったり、各サービスの開発者が協力して行ったりします。
microservice-sample-integrationという名前でリポジトリを作成し、cloneしておきましょう。つづいて、このリポジトリ内で行う各種操作を
Makefileに記述し、makeコマンドから実行できるようにします。
作成するコマンドは下記の通りです。
clone: 各サービスをservicesディレクトリ以下にcloneするpull: 各サービスをgit pullするbuild: 各サービスのDockerイメージのビルドを行うup: 全サービスをdocker-composeを使って、ローカルのDocker環境で立ち上げるdown: 全サービスをdocker-composeを使って削除するseed: UserサービスとTweetサービスでscripts/initialize.jsを実行する
Makefileは以下のようになります。MakefileWEB_REPOSITORY := https://github.com/reireias/microservice-sample-web USER_REPOSITORY := https://github.com/reireias/microservice-sample-user TWEET_RESPOSITORY := https://github.com/reireias/microservice-sample-tweet clone: git clone $(WEB_REPOSITORY) ./services/microservice-sample-web git clone $(USER_REPOSITORY) ./services/microservice-sample-user git clone $(TWEET_RESPOSITORY) ./services/microservice-sample-tweet pull: cd ./services/microservice-sample-web && git pull cd ./services/microservice-sample-user && git pull cd ./services/microservice-sample-tweet && git pull build: docker-compose build up: docker-compose up -d down: docker-compose down seed: docker-compose exec user node /app/scripts/initialize.js docker-compose exec tweet node /app/scripts/initialize.js
Makefileが作成できたら、make pullを実行し、各サービスをservicesディレクトリ配下へcloneします。make cloneDockerイメージの作成
つづいて、各サービスのDockerイメージを作成するための、Dockerファイルを実装していきます。
このファイルから生成されるDockerイメージは本番環境でも使用されます。
実際の開発現場では各サービスが責任を持って作成する場合もありますし、SREチームなど、Dockerのノウハウに長けたチームがサービス横断で管理するケースもあるでしょう。services/microservice-sample-tweet/DockerfileFROM node:11.10-alpine ENV NODE_ENV=production WORKDIR /app RUN apk add --no-cache curl && \ curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | sh ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.5.0/wait /wait RUN chmod +x /wait COPY package.json . RUN yarn install && ./bin/node-prune COPY . . EXPOSE 3000 CMD /wait && yarn startservices/microservice-sample-user/DockerfileFROM node:11.10-alpine ENV NODE_ENV=production WORKDIR /app RUN apk add --no-cache curl && \ curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | sh ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.5.0/wait /wait RUN chmod +x /wait COPY package.json . RUN yarn install && ./bin/node-prune COPY . . EXPOSE 3000 CMD /wait && yarn startservices/microservice-sample-web/DockerfileFROM node:11.10-alpine as builder ENV NODE_ENV=production ARG GITHUB_CLIENT_ID ARG GITHUB_CLIENT_SECRET WORKDIR /app RUN apk add --no-cache curl && \ curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | sh COPY package.json . COPY yarn.lock . RUN yarn install && ./bin/node-prune COPY . . RUN yarn build FROM node:11.10-alpine WORKDIR /app ADD package.json ./ ADD nuxt.config.js ./ COPY --from=builder ./app/server ./server COPY --from=builder ./app/node_modules ./node_modules COPY --from=builder ./app/.nuxt ./.nuxt EXPOSE 3000 CMD yarn startWebサービスに関してはマルチステージビルドを利用して最終的なイメージのサイズを軽くするように工夫しています。
実装後、各
Dockerfileはcommitしておきましょう。docker-compose.ymlの作成
上記で用意した
Dockerfileを、dockerコマンドを利用してビルド、コンテナの起動等やってもよいのですが、環境変数等、引数が多くなってしまうため、今回はdocker-composeを利用して一元管理します。
docker-compose.ymlに起動したいコンテナの設定を書いていくことで、一括で立ち上げたり、削除することが可能です。今回は、Webサービス、Userサービス、Tweetサービスと、Userサービス用DB、Tweetサービス用DBの合計5つのコンテナを定義します。
docker-compose.yml--- version: '3' services: web: build: context: ./services/microservice-sample-web dockerfile: Dockerfile image: microservice_web:1.0 ports: - 3000:3000 environment: NUXT_HOST: 0.0.0.0 USER_SERVICE: http://user:3000 TWEET_SERVICE: http://tweet:3000 GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID} GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET} user: build: context: ./services/microservice-sample-user dockerfile: Dockerfile image: microservice_user:1.0 environment: MONGODB_URL: mongodb://user-db:27017/user MONGODB_ADMIN_NAME: root MONGODB_ADMIN_PASS: example WAIT_HOSTS: user-db:27017 depends_on: - user-db user-db: image: mongo volumes: - user-db:/data/db environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: example tweet: build: context: ./services/microservice-sample-tweet dockerfile: Dockerfile image: microservice_tweet:1.0 environment: MONGODB_URL: mongodb://tweet-db:27017/user MONGODB_ADMIN_NAME: root MONGODB_ADMIN_PASS: example WAIT_HOSTS: tweet-db:27017 USER_SERVICE: http://user:3000 depends_on: - tweet-db tweet-db: image: mongo volumes: - tweet-db:/data/db environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: example volumes: user-db: tweet-db:GitHubのOAuthのキー等はGit管理する対象ではないので、
.envファイルに記述します。
docker-composeの機能により、カレントディレクトリの.envファイルが読み込まれ、docker-compose.yml中で利用することができるようになります。
値はDocker環境の場合エンドポイントがlocalhost:3000と、前章までと同じなため、前章で使用していたのと同じGitHubのOAuth設定が利用可能です。.envGITHUB_CLIENT_ID=xxxxxxxxxxxxxxxxxxx GITHUB_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
services/.gitkeepファイルを作成しておきます。touch services/.gitkeep
.gitignoreファイルを以下のように設定します。.gitignoreservices/* !services/.gitkeep .envサービスを起動してみる
それでは、サービスを起動してみます。
# Dockerイメージをビルド make build # サービスの立ち上げ make up # ダミーデータの投入 make seed
http://localhost:3000へアクセスしてみましょう。
問題なく動作していれば本章の実装は完了になります。第5章まとめ
本章では、各サービスの
Dockerfileを作成し、docker-composeを利用してマイクロサービスをローカルのDocker環境上に構築しました。次の章ではいよいよKubernetes上でマイクロサービスを動作させてみます。
- 投稿日:2019-03-30T00:21:35+09:00
Responder + Vue.jsプロジェクト作成手順
TL;DR
最近
responderというPython製の非同期処理が売りのWebフレームワークを知ったので、Vue.jsと組み合わせてプロジェクトを作成した手順を紹介します。プロジェクト作成
Responder側
まずはディレクトリを作成して
pipenvで環境を作っていきます。$ mkdir responder-vue-sample $ cd responder-vue-sample $ pipenv --python 3.7
Pipfileを以下のように編集[[source]] name = "pypi" url = "https://pypi.org/simple" verify_ssl = true [dev-packages] [packages] asgiref = "==2.3.2" responder = "*" [requires] python_version = "3.7"
responder以外にasgirefをPipfileにバージョンを固定して記述しています。
asgirefはresponderの依存パッケージでresponderをインストールすれば自動的にインストールされるのですが、記事投稿時そのままでは3.0.0が入ってしまいVue.jsのビルド済みファイルが入るstaticディレクトリ配下のファイルを正しく読み込めないため2.3.2で固定しています。以下のコマンドでライブラリをインストールします。
$ pipenv installサーバープログラム
プロジェクト直下に
app.pyを作成してstatic/index.htmlを表示するだけのコードを記述します。app.pyjimport responder api = responder.API() if __name__ == '__main__': api.add_route('/', static=True) api.run()Vue.js側
まず
vue-cliをグローバルにインストールします。$ npm install -g @vue/cliインストール後、先程作ったプロジェクト直下にVue.jsのプロジェクトを作っていきます。
カレントディレクトリの指定以外はお好みで大丈夫です。$ vue create . Vue CLI v3.5.1 ? Generate project in current directory? (Y/n) Y ? Please pick a preset: (Use arrow keys) ❯ default (babel, eslint) Manually select featuresVue.jsのプロジェクトの作成ができたら、ビルドしたファイルを
staticに吐くようにpackage.jsonを編集します。package.json{ "name": "responder-vue-sample", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", - "build": "vue-cli-service build", + "build": "vue-cli-service build --dest static", "lint": "vue-cli-service lint" }, "dependencies": { "vue": "^2.6.6" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.5.0", "@vue/cli-plugin-eslint": "^3.5.0", "@vue/cli-service": "^3.5.0", "babel-eslint": "^10.0.1", "eslint": "^5.8.0", "eslint-plugin-vue": "^5.0.0", "vue-template-compiler": "^2.5.21" }, "eslintConfig": { "root": true, "env": { "node": true }, "extends": [ "plugin:vue/essential", "eslint:recommended" ], "rules": {}, "parserOptions": { "parser": "babel-eslint" } }, "postcss": { "plugins": { "autoprefixer": {} } }, "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ] }次にベースURLを
/からstaticに変更するためにvue.config.jsを作成してpublicPathを変更します。vue.config.jsmodule.exports = { publicPath: "static" }最終的に以下のようなファイル構成になるかと思います。
$ tree -L 1 tree -L 1 . ├── Pipfile ├── Pipfile.lock ├── README.md ├── app.py ├── babel.config.js ├── node_modules ├── package.json ├── public ├── src ├── static ├── templates ├── vue.config.js └── yarn.lockビルドと実行
では実際にビルドしてからサーバープログラムを起動して
responderからVue.jsが呼べているか確認していきます。$ pipenv run responder build # or yarn build $ pipenv run python app.pyこれで
localhost:5042にアクセスして以下のようにVue.jsのホーム画面が出たら終了です。お疲れ様でした。完成したものはこちら
https://github.com/KeisukeToyota/responder-vue-sample






