Có nhiều cách để optimize container images:
- Sử dụng Minimal Base Image: mình sẽ viết 1 bài này trong tương lai. Cơ bản là sẽ sử dụng Alpine với Distroless Image. Trước tiên, bạn cần khám phá bên trong một images: https://nhatkyhoctap.blogspot.com/2022/09/docker-kham-pha-qua-trinh-build-1-image.html
- Multistage image: https://nhatkyhoctap.blogspot.com/2022/09/docker-kham-pha-qua-trinh-build-1-image_14.html
- Giảm số lượng layer
- Caching
- DockerIgnore: https://nhatkyhoctap.blogspot.com/2022/09/docker-kham-pha-qua-trinh-build-1-image_14.html
- Sử dụng Volume để lưu trữ Applicition Data
Trong bài viết này, mình sẽ hướng dẫn các bạn cách copy tự động file *.csproj thay vì phải manually. Một là để tránh việc tạo nhiều layer. Hai là hạn chế sai sót trong quá trình copy.
Bạn có thể tham khảo bài viết: https://nhatkyhoctap.blogspot.com/2022/08/docker-tao-docker-file-cho-nhieu.html để lấy source code
Bài toán
Cấu trúc thư mục Project như sau:
📁 containerize-a-net-app
└──DockerizeMultiProject.sln
└──📂 DockerizeMultiProject
├──DockerizeMultiProject.csproj
├──*.*
└──📂 DockerizeMultiProject.Domain
├──DockerizeMultiProject.Domain.csproj
├──*.*
└──📂 DockerizeMultiProject.Infrastructure
├──DockerizeMultiProject.Infrastructure.csproj
├──*.*
Dockerfile tương ứng như sau:
FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build-env
WORKDIR /app
#
# copy csproj and restore as distinct layers
COPY *.sln .
# 3 below lines will be replaced
COPY DockerizeMultiProject/*.csproj ./DockerizeMultiProject/
COPY DockerizeMultiProject.Domain/*.csproj ./DockerizeMultiProject.Domain/
COPY DockerizeMultiProject.Infrastructure/*.csproj ./DockerizeMultiProject.Infrastructure/
#
RUN dotnet restore
#
# copy everything else and build app
COPY . ./
#
WORKDIR /app/DockerizeMultiProject
RUN dotnet publish -c Release -o out
#
FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine
WORKDIR /app
#
COPY --from=build-env /app/DockerizeMultiProject/out .
ENTRYPOINT ["dotnet", "DockerizeMultiProject.dll"]Với project đơn giản, bạn có thể thực hiện việc copy thủ công. Nhưng giả sử số lượng project của bạn có từ 50 project trở lên, thì việc làm thủ công này cần cẩn thận và dễ bị sai sót
Để khắc phục, bạn có thể dùng những câu lệnh trong Bash để thực hiện việc copy file.
Note: bạn có thể gõ nhiều dòng trong Bash bằng cách: Sau khi kết thúc 1 dòng, bạn gõ Ctrl + Enter. Cuối cùng bạn gõ Enter để kết thúc.
Dựa vào các câu lệnh trên Bash, có 2 cách như sau: (còn nhiều cách khác nữa mà mình chưa tìm hiểu)
Cách 1: Folder name và Project name giống nhau
Thư mục Project chứa file *.csproj
COPY ./*/*.csproj ./
RUN for file in $(ls *.csproj); do mkdir -p ./${file%.*}/ && mv $file ./${file%.*}/; doneDòng 1: Thực hiện việc copy toàn bộ file *.csproj vào thư mục WORKDIR
Dòng 2: Duyệt toàn bộ danh sách file *.csproj. Ở mỗi vòng lặp, thực hiện việc
- Tạo folder và sub-folder với tham số -p.
- ${file%.*}: loại bỏ extension ra khỏi đường dẫn file.
VD: folder-path/abc.csproj => folder-path/abc - mv: move file từ WORKDIR vào folder project
Cách 2: Tìm kiếm và copy từng file csproj
Lúc đầu mình có thử cách làm này trong Bash thì chạy được, nhưng khi đưa vào Dockerfile thì gặp vấn đề do WORKDIR trong context nằm đường dẫn khác
RUN for file in $(ls */*.csproj); do mkdir -p ./$(dirname "$file")/ && cp $file ./$file ;done;
Trên Cloudaffaire có hướng dẫn 1 cách như sau: (tương tự cách 1)COPY *.sln .
COPY */*.csproj ./
RUN find *.csproj | sed -e 's/.csproj//g' | xargs mkdir
RUN find *.csproj | sed -r -e 's/((.+).csproj)/.\/\1 .\/\2/g' | xargs -I % sh -c 'mv %'Lệnh seq:
$ echo 'DockerizeMultiProject.Domain/abc.csproj' | sed -e 's/.csproj//g'
DockerizeMultiProject.Domain/abc'-e script, --expression=script
add the script to the commands to be executed
$ echo 'DockerizeMultiProject.Domain/abc.csproj' | sed -r -e 's/((.+).csproj)/.\/\1 .\/\2/g'
./DockerizeMultiProject.Domain/abc.csproj ./DockerizeMultiProject.Domain/abc
-E, -r, --regexp-extended
use extended regular expressions in the script
(for portability use POSIX -E).
According to Docker's COPY documentation:
Trả lờiXóaCOPY obeys the following rules:
The path must be inside the context of the build; you cannot COPY ../something /something, because the first step of a docker build is to send the context directory (and subdirectories) to the docker daemon.
It turns out that you cannot include files outside Docker's build context. However, you can copy files from the Dockerfile's parent directory.