諸事情でDocker上のnode.jsとPosgreSQLを連携させる必要があったので調査。
方針
最終的にクラウド上のマネージドサービス(Cloud Run想定)でコンテナを動かす予定なのでdocker-composeへの依存は可能な限り下げます。
- 書ける記述はなるべくDockerfileに記述する
- 全体制御をdocker-composeに記述する
環境はDocker Desktop for Macを利用。
準備
作業場の確保
cd
mkdir docker-node-pg-test
cd docker-node-pg-test
mkdir app db
PostgreSQL(db)関連の作業
appはdbに依存するので、先にdbの準備をします。
dbに移動
cd db
ディレクトリ作成
mkdir data init
必要なファイル作成
touch Dockerfile .dockerignore init/init.sql
init/init.sqlの記述
create table if not exists members(
id serial primary key,
name text,
age int
);
insert into members(name,age) values('hoge',10);
Dockerfileの記述
FROM postgres:14.5-bullseye
COPY ./init/init.sql /docker-entrypoint-initdb.d/init.sql
ENV POSTGRES_USER postgres
ENV POSTGRES_PASSWORD postgres
EXPOSE 5432
イメージのビルド
docker build -t my_postgres .
動作確認
起動
docker run --name my_db -p 5432:5432 -d my_postgres
ログイン
psql -h localhost -U postgres -d postgres
データ確認
postgres=# select * from members;
id | name | age
----+------+-----
1 | hoge | 10
(1 row)
どうやら動いているようです。
この状態ではデータは永続化されていないので、コンテナを削除するとデータも消えます(停止では消えません)。
node.js(app)の開発作業
では次にPostgreSQLにアクセスするnode appを作成したいと思います。
appに移動
cd ../app
必要なファイルの生成
touch index.js
モジュールのインストール
npm install express pg
実装
const express = require("express");
const { send } = require("express/lib/response");
const app = express();
const pg = require("pg");
// PostgreSQL connect info
const pool = new pg.Pool({
user: "postgres",
host: "localhost",
database: "postgres",
password: "postgres",
port: 5432
});
// /
app.get("/", (req, res) => {
res.send("Hello Node.js");
});
// /members
app.get("/members", (req, res) => {
pool.connect((err, client) => {
if (err) {
res.send(err.stack);
} else {
const query = {
text: "select * from members",
values: [],
};
client.query(query, (error, response) => {
if (error) {
res.send(error.stack);
} else {
res.json(response.rows);
}
})
}
});
})
app.listen(3000, () => {
console.log("Start server on port 3000.");
});
動作確認
node index.js
http://localhost:3000/
http://localhost:3000/members
[{"id":1,"name":"hoge","age":10}]
PostgreSQLにアクセスするには先述のmy_postgresが動作している必要があります。
node.js(app)のDocker Image化
必要なファイルの生成
touch Dockerfile .dockerignore
Dockerfile
FROM node:18-bullseye-slim
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node","index.js"]
.dockerignore
node_modules
npm-debug.log
イメージのビルド
docker build -t my_node .
実行
docker run --name my_app -p 3000:3000 -d my_node
動作確認(エラーと対応)
/は問題ないが、/membersはPostgreSQLの接続エラーが発生する。
Error: connect ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1300:16)
エラーの原因と対応
原因
localhostはコンテナ自信を指すので(ここではmy_app)当然、PostgreSQLは動いていない。
localhostではなくコンテナ名を指定する必要がある。また、コンテナ間で通信を行うには同じネットワークに所属してる必要があるみたい。
対応
以下の2つの対応が必要。
- localhostではなく、コンテナ名(my_db)でhostを指定
- 接続するコンテナを同じネットワークに所属させる
docker-composeを利用している場合はサービス名orコンテナ名でいけるみたい。
localhostではなくコンテナ名でホストを指定
// PostgreSQL connect info
const pool = new pg.Pool({
user: "postgres",
host: "my_db", //ココを変更
database: "postgres",
password: "postgres",
port: 5432
});
同じネットワークでコンテナを起動
docker network create test-network
docker network lsで一覧が確認できる。
docker run --name my_db --network test-network -p 5432:5432 -d my_postgres
docker run --name my_app --network test-network -p 3000:3000 -d my_node
動作確認(再)
[{"id":1,"name":"hoge","age":10}]
docker-compose化する
cd
cd docker-node-pg-test
touch docker-compose.yml
version: '3'
services:
app:
build: ./app
container_name: my_app
ports:
- 3000:3000
networks:
- backend
depends_on:
- db
db:
build: ./db
container_name: my_db
ports:
- 5432:5432
networks:
- backend
networks:
backend:
docker-compose up -d
docker-compose down
Kubernetesで動かす
長くなりそうなのでこちらに分けました。