Cách nâng cấp container docker sau khi hình ảnh của nó thay đổi


518

Giả sử tôi đã lấy hình ảnh chính thức của mysql: 5.6,21 .

Tôi đã triển khai hình ảnh này bằng cách tạo ra một số container docker.

Các container này đã chạy được một thời gian cho đến khi MySQL 5.6.22 được phát hành. Hình ảnh chính thức của mysql: 5.6 được cập nhật với bản phát hành mới, nhưng các container của tôi vẫn chạy 5.6.21.

Làm cách nào để truyền bá các thay đổi trong hình ảnh (tức là nâng cấp distro MySQL) cho tất cả các container hiện có của tôi? Cách Docker thích hợp để làm điều này là gì?

Câu trả lời:


579

Sau khi đánh giá các câu trả lời và nghiên cứu chủ đề tôi muốn tóm tắt.

Cách Docker để nâng cấp container dường như là như sau:

Container ứng dụng không nên lưu trữ dữ liệu ứng dụng . Bằng cách này, bạn có thể thay thế bộ chứa ứng dụng bằng phiên bản mới hơn bất cứ lúc nào bằng cách thực hiện một cái gì đó như thế này:

docker pull mysql
docker stop my-mysql-container
docker rm my-mysql-container
docker run --name=my-mysql-container --restart=always \
  -e MYSQL_ROOT_PASSWORD=mypwd -v /my/data/dir:/var/lib/mysql -d mysql

Bạn có thể lưu trữ dữ liệu trên máy chủ (trong thư mục được gắn dưới dạng âm lượng) hoặc trong (các) thùng chứa chỉ dữ liệu đặc biệt . Tìm hiểu thêm về nó

Nâng cấp các ứng dụng (ví dụ: với nâng cấp yum / apt-get) trong các thùng chứa được coi là một mô hình chống . Các thùng chứa ứng dụng được coi là bất biến , sẽ đảm bảo hành vi có thể lặp lại. Một số hình ảnh ứng dụng chính thức (đặc biệt là mysql: 5.6) thậm chí không được thiết kế để tự cập nhật (nâng cấp apt-get sẽ không hoạt động).

Tôi muốn cảm ơn tất cả những người đã đưa ra câu trả lời của họ, vì vậy chúng tôi có thể thấy tất cả các cách tiếp cận khác nhau.


31
Điều gì nếu di chuyển dữ liệu là cần thiết? Máy chủ mới không thể gắn dữ liệu vì nó ở định dạng cũ, nó cần biết di chuyển đang xảy ra và thay đổi biểu diễn dữ liệu.
Dor Rotman

12
Tôi nghĩ, các nhà thiết kế hình ảnh nên tính đến điều đó và cho phép khởi chạy các lệnh tùy chỉnh (ví dụ: di chuyển dữ liệu) trong lần chạy đầu tiên của bộ chứa.
Yaroslav Stavnichiy


4
@static_rtti Làm thế nào về docker rename my-mysql-container trash-containertrước khi tạo cái mới?
Franklin Yu

4
Sẽ có bất kỳ lệnh tất cả trong một nào để cập nhật container mà không phải dừng thủ công, gỡ bỏ nó và tạo lại nó (dựa trên hình ảnh mới đã được kéo)?
Michaël Perrin

83

Tôi không thích gắn khối lượng như một liên kết đến một thư mục máy chủ, vì vậy tôi đã đưa ra một mô hình để nâng cấp các container docker với các container hoàn toàn được quản lý docker. Tạo một container docker mới với --volumes-from <container>sẽ cung cấp cho container mới với các hình ảnh được cập nhật chia sẻ quyền sở hữu khối lượng được quản lý docker.

docker pull mysql
docker create --volumes-from my_mysql_container [...] --name my_mysql_container_tmp mysql

