zoukankan      html  css  js  c++  java
  • Docker 入门 第二部分: 容器

    Docker 入门 第二部分: 容器

    先决条件

    • 安装 Docker 1.13 或更高版本

    • 阅读第一部分

    • 给你的环境做一个快速运行测试,以确保你已经准备好了

      docker run hello-world
      

    介绍

    现在到了以docker的方式去构建一个应用了。我们从整个应用的架构的底层开始,这里是一个容器,我们会在本页介绍它。在此层级之上是一个服务,它定义了容器在生产环境中会如何工作,我们会在第三部分介绍。最后,最顶层的是堆栈,定义所有服务的交互行为,我们会在第五部分介绍。

    • Stack 堆栈
    • Services 服务
    • Container 容器 (现在你在这里)

    你的新开发环境

    在过去,如果你要写一个ptyhon应用,首先你要在你的机器上安装一套ptyhon的运行环境。但是,这就产生了一种情况,为了让你的应用按照预期运行,你机器上的环境需要精确部署,而且还需要和你的生产环境相匹配。
    使用Dcoker,你只需要将一个便携的python运行时(就是一套可执行的python)作为镜像,而没有安装的必要。然后,你构建的镜像可以包括基本的Python镜像和你的应用代码。以确保你的应用、应用的依赖和运行环境全部在一起。
    这些由某些内容定义的便携的镜像叫做 Dockerfile

    使用 Dockerfile 定义一个容器

    Dockerfile 定义容器内环境中的内容。对注入网络接口和磁盘驱动的访问都在这个环境中被虚拟化。以和系统的其余部分分离开来,因此你需要将端口映射到外部,并具体指明你想要将什么文件“复制到”该环境。

    对诸如网络接口和磁盘驱动器等资源的访问在这个环境中被虚拟化, 这与系统的其余部分隔离开来, 因此您需要将端口映射到外部世界, 并具体要 "复制" 哪些文件到该环境。不管怎样,可以保证在这个 Dockerfile中构建定义的应用可以在任何地方都可以一样的运行。

    Dockerfile

    创建一个空目录。进入该目录,并创建一个名为 Dockerfile 的文件,把以下内容复制粘贴到该文件中,并保存。注意dockerfile文件中解释每一条语句的注释内容。

    # Use an official Python runtime as a parent image
    FROM python:2.7-slim
    
    # Set the working directory to /app
    WORKDIR /app
    
    # Copy the current directory contents into the container at /app
    ADD . /app
    
    # Install any needed packages specified in requirements.txt
    RUN pip install --trusted-host pypi.python.org -r requirements.txt
    
    # Make port 80 available to the world outside this container
    EXPOSE 80
    
    # Define environment variable
    ENV NAME World
    
    # Run app.py when the container launches
    CMD ["python", "app.py"]
    

    这个 Dockerfile 文件引用了我们还没有创建的文件,app.pyrequirements.txt。 稍后我们会创建它们。

    应用本身

    再创建两个文件,app.pyrequirements.txt,并把他们放入和 Dcokerfile 相同的目录里。这样我们就完成了应用,你可以看到,这很简单。当上面的 Dcokerfile 被构建到一个镜像中时,app.pyrequirements.txt 也会存在于这个镜像,这是因为 DockerfileADD 命令,并且因为有 EXPOSE 命令, app.py 的输出可以通过 HTTP 访问。

    requirements.txt

    Flask
    Redis
    

    app.py

    from flask import Flask
    from redis import Redis, RedisError
    import os
    import socket
    
    # Connect to Redis
    redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
    
    app = Flask(__name__)
    
    @app.route("/")
    def hello():
        try:
            visits = redis.incr("counter")
        except RedisError:
            visits = "<i>cannot connect to Redis, counter disabled</i>"
    
        html = "<h3>Hello {name}!</h3>" 
               "<b>Hostname:</b> {hostname}<br/>" 
               "<b>Visits:</b> {visits}"
        return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)
    
    if __name__ == "__main__":
        app.run(host='0.0.0.0', port=80)
    

    现在我们看到 pip install -r requirements.txt 为 Python 安装了Flask 和Redis库,应用程序打印了环境变量NAME,以及调用socket.gethostname()后的输出。最终,因为Redis没有运行(我们只安装了Python库,没有安装redis),正常的话,我们可以看到尝试使用redis失败,产生的错误信息。

    注意:当在容器内部检索容器ID时,访问的主机名就像一个正在运行的进程的ID

    就是这样,你的系统中不需要 python 或 requirements.txt 中的任何内容,构建和运行这个镜像时也不需要在你的系统上安装它们。看起来你并没有安装Python和Flask的环境,但你已经有了。

    构建应用

    我们已经准备好了去构建应用。确保你一直都在新建目录的最上级。你可以看到如下内容:

    $ ls
    Dockerfile		app.py			requirements.txt
    

    现在运行构建命令,这会创建一个Docker镜像,我们可以使用 -t 参数给它指定一个友好的名字。

    docker build -t friendlyhello .
    

    构建的镜像保存在哪里呢?镜像会保存在你机器上本地的Docker镜像registry里:

    $ docker image ls
    
    REPOSITORY            TAG                 IMAGE ID
    friendlyhello         latest              326387cea398
    
    

    Linux用户故障排查
    代理服务器设置
    代理服务器启动运行后,可以阻塞与web应用的链接。如果你使用了代理服务器,把下面的内容添加到你的Dockerfile,使用ENV命令指定你的代理服务器的主机和端口:

    # Set proxy server, replace host:port with values for your servers
    ENV http_proxy host:port
    ENV https_proxy host:port
    

    DNS设置
    DNS配置错误可以导致使用pip时出现问题。你需要设置你自己的DNS服务器地址来pip正常工作。可能你想修改 Docker守护进程的DNS设置,你可以编辑(或创建)配置文件/etc/docker/daemon.json,设置 dns关键字,内容如下:

    {
      "dns": ["your_dns_address", "8.8.8.8"]
    }
    

    上面的例子中,列表的第一个元素是DNS服务器的地址,第二个是Google的DNS,当第一个地址无法使用时,会使用第二个。
    在继续之前,先保存 daemon.json 并重启docker服务。

    sudo service docker restart
    

    问题修复后,重新尝试build命令

    运行应用

    运行应用,使用-p参数将你的机器的4000端口映射到容器的发布端口80上。

    docker run -p 4000:80 friendlyhello
    

    你会在http://0.0.0.0:80看到一个Pyhton正常为你的应用服务的消息。但是这个消息来自容器内部,它不知道你将该容器的80端口映射到4000端口,需要输入正确的URL http://localhost:4000

    在浏览器中访问正确的URL,以查看网页中显示的内容。

    图片名称

    注意:如果你在Windows7中使用Docker Toolbox,要使用Docker机器的IP替换localhost。例如,http://192.168.99.100:4000/。使用 docker-machine ip 命令可以找到这个IP地址。

    你也可以在shell中使用curl命令看到相同的内容。

    $ curl http://localhost:4000
    
    <h3>Hello World!</h3><b>Hostname:</b> 8fc990912a14<br/><b>Visits:</b> <i>cannot connect to Redis, counter disabled</i>
    

    这里重新映射 4000:80 演示了和 DockerfiileEXPOSE的不同之处,而且在运行 docker run -p 时设置了发布的值。在后面的步骤中,映射了主机的4000端口到容器的80端口,并使用了 http://localhost

    在你的终端中使用 CTRL+C 来退出。

    在Windows中,显示的关闭容器
    在windows系统,CTRL+C 不会关闭容器。所以,首先输入 CTRL+C 返回提示符(或者新打开一个shell),然后输入 docker container ls 来列出运行中的容器,随后通过 docker container stop <Container NAME or ID>来关闭容器。否则,在稍后的步骤中,当你尝试重新运行容器会得到一个来自守护进程错误的响应。

    现在我们以分离模式在后台运行应用:

    docker run -d -p 4000:80 friendlyhello
    

    你会获得应用容器的长ID并返回终端,容器会在后台运行,你还可以荣国 docker container ls 查看缩写的容器ID()

    $ docker container ls
    CONTAINER ID        IMAGE               COMMAND             CREATED
    1fa4ab2cf395        friendlyhello       "python app.py"     28 seconds ago
    

    注意 容器IDhttp://localhost:4000上的内容相匹配。
    现在使用 docker container stop 指定 容器ID 来结束进程,如下:

    docker container stop 1fa4ab2cf395
    

    共享你的镜像

    为了证明我们刚刚创建的镜像的可移植性,我们可以上传构建的镜像并在任何地方运行它。毕竟,当你想把容器部署到生产环境中时,你需要知道如何推送到 registries。

    一个registry 是一个仓库的集合,一个仓库是一个镜像的集合——类似于GitHub的仓库。只是代码已经构建好了。registry上的一个用户可以创建多个仓库。docker CLI默认使用Docker的公共registry。

    注意: 这里我们使用Docker的公共registry是因为它是免费的和预置的,不过我们还是有很多公共的registry可以选择,甚至你可以使用 Docker Trusted Registry来搭建你自己私有的registry。

    使用你的docker ID进行登录

    如果你没有docker用户,可以在hub.docker.com注册一个。记下你的用户名。在你的本地机器上登录到DOcker公共的registry。

    $ docker login
    

    为镜像加标签

    关联本地镜像与registry仓库的方法是 username/repository:tag 。标签是可选的,但建议设置上,因为这是registry为docekr镜像提供版本的机制,根据上下文给仓库和标签起一个有意义的名字,例如:get-started:part2。这里我们将镜像放在 get-started仓库,标签名字为 part2
    现在,把它们放在一起来标记镜像。使用in的用户名、仓库和标签名来运行 docker tag image,以便将镜像上传到你想要上传的位置,命令语法如下:

    docker tag image username/repository:tag
    

    例如:

    docker tag friendlyhello gordon/get-started:part2
    

    运行docker image ls来查看你信打标签的镜像:

    $ docker image ls
    
    REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
    friendlyhello            latest              d9e555c53008        3 minutes ago       195MB
    gordon/get-started         part2               d9e555c53008        3 minutes ago       195MB
    python                   2.7-slim            1c7128a655f6        5 days ago          183MB
    ...
    

    发布镜像

    把打过标签的镜像上传到仓库:

    docker push username/repository:tag
    

    完成之后,上传之的镜像会被公开发布。如果你登录到了 Docker Hub,你可以使用pull命令,在Docker Hub看到最新的镜像。

    从远程仓库拉取并运行镜像

    从现在开始,你可以在在任何机器上使用docker run运行你的应用:

    docker run -p 4000:80 username/repository:tag
    

    如果镜像在本地机器上不可用,Docker会从远程仓库上拉取镜像:

    $ docker run -p 4000:80 gordon/get-started:part2
    Unable to find image 'gordon/get-started:part2' locally
    part2: Pulling from gordon/get-started
    10a267c67f42: Already exists
    f68a39a6a5e4: Already exists
    9beaffc0cf19: Already exists
    3c1fe835fb6b: Already exists
    4c9f1fa8fcb8: Already exists
    ee7d8f576a14: Already exists
    fbccdcced46e: Already exists
    Digest: sha256:0601c866aab2adcc6498200efd0f754037e909e5fd42069adeff72d1e2439068
    Status: Downloaded newer image for gordon/get-started:part2
     * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
    

    现在无论你在哪里运行docker run,他会拉取镜像,Pyhton、requirements.txt中指定的依赖和你的代码 会在一起运行,他们全部打包在一个小包裹里(镜像),你不需要在你的机器上安装任何东西,Docker 就可以运行它了。

    第二部分总结

    这就是本页所有内容,下一节,我们会通过在服务中运行这个容器来学习如何扩展我们的应用程序。

    回顾和备忘【可选】

    官方在这里提供了一段视频,但我这里观看有问题,我就找把视频录制下来放到了B站,地址如下:

    官方视频

    B站的“分享视频”中给的代码放在博客园无法播放,只能在这里写一个地址了。

    下面是本页中关于docker的基本命令列表,以及一些相关的命令,在继续后面的学习前可以研究一下。

    docker build -t friendlyhello .  # Create image using this directory's Dockerfile
    docker run -p 4000:80 friendlyhello  # Run "friendlyname" mapping port 4000 to 80
    docker run -d -p 4000:80 friendlyhello         # Same thing, but in detached mode
    docker container ls                                # List all running containers
    docker container ls -a             # List all containers, even those not running
    docker container stop <hash>           # Gracefully stop the specified container
    docker container kill <hash>         # Force shutdown of the specified container
    docker container rm <hash>        # Remove specified container from this machine
    docker container rm $(docker container ls -a -q)         # Remove all containers
    docker image ls -a                             # List all images on this machine
    docker image rm <image id>            # Remove specified image from this machine
    docker image rm $(docker image ls -a -q)   # Remove all images from this machine
    docker login             # Log in this CLI session using your Docker credentials
    docker tag <image> username/repository:tag  # Tag <image> for upload to registry
    docker push username/repository:tag            # Upload tagged image to registry
    docker run username/repository:tag                   # Run image from a registry
    
  • 相关阅读:
    Java 8与Runtime.getRuntime().availableProcessors()
    nginx配置
    周末完成工作小结
    CentOS 8 安装MySQL 8.0
    centOS8网络获取不了
    IUAP平台新增菜单存储过程
    centOS8安装Docker
    Mybatis里用到的设计模式
    2020,回顾过往,展望未来
    使用 Apache SSI(Server Side Includes) 制作多语言版静态网页
  • 原文地址:https://www.cnblogs.com/resn/p/9558531.html
Copyright © 2011-2022 走看看