docker pull busybox
docker pull busybox
docker pull busybox echo "hello busybox"
docker images
docker ps
docker ps -a
- mỗi lần
docker run image
nhiều lần và rời khỏi các container sẽ ăn hết các không gian đĩa cứng => Tôi dọn dẹp các Container khi tôi đã làm xong với chúng. Để làm điều đó, bạn có thể chạy lệnhdocker rm
. Chỉ cần sao chép Container ID vùng chứa từ phía trêndocker ps -a
và dán chúng cùng với lệnh.
docker rm 305297d7a235 ff0a5c3750b9
docker rm $(docker ps -a -q -f status=exited)
đối với phiên bản docker mới nhất chỉ cần
docker container prune
đều cho ra kết quả tương đồng
Images - The blueprints of our application which form the basis of containers. In the demo above, we used the docker pull command to download the busybox image.
Containers - Created from Docker images and run the actual application. We create a container using docker run which we did using the busybox image that we downloaded. A list of running containers can be seen using the docker ps command.
Docker Daemon - The background service running on the host that manages building, running and distributing Docker containers. The daemon is the process that runs in the operating system to which clients talk to.
Docker Client - The command line tool that allows the user to interact with the daemon. More generally, there can be other forms of clients too - such as Kitematic which provide a GUI to the users.
Docker Hub - A registry of Docker images. You can think of the registry as a directory of all available Docker images. If required, one can host their own Docker registries and can use them for pulling images.
Hình ảnh mà chúng tôi sẽ sử dụng là một trang web đơn trang đã được đăng ký trên docker hub với địa chỉ prakhar1989/static
docker run --rm prakhar1989/static-site
Chúng tôi có thể tải xuống và chạy hình ảnh trực tiếp trong một lần bằng cách sử dụng trình chạy docker run
. Như đã lưu ý ở trên, cờ --rm
sẽ tự động xóa vùng chứa khi nó thoát.
Vì image không tồn tại cục bộ, trước tiên client sẽ tìm nạp hình ảnh từ sổ đăng ký và sau đó chạy image. Nếu công việc suôn sẻ, bạn sẽ thấy thông điệp Nginx is running...
trên terminal, bây giờ máy chủ đang chạy, làm thể nào để xác định đang chạy trên cổng nào?
trong trường hợp này, máy khách không hiển thị bất kỳ cổng nào vì vậy chúng tôi cần chạy lại lệnh docker-run
để xuất bản các cổng
Trong khi chúng tôi đang ở đó, chúng tôi cũng nên tìm một cách để thiết bị đầu cuối của chúng tôi không được gắn vào container đang chạy. Bằng cách này, bạn có thể vui vẻ đóng thiết bị đầu cuối của bạn và giữ cho container chạy. Điều này được gọi là chế độ tách rời detached mode
.
$ docker run -d -P --name static-site prakhar1989/static-site
Trong lệnh trên, -d sẽ tách thiết bị đầu cuối của chúng tôi, -P sẽ publish tất cả các port tiếp xúc với các port ngẫu nhiên và cuối cùng --name static-site
tương ứng với một tên mà chúng tôi muốn cung cấp. Bây giờ chúng ta có thể thấy các port bằng cách chạy lệnh docker port
:
docker port static-site
docker run -p 8888:80 prakhar1989/static-site
sau đó ra trình duyệt gõ localhost:8888
để dừng một detached container, chạy docker stop
bằng cách cung cấp container ID
docker stop static-site
docker images là nền tảng của các container, trong các vd trước chúng ta đã pull Busybox từ registry trên docker hub, để xem các images có sẵn trong pc cục bộ sử dụng lệnh: docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
prakhar1989/catnip latest c7ffb5626a50 2 hours ago 697.9 MB
prakhar1989/static-site latest b270625a1631 21 hours ago 133.9 MB
python 3-onbuild cf4002b2c383 5 days ago 688.8 MB
martin/docker-cleanup-volumes latest b42990daaca2 7 weeks ago 22.14 MB
ubuntu latest e9ae3c220b23 7 weeks ago 187.9 MB
busybox latest c51f86c28340 9 weeks ago 1.109 MB
hello-world latest 0a6ba66e537a 11 weeks ago
TAG: để đơn giản, hãy nghĩ 1 image giống như một kho lưu trữ git repository, image có thể được committed with changes và có nhiều phiên bản, nếu không cung cấp phiên bản cụ thể image sẽ mặc định là lasted
. ví dụ bạn có thể pull một image cụ thể của một phiên bản ubuntu image:
docker pull ubuntu:12.04
có 4 loại images như sau: Base images are images that have no parent image, usually images with an OS like ubuntu, busybox or debian. Child images are images that build on base images and add additional functionality. Then there are official and user images, which can be both base and child images. Official images are images that are officially maintained and supported by the folks at Docker. These are typically one word long. In the list of images above, the python, ubuntu, busybox and hello-world images are official images User images are images created and shared by users like you and me. They build on base images and add additional functionality. Typically, these are formatted as user/image-name.
- Dockerfile đơn giản là text-file, chứa những dòng lệnh ở phía client mà Docker gọi khi tạo image, đó là một cách đơn giản để tự động hóa quá trình tạo image, phần tốt nhất là cách lệnh bạn viết trong Dockerfile gần giống các lệnh Linux tương đương chúng, điều đó có nghĩa là bạn phải học cú pháp mới để tạo Dockerfile của riêng bạn.
- mốt thư mục ứng dụng có chứa một Dockerfile, nhưng vì chúng ta đang làm điều này lần đầu tiên, chúng ta sẽ tạo một thư mục lần đầu. Để bắt đầu hãy tạo một blank file và lưu nó trong cùng thư mục của ứng dụng với tên "Dockerfile"
- Chúng tôi bắt đầu với việc xác định base image bằng cách sử dụng từ khóa FROM
FROM python:3-onbuild
- Bước tiếp theo là viết các lệnh sao chép tất cả tập tin và cài đặt các phụ thuộc, may mắn thay phiên bản
onbuild
đã lo điều đó. điều tiếp theo chúng ta cần là xác định số :port cần hiển thị (EXPOSE: lộ, phơi ra )
EXPOSE 5000
- Bước cuối cùng là viết lệnh để chạy ứng dụng, đơn giản là
python ./app.py
chúng ta sử dụng CMD command để làm điều đó.
CMD ["python", "./app.py"]
mục đích chính của CMD là nói cho container biết lệnh nào chạy khi khởi động cùng với nó, Dockerfile của chúng tôi đã sẵn sàng.
# our base image
FROM python:3-onbuild
# specify the port number the container should expose
EXPOSE 5000
# run the application
CMD ["python", "./app.py"]
Bây giờ chúng ta đã có Dockerfile, có thể build Image, lệnh docker build
thực hiện tạo một docker image từ Dockerfile
Phần dưới đây cho bạn thấy kết quả giống nhau. Trước khi bạn chạy lệnh (đưng quên thời gian)
Lệnh docker build
khá đơn giản - nó có một tên tag tùy chọn với cờ --t là vị trí của thư mục chứa Dockerfile
$ docker build -t nmvuong92/catnip .
- nmvuong92: account đăng ký trên docker hub
- catnip: tên Image đã tạo trên docker hub ứng với account
- "." all files
$docker build -t nmvuong92/catnip .
Sending build context to Docker daemon 8.704 kB
Step 1 : FROM python:3-onbuild
# Executing 3 build triggers...
Step 1 : COPY requirements.txt /usr/src/app/
---> Using cache
Step 1 : RUN pip install --no-cache-dir -r requirements.txt
---> Using cache
Step 1 : COPY . /usr/src/app
---> 1d61f639ef9e
Removing intermediate container 4de6ddf5528c
Step 2 : EXPOSE 5000
---> Running in 12cfcf6d67ee
---> f423c2f179d1
Removing intermediate container 12cfcf6d67ee
Step 3 : CMD python ./app.py
---> Running in f01401a5ace9
---> 13e87ed1fbc2
Removing intermediate container f01401a5ace9
Successfully built 13e87ed1fbc2
Nếu bạn chưa có Image python:3-onbuild
, phía Client trước tiên sẽ pull Image và sau đó tạo Image của bạn.
Sau khi chạy lệnh build xong, chạy tiếp docker images
để kiểm tra Image của bạn mới được build có hiển thị hay không.
Bước cuối cùng trong phần này là chạy Image xem nó có hoạt động hay không
docker run -p 8888:5000 nmvuong92/catnip
Lệnh chúng ta vừa chạy được sử dụng cổng 5000 cho máy chủ bên trong Container, và hiển thị bên ngoài local trên cổng 8888. đi đến URL có cổng 8888 nơi ứng dụng của bạn sẽ hoạt động.
Congratulations! You have successfully created your first docker image.
Trong phần cuối, chúng ta thấy việc chạy các ứng dụng với Docker dễ dàng và thú vị như thế nào. Chúng ta bắt đầu với một trang web tĩnh đơn giản và sau đó đã thử một ứng dụng Flask. Cả 2 điều đó có thể chạy trên đám mây chỉ với một lệnh. Một điều mà cả hai ứng dụng này có điểm chung là chạy trong Single Container (1 Container)
Những người bạn có kinh nghiệm chạy các services bên trong production không đơn giản như vậy. Hầu như luôn có một CSDL hoặc bất kì một loại lưu trữ nào có liên quan. Các hệ thống như Redis, Memcached đã trở thành khắt khe hơn trong hầu hết các application architectures. Do đó trong phần này chúng ta sẽ dành một chút thời gian học cách Dockerize các ứng dụng dựa trên các services khác nhau để chạy. Đặc biệt, chúng ta sẽ xem cách chúng ta có thể chạy và quản lý các multi-container. Tại sao multi-container phi thường? well, một trong những điểm nổi bật của Docker là cách nó cung cấp sự cô lập (isolation). Ý tưởng đóng gói một quy trình (proccess) với các phụ thuộc (dependencies) của nó trong một hộp cát (sandbox) được gọi là (container) là những gì làm cho nó rất mạnh mẽ.
Đó đó là một chiến lược tốt để phân tách các tầng ứng dụng của bạn (application tiers), nó khôn ngoan để giữ container cho từng services riêng biệt (separate). Mỗi tier có nhu cầu tài nguyên (resources) khác nhau và những nhu cầu đó có thể tăng ở mức khác nhau bằng cách tách các tiers thành các Container khác nhau, chúng ta có thể biên soạn (compose) mỗi tier bằng cách sử dụng loại cá thể thích hợp nhất dựa trên các nhu cầu tài nguyên khác nhau. Điều này cũng chơi rất tốt đối với toàn bộ phong trào Microservices, đó là lý do tại sao Docker hay bất kì một Container technology khác là đi đầu trong kiến trúc Microservices hiện đại.
Ứng dụng demo Food Trucks backend được viết bằng python Flask và nó sử dụng Elasticsearch. source sẵn có trên github
$ git clone https://github.com/prakhar1989/FoodTrucks
$ cd FoodTrucks
$ tree -L 2
.
├── Dockerfile
├── README.md
├── aws-compose.yml
├── docker-compose.yml
├── flask-app
│ ├── app.py
│ ├── package-lock.json
│ ├── package.json
│ ├── requirements.txt
│ ├── static
│ ├── templates
│ └── webpack.config.js
├── setup-aws-ecs.sh
├── setup-docker.sh
├── shot.png
└── utils
├── generate_geojson.py
└── trucks.geojson
thư mục flask-app
chưa ứng dụng python, thư mục utils chứa các tiện ích để tải dữ liệu vào elasticsearch, thư mục này cũng chứa một số tiệp YAML và một Dockerfile,
Hãy nghĩ về cách chúng ta Dockerize một ứng dụng. Chúng ta có thể thấy rằng ứng dụng bao gồm một máy chủ hỗ trợ Flask và dịch vụ Elasticsearch, Một cách tự nhiên để chia ứng dụng này là có 2 container, một để chạy Flask, một để chạy Elasticsearch Bằng cách đó nếu ứng dụng của tôi trở nên phổ biến chúng tôi có thể mở rộng quy mô bằng cách thêm các container tùy thuộc vào nơi các nút cổ chai đang nằm (bottleneck lies).
Đến đây chúng ta đã dễ dàng build một Flask Image, việc tiếp theo là Elasticsearch, chúng ta sẽ tìm vài thứ trên Docker Hub:
$ docker search elasticsearch
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
elasticsearch Elasticsearch is a powerful open source se... 697 [OK]
itzg/elasticsearch Provides an easily configurable Elasticsea... 17 [OK]
tutum/elasticsearch Elasticsearch image - listens in port 9200. 15 [OK]
barnybug/elasticsearch Latest Elasticsearch 1.7.2 and previous re... 15 [OK]
digitalwonderland/elasticsearch Latest Elasticsearch with Marvel & Kibana 12 [OK]
monsantoco/elasticsearch ElasticSearch Docker image 9
không ngạc nhiên, có một Image hỗ trợ chính thức cho Elasticsearch, để chạy ES chúng ta có thể sử dụng docker run
và có một single-node ES container chạy cục bộ trong thời gian không.
Trước tiên hãy pull Image:
$ docker pull docker.elastic.co/elasticsearch/elasticsearch:6.3.2
sau đó chạy nó trong development mode bằng cách chỉ định :port và thiết lập biến môi trường cấu hình cụm Elasticsearch Cluser để chạy single-node.
$ docker run -d --name es -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.3.2
Như đã thấy ở trên, sử dụng --name es
cung cấp cho container một cái tên để dễ sử dụng trong các câu lệnh tiếp theo
Khi Container được bắt đầu, chúng ta có thể xem log bằng cách chạy
docker container logs
với Container name (or ID) để kiểm tra log
Chạy docker container ls
để xem container nào đang chạy
docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f6e96aef2d49 docker.elastic.co/elasticsearch/elasticsearch:6.3.2 "/usr/local/bin/dock…" 4 minutes ago Up 4 minutes 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp es
fce04bb4eb59 nmvuong92/catnip "python ./app.py" About an hour ago Up About an hour 0.0.0.0:8889->5000/tcp jolly_vaughan
56a3c2146192 prakhar1989/static-site "./wrapper.sh" 10 hours ago Up 10 hours 0.0.0.0:32769->80/tcp, 0.0.0.0:32768->443/tcp static-site
Bây giờ chúng ta gửi yêu cầu tới máy chủ Elasticsearch container để kiểm tra reponse:
curl 127.0.0.1:9200
-----------------
{
"name" : "OHIAAsO",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "uIAjG3sPTnWhaxx6SjmjeQ",
"version" : {
"number" : "6.3.2",
"build_flavor" : "default",
"build_type" : "tar",
"build_hash" : "053779d",
"build_date" : "2018-07-20T05:20:23.451332Z",
"build_snapshot" : false,
"lucene_version" : "7.3.1",
"minimum_wire_compatibility_version" : "5.6.0",
"minimum_index_compatibility_version" : "5.0.0"
},
"tagline" : "You Know, for Search"
}
File docker của chúng ta:
# start from base
FROM ubuntu:latest
MAINTAINER Prakhar Srivastav <[email protected]>
# install system-wide deps for python and node
RUN apt-get -yqq update
RUN apt-get -yqq install python-pip python-dev curl gnupg
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash
RUN apt-get install -yq nodejs
# copy our application code
ADD flask-app /opt/flask-app
WORKDIR /opt/flask-app
# fetch app specific deps
RUN npm install
RUN npm run build
RUN pip install -r requirements.txt
# expose port
EXPOSE 5000
# start app
CMD [ "python", "./app.py" ]
- Chúng ta bắt đầu với Ubuntu là base Image và add gói quản lý
apt-get
để cài đặt dependencies cụ thể là Python và Node. - yqq là cờ để chặn đầu ra, và "Yes" cho tất cả prompts.
- Sau đó chúng ta sử dụng lệnh ADD để sao chép ứng dụng vào một ổ đĩa mới trong Container,
/opt/flask-app
, đây là nơi code sẽ cư trú nơi làm việc chạy trong ngữ cảnh này, bây giờ các phụ thuộc trên hệ thống đã được cài đặt. Đầu tiên chúng ta giải quyết Node bằng cách cài đặt các gói npm đã được định nghĩa trong file package.json, Chúng ta kết thúc file bằng các cài đặt gói Python, hiển thị :port và xác định CMD để chạy. - Cuối cùng, build image và run container
docker build -t nmvuong92/foodtrucks-web .
Trong lần chạy đầu tiên việc này mất chút thời gian vì client Docker sẽ tải xuống Image ubuntu. Chạy tất cả các lệnh và build Image của bạn. Việc chạy lại docker build
những lần sau sẽ gần như tức thời, bây giờ thử mã:
$docker run -P --rm nmvuong92/foodtrucks-web
-----
Unable to connect to ES. Retying in 5 secs...
Unable to connect to ES. Retying in 5 secs...
Unable to connect to ES. Retying in 5 secs...
Out of retries. Bailing out...
Rất tiếc, ứng dụng chúng ta không thể chạy vì thiếu kết nối với ES, vậy làm thế nào để kết nói với một container khác và cho nó nói chuyện với nhau? sang phần docker network
có một ES container đang chạy trên cổng 0.0.0.0:9200 chúng ta có thể truy cập trực tiếp. Bây giờ cấu hình cho Flask có thể connect tới ES này dòng 8 của app.py
es = Elasticsearch(host='es')
để làm cho nó chạy, chúng ta cần nói với Flask container rằng ES container đang chạy trên máy chủ 0.0.0.0:9200 (cổng mặc định) và điều đó làm cho nó hoạt động đúng không? thật không may là không đúng vì IP 0.0.0.0 là IP để truy cập vào container ES từ máy chủ, tức là từ máy MAC hiện tại của tôi, và một container khác sẽ không thể truy cập vào địa chỉ IP này.
Được rồi, không phải IP đó thì địa chỉ IP nào mà vùng chứa ES có thể truy cập được?
Khi docker được cái đặt nó có 3 vùng mạng:
$ docker network ls
--------
NETWORK ID NAME DRIVER SCOPE
4d800e747ae2 bridge bridge local
b598443a0b74 host host local
a2a00fed5ae7 none null local
Bridge network là mạng mà các container được chạy mặc định, điều này có nghĩa là khi tôi chạy ES container thì nó sẽ được chạy trong mạng Bridge này, để xác thực hãy kiểm tra mạng:
$ docker network inspect bridge
-------
[
{
"Name": "bridge",
"Id": "4d800e747ae2a0cff0c1db3dd2f9c0ce15493ba4e7e327ae0f49aa10acb49b6b",
"Created": "2018-11-23T17:25:29.1364376Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"439d19336d29467d2d0bfa3cb6340bf60a635c29e02204ae80346b9d31e4d50a": {
"Name": "vibrant_varahamihira",
"EndpointID": "d8bd82497c080edf47f7722419985193734ffd7e5dbcd951962b6822d7e2f04c",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"56a3c21461922cb77872ecc569acb3a198fd64ff1915f9e2c69268b9fdb423e2": {
"Name": "static-site",
"EndpointID": "2407187a2416c48f39e0584254c412553288c6f9810e719c3bf44ce398d7a3a0",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"f6e96aef2d49ed3b9293e830ecd33dbf7f19a78b27f0877c6c619a02e124f0e7": {
"Name": "es",
"EndpointID": "2fee110b542cf856da830907a791ddf869592b56bb8c1a9eb8eef161cd5a630a",
"MacAddress": "02:42:ac:11:00:05",
"IPv4Address": "172.17.0.5/16",
"IPv6Address": ""
},
"fce04bb4eb59273a18637d7bfc4006c538b3009ad037927549476a68f6960640": {
"Name": "jolly_vaughan",
"EndpointID": "8bf730309dfde4b5f2e836b05383cf848e9f09c64b68a42abf52ee333b4445b5",
"MacAddress": "02:42:ac:11:00:04",
"IPv4Address": "172.17.0.4/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": {}
}
]
chú ý
{
"Containers": {
"439d19336d29467d2d0bfa3cb6340bf60a635c29e02204ae80346b9d31e4d50a": {
"Name": "vibrant_varahamihira",
"EndpointID": "d8bd82497c080edf47f7722419985193734ffd7e5dbcd951962b6822d7e2f04c",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"56a3c21461922cb77872ecc569acb3a198fd64ff1915f9e2c69268b9fdb423e2": {
"Name": "static-site",
"EndpointID": "2407187a2416c48f39e0584254c412553288c6f9810e719c3bf44ce398d7a3a0",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"f6e96aef2d49ed3b9293e830ecd33dbf7f19a78b27f0877c6c619a02e124f0e7": {
"Name": "es",
"EndpointID": "2fee110b542cf856da830907a791ddf869592b56bb8c1a9eb8eef161cd5a630a",
"MacAddress": "02:42:ac:11:00:05",
"IPv4Address": "172.17.0.5/16",
"IPv6Address": ""
},
"fce04bb4eb59273a18637d7bfc4006c538b3009ad037927549476a68f6960640": {
"Name": "jolly_vaughan",
"EndpointID": "8bf730309dfde4b5f2e836b05383cf848e9f09c64b68a42abf52ee333b4445b5",
"MacAddress": "02:42:ac:11:00:04",
"IPv4Address": "172.17.0.4/16",
"IPv6Address": ""
}
}
}
Những gì chúng ta thấy là địa chỉ IP mà container này được phân bổ 172.17.0.5
đây có phải IP mà ta đang tìm? Hãy tìm hiểu cách chạy Flask container và cố gắng truy cập vào IP bằng cách vào trong bash của container:
hãy chắc là đã build foodtrucks-web
$docker build -t nmvuong92/foodtrucks-web .
Sau đó truy cập vào bash terminal của container đang chạy foodtrucks-web
$ docker run -it --rm nmvuong92/foodtrucks-web bash
Khi đang ở bash của container gõ lệnh: curl 172.17.0.5:9200
như ở dưới
root@35180ccc206a:/opt/flask-app# curl 172.17.0.5:9200
{
"name" : "Jane Foster",
"cluster_name" : "elasticsearch",
"version" : {
"number" : "2.1.1",
"build_hash" : "40e2c53a6b6c2972b3d13846e450e66f4375bd71",
"build_timestamp" : "2015-12-15T13:05:55Z",
"build_snapshot" : false,
"lucene_version" : "5.3.1"
},
"tagline" : "You Know, for Search"
}
root@35180ccc206a:/opt/flask-app# exit
--rm là một lá cờ thuận tiện cho việc chạy một lệnh tắt kể từ khi container được làm sạch lên khi nó làm việc được thực hiện.
Khi chúng ta làm điều đó ta thấy có thể thực sự nói chuyện được với ES trên 172.17.0.5:9200, tuyệt vời!!!
Mặc dù đã tìm được cách cho 2 container nói chuyện được với nhau nhưng vẫn còn 2 vấn để:
- giữ cuộc trò chuyện khi IP 172.17.0.5 thay đổi?
- do mạng Bridge được shared chia sẽ ở mỗi container mặc định, phương pháp này không an toàn. Làm thế nào để tách biệt mạng của chúng tối?
Tin tốt lành mà Docker có một câu trả lời tuyệt vời cho câu hỏi trên là nó cho phép chúng ta định nghĩa mạng của riêng mình bằng lệnh docker network
trước tiên hãy tạo mạng riêng của chúng ta, sau đó liệt kê để kiểm tra:
$docker network create foodtrucks-net
$docker network ls
--------
NETWORK ID NAME DRIVER SCOPE
4d800e747ae2 bridge bridge local
4cd2ef3e747c foodtrucks-net bridge local
b598443a0b74 host host local
a2a00fed5ae7 none null local
Bây giờ chúng ta đã có mạng riêng, chúng ta có thể launch các container bên trong mạng này bằng cờ --net, Hãy làm ngay điều đó nhưng trước tiên tôi stop ES container đang chạy trong mạng bridge (mặc định)
docker container stop es
chạy lại ES trong mạng mới tạo là foodtrucks-net
docker run -d --name es --net foodtrucks-net -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.3.2
vào bash container kiểm tra:
docker run -it --rm --net foodtrucks-net nmvuong92/foodtrucks-web bash
>>>
root@3057e0407d85:/usr/src/app# curl es:9200
{
"name" : "EruYneC",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "g76_G94aR2iAHZe0oFcDcQ",
"version" : {
"number" : "6.3.2",
"build_flavor" : "default",
"build_type" : "tar",
"build_hash" : "053779d",
"build_date" : "2018-07-20T05:20:23.451332Z",
"build_snapshot" : false,
"lucene_version" : "7.3.1",
"minimum_wire_compatibility_version" : "5.6.0",
"minimum_index_compatibility_version" : "5.0.0"
},
"tagline" : "You Know, for Search"
}
Wow, that works! bây giờ khởi động Flask container cũng bên trong mạng foodtrucks-net stop
docker network stop foodtrucks-web
start
docker run -d --net foodtrucks-net -p 5000:5000 --name foodtrucks-web nmvuong92/foodtrucks-web
Hãy truy cập 0.0.0.0:5000 để xem ứng dụng tuyệt vời của bạn.
Mặc dù khá nhiều công việc để chạy nhưng chỉ tóm tắt 4 lệnh để chạy từ con số không, setup-docker.sh
#!/bin/bash
# build the flask container
docker build -t nmvuong92/foodtrucks-web .
# create the network
docker network create foodtrucks-net
# start the ES container
docker run -d --name es --net foodtrucks-net -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.3.2
# start the flask app container
docker run -d --net foodtrucks-net -p 5000:5000 --name foodtrucks-web nmvuong92/foodtrucks-web
Bây giờ tưởng tượng bạn đang phân phối ứng dụng cho một ai dó, hoặc đang chạy trên máy chủ đã cài đặt trình docker, bạn có thể tải toàn bộ ứng dụng chỉ bằng 1 lệnh:
$ git clone https://github.com/prakhar1989/FoodTrucks
$ cd FoodTrucks
$ ./setup-docker.sh
Trong docker còn vài công cụ độc đáo khác như
- Docker Machine - Create Docker hosts on your computer, on cloud providers, and inside your own data center Tạo máy chủ docker trên pc của bạn, trên các nhà cung cấp đám mây và bên trong trung tâm dữ liệu của riêng bạn
- Docker Compose - A tool for defining and running multi-container Docker applications. Một công cụ để xác định và chạy các ứng dụng multi-container
- Docker Swarm - A native clustering solution for Docker Một giải pháp phân cụm gốc cho docker
- Kubernetes - Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications. Là một hệ thống mã nguồn mở để tự động hóa việc triễn khai, mở rộng quy mô và quản lý các ứng dụng Container.
Docker compose để làm việc với multi-container một cách dễ dàng hơn
Nó cung cấp một file cấu hình có tên là docker-compose.yml có thể được sử dụng để đưa ra một ứng dụng và một bộ dịch vụ phụ thuộc vào chỉ với một lệnh.
Compose làm việc được trong mọi môi trường: production, staging (giàn dựng), development, testing, cũng như quy trình CI...
Bước đầu tiên là kiểm tra cài đặt docker compose:
$docker-compose --version
---
docker-compose version 1.23.1, build b02f1306
Tiệp docker-compose.yml được viết theo cú pháp YML một cú pháp đơn giản.
version: "3"
services:
es:
image: docker.elastic.co/elasticsearch/elasticsearch:6.3.2
container_name: es
environment:
- discovery.type=single-node
ports:
- 9200:9200
volumes:
- esdata1:/usr/share/elasticsearch/data
web:
image: prakhar1989/foodtrucks-web
command: python app.py
depends_on:
- es
ports:
- 5000:5000
volumes:
- ./flask-app:/opt/flask-app
volumes:
esdata1:
driver: local
Ở cấp độ gốc, chúng ta định nghĩa tên của các dịch vụ là là es và web. Ở mỗi dịch vụ mà docker cần chạy chúng ta thêm các tham số bổ sung mà Image yêu cầu,
Đối với es thì đề cập đến Image alasticsearch có sẵn trên Elastic registry
Đối với web là Flask Web App, chúng tôi đề cập đến Image mà chúng tôi đã tạo ở đầu phần này.
Thông qua các thông số như lệnh và :port chúng tôi cung cấp thêm thông tin về Container, volume mã sẽ cư chú cho Container chạy.
Tuyệt vời, để chạy demo docker-compose.yml, hãy chắc rằng mọi thứ đều free để tránh đụng độ, tạm thời stop tất cả các container đang chạy:
$ docker stop $(docker ps -q)
Bây giờ có thể chạy docker-compose đơn giản như sau, vào thư mục chưa docker-compose.yml và gõ lệnh:
$ docker-compose up
Chúng ta có thể thấy cả hai Container đang chạy thành công. Các tên đến từ đâu? Chúng được tạo ra tự động bởi Compose. Nhưng Compose cũng tạo ra mạng tự động? Câu hỏi hay! Hãy cùng tìm hiểu.
Để destroy cluser và data volumn chỉ cần chạy lệnh
docker-compose down -v
xóa mạng foodtrucks-net đã tồn tại
docker network rm foodtrucks-net
>>>
docker network ls
Tuyệt! chúng ta có một vùng sạch sẽ, chúng ta hãy chạy lại các dịch vụ của mình và xem Compose có thực sự là phép thuật hay không.
docker-compose up -d
sau đó kiểm tra mạng có được tạo?
docker network ls
Có thể thấy compose đã đi trước và tạo ra một mạng mới có tên là "foodtrucks_default" và đính kèm cả hai dịch vụ mới trong mạng đó để mỗi cái có thể phát hiện được với nhau.
Mỗi container cho một dịch vụ tham gia vào mạng mặc định và cả hai đều có thể truy cập được bởi các Container khác trên mạng đó và chúng có thể được tìm thấy ở một tên máy giống hệt với tên vùng chứa.
docker network inspect foodtrucks_default
kiểm tra container đang chạy
docker container ls
---
docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
33ec27707ee5 prakhar1989/foodtrucks-web "python app.py" 8 minutes ago Up 3 minutes 0.0.0.0:5000->5000/tcp foodtrucks_web_1_ff436ea033a7
29a9f2e1d9f8 docker.elastic.co/elasticsearch/elasticsearch:6.3.2 "/usr/local/bin/dock…" 8 minutes ago Up 3 minutes 0.0.0.0:9200->9200/tcp, 9300/tcp es
nhưng khi chạy
$ curl -I 0.0.0.0:5000/hello
---
HTTP/1.0 404 NOT FOUND
Content-Type: text/html
Content-Length: 233
Server: Werkzeug/0.11.2 Python/2.7.15rc1
Date: Mon, 30 Jul 2018 15:34:38 GMT
Chuyện gì đang xảy ra với Flask app? chúng ta có thể thấy app.py cho câu trả lời, trong Flask các routes được định nghĩa với cú pháp @app.route bên trong tiệp, hiện chỉ có 3 route được xác định /, /debug và /search
"/" là route cho ứng dụng chính "/debug" route cho gỡ lỗi "/search" cho elastichsearch
$ curl 0.0.0.0:5000/debug
{
"msg": "yellow open sfdata Ibkx7WYjSt-g8NZXOEtTMg 5 1 618 0 1.3mb 1.3mb\n",
"status": "success"
}
Với bối cảnh đó, làm thế nào ta có thể thêm một route mới cho "/hello", hãy mở flask-app/app.py và thực hiện thay đổi:
@app.route('/')
def index():
return render_template("index.html")
# add a new hello route
@app.route('/hello')
def hello():
return "hello world!"
và giờ test request thử:
$ curl -I 0.0.0.0:5000/hello
HTTP/1.0 404 NOT FOUND
Content-Type: text/html
Content-Length: 233
Server: Werkzeug/0.11.2 Python/2.7.15rc1
Date: Mon, 30 Jul 2018 15:34:38 GMT
Oh no!!! nó không hiệu quả, chúng ta làm gì sai? mặc dù đã thay đổi trong "app.py", tiếp nằm trong máy pc của tôi hoặc máy chủ, nhưng vì Docker đang chạy docker của chúng tôi dựa trên Image "nmvuong92/foodtrucks-web", nó không biết về thay đổi này, để xác định điều này hãy thử
docker-compose run web bash
Starting es ... done
root@581e351c82b0:/opt/flask-app# ls
app.py package-lock.json requirements.txt templates
node_modules package.json static webpack.config.js
root@581e351c82b0:/opt/flask-app# grep hello app.py
root@581e351c82b0:/opt/flask-app# exit
Những gì chúng tôi đang cố gắng làm ở đây là xác thực rằng các thay đổi của chúng tôi không có trong app.py đang chạy trong vùng chứa
cách khắc phục:
Chúng tôi sẽ đặt cờ gỡ lỗi là true để Flask biết tải lại máy chủ khi app.py thay đổi, thay thế phần web của tệp docker-compose.yml như sau:
version: "3"
services:
es:
image: docker.elastic.co/elasticsearch/elasticsearch:6.3.2
container_name: es
environment:
- discovery.type=single-node
ports:
- 9200:9200
volumes:
- esdata1:/usr/share/elasticsearch/data
web:
build: . # replaced image with build
command: python app.py
environment:
- DEBUG=True # set an env var for flask
depends_on:
- es
ports:
- "5000:5000"
volumes:
- ./flask-app:/opt/flask-app
volumes:
esdata1:
driver: local
Với thay đổi đó (diff), hãy dừng lại và bắt đầu các container.
$ docker-compose down -v
Stopping foodtrucks_web_1 ... done
Stopping es ... done
Removing foodtrucks_web_1 ... done
Removing es ... done
Removing network foodtrucks_default
Removing volume foodtrucks_esdata1
$ docker-compose up -d
Creating network "foodtrucks_default" with the default driver
Creating volume "foodtrucks_esdata1" with local driver
Creating es ... done
Creating foodtrucks_web_1 ... done
Bước cuối cùng, cho phép thực hiện thay đổi trong app.py bằng cách thêm route đường mới. Bây giờ chúng tôi cố gắng cuộn tròn
$ curl 0.0.0.0:5000/hello
hello world
Wow bây giờ chúng tôi đã nhận được một phản hồi hợp lệ
It's great. Thank you!