Sổ tay Docker: Thao tác Docker Image cơ bản

Trong phần trước, bạn đã tìm hiểu về các thao tác Docker Container cơ bản. Nếu bỏ lỡ thì bạn có thể xem lại bài viết này ở dưới đây:

Sổ tay Docker: Thao tác Docker Container cơ bản
Trong phần này, bạn sẽ tìm hiểu về các thao tác container như: chạy một container, publish port cho container, chế độ detached, liệt kê các container, ...

Bây giờ bạn đã hiểu rõ về cách chạy container bằng cách sử dụng image có sẵn công khai, đã đến lúc bạn tìm hiểu về cách tạo image của riêng mình.

Trong phần này, bạn sẽ tìm hiểu các nguyên tắc cơ bản về việc tạo image, chạy container bằng cách sử dụng chúng và chia sẻ chúng trực tuyến.

Tôi khuyên bạn nên cài đặt Visual Studio Code với Docker Extension chính thức từ marketplace. Điều này sẽ giúp ích rất nhiều cho kinh nghiệm lập trình của bạn.

Cách tạo Docker Image

Như tôi đã giải thích trong những phần trước, image là các tệp tự chứa nhiều lớp hoạt động như template để tạo Docker Container. Chúng giống như một bản sao cố định, chỉ đọc của một container.

Để tạo image bằng một trong các chương trình của bạn, bạn phải có tầm nhìn rõ ràng về những gì bạn muốn từ image. Lấy ví dụ như image nginx chính thức. Bạn có thể bắt đầu một container bằng cách sử dụng image này chỉ bằng cách thực hiện lệnh sau:

docker container run --rm --detach --name default-nginx --publish 8080:80 nginx

# b379ecd5b6b9ae27c144e4fa12bdc5d0635543666f75c14039eea8d5f38e3f56

docker container ls

# CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
# b379ecd5b6b9        nginx               "/docker-entrypoint.…"   8 seconds ago       Up 8 seconds        0.0.0.0:8080->80/tcp   default-nginx

Bây giờ, nếu bạn truy cập http://127.0.0.1:8080 trong trình duyệt, bạn sẽ thấy một trang phản hồi mặc định như sau.

Cách tạo Docker Image

Tất cả đều tốt đẹp, nhưng nếu bạn muốn tạo một image NGINX tùy chỉnh có chức năng chính xác như image chính thức nhưng do bạn tạo ra thì sao? Thành thật mà nói, đó là một kịch bản hoàn toàn hợp lệ. Trên thực tế, hãy thử làm điều đó.‌

Để tạo image NGINX tùy chỉnh, bạn phải có bức tranh rõ ràng về trạng thái cuối cùng của image. Theo tôi, image sẽ như sau:

  • Image phải được cài đặt sẵn NGINX có thể được thực hiện bằng trình quản lý gói hoặc có thể được tạo từ mã nguồn.
  • Image sẽ tự động khởi động NGINX khi chạy.

Đơn giản vậy thôi. Để bắt đầu bạn clone repository https://github.com/fhsinchy/docker-handbook-projects về máy của bạn, sau đó hãy vào bên trong thư mục gốc của dự án và tìm một thư mục có tên custom-nginx trong đó.

Bây giờ, hãy tạo một tệp mới có tên Dockerfile bên trong thư mục đó. Dockerfile là một tập hợp các lệnh, một khi được xử lý bởi daemon, sẽ tạo ra một image. Nội dung cho Dockerfile như sau:

FROM ubuntu:latest

EXPOSE 80

