如何从 Docker 容器中的 python 脚本连接到 Ubuntu 上的本地 SQL 服务器

Posted

技术标签:

【中文标题】如何从 Docker 容器中的 python 脚本连接到 Ubuntu 上的本地 SQL 服务器【英文标题】:How do I connect to the local SQL server on Ubuntu from a python script in Docker container 【发布时间】:2022-01-04 16:42:51 【问题描述】:

我在 Ubuntu 20.04 中安装了一个 SQL Server(它安装在 VirtualBox 中)和一个带有 python 脚本的 Docker 容器。 我要做的是从这个python脚本连接到主机上运行的SQL服务器。

这是我用来创建镜像的 Dockerfile:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster
WORKDIR /app
COPY requirements.txt requirements.txt

RUN apt-get update && apt-get install -y gnupg2 curl
RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
RUN curl https://packages.microsoft.com/config/ubuntu/20.04/prod.list > /etc/apt/sources.list.d/mssql-release.list

RUN apt-get update
RUN ACCEPT_EULA=Y apt-get install -y msodbcsql17
RUN ACCEPT_EULA=Y apt-get install -y mssql-tools
RUN echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bash_profile
RUN echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc

RUN apt-get update \
  && apt-get -y install gcc \
  && apt-get -y install g++ \
  && apt-get -y install unixodbc unixodbc-dev \
  && apt-get clean 

RUN pip3 install -r requirements.txt

COPY . .

CMD [ "python3", "-m" , "test", "run", "--host=0.0.0.0"]

我知道它看起来很乱,但是我在安装 pyodbc 时遇到了几个问题,看起来所有这些代码都解决了问题。

我现在可以成功创建映像,但是在启动容器时,它会产生以下输出:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/local/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/app/test.py", line 10, in <module>
    connection = pyodbc.connect(connection_string, autocommit=True)
pyodbc.OperationalError: ('HYT00', '[HYT00] [Microsoft][ODBC Driver 17 for SQL Server]Login timeout expired (0) (SQLDriverConnect)')
['ODBC Driver 17 for SQL Server']

连接字符串好像有问题,如下所示:

connection_string = 'Driver=ODBC Driver 17 for SQL Server;Server=tcp:host.docker.internal,1433;UID=SA;PWD=<my_password>'

我已经为此苦苦挣扎了一段时间,但仍然无法使其发挥作用。非常感谢任何帮助或建议!

更新

按照 Pato 的建议,我尝试将连接字符串中的 Server 选项更改为机器的 IP 地址。

ip addr show 给我返回了以下输出:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:29:e8:b8 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute enp0s3
       valid_lft 71149sec preferred_lft 71149sec
    inet6 fe80::2517:652e:ac69:8ec9/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:35:26:ba:86 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:35ff:fe26:ba86/64 scope link 
       valid_lft forever preferred_lft forever

所以,我尝试用127.0.0.110.0.2.15172.17.0.1 替换host.docker.internal。他们都给了我以下错误:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/local/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/app/test.py", line 10, in <module>
    connection = pyodbc.connect(connection_string, autocommit=True)
pyodbc.Error: ('01000', "[01000] [unixODBC][Driver Manager]Can't open lib 'ODBC Driver 17 for SQL Server;Server=tcp:172.17.0.1,1433;UID=SA;PWD=<my_password>' : file not found (0) (SQLDriverConnect)")

ifconfig 回复了我这个:

docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:35ff:fe26:ba86  prefixlen 64  scopeid 0x20<link>
        ether 02:42:35:26:ba:86  txqueuelen 0  (Ethernet)
        RX packets 25340  bytes 1240556 (1.2 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 39308  bytes 691001962 (691.0 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.2.15  netmask 255.255.255.0  broadcast 10.0.2.255
        inet6 fe80::2517:652e:ac69:8ec9  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:29:e8:b8  txqueuelen 1000  (Ethernet)
        RX packets 649701  bytes 946934508 (946.9 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 55603  bytes 7183567 (7.1 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 27675  bytes 49732911 (49.7 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 27675  bytes 49732911 (49.7 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

【问题讨论】:

你可以远程连接到 SQL Server 对吧? @Pato 忘了说:Ubuntu 安装在 VirtualBox 中。我不确定是否可以远程连接到 SQL Server。我该如何检查? ***.com/a/43025206/9925593 SQL Server 实例配置为侦听哪些地址和端口?可能它被配置为仅接受 127.0.0.1:1433 上的连接,这将无法从 Docker 容器访问 - 因为 127.0.0.0/24 是每台机器/来宾/容器的不可路由环回网络。在 Ubuntu 客户机中运行 less /var/opt/mssql/mssql.conf 并查找 the network.* settings。 谢谢,您能否在 SQL Server 所在的同一虚拟机中安装和运行您的 python 脚本?在 connection_string 中使用这个 ip 127.0.0.1,1433。如果不起作用,请将 SQL Server 驱动程序更改为 pymssql。 【参考方案1】:

我正在研究这个连接问题,错误找不到文件是in this post。我试图重新创建你的设置,但我得到了同样的错误。

一个快速的解决方案是将驱动程序更改为 pymssql==2.2.2(在我的 docker 容器中测试)。

pip3 install pymssql==2.2.2

示例如下:

import pymssql

conn = pymssql.connect('host.docker.internal', 'sa', 'yourPassword', "database")

cursor = conn.cursor()

cursor.execute("""
IF OBJECT_ID('persons', 'U') IS NOT NULL
    DROP TABLE persons
CREATE TABLE persons (
    id INT NOT NULL,
    name VARCHAR(100),
    salesrep VARCHAR(100),
    PRIMARY KEY(id)
)
""")

cursor.executemany(
    "INSERT INTO persons VALUES (%d, %s, %s)",
    [(1, 'John Smith', 'John Doe'),
     (2, 'Jane Doe', 'Joe Dog'),
     (3, 'Mike T.', 'Sarah H.')])

# you must call commit() to persist your data if you don't set autocommit to True
conn.commit()

cursor.execute('SELECT * FROM persons WHERE salesrep=%s', 'John Doe')
row = cursor.fetchone()
while row:
    print("ID=%d, Name=%s" % (row[0], row[1]))
    row = cursor.fetchone()

conn.close()

# Result
'''
ID=1, Name=John Smith
'''

更新

SQL Server Linux 版本疑难解答

    检查 mssql-server.service 是否正在运行。
patricio@server2:~$ sudo systemctl status mssql-server.service
[sudo] password for patricio:
● mssql-server.service - Microsoft SQL Server Database Engine
   Loaded: loaded (/lib/systemd/system/mssql-server.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2021-11-10 09:56:38 -03; 2 weeks 4 days ago
     Docs: https://docs.microsoft.com/en-us/sql/linux
 Main PID: 14885 (sqlservr)
    Tasks: 164
   CGroup: /system.slice/mssql-server.service
           ├─14885 /opt/mssql/bin/sqlservr
           └─14913 /opt/mssql/bin/sqlservr
    检查端口 1433 或您的默认端口是否打开。
patricio@server2:~$ telnet 127.0.0.1 1433
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
patricio@server2:~$ nmap 127.0.0.1

Starting Nmap 7.60 ( https://nmap.org ) at 2021-11-29 08:41 -03
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000085s latency).
Not shown: 994 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
443/tcp  open  https
1433/tcp open  ms-sql-s
// ...
    如果没有安装sqlcmd,请安装
patricio@server2:~$ sqlcmd -S 127.0.0.1 -U sa -p
Password:
1> SELECT CONVERT(varchar, SERVERPROPERTY('collation'))
2> GO

------------------------------
SQL_Latin1_General_CP1_CI_AS

(1 rows affected)

参考资料:

From inside of a Docker container, how do I connect to the localhost of the machine?

Trying to access host.docker.internal results in Connection refused

【讨论】:

非常感谢您的帮助!不幸的是,现在我收到以下错误:File "src/pymssql/_pymssql.pyx", line 652, in pymssql._pymssql.connect pymssql._pymssql.OperationalError: (20009, b'DB-Lib error message 20009, severity 9:\nUnable to connect: Adaptive Server is unavailable or does not exist (host.docker.internal)\n') @OlegIvanytskyi 检查更新。如果您的 docker 容器无法 ping 或无法访问您的 SQL Server,也许这篇文章可以帮助您***.com/q/20430371/9925593 @Oleg 检查参考文献 天啊,非常感谢!事实证明,--network="host" 是运行容器时的必要选项。然后我可以使用127.0.0.1 而不是host.docker.internal 并且一切正常。你拯救了我的一天! 我很高兴它成功了

以上是关于如何从 Docker 容器中的 python 脚本连接到 Ubuntu 上的本地 SQL 服务器的主要内容,如果未能解决你的问题,请参考以下文章

将参数传递给 Docker 容器中的 Python argparse

Python 脚本在 docker 容器中找不到使用 CRON 运行的 ENV 变量

无法从 docker 容器内部访问 datadog 代理

如何将 python 库从主机共享到多个 docker 容器?

如何从运行 Fargate ECS 任务中查看 Python 打印语句?

在 nginx docker 容器中运行 python 脚本