Trong bài viết này, mình sẽ hướng dẫn các bạn cách deploy một ứng dụng Spring Boot lên Docker các bạn nhé!
Các bước chúng ta cần phải làm là:
- Tạo một Spring Boot project đơn giản, có khai báo sử dụng database nhưng sẽ không có thao tác nào đến database cả.
- Viết Dockerfile
- Deploy ứng dụng lên Docker Container.
OK, bắt đầu nào các bạn.
Tạo Spring Boot project
Mình sẽ sử dụng Spring Tool Suite để tạo một Spring Boot project với Web, Data Jpa Starter và PostgreSQL driver dependencies như sau:
Kết quả:
Để đơn giản, mình sẽ mở tập tin SpringBootDockerApplication:
1 2 3 4 5 6 7 8 9 10 11 12 |
package com.huongdanjava.springbootdocker; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringBootDockerApplication { public static void main(String[] args) { SpringApplication.run(SpringBootDockerApplication.class, args); } } |
và modify nó một chút để thêm một RESTful Web Service return về chuỗi “Hello Docker! Idle connection to database is 10” như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package com.huongdanjava.springboot; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.jdbc.metadata.HikariDataSourcePoolMetadata; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.zaxxer.hikari.HikariDataSource; @SpringBootApplication @RestController public class SpringBootDockerApplication { @Autowired private DataSource dataSource; @RequestMapping("/hello") public String helloDocker() { Integer idleConnection = new HikariDataSourcePoolMetadata((HikariDataSource) dataSource).getIdle(); return "Hello Docker! Idle connection to database is " + idleConnection; } public static void main(String[] args) { SpringApplication.run(SpringBootDockerApplication.class, args); } } |
Mặc định Spring Data Jpa sử dụng Hikari cho phần connection pool và như các bạn thấy, mình sử dụng Hikari để lấy ra số idle connection tới database.
Database configuration được cấu hình trong tập tin application.properties như sau:
1 2 3 |
spring.datasource.url=jdbc:postgresql://${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_NAME} spring.datasource.username=${DATABASE_USERNAME} spring.datasource.password=${DATABASE_PASSWORD} |
Kết quả khi mình chạy chương trình với:
như sau:
Bây giờ, mình sẽ viết một Dockerfile để deploy ứng dụng của chúng ta sử dụng Docker Container.
Viết Dockerfile
Mình sẽ tạo mới một tập tin Dockerfile nằm trong thư mục của project:
Bây giờ, chúng ta sẽ sửa Dockerfile này để xây dựng một Docker Image nhé các bạn.
- Đầu tiên là mình sẽ sử dụng một Image trên Docker Hub để build Image cho chúng ta.
Image đó có tên là 11.0.11-jre nằm trong repository openjdk. Do đó, câu lệnh FROM của mình sẽ có nội dung như sau:
1 |
FROM openjdk:11.0.11-jre |
- Tiếp theo mình sẽ link thư mục /tmp của Docker Container vào thư mục của Docker (/Applications/Docker.app/Contents/MacOS).
Ở đây, chúng ta sẽ sử dụng câu lệnh VOLUME nhé các bạn:
1 |
VOLUME /tmp |
- Để chạy ứng dụng, các bạn cần khai báo một số biến môi trường liên quan đến cấu hình database như trên:
1 2 3 4 5 |
ENV DATABASE_HOST=host.docker.internal ENV DATABASE_PORT=5432 ENV DATABASE_NAME=test ENV DATABASE_USERNAME=khanh ENV DATABASE_PASSWORD=1 |
Ở đây, mình đang sử dụng PostgreSQL database server được cài đặt trên máy của mình nên mình khai báo database host là host.docker.internal.
- Giờ mình sẽ copy tập tin spring-boot-docker-0.0.1-SNAPSHOT.jar nằm trong thư mục target của project vào Docker Container.
1 |
ADD target/spring-boot-docker-0.0.1-SNAPSHOT.jar app.jar |
- Và cuối cùng thực thi câu lệnh để chạy ứng dụng Spring Boot của chúng ta mỗi khi Docker Container của chúng ta được chạy.
1 |
ENTRYPOINT exec java -jar app.jar |
Nội dung toàn bộ của Dockerfile như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
FROM openjdk:11.0.11-jre VOLUME /tmp ENV DATABASE_HOST=host.docker.internal ENV DATABASE_PORT=5432 ENV DATABASE_NAME=test ENV DATABASE_USERNAME=khanh ENV DATABASE_PASSWORD=1 ADD target/spring-boot-docker-0.0.1-SNAPSHOT.jar app.jar ENTRYPOINT exec java -jar app.jar |
Deploy ứng dụng lên Docker Container
Để deploy ứng dụng Spring Boot lên Docker Container từ Dockerfile, trước tiên chúng ta phải tạo Docker Image từ Dockerfile.
Các bạn hãy mở Terminal lên và đi đến thư mục của project, sau đó hãy nhập dòng lệnh sau:
1 |
mvn clean package -DskipTests && docker build -t spring-boot-docker . |
Câu lệnh trên có 2 phần:
- Phần đầu tiên là chúng ta sẽ dùng Maven để build ứng dụng Spring Boot, mình skip tất cả unit test vì nó không phải là mục tiêu của bài viết này.
- Phần tiếp theo là dùng Docker để build Docker Image từ Dockerfile.
Sau khi chạy câu lệnh trên, kiểm tra tất cả Docker Image, các bạn sẽ thấy Docker Image mà chúng ta vừa tạo như sau:
Và bây giờ chúng ta có thể chạy Docker Container từ Docker Image mà chúng ta vừa tạo rồi.
1 |
docker run -p 8080:8080 -t spring-boot-docker |
Kết quả:
Ở đây, mình đã mapping port của máy mình với port của Docker Container là 8080 luôn, nên để kiểm tra kết quả, các bạn cũng truy cập vào http://localhost:8080/hello các bạn nhé!
duy
Hi bạn, làm sao để mình truyền biến lúc run docker vào Chương trình java vậy? như biến confif database chẳng hạn.
ThanhLong
Chào bạn, bạn cho mình hỏi làm thể nào để xem được log của app đang chạy trong docker với ?
Khang
Hi anh,
Em đã tạo project và thực hiện như hướng dẫn nhưng có lỗi khi build ạ. Anh có thể xem giúp em không anh?
Khang
Sending build context to Docker daemon 17.39MB
Error response from daemon: No build stage in current context
Khanh Nguyen
Em gửi anh nội dung file Dockerfile của em nhé!
huy quoc
Cho minh hoi chut. Dung docker de chay tren tomcat duoc khong. docker co the chay load balancing duoc kh ban
Khanh Nguyen
Docker chạy trên tomcat là sao bạn nhỉ?
Kien
Cho em hỏi lúc ở hình thứ 2 không có thư mục target. Mà sao hình lúc viết Dokerfile lại có thư mục target vậy? Em không thấy file spring-boot-docker-0.0.1-SNAPSHOT.jar .
À với em đang dùng gradle chứ không phải maven. Thế thì câu lệnh ở dưới sẽ thay đổi như thế nào ạ?
mvn clean package && docker build -t spring-boot-docker .
Thanks
Khanh Nguyen
Khi em chạy ứng dụng thì sẽ có thư mục target do Maven build tạo ra em nhé!
MrLEE
Mình không truy cập được qua http://localhost:8080/hello. Mặc dù lệnh docker ps ra kết quả như hướng dẫn. Có phải cấu hình gì về IP của docker để có thể truy cập qua URL không?
Khanh Nguyen
Bạn gửi mình nội dung tập tin Dockerfile mà bạn đang có nhé.
MrLEE
Đây là nội dung trên Dockerfile:
FROM openjdk:8u141-jre
VOLUME /tmp
ADD target/spring-boot-docker-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT exec java -jar app.jar
Làm giống hệt của bạn.
Khanh Nguyen
Bạn phải chạy docker run -p 8080:8080 -t spring-boot-docker nữa nhé. Quan trọng là cái này đấy.
MrLEE
Mình chạy được rồi, cảm ơn bạn nhé.