Docker – Phần 4 – Tạo image từ Dockerfile

Mở đầu#

Ở các phần trước mình đã giới thiệu tổng quan Docker Engine và kiến trúc của Docker, cũng như chạy một container đầu tiên với các image có sẵn. Ở phần này mình sẽ viết hướng dẫn tạo image bằng Dockerfile

1. Dockerfile là gì ?#

  • Dockerfile là một file dạng text không có extension, và tên bắt buộc phải là Dockerfile
  • Dockerfile là một file kịch bản sử dụng để tạo mới một image

2. Cấu trúc một Dockerfile#

Đây là cấu trúc một Dockerfile của ứng dụng WordPress

FROM php:7.1-apache

# install the PHP extensions we need
RUN set -ex; \
	\
	savedAptMark="$(apt-mark showmanual)"; \
	\
	apt-get update; \
	apt-get install -y --no-install-recommends \
		libjpeg-dev \
		libpng-dev \
	; \
	\
	docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr; \
	docker-php-ext-install gd mysqli opcache zip; \
	\
# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
	apt-mark auto '.*' > /dev/null; \
	apt-mark manual $savedAptMark; \
	ldd "$(php -r 'echo ini_get("extension_dir");')"/*.so \
		| awk '/=>/ { print $3 }' \
		| sort -u \
		| xargs -r dpkg-query -S \
		| cut -d: -f1 \
		| sort -u \
		| xargs -rt apt-mark manual; \
	\
	apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
	rm -rf /var/lib/apt/lists/*

# set recommended PHP.ini settings
# see https://secure.php.net/manual/en/opcache.installation.php
RUN { \
		echo 'opcache.memory_consumption=128'; \
		echo 'opcache.interned_strings_buffer=8'; \
		echo 'opcache.max_accelerated_files=4000'; \
		echo 'opcache.revalidate_freq=2'; \
		echo 'opcache.fast_shutdown=1'; \
		echo 'opcache.enable_cli=1'; \
	} > /usr/local/etc/php/conf.d/opcache-recommended.ini

RUN a2enmod rewrite expires

VOLUME /var/www/html

ENV WORDPRESS_VERSION 5.0.3
ENV WORDPRESS_SHA1 f9a4b482288b5be7a71e9f3dc9b5b0c1f881102b

RUN set -ex; \
	curl -o wordpress.tar.gz -fSL "https://wordpress.org/wordpress-${WORDPRESS_VERSION}.tar.gz"; \
	echo "$WORDPRESS_SHA1 *wordpress.tar.gz" | sha1sum -c -; \
# upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress
	tar -xzf wordpress.tar.gz -C /usr/src/; \
	rm wordpress.tar.gz; \
	chown -R www-data:www-data /usr/src/wordpress

COPY docker-entrypoint.sh /usr/local/bin/

ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["apache2-foreground"]

3. Các command cơ bản trong Dockerfile#

FROM

FROM centos:centos7

FROM chỉ định rằng image build này sẽ base trên image gốc nào

LABEL

LABEL "image-type"="huy-test"
LABEL "image-type1"="huy-test1"
LABEL "image-type2"="huy-test2"

LABEL: Chỉ định label metadata của image. Để xem được các label này sử dụng câu lệnh docker inspect <IMAGE ID>

MAINTAINER

MAINTAINER huytm

MAINTERNER là author (tác giả) build image đó.

RUN

RUN yum update -y

RUN thực hiện một câu lệnh Linux. Tùy vào image gốc mà có các câu lệnh tương ứng (ví dụ Ubuntu sẽ là RUN apt-get update -y)

COPY

COPY start.sh /start.sh

COPY Copy một file từ Dockerhost vào image trong quá trình build image

ENV

ENV source /var/www/html/
COPY index.html ${source}

ENV là biến môi trường sử dụng trong quá trình build image.

ENV chỉ có thể được sử dụng trong các command sau:

  • ADD
  • COPY
  • ENV
  • EXPOSE
  • FROM
  • LABEL
  • STOPSIGNAL
  • USER
  • VOLUME
  • WORKDIR

CMD

CMD ["./start.sh"]

CMD dùng để truyền một Linux command khi khởi tạo container từ image

VOLUME

VOLUME ["/etc/http"]

VOLUME Tạo một volume nằm trong folder /var/lib/docker/volumes của docker host và mount với folder chẳng hạn /etc/http khi khởi chạy container

EXPOSE

EXPOSE 80 443

EXPOSE Chỉ định các port sẽ Listen trong container khi khởi chạy container từ image

4. Tạo images httpd với Dockerfile#

Yêu cầu:

  • VPS đã cài đặt Docker, nếu bạn chưa cài đặt thì cài đặt theo bài viết này
  • Giả định môi trường lý tưởng, các câu lệnh sau đều chạy dưới quyền root

a. Tạo một folder làm việc với Dockerfile

cd ~
mkdir build_image && cd build_image

b. Tạo Dockerfile để build image

touch Dockerfile

Lưu ý: Filename phải là Dockerfile và không có phần mở rộng

c. Nội dung Dockerfile như sau:

FROM centos:centos7

LABEL "image-type"="huy-test"
MAINTAINER huytm

RUN yum update -y
RUN yum install httpd -y

ENV source /var/www/html/

VOLUME ["/var/log/httpd"]

COPY index.html ${source}
COPY start.sh /start.sh

RUN chmod +x /start.sh
CMD ["/start.sh"]

EXPOSE 80

Giải thích:

  • FROM centos:centos7 : Build image dựa trên image gốc là centos 7.
  • RUN yum update -y và RUN yum install httpd -y : Thực hiện các command của centos để update và cài đặt httpd
  • LABEL “image-type”=”huy-test” : Metadata của image có thể có hoặc không.
  • MAINTAINER huytm : Tác giả viết Dockerfile để build image có thể có hoặc không.
  • ENV source /var/www/html/ : Khai báo một biến môi trường tên là source và có giá trị là /var/www/html/. Biến này sẽ được sử dụng ở bước COPY index.html ${source}
  • COPY index.html ${source} : Copy file index.html từ Dockerhost vào đường dẫn biến – được khai báo ở bước trên.
  • COPY start.sh /start.sh : Copy một bash script file vào đường dẫn / để thực hiện một số Linux command khi khởi tạo container từ image này.
  • RUN chmod +x /start.sh : Cho phép quyền thực thi đối với script vừa copy từ bước trên.
  • CMD [“/start.sh”] : Thực thi script khi khởi chạy container từ image này.
  • EXPOSE 80 : Chỉ ra rằng container khi khởi chạy từ image này sẽ LISTEN port 80
  • VOLUME [“/var/log/httpd”] : Chỉ định một phân vùng trên Docker host mount với container khi chạy. Ở đây mình mount theo dạng volume (Có ba kiểu volume, mình sẽ nói trong các bài viết sau). Mount folder /var/log/httpd của container với volume nằm tại /var/lib/portal/volume/ của Docker host

d. Tạo các file cần thiết bổ trợ cho Dockerfile

Vì ở trên mình có sử dụng COPY để COPY các file script start.sh và file source code index.html nên mình sẽ tạo tiếp hai file này.

cat <<EOF > start.sh
	#!/bin/bash
	rm -rf /run/httpd/* /tmp/httpd*
	exec /usr/sbin/apachectl -DFOREGROUND
EOF
echo "<h1>Hello World</h1>" > index.html

e. Tạo image từ Dockerfile

docker build -t huytm/centos7-httpd .

Đây là toàn bộ log của quá trình build

Sending build context to Docker daemon  4.096kB
Step 1/12 : FROM centos:centos7
 ---> 1e1148e4cc2c
Step 2/12 : LABEL "image-type"="huy-test"
 ---> Running in 220997b26a05
Removing intermediate container 220997b26a05
 ---> 767cb6c0db64
Step 3/12 : MAINTAINER huytm
 ---> Running in 87547a35ac1f
Removing intermediate container 87547a35ac1f
 ---> 1333a9d9ceed
Step 4/12 : RUN yum update -y
 ---> Running in 66eb8c88d680
Loaded plugins: fastestmirror, ovl
Determining fastest mirrors
 * base: centos-hn.viettelidc.com.vn
 * extras: centos-hn.viettelidc.com.vn
 * updates: centos-hn.viettelidc.com.vn
Resolving Dependencies
--> Running transaction check
---> Package bind-license.noarch 32:9.9.4-72.el7 will be updated
---> Package bind-license.noarch 32:9.9.4-73.el7_6 will be an update
---> Package device-mapper.x86_64 7:1.02.149-10.el7_6.2 will be updated
---> Package device-mapper.x86_64 7:1.02.149-10.el7_6.3 will be an update
---> Package device-mapper-libs.x86_64 7:1.02.149-10.el7_6.2 will be updated
---> Package device-mapper-libs.x86_64 7:1.02.149-10.el7_6.3 will be an update
---> Package glibc.x86_64 0:2.17-260.el7 will be updated
---> Package glibc.x86_64 0:2.17-260.el7_6.3 will be an update
---> Package glibc-common.x86_64 0:2.17-260.el7 will be updated
---> Package glibc-common.x86_64 0:2.17-260.el7_6.3 will be an update
---> Package krb5-libs.x86_64 0:1.15.1-34.el7 will be updated
---> Package krb5-libs.x86_64 0:1.15.1-37.el7_6 will be an update
---> Package nss.x86_64 0:3.36.0-7.el7_5 will be updated
---> Package nss.x86_64 0:3.36.0-7.1.el7_6 will be an update
---> Package nss-sysinit.x86_64 0:3.36.0-7.el7_5 will be updated
---> Package nss-sysinit.x86_64 0:3.36.0-7.1.el7_6 will be an update
---> Package nss-tools.x86_64 0:3.36.0-7.el7_5 will be updated
---> Package nss-tools.x86_64 0:3.36.0-7.1.el7_6 will be an update
---> Package nss-util.x86_64 0:3.36.0-1.el7_5 will be updated
---> Package nss-util.x86_64 0:3.36.0-1.1.el7_6 will be an update
---> Package openldap.x86_64 0:2.4.44-20.el7 will be updated
---> Package openldap.x86_64 0:2.4.44-21.el7_6 will be an update
---> Package systemd.x86_64 0:219-62.el7 will be updated
---> Package systemd.x86_64 0:219-62.el7_6.3 will be an update
---> Package systemd-libs.x86_64 0:219-62.el7 will be updated
---> Package systemd-libs.x86_64 0:219-62.el7_6.3 will be an update
---> Package tzdata.noarch 0:2018g-1.el7 will be updated
---> Package tzdata.noarch 0:2018i-1.el7 will be an update
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package                Arch       Version                    Repository   Size
================================================================================
Updating:
 bind-license           noarch     32:9.9.4-73.el7_6          updates      87 k
 device-mapper          x86_64     7:1.02.149-10.el7_6.3      updates     292 k
 device-mapper-libs     x86_64     7:1.02.149-10.el7_6.3      updates     320 k
 glibc                  x86_64     2.17-260.el7_6.3           updates     3.7 M
 glibc-common           x86_64     2.17-260.el7_6.3           updates      12 M
 krb5-libs              x86_64     1.15.1-37.el7_6            updates     803 k
 nss                    x86_64     3.36.0-7.1.el7_6           updates     835 k
 nss-sysinit            x86_64     3.36.0-7.1.el7_6           updates      62 k
 nss-tools              x86_64     3.36.0-7.1.el7_6           updates     515 k
 nss-util               x86_64     3.36.0-1.1.el7_6           updates      78 k
 openldap               x86_64     2.4.44-21.el7_6            updates     356 k
 systemd                x86_64     219-62.el7_6.3             updates     5.1 M
 systemd-libs           x86_64     219-62.el7_6.3             updates     406 k
 tzdata                 noarch     2018i-1.el7                updates     490 k

Transaction Summary
================================================================================
Upgrade  14 Packages

Total download size: 24 M
Downloading packages:
Delta RPMs disabled because /usr/bin/applydeltarpm not installed.
warning: /var/cache/yum/x86_64/7/updates/packages/bind-license-9.9.4-73.el7_6.noarch.rpm: Header V3 RSA/SHA256 Signature, key ID f4a80eb5: NOKEY
Public key for bind-license-9.9.4-73.el7_6.noarch.rpm is not installed
--------------------------------------------------------------------------------
Total                                              5.4 MB/s |  24 MB  00:04
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Importing GPG key 0xF4A80EB5:
 Userid     : "CentOS-7 Key (CentOS 7 Official Signing Key) <[email protected]>"
 Fingerprint: 6341 ab27 53d7 8a78 a7c2 7bb1 24c6 a8a7 f4a8 0eb5
 Package    : centos-release-7-6.1810.2.el7.centos.x86_64 (@CentOS)
 From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Updating   : tzdata-2018i-1.el7.noarch                                   1/28
  Updating   : glibc-2.17-260.el7_6.3.x86_64                               2/28
  Updating   : glibc-common-2.17-260.el7_6.3.x86_64                        3/28
  Updating   : nss-util-3.36.0-1.1.el7_6.x86_64                            4/28
  Updating   : systemd-libs-219-62.el7_6.3.x86_64                          5/28
  Updating   : systemd-219-62.el7_6.3.x86_64                               6/28
Failed to get D-Bus connection: Operation not permitted
  Updating   : 7:device-mapper-1.02.149-10.el7_6.3.x86_64                  7/28
  Updating   : 7:device-mapper-libs-1.02.149-10.el7_6.3.x86_64             8/28
  Updating   : nss-sysinit-3.36.0-7.1.el7_6.x86_64                         9/28
  Updating   : nss-3.36.0-7.1.el7_6.x86_64                                10/28
  Updating   : nss-tools-3.36.0-7.1.el7_6.x86_64                          11/28
  Updating   : openldap-2.4.44-21.el7_6.x86_64                            12/28
  Updating   : krb5-libs-1.15.1-37.el7_6.x86_64                           13/28
  Updating   : 32:bind-license-9.9.4-73.el7_6.noarch                      14/28
  Cleanup    : 32:bind-license-9.9.4-72.el7.noarch                        15/28
  Cleanup    : openldap-2.4.44-20.el7.x86_64                              16/28
  Cleanup    : nss-tools-3.36.0-7.el7_5.x86_64                            17/28
  Cleanup    : nss-sysinit-3.36.0-7.el7_5.x86_64                          18/28
  Cleanup    : nss-3.36.0-7.el7_5.x86_64                                  19/28
  Cleanup    : 7:device-mapper-1.02.149-10.el7_6.2.x86_64                 20/28
  Cleanup    : 7:device-mapper-libs-1.02.149-10.el7_6.2.x86_64            21/28
  Cleanup    : systemd-219-62.el7.x86_64                                  22/28
  Cleanup    : systemd-libs-219-62.el7.x86_64                             23/28
  Cleanup    : nss-util-3.36.0-1.el7_5.x86_64                             24/28
  Cleanup    : krb5-libs-1.15.1-34.el7.x86_64                             25/28
  Cleanup    : glibc-common-2.17-260.el7.x86_64                           26/28
  Cleanup    : glibc-2.17-260.el7.x86_64                                  27/28
  Cleanup    : tzdata-2018g-1.el7.noarch                                  28/28
  Verifying  : systemd-libs-219-62.el7_6.3.x86_64                          1/28
  Verifying  : nss-tools-3.36.0-7.1.el7_6.x86_64                           2/28
  Verifying  : systemd-219-62.el7_6.3.x86_64                               3/28
  Verifying  : 32:bind-license-9.9.4-73.el7_6.noarch                       4/28
  Verifying  : nss-util-3.36.0-1.1.el7_6.x86_64                            5/28
  Verifying  : 7:device-mapper-libs-1.02.149-10.el7_6.3.x86_64             6/28
  Verifying  : krb5-libs-1.15.1-37.el7_6.x86_64                            7/28
  Verifying  : nss-sysinit-3.36.0-7.1.el7_6.x86_64                         8/28
  Verifying  : nss-3.36.0-7.1.el7_6.x86_64                                 9/28
  Verifying  : tzdata-2018i-1.el7.noarch                                  10/28
  Verifying  : glibc-2.17-260.el7_6.3.x86_64                              11/28
  Verifying  : glibc-common-2.17-260.el7_6.3.x86_64                       12/28
  Verifying  : 7:device-mapper-1.02.149-10.el7_6.3.x86_64                 13/28
  Verifying  : openldap-2.4.44-21.el7_6.x86_64                            14/28
  Verifying  : nss-3.36.0-7.el7_5.x86_64                                  15/28
  Verifying  : openldap-2.4.44-20.el7.x86_64                              16/28
  Verifying  : nss-util-3.36.0-1.el7_5.x86_64                             17/28
  Verifying  : 7:device-mapper-1.02.149-10.el7_6.2.x86_64                 18/28
  Verifying  : systemd-libs-219-62.el7.x86_64                             19/28
  Verifying  : glibc-common-2.17-260.el7.x86_64                           20/28
  Verifying  : systemd-219-62.el7.x86_64                                  21/28
  Verifying  : nss-tools-3.36.0-7.el7_5.x86_64                            22/28
  Verifying  : glibc-2.17-260.el7.x86_64                                  23/28
  Verifying  : krb5-libs-1.15.1-34.el7.x86_64                             24/28
  Verifying  : 7:device-mapper-libs-1.02.149-10.el7_6.2.x86_64            25/28
  Verifying  : 32:bind-license-9.9.4-72.el7.noarch                        26/28
  Verifying  : nss-sysinit-3.36.0-7.el7_5.x86_64                          27/28
  Verifying  : tzdata-2018g-1.el7.noarch                                  28/28

Updated:
  bind-license.noarch 32:9.9.4-73.el7_6
  device-mapper.x86_64 7:1.02.149-10.el7_6.3
  device-mapper-libs.x86_64 7:1.02.149-10.el7_6.3
  glibc.x86_64 0:2.17-260.el7_6.3
  glibc-common.x86_64 0:2.17-260.el7_6.3
  krb5-libs.x86_64 0:1.15.1-37.el7_6
  nss.x86_64 0:3.36.0-7.1.el7_6
  nss-sysinit.x86_64 0:3.36.0-7.1.el7_6
  nss-tools.x86_64 0:3.36.0-7.1.el7_6
  nss-util.x86_64 0:3.36.0-1.1.el7_6
  openldap.x86_64 0:2.4.44-21.el7_6
  systemd.x86_64 0:219-62.el7_6.3
  systemd-libs.x86_64 0:219-62.el7_6.3
  tzdata.noarch 0:2018i-1.el7

Complete!
Removing intermediate container 66eb8c88d680
 ---> 7827c1f58d69
Step 5/12 : RUN yum install httpd -y
 ---> Running in b3b027cccc5b
Loaded plugins: fastestmirror, ovl
Loading mirror speeds from cached hostfile
 * base: centos-hn.viettelidc.com.vn
 * extras: centos-hn.viettelidc.com.vn
 * updates: centos-hn.viettelidc.com.vn
Resolving Dependencies
--> Running transaction check
---> Package httpd.x86_64 0:2.4.6-88.el7.centos will be installed
--> Processing Dependency: httpd-tools = 2.4.6-88.el7.centos for package: httpd-2.4.6-88.el7.centos.x86_64
--> Processing Dependency: system-logos >= 7.92.1-1 for package: httpd-2.4.6-88.el7.centos.x86_64
--> Processing Dependency: /etc/mime.types for package: httpd-2.4.6-88.el7.centos.x86_64
--> Processing Dependency: libaprutil-1.so.0()(64bit) for package: httpd-2.4.6-88.el7.centos.x86_64
--> Processing Dependency: libapr-1.so.0()(64bit) for package: httpd-2.4.6-88.el7.centos.x86_64
--> Running transaction check
---> Package apr.x86_64 0:1.4.8-3.el7_4.1 will be installed
---> Package apr-util.x86_64 0:1.5.2-6.el7 will be installed
---> Package centos-logos.noarch 0:70.0.6-3.el7.centos will be installed
---> Package httpd-tools.x86_64 0:2.4.6-88.el7.centos will be installed
---> Package mailcap.noarch 0:2.1.41-2.el7 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package             Arch          Version                    Repository   Size
================================================================================
Installing:
 httpd               x86_64        2.4.6-88.el7.centos        base        2.7 M
Installing for dependencies:
 apr                 x86_64        1.4.8-3.el7_4.1            base        103 k
 apr-util            x86_64        1.5.2-6.el7                base         92 k
 centos-logos        noarch        70.0.6-3.el7.centos        base         21 M
 httpd-tools         x86_64        2.4.6-88.el7.centos        base         90 k
 mailcap             noarch        2.1.41-2.el7               base         31 k

Transaction Summary
================================================================================
Install  1 Package (+5 Dependent packages)

Total download size: 24 M
Installed size: 31 M
Downloading packages:
--------------------------------------------------------------------------------
Total                                              4.9 MB/s |  24 MB  00:04
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : apr-1.4.8-3.el7_4.1.x86_64                                   1/6
  Installing : apr-util-1.5.2-6.el7.x86_64                                  2/6
  Installing : httpd-tools-2.4.6-88.el7.centos.x86_64                       3/6
  Installing : centos-logos-70.0.6-3.el7.centos.noarch                      4/6
  Installing : mailcap-2.1.41-2.el7.noarch                                  5/6
  Installing : httpd-2.4.6-88.el7.centos.x86_64                             6/6
  Verifying  : mailcap-2.1.41-2.el7.noarch                                  1/6
  Verifying  : apr-util-1.5.2-6.el7.x86_64                                  2/6
  Verifying  : httpd-tools-2.4.6-88.el7.centos.x86_64                       3/6
  Verifying  : httpd-2.4.6-88.el7.centos.x86_64                             4/6
  Verifying  : apr-1.4.8-3.el7_4.1.x86_64                                   5/6
  Verifying  : centos-logos-70.0.6-3.el7.centos.noarch                      6/6

Installed:
  httpd.x86_64 0:2.4.6-88.el7.centos

Dependency Installed:
  apr.x86_64 0:1.4.8-3.el7_4.1
  apr-util.x86_64 0:1.5.2-6.el7
  centos-logos.noarch 0:70.0.6-3.el7.centos
  httpd-tools.x86_64 0:2.4.6-88.el7.centos
  mailcap.noarch 0:2.1.41-2.el7

Complete!
Removing intermediate container b3b027cccc5b
 ---> ceaa2c4dc611
Step 6/12 : ENV source /var/www/html/
 ---> Running in 0dae76377156
Removing intermediate container 0dae76377156
 ---> 3d6abf9b4e60
Step 7/12 : VOLUME ["/var/log/httpd"]
 ---> Running in a5851687922f
Removing intermediate container a5851687922f
 ---> 5bedc77aef79
Step 8/12 : COPY index.html ${source}
 ---> 233244890ca7
Step 9/12 : COPY start.sh /start.sh
 ---> dde006532a47
Step 10/12 : RUN chmod +x /start.sh
 ---> Running in e639aca6cafe
Removing intermediate container e639aca6cafe
 ---> eeb387dba352
Step 11/12 : CMD ["/start.sh"]
 ---> Running in 2ae88e34619b
Removing intermediate container 2ae88e34619b
 ---> 1e9cab6ef65f
Step 12/12 : EXPOSE 80
 ---> Running in e697bcee7199
Removing intermediate container e697bcee7199
 ---> 97ba637ff1df
Successfully built 97ba637ff1df
Successfully tagged huytm/centos7-httpd:latest

f. Kiểm tra lại image vừa build

docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
huytm/centos7-httpd   latest              97ba637ff1df        4 minutes ago       457MB
centos                centos7             1e1148e4cc2c        2 months ago        202MB

g. Chạy container với image vừa build

docker run -itd -p 9000:80 --name huytm huytm/centos7-httpd

h. Truy cập vào apache của container

Truy cập vào apache của container theo địa chỉ

http://<Docker_host_ip>:9000

Tổng kết#

Như vậy, trong bài viết này mình đã giới thiệu cách build một image từ Dockerfile cũng như giải thích các command trong cấu trúc file của Dockerfile.

Ở bài viết tiếp theo mình sẽ viết về các command cở của Docker, để thao tác với container.

Leave a Reply

Your email address will not be published.