使用Ansible进行Azure上的Windows 10开发环境的配置和尝试win_dsc模块

这篇文章是FUJITSU Advent Calendar 2017第13天的文章。
在第11天的文章《使用ansible在远程端解压多个zip文件的方法》之后,我们将继续讨论Ansible。

我基于微软今年发布的两个我个人认为非常出色的功能,在Azure上构建了Windows开发环境。另外,我还亲自体验了今年9月正式发布的Ansible 2.4中新增的win_dsc模块。

太长没看。

Nested Virtualization in Azure は開発環境にやさしい

Ansible on Windows Subsystem for Linux は開発環境の整備に使える
Ansible v2.4 から導入された win_dsc モジュールは、Windows PowerShell Desired State Configuration の 組み込みリソース もしくは PowerShell Gallery にあるカスタムリソースを使いたい場合には有効

请注意

Windows Subsystem for Linux不受Microsoft或Ansible支持,不应该用于生产系统。
– Ansible可以在Windows上运行吗?- Ansible文档

请注意,在生产环境中,不支持在 WSL 上运行 Ansible。

Ansible 和期望状态配置

安速倍

红帽提供的是一款简单的运维自动化工具。它在开源社区的发展也非常活跃。它通过名为Playbook的YAML格式配置文件,以声明方式描述资源的状态。与Chef或Puppet的区别可以在@IT的文章中找到。Ansible的引擎和模块(库)是用Python开发的,但对于Windows模块来说,它与目标机器的连接使用的是WinRM协议,与之高度兼容,而且使用Powershell来开发,可以获得.NET框架的好处。

Ansible 提供了一系列服务,包括 Ansible Galaxy(用于共享处理单元的角色),Ansible Tower(提供 GUI 仪表盘和任务调度功能),以及其上游版本 AWX。

此外,还有一种叫做Ansible Container的工具,可以使用现有的Ansible Playbook创建和执行容器镜像。

期望的状态配置

这是一个基于 Microsoft 提供的 Powershell 的配置管理工具。虽然也以开源方式公开,但开发社区的活跃度并不高。DSC 首先通过 WinRM 连接到目标机器,然后分别执行任务的三个阶段:获取当前状态、检查是否需要进行配置更改、执行配置更改。在 Ansible 中称之为模块,而在 DSC 中称之为资源。

胜利 的 DSC 模块

Ansible的win_dsc模块是一个用于执行目标主机上存在的DSC资源的包装器模块。最初,它是作为非官方模块发布的,但是从v2.4开始,它终于成为官方模块了。可以说,它成为了已经稀缺的Windows模块的救世主,许多人都期待着这个模块的到来。 (遥远的目光)

通过安装和配置Windows Subsystem for Linux,使得在Windows操作系统上能够运行Ansible。

这次在Azure上创建了支持嵌套虚拟化的Dv3尺寸虚拟机,然后在上面进行了各种尝试。我想试试WSL,所以使用了Windows 10 Pro,版本为1709的虚拟机映像。虽然Windows Server 2016从版本1709开始支持Semi-Anual Channel发布,但桌面版的Windows Server不支持,所以这次选择了Windows 10。

此外,借助于Vagrant,在VirtualBox虚拟环境中建立客户操作系统(验证环境),并尝试使用win_dsc模块。

主机操作系统的配置

主机操作系统的配置如下所示。

Azure的基础设施即服务

    • VM サイズ: Standard D2 v3 (2 vcpu 数、8 GB メモリ)

 

    • OS: Windows 10, Version 1709 (OS Build 16299.64)

 

    • VirtualBox: v5.1.30

 

    Vagrant: v2.0.1

搭建 Ansible 执行环境

我在WSL上使用了Ubuntu的软件包。关于安装Ubuntu的具体步骤,请参考Build Insider的文章。

$ cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.3 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.3 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial

查看官方文档才能准确安装Ansible,但以下是参考步骤。这次我们将配置CredSSP认证,允许委派凭据,即使登录域账户也能顺利建立WinRM连接。

$ sudo apt-get update
$ sudo apt-get install python-pip git libffi-dev libssl-dev -y
$ sudo pip install --upgrade pip
$ sudo pip install ansible pywinrm
$ sudo pip install pywinrm[credssp]
$ ansible --version
ansible 2.4.1.0
  config file = None
  configured module search path = [u'/home/toversus/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/toversus/.local/lib/python2.7/site-packages/ansible
  executable location = /home/toversus/.local/bin/ansible
  python version = 2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609]

