问题描述
在Azure上创建的数据库,单独通过SQL的连接工具是可以访问,但在Web App却无法访问,错误信息为:
{
"timestamp": "2021-05-20T05:21:04.672+0000",
"status": 500,
"error": "Internal Server Error",
"message": "nested exception is org.apache.ibatis.exceptions.PersistenceException: \n### Error querying database.
Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection;
nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 0, maxActive 100, creating 0\n
### The error may exist in com/digital/dao/SumOrderMapper.java (best guess)\n
### The error may involve com.digital.dao.SumOrderMapper.selectOne\n
### The error occurred while executing a query\n
### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection;
nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 0, maxActive 100, creating 0",
"path": "/ba/getGapAndOffset"
}
问题分析&解决
一:分析从Web App到数据库的网络是否相通:使用 PsPing & PaPing 进行 TCP 端口连通性测试
PsPing 是微软 PSTools 工具套件中的其中一个命令。除了ICMP ping 测试,它主要用来测试 TCP 端口的连通性,还可以测试 TCP/UDP 网络时延和带宽。下载地址为:https://technet.microsoft.com/zh-cn/sysinternals/jj729731.aspx
PsPing 进行 TCP 连接测试时所支持的参数说明:
-t 类似于 ICMP 的长 ping 测试,直到按下 Ctrl+C 停止测试,并显示统计结果;
-n 指定测试次数。还可以指定测试的时间长度,以秒为单位,使用时在数字后加上 s,例如“10s”;
-i 每次测试的间隔,默认为 1 秒。还可以指定为 0 来进行快速 ping 测试;
-w 热身次数,默认为 1 次;
-q 测试过程中不输出结果,结束后显示统计结果;
-h 将时延结果统计为直方图打印(默认打印 20行),也可以指定结果行数,比如 -h 10,指定 10 行;另一种使用方法是统计自定义时延,比如 -h "65,70",结果将统计时延分别为 65 和 70 毫秒的次数;
-4 强制使用 IPv4;
-6 强制使用 IPv6;
如果在 Linux 中发起 TCP 端口连通性和网路时延的测试,可以使用 PaPing 。PaPing 是一个跨平台的开源工具。它的功能相对 PsPing 而言更简单,只支持 TCP 端口的相关测试,不支持 UDP 端口的测试。下载地址为:https://code.google.com/archive/p/paping/downloads
安装方式(Linux):
#cd ~
#wget https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/paping/paping_1.5.5_x86-64_linux.tar.gz
#tar zxvf paping_1.5.5_x86-64_linux.tar.gz
测试结果, 在App Service(Web App)上去paping数据库可以连通的,由此可见网络层面应该是可以通的。
Add alt text二:在网络层是连通的情况下,进一步就需要查看应用的异常信息
在App Service(Web App)的应用日志中,发现了 “ ssl connection is required ” 的错误消息,这是因为数据库(PostgreSQL) 配置SSL的连接要求,而应用程序时运行在Linux环境Docker中,也是需要开启SSL的。而在Linux中为容器开启SSL,需要以下几步:
注:SSH 实现容器和客户端之间的安全通信。 为了使自定义容器支持 SSH,必须将其添加到 Docker 映像本身。
第一步:将 sshd_config 文件添加到项目文件中,示例内容如下
Port 2222
ListenAddress 0.0.0.0
LoginGraceTime 180
X11Forwarding yes
Ciphers aes128-cbc,3des-cbc,aes256-cbc,aes128-ctr,aes192-ctr,aes256-ctr
MACs hmac-sha1,hmac-sha1-96
StrictModes yes
SyslogFacility DAEMON
PasswordAuthentication yes
PermitEmptyPasswords no
PermitRootLogin yes
Subsystem sftp internal-sftp
此文件配置 OpenSSH 并且必须包括以下项:
- Port 必须设置为 2222。
- Ciphers 必须至少包含此列表中的一项:aes128-cbc,3des-cbc,aes256-cbc。
- MACs 必须至少包含此列表中的一项:hmac-sha1,hmac-sha1-96。
第二步:在 Dockerfile 中,添加以下命令
# Install OpenSSH and set the password for root to "Docker!". In this example, "apk add" is the install instruction for an Alpine Linux-based image.
RUN apk add openssh \
&& echo "root:Docker!" | chpasswd
# Copy the sshd_config file to the /etc/ssh/ directory
COPY sshd_config /etc/ssh/
# Open port 2222 for SSH access
EXPOSE 80 2222
此配置不允许从外部建立到容器的连接。 容器的端口 2222 只能在专用虚拟网络的桥网络中访问,Internet 上的攻击者无法访问该端口。
第三步:在容器的启动脚本中启动 SSH 服务器
/usr/sbin/sshd
三:App Service(Web App)如何能够解析内网中资源的Endpoint呢?
由于在App Service(Web App)中无法解析Redis的Private Endpoint IP,所以无法连接Redis,由于Redis在开启Private Endpoint时也有创建Azure Private DNS Zone,所以需要在App Service配置使用Azure Private DNS Zone用于解析Redis Private E你的point。
在App Service的配置中添加两项应用程序设置:
WEBSITE_DNS_SERVER= 168.63.129.16 #Azure Private DNS Server IP Address
**WEBSITE_VNET_ROUTE_ALL=1 **
这些设置会将所有出站调用从应用发送到 VNet,还允许应用访问 Azure DNS 专用区域。配置使用Azure Private DNS Zone的官方文档:https://docs.azure.cn/zh-cn/app-service/web-sites-integrate-with-vnet#azure-dns-private-zones
参考资料
配置使用Azure Private DNS Zone: https://docs.azure.cn/zh-cn/app-service/web-sites-integrate-with-vnet#azure-dns-private-zones
App service启用应用程序日志: https://docs.microsoft.com/zh-cn/azure/app-service/troubleshoot-diagnostic-logs#enable-application-logging-linuxcontainer
PostgreSQL配置ssl: https://docs.azure.cn/zh-cn/postgresql/concepts-ssl-connection-security
【完】