Sao chép tập tin từ Docker container vào máy chủ


1709

Tôi đang nghĩ đến việc sử dụng Docker để xây dựng các phụ thuộc của mình trên máy chủ Tích hợp liên tục (CI), do đó tôi không phải cài đặt tất cả thời gian chạy và thư viện trên chính các tác nhân.

Để đạt được điều này, tôi sẽ cần phải sao chép các tạo phẩm xây dựng được xây dựng bên trong container trở lại máy chủ. Điều đó có thể không?


các bạn có thể thích phương pháp hacker của tôi ở đây: stackoverflow.com/a/55876794/990618
colin lamarre

1
Câu trả lời đúng và thực tế từ thuyền trưởng docker ở cuối câu trả lời.
burtsevyg

Câu trả lời:


2950

Để sao chép một tập tin từ một thùng chứa sang máy chủ lưu trữ, bạn có thể sử dụng lệnh

docker cp <containerId>:/file/path/within/container /host/path/target

Đây là một ví dụ:

$ sudo docker cp goofy_roentgen:/out_read.jpg .

Ở đây goofy_roentgen là tên container tôi nhận được từ lệnh sau:

$ sudo docker ps

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                                            NAMES
1b4ad9311e93        bamos/openface      "/bin/bash"         33 minutes ago      Up 33 minutes       0.0.0.0:8000->8000/tcp, 0.0.0.0:9000->9000/tcp   goofy_roentgen

Bạn cũng có thể sử dụng (một phần) ID Container . Lệnh sau tương đương với lệnh đầu tiên

$ sudo docker cp 1b4a:/out_read.jpg .

42
Đây là một cách thuận tiện để lấy tại container mới nhất của bạn nếu bạn chỉ đơn giản sử dụng docker cho môi trường Linux tạm thời : docker ps -alq.
Josh Habdas

37
lệnh cp này cũng hoạt động như là để sao chép cây thư mục (không chỉ là một tệp).
ecoe

88
Trong các phiên bản mới hơn của docker, bạn có thể sao chép hai chiều (máy chủ sang container hoặc container sang máy chủ) vớidocker cp ...
Freedom_Ben

9
Tôi cần docker cp -Lsao chép liên kết tượng trưng
Harrison Powers

24
LƯU Ý: container không phải chạy để sử dụng lệnh cp. Tiện dụng nếu container của bạn liên tục gặp sự cố.
Martlark

219

Bạn không cần sử dụng docker run.

Bạn có thể làm điều đó với docker create.

Từ các tài liệu :

Các docker createlệnh tạo ra một lớp chứa có thể ghi lên ảnh cụ thể và chuẩn bị nó cho chạy lệnh chỉ định. ID container sau đó được in ra STDOUT. Điều này tương tự như docker run -dngoại trừ container không bao giờ được bắt đầu.

Vì vậy, bạn có thể làm:

docker create -ti --name dummy IMAGE_NAME bash
docker cp dummy:/path/to/file /dest/to/file
docker rm -f dummy

Ở đây, bạn không bao giờ bắt đầu container. Điều đó có vẻ có lợi cho tôi.


19
Điều này cần nhiều upvote. Tuyệt vời khi bạn chỉ cần xây dựng một cái gì đó trong một thùng chứa và sau đó sao chép các đầu ra.
Honza Kalfus

4
@HonzaKalfus Tôi đồng ý điều này cần phải cao hơn. Đây chính xác là những gì tôi đã sau. Tôi đã sử dụng điều này để tôi có thể xây dựng một số tệp nhị phân bằng một môi trường đã biết (amazon linux ở một phiên bản cụ thể). đã có thể tạo một tập lệnh shell xây dựng đầy đủ docker và trích xuất nhị phân kết quả từ nó! Hoàn hảo.
Đánh dấu

1
Được -tiyêu cầu và bashbắt buộc?
jII

