WDS是Windows下专门用于远程部署的服务,此外,也可以使用MDT(微软部署工具包)扩展WDS服务,进行高级安装定制并生成PE供WDS加载。

本文作为前文 Windows远程部署 – WDS服务 的扩充,将演示由MDT扩展的,独立模式下WDS服务的配置(独立模式不依赖域环境),并结合syslinux实现Linux远程部署。

1. 安装服务和环境准备:

  • 安装WDS服务和DHCP(安装流程以及WDS配置与前篇中提到的WDS部署一样);
  • 安装MDT、ADK、WinPE addon for ADK(MDT依赖ADK,而ADK的PE镜像制作功能依赖WINPE addon)。

2. MDT配置

* 与纯WDS环境相比,加入MDT以后,系统可以利用MDT定制好的Lite Touch Installation镜像替代安装光盘中的boot.wim,并由该镜像结合MDT配置文件进行系统安装(所以与前文不同的是,WDS不再需要添加安装光盘中的boot.wim和install.wim)。

  • 打开MDT工作台,添加新的部署共享目录
  • 添加操作系统
  • 添加应用 – Office 2016 Professional Plus CN、Chrome
  • 创建任务序列
    • 为windows 10 pro, home和windows 8.1创建任务序列
    • 为创建好的任务序列进行设置(右键点击后,选择“属性”打开配置窗口)
      • 按需对任务序列进行编辑(例如,为准备安装的软件添加Install Application步骤)
      • 为DeploymentShare目录添加共享(如果2.1中没有自动创建共享的话)
      • 更新配置(做完任何改动——无论是配置、还是Rules应答文件——都需要进行Update操作)

3. WDS配置和DHCP配置

  • WDS和DHCP基础配置(与前文一样)
  • WDS镜像设置
    与前文不同的是:
    • 如果是新建WDS服务,则不再需要添加安装光盘中的boot.wim和install.wim;
    • 如果是在原有WDS服务上新部署MDT(例如基于前文的配置进行修改),需要删除所有已导入的boot.wim;
    • 若存在多个启动镜像,客户端在引导时,将进行引导镜像选择界面;
    • 当必须存在多个启动镜像时,需要为不同的启动镜像设置优先级;
    • 服务器属性的Boot标签中,引导镜像需要换成MDT中生成的定制镜像。
*WDS服务存在多个启动镜像时,会显示列表供选择,不需要的应当删除

没有Linux的安装需求时,本配置已经可以满足需要;以下内容将修改前文中的配置以适配Linux安装需求。

wds的原理是通过PXE推送windows bootmgr给客户端,通过bootmgr引导封装了安装程序的wim镜像开始系统安装。在纯wds和wds+mdt两种模式下,前者使用安装光盘中自带的启动镜像boot.wim进入PE进行普通安装,后者通过根据mdt工作台配置而创建的LiteTouch WinPE镜像进行定制安装。而前文Linux远程部署中,远程部署服务器则是推送syslinux引导程序进行系统安装,二者的区别就在于引导程序。我做过的尝试是将syslinux封装到wim文件中制作linux pe镜像,linux pe加载vmlinuz内核和初始化镜像initrd,开启系统安装流程,但因为能力有限,未能成功。所以只能演示一下syslinux引导winpe.iso光盘镜像来实现windows安装,通过syslinux引导linux内核和初始化镜像进行Linux安装。

反过来,同样的需求也可以在Linux服务器中配置完成,只要在Linux远程部署服务器的配置菜单中加入MDT制作完成的winpe.iso的引导即可,其本质是完全一样的。

四、安装syslinux,配置Linux远程安装(只演示x64,按照本步骤无法正常安装CentOS,troubleshooting和解决方案在本文的步骤六)

所需修改:将dhcp的pxe引导程序由windows bootmgr改为syslinux;配置pxelinux/default文件,分别加入linux启动项和winpe启动项。

