Thôi hôm nay không viết dài dòng vì giờ cũng 5h sáng rồi. Vì để cho bản thân cảm thấy đỡ lười nên mình viết tiếp seri cho vui mà chả ai thèm đọc này. Bài hôm nay chúng ta sẽ nói đến socket, và giải thích rõ tại sao trong tất cả các ảnh Docker Architecture đều nói về việc Docker được thế kế dưới dạng Client/Server mà trong quy trình sử dụng hàng ngày chả ai thấy nó hiện hữu.
Vì tối qua là tối chủ nhật mình làm luôn thêm cả tí Lab vì mình rảnh nợ. Nên là bài này có cả Lab. Thế thôi.
Đầu tiên cái này sẽ là một câu hỏi cho những người đã từng dùng docker rồi, bạn đã bao giờ sử dụng các images mà trong hướng dẫn sử dụng bắt làm hành động mount /var/run/docker.sock chưa? Và các bạn cho rằng nó là để làm gì?
Vậy thì nói qua từ docker.sock. Đây là một Unix socket, là một điểm kết nối được mở ra bởi docker-daemon, bạn có thể tưởng tượng nó như cái ổ cắm sẽ xuất hiện khi services docker ở trạng thái Running. Và từ đấy có thể dễ nhận ra bình thường chúng ta ít chú ý tới nó bởi vì cả server-side và client-side của chúng ta ở trên cùng một máy, cũng như cái phích cắm ở đây chính là docker-cli mà các bạn gõ hàng ngày.
Có thể dễ thấy các công nghệ, app hướng tới các tác vụ điều khiển container khác hay hướng tới các tác vụ thu thập dữ liệu thì phần lớn sẽ cần làm động tác mount /var/run/docker.sock. Ví dụ ở đây chúng ta có: Portainer, Metricbeat vân vân, mây mây. Tới đây các bạn hẳn đã hiểu, những container này sử dụng ý tưởng là biến bản thân thành một client để điều khiển cũng như thu thập thông tin từ docker-daemon.
Yep, và để nó trở lên rõ ràng chúng ta sẽ lab một cơ chế thường thấy để nhận diện container từ vị trí một container khác. Docker Discovery.
I, Ý tưởng.
>Sử dụng 2 container, một đóng vai trò master có nhiệm vụ scan và tìm kiếm container client nhằm xác định được thông tin của client container.
>Container client được đưa lên đồng thời cùng một container khác, để xác định được client mong muốn chúng ta sẽ label client và dựa vào đó giúp cho master có thể tìm được ip của client.
>Việc master hiển thị được client ip coi như thành công.
II, Cấu trúc
>Master sử dụng Docker SDK Python, cái này là vì mình muốn luyện python, không có lý do cụ thể. Từ đó chúng ta sẽ tạo một python script nho nhỏ để nó chạy…. Tất nhiên rồi.
>Dockerfile, build luôn một con image mới cho Master.
>Docker-Compose. phân tách ra 2 khối docker-compose dễ cho demo, dễ cho tưởng tượng.
III, Thực hành
1.Cấu trúc thư mục.
[vagrant@localhost pythonpj]$ tree .
.
├── build
│ ├── dockerdisco.py
│ └── Dockerfile
├── client
│ └── docker-compose.yml
├── docker-compose.yml
└── tags
2 directories, 5 files. 2 layer, về cơ bản chả có gì đáng nói.
Script trung tâm ở đây là dockerdisco.py.
các lib sẽ được viết thẳng vào Dockerfile để fix môi trường chuẩn.
2. File python.
[vagrant@localhost build]$ cat dockerdisco.py
import docker
from time import sleep
#define docker host
client = docker.DockerClient(base_url='unix://var/run/docker.sock')
#get list contaienr running - 1 time only
psact=client.containers.list(all)
print(psact)
print("-----------")
#loop keep container running and check new container
while True:
#get list container got label "alo=alo"
psall=client.containers.list( sparse='true', filters={'label':'alo=alo'})
for con in psall:
con_id = repr(con)[12:22]
print(con_id)
container = client.containers.get(con_id)
ip_add = container.attrs['NetworkSettings']['IPAddress']
print(ip_add)
print("-------new container-------")
sleep(0.1)
Mình hardcoded label mong muốn là alo=alo, vì mình lười, cũng một phần vì mình ngu. Tuy nhiên các bạn có thể dùng biến môi trường các thứ cho chuyên nghiệp, cá nhân mình thì không, mình lười.
À mà thôi chả nhẽ biến buổi demo docker thành demo python, thôi ai cần hỏi gì thì comment nhé, ở chỗ nào cũng được.
3. Dockerfile
[vagrant@localhost build]$ cat Dockerfile
FROM centos:7
RUN yum update -y && yum install -y python3
RUN pip3 install docker six
RUN mkdir /opt/pycon
COPY ./dockerdisco.py /opt/pycon
WORKDIR /opt/pycon
ENTRYPOINT python3 ./dockerdisco.py
Ờm ở đây có cái ‘six’ có thể hơi khó hiểu một chút, nhưng đây là lỗi dependence của SDK, code mới có bố trẻ nào xóa hay thêm con Lib một cách thiếu chuyên nghiệp nên chúng ta cần cài thêm ngoài.
4. Docker-Compose Master.
[vagrant@localhost pythonpj]$ cat docker-compose.yml
version: "2.4"
services:
master:
build: ./build
image: master-check-connection:1.0
container_name: masterrr
user: root
networks:
- test-br
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
test-br:
external: true
Docker sử dụng bản 2. vì cơ bản mình ghét việc lên bản 3 chỉ chuyên hỗ trợ swamp vứt mất mấy config container, mà về cơ bản ở project này chả khác gì nhau, anh em thích để sao cũng được.
Chúng ta sẽ khai báo build ở đây luôn, đại khái là biến nó thành một bản tách biệt vì mình thích. Nếu anh em nào chửi bảo thằng rảnh nợ thì anh em có thể mount nó thẳng bằng volume bind, cơ mà đằng nào cũng phải khai báo lại entrypoint thôi.
user: root. Đây là một lỗi sercurity khá là buồn cười mà người lười hay gặp phải. Về cơ bản docker.sock thuộc quyền quản trị của root, group docker. Điều này có nghĩa là nếu bạn tạo một user nào đó có join vào group docker thì ừ bạn có thể đơn giản tránh việc lỗ hổng bảo mật các thứ. Nhưng vì mình lười. Mà ở các bản hướng dẫn cài đặt cơ bản họ cũng để thế phần lớn vì anh em chả biết user: root nó để làm gì thà để cái đặt phát ăn luôn thì hơn, thế nên là đây user:root. Vì chúng ta đều ngu.
/var/run/docker.sock:/var/run/docker.sock link nó vào vì một container chỉ hiểu việc control đúng nội bộ nó thôi, ở đây chúng ta mimic việc SDK control nội bộ container thành control daemon host.
networks: external. Cái này là xử dụng một network có trước, lý do là vì mình tách bộ cài này thành 2 file docker-compose. Mà để nhìn thấy nhau phải chung network. Nên là ừ tạo nó ở ngoài, và các bạn phải tạo tay cái network này.
Done Master.
5. Docker-Compose Client.
[vagrant@localhost client]$ cat docker-compose.yml
version: "2.4"
services:
client:
image: nginx
container_name: clienttt
networks:
- test-br
labels:
- "alo=alo"
client2:
image: nginx
container_name: clienttt2
networks:
- test-br
networks:
test-br:
external: true
Các bạn sẽ kiểu, ơ sao không để cái image OS base nào cho nó thực tế hả Bách Ngu. À vì mình lười không muốn tạo loop thread cho nó. Nên dùng con nginx này khi khởi chạy lên nó không chết, vì tụi này cũng chỉ làm dummy thôi.
Ở đây có 2 services, một cái có labels “alo=alo”, Nói cách khác khi chúng ta deploy đồng thời lên, nếu master chỉ hiện container alo thì bài test thành công.
Còn gì nữa nhỉ??? À hết rồi. Thế đi. Thực hành.
STEP 1: Bật server. À mình chạy build xong hết rồi nên mình chỉ bật server thôi =)) mình lười.
Master màn bên phải đã khởi chạy nhưng chưa trả về. Chúng ta chạy master không có flag -d để còn xem log.
STEP 2: Bật đồng thời 2 client trong đó 1 client đạt chuẩn.
master bắt đầu trả về thông tin.
Master trả được về id của container client alo=alo. Thiếu IP =)))
À CÁI NÀY KHÔNG PHẢI LỖI, LÀ TÍNH NĂNG ĐẤY, MÌNH THÊM MỘT CÁI CONFIG NHỎ. NẾU CÁC BẠN ĐỦ TRÌNH THÌ SỬA NÓ ĐI, MỘT DÒNG CONFIG THÔI. MẠNH MẼ LÊN.
Aw, this was an exceptionally good post. Spending some time and actual effort to generate a very good article… but what can I say… I procrastinate a whole lot and don’t manage to get anything done.