Dockerで立ち上げたPostgresにSAMでbuildしたpsycopg2からクエリを実行する方法

目次

やりたかったこと

  1.  AWS S3内に保存されているxmlファイルを読み込み、必要なデータ取得
  2.  pythonプログラムからdockerコンテナ内のPostgresに接続 ←躓いたパート1
  3.  psycopg2を用いて、クエリ実行してデータ取得 ←躓いたパート2
  4.  データ加工後、クエリ実行してPostgresに保存

躓いたパート1の問題点

最初Pythonのファイルには下記のように記述していた。

				
					# DB接続情報
    postgre_user_name = "admin"
    postgre_user_password = "password"
    postgre_server_port = 5432
    postgre_database_name = "hugahoge"

    connection = psycopg2.connect(f"host=localhost port={postgre_server_port} dbname={postgre_database_name} user={postgre_user_name} password={postgre_user_password}")
				
			

実行時は、sam local invokeの後にオプションでPostgresが立ち上がっているコンテナのIDを指定していた。
結果、そんなネットワークは見つからないよ、とエラー。

				
					% sam local invoke --docker-network 8f1abbbc8138
.
.
.
docker.errors.NotFound: 404 Client Error: Not Found ("network 8f1abbbc8138 not found")
				
			

それもそのはず、オプションコマンド名にもあるように、指定するのはコンテナIDではなく、コンテナが接続されているネットワークIDもしくは名前を指定する必要がある。凡ミス。

躓いたパート2の解決方法

Dockerネットワークを生成し、確認。

				
					% docker network create sam-calc-price-network
c87bcb8cee0e92f57a10de53b2a4930e8e3cf890b5f7448299e45bad3ffed3c4

% docker network inspect sam-calc-price-network
[
    {
        "Name": "sam-calc-price-network",
        "Id": "c87bcb8cee0e92f57a10de53b2a4930e8e3cf890b5f7448299e45bad3ffed3c4",
        "Created": "2020-12-23T05:06:07.9437227Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]
				
			

Postgresのコンテナをネットワークに接続し、確認。

				
					% docker network connect sam-calc-price-network dev-postgres

% docker network inspect sam-calc-price-network            
[
    {
        "Name": "sam-calc-price-network",
        "Id": "c87bcb8cee0e92f57a10de53b2a4930e8e3cf890b5f7448299e45bad3ffed3c4",
        "Created": "2020-12-23T05:06:07.9437227Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "8f1abbbc813844cb52395b65c711d56cc4701058f2d436bf25ffd553280aa133": {
                "Name": "dev-postgres",
                "EndpointID": "dd5ab005654b3a58b1604014ba1284e1690e5cd009b8d7bf62db655caf3e0657",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
				
			

気を取り直して、DockerネットワークIDを指定して実行!

				
					% sam local invoke --docker-network c87bcb8cee0e92f57a10de53b2a4930e8e3cf890b5f7448299e45bad3ffed3c4
				
			
				
					[ERROR] OperationalError: could not connect to server: Connection refused
    Is the server running on host "localhost" (127.0.0.1) and accepting
    TCP/IP connections on port 5432?
could not connect to server: Cannot assign requested address
    Is the server running on host "localhost" (::1) and accepting
    TCP/IP connections on port 5432?
				
			

…えええ。

色々調べた結果、ユーザー側からみるローカルホストとコンテナからコンテナを見たときのローカルホストは違うらしい。コンテナから見たローカルホストはコンテナ名らしいので、下記のようにPythonファイルを書き換えた。

				
					# DB接続情報
    postgre_user_name = "admin"
    postgre_user_password = "password"
    postgre_server_port = 5432
    postgre_database_name = "fugahoge"

    connection = psycopg2.connect(dbname=f"{postgre_database_name}", 
                                  user=f"{postgre_user_name}", 
                                  password=f"{postgre_user_password}",
                                  host="dev-postgres")
				
			

躓いたパート2の問題点

再度、実行!

				
					{"errorMessage": "relation \"place\" does not exist\nLINE 1: SELECT contract_id FROM place WHERE num...\n                                ^\n", "errorType": "UndefinedTable", "stackTrace": ["  File \"/var/task/app.py\", line 145, in lambda_handler\n    num = get_contract_id(df['num'][i])\n", "  File \"/var/task/app.py\", line 139, in get_contract_id\n    cur.execute(sql_sentence)\n"]}%
				
			

…えええ。違うエラー。
とりあえずDBには接続できた。けど存在しているはずのテーブルが無いと言われる。
SAMから実行ではなく、スクリプトとして実行する分には問題なくデータが取得できる。
ということは、コンテナからクエリを実行するとき特有の問題なのかな。

躓いたパート2の解決方法

色々調べたが、解決方法がわからなかった…。
とりあえず、クエリ実行時にテーブルのschemaを指定してあげたらいけた。

before

				
					def get_contract_id(num):
        cur = connection.cursor()
        sql_sentence = "SELECT id FROM place WHERE number = %s;"
        cur.execute(sql_sentence, (num,))
        return cur.fetchall()
				
			

after

				
					def get_contract_id(num):
        cur = connection.cursor()
        sql_sentence = "SELECT id FROM public.place WHERE number = %s;"
        cur.execute(sql_sentence, (num,))
        return cur.fetchall()