痛点
- 在
Windows
中,Docker
只能安装在虚拟机或者其他服务器上,如果要传输文件,只能构建镜像,或者借助共享文件夹 - 在调试期间,经常修改,在
Docker
中遗留大量的镜像,事后清理的时候,需要回忆这个镜像是否会用到 Windows
共享文件夹的方式不便于移植,即使使用脚本创建共享文件夹也需要手工操作,而且需要管理员权限- 很多时候只是传输配置文件,构建镜像显得太重了,共享文件夹就更重了,而且造成大量细碎的镜像
- 尽管可以在
docker-compose.yml
文件中运行脚本,不过需要处理转义,而且排版也不方便,传输脚本上去不会有转义问题,例如$
美元符号在docker-compose.yml中会被识别为变量,同时单独写的脚本文件也适合阅读,只需要在镜像中调试成功后,既可以使用本文介绍的方式执行
向Docker共享文件夹的另一个轮子请访问
https://github.com/huzhenghui/share-windows-folder-to-docker
开包即用,不用看代码
下载项目
git clone https://github.com/huzhenghui/transfer-file-to-docker-by-env-file
代码结构
│ generate-env-file.ps1
│ LICENSE
│
├─sample
│ transfer-file-to-docker-by-env-file.env
│ transfer-file-to-docker-by-env-file.yml
│
└─source
sample1.txt
sample2.txt
sample3.txt
其中generate-env-file.ps1
是脚本,source
是示例源文件,sample
是示例生成文件。
生成示例文件
在PowerShell
中进入项目文件夹,运行命令
.\generate-env-file.ps1 -sample
输出如下所示
extract script:
k=1
while true; do
eval c=\$_T_${k}_C
if [ -z $c ]; then
exit;
fi
eval n=\$_T_${k}_N
echo $c | base64 -d > /dev/shm/$n;
k=`expr $k + 1`
done;
File Number : 1
File Name : sample1.txt
File Number : 2
File Name : sample2.txt
File Number : 3
File Name : sample3.txt
env-file : C:\Users\huzh\OneDrive\Docker\transfer-file-to-docker-by-env-file\transfer-file-to-docker-by-env-file.env
Sample Docker Compose File : C:\Users\huzh\OneDrive\Docker\transfer-file-to-docker-by-env-file\transfer-file-to-docker-by-env-file.yml
Usage : docker-compose -f C:\Users\huzh\OneDrive\Docker\transfer-file-to-docker-by-env-file\transfer-file-to-docker-by-env-file.yml up
其中extract script
是用于解析环境变量到文件的脚本,这个脚本预制在generate-env-file.ps1
文件中,这个脚本本身也是借助环境变量传输到Docker
执行。
接下来显示在source
文件夹中搜索到的三个文件。
extract script
和source
都生成在下面的env-file
文件中。
本例中使用-sample
参数,同时生成了Docker Compose
示例文件,路径在Sample Docker Compose File
中。
运行示例文件
Usage
为示例文件的使用方式,运行
docker-compose -f C:\Users\huzh\OneDrive\Docker\transfer-file-to-docker-by-env-file\transfer-file-to-docker-by-env-file.yml up
可以看到输出结果为
Starting transferfiletodockerbyenvfile_sample_1 ... done
Attaching to transferfiletodockerbyenvfile_sample_1
sample_1 | export HOME='/root'
sample_1 | export HOSTNAME='ef5ce5fc2149'
sample_1 | export PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
sample_1 | export PWD='/'
sample_1 | export SHLVL='1'
sample_1 | export _T_0_C='az0xCndoaWxlIHRydWU7IGRvCiAgZXZhbCBjPVwkX1RfJHtrfV9DCiAgaWYgWyAteiAkYyBdOyB0aGVuCiAgICBleGl0OwogIGZpCiAgZXZhbCBuPVwkX1RfJHtrfV9OCiAgZWNobyAkYyB8IGJhc2U2NCAtZCA+IC9kZXYvc2htLyRuOwogIGs9YGV4cHIgJGsgKyAxYApkb25lOw=='
sample_1 | export _T_1_C='c2FtcGxlMQo='
sample_1 | export _T_1_N='sample1.txt'
sample_1 | export _T_2_C='c2FtcGxlMg0Kc2FtcGxlMg0K'
sample_1 | export _T_2_N='sample2.txt'
sample_1 | export _T_3_C='c2FtcGxlMw0Kc2FtcGxlMw0Kc2FtcGxlMw0K'
sample_1 | export _T_3_N='sample3.txt'
sample_1 | total 12
sample_1 | drwxrwxrwt 2 root root 100 Mar 14 16:59 .
sample_1 | drwxr-xr-x 5 root root 340 Mar 14 16:59 ..
sample_1 | -rw-r--r-- 1 root root 8 Mar 14 16:59 sample1.txt
sample_1 | -rw-r--r-- 1 root root 18 Mar 14 16:59 sample2.txt
sample_1 | -rw-r--r-- 1 root root 27 Mar 14 16:59 sample3.txt
transferfiletodockerbyenvfile_sample_1 exited with code 0
可以看到前述的extract script
和source
都通过环境变量传输到了docker
中,下面列出了传输后的文件。
示例文件解析
查看示例文件可以看到
version: "3"
services:
sample:
image: alpine
env_file:
- transfer-file-to-docker-by-env-file.env
entrypoint: |
/bin/sh
-c
"export;
echo $$_T_0_C | base64 -d | /bin/sh;
ls -al /dev/shm"
其中env_file
为引用环境变量
env_file:
- transfer-file-to-docker-by-env-file.env
entrypoint
中为示例脚本,export
为输出环境变量,仅用于演示和调试,实际项目不需要。
在实际项目中需要使用如下命令
echo $$_T_0_C | base64 -d | /bin/sh
也就是还原保存在环境变量_T_0_C
中的脚本,并运行。阅读这行命令会发现使用了两个美元符号$$
,
这是因为美元符号$
即是Shell
脚本中的变量前缀,也是Docker Compose
文件中的变量前缀,
为了避免Docker Compose
转义,就需要使用两个美元符号$$
,这也是制作这个轮子的初衷之一。
最后一行是显示生成的文件,仅用于演示和调试,实际项目不需要。
ls -al /dev/shm
本例中为了速度以及不影响后续的容器操作,把文件直接放在内存文件系统中,
考虑到使用场景主要是小文件,内存文件系统足够用了。
参数说明
-source,指定源文件路径
如果不指定的话,自动为当前路径下的source
路径
-target,指定目标文件路径
如果不指定的话,自动为当前路径下的transfer-file-to-docker-by-env-file.env
路径
-sample,是否生成示例Docker Compose
文件
如果不设置这个参数,则仅生成.env
文件,用于使用脚本自动生成。
源代码解析
源代码很简单,简单说明一下
声明参数
Param(
[string]$source,
[string]$target,
[switch]$sample
)
处理source
参数,源文件路径
if ([string]::IsNullOrEmpty($source))
{
$source = -Join((Get-Location).Path, '\source')
}
$sourcePath = (Resolve-Path $source).Path
if ([string]::IsNullOrEmpty($sourcePath))
{
echo "source is not exists: $source"
exit
}
处理target
参数,目标文件路径
if ([string]::IsNullOrEmpty($target))
{
$target = -Join((Get-Location).Path, '\transfer-file-to-docker-by-env-file.env')
}
内置的解析脚本
$extract=@'
k=1
while true; do
eval c=\$_T_${k}_C
if [ -z $c ]; then
exit;
fi
eval n=\$_T_${k}_N
echo $c | base64 -d > /dev/shm/$n;
k=`expr $k + 1`
done;
'@
Write-Host 'extract script:'
Write-Host ''
Write-Host $extract
解析脚本不需要转义,因此使用单引号'
。
输出解析脚本到环境变量文件
Write-Output _T_0_C=$([Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($extract.Replace("`r`n", "`n")))) |
Out-File $target -Encoding UTF8
先替换解析脚本中的换行符,从CRLF
替换为LF
,然后转换为字节数组,接下来使用base64
转码,输出到环境变量文件中。
输出源文件到环境变量中
$k=1
Get-ChildItem $sourcePath -File |
ForEach-Object -Process{
Write-Host ''
Write-Host 'File Number : ' $k
Write-Host 'File Name : ' $_.Name
Write-Output _T_${k}_N=$_
Write-Output _T_${k}_C=$([Convert]::ToBase64String([System.IO.File]::ReadAllBytes($_.FullName)))
$k=$k+1
} |
Out-File $target -Append -Encoding UTF8
Write-Host ''
Write-Host 'env-file : ' $target
遍历源文件路径,把源文件名和源文件内容分别保存在环境变量中,源文件内容按照字节数组读取,然后使用base64
转码。
显示示例Docker Compose
文件
if($sample.isPresent)
{
$sampleCompose=@"
version: `"3`"
services:
sample:
image: alpine
env_file:
- $(([system.io.fileinfo]$target).Name)
entrypoint: |
/bin/sh
-c
`"export;
echo `$`$_T_0_C | base64 -d | /bin/sh;
ls -al /dev/shm`"
"@
Write-Host ''
$sampleYmlFile = -Join(([system.io.fileinfo]$target).DirectoryName, '\', ([system.io.fileinfo]$target).BaseName, '.yml')
Write-Output $sampleCompose | Out-File $sampleYmlFile -Encoding UTF8
Write-Host 'Sample Docker Compose File :' $sampleYmlFile
Write-Host ''
Write-Host "Usage : docker-compose -f $sampleYmlFile up"
}
仅在设置-sample
参数时执行,由于示例文件中需要指定环境变量文件的路径,因此需要使用双引号"
,因而其中的双引号和美元符号$
都需要转义。