初めに
Dockerコンテナ上で動くPythonプログラムを作成してPostgreSQLの接続をしようと思ったら,PostgreSQLのコンテナに接続ができなくてハマったのでその備忘録.
PostgreSQLのコンテナの作成
まず初めにPostgreSQLと開発するアプリケーションのコンテナを作成する.最終的に,APPコンテナからDBコンテナにPythonプログラムを用いて接続を行い,DBコンテナのデータをAPPコンテナから扱うことができるようにする..
version: "2"
services:
postgres:
image: postgres:10
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgre
POSTGRES_PASSWORD: postgre
POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --locale=ja_JP.UTF-8"
volumes:
- ./db/psgl:/var/lib/postgresql/data
app:
build: .
depends_on:
- postgres
environment:
TZ: "Asia/Tokyo"
こちらのファイルでPostgreSQLのコンテナを起動して,データベースに接続してみる.
$ docker-compose up -d postgres
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d6aa15a203fc postgres:10 "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:5432->5432/tcp dbdocker_postgres_1
# 起動が確認できたので,コンテナにログイン.
$ docker exec -it dbdocker_postgres_1 /bin/bash
oot@d6aa15a203fc:/# psql -U postgre #←docker-composeの環境変数で指定したユーザー名
psql (10.4 (Debian 10.4-2.pgdg90+1))
Type "help" for help.
postgre=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+----------+----------+------------+------------+-----------------------
postgre | postgres | UTF8 | en_US.utf8 | en_US.utf8 |
postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 |
template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +
| | | | | postgres=CTc/postgres
(4 rows)
postgre=#
コンテナ内のPostgreSQLに,docker-compose.yml
に記述した環境変数のユーザ名のアカウントとデータベースが作成され,作成されたユーザでログインできることが確認できた.
ローカルからPostgreSQLのコンテナへの接続
次にPythonスクリプトを用いて,ローカルの環境から先ほど作成したPostgreSQLに接続を試みる.
アプリケーション側から接続の意思確認だけしたいので,SQLの情報をプリントするだけの簡単なソースコードで接続確認を行う.
import os
import psycopg2
DATABASE_URL='postgresql://postgre:postgre@localhost:5432/postgres'
def main():
cursor = psycopg2.connect(DATABASE_URL)
print(cursor)
if __name__ == '__main__':
main()
こちらを実行すると接続に成功した場合,以下のようにデータベースのステータスが表示される.
$ python main.py
<connection object at 0x10bd09e60; dsn: 'user=postgre password=xxx dbname=postgres host=localhost port=5432', closed: 0>
$
コンテナからPostgreSQLのコンテナへの接続
上のmain.py
をコンテナ上から起動をして,APPコンテナからDBコンテナへの意思疎通を図ってみる.以下のDockerfile
を用いてPostgreSQLに接続を行うAPPコンテナを作成する.
FROM python:latest
# pythonライブラリのインストール
RUN apt-get update
RUN pip install --upgrade pip
RUN pip install psycopg2
# プロジェクト追加
ADD main.py .
CMD [ "python", "./main.py" ]
先のdocker-compose.yml
と合わせて以下のコマンドで実行.
$ docker-compose up -d postgres app
Creating dbdocker_postgres_1 ... done
Creating dbdocker_app_1 ... done
# 起動できたかログを確認
$ docker-compose logs
Attaching to dbdocker_app_1, dbdocker_postgres_1
postgres_1 | 2018-08-24 09:14:30.228 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
postgres_1 | 2018-08-24 09:14:30.228 UTC [1] LOG: listening on IPv6 address "::", port 5432
postgres_1 | 2018-08-24 09:14:30.232 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
postgres_1 | 2018-08-24 09:14:30.332 UTC [25] LOG: database system was shut down at 2018-08-24 09:14:21 UTC
postgres_1 | 2018-08-24 09:14:30.364 UTC [1] LOG: database system is ready to accept connections
app_1 | /usr/local/lib/python3.6/site-packages/psycopg2/__init__.py:144: UserWarning: The psycopg2 wheel package will be renamed from release 2.8; in order to keep installing from binary please use "pip
install psycopg2-binary" instead. For details see: <http://initd.org/psycopg/docs/install.html#binary-install-from-pypi>.
app_1 | """)
app_1 | Traceback (most recent call last):
app_1 | File "./main.py", line 11, in <module>
app_1 | main()
app_1 | File "./main.py", line 7, in main
app_1 | cursor = psycopg2.connect(DATABASE_URL)
app_1 | File "/usr/local/lib/python3.6/site-packages/psycopg2/__init__.py", line 130, in connect
app_1 | conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
app_1 | psycopg2.OperationalError: could not connect to server: Connection refused
app_1 | Is the server running on host "localhost" (127.0.0.1) and accepting
app_1 | TCP/IP connections on port 5432?
app_1 | could not connect to server: Cannot assign requested address
app_1 | Is the server running on host "localhost" (::1) and accepting
app_1 | TCP/IP connections on port 5432?
app_1 |
コネクションエラーでた.
コンテナ間のネットワークの指定方法によるエラー
ローカルホストのポート5432が空いてないということでエラーを返された.main.py
ではDATABASE_URL
にapp_1
のコンテナのlocalhostを指定しているため起きたエラー.
ユーザから見たらDBコンテナはlocalhostだが,APPコンテナから見るとDBコンテナはlocalではない.コンテナから別のコンテナを指定するときは,コンテナの名前が接続可能なアドレスになるらしい.なのでmain.py
のDATABASE_URL
を以下のように変更する.
import os
import psycopg2
# DATABASE_URL='postgresql://postgre:postgre@localhost:5432/postgres'
DATABASE_URL='postgresql://postgre:postgre@dbdocker_postgres_1:5432/postgres'
def main():
cursor = psycopg2.connect(DATABASE_URL)
print(cursor)
if __name__ == '__main__':
main()
参考にさせていただいたもの
Docker の基本学習 ~ コンテナ間のリンク
Docker Composerではコンテナ間通信はコンテナ名を指定する
修正をしたので,もう一度起動して見る.
$ docker-compose up -d postgres app
Creating dbdocker_postgres_1 ... done
Creating dbdocker_app_1 ... done
$ docker-compose logs
Attaching to dbdocker_app_1, dbdocker_postgres_1
postgres_1 | 2018-08-24 09:14:30.228 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
postgres_1 | 2018-08-24 09:14:30.228 UTC [1] LOG: listening on IPv6 address "::", port 5432
postgres_1 | 2018-08-24 09:14:30.232 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
postgres_1 | 2018-08-24 09:14:30.332 UTC [25] LOG: database system was shut down at 2018-08-24 09:14:21 UTC
postgres_1 | 2018-08-24 09:14:30.364 UTC [1] LOG: database system is ready to accept connections
app_1 | /usr/local/lib/python3.6/site-packages/psycopg2/__init__.py:144: UserWarning: The psycopg2 wheel package will be renamed from release 2.8; in order to keep installing from binary please use "pip
install psycopg2-binary" instead. For details see: <http://initd.org/psycopg/docs/install.html#binary-install-from-pypi>.
app_1 | """)
app_1 | Traceback (most recent call last):
app_1 | File "./main.py", line 11, in <module>
app_1 | main()
app_1 | File "./main.py", line 7, in main
app_1 | cursor = psycopg2.connect(DATABASE_URL)
app_1 | File "/usr/local/lib/python3.6/site-packages/psycopg2/__init__.py", line 130, in connect
app_1 | conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
app_1 | psycopg2.OperationalError: could not connect to server: Connection refused
app_1 | Is the server running on host "dbdocker_postgres_1" (172.19.0.2) and accepting
app_1 | TCP/IP connections on port 5432?
app_1 |
$
コネクションエラー出た(2回目).
コンテナの起動待ちによる接続エラー
どうやらこれはDBコンテナの起動が完了する前に,main.py
でデータベースに接続しようとしたために起きるエラーらしい.解決するには,DBコンテナが起動した後にAPPコンテナを実行するように,処理順を定義する必要がある.
そこで今回はこちら
を参考に,dadarek/wait-for-dependencies
を利用することにした.dadarek/wait-for-dependencies
イメージは,depends_on
で指定したコンテナの起動が確認できるまで他のコンテナの実行を待たせることができるので,PostgreSQLの起動が完了した後に,APPのコンテナを実行するように修正した.
version: "2"
services:
waitfordb:
image: dadarek/wait-for-dependencies
depends_on:
- postgres
command: postgres:5432
postgres:
image: postgres:10
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgre
POSTGRES_PASSWORD: postgre
POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --locale=ja_JP.UTF-8"
volumes:
- ./db/psgl:/var/lib/postgresql/data
app:
build: .
depends_on:
- postgres
- waitfordb
environment:
TZ: "Asia/Tokyo"
$ docker-compose run --rm waitfordb
Creating dbdocker_postgres_1 ... done
Waiting for postgres to listen on 5432...
sleeping
sleeping
$ docker-compose up -d postgres app
dbdocker_postgres_1 is up-to-date
Starting dbdocker_waitfordb_1 ... done
Creating dbdocker_app_1 ... done
$ docker-compose logs
Attaching to dbdocker_app_1, dbdocker_postgres_1, dbdocker_waitfordb_1
postgres_1 | 2018-08-24 09:29:50.620 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
postgres_1 | 2018-08-24 09:29:50.620 UTC [1] LOG: listening on IPv6 address "::", port 5432
postgres_1 | 2018-08-24 09:29:50.623 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
postgres_1 | 2018-08-24 09:29:50.714 UTC [24] LOG: database system was shut down at 2018-08-24 09:28:38 UTC
postgres_1 | 2018-08-24 09:29:50.748 UTC [1] LOG: database system is ready to accept connections
postgres_1 | 2018-08-24 09:29:51.849 UTC [31] LOG: incomplete startup packet
postgres_1 | 2018-08-24 09:29:53.968 UTC [32] LOG: incomplete startup packet
app_1 | /usr/local/lib/python3.6/site-packages/psycopg2/__init__.py:144: UserWarning: The psycopg2 wheel package will be renamed from release 2.8; in order to keep installing from binary please use "pi
p install psycopg2-binary" instead. For details see: <http://initd.org/psycopg/docs/install.html#binary-install-from-pypi>.
app_1 | """)
app_1 | <connection object at 0x7f2953df5e88; dsn: 'user=postgre password=xxx dbname=postgres host=dbdocker_postgres_1 port=5432', closed: 0>
waitfordb_1 | Waiting for postgres to listen on 5432...
waitfordb_1 | sleeping
waitfordb_1 | sleeping
waitfordb_1 | Waiting for postgres to listen on 5432...
やっと繋がった!
終わりに
いろいろ探したところ起動順の問題を解決するには,自前でスクリプトを書くかdadarek/wait-for-dependencies
イメージを利用する以外の解決策はなかった.
そもそもコンテナ起動と同時にデータベスに接続しようというのが,構築としてよくないのかもしれない.
ソースコード
最終的なファイル構成はこんな感じ
GitHub - sey323/docker-postgresql-python: Docker上でPostgreSQLをPythonから扱うときの最小構成