Bằng cách không xóa ngay bản gốc my_mysql_container, bạn có thể hoàn nguyên trở lại vùng chứa đang hoạt động nếu thùng chứa được nâng cấp không có dữ liệu phù hợp hoặc không thực hiện kiểm tra độ tỉnh táo.

Tại thời điểm này, tôi thường chạy bất kỳ tập lệnh sao lưu nào tôi có cho container để tạo cho mình một mạng lưới an toàn trong trường hợp xảy ra sự cố

docker stop my_mysql_container
docker start my_mysql_container_tmp

Bây giờ bạn có cơ hội để đảm bảo dữ liệu bạn mong đợi có trong thùng chứa mới ở đó và chạy kiểm tra độ tỉnh táo.

docker rm my_mysql_container
docker rename my_mysql_container_tmp my_mysql_container

Khối lượng docker sẽ bám xung quanh miễn là bất kỳ container nào đang sử dụng chúng, vì vậy bạn có thể xóa container ban đầu một cách an toàn. Khi bộ chứa ban đầu được gỡ bỏ, bộ chứa mới có thể giả sử tên của bản gốc để làm cho mọi thứ đẹp như lúc bắt đầu.

Có hai ưu điểm chính khi sử dụng mẫu này để nâng cấp các container docker. Đầu tiên, nó loại bỏ sự cần thiết phải gắn kết khối lượng vào thư mục lưu trữ bằng cách cho phép khối lượng được chuyển trực tiếp đến một container được nâng cấp. Thứ hai, bạn không bao giờ ở vị trí không có container docker hoạt động; vì vậy nếu nâng cấp thất bại, bạn có thể dễ dàng trở lại cách nó hoạt động trước đó bằng cách quay lại thùng chứa docker ban đầu.


3
Tại sao bạn không thích gắn khối lượng máy chủ bên trong một container Docker? (Tôi đang làm chính xác điều đó nên tôi rất thích tranh luận về việc đó: -) Tôi đã gắn kết, ví dụ: ./postgres-data/:/var/lib/postgres/data- tức là đã gắn thư mục máy chủ ./postgres-data/, bên trong thùng chứa
PostgreQuery

4
@KajMagnus Tôi sử dụng docker swarms rất nhiều, và tôi thích viết các container của tôi để hoạt động tốt trong một bầy. Khi tôi quay một container trong một bầy tôi không biết nút swarm nào mà container sẽ sống, vì vậy tôi không thể dựa vào đường dẫn máy chủ chứa dữ liệu mà tôi muốn. Vì khối lượng Docker 1.9 (tôi nghĩ) có thể được chia sẻ trên các máy chủ, điều này giúp cho việc nâng cấp và di chuyển các container trở nên dễ dàng bằng phương pháp tôi mô tả. Một giải pháp thay thế sẽ là đảm bảo một số khối lượng mạng được gắn trên tất cả các nút swarm, nhưng điều đó nghe có vẻ như là một nỗi đau rất lớn để duy trì.
kMaiSmith

Cảm ơn! Ok, gắn khối lượng máy chủ lưu trữ có vẻ như một cái gì đó tôi quá muốn tránh, bây giờ. Ít nhất một
lát

32

Chỉ để cung cấp một câu trả lời tổng quát hơn (không cụ thể về mysql) ...

  1. Nói ngắn gọn