1. 下载syslinux并提取相关文件

  • 从https://mirrors.edge.kernel.org/pub/linux/utils/boot/syslinux下载最新版本(本文使用6.03)
  • 解压后复制如下文件到WDS目录\Boot\x64:
    • 压缩包\bios\core\pxelinux.0
    • 压缩包\bios\com32\menu\menu.c32
    • 压缩包\bios\com32\chain\chain.c32
    • 压缩包\bios\com32\elflink\ldlinux\{ldlinux.c32, ldlinux.elf}
    • 压缩包\bios\com32\lib\{libcom32.c32, libcom32.elf}
    • 压缩包\bios\com32\libutil\libutil.c32
  • 进入WDS目录\Boot\x64目录,将目录下的指定文件复制并更名(相同目录下操作,复制而非重命名的目的是保留备份),新建pxelinux.cfg目录,用于保存引导菜单:
    • pxeboot.n12 => pxeboot.0
    • 创建pxelinux.cfg目录
    • 创建LinuxImages目录,在LinuxImages目录里为每个需要安装的Linux发行版建立独立目录(如LinuxImages\centos7),并将对应的iso镜像中的文件拷入目录中
  • 为CentOS7安装添加kickstart文件(LinuxImages\centos7\ks.cfg)
graphical

firstboot --enable
keyboard us
lang en_US.UTF-8

network  --bootproto=dhcp --device=ens33 --onboot=off --ipv6=auto --activate
network  --hostname=localhost.localdomain

rootpw 1234
services --disabled="chronyd"
timezone Asia/Shanghai --isUtc --nontp
bootloader --append=" crashkernel=auto" --location=mbr --boot-drive=sda
clearpart --all --initlabel
part /boot --fstype xfs --size 1024
part swap --size 1024
part / --fstype xfs --size 1 --grow

%packages
@^minimal
kexec-tools
%end

%addon com_redhat_kdump --enable --reserve-mb='auto'
%end
  • 在pxelinux.cfg目录新増文本文件,名为default,内容如下

DEFAULT      vesamenu.c32
 PROMPT       0
 NOESCAPE     0
 ALLOWOPTIONS 0
 TIMEOUT 300
 MENU MARGIN 10
 MENU ROWS 16
 MENU TABMSGROW 21
 MENU TIMEOUTROW 26
 MENU COLOR BORDER 30;44        #20ffffff #00000000 none
 MENU COLOR SCROLLBAR 30;44        #20ffffff #00000000 none
 MENU COLOR TITLE 0         #ffffffff #00000000 none
 MENU COLOR SEL   30;47        #40000000 #20ffffff
 MENU BACKGROUND MyMenuBackgroundPicture640x480.jpg
 MENU TITLE PXE Boot Menu

 LABEL wds
 MENU LABEL WDS (win10 pro/home, win8.1 pro)
 KERNEL pxeboot.0

 LABEL centos7
 MENU LABEL centos7
 KERNEL LinuxImages\centos7\images\pxeboot\vmlinuz
 APPEND initrd=LinuxImages\centos7\images\pxeboot\initrd.img repo=http://192.168.10.254/centos7 ramdisk_size=100000 devfs=nomount ks=http://192.168.10.254/centos7/ks.cfg

 LABEL local 
 MENU DEFAULT
 MENU LABEL Boot from Harddisk
 LOCALBOOT 0
 Type 0x80
  • 下载NGINX,发布linux的安装目录LinuxImages
  • 执行以下命令,将wds的引导程序由bootmgr替换为pxelinux
wdsutil /set-server /bootprogram:boot\x64\pxelinux.0 /architecture:x64
wdsutil /set-server /N12bootprogram:boot\x64\pxelinux.0 /architecture:x64

五、系统安装测试

1. Windows 10 Pro(EFI)

2. CentOS7 (Legacy BIOS)

六、Troubleshooting

按步骤四进行部署后(正常情况下,步骤四的部署和Linux pxe远程安装服务的部署一模一样),我碰到了两个异常状况:

  • 加载安装程序到一半的时候,直接退出,返回命令行
  • 加载了图形安装程序,正常下载ks.cfg,按配置文件自动设置好时区、语言、键盘、硬盘分区后,会一直停在Downloading packages metadata步骤

问题1

  • 根据提示,所有的日志信息都保存在/run/initramfs/rdsosreport.txt这个文件中,所以先导出这个文件(最简便的办法是用U盘拷出来);
  • 在日志文件中可以看到初始化的完整的命令行命令,而且没有报错,说明加载内核文件和初始镜像都正常;
