Docker
Tổng quan về Docker
Docker là gì ?
Docker là một nền tảng cung cấp cho lập trình viên cách building, deploying và running ứng dụng một cách dễ dàng bằng cách đóng gói phần mềm vào các đơn vị tiêu chuẩn hóa được gọi là container (sẽ được tìm hiểu ở phần sau). Nó có mọi thứ mà phần mềm cần để chạy, như là thư viện, tool, code, ...
Một cách hiểu khác như sau: Docker chính là một platform ở tầng OS, nó có thể tinh chỉnh được và nó được phục vụ cho việc chạy ảo hóa các dịch vụ và ứng dụng một cách nhanh chóng hơn.
Khi nào ta cần dùng Docker?
- Phát triển các ứng dụng, dịch vụ yêu cầu cài đặt quá nhiều thứ liên quan, hoặc có version không tương thích với máy chủ hiện tại.
- Khi có nhu cầu thu nhỏ, mở rộng linh hoạt để đáp ứng nhanh. VD như bật/tắt nhanh các container để hỗ trợ tăng tải cho hệ thống của bạn.
- Rất phù hợp với Microservices. Mình chắc chắn rằng bạn sẽ không muốn chạy từng service nhỏ lên và cấu hình chúng bằng tay.
- Tăng tốc, hỗ trợ CI/CD tốt hơn. Vì lúc này automation server chỉ cần quan tâm Docker thay vì lại phải cài đặt đủ thứ vào.
- Dễ thay đổi, di chuyển hơn vì mọi thứ ở trong container. Bản thân Docker vẫn có version control cho các Image, từ đó dễ dàng up/down version ứng dụng hơn.
- An toàn hơn vì mỗi container là một môi trường hoàn toàn độc lập với bên ngoài.
Setup Docker.
Cài đặt Docker trên Windows 10
Tải file cài đặt doker cho windows tại https://www.docker.com/get-started
Sau khi đã tải về, các bạn tiến hành cài đặt thông qua file Docker Desktop Installer.exe vừa tải về. Như mình đã giới thiệu, để chạy được doker cần bật tính năng Hyper-V của windows và trong quá trình cài đặt docker nếu windows chưa được bật Hyper-V, sẽ có một checkbox hỏi xem có muốn bật Hyper-V luôn không, thì mình nên lựa chọn checkbox để bật Hyper-V luôn.
Sau khi cài đặt hoàn tất, cần khởi động lại máy để có thể chạy docker, sau khi khởi động chúng ta có thể thấy biểu tượng Docker ở Tray Icon
Ok vậy là bạn cài xong rồi, giờ chúng ta chỉ cần mở cửa sổ console để kiểm tra xem Docker chạy chưa nhé. Để kiểm tra thông tin chi tiết: chúng ta thực hiện command.
docker info
Để kiểm tra phiên bản Docker, chúng ta dùng command
Khi quá trình khởi chạy hoàn tất, chúng ta có một màn hình giới thiệu khi mở Docker Desktop.
Cài đặt Docker trên Ubuntu 20.4
Đầu tiên bạn phải update và upgrade hệ thống apt của mình trước bằng 2 câu lệnh sau nhé
sudo apt update
sudo apt upgrade
Tiếp theo dùng lệnh sau để download và cài đặt docker
sudo apt install docker.io
Sau khi cài đặt xong, các bạn có thể kiểm tra thông tin docker bằng lệnh
docker info
và thông tin phiên bản bằng lệnh
docker --version
Các bạn có thể tham khảo thêm cách cài docker cho các phiên bản Linux khác tại https://docs.docker.com/engine/
Tài nguyên tham khảo khi học Docker
- Trang tài liệu chính thức: https://docs.docker.com/get-started/overview/
- Nơi các bạn có thể học bằng cách code trực tiếp các câu lệnh https://training.play-with-docker.com/
Kiến trúc ảo hóa và cách hoạt động của Docker
Sự khác biệt Docker và VMWare/VirtualBox
Comment
- Lý do mình để mục này trước là vì đơn giản phỏng vấn intern mình từng bị hỏi câu này. Và vì cũng muốn có cái nhìn rõ ràng hơn về Docker.
Khác biệt
- Hình dưới đây là cấu trúc khái quát từ trái sang phải của công nghệ ảo hóa của máy ảo (Hypervisor) và Docker
Bạn muốn test trên nhiều hệ điều hành, nhiều phiên bản (distributions) của Linux thì việc đầu tiên mà bạn đa số sẽ làm là tạo một máy ảo sau đó cài đặt hệ điều hành trên máy ảo đó, rồi cài tiếp môi trường, IDE các thứ,... Sau đó đối với các máy ảo khác thì bạn làm tương tự. Lúc này bạn có thể chạy các ứng dụng trên đó dễ dàng. Tuy nhiên, điều đó nảy sinh vấn đề: vì ảo hóa trên VirtualBox/VMWare là ảo hóa vật lý, tức là mô phỏng RAM, bộ nhớ, CPU cả hệ điều hành nên dung lượng tổng khá là ì ạch, và chạy khá là chậm.
Docker thì khác, cũng là công nghệ ảo hóa, nhưng mà là ảo hóa hệ điều hành. Container Engine đóng vai trò là mô phỏng hệ điều hành, và các ứng dụng chạy trên container đó, và dĩ nhiên là chạy trên phần lõi được phân phát, chia sẻ (shared kernel), dùng chung trên hệ điều hành với máy host cho nên là chạy nhanh hơn.
Một lý do để phân biệt Docker và VM đó chính là trên máy ảo phải thiết lập cố định tài nguyên, tức là set RAM bao nhiêu GB, bộ nhớ bao nhiêu, CPU mấy nhân và các thông số khác. Còn Docker thì do chạy trên các containers giống như là một ứng dụng độc lập nên là không cần phải làm điều đó.
Sơ lược về kiến trúc Docker
Docker sử dụng kiến trúc client-server (môn Mạng máy tính bạn sẽ được học và làm đồ án). Docker client giao tiếp với Docker daemon, nằm tại local hoặc tại remote (nhiệm vụ là build, thực thi và phân phối các containers). Cả hai giao tiếp với nhau thông qua REST API, thay vì UNIX socket (socket thuần) hay là giao tiếp card mạng. Có một thành phần là Docker Registry, với vai trò là lưu trữ các images (chi tiết về khái niệm sẽ được giải thích phần sau). Docker Hub là một nơi lưu trữ Docker images cho cộng đồng giống như Github (nơi lưu trữ các repositories). Ta vẫn có thể thiết lập image do mình tự tạo ra là “private", chỉ duy nhất bản thân mình sử dụng được.
Docker CLI
Các keywords cần lưu ý
Image
Là một read-only template dùng để tạo ra các container. Một image có thể được tạo ra dựa trên một image khác với một số tùy chỉnh bổ sung.
Nếu bạn đã quen với hướng đối tượng, chúng ta có thể hiểu images như những class còn cointaner là những object.
Container
Là đơn vị phần mềm cung cấp cơ chế đóng gói ứng dụng, mã nguồn, thiết lập, thư viện… vào một đối tượng duy nhất. Ứng dụng sau khi được đóng gói có thể hoạt động một cách nhanh chóng và hiệu quả trên các môi trường điện toán khác nhau. Từ đó nó có thể tạo ra một môi trường hoàn hảo nơi mà có mọi thứ để chương trình có thể hoạt động được, không chịu sự tác động từ môi trường của hệ thống cũng như không làm ảnh hưởng ngược lại về phía hệ thống chứa nó.
Dockerfile
Là một file dạng text không có phần đuôi mở rộng, chứa các đặc tả về một trường thực thi phần mềm, cấu trúc cho Docker image. Từ những câu lệnh đó, Docker sẽ build ra Docker image (thường có dung lượng nhỏ từ vài MB đến lớn vài GB).
Volume
Là cơ chế tạo và sử dụng dữ liệu của docker, có nhiệm vụ lưu trữ dữ liệu độc lập với vòng đời của container.
Có 3 trường hợp sử dụng Volume:
- Giữ lại dữ liệu khi một Container bị xóa.
- Để chia sẻ dữ liệu giữa máy chủ vật lý và Docker Container.
- Chia sẻ dữ liệu giữa các Docker Container.
Docker CLI Cheatsheet
Command cơ bản
Command | Chức năng |
---|---|
docker --version | Kiểm tra phiên bản docker |
docker info | Thông tin hệ thống docker |
docker inspect <name-or-id-of-image-container> | Lấy thông tin về image hoặc container |
docker help | Hiển thị các chức năng của các command trong docker |
Command thao tác với Image
Command | Chức năng |
---|---|
docker image ls | Xem toàn bộ image |
docker image build <dockerfile-path> | Build image từ docker file |
docker pull <image-name>:<tag> | Pull image từ Docker Huyb |
docker push <image-name>:<tag> | Push image lên Docker Hub |
docker image prune | Xoá những image không tên hoặc chưa bao giờ dùng |
docker image rm <image-name-or-id> | Xoá image |
docker system prune | sự kết hợp của docker image prune và
docker container prune |
Command thao tác với Container
Command | Chức năng |
---|---|
docker container run <image> | Chạy 1 container từ image |
docker start <container> | Chạy 1 container đã tồn tại |
docker container stop <container> | Dừng containter |
docker container kill <container> | Kill container |
docker container rm <container> | Xoá container |
docker container prune | Xoá toàn bộ container đã dừng |
docker logs -f <container> | Đọc log container |
docker system prune | sự kết hợp của docker image prune và
docker container prune |
Command thao tác với Network
Command | Chức năng |
---|---|
docker network ls | Liệt kê danh sách các mạng hiện có |
docker network connect <name-network> <container> | Nối container vào mạng |
Một số flag thường dùng trong các command
Command | Chức năng |
---|---|
--help | Hiển thị toàn bộ các flag của command và chức năng của chúng |
-name | Tên |
-d | Chạy nền (run in background) |
-t | cho phép xuất ra terminal |
-i | cho phép người dùng thao tác bằng command |
-f | cho phép xuất ra file |
-a | tất cả |
-v | volume (tìm hiểu bên dưới) |
--last <number> | lọc n container gần nhất |
-it | cho phép thao tác vào và ra (nên dùng) |
Chạy container hello-world
Để kiểm tra xem docker đã có thể hoạt động hay chưa, chúng ta sẽ thử tạo 1 container đơn giản nhất bằng lệnh
docker run -ls hello-world
Lệnh này sẽ yêu cầu Docker chạy image có tên là hello-world, còn tag -it là để gắn container vào process hiện tại của terminal, khi đó ta có thể dùng Ctrl+ C để tắt container.
Sau khi chạy lệnh trên, ta ngay lập tức sẽ nhận được kết quả là
Unable to find image 'hello-world:latest' locally
Điều này có nghĩa là hiện tại trên máy của chúng ta không có image "hello-world" vì đây là lần chạy đầu tiên, do đó, Docker sẽ phải kéo image "hello-world" về máy từ Docker hub. Và sau khi kéo image về thành công, ta sẽ nhận được thông báo như sau:
Status: Downloaded newer image for hello-world:latest
Và sau đó docker sẽ thực hiện chạy image "hello-world" vừa kéo về được. Kết quả ta có thể thấy bên dưới
$ docker run -ls hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:37a0b92b08d4919615c3ee023f7ddb068d12b8387475d64c622ac30f45c29c51
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
Chi tiết Docker CLI
Download image
Bước đầu, để có image nào đó bạn tải về từ https://hub.docker.com/search?q=&type=image, tại đó có đủ các loại phù hợp với công việc của bạn!
Và các bạn lưu ý những image nào có dòng Docker Official Images thì đó là những image chính thức được Docker cung cấp, đảm bảo hạn chế rủi ro mã độc nhé
Để download image có sẵn từ DockerHub, ta dùng
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
Ví dụ về 1 lệnh pull đơn giản
$ docker pull debian
Using default tag: latest
latest: Pulling from library/debian
bb7d5a84853b: Pull complete
Digest: sha256:4d6ab716de467aad58e91b1b720f0badd7478847ec7a18f66027d0f8a329a43c
Status: Downloaded newer image for debian:latest
docker.io/library/debian:latest
Ở ví dụ trên, bạn có thể thấy tag được dùng là
latest
.Đây là tag mặc định của docker khi mà không có đầu vào cho tag. Ta có thể pull
image debian với tag khác là jessie
bằng lệnh bên dưới
$ docker pull debian:jessie
jessie: Pulling from library/debian
b82b9923b08d: Pull complete
Digest: sha256:32ad5050caffb2c7e969dac873bce2c370015c2256ff984b70c1c08b3a2816a0
Status: Downloaded newer image for debian:jessie
docker.io/library/debian:jessie
Tuy vậy, tag là thứ có thể được sửa đổi dễ dàng. Chính
vì vậy, mỗi phiên bản của image đều có Digest
là 1 mã độc nhất cho mỗi phiên bản. Ta có thể
tải image debian:jessie
trên bằng lệnh sau sẽ cho kết quả tương tự
$ docker pull debian@sha256:32ad5050caffb2c7e969dac873bce2c370015c2256ff984b70c1c08b3a2816a0
docker.io/library/debian@sha256:32ad5050caffb2c7e969dac873bce2c370015c2256ff984b70c1c08b3a2816a0: Pulling from library/debian
b82b9923b08d: Pull complete
Digest: sha256:32ad5050caffb2c7e969dac873bce2c370015c2256ff984b70c1c08b3a2816a0
Status: Downloaded newer image for debian@sha256:32ad5050caffb2c7e969dac873bce2c370015c2256ff984b70c1c08b3a2816a0
docker.io/library/debian@sha256:32ad5050caffb2c7e969dac873bce2c370015c2256ff984b70c1c08b3a2816a0
Bên dưới là những options có thể dùng được trong câu lệnh này
Name | Chức năng |
---|---|
--all-tags hoặc -a | Tải xuống toàn bộ tag của image từ kho |
--disable-content-trust | Bỏ qua xác minh image |
--platform | Bổ sung từ API 1.32 trở lên Đặt platform nếu máy chủ là máy multi-platform |
-quiet , -q | Giảm những dòng output dài dòng, chỉ xuất hiện output khi đã download xong |
Lệnh xem các image và container
Để xem danh sách các image đang có trên máy, ta dùng lệnh sau:
docker images [OPTIONS] [NAME[:TAG]]
Để xem danh sách các container đang chạy trên máy, ta dùng:
docker ps [OPTIONS]
Hai câu lệnh trên có phần Options tương đối giống nhau, được liệt kê ở bảng bên dưới
Name | Chức năng |
---|---|
--all hoặc -a | Hiển thị toàn bộ image (mặc định image trung gian bị ẩn) hoặc toàn bộ container (mặc định chỉ hiển thị container đang chạy) |
--filter hoặc -f | Lọc kết quả dựa theo điều kiện cho sẵn, đọc thêm về nó trên trang chủ cho image và cho container |
-quiet , -q | Chỉ hiện thị image ID hoặc container ID |
--format | Xem thêm cho image và cho container |
--no-trunc | Hiển thị ID ở dạng đầy đủ |
--digests | (Chỉ có cho images) hiện digests |
--size hoặc-s | (Chỉ có cho container) hiển thị file size |
--last <num> hoặc -n <num> | (Chỉ có cho container) chỉ hiển thị num conterner vừa mới được tạo |
--latest hoặc -l | (Chỉ có cho container) tương tự flag -n 1 |
Xóa các containers và images
Để xoá 1 image, ta dùng lệnh
docker rmi [OPTIONS] IMAGE [IMAGE...]
Còn với container, câu lệnh có cú pháp như sau
docker rm [OPTIONS] CONTAINER [CONTAINER...]
Các tham số đó là:
[OPTIONS]
các thiết lập khi chạy command, bạn hãy xem bảng bên dưới
IMAGE
hoặcCONTAINER
tên image/container hoặc ID của chúng. Mỗi command có thể xoá cùng lúc nhiều image hoặc nhiều container
Chúng cũng có những options tương tự nhau
Name | Chức năng |
---|---|
--force hoặc -f | Buộc hệ thống xoá image hoặc container dù có lỗi xảy ra |
--no-prune | (Chỉ dành cho image) không xoá những image mà image bị xoá phụ thuộc (không xoá image parent) |
--link hoặc -l | (Chỉ dành cho container) Xoá liên kết mạng được chỉ định (tìm hiểu thêm tại phần Docker Networking) |
--volumes hoặc -v | (Chỉ dành cho container) Xoá toàn bộ hoặc các volume được chỉ định có liên kết với container |
Chạy container và dừng tiến trình chạy container.
Để tạo và chạy một container theo cú pháp:
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Các tham số đó là:
[OPTIONS]
các thiết lập khi tạo container, có rất nhiều thiết lập tùy mục đích tạo container, bảng bên dưới sẽ chỉ liệt kê các options thường dùng. Nếu muốn tìm hiểu sâu hơn, các bạn có thể dùng lênhdocker run help
hoặc tìm hiểu tại đây
IMAGE
tên image hoặc ID của image từ nó sinh ra container.
[COMMAND] [ARG...]
lệnh và tham số khi container chạy.
Các options thường dùng
Name | Chức năng |
---|---|
-i | Duy trì mở stdin để nhập lệnh. |
-t | Cho phép kết nối với terminal để tương tác |
-it | Là sự kết hợp của -i và -t , và đây là option
được khuyên dùng khi tạo container mới mà bạn cần truy cập vào terminal của nó
|
-d | Chuyển container sang chế độ chạy trong background ngay khi vừa tạo |
-v <host-path>:<containner-path> | Ánh xạ một thự mục máy host vào một thư mục container, chia sẻ dữ liệu |
-p <host-port>:<container-port> | Ánh xạ 1 cổng từ máy host vào 1 cổng của container |
Khi bạn đang ở trong terminal của 1 container, bạn có
thể gõ Ctrl + C
để thoát và dừng container đó, hoặc bạn gõ Ctrl + P, Ctrl + Q
để thoát khỏi container và đứa nó vào chế độ chạy ngầm
Các container ở chế độ chạy ngầm có thể được kiểm tra
bằng lệnh docker ps
Để quay trở lại 1 terminal của 1 container chạy ngầm, chúng ta dùng lệnh
docker container attach <container>
Vậy nếu chúng ta muốn chạy một container có sẵn nhưng đã dừng thì sao? Ta sẽ dùng lệnh
docker container start -i <container>
Dockerfile
Build Dockerfile
Mục tiêu đạt được trong phần này
- Hiểu được cách hoạt động của Dockerfile
- Đọc hiểu cách setup trên README.md của một vài thư viện trên Github hoặc ở trên các Package Manager khác.
- Vai trò lệnh FROM, WORKDIR, RUN, CMD
- Cách build một image bằng lệnh docker build
- Cách copy một file nằm trong container sang thư mục làm việc (local).
Giới thiệu sơ lược về Dockerfile
Dockerfile là một file script hướng dẫn Docker cách build một image, bằng cách chỉ rõ ra sẽ bao gồm những lệnh nào. Theo cách hiểu ví von của Jeff Delaney (tác giả của video Docker in 100 Seconds) thì Dockerfile giống như DNA (ADN) - thành phần quan trọng để hình thành nên cơ thể con người.
Trong bài hướng dẫn này, ta sẽ tự build một image dùng để download video Youtube bất kì. Khi đó bạn chỉ cần gọi container và link video thì việc còn lại sẽ tự động hóa việc tải video của bạn.
Để làm quen với Dockerfile thì ta sẽ cho phép download một video có sẵn link.
Mở đầu
Để có thể download được video trên Youtube, ta cần sử dụng command youtube-dl trên Github: https://github.com/ytdl-org/youtube-dl
Ở phần Setup các bạn lưu ý duy nhất mục To install it right away for all UNIX users (Linux, macOS, etc.)... Tuy nhiên command này yêu cầu người dùng phải có Python phiên bản 2.6, 2.7, or 3.2+ và sử dụng curl. (nằm ở file README.md)
Để kiểm tra xem Ubuntu có sẵn curl và python hay không thì ta thực hiện bước dưới đây:
(base) potluck:~$ docker container run -it ubuntu
root@ade9d55eced1:/# python
bash: python: command not found
root@ade9d55eced1:/# curl
bash: curl: command not found
Điều này chứng tỏ Ubuntu không có sẵn curl và python. Lúc này ta sẽ dùng lệnh để cài đặt lần lượt như sau (lưu ý bỏ sudo đi):
#Curl install
apt-get update && apt-get install -y curl
#Python install
apt-get install -y python
#Install youtube-dl
curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl
#Change mod that can execute command.
chmod a+rx /usr/local/bin/youtube-dl
Việc sử dụng câu lệnh download video Youtube khá đơn giản: youtube-dl <tên youtube URL>. Tuy nhiên để gọi câu lệnh đó, ta cần chỉ ra thư mục dẫn tới youtube-dl
/usr/local/bin/youtube-dl https://www.youtube.com/watch?v=dQw4w9WgXcQ
Viết Dockerfile để tự động hóa việc download video trên Youtube
Lần này ta sẽ tự động hóa các lệnh trên bằng cách việc viết Dockerfile và build image dựa trên chỉ dẫn trên.
Lúc này ta sẽ xây dựng một image có thể hoạt động giống như image hello-world. Chúng ta cần phải lưu ý rằng image không thể chỉnh sửa được (immutable), thay vào đó ta phải dựa vào image gốc đó mà ta thêm vào các layers, các tập lệnh mà người dùng mong muốn để hình thành một image mới. Như vậy ta sẽ phải cần image gốc.
Lựa chọn môi trường phù hợp khi thực hiện các tập lệnh trên là một điều quan trọng. Khi vào trang Docker Hub để khám phá (chọn mục Operating System), thì vô vàn sự lựa chọn.
https://hub.docker.com/search?type=image&image_filter=official&category=os
Trong bài hướng dẫn này, ta sẽ sử dụng Ubuntu với tag là latest, vì Ubuntu có khá nhiều tools hỗ trợ cho đại đa số công việc.
Có một lưu ý quan trọng là bạn cần phải cẩn thận khi chọn tag version, nhất là bạn làm việc với Python thì vấn đề tương thích package trong phiên bản đó.
Ok, bắt tay vào làm thôi nào!!!
FROM ubuntu:latest
WORKDIR /learn_docker
RUN apt-get update && apt-get install -y curl
RUN apt-get install -y python
RUN curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl
RUN chmod a+rx /usr/local/bin/youtube-dl
CMD ["/usr/local/bin/youtube-dl","https://www.youtube.com/watch?v=dQw4w9WgXcQ"]
Chúng ta có thể các bước như sau:
- Bắt đầu FROM một image: sử dụng image làm bản gốc cùng với :tag. Tag có
thể tìm thấy trên Supported tags and respective
Dockerfile
links. Ta cứ hiểu Tag là một phiên bản.
- WORKDIR tại thư mục: lúc này xác định nơi thực thi nằm ở đâu
- RUN command: thực thi các lệnh trên command line, thường thì lệnh RUN được dùng để setup cấu hình, môi trường, ví dụ như yarn install trước khi chạy. Ta có thể dùng nhiều lệnh RUN vẫn được
- CMD command: dùng để thực thi câu lệnh khi thực hiện container run. CMD
được thực thi khi chạy docker container run, trừ khi ta ghi đè lên. Còn ghi đè như thế nào thì bạn
đọc tham khảo ở phần copy một file từ local sang container.
- CMD, RUN, ENTRYPOINT có 2 dạng, thực thi dạng shell và dạng exec.
- Chú ý rằng nếu nhiều lệnh CMD thì sẽ chỉ lấy lệnh CMD cuối cùng để thực thi
Shell form và Exec form
- Dạng shell là một chuỗi làm tham số để chạy trong /bin/sh -c. Shell form đơn giản là lệnh terminal mà ta sử dụng. Ví dụ "docker container ls". ⇒ Chạy trong shell.
- Còn dạng exec là phân cách từng phần giữa các đoạn lệnh. Bản chất của exec là một mảng JSON được format. Ưu điểm của dạng này là sẽ ghi đè lên /bin/sh -c. ⇒ Không chạy trong shell mà chạy trực tiếp.
Nếu các bạn đã sử dụng python, các bạn sẽ gặp dạng exec form dưới đây, dạng này được phân tách từng phần. Câu lệnh dưới là để xuất địa chỉ MAC trong máy tính ở Windows.
import subprocess
subprocess.check_output(["getmac", "/v","/fo","list"])
Tiếp tục phần demo...
Sau khi đã build xong, thì ta sẽ build image hello-world, với câu lệnh:
docker build . -t surprise
Trong đó: dấu chấm chính là nơi chứa Dockerfile và ta cần đặt tên image đó ( -t <name>):
Dưới đây vì log khá là dài nên đã bỏ bớt một số phần, chỉ chừa lại một vài phần thật sự quan trọng!!!!
$ docker build . -t surprise
Sending build context to Docker daemon 2.048kB
Step 1/7 : FROM ubuntu:latest
latest: Pulling from library/ubuntu
7b1a6ab2e44d: Pull complete
Digest: sha256:626ffe58f6e7566e00254b638eb7e0f3b11d4da9675088f4781a50ae288f3322
Status: Downloaded newer image for ubuntu:latest
---> ba6acccedd29
Step 2/7 : WORKDIR /learn_docker
---> Running in 5fba3acb0753
Removing intermediate container 5fba3acb0753
---> d62babcb46a7
Step 3/7 : RUN apt-get update && apt-get install -y curl
---> Running in 230a06dc266b
...
Removing intermediate container 230a06dc266b
---> 226d9af0dca8
Step 4/7 : RUN apt-get install -y python
---> Running in 069f6b81cffb
...
Step 5/7 : RUN curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl
---> Running in 878c2014461f
Removing intermediate container 878c2014461f
---> 0a7471807a71
Step 6/7 : RUN chmod a+rx /usr/local/bin/youtube-dl
---> Running in 2291d1c1c15f
Removing intermediate container 2291d1c1c15f
---> 4ca8cdd2e3c1
Step 7/7 : CMD ["/usr/local/bin/youtube-dl","https://www.youtube.com/watch?v=dQw4w9WgXcQ"]
---> Running in cafb784e508e
Removing intermediate container cafb784e508e
---> cbda1a1f634c
Successfully built cbda1a1f634c
Successfully tagged surprise:latest
Trong quá trình build thì ta nhận ra có một vài bước liên quan đến hashes và sha256. Những bước này cho thấy các layer mới chồng lên các image. Layers đóng vai trò như là cache trong quá trình build. Nếu như có một sự thay đổi nhỏ trong Dockerfile thì các bước trước sự thay đổi đó sẽ được bỏ qua một cách nhanh chóng để tới bước thay đổi ấy.
Bây giờ ta chạy thử container dưới đây, khá là OK đúng không :)) ?
$ docker container run surprise
WARNING: Assuming --restrict-filenames since file system encoding cannot encode all characters. Set the LC_ALL environment variable to fix this.
[youtube] dQw4w9WgXcQ: Downloading webpage
[youtube] dQw4w9WgXcQ: Downloading player 9216d1f7
[download] Destination: Rick_Astley_-_Never_Gonna_Give_You_Up_Official_Music_Video-dQw4w9WgXcQ.mp4
[download] 100% of 15.95MiB in 04:1266KiB/s ETA 00:003
Ta sẽ kiểm tra loại container của image surprise mà ta đã sử dụng (vì tên của container sẽ được sinh ra ngẫu nhiên). Ta nhận thấy image surprise có container name tên là nostalgic_bartik.
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6c35aa43288c surprise "/usr/local/bin/yout…" 5 minutes ago Exited (0) 52 seconds ago nostalgic_bartik
bfdef9f59b1e hello-world "sh" About an hour ago Up About an hour lucid_hamilton
afdf4cc8a7a2 hello-world "/bin/sh -c ./hello.…" 2 hours ago Exited (0) 2 hours ago youthful_cray
Sau khi đã download xong video, thì bạn sẽ copy file đó vào thư mục local hiện tại
$ docker cp "nostalgic_bartik://learn_docker/Rick_Astley_-_Never_Gonna_Give_You_Up_Official_Music_Video-dQw4w9WgXcQ.mp4" .
Build Dockerfile cải tiến
Mục tiêu đạt được trong phần này
- Phân biệt RUN và CMD
- Phân biệt được CMD và ENTRYPOINT
- Quản lý image verison bằng Tag
- Volume: Bind Mount
Phân biệt RUN và CMD
- RUN: nằm ở giai đoạn build image, và kết quả đó sẽ nằm trong container image. Tức là ta chỉ cần cài 1 lần, những lần sau không cần cài lại (trừ khi gỡ image đó hoặc build Dockerfile khác). Và có thể dùng nhiều lệnh RUN
- CMD: lệnh dùng để thực thi câu lệnh mặc định khi chạy container. CMD có thể bị override nếu chạy container với cú pháp "docker container run <image> <other_command>". Và CMD chỉ thực thi 1 lần thôi, nếu có nhiều CMD thì sẽ lấy lần cuối.
Các bạn lưu ý phần tô đen, vì trong bài hướng dẫn này ta sẽ đề cập một lần nữa.
Tiếp tục phần trước
Lúc này ta sẽ thay đổi 1 tí câu lệnh ở CMD ta chỉ cần download video trên Youtube với link bất kỳ. Ta sẽ quy định ở CMD là gọi youtube-dl ra và phần còn lại sau khi build xong, ta sẽ thực hiện chạy container với lệnh dưới đây:
docker container run surprise https://www.youtube.com/watch?v=dQw4w9WgXcQ
FROM ubuntu:latest
WORKDIR /learn_docker
RUN apt-get update && apt-get install -y curl
RUN apt-get install -y python
RUN curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl
RUN chmod a+rx /usr/local/bin/youtube-dl
CMD ["/usr/local/bin/youtube-dl"]
- Vì ta cập nhật (thêm, xóa, sửa) trên Dockerfile và đã build sẵn image, nên thay vì ghi đè, ta sẽ quản lý phiên bản của image đó bằng cách sử dụng Tag. Đó là lý do vì sao Docker Hub có mục Supported Tags trong đó mỗi tag là một phiên bản.
Còn 1 cách khác là docker commit <tên container cũ> <tên image mới>. Nhưng cách này ít người sử dụng nên sẽ không khuyến khích dùng cách commit. Ví dụ ở câu lệnh này:
docker commit nostalgic_bartik surprise_additional
Thay vào đó ta sẽ dùng lệnh này để cập nhật tag mới:
docker build . -t surprise:v2
:Tag trong đó Tag là phiên bản mà ta đã chỉnh sửa img
Chạy lệnh trên và ta nhận thấy có các dòng lệnh sau:
$ docker build . -t surprise:v2
Sending build context to Docker daemon 16.73MB
Step 1/7 : FROM ubuntu:latest
---> ba6acccedd29
Step 2/7 : WORKDIR /learn_docker
---> Using cache
---> d62babcb46a7
Step 3/7 : RUN apt-get update && apt-get install -y curl
---> Using cache
---> 226d9af0dca8
Step 4/7 : RUN apt-get install -y python
---> Using cache
---> 3a86c830e3d0
Step 5/7 : RUN curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl
---> Using cache
---> 0a7471807a71
Step 6/7 : RUN chmod a+rx /usr/local/bin/youtube-dl
---> Using cache
---> 4ca8cdd2e3c1
Step 7/7 : CMD ["/usr/local/bin/youtube-dl"]
---> Running in c95b89810885
Removing intermediate container c95b89810885
---> 738c53ece616
Successfully built 738c53ece616
Successfully tagged surprise:v2
Nhìn vào dòng log của build thì ta thấy có chữ Using cache và so sánh với mã hash ở mục trên thì hầu như tương đương nhau, chính là các layers đó được dùng để lưu lại cache, giúp cho quá trình build nhanh hơn. Đây là một kỹ thuật tối ưu của Docker mà ta sẽ tìm hiểu ở phần 3.
Sau đó ta sẽ chạy container của surprise:v2
$ docker container run surprise:v2 https://www.youtube.com/watch?v=dQw4w9WgXcQ
docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "https://www.youtube.com/watch?v=dQw4w9WgXcQ": stat https://www.youtube.com/watch?v=dQw4w9WgXcQ: no such file or directory: unknown.
ERRO[0000] error waiting for container: context canceled
Ở đây ta thấy dòng xuất hiện lỗi. Đây là lý do starting container process caused: exec: "https://www.youtube.com/watch?v=dQw4w9WgXcQ". Tức là lệnh này đã bị ghi đè lên CMD khiến cho không thể thực thi được. Chính vì vậy ta cần tìm cách nào đó có thể truyền link như là một tham số. Lúc này ta nhận thấy có ENTRYPOINT. Vậy ENTRYPOINT là gì? Và có khác gì so với CMD?
Theo Document trên Docker, ENTRYPOINT cho phép việc cấu hình container như là một file thực thi lệnh, mà lúc này người dùng chỉ cần cung cấp tham số để hoạt động. Hay cách hiểu khác là cung cấp interface dưới dạng bash package để người dùng tương tác. Ví dụ yarn (Package Manager thay thế npm) có các lệnh yarn add, yarn start, yarn install,... Những từ khóa đó là người dùng truyền vào để thực thi lệnh.
Sau khi thay CMD thành ENTRYPOINT
...
ENTRYPOINT ["/usr/local/bin/youtube-dl"]
Ta sẽ build lại image:
$ docker build . -t surprise
Sending build context to Docker daemon 16.73MB
Step 1/7 : FROM ubuntu:latest
---> ba6acccedd29
Step 2/7 : WORKDIR /learn_docker
---> Using cache
---> d62babcb46a7
Step 3/7 : RUN apt-get update && apt-get install -y curl
---> Using cache
---> 226d9af0dca8
Step 4/7 : RUN apt-get install -y python
---> Using cache
---> 3a86c830e3d0
Step 5/7 : RUN curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl
---> Using cache
---> 0a7471807a71
Step 6/7 : RUN chmod a+rx /usr/local/bin/youtube-dl
---> Using cache
---> 4ca8cdd2e3c1
Step 7/7 : ENTRYPOINT ["/usr/local/bin/youtube-dl"]
---> Running in d138a3da1e56
Removing intermediate container d138a3da1e56
---> 1b58e25348d7
Successfully built 1b58e25348d7
Successfully tagged surprise:latest
Lúc này ta chạy lại câu lệnh và build thành công
$ docker container run surprise https://www.youtube.com/watch?v=dQw4w9WgXcQD
WARNING: Assuming --restrict-filenames since file system encoding cannot encode all characters. Set the LC_ALL environment variable to fix this.
[youtube] dQw4w9WgXcQ: Downloading webpage
[youtube] dQw4w9WgXcQ: Downloading player f8cb7a3b
[download] Destination: Rick_Astley_-_Never_Gonna_Give_You_Up_Official_Music_Video-dQw4w9WgXcQ.mp4
[download] 100% of 15.95MiB in 04:1271KiB/s ETA 00:005
Sau khi đã download xong video, thì bạn sẽ copy file đó vào thư mục local hiện tại
$ docker cp "nostalgic_bartik://learn_docker/Rick_Astley_-_Never_Gonna_Give_You_Up_Official_Music_Video-dQw4w9WgXcQ.mp4" .
Như vậy đã hoàn thành!!!
So sánh CMD và ENTRYPOINT
- Một đặc điểm trong Docker là ENTRYPOINT mặc định là /bin/sh -c nhưng không có COMMAND mặc định.
Lệnh docker run -it ubuntu bash: thì thực chất ta thực thi lệnh /bin/sh -c bash.
Sau này thì ENTRYPOINT, —entrypoint ra đời để có thể tự tùy biến đầu vào hoặc interface.
Ở bài hướng dẫn này, ta sẽ tổng kết CMD và ENTRYPOINT có khác gì nhau?
- ENTRYPOINT:
- Không bị ghi đè, thay vào đó thêm vào khúc sau của command trong ENTRYPOINT
- CMD:
- Bị override khi thực hiện "docker container run <image> <other_command>"
- Dùng để thực thi lệnh mặc định
Trường hợp ENTRYPOINT và CMD tồn tại song song thì sẽ ra sao. Đây là bảng phân tích các trường hợp (sử dụng dạng exec form và shell form)
Các trường hợp
Lệnh | Kết quả |
---|---|
ENTRYPOINT yarn add CMD react | /bin/sh -c ‘yarn add’ /bin/sh -c react |
ENTRYPOINT ["yarn", "add"] CMD react | yarn add /bin/sh -c react |
ENTRYPOINT yarn add CMD ["react"] | bin/sh -c ‘yarn add’ react |
ENTRYPOINT ["yarn", "add"] CMD ["react"] | yarn add react |
Volume: Bind mount
Đây là một trick thay vì bạn phải thao tác copy thêm một lần nữa sang thư mục hiện hành, thì bạn chỉ cần dẫn tới một đường dẫn file về thư mục local hiện tại. Cú pháp ở dưới như sau:
$ docker run -v "$(pwd):/learn_docker" surprise https://www.youtube.com/watch?v=U3ASj1L6_sY
- Trong đó: -v "<đường dẫn local>:<đường dẫn trong container>". pwd là vị trí của thư mục hiện hành.
- Volume được hiểu là folder hoặc file được chia sẻ giữa container và máy thật. Trong máy ảo (VMWare, VirtualBox) có khái niệm tương tự là Shared Folders, dùng để chia sẻ file, folder giữa máy ảo và máy thực, theo một phương thức trao đổi.
- Một cách khác cho công dụng của bind mount đó là đồng bộ việc chỉnh sửa file trên máy sang trên container và ngược lại. -v "<đường dẫn>/<file trùng tên>:<đường dẫn container>/<file trùng tên>".
Cho phép kết nối bên ngoài vào trong Containers
Giới thiệu
Để có thể cho phép kết nối "public network" thì ta cần thực hiện 2 bước:
- EXPOSE <port>: Cho Docker container mở port tại thời điểm chạy. Bước này ta có thể cho phép giao thức TCP hay UDP, và mặc định là TCP. Lưu ý rằng bước này không có mở port cho các dịch vụ khác hay bên ngoài truy cập, mà chỉ là xác định loại port nào sẽ được xuất ra.
- PUBLISH <port> (Không có lệnh này trong Dockerfile): Mở port của Docker container cho các dịch vụ khác truy cập hay các Docker container khác truy cập.
Lần này ta sẽ chuyển sang ví dụ khác, đó là Dockerize hóa một trang web tĩnh sử dụng React framework.
Trước hết ta dùng dòng lệnh dưới đây. Lưu ý là chúng ta sẽ sử dụng Visual Studio Code nên có lệnh "code .".
git clone https://github.com/lenhatquang97/React_Learn.git
cd React_Learn
code .
Mô tả sơ lược:
Để có thể setup React project, thì điều đầu tiên ta cần làm là cài đặt Node.js, sau đó thiết lập biến môi trường (environment variables) để có thể chạy lệnh node <câu lệnh> toàn cục. Sau đó thì cài đặt các package như sau:
npm install --silent
npm install react-scripts -g --silent
Cuối cùng là npm start để chạy ứng dụng. Như vậy là ta đã hình dung cách thức setup dự án sử dụng React. Bây giờ đây là lúc chúng ta sẽ viết Dockerfile cho React Application. Bước này khá khó với các bạn, nên mình sẽ giải thích kĩ hơn ở phần mô tả.
FROM node:16.13
WORKDIR /react_learn
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json ./
COPY package-lock.json ./
RUN npm install --silent
RUN npm install react-scripts -g --silent
COPY . ./
EXPOSE 3000
CMD [ "npm","start" ]
Mô tả Dockerfile:
FROM node:16.13: lý do mình chọn bản 16.13 thay vì bản latest (17.0) là trong quá trình Setup gặp phải lỗi này: https://github.com/webpack/webpack/issues/14532. Do đó ta có thể thấy vì sao node có khá nhiều tag trên Docker Hub.
ENV PATH /app/node_modules/.bin:$PATH: Lệnh ENV dùng để thiết lập biến môi trường. Cú pháp ở đây là ENV <key> = <value> . Lệnh này dùng thiết lập cho Node
COPY package.json ./: lệnh này dùng để copy 1 file tới đường dẫn làm việc hiện hành trong container. Cú pháp ở đây là COPY <đường dẫn file> <đường dẫn thư mục của container>. Mục đích của việc copy 2 file package.json và package-lock.json là để chuẩn bị cho việc thực hiện lệnh npm install —silent . Vì lệnh npm install cài đặt dựa trên 2 file json nên phải đưa 2 file json trước. Tiếp đó ta cài đặt react-scripts.
COPY . ./ : Sao chép tất cả thư mục, tệp trừ những mục trong .dockerignore (phần này sẽ nói sau).
EXPOSE 3000: Cho phép Docker container mở port 3000.
CMD ["npm", "start"] : Chạy lệnh npm start thay vì chạy /bin/sh -c npm start.
.dockerignore
Bước tiếp theo ta cần thực hiện là liệt kê những thư mục hay tệp sẽ không đưa vào trong container. Tạo file .dockerignore (giống như .gitignore) và thực hiện như sau:
node_modules
build
.dockerignore
Dockerfile
Dockerfile.prod
Ta làm như vậy là để tăng tốc việc build một image, từ đó tiết kiệm được thời gian. node_modules dùng để lưu lại các package đã download trên NPM nhưng thực tế là Docker đã lưu lại cache cho việc đó.
Tiếp tục Dockerize React Application
Sau đó ta sẽ thực hiện việc build image tên là react_learn trên Dockerfile
$ docker build . -t learn_react
Sending build context to Docker daemon 1.263MB
Step 1/10 : FROM node:16.13
...
Step 2/10 : WORKDIR /react_learn
---> Running in 1cec2b43be52
Removing intermediate container 1cec2b43be52
---> 25ac09c5b477
Step 3/10 : ENV PATH /app/node_modules/.bin:$PATH
---> Running in 6efc45d012e5
Removing intermediate container 6efc45d012e5
---> d19233389da8
Step 4/10 : COPY package.json ./
---> cbbfc5fddc20
Step 5/10 : COPY package-lock.json ./
---> 7919324b36ef
Step 6/10 : RUN npm install --silent
---> Running in 81d05dded36e
...
Removing intermediate container 81d05dded36e
---> 6e0f0435d398
Step 7/10 : RUN npm install react-scripts -g --silent
---> Running in 8c7f3b2439c6
Removing intermediate container 8c7f3b2439c6
---> d4d5818fe8a8
Step 8/10 : COPY . ./
---> 9a54a4e37393
Step 9/10 : EXPOSE 3000
---> Running in 08496878700c
Removing intermediate container 08496878700c
---> 775d5b1692fc
Step 10/10 : CMD [ "npm","start" ]
---> Running in c36913be2d41
Removing intermediate container c36913be2d41
---> 599b6eb4b3e4
Successfully built 599b6eb4b3e4
Successfully tagged learn_react:latest
Cuối cùng ta sẽ thực hiện việc chạy container bằng cách dùng lệnh dưới đây, trong đó -p là port.
docker container run -p 3000 learn_react
Nếu trường hợp không sử dụng EXPOSE 3000 thì có một câu lệnh khác thay thế:
docker run -p 3000:3000 learn_react với -p <port của local>:<port của container>
$ docker container run -p 3000 learn_react
> my-app@0.1.0 start
> react-scripts start
ℹ 「wds」: Project is running at http://172.17.0.2/
ℹ 「wds」: webpack output is served from
ℹ 「wds」: Content not from webpack is served from /react_learn/public
ℹ 「wds」: 404s will fallback to /
Starting the development server...
Compiled successfully!
You can now view my-app in the browser.
Local: http://localhost:3000
On Your Network: http://172.17.0.2:3000
Note that the development build is not optimized.
To create a production build, use npm run build.
Kết quả thực hiện ở dưới đây:
Tuy nhiên, ta không thể stop được container bằng Ctrl + C. Lúc này ta sẽ mở cửa sổ terminal khác và dùng lệnh trong docker
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7df505285d90 learn_react "docker-entrypoint.s…" 7 minutes ago Up 7 minutes 0.0.0.0:49161->3000/tcp, :::49161->3000/tcp kind_almeida
$ docker container stop kind_almeida
kind_almeida
Và thế là bạn đã có thể tự setup React Application trong Docker.
Xuất bản image lên public repository
Bước cuối cùng là xuất bản project lên trên trang web hub.docker.com
- Vào trang hub.docker.com
- Lần này ta sẽ xuất bản learn_react image. Ta sẽ chọn Create Repository, với tên project là learn_react
- Chọn sang public và bấm nút Create
- Trên terminal hiện có của VSCode, ta sẽ thực hiện: docker login → để có thể xác thực.
- Sau đó ta thực hiện 2 câu lệnh với cú pháp sau
docker tag learn_react <docker_id>/learn_react
docker push <docker_id>/learn_react
Đợi một khoảng thời gian và chờ Docker push trên Docker Hub thành công!!
Docker Compose
Docker Compose là gì?
Theo tài liệu của Docker:
Docker Compose là một công cụ để định nghĩa và chạy các ứng dụng trên nhiều Docker Container. Với Docker Compose, bạn sử dụng file YAML để cấu hình các dịch vụ của ứng dụng. Sau đó, với một lệnh duy nhất, bạn tạo và khởi động tất cả các dịch vụ từ cấu hinh của mình
Vậy khi nào ta cần dùng Docker Compose?
Một câu chuyện được đặt ra như sau:
Bạn muốn ứng dụng Docker cho một dự án mới hoặc một dự án đang phát triển, bạn phải làm sao? Qua sự tìm hiểu ở các phần trước, bạn hoàn toàn có thể sử dụng Dockerfile build cho mình 1 image và cài đặt tất cả những môi trường cần thiết (như mysql, redis, php,... ) lên một container duy nhất. Tuy nhiên:
- Nếu như bạn muốn dùng kết hợp nhiều image có sẵn trên DockerHub thì sao ?
- Nếu một cơ sở dữ liệu dùng chung cho nhiều project thì sẽ xử lý thế nào ?
- Hơn nữa, với tư duy của OOP, 1 class thì không nên cõng nhiều nhiệm vụ.
Từ đó docker-compose được sinh ra để kết nối các container riêng lẻ với nhau.
Khi đó, chúng ta sẽ xây dựng nhiều container, khi nào cần tương tác với database thì gọi tới container mysql chẳng hạn, tương tác với redis thì gọi tới container redis, cần cái gì thì gọi tới container làm nhiệm vụ đó.
Cài đặt Docker Compose
Đối với Windows: Docker Compose đã được tích hợp vào Docker Desktop bạn đã cài đặt ở phần đầu tiên
Đối với Linux:
- Chạy command bên dưới để tải xuống bản phát hành ổn định mới nhất của Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
- Cấp quyền thực thi cho các file
sudo chmod +x /usr/local/bin/docker-compose
- Kiểm tra Docker Compose đã được cài đặt hay chưa bằng cách kiểm tra phiên bản của nó:
docker-compose --version
Sau khi kiểm tra thì phiên bản trên máy mình là
Docker Compose version v2.0.0
Sử dụng Docker Compose với một project đơn giản
Trong phần này, bạn xây dựng một ứng dụng web Python đơn giản chạy trên Docker Compose. Ứng dụng sử dụng framework Flask và duy trì bộ đếm lượt truy cập trong Redis. Mặc dù ứng dụng mẫu này sử dụng Python, nhưng các khái niệm được trình bày ở đây sẽ dễ hiểu ngay cả khi bạn không quen thuộc với nó.
Setup
Đầu tiên, bạn tạo 1 thư mục chứa project
mkdir composetest
cd composetest
Tiếp theo, tạo 1 file app.py
trong thư
mục mục project vừa tạo với nội dung như sau:
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
Trong ví dụ này, redis
là hostname của
container resdis nằm cùng 1 mạng. Chúng ta sử dụng cổng mặc định cho Redis là 637
Kế tiếp, tạo 1
file requirements.txt
trong thư mục project của bạn, copy và dán nội dung bên dưới vào
file:
flask
redis
Tạo 1 Dockerfile
Trong bước này, bạn viết 1 Dockerfile dùng để build image chứa tất cả những thứ mà một ứng dụng Python cần. Dockerfile có nội dung như bên dưới
# syntax=docker/dockerfile:1
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
File trên cho Docker biết nó cần:
- Build một image từ image Python 3.7.
- Đặt thư mục làm việc là
/code
.
- Cài đặt biến môi trường bằng flask command.
- Cài đặt gcc và những thứ liên quan
- Copy
requirements.txt
và cài đặt những thứ cần thiết.
- Thêm metadata vào image để mô tả rằng container luôn lắng nghe cổng 5000
- Copy thư mục hiện tại
.
vào thư mục làm việc.
trong image.
- Đặt command mặc định cho container thành
flask run
.
Định nghĩa các service trong Docker Compose
Tạo file docker-compose.yml
và dán nội
dung bên dưới vào:
version: "3.8"
services:
web:
build: .
container_name: web
restart: always
ports:
- "5000:5000"
volumes:
- .:/code
environment:
FLASK_ENV: development
redis:
image: "redis:alpine"
container_name: redis
restart: always
Đây là file dùng để khai báo và điều phối hoạt động của các container trong project.
Mọi file docker-compose.yml
đều phải bắt
đầu bằng cách chỉ định phiên bản file. Bạn có thể tra cứu phiên bản mới nhất tại đây
Các khối trong file YAML
được định nghĩa
bằng cách thụt lề, bảng bên dưới sẽ giải thích cho bạn ý nghĩa của các lệnh trong file mẫu
Tên | Chức năng |
---|---|
service | Chứa các định nghĩa cho mỗi service (hoặc container) trong app. Ở ví dụ trên ta có 2 service là web và redis |
build | Sử dụng khi chúng ta không xây dựng container từ image có sẵn nữa mà xây dựng nó từ Dockerfile. |
container_name | Chỉ định tên container tùy chỉnh, thay vì tên mặc định. do hệ thống tự đặt |
ports | Có thể chỉ định cả 2 cổng (HOST:CONTAINER) tức là (cổng ở máy thật: cổng ở máy ảo) hoặc chỉ định mình cổng cho máy ảo thôi. Ví dụ: "2222:3333" Khi bạn truy cập vào cổng 2222 ở máy thật thì sẽ được trỏ tới truy cập ở cổng 3333 của máy ảo. |
restart | Giá trị mặc định là no, còn nếu bạn đặt là always thì container sẽ khởi động lại nếu exit code cho biết lỗi không thành công. |
environment | Thêm các biến môi trường |
volume | Option này sẽ giúp copy thư mục từ máy host (trên ví dụ là . - thư
mục gốc) vào thư mục trên container (trên ví dụ là thư mục code ), nhờ đó, ta
có thể chỉnh sửa file code app.py mà không cần rebuild. Đồng thời, nhờ lệnh
này, ta có thể backup lại dữ liệu từ container (vì mỗi lần container dừng dữ liệu sẽ bị xoá)
|
depends_on | Option này giúp ta đánh dấu những container mà ta phụ thuộc vào, những container đó sẽ được tạo trước. |
Mọi người có thể tham khảo thêm các option khác tại đây
Build và chạy app của bạn với Docker Compose
Từ thư mục project của bạn, chạy ứng dụng của bạn bằng command
docker-compose up
Màn hình của bạn sẽ hiển thị tương tự bên dưới
[+] Running 7/7
- redis Pulled 12.8s
- a0d0a0d46f8b Already exists 0.0s
- a04b0375051e Pull complete 2.3s
- cdc2bb0f9590 Pull complete 2.5s
- 0aa2a8e7bd65 Pull complete 2.9s
- f64034a16b58 Pull complete 3.6s
- 7b9178a22893 Pull complete 3.6s
[+] Building 30.0s (17/17) FINISHED
=> [internal] load build definition from dockerfile 0.0s
=> => transferring dockerfile: 329B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> resolve image config for docker.io/docker/dockerfile:1 5.5s
=> [auth] docker/dockerfile:pull token for registry-1.docker.io 0.0s
=> docker-image://docker.io/docker/dockerfile:1@sha256:42399d4635eddd7a9b8a24be879d2f9a930d0ed040a61324cfdf59ef1 1.7s
=> => resolve docker.io/docker/dockerfile:1@sha256:42399d4635eddd7a9b8a24be879d2f9a930d0ed040a61324cfdf59ef1357b 0.0s
=> => sha256:42399d4635eddd7a9b8a24be879d2f9a930d0ed040a61324cfdf59ef1357b3b2 2.00kB / 2.00kB 0.0s
=> => sha256:93f32bd6dd9004897fed4703191f48924975081860667932a4df35ba567d7426 528B / 528B 0.0s
=> => sha256:e532695ddd93ca7c85a816c67afdb352e91052fab7ac19a675088f80915779a7 1.21kB / 1.21kB 0.0s
=> => sha256:24a639a53085eb680e1d11618ac62f3977a3926fedf5b8471ace519b8c778030 9.67MB / 9.67MB 1.3s
=> => extracting sha256:24a639a53085eb680e1d11618ac62f3977a3926fedf5b8471ace519b8c778030 0.2s
=> [internal] load .dockerignore 0.0s
=> [internal] load build definition from dockerfile 0.0s
=> [internal] load metadata for docker.io/library/python:3.7-alpine 7.0s
=> [auth] library/python:pull token for registry-1.docker.io 0.0s
=> [1/6] FROM docker.io/library/python:3.7-alpine@sha256:68dc2f52411f1071069a75c00029a0620d98139d638153cd33d029c 3.1s
=> => resolve docker.io/library/python:3.7-alpine@sha256:68dc2f52411f1071069a75c00029a0620d98139d638153cd33d029c 0.0s
=> => sha256:c11246b421beac7bdab2cd3f620a098af2a8bbbf8e608bef7bf056866e710734 281.50kB / 281.50kB 0.9s
=> => sha256:c5f7759615a9d583124f55e1df5aed6dda463b4f79867d2ea7ed78a8e3eb49b1 10.58MB / 10.58MB 2.1s
=> => sha256:6dc4dde3f226ebab24bd4e8fc69a7bd393bdce332980ef22cd962f5d884d16e8 233B / 233B 0.4s
=> => sha256:68dc2f52411f1071069a75c00029a0620d98139d638153cd33d029cf9810c8d6 1.65kB / 1.65kB 0.0s
=> => sha256:5d0900f6a439b7c28c508346b17cf5408bd6e670033e95d02c7a8ce81c69168a 1.37kB / 1.37kB 0.0s
=> => sha256:4d1c95b3db1c2a209efca1819a5c6015145799dbf47057082cd2266d13f27786 8.10kB / 8.10kB 0.0s
=> => sha256:9928d5d652ca3325f5a6558376ea5438008763664e06031998750e114659543c 2.35MB / 2.35MB 2.3s
=> => extracting sha256:c11246b421beac7bdab2cd3f620a098af2a8bbbf8e608bef7bf056866e710734 0.1s
=> => extracting sha256:c5f7759615a9d583124f55e1df5aed6dda463b4f79867d2ea7ed78a8e3eb49b1 0.4s
=> => extracting sha256:6dc4dde3f226ebab24bd4e8fc69a7bd393bdce332980ef22cd962f5d884d16e8 0.0s
=> => extracting sha256:9928d5d652ca3325f5a6558376ea5438008763664e06031998750e114659543c 0.2s
=> [internal] load build context 0.0s
=> => transferring context: 1.12kB 0.0s
=> [2/6] WORKDIR /code 0.1s
=> [3/6] RUN apk add --no-cache gcc musl-dev linux-headers 6.5s
=> [4/6] COPY requirements.txt requirements.txt 0.0s
=> [5/6] RUN pip install -r requirements.txt 4.9s
=> [6/6] COPY . . 0.0s
=> exporting to image 0.7s
=> => exporting layers 0.7s
=> => writing image sha256:e226c7105e104036d673e098ee4cf65d5d1765a491154c867e91f0993eb7f6ea 0.0s
=> => naming to docker.io/library/composetest_web 0.0s
[+] Running 3/3
- Network composetest_default Created 0.7s
- Container composetest-redis-1 Created 0.1s
- Container composetest-web-1 Created 0.1s
Attaching to composetest-redis-1, composetest-web-1
composetest-redis-1 | 1:C 09 Nov 2021 11:21:37.411 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
composetest-redis-1 | 1:C 09 Nov 2021 11:21:37.412 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=1, just started
composetest-redis-1 | 1:C 09 Nov 2021 11:21:37.412 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
composetest-redis-1 | 1:M 09 Nov 2021 11:21:37.412 * monotonic clock: POSIX clock_gettime
composetest-redis-1 | 1:M 09 Nov 2021 11:21:37.412 * Running mode=standalone, port=6379.
composetest-redis-1 | 1:M 09 Nov 2021 11:21:37.412 # Server initialized
composetest-redis-1 | 1:M 09 Nov 2021 11:21:37.412 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
composetest-redis-1 | 1:M 09 Nov 2021 11:21:37.413 * Ready to accept connections
composetest-web-1 | * Serving Flask app 'app.py' (lazy loading)
composetest-web-1 | * Environment: production
composetest-web-1 | WARNING: This is a development server. Do not use it in a production deployment.
composetest-web-1 | Use a production WSGI server instead.
composetest-web-1 | * Debug mode: off
composetest-web-1 | * Running on all addresses.
composetest-web-1 | WARNING: This is a development server. Do not use it in a production deployment.
composetest-web-1 | * Running on http://172.18.0.3:5000/ (Press CTRL+C to quit)
Những dòng trên mô tả việc Compose tải image Redis về, và đồng thời build 1 image từ dockerfile của bạn, và sau đó bắt đầu những dịch vụ bạn đã định nghĩa. Trong trường hợp này, code của bạn được sao chép tĩnh (statically copied) vào image trong lúc build.
Truy cập vào http://localhost:5000/ để thấy ứng dụng đang chạy của bạn
Sau đó, bạn tải lại trang và sẽ thấy con số tăng lên
Chuyển sang một cửa sổ terminal (hoặc command prompt)
khác, gõ lệnh docker images -a
để liệt kê những image có trên máy.
images -a
REPOSITORY TAG IMAGE ID CREATED SIZE
composetest_web latest e226c7105e10 2 hours ago 183MB
redis alpine e24d2b9deaec 4 weeks ago 32.3MB
Docker Network
Xem các mạng (network) trong Docker
Mục tiêu đạt được trong phần này
- Liệt kê các network: docker network ls
- Xem thành phần bên trong network: docker network inspect
- Xem các thông tin driver về network mà ta cài đặt Docker: docker info
Chi tiết thực hiện
- Để xem các kết nối trong Docker, ta sẽ thực hiện dưới đây, trong đó có các thông số về ID, tên, loại driver và phạm vi kết nối
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
7b533e835c4c bridge bridge local
6ee1c411176c host host local
98eda4bd78ae none null local
Nhận thấy rằng bridge network đi liền với bridge driver. Lưu ý rằng cả hai đều kết nối với nhau, nhưng không đồng nhất với nhau.
Ngoài ra ta còn thấy phạm vi kết nối của bridge là local - tức là chỉ tồn tại trong Docker host (trong máy tính có cái gọi là localhost).
Mọi kết nối được tạo từ bridge driver đều dựa trên cơ chế hoạt động của Linux bridge, cách hoạt động giống như switch (https://developers.redhat.com/blog/2018/10/22/introduction-to-linux-interfaces-for-virtual-networking#bridge)
- Để xem thông tin chi tiết của network đó thì ta thực hiện lệnh với cú pháp: docker network inspect <network name>. Thông tin được hiển thị dưới dạng một mảng chứa nhiều JSON object, thường thì ta sẽ xem IP Address, Subnet hoặc ConfigFrom.
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "7b533e835c4ce2e4feb9bd8b5a14ccf560e84c75e25197e1ee190737320d473d",
"Created": "2021-11-07T10:29:48.850215535+07:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"a12d9e60bf31a8fa8e5627560b38eeb30b3652a393c51886fcef3a264263015d": {
"Name": "client",
"EndpointID": "3b7f9cb790d4b8331b276d7b0844ef3742f91060cd36dab97f8aba255b17fb9e",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
- Để xem thông tin các plugins (extensions) về network được cài đặt khi bạn cài Docker, ta sử dụng lệnh: docker info. Ta sẽ xem plugín ở 2 mục: mục Client và mục Server. Đấy Docker hoạt động theo mô hình client-server !!!
$ docker info
Client:
Context: default
Debug Mode: false
Plugins:
app: Docker App (Docker Inc., v0.9.1-beta3)
buildx: Build with BuildKit (Docker Inc., v0.6.1-docker)
Server:
Containers: 3
Running: 2
Paused: 0
Stopped: 1
Images: 12
Server Version: 20.10.8
...
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
....
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
Mô phỏng kiến trúc client-server bằng Docker Network
Mục tiêu đạt được trong phần này
Trong bài hướng dẫn này ta sẽ làm việc với network trong Docker. Ta sẽ mô phỏng lại kiến trúc client-server dưới đây và đơn giản bằng 3 bước sau:
- Tạo cầu nối (bridge network) ở đây là đường mạng
- Tạo endpoint cho client (terminal đang tương tác) và mối liên kết client - bridge
- Tạo endpoint cho server (là ứng dụng Web đã mở port sẵn) và mối liên kết server - bridge.
Và bây giờ là bắt tay vào làm thôi nào!!!
Lab Time
- Trước hết ta sẽ dùng câu lệnh để xem các network đang tồn tại trong máy: docker network ls
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
7b533e835c4c bridge bridge local
6ee1c411176c host host local
98eda4bd78ae none null local
- Ta sẽ tạo bridge network (cầu nối). bridge có sẵn khi ta cài đặt Docker, và nằm trong Docker Engine. -d ở đây là --driver, chỉ ra rằng sẽ tạo loại kết nối nào nằm trong danh sách network của docker.
$ docker network create -d bridge my-bridge-network
- Sau đó ta sẽ chạy container liên kết với cầu nối mạng đó với cổng là 8081
$ docker run -d -p 8081:8081 -e "port=8081" --name app --network=my-bridge-network selaworkshops/python-app:2.0
- Để tìm địa chỉ IP của container app đó, ta cần:
$ docker inspect app
Ta thấy khá là nhiều tham số nhưng ta chỉ việc chú ý đến tham số IPAddress: 172.18.0.2 (mỗi máy sẽ có một địa chỉ IP khác nhau)
- Chạy alphine container dưới dạng terminal (interactive mode)
$ docker run -it --name client alpine:latest
- Sau đó ta sẽ cài đặt curl
# apk --no-cache add curl
- Từ container đang làm việc tên là "client", ta sẽ kết nối tới container tên "app". Ở đây IP Address trong bài mình dùng là 172.18.0.2
# curl <IP Address>:8081 --connect-timeout 5
- Tuy nhiên ta vẫn không thấy kết nối nào cả, do đã hết thời gian. Lúc này nguyên nhân là chưa có cầu nối nào để kết nối với "app" container.
curl: (28) Connection timeout after 5001 ms
- Lúc này ta mở terminal mới và gắn cho "client" một cầu nối kết nối tới "app"
$ docker network connect my-bridge-network client
- Sau đó ta thực hiện lại lệnh trước:
$ curl 172.18.0.2:8081 --connect-timeout 5
<h1>Python App</h1>/
- Lúc này ta kiểm tra mối liên kết đó
$ docker inspect my-bridge-network
- Chú ý phần này, nhận thấy rằng 2 container cùng nằm trên 1 đường mạng là 172.18.0.0/16 và đường mạng này chứa 2 container, chứng tỏ là đã kết nối thành công.
"Containers": {
"81d83e572df2fdcc8eb5a323b87b6ecd07fb34d3ed1f80c497965324a7bddef8": {
"Name": "app",
"EndpointID": "7ca0738981c352ab241fd928b02807f725c0596bd38e56c92ada98d2ffce4b81",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
},
"a12d9e60bf31a8fa8e5627560b38eeb30b3652a393c51886fcef3a264263015d": {
"Name": "client",
"EndpointID": "83632bd41e2fad9bc7fdf1de6eb77c604283bd56448bffdc328d104ca2a78347",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": ""
}
},
- Lúc này ta sẽ ngắt endpoint của cả hai container
$ docker network disconnect my-bridge-network app
$ docker network disconnect my-bridge-network client
- Xóa cầu nối mạng này
$ docker network rm my-bridge-network
- Kiểm tra một lần nữa. Và không thấy sự xuất hiện của my-bridge-network
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
7b533e835c4c bridge bridge local
6ee1c411176c host host local
98eda4bd78ae none null local
Bridge networking trong Docker
Mục tiêu đạt được trong phần này
- Hiểu được NAT là gì và kỹ thuật port-mapping
- Cách kiểm tra kết nối từ host sang container và từ container sang Internet.
Tạo bridge sử dụng tool và kiểm tra kết nối
- Đầu tiên ta sẽ cài đặt tool có tên là "bridge-utils", sau đó ta sẽ xem các bridge có trong Docker host.
- Lệnh brctl được sử dụng khi có nhiều mạng Ethernet trên máy chủ và bạn muốn kết hợp lại với nhau. Hay nói là cách khác là brctl là mô phỏng thiết bị switch
Tên bridge name có tên là docker0, ta nhận thấy có xuất hiện interfaces kết nối tới bridge (*)
$ sudo apt-get install bridge-utils
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02427c679daf no
- Để xem chi tiết thông tin về docker0 ta dùng lệnh: ip a
$ ip a
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:7c:67:9d:af brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:7cff:fe67:9daf/64 scope link
valid_lft forever preferred_lft forever
- Tiếp đó ta sẽ tiến hành kết nối container. Đầu tiên ta sẽ tạo một container mới. Container này với nhiệm vụ là chạy ngầm
$ docker run -dt ubuntu sleep infinity
9ed0cb731d5d88d49779ae2031e2f396f71e1fb5920bb7c47fc2237ceb3a1402
- Sau đó kiểm tra các bridge. So sánh tại dòng (*) ta nhận thấy interface mới là veth93015a2 tức là container vửa mới tạo đã kết nối tới bridge
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02427c679daf no veth93015a2
- Sau đó ta sẽ kiểm tra bridge. Lưu ý là container vừa mới tạo có tên là musing_hermann với địa chỉ ip là 172.17.0.2/16.
docker network inspect bridge
...
"Containers": {
"9ed0cb731d5d88d49779ae2031e2f396f71e1fb5920bb7c47fc2237ceb3a1402": {
"Name": "musing_hermann",
"EndpointID": "5554d3235a6372c3d638e8f125f97ed71e01c6753d5cd686cf80e100df8d1d7a",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
- Sau khi có được địa chỉ IP, bước tiếp theo là kiểm tra kết nối tới container đó với lệnh ping. Để ngắt ping thì ta sẽ ctrl + c. Tại đây thì thông số 0% loss, chứng tỏ là kết nối ổn định.
ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.177 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.051 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.072 ms
64 bytes from 172.17.0.2: icmp_seq=4 ttl=64 time=0.061 ms
64 bytes from 172.17.0.2: icmp_seq=9 ttl=64 time=0.091 ms
^C
--- 172.17.0.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 8200ms
rtt min/avg/max/mdev = 0.045/0.088/0.177/0.037 ms
- Lúc này ta vào trong container có tên là frosty_wilbur để tương tác với terminal. Sau đó ta sẽ cài đặt ping.
$ docker exec -it musing_hermann /bin/bash
root@9ed0cb731d5d:/#
root@9ed0cb731d5d:/# apt-get update && apt-get install -y iputils-ping
- Bước tiếp theo là ta ping tới trang hcmus.edu.vn. Ta thấy được là container đã ping Internet và có cấu hình đúng. Cuối cùng ta sẽ thoát ra bằng lệnh exit.
root@9ed0cb731d5d:# ping -c5 hcmus.edu.vn
PING hcmus.edu.vn (14.241.254.131) 56(84) bytes of data.
64 bytes from 14.241.254.131: icmp_seq=1 ttl=56 time=7.67 ms
64 bytes from 14.241.254.131: icmp_seq=2 ttl=56 time=6.84 ms
64 bytes from 14.241.254.131: icmp_seq=3 ttl=56 time=8.73 ms
64 bytes from 14.241.254.131: icmp_seq=4 ttl=56 time=6.85 ms
64 bytes from 14.241.254.131: icmp_seq=5 ttl=56 time=8.66 ms
--- hcmus.edu.vn ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4006ms
rtt min/avg/max/mdev = 6.848/7.755/8.735/0.827 ms
Cấu hình dịch vụ NAT
- NAT là một dịch vụ cho phép chuyển đổi từ một địa chỉ IP này thành một địa chỉ IP khác. Thông thường, NAT được dùng phổ biến trong mạng sử dụng địa chỉ cục bộ, cần truy cập đến mạng Internet.
- Tại bước này ta sẽ cấu hình NGINX container và map port 8080 của Docker host tới port 80 bên trong container. Điều này đồng nghĩa với việc khi kết nối tới Docker host tại cổng 8080 sẽ được map sang cổng 80 trong container.
- Chạy container tên là web1 với interface ngoài là 8080 và bên trong container là 80
$ docker run --name web1 -d -p 8080:80 nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
b380bbd43752: Pull complete
fca7e12d1754: Pull complete
745ab57616cb: Pull complete
a4723e260b6f: Pull complete
1c84ebdff681: Pull complete
858292fd2e56: Pull complete
Digest: sha256:644a70516a26004c97d0d85c7fe1d0c3a67ea8ab7ddf4aff193d9f301670cf36
Status: Downloaded newer image for nginx:latest
c023f449a8ac32555d53b09fcf1897daf1ca25e0d0c442b3c672b974652742c1
- Sau đó, ta sẽ check loại container. Ở đây, ta nhận thấy web1 container chạy NGINX. Ta nhận thấy đã 0.0.0.0:8080->80/tcp là tất cả địa chỉ IPv4 cục bộ với cổng 8080 ánh xạ tới cổng 80.
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c023f449a8ac nginx "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 0.0.0.0:8080->80/tcp, :::8080->80/tcp
- Lúc này ta sẽ vào trang web có địa chỉ IP: <port>. Để lấy được địa chỉ IP ta cần tìm địa chỉ IP của Docker host đó. Có thể bạn tìm ifconfig và chọn 1 địa chỉ bất kì trong đó, miễn ta có thể vào được. Hình dưới đây là kết quả khi vào được.
- Lưu ý là chỉ vào đúng port của nó là 8080 như ta đã đề ra
Resources
Reference
Kubernetes Cheat Sheet
Online Courses
- Docker Tutorial Course: https://www.youtube.com/watch?v=fqMOX6JJhGo
- Recommended for learning Docker: https://dockerlabs.collabnix.com/
- DevOps With Docker of University of Helsinki: https://devopswithdocker.com/
Exercises
Docker CLI
- Bạn hãy dùng kiến thức đã học, tìm hiểu xem lệnh
docker volume
có những flag nào và chức năng của chúng là gì?
- Hãy chạy 1 container ubuntu ở chế độ chạy nền và kiểm tra các thông tin sau:
- REPOSITORY
- TAG
- IMAGE ID ở dạng đầy đủ
- SIZE
- Hãy dừng container Ubuntu đang chạy
- Hãy xoá tất cả image trong máy bằng 1 câu lệnh duy nhất
Dockerfile
Việc viết hướng dẫn cài đặt trong README là điều nên làm, và quan trọng!!! Khi được/bị điều sang dự án mới với ngôn ngữ mà mình chưa từng biết, việc đầu tiên ta cần tìm hiểu là đọc kỹ README, search google và setup theo hướng dẫn!!!
Trong bài tập về Dockerfile ta sẽ tiến hành Dockerize hóa project có sẵn:
- Clone hoặc tải về git clone https://github.com/docker-hy/material-applications sau đó vào thư mục example-frontend.
- Sau đó đọc kỹ những yêu cầu trong README (lưu ý không đọc phần connect to backend)
- Viết Dockerfile và chạy container với port 5000. Lưu ý là nếu chạy container thì app sẽ bắt đầu kết nối (điều này tốn một vài giây).
- Lưu ý là có một số thao tác ta không cần phải chạy!!! Những lệnh nào mà README đề cập không cần chạy ??
Docker Compose
Bạn hãy tạo 1 compose chạy wordpress có thể được truy
cập từ trình duyệt bằng địa chỉlocalhost:8000
với các thông tin sau:
- File
docker-compose.yaml
có các dòng đầu như sau:
version: "3.9"
volumes:
db_data: {}
wordpress_data: {}
services:
- Gồm 2 container
- Container chứa database gồm các thông tin sau:
- Được tạo từ image mysql:5.7
- Volume
db_data
được ánh xạ đến thư mục/var/lib/mysql
của container
- Port hoạt động mặc định trên container là 3306
- Các biến môi trường với giá trị tuỳ chỉnh
- MYSQL_ROOT_PASSWORD
- MYSQL_DATABASE (tên database)
- MYSQL_USER
- MYSQL_PASSWORD
- Container chứa wordpress gồm các thông tin sau:
- Được tạo từ wordpress:latest
- Phụ thuộc vào container chứa database
- Port hoạt động mặc định trên container là 80
- Các biến môi trường cần thiết:
- WORDPRESS_DB_HOST: <tên container database> : <port hoạt động>
- WORDPRESS_DB_USER
- WORDPRESS_DB_PASSWORD
- WORDPRESS_DB_NAME
Docker Network
Vì Docker Network trong bài tutorial khá là đơn giản (mức độ Beginner và Intermediate).
Do đó, bạn nên tự mô phỏng kiến trúc client-server và tự cấu hình NAT với Docker trong bài tutorial.