This page looks best with JavaScript enabled

Tìm hiểu và sử dụng docker (P6)

 ·  ☕ 12 min read  ·  🐉 Edisc
Kể từ chương này, mình bắt đầu chuyển qua học nội dụng docker trên katacoda.com. Và cách học mình cũng sẽ khác, tóm tắt bằng sơ đồ tư duy và trình bày lại những gì mình hiểu sau khi học được trên blog theo flow của sơ đồ tư duy. Mục đích của blog này giúp mình ôn lại, hiểu rõ hơn những kiến thức đã học.

Tổng quan về khóa học

Trong khóa học này, mình sẽ đi qua một số chủ đề chính như hình bên dưới. Trong những chủ đề này có những chủ đề mới và những chủ đề đã đề cập qua trong khóa học của anh XuanThuLabChanel. Những phần đã nói qua thì sẽ không nói lại mà chỉ ghi chú thêm những mục mới, chưa đề cập ở những phần trước đó.
what_is_container

What is the container

Những khái niệm căn bản về container đã được đề cập khá nhiều ở trong bài docker_overview và ở p1 trong chuỗi bài học từ anh XuanThuLabChanel. Trong phần này chúng ta sẽ đi qua một số khái niệm cũng như một số chủ đề:
what_is_container

namespaces

namespaces

Khái niệm:

Namespace - không gian tên:

  • Là một trong những thành phần cơ bản của container.

  • giới hạn những gì các process có thể thấy và truy cập trên hệ thống.

  • định nghĩa vị trí inode trên đĩa.
    inode là một cấu trúc dữ liệu trong một hệ thống tập tin Unix - phong cách mô tả của một tập tin hệ thống đối tượng như một tập tin hoặc một thư mục. Số lượng nút trong tài khoản bằng với số lượng tệp và thư mục có trên đó. Hay có thể hiểu, inodes = số files + folder.

  • Thông qua một namespace, nhiều process khác nhau có thể chia sẻ, giao tiếp với nhau.

  • Khi một container bắt đầu chạy, nó sẽ tạo ra một namespace mới cho để các process chạy trong hộp cát (sandboxes) chạy. Bằng cách phân bổ namespace dựa trên các pid, tại thời điểm chạy, một process sẽ truy cập vào namespace tương ứng với pid của mình, việc này giúp cho process có cảm giác chỉ một mình nó chạy trên hệ thống.

Một số loại biến thể của namespace

Trong namespace định nghĩa một số biến thể:

Mount (mnt):

mount namespace điều khiển các mount point. Khi tạo, các liên kết từ mount namespace hiện tại được sao chép sang không gian tên mới, nhưng các mount point được tạo sau đó không truyền giữa mount namespace. Cờ sao chép được sử dụng để tạo không gian tên mới thuộc loại này là CLONE_NEWNS - viết tắt của “NEW NameSpace”. Thuật ngữ này không mang tính mô tả (vì nó không cho biết loại không gian tên nào sẽ được tạo) bởi vì mount namespaces là loại namespace đầu tiên và tại thời điểm, các nhà thiết kế không lường trước được sẽ có bất kỳ loại namespaces nào khác.

Process ID(pid):

PID namespaces cung cấp những process với tập pid độc lập với những namespace khác. Các PID namespace có thể lồng vào nhau nghĩa là: mỗi process được tạo trong một namespace sẽ có 1 pid, và pid này sẽ được thấy bởi các process ở trong namespace hiện tại ra cho đến namespace đầu tiên. Nghe có vẻ giống như trong công ty, một nhân viên mới vào sẽ có một thông tin và định danh riêng, những thông tin này sẽ được biết bởi sếp anh ta, sếp của sếp, sếp của sếp của sếp 🤣🤣🤣.
Process đầu tiên sẽ được gán PID là 1, nó có vị trí giống như chủ tich hội đồng quản trị của công ty, từ trên nhìn xuống có thể thấy tất cả những process khác, và có quyền “đuổi” tất cả những process dưới trướng của mình.

User ID (user):

