您的位置:首页 > 运维架构 > Docker

Docker for Windows 自动共享本机文件的脚本示例

2018-01-17 16:40 537 查看
# Docker for Windows 自动共享本机文件的脚本示例
# Docker for Windows 使用 Hyper-V,而 Hyper-V 自身并没有包含设备驱动方式的文件共享,因此只能使用 Windows 自带的 SMB 文件共享,SMB 也称 CIFS
# Docker for Windows 的客户端可以使用图形界面设置文件共享,不过技术实现方式也是 SMB,而且自动创建的文件共享对于局域网是开发的,未免有安全隐患
# 本脚本用于使用 Docker-Machine 命令在本机 Hyper-V 中创建的虚拟机中的 Docker 共享文件,也就是 Host Windows - Guest Linux - Docker
# 本脚本假设已经使用 Docker-Machine 命令创建了至少一个 Docker 虚拟机,该虚拟机的网络使用 Hyper-V 的虚拟交换机方式
# 使用 Hyper-V 的虚拟交换机方式创建虚拟机的命令类似于:docker-machine create -d hyperv --hyperv-virtual-switch "myswitch" myvm1
# 本脚本运行时自动创建用于共享的用户,自动创建共享文件夹,自动在虚拟机中加载,并生成用于测试的命令
# 本脚本中的密码不仅随机生成,而且密码不保存在任何持久存储。不过仍旧建议不要在系统文件夹中运行本脚本
# 免责声明:本脚本涉及技术较多,包括:PowerShell、Unix Shell、WMI、Docker,而且需要运行在管理员角色,在不同环境可能导致难以预期的结果,仅供学习参考
# 本脚本测试方法:创建一个英文名称的文件夹,其中创建一个 .ps1 文件,把本脚本的内容完整复制进去,然后运行 PowerShell 管理员控制台,进入文件夹,运行脚本
# 环境变量
## 获取当前路径
$pwd=Get-Location
## 获取路径的最后一段,也就是文件夹名,由于后面会多次使用该文件夹名,避免出现字符集问题建议使用纯英文,或者手工设置
$directory_name = [System.IO.Path]::GetFileName($pwd)
## 为了避免文件夹名重复,通过计算路径的散列值区分
## PowerShell 中计算字符串的散列值比较麻烦,不能像 Linux 那样直接用管道,先创建UTF8对象
$utf8 = new-object -TypeName System.Text.UTF8Encoding
## 把字符串转换成内存中的流,从这里也可以看出 PowerShell 实现的合理性,因为 PowerShell 中的字符串是字符,而散列值需要按照字节计算,因此需要按照字符集转换
$pwd_stream = [System.IO.MemoryStream]::new($utf8.GetBytes($pwd))
## 计算散列值,此处使用 MD5 算法
$pwd_hash = Get-FileHash -Algorithm MD5 -InputStream $pwd_stream | Select-Object -ExpandProperty Hash
## 用于共享的用户名增加 Docker 前缀,以便于未来管理
$username = -Join('Docker_', $directory_name)
## 由于 Windows 用户的用户名最长 20 字符,为了后面的散列值,按照 13 个字符截断
if ($username.Length -gt 13) {$username = $username.Substring(0, 13)}
## 用户名增加散列值作为后缀
$username = -Join($username, '_', $pwd_hash)
## 由于 Windows 用户的用户名最长 20 字符,按照 20 个字符截断
if ($username.Length -gt 20) {$username = $username.Substring(0, 20)}
## 密码使用自动生成,一个潜在的风险就是自动生成的密码也许在后面的脚本中可能导致转义字符的混乱,如果遇到了请告诉我,换个算法即可
## 引用密码所需要的包
Add-Type -AssemblyName System.Web
## 自动生成密码
$password = [System.Web.Security.Membership]::GeneratePassword(20,5)
## 共享文件夹名称,使用前缀和散列值区分
$share_name = -Join('Share_for_Docker_', $directory_name, '_', $pwd_hash)
# 创建用户
$winnt_localhost = [ADSI]"WinNT://localhost"
## 创建用户对象
$new_user = $winnt_localhost.create("user", $username)
## 设置密码
$new_user.setpassword($password)
## 设置信息,用户只有设置信息后才会创建,因此没有参数也要调用
$new_user.setinfo()
## 设置这个账户的密码永远不过期
Get-WmiObject -Class Win32_UserAccount -Filter "name = '${username}'" | Set-WmiInstance -Argument @{PasswordExpires = 0}
# 文件夹权限
## 创建一个访问规则
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($username, "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow")
## 获取当前路径的访问控制列表
$acl = Get-Acl $pwd
## 在访问控制列表中添加访问规则
$acl.SetAccessRule($accessRule)
## 在当前路径中设置访问控制列表
Set-Acl -Path $pwd -AclObject $acl
# 共享文件夹
## 创建一个信任用户对象
$Trustee = ([wmiclass]'Win32_trustee').psbase.CreateInstance()
## 设置信任用户的名字
$Trustee.Name = $username
## 设置信任用户的域,本地域为空
$Trustee.Domain = $Null
## 创建一个访问控制对象
$ACE = ([WMIClass] "Win32_ACE").psbase.CreateInstance()
## 访问控制的掩码,Magic Number,具体含义请自行搜索
$ACE.AccessMask = 2032127
## 访问控制的标志
$ACE.AceFlags = 3
## 访问控制的类型
$ACE.AceType = 0
## 访问控制的信任用户
$ACE.Trustee = $Trustee
## 创建一个安全描述对象
$sd = ([WMIClass] "Win32_SecurityDescriptor").psbase.CreateInstance()
## 安全描述的标志
$sd.ControlFlags = 4
## 安全描述的访问控制
$sd.DACL = $ACE
## 安全描述的信任用户组
$sd.group = $Trustee
## 安全描述的信任用户
$sd.owner = $Trustee
## 获得共享对象
$Share = [WMICLASS]"WIN32_Share"
## 为当前文件夹创建共享文件夹
$Share.Create($pwd, $share_name, 0, $Null, "Share for Docker", "", $sd)
# 启动docker machine
## 从本机的 Docker Machine 列表中获取第一台虚拟机的名称,再次强调,本文假设已经使用 Docker Machine 创建了一台虚拟机。本行代码也可以修改为其他的筛选条件
$machine_name = $(docker-machine ls --format "{{.Name}}" | Select-Object -First 1)
## 启动 Docker 虚拟机
docker-machine start $machine_name
## 在虚拟机中运行脚本获取 Host Windows 的IP地址,再次强调,本文假设虚拟机使用虚拟交换机的方式创建
## 此处欢迎集思广益:这种获取IP的方法不是很优雅,比较hack。先获取tty,然后切掉前面的/dev/,由于虚拟机上的w命令没有IP地址,所以使用last,按照tty和登出时间过滤,最后输出第三个字段
$local_ip = $(docker-machine ssh $machine_name 'tty=$(tty | cut -c 6-); last | grep $tty | grep ''still logged in'' | awk ''{print $3;}''')
# 加载共享文件夹
## 在虚拟机中创建用于挂载的共享文件夹,如果已经创建会出错,不过无所谓
docker-machine ssh $machine_name "sudo mkdir /${share_name}"
## 卸载这个路径中已经有的挂载,如果没有挂载会出错,不过无所谓
docker-machine ssh $machine_name "sudo umount /${share_name}"
## 挂载前面在 Windows 中创建共享文件夹,如前所述,这里的password可能有潜在的随机字符导致转义字符混乱的情况,如果发生了告诉我
docker-machine ssh $machine_name "sudo mount -t cifs -o username=${username},password='${password}',uid=0,gid=0 //${local_ip}/${share_name} /${share_name}"
#docker环境变量
## 使用 docker-machine 命令生成环境变量并运行,以便于后续的的 docker 命令直接使用,注意此处为了解决中文问题做了转码,
foreach ($line in (& "C:\Program Files\Docker\Docker\Resources\bin\docker-machine.exe" env $machine_name))
{
[System.Text.Encoding]::GetEncoding("utf-8").GetString([System.Text.Encoding]::GetEncoding("gb2312").GetBytes($line)) |  Invoke-Expression
}
#演示命令
echo 'demo:'
## 这个命令在共享文件夹中随机创建一个文件
echo "docker run --rm -v /${share_name}:/share alpine touch (get-date -Format '/\s\h\are/yyyy-MM-dd-HH-mm-ss-fffffff.\tx\t')"
## 这个命令列出共享文件夹,可以查询到刚才创建的文件
echo "docker run --rm -v /${share_name}:/share alpine ls /share"
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息