Setup CI/CD with Gitlab, Spring Boot and Docker
Hí anh em!
Hôm nay rảnh quá, chưa biết phải làm gì, đang lướt facebook thì có cái quảng cáo về khoá học Docker thì chợt nhớ ra là mình đã muốn viết bài về Docker từ lâu nhưng mà do lười nên tiện đang rảnh thì viết luôn vậy!
Nhưng mà có mỗi Docker thì không có gì thú vị lắm nên mình sẽ bế thêm cả Gitlab vào cho vui. , còn code example thì mình dùng Spring Boot cho nó thân thiện
Bắt đầu
- Thường thi khi chúng ta code xong, sẽ phải build, test, deploy thủ công, rất là mất thời gian. Thay vị phải làm mọi thứ thủ công thì chúng ta sẽ dùng một qyu trình triển khai tự động có tên là CI/CD
- Nếu bạn là người có kiến thức về CI/CD thì bài viết này sẽ là vô nghĩa. Tất nhiên là sẽ có rất nhiều kiến thức nâng cao khác về CI/CD nhưng phạm vi bài viết này sẽ giúp các bạn nào cần tìm một công nghệ, quy trình triển khai tự động cho các dự án cá nhân của mình,
- Đây là một quy trình mình sẽ setup cơ bản, ở các doanh nghiệp tuỳ thuộc vào bảo mật, công nghệ, độ scale của dự án mà cách triển khai cũng sẽ khác nhau!
Mục đích CI/CD
Ngày xửa ngày xưa, có một cô gái người Nhật Bổn tên là Eimi Fukada, cô là chủ một tiệm bánh nhỏ trong làng. Tiệm bánh của cô rất nhỏ nên cô chỉ có thể ship đến cho khách hàng hoặc khách hàng tự đến lấy. Do cô ngon à quên do bánh của cô ngon và hấp dẫn, nhanh chóng có rất nhiều chàng trai trong làng mua bánh của cô. Càng ngày, số lượng chàng trai tìm đến tiệm bánh cô càng tăng lên.
Nhưng Eimi có một vấn đề đáng lo ngại là mỗi một mẻ bánh mới ra lò cô đều phải tự tay kiểm tra từng chiếc bánh và phải kiểmn tra chất lượng một cách thật tỉ mỉ mà kỹ lưỡng. Điều này dẫn đến việc thời gian chờ đợi cho khách hàng cũng tăng lên.
Eimi hiểu rằng để giữ chân và làm hài lòng khách hàng, cô cần phải giảm thiểu thời gian chờ đợi và vẫn phải giữ được chất lượng hoàn hảo nhất mỗi khi đến tay khách hàng của mình, cô chơi lớn, quyết định cải thiện quy trình sản xuất bánh của mình.
Với mong muốn cải thiện tiệm bánh của mình, Eimi đã quyết định áp dụng một phương pháp phát triển phần mềm được gọi là CI/CD (Continuous Integration/Continuous Deployment). CI/CD là một quy trình phát triển phần mềm mà Eimi tin tưởng sẽ giúp cô tối ưu hóa sản xuất bánh và giảm thiểu lỗi trong quá trình làm bánh.
Với CI/CD, Eimi đã tạo ra một hệ thống tự động hóa quy trình sản xuất bánh. Thay vì làm mọi thứ một cách thủ công, cô đã sử dụng công nghệ để tự động hóa các bước quan trọng trong quy trình làm bánh.
Đầu tiên, cô đã xây dựng và lên kịch bản cho một hệ thống tự động kiểm tra liên tục mỗi khi có một mẻ bánh mới ra lò xem có chiếc bánh nào bị lỗi hay không. Điều này giúp cô tiết kiệm thời gian và tăng năng suất. ( CI, Continuous Integration )
Tiếp theo, Eimi đã xây dựng một hệ thống tự động đóng gói và đóng hộp bánh. Cô đã lắp đặt các máy móc để tự động đóng gói bánh và gắn nhãn. Việc này giúp cô tiết kiệm công sức và đảm bảo rằng bánh luôn được đóng gói một cách chính xác và chuyên nghiệp trước khi tới tay khách hàng. (CD, Continuous Deployment
Nhờ việc áp dụng CI/CD, Eimi đã giảm thiểu rất nhiều thời gian trong quy trình sản xuất bánh. Điều này có nghĩa là khách hàng không cần phải chờ đợi lâu để có được bánh của cô. Hơn nữa, việc tự động hóa quy trình cũng giảm thiểu sai sót và lỗi trong quá trình làm bánh, đảm bảo chất lượng sản phẩm luôn cao nhất.
Kết quả là, từ khi Eimi áp dụng CI/CD, tiệm bánh của cô trở nên phổ biến hơn bao giờ hết. Khách hàng không chỉ đến tiệm cô để mua bánh, mà họ còn khen ngợi quy trình sản xuất tối ưu và chất lượng bánh tuyệt vời. Điều này đã thu hút thêm nhiều khách hàng mới và giữ chân khách hàng cũ.
Với việc áp dụng CI/CD, Eimi Fukada đã biến tiệm bánh nhỏ của mình thành một điểm đến hấp dẫn với quy trình sản xuất tối ưu và bánh ngon tuyệt vời.
Khái niệm
Docker
Khi mà tiệm bánh của Eimi đã quá nổi tiếng vì độ ngon và hấp dẫn, ở ngôi làng của cô và các ngôi làng xung quanh không ai là không biết đến tiệm bánh của cô.
Một ngày nọ, có một phú ông tên là Tokuda đến tìm cô và ngỏ lời muốn nhượng quyền lại tiệm bánh của cô và nhân rộng tiệm bánh ra toàn cõi Nhật Bổn, tăng độ nổi tiếng cho tiệm bánh. Công việc của cô chỉ là cung cấp nguyên liệu làm bánh cho ông ta và cô sẽ thu được lợi nhuận từ việc này!
Vì nghĩ chỉ việc cung cấp nguyên liệu thì quá dễ nên đồng ý mà không cần suy nghĩ cho ông ta nhượng quyền thương hiệu tiệm bánh của cô. Những tường rằng sau khi nhượng quyền thì độ nổi tiếng của tiệm bánh sẽ lan rộng toàn cõi Nhật, nhưng không, trên website của tiệm của nhận về rất nhiều feedback chê bai chất lượng bánh của cô. Cô rất tức giận và lập tức đi đến tiệm bánh mà cô đã nhượng quyền, cô phát hiện ra tất cả các máy móc để sản suất ra những chiếc bánh trong tiệm toàn là hàng Tàu Khựa rẻ tiền, cho dù nguyên liệu của cô có chất lượng đến mấy thì khi cho vào những chiếc máy này sản phẩm sẽ là những chiếc bánh kém chất lượng và lỗi.
Cô buồn bã, nhưng không bỏ cuộc, cô tìm tòi trên internet và biết được có dịch vụ có tên là Docker công cụ này dùng để đóng gói tất cả các máy móc giống y như các máy móc trong tiệm cô. Cô đóng gói lại tất cả các máy móc rồi đem sang chỗ ông Tokuda và ném thẳng vào mặt ông ý rồi nói "hãy dùng loại máy này để thay thế cho mấy chiếc máy Tàu Khựa của ông đi".
Đúng như dự đoán, sau khi áp dụng thì những mẻ bánh mới ra lò toàn là những mẻ bánh đẹp, không có lỗi. Khách hàng lại feedback rất tốt trên website của tiệm bánh, vì thế nên tiệm bánh càng ngày càng nổi tiếng hơn!
- đến đây thì các bạn cũng đã hiểu hơn về Docker rồi nhỉ, mình mong là các bạn hiểu ><.
Gitlab Ci
- Chúng ta sẽ dùng Gitlab Ci để triển khai phần CI/CD này. Có rất nhiều các công cụ để triển khai CI/CD như : Jenkins, Github Action ...v.v. Nhưng vì tiện và Gitlab mình cũng đang sử dụng ở công ty và các dự án cá nhân nên mình sẽ dùng nó!
Triển khai
Trước khi bắt tay vào làm thì các bạn phải có cho mình những thứ sau:
1. Tìm hiểu khái niệm về docker
2. Tài khoản hub.docker.com hoặc registry của các bạn để push images docker
3. Docker desktop đã được cài vào máy tính
4. Tài khoản gitlab để commit code và triển khai CI/CD
Create project Spring Boot
- Sau khi có những thứ trên thì chúng ta bắt đầu bằng việc tạo một project spring boot, các bạn có thể vào https://start.spring.io/ để tạo 1 project spring boot đơn giản nhanh chóng
- Sau khi tạo project xong thì mình sẽ tạo 1 entity là Student:
package com.student;
public class Student {
private Long id;
private String name;
private String className;
private int age;
private String address;
private int englishScore;
private int literatureScore;
private int mathScore;
private int physicsScore;
private int chemistryScore;
public Student() {
}
public Student(String name, String className, int age, String address, int englishScore, int literatureScore, int mathScore, int physicsScore, int chemistryScore) {
this.name = name;
this.className = className;
this.age = age;
this.address = address;
this.englishScore = englishScore;
this.literatureScore = literatureScore;
this.mathScore = mathScore;
this.physicsScore = physicsScore;
this.chemistryScore = chemistryScore;
}
// Getter và Setter cho các trường dữ liệu
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getEnglishScore() {
return englishScore;
}
public void setEnglishScore(int englishScore) {
this.englishScore = englishScore;
}
public int getLiteratureScore() {
return literatureScore;
}
public void setLiteratureScore(int literatureScore) {
this.literatureScore = literatureScore;
}
public int getMathScore() {
return mathScore;
}
public void setMathScore(int mathScore) {
this.mathScore = mathScore;
}
public int getPhysicsScore() {
return physicsScore;
}
public void setPhysicsScore(int physicsScore) {
this.physicsScore = physicsScore;
}
public int getChemistryScore() {
return chemistryScore;
}
public void setChemistryScore(int chemistryScore) {
this.chemistryScore = chemistryScore;
}
}
- Sau đó mình tạo 1 package có tên là controller và 1 class controller có tên là StudentController để hiển thị list học sinh dưới dạng json và có enpoint là students như thế này,
package com.student.controller;
import com.student.Student;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/students")
public class StudentController {
@GetMapping
public List<Student> getStudents() {
List<Student> students = new ArrayList<>();
// Thêm các sinh viên vào danh sách
students.add(new Student("John", "12A", 17, "123 Main St", 85, 75, 90, 80,90));
students.add(new Student("Jane", "11B", 16, "456 Park Ave", 90, 80, 85, 75,89));
students.add(new Student("Alice", "10C", 15, "789 Oak St", 80, 85, 70, 90,78));
return students;
}
}
- Rồi bây giờ chúng ta chạy project lên và truy cập vào url : http://localhost:8080/students thì sẽ hiển thị như này
Create Dockerfile
Dockerfile hiểu đơn giản nó giống một cái kịch bản để xây dựng một cái image, nó sẽ định nghĩa từng bước để có thể build được 1 image
- Sau khi tạo project xong thì chúng ta cùng tạo một Dockerfile ở thư mục gốc của dự án,
*chú ý: Dockerfile thì chữ D phải viết hoa nhé!*
FROM eclipse-temurin:17-jdk-alpine
VOLUME /tmp
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
- Giải thích:
- FROM eclipse-temurin:17-jdk-alpine bắt đầu Dockerfile phải có lệnh FROM, đây sẽ là image base, giống như jdk trên máy ở đây mình dung image eclipse-temurin:17-jdk-alpine vì nó phổ biến nhất
- COPY target/.jar app.jar* coppy file jar đã được build vào thư mục app
- Nếu các bạn không build file jar trước thì các bạn phải định nghĩa lệnh build RUN mvn package, với Dockerfile này bạn không build jar trước khi chạy sẽ bị lỗi
- Có thể để lệnh build kia vào trước lệnh COPY target/.jar .* cũng được, nhưng mình thích để trong file .gitlab-ci.yml hơn
- EXPOSE 8080 đây là cổng để giao tiếp giữa các container với nhau, có thể bỏ cái này đi cũng được
- ENTRYPOINT ["java","-jar","/app.jar"] các bạn để ý: java -jar /app.jar chính là câu lệnh dùng để run project java trong comand line, câu lệnh này trong Dockerfile cũng tương tự
Chỉ cần bấy nhiêu đó thì chúng ta có thể build được một image docker rồi
Setup Gitlab Ci
-
Mình mặc định là bạn đã có tài khoản gitlab rồi nhé
Tạo 1 repo mới click "New Project" trong trang chủ, chọn "Create Blank Project" mình sẽ đặt tên là "student-gitlab" rồi click "Create"
Remote repo mình sẽ remote git vào project spring boot mình đã tạo bên trên
- Các bạn mở Terminal lên, với windows thì Cmd rồi cd vào project rồi paste *git remote add origin https://gitlab.com/thaond1205/student-gitlab.git* bạn hãy thay link git của bạn vào - Tiếp theo hãy commit đống code vừa nãy viết lên nhánh main nhé
Nó chạy 100% như này là okee
Create .gitlab-ci
- Trước khi tạo file này thì các bạn hãy tạo cho mình 1 repo trên https://hub.docker.com/ với tên tuỳ chọn theo các bạn, mình sẽ đặt tên là student-gitlab
- Bây giờ mình sẽ tạo 1 file có tên .gitlab-ci.yml ở thư mục gốc của dự án
File này khi được commit lên, gitlab sẽ tự động tìm đến file này và sẽ chạy theo từng bước trong file giống như Dockerfile vậy!
image: gitlab/dind # sử dụng môi trường gitlab
services:
- docker:dind # sử dụng docker để run các thành phần bên dưới
variables:
IMAGE_NAME: thaond12052001/student-gitlab # iamge mà các bạn vừa tạo, nhớ thay tên username và tên image bạn vừa tạo ở trên
stages: # định nghĩa các bước, ở đây minhd có 3 bước tương ứng như bên dưới
- build
- test
- docker
maven-build: # đây chính là bước build file .jar mà mình đã đề cập ở bên trên
image: maven:3.8.1-openjdk-17-slim
stage: build
script: "./mvn clean package"
artifacts:
paths:
- target/*.jar
maven-test: # run unit test của project
image: maven:3.8.1-openjdk-17-slim
stage: test
script: "mvn test"
artifacts:
paths:
- target/*.jar
docker-build: # bước này sẽ build iamge từ Dockerfile và push lên docker hub
stage: docker
script:
- docker login -u $USERNAME_DOCKER -p $PASSWORD_DOCKER # nhập tên username và password docker hub của các bạn ở đây để có quyền push
- docker build -t $IMAGE_NAME:1.0 . # build image, 1.0 là tag coi nó như version
- docker push $IMAGE_NAME:1.0 # push image docker hub sau khi build thành công
Mỗi lần commit future mới thì bạn có thể thay đổi tag image để có push tag mới, hoặc bạn có thể giữ nguyên và tag sau sẽ overide lại tag trước
Commit code
- Bây giờ mình sẽ commit file git và xem có có tự động Build, Test, Build Image, Push to Docker Hub hay không
Sau khi push lên thì đến stage docker-build thì nó tạch luôn , mấy cái fail ở dứoi là do mình sai syntax file yml hihi
Hãy check log xem nó bị lỗi gì. Click vào stage bị fail, chính là cái dấu x đỏ kia
Xem log thì thấy là mình chưa tạo biến môi trường $USERNAME_DOCKER và $PASSWORD_DOCKER nên không login vào được docker hub
Bây giờ mình sẽ tạo biến môi trường,
Click Setting => CI/CD => Variables => add variables và add như bên dưới
Mình dùng biến môi trường để ẩn đi những thông tin nhạy cảm này, các bạn nên nhét những thông tin này vào biến môi trường nhé!
- Sau khi tạo biến môi trường xong thì vào lại phần Pipeline để restart lại nhé!
- Kiểm tra xem đã pass hết các stages chưa, nếu rồi thì vào https://hub.docker.com/ và vào repo vừa tạo => mở tab Tag xem đã lên chưa!
- Đã lên rồi này, giờ thì chúng ta run cái image vừa build xong em chạy có ngon không nhá!
- Mở Docker desktop lên và chạy câu lệnh này:
-
docker run -p 8888:8080 thaond12052001/student-gitlab:1.0
- Lệnh docker run này sẽ anh xạ từ port 8080 ra port 8888
Và truy cập http://localhost:8888/students và sẽ thấy như này là thành công rồi
- Như vậy là mình đã hướng dẫn xong phần setup CI/CD, đã build,test,push image docker tự động mà bạn không phải làm thủ công nữa rồi
- Bạn có thể mang image vừa build deploy lên cloud hay server của bạn thôi
Còn phần deploy image vừa tạo lên cloud hoặc server nữa thì mình sẽ làm sau vì bài viết này cũng đã dài rồi!
soucre: https://gitlab.com/thaond1205/student-gitlab.git
Kết
- Cảm ơn các bạn đã đọc bài viết này, mong các bạn sẽ hiểu được phần nào và áp dụng nó cho những dự án của mình nhé!
All rights reserved