@jII, tôi đã làm điều đó bởi vì sau này, tôi làm docker chạy trên nó. Trong những trường hợp đơn giản, nó không cần thiết nhưng nó cũng không gây hại ở đây.
Ishan Bhatt

Có cách nào đó có thể sử dụng ký tự đại diện? Ý tôi là ... tôi không biết tên chính xác của tập tin tôi cần sao chép vì nó có số phiên bản trên đó.
juzzlin

87

Gắn một "âm lượng" và sao chép các vật phẩm vào đó:

mkdir artifacts
docker run -i -v ${PWD}/artifacts:/artifacts ubuntu:14.04 sh << COMMANDS
# ... build software here ...
cp <artifact> /artifacts
# ... copy more artifacts into `/artifacts` ...
COMMANDS

Sau đó, khi quá trình xây dựng kết thúc và container không còn chạy nữa, nó đã sao chép các tạo phẩm từ bản dựng vào artifacts thư mục trên máy chủ.

Biên tập

Hãy cẩn thận: Khi bạn thực hiện việc này, bạn có thể gặp sự cố với id người dùng của người dùng docker khớp với id người dùng của người dùng đang chạy hiện tại. Nghĩa là, các tệp trong /artifactssẽ được hiển thị là sở hữu của người dùng với UID của người dùng được sử dụng bên trong thùng chứa docker. Cách này có thể là sử dụng UID của người dùng đang gọi:

docker run -i -v ${PWD}:/working_dir -w /working_dir -u $(id -u) \
    ubuntu:14.04 sh << COMMANDS
# Since $(id -u) owns /working_dir, you should be okay running commands here
# and having them work. Then copy stuff into /working_dir/artifacts .
COMMANDS

7
Trên thực tế, bạn có thể sử dụng chownlệnh để khớp id người dùng và id nhóm trên máy chủ.
Dimchansky 30/03/2015

@Frondor Xem tham chiếu cấu hình âm lượng docs.docker.com/compose/compose-file/iêu
djhaskin987

Đã làm, và điều đó sẽ không làm việc. Khi container đã sao chép tệp vào ổ đĩa lần đầu tiên, lần sau, ổ đĩa sẽ không còn trống nữa và các tệp sẽ không bị ghi đè bởi các tệp mới hơn. Container đang ưu tiên cho các tệp máy chủ (những tệp được sao chép lần đầu tiên bạn gắn ảnh chứa).
Frondor

Nghe có vẻ giống như câu hỏi SO của chính nó @Frondor
djhaskin987

1
Tôi đang mua cho bạn một người bạn bia! Cảm ơn!
Dimitar Vukman

27

Gắn kết một tập, sao chép các tạo phẩm, điều chỉnh id chủ sở hữu và id nhóm:

mkdir artifacts
docker run -i --rm -v ${PWD}/artifacts:/mnt/artifacts centos:6 /bin/bash << COMMANDS
ls -la > /mnt/artifacts/ls.txt
echo Changing owner from \$(id -u):\$(id -g) to $(id -u):$(id -u)
chown -R $(id -u):$(id -u) /mnt/artifacts
COMMANDS

24

TLDR;

$ docker run --rm -iv${PWD}:/host-volume my-image sh -s <<EOF
chown $(id -u):$(id -g) my-artifact.tar.xz
cp -a my-artifact.tar.xz /host-volume
EOF

Sự miêu tả

docker runvới một khối lượng máy chủ, chowntạo tác, cptạo tác cho khối lượng máy chủ:

$ docker build -t my-image - <<EOF
> FROM busybox
> WORKDIR /workdir
> RUN touch foo.txt bar.txt qux.txt
> EOF
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM busybox
 ---> 00f017a8c2a6
Step 2/3 : WORKDIR /workdir
 ---> Using cache
 ---> 36151d97f2c9
Step 3/3 : RUN touch foo.txt bar.txt qux.txt
 ---> Running in a657ed4f5cab
 ---> 4dd197569e44
Removing intermediate container a657ed4f5cab
Successfully built 4dd197569e44

