สร้างคอนเทนเนอร์ Docker ใหม่เมื่อมีการเปลี่ยนแปลงไฟล์


93

สำหรับการเรียกใช้แอปพลิเคชัน ASP.NET Core ฉันได้สร้าง Dockerfile ซึ่งสร้างแอปพลิเคชันและคัดลอกซอร์สโค้ดในคอนเทนเนอร์ซึ่ง Git ดึงข้อมูลโดยใช้ Jenkins ดังนั้นในพื้นที่ทำงานของฉันฉันทำสิ่งต่อไปนี้ใน dockerfile:

WORKDIR /app
COPY src src

ในขณะที่ Jenkins อัปเดตไฟล์บนโฮสต์ของฉันอย่างถูกต้องด้วย Git Docker จะไม่ใช้สิ่งนี้กับรูปภาพของฉัน

สคริปต์พื้นฐานของฉันสำหรับการสร้าง:

#!/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

ฉันลองสิ่งต่างๆเช่น--rmและ--no-cacheพารามิเตอร์สำหรับdocker runและหยุด / ลบคอนเทนเนอร์ก่อนที่จะสร้างใหม่ ฉันไม่แน่ใจว่าฉันทำอะไรผิดที่นี่ ดูเหมือนว่านักเทียบท่ากำลังอัปเดตภาพอย่างถูกต้องเนื่องจากการเรียกใช้COPY src srcจะส่งผลให้เกิดรหัสชั้นและไม่มีการเรียกแคช:

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

วิธีที่แนะนำในการอัปเดตคอนเทนเนอร์คืออะไร

สถานการณ์ทั่วไปของฉันจะเป็น: แอปพลิเคชันกำลังทำงานบนเซิร์ฟเวอร์ในคอนเทนเนอร์ Docker ตอนนี้บางส่วนของแอพได้รับการอัปเดตเช่นโดยการแก้ไขไฟล์ ตอนนี้คอนเทนเนอร์ควรเรียกใช้เวอร์ชันใหม่ ดูเหมือนว่านักเทียบท่าจะแนะนำให้สร้างอิมเมจใหม่แทนการปรับเปลี่ยนคอนเทนเนอร์ที่มีอยู่ดังนั้นฉันคิดว่าวิธีการสร้างใหม่ทั่วไปเหมือนที่ฉันทำนั้นถูกต้อง แต่ต้องปรับปรุงรายละเอียดบางอย่างในการใช้งาน


คุณสามารถระบุขั้นตอนที่แน่นอนที่คุณใช้ในการสร้างคอนเทนเนอร์ของคุณรวมถึงคำสั่ง build และเอาต์พุตทั้งหมดจากแต่ละคำสั่งได้หรือไม่
BMitch

คำตอบ:


140

หลังจากการค้นคว้าและทดสอบบางอย่างฉันพบว่าฉันมีความเข้าใจผิดเกี่ยวกับอายุการใช้งานของคอนเทนเนอร์ Docker เพียงแค่รีสตาร์ทคอนเทนเนอร์ไม่ได้ทำให้ Docker ใช้อิมเมจใหม่เมื่ออิมเมจถูกสร้างขึ้นใหม่ในระหว่างนี้ แต่ Docker กำลังดึงภาพก่อนหน้านี้เท่านั้นสร้างคอนเทนเนอร์เท่านั้น ดังนั้นสถานะหลังจากเรียกใช้คอนเทนเนอร์จึงคงอยู่

ทำไมต้องลบ

ดังนั้นการสร้างใหม่และรีสตาร์ทจึงไม่เพียงพอ ฉันคิดว่าคอนเทนเนอร์ทำงานเหมือนบริการ: หยุดบริการทำการเปลี่ยนแปลงของคุณเริ่มต้นใหม่และจะมีผล นั่นเป็นความผิดพลาดครั้งใหญ่ที่สุดของฉัน

เนื่องจากคอนเทนเนอร์เป็นแบบถาวรคุณต้องนำออกโดยใช้docker rm <ContainerName>ก่อน หลังจากนำคอนเทนเนอร์ออกแล้วคุณไม่สามารถเริ่มได้โดยdocker startง่าย ต้องทำโดยใช้docker runซึ่งตัวเองใช้อิมเมจล่าสุดในการสร้างอินสแตนซ์คอนเทนเนอร์ใหม่