RUN apt-get update && \
    apt-get install nginx -y && \
    apt-get clean && rm -rf /var/lib/apt/lists/*

CMD ["nginx", "-g", "daemon off;"]

Image là tệp nhiều lớp và trong tệp này, mỗi dòng (được gọi là chỉ dẫn) mà bạn đã viết sẽ tạo ra một lớp cho image của bạn.

  • Mọi Dockerfile hợp lệ đều bắt đầu bằng một chỉ dẫn FROM. Chỉ dẫn này thiết lập image cơ sở cho image của bạn. Bằng cách thiết lập ubuntu:latest làm image cơ sở ở đây, bạn nhận được tất cả ưu điểm của Ubuntu có sẵn trong image tùy chỉnh của mình, vì vậy bạn có thể sử dụng những thứ như lệnh apt-get để cài đặt gói dễ dàng.
  • Chỉ dẫn EXPOSE được sử dụng để chỉ ra cổng mà cần phải được publish. Sử dụng chỉ dẫn này không có nghĩa là bạn sẽ không cần đến tùy chọn --publish để publish port. Bạn sẽ vẫn cần sử dụng tùy chọn --publish một cách rõ ràng. Chỉ dẫn EXPOSE này hoạt động giống như một tài liệu dành cho ai đó đang cố gắng chạy một container bằng image của bạn. Nó cũng có một số công dụng khác mà tôi sẽ không thảo luận ở đây.
  • Chỉ dẫn RUN trong Dockerfile thực thi một lệnh bên trong container. Các lệnh apt-get update && apt-get install nginx -y sẽ kiểm tra các phiên bản gói cập nhật và cài đặt Nginx. Các lệnh apt-get clean && rm -rf /var/lib/apt/lists/* được sử dụng để xóa bộ nhớ cache gói bởi vì bạn không muốn bất kỳ thành phần không cần thiết trong image của bạn. Hai lệnh này là những thứ đơn giản của Ubuntu, không có gì phức tạp. Chỉ dẫn RUN tại đây được viết dưới dạng shell. Những nó cũng có thể được viết dưới dạng exec. Bạn có thể tham khảo tài liệu tham khảo chính thức để biết thêm thông tin.
  • Cuối cùng, chỉ dẫn CMD thiết lập lệnh mặc định cho image của bạn. Chỉ dẫn này được viết dưới dạng exec bao gồm ba phần riêng biệt. Trong đó, nginx đề cập đến tệp thực thi NGINX. Cả -gdaemon off là các tùy chọn cho NGINX. Chạy NGINX như một tiến trình duy nhất bên trong container được coi là phương pháp tối ưu nhất, vì vậy nên sử dụng tùy chọn này. Chỉ dẫn CMD cũng có thể được viết dưới dạng shell. Bạn có thể tham khảo tài liệu tham khảo chính thức để biết thêm thông tin.

Bây giờ bạn có một tệp Dockerfile hợp lệ, bạn có thể xây dựng một image từ nó. Cũng giống như các lệnh liên quan đến container, các lệnh liên quan đến image có thể được đưa ra bằng cú pháp sau:

docker image <command> <options>

Để xây dựng một image bằng cách sử dụng Dockerfile bạn vừa viết, hãy mở terminal của bạn bên trong thư mục custom-nginx và thực hiện lệnh sau:

docker image build .

# Sending build context to Docker daemon  3.584kB
# Step 1/4 : FROM ubuntu:latest
#  ---> d70eaf7277ea
# Step 2/4 : EXPOSE 80
#  ---> Running in 9eae86582ec7
# Removing intermediate container 9eae86582ec7
#  ---> 8235bd799a56
# Step 3/4 : RUN apt-get update &&     apt-get install nginx -y &&     apt-get clean && rm -rf /var/lib/apt/lists/*
#  ---> Running in a44725cbb3fa
### LONG INSTALLATION STUFF GOES HERE ###
# Removing intermediate container a44725cbb3fa
#  ---> 3066bd20292d
# Step 4/4 : CMD ["nginx", "-g", "daemon off;"]
#  ---> Running in 4792e4691660
# Removing intermediate container 4792e4691660
#  ---> 3199372aa3fc
# Successfully built 3199372aa3fc

Để thực hiện xây dựng image, daemon cần hai phần thông tin rất cụ thể. Đó là tên của Dockerfile và bối cảnh xây dựng. Trong lệnh được đưa ra ở trên:

  • docker image build là lệnh để xây dựng image. Daemon tìm thấy bất kỳ tệp nào có tên Dockerfile trong bối cảnh.
  • Phần . cuối đặt bối cảnh cho bản dựng này. Bối cảnh có nghĩa là thư mục mà daemon có thể truy cập được trong quá trình xây dựng.

Bây giờ để chạy container bằng image này, bạn có thể sử dụng lệnh container run cùng với Image ID mà bạn nhận được trong quá trình xây dựng. Trong trường hợp của tôi, Image ID 3199372aa3fc được hiển thị bằng dòng Successfully built 3199372aa3fc trong khối mã trước đó.

docker container run --rm --detach --name custom-nginx-packaged --publish 8080:80 3199372aa3fc

# ec09d4e1f70c903c3b954c8d7958421cdd1ae3d079b57f929e44131fbf8069a0

docker container ls

# CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
# ec09d4e1f70c        3199372aa3fc        "nginx -g 'daemon of…"   23 seconds ago      Up 22 seconds       0.0.0.0:8080->80/tcp   custom-nginx-packaged

Để xác minh, hãy truy cập http://127.0.0.1:8080 và bạn sẽ thấy trang phản hồi mặc định.

Cách tạo Docker Image

Cách gắn thẻ cho Docker Image

Cũng giống như container, bạn có thể chỉ định số nhận dạng tùy chỉnh cho image của mình thay vì dựa vào ID được tạo ngẫu nhiên. Trong trường hợp của image, nó được gọi là gắn thẻ thay vì đặt tên. Tùy chọn --tag hoặc -t được sử dụng trong những trường hợp như vậy.

Cú pháp chung cho tùy chọn như sau:

--tag <image repository>:<image tag>

Image trong kho lưu trữ thường kết hợp tên image và thẻ để chỉ ra một bản dựng hoặc phiên bản nhất định.

Lấy ví dụ như image mysql chính thức. Nếu bạn muốn chạy container bằng cách sử dụng phiên bản MySQL cụ thể, chẳng hạn như 5.7, bạn có thể thực thi docker container run mysql:5.7, trong đó mysql là tên image và 5.7 là thẻ.

Để gắn thẻ image NGINX tùy chỉnh custom-nginx:packaged của bạn, bạn có thể thực hiện lệnh sau:

docker image build --tag custom-nginx:packaged .

# Sending build context to Docker daemon  1.055MB
# Step 1/4 : FROM ubuntu:latest
#  ---> f63181f19b2f
# Step 2/4 : EXPOSE 80
#  ---> Running in 53ab370b9efc
# Removing intermediate container 53ab370b9efc
#  ---> 6d6460a74447
# Step 3/4 : RUN apt-get update &&     apt-get install nginx -y &&     apt-get clean && rm -rf /var/lib/apt/lists/*
#  ---> Running in b4951b6b48bb
### LONG INSTALLATION STUFF GOES HERE ###
# Removing intermediate container b4951b6b48bb
#  ---> fdc6cdd8925a
# Step 4/4 : CMD ["nginx", "-g", "daemon off;"]
#  ---> Running in 3bdbd2af4f0e
# Removing intermediate container 3bdbd2af4f0e
#  ---> f8837621b99d
# Successfully built f8837621b99d
# Successfully tagged custom-nginx:packaged

Không có gì sẽ thay đổi ngoại trừ thực tế là bây giờ bạn có thể truy cập image của mình bằng custom-nginx:packaged thay vì một chuỗi dài ngẫu nhiên.

Trong trường hợp bạn quên gắn thẻ image trong lúc xây dựng hoặc có thể bạn muốn thay đổi thẻ, bạn có thể sử dụng lệnh image tag để thực hiện điều đó:

docker image tag <image id> <image repository>:<image tag>

## or ##

docker image tag <image repository>:<image tag> <new image repository>:<new image tag>

Cách liệt kê và xóa Docker Image

Cũng giống như lệnh container ls, bạn có thể sử dụng lệnh image ls để liệt kê tất cả các image trong hệ thống cục bộ của mình:

docker image ls

# REPOSITORY     TAG        IMAGE ID       CREATED         SIZE
# <none>         <none>     3199372aa3fc   7 seconds ago   132MB
# custom-nginx   packaged   f8837621b99d   4 minutes ago   132MB

Image được liệt kê ở đây có thể được xóa bằng cách sử dụng lệnh image rm. Cú pháp chung như sau:

docker image rm <image identifier>

Định danh có thể là Image ID hoặc tên image. Nếu bạn sử dụng kho lưu trữ, bạn cũng sẽ phải chỉ định thẻ. Để xóa image custom-nginx:packaged, bạn có thể thực hiện lệnh sau:

docker image rm custom-nginx:packaged

# Untagged: custom-nginx:packaged
# Deleted: sha256:f8837621b99d3388a9e78d9ce49fbb773017f770eea80470fb85e0052beae242
# Deleted: sha256:fdc6cdd8925ac25b9e0ed1c8539f96ad89ba1b21793d061e2349b62dd517dadf
# Deleted: sha256:c20e4aa46615fe512a4133089a5cd66f9b7da76366c96548790d5bf865bd49c4
# Deleted: sha256:6d6460a744475a357a2b631a4098aa1862d04510f3625feb316358536fcd8641

Bạn cũng có thể sử dụng lệnh image prune để xóa tất cả các image chưa được gắn thẻ như sau:

docker image prune --force

# Deleted Images:
# deleted: sha256:ba9558bdf2beda81b9acc652ce4931a85f0fc7f69dbc91b4efc4561ef7378aff
# deleted: sha256:ad9cc3ff27f0d192f8fa5fadebf813537e02e6ad472f6536847c4de183c02c81
# deleted: sha256:f1e9b82068d43c1bb04ff3e4f0085b9f8903a12b27196df7f1145aa9296c85e7
# deleted: sha256:ec16024aa036172544908ec4e5f842627d04ef99ee9b8d9aaa26b9c2a4b52baa

# Total reclaimed space: 59.19MB

Tùy chọn --force hoặc -f bỏ qua bất kỳ câu hỏi xác nhận. Bạn cũng có thể sử dụng tùy chọn --all hoặc -a để xóa tất cả các image được lưu trong bộ nhớ cache trong sổ đăng ký cục bộ của mình.

Tìm hiểu về nhiều lớp của Docker Image

Ngay từ đầu của loạt bài viết sổ tay Docker này, tôi đã nói rằng image là các tệp nhiều lớp. Trong phần này, tôi sẽ trình bày các lớp khác nhau của một image và cách chúng đóng vai trò quan trọng trong quá trình xây dựng image đó.

Để minh họa, tôi sẽ sử dụng image custom-nginx:packaged ở phần trước.

Để hình dung nhiều lớp của một image, bạn có thể sử dụng lệnh image history. Các lớp khác nhau của image custom-nginx:packaged có thể được liệt kê như sau:

docker image history custom-nginx:packaged

# IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
# 7f16387f7307        5 minutes ago       /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B                             
# 587c805fe8df        5 minutes ago       /bin/sh -c apt-get update &&     apt-get ins…   60MB                
# 6fe4e51e35c1        6 minutes ago       /bin/sh -c #(nop)  EXPOSE 80                    0B                  
# d70eaf7277ea        17 hours ago        /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B                  
# <missing>           17 hours ago        /bin/sh -c mkdir -p /run/systemd && echo 'do…   7B                  
# <missing>           17 hours ago        /bin/sh -c [ -z "$(apt-get indextargets)" ]     0B                  
# <missing>           17 hours ago        /bin/sh -c set -xe   && echo '#!/bin/sh' > /…   811B                
# <missing>           17 hours ago        /bin/sh -c #(nop) ADD file:435d9776fdd3a1834…   72.9MB

Image này có 8 lớp. Lớp trên cùng là lớp mới nhất và khi bạn đi xuống các lớp sẽ cũ hơn. Lớp trên cùng là lớp mà bạn thường sử dụng để chạy các container.

Bây giờ, chúng ta hãy xem xét kỹ hơn các image bắt đầu từ image d70eaf7277ea đến 7f16387f7307. Tôi sẽ bỏ qua bốn lớp dưới cùng có cột IMAGE<missing>.

  • d70eaf7277ea được tạo bởi /bin/sh -c #(nop)  CMD ["/bin/bash"], nó cho biết rằng shell mặc định bên trong Ubuntu đã được tải thành công.
  • 6fe4e51e35c1 được tạo bởi /bin/sh -c #(nop)  EXPOSE 80 đó là hướng dẫn thứ hai trong mã của bạn.
  • 587c805fe8df được tạo bởi /bin/sh -c apt-get update && apt-get install nginx -y && apt-get clean && rm -rf /var/lib/apt/lists/* đó là hướng dẫn thứ ba trong mã của bạn. Bạn cũng có thể thấy rằng hình ảnh này có kích thước 60MB của tất cả các gói cần thiết đã được cài đặt trong quá trình thực thi hướng dẫn này.
  • Cuối cùng, lớp trên cùng 7f16387f7307 được tạo bởi /bin/sh -c #(nop)  CMD ["nginx", "-g", "daemon off;"], nó thiết lập lệnh mặc định cho image này.

Như bạn có thể thấy, image bao gồm nhiều lớp chỉ đọc, mỗi lớp ghi lại một tập hợp các thay đổi mới đối với trạng thái được kích hoạt bởi một số chỉ dẫn nhất định. Khi bạn bắt đầu một container bằng cách sử dụng một image, bạn sẽ nhận được một lớp có thể ghi mới ở trên cùng các lớp khác.

Hiện tượng phân lớp này xảy ra mỗi khi bạn làm việc với Docker đã được thực hiện bởi một khái niệm kỹ thuật tuyệt vời được gọi là hệ thống tệp liên hợp (union file system). Ở đây, union có nghĩa là hợp trong lý thuyết tập hợp. Theo Wikipedia -

Nó cho phép các tệp và thư mục của các hệ thống tệp riêng biệt, được gọi là các nhánh, được phủ một cách minh bạch, tạo thành một hệ thống tệp thống nhất. Nội dung của các thư mục có cùng đường dẫn trong các nhánh được hợp nhất sẽ được nhìn thấy cùng nhau trong một thư mục được hợp nhất duy nhất, trong hệ thống tệp ảo mới.

Bằng cách sử dụng khái niệm này, Docker có thể tránh trùng lặp dữ liệu và có thể sử dụng các lớp đã tạo trước đó làm bộ nhớ đệm cho các bản dựng sau. Điều này dẫn đến image nhỏ gọn, hiệu quả có thể được sử dụng ở mọi nơi.

Cách tạo image NGINX từ mã nguồn

Trong các phần trước, bạn đã tìm hiểu về các chỉ dẫn FROM, EXPOSE, RUNCMD. Trong phần này, bạn sẽ tìm hiểu thêm về các chỉ dẫn khác.

Trong phần này, bạn sẽ lại tạo một image NGINX tùy chỉnh. Nhưng điều khác biệt là bạn sẽ xây dựng NGINX từ mã nguồn thay vì cài đặt nó bằng cách sử dụng một số trình quản lý gói như apt-get trong ví dụ trước.

Để xây dựng NGINX từ mã nguồn, trước tiên bạn cần mã nguồn NGINX. Nếu bạn đã clone repository https://github.com/fhsinchy/docker-handbook-projects ở phần trước, bạn sẽ thấy một tệp có tên nginx-1.19.2.tar.gz bên trong thư mục custom-nginx. Bạn sẽ sử dụng cái này làm mã nguồn để xây dựng image NGINX.

Trước khi đi sâu vào viết một số mã, chúng ta hãy lên kế hoạch cho quy trình trước. Quá trình tạo image lần này có thể được thực hiện trong bảy bước. Cụ thể như sau:

  • Lấy một image cơ sở tốt để xây dựng ứng dụng, như Ubuntu.
  • Cài đặt các phụ thuộc xây dựng cần thiết trên image cơ sở.
  • Sao chép tệp nginx-1.19.2.tar.gz vào bên trong image.
  • Giải nén nội dung của file nginx-1.19.2.tar.gz và xóa nó.
  • Cấu hình bản dựng, biên dịch và cài đặt chương trình bằng công cụ make.
  • Xóa mã nguồn đã giải nén.
  • Chạy tệp thực thi nginx.

Bây giờ bạn đã có một kế hoạch, hãy bắt đầu bằng cách mở cũ Dockerfile và cập nhật nội dung của nó như sau:

FROM ubuntu:latest

RUN apt-get update && \
    apt-get install build-essential\ 
                    libpcre3 \
                    libpcre3-dev \
                    zlib1g \
                    zlib1g-dev \
                    libssl-dev \
                    -y && \
    apt-get clean && rm -rf /var/lib/apt/lists/*

COPY nginx-1.19.2.tar.gz .

RUN tar -xvf nginx-1.19.2.tar.gz && rm nginx-1.19.2.tar.gz

RUN cd nginx-1.19.2 && \
    ./configure \
        --sbin-path=/usr/bin/nginx \
        --conf-path=/etc/nginx/nginx.conf \
        --error-log-path=/var/log/nginx/error.log \
        --http-log-path=/var/log/nginx/access.log \
        --with-pcre \
        --pid-path=/var/run/nginx.pid \
        --with-http_ssl_module && \
    make && make install

RUN rm -rf /nginx-1.19.2

CMD ["nginx", "-g", "daemon off;"]

Như bạn có thể thấy, mã bên trong Dockerfile phản ánh bảy bước mà tôi đã nói ở trên.

  • Chỉ dẫn FROM thiết lập Ubuntu như image cơ sở tạo ra một môi trường lý tưởng cho việc xây dựng bất kỳ ứng dụng nào.
  • Chỉ dẫn RUN cài đặt gói tiêu chuẩn cần thiết cho việc biên dịch nginx từ mã nguồn.
  • Chỉ dẫn COPY chịu trách nhiệm sao chép tệp nginx-1.19.2.tar.gz vào bên trong image. Cú pháp chung cho chỉ dẫn COPYCOPY <source> <destination> với <source> nằm trong hệ thống tệp cục bộ của bạn và <destination> nằm bên trong image của bạn. Đích là . có nghĩa là thư mục làm việc bên trong image, theo mặc định là / trừ khi được thiết lập khác.
  • Chỉ dẫn RUN thứ hai ở đây giải nén nội dung từ file nginx-1.19.2.tar.gz bằng cách sử dụng tar và xóa nó sau khi giải nén xong.
  • File nginx-1.19.2.tar.gz chứa một thư mục được gọi là nginx-1.19.2 chứa mã nguồn. Vì vậy, trong bước tiếp theo, bạn sẽ phải di chuyển vào bên trong thư mục đó bằng lệnh cd và thực hiện quá trình xây dựng. Bạn có thể đọc bài viết Cách cài đặt phần mềm từ mã nguồn… và loại bỏ nó sau đó để tìm hiểu thêm về chủ đề này.
  • Khi quá trình xây dựng và cài đặt hoàn tất, bạn xóa thư mục nginx-1.19.2 bằng lệnh rm.
  • Ở bước cuối cùng, bạn khởi động NGINX ở chế độ tiến trình duy nhất giống như bạn đã làm trước đây.

Bây giờ để xây dựng một image bằng cách sử dụng mã nguồn, hãy thực hiện lệnh sau:

docker image build --tag custom-nginx:built .

# Step 1/7 : FROM ubuntu:latest
#  ---> d70eaf7277ea
# Step 2/7 : RUN apt-get update &&     apt-get install build-essential                    libpcre3                     libpcre3-dev                     zlib1g                     zlib1g-dev                     libssl-dev                     -y &&     apt-get clean && rm -rf /var/lib/apt/lists/*
#  ---> Running in 2d0aa912ea47
### LONG INSTALLATION STUFF GOES HERE ###
# Removing intermediate container 2d0aa912ea47
#  ---> cbe1ced3da11
# Step 3/7 : COPY nginx-1.19.2.tar.gz .
#  ---> 7202902edf3f
# Step 4/7 : RUN tar -xvf nginx-1.19.2.tar.gz && rm nginx-1.19.2.tar.gz
# ---> Running in 4a4a95643020
### LONG EXTRACTION STUFF GOES HERE ###
# Removing intermediate container 4a4a95643020
#  ---> f9dec072d6d6
# Step 5/7 : RUN cd nginx-1.19.2 &&     ./configure         --sbin-path=/usr/bin/nginx         --conf-path=/etc/nginx/nginx.conf         --error-log-path=/var/log/nginx/error.log         --http-log-path=/var/log/nginx/access.log         --with-pcre         --pid-path=/var/run/nginx.pid         --with-http_ssl_module &&     make && make install
#  ---> Running in b07ba12f921e
### LONG CONFIGURATION AND BUILD STUFF GOES HERE ###
# Removing intermediate container b07ba12f921e
#  ---> 5a877edafd8b
# Step 6/7 : RUN rm -rf /nginx-1.19.2
#  ---> Running in 947e1d9ba828
# Removing intermediate container 947e1d9ba828
#  ---> a7702dc7abb7
# Step 7/7 : CMD ["nginx", "-g", "daemon off;"]
#  ---> Running in 3110c7fdbd57
# Removing intermediate container 3110c7fdbd57
#  ---> eae55f7369d3
# Successfully built eae55f7369d3
# Successfully tagged custom-nginx:built

Tệp Dockerfile này ổn nhưng có một số chỗ mà chúng tôi có thể cải thiện.

  • Thay vì hard code tên tệp như nginx-1.19.2.tar.gz, bạn có thể tạo đối số bằng cách sử dụng chỉ dẫn ARG. Bằng cách này, bạn sẽ có thể thay đổi phiên bản hoặc tên tệp chỉ bằng cách thay đổi đối số.
  • Thay vì tải xuống file nén mã nguồn theo cách thủ công, bạn có thể cho phép daemon tải tệp xuống trong quá trình xây dựng. Có một chỉ dẫn khác tương tự COPY được gọi là chỉ dẫn ADD có khả năng tải tệp từ internet.

Mở tệp Dockerfile và cập nhật nội dung của nó như sau:

FROM ubuntu:latest

RUN apt-get update && \
    apt-get install build-essential\ 
                    libpcre3 \
                    libpcre3-dev \
                    zlib1g \
                    zlib1g-dev \
                    libssl-dev \
                    -y && \
    apt-get clean && rm -rf /var/lib/apt/lists/*

ARG FILENAME="nginx-1.19.2"
ARG EXTENSION="tar.gz"

ADD https://nginx.org/download/${FILENAME}.${EXTENSION} .

RUN tar -xvf ${FILENAME}.${EXTENSION} && rm ${FILENAME}.${EXTENSION}

RUN cd ${FILENAME} && \
    ./configure \
        --sbin-path=/usr/bin/nginx \
        --conf-path=/etc/nginx/nginx.conf \
        --error-log-path=/var/log/nginx/error.log \
        --http-log-path=/var/log/nginx/access.log \
        --with-pcre \
        --pid-path=/var/run/nginx.pid \
        --with-http_ssl_module && \
    make && make install

RUN rm -rf /${FILENAME}}

CMD ["nginx", "-g", "daemon off;"]

Nội dung gần như giống với nội dung trước đó ngoại trừ một chỉ dẫn ARG trên dòng 13, 14 và chỉ dẫn ADD trên dòng 16. Giải thích cho mã được cập nhật như sau:

  • Chỉ dẫn ARG cho phép bạn khai báo biến giống như trong các ngôn ngữ lập trình khác. Các biến hoặc đối số này sau đó có thể được truy cập bằng cú pháp ${argument name}. Ở đây, tôi đã đặt tên tệp nginx-1.19.2 và phần mở rộng tệp tar.gz trong hai đối số riêng biệt. Bằng cách này, tôi có thể chuyển đổi giữa các phiên bản NGINX mới hơn hoặc định dạng lưu trữ bằng cách thực hiện thay đổi chỉ ở một nơi. Trong đoạn mã trên, tôi đã thêm các giá trị mặc định vào các biến. Giá trị biến cũng có thể được truyền dưới dạng các tùy chọn của lệnh image build. Bạn có thể tham khảo tài liệu tham khảo chính thức để biết thêm chi tiết.
  • Trong chỉ dẫn ADD, tôi đã định dạng động URL tải xuống bằng cách sử dụng các đối số được khai báo ở trên. Dòng https://nginx.org/download/${FILENAME}.${EXTENSION} sẽ dẫn đến một cái gì đó giống như https://nginx.org/download/nginx-1.19.2.tar.gz trong quá trình xây dựng. Bạn có thể thay đổi phiên bản tệp hoặc phần mở rộng bằng cách thay đổi nó chỉ ở một nơi nhờ chỉ dẫn ARG.
  • Chỉ dẫn ADD không giải nén file thu được từ internet theo mặc định, do đó tar được sử dụng trên dòng 18 để giải nén file.

Phần còn lại của mã hầu như không thay đổi. Bây giờ bạn sẽ có thể hiểu cách sử dụng các đối số của chính mình. Cuối cùng, hãy xây dựng lại image từ cập nhật này.

docker image build --tag custom-nginx:built .

# Step 1/9 : FROM ubuntu:latest
#  ---> d70eaf7277ea
# Step 2/9 : RUN apt-get update &&     apt-get install build-essential                    libpcre3                     libpcre3-dev                     zlib1g                     zlib1g-dev                     libssl-dev                     -y &&     apt-get clean && rm -rf /var/lib/apt/lists/*
#  ---> cbe1ced3da11
### LONG INSTALLATION STUFF GOES HERE ###
# Step 3/9 : ARG FILENAME="nginx-1.19.2"
#  ---> Running in 33b62a0e9ffb
# Removing intermediate container 33b62a0e9ffb
#  ---> fafc0aceb9c8
# Step 4/9 : ARG EXTENSION="tar.gz"
#  ---> Running in 5c32eeb1bb11
# Removing intermediate container 5c32eeb1bb11
#  ---> 36efdf6efacc
# Step 5/9 : ADD https://nginx.org/download/${FILENAME}.${EXTENSION} .
# Downloading [==================================================>]  1.049MB/1.049MB
#  ---> dba252f8d609
# Step 6/9 : RUN tar -xvf ${FILENAME}.${EXTENSION} && rm ${FILENAME}.${EXTENSION}
#  ---> Running in 2f5b091b2125
### LONG EXTRACTION STUFF GOES HERE ###
# Removing intermediate container 2f5b091b2125
#  ---> 2c9a325d74f1
# Step 7/9 : RUN cd ${FILENAME} &&     ./configure         --sbin-path=/usr/bin/nginx         --conf-path=/etc/nginx/nginx.conf         --error-log-path=/var/log/nginx/error.log         --http-log-path=/var/log/nginx/access.log         --with-pcre         --pid-path=/var/run/nginx.pid         --with-http_ssl_module &&     make && make install
#  ---> Running in 11cc82dd5186
### LONG CONFIGURATION AND BUILD STUFF GOES HERE ###
# Removing intermediate container 11cc82dd5186
#  ---> 6c122e485ec8
# Step 8/9 : RUN rm -rf /${FILENAME}}
#  ---> Running in 04102366960b
# Removing intermediate container 04102366960b
#  ---> 6bfa35420a73
# Step 9/9 : CMD ["nginx", "-g", "daemon off;"]
#  ---> Running in 63ee44b571bb
# Removing intermediate container 63ee44b571bb
#  ---> 4ce79556db1b
# Successfully built 4ce79556db1b
# Successfully tagged custom-nginx:built

Bây giờ bạn sẽ có thể chạy một container bằng cách sử dụng image custom-nginx:built.

docker container run --rm --detach --name custom-nginx-built --publish 8080:80 custom-nginx:built

# 90ccdbc0b598dddc4199451b2f30a942249d85a8ed21da3c8d14612f17eed0aa

docker container ls

# CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS                  NAMES
# 90ccdbc0b598        custom-nginx:built   "nginx -g 'daemon of…"   2 minutes ago       Up 2 minutes        0.0.0.0:8080->80/tcp   custom-nginx-built

Một container sử dụng image custom-nginx:built đã được chạy thành công. Container đã có thể truy cập được ngay bây giờ tại http://127.0.0.1:8080.

Cách tạo image NGINX từ mã nguồn

Và đây là trang phản hồi mặc định đáng tin cậy từ NGINX. Bạn có thể truy cập trang web tham khảo chính thức để tìm hiểu thêm về các chỉ dẫn có sẵn.

Cách tối ưu hóa Docker Image

Image chúng tôi xây dựng trong phần trước có đầy đủ chức năng nhưng chưa được tối ưu hóa. Để chứng minh điều này, hãy xem kích thước của image bằng lệnh image ls:

docker image ls

# REPOSITORY         TAG       IMAGE ID       CREATED          SIZE
# custom-nginx       built     1f3aaf40bb54   16 minutes ago   343MB

Đối với một image chỉ chứa NGINX, 343MB là quá nhiều. Nếu bạn tải image NGINX chính thức và kiểm tra kích thước của nó, bạn sẽ thấy nó nhỏ như thế nào:

docker image pull nginx:stable

# stable: Pulling from library/nginx
# a076a628af6f: Pull complete 
# 45d7b5d3927d: Pull complete 
# 5e326fece82e: Pull complete 
# 30c386181b68: Pull complete 
# b15158e9ebbe: Pull complete 
# Digest: sha256:ebd0fd56eb30543a9195280eb81af2a9a8e6143496accd6a217c14b06acd1419
# Status: Downloaded newer image for nginx:stable
# docker.io/library/nginx:stable

docker image ls

# REPOSITORY         TAG       IMAGE ID       CREATED          SIZE
# custom-nginx       built     1f3aaf40bb54   25 minutes ago   343MB
# nginx              stable    b9e1dc12387a   11 days ago      133MB

Để tìm ra nguyên nhân gốc rễ, chúng ta hãy tìm hiểu nguyên nhân ở file Dockerfile đầu tiên:

FROM ubuntu:latest

RUN apt-get update && \
    apt-get install build-essential\ 
                    libpcre3 \
                    libpcre3-dev \
                    zlib1g \
                    zlib1g-dev \
                    libssl-dev \
                    -y && \
    apt-get clean && rm -rf /var/lib/apt/lists/*

ARG FILENAME="nginx-1.19.2"
ARG EXTENSION="tar.gz"

ADD https://nginx.org/download/${FILENAME}.${EXTENSION} .

RUN tar -xvf ${FILENAME}.${EXTENSION} && rm ${FILENAME}.${EXTENSION}

RUN cd ${FILENAME} && \
    ./configure \
        --sbin-path=/usr/bin/nginx \
        --conf-path=/etc/nginx/nginx.conf \
        --error-log-path=/var/log/nginx/error.log \
        --http-log-path=/var/log/nginx/access.log \
        --with-pcre \
        --pid-path=/var/run/nginx.pid \
        --with-http_ssl_module && \
    make && make install

RUN rm -rf /${FILENAME}}

CMD ["nginx", "-g", "daemon off;"]

Như bạn có thể thấy ở dòng 3, chỉ dẫn RUN cài đặt rất nhiều thứ. Mặc dù các gói này cần thiết để xây dựng NGINX từ mã nguồn, nhưng chúng không còn cần thiết để chạy nó.

Trong số 6 gói mà chúng tôi đã cài đặt, chỉ có hai gói là cần thiết để chạy NGINX. Đó là libpcre3zlib1g. Vì vậy, ý tưởng tốt hơn là gỡ cài đặt các gói khác sau khi quá trình xây dựng hoàn tất.

Để làm như vậy, hãy cập nhật của bạn file Dockerfile như sau:

FROM ubuntu:latest

EXPOSE 80

ARG FILENAME="nginx-1.19.2"
ARG EXTENSION="tar.gz"

ADD https://nginx.org/download/${FILENAME}.${EXTENSION} .

RUN apt-get update && \
    apt-get install build-essential \ 
                    libpcre3 \
                    libpcre3-dev \
                    zlib1g \
                    zlib1g-dev \
                    libssl-dev \
                    -y && \
    tar -xvf ${FILENAME}.${EXTENSION} && rm ${FILENAME}.${EXTENSION} && \
    cd ${FILENAME} && \
    ./configure \
        --sbin-path=/usr/bin/nginx \
        --conf-path=/etc/nginx/nginx.conf \
        --error-log-path=/var/log/nginx/error.log \
        --http-log-path=/var/log/nginx/access.log \
        --with-pcre \
        --pid-path=/var/run/nginx.pid \
        --with-http_ssl_module && \
    make && make install && \
    cd / && rm -rfv /${FILENAME} && \
    apt-get remove build-essential \ 
                    libpcre3-dev \
                    zlib1g-dev \
                    libssl-dev \
                    -y && \
    apt-get autoremove -y && \
    apt-get clean && rm -rf /var/lib/apt/lists/*

CMD ["nginx", "-g", "daemon off;"]

Như bạn có thể thấy, trên dòng 10, một chỉ dẫn RUN duy nhất đang thực hiện tất cả các thao tác rất nặng. Chuỗi sự kiện chính xác như sau:

  • Từ dòng 10 đến dòng 17, tất cả các gói cần thiết đang được cài đặt.
  • Dòng 18, mã nguồn được giải nén và file nén đã tải xuống sẽ bị xóa.
  • Từ dòng 19 đến dòng 28, NGINX được cấu hình, xây dựng và cài đặt trên hệ thống.
  • Dòng 29, mã nguồn NGINX sẽ bị xóa.
  • Từ dòng 30 đến dòng 36, xóa tất cả các gói không cần thiết đã được gỡ cài đặt và xóa bộ nhớ cache. Các gói libpcre3zlib1g cần thiết để chạy NGINX nên chúng tôi giữ chúng.

Bạn có thể hỏi tại sao tôi lại làm quá nhiều việc trong một chỉ dẫn RUN thay vì tách chúng thành nhiều chỉ dẫn như chúng ta đã làm trước đây. Chà, tách chúng ra sẽ là một sai lầm.

Nếu bạn cài đặt các gói và sau đó gỡ bỏ chúng trong các chỉ dẫn RUN riêng biệt , chúng sẽ nằm trong các lớp riêng biệt của image. Mặc dù image cuối cùng sẽ không có các gói bị loại bỏ, nhưng kích thước của chúng sẽ vẫn được thêm vào image cuối cùng vì chúng tồn tại ở một trong các lớp của image. Vì vậy, hãy đảm bảo rằng bạn thực hiện những loại thay đổi này trên một lớp duy nhất.

Hãy xây dựng một image mới bằng cách sử dụng tệp Dockerfile này và xem sự khác biệt.

docker image build --tag custom-nginx:built .

# Sending build context to Docker daemon  1.057MB
# Step 1/7 : FROM ubuntu:latest
#  ---> f63181f19b2f
# Step 2/7 : EXPOSE 80
#  ---> Running in 006f39b75964
# Removing intermediate container 006f39b75964
#  ---> 6943f7ef9376
# Step 3/7 : ARG FILENAME="nginx-1.19.2"
#  ---> Running in ffaf89078594
# Removing intermediate container ffaf89078594
#  ---> 91b5cdb6dabe
# Step 4/7 : ARG EXTENSION="tar.gz"
#  ---> Running in d0f5188444b6
# Removing intermediate container d0f5188444b6
#  ---> 9626f941ccb2
# Step 5/7 : ADD https://nginx.org/download/${FILENAME}.${EXTENSION} .
# Downloading [==================================================>]  1.049MB/1.049MB
#  ---> a8e8dcca1be8
# Step 6/7 : RUN apt-get update &&     apt-get install build-essential                     libpcre3                     libpcre3-dev                     zlib1g                     zlib1g-dev                     libssl-dev                     -y &&     tar -xvf ${FILENAME}.${EXTENSION} && rm ${FILENAME}.${EXTENSION} &&     cd ${FILENAME} &&     ./configure         --sbin-path=/usr/bin/nginx         --conf-path=/etc/nginx/nginx.conf         --error-log-path=/var/log/nginx/error.log         --http-log-path=/var/log/nginx/access.log         --with-pcre         --pid-path=/var/run/nginx.pid         --with-http_ssl_module &&     make && make install &&     cd / && rm -rfv /${FILENAME} &&     apt-get remove build-essential                     libpcre3-dev                     zlib1g-dev                     libssl-dev                     -y &&     apt-get autoremove -y &&     apt-get clean && rm -rf /var/lib/apt/lists/*
#  ---> Running in e5675cad1260
### LONG INSTALLATION AND BUILD STUFF GOES HERE ###
# Removing intermediate container e5675cad1260
#  ---> dc7e4161f975
# Step 7/7 : CMD ["nginx", "-g", "daemon off;"]
#  ---> Running in b579e4600247
# Removing intermediate container b579e4600247
#  ---> 512aa6a95a93
# Successfully built 512aa6a95a93
# Successfully tagged custom-nginx:built

docker image ls

# REPOSITORY         TAG       IMAGE ID       CREATED              SIZE
# custom-nginx       built     512aa6a95a93   About a minute ago   81.6MB
# nginx              stable    b9e1dc12387a   11 days ago          133MB

Như bạn có thể thấy, kích thước image đã giảm từ 343MB xuống 81,6MB. Image chính thức là 133MB. Đây là một bản dựng được tối ưu hóa khá tốt, nhưng chúng ta có thể đi xa hơn một chút trong phần tiếp theo.

Tạo Docker Image dựa trên Alpine Linux

Nếu bạn đang làm việc với các container một thời gian, bạn có thể đã nghe nói về một thứ gọi là Alpine Linux. Đó là một bản phân phối Linux đầy đủ tính năng như Ubuntu, Debian hoặc Fedora.

Nhưng điều tuyệt vời nhất của Alpine là nó được xây dựng xung quanh musl, libc, busybox và nó rất nhẹ. Trong khi image ubuntu mới nhất nặng khoảng 28 MB, thì image Alpine chỉ là 2,8 MB.

Ngoài bản chất nhẹ, Alpine cũng an toàn và phù hợp hơn nhiều để tạo container so với các bản phân phối khác.

Mặc dù không thân thiện với người dùng như các bản phân phối thương mại khác, việc chuyển đổi sang Alpine vẫn rất đơn giản. Trong phần này, bạn sẽ tìm hiểu về cách tạo lại image custom-nginx bằng cách sử dụng Alpine làm image cơ sở của nó.

Mở Dockerfile của bạn và cập nhật nội dung của nó như sau:

FROM alpine:latest

EXPOSE 80

ARG FILENAME="nginx-1.19.2"
ARG EXTENSION="tar.gz"

ADD https://nginx.org/download/${FILENAME}.${EXTENSION} .

RUN apk add --no-cache pcre zlib && \
    apk add --no-cache \
            --virtual .build-deps \
            build-base \ 
            pcre-dev \
            zlib-dev \
            openssl-dev && \
    tar -xvf ${FILENAME}.${EXTENSION} && rm ${FILENAME}.${EXTENSION} && \
    cd ${FILENAME} && \
    ./configure \
        --sbin-path=/usr/bin/nginx \
        --conf-path=/etc/nginx/nginx.conf \
        --error-log-path=/var/log/nginx/error.log \
        --http-log-path=/var/log/nginx/access.log \
        --with-pcre \
        --pid-path=/var/run/nginx.pid \
        --with-http_ssl_module && \
    make && make install && \
    cd / && rm -rfv /${FILENAME} && \
    apk del .build-deps

CMD ["nginx", "-g", "daemon off;"]

Mã gần như giống hệt nhau ngoại trừ một vài thay đổi. Tôi sẽ liệt kê các thay đổi và giải thích chúng:

  • Thay vì dựa trên image cơ sở là ubuntu:latest, bây giờ chúng ta sử dụng alpine:latest.
  • Thay vì sử dụng apt-get install để cài đặt các gói, chúng ta sử dụng apk add. Tùy chọn --no-cache nghĩa là gói tải về sẽ không được lưu trữ. Tương tự như vậy, chúng ta sẽ sử dụng apk del thay vì apt-get remove để gỡ cài đặt các gói.
  • Tùy chọn --virtual cho lệnh apk add được sử dụng cho bundling một loạt các gói vào một gói phần mềm ảo duy nhất cho việc quản lý dễ dàng hơn. Các gói chỉ cần thiết để xây dựng chương trình được dán nhãn .build-deps sau đó được xóa trên dòng 29 bằng cách thực hiện lệnh apk del .build-deps. Bạn có thể tìm hiểu thêm về virtual trong các tài liệu chính thức.
  • Các tên gói có một chút khác biệt ở đây. Thông thường, mọi bản phân phối Linux đều có kho lưu trữ gói dành cho mọi người, nơi bạn có thể tìm kiếm các gói. Nếu bạn biết các gói cần thiết cho một nhiệm vụ nhất định, thì bạn có thể đi đến kho lưu trữ được chỉ định để phân phối và tìm kiếm nó. Bạn có thể tra cứu các gói Alpine Linux tại đây.

Bây giờ, hãy tạo một image mới bằng cách sử dụng file Dockerfile mới và xem sự khác biệt về kích thước tệp:

docker image build --tag custom-nginx:built .

# Sending build context to Docker daemon  1.055MB
# Step 1/7 : FROM alpine:latest
#  ---> 7731472c3f2a
# Step 2/7 : EXPOSE 80
#  ---> Running in 8336cfaaa48d
# Removing intermediate container 8336cfaaa48d
#  ---> d448a9049d01
# Step 3/7 : ARG FILENAME="nginx-1.19.2"
#  ---> Running in bb8b2eae9d74
# Removing intermediate container bb8b2eae9d74
#  ---> 87ca74f32fbe
# Step 4/7 : ARG EXTENSION="tar.gz"
#  ---> Running in aa09627fe48c
# Removing intermediate container aa09627fe48c
#  ---> 70cb557adb10
# Step 5/7 : ADD https://nginx.org/download/${FILENAME}.${EXTENSION} .
# Downloading [==================================================>]  1.049MB/1.049MB
#  ---> b9790ce0c4d6
# Step 6/7 : RUN apk add --no-cache pcre zlib &&     apk add --no-cache             --virtual .build-deps             build-base             pcre-dev             zlib-dev             openssl-dev &&     tar -xvf ${FILENAME}.${EXTENSION} && rm ${FILENAME}.${EXTENSION} &&     cd ${FILENAME} &&     ./configure         --sbin-path=/usr/bin/nginx         --conf-path=/etc/nginx/nginx.conf         --error-log-path=/var/log/nginx/error.log         --http-log-path=/var/log/nginx/access.log         --with-pcre         --pid-path=/var/run/nginx.pid         --with-http_ssl_module &&     make && make install &&     cd / && rm -rfv /${FILENAME} &&     apk del .build-deps
#  ---> Running in 0b301f64ffc1
### LONG INSTALLATION AND BUILD STUFF GOES HERE ###
# Removing intermediate container 0b301f64ffc1
#  ---> dc7e4161f975
# Step 7/7 : CMD ["nginx", "-g", "daemon off;"]
#  ---> Running in b579e4600247
# Removing intermediate container b579e4600247
#  ---> 3e186a3c6830
# Successfully built 3e186a3c6830
# Successfully tagged custom-nginx:built

docker image ls

# REPOSITORY         TAG       IMAGE ID       CREATED         SIZE
# custom-nginx       built     3e186a3c6830   8 seconds ago   12.8MB

Trong khi phiên bản dựa trên Ubuntu là 81,6MB, phiên bản dựa trên Alpine đã giảm xuống chỉ còn 12,8MB. Ngoài trình quản lý gói apk, có một số thứ trong Alpine khác với Ubuntu nhưng chúng không phải là vấn đề lớn. Bạn có thể tìm kiếm trên internet bất cứ khi nào bạn gặp khó khăn.

Cách tạo Docker Image có thể thực thi

Trong phần trước, bạn đã làm việc với image thực thi fhsinchy/rmbyext. Trong phần này, bạn sẽ học cách tạo một image thực thi như vậy.

Để bắt đầu, hãy mở thư mục mà bạn đã clone repository https://github.com/fhsinchy/docker-handbook-projects ở phần trước. Mã cho ứng dụng rmbyext nằm bên trong thư mục con có cùng tên.

Trước khi bắt đầu làm việc trong file Dockerfile, hãy dành một chút thời gian để lên kế hoạch cho kết quả cuối cùng. Theo ý kiến ​​của tôi, nó sẽ giống như thế này:

  • Image phải được cài đặt sẵn Python.
  • Nó phải chứa một bản sao của tập lệnh rmbyext của tôi .
  • Một thư mục làm việc nên được đặt nơi tập lệnh sẽ được thực thi.
  • Tập lệnh rmbyext phải được thiết lập làm điểm vào để image có thể lấy tên phần mở rộng làm đối số.

Để xây dựng hình ảnh được đề cập ở trên, hãy thực hiện các bước sau:

  • Nhận một hình ảnh cơ sở tốt để chạy các tập lệnh Python, như python .
  • Thiết lập thư mục làm việc thành một thư mục dễ truy cập.
  • Cài đặt Git để tập lệnh có thể được cài đặt từ kho lưu trữ GitHub của tôi.
  • Cài đặt tập lệnh bằng Git và pip.
  • Loại bỏ các gói không cần thiết của bản dựng.
  • Đặt rmbyext làm điểm vào cho image này.

Bây giờ, hãy tạo một tệp Dockerfile mới bên trong thư mục rmbyext và đặt đoạn mã sau vào đó:

FROM python:3-alpine

WORKDIR /zone

RUN apk add --no-cache git && \
    pip install git+https://github.com/fhsinchy/rmbyext.git#egg=rmbyext && \
    apk del git

ENTRYPOINT [ "rmbyext" ]

Giải thích cho các chỉ dẫn trong tệp này như sau:

  • Chỉ dẫn FROM thiết lập python là image cơ sở, làm môi trường lý tưởng để chạy các script Python. Thẻ 3-alpine cho biết rằng bạn muốn phiên bản Python 3 chạy trên Alpine.
  • Chỉ dẫn WORKDIR thiết lập thư mục làm việc để mặc định là /zone. Tên của thư mục làm việc là hoàn toàn ngẫu nhiên ở đây. Tôi thấy zone là một tên phù hợp, bạn có thể sử dụng bất cứ thứ gì bạn muốn.
  • rmbyext được cài đặt từ GitHub, git là một phụ thuộc vào thời gian cài đặt. Chỉ dẫn RUN trên dòng 5 cài đặt git sau đó cài đặt rmbyext sử dụng Git và pip. Nó cũng xóa git sau đó.
  • Cuối cùng trên dòng 9, chỉ dẫn ENTRYPOINT thiết lập rmbyext làm điểm vào cho image này.

Trong toàn bộ tệp này, dòng 9 là phép thuật biến image có vẻ bình thường này thành một image có thể thực thi được. Bây giờ để xây dựng image, bạn có thể thực hiện lệnh sau:

docker image build --tag rmbyext .

# Sending build context to Docker daemon  2.048kB
# Step 1/4 : FROM python:3-alpine
# 3-alpine: Pulling from library/python
# 801bfaa63ef2: Already exists 
# 8723b2b92bec: Already exists 
# 4e07029ccd64: Already exists 
# 594990504179: Already exists 
# 140d7fec7322: Already exists 
# Digest: sha256:7492c1f615e3651629bd6c61777e9660caa3819cf3561a47d1d526dfeee02cf6
# Status: Downloaded newer image for python:3-alpine
#  ---> d4d4f50f871a
# Step 2/4 : WORKDIR /zone
#  ---> Running in 454374612a91
# Removing intermediate container 454374612a91
#  ---> 7f7e49bc98d2
# Step 3/4 : RUN apk add --no-cache git &&     pip install git+https://github.com/fhsinchy/rmbyext.git#egg=rmbyext &&     apk del git
#  ---> Running in 27e2e96dc95a
### LONG INSTALLATION STUFF GOES HERE ###
# Removing intermediate container 27e2e96dc95a
#  ---> 3c7389432e36
# Step 4/4 : ENTRYPOINT [ "rmbyext" ]
#  ---> Running in f239bbea1ca6
# Removing intermediate container f239bbea1ca6
#  ---> 1746b0cedbc7
# Successfully built 1746b0cedbc7
# Successfully tagged rmbyext:latest

docker image ls

# REPOSITORY         TAG        IMAGE ID       CREATED         SIZE
# rmbyext            latest     1746b0cedbc7   4 minutes ago   50.9MB

Ở đây tôi không cung cấp bất kỳ thẻ nào sau tên image, vì vậy image đã được gắn thẻ latest theo mặc định. Bạn sẽ có thể chạy image như bạn đã thấy trong phần trước. Hãy nhớ tham khảo tên image thực tế mà bạn đã đặt thay vì fhsinchy/rmbyext.

Cách chia sẻ Docker Image của bạn

Bây giờ bạn đã biết cách tạo image, đã đến lúc chia sẻ chúng với mọi người. Chia sẻ image trực tuyến rất dễ dàng. Tất cả những gì bạn cần là một tài khoản tại bất kỳ cơ sở đăng ký trực tuyến nào. Tôi sẽ sử dụng Docker Hub ở đây.

Chuyển hướng đến trang Đăng ký và tạo một tài khoản miễn phí. Một tài khoản miễn phí cho phép bạn lưu trữ không giới hạn các kho lưu trữ công cộng và một kho lưu trữ riêng tư.

Khi bạn đã tạo tài khoản, bạn sẽ phải đăng nhập vào tài khoản đó bằng cách sử dụng docker CLI. Vì vậy, hãy mở terminal của bạn và thực hiện lệnh sau để làm như vậy:

docker login

# Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
# Username: fhsinchy
# Password: 
# WARNING! Your password will be stored unencrypted in /home/fhsinchy/.docker/config.json.
# Configure a credential helper to remove this warning. See
# https://docs.docker.com/engine/reference/commandline/login/#credentials-store
#
# Login Succeeded

Bạn sẽ được nhắc nhập tên người dùng và mật khẩu của mình. Nếu bạn nhập đúng, bạn sẽ đăng nhập thành công vào tài khoản của mình.

Để chia sẻ image trực tuyến, image đó phải được gắn thẻ. Bạn đã học về cách gắn thẻ trong phần phụ trước. Chỉ để làm mới bộ nhớ của bạn, cú pháp chung cho tùy chọn --tag hoặc -t như sau:

--tag <image repository>:<image tag>

Ví dụ, bạn muốn chia sẻ image custom-nginx trực tuyến. Để làm như vậy, hãy mở một cửa sổ terminal mới bên trong thư mục dự án custom-nginx.

Để chia sẻ image trực tuyến, bạn sẽ phải gắn thẻ image đó theo cú pháp <docker hub username>/<image name>:<image tag>. Tên người dùng của tôi là fhsinchy vì vậy lệnh sẽ giống như sau:

docker image build --tag fhsinchy/custom-nginx:latest --file Dockerfile.built .

# Step 1/9 : FROM ubuntu:latest
#  ---> d70eaf7277ea
# Step 2/9 : RUN apt-get update &&     apt-get install build-essential                    libpcre3                     libpcre3-dev                     zlib1g                     zlib1g-dev                     libssl-dev                     -y &&     apt-get clean && rm -rf /var/lib/apt/lists/*
#  ---> cbe1ced3da11
### LONG INSTALLATION STUFF GOES HERE ###
# Step 3/9 : ARG FILENAME="nginx-1.19.2"
#  ---> Running in 33b62a0e9ffb
# Removing intermediate container 33b62a0e9ffb
#  ---> fafc0aceb9c8
# Step 4/9 : ARG EXTENSION="tar.gz"
#  ---> Running in 5c32eeb1bb11
# Removing intermediate container 5c32eeb1bb11
#  ---> 36efdf6efacc
# Step 5/9 : ADD https://nginx.org/download/${FILENAME}.${EXTENSION} .
# Downloading [==================================================>]  1.049MB/1.049MB
#  ---> dba252f8d609
# Step 6/9 : RUN tar -xvf ${FILENAME}.${EXTENSION} && rm ${FILENAME}.${EXTENSION}
#  ---> Running in 2f5b091b2125
### LONG EXTRACTION STUFF GOES HERE ###
# Removing intermediate container 2f5b091b2125
#  ---> 2c9a325d74f1
# Step 7/9 : RUN cd ${FILENAME} &&     ./configure         --sbin-path=/usr/bin/nginx         --conf-path=/etc/nginx/nginx.conf         --error-log-path=/var/log/nginx/error.log         --http-log-path=/var/log/nginx/access.log         --with-pcre         --pid-path=/var/run/nginx.pid         --with-http_ssl_module &&     make && make install
#  ---> Running in 11cc82dd5186
### LONG CONFIGURATION AND BUILD STUFF GOES HERE ###
# Removing intermediate container 11cc82dd5186
#  ---> 6c122e485ec8
# Step 8/9 : RUN rm -rf /${FILENAME}}
#  ---> Running in 04102366960b
# Removing intermediate container 04102366960b
#  ---> 6bfa35420a73
# Step 9/9 : CMD ["nginx", "-g", "daemon off;"]
#  ---> Running in 63ee44b571bb
# Removing intermediate container 63ee44b571bb
#  ---> 4ce79556db1b
# Successfully built 4ce79556db1b
# Successfully tagged fhsinchy/custom-nginx:latest

Trong lệnh này, fhsinchy/custom-nginx là kho lưu trữ image và latest là thẻ. Tên image có thể là bất kỳ thứ gì bạn muốn và không thể thay đổi sau khi bạn đã tải image lên. Thẻ có thể được thay đổi bất cứ khi nào bạn muốn và thường phản ánh phiên bản của phần mềm hoặc các loại bản dựng khác nhau.

Lấy image node làm ví dụ. Các image node:lts liên quan đến các phiên bản hỗ trợ lâu dài của Node.js trong khi phiên bản node:lts-alpine dùng để chỉ phiên bản Node.js xây dựng cho Alpine Linux, nó có kích thước nhỏ hơn nhiều so với phiên bản bình thường.

Nếu bạn không cung cấp cho image bất kỳ thẻ nào, nó sẽ tự động được gắn thẻ latest. Nhưng điều đó không có nghĩa thẻ latest sẽ luôn đề cập đến phiên bản mới nhất. Nếu vì lý do nào đó, bạn gắn thẻ phiên bản cũ hơn của image một cách rõ ràng latest, thì Docker sẽ không thực hiện thêm bất kỳ nỗ lực nào để kiểm tra chéo điều đó.

Sau khi image đã được xây dựng, bạn có thể tải image lên bằng cách thực hiện lệnh sau:

docker image push <image repository>:<image tag>

Vì vậy, trong trường hợp của tôi, lệnh sẽ như sau:

docker image push fhsinchy/custom-nginx:latest

# The push refers to repository [docker.io/fhsinchy/custom-nginx]
# 4352b1b1d9f5: Pushed 
# a4518dd720bd: Pushed 
# 1d756dc4e694: Pushed 
# d7a7e2b6321a: Pushed 
# f6253634dc78: Mounted from library/ubuntu 
# 9069f84dbbe9: Mounted from library/ubuntu 
# bacd3af13903: Mounted from library/ubuntu 
# latest: digest: sha256:ffe93440256c9edb2ed67bf3bba3c204fec3a46a36ac53358899ce1a9eee497a size: 1788

Tùy thuộc vào kích thước image, quá trình tải lên có thể mất một khoảng thời gian. Sau khi hoàn tất, bạn sẽ có thể tìm thấy image trong trang hồ sơ trung tâm của mình.

Trong phần tiếp theo bạn sẽ tìm hiểu cách xây dựng ứng dụng JavaScript với Docker.

Sổ tay Docker: Cách xây dựng ứng dụng JavaScript với Docker
Trong hướng dẫn này, bạn sẽ học cách xây dựng ứng dụng JavaScript với Docker. Tạo image cho ứng dụng, tối ưu image, chạy container, ...

Bài viết này được dịch từ cuốn sách The Docker Handbook của Farhan Hasin Chowdhury:

The Docker Handbook – 2021 Edition
The concept of containerization itself is pretty old. But the emergence of the Docker Engine [https://docs.docker.com/get-started/overview/#docker-engine] in2013 has made it much easier to containerize your applications. According to the Stack Overflow Developer Survey - 2020[https://insights.stackoverflow.com/survey/2020#overview…
DockerDevOps
Bài Viết Liên Quan:
Deploy ứng dụng ASP.NET Core bằng Docker
Trung Nguyen 29/03/2021
Deploy ứng dụng ASP.NET Core bằng Docker

Bài viết này sẽ hướng dẫn bạn chi tiết cách deploy ứng dụng ASP.NET Core bằng Docker.

Sổ tay Docker: Cách sử dụng Docker Compose
Trung Nguyen 20/03/2021
Sổ tay Docker: Cách sử dụng Docker Compose

Trong hướng dẫn này, bạn sẽ tìm hiểu các kiến thức cơ bản về Docker Compose, cách chạy và quản lý các dịch vụ sử dụng Docker Compose.

Sổ tay Docker: Cách chạy ứng dụng JavaScript trên nhiều Container
Trung Nguyen 20/03/2021
Sổ tay Docker: Cách chạy ứng dụng JavaScript trên nhiều Container

Trong hướng dẫn này, bạn sẽ học cách triển khai ứng dụng trên nhiều container trong Docker.

Sổ tay Docker: Thao tác Docker Network cơ bản
Trung Nguyen 19/03/2021
Sổ tay Docker: Thao tác Docker Network cơ bản

Bạn sẽ tìm hiểu các kiến thức cơ bản về Docker Network, các thao tác mạng cơ bản như: tạo và xóa mạng, gắn container vào mạng, gỡ container khỏi mạng.