User namespace là một tính năng để cung cấp khả năng phân quyền và phân nhóm người dùng trên nhiều process, khả nằng đã sẵn có từ kernel 3.8. Với quyền admin, có thể xây dựng một container có vẻ như có quyền admin nhưng không thực sự cấp các đặc quyền nâng cao cho các process của người dùng. Giống như PID namespace, user namespace dùng được lồng vào nhau và mỗi user namespace mới được coi là con của user namespace đã tạo ra nó.
User namespace chứa một bảng ánh xạ để chuyển đổi các user ID từ container sang hệ thống. Điều này cho phép, ví dụ, người dùng root có user id 0 trong container nhưng thực sự được hệ thống coi là user id 1.400.000 để kiểm tra quyền sở hữu. Một bảng tương tự được sử dụng để ánh xạ group id và kiểm tra quyền sở hữu.
Để tạo điều kiện thuận lợi cho việc cô lập đặc quyền của các hành động admin, mỗi loại namespace được coi là thuộc sở hữu của một user namespace dựa trên user namespace đang hoạt động tại thời điểm tạo. Người dùng quyền admin trong user namespace thích hợp sẽ được phép thực hiện các hành động admin trong loại namespace đó. Ví dụ: nếu một process có quyền quản trị để thay đổi địa chỉ IP của giao diện mạng, nó có thể làm như vậy miễn là user namespace của chính nó giống với (hoặc tổ tiên của) user namespace sở hữu network namespace. Do đó user namespace ban đầu có quyền kiểm soát quản trị đối với tất cả các loại user namespace trong hệ thống.

Network (net):

Network namespace ảo hóa network stack. Khi được tạo, network namespace chỉ chứa một loopback interface. Mỗi network interface (là physical hay virtual) thể hiện chính xác một namespace và có thể di chuyển giữa các namespace với nhau.
Một namespace sẽ có tập các địa chỉ IP riêng, chứa những routing table, socket listing, connection tracking table, firewall, và những resources liên quan tới network khác.
Khi hủy một không gian tên mạng sẽ phá hủy bất kỳ giao diện ảo nào bên trong nó và di chuyển bất kỳ giao diện vật lý nào bên trong nó trở lại không gian tên mạng ban đầu.

Interprocess Communication (ipc):

ipc namespace dùng để cô lập các process trong giao tiếp. Điều này ngăn không cho các quá trình trong các vùng tên IPC khác nhau sử dụng, chẳng hạn như nhóm hàm SHM để thiết lập phạm vi bộ nhớ dùng chung giữa hai quá trình. Thay vào đó, mỗi quá trình sẽ có thể sử dụng cùng một số nhận dạng cho một vùng bộ nhớ dùng chung và tạo ra hai vùng riêng biệt như vậy.

UTS:

UTS namespace (UNIX Time-Sharing) cho phép một hệ thống duy nhất có vẻ như có các tên miền và máy chủ lưu trữ khác nhau cho các quá trình khác nhau. “Khi một quy trình tạo một vùng tên UTS mới … tên máy chủ và miền của vùng tên UTS mới được sao chép từ các giá trị tương ứng trong vùng tên UTS của người gọi”.

  • Còn nhiều loại biến thể khác, chúng ta có thể xem chúng tại đây.

Unshare tool

  • là công cụ giúp chúng ta có thể chạy một process và yêu cầu nó tạo ra một namespace mới.
1
2
3
4
5
6
7
$ sudo unshare --fork --pid --mount-proc bash
$ ps
  PID TTY          TIME CMD
    1 pts/0    00:00:00 bash
    9 pts/0    00:00:00 ps
$ exit
exit

What happens when we share a namespace?

Chúng ta có thể liệt kê tất cả những namespace bằng lệnh:

1
ls -lha /proc/$DBPID/ns/  

Một công cụ khác chúng ta có thể dùng đó là nsenter dùng để gán một process vào một không gian tên đã tồn tại, công cụ này sẽ hữu dụng khi debug.

1
nsenter --target $DBPID --mount --uts --ipc --net --pid ps aux

Container images

