Bài viết này gồm 2 phần.
Phần 1, chúng ta sẽ khám phá bên trong container có những gì. Sau đó, chúng ta ngẫm nghĩ xem để build 1 file image, thay vì sử dụng template, chúng ta sẽ tự custom và build template mới.
Phần 2, chúng ta sẽ tìm cách optimize dung lượng file image.
Tạo 1 Docker image đơn giản
Trước khi bắt đầu, chúng ta sẽ xem lại các bước cơ bản để tạo 1 image
Trong Dockerfile, có thể chia ra làm 3 phần cơ bản:
- Sử dụng base image
- Install dependency packages
- Truyền tham số như thế nào để tạo container từ image
#Use an existing docker image as a base
#Download and install dependency
#Tell the image what to do when it starts
#as a container
Ý nghĩa các câu lệnhFROM : Là base image để chúng ta tiến hành build một image mới trên image. Chỉ thị này phải được đặt trên cùng của Dockerfile
FROM <image> [AS <name>]
FROM <image>[:<tag>] [AS <name>]
FROM <image>[@<digest>] [AS <name>]
Vd: FROM ruby || ubuntu || mysql || mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
RUN
Chỉ thị RUN dùng để chạy một lệnh nào đó trong quá trình build image và thường là các câu lệnh Linux.
Ví dụ, để chạy câu lệnh update đối với Ubuntu sẽ là RUN apt-get update -y còn đối với CentOS thì sẽ là Run yum update -y. Kết quả của câu lệnh sẽ được commit lại, kết quả commit đó sẽ được sử dụng trong bước tiếp theo của Dockerfile.
CMD : Sử dụng khi muốn thực thi các command trong quá trình build container mới từ image
Hello World bằng ExpressJs
Để run 1 ứng dụng ExpressJs, bạn cần:
- 1 hệ điều hành
- Cài đặt NodeJs
- Cài đặt Express
- Source code web application
- Khởi động ứng dụng: npm start
Tạo file app.js
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
Tạo file package.json
{
"dependencies": {
"express": "*"
},
"scripts": {
"start": "node app.js"
}
}
Để build image Express, bạn tạo file Dockerfile có nội dung như sau:
# syntax=docker/dockerfile:1
FROM node:18.8
WORKDIR /usr/app
#Copy file and restore as distinct layers
COPY ./ /usr/app/
RUN npm install
#Default command
CMD ["npm", "start"]
Build Express image
docker build -t expressjs .
Output
Sending build context to Docker daemon 2.43MB
Step 1/5 : FROM node:18.8
18.8: Pulling from library/node
1671565cc8df: Pull complete
3e94d13e55e7: Pull complete
fa9c7528c685: Pull complete
53ad072f9cd1: Pull complete
d6b983117533: Pull complete
eac9917c3316: Pull complete
56b8d2414e53: Pull complete
8342b6d78d6f: Pull complete
52437043774d: Pull complete
Digest: sha256:a0a2fc4435b0c9ae7bec0a69b1279323a4a41c5a005581fbf30d39cd5777db37
Status: Downloaded newer image for node:18.8
---> ac3dcfe39c7e
Step 2/5 : WORKDIR /usr/app
---> Running in b8931910d7c3
Removing intermediate container b8931910d7c3
---> d6d9c8709983
Step 3/5 : COPY ./ /usr/app/
---> 1a615975d8ab
Step 4/5 : RUN npm install
---> Running in f22c94db38d6
up to date, audited 58 packages in 507ms
7 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Removing intermediate container f22c94db38d6
---> d4b3c1db3ee7
Step 5/5 : CMD ["npm", "start"]
---> Running in 3661bd98f384
Removing intermediate container 3661bd98f384
---> a45ba4b7be61
Successfully built a45ba4b7be61
Successfully tagged expressjs:latest
Trở lại Terminal, gõ lệnh:
docker images
Kết quả
REPOSITORY TAG IMAGE ID CREATED SIZE
expressjs latest a45ba4b7be61 About a minute ago 993MB
node 18.8 ac3dcfe39c7e 9 days ago 991MB
Run container
docker run -p 1001:3000 -d expressjs
Để khám phá bên trong container, bạn gõ:
docker exec -it <container-id> /bin/bash
Dùng lệnh ls để liệt kê folder và file
/usr/app# ls
Dockerfile app.js node_modules package-lock.json package.json
Vậy ý nghĩa thực sự của dòng khai báo: WORKDIR là bạn khai báo folder. Nhưng folder đó nằm ở đâu.
Tiếp tục, bạn gõ lệnh kiểm tra OS version:
cat /etc/os-release
Kết quả:
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
Đây là phiên bản Debian release vào 9/7/2022. Hiện tại khi viết bài này là 3/9/2022.
Vậy bản chất việc khai báo FROM là bạn khai báo OS kèm thư viện hỗ trợ (trong bài viết này là NodeJS 18.8)
Quay lại ví dụ bên trên, bạn quan sát các dòng output trong quá trình build và các images được liệt kê
Có 1 sự kì lạ là ở Step 1, chúng ta khai báo FROM NodeJs thì Docker tải image về. Và khi tìm hiểu sâu hơn thì đó không phải là 1 library mà là 1 Operating System đã được cài đặt NodeJs.
Về bản chất, Docker coi mỗi dòng lệnh ta khai báo ở trong Dockerfile như là 1 instruction - chỉ dẫn, các instruction RUN, COPY, ADD tạo ra các layer.
Khi ta build image, Docker sẽ đọc từng dòng trong Dockerfile. Với mỗi chỉ dẫn, 1 container mới sẽ được tạo ra, và sau đó 1 image mới được tạo ra từ container đó.
Bạn có thể kiểm tra lại bằng lệnh: docker images -a
Ví dụ ở step 2
Status: Downloaded newer image for node:18.8
---> ac3dcfe39c7e
Step 2/5 : WORKDIR /usr/app
---> Running in b8931910d7c3
Removing intermediate container b8931910d7c3
---> d6d9c8709983
Chúng ta có image với TagId là ac3dcfe39c7e. Container b8931910d7c3 được tạo ra sau đó để thực hiện việc Thêm mới 1 folder và cd tới thư mục mới. Cuối cùng Container b8931910d7c3 được remove đi để tạo ra image mới ở bước tiếp theo.Nếu thấy layer nào ko thay đổi, đã có từ những lần build trước thì Docker sẽ tận dụng lại.
Để kiểm tra quá trình tạo và xóa container, mở 2 màn hình bash
- Bash 1: docker stats -a
- Bash 2: bạn thực hiện việc build Docker image.
Do vậy nếu chúng ta biết cách tổ chức Dockerfile, đưa các thành phần ít thay đổi lên trên, thành phần hay thay đổi xuống dưới thì sẽ tận dụng được tính năng tuyệt vời này và giảm đáng kể thời gian chờ build image.
Tham khảo
https://viblo.asia/p/tang-toc-do-build-va-toi-gian-docker-image-Eb85oODB52G
https://iximiuz.com/en/posts/you-need-containers-to-build-an-image/
Bổ sung câu lệnh: RUN sleep 2 để làm chậm tiến trình build Docker image
Trả lờiXóa