Docker
Rất nhanh về Docker
- Docker để dễ hiểu mình sẽ nói sai định nghĩa của nó một chút. Hiểu docker như một máy ảo, nhưng bạn có thể khởi tạo hay quản lý “máy ảo” này hoàn toàn bằng lệnh thông thường. Thế nên bạn có thể rất dễ quản lý và khởi tạo những cái “máy ảo” thông qua vài dòng lệnh.
- Bạn sẽ có thể viết những config để Docker xây dựng “máy ảo” và chia sẻ file config cho người khác để build một “máy ảo” giống 100% của bạn. Mà không cần những file snapshot bự tới hàng Gigabytes
- Tuy nhiên cái hay hơn của Docker so với máy ảo, là Docker chỉ áo hoá phần hệ điều hành. Vì thế Docker sẽ ít tốn tài nguyên hơn và ít tốn dung lượng của ổ cứng hơn. Để dễ hình dung, Docker sẽ giúp bạn chạy 1 hệ điều hành y như một phần mềm trên máy của bạn. Không cần VMWare hay bất kỳ hypervisor nào khác
- Hầu hết mọi người dùng Docker để tạo ra một môi trường chuẩn để phát triển phần mềm cho nhóm. Tránh việc test trên máy A bị lỗi nhưng máy B thì lại không bị gì do khác nhau về thư viện, hệ điều hành, cài đặt và nhiều thứ khác
Thành phần siêu cơ bản của Docker
- Image ~ Class trong lập trình. Là file “máy ảo” đã được Docker xây dựng với những config bạn tuỳ chỉnh.
- Container ~ Object/Instance trong lập trình. Ở Docker nó có nghĩa là instance của Image được build ra. Khi bạn bật “máy ảo” từ image, thì cái máy ảo đang chạy đó gọi là container
Image
- Pull image từ docker hub về máy
docker pull <image_name>:<tag> # image_id cũng ok
- Chạy một image (tạo container)
docker run <image_name>:<tag> # image_id cũng ok
- Tạo container với tên cho container đó
Ví dụdocker run --name=<container_name> <image_name>:<tag> # image_id cũng ok
docker run --name=my-container example_python:1.0
- Xem các image hiện có
hoặcdocker images
docker image ls
- Khi chạy một docker, tức ta sẽ tạo ra 1 container
- Image id, Image tag cso thể tìm thấy ở trong các lệnh xem images
- Một image có thể tạo ra nhiều container
Container
- Docker container có dung lượng cực kỳ nhẹ, nó chỉ tính thêm dung lượng được tạo ra bởi các file trong container đó
- Để xem các container đang chạy
hoặcdocker ps
docker container ls
- Để xem toàn bộ container đã tắt và chạy
docker container ls -a
- Nếu bạn không muốn chạy một container nào đó nữa
hoặcdocker stop <container_id>
docker stop <container_name>
- Container name và Container ID có thể tìm thấy trong lệnh xem container đang chạy hoặc lệnh xem toàn bộ container
- Ép một container phải dừng ngay lập tức
docker kill <contaier_id|contaier_name>
- Nếu bạn muốn restart một container đang chạy
docker restart <container_id|container_name>
- Bạn muốn chạy lại một container đã tắt
docker start <container_id|container_name>
Execute Command
-
Với Dockerfile, có lệnh
CMD
ở cuối cùng. Có thể override command đó với lệnh saudocker run <image_name>:<tag> <new_command> # image_id cũng ok
Ví dụ
docker run example_python:1.0 python -u software2.py
-
Khi này, nếu có dòng
CMD ["python", "-u", "software.py"]
Thì nó sẽ bị thay thế bởi
python -u software2.py
-
Nếu Dockerfile, không có bất kỳ
ENTRYPOINT
hoặcCMD
nào. vẫn có thể chạy container đó với command bất kỳ bằng với lệnh saudocker run <image_name>:<tag> <command to run> # image_id cũng ok
Ví dụ
docker run example_python:1.0 python -u abc.py
-
Nếu bạn muốn đi vào trong docker để debug hoặc chỉnh sửa file, dùng lệnh
docker exec -it <container_id|container_name> /bin/bash
-
Lưu ý phải có option
-it
nếu muốn tương tác với bash shell. Nếu không, docker chỉ đơn giản chạy lệnh và hiển thị logs ra màn hình -
Nếu chỉ muốn chạy lệnh đơn giản, như chạy 1 file python, thì không cần
-it
cũng đượcdocker exec <container_id|container_name> python abc.py
Logs
- Để xem logs của docker container đang chạy
docker logs <container_id|container_name>
- Lệnh trên sẽ show ra TOÀN BỘ LOGS. Nếu muốn giới hạn logs lại khoảng 5’ gần đây, hoặc 5s gần đây thì thêm option
--since
docker logs <container_id|container_name> --since=5m
docker logs <container_id|container_name> --since=5s
- Nếu muốn follow các logs mới, xài option
-f
docker logs <container_id|container_name> --since=5s -f
docker logs <container_id|container_name> -f
Image Build
- Nếu có sẵn file Dockerfile ở folder hiện tại. Xài lệnh
build
để build Dockerfile ra Docker Imagedocker build .
- Nếu có sẵn file Dockerfile ở folder khác.
docker build ./path/to/Dockerfile/folder
- Mặc định docker image có thể không có tên + tag. Để thêm tên + tag, thêm option
-t <name>:<tag>
docker build . -t <name>:<tag>
- Nếu không có file Dockerfile mà một file khác như abc.dockerfile. Xài option
-f
để chỉ định file name
Ví dụdocker build -f <filename> <path> -t <name>:<tag>
docker build -f python.dockerfile ./my-docker -t my-python:1.0
Image Push
- Tạo sẵn 1 tài khoảng trên hub.docker.com
- Khi tạo docker name, tạo theo định dạng
<docker_username>/<image_name>
- Nếu lỡ tạo 1 tag khác định dạng trên, tag lại bằng lệnh
Ví dụdocker tag <name>:<tag> <docker_username>/<image_name>:<tag>
docker tag example:latest foo/example:latest
- Login vào docker hub
docker login
- Làm theo chỉ dẫn login của docker login tới khi dòng chữ
Login Successfully
- Push lên docker hub
docker push <docker_username>/<image_name>:<tag>
- Nếu không có tag, mặc định nó sẽ là
latest
Port Expose
- Mặc định docker sẽ tạo ra 1 môi trường network không thể truy cập từ máy host và host cũng không thể truy cập container. Đây là chính sách bảo mật của docker vì có thể có người lừa người dùng chạy các docker image nguy hiểm
- Để public 1 port từ docker ra ngoài, ta xài option
-p
Ví dụdocker run -p <host_port>:<container_port> <image>
Lệnh trên sẽ map port 8000 (tại địa chỉdocker run -p 1234:8000 <image>
0.0.0.0
) của container ra port 1234 của địa chỉ0.0.0.0
. Tức khi này, khi ta truy cập 0.0.0.0:1234 (host) tức ta sẽ truy cập được 0.0.0.0:8000 ở container - Lưu ý, nếu container chạy trên địa chỉ
127.0.0.1
=localhost
, thì port sẽ không được public ra. - Mặc định port đc expose sẽ ra trên địa chỉ
0.0.0.0
, để tránh máy khác trong cùng subnet truy cập. Ta thêm127.0.0.1
vào host_portdocker run -p 127.0.0.1:<host_port>:<container_port> <image>
- Mặc định port là TCP, nếu muốn expose UDP xài lệnh
docker run -p <host_port>:<container_port>/UDP <image>
- Ta cũng có thể expose 1 range của port
docker run -p from-to:from-to <image>
- Ví dụ
Khi này ta sẽ expose 1k port từ port 7000 -> port 8000docker run -p 7000-8000:7000-8000 <image>
- Cú pháp ngắn khi port host và port container giống nhau
Sẽ rút gọn thànhdocker run -p <port>:<port> <image>
Tương đươngdocker run -p <port> <image>
docker run -p 127.0.0.1:<host_port>:<container_port> <image>
- Ví dụ về cú pháp ngắn
Khi này truy cập vào localhost:8000 sẽ truy cập vào được 0.0.0.0:8000 ở containerdocker run -p 8000 <image>
Volume Mounting
- Khi 1 file được copy vô container, file và file bên ngoài host là 2 file tồn tại độc lập. Tức file A ở ngoài host có chỉnh sửa, thì file A trong container cũng sẽ không ảnh hưởng
- Điều này sẽ làm khó trong việc lưu trữ file từ container ra ngoài host hay việc chỉnh sửa code từ host để debug trên container
- Để container và host trỏ tới cùng 1 file / folder, ta sẽ xài option
-v
(volume)
Ví dụdocker run -v host/path:container/path <image>
docker run -v D:\Docker\fileA.md:/app/fileA.md <image>
- Ví dụ trên thì khi này host và docker sẽ cùng đọc chung 1 file là
fileA
- Tức nếu host có đổi
fileA
thìfileA
trong container cũng sẽ thay đổi theo - Tương tự, nếu container thay đỔi
fileA
thì host cũng sẽ thấy nó được thay đổi - Ví dụ folder
docker run -v D:\Docker:/home/foo <image>
- Khi này
Docker
vàfoo
dù khác tên nhưng đều trỏ về 1 folder làDocker
. Mọi sự thay đổi của Docker đề sẽ ảnh hưởng lênfoo
và ngược lại
Environment Variables
- Xài lệnh
-e
hoặc--env
để thêm các biến môi trường. - Một biến môi trường phổ biến trong linux chính là
PATH
docker run -e MY_NAME=foo -e MY_FEN=bar <image>
- Doạn trên có thể viết thành
docker run -e MY_NAME=foo,MY_FEN=bar <image>
- Hoặc có thể dùng file. Với cú pháp
KEY=VALUE
- Ví dụ file
my-env.env
MY_NAME=foo MY_FEN=bar
docker run --env-file=my-env.env <image>
- Cả 3 ví dụ trên, khi execute vào container và chạy lệnh
echo $MY_NAME echo $MY_FEN
- Sẽ ra kết quả
foo bar
Dockerfile
- Một Dockerfile luôn và phải luôn có lệnh
FROM
FROM <image_name>:<tag>
- Ví dụ
Sẽ lấy base image của python, version 3.9 vềFROM python:3.9-alpine
-
Sẽ lấy base image là ubuntu version 20.04FROM ubuntu:20.04
- Nếu không có image ưng ý và muốn build từ đầu
FROM scratch
- Để chạy các lệnh setup Dockerimage, ta xài lệnh
RUN
- Lưu ý, khi xài RUN, khi build 1 docker image. Docker sẽ chờ cho lệnh RUN này chạy xong. -> TUYỆT ĐỐI KHÔNG DÙNG LỆNH RUN ĐỂ CHẠY CHƯƠNG TRÌNH CHÍNH
RUN <command>
- Ví dụ
Để update và cài dặt toàn bộ package của ubuntu lên phiên bản mới nhấtRUN apt update && apt install -y
- Một lưu ý nữa, các command trong
RUN
, tuyệt đối không được đòi hỏi người dùng phải tương tác với nó. Như nhấn yes để tiếp tục, điền thông tin, v.v -
RUN python inf_loop
- Ở ví dụ trên, khi build, docker sẽ chờ lệnh này vĩnh viễn
- Tạo ra một folder để làm việc. Ta xài lệnh
WORKDIR
-
WORKDIR /path/to/work/folder
- Nếu folder chưa có, nó sẽ tự động tạo, và chuyển thư mục hiện hành tới folder vừa tạo
-
WORKDIR /app
- Lệnh trên sẽ tạo ra folder
/app
và chuyển thư mục đang ở hiện tại (ví dụ như/home
) sang/app
-
WORKDIR
là bao gồm sự kết hợp của 2 lệnh -
RUN mkdir /path/to/work/folder RUN cd /path/to/work/folder
- Để copy các file hiện hành sang container, ta xài lệnh
COPY
-
COPY src dest
- Ví dụ
Copy toàn bộ nội dung của thư mục đang đứng, sang thư mục hiện tại trong containerCOPY . .
-
Copy fileCOPY requirement.txt .
requirement.txt
sang folder hiện tại trong container -
Copy fileCOPY ./xyz/abc.py /app/abc.py
abc.py
từ folderxyz
của host sang folder/app
của container
- Để kích hoạt một lệnh khi 1 container được khởi tạo, ta xài
CMD
hoặcENTRYPOINT
- Với lệnh
CMD
, ta có thể replace lệnh khi tạo container như ở ví dụ container trên - Còn với
ENTRYPOINT
, ta không thể replace lệnh này khi khởi tạo container -
CMD
vàENTRYPOINT
có 3 phiên bảnCMD command args1 args2 -> gọi /bin/bash -c 'command args1 args2' CMD [args1, args2] -> gọi python hoặc bin/bash để chạy các args1, tuỳ theo file CMD [command, arg1, arg2] -> gọi command arg1 arg2
-
Yêu cầu container chạy lệnhCMD python run.py
/bin/sh -c 'python run.py'
khi container vừa khởi tạo -
Chạy lệnhCMD ["start.sh"]
/bin/sh start.sh
khi container vừa khởi tạo -
Chạy lệnhCMD ["run.py"]
python run.py
khi container vừa khởi tạo. Lưu ý, một vài image như alpine có thể sẽ không đủ thông minh để gọi lệnh python. Mà mặc định là/bin/bash
-
Chạy lệnhCMD ["python", "run.py"]
python run.py
khi docker vừa khởi tạo - Cú pháp
CMD
vàENTRYPOINT
tương tự nhau. Nên thayCMD
=ENTRYPOINT
ta sẽ được cú pháp không thể thay thế khi khởi tạo container -
ENTRYPOINT python run.py ENTRYPOINT ["start.sh"] ENTRYPOINT ["python", "run.py"]
- Tuy nhiên, hiện tại Docker đã cho phép thay thế
ENTRYPOINT
bằng option--entrypoint
-
docker run --entrypoint=<command> <image>
- Để set biến môi trường, ta có thể dùng lệnh
ENV
ngay trong Dockerfile -
ENV KEY=VALUE
-
ENV MY_NAME=foo ENV MY_FEN=bar