Container images chúng ta đã được học kĩ trong các bài trước, ở đây có một số chú ý nhỏ nhỏ:

  • Những images chứa dữ liệu thô (metadata) để làm cơ sở để xây dựng những image khác.
  • Chúng ta đã biết, images sẽ có nhiều layer, có 1 layer gốc và những layer sau là quá trình chúng ta cài đặt, thiết lâp thêm môi trường khi chạy contaier được sinh ra từ image ban đầu. Ở đây, mỗi tar file được xem như một layer, và tất cả các tar file được giải nén tới cùng môt vị trí thì

Learn Docker Foundation

Deploying First Docker Container

Phần này chúng ta cũng đã trình bày kĩ ở những phần trước, theo sơ đồ, ta có thể thấy, để build một docker container, chúng ta có 5 bước chính:
deploying_first_container

  • Step1: Tìm kiếm image thích hợp thông qua lệnh docker search, tải về bằng docker pull và chạy bằng docker run
  • Step2: Kiểm tra những container đang chạy:
    • docker ps: liệt kê tất cả các container đang chạy và các thông tin về nó
    • docker inspect: cung cấp thông tin chi tiết về một container đang chạy.
    • docker logs: những message mà container đã log khi chạy.
  • Step3: Truy cập vào container đang chạy. Ở đây, đối với một container đang chạy để lấy port của nó ta có thể dùng một trong 2 lệnh:
1
2
docker ps
docker port container_id port_number
  • Step 4: Thiết lập chia sẻ dữ liệu giữa container và host cũng như các container với nhau, đảm bảo dữ liệu cần thiết sẽ được lưu khi container bị xóa.
  • Step 5: Chạy container, ở đây có một số thiết lập như -d để chạy nền hay -it để tương tác với shell.
    Những phần này chúng ta đã được học ở những bài trước.

Deploy Static HTML Website as Container

Tóm tắt cho phần này
deploying_static_HTML_Website_as_container

Create Dockerfile

Docker file là gì?

Dockerfile là một dạng file text, không có phần đuôi mở rộng, chứa tập các lệnh mô tả các bước để build một docker image.

Cú pháp của một dockerfile.

Cấu trúc chung của một dockerfile có dạng

INSTRUCTION arguments

Trong đó:

  • INSTRUCTION: là tên các chỉ thị có trong Dockerfile, mỗi chỉ thị thực hiện một nhiệm vụ nhất định, do dockerfile quy định. Mặc định, lệnh này phải được viết bằng chữ IN HOA
  • arguments: nội dung mà lệnh thực hiện
    Ví dụ:
1
2
FROM nginx:alpine
COPY . /usr/share/nginx/html

Dòng lệnh 1 để định nghĩa base images và dòng lệnh 2 để chỉ thị copy tất cả nội dụng tại vị trí hiện tại vào một địa chỉ cụ thể trên container, ở đây là /usr/share/nginx/html.

Các chỉ thị chính trong dockerfile
  1. FROM
    Chỉ định rằng image nào sẽ được làm cơ sở trong quá trình build image để thực thi những câu lệnh tiếp theo. Các image này được tải về từ Public Repository hoặc Private Reppository riêng của mỗi người.
    Chỉ thị FROM là bắt buộc và phải được đặt trên cùng của Dockerfile
    Cú pháp:
1
2
3
FROM <image> [AS <name>] 
FROM <image>[:<tag>] [AS <name>]
FROM <image>[@<digest>] [AS <name>] 

Ví dụ:

1
2
3
FROM ubuntu 
hoặc 
FROM ubuntu:latest
  1. LABEL
    Chỉ thị LABEL được dùng để thêm các thông tin meta vào Docker Image khi chúng được build. Chúng tồn tại dưới dạng cặp key-value, lưu dưới dạng chuỗi. Có thể chỉ định nhiều label cho một Docker Image, mỗi cặp key-value là duy nhất.
    Nếu cùng 1 key mà có nhiều value thì value gần nhất sẽ đè những value trước đó.

Cú pháp:

1
LABEL <key1><value1> <key2><value2> ... <key_n><value_n>

Có thể khai báo metadata cho Image theo từng dòng chỉ thị hoặc có thể tách ra khai báo thành từng dòng riêng biệt.
Để xem thông tin meta của một image, ta dùng lệnh docker inspect

