ZhangZhihui's Blog  

https://learn.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker?view=sql-server-ver16&preserve-view=true&tabs=cli&pivots=cs1-powershell

 

Download sqlcmd-windows-amd64.zip from https://github.com/microsoft/go-sqlcmd/releases/tag/v1.9.0 .

 

PS C:\Users\ZhangZhihui> docker pull mcr.microsoft.com/mssql/server:2022-latest
PS C:\Users\ZhangZhihui> docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=SAPWD@sqlsvr22" -p 1433:1433 --name sqlsvr22 --hostname sqlsvr22 -d mcr.microsoft.com/mssql/server:2022-latest

 

1

 

By default, this quickstart creates a container with the Developer edition of SQL Server. The process for running production editions in containers is slightly different. For more information, see Run production container images.

The following table provides a description of the parameters in the previous docker run example:

 
ParameterDescription
-e "ACCEPT_EULA=Y" Set the ACCEPT_EULA variable to any value to confirm your acceptance of the End-User Licensing Agreement. Required setting for the SQL Server image.
-e "MSSQL_SA_PASSWORD=<password>" Specify your own strong password that is at least eight characters and meets the Password Policy. Required setting for the SQL Server image.
-e "MSSQL_COLLATION=<SQL_Server_collation>" Specify a custom SQL Server collation, instead of the default SQL_Latin1_General_CP1_CI_AS.
-p 1433:1433 Map a TCP port on the host environment (first value) with a TCP port in the container (second value). In this example, SQL Server is listening on TCP 1433 in the container and this container port is then exposed to TCP port 1433 on the host.
--name sql1 Specify a custom name for the container rather than a randomly generated one. If you run more than one container, you can't reuse this same name.
--hostname sql1 Used to explicitly set the container hostname. If you don't specify the hostname, it defaults to the container ID, which is a randomly generated system GUID.
-d Run the container in the background (daemon).
mcr.microsoft.com/mssql/server:2022-latest The SQL Server Linux container image.

 

PS C:\Users\ZhangZhihui> docker ps
CONTAINER ID   IMAGE                                        COMMAND                   CREATED         STATUS         PORTS                                         NAMES
c0f4897c1031   mcr.microsoft.com/mssql/server:2022-latest   "/opt/mssql/bin/laun…"   5 minutes ago   Up 5 minutes   0.0.0.0:1433->1433/tcp, [::]:1433->1433/tcp   sqlsvr22
PS C:\Users\ZhangZhihui> docker ps --no-trunc CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c0f4897c103191bbf91aba83217e6ad9d7184b39c8bd09e7d45bf5c8ce7f2776 mcr.microsoft.com/mssql/server:2022-latest "/opt/mssql/bin/launch_sqlservr.sh /opt/mssql/bin/sqlservr" 8 minutes ago Up 8 minutes 0.0.0.0:1433->1433/tcp, [::]:1433->1433/tcp sqlsvr22

 

PS C:\Users\ZhangZhihui> docker exec -t sqlsvr22 sh -c "cat /var/opt/mssql/log/errorlog | grep connection"
2025-12-29 06:59:47.26 Server      The maximum number of dedicated administrator connections for this instance is '1'
2025-12-29 06:59:48.36 Server      Dedicated admin connection support was established for listening locally on port 1434.
2025-12-29 06:59:48.37 spid25s     SQL Server is now ready for client connections. This is an informational message; no user action is required.
2025-12-29 06:59:48.87 spid38s     Always On: The availability replica manager is waiting for the instance of SQL Server to allow client connections. This is an informational message only. No user action is required.

 

PS C:\Users\ZhangZhihui> docker exec -it sqlsvr22 bash
mssql@sqlsvr22:/$ /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "SAPWD@sqlsvr22"
Sqlcmd: Error: Microsoft ODBC Driver 18 for SQL Server : SSL Provider: [error:0A000086:SSL routines::certificate verify failed:self-signed certificate].
Sqlcmd: Error: Microsoft ODBC Driver 18 for SQL Server : Client unable to establish connection. For solutions related to encryption errors, see https://go.microsoft.com/fwlink/?linkid=2226722.

 

这个错误是由 SQL Server 2022ODBC Driver 18 默认的安全策略变更引起的。

在新版本中,sqlcmd 默认强制开启了加密连接(Encrypt=yes),但由于容器使用的是自签名证书(Self-signed certificate),客户端默认不信任它,导致连接被拦截。

解决方法

你只需要在命令末尾添加 -C 参数(其含义是 TrustServerCertificate,即信任服务器证书):

/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "SAPWD@sqlsvr22" -C

为什么会这样?(原理分析)

