Docker: Chưa biết gì đến biết dùng (Phần 2 - Dockerfile)
1. Mở đầu
Xin chào các bạn, ở bài trước Docker: Chưa biết gì đến biết dùng (Phần 1):
-
Chúng ta đã cùng nhau tìm hiểu công nghệ ảo hóa
containerlization
với công cụdocker
của công ty cùng tên. Tuy nhiên nội dung chủ yếu tập trung vào lý thuyết vào lịch sử và cơ chế hoạt động. -
Hôm nay, chúng ta sẽ đi sâu hơn để khám phá
cách sử dụng Docker trong thực tế
.
-
Như bác
Hoàng Nam Tiến
từng nói:Hãy nhớ rằng Tiếng Anh là ngôn ngữ để làm việc , để sống, không còn là ngoại ngữ nữa.
-
Thì
Docker
hiện tại gần như đã trở thành tiêu chuẩn trong phát triển phần mềm, không chỉ xuất phát từ sự tiện lợi, mà còn bởi cách nó thay đổi tư duy, quy trình và phương pháp làm việc. -
Nên nếu như bạn chưa dùng
Docker
thì câu hỏi không phải làTại sao
nữa mà làLàm thế nào để bắt đầu
. Chuỗi bài viết này sẽ hỗ trợ bạn giải quyết phần nào câu hỏi đó. -
Trước khi đọc phần 2, đừng quên đọc lại phần 1 - để mình hiểu rõ
lý thuyết
vàbản chất
của Docker đã nhé.
Let's go ! 👋👋👋
2. Source code
Bớt lý thuyết lại, coding luôn:
Dưới đây là cấu trúc demo source code
của chúng ta:
-
Thư mục
web_app
:-
Đây là nơi chứa mã nguồn chương trình của bạn.
-
Nó có thể là một ứng dụng viết bằng
PHP
,Node.js
hoặcPython
... -
Trong ví dụ này, để cho đơn giản, ta chỉ tạo file
index.html
với nội dung:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Docker tutorial</title> </head> <body> <div>Hello World</div> </body> </html>
Sẽ hiển thị dòng
Hello World
khi chạy trên trình duyệt. -
Trong quá trình phát triển, bạn có thể chỉnh sửa mã nguồn trong thư mục này trực tiếp trên máy thật (host OS) bằng các trình soạn thảo như
VS Code
. -
Nhờ
docker volume
, những thay đổi sẽngay lập tức
được cập nhật vào bên trong container, kết hợp vớihot reload
để hiển thị kết quả tức thì. Ví dụ sửa textHello World
thànhHello World From Asia
.
-
-
Dockerfile
:- Như đã tìm hiểu, file này định nghĩa cách tạo
docker image
. - Sau khi build được
image
, bạn có thể khởi tạocontainer
- đây chính là môi trường ảo hoá để chạy ứng dụng ở bên trong.
- Như đã tìm hiểu, file này định nghĩa cách tạo
-
File
start.sh
:- Đây là script chứa những câu lệnh được thực thi mỗi khi container khởi động.
- Có thể dùng script này để khởi chạy các dịch vụ như MySQL, Nginx, Redis, ...
3. Dockerfile là gì ?
-
- Một file dạng text thuần tuý.
- Không có đuôi mở rộng (như .txt, .sh ...) mà docker
nhận diện
nó thông qua tênDockerfile
cố định. - Gồm các lệnh được viết tuần tự, hướng dẫn Docker xây dựng
docker image
mà bạn muốn.
-
Có thể liên tưởng tới việc cài lại hệ điều hành:
- Khi cài lại Windows:
- Bạn dùng usb boot để cài os lên laptop.
- Sau đó cài các phần mềm lên như Chrome, VS code, ...
- Thì khi
build docker images
từDockerfile
cũng tương tự:-
Bạn cũng bắt đầu từ một base image ví dụ:
ubuntu
,centos
... -
Sau đó thêm các lệnh cài đặt, sao chép hoặc cấu hình.
# Use the official Ubuntu image as the base FROM ubuntu:latest # Configure the main working directory WORKDIR /app # Update package lists and install necessary packages RUN apt-get update; \ apt-get install -y --no-install-recommends \ nginx \ nano \ vim \ postgresql; \ apt-get clean; \ rm -rf /var/lib/apt/lists/* # Copy the startup script and set permissions COPY start.sh . RUN chmod +x ./start.sh # Set the entrypoint ENTRYPOINT ["./start.sh"] # Expose the required port EXPOSE 80
-
- Như việc xây một bức tường:
- Từng hàng gạch lần lượt được xây.
- Hàng dưới xây xong rồi mới tới hàng trên, hàng trên được kế thừa từ hàng dưới.
- Mỗi một hàng gạch được xem tương tự như một layer (Lát sẽ tìm hiểu kĩ hơn)
- Khi cài lại Windows:
4. Cách viết Dockerfile
Các câu lệnh trong Dockerfile
được liệt kê và giải thích trên trang chủ cũng khá đầy đủ
Sau đây chúng ta sẽ điểm qua những câu lệnh nào hay sử dụng nhất.
4.1 Thiết lập thông tin cơ bản
-
FROM
-
Ví dụ:
FROM centos:7 FROM ubuntu:16.04 FROM ubuntu:22.04 ... FROM ubuntu:latest
-
Câu lệnh này luôn nằm đầu tiên, nó giúp chúng ta chỉ định image ta đang muốn tạo khởi nguồn từ đâu.
-
Hiểu nôm na là bắt nguồn từ ubuntu, hay centos ... cùng phiên bản của nó (
latest
có nghĩa là phiên bản mới nhất) sau đó cài thêm những gì lên. -
Có thể bạn đã biết, Docker hub - nơi lưu trữ và chia sẻ các image sẽ chứa những
image gốc
này. -
Khi Docker đọc tới câu lệnh này, nó sẽ tự động tìm xem image
ubuntu:16.04
này đã tồn tại trong host OS chưa, nếu chưa thì Docker sẽ tự động pull image này về. Trong đó ubuntu là tên của image, 16:04 là tag,latest
nghĩa là tag mới nhất.
-
-
WORKDIR
-
Ví dụ:
WORKDIR /app WORKDIR /path/to/workdir
-
Thay đổi thư mục làm việc
-
4.2 Cài thêm ứng dụng
Bây giờ, chúng ta sẽ cài thêm
các ứng dụng, thiết lập
môi trường cần thiết trên ubuntu:16.04
này
Bạn có thể cài nginx, php, python, ruby, java ... phụ thuộc vào nhu cầu
của bạn, sử dụng:
-
RUN : Để thực thi một câu lệnh nào đó trong
quá trình build images
. -
CMD : Để thực thi một câu lệnh trong quá trình
bật container
.Mỗi Dockerfile
chỉ có
một câu lệnh CMD, nếu như cónhiều hơn
một câu lệnh CMD thì chỉ có câu lệnh CMDcuối cùng
được sử dụng.Một
câu hỏi
đặt ra là nếu tôi muốn khởi độngnhiều ứng dụng
khi start container thì sao, lúc đó hay nghĩ tớiENTRYPOINT
-
ENTRYPOINT: Để thực thi
một số câu lệnh
trong quá trìnhstart container
, những câu lệnh này sẽ được viết trongfile .sh
.
Ví dụ:
# Update ubuntu
RUN apt-get update
# Install nginx
RUN apt-get install -y nginx
# Install mysql server
RUN echo "mysql-server mysql-server/root_password password root" | debconf-set-selections \
&& echo "mysql-server mysql-server/root_password_again password root" | debconf-set-selections \
&& apt-get install -y mysql-server
Trong khi cài nginx, sẽ có câu hỏi xuất hiện và bạn cần trả lời yes/no, khi đó tùy chọn -y
trong RUN apt-get install -y nginx
sẽ thể hiện cho sự lựa chọn yes
của bạn.
4.3 Cấu hình
-
EXPOSE: Container sẽ lắng nghe trên các cổng mạng được chỉ định khi chạy
-
ADD : Copy file, thư mục, remote file thêm chúng vào filesystem của image.
-
COPY : Copy file, thư mục từ host machine vào image. Có thể sử dụng url cho tập tin cần copy.
-
WORKDIR : Định nghĩa directory cho CMD
-
VOLUME : Mount thư mục từ máy host vào container.
Tạo file .sh
Như ở phần entrypoint đã nói, cho dù chỉ cần một câu lệnh mình vẫn dùng ENTRYPOINT, để sau này dễ dàng tùy biến, phát triển.
Tạo file start.sh như sau
#!/bin/bash
service nginx start
exec $@
Ta có ví dụ ở phần này như sau:
ADD start.sh /venv
WORKDIR /venv
RUN chmod a+x /venv/*
ENTRYPOINT ["/venv/start.sh"]
EXPOSE 80
Tổng hợp lại, ta có một ví dụ cho Dockerfile như sau :
FROM ubuntu:16.04
MAINTAINER HoanKi<hoanki2212@gmail.com>
RUN DEBIAN_FRONTEND=noninteractive
RUN apt-get update
RUN apt-get install -y nginx
RUN echo "mysql-server mysql-server/root_password password root" | debconf-set-selections \
&& echo "mysql-server mysql-server/root_password_again password root" | debconf-set-selections \
&& apt-get install -y mysql-server
WORKDIR /venv
COPY start.sh /venv
RUN chmod a+x /venv/*
ENTRYPOINT ["/venv/start.sh"]
EXPOSE 80
- Tạo file
hello.html
trong thư mục webroot:
<h1>Hello word</h1>
-
Mình có push code mẫu lên GitHub: HoanKy/docker_tutorial
-
Còn đây là full document về Dockerfile
5. Cách sử dụng Dockerfile
5.1 Build docker image từ Dockerfile
Ta sử dụng câu lệnh sau:
sudo docker build -t <image_name> .
Ví dụ:
sudo docker build -t ubuntu-nginx .
Bạn có thể dùng lệnh
docker images
để xem thành quả nhé !
5.2 Tạo container từ image.
- Gõ lệnh theo syntax:
sudo docker run -v <forder_in_computer>:<forder_in_container> -p <port_in_computer>:<port_in_container> -it <image_name> /bin/bash
Trong đó:
-
-v : Thể hiện việc mount volume, dữ liệu từ thư mục từ máy thật có thể được truy cập từ thư mục của máy ảo.
-
-p: Cổng mạng từ máy thật để dẫn tới cổng mạng của máy ảo đang chạy.
-
-t: Chạy container và mở terminal bằng /bin/bash
-
Ví dụ vào localhost mặc định của nginx:
sudo docker run -p 9000:80 -it ubuntu-nginx /bin/bash
Kiểm tra log trên Terminal:
Trên trình duyệt:
- Ví dụ vào thư mục dự án ở máy thật:
sudo docker run -v /media/hoanki/PROJECT4/GitRepo/docker_tutorial/webroot:/var/www/html -p 9000:80 -it ubuntu-nginx /bin/bash
Thay thế /media/hoanki/PROJECT4/GitRepo/docker_tutorial/webroot
cho đúng với trên máy bạn nhé !
Kết quả:
6. Cách chia sẻ Dockerfile
Docker-hub: Nơi lưu trữ và chia sẻ các image của Docker, nhưng không chỉ có vậy.
Nãy giờ bạn và tôi đang build image hoàn toàn dưới local, nhưng Docker Hub còn hỗ trợ chúng ta làm việc này trên server nữa.
- Mình tạo mới repo docker-basic trên Github, như này:
- Sau đó mình tạo mới một repo trên Dockerhub.
Vào Create chọn Create Automated Build, chọn Github rồi trỏ tới docker-basic bạn vừa tạo ở GitHub.
Và ta có docker_basic, trông như sau:
-
Dockerhub sẽ hỗ trợ bạn build docker image online, sau đó bạn có thể pull nó về để sử dụng.
-
Vào tab Build Settings,
-
Từ giờ trở đi, mỗi khi bạn push code lên branch nào trên github branch đó bạn đã setting trên DockerHub thì images sẽ tự động được build.
Việc build này sẽ tự động thực hiên trên server Docker Hub nhé, ví dụ mình push code lên branch init_dockerfile
thì Dockerhub tự động build image, kết quả có ở trong tab Build Detail.
Mình tạo pull request, sau khi mình merge nó thì DockerHub cũng sẽ build image tiếp trên branch master.
- Thử đi nhé, đôi khi buid ở local thì pass mà build trên server thì lỗi, cảm giác fix mãi nó mới hiện dòng chữ "Success" nó mới awesome làm sao.
###############################################
Thank for your attention
Mình đã viết xong Docker: Chưa biết gì đến biết dùng (Phần 3), mời các bạn đọc tiếp.
All rights reserved
Bình luận
Thật sự là phải tạo tk để vào vote cho bài viết này, thank chủ thớt
Hehe, mình cảm ơn bạn nhìu nhìu ná =))))))
Cùng ý với pác trên. Phải tạo tk để commend :d Cảm ơn chủ thớt nhiều. Hy vọng có bài mới. Rất thực tiễn, mỗi lần sang project khác là cả đống setup mới conflict điên loạn.
Anh oi cho em hoi .. Em muon link image mysql vao image ubuntu thay vi RUN cmd trong Dockerfile duoc khong a ?
Có bạn nhé, mình sẽ dùng docker-compose bạn ạ, trong phần 3 mình sẽ hướng dẫn.
mình muốn sử dụng sql server trên ubuntu thì docker có xử lí được không :>
Có bạn ạ, theo như hướng dẫn trong phần 2 này là đã sử dụng mysql server trên ubuntu được rồi đó.
Cảm ơn tác giả rất nhiều Mong bác giữ vững phong độ để tiếp tục các phần tiếp theo Hóng phần sau
Cảm ơn comment của bạn nhé ^_^
E hóng phần 3 của bác mà mãi chưa thấy đâu
))
Mình cũng đang viết phần 3 rồi bạn nhé.
Cảm ơn bác rất nhiều
Mình có 3 dự án chạy trên 3 cổng khác nhau. Các dự án là node js dùng chung thư viện, vậy giờ phải tạo 3 docker. Như vậy làm nặng thêm vps không bạn.
Mình có 3 dự án chạy liên tục:
Mình ko tìm cách nào chia nhỏ ra để ứng dụng docker.
Câu trả lời nằm ở
docker-compose
nhé, nó giúp bạn tách biệt ra từng service riêng, mỗi container sẽ chạy 1 service ! Bạn khởi động containerdatabse
vàredis
mà dùng chung lên, ví dụ:Sau đó bật các container mà dùng riêng cho cho mỗi dự án, chúng sẽ kết nối tới chung tới
database
vàredis
ở trên kia.Mình đang cập nhật tiếp phần 3 và viết phần 4 để hướng dẫn triển khai docker theo cách này ở mức đơn giản hơn.
Bạn ơi Link phần 3 bị lỗi à? Và cho mình hỏi khi nào có phần 4 vậy
Mình có save bài viết phần 3 vào Drafts vài ngày để cập nhật thêm một số mục, bây giờ thì bài viết đã public rồi bạn nhé !
Bài viết rất hay, xin cảm ơn nhiều! Link phần 3 hỏng rồi. ad cho xin lại link phần 3 với. thanks!
Mình có save bài viết phần 3 vào Drafts vài ngày để cập nhật thêm một số mục, bây giờ thì bài viết đã public rồi bạn nhé !
Tuyệt vời
cảm ơn thớt đã viết bài này!hay quá :V
bài viết hay. Thank bạn nhiều
Thanks Bác.
đọc bằng tiếng anh mình không thể hiểu hết được, vì kiến thức còn bị hổng, tiếng anh yếu. Nhờ bài này mà hiểu được rõ hơn. Thank bác thớt
xin lỗi, hình như ADD mới cho phép URL và giải nén file tar?
Mình làm tới Kubernetes luôn đi bạn ơi. Bài hay thiệt ak.
Mình sử dụng window để build container, khi chạy câu lệnh "docker run -v D:/Docker/webroot:/var/www/html -p 9000:80 -it ubuntu-nginx /bin/bash" kết quả là success. Nhưng khi vào local host thì hiện lỗi 403, Forbiden. Mình hiểu có thể lỗi do permission của file helloworld.html trong thư mục /var/www/html không được set quyền cho user của nginx là www-data, nhưng khi mình chạy câu lệnh "chown -R www-data:www-data hello.html" thì file vẫn không đổi permission. Không biết là mình hiểu sai lỗi hay bạn có cách nào khắc phục không ạ? Cảm ơn
mình cũng dính lỗi tương tự, hóng solution xD
cho ehỏi tham số -v ở docker run và VOLUME ở DockerFile là cùng 1 chức năng ạ??
đúng rồi đấy bạn
quá tuyệt vời
Hay quá. VN rất cần những người như bạn !!!😀😀😀😀
RUN DEBIAN_FRONTEND=noninteractive Dòng này để làm gì bạn nhỉ
Ví dụ như ban đầu mình build 1 image, sau mình muốn setup thêm 1 số thứ thì mình sửa lại docker file và build lại à bạn, vậy thì nó sẽ lại tạo image mới à.
sao bên trên bạn dùng ADD start.sh /venv, xuống dưới lại dùng COPY start.sh /venv vậy?