1. ホーム
  2. Windows

DockerでWindows 10からVolumeボリュームにフォルダを共有する

2022-03-21 22:48:21





機能は以前とあまり変わらず、構造が大きく変わり、スクリプトは使いやすいようにパラメータ化されています。



また、このスクリプトは、あるWindows 10から別のWindows 10上のDocker仮想マシンのDockerへのフォルダ共有を制限するものではありません。



また、あるWindows 10から別のMac OXS上のDocker仮想マシン内のDockerにフォルダを共有することもできます。



または、あるWindows 10から別のLinux上で動作するDockerにフォルダを共有します。



最後のシナリオがこの記事の核心です。



仮想マシンを強調する理由は、Windows 10の自動フォルダ共有の助けを借りて、Dockerの実行中に変化した状態をそのフォルダに保持し、実行後はDockerプロセスもそれが常駐する仮想マシンも元の状態に戻る、つまりDockerを状態自体を保持しない関数呼び出しとして扱うためです。

## Automatically share a Windows folder to a Docker host
## git clone https://github.com/huzhenghui/share-windows-folder-to-docker
## share-windows-folder-to-docker/share-windows-folder-to-docker.ps1
## Docker for Windows uses Hyper-V, which doesn't include a device-driven file share, so it can only use Windows' own SMB file share, also known as CIFS
## Docker for Windows clients can use the GUI to set up file shares, but the technical implementation is also SMB, and the automatically created file shares are open to the LAN, so there are security risks
## This script is used for Docker hosts that can be accessed using the Docker-Machine command
## Can be used to share files in a Docker virtual machine created with the Docker-Machine command on the local Hyper-V, i.e. Host Windows - Guest Linux - Docker
## Can also be used for other Docker hosts on the same LAN that can be managed locally using the Docker-Machine command, i.e. Windows - Intranet - Linux - Docker
## This script assumes that the Docker-Machine command can query at least one Docker host, and if the Docker host is installed in Hyper-V of the current Windows, then it can use the virtual switch to ensure that it is on the same LAN.
## The command to create a virtual machine using Hyper-V's virtual switch method is similar to: docker-machine create -d hyperv --hyperv-virtual-switch "myswitch" myvm1
# This script automatically creates users for sharing, automatically creates shared folders, automatically loads in the Docker host, and generates commands for testing when run
# The passwords in this script are not only randomly generated, but they are not stored in any persistent storage. However, it is still recommended not to run this script in the Windows system folder to avoid security risks caused by shared system folders.
# Disclaimer: This script involves many technologies, including PowerShell, Unix Shell, WMI, Docker, and needs to be run in the administrator role, which may lead to unpredictable results in different environments, and is for learning purposes only.
# Create a folder with an English name, create a .ps1 file, copy the contents of this script into it, then run the PowerShell Administrator Console, enter the folder, and run the script.
## Parameters
## -workingDir The folder to be shared, or the current folder if it is not provided, or is not a folder
## -machineName The name of the Docker host that should be in the docker-machine ls list, or the first host in the docker-machine ls list that is running if it is not provided or does not exist or is not running
## -volumeName The name of the volume shared to the Docker host, or automatically generated if not provided
## -sharePath The name of the shared folder, or automatically if not provided, which is automatically passed to the Docker host and is not involved in the use of the parameter
## -userName The user of the shared folder, if not provided, is automatically generated, and the parameter is automatically passed to the Docker host and is not involved when using it
## -password The password for the user of the shared folder, or automatically generated if not provided, is automatically passed to the Docker host and is not involved when used
## -replaceVolume replaces the existing settings when the Docker host contains a volume with the same name, otherwise exit
Param(
    [string]$workingDir,
    [string]$machineName,
    [string]$volumeName,
    [string]$sharePath,
    [string]$userName,
    [string]$password,
    [switch]$replaceVolume
)
## Constants
## safe_characters
$safe_charactors = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
# Parameters
## workingDir
### Determine the folder to be shared, if not provided, or not a folder, then use the current folder
if ([string]::IsNullOrEmpty($workingDir))
{
    $workingDir = (Get-Location).Path
}
elseif (-Not ([io.Directory]::Exists($workingDir)))
{
    $workingDir = (Get-Location).Path
}
echo "Working Directory : " $workingDir
### Get the last paragraph of the path, that is, the folder name, because the folder name will be used many times later, to avoid problems with the character set is recommended to use plain English, or manually set
$directory_name = [System.IO.Path]::GetFileName($workingDir)
echo "Directory Name : " $directory_name
### To avoid duplicate folder names, distinguish them by calculating the hash of the path
### Convert the string into a stream in memory to calculate the hash value, from here you can also see the reasonableness of the PowerShell implementation, because the string in PowerShell is a character, and the hash value needs to be calculated by bytes, so it needs to be converted by character set, here using MD5 algorithm
$directory_hash = Get-FileHash -Algorithm MD5 -InputStream ([System.IO.MemoryStream]::new((new-object -TypeName System.Text.UTF8Encoding). GetBytes($workingDir))) | Select-Object -ExpandProperty Hash
echo "Directory Hash : " $directory_hash
### To avoid non-ASCII characters in the path, filter on the folder name
$directory_filter_name = ""
foreach($c in $directory_name.ToCharArray()) {
if ($safe_charactors.Contains($c)) {
$directory_filter_name += $c}}
echo "Directory Filter Name : " $directory_filter_name
## volumeName
### Determine if the volume parameter is set
if([string]::IsNullOrEmpty($volumeName))
{
    ### The name of the volume, distinguished using a prefix and a hash
    $volumeName = -Join('Share_for_Docker_', $directory_filter_name, '_', $directory_hash)
}
## sharePath
### Determine if the share path parameter is set
if([string]::IsNullOrEmpty($sharePath))
{
    ### Share folder name, use prefix and hash to distinguish
    $sharePath = -Join('Share_for_Docker_', $directory_filter_name, '_', $directory_hash)
}
echo "Share Path : " $sharePath
## userName
### Determine if the username parameter is set
if([string]::IsNullOrEmpty($userName))
{
    ### Add a Docker prefix to the user name used for sharing for future management
    $userName = -Join('Docker_', $directory_filter_name)
    ### Since Windows user names are up to 20 characters long, truncate by 13 characters for the rest of the hash
    if ($userName.Length -gt 13) {
$userName = $userName.Substring(0, 13)}
    ### Add a hash to the username as a suffix
    $userName = -Join($userName, '_', $directory_hash)
    ### Since Windows user names are up to 20 characters long, truncate by 20 characters
    if ($userName.Length -gt 20) {

$userName = $userName.Substring(0, 20)}
}
echo "User Name :" $userName
## password
### Determine if the password parameter is set
if([string]::IsNullOrEmpty($password))
{
    ### Password is automatically generated using random
    for($password = $Null ; $password.length -le 32; $password += "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ". ToCharArray() | Get-Random){}
}
echo "Password Generated"
# Determine the parameters
## machineName
### Determine if the machineName parameter is set
if(-not [string]::IsNullOrEmpty($machineName))
{
    ### Determine if there is a running Docker host with the name machineName
    if((docker-machine ls --filter 'STATE=Running' --filter "Name=${machineName}" --format '{{.Name}}') -eq $Null)
    {
        ### If not present, machineName parameter is invalid, set to null
        $machineName = $Null
    }
}
### Determine if the machineName parameter is set
if([string]::IsNullOrEmpty($machineName))
{
    $machineName = $(docker-machine ls --filter 'STATE=Running' --format "{{.Name}}" | Select-Object -First 1)
}
### Determine if the machineName parameter is set
if([string]::IsNullOrEmpty($machineName))
{
    echo 'machineName invalid and/or no running docker machine'
    exit
}
### docker environment variables
### Use the docker-machine command to generate environment variables and run them for subsequent docker commands, note that they are transcoded for Chinese
foreach ($line in (& "C:\Program Files\Docker\Docker\Resources\bin\docker-machine.exe" env $machineName))
{
[System.Text.Encoding]::GetEncoding("utf-8").GetString([System.Text.Encoding]::GetEncoding("gb2312").GetBytes( $line)) | Invoke-Expression
}
## volumeName
### Determine if a volume with the name volumeName exists
if((docker volume ls --filter "Name=${volumeName}" --format '{{.Name}}') -ne $Null)
{
    echo 'Volume Name exists : ' $volumeName
    if($replaceVolume.IsPresent)
    {
        echo 'Remove Existence volume'
        docker volume rm --force ${volumeName}
        echo 'Existence volume removed'
    }
    else
    {
        exit
    }
}
## sharePath
### Determine if the share path exists
##[WMICLASS]"WIN32_Share"
if((Get-WmiObject -Class Win32_Share -Filter "name = '$sharePath'") -ne $Null)
{
    ### Exit when share path exists, handled by manual
    echo "Share path exist : " $sharePath
    exit
}
## userName
### Determine if user name exists
if((Get-WmiObject -Class Win32_UserAccount -Filter "name = '${userName}'") -ne $Null)
{
    ### Quit when user exists, handled by human
    echo "User Exist : " $userName
    exit
}
### Create user
$winnt_localhost = [ADSI]"WinNT://localhost"
## Create user object
$new_user = $winnt_localhost.create("user", $userName)
## Set password
$new_user.setpassword($password)
## Set the information, the user will be created only after setting the information, so it should be called without parameters
$new_user.setinfo()
## Set the password for this account to never expire
Get-WmiObject -Class Win32_UserAccount -Filter "name = '${username}'" | Set-WmiInstance -Argument @{PasswordExpires = 0}
# Folder permissions
## Create an access rule
$access_rule = New-Object System.Security.AccessControl.FileSystemAccessRule($userName, "FullControl", "ContainerInherit "ObjectInherit", "None", "Allow")
## Get the access control list for the current path
$acl = Get-Acl $workingDir
## Add access rules to the access control list
$acl.SetAccessRule($access_rule)
## Set the access control list in the current path
Set-Acl -Path $workingDir -AclObject $acl
## Share a folder
## Create a trusted user object
$Trustee = ([wmiclass]'Win32_trustee').psbase.CreateInstance()
## Set the name of the trusted user
$Trustee.Name = $userName
## Set the domain of the trusted user, the local domain is empty
$Trustee.Domain = $Null
## Create an access control object
$ACE = ([WMIClass] "Win32_ACE").psbase.CreateInstance()
## Access control mask, Magic Number, please search for the specific meaning
$ACE.AccessMask = 2032127
## Access control flags
$ACE.AceFlags = 3
## Type of access control
$ACE.AceType = 0
## Trusted user for access control
$ACE.Trustee = $Trustee
## Create a security description object
$sd = ([WMIClass] "Win32_SecurityDescriptor").psbase.CreateInstance()
## Flags for security descriptions
$sd.ControlFlags = 4
## Access control for security descriptions
$sd.DACL = $ACE
## Trusted user groups for security descriptions
$sd.group = $Trustee
## Trusted users for security descriptions
$sd.owner = $Trustee
## Get the shared object
$Share = [WMICLASS]"WIN32_Share"
## Create a shared folder for the current folder
$Share.Create($workingDir, $sharePath, 0, $Null, "Share for Docker", "", $sd)
## Run a script in the virtual machine to get the IP address of Host Windows
## here welcome brainstorming: this method of getting IP is not very elegant and rather hack. first get tty, then cut off the front /dev/, use the w command on the VM, filter by tty, and finally output the third field
$local_ip = $(docker-machine ssh $machineName 'tty=$(tty | cut -c 6-); w -i | grep $tty | awk ''{print $3;}''')
echo "Local IP : " $local_ip
# Load the shared folder
docker volume create --driver local --opt type=cifs --opt device=//${local_ip}/${sharePath} --opt o=username=${userName},password=${password } --name ${volumeName}
#demo command
echo 'demo:'
## This command creates a random file in the shared folder
echo "docker run --rm -v ${volumeName}:/share alpine touch (get-date -Format '/\s\h\are/yyyyy-MM-dd-HH-mm-ss-fffffffff.\tx\t')"
## This command lists the shared folders and can look up the files you just created
echo "docker run --rm -v ${volumeName}:/share alpine ls /share"
echo ""
echo "WARNING : '. . input/output error' is a known error. This may occur with mobile storage devices"