Đồng bộ hóa với đăng ký hình ảnh dịch vụ ( https://docs.docker.com/compose/compose-file/#image ):

docker-compose pull 

Tái tạo container nếu tập tin hoặc hình ảnh docker-compose đã thay đổi:

docker-compose up -d
  1. Lý lịch

Quản lý hình ảnh container là một trong những lý do để sử dụng docker-compose (xem https://docs.docker.com/compose/reference/up/ )

Nếu có các bộ chứa hiện có cho một dịch vụ và cấu hình hoặc hình ảnh của dịch vụ đã bị thay đổi sau khi tạo bộ chứa, docker-compose up nhặt các thay đổi bằng cách dừng và tạo lại các bộ chứa (bảo toàn khối lượng được gắn). Để ngăn Compose chọn các thay đổi, hãy sử dụng cờ --no-rece.

Khía cạnh quản lý dữ liệu cũng được bao phủ bởi docker-compose thông qua "khối lượng" bên ngoài được gắn (Xem https://docs.docker.com/compose/compose-file/#volume ) hoặc thùng chứa dữ liệu.

Điều này khiến các vấn đề tương thích và di chuyển dữ liệu tiềm ẩn chưa được xử lý, nhưng đây là những vấn đề "mang tính ứng dụng", không phải là Docker cụ thể, phải được kiểm tra đối với các ghi chú và kiểm tra phát hành ...


Làm thế nào để bạn điều này với phiên bản? ví dụ hình ảnh mới là foo / image: 2 và docker-compose.yml có hình ảnh: foo / image: 1?
dman

CẢM ƠN BẠN. Câu trả lời tốt nhất!
Mick

Mặc dù đây chắc chắn là con đường để đi, người ta nên lưu ý rằng mọi thay đổi được thực hiện trong container vẫn sẽ bị mất sau khi container được tạo lại. Vì vậy, giữ cho các thay đổi container chỉ trong khối lượng gắn kết vẫn là cần thiết.
Petr Bodnár

23

Tôi muốn thêm rằng nếu bạn muốn thực hiện quy trình này một cách tự động (tải xuống, dừng và khởi động lại một vùng chứa mới có cùng cài đặt như được mô tả bởi @Yaroslav), bạn có thể sử dụng WatchTower. Một chương trình tự động cập nhật các thùng chứa của bạn khi chúng được thay đổi https://github.com/v2tec/watchtower


20

Xem xét cho câu trả lời này:

  • Tên cơ sở dữ liệu là app_schema
  • Tên container là app_db
  • Mật khẩu gốc là root123

Cách cập nhật MySQL khi lưu trữ dữ liệu ứng dụng bên trong container

Đây được coi là một thực tiễn xấu , bởi vì nếu bạn mất container, bạn sẽ mất dữ liệu. Mặc dù đó là một thực hành xấu, đây là một cách có thể để làm điều đó:

1) Thực hiện kết xuất cơ sở dữ liệu dưới dạng SQL:

docker exec app_db sh -c 'exec mysqldump app_schema -uroot -proot123' > database_dump.sql

2) Cập nhật hình ảnh:

docker pull mysql:5.6

3) Cập nhật container:

docker rm -f app_db
docker run --name app_db --restart unless-stopped \
-e MYSQL_ROOT_PASSWORD=root123 \
-d mysql:5.6

4) Khôi phục kết xuất cơ sở dữ liệu:

docker exec app_db sh -c 'exec mysql -uroot -proot123' < database_dump.sql

Cách cập nhật vùng chứa MySQL bằng cách sử dụng âm lượng bên ngoài

Sử dụng một khối lượng bên ngoài là một cách tốt hơn để quản lý dữ liệu và giúp cập nhật MySQL dễ dàng hơn. Mất container sẽ không mất bất kỳ dữ liệu. Bạn có thể sử dụng docker-compose để tạo điều kiện quản lý các ứng dụng Docker nhiều container trong một máy chủ:

1) Tạo docker-compose.ymltệp để quản lý các ứng dụng của bạn:

version: '2'
services:
  app_db:
    image: mysql:5.6
    restart: unless-stopped
    volumes_from: app_db_data
  app_db_data:
    volumes: /my/data/dir:/var/lib/mysql

2) Cập nhật MySQL (từ cùng thư mục với docker-compose.ymltệp):

docker-compose pull
docker-compose up -d

Lưu ý: lệnh cuối cùng ở trên sẽ cập nhật hình ảnh MySQL, tạo lại và bắt đầu vùng chứa với hình ảnh mới.