$ docker run --rm -iv${PWD}:/host-volume my-image sh -s <<EOF
chown -v $(id -u):$(id -g) *.txt
cp -va *.txt /host-volume
EOF
changed ownership of '/host-volume/bar.txt' to 10335:11111
changed ownership of '/host-volume/qux.txt' to 10335:11111
changed ownership of '/host-volume/foo.txt' to 10335:11111
'bar.txt' -> '/host-volume/bar.txt'
'foo.txt' -> '/host-volume/foo.txt'
'qux.txt' -> '/host-volume/qux.txt'

$ ls -n
total 0
-rw-r--r-- 1 10335 11111 0 May  7 18:22 bar.txt
-rw-r--r-- 1 10335 11111 0 May  7 18:22 foo.txt
-rw-r--r-- 1 10335 11111 0 May  7 18:22 qux.txt

Thủ thuật này hoạt động vì chownlời gọi trong di truyền nhận $(id -u):$(id -g)các giá trị từ bên ngoài vùng chứa đang chạy; tức là máy chủ docker.

Những lợi ích là:

  • bạn không cần phải docker container run --namehoặc docker container create --nametrước đó
  • bạn không phải docker container rmsau

2
Nâng cao để so sánh giữa cpvà câu trả lời dựa trên khối lượng. Ngoài ra, đối với idmánh khóe để sở hữu, đôi khi thực sự đau đầu
Marc Ghorayeb

18

Hầu hết các câu trả lời không chỉ ra rằng container phải chạy trước docker cpsẽ hoạt động:

docker build -t IMAGE_TAG .
docker run -d IMAGE_TAG
CONTAINER_ID=$(docker ps -alq)
# If you do not know the exact file name, you'll need to run "ls"
# FILE=$(docker exec CONTAINER_ID sh -c "ls /path/*.zip")
docker cp $CONTAINER_ID:/path/to/file .
docker stop $CONTAINER_ID

3
BTW, việc container phải / có thể đang chạy / dừng / dường như phụ thuộc vào loại máy chủ / kỹ thuật ảo hóa . Tài liệu docker hiện tại cho biết "CONTAINER có thể là một container đang chạy hoặc đã dừng." Nhiều nơi trên SO, bao gồm cả nhận xét về câu trả lời được chấp nhận, nói rằng "điều này cũng hoạt động trên một container bị dừng". Theo Windows Hyper-V, rõ ràng là cần phải dừng container trước khi sao chép một tập tin .
ToolmakerSteve

Sao chép cũng hoạt động khi dừng container.
Luke W

17

Nếu bạn không có bộ chứa đang chạy, chỉ cần một hình ảnh và giả sử bạn muốn sao chép chỉ một tệp văn bản, bạn có thể làm một cái gì đó như thế này:

docker run the-image cat path/to/container/file.txt > path/to/host/file.txt

7

Tôi đang đăng bài này cho bất cứ ai đang sử dụng Docker cho Mac. Đây là những gì làm việc cho tôi:

 $ mkdir mybackup # local directory on Mac

 $ docker run --rm --volumes-from <containerid> \
    -v `pwd`/mybackup:/backup \  
    busybox \                   
    cp /data/mydata.txt /backup 

Lưu ý rằng khi tôi gắn kết sử dụng -vbackup thư mục sẽ tự động được tạo.

Tôi hy vọng điều này hữu ích cho một ngày nào đó. :)


Nếu bạn sử dụng docker-compose, volume-from bị phản đối trong phiên bản 3 trở đi.
mulg0r

Để thêm vào nhận xét của mulg0r, hãy xem stackoverflow.com/a/45495380/199364 - trong v.3, bạn đặt một volumeslệnh tại root của config.yml, để có thể truy cập nhiều khối lượng bằng nhiều container.
ToolmakerSteve

5

Nếu bạn chỉ muốn kéo một tập tin từ một hình ảnh (thay vì một container đang chạy), bạn có thể làm điều này:

