美文网首页docker
8 Docker教程

8 Docker教程

作者: quitus | 来源:发表于2017-02-16 19:56 被阅读194次

    8 Docker教程

    大量借鉴https://prakhar.me/docker-curriculum/,以下不做更多说明

    试玩Busybox

    Busybox可以认为就是一个简化的linux系统

    运行

    $ docker pull busybox
    

    由于系统权限问题,上述命令可能会有问题。Mac系统请查看Docker引擎正在运行。Linux请尝试“sudo docker pull busybox”

    pull命令将busybox镜像从Decker寄存器提取并保存在你的系统。

    运行下面命令,可以看到系统上安装的所有镜像

    $ docker images
    

    run

    让我们运行一下这个镜像

    $ docker run busybox
    

    嗯。。。有什么变化吗?其实后台发生了很多事情,虽然你现在看不出来。当你运行run命令时,客户端找到镜像(这里就是busybox),载入容器然后在这个容器中运行命令。我们刚才并没有提供任何命令,所以这个容器启动,空跑一通就直接退出了。

    那我们试着提供一个命令

    $ docker run busybox echo "hello from busybox"
    hello from busybox
    

    赞!终于看到些变化了。这一个命令,我们经历了启动一个镜像,运行一个命令,然后退出。而且这个过程真的很快!你用是任何一个虚拟机能做到这个速度吗?知道Docker很快了吧。

    我们再来看看docker ps命令,它显示所有正在运行的容器。

    $ docker ps
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
    

    因为现在并没有运行的容器,我们看不到什么信息。我们是一个更有用的形式:docker ps -a

    $ docker ps -a
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
    305297d7a235        busybox             "uptime"            11 minutes ago      Exited (0) 11 minutes ago                       distracted_goldstine
    ff0a5c3750b9        busybox             "sh"                12 minutes ago      Exited (0) 12 minutes ago                       elated_ramanujan
    So what we see above is a list of all containers that we ran. Do notice that the STATUS column shows that these containers exited a few minutes ago.
    

    看到了吗?这是我们之前运行过的容器的清单。其中STATUS显示这些容器在多久前退出。

    那我们可以运行多条命令吗?我们试下启动镜像的一个bash可以吗?

    $ docker run -it busybox sh
    / # ls
    bin   dev   etc   home  proc  root  sys   tmp   usr   var
    / # uptime
     05:45:21 up  5:58,  0 users,  load average: 0.00, 0.01, 0.04
    

    it将我们连接到容器中一个交互的tty。其中, -t选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i则让容器的标准输入保持打开,即交互状态。

    这里,我们可以输入任何我们想输入的命令。

    在容器中,尝试新建一个文件,比如touch myfile.txt,然后ls确认新建成功。输入exit,点击Enter。再次docker run -it busybox sh进入容器,运行ls。有没有发现,刚才新建的文件消失了。这是因为我们使用run启动镜像,每次都会创建全新的容器。

    下面在说下怎么删除容器。我们刚才看到,即使我们退出容易,仍然可以通过docker ps -a命令看到退出容器的残留。随着容器建立退出次数越来越多,这些残留将会占用大量硬盘空间。因此,如果没有特殊需求,最好用完之后清除这些残留。我们可以使用docker rm命令,后面填容器的container ID(通过docker ps -a可见此ID)

    $ docker rm 305297d7a235 ff0a5c3750b9
    305297d7a235
    ff0a5c3750b9
    

    如果想更快地删除大量容器,可以:

    $ docker rm $(docker ps -a -q -f status=exited)
    

    最后,还可以通过docker rmi删除不用的镜像

    容器其他命令

    docker start | restart | stop:启动停止的容器,重启容器,停止容器
    docker exec:运行正在运行的一个容器的命令

    建议自己查查,因为后面不用这些命令,这里略过,以免影响心情。

    Docker的Webapp

    玩够了,我们现在可以搞点真实的东西了。

    静态网站

    我们一步步来。我们首先尝试建立一个巨简单的静态网站。我们会从Docker Hub拖下来一个镜像,跑一个容器,感受一下跑一个网站服务是多么简单。

    开始干活。这个静态网站托管在在Docker寄存器 - prakhar1989/static-site。下载这个镜像然后跑起来,以下这一条命令搞定:

    $ docker run prakhar1989/static-site
    

    因为本地没有这个镜像,所以首先下载才能跑起来。等到看到Nginx is running...就说明已经跑起来了。那怎么看到网站呢?我们怎么才能网站这个容器?

    额。。。现在客户端没有暴露任何端口,我们需要重新运行docker run以开放端口。同时,当关掉终端时,我们也不想关掉运行中的容器。满足这种需求的模式叫做分离模式(detached mode)。

    $ docker run -d -P --name static-site prakhar1989/static-site
    e61d12292d69556eabe2a44c16cbd54486b2527e2ce4f95438e504afb7b02810
    

    上述命令中,, -d进入分离模式, -P将所有端口暴露到随机端口。--name后面是我们起的名字。现在我们通过下面命令查看端口吧

    $ docker port static-site
    80/tcp -> 0.0.0.0:32769
    443/tcp -> 0.0.0.0:32768
    

    浏览器输入“http://localhost:32769”访问。当然,我们也可以指定端口,比如

    $ docker run -p 8888:80 prakhar1989/static-site
    Nginx is running...
    

    想要停止容器,使用docker stop 容器id 命令。

    是不是巨简单?你已经知道怎么使用Docker镜像跑起来一个网站了,那么,我们怎么建立自己的镜像呢?

    Docker镜像

    本小结介绍如何建立自己的镜像。

    简单来讲,Docker镜像就像是一个git仓库,镜像可以做出有很多改变和版本的提交。如果不指定版本,默认使用最新版。

    比如像要拉取某个版本的Ubuntu

    $ docker pull ubuntu:12.04
    

    想要得到一个Docker镜像,或者从一个Docker寄存器(比如Docker Hub)拉取,或者自己建立。你可以在Docker Hub搜索自己想要的镜像。

    镜像有一个需要特变注意的区别是基镜像和子镜像(base and child images)。

    基镜像没有任何父镜像,一般是系统镜像比如Ubuntu和刚才用到的busybox。子镜像基于基镜像并于其上添加一些功能。这样就有了官方镜像和用户镜像。

    官方镜像由Docker员工维护,用户镜像由类似你我的用户建立。

    我们的第一个镜像

    需要python支持,没有的话自己去安装。

    我们的目标是建立一个Flash应用。这个应用是原作者做的,每次载入会显示一个随机的小猫动图。请复制此库到本地

    验证本地工作

    进入flask-app路径然后运行此应用,如下:

    $ cd flask-app
    $ pip install -r requirements.txt
    $ python app.py
     * Running on http://0.0.0.0:5000/ (Press CTRL**C to quit)
    

    一切顺利的话,你可以在http://localhost:5000看到效果。

    如果出现权限问题,可能需要sudo,或者你不想吧各种包安装到系统层面,可以试试pip install --user -r requirements.txt

    不错吧?下一步我们就要用此镜像建立一个自己的镜像。

    建立自己的镜像

    如前所述,所有的用户镜像都是基于基镜像。

    因为我们这个应用使用Python写成,我们使用的基镜像是Python 3。下载:

    $ docker pull python:3-onbuild
    

    onbuild版的镜像包括可以使应用启动的一些帮助项。现在我们有了足够的内容:可工作的app和基镜像。下一步做什么呢?我们使用Dockerfile

    Dockerfile

    什么是Dockerfile

    Dockerfile就是一个简单的文本文件,它里面包含了创建镜像的时候Docker客户端调用的命令。这是一个简化自动化镜像创建的过程。你在Dockerfile写的命令和Linux命令几乎相同,是不是很爽?不用学就会了吧?

    下面我们脑补一下Dockerfile应该有什么内容。

    1. 基镜像是谁?
    2. 怎么把我们需要的文件等装到基镜像?
    3. 怎么访问我们的应用?使用哪个端口?
    4. 怎样启动应用?

    我们看下Dockerfile的内容(位于flask-app文件夹)

    # 基镜像
    FROM python:3-onbuild
    
    # 端口
    EXPOSE 5000
    
    # 启动应用
    CMD ["python", "./app.py"]
    

    我们可以看到,这个文件没有回答第二个问题,如何是好?
    不用担心,因为我们的Dockerfile位于flask-app文件夹,且使用onbuild版本,第二个问题自动解决了。

    使用Dockerfile

    怎么用呢?让docker build命令去干就行了。

    首先,没有注册Docker hub的同学要去注册,记录自己的用户名
    cdDockerfile路径,然后:

    $ docker build -t 你的用户名/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
    

    其中-t标记来添加tag,指定新的镜像的用户信息。 “.” 是 Dockerfile所在的路径(当前目录),也可以替换为一个具体的 Dockerfile的路径。

    跑起来

    $ docker run -p 8888:5000 用户名/catnip
    * Running on http://0.0.0.0:5000/ (Press CTRL**C to quit)
    

    访问“http://0.0.0.0:8888/”看看效果吧。

    祝贺你,成功了!

    Docker on AWS

    算了吧

    多容器环境

    上面部分只运行了一个Docker。但是为了运行一个应用,你还用很多其他依赖吧,数据库得有吧?

    这部分我们就讨论下怎么运行有依赖的应用。具体来讲,就是讨论运行和管理多容器环境。

    将各种服务解耦很重要,使用容器隔离各个服务。这样就可以针对各个服务进行单独优化。

    SF Food Trucks为例

    此应用包含Flask后台和Elasticsearch服务。自然我们想把它分成两个容器。如果以后某部分成为性能瓶颈,我们可以通过增加容器单独优化。

    Elasticsearch容器

    建立容器应该不难吧,我们已经建立过一个Flask容器,那我们看看有没有Elasticsearch容器:

    $ 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                    [OK]
    

    我们可以看到有一个官方版本(没有前缀那个)。我们迅速就可以把它运行起来:

    $ docker run -dp 9200:9200 --name es elasticsearch
    d582e031a005f41eea704cdc6b21e62e7a8a42021297ce7ce123b945ae3d3763
    
    $ curl 0.0.0.0:9200
    {
      "name" : "Ultra-Marine",
      "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"
    }
    

    Flask容器

    下一步就是跑起来Flask****容器

    首先我们需要Dockerfile。这次,除了Python,我们需要JavaScript依赖和nodejs。因为有了自定义构建的步骤,我们需要从Ubuntu基镜像开始。

    Dockerfile如下:

    # start from base
    FROM ubuntu:14.04
    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
    RUN apt-get -yqq install nodejs npm
    RUN ln -s /usr/bin/nodejs /usr/bin/node
    
    # 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" ]
    

    我们从基镜像开始,安装依赖项。yqq说明任何询问都答yes,而且创建了到node的符号链接来保证兼容性。

    然后使用ADD命令将我们的应用复制到容器中的/opt/flask-app。然后我们将这个目录设为工作目录,这样下面的操作将会在这个目录进行。

    现在系统级别的依赖都搞定了,下面就要安装应用级的依赖了。首先使用npm安装然后运行pip。后面步骤前别按介绍过,不再详述。

    最后,跑起来吧(其实是爬,因为要下载很多东西):

    $ docker build -t 你的用户名/foodtrucks-web .
    

    都安装好后,再次运行会很快。我们试试:

    $ docker run -P prakhar1989/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...
    

    额。。。我们的应用跑步起来,因为它不知道Elasticsearch在哪。怎么办呢?

    Docker Network

    首先,我们想想怎么解决这个问题。想想我们学过什么命令可以用来给我们一些启示吗?docker ps如何?其实除了这个命令我们不知道该干什么了吧。

    我们来试下:

    $ docker ps
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                              NAMES
    e931ab24dedc        elasticsearch       "/docker-entrypoint.s"   2 seconds ago       Up 2 seconds        0.0.0.0:9200->9200/tcp, 9300/tcp   cocky_spence
    

    这段输出跟我们说在0.0.0.0:9200有一个ES容器可以访问。我们能告诉Flask去访问这里吗?

    那么我们就得和Flask容器中的应用说去访问主机的0.0.0.0:9200吧。但是ES容器也运行在0.0.0.0,只不过是另外一个端口。那么并不能通过访问这个IP来交流。哪还有什么IP可用吗?

    下面我们分析一下docker的网络。当docker安装之后,自动建立三个网络:

    $ docker network ls
    NETWORK ID          NAME                DRIVER
    075b9f628ccc        none                null
    be0f7178486c        host                host
    8022115322ec        bridge              bridge
    

    bridge网络是运行的默认网络。我们查看下:

    $ docker network inspect bridge
    [
        {
            "Name": "bridge",
            "Id": "8022115322ec80613421b0282e7ee158ec41e16f565a3e86fa53496105deb2d7",
            "Scope": "local",
            "Driver": "bridge",
            "IPAM": {
                "Driver": "default",
                "Config": [
                    {
                        "Subnet": "172.17.0.0/16"
                    }
                ]
            },
            "Containers": {
                "e931ab24dedc1640cddf6286d08f115a83897c88223058305460d7bd793c1947": {
                    "EndpointID": "66965e83bf7171daeb8652b39590b1f8c23d066ded16522daeb0128c9c25c189",
                    "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"
            }
        }
    ]
    

    可以见到我们的容器e931ab24dedc在"Containers"中列出,而且分配了一个IP:172.17.0.2。这个IP是我们想要用的那个吗?我们运行flask容器试试。

    $ docker run -it --rm 你的用户名/foodtrucks-web bash
    root@35180ccc206a:/opt/flask-app# curl 172.17.0.2:9200
    bash: curl: command not found
    root@35180ccc206a:/opt/flask-app# apt-get -yqq install curl
    root@35180ccc206a:/opt/flask-app# curl 172.17.0.2: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说明运行完就自动删除这个容器。我们进入容器后,运行了一个curl命令,但是提示此命令没有安装,所以需要安装之后,再次运行。然后,大功告成!

    虽然我们知道了怎么样让它们之间通信,但是我们仍然有两个问题需要解决:

    1. bridge网络是所有容器共享的,不够安全。
    2. 告诉Flask连接我们让他连接的地址。

    Docker允许我们建立我们自己的网络,还能是它们互相隔离.

    首先,我们建立一个自己的网络

    $ docker network create foodtrucks
    1a3386375797001999732cb4c4e97b88172d983b08cd0addfcb161eed0c18d89
    
    $ docker network ls
    NETWORK ID          NAME                DRIVER
    1a3386375797        foodtrucks          bridge
    8022115322ec        bridge              bridge
    075b9f628ccc        none                null
    be0f7178486c        host                host
    

    这个命令新建了一个名为foodtrucksbirdge网络。Docker网路的详细内容,请阅读官方文档

    现在我们有了网络,我们可以使用 --net将我们的容器运行在这个网络。首先,我们先把之前的容器停止。

    $ docker ps
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                              NAMES
    e931ab24dedc        elasticsearch       "/docker-entrypoint.s"   4 hours ago         Up 4 hours          0.0.0.0:9200->9200/tcp, 9300/tcp   cocky_spence
    
    $ docker stop e931ab24dedc
    e931ab24dedc
    
    $ docker run -dp 9200:9200 --net foodtrucks --name es elasticsearch
    2c0b96f9b8030f038e40abea44c2d17b0a8edda1354a08166c33e6d351d0c651
    
    $ docker network inspect foodtrucks
    [
        {
            "Name": "foodtrucks",
            "Id": "1a3386375797001999732cb4c4e97b88172d983b08cd0addfcb161eed0c18d89",
            "Scope": "local",
            "Driver": "bridge",
            "IPAM": {
                "Driver": "default",
                "Config": [
                    {}
                ]
            },
            "Containers": {
                "2c0b96f9b8030f038e40abea44c2d17b0a8edda1354a08166c33e6d351d0c651": {
                    "EndpointID": "15eabc7989ef78952fb577d0013243dae5199e8f5c55f1661606077d5b78e72a",
                    "MacAddress": "02:42:ac:12:00:02",
                    "IPv4Address": "172.18.0.2/16",
                    "IPv6Address": ""
                }
            },
            "Options": {}
        }
    ]
    

    我们这次给了这个容器一个名字es,运行在我们自定义的网络,其他都和以前一样。我们在试着将我们的flask容器运行在这个网络。

    $ docker run -it --rm --net foodtrucks 你的账户名/foodtrucks-web bash
    root@53af252b771a:/opt/flask-app# cat /etc/hosts
    172.18.0.3  53af252b771a
    127.0.0.1   localhost
    ::1 localhost ip6-localhost ip6-loopback
    fe00::0 ip6-localnet
    ff00::0 ip6-mcastprefix
    ff02::1 ip6-allnodes
    ff02::2 ip6-allrouters
    172.18.0.2  es
    172.18.0.2  es.foodtrucks
    
    root@53af252b771a:/opt/flask-app# curl es:9200
    bash: curl: command not found
    root@53af252b771a:/opt/flask-app# apt-get -yqq install curl
    root@53af252b771a:/opt/flask-app# curl es:9200
    {
      "name" : "Doctor Leery",
      "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@53af252b771a:/opt/flask-app# ls
    app.py  node_modules  package.json  requirements.txt  static  templates  webpack.config.js
    root@53af252b771a:/opt/flask-app# python app.py
    Index not found...
    Loading data in elasticsearch ...
    Total trucks loaded:  733
     * Running on http://0.0.0.0:5000/ (Press CTRL**C to quit)
    root@53af252b771a:/opt/flask-app# exit
    

    搞定!下一步我们将容器正式运行起来。

    $ docker run -d --net foodtrucks -p 5000:5000 --name foodtrucks-web 你的用户名/foodtrucks-web
    2a1b77e066e646686f669bab4759ec1611db359362a031667cacbe45c3ddb413
    
    $ docker ps
    CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS                              NAMES
    2a1b77e066e6        prakhar1989/foodtrucks-web   "python ./app.py"        2 seconds ago       Up 1 seconds        0.0.0.0:5000->5000/tcp             foodtrucks-web
    2c0b96f9b803        elasticsearch                "/docker-entrypoint.s"   21 minutes ago      Up 21 minutes       0.0.0.0:9200->9200/tcp, 9300/tcp   es
    
    $ curl -I 0.0.0.0:5000
    HTTP/1.0 200 OK
    Content-Type: text/html; charset=utf-8
    Content-Length: 3697
    Server: Werkzeug/0.11.2 Python/2.7.6
    Date: Sun, 10 Jan 2016 23:58:53 GMT
    

    进入http://0.0.0.0:5000享受一个你的应用吧。这个过程虽然看起来很恐怖,实际只有四条命令。原作者将其写入一个bash script

    #!/bin/bash
    
    # build the flask container
    docker build -t prakhar1989/foodtrucks-web .
    
    # create the network
    docker network create foodtrucks
    
    # start the ES container
    docker run -d --net foodtrucks -p 9200:9200 -p 9300:9300 --name es elasticsearch
    
    # start the flask app container
    docker run -d --net foodtrucks -p 5000:5000 --name foodtrucks-web prakhar1989/foodtrucks-web
    

    下次想运行这个应用,只要这样既可:

    $ git clone https://github.com/prakhar1989/FoodTrucks
    $ cd FoodTrucks
    $ ./setup-docker.sh
    

    完!很清爽吧!

    还有更多docker内容,可以访问原文教程

    本人也正在学习,以后可能会有更新

    相关文章

      网友评论

        本文标题:8 Docker教程

        本文链接:https://www.haomeiwen.com/subject/tovhwttx.html