Giả sử tôi có một cơ sở dữ liệu khổng lồ (vài GB), liệu dữ liệu của tôi sẽ không thể truy cập được cho đến khi toàn bộ cơ sở dữ liệu được nhập? Đó có thể là một "thời gian chết" khổng lồ
hellimac

Kể từ khi bạn đề cập docker-compose, điều này sẽ làm việc? stackoverflow.com/a/31485685/65313
sivabudh

1
volumes_fromkhóa hiện không được chấp nhận (thậm chí đã bị xóa trong phiên bản 3 của tệp soạn thảo) có lợi cho volumeskhóa mới .
Franklin Yu

docker pull image_uri:tag && docker restart container_running_that_imageđã làm cho tôi. Không cần docker-compose pull && docker-compose up -d.
Yuriy Pozniak

16

Câu trả lời tương tự như trên

docker images | awk '{print $1}' | grep -v 'none' | grep -iv 'repo' | xargs -n1 docker pull

1
Xuất sắc! Khá ngạc nhiên khi nó không nhận được nhiều phiếu hơn. Bây giờ điều duy nhất còn thiếu là kích hoạt khởi động lại tất cả các container đã được cập nhật.
sorin

7
Thật không may, điều này sẽ không cập nhật các container hiện có. Điều này sẽ chỉ cập nhật hình ảnh kéo, nhưng bộ chứa hiện tại là bất biến và vẫn sử dụng hình ảnh gốc được sử dụng để tạo ra nó. Điều này chỉ hoạt động nếu bạn tạo một vùng chứa mới từ hình ảnh, nhưng bất kỳ vùng chứa hiện có nào vẫn dựa trên hình ảnh gốc.
Eric B.

Kinh ngạc. Nếu bạn cần kéo phiên bản cụ thể của container, hãy làm như thế này: hình ảnh docker | awk '{in $ 1 ":" $ 2}' | grep -v 'không' | grep -iv 'repo' | xargs -n1
docker

11

Đây là những gì nó trông giống như sử dụng docker-composekhi xây dựng một tùy chỉnh Dockerfile.

  1. Trước tiên hãy xây dựng Dockerfile tùy chỉnh của bạn, nối thêm số phiên bản tiếp theo để phân biệt. Ví dụ: docker build -t imagename:version . Điều này sẽ lưu trữ phiên bản mới của bạn tại địa phương.
  2. Chạy docker-compose down
  3. Chỉnh sửa docker-compose.ymltệp của bạn để phản ánh tên hình ảnh mới bạn đặt ở bước 1.
  4. Chạy đi docker-compose up -d. Nó sẽ tìm kiếm cục bộ cho hình ảnh và sử dụng nâng cấp của bạn.

-BIÊN TẬP-

Các bước của tôi ở trên dài hơn so với họ cần. Tôi đã tối ưu hóa quy trình làm việc của mình bằng cách đưa build: .tham số vào tệp soạn thảo docker của tôi. Các bước có vẻ như bây giờ:

  1. Xác minh rằng Dockerfile của tôi là những gì tôi muốn nó trông như thế nào.
  2. Đặt số phiên bản của tên hình ảnh của tôi trong tệp soạn thảo docker của tôi.
  3. Nếu hình ảnh của tôi chưa được xây dựng: chạy docker-compose build
  4. Chạy docker-compose up -d

Lúc đó tôi không nhận ra, nhưng docker-compose đủ thông minh để chỉ cần cập nhật container của tôi lên hình ảnh mới bằng một lệnh, thay vì phải đưa nó xuống trước.


Trong tình huống thực tế, bạn không thể sử dụng tay của chính mình và thực hiện những thay đổi đó. Giải pháp của bạn không hỗ trợ các cách tự động để giải quyết vấn đề.
Carlos Vázquez Losada