Command line: BOOT_IMAGE=LinuxImages\centos7\images\pxeboot\vmlinuz initrd=LinuxImages\centos7\images\pxeboot\initrd.img repo=http://192.168.10.254/centos7 ramdisk_size=100000 devfs=nomount ks=http://192.168.10.254/centos7/ks.cfg
  • 继续查看文件,直到dhclient报告正常获取到IP地址,都没有告警或报错;
  • 获取到IP地址后,安装程序第一件事就是下载Live image文件squashfs.img,但是下载到50M的时候,curl就中断了,后续的mount操作因为文件不完整,自然也失败了:网络没有问题,因为下面马上开始开始另一项下载:kickstart配置文件,这一次下载顺利,但是保存下载文件到本地的时候出现了磁盘不足的报错,所以live image的下载可以肯定也是由于磁盘空间不足而中断;
[   15.697976] localhost dracut-initqueue[677]: % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
[   15.700127] localhost dracut-initqueue[677]: Dload  Upload   Total   Spent    Left  Speed
[   16.542288] localhost dracut-initqueue[677]: 0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0  2  475M    2 13.1M    0     0  43.0M      0  0:00:11 --:--:--  0:00:11 43.0M  8  475M    8 42.1M    0     0  49.9M      0  0:00:09 --:--:--  0:00:09 49.9M
[   16.545616] localhost dracut-initqueue[677]: curl: (23) Failed writing body (11760 != 16384)
[   17.126049] localhost kernel: loop: module loaded
[   16.725758] localhost dracut-initqueue[677]: mount: wrong fs type, bad option, bad superblock on /dev/loop0,
  • 当时发现磁盘空间不足的错误提示时并没能找到产生报错的原因,上网搜索之后才明白过来,这是内存不足造成的(因为整个环境都保存在内存中,而非硬盘里,所以“磁盘空间不足”的根本原因是内存不足,参考:https://access.redhat.com/solutions/3293511)。

最终解决办法:将机器的内存加到2G(我的虚拟机只有1G内存,后面复现问题的时候配置了512M内存)。

问题2:

  • 因为已经加载出安装程序的图形界面,所以排除网络问题(安装途中的下载一切顺利);
  • kickstart配置文件已经顺利下载到本地,所以ks.cfg之前的所有配置暂时不用排查;
  • 因为CentOS在安装完成后,会自动生成一套kickstart配置(/root/anaconda-ks.cfg),所以我直接手工安装了一遍,提取了最小化安装的kickstart配置文件,将这个配置文件里的安装源部分删掉(手工安装的源为CDROM)替换到服务器,问题依旧,因此并不能粗暴地认为是原来的kickstart文件有问题(而且原来的ks.cfg在前文Linux远程部署的时候用过,并没有问题);
  • 切到其它控制台,将/tmp目录下的日志全部拷出;
  • 首先查看anaconda.log,确实有报错,但是报错信息并不明确,只是提到minimal无法解析到对应的包组;

03:49:51,866 WARN anaconda: ("Can't translate group names to group ID - %s", "Yum's groups are not available")
03:49:51,868 INFO anaconda: Module initialized: SoftwareSelectionSpoke
03:49:51,868 INFO anaconda: Thread Done: AnaSoftwareWatcher (140354201392896)
03:49:51,869 INFO anaconda: Running Thread: AnaCheckSoftwareThread (140354470315776)
03:49:51,870 INFO anaconda: Thread Done: AnaCheckSoftwareThread (140354470315776)
03:49:51,888 INFO anaconda: setting SourceSpoke status to: Error downloading package metadata
03:49:51,894 INFO anaconda: spoke is ready: SourceSpoke
03:49:51,921 INFO anaconda: setting SoftwareSelectionSpoke status to: Error downloading package metadata
03:49:51,927 INFO anaconda: spoke is ready: SoftwareSelectionSpoke
03:49:51,928 DEBUG anaconda: running handleException
03:49:51,929 CRIT anaconda: Traceback (most recent call last):

  File "/usr/lib64/python2.7/site-packages/pyanaconda/ui/gui/hubs/__init__.py", line 329, in _update_spokes
    if not args[1] and spoke.changed and spoke.visitedSinceApplied:

  File "/usr/lib64/python2.7/site-packages/pyanaconda/ui/gui/spokes/software.py", line 243, in changed
    addons = self._get_selected_addons()

  File "/usr/lib64/python2.7/site-packages/pyanaconda/ui/gui/spokes/software.py", line 472, in _get_selected_addons
    addons = self._allAddons()

  File "/usr/lib64/python2.7/site-packages/pyanaconda/ui/gui/spokes/software.py", line 467, in _allAddons
    [""] + \

KeyError: 'minimal'
  • X.log只是图形界面的日志,暂时排除;storage.log和program.log里连一个warning都没有,暂时排除;ifcfg.log网络接口配置日志,排除;
  • 最终在anaconda-tb-WZZ6nw和packaging.log中都发现了相同的http 404错误,发生在下载metadata的时候,和图形界面的症状吻合(卡在downloading package metadata):
19:41:17,906 INFO packaging: gathering repo metadata for anaconda
19:41:17,910 ERR packaging: failed to grab repo metadata for anaconda: failure: repodata/03dde1157310b4a6d8e9128de19651c147a71150d406f3efaaf12f7eb83a1f35-primary.xml.gz from anaconda: [Errno 256] No more mirrors to try.
http://192.168.10.254/centos7/repodata/03dde1157310b4a6d8e9128de19651c147a71150d406f3efaaf12f7eb83a1f35-primary.xml.gz: [Errno 14] HTTP Error 404 - Not Found
19:41:17,912 INFO packaging: retrying metadata download for repo anaconda, retrying (1/10)
19:41:18,412 INFO packaging: gathering repo metadata for anaconda
19:41:18,425 ERR packaging: failed to grab repo metadata for anaconda: failure: repodata/03dde1157310b4a6d8e9128de19651c147a71150d406f3efaaf12f7eb83a1f35-primary.xml.gz from anaconda: [Errno 256] No more mirrors to try.
http://192.168.10.254/centos7/repodata/03dde1157310b4a6d8e9128de19651c147a71150d406f3efaaf12f7eb83a1f35-primary.xml.gz: [Errno 14] HTTP Error 404 - Not Found
19:41:18,426 INFO packaging: retrying metadata download for repo anaconda, retrying (2/10)

产生错误的原因是下载repodata/03dde1157310b4a6d8e9128de19651c147a71150d406f3efaaf12f7eb83a1f35-primary.xml.gz这个文件时找不到文件,所以回到服务器上检查安装光盘内容(WDS目录\Boot\X64\LinuxImages\centos7\repodata),发现当中有repomd.xml文件、和几个repometa文件,但几个repometa文件并不带后缀名,所以才会报404错误;

  • 打开repomd.xml后发现,每个entry都有location href属性,这个属性的值正好和报错的资源相一致,由此可见,安装程序完全根据 repomd.xml文件进行文件下载,预期的文件都带有文件扩展名,而安装光盘中的文件名只是一个checksum, 预期的名字和实际不匹配,将所有repometa文件按xml中的文件进行改名,重新启动pxe客户端后,一切正常。

<?xml version="1.0" encoding="UTF-8"?>
<repomd xmlns="http://linux.duke.edu/metadata/repo" xmlns:rpm="http://linux.duke.edu/metadata/rpm">
 <revision>1568227703</revision>
<data type="group">
  <checksum type="sha256">521f322f05f9802f2438d8bb7d97558c64ff3ff74c03322d77787ade9152d8bb</checksum>
  <location href="repodata/521f322f05f9802f2438d8bb7d97558c64ff3ff74c03322d77787ade9152d8bb-c7-x86_64-comps.xml"/>
  <timestamp>1568227719</timestamp>
  <size>910003</size>
</data>
<data type="filelists">
  <checksum type="sha256">84ff0ada5bdbf970afd2791ab1850d0596e41b5442e1ec3f7073974b222b7be9</checksum>
  <open-checksum type="sha256">b9f5d9207d6df75c2096fac4ddb65b13b10360be43f0fe7cbde7b23a217326dd</open-checksum>
  <location href="repodata/84ff0ada5bdbf970afd2791ab1850d0596e41b5442e1ec3f7073974b222b7be9-filelists.xml.gz"/>
  <timestamp>1568227713</timestamp>
  <size>3380126</size>
  <open-size>44228424</open-size>
</data>
<data type="group_gz">
  <checksum type="sha256">4af1fba0c1d6175b7e3c862b4bddfef93fffb84c37f7d5f18cfbff08abc47f8a</checksum>
  <open-checksum type="sha256">521f322f05f9802f2438d8bb7d97558c64ff3ff74c03322d77787ade9152d8bb</open-checksum>
  <location href="repodata/4af1fba0c1d6175b7e3c862b4bddfef93fffb84c37f7d5f18cfbff08abc47f8a-c7-x86_64-comps.xml.gz"/>
  <timestamp>1568227719</timestamp>
  <size>169182</size>
</data>
<data type="primary">
  <checksum type="sha256">03dde1157310b4a6d8e9128de19651c147a71150d406f3efaaf12f7eb83a1f35</checksum>
  <open-checksum type="sha256">d46fb41634820b6924ecdc855bd600767b2aaa4362e37f5f2eb6db4c4072f542</open-checksum>
  <location href="repodata/03dde1157310b4a6d8e9128de19651c147a71150d406f3efaaf12f7eb83a1f35-primary.xml.gz"/>
  <timestamp>1568227713</timestamp>
  <size>1614194</size>
  <open-size>13376281</open-size>
</data>
<data type="primary_db">
  <checksum type="sha256">7a2197223a2689b44594b09351dead84fe2d8cd0459be3f885bb985bb28af7ca</checksum>
  <open-checksum type="sha256">e3d7dd150b2d36ff47b2c66c67068e283b62ec40a5fd0a5dea032f9eaecc1a49</open-checksum>
  <location href="repodata/7a2197223a2689b44594b09351dead84fe2d8cd0459be3f885bb985bb28af7ca-primary.sqlite.bz2"/>
  <timestamp>1568227719</timestamp>
  <database_version>10</database_version>
  <size>3389624</size>
  <open-size>15980544</open-size>
</data>
<data type="other_db">
  <checksum type="sha256">177815eb15f48fcab91407f00675f26cbc13f07601c7d971b6255957faa2d1e0</checksum>
  <open-checksum type="sha256">f812e4e524e55150a66e8160fa2f5ecd0d5c129316fdccef3ec15249cea17ebb</open-checksum>
  <location href="repodata/177815eb15f48fcab91407f00675f26cbc13f07601c7d971b6255957faa2d1e0-other.sqlite.bz2"/>
  <timestamp>1568227714</timestamp>
  <database_version>10</database_version>
  <size>1386255</size>
  <open-size>7891968</open-size>
</data>
<data type="other">
  <checksum type="sha256">a7120e4fc4c651382e22fb4c126798235095a1ce58c2c0783cc6d31c2672f544</checksum>
  <open-checksum type="sha256">d9ddd89f80a945ca343a0c998b252733574aed4433c3bbd618b1db09c8dfca84</open-checksum>
  <location href="repodata/a7120e4fc4c651382e22fb4c126798235095a1ce58c2c0783cc6d31c2672f544-other.xml.gz"/>
  <timestamp>1568227713</timestamp>
  <size>1013045</size>
  <open-size>8419528</open-size>
</data>
<data type="filelists_db">
  <checksum type="sha256">17f82b7ec55c8c3c30bd0093d3241f1824aa4dafde0081f9a31829dbb7e3738e</checksum>
  <open-checksum type="sha256">a2e4e5fcf7eda798a9a216c68c392f812e723f86d8bdd797f340e8a81bcff831</open-checksum>
  <location href="repodata/17f82b7ec55c8c3c30bd0093d3241f1824aa4dafde0081f9a31829dbb7e3738e-filelists.sqlite.bz2"/>
  <timestamp>1568227717</timestamp>
  <database_version>10</database_version>
  <size>3439677</size>
  <open-size>20947968</open-size>
</data>
</repomd>

最终解决办法:将repodata中的metadata文件按照repomd.xml中的location href属性值进行改名。