ภาชนะควรมีความเป็นอิสระมากที่สุด

ด้วยความรู้นี้จึงเข้าใจได้ว่าทำไมการจัดเก็บข้อมูลในคอนเทนเนอร์จึงมีคุณสมบัติเป็นแนวทางปฏิบัติที่ไม่ดีและ Docker แนะนำให้ใช้ไดรฟ์ข้อมูล / ไดเร็กทอรีโฮสต์ที่ติดตั้งแทนเนื่องจากคอนเทนเนอร์ต้องถูกทำลายเพื่ออัปเดตแอปพลิเคชันข้อมูลที่จัดเก็บไว้ภายในก็จะสูญหายไปด้วย สิ่งนี้ทำให้ต้องทำงานพิเศษในการปิดบริการสำรองข้อมูลและอื่น ๆ

ดังนั้นจึงเป็นโซลูชันที่ชาญฉลาดในการแยกข้อมูลเหล่านั้นออกจากคอนเทนเนอร์โดยสิ้นเชิง: เราไม่ต้องกังวลเกี่ยวกับข้อมูลของเราเมื่อเก็บข้อมูลไว้อย่างปลอดภัยบนโฮสต์และคอนเทนเนอร์จะเก็บเฉพาะแอปพลิเคชันเท่านั้น

ทำไม-rfอาจไม่ช่วยคุณจริงๆ

docker runคำสั่งมีขึ้นสะอาด-rfสวิทช์ที่เรียกว่า มันจะหยุดพฤติกรรมการเก็บคอนเทนเนอร์นักเทียบท่าอย่างถาวร การใช้-rfDocker จะทำลายคอนเทนเนอร์หลังจากออกจากคอนเทนเนอร์แล้ว แต่สวิตช์นี้มีปัญหาสองประการ:

  1. Docker ยังลบไดรฟ์ข้อมูลที่ไม่มีชื่อที่เกี่ยวข้องกับคอนเทนเนอร์ซึ่งอาจฆ่าข้อมูลของคุณ
  2. เมื่อใช้ตัวเลือกนี้จะไม่สามารถเรียกใช้คอนเทนเนอร์ในพื้นหลังโดยใช้-dสวิตช์ได้

แม้ว่า-rfสวิตช์จะเป็นตัวเลือกที่ดีในการบันทึกงานระหว่างการพัฒนาเพื่อการทดสอบอย่างรวดเร็ว แต่ก็ไม่เหมาะสมในการผลิต โดยเฉพาะอย่างยิ่งเนื่องจากตัวเลือกที่ขาดหายไปในการเรียกใช้คอนเทนเนอร์ในพื้นหลังซึ่งส่วนใหญ่จะต้องใช้

วิธีการถอดคอนเทนเนอร์

เราสามารถข้ามข้อ จำกัด เหล่านั้นได้เพียงแค่นำคอนเทนเนอร์ออก:

docker rm --force <ContainerName>

--force(หรือ-f) สวิทช์ที่ใช้ในการทำงาน SIGKILL ตู้คอนเทนเนอร์ คุณสามารถหยุดคอนเทนเนอร์ก่อน:

docker stop <ContainerName>
docker rm <ContainerName>

ทั้งสองมีค่าเท่ากัน docker stopนอกจากนี้ยังมีการใช้SIGTERM แต่การใช้--forceสวิตช์จะทำให้สคริปต์ของคุณสั้นลงโดยเฉพาะเมื่อใช้เซิร์ฟเวอร์ CI: จะdocker stopแสดงข้อผิดพลาดหากคอนเทนเนอร์ไม่ทำงาน สิ่งนี้จะทำให้เจนกินส์และเซิร์ฟเวอร์ CI อื่น ๆ อีกมากมายพิจารณาการสร้างผิดว่าล้มเหลว ในการแก้ไขปัญหานี้คุณต้องตรวจสอบก่อนว่าคอนเทนเนอร์กำลังทำงานตามที่ฉันทำในคำถามหรือไม่ (ดูcontainerRunningตัวแปร)

สคริปต์แบบเต็มสำหรับการสร้างคอนเทนเนอร์ Docker ใหม่

ตามความรู้ใหม่นี้ฉันแก้ไขสคริปต์ของฉันด้วยวิธีต่อไปนี้:

#!/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

ทำงานได้อย่างสมบูรณ์ :)


