部署篇开头以两个简单的例子快速入门,然后介绍 Dockerfile 的常用命令


简单的例子

以下使用两个例子快速入题

官网的例子

这是来自 Docker 官方文档的例子,例子中使用了 Python,但不需要 Python 知识

使用 Dockerfile 生成容器

Docker 使用 Dockerfile 来生成容器。Dockerfile 会自动装配所用到的环境,所以你唯一需要做的就是敲几行命令。

Dockerfile

在任意文件夹下创建一个 Dockerfile,把下面的代码复制进去。

# 使用 Python 官方运行环境作为一个父镜像
FROM python:2.7-slim

# 将工作目录设置为 /app
WORKDIR /app

# 把当前目录下的内容复制到位于 /app 下的容器中
ADD . /app

# 安装所有在 requirements.txt 中列出的所需要的包
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# 向外界开放使容器的 80 端口
EXPOSE 80

# 定义环境变量
ENV NAME World

# 容器装配好之后运行
CMD ["python", "app.py"]

上面的 Dockerfile 需要 app.pyrequirements.txt 两个文件,我们继续创建

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)

现在执行下面的命令,生成一个镜像,-t 参数后的是镜像名,最后的 . 表示当路径

docker build -t friendlyhello .

使用以下命令查看所有镜像,命令 docker images 与其作用相同

$ docker image ls

REPOSITORY      TAG        IMAGE ID
friendlyhello   latest     326387cea398

使用 Linux 的同学可能会遇到网络的问题
以下是官网文档的简单的翻译。博主不精通此问题,如有其他需要,请自行百度、Bing、Google…
DNS设置
如果使用了代理服务器,需要使用 ENV 命令开启 host 的 port

ENV http_proxy host:port ENV https_proxy host:port

代理服务器设置
DNS 的错误配置会导致 pip 的问题,可以使用自己的 DNS 服务器地址使 pip 正常工作。通过编辑 /etc/docker/daemon.json ,像下面这样

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

RUN

使用以下命令来运行刚才创建好的镜像,-d 参数使其在后台运行,-p 参数使容器的 80 端口映射到主机的 4000 端口

docker run -d -p 4000:80 friendlyhello

访问 4000 端口 localhost:4000,如果 Docker 在其他服务器上,将 localhost 改为服务器 IP 即可。如果之前的一切顺利,你应该会看到久违的 “Hello World”

docker下修改hostname docker设置hostname_Docker

然后,使用以下的命令可以查看所有正在运行的容器。命令 docker ps 与其作用相同

$ docker container ls

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

书上的例子

这是周立所著 《Spring Cloud与Docker 微服务架构实战》一书中的例子。从篇幅就看到,这个例子更加简单一点

使用 Dockerfile 创建容器

和上面例子相同,首先创建一个 Dockerfile

FROM nginx
RUN echo '<h1> Hello World!</h1>' > /usr/share/nginx/html/index.html

Dockerfile 所在路径执行以下构建镜像的命令, : 为新镜像的标签, . 表示当前路径

docker build -t nginx:my .

添加标签页可由单独的命令 docker tag image name:tag 完成。以上面的例子为例:docker tag friendlyhello jackson:v_1

运行

执行启动 Docker 容器的命令

docker run -d -p 92:80 nginx:my

访问宿主机 92 端口,例如 123.206.98.122:92,可以看到 “Hello World”

docker下修改hostname docker设置hostname_docker下修改hostname_02

Dockerfile 常用命令

上面两个例子中用到了 FROMRUN 等命令,事实上,Dockerfile 有十多条指令。指令的一般格式为:指令名称 参数

  1. ADD 复制文件
  • ADD <src> ... <dest>
  • ADD ["<src>",... "dest"]

src 目录复制文件到容器 destsrc 可以是 Dockerfile 所在目录的相对路径,也可以是一个 URL,还可以是一个压缩包

注意

  • src 必须在构建的上下文中,不能使用例如:ADD ../something/something/ 这样的命令
  • dest 不以 / 结尾视作文件,否则视作目录
  • src 如果是 URL,其对应内容将被下载到 dest ,若 src 是一个目录,整个目录下的内容都将被复制。如果文件是可识别的压缩包,则 docker 将会自动解压

示例:

ADD hello.jar app.jar
  1. ARG 设置构建参数
    ARG 指令用于设置构建参数,类似于 ENV。和 ENV 不同的是, ARG 设置的是构建时的环境变量,在容器运行时是不会存在这些变量的。
    示例:
ARG use1=someuser
  1. CMD 启动容器
    每个 Dockerfile 只有一个 CMD 命令,如果指定了多个 CMD ,则执行最后一条。有 3 种格式
  • CMD ["executable","param1","param2"](推荐)
  • CMD ["param1","param2"](为 ENTRYPOINT 指令提供预设参数)
  • CMD command param1 param2(在 Shell 中执行)

示例:

CMD echo "This is a test." | wc -
  1. COPY 复制文件
  • ADD <src> ... <dest>
  • ADD ["<src>",... "dest"]

复制 src 到容器的 dest ,和 ADD 命令类似。不同的是 COPY 指令不支持 URL 和压缩包

  1. ENTRYPOINT 入口点
  • ENTRYPOINT ["executable","param1","param2"]
  • ENTRYPOINT command param1 param2

ENTRYPOINTCMD 指令目的相同

  1. ENV 设置环境变量
  • ENV <key> <value>
  • ENV <key>=<value> ...

示例:

ENV JAVA_HOME /path/to/java
  1. EXPOSE 声明暴露的端口
    EXPOSE 声明用于声明在运行时容器提供服务的端口,格式为 : EXPOSE <port> [<port>...]这只是一个声明,运行时并不会因为一个声明打开端口。该指令的作用主要是帮助镜像使用者理解该镜像服务的守护端口;其次是当运行时使用随机映射时,会自动映射 EXPOSE 的端口。
    示例:
# 声明暴露一个端口示例

EXPOSE port1

# 相应的运行容器使用的命令

docker run -p port1 image

# 也可以使用 -P 选项启动

docker run -P image


# 声明暴露多个端口示例

EXPOSE port1 port2

# 相应的运行容器使用的命令

docker run -p port1 -p port2 image

# 也可以指定需要映射到宿主机上的端口号

docker run -p host_port1:port1 -p host_port2:port2 image
  1. FROM 指定基础镜像
    使用 FROM 指定基础镜像,有点像 Java 中的 extends关键字。FROM 指令必须指定且要在其他指令之前。FROM 指令过后的所有指令都依赖于该指令所指定的镜像。支持 3 种格式:
  • FROM <image>
  • FROM <image>:<tag>
  • FROM <image>@<digeset>
  1. LABEL 为镜像添加元数据
    格式为:LABEL <key>=<value> <key>=<value>...使用 \ 换行。示例:
LABEL version="1.0"
LABEL description="This is a \
test text"
  1. MAINTAINER 指定维护者的信息
    这个指令用于为 Dockerfile 署名
    示例:
MAINTAINER Cloudy<itsclody.gitee.io/blog>
  1. RUN 执行命令
  • RUN <command>
  • RUN ["executable","param1","param2"]

RUN <command> 在 shell 终端中运行,在 Linux 中默认是 /bin/sh -c,在 Windows 中是 cmd /s /c ,使用这种格式,就像直接在命令行中输入命令一样。

RUN ["executable","param1","param2"] 使用 exec 执行,这种方式类似于函数调用。指定其他终端可以通过该方式操作,例如:RUN ["/bin/bash","-c","echo hello"],该方式必须使用双引号 " ,而不能使用 ' ,因为该方式会被转换成一个 JSON 数组

  1. USER 设置用户
    这个指令设置启动镜像时的用户或 UID,写在该指令后的RUNCMD 以及 ENTRYPOINT 指令都将使用该用户执行命令
    示例:
USER daemon
  1. VOLUME 指定挂载点
    这个指令使容器中的一个目录具有持久化储存的功能,该目录可被容器本身使用,也可共享给其他容器。当容器中的应用有持久化数据的需求时可以在 Dockerfile 中使用该指令。格式为: VOLUME ["/data"]示例:
VOLUME /data
  1. WORKDIR 指定工作目录
    格式为:WORKDIR /path/to/workdir写在该命令之后的 RUNCMD 以及 ENTRYPOINT 指令都将该目录作为当前目录,并执行响应的操作
  2. 其他
    其他命令并不常用,不在赘述。有兴趣请前往官网文档扩展。

后记

借鉴《Spring Cloud与Docker微服务架构实战》/周立 著