确认与主机操作系统的连接

在主机操作系统上执行Ansible官方提供的用于Windows环境的设置脚本examples/scripts/ConfigureRemotingForAnsible.ps1。

PS > Invoke-WebRequest `
-Uri https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1 `
-OutFile ConfigureRemotingForAnsible.ps1
PS > powershell -ExecutionPolicy RemoteSigned .\ConfigureRemotingForAnsible.ps1 -EnableCredSSP

创建一个清单文件,并写下连接目标机器的信息。

[windows]
localhost

[windows:vars]
ansible_user=<your_account>
ansible_password=<your_password>
ansible_port=5986
ansible_connection=winrm
ansible_winrm_server_cert_validation=ignore

使用win_ping模块对托管操作系统进行连通性检查。

$ ansible windows -m win_ping -i development
localhost | SUCCESS => {
    "changed": false,
    "failed": false,
    "ping": "pong"
}

成功连接到主操作系统,没有任何问题。

如果遇到SSL相关的错误…

localhost | UNREACHABLE! => {
    "changed": false,
    "msg": "ssl: HTTPSConnectionPool(host='localhost', port=5986): Max retries exceeded with url: /wsman (Caused by SSLError(SSLError(\"bad handshake: SysCallError(-1, 'Unexpected EOF')\",),))",
    "unreachable": true
}

使用”ConfigureRemotingForAnsible.ps1″脚本时,传递”-ForceNewSSLCert”选项,重新生成自签名证书。

PS > powershell -ExecutionPolicy RemoteSigned .\ConfigureRemotingForAnsible.ps1 -ForceNewSSLCert

参考:创建一个Windows主机

开发环境的准备

既然如此,我們可以試試用WSL上的Ansible來搭建宿主操作系統作為開發環境。

作業目錄的結構

我参考了Ansible最佳实践中提到的目录结构。

$ tree
.
├── ansible.cfg
├── dev4win.yml
├── development
└── roles
   └── dev4win
       ├── handlers
       │   └── main.yml
       └── tasks
           └── main.yml

4 directories, 5 files

创建ansible.cfg文件

由于每次执行 Playbook 失败时都会创建 .retry 文件,这让人感到厌烦,因此我们将设置为不创建 .retry 文件。同时,还会设置保存 Ansible 执行日志。

[defaults]
retry_files_enabled = False
log_path=/var/tmp/ansible.log

处理程序的定义

當特定任務被執行並且狀態發生變化(返回了”changed”),我們可以定義一個以此為觸發條件的任務。例如,如果只想在執行某個任務時重新啟動機器,可以使用這個方法。關於其他 handlers 的特點,可參考《Ansible:了解notify和handlers的用法》一文中有詳細介紹。

- name: reboot a machine
  win_reboot:
    reboot_timeout_sec: 6000
    test_command: whoami

- name: restart Explorer process
  win_shell: Stop-Process -Name Explorer

任务的定义

任务定义的内容如下:

    • システムロケールの変更

 

    • ログインユーザーのパスワード無期限化

 

    • 「自動的に現在のフォルダーまで展開する」を有効化

 

    • 「ファイル名拡張子の表示」を有効化

 

    • ファイアウォールの受信ルールで ping を受け付けるよう設定

 

    • chocolatey 経由でパッケージをインストール

chocolatey で入手したいパッケージはここで探せます。

- name: Get current system locale
  win_shell: (Get-WinSystemLocale).Name
  register: locale
  changed_when: false

- name: Set system locale to ja-JP
  win_shell: Set-WinSystemLocale ja-JP
  when: locale.stdout.find('ja-JP') == -1
  notify: reboot a machine

- name: Set individual user's password to never expire
  win_user:
    name: "{{ ansible_env.USERNAME }}"
    password_expired: no

- name: Enable automatically expand to current folder option
  win_regedit:
    path: HKCU:\\Software\\Microsoft/Windows\\CurrentVersion\\Explorer\\Advanced
    name: NavPaneExpandToCurrentFolder
    data: 1
    type: dword
  notify: restart Explorer process

- name: Show file name extentions
  win_regedit:
    path: HKCU:\\Software\\Microsoft/Windows\\CurrentVersion\\Explorer\\Advanced
    name: HideFileExt
    data: 0
    type: dword
  notify: restart Explorer process

- name: Enable firewall rule for receiving echo request
  win_firewall_rule:
    name: Virtual Machine Monitoring (Echo Request - ICMPv4-In)
    direction: in
    action: allow

