Xây dựng lại vùng chứa Docker khi thay đổi tệp


86

Để chạy một ứng dụng ASP.NET Core, tôi đã tạo một tệp dockerfile để xây dựng ứng dụng và sao chép mã nguồn trong vùng chứa, được Git tìm nạp bằng Jenkins. Vì vậy, trong không gian làm việc của tôi, tôi thực hiện như sau trong tệp dockerfile:

WORKDIR /app
COPY src src

Trong khi Jenkins cập nhật các tệp trên máy chủ của tôi một cách chính xác bằng Git, Docker không áp dụng điều này cho hình ảnh của tôi.

Kịch bản cơ bản của tôi để xây dựng:

#!/bin/bash
imageName=xx:my-image
containerName=my-container

docker build -t $imageName -f Dockerfile  .

containerRunning=$(docker inspect --format="{{ .State.Running }}" $containerName 2> /dev/null)

if [ "$containerRunning" == "true" ]; then
        docker stop $containerName
        docker start $containerName
else
        docker run -d -p 5000:5000 --name $containerName $imageName
fi

Tôi đã thử những thứ khác nhau như --rm--no-cachetham số cho docker runvà cũng dừng / xóa vùng chứa trước khi xây dựng vùng chứa mới. Tôi không chắc mình đang làm gì sai ở đây. Có vẻ như docker đang cập nhật hình ảnh một cách chính xác, vì lệnh gọi của COPY src srcsẽ dẫn đến một id lớp và không có lệnh gọi bộ nhớ cache:

Step 6 : COPY src src
 ---> 382ef210d8fd

Cách đề xuất để cập nhật vùng chứa là gì?

Tình huống điển hình của tôi sẽ là: Ứng dụng đang chạy trên máy chủ trong vùng chứa Docker. Giờ đây, các phần của ứng dụng đã được cập nhật, chẳng hạn như bằng cách sửa đổi tệp. Bây giờ vùng chứa sẽ chạy phiên bản mới. Docker dường như khuyên bạn nên xây dựng một hình ảnh mới thay vì sửa đổi một vùng chứa hiện có, vì vậy tôi nghĩ rằng cách xây dựng lại chung như tôi làm là đúng, nhưng một số chi tiết trong việc triển khai phải được cải thiện.


Bạn có thể liệt kê các bước chính xác mà bạn đã thực hiện để xây dựng vùng chứa của mình, bao gồm lệnh xây dựng và toàn bộ kết quả đầu ra từ mỗi lệnh không?
BMitch

Câu trả lời:


137

Sau một số nghiên cứu và thử nghiệm, tôi nhận thấy rằng mình đã có một số hiểu lầm về vòng đời của Docker container. Đơn giản chỉ cần khởi động lại vùng chứa không làm cho Docker sử dụng hình ảnh mới, khi hình ảnh được xây dựng lại trong thời gian chờ đợi. Thay vào đó, Docker chỉ tìm nạp hình ảnh trước khi tạo vùng chứa. Vì vậy, trạng thái sau khi chạy một container là liên tục.

Tại sao phải xóa

Do đó, xây dựng lại và khởi động lại là không đủ. Tôi nghĩ vùng chứa hoạt động giống như một dịch vụ: Dừng dịch vụ, thực hiện các thay đổi của bạn, khởi động lại nó và chúng sẽ áp dụng. Đó là sai lầm lớn nhất của tôi.

Vì các thùng chứa là vĩnh viễn, bạn phải xóa chúng bằng cách sử dụng docker rm <ContainerName>trước. Sau khi một vùng chứa bị xóa, bạn không thể bắt đầu nó một cách đơn giản docker start. Điều này phải được thực hiện bằng cách sử dụngdocker run sử dụng hình ảnh mới nhất để tạo một phiên bản vùng chứa mới.

Các thùng chứa phải càng độc lập càng tốt

Với kiến ​​thức này, có thể hiểu tại sao việc lưu trữ dữ liệu trong các vùng chứa lại được coi là phương pháp không tốt và Docker đề xuất khối lượng dữ liệu / gắn thư mục máy chủ lưu trữ thay thế: Vì một vùng chứa phải bị phá hủy để cập nhật ứng dụng, dữ liệu được lưu trữ bên trong cũng sẽ bị mất. Điều này gây ra thêm công việc để tắt các dịch vụ, dữ liệu sao lưu, v.v.

Vì vậy, đó là một giải pháp thông minh để loại trừ hoàn toàn những dữ liệu đó khỏi vùng chứa: Chúng tôi không phải lo lắng về dữ liệu của mình, khi dữ liệu được lưu trữ an toàn trên máy chủ lưu trữ và vùng chứa chỉ chứa ứng dụng.

Tại sao -rfcó thể không thực sự giúp bạn

Các docker runlệnh, có lên sạch switch gọi -rf. Nó sẽ dừng hành vi lưu giữ các bộ chứa docker vĩnh viễn. Khi sử dụng -rf, Docker sẽ phá hủy vùng chứa sau khi nó đã được thoát. Nhưng công tắc này có hai vấn đề:

  1. Docker cũng xóa các ổ đĩa không có tên được liên kết với vùng chứa, điều này có thể giết dữ liệu của bạn
  2. Sử dụng tùy chọn này, không thể chạy các vùng chứa trong nền bằng cách sử dụng -dcông tắc