4
'ฉันพบว่าฉันมีความเข้าใจผิดเกี่ยวกับอายุการใช้งานของคอนเทนเนอร์ Docker' คุณเอาคำพูดนั้นออกจากปากของฉัน ขอบคุณสำหรับคำอธิบายโดยละเอียดดังกล่าว ฉันอยากจะแนะนำสิ่งนี้ให้กับนักเทียบท่ามือใหม่ สิ่งนี้ชี้แจงความแตกต่างของ VM เทียบกับคอนเทนเนอร์
Vince Banzon

2
หลังจากคำอธิบายของคุณสิ่งที่ฉันทำคือจดบันทึกสิ่งที่ฉันทำกับภาพที่มีอยู่ของฉัน เพื่อให้การเปลี่ยนแปลงยังคงอยู่ฉันได้สร้าง Dockerfile ใหม่เพื่อสร้างอิมเมจใหม่ซึ่งมีการเปลี่ยนแปลงที่ฉันต้องการเพิ่มอยู่แล้ว ด้วยวิธีนี้ภาพใหม่ที่สร้างขึ้นจะได้รับการอัปเดต (บ้าง)
Vince Banzon

เป็น--force-recreateตัวเลือกในการเขียนนักเทียบท่าคล้ายกับสิ่งที่คุณอธิบายที่นี่? และถ้าเป็นเช่นนั้นจะไม่คุ้มที่จะใช้วิธีนี้แทน (ขออภัยหากคำถามนั้นโง่ แต่ฉันเป็นนักเทียบท่า noob ^^)
cglacet

2
@cglacet ใช่มันคล้ายกันไม่สามารถเปรียบเทียบได้โดยตรง แต่docker-composeฉลาดกว่าdockerคำสั่งธรรมดา ฉันทำงานเป็นประจำdocker-composeและการตรวจจับการเปลี่ยนแปลงทำงานได้ดีดังนั้นฉันจึงใช้--force-recreateน้อยครั้งมาก เพียงแค่docker-compose up --buildเป็นสิ่งสำคัญเมื่อคุณกำลังสร้างรูปแบบกำหนดเอง ( buildสั่งในไฟล์เขียน) แทนการใช้ภาพจากเช่นฮับเทียบท่า
สิงโต

35

เมื่อใดก็ตามที่มีการเปลี่ยนแปลงใน dockerfile หรือเขียนหรือความต้องการ re-run docker-compose up --buildโดยใช้ เพื่อให้ภาพสร้างใหม่และรีเฟรช


1
มีภาชนะนักเทียบท่า MySQL เป็นบริการหนึ่ง DB จะเป็นที่ว่างเปล่าหลังจากนั้นหากใช้ไดรฟ์สำหรับ/opt/mysql/data:/var/lib/mysql?
Martin Thoma

สำหรับฉันดูเหมือนจะไม่มีข้อเสียใด ๆ ที่จะใช้--buildในสภาพแวดล้อมการพัฒนาท้องถิ่นเสมอไป ความเร็วที่นักเทียบท่าคัดลอกไฟล์ซ้ำซึ่งอาจถือว่าไม่จำเป็นต้องคัดลอกใช้เวลาเพียงไม่กี่มิลลิวินาทีและช่วยประหยัดช่วงเวลา WTF จำนวนมาก
Danack

1

คุณสามารถเรียกใช้buildบริการเฉพาะได้โดยเรียกใช้docker-compose up --build <service name>ที่ชื่อบริการต้องตรงกับวิธีที่คุณเรียกในไฟล์นักเทียบท่า

ตัวอย่าง สมมติว่าไฟล์นักเทียบท่าของคุณมีบริการมากมาย (แอป. net - ฐานข้อมูล - เข้ารหัสกันเถอะ ... ฯลฯ ) และคุณต้องการอัปเดตเฉพาะแอป. net ที่มีชื่อapplicationอยู่ในไฟล์นักเทียบท่า จากนั้นคุณสามารถเรียกใช้docker-compose up --build application

พารามิเตอร์พิเศษ ในกรณีที่คุณต้องการเพิ่มพารามิเตอร์พิเศษให้กับคำสั่งของคุณเช่น-dสำหรับการรันในพื้นหลังพารามิเตอร์ต้องอยู่หน้าชื่อบริการ: docker-compose up --build -d application

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.