docker 简介
docker 是一个开发,运输,运行程序的一个平台。docker 将你的程序和基础设施隔离开来,以便于快速的运输,测试,开发。
docker 引擎
docker 架构(体系结构)
使用
client-server
架构,docker 客户端告诉 daemon(docker 后台常驻进程)去做一个构建,运行,分发docker 容器。
docker 客户端和 daemon 的交流通信使用rest api
,基于一个unix socket
或者network interface
之上。
几个术语(glossary)
Docker registries
: docker 镜像源,docker 默认被配置为去docker hub
上下载镜像。images
: 镜像,一个镜像是带有一系列指令的只读的模板(包),用来创建 docker 容器。当你创建自己的镜像的时候,你需要在Dockerfile
中按照一定的简单的语法去定义一些步骤去创建镜像并运行它。
一个镜像包含了运行一个程序所需要的各种依赖(代码,运行时,库,环境变量,配置文件)。Dockerfile
中的每一条指令都会生成一个层。containers
: 容器,一个容器是一个镜像的可运行的实例。可以通过docker api
或则CLI
对实例进行CRUD
等操作。services
: 服务,服务允许你跨多个 daemon 扫描容器,这些 daemon 相互协作称之为一个集群(swarm)。
docker 基本开发环境搭建
容器(containers)
- 使用
Dockerfile
定义容器
Dockerfile
1 | # Use an official Python runtime as a parent image |
app.py
1 | from flask import Flask |
- 构建 docker 镜像
1 | $ docker build -t friendlyhello . |
- 查看上一步构建的镜像
1 | $ docker image ls |
- 运行 app
1 | $ docker run -p 4000:80 friendlyhello |
- 查看 app.py 的输出
在浏览器访问http://localhost:4000
或者
在终端使用 curl 查看:
1 | $ curl http://localhost:4000 |
- 查看运行中的容器
1 | $ docker container ls |
- 停止运行一个容器
1 | $ docker container stop <IMAGE_ID> |
服务(services)
一个服务只会运行一个镜像,但是它定义了镜像运行的法规:比如该使用什么端口,产生多少个容器的复制品。
关于服务的概念比较的抽象,可以查看官方例子对于服务的解释
task:
一个 service 中运行的一个容器叫做 task。task 被给予一个自增的 id,增加到
replicas
的数量位置。
查看 service 中的 tasks一个
docker-compose.yml
文件定义了 docker 容器在产品模式下应该具有的行为。
1 | version: "3" |
运行我们的服务:
1 | $ docker stack deploy -c docker-compose.yml getstartedlab |
在使用上面的命令来启动一个服务的时候,必须先设置一个swarm manager
(集群 leader), 不然会抛出一个错误提示。后面解释为什么要这样做:
1 | $ docker swarm init |
查看启动的服务:
1 | $ docker service ls |
列出一个服务中有多少个任务:
1 | docker service ps getstartedlab_web |
请求服务:
1 | $ curl -4 http://localhost |
扫描 app: 可以改变docker-compose.yml
中的相关配置,然后重新运行发布命令
1 | $ docker stack deploy -c docker-compose.yml getstartedlab |
拆卸 app 和 swarm
1 | $ docker stack rm getstartedlab |
Stacks(栈)
栈是一组相互关联的服务,这些服务可以共享依赖关系,被组织和缩放。一个栈有着定义和协调整个应用的能力。
Dockerfile 常用指令(选项)
Dockerfile
中的指令大小写不敏感,但是按照约定都使用大写,以便于和指定的参数区分。Dockerfile
中的指令按顺序一条一条的执行,并且Dockerfile
文件必须以FROM
开始
构建上下文 context
docker build
命令基于Dockerfile
文件和context
构建镜像。context
是一个PATH
或则URL
指定的文件集合。PATH
是一个本地文件系统的一个目录,URL
是一个 git 仓库。
.dockerignore
为了增加构建的性能,可以将不需要的文件添加到
.dockerignore
一般情况下,Dockerfile
放置在context
的根目录下,可以使用-f
选项指定Dockerfile
文件的位置:
1 | $ docker build -f /path/to/a/Dockerfile . |
可以为一个镜像指定多个仓库标签:
1 | $ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest . |
解析器指令(parser directives)
Dockerfile
中的注释已#
开头,但是解析器指令时一种特殊的注释,必须出现在Dockerfile
中的开头部分。
语法:# directive=value
FROM
初始化一个新的构建时期,为接下来的指令设置一个基本镜像。
常用的 3 种形式:
1 | FROM <image> [AS <name>] |
RUN
在当前镜像的顶部新的层里运行任何指令,并将结果提交到下一步中。
层运行指令并且生成提交信息符合Docker
的核心概念:这个提交是非常便宜的,容器可以基于一个镜像的任何历史点创建。
2 种使用形式:shell 形式和 exec 形式
1 | RUN /bin/bash -c 'source $HOME/.bashrc; \ |
CMD
一个
Dockerfile
中只能出现一个CMD
,如果出现了多个,那么只有最后一个起作用。
3 种使用形式:
1 | # exec形式,最受欢迎的形式 |
LABEL
为镜像添加一些元数据,可以使用
docker inspect
查看 label 的值。使用形式:
1 | LABEL <key>=<value> <key>=<value> <key>=<value> ... |
一些常用情况的例子
1 | LABEL "com.example.vendor"="ACME Incorporated" |
MAINTAINER
已过期,使用
LABEL
代替。
EXPOSE
指定 docker 容器在运行的时候监听那个网络端口,可以指定使用 TCP 还是 DUP。如果没有指定协议,默认使用 TCP。使用形式:
1 | EXPOSE <port> [<port>/<protocol>...] |
ENV
可以使用
docker inspect
查看它的值,
可以使用docker run --env <key>=<value>
改变值。
环境变量持久化可能会带来一些副作用,所以可以通过RUN <key>=<value> <command>
来设置单个指令变量。
使用形式:
1 |
|
如:
1 | ENV myName="John Doe" myDog=Rex\ The\ Dog \ |
等价于
1 | ENV myname John Doe |
ADD
该指令将
src
中的文件,文件夹或者远程文件拷贝到镜像所在的文件系统的dest
目录。src
中的路径被解析为相对于构建上下文context
的路径,路径中可包含通配符。dest
路径必须是一个绝对路径或者相对于WORKDIR
的路径。
使用形式:
1 | ADD [--chown=<user>:<group>] <src>... <dest> |
关于该指令的详细说明,请参看官方文旦
COPY
该指令和
ADD
指令类似。区别是ADD
支持src
是一个URL
,而COPY
不支持。
ENTRYPOINT
2 种使用形式:shell 形式和 exec 形式
1 | ENTRYPOINT ["executable", "param1", "param2"] (exec form, preferred) |
VOLUME
用指定的名称创建一个挂载点。使用形式:
1 | VOLUME ["/var/log/"] |
USER
设置 docker 镜像跑在哪个用户或者用户 ID 下,也可以设置一个可选的组或者组 ID 下。
如果没有设置组,默认使用root
组。
其他的指令RUN, CMD, ENTRYPOINT
也遵守这一规则。
1 | USER <user>[:<group>] |
WORKDIR
为
Dockerfile
中的RUN, CMD, ENTRYPOINT, ADD, COPY
指令提供工作目录。WORKDIR
可以在Dockerfile
中出现多次,那么后面的路径时相对于前面的。
TODO: 类似于 nodejs 中的 resolve 方法?解析到一个绝对路径为止?
1 | WORKDIR /a |
WORKDIR
可以使用Dockerfile
中已经定义的环境变量。
1 | ENV DIRPATH /path |
compose
文件结构
1 | services: |
网络(network)
桥接网络(bridge networks)
一个桥接可以是一个硬件设备或者是运行在一个主机内核中的软件。
用户自定义桥接网路的好处:
- 提供更好的隔离和互通性。
- 在容器间提供自动化
DNS
解析。 - 随时绑定或者解绑一个容器到自定义网络。
- 每个用户自定义网络可以创建一个可配置的网络。
创建用户自定义的网络:
1 | $ docker network create my-net |
移除一个自定义网络:
1 | $ docker network rm my-net |
如果一个容器当前正在使用这个网络,那么必须选断开:
1 | $ docker network disconnect my-net my-nginx |
宗卷(Volumes)
docker 中挂载数据的 3 种方式:
- bind mount:利用机器的文件系统。
- volumes:在文件系统中开辟一块区域来持久化数据,专门由 docker 管理。
- tmpfs: 在内存中挂载数据,并不能持久化,当容器停止,数据丢失。
关于以上 3 种的数据挂载方式,具体的介绍查看官方文档
最推荐的方式是使用 Volumes(实际还是视具体情况而定),此处只讲 Volumes。
创建 Volumes:
1 | $ docker volume create my-vol |
列出所有的 Volumes:
1 | $ docker volume ls |
发布镜像
在
docker cloud
上只能免费创建一个私有仓库,无限制多个公有仓库。
- 创建镜像
1 | $ docker build -t username/tag . |
- 登录
1 | docker login |
- 推送镜像
1 | $ docker push |