Skip to content

Instantly share code, notes, and snippets.

@nmvuong92
Last active July 10, 2021 19:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nmvuong92/10bdbbdad0f1efe2d5cec7c9c3a90b9c to your computer and use it in GitHub Desktop.
Save nmvuong92/10bdbbdad0f1efe2d5cec7c9c3a90b9c to your computer and use it in GitHub Desktop.
DOCKER

DOCKER

pull docker images

docker pull busybox

run docker pulled

docker pull busybox

run and echo

docker pull busybox  echo "hello busybox"

show all current images

docker images

hiển thị các container đang chạy

docker ps

Hiển thị các container đã từng chạy

docker ps -a

Gỡ bỏ các container đang chạy

  • 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ệnh docker rm. Chỉ cần sao chép Container ID vùng chứa từ phía trên docker ps -a và dán chúng cùng với lệnh.
docker rm 305297d7a235 ff0a5c3750b9

Xóa các container đã hết hạn

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

Terminology (thuật 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.

Chạy web tĩnh demo

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

Chạy trực tiếp không cần pull

docker run -p 8888:80 prakhar1989/static-site

sau đó ra trình duyệt gõ localhost:8888

stop detached container

để 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

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.

Tạo images

Dockerfile

  • 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.

Docker trên AWS

MULTI-CONTAINER ENVIRONMENTS

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.

Demo SF Food Trucks

Ứ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 <prakhar@prakhar.me>

# 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

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 để:

  1. giữ cuộc trò chuyện khi IP 172.17.0.5 thay đổi?
  2. 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

Docker Compose

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

Development Workflow

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ệ

@thedan65
Copy link

It's great. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment