ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用docker-compose快速部署SQL Server:从环境配置到日常运维全攻略

用docker-compose快速部署SQL Server:从环境配置到日常运维全攻略 做后端的兄弟尤其是公司里历史系统比较多、跑着微软技术栈项目的一定对 SQL Server 不陌生。这数据库本身很能打事务处理、BI 能力、生态工具都成熟但最让人头疼的往往不是 SQL 怎么写而是环境怎么搭。以前我在 Windows 上装 SQL Server从安装包下载到环境检查、配置管理器、服务启动再到各种权限和防火墙问题一套流程走下来中途任何一步出错都可能让人血压升高。后来改用 docker-compose 启动 sql-server整个体验立刻不一样了。本文就把我用 docker-compose 部署 SQL Server 的完整过程、踩过的坑和日常实操经验整理出来想省事的同学可以直接照抄想搞懂原理的也可以慢慢看。这篇内容适合谁适合那些需要在本地开发环境、测试环境或轻量生产环境快速拉起一个 SQL Server 的开发者也适合运维同学想用统一编排方式管理数据库服务。无论你是用 Windows、macOS 还是 Linux只要装了 Docker 和 Docker Compose都能用同一套配置把 SQL Server 跑起来。下面进入正题。1. 为什么我最终选了 docker-compose 部署 SQL Server1.1 绕开 Windows 安装的“老大难”问题先说背景。SQL Server 的原生安装方式虽然 GUI 做得很友好但实际踩坑的人非常多。搜索“sqlserver 安装教程”时能看到几十篇“史上最详细”的教程评论区照样一堆报错。常见的有安装程序遇到句柄无效、异常来自 HRESULT、安装到一半回滚、服务启动失败、SQL Server 配置管理器里看不到实例等等。这些问题绝大多数和 Windows 系统环境有关注册表残留、.NET 环境不干净、杀毒软件拦截、安装包损坏、权限不够每一样都能把人逼疯。用 docker-compose 启动 sql-server 之后这些桌面安装的坑基本上全部绕开了。因为数据库跑在容器里宿主机不需要安装 SQL Server 本体也就不存在注册表、服务管理器、配置管理器这些乱七八糟的依赖。装好 Docker拉镜像跑容器就这么简单。我见过很多同事第一次在 Linux 服务器上用 docker 跑 SQL Server五分钟不到就把以前折腾一整天的事情搞定了。当然也有一个坏处如果你必须要用 SQL Server Agent 的某些图形化运维界面或者依赖 Windows 集成认证连域环境容器方案会麻烦一些。但对于开发、测试、以及大量常规业务场景容器方案完全够用而且团队协作时环境一致性更高配置直接放进仓库谁都能一键复现。1.2 镜像版本怎么选2022-latest 还是 2019微软官方镜像mcr.microsoft.com/mssql/server目前主推 2022 系列也有 2019、2017 的 tag 可选。我的建议是默认用2022-latest除非你的项目有老版本兼容性限制。2022 相比 2019 在性能、安全、JSON 支持、查询优化方面都有提升而且兼容性做得很好绝大多数 2019 甚至 2016 的库都能平滑迁过去。这里有个细节要注意官方镜像的 Linux 容器版本是 SQL Server 的 Evaluation评估版180 天授权期功能上是完整的企业版功能。对开发、测试环境来说这完全够用生产环境如果要用就需要考虑微软的 License 策略不在本文讨论范围内。另外镜像体积不小大概 1.5GB 左右拉取的时候要有耐心。拉完之后可以打一个自己的 tag比如mssql-custom:2022后续如果基于这个镜像做初始化脚本、装工具之类的扩展方便管理。1.3 部署前的资源规划和检查SQL Server 不是轻量数据库容器跑起来内存吃得很凶。微软官方要求最低 2GB 内存实际开发环境建议至少给 4GB如果你的机器只有 8GB 内存再跑一个 SQL Server 容器会比较紧张。磁盘方面镜像加数据卷至少预留 10GB 以上数据库文件会随时间膨胀别等满了再想办法。端口方面默认是 1433这个端口在日常开发中非常常用容易被本机已装的 MySQL、其他 SQL Server 实例或杂七杂八的中间件占用。部署前先检查一下sudo lsof -i :1433 # 或者 sudo netstat -tunlp | grep 1433如果端口被占用要么停掉占用进程要么在 compose 文件里把宿主机端口映射改成别的比如11433:1433。我个人更倾向于保持容器内部 1433 不变只在宿主机端口上做调整这样容器配置始终是标准化的宿主机端口冲突时只需要改映射关系不用改容器内部任何东西。Docker Compose 本身要提前确认好版本。现在 Docker 官方推荐的是docker compose插件方式命令是docker compose中间没有横杠。老用户可能还在用 python 的docker-compose独立工具。两种方式的 compose 文件语法基本一致但命令行格式略有差别后者一般是docker-compose。本文我统一用新版docker compose语法写示例老版用户把命令换成docker-compose即可。2. 手把手配置 docker-compose.yml逐项拆解2.1 最小可用的 compose 配置先给出一份我实际在用的完整配置然后逐项解释为什么这么写。services: mssql: image: mcr.microsoft.com/mssql/server:2022-latest container_name: sqlserver environment: ACCEPT_EULA: Y MSSQL_SA_PASSWORD: YourStrong!Passw0rd TZ: Asia/Shanghai ports: - 1433:1433 volumes: - mssql_data:/var/opt/mssql restart: unless-stopped volumes: mssql_data:看起来很短但每一个字段都有讲究展开说一下。ACCEPT_EULA必须设置成Y表示接受 SQL Server 的最终用户许可协议不设置这一项镜像启动会直接报错退出。MSSQL_SA_PASSWORD是系统管理员 sa 账号的密码这个密码有严格策略要求至少 8 个字符并且必须包含大写字母、小写字母、数字和符号四类中的至少三类。别图省事设置一个12345678容器会在启动阶段直接拒绝并退出日志里会提示密码强度不符合要求。这个密码也是后续所有数据库连接的管理员凭证一定要放到安全的配置管理里不要硬编码提交到公开仓库。TZ设置时区这个很多人会忽略。容器默认是 UTC 时间如果你在业务 SQL 里用了GETDATE()返回的时间和本地时间会差 8 个小时排查起来很痛苦。设置成Asia/Shanghai后SQL Server 内部的时间函数就会使用中国时区减少很多不必要的麻烦。端口映射1433:1433左边是宿主机端口右边是容器内 SQL Server 监听的端口。如果宿主机 1433 端口已经被占改成11433:1433即可。数据卷mssql_data:/var/opt/mssql是最关键的部分。SQL Server 的数据文件、日志文件、系统数据库都存在/var/opt/mssql目录下。不挂数据卷的话容器一旦被删除整个数据库里的数据全部消失等于白干。用命名卷mssql_data的好处是数据保存在 Docker 管理的卷目录中即使容器删除重建数据还在。而且多容器共享同一套 compose 文件时每个项目的卷是隔离的不会互相干扰。restart: unless-stopped表示容器异常退出时自动重启除非你手动 stop。对服务器环境来说很重要机器重启后数据库能自动拉起来不用人工干预。2.2 启动容器并完成首次连接验证配置写好之后在 docker-compose.yml 所在的目录执行docker compose up -d-d表示后台运行不加的话终端会被日志刷屏。启动后先看容器状态docker ps正常状态下STATUS列会显示Up比如Up 2 minutes。接着看一眼日志确认没有异常docker logs sqlserver日志里如果出现类似SQL Server is ready for client connections或者Recovery is complete的提示说明启动成功。如果一直卡住或者能看到ERROR: [Microsoft][ODBC Driver 18 for SQL Server]这类信息多半是密码策略或资源问题后面常见问题里我会详细说。然后进入容器用 sqlcmd 验证连接。这里有个关键差异要注意2022 镜像里的 sqlcmd 路径是/opt/mssql-tools18/bin/sqlcmd2019 及更早镜像里是/opt/mssql-tools/bin/sqlcmd。因为 2022 默认启用强制加密连接sqlcmd 18 版本需要额外加-C参数来信任服务器证书否则会报证书相关的错误。docker exec -it sqlserver /opt/mssql-tools18/bin/sqlcmd \ -S localhost -U sa -P YourStrong!Passw0rd -C \ -Q SELECT VERSION参数说明-S指定服务器地址容器内部就是localhost-U和-P是账号密码-C是信任服务器证书-Q是执行一段 SQL 后退出。如果能输出版本信息比如 SQL Server 2022 的版本号说明整个链路已经通了。2.3 客户端接入连接串、JDBC 驱动和图形化工具容器跑起来只是第一步大家日常肯定要用各种工具连进去操作。这里把常见连接方式整理一下。使用官方图形化工具的话Windows 上最顺手的是 SQL Server Management StudioSSMSmacOS 上则是 Azure Data Studio。DBeaver 也可以它对各种数据库统一管理习惯用 DBeaver 的也能直接连。连接时服务器地址填写localhost,1433注意是英文逗号不是冒号登录方式选 SQL Server 身份验证账号sa密码就用刚才设的强密码。如果宿主机端口改了比如映射到 11433就填localhost,11433。开发场景里Java 项目用 JDBC 连接是高频需求。连接串格式如下jdbc:sqlserver://localhost:1433;encrypttrue;trustServerCertificatetrue;databaseNamemaster这里有两个参数非常关键encrypttrue和trustServerCertificatetrue。SQL Server 2022 默认强制加密连接如果不在连接串里显式信任客户端证书校验会失败IDE 里报错会让人百思不得其解。加了trustServerCertificatetrue就是告诉驱动“本地开发环境不需要校验服务器证书”问题立刻解决。顺带说一个很多人痛点IntelliJ IDEA 里新建 SQL Server 数据源时IDEA 尝试自动下载 JDBC 驱动结果卡在download from maven failed半天连不上。这个问题的根源不在数据库配置而是 IDEA 从 Maven 仓库拉取mssql-jdbc驱动时受网络环境影响失败。最省心的解决办法是手动去微软官方下载对应版本的mssql-jdbcjar 包放进 IDEA 数据源的驱动列表里或者放到项目libs目录并引入。KettlePDI连 SQL Server 也是类似的道理把官方 JDBC 驱动放进 Kettle 的lib目录即可驱动类名是com.microsoft.sqlserver.jdbc.SQLServerDriver。3. 部署 SQL Server 后的常用实操经验3.1 数据持久化、备份与还原挂载了数据卷之后数据库文件已经持久化了但备份还是得自己做。Docker 容器里做备份核心思路是把备份文件写到容器内挂载的卷目录中再拷贝到宿主机存档。先创建备份目录。因为容器内 SQL Server 进程是以mssql用户运行的备份目录如果权限不对写入会报“拒绝访问”的错误docker exec -it sqlserver mkdir -p /var/opt/mssql/backup docker exec -it sqlserver chown mssql:mssql /var/opt/mssql/backup然后进容器或用 sqlcmd 执行备份docker exec -it sqlserver /opt/mssql-tools18/bin/sqlcmd \ -S localhost -U sa -P YourStrong!Passw0rd -C \ -Q BACKUP DATABASE [YourDB] TO DISK N/var/opt/mssql/backup/YourDB.bak WITH INIT备份文件在容器内生成后用docker cp拉出来docker cp sqlserver:/var/opt/mssql/backup/YourDB.bak /your/host/path/还原的操作是反过来的。先把备份文件docker cp进容器再执行还原语句。注意还原时如果数据库文件路径和原库不一致可能需要用到WITH MOVE把数据文件和日志文件挪到容器内正确的目录。这个细节很烦人但实际工作中绕不开。我的建议是第一次还原之前用RESTORE FILELISTONLY FROM DISK N/var/opt/mssql/backup/YourDB.bak查看备份内部的文件逻辑名再决定WITH MOVE怎么写避免盲目还原报错。3.2 开发中高频踩坑的三个 SQL 场景数据库环境跑起来了业务开发时有些高频 SQL 场景也值得展开说说因为很多人都是临时上网搜。这里挑了三个和 SQL Server 最常被搜索的场景相关的问题。字符串转数字这个几乎是每天都会遇到的。SQL Server 提供CAST和CONVERT函数但直接转换非数字字符串会直接报错中断整个批处理。比如SELECT CAST(abc AS INT)会抛出转换失败错误。更稳妥的做法是使用TRY_CAST或TRY_CONVERT转换失败时返回 NULL 而不是报错程序里再处理 null 逻辑就行SELECT TRY_CAST(123 AS INT) AS ok, TRY_CAST(abc AS INT) AS failed; -- ok 返回 123failed 返回 NULL多行合并成一行也是搜索量很高的需求。SQL Server 2017 以上版本提供了STRING_AGG比老办法FOR XML PATH清晰太多SELECT DepartmentID, STRING_AGG(EmployeeName, ,) AS NameList FROM Employees GROUP BY DepartmentID;排序有要求的话STRING_AGG函数内支持WITHIN GROUP (ORDER BY ...)这一点比FOR XML PATH写法直观得多。老版本环境只能用FOR XML PATH配合 STUFF 实现语法比较绕不展开细说了。单表上亿数据导致存储空间膨胀这个场景越到项目后期越头疼。如果表结构已经稳定最直接有效的降空间手段是开启页级压缩ALTER TABLE YourBigTable REBUILD PARTITION ALL WITH (DATA_COMPRESSION PAGE);页级压缩对数字、字符串这种重复性高的数据效果明显但会增加 CPU 开销OLTP 高频写入场景要压测后再上。更系统的做法是考虑分区表或归档旧数据把历史数据挪到独立的归档库或冷存储中让在线表瘦身。这属于优化级别的方案真要做了建议先分析表占用的空间分布定位是哪类数据占了大头再决定压缩还是分区。3.3 存储过程、触发器与变更记录的正确姿势有一些网友会问“存储记录时触发怎么写”其实就是想在数据变更时自动记录日志。触发器可以实现比如在表上建AFTER INSERT, UPDATE, DELETE触发器把变更前后的数据写入审计表。没接触过的同学可能会写出非常臃肿的逐字段判断逻辑。实际上可以利用INSERTED和DELETED临时表一次拿到变更前后的整行数据然后统一记录 JSON 全文或差异列。不过我想多提一句如果是正经的业务审计、数据同步场景SQL Server 内置了变更数据捕获CDC和更改跟踪Change Tracking功能尤其以 CDC 能力更强可以读取到变更流而不需要自己写触发器。对开发者来说这两者的学习成本比造轮子触发器低得多还能避免触发器带来的隐性死锁和事务膨胀问题。触发器适合业务规则非常明确的场景比如订单状态流转时强制校验合法性而不是拿来记录日志。3.4 日常运维日志、重启策略和版本升级容器跑起来以后不是彻底不管。数据库容器如果长期不重启内存会持续增长这是 SQL Server 在 Linux 容器下的常见表现。我的做法是定期执行一次docker compose restart或者设置计划任务在低峰期重启容器。重启后内存会释放但注意恢复数据库同样需要时间不要在高负载时段做这个事。因为设置了restart: unless-stopped容器异常退出后会自己拉起但真正宕机原因还是要看日志。日志路径直接用docker logs --tail 200 sqlserver镜像更新也是运维的一部分。微软推送新版本补丁后旧的镜像不会自动升级。升级流程是先备份所有业务库然后拉取新镜像重新执行docker compose up -d确认 SQL Server 版本变化后检查业务 SQL 是否有兼容性波动。这个过程在 compose 文件管理下非常顺滑不像物理机升级那样有很长的停机窗口。4. 常见问题与排查技巧实录4.1 容器起不来或者反复重启容器启动后一直处于Restarting状态是最常见的坑。先别急着重建容器执行docker logs看具体日志基本一看就知道问题在哪。我把高频错误整理成一张速查表报错特征根本原因处理办法日志提示SQL Server 2019 is not supported on this OS镜像和宿主机内核/系统版本不兼容换用官方支持的镜像 tag比如 2022-latest或者升级 Docker日志提示密码强度校验失败MSSQL_SA_PASSWORD不符合复杂度要求改成 8 位以上、含大小写字母和数字符号的强密码启动没报错但端口连不上容器映射端口和实际监听不一致或宿主机防火墙拦截确认docker ps中的端口映射检查防火墙入站规则容器反复重启且日志有Permission denied数据卷目录权限不对mssql 用户无法写入手动创建目录并 chown 给 101 用户mssql内存不足导致 SQL Server 进程被杀宿主机内存小于 2GB加内存或限制容器内存甚至换更轻量的数据库方案4.2 客户端连不上数据库的排查路径容器明明起来了客户端就是连不上。遇到这种问题我建议按六步走。第一步宿主机上确认端口映射是不是对得上docker port sqlserver能看到实际映射。第二步检查容器内 sqlcmd 能不能连如果能连说明数据库服务本身没问题问题在网络链路。第三步检查宿主机防火墙特别是云服务器安全组里 1433 端口放行没有。第四步看连接串是不是把encrypttrue和trustServerCertificatetrue加上了2022 版本缺这个就是连不上。第五步确认 sa 账号有没有被锁定密码输错太多次会导致账号锁死。第六步换个客户端工具再试一次很多诡异问题其实是工具自身缓存或驱动版本导致。4.3 安装类问题与面板识别问题的避坑建议网上还有很多“sqlserver 安装程序遇到句柄无效”“已经在服务器面板安装了 SQL Server 但面板无法识别”这类问题。如果你走的是容器部署上面这些问题基本不会碰到因为容器内服务不受宿主机 init 系统管理服务器面板扫不到也是正常的。数据库是否在跑只看docker ps的输出不要依赖面板里的服务列表。如果实在遇到了需要在 Windows 本机安装的硬性需求那我只提醒几点使用管理员身份运行安装程序、暂时关闭杀毒软件和防火墙、安装前清理旧版本残留的注册表项、优先用 SQL Server Configuration Manager 检查服务账号权限。这些经验都是从一次次“句柄无效”“HRESULT 异常”的报错里攒出来的尤其是 “句柄无效”很大程度上是用户账户权限不够或安装包来源不干净重新下载官方安装包并右键管理员运行往往能解决。说起容器方案我在实际项目里的体会是它解决的不仅是“装环境”的速度问题更是团队协作的一致性问题。以前新同学入职配开发环境要花半天现在 docker compose 一条命令五分钟就有一个和线上配置几乎一致的 SQL Server。数据卷备份、灾后重建这些常规操作也比物理机或云主机 RDS 更可控。最后分享一个小技巧docker-compose.yml 文件本身就是最好的运维文档。你可以在服务环境变量下方写注释把 sa 密码的存放位置、备份策略、端口变更原因都记进去下次任何人接手都能快速了解这套 SQL Server 的治理逻辑。我自己就把这份 compose 文件放进了团队的知识库数据库相关的初始化脚本、定时备份脚本都围绕它展开后续如果有更多服务需要编排也可以在这个文件里直接扩展。
返回列表