Docker Composeによる開発環境の構築(TypeScript,Node.js,Redis,Elasticsearch)
TypeScriptで作ってみるシリーズの作業ログです。
今回はDockerおよびでDocker ComposeでTypeScriptの開発環境を作ります。
TypeScriptによるNode.jsアプリの他にRedis、Elasticsearchを使います。
前回はこちらです。
Node.jsアプリの作成
Expressを使ってWebAPI化します。
index.ts
import bodyParser from 'body-parser'; import compression from 'compression'; import cors from 'cors'; import express from 'express'; import helmet from 'helmet'; import { main } from './main'; const app = express(); app.use(compression()); app.use(helmet()); app.use(cors()); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.get('/', async (_, res) => { const data = await main(); return res.json(data); }); const port = process.env.PORT || '3000'; app.listen(port, () => console.info(`API running on localhost:${port}`));
特に難しいことはしていないので、説明は省略します。
Redisのセットアップ
公式のDockerイメージを使います。
docker pull redis:5.0.3-alpine3.8 docker run --rm -d -p 6379:6379/tcp redis:5.0.3-alpine3.8
npmモジュールをインストールします。
npm install --save redis npm install --save-dev @types/redis
Node.jsでRedisが使えるようになります。
redis.ts
import redis from 'redis'; const client = redis.createClient(); client.on('error', (err: any) => { console.log('Error ' + err); }); client.set('string key', 'string val', redis.print); client.hset('hash key', 'hashtest 1', 'some value', redis.print); client.hset('hash key', 'hashtest 2', 'some other value', redis.print); client.hkeys('hash key', (_, replies: any) => { console.log(replies.length + ' replies:'); replies.forEach((reply: any, i: any) => { console.log(' ' + i + ': ' + reply); }); client.quit(); });
Elasticsearchのセットアップ
ElasticsearchとKibanaを起動するため、Docker Composeを使います。
docker-compose.yml
version: '2' services: elasticsearch: image: elasticsearch:6.5.3 volumes: - elasticsearch-data:/usr/share/elasticsearch/data ports: - 9200:9200 expose: - 9300 kibana: image: kibana:6.5.3 ports: - 5601:5601 volumes: elasticsearch-data: driver: local
docker-compose up
npmモジュールをインストールします。
npm install --save elasticsearch npm install --save-dev @types/elasticsearch
Node.jsでElasticsearchが使えるようになります。
elasticsearch.ts
import elasticsearch from 'elasticsearch'; const client = new elasticsearch.Client({ host: 'localhost:9200', log: 'trace', }); client.ping( { requestTimeout: 30000, }, function(error: any) { if (error) { console.error('elasticsearch cluster is down!'); } else { console.log('All is well'); } }, ); client.index({ body: { text: 'Hello World!', }, index: 'myindex', type: 'mytype', }); client.search({ body: { query: { match_all: {}, }, }, }) .then( function(resp: any) { const hits = resp.hits.hits; }, function(err: any) { console.trace(err.message); }, );
Node.jsアプリのDockerfile作成
TypeScriptによるNode.jsアプリのDockerfileを作成します。
Dockerfile
# Build FROM node:10.14.2-alpine AS build-stage WORKDIR /usr/src/app COPY package*.json ./ RUN npm install --silent COPY . . RUN npm run build # Release FROM node:10.14.2-alpine AS release-stage WORKDIR /usr/src/app COPY package*.json ./ RUN npm install --production --silent COPY --from=build-stage /usr/src/app/dist /usr/src/app/dist EXPOSE 3000 ENV NODE_ENV production CMD [ "node", "dist/index.js" ]
Multi-Stage Buildを使って、TypeScriptからJavaScriptへのトランスパイル処理とNode.jsの実行を分けています。
これによって、Node.jsの実行には不要なTypeScript関係のモジュールを排除してDockerイメージのサイズを削減しています。
Docker Composeのセットアップ
Elasticsearch,Redis,Kibanaが起動するようにDocker Composeを構成します。
docker-compose.yml
version: '2' services: elasticsearch: image: elasticsearch:6.5.3 volumes: - elasticsearch-data:/usr/share/elasticsearch/data ports: - 9200:9200 expose: - 9300 kibana: image: kibana:6.5.3 ports: - 5601:5601 redis: image: redis:5.0.3-alpine3.8 ports: - 6379:6379 ts-example-api: build: . ports: - 3000:3000 environment: - ES_HOST=elasticsearch - ES_PORT=9200 - REDIS_HOST=redis - REDIS_PORT=6379 depends_on: - elasticsearch - kibana - redis volumes: elasticsearch-data: driver: local
Node.jsアプリのソースはこちらです。
main.ts
import axios from 'axios'; import elasticsearch from 'elasticsearch'; import redis from 'redis'; import { promisify } from 'util'; import { Qiita } from './qiita'; const esclient = new elasticsearch.Client({ host: `${process.env.ES_HOST}:${process.env.ES_PORT}`, }); export async function main() { const client = redis.createClient({ host: process.env.REDIS_HOST, port: parseInt(process.env.REDIS_PORT!, 10), }); const getAsync = promisify(client.get).bind(client); const items = await getAsync('items'); let returned = null; if (items) { returned = JSON.parse(items); } else { const res = await get(); client.set('items', JSON.stringify(res.data), 'EX', 1800); const body: Array<{}> = []; res.data.forEach(d => { body.push({ index: { _id: d.id, }, }); body.push(d); }); esclient.bulk({ body, index: 'qiita', type: 'items', }); returned = res.data; } client.quit(); return returned; } export function get() { return axios.get<Qiita[]>('https://qiita.com/api/v2/items'); }
KibanaでもElasticsearchの内容が見れます。
まとめ
ここまでの成果物
プログラム実行に複数のミドルウェアが関係する場合、Docker Composeでコンテナ環境を作ってしまうのが開発が楽になります。