7
vì vậy bạn đang nói rằng vì giải pháp của tôi không tự động, nên nó không hợp lệ? Đó có phải là một yêu cầu từ OP? Và các câu trả lời khác có nghĩa là tự động hóa? Thực sự bối rối. Và, tôi nghĩ rằng các downvote đang làm bất đồng cho những người khác đến đây. Câu trả lời của tôi là 100% hợp lệ cho câu hỏi được hỏi.
gdbj

cảm ơn vì câu trả lời này, tôi cũng không nhận ra bạn có thể chạy đơn giản docker-compose up -dmà không cần dừng mọi thứ trước.
radicand

4

Nếu bạn không muốn sử dụng Docker Compose, tôi có thể khuyên dùng portainer . Nó có chức năng tái tạo cho phép bạn tạo lại một container trong khi kéo hình ảnh mới nhất.


2

Bạn cần phải xây dựng lại tất cả các hình ảnh và khởi động lại tất cả các thùng chứa, hoặc bằng cách nào đó yum cập nhật phần mềm và khởi động lại cơ sở dữ liệu. Không có con đường nâng cấp nhưng bạn tự thiết kế.


Chính xác những gì bạn có nghĩa là bằng cách khởi động lại container? Có docker restartlệnh, nhưng tôi không chắc nó sẽ nhận thay đổi hình ảnh. Và những gì xảy ra với dữ liệu của tôi trong các container?
Yaroslav Stavnichiy

1
Xin lỗi, tôi không có nghĩa là khởi động lại docker. Ý tôi là docker rm -f NỘI DUNG; docker chạy NEW_IMAGE. Dữ liệu trong container sql của bạn sẽ biến mất. Đó là lý do tại sao mọi người thường sử dụng khối lượng để lưu trữ dữ liệu.
seanmcl

Nếu bạn có tất cả dữ liệu của mình được gắn theo khối lượng trong các thùng chứa hoặc máy chủ riêng biệt, thì Nas @seanmcl cho biết chỉ cần tạo các thùng chứa mới với mysql mới được kết nối với cùng một dữ liệu. Nếu bạn không làm điều đó (bạn nên) nhưng bạn có thể sử dụng lệnh docker exec có sẵn trong docker 1.3 để cập nhật mysql và khởi động lại nó bên trong container.
Usman Ismail

2

Lấy từ http://blog.stefanxo.com/2014/08/update-all-docker-images-at-once/

Bạn có thể cập nhật tất cả các hình ảnh hiện có của mình bằng cách sử dụng đường dẫn lệnh sau:

docker images | awk '/^REPOSITORY|\<none\>/ {next} {print $1}' | xargs -n 1 docker pull

6
Điều này sẽ cập nhật hình ảnh, nhưng không phải là container. Container là bất biến và hình ảnh cơ sở của nó không thể thay đổi mà không tạo ra một container mới từ một hình ảnh được cập nhật.
Eric B.

2

Đảm bảo rằng bạn đang sử dụng khối lượng cho tất cả dữ liệu liên tục (cấu hình, nhật ký hoặc dữ liệu ứng dụng) mà bạn lưu trữ trên các thùng chứa liên quan đến trạng thái của các quy trình bên trong thùng chứa đó. Cập nhật Dockerfile của bạn và xây dựng lại hình ảnh với những thay đổi bạn muốn và khởi động lại các thùng chứa với khối lượng của bạn được gắn tại vị trí thích hợp của chúng.


1

Đây là điều mà tôi cũng đang vật lộn với hình ảnh của chính mình. Tôi có một môi trường máy chủ mà từ đó tôi tạo một hình ảnh Docker. Khi tôi cập nhật máy chủ, tôi muốn tất cả người dùng đang chạy container dựa trên hình ảnh Docker của tôi có thể nâng cấp lên máy chủ mới nhất.

