Docker学习4-Containers

用Docker方式构建应用程序,从这个应用程序层次结构的底层容器开始。高于此级别的是一项服务,它定义了容器在生产中的行为方式。在顶层是堆栈,它定义了所有服务的交互。

  • Stack  堆栈
  • Services  服务
  • Container  容器

如果想用Python开发一个应用程序,必须先在机器上安装一个Python运行环境,计算机上的环境非常适合想要开发的应用程序,这样应用程序才能按预期运行,并且还需要与生产环境相匹配。若使用Docker,可以将可移植的Python运行时攫取作为一个镜像,而无需安装。构建过程包含了应用程序代码和基本的Python镜像,确保应用程序、其依赖项和运行时能够同时运行。这些可移植镜像定义在一个称为Dockerfile的文件中。Dockerfile用来创建一个自定义的镜像 image或定制镜像 image,包含了用户指定的软件依赖等。当前目录下包含Dockerfile文件,使用命令 build来创建新的镜像 image,并命名为edwardsbean/centos6-jdk1.7:

docker build -t edwardsbean/centos6-jdk1.7 .

使用 Dockerfile 定制镜像

  镜像的定制实际上就是定制每一层所添加的配置、文件。如果可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决,这个脚本就是 Dockerfile。Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

Dockerfile关键字

FROM: 指定基础镜像,基于哪个镜像

所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。而 FROM 就是指定基础镜像,因此一个 Dockerfile 中 FROM 是必备的指令,并且必须是第一条指令。除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch 。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。如果以 scratch 为基础镜像的话,意味着不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。

RUN: 执行命令,安装软件用,RUN 指令是用来执行命令行命令的。

MAINTAINER: 镜像创建者

CMD: container启动时执行的命令,一个Dockerfile中只能有一条CMD命令,多条则只执行最后一条CMD。CMD主要用于container时启动指定的服务,当docker run command的命令匹配到CMD command时,会替换CMD执行的命令。

ENTRYPOINT: 入口点,container启动时执行的命令,一个Dockerfile中只能有一条ENTRYPOINT命令,如果多条,则只执行最后一条。

USER: 指定当前用户,使用哪个用户跑container。

EXPOSE: 声明端口,container内部服务开启的端口。EXPOSE 指令是声明运行时容器提供服务端口,只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。

ENV: 用来设置环境变量

格式:

  • ENV <key> <value>
  • ENV <key1>=<value1> <key2>=<value2>... 

COPY: 复制文件

格式:

  • COPY <源路径>... <目标路径>
  • COPY ["<源路径1>",... "<目标路径>"]

ADD:

将文件<src>拷贝到container的文件系统对应的路径<dest>;

所有拷贝到container中的文件和文件夹权限为0755,uid和gid为0;

如果文件是可识别的压缩格式,则docker会帮忙解压缩;

如果要ADD本地文件,则本地文件必须在 docker build <PATH>,指定的<PATH>目录下;

如果要ADD远程文件,则远程文件必须在 docker build <PATH>,指定的<PATH>目录下。比如: docker build github.com/creack/docker-firefox,docker-firefox目录下必须有Dockerfile和要ADD的文件。ADD只有在build镜像的时候运行一次,后面运行container的时候不会再重新加载了。

VOLUME: 可以将本地文件夹或者其他container的文件夹挂载到container中。

WORKDIR: 指定工作目录(或者称为当前目录),切换目录用,可以多次切换(相当于cd命令),对RUN,CMD,ENTRYPOINT生效。

ONBUILD: ONBUILD指定的命令在构建镜像时并不执行,而在它的子镜像中执行。ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN , COPY 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。

Docker 组件与元素

Docker有三个组件和三个基本元素,三个组件分别是:

  • Docker Client是用户界面,它支持用户与Docker Daemon之间通信。
  • Docker Daemon运行于主机上,处理服务请求。
  • Docker Index是中央registry,支持拥有公有与私有访问权限的Docker容器镜像的备份。

三个基本要素分别是:

  • Docker Containers负责应用程序的运行,包括操作系统、用户添加的文件以及元数据。
  • Docker Images是一个只读模板,用来运行Docker容器。
  • DockerFile是文件指令集,用来说明如何自动创建Docker镜像。

运行任何应用程序,都需要有两个基本步骤:

  • ·     构建一个镜像;
  • ·     运行容器。

这些步骤都是从Docker Client的命令开始的。Docker Client使用的是Docker二进制文件。在基础层面上,Docker Client会告诉Docker Daemon需要创建的镜像以及需要在容器内运行的命令。当Daemon接收到创建镜像的信号后,会进行如下操作:

第1步:构建镜像

