- 投稿日:2020-07-27T23:39:40+09:00
Node.jsとMySQLをDockerComposeで組んでみた
初めてのDockerネットワーク
これから先僕が環境構築する際、思い出せるようにするための、です。事前準備
まずDockerHubから二つのイメージをpullするところから
それぞれ12系と5.7系を使うのでバージョン指定
$ docker run -it node:12
$ docker run -it mysql:5.7
runはpull,create,startを一気に行ってくれる
control+p+qでコンテナから出る
その後
$ docker ps -a
で一度止めて、
コンテナが作成されているかを確認する(Upなら一度stopしてExitedにしておく)
これからは$docker start [コンテナID]
で起動できる
$ docker rename [古いコンテナ名] [新しいコンテナ名]
しておいた方がわかりやすい正しいバージョンのものが入っているかの確認は起動後
$ docker exec -it [コンテナ名] bash
attachと違うのはコンテナを起動したまま抜けることができるのかできないのかが違うexecは起動したまま抜けることができる
でコンテナに入り、バージョン確認...MySQLが起動しない...
先人様がいらっしゃいました。ありがとうございます。↓
https://qiita.com/takepan/items/0cbf13af3a0bb2c243abよしこれで確認できた
...でどうやってつなげるん??
Docker Composeとは
複数のコンテナを組み合わせてシステムを構成するための仕組みに用いるのがDocker Composeと呼ばれるもので、予めコンテナの起動方法やボリューム、ネットワークの構成などが書かれている
こんなものがあるのか、今まで一つのコンテナを独立させて動かしていたので、全く知りませんでした...
事前知識でDockerネットワークやマウントのことを知らなければならないとのことで書籍を借りて軽く頭に入れておきました。
全然知らないままDockerを使っていたのだなと反省そしてこれを書いていく...
docker-compose.yml
を作ってバージョン決めて、サービスの中身にnode.jsとmysql使いますよってこととかそれぞれの設定など、先人のymlファイルを参考にゴリゴリ書いていく...書き方を丁寧に載せてる先人様の記事はこちらです。ありがとうございます。↓
https://qiita.com/zembutsu/items/9e9d80e05e36e882caaaかけたら、
docker-compose up
で起動
しばし待つ...しばし待つ
終わらねえ
db | Version: '5.7.31' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL)ここで止まって一向に動かない...
仕方ないので、control+cで一度抜けました
docker-compose ps
で確認すると動いといて欲しいdb(mysql)が動いてない...一か八かで
docker start db
を試すと...stateがExitからUpに
一件落着ていうかこのやり方だと最初にコンテナは立ち上げなくても、なんならimageもpullしてこなくて急にdocker compose書いてあげた方がよかったのね...
じゃあ他のコンテナもたてます
今回はフロント、バック、dbの三つのコンテナを用意します。
docker-compose start
- 投稿日:2020-07-27T23:39:40+09:00
Node.jsとMySQLをDockerComposeで組みつつ、React,Express,MySQLの環境構築を躓きながらやっていく
初めてのDockerネットワーク
これから先僕が環境構築する際、思い出せるようにするための、です。事前準備
まずDockerHubから二つのイメージをpullするところから
それぞれ12系と5.7系を使うのでバージョン指定
$ docker run -it node:12
$ docker run -it mysql:5.7
runはpull,create,startを一気に行ってくれる
control+p+qでコンテナから出る
その後
$ docker ps -a
で一度止めて、
コンテナが作成されているかを確認する(Upなら一度stopしてExitedにしておく)
これからは$docker start [コンテナID]
で起動できる
$ docker rename [古いコンテナ名] [新しいコンテナ名]
しておいた方がわかりやすい正しいバージョンのものが入っているかの確認は起動後
$ docker exec -it [コンテナ名] bash
attachと違うのはコンテナを起動したまま抜けることができるのかできないのかが違うexecは起動したまま抜けることができる
でコンテナに入り、バージョン確認...MySQLが起動しない...
先人様がいらっしゃいました。ありがとうございます。↓
https://qiita.com/takepan/items/0cbf13af3a0bb2c243abよしこれで確認できた
...でどうやってつなげるん??
Docker Composeとは
複数のコンテナを組み合わせてシステムを構成するための仕組みに用いるのがDocker Composeと呼ばれるもので、予めコンテナの起動方法やボリューム、ネットワークの構成などが書かれている
こんなものがあるのか、今まで一つのコンテナを独立させて動かしていたので、全く知りませんでした...
事前知識でDockerネットワークやマウントのことを知らなければならないとのことで書籍を借りて軽く頭に入れておきました。
全然知らないままDockerを使っていたのだなと反省そしてこれを書いていく...
docker-compose.yml
を作ってバージョン決めて、サービスの中身にnode.jsとmysql使いますよってこととかそれぞれの設定など、先人のymlファイルを参考にゴリゴリ書いていく...書き方を丁寧に載せてる先人様の記事はこちらです。ありがとうございます。↓
https://qiita.com/zembutsu/items/9e9d80e05e36e882caaaかけたら、
docker-compose up
で起動
しばし待つ...しばし待つ
終わらねえ
db | Version: '5.7.31' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL)ここで止まって一向に動かない...
仕方ないので、control+cで一度抜けました
docker-compose ps
で確認すると動いといて欲しいdb(mysql)が動いてない...一か八かで
docker start db
を試すと...stateがExitからUpに
一件落着ていうかこのやり方だと最初にコンテナは立ち上げなくても、なんならimageもpullしてこなくて急にdocker compose書いてあげた方がよかったのね...
じゃあ他のコンテナもたてます
今回はフロント、バック、dbの三つのコンテナを用意します。
docker-compose start
動かない...
それもそのはず、docker-composeに必要なコマンドを書いてなかったからなんですね
いやymlファイルに書くコマンド全然わからん...ってなった僕はとりあえずdocker-compose run [コンテナ名] bash
で中に潜り込んで、reactの環境を作ることに...
node -v
,npm -v
でバージョンを確認
npm install -g create-react-app
でcreate-react-appをインストール
create-react-app [プロジェクトを入れるディレクトリ名]
しばしまつ...
Happy hacking!
これで完了(docker-compose run [コンテナ名] bashで中に潜り込んで作ったせいで、docker-compose upでローカルに作られたディレクトリの中に[プロジェクトを入れるディレクトリ名]のディレクトリができてしまったので削除しておく)
- 投稿日:2020-07-27T20:07:32+09:00
Firebaseのfunctionsでadmin.storage().bucketを使って画像をアップロードした時に困ったこと集
firebase client sdkと違う
まずはじめに、firebase admin sdkでは、
firebaseのフロント側で使うSDKの様にcloud storageをそのまま対応してないみたいです。そのため、下記のようなコードは掛けません。
import firebase from 'firebase/app' const storage = firebase.storage() const uploadImage = async ( imageData: Blob, uploadPath: string ) => { const refStorage = storage.ref(uploadPath) await refStorage.put(imageData) await refStorage.getDownloadURL() .then((downloadURL) => { console.log('cloud storageに登録した画像URL', downloadURL) }) }functionsで書き込みできるのは
/tmp
のみ画像データをBufferなどで取得した後にファイルに一時保存したいけど書き込み権限エラーが出る。
const fs = require('fs')
のファイルシステムなどを使って書き込もうとしますができません。ファイル システムで書き込み可能な部分は /tmp ディレクトリだけです。このディレクトリは、関数インスタンスの一時ファイルの保存先として使用できます。このディレクトリは、ボリュームに書き込まれたデータがメモリに格納される「tmpfs」ボリュームと呼ばれるローカル ディスクのマウント ポイントです。これにより、関数用にプロビジョニングされたメモリリソースが消費されるので注意してください。
ファイル システムの残りの部分は読み取り専用で、関数からアクセスできます。
引用元
https://cloud.google.com/functions/docs/concepts/exec?hl=ja#file_system解決策
fs
やos
やpath
が分からなければ 「node.js fs」 などで調べて見てください。const fs = require('fs') const os = require('os') const path = require('path') const tempDir: string = os.tmpdir() const writeImageFile = async ( imageBuffer: Buffer, fileName: string ) => { // functionsの/tmpの書き込み先ファイルパスを生成 const localPath: string = path.join(tempDir, fileName) // ローカルの/tmp以下のファイルにBufferの画像データを書き込み await fs.writeFile(localPath, imageBuffer, (err: Error) => { if (err) { throw err } }) }画像のリサイズや整形をしたかった
sharp 公式ドキュメント
https://sharp.pixelplumbing.com/今回はsharpがnode.js上で画像整形するのに都合が良かったので使わせてもらいました!
toBuffer
Buffer or ファイル形式を受け取ってリサイズする。
image
の引数にはBuffer
データ以外にもimage.jpg
のような指定ファイルでも使えます!const sharp = require('sharp') export const resizeImageToBuffer = async ( image: Buffer|string ): Promise<Buffer> => { const resizeBuffer: Buffer = await sharp(image) .resize({ height: 500, width: 500 }) .toBuffer() .catch((err: Error) => { throw err }) return resizeBuffer }toFile
こちらの例では
toFile()
メソッドで結果をローカルファイルに書き込みする事ができる。
functionsでは先程の/tmp
のpathを渡してあげたりなどしてfs
を使わずにファイルを書き込むことも可能返り値の型を
Promise<void>
と書いているが、toFile
の返り値は画像データでは無いので敢えて返さないコードを書いてみた。
本来なら成功結果のBooleanを返すといいかも。const sharp = require('sharp') export const resizeImageToFile = async ( image: Buffer|string, localFilePath: string ): Promise<void> => { await sharp(image) .resize({ height: 500, width: 500 }) .toFile(localFilePath) .catch((err: Error) => { throw err }) }uploadしたけどfirebaseコンソール画面で画像が読み込まれなかった(クルクルしてた)
重要なのは
firebaseStorageDownloadTokens
オプションにuuid v4
を指定すること。
これでアクセストークンができて外部でも読み込み可能になる。注意!!
要注意なのは
{metadata: {metadata: {firebaseStorageDownloadTokens: uuid}}}
のように
metadata
を二重にして指定してあげないと駄目ってところ。import * as admin from 'firebase-admin' import { v4 as uuidv4 } from 'uuid' // 環境変数の取得 // NOTE: 複数バケットがある場合は読み分けて const firebaseConfig: string | any = process.env.FIREBASE_CONFIG const firebaseConfigObj: object | any = JSON.parse(firebaseConfig) const bucket = admin.storage().bucket(firebaseConfigObj.storageBucket export const upload = async ( localPath: string, remotePath: string ) => { const uuid = uuidv4() await bucket .upload(localPath, { destination: remotePath, metadata: { metadata: { // uuidv4をトークンに指定すると画像が外部で表示できる firebaseStorageDownloadTokens: uuid } } }) }getDownloadURL()みたいな無期限の保存先URLを取りたい
No!! getSignedUrl()
file.getSignedUrl()
で最大7日間の期限付きのURLは取れたけど、
コレではDBに保存しておくタイプの用途には向かない…。アクセストークン付きのURLを生成しよう
先程
uuid
のアクセストークンを付けて上げたのが大切
それから${STORAGE_ROOT}/${bucket.name}/o/${dlPath}?alt=media&token=${uuid}
.then()
の引数を使ってDownloadURL
もどきを生成してあげましょう。また、
.finally()
で登録処理の成否に関わらずfs.unlinkSync(localPath)
で
書き込んだ画像ファイルを削除してあげましょう。import * as admin from 'firebase-admin' import { v4 as uuidv4 } from 'uuid' // storageURLの接頭文字列 const STORAGE_ROOT = 'https://firebasestorage.googleapis.com/v0/b' // 環境変数の取得 // NOTE: 複数バケットがある場合は読み分けて const firebaseConfig: string | any = process.env.FIREBASE_CONFIG const firebaseConfigObj: object | any = JSON.parse(firebaseConfig) const bucket = admin.storage().bucket(firebaseConfigObj.storageBucket export const upload = async ( localPath: string, remotePath: string ) => { const uuid = uuidv4() const downloadUrl = await bucket .upload(localPath, { destination: remotePath, metadata: { metadata: { // uuidv4をトークンに指定すると画像が外部で表示できる firebaseStorageDownloadTokens: uuid } } }) .then((data): string => { const file = data[0] const dlPath = encodeURIComponent(file.name) return `${STORAGE_ROOT}/${bucket.name}/o/${dlPath}?alt=media&token=${uuid}` }) .catch((err) => { throw new Error(err) }) .finally(() => { // ローカルの一時ファイルの削除 fs.unlinkSync(localPath) }) }最後に
firebase admin sdkでstorage使う時は下記のリンクから関数の動きを見ておくように!
このリンクはuploadの関数の内部リンクを付けてます。
https://googleapis.dev/nodejs/storage/latest/Bucket.html#upload
- 投稿日:2020-07-27T18:57:21+09:00
npm versionでアルファ版やRC版にインクリメントする
Node.jsのプロジェクトでは
npm version
コマンドでバージョンをインクリメントできます。package.json, package-lock.jsonの書き換えと、Gitタグの追加が自動化されます。よく使われるmajor
,minor
,patch
の他に、pre*
というプレリリース用のオプションが実装されています。以下の動作確認はnpm v6.14.5で行いました。
次期バージョンのアルファ版作成
# 1.2.3 => 2.0.0-alpha.0 npm version premajor --preid alpha
premajor
を指定すると、次期メジャーバージョンのプレリリース版となります。このとき--preid
オプションを指定すると任意の接頭辞(この場合はalpha
)が付加されます。接頭辞を指定しなかった場合は2.0.0-0
となりました。アルファ版の中でのインクリメント
# 2.0.0-alpha.0 => 2.0.0-alpha.1 npm version prerelease
プレリリース部分の番号をインクリメントするには、
prerelease
を指定します。この場合は--preid alpha
を指定してもインクリメントされます。ただ--preid
なしの方がbeta.0 => beta.1
やrc.0 => rc.1
のときでも同様に動作するので、ミスが起こりにくそうです。アルファ版からRC版に格上げする
# 2.0.0-alpha.1 => 2.0.0-rc.0 npm version prerelease --preid rc2.0.0のプレリリースであることには変わらないので
prerelease
を指定し、--preid
オプションでアルファからRCに変更します。自動的にrc.0
がセットされました。Semverではプレリリース版の接頭辞はアルファベット順に解釈されます。(2.0.0-alpha.1
<2.0.0-rc.0
<2.0.0
)正式版のリリース
# 2.0.0-rc.0 => 2.0.0 npm version major
上記のコマンドで動きましたが、バージョン指定は
minor
でもpatch
でも同じ結果でした。そもそもバージョン番号は上がってないのでどれもしっくり来ません。明示的にnpm version 2.0.0
を実行してもいい気がします。
- 投稿日:2020-07-27T18:57:21+09:00
npm versionでアルファ版やRC版を作成する
Node.jsのプロジェクトでは
npm version
コマンドでバージョンをインクリメントできます。package.json, package-lock.jsonの書き換えと、Gitタグの追加が自動化されます。よく使われるmajor
,minor
,patch
の他に、pre*
というプレリリース用のオプションが実装されています。以下の動作確認はnpm v6.14.5で行いました。
次期バージョンのアルファ版作成
# 1.2.3 => 2.0.0-alpha.0 npm version premajor --preid alpha
premajor
を指定すると、次期メジャーバージョンのプレリリース版となります。このとき--preid
オプションを指定すると任意の接頭辞(この場合はalpha
)が付加されます。接頭辞を指定しなかった場合は2.0.0-0
となりました。アルファ版の中でのインクリメント
# 2.0.0-alpha.0 => 2.0.0-alpha.1 npm version prerelease
プレリリース部分の番号を上げるには、
prerelease
を指定します。この場合は--preid alpha
を指定してもインクリメントされます。ただ--preid
なしの方がbeta.0 => beta.1
やrc.0 => rc.1
のときでも同様に動作するので、ミスが起こりにくそうです。アルファ版からRC版に格上げする
# 2.0.0-alpha.1 => 2.0.0-rc.0 npm version prerelease --preid rc2.0.0のプレリリースであることには変わらないので
prerelease
を指定し、--preid
オプションでアルファからRCに変更します。自動的にrc.0
がセットされました。Semverではプレリリース版の接頭辞はアルファベット順に解釈されます。(2.0.0-alpha.1
<2.0.0-rc.0
<2.0.0
)正式版のリリース
# 2.0.0-rc.0 => 2.0.0 npm version major
上記のコマンドで動きましたが、引数は
minor
でもpatch
でも同じ結果でした。そもそもバージョン番号は上がってないのでどれもしっくり来ません。明示的にnpm version 2.0.0
を実行してもいい気がします。
- 投稿日:2020-07-27T16:34:15+09:00
Node.js+Express+MySQLでWebAPIを作成してみる①
はじめに
NodeでReactを用いてフロントの作業をしていて、せっかくならバックエンドもNodeつかってJavaScriptで作ってみようということでWebApiサーバーを作ってみました。
実行環境
- CentOS7
- Node.js 8.17.0
Expressの動作確認
npm install express
でインストール以下のサンプルソースを任意のフォルダに保存
index.jsvar exp = require("express"); var app = exp(); app.get("/",function(req,res){ res.send("Hello,World"); }) app.listen(3000,function(){ console.log("成功") })保存したフォルダに移動し、
node index.js
で実行した後http://localhost:3000/
にアクセス。
サーバーが起動し、GETリクエストのレスポンスが返ってきました。MySQLの設定
まずはMySQLにログイン。その後
create database ApiTest;
use ApiTest;
create table api_tbl(id int(5),name varchar(10));
insert into api_tbl values(2,'test');
上記のコマンドで、データベースとテーブルを作成、レコードを追加。データの取得
作成したテーブルデータをGETリクエストで実際に取得してみます。
index.jsvar exp = require("express"); var app = exp(); var mysql = require("mysql"); var connection = mysql.createConnection({ host: "localhost", user: "root", password: "****", database: "ApiTest" }); app.get("/", function (req, res) { connection.query('select * from api_tbl', function (error, results, fields) { if (error) throw error; res.send(results); }); }); //任意のポート番号 app.listen(****, function () { console.log("成功"); })上記のソースを保存し、
node index.js
を実行。Postmanを使用してレスポンスを確認します。
先ほど登録したデータがちゃんと返ってきたので成功です。
感想
Expressを使うことでとても簡単にWebApiが作成できて感動しました!
今回は初めてということでGETリクエストのみの実装でしたが、今後CRUDすべてのAPIを実装し、フロント側もReactなどで開発することで1つのWebアプリケーションを作ろうと考えております。
- 投稿日:2020-07-27T14:57:05+09:00
Node.js: CouchDB のデータを削除 (Delete)
couch_delete.js#! /usr/bin/node // --------------------------------------------------------------- // couch_delete.js // // Jul/27/2020 // --------------------------------------------------------------- var Client = require('node-rest-client').Client // --------------------------------------------------------------- console.error("*** 開始 ***") const key_in=process.argv[2] console.log (key_in) const url = "http://localhost:5984/nagano/" + key_in var client = new Client() client.get(url, function(data, response) { console.log (data) console.log (data._rev) const url_del = url + '?rev=' + data._rev client.delete(url_del, function(data, response) { console.log (data) console.error("*** 終了 ***") }) }) // ---------------------------------------------------------------実行
export NODE_PATH=/usr/lib/node_modules ./couch_delete.js t2029
- 投稿日:2020-07-27T14:54:59+09:00
Node.js: CouchDB のデータを更新 (Update)
couch_update.js#! /usr/bin/node // --------------------------------------------------------------- // couch_update.js // // Jul/27/2020 // --------------------------------------------------------------- var Client = require('node-rest-client').Client // --------------------------------------------------------------- function get_current_date_proc() { const today = new Date () var ddx = (1900 + today.getYear ()) + "-" + (today.getMonth () +1) ddx += "-" + today.getDate () return ddx } // --------------------------------------------------------------- console.error("*** 開始 ***") const key_in=process.argv[2] const population_in=process.argv[3] console.log(key_in) console.log(population_in) const url = "http://localhost:5984/nagano/" + key_in var client = new Client() client.get(url, function(data, response) { console.log(data) console.log(data._rev) console.log(data.name) const date_mod = get_current_date_proc() console.log(date_mod) var unit_aa = new Object () unit_aa["population"] = population_in unit_aa["name"] = data.name unit_aa["date_mod"] = date_mod const json_str = JSON.stringify (unit_aa) console.log(json_str) const url_put = url + '?rev=' + data._rev var args = { data: json_str, headers: { "Content-Type": "application/json" } } client.put(url_put, args, function(data, response) { console.log(data) console.error("*** 終了 ***") }) }) // ---------------------------------------------------------------実行
export NODE_PATH=/usr/lib/node_modules ./couch_update.js t2023 9345100
- 投稿日:2020-07-27T14:52:35+09:00
Node.js: CouchDB のデータを読む (Read)
couch_read.js#! /usr/bin/node // --------------------------------------------------------------- // couch_read.js // // Jul/27/2020 // --------------------------------------------------------------- var Client = require('node-rest-client').Client // --------------------------------------------------------------- console.error("*** 開始 ***") var client = new Client() const url = "http://localhost:5984/nagano/_all_docs?include_docs=true" client.get(url, function(data, response) { data.rows.forEach (function(row) { var out_str = row.id + "\t" out_str += row.doc.name + "\t" out_str += row.doc.population + "\t" out_str += row.doc.date_mod; console.log (out_str) }) console.error("*** 終了 ***") }) // ---------------------------------------------------------------実行
export NODE_PATH=/usr/lib/node_modules ./couch_read.js
- 投稿日:2020-07-27T14:46:05+09:00
Node.js: CouchDB のデータを作成 (Create)
couch_create.js#! /usr/bin/node // --------------------------------------------------------------- // couch_create.js // // Jul/27/2020 // // --------------------------------------------------------------- var Client = require('node-rest-client').Client // --------------------------------------------------------------- console.error ("*** 開始 ***") // const url_collection = 'http://localhost:5984/nagano' var client = new Client() client.delete(url_collection, function(data, response) { console.log(data) client.put(url_collection, function(data, response) { console.log(data,url_collection) create_proc (client,url_collection) console.error ("*** 終了 ***") }) }) // create_proc (db) // --------------------------------------------------------------- // [4]: function create_proc (client,url_collection) { var dict_aa = data_prepare_proc () for (var key in dict_aa) { const url_put = url_collection + "/" + key const json_str = JSON.stringify(dict_aa[key]) var args = { data: json_str, headers: { "Content-Type": "application/json" } } client.put(url_put, args, function(data, response) { console.log(data) }) } } // --------------------------------------------------------------- function data_prepare_proc () { var dict_aa = new Object () dict_aa = dict_append_proc (dict_aa,'t2021','長野',52136,'1950-6-18') dict_aa = dict_append_proc (dict_aa,'t2022','松本',71928,'1950-3-15') dict_aa = dict_append_proc (dict_aa,'t2023','上田',63241,'1950-10-2') dict_aa = dict_append_proc (dict_aa,'t2024','小諸',38724,'1950-6-22') dict_aa = dict_append_proc (dict_aa,'t2025','岡谷',49357,'1950-8-14') dict_aa = dict_append_proc (dict_aa,'t2026','塩尻',67283,'1950-9-12') dict_aa = dict_append_proc (dict_aa,'t2027','茅野',36251,'1950-3-21') dict_aa = dict_append_proc (dict_aa,'t2028','飯田',54623,'1950-7-26') dict_aa = dict_append_proc (dict_aa,'t2029','中野',21847,'1950-10-2') dict_aa = dict_append_proc (dict_aa,'t2030','諏訪',48392,'1950-12-20') dict_aa = dict_append_proc (dict_aa,'t2031','駒ヶ根',51875,'1950-2-7') dict_aa = dict_append_proc (dict_aa,'t2032','佐久',69234,'1950-5-17') dict_aa = dict_append_proc (dict_aa,'t2033','伊那',31897,'1950-6-19') dict_aa = dict_append_proc (dict_aa,'t2034','千曲',51768,'1950-3-24') return dict_aa } // --------------------------------------------------------------- function dict_append_proc(dict_aa,id_in,name_in,population_in,date_mod_in) { unit_aa = {} unit_aa['name'] = name_in unit_aa['population'] = population_in unit_aa['date_mod'] = date_mod_in dict_aa[id_in] = unit_aa return dict_aa } // ---------------------------------------------------------------実行
export NODE_PATH=/usr/lib/node_modules ./couch_create.js