はじめに
リンクラフト株式会社の竹内です。
リンクラフトアドベントカレンダーの16日目を担当します。
最近、Webアプリを開発するための環境構築を私的に試しましたので、今回はそちらの紹介をさせていただきます(無知レベルで構築していますので、いろいろ適切ではない部分もあるかと思いますが、そこはご容赦を)。
なぜ環境構築を試したのか
私は社会人になって以降、ずっとエンジニアとして活動してきたのですが、Webアプリの開発業務に携わる機会がほぼ無かったんですよね(バッチとかのバックエンド側の開発が主でして)。
長年経験してきてバックエンド一筋、はさすがに武器として貧弱と感じており、フロント側のことも基礎ぐらいは抑えておきたいと思ったのが発端になります。
さらに言うと、業務だとどうしてもアプリケーション(ビジネスロジックとか)の部分だけ意識をして開発を進めている節があり、今回良い機会だったので「どうせなら基盤となる環境の構築からやってみるかー!」というノリでお試しに至ったわけです。
開発環境が整った暁には、個人的に使えるWebアプリの開発も計画しているので、結構気合は入ってマス。
というわけで環境構築
では早速、手順の紹介を!・・・といきたいところですが
はじめに、構築するWebアプリのサーバー構成を紹介しておきます。
Dockerを用いて各サーバーをコンテナ化しています。
各コンテナの概要は以下になります。
-
Vueコンテナ
ユーザーからのリクエストを受けます。今回はフロントエンドにVue.jsを使います。 -
APIコンテナ
DBサーバーを操作するためのAPIを提供します。Vueコンテナは本サーバーが提供するAPIを利用してデータベースを操作します。ORMライブラリであるSequelize
をインストールします。 -
DBコンテナ
家計簿アプリのデータを保管するデータベースサーバーです。RDBMSはMySQL
を使用します。
また、Dockerネットワーク(backend
)を作成することで、コンテナ名でコンテナ間通信が行えるようにします。
Dockerのインストール
ここからが環境構築の手順となります。まずはDockerを入れないと始まらないので、インストールしていきます。手順は以下を参考にしました。
作業ディレクトリ準備
PCの任意ディレクトリに、アプリの資材を配置するための作業ディレクトリを作成します。
私はMacで作業しているのですが、個人ユーザーディレクトリ配下に作業ディレクトリを作成しました。
アプリ名はMoneyTracker
(仮称)とし、作業ディレクトリ名に採用しています。
# ディレクトリ移動
$ cd /Users/[個人フォルダ]
# ディレクトリ作成
$ mkdir -p development/money_tracker/vue
$ mkdir -p development/money_tracker/api
$ mkdir -p development/money_tracker/db/logs
$ mkdir -p development/money_tracker/db/conf
# 設定ファイル、ログファイル生成
$ touch development/money_tracker/compose.yml
$ touch development/money_tracker/.env
$ touch development/money_tracker/db/logs/mysql-error.log
$ touch development/money_tracker/db/logs/mysql-query.log
$ touch development/money_tracker/db/logs/mysql-slow.log
$ touch development/money_tracker/db/conf/my.cnf
設定ファイル編集
compose.yml
作業ディレクトリ準備時に作成した、compose.yml
(Dockerのコンテナ定義ファイル)を編集していきます。
最終的に、以下内容としました。
compose.yml
services:
db:
# 起動するイメージを指定
image: mysql:8.0.32-debian
platform: linux/amd64
# 環境変数を設定
environment:
- MYSQL_ROOT_HOST=${DB_ROOT_HOST}
- MYSQL_DATABASE=${DB_NAME}
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASS}
- MYSQL_ROOT_PASSWORD=${DB_PASS}
- TZ=${TZ}
# ホスト側のポート:コンテナのポート
ports:
- '3306:3306'
expose:
- '3306'
# ボリュームバインド
volumes:
- ${PWD_PATH}/db/conf:/etc/mysql/conf.d:ro
- mysqldata:/var/lib/mysql/
- ${PWD_PATH}/db/logs:/var/log/mysql/
#使用するネットワーク
networks:
- backend
api:
image: node:14.21.2-buster
build:
context: .
dockerfile: ./Dockerfile
environment:
- MYSQL_SERVER=db
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASS}
- MYSQL_DATABASE=${DB_NAME}
- TZ=${TZ}
- CHOKIDAR_USEPOLLING=true
#コンテナを起動させ続けるよう設定
tty: true
ports:
- '3000:3000'
# ソースコードを格納するフォルダをマウント
#(ホスト側の./apiをコンテナの/appにマウント)
volumes:
- ${PWD_PATH}/api:/app
# 起動時のカレントフォルダを指定
working_dir: /app
# 起動後に実行するコマンドを指定
command: sh -c "npm run dev"
networks:
- backend
#依存関係(apiコンテナより先にdbコンテナが起動するように設定)
depends_on:
- db
vue:
image: node:14.21.2-buster
build:
context: .
dockerfile: ./Dockerfile
environment:
- CHOKIDAR_USEPOLLING=true
tty: true
ports:
- '8080:8080'
volumes:
- ${PWD_PATH}/vue:/app
working_dir: /app/money-tracker
command: npm run serve
networks:
- backend
depends_on:
- api
networks:
backend:
driver: bridge
volumes:
mysqldata:
Dockerネットワーク(backend
)については、networks:
キーで指定しています。
compose.yml
の記述形式について詳しく知りたい方は、以下が参考になると思います。
my.cnf
MySQLで使用する文字コードや、ログ出力先の指定などをmy.cnf
に追記していきます。
この設定ファイルは、コンテナ起動時にボリュームマウントによりDBコンテナ上に配置されます。
my.cnf
# MySQLサーバーへの設定
[mysqld]
# 文字コード/照合順序の設定
character-set-server = utf8mb4
collation-server = utf8mb4_bin
# タイムゾーンの設定
default-time-zone = SYSTEM
log_timestamps = SYSTEM
# デフォルト認証プラグインの設定
default-authentication-plugin = mysql_native_password
# エラーログの設定
log-error = /var/log/mysql/mysql-error.log
# スロークエリログの設定
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 5.0
log_queries_not_using_indexes = 0
# 実行ログの設定
general_log = 1
general_log_file = /var/log/mysql/mysql-query.log
# mysqlオプションの設定
[mysql]
# 文字コードの設定
default-character-set = utf8mb4
# mysqlクライアントツールの設定
[client]
# 文字コードの設定
default-character-set = utf8mb4
my.cnf
で設定できるシステム変数については、以下が参考になります。
.env
compose.yml
内で参照する環境変数の設定を記述していきます。
具体的には、compose.yml
でMySQLの環境変数を設定している部分が、このファイルから具体的な値を取得していることになります。
.env
PWD_PATH=/Users/[ユーザ名]/development/money_tracker
DB_ROOT_HOST=%
DB_NAME=money_tracker
DB_USER=[MySQLユーザ名]
DB_PASS=[MySQLパスワード]
TZ=Asia/Tokyo
Dockerコンテナの起動
ここで一度、Dockerコンテナを以下コマンドにて起動します。
# Dockerコンテナ起動
$ cd /Users/[ユーザ名]/development/money_tracker
$ docker-compose up -d
# コンテナの起動状態確認
$ docker-compose ps
起動状態を確認すると、起動しているコンテナがリスト表示されます。
この時点で表示されたのはDBコンテナとAPIコンテナで、Vueコンテナは表示されませんでした。すなわち、DBコンテナとAPIコンテナは起動できており、Vueコンテナは起動できていないことがわかります。
DBコンテナの準備
MySQL接続確認
起動済のDBコンテナにアクセスし、MySQLに接続できるか確認します。
# DBコンテナにアクセス
$ docker-compose exec db /bin/bash
#### ここからDBコンテナ内 ####
# MySQLに接続
$ mysql -u root -p
MySQL接続時にパスワードを聞かれますので、.env
ファイルで設定したパスワード(DB_PASS
)を入力してEnterキーを入力します。
文字コード確認
接続成功したら、MySQLの文字コード設定について確認します。
#### DBコンテナ内 ####
# 文字コード確認
mysql> show variables like 'char%';
+--------------------------+--------------------------------+
| Variable_name | Value |
+--------------------------+--------------------------------+
| character_set_client | utf8mb4 |
| character_set_connection | utf8mb4 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8mb4 |
| character_set_server | utf8mb4 |
| character_set_system | utf8mb3 |
| character_sets_dir | /usr/share/mysql-8.0/charsets/ |
+--------------------------+--------------------------------+
8 rows in set (0.04 sec)
my.cnf
で指定した値になっていれば、正常に反映されていることになります。
データベース確認
続いて、データベースの状態を確認します。
#### DBコンテナ内 ####
# データベース確認
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| money_tracker |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.05 sec)
money_tracker
というデータベースが存在していることが確認できます。
money_tracker
の中身についても確認してみます。
#### DBコンテナ内 ####
# データベース切り替え
mysql> use money_tracker;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
# テーブル確認
mysql> show tables;
Empty set (0.00 sec)
money_tracker
データベースの中身は空です(テーブルは1つも存在しません)。
テーブルの作成は手作業でも可能ですが、今回はAPIコンテナからSequelize
のDBマイグレーション機能を使用して行いますので、ここで確認作業を終了します。
コンテナ切断
quit
でMySQLを抜け、exit
でDBコンテナも抜けます。
#### DBコンテナ内 ####
# MySQLログアウト
mysql> quit
Bye
# コンテナ切断
$ exit
APIコンテナの準備
パッケージインストール
続いて、APIコンテナで必要なパッケージをインストールしていきます。
Express
まずはexpress-generator
パッケージをインストールし、実行します。
Expressは、Node.jsで利用できるWebアプリケーションフレームワークです。
# APIコンテナにアクセス
$ docker-compose exec api /bin/bash
#### ここからAPIコンテナ ####
# express-generatorパッケージのインストール
$ npm install -g express-generator
$ express .
nodemon
次に、nodemon
パッケージをインストールします。ソースコードを変更したときに、自動でサーバーを再起動してくれる便利なツールです。
#### APIコンテナ内 ####
# nodemonパッケージのインストール
$ npm install -D nodemon
Sequelize
さらに、Sequelizeに関連するパッケージをインストールします。その後、sequelize-cli
を使用してSequelizeの初期化を行います。
#### APIコンテナ内 ####
# Sequelize関連のパッケージインストール
$ npm install mysql2 sequelize sequelize-cli
# Sequelizeの初期化
$ npx sequelize-cli init
設定ファイル編集
package.json
上記でインストールしたnodemon
については、開発環境でのみ使用するため、その設定を/app/package.jsonに反映します。"scripts"
の部分に"dev"
を追加します。
#### APIコンテナ内 ####
# package.jsonの編集
$ vi /app/package.json
package.json
{
"name": "app",
"version": "0.0.0",
"private": true,
"scripts": {
+ "dev": "nodemon ./bin/www",
"start": "node ./bin/www"
},
"dependencies": {
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"debug": "^2.6.9",
"express": "^4.16.4",
"http-errors": "^1.6.3",
"jade": "^1.11.0",
"morgan": "^1.9.1",
"mysql2": "^3.11.4",
"sequelize": "^6.37.5"
},
"devDependencies": {
"nodemon": "^3.1.7"
}
}
config.json
上記でSequelizeの初期化を行った時に、/app/config
にconfig.json
というJSONファイルが作成されていますが、それをconfig.js
に変更してJavascriptファイルにします。
#### APIコンテナ内 ####
# ファイル名変更
$ mv /app/config/config.json /app/config/config.js
そして、内容を以下のように書き換えます。
config.js
module.exports = {
development: {
username: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
host: process.env.MYSQL_SERVER,
dialect: 'mysql',
},
test: {
username: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
host: process.env.MYSQL_SERVER,
dialect: 'mysql',
},
production: {
username: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
host: process.env.MYSQL_SERVER,
dialect: 'mysql',
},
};
この書き換えにより、compose.yml
で設定した環境変数の値をそのままDB接続情報として使うことが可能となります。
index.js
上記でconfig.json
→config.js
にファイル名変更したので、app/models/index.js
を開き、const config =...
の部分を修正します(config.json
をconfig.js
に変更)。
#### APIコンテナ内 ####
# index.jsの編集
$ vi /app/models/index.js
index.js
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const process = require('process');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
- const config = require(__dirname + '/../config/config.json')[env];
+ const config = require(__dirname + '/../config/config.js')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(file => {
return (
file.indexOf('.') !== 0 &&
file !== basename &&
file.slice(-3) === '.js' &&
file.indexOf('.test.js') === -1
);
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
このファイルはSequelizeによるDBマイグレーション実行に参照されます(今回は、以降使用しません)。
コンテナ切断
exit
でAPIコンテナを抜けます。
#### APIコンテナ内 ####
# コンテナ切断
$ exit
Vueコンテナの準備
コンテナ起動
本手順実行時点で、Vueコンテナは停止しています。
ここでは、docker-compose run
を使ってvueコンテナを一時的に起動します。
# Vueコンテナの起動
$ docker-compose run --rm --no-deps vue /bin/bash
--rm
は、コンテナ切断時に自動でコンテナを削除するオプションです。
--no-deps
は、リンクしたサービスを起動しないようにするオプションです。compose.yml
でコンテナの依存関係を定義したため、本来であればVueコンテナが起動する前にAPIコンテナ(さらにその前のDBコンテナも)が立ち上がりますが、このオプションを設定することでVureコンテナのみ起動するようにしています。
パッケージインストール
Vue CLI
まずはvue-cli
パッケージをインストールします。
#### Vueコンテナ内 ####
# vue-cliパッケージのインストール
$ npm install -g @vue/cli
インストール後、プロジェクトの作成を行います。
#### Vueコンテナ内 ####
# プロジェクトの作成
$ vue create money_tracker
プロジェクト作成にあたり、いくつか設定を問われるため、以下の通り選択し設定を進めます。
? Please pick a preset:
→ Manually select features
を選択
? Check the features needed for your project:
→ Babel, Linter / Formatter, Router, Vuex
を選択
? Choose a version of Vue.js that you want to start the project with
→ 2.x
を選択
? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n)
→ n
を入力
? Pick a linter / formatter config:
→ ESLint with error prevention only
を選択
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)
→ Lint on save
を選択
? Where do you prefer placing config for Babel, ESLint, etc.?
→ In dedicated config files
を選択
? Save this as a preset for future projects? (y/N)
→ N
を入力
? Pick the package manager to use when installing dependencies:
→ Use NPM
を選択
完了すると以下メッセージが表示されます。
🎉 Successfully created project money_tracker.
👉 Get started with the following commands:
$ cd money_tracker
$ npm serve
Axios
Axiosは、Node.jsとブラウザのためのPromiseベースのHTTPクライアントです。REST-APIを実行したいときなどに、これを使うと実装が容易になるようです。
#### Vueコンテナ内 ####
# axiosパッケージのインストール
$ npm install axios
Vuetify
Vuetifyは整えられたマテリアルコンポーネント(誰にとっても分かりやすく使いやすいもの)を提供するVue UIライブラリです。後々使えそうなので、追加しておきます。
#### Vueコンテナ内 ####
# vuetifyの追加
$ vue add vuetify
ここでも設定について問われるため、以下の通り選択し設定を進めます。
? Choose a preset:
→ Default (recommended)
を選択
以下のように表示されれば追加完了です。
✔ Successfully invoked generator for plugin: vue-cli-plugin-vuetify
vuetify Discord community: https://community.vuetifyjs.com
vuetify Github: https://github.com/vuetifyjs/vuetify
vuetify Support Vuetify: https://github.com/sponsors/johnleider
コンテナ切断
ここまで完了したら、exit
でVueコンテナを抜けます。
#### Vueコンテナ内 ####
# コンテナ切断
$ exit
全てのコンテナを再起動
各コンテナのセットアップが一通り完了したので、一度コンテナを全て停止した後、再起動させます。
# 全てのコンテナを停止
$ docker-compose down
# コンテナ再起動
$ docker-compose up -d
# 起動状態確認
$ docker-compose ps
docker-compose ps
実行時にVue, API, DBの全コンテナが表示されれば起動完了となります。
ブラウザアクセス確認
ブラウザから http://localhost:8080/ にアクセスします。
このようにVue.jsの初期画面が表示されれば完了です!(お疲れ様でした!)
※私はここに至るまでに1週間かかりました←
開発計画しているアプリとは?
環境構築の手順としては以上となるのですが、冒頭にも書きました開発を計画しているアプリについても、おまけとして紹介させていただきます。
開発を計画しているアプリはズバリ、家計簿アプリになります。
家計簿アプリを題材に選定した背景は以下の通りです。
我が家の家計管理はずっとエクセルを用いてきたのですが、以下のような問題点も抱えていました。
- オフライン(自宅PC)で管理・更新しているため、外出先とかでリアルタイムに更新できない
- フォーマットを作成したのが私であり、妻から見るとフォーマットが分かりにくく入力のハードルが高い(らしい)
- エクセルファイルの肥大化を回避すべく、年次でフォーマットを刷新(ファイルを新たに作成)しているが、地味に面倒
- 収入や支出の項目が増減する度に、フォーマットを更新しなければならない
少し前から管理の方法は変えたいと思っており、Webアプリ化することで抱えている問題は概ね解決できそうだったので、今回の題材選定に至っています。
せっかく作るなら実用的なものが良く、使えるものを題材にした方が開発の熱量も上がると思いますし(希望的観測)。
現在、絶賛設計中なので、引き続き開発に取り組んでいけたらと思います(果たしていつ完成するやら)。
さいごに
いかがでしたでしょうか?
今回はWebアプリの環境構築について紹介させていただきました。
アプリの基盤部分に触れる機会が無かった私としては、Webアプリの仕組みを理解するには非常に役立ったので、ひとまず満足しています。
実際に操作して動作しているものを目の当たりにした方が理解は深まると思いますし、コンソールでコマンドをバチバチ叩いていると「やってる感」が出ます(笑)
実際に動かしながらWebアプリ開発を学んでみたいという方の参考になれば幸いです…!
一緒に働く仲間を募集中です!
リンクラフト株式会社では、組織拡大に伴い積極的な採用活動を行っています。
少しでも興味がある方はぜひご連絡ください。
▽会社ホームページ
https://lincraft.co.jp/
▽Instagram
https://www.instagram.com/lincraft.inc/
▽ご応募はこちらより
https://lincraft.co.jp/recruit
※カジュアル面談も受付中です。ご希望の方はHPのお問い合わせフォームよりご連絡ください。