docker run --rm <image> cat <source> > <local_dest>

Thao tác này sẽ hiển thị vùng chứa, ghi tệp mới, sau đó xóa vùng chứa. Tuy nhiên, một nhược điểm là các quyền của tệp và ngày sửa đổi sẽ không được giữ nguyên.



5

Với việc phát hành Docker 19.03, bạn có thể bỏ qua việc tạo container và thậm chí xây dựng hình ảnh. Có một tùy chọn với các bản dựng dựa trên BuildKit để thay đổi đích đầu ra. Bạn có thể sử dụng điều này để viết kết quả của bản dựng vào thư mục cục bộ của bạn chứ không phải vào một hình ảnh. Ví dụ: đây là bản dựng của nhị phân đi:

$ ls
Dockerfile  go.mod  main.go

$ cat Dockerfile
FROM golang:1.12-alpine as dev
RUN apk add --no-cache git ca-certificates
RUN adduser -D appuser
WORKDIR /src
COPY . /src/
CMD CGO_ENABLED=0 go build -o app . && ./app

FROM dev as build
RUN CGO_ENABLED=0 go build -o app .
USER appuser
CMD [ "./app" ]

FROM scratch as release
COPY --from=build /etc/passwd /etc/group /etc/
COPY --from=build /src/app /app
USER appuser
CMD [ "/app" ]

FROM scratch as artifact
COPY --from=build /src/app /app

FROM release

Từ Dockerfile ở trên, tôi đang xây dựng artifactgiai đoạn chỉ bao gồm các tệp tôi muốn xuất. Và --outputcờ mới được giới thiệu cho phép tôi viết chúng vào một thư mục cục bộ thay vì hình ảnh. Điều này cần được thực hiện với động cơ BuildKit đi kèm với 19.03:

$ DOCKER_BUILDKIT=1 docker build --target artifact --output type=local,dest=. .
[+] Building 43.5s (12/12) FINISHED
 => [internal] load build definition from Dockerfile                                                                              0.7s
 => => transferring dockerfile: 572B                                                                                              0.0s
 => [internal] load .dockerignore                                                                                                 0.5s
 => => transferring context: 2B                                                                                                   0.0s
 => [internal] load metadata for docker.io/library/golang:1.12-alpine                                                             0.9s
 => [dev 1/5] FROM docker.io/library/golang:1.12-alpine@sha256:50deab916cce57a792cd88af3479d127a9ec571692a1a9c22109532c0d0499a0  22.5s
 => => resolve docker.io/library/golang:1.12-alpine@sha256:50deab916cce57a792cd88af3479d127a9ec571692a1a9c22109532c0d0499a0       0.0s
 => => sha256:1ec62c064901392a6722bb47a377c01a381f4482b1ce094b6d28682b6b6279fd 155B / 155B                                        0.3s
 => => sha256:50deab916cce57a792cd88af3479d127a9ec571692a1a9c22109532c0d0499a0 1.65kB / 1.65kB                                    0.0s
 => => sha256:2ecd820bec717ec5a8cdc2a1ae04887ed9b46c996f515abc481cac43a12628da 1.36kB / 1.36kB                                    0.0s
 => => sha256:6a17089e5a3afc489e5b6c118cd46eda66b2d5361f309d8d4b0dcac268a47b13 3.81kB / 3.81kB                                    0.0s
 => => sha256:89d9c30c1d48bac627e5c6cb0d1ed1eec28e7dbdfbcc04712e4c79c0f83faf17 2.79MB / 2.79MB                                    0.6s
 => => sha256:8ef94372a977c02d425f12c8cbda5416e372b7a869a6c2b20342c589dba3eae5 301.72kB / 301.72kB                                0.4s
 => => sha256:025f14a3d97f92c07a07446e7ea8933b86068d00da9e252cf3277e9347b6fe69 125.33MB / 125.33MB                               13.7s
 => => sha256:7047deb9704134ff71c99791be3f6474bb45bc3971dde9257ef9186d7cb156db 125B / 125B                                        0.8s
 => => extracting sha256:89d9c30c1d48bac627e5c6cb0d1ed1eec28e7dbdfbcc04712e4c79c0f83faf17                                         0.2s
 => => extracting sha256:8ef94372a977c02d425f12c8cbda5416e372b7a869a6c2b20342c589dba3eae5                                         0.1s
 => => extracting sha256:1ec62c064901392a6722bb47a377c01a381f4482b1ce094b6d28682b6b6279fd                                         0.0s
 => => extracting sha256:025f14a3d97f92c07a07446e7ea8933b86068d00da9e252cf3277e9347b6fe69                                         5.2s
 => => extracting sha256:7047deb9704134ff71c99791be3f6474bb45bc3971dde9257ef9186d7cb156db                                         0.0s
 => [internal] load build context                                                                                                 0.3s
 => => transferring context: 2.11kB                                                                                               0.0s
 => [dev 2/5] RUN apk add --no-cache git ca-certificates                                                                          3.8s
 => [dev 3/5] RUN adduser -D appuser                                                                                              1.7s
 => [dev 4/5] WORKDIR /src                                                                                                        0.5s
 => [dev 5/5] COPY . /src/                                                                                                        0.4s
 => [build 1/1] RUN CGO_ENABLED=0 go build -o app .                                                                              11.6s
 => [artifact 1/1] COPY --from=build /src/app /app                                                                                0.5s
 => exporting to client                                                                                                           0.1s
 => => copying files 10.00MB                                                                                                      0.1s