从版本 18 开始,sqlcmd 的默认行为发生了变化,以增强安全性:

属性 旧版本 (v17 以前) 新版本 (v18+)
加密 (Encrypt) 默认 Optional (不强制) 默认 Mandatory (强制加密)
信任证书 (Trust Certificate) 默认 True 默认 False (必须验证证书合法性)

由于 Docker 容器内的 SQL Server 默认没有配置由受信任机构签发的 SSL 证书,它只能使用生成的“自签名证书”。客户端(sqlcmd)发现证书不是权威机构签发的,就会报错终止。

进阶建议

如果你在 Windows 宿主机(D 盘那个 sqlcmd.exe)连接容器,也需要加上同样的参数:

./sqlcmd.exe -S localhost -U sa -P "SAPWD@sqlsvr22" -C

或者使用长参数名:

./sqlcmd.exe -S localhost -U sa -P "SAPWD@sqlsvr22" -TrustServerCertificate

 

mssql@sqlsvr22:/$ /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "SAPWD@sqlsvr22" -C
1>

 

PS D:\sqlcmd-windows-amd64> ./sqlcmd.exe -S localhost -U sa -P "SAPWD@sqlsvr22"
1>

 

我在Windows宿主机(D 盘那个 sqlcmd.exe)上连接容器,./sqlcmd.exe -S localhost -U sa -P "SAPWD@sqlsvr22" 没有加"-C",也没有加-TrustServerCertificate,连接成功了。

 

这确实是一个非常有趣的现象,但也完全合乎逻辑。之所以你在 Windows 宿主机上不需要加 -C 就能连接,是因为你使用的 sqlcmd.exe 版本与容器内部的 sqlcmd 版本不一致

核心原因:驱动版本差异

  1. 容器内部 (sqlcmd v18+):

    你之前进入容器看到的是 /opt/mssql-tools18/...。这是微软最新的 v18 版本,它遵循“安全优先”原则,默认强制执行 SSL 证书验证。如果没有 -C,它会因为不信任自签名证书而报错。

  2. Windows 宿主机 (旧版 sqlcmd):

    你 D 盘下的 sqlcmd.exe 很可能是 v17 或更早的版本(或者是基于旧版 ODBC Driver 17 构建的)。

    • 旧版逻辑:默认不强制加密,或者即使加密也不强制验证服务器证书。

    • 验证方法:你可以通过 ./sqlcmd.exe -? 查看版本号。如果看到版本是 15.x 或 17.x,那么它就不会像 v18 那样“吹毛求疵”。


版本的行为对比

特性 容器内 (v18) 你的 Windows 宿主机 (旧版)
默认加密 开启 (Mandatory) 关闭 (Optional)
证书验证 严格校验 自动信任 (或不校验)
连接结果 报错,需加 -C 直接成功

潜在的风险提示

虽然现在连接成功了,但如果你以后将 Windows 上的 sqlcmd 升级到最新版(或者换用最新的 Go 版 sqlcmd),你可能也会遇到同样的 SSL 报错。

验证一下?

你可以尝试在宿主机上运行这个命令,看看你的 sqlcmd 到底是什么来头:

./sqlcmd.exe -?

如果你在输出的第一行看到 Microsoft (R) SQL Server Command Line Tool 后面没有明确的 Version 18.x,那就证实了我的推测。

 

PS D:\sqlcmd-windows-amd64> ./sqlcmd.exe -?
sqlcmd: Install/Create/Query SQL Server, Azure SQL, and Tools

Version: v1.9.0

 

这正是谜底所在!你使用的 v1.9.0 是微软最新推出的 Go 语言版本 sqlcmd

虽然它是最新的,但它的默认行为逻辑与容器里那个基于 ODBC 驱动的 C++ 版 sqlcmd (v18) 有所不同:

1. 为什么它不需要 -C

在 Go 版本的 sqlcmd (v1.x) 中,微软为了保持开发者的使用习惯,处理加密的方式更加智能。

  • Go 版行为:如果它发现你连接的是 localhost127.0.0.1,或者它检测到服务器要求加密但客户端没有配置证书,它在某些默认配置下会表现得比 v18 ODBC 版更“宽容”,或者它的默认 Encrypt 选项在非 Azure 环境下可能仍然是 false(或者是 Optional)。

  • 容器内 v18 版行为:它是严格绑定 Linux ODBC Driver 18 的,该驱动强制要求 Encrypt=yes 和证书校验。


2. 两个版本的小科普

目前微软官方同时维护着两个 sqlcmd