Lý tưởng nhất là tôi muốn tạo một phiên bản mới của hình ảnh Docker và có tất cả các thùng chứa dựa trên phiên bản trước của hình ảnh đó tự động cập nhật lên hình ảnh mới "tại chỗ". Nhưng cơ chế này dường như không tồn tại.

Vì vậy, thiết kế tốt nhất tiếp theo mà tôi có thể đưa ra cho đến nay là cung cấp một cách để tự cập nhật container - tương tự như cách ứng dụng máy tính để bàn kiểm tra các bản cập nhật và sau đó tự nâng cấp. Trong trường hợp của tôi, điều này có thể sẽ có nghĩa là tạo ra một kịch bản liên quan đến Git kéo từ một thẻ nổi tiếng.

Hình ảnh / container không thực sự thay đổi, nhưng "phần bên trong" của container đó thay đổi. Bạn có thể tưởng tượng làm tương tự với apt-get, yum hoặc bất cứ điều gì phù hợp với môi trường của bạn. Cùng với điều này, tôi sẽ cập nhật myserver: hình ảnh mới nhất trong sổ đăng ký để mọi container mới sẽ dựa trên hình ảnh mới nhất.

Tôi muốn biết liệu có nghệ thuật nào trước đây giải quyết tình huống này không.


7
Nó đi ngược lại khái niệm cơ sở hạ tầng bất biến và một số lợi ích của nó. Bạn có thể kiểm tra ứng dụng / môi trường của mình để thấy rằng nó hoạt động và nó không được bảo đảm nếu bạn cập nhật các thành phần bên trong. Việc tách mã vùng chứa khỏi dữ liệu từ cấu hình cho phép bạn cập nhật, kiểm tra xem chúng tôi hiện đang làm việc và triển khai vào sản xuất, biết rằng không có dòng mã nào khác từ hình ảnh được thử nghiệm đến hình ảnh sản xuất. Dù sao, hệ thống cho phép bạn quản lý nó như bạn nói, đó là sự lựa chọn của bạn.
gmuslera

Gmuslera điểm rất tốt. Đồng ý rằng đó là một mô hình chống cập nhật 'nội bộ' của một container docker hiện có.
bjlevine

Vì vậy, giải pháp tốt nhất để tự động cập nhật container docker dựa trên các cập nhật hình ảnh docker để cung cấp các bản cập nhật cho tất cả các container dễ dàng là gì?
tarek salem

1

Cập nhật

Điều này chủ yếu là để truy vấn container không cập nhật vì xây dựng hình ảnh là cách cần thực hiện

Tôi có cùng một vấn đề vì vậy tôi đã tạo ra docker-run , một công cụ dòng lệnh rất đơn giản chạy bên trong một container docker để cập nhật các gói trong các container đang chạy khác.

Nó sử dụng docker-py để liên lạc với các container đang chạy và cập nhật các gói hoặc chạy bất kỳ lệnh đơn lẻ nào

Ví dụ:

docker run --rm -v /var/run/docker.sock:/tmp/docker.sock itech/docker-run exec

theo mặc định, điều này sẽ chạy datelệnh trong tất cả các container đang chạy và trả về kết quả nhưng bạn có thể đưa ra bất kỳ lệnh nào, vddocker-run exec "uname -a"

Để cập nhật các gói (hiện chỉ sử dụng apt-get):

docker run --rm -v /var/run/docker.sock:/tmp/docker.sock itech/docker-run update

Bạn có thể tạo và bí danh và sử dụng nó như một dòng lệnh thông thường, vd

alias docker-run='docker run --rm -v /var/run/docker.sock:/tmp/docker.sock itech/docker-run'


Đây có phải là một ý tưởng tốt? (Nếu bạn làm như vậy apt update; apt upgrade, hình ảnh sẽ phát triển.)
ctrl-alt-delor

Hình ảnh của @yar Tư là một giải pháp tốt hơn cho vấn đề này. Ở trên không thực sự là cách làm Docker.
Joost van der Laan
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.