- name: Install latest packages via chocolatey
  win_chocolatey:
    name: "{{ item }}"
    state: latest
  with_items:
    - chocolatey-core.extension
    - 7zip.install
    - visualstudiocode
    - sourcetree
    - teraterm
    - git

- name: Install fixed verion of packages via chocolatey
  win_chocolatey:
    name: "{{ item.name }}"
    version: "{{ item.version}}"
    state: present
  with_items:
    - name: virtualbox
      version: 5.1.30
    - name: vagrant
      version: 2.0.1
    - name: nodejs.install
      version: 6.12.1
    - name: nvm
      version: 1.1.5
    - name: googlechrome
      version: 62.0.3202.94
    - name: selenium-chrome-driver
      version: 2.33

创建Playbook

创建Playbook,并导入刚刚定义的任务。

- hosts: windows
  gather_facts: yes
  tasks:
    - import_tasks: roles/dev4win/tasks/main.yml
  handlers:
    - import_tasks: roles/dev4win/handlers/main.yml

执行Playbook

在执行完所有任务后,将执行在handlers中定义的主机操作系统的重新启动。

$ ansible-playbook dev4win.yml -i development

PLAY [windows] *************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [localhost]

TASK [Get current system locale] ***************************************************************************************
ok: [localhost]

TASK [Set system locale to ja-JP] **************************************************************************************
changed: [localhost]