Sau khi xây dựng xong, appnhị phân được xuất ra:

$ ls
Dockerfile  app  go.mod  main.go

$ ./app
Ready to receive requests on port 8080

Docker có các tùy chọn khác cho --outputcờ được ghi lại trong repo BuildKit ngược dòng của họ: https://github.com/moby/buildkit#output


bộ đệm xây dựng tiêu chuẩn không được sử dụng để xây dựng với đầu ra, thật tệ
burtsevyg

@burtsevyg Buildkit là một trình xây dựng khác, sử dụng môi trường bộ đệm khác. Đó là bộ nhớ cache hiệu quả hơn nhiều.
BMitch

cảm ơn bạn tôi sẽ cải thiện các nút xây dựng của tôi.
burtsevyg

4

Là một giải pháp tổng quát hơn, có một plugin CloudBees để Jenkins xây dựng bên trong một Docker container . Bạn có thể chọn một hình ảnh để sử dụng từ sổ đăng ký Docker hoặc xác định Dockerfile để xây dựng và sử dụng.

Nó sẽ gắn không gian làm việc vào vùng chứa dưới dạng âm lượng (với người dùng phù hợp), đặt nó làm thư mục làm việc của bạn, thực hiện bất kỳ lệnh nào bạn yêu cầu (bên trong vùng chứa). Bạn cũng có thể sử dụng plugin docker-workflow (nếu bạn thích mã hơn UI) để thực hiện việc này, với lệnh image.inside () {}.

Về cơ bản tất cả những thứ này, nướng vào máy chủ CI / CD của bạn và sau đó một số.


4

Tôi đã sử dụng PowerShell (Quản trị viên) với lệnh này.

docker cp {container id}:{container path}/error.html  C:\\error.html

Thí dụ

docker cp ff3a6608467d:/var/www/app/error.html  C:\\error.html

2

Một tùy chọn tốt khác trước tiên là xây dựng vùng chứa và sau đó chạy nó bằng cờ -c với trình thông dịch shell để thực thi một số dấu phẩy

docker run --rm -i -v <host_path>:<container_path> <mydockerimage> /bin/sh -c "cp -r /tmp/homework/* <container_path>"

Lệnh trên thực hiện điều này:

-Tôi = chạy container trong chế độ tương tác

--rm = loại bỏ container sau khi thực hiện.