特性 经典版 (C++) 现代版 (Go)
安装路径 随 SSMS 或 ODBC 驱动安装 独立下载 (sqlcmd-windows-amd64)
你目前的位置 容器内部使用的版本 D 盘存放的版本
版本号格式 v15.x, v18.x v1.x.x (如你的 v1.9.0)
主要优势 与旧系统兼容性极佳 跨平台、小巧、支持查询结果导出 JSON/CSV

3. 一个有趣的尝试

如果你想让这个 Go 版本的 sqlcmd 也报错(模拟生产环境的严格加密),你可以尝试显式开启加密:

PowerShell
 
./sqlcmd.exe -S localhost -U sa -P "SAPWD@sqlsvr22" --encrypt mandatory

这时候,它大概率也会跳出那个熟悉的 "certificate verify failed" 错误。

 

PS D:\sqlcmd-windows-amd64> ./sqlcmd.exe -S localhost -U sa -P "SAPWD@sqlsvr22" --encrypt mandatory
Sqlcmd:  unknown flag: --encrypt

 

这是因为在 Go 版本的 sqlcmd(v1.x)中,虽然大部分经典参数都保留了,但部分长参数的命名规范发生了变化。

在 Go 版中,控制加密的参数是 -N (缩写) 或 --encrypt,但它接受的是字符串值,而不是简单的开关。

1. 为什么报错?

报错 unknown flag: --encrypt 通常是因为版本对参数的解析非常严格。在 Go 版 sqlcmd 中:

  • 正确参数:使用 -N 后面跟 truefalsedisable

  • 对于 SQL Server 2025/v2.x 环境-N 甚至支持 o (optional), m (mandatory), s (strict)。

2. 在 Go 版中模拟加密报错

如果你想测试证书验证失败的情况,请尝试以下命令:

# 使用 -N true 强制开启加密,且不带 -C
./sqlcmd.exe -S localhost -U sa -P "SAPWD@sqlsvr22" -N true

此时,由于没有 -C (TrustServerCertificate),它应该会报出和容器内部一样的证书错误。


3. Go 版 sqlcmd 的常用新参数

既然你已经在用这个新工具了,以下是几个它独有的、非常有用的参数:

功能 参数 示例
垂直输出 --vertical 像 MySQL 的 \G 一样显示行,适合列很多的表
查询耗时 --query-timeout 设置查询超时时间(秒)
格式化输出 --format 支持 table, csv, json, xml, vertical
静默模式 --silent 减少不必要的提示信息输出

4. 总结:两版 sqlcmd 的对比

特性 经典版 (容器内 v18) Go 版 (宿主机 v1.9)
加密参数 -N (开关) -N [true/false/disable]
信任证书 -C (开关) -C (开关)
默认行为 必须加密,不信任自签名 默认可能不强制加密(连 localhost 时)

💡 建议操作

你可以运行以下 T-SQL 来检查当前的连接是否真的加密了:

./sqlcmd.exe -S localhost -U sa -P "SAPWD@sqlsvr22" -Q "SELECT session_id, connect_time, encrypt_option FROM sys.dm_exec_connections WHERE session_id = @@SPID"

如果在 encrypt_option 列看到 FALSE,说明 Go 版在连接 localhost 时默认确实为了方便没有开启加密。

 

PS D:\sqlcmd-windows-amd64> ./sqlcmd.exe -S localhost -U sa -P "SAPWD@sqlsvr22" -N true
TLS Handshake failed: tls: failed to verify certificate: x509: certificate signed by unknown authority
TLS Handshake failed: tls: failed to verify certificate: x509: certificate signed by unknown authority

 

 

1> CREATE DATABASE ZZHDB;
2> SELECT name FROM sys.databases;
3> GO
name
--------------------------------------------------------------------------------------------------------------------------------
master
tempdb
model
msdb
ZZHDB

(5 rows affected)

 

1> USE ZZHDB;
2> CREATE TABLE Inventory(
3>     id INT,
4>     name NVARCHAR(50),
5>     quantity INT
6> );
7> INSERT INTO Inventory VALUES (1, 'banana', 150), (2, 'orange', 154);
8> GO
Changed database context to 'ZZHDB'.
(2 rows affected)

 

1> SELECT * FROM Inventory WHERE quantity > 152;
2> GO
id          name                                               quantity
----------- -------------------------------------------------- -----------
          2 orange                                                     154

(1 row affected)

 

1> QUIT
PS D:\sqlcmd-windows-amd64>

 

Install SQL Server Management Studio

Download the installer: https://aka.ms/ssms/22/release/vs_SSMS.exe

 

posted on 2025-12-29 15:49  ZhangZhihuiAAA  阅读(0)  评论(0)    收藏  举报