TASK [Set individual user's password to never expire] ******************************************************************
ok: [localhost]

TASK [Enable automatically expand to current folder option] ************************************************************
changed: [localhost]

TASK [Show file name extentions] ***************************************************************************************
changed: [localhost]

TASK [Enable firewall rule for receiving echo request] *****************************************************************
changed: [localhost]

TASK [Install latest packages via chocolatey] **************************************************************************
changed: [localhost] => (item=chocolatey-core.extension)
changed: [localhost] => (item=7zip.install)
changed: [localhost] => (item=visualstudiocode)
changed: [localhost] => (item=sourcetree)
changed: [localhost] => (item=teraterm)
changed: [localhost] => (item=git)
 [WARNING]: Chocolatey was missing from this system, so it was installed during this task run.


TASK [Install fixed verion of packages via chocolatey] **************************************************************************
changed: [localhost] => (item={u'version': u'5.1.30', u'name': u'virtualbox'})
changed: [localhost] => (item={u'version': u'2.0.1', u'name': u'vagrant'})
changed: [localhost] => (item={u'version': u'6.12.1', u'name': u'nodejs.install'})
changed: [localhost] => (item={u'version': u'1.1.5', u'name': u'nvm'})
changed: [localhost] => (item={u'version': u'62.0.3202.94', u'name': u'googlechrome'})
changed: [localhost] => (item={u'version': u'2.33', u'name': u'selenium-chrome-driver'})

定义额外任务

我将尝试定义以下附加任务:
– 安装GO
– 创建GO的工作文件夹
– 将GOPATH添加到用户环境变量中

因为从头开始执行所有任务很烦琐,所以我们将设置标签,只执行附加任务。

- name: Install Golang v1.9.2 via chocolatey
  win_chocolatey:
    name: golang
    version: 1.9.2
    state: present
  tags: golang

- name: Create Go workplace
  win_file:
    path: "{{ ansible_env.USERPROFILE }}/go"
    state: directory
  tags: golang

- name: Set GOPATH environment variable for current user
  win_path:
    name: GOPATH
    elements: "{{ ansible_env.USERPROFILE }}/go"
    scope: user
    state: present
  tags: golang

执行额外任务

只需通过指定 tags 选项,就可以执行特定的任务。

$ ansible-playbook dev4win.yml -i development --tags golang

PLAY [windows] **************************************************************************************

TASK [Gathering Facts] ******************************************************************************
ok: [localhost]

TASK [Install Golang v1.9.2 via chocolatey] *********************************************************
changed: [localhost]

TASK [Create Go workplace] **************************************************************************
changed: [localhost]

TASK [Set GOPATH environment variable for current user] *********************************************
changed: [localhost]

PLAY RECAP ******************************************************************************************
localhost                  : ok=4    changed=3    unreachable=0    failed=0

以上是Windows 10开发环境的准备工作完成。

在VirtualBox中搭建Windows Server 2016评估版。

我使用了“gusztavvargadr/w16s Vagrant box”作为基础的 Vagrant box。

以下是创建的Vagrantfile内容:”config.winrm.retry_limit”和”config.winrm.retry_delay”是为了解决虚拟机启动超时频繁而添加的。

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "gusztavvargadr/w16s"
  config.vm.network "private_network", ip: "192.168.56.10"
  config.winrm.retry_limit = 30
  config.winrm.retry_delay = 20
  config.vm.provider "virtualbox" do |vb|
    vb.gui = true
    vb.name = "win2016-test01"
    vb.memory = "2048"
    vb.cpus = 1
    vb.customize [ "modifyvm", :id, "--clipboard", "bidirectional", "--draganddrop", "bidirectional" ]
  end
end
    Windows Server 2016 評価版 box を起動します。
PS> vagrant up

在宿主操作系统中设置WinRM

因为与主机操作系统相同,所以我省略了。

确认操作

将连接信息添加到hosts文件中。

[winservers]
192.168.56.10

[winservers:vars]
ansible_user=vagrant
ansible_password=vagrant
ansible_port=5986
ansible_connection=winrm
ansible_winrm_server_cert_validation=ignore
ansible_winrm_transport=credssp

通过 win_ping 模块进行连通性测试。

$ ansible winservers -m win_ping -i staging
192.168.56.10 | SUCCESS => {
    "changed": false,
    "failed": false,
    "ping": "pong"
}

使用 win_dsc 模块

从这里开始进入正题。
本次我们将操作 xCertificate 资源,尝试将 .pfx 文件证书导入证书存储。

作業目錄的組成

$ tree
.
├── ansible.cfg
├── dev4win.yml
├── development
├── roles
│   ├── common
│   │   ├── certs
│   │   │   └── myserver.pfx
│   │   └── tasks
│   │       └── main.yml
│   └── dev4win
│       ├── handlers
│       │   └── main.yml
│       └── tasks
│           └── main.yml
├── staging
└── winservers.yml

7 directories, 9 files

任务的定义

您可以在此处查看有关 xCertificate 资源所需的参数。可以看到,在证书导入时需要使用 PSCredential 传递凭据。在处理 PSCredential 时,需要注意在Ansible中处理。为了防止将凭据信息记录在日志中,需要将no_log:true 添加到定义了该任务的中。


- name: install xCertificate DSC resource
  win_psmodule:
    name: xCertificate
    state: present

- name: Copy certs file to tmp directory
  win_copy:
    src: roles/common/certs/myserver.pfx
    dest: C:/tmp/

- name: import pfx certificate
  win_dsc:
    resource_name: xPfxImport
    Thumbprint: 8b952114b7774787a2d7c7492eb8ca15473cfffd
    Path: C:/tmp/myserver.pfx
    Location: LocalMachine
    Store: My
    Credential_username: vagrant
    Credential_password: XXXXXXXXXX
  no_log: true

创建Playbook

我們將創建一個導入先前定義的任務的Playbook。

- hosts: staging
  gather_facts: no
  tasks:
    - import_tasks: roles/common/tasks/main.yml

执行Playbook

$ ansible-playbook winservers.yml -i staging

PLAY [winservers] *********************************************************************************************************

TASK [install xCertificate DSC resource] *******************************************************************************
changed: [192.168.56.10]

TASK [Copy certs file to tmp directory] ********************************************************************************
changed: [192.168.56.10]

TASK [import pfx certificate] ******************************************************************************************
changed: [192.168.56.10]

PLAY RECAP *************************************************************************************************************
192.168.56.10              : ok=0    changed=3    unreachable=0    failed=0

太好了!截止到2017年12月13日,Ansible的Windows模块中没有处理证书的模块,因此需要结合PowerShell的命令来解决,并且不需要考虑如何保证幂等性(对于证书而言,只需要在证书存储中进行Test-Path,如果不存在则放入即可),这样会更轻松。然而,由于xCertificate是一个试验性的资源,因此即使出现问题,自负责任的部分仍然不会改变。

由于Powershell Gallery上还有许多其他DSC资源可以公开,所以也许去搜索一下是个好主意。(顺便说一句,我没有找到其他可用的资源。)

总结一下

我在WSL上使用Ansible搭建了Windows开发环境,并尝试了win_dsc模块。个人觉得win_dsc模块中仅限于DSC资源的魅力有点少,希望将来能有更多的更新。WSL本身功能正在不断扩展,也许从下一个Windows 10主要更新版本开始,还会支持后台任务。我有一种相当认真的预感,即在Windows上进行开发不再被嘲笑,一个不远的时代即将来临。

注意:本文内容属于个人观点,不代表所属公司或组织的立场。

广告
将在 10 秒后关闭
bannerAds