Đây là đóng góp của tôi.
Tôi sẽ không cố gắng liệt kê tất cả các công cụ / thư viện / plugin tồn tại để tận dụng Docker với Maven. Một số câu trả lời đã làm được.
thay vào đó, tôi sẽ tập trung vào phân loại ứng dụng và cách thức Dockerfile.
Dockerfile
thực sự là một khái niệm đơn giản và quan trọng về Docker (tất cả các hình ảnh được biết đến / công khai đều dựa vào đó) và tôi nghĩ rằng cố gắng tránh hiểu và sử dụng Dockerfile
s không nhất thiết là cách tốt hơn để tham gia vào thế giới Docker.
Tài liệu hóa một ứng dụng phụ thuộc vào chính ứng dụng đó và mục tiêu cần đạt đến
1) Đối với các ứng dụng mà chúng tôi muốn tiếp tục chạy chúng trên máy chủ Java được cài đặt / độc lập (Tomcat, JBoss, v.v.)
Con đường khó hơn và đó không phải là mục tiêu lý tưởng vì điều đó làm tăng thêm sự phức tạp (chúng tôi phải quản lý / bảo trì máy chủ) và nó kém khả năng mở rộng và kém nhanh hơn so với các máy chủ nhúng về mặt xây dựng / triển khai / hủy triển khai.
Nhưng đối với các ứng dụng kế thừa, đó có thể được coi là bước đầu tiên.
Nói chung, ý tưởng ở đây là xác định hình ảnh Docker cho máy chủ và xác định hình ảnh cho mỗi ứng dụng để triển khai.
Hình ảnh docker cho các ứng dụng tạo ra WAR / EAR dự kiến nhưng chúng không được thực thi dưới dạng vùng chứa và hình ảnh cho ứng dụng máy chủ triển khai các thành phần được tạo ra bởi các hình ảnh này dưới dạng các ứng dụng được triển khai.
Đối với các ứng dụng khổng lồ (hàng triệu dòng mã) với rất nhiều nội dung cũ và quá khó để chuyển sang giải pháp nhúng khởi động mùa xuân đầy đủ, đó thực sự là một cải tiến tốt.
Tôi sẽ không trình bày chi tiết hơn về cách tiếp cận đó vì đó là dành cho các trường hợp sử dụng nhỏ của Docker nhưng tôi muốn trình bày ý tưởng tổng thể của cách tiếp cận đó bởi vì tôi nghĩ rằng đối với các nhà phát triển phải đối mặt với những trường hợp phức tạp này, thật tuyệt khi biết rằng một số cánh cửa được mở ra tích hợp Docker.
2) Đối với các ứng dụng tự nhúng / bootstrap máy chủ (Spring Boot với máy chủ nhúng: Tomcat, Netty, Jetty ...)
Đó là mục tiêu lý tưởng với Docker . Tôi đã chỉ định Spring Boot vì đó là một khuôn khổ thực sự tốt để làm điều đó và điều đó cũng có mức độ bảo trì rất cao nhưng về lý thuyết, chúng tôi có thể sử dụng bất kỳ cách Java nào khác để đạt được điều đó.
Nói chung, ý tưởng ở đây là xác định một hình ảnh Docker cho mỗi ứng dụng để triển khai.
Hình ảnh docker cho các ứng dụng tạo ra một JAR hoặc một tập hợp các tệp JAR / lớp / cấu hình và chúng bắt đầu một JVM với ứng dụng (lệnh java) khi chúng ta tạo và khởi động một vùng chứa từ các hình ảnh này.
Đối với các ứng dụng mới hoặc ứng dụng không quá phức tạp để di chuyển, cách đó phải được ưu tiên hơn các máy chủ độc lập vì đó là cách tiêu chuẩn và là cách sử dụng vùng chứa hiệu quả nhất.
Tôi sẽ trình bày chi tiết cách tiếp cận đó.
Tài liệu hóa một ứng dụng maven
1) Không có Spring Boot
Ý tưởng là tạo một lọ béo với Maven (plugin lắp ráp maven và plugin bóng maven giúp thực hiện điều đó) chứa cả các lớp đã biên dịch của ứng dụng và các phụ thuộc maven cần thiết.
Sau đó, chúng tôi có thể xác định hai trường hợp:
nếu ứng dụng là ứng dụng dành cho máy tính để bàn hoặc ứng dụng tự trị (không cần phải được triển khai trên máy chủ): chúng tôi có thể chỉ định như CMD/ENTRYPOINT
trong quá Dockerfile
trình thực thi java của ứng dụng:java -cp .:/fooPath/* -jar myJar
nếu ứng dụng là một ứng dụng máy chủ, chẳng hạn như Tomcat, thì ý tưởng là giống nhau: lấy một cái lọ béo của ứng dụng và chạy một JVM trong CMD/ENTRYPOINT
. Nhưng ở đây có một sự khác biệt quan trọng: chúng ta cần bao gồm một số thư viện logic và cụ thể ( org.apache.tomcat.embed
thư viện và một số thư viện khác) khởi động máy chủ nhúng khi ứng dụng chính được khởi động.
Chúng tôi có một hướng dẫn toàn diện trên trang web heroku .
Đối với trường hợp đầu tiên (ứng dụng tự trị), đó là một cách đơn giản và hiệu quả để sử dụng Docker.
Đối với trường hợp thứ hai (ứng dụng máy chủ), hoạt động nhưng không thẳng, có thể dễ xảy ra lỗi và không phải là một mô hình có thể mở rộng vì bạn không đặt ứng dụng của mình trong khung của một khuôn khổ hoàn thiện như Spring Boot. những thứ này cho bạn và cũng cung cấp mức độ mở rộng cao.
Nhưng điều đó có một lợi thế: bạn có mức độ tự do cao vì bạn sử dụng trực tiếp API Tomcat được nhúng.
2) Với Spring Boot
Cuối cùng, chúng ta bắt đầu.
Điều đó vừa đơn giản, hiệu quả và được ghi chép rất đầy đủ.
Thực sự có một số cách tiếp cận để tạo một ứng dụng Maven / Spring Boot chạy trên Docker.
Việc phơi bày tất cả chúng sẽ lâu và có thể nhàm chán.
Sự lựa chọn tốt nhất phụ thuộc vào yêu cầu của bạn.
Nhưng dù theo cách nào thì chiến lược xây dựng về các lớp docker đều giống nhau.
Chúng tôi muốn sử dụng một bản dựng nhiều giai đoạn: một bản dựa vào Maven để phân giải phụ thuộc và cho bản dựng và một bản khác dựa vào JDK hoặc JRE để khởi động ứng dụng.
Xây dựng giai đoạn (Hình ảnh Maven):
- bản sao pom vào hình ảnh
- phụ thuộc và tải xuống plugin.
Về điều đó, mvn dependency:resolve-plugins
xích để mvn dependency:resolve
có thể làm công việc nhưng không phải luôn luôn.
Tại sao ? Bởi vì các plugin này và việc package
thực thi để đóng gói lọ béo có thể phụ thuộc vào các tạo tác / plugin khác nhau và thậm chí đối với cùng một tạo tác / plugin, chúng vẫn có thể tạo ra một phiên bản khác. Vì vậy, một cách tiếp cận an toàn hơn trong khi có khả năng chậm hơn là giải quyết các phụ thuộc bằng cách thực thi chính xác mvn
lệnh được sử dụng để đóng gói ứng dụng (sẽ kéo chính xác các phụ thuộc mà bạn cần) nhưng bằng cách bỏ qua biên dịch nguồn và xóa thư mục đích để làm cho quá trình xử lý nhanh hơn và ngăn chặn bất kỳ phát hiện thay đổi lớp không mong muốn nào cho bước đó.
- bản sao mã nguồn vào hình ảnh
- đóng gói ứng dụng
Giai đoạn chạy (hình ảnh JDK hoặc JRE):
- sao chép bình từ giai đoạn trước
Đây là hai ví dụ.
a) Một cách đơn giản mà không cần bộ nhớ cache cho các phụ thuộc maven đã tải xuống
Dockerfile:
FROM maven:3.6-jdk-11 as maven_build
WORKDIR /app
COPY pom.xml .
RUN mvn clean package -Dmaven.test.skip -Dmaven.main.skip -Dspring-boot.repackage.skip && rm -r target/
COPY src ./src
RUN mvn clean package -Dmaven.test.skip
RUN mkdir -p target/docker-packaging && cd target/docker-packaging && jar -xf ../my-app*.jar
FROM openjdk:11.0-jre
WORKDIR /app
ARG DOCKER_PACKAGING_DIR=/app/target/docker-packaging
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/lib /app/lib
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/classes /app/classes
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/META-INF /app/META-INF
CMD java -cp .:classes:lib
Mặt hạn chế của giải pháp đó? Bất kỳ thay đổi nào trong pom.xml có nghĩa là tạo lại toàn bộ lớp tải xuống và lưu trữ các phụ thuộc maven. Điều đó thường không được chấp nhận đối với các ứng dụng có nhiều phụ thuộc (và Spring Boot kéo nhiều phụ thuộc), về tổng thể nếu bạn không sử dụng trình quản lý kho lưu trữ maven trong quá trình xây dựng hình ảnh.
b) Một cách hiệu quả hơn với bộ nhớ cache cho các phụ thuộc maven đã tải xuống
Cách tiếp cận ở đây là tương tự nhưng các bản tải xuống phụ thuộc maven được lưu trong bộ đệm ẩn của trình tạo docker.
Hoạt động của bộ đệm dựa trên bộ xây dựng (api thử nghiệm của docker).
Để kích hoạt bộ xây dựng, biến env DOCKER_BUILDKIT = 1 phải được đặt (bạn có thể làm điều đó ở nơi bạn muốn: .bashrc, dòng lệnh, tệp json daemon docker ...).
Dockerfile:
FROM maven:3.6-jdk-11 as maven_build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN --mount=type=cache,target=/root/.m2 mvn clean package -Dmaven.test.skip
RUN mkdir -p target/docker-packaging && cd target/docker-packaging && jar -xf ../my-app*.jar
FROM openjdk:11.0-jre
WORKDIR /app
ARG DOCKER_PACKAGING_DIR=/app/target/docker-packaging
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/lib /app/lib
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/classes /app/classes
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/META-INF /app/META-INF
CMD java -cp .:classes:lib
mavenCentral()
phần phụ thuộc vào lớp của mình bằngmaven {url "http://nexus:8081..."
và bây giờ chỉ nhận được các vấn đề giải quyết.