1
docker inspect <image id>
  1. MAINTAINER
    Dùng để khai báo thông tin tác giả của dockerfile
    Cú pháp:
1
MAINTAINER <name> [<email>]

Ví dụ:

1
MAINTAINER <edisc> [<edisc.ctf@gmail.com>]
  1. RUN
    Dùng để chạy một lệnh nào đó trong quá trình build image, thường là các câu lệnh Linux. Tùy vào image gốc được khai báo trong FROM thì sẽ có các câu lệnh tương ứng.
    Cú pháp:
1
2
RUN <command>
RUN ["executable", "param1", "param2"]

Ví dụ

1
2
RUN apt-get update -y
RUN ["/bin/bash", "-c", "echo Run"]

Ở shell form, có thể thực hiện nhiều câu lệnh bằng dấu \:

1
2
3
4
FROM ubuntu:latest
RUN apt-get update -y \
    apt-get install vim -y
    apt-get install curl -y
  1. ADD
    Thực hiện sao chép các tệp, thư mục từ máy đang build hoặc remote file từ src và thêm chúng vào filesystem của image dest
    Cú pháp:
1
2
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>", ... "<dest>"]  

trong đó:

  • src có thể là một hoặc nhiều file, thư mục
  • dest là địa chỉ tuyệt đối hoặc được chỉ định bởi WORKDIR

Ví dụ:

1
ADD --chown=user:mygroup file* /somedir/
  1. COPY
    Giống như ADD là copy file từ thư mục src đến dest, khác ở việc COPY không hỗ trợ copy các remote file URLs từ các nguồn trên mạng.
    Cú pháp:
1
2
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
  1. ENV
    Dùng để khai báo các biến môi trường. Các biến được khai báo dưới dạng key-value bằng các chuỗi. Chúng sẽ được sử dụng trong các chỉ thị tiếp theo của Dockerfile.
    Cú pháp:
1
ENV <key>=<value>

Ví dụ:

1
2
ENV PORT=80
ENV USERNAME="user" PASSWORD="password"

Các biến môi trường có thể thay đổi với lệnh docker run

1
docker run --env <key>=<value>

Các command được sử dụng ENV:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
ADD
COPY
ENV
EXPOSE
FROM
LABEL
STOPSIGNAL
USER
VOLUME
WORKDIR
  1. CMD
    Dùng để định nghĩa các câu lệnh được chạy sau khi container được khởi động từ image vừa build.
    Cú pháp:
1
2
3
CMD ["executable", "param1", "param2"]
CMD ["param1", "param2"]
CMD command param1 param2

Ví dụ:

1
2
FROM ubuntu:latest
CMD cat password.txt 
  1. USER
    Thiết lập username hoặc UID để sử dụng khi chạy image và khi chạy các lệnh có trong RUN, CMD, ENTRYPOINT sau đó.
    Cú pháp:
1
2
3
USER <user>[:<group>]
hoặc 
USER <UID>[:<GID>] 

Ví dụ:

1
2
3
FROM alpine:3.4
RUN useradd -G /bin/bash edisc
USER edisc  

Build Docker Image

Ta dùng lệnh docker build để tiến hành build docker image từ Dockerfile vừa tạo.

Cú pháp:

1
docker build -t <image_name> <build directory>

Ví dụ:

1
docker build -t myimage:v1 .

-t ở đây cho phép chỉ định tên image và tag của nó.

RUN

Sau khi đã tạo thành image, ta chỉ cần run nó với lệnh docker run để tiến hành chạy container.

Tổng kết

Phần này chúng ta đi qua 2 nhánh chính là What is the containerContainer images, ở đó chủ yếu là những kiến thức chúng ta đã thảo luận qua.
Ngoài ra, chúng ta đã đi qua một vài nhánh nhỏ của Docker Foundation. Vì phần này liên quan đến mục đích công việc của tôi, nên tôi sẽ học kĩ cũng như tìm hiểu kĩ phần này. Bài tiếp theo chúng ta sẽ cùng đi qua những nhánh kế tiếp.

Share on

Edisc
WRITTEN BY
Edisc
Cyber Security Engineer

 
What's on this Page