Mặc dù công -rftắc là một lựa chọn tốt để tiết kiệm công việc trong quá trình phát triển cho các thử nghiệm nhanh, nhưng nó ít phù hợp hơn trong sản xuất. Đặc biệt là vì thiếu tùy chọn để chạy vùng chứa trong nền, điều này hầu hết sẽ được yêu cầu.

Cách xóa hộp đựng

Chúng tôi có thể bỏ qua những hạn chế đó bằng cách chỉ cần loại bỏ vùng chứa:

docker rm --force <ContainerName>

Công tắc --force(hoặc -f) sử dụng SIGKILL trên các vùng chứa đang chạy. Thay vào đó, bạn cũng có thể dừng vùng chứa trước đó:

docker stop <ContainerName>
docker rm <ContainerName>

Cả hai đều bằng nhau. docker stopcũng đang sử dụng SIGTERM . Nhưng việc sử dụng --forceswitch sẽ rút ngắn tập lệnh của bạn, đặc biệt là khi sử dụng máy chủ CI: docker stoptạo ra lỗi nếu vùng chứa không chạy. Điều này sẽ khiến Jenkins và nhiều máy chủ CI khác coi việc xây dựng sai là không thành công. Để khắc phục điều này, trước tiên bạn phải kiểm tra xem vùng chứa có đang chạy như tôi đã làm trong câu hỏi hay không (xem containerRunningbiến).

Tập lệnh đầy đủ để xây dựng lại vùng chứa Docker

Theo kiến ​​thức mới này, tôi đã sửa tập lệnh của mình theo cách sau:

#!/bin/bash
imageName=xx:my-image
containerName=my-container

docker build -t $imageName -f Dockerfile  .

echo Delete old container...
docker rm -f $containerName

echo Run new container...
docker run -d -p 5000:5000 --name $containerName $imageName

Điều này hoạt động hoàn hảo :)


4
'Tôi thấy rằng tôi đã có một số hiểu lầm về tuổi thọ của Docker container', bạn đã nói ngay từ miệng tôi. Cảm ơn bạn đã giải thích chi tiết như vậy. Tôi muốn giới thiệu điều này cho người mới docker. Điều này làm rõ sự khác biệt của VM và vùng chứa.
Vince Banzon

2
Sau lời giải thích của bạn, điều tôi làm là ghi lại những gì tôi đã làm với hình ảnh hiện có của mình. Để giữ lại các thay đổi, tôi đã tạo một Dockerfile mới để tạo một hình ảnh mới đã bao gồm các thay đổi mà tôi muốn thêm. Bằng cách đó, hình ảnh mới được tạo (phần nào) được cập nhật.
Vince Banzon

--force-recreatetùy chọn trên Docker soạn tương tự như những gì bạn mô tả ở đây? Và nếu vậy, sẽ không đáng để sử dụng giải pháp này thay thế (xin lỗi nếu câu hỏi đó là ngớ ngẩn nhưng tôi là một noob docker ^^)
cglacet

2
@cglacet Có, nó tương tự, không thể so sánh trực tiếp. Nhưng docker-composethông minh hơn các dockerlệnh đơn giản . Tôi làm việc thường xuyên docker-composevà tính năng phát hiện thay đổi hoạt động tốt, vì vậy tôi --force-recreaterất hiếm khi sử dụng . Chỉ docker-compose up --buildquan trọng khi bạn đang xây dựng một hình ảnh tùy chỉnh ( buildchỉ thị trong tệp soạn thảo) thay vì sử dụng một hình ảnh từ trung tâm Docker.
Lion

33

Bất cứ khi nào thay đổi được thực hiện trong dockerfile hoặc soạn thư hoặc các yêu cầu, hãy chạy lại nó bằng cách sử dụng docker-compose up --build. Để hình ảnh được xây dựng lại và làm mới


1
Có một bộ chứa MySQL docker như một dịch vụ, liệu sau đó DB sẽ trống nếu một người sử dụng một ổ đĩa cho /opt/mysql/data:/var/lib/mysql?
Martin Thoma

Đối với tôi, dường như không có bất kỳ nhược điểm nào khi luôn sử dụng --buildtrong môi trường nhà phát triển cục bộ. Tốc độ mà docker sao chép lại các tệp mà nó có thể cho rằng không cần sao chép chỉ mất vài mili giây và nó giúp tiết kiệm số lượng lớn các khoảnh khắc WTF.
Danack

0

Bạn có thể chạy buildcho một dịch vụ cụ thể bằng cách chạy docker-compose up --build <service name>ở nơi tên dịch vụ phải khớp với cách bạn đã gọi nó trong tệp docker-soạn của mình.

Ví dụ Giả sử rằng tệp docker-soạn của bạn chứa nhiều dịch vụ (ứng dụng .net - cơ sở dữ liệu - hãy mã hóa ... vv) và bạn chỉ muốn cập nhật ứng dụng .net có tên applicationtrong tệp docker-comp. Sau đó, bạn có thể chỉ cần chạydocker-compose up --build application

Tham số bổ sung Trong trường hợp bạn muốn thêm tham số bổ sung vào lệnh của mình, chẳng hạn như -dđể chạy ở chế độ nền, tham số phải ở trước tên dịch vụ: docker-compose up --build -d application

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.