如何使用 kubectl cp 命令覆盖目录

4

我正在使用 kubectl cp 命令将本地目录复制到 Kubernetes pod 中。

kubectl cp test $POD:/tmp

它将test目录复制到Kubernetes pod的/tmp目录中。

现在我想要覆盖pod中的test目录。 我没有找到任何使用kubectl cp命令进行复制时覆盖目录的选项。

目前,我正在从pod中删除test目录,然后再复制该目录。

kubectl exec $POD -- sh -c 'rm -rf /tmp/test'
kubectl cp test $POD:/tmp

目前这个方法可以正常工作,但是如果在复制过程中出现任何错误,那么pod中的现有目录也将被删除。

我该如何在不先删除pod目录的情况下用本地目录覆盖pod目录?

提前感谢您的帮助。


1
记住,只要删除了Pod,所有这些工作都会被撤销;如果节点故障或您使用HorizontalPodAutoscaler,这可能发生在您的控制之外。最好找到一种方法,在不依赖于kubectl cp这样的命令的情况下实现您的最终目标,无论是更新镜像还是将持久存储挂载到Pod中。 - David Maze
1
我正在复制/tmp/test目录中的数据。并且EFS已经挂载在这个测试目录上。因此,当Pod终止时,数据不会被删除。 - Kishore
3个回答

10

目前很不幸地,使用kubectl cp命令无法实现您所需的状态。

如果有一些未记录的功能,请随时编辑此答案并提供解决方案,但目前文档中没有任何地方能够建议相反的操作。

无论是在这里还是在运行kubectl cp --help命令后获得的上下文帮助中,都没有提到可以修改kubectl cp命令默认操作的选项,该命令基本上是将已经存在的目录内容与复制的目录内容合并。

$ kubectl cp --help

Copy files and directories to and from containers.
    
Examples:
  # !!!Important Note!!!
  # Requires that the 'tar' binary is present in your container
  # image.  If 'tar' is not present, 'kubectl cp' will fail.
  #
  # For advanced use cases, such as symlinks, wildcard expansion or
  # file mode preservation, consider using 'kubectl exec'.
  
  # Copy /tmp/foo local file to /tmp/bar in a remote pod in namespace <some-namespace>
  tar cf - /tmp/foo | kubectl exec -i -n <some-namespace> <some-pod> -- tar xf - -C /tmp/bar
  
  # Copy /tmp/foo from a remote pod to /tmp/bar locally
  kubectl exec -n <some-namespace> <some-pod> -- tar cf - /tmp/foo | tar xf - -C /tmp/bar
  
  # Copy /tmp/foo_dir local directory to /tmp/bar_dir in a remote pod in the default namespace
  kubectl cp /tmp/foo_dir <some-pod>:/tmp/bar_dir
  
  # Copy /tmp/foo local file to /tmp/bar in a remote pod in a specific container
  kubectl cp /tmp/foo <some-pod>:/tmp/bar -c <specific-container>
  
  # Copy /tmp/foo local file to /tmp/bar in a remote pod in namespace <some-namespace>
  kubectl cp /tmp/foo <some-namespace>/<some-pod>:/tmp/bar
  
  # Copy /tmp/foo from a remote pod to /tmp/bar locally
  kubectl cp <some-namespace>/<some-pod>:/tmp/foo /tmp/bar

Options:
    -c, --container='':
    Container name. If omitted, use the kubectl.kubernetes.io/default-container annotation for selecting the
    container to be attached or the first container in the pod will be chosen

    --no-preserve=false:
    The copied file/directory's ownership and permissions will not be preserved in the container

    --retries=0:
    Set number of retries to complete a copy operation from a container. Specify 0 to disable or any negative
    value for infinite retrying. The default is 0 (no retry).

Usage:
  kubectl cp <file-spec-src> <file-spec-dest> [options]

Use "kubectl options" for a list of global command-line options (applies to all commands).

kubectl cp 命令的默认行为基本上是合并源目录和目标目录的内容。假设我们有一个包含如下内容的本地目录:/tmp/test

/tmp/test$ ls
different_file.txt

带有单行文本 "some content"。如果我们将本地的 /tmp/test 目录 复制到我们的 Pod 上的 /tmp 目录中,该目录已经包含一个不同文件的 test 文件夹,假设是 testfile.txt,则两个目录的内容将合并,因此我们目标目录 /tmp/test 最终将包含:

/tmp/test# ls
different_file.txt  testfile.txt

如果我们更改本地文件different_file.txt的内容为"另一个内容",并再次运行命令:
kubectl cp /tmp/test pod-name:/tmp

它只会覆盖目标different_file.txt,而该文件已经存在于目标/tmp/test目录中。

目前还没有办法覆盖这个默认行为。


谢谢你的解释。是否可以添加异常处理?例如,当删除失败时,执行其他命令。 - Kishore
1
你可以使用简单的bash脚本来执行第二个命令,只有在第一个命令成功的情况下才会执行,例如:kubectl exec $POD -- sh -c 'rm -rf /tmp/test' && kubectl cp test $POD:/tmp。如果删除失败,kubectl cp将不会被执行。你也可以做相反的操作:kubectl exec $POD -- sh -c 'rm -rf /tmp/test' || echo "directory couldn't be deleted" - 只有在第一个命令失败的情况下才会执行第二个命令。kubectl命令与其他bash命令类似,并具有它们自己的退出状态,因此可以很容易地编写脚本。 - mario
谢谢,真是个很好的解决方法。还有一件事。在test目录下有许多子目录。有时候我会遇到错误 - rm: cannot remove '/tmp/test/__pycache__': Directory not empty。但它会删除其余的所有目录并显示错误退出。而且复制命令也不会执行。这会导致k8s pod test上没有任何数据,甚至连旧数据也没有了。 - Kishore
请参考这个帖子。它可能会有所帮助。问题下的第二条评论很好地解释了可能的情况。 - mario
这是在删除__pycache__时出现了其他进程运行的问题。当我在2秒后重试相同的命令时,它可以正常工作。 - Kishore
显示剩余3条评论

0

非常有可能。我做到了。在 PowerShell 中试试这个:

PS> $items = ls <dir1>

PS> foreach ($item in $items) { kubectl cp <dir1>/$item podname:var/opt/mount/<dir2>/$item }

PS> if ($? -eq $false) { kubectl cp <dir1>podname:var/opt/mount/<dir2>}

这是为了持久卷,因此它将存在直到卷被销毁。

这个逻辑是:

  1. 获取所需目录的所有项目
  2. 尝试写入所需目录的所有项目,如果目标目录不存在,则转到第二个语句。
  3. 第二部分是将所需目录写为目标目录名称
  4. 如果重新运行命令(将所需目录放在目标目录中),而不是再次运行第二部分,您可以将所有项目写入目标目录

-4

不要每次都使用 "kubectl cp" 命令来复制你的目录,而是使用 "volume mounts" 将你的本地目录挂载到你的 Pod 上。


这在集群环境中不会很好地工作,特别是如果节点会根据负载或其他条件动态创建和销毁。 - David Maze
是的,我同意。但他正在尝试使用kubectl cp,这也不是一个好的方法。最终,他需要在Pod中使用卷和挂载。hostPath只是下一行的建议。 - Anmol Agrawal
在大多数集群配置中,如果您想要将工作站的源代码挂载到一个 pod 内部,使用 "volume mounts" 将不起作用。复制代码是一种更通用的选择,比使用 https://www.telepresence.io 更简单,后者可能是更强大的技术。 - Fabrice Jammes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接