如前所述,Docker Image是一个构建容器的只读模板,它包含了容器启动所需的所有信息,包括运行程序和配置数据等。每个镜像都源于一个基本的镜像,然后根据Dockerfile中的指令创建模板。对于每个指令,在镜像上创建一个新的层面。一旦镜像创建完成,就可以将它们推送到中央registryDocker Index,以供他人使用。然而,Docker Index为镜像提供了两个级别的访问权限:公有访问私有访问。可以将镜像存储在私有仓库,Docker官网有私有仓库的套餐可以供你选择。总之,公有仓库是可搜索和可重复使用的,而私有仓库只能给那些拥有访问权限的成员使用。Docker Client可用于Docker Index内的镜像搜索。

第2步:运行容器

运行容器源于之前在第一步中创建的镜像。当容器被启动后,一个读写层会被添加到镜像的顶层。当分配到合适的网络和IP地址后,需要的应用程序就可以在容器中运行了。

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

COPY . /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.py和requirements.txt文件。手动创建这些文件。当上述Dockerfile内置于镜像中时,由于Dockerfile中的COPY命令,app.py和requirements.txt文件同样存在,并且由于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本身)。

注意:在容器内部访问容器ID时,请访问主机名称,类似于正在运行的可执行文件的进程ID。

系统不需要Python或requirements.txt中的任何内容,构建或运行此镜像也不需要在系统上安装它们。看起来并没有真正建立一个Python和Flask的环境,但是实际上已经具有了环境。

Build the app

运行build命令。创docker建一个Docker镜像,使用 -t 标记:

[jeshy@master DockerStudy]$ sudo docker build -t friendlyhello .

构建的镜像位于机器的本地Docker镜像注册表中:

[jeshy@master DockerStudy]$ sudo docker image ls

Run the app

运行应用程序,使用-p将计算机的端口4000映射到容器的已发布端口80:

[jeshy@master DockerStudy]$ sudo docker run -p 4000:80 friendlyhello

通过浏览器打开URL http://localhost:4000

 

注意:如果在Windows 7上使用Docker Toolbox,请使用Docker Machine IP而不是localhost。 如,http://192.168.99.100:4000/。 要查找IP地址,请使用命令docker-machine ip。

 

还可以在shell中使用curl命令来查看相同的内容:

$ curl http://localhost:4000

以分离模式在后台运行应用程序:

sudo docker run -d -p 4000:80 friendlyhello

[jeshy@master DockerStudy]$ sudo docker run -d -p 4000:80 friendlyhello

f176c313b58bf6b5f50d92a37b736632e03bdbd102bd6ef8283538d2dcbfe239

[jeshy@master DockerStudy]$ sudo docker container ls

CONTAINER ID与http:// localhost:4000上的内容匹配。

使用docker container stop结束进程,使用CONTAINER ID:

[jeshy@master DockerStudy]$ sudo docker container stop f176c313b58b 

共享镜像

为了说明上述创建的内容的可移植性,上传已构建的镜像并在其他地方运行。当想要将容器部署到生产环境时,就需要知道和了解如何推送到注册表。注册表是存储库的集合,存储库是镜像的集合 - 类似于GitHub存储库,除了已经构建的代码。 注册表上的帐户可以创建许多存储库。Docker CLI默认使用Docker的公共注册表。

注意:这里使用Docker的公共注册表只是因为它是免费和预先配置的,有许多公共注册表可供选择,甚至可以使用Docker Trusted Registry设置自己的私有注册表。

使用Docker ID登录

如果没有Docker帐户,请在hub.docker.com上注册一个帐户,并记下用户名。

登录本地计算机上的Docker公共注册表:$ sudo docker login

标记镜像  Tag the image

将本地镜像与注册表上的存储库相关联的表示法是 username/repository:tag。建议使用标签,因为它是一种为Docker镜像提供版本标记的机制。给定一个存储库并用与上下文有意义的名称作标记,比如get-started:part2,这会将镜像植入get-started 存储库并将其标记为part2。现在,把它们放在一起来标记镜像。使用用户名、存储库和标记名称来运行docker标记镜像,以便将镜像上载到期望的目标位置。命令的语法格式如下:

docker tag image username/repository:tag

示例:

sudo docker tag friendlyhello gordon/get-started:part2

[jeshy@master DockerStudy]$ sudo docker image ls

REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE

gordon/get-started   part2               fe3b0a6a01ab        6 hours ago         131MB

friendlyhello        latest              fe3b0a6a01ab        6 hours ago         131MB

python               2.7-slim            0dc3d8d47241        3 weeks ago         120MB

hello-world          latest              4ab4c602aa5e        3 months ago        1.84kB

发布镜像  Publish the image

上载已经标记的镜像到存储库:

docker push username/repository:tag

从远端存储库攫取并运行镜像

使用docker run命令,使用此命令在任何计算机上运行应用程序:

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

如果本地镜像在计算机上不可用,则Docker会从存储库中攫取镜像。

$sudo 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在哪儿执行,都会pull你的镜像,以及Python和requirements.txt中的所有依赖项,并运行代码。不需要在主机上安装任何东西,它们在一个整洁的小包中一起运行。

以下是一些基本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

原文地址:https://www.cnblogs.com/jeshy/p/10518958.html