-v = đã chia sẻ một thư mục dưới dạng âm lượng từ đường dẫn máy chủ của bạn đến đường dẫn container.

Cuối cùng, / bin / sh -c cho phép bạn giới thiệu một lệnh dưới dạng tham số và lệnh đó sẽ sao chép các tệp bài tập về nhà của bạn vào đường dẫn container.

Tôi hy vọng câu trả lời bổ sung này có thể giúp bạn


1

Tạo một thư mục dữ liệu trên hệ thống máy chủ (bên ngoài vùng chứa) và gắn kết nó vào một thư mục hiển thị từ bên trong vùng chứa. Điều này đặt các tệp vào một vị trí đã biết trên hệ thống máy chủ và giúp các công cụ và ứng dụng trên hệ thống máy chủ dễ dàng truy cập các tệp

docker run -d -v /path/to/Local_host_dir:/path/to/docker_dir docker_image:tag

4
Điều đó cho phép bạn tiêm một thư mục và nội dung của nó từ máy chủ lưu trữ vào thùng chứa. Nó không cho phép bạn sao chép các tập tin từ container trở lại máy chủ.
BMitch

Nó làm gì nếu thư mục máy chủ có quyền rất rộng?
giorgiosironi

0

Tạo một đường dẫn mà bạn muốn sao chép tệp và sau đó sử dụng:

docker run -d -v hostpath:dockerimag

0

Bạn có thể sử dụng bindthay vì volumenếu bạn chỉ muốn gắn một thư mục, không tạo bộ nhớ đặc biệt cho một container:

  1. Xây dựng hình ảnh của bạn với thẻ:

    docker build . -t <image>

  2. Chạy hình ảnh của bạn và liên kết thư mục $ (pwd) hiện tại nơi app.py lưu trữ và ánh xạ nó tới / root / example / bên trong thùng chứa của bạn.

    docker run --mount type=bind,source="$(pwd)",target=/root/example/ <image> python app.py


0

Điều này cũng có thể được thực hiện trong SDK ví dụ python. Nếu bạn đã có sẵn một thùng chứa, bạn có thể tra cứu tên qua bảng điều khiển (docker ps -a ) dường như là sự ghép nối của một nhà khoa học và một tính từ (tức là "relax_pasteur").

Kiểm tra help(container.get_archive):

Help on method get_archive in module docker.models.containers:

get_archive(path, chunk_size=2097152) method of docker.models.containers.Container instance
    Retrieve a file or folder from the container in the form of a tar
    archive.

    Args:
        path (str): Path to the file or folder to retrieve
        chunk_size (int): The number of bytes returned by each iteration
            of the generator. If ``None``, data will be streamed as it is
            received. Default: 2 MB

    Returns:
        (tuple): First element is a raw tar data stream. Second element is
        a dict containing ``stat`` information on the specified ``path``.

    Raises:
        :py:class:`docker.errors.APIError`
            If the server returns an error.

    Example:

        >>> f = open('./sh_bin.tar', 'wb')
        >>> bits, stat = container.get_archive('/bin/sh')
        >>> print(stat)
        {'name': 'sh', 'size': 1075464, 'mode': 493,
         'mtime': '2018-10-01T15:37:48-07:00', 'linkTarget': ''}
        >>> for chunk in bits:
        ...    f.write(chunk)
        >>> f.close()

Vì vậy, một cái gì đó như thế này sẽ kéo ra từ đường dẫn (/ đầu ra) được chỉ định trong vùng chứa đến máy chủ của bạn và giải nén tar.

import docker
import os
import tarfile

# Docker client
client = docker.from_env()
#container object
container = client.containers.get("relaxed_pasteur")
#setup tar to write bits to
f = open(os.path.join(os.getcwd(),"output.tar"),"wb")
#get the bits
bits, stat = container.get_archive('/output')
#write the bits
for chunk in bits:
    f.write(chunk)
f.close()
#unpack
tar = tarfile.open("output.tar")
tar.extractall()
tar.close()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.