Docker生命周期:Build,Ship and Run

摘要

“Build,Ship and Run” 是docker最早的一句宣传语,讲的是docker的整个生命周期。

Build:指的是如何创建镜像
Ship:指的是镜像如何流转
Run:指的是镜像如何运行(进入运行态)

Docker生命周期:Build,Ship and Run

“Build,Ship and Run” 是docker最早的一句宣传语,讲的是docker的整个生命周期。

  • Build:指的是如何创建镜像
  • Ship:指的是镜像如何流转
  • Run:指的是镜像如何运行(进入运行态)
    docker的整个生命周期实际上就是镜像的流转和状态的改变。

众所周知docker最核心的是容器技术,那么在docker中容器技术的是什么?镜像,而container仅仅只是容器(镜像的运行态),镜像才是docker容器技术的核心。

  • Docker生命周期的流转基本都是通过镜像来流转的
  • 容器是基于镜像启动的,没有镜像也就没有了容器
  • 镜像采用分层技术
  • 通过CoW技术让容器可以共享镜像的文件系统,确保底层数据不丢失

容器技术

  • 又称为容器虚拟化
  • 是一种操作系统虚拟化技术(与其他虚拟化差异比较大)
  • 轻量级虚拟化技术
  • linux内核支持

什么是容器技术(普遍认可的说法)

  • 首先要有一个相对独立的运行环境
  • 最小化其对外界的影响
  • namespace(命名空间):访问隔离
  • cgroup(控制组):资源控制

容器与持久化数据

为了让Docker生命周期更灵活的run起来,容器的运行层似乎应该是可被丢弃的,那么就要求容器不要在本地生成持久化数据,又或者如何将原本要写在本地的持久化数据转义到可靠的存储位置。

容器的持久化数据

容器技术发展历史

Docker为什么脱颖而出

  • 开源
  • 定义了镜像的概念

创建镜像:Build

build一个image

镜像基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 查看镜像
docker image ls [-a]
# 搜索镜像、下载镜像
docker search|pull image-name
# 创建镜像
docker build|commit
# 导出、导入镜像
docker save|load
# 导出容器、导入生成镜像
docker export|import
# 删除镜像
docker rmi
# 深入镜像
docker inspect image-name
# 查看镜像构建历史
docker history imageid
# 查看镜像开放端口
docker port image-name
# 创建新的镜像标签
docker tag old-image new-image

容器是怎么变成镜像的

docker在发布初期就提供了将容器变成镜像的方法:

  • 通过基础镜像创建容器
  • 然后登陆容器像普通的操作系统一样手动部署环境
  • 然后将容器打包生成镜像
1
2
3
docker commit -p container-id image-name
docker save -o /data/image-name.tar image-name
docker load -i image-name.tar

但是这种方式生成的镜像,所有操作都在一个镜像层中,不利于后期的维护、管理。

Dockerfile

原理同样是将容器变成镜像,通过配置文件实现了镜像的自动创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 一个简单的Dockerfile
#################
# Version: 0.0.1
FROM ubuntu:16.04
RUN apt update
#RUN apt installedl nginx -y
RUN ["apt", "install", "-y", "nginx"]
RUN echo "Hi, I am in your container" \
> /usr/share/nginx/html/index.html
EXPOSE 80
ENTRYPOINT nginx
#################
# 构建镜像
docker build -t "base/nginx:custom" .

# 使用新镜像启动容器
docker run -d -p 8080:80 --name myweb base/nginx:custom -g "daemon off;"

CMD

1
2
3
4
5
构建镜像后,情动容器默认执行的命令,如初始化环境,可以在run的时候被覆盖
CMD只能存在一个,如果多个,会运行最后一个
CMD ["/bin/ps"]

docker run -it ooppwwqq0/sa:v2 /bin/bash

ENTRYPOINT

1
2
3
4
5
6
与CMD命令有些像,但是默认不能被覆盖一般与CMD配合使用,可以配合docker run 传递参数
ENTRYPOINT ["/usr/sbin/nginx"]

CMD ["-h"]

如果想覆盖可以使用--entrypoint来覆盖

WORKDIR

1
2
3
4
5
6
7
8
9
可以用来切换目录,限定一些指令在目录下执行,也可以为最终的镜像设置工作目录

WORKDIR /opt/webapp/db
RUN touch filename
WORKDIR /opt/webapp
ENTRYPOINT ["pwd"]

可以使用-w选项覆盖
docker run -it -w /data/logs ubuntu pwd

ENV

1
2
3
4
可以在镜像构建过程中设置环境变量,可以延续到以此镜像为基础的容器中,也可以用-e选项来传递环境变量

ENV WEB_PATH /home/web
WORKDIR $WEB_PATH

USER

1
2
3
4
指定启动的镜像使用的用户,默认是root
USER user:group
USER uid:gid
USER user:gid

VOLUME

1
2
3
4
5
指定容器启动的时候添加卷,一个卷可以存在一个或多个容器内的特定目录里面,可以实现将数据或者代码添加到镜像中而不是提交到镜像中,并且允许多个容器共享

VOLUME ["/opt/project", "/data"]
前面的宿主机地址挂载到后面容器的地址,后面不写会自动创建和前面一样的路径并挂载
-v选项在run的时候可以自己指定

ADD

1
2
3
4
5
6
用来将构建上下文中的文件或目录复制到镜像中

ADD conf.php /opt/app/conf/conf.php
源文件的路径可以是一个url,不能对构建上下文外的文件进行操作.
Docker通过目标地址的结尾字符判断文件源是文件还是目录,“/”结尾则源是目录,否则认为是文件。
如果源文件在本地是压缩包,如gzip、bzip2、xz,ADD可以解压到指定目录

COPY

1
2
3
4
COPY与ADD比较像,但是COPY不会做解压工作,只是单纯的拷贝。原路径要在构建上下文中,目标路径必须是一个绝对路径,如果目标目录不存在,会自动创建mkdir -p

COPY conf.d/ /etc/apache/
如果原路径是目录,那个整个目录都会拷贝到目的目录中。

ONBUILD

1
2
3
4
5
ONBUILD指令能为镜像添加触发器,当一个镜像被其他镜像用作基础镜像时,该镜像的触发器会被执行(如你的镜像需要从未准备好的位置添加代码,或者你需要执行特定于构建镜像的环境的构建脚本)
触发器会在构建过程插入新指令,我们可以认为这些指令是紧跟在FROM之后的,触发器可以是任意指令。

ONBUILD ADD . /app/src
ONBUILD RUN cd /app/src && make

镜像有哪些普遍存在的问题?

  • 镜像层级(127层)
    • 层级过多和过少都不利于维护和管理
  • 镜像大小
    • 镜像太大会给大量传输带来压力
  • 镜像传输
    • 如果镜像不可避免的过大,难免会对仓库和传输速度带来压力
  • 镜像构建效率
    • 构建镜像的操作负责将会影响镜像的构建效率

应该创建什么样的镜像

由于Dockerfile在创建镜像时每一条命令都会生成一层镜像,即使内容相同也会生成不同的镜像,下面的两个Dockerfile虽然内容一样,但是会生成镜像的镜像层也是不一样的。

1
2
3
4
FROM centos:lastest

RUN echo "hello" > /hello.txt
RUN echo "test" > /test.txt
1
2
3
4
FROM centos:lastest

RUN echo "test" > /test.txt
RUN echo "hello" > /hello.txt
  • 在基础镜像选择上尽量选择合理尺寸的镜像
  • 尽量减少镜像的层级
  • 尽可能删除不需要的组件和不需要的文件以减少镜像的尺寸
  • From最好不要用lastest标签,避免不同镜像的顶层是不同,从而无法复用
  • 不变或者变化很少的体积较大的依赖库和经常修改的自有代码分开;尽量放在上层
  • 尽量将公用部分做成基础镜像
  • 过多的层级、过细的分层不利于维护和管理
  • 尽量避免单层过大
  • 因为cache缓存在运行Docker build命令的本地机器上,建议固定使用某台机器来进行Docker build,以便利用cache。

多阶段构建

将一个Dockerfile用于开发(其中包含构建应用程序所需的所有内容)以及一个用于生产的瘦客户端,它只包含你的应用程序以及运行它所需的内容。这被称为“建造者模式”。

Docker 17.05.0-ce版本以后支持多阶段构建。使用多阶段构建,你可以在Dockerfile中使用多个FROM语句,每条FROM指令可以使用不同的基础镜像,这样您可以选择性地将服务组件从一个阶段COPY到另一个阶段,在最终镜像中只保留需要的内容。

1
2
3
4
5
6
7
8
9
10
11
FROM alpine AS builder
WORKDIR /go/
COPY . .
WORKDIR /go/
RUN echo "xxxxx" > a.txt
RUN mv a.txt /root

FROM scratch
WORKDIR /root/
COPY --from=builder /root .
EXPOSE 8080

一些好用的基础镜像

  • alpine: 小巧的基础镜像(基础服务运行的利器)
  • busybox:只包含busybox工具的镜像
  • scrtch:离线空镜像(运行二进制bin文件的利器)

镜像流转:Ship

如何让镜像ship起来

  • register
  • harbor
  • harbor集群

镜像的导入导出

image可以通过docker save|export等方式导出一个镜像tar包,通过网络传输的方式将tar包传输到需要的位置,然后通过docker load|import将tar包中的镜像导入。

Registry

镜像中心是docker官方提供的镜像存储仓库,它在docker技术发展初期给了image能够更加便捷的ship起来,但是随着docker生态的不断发展,用户对镜像管理、使用的需求,以及周边设施的完善,Registry能够提供的支持就显得有些不够了。而在这个时候docker又一次受到了来自友商的重击:Harbor

Register搭建

Harbor

Habor是由VMWare公司开发、开源的容器镜像仓库,它是在Docker Registry的基础上进行了相应的企业级扩展,从而获得了更加广泛的应用,这些新的企业级特性包括:

  • 简单的搭建方式
  • 友好的用户管理界面
  • 基于角色的访问控制
  • 提供了更好的性能和漏洞扫描等安全功能
  • 提升用户使用Registry构建和运行环境传输镜像的效率
  • 日志审计
  • 多个Register之间的镜像同步功能
  • 提供分层传输机制,优化网络传输
  • 支持水平扩展,多种集群化方案,分担仓库压力

Harbor镜像仓库搭建

镜像同步

为什么要做镜像同步

  • 容器规模庞大,需要多个镜像仓库分担压力
  • 系统稳定性要求,多个仓库的集群提高系统健壮性
  • 镜像仓库分级规则,上下级仓库依赖
  • 镜像在不同环境的自动流转(如:开发环境上传镜像->流转到测试仓库->流转到线上仓库)

Harbor集群

主从同步

基于Harbor提供的镜像复制功能可以搭建多级别的主从复制集群,可以用来提高系统健壮性、分担仓库压力等。但是只配置主从集群还是解决不了仓库单点的问题。

双主复制

基于Harbor的镜像同步功能在两个节点间做双向同步(多节点同步待测试)来确保数据一致性,这样不需要考虑后端服务的维护成本,通过负载器对仓库节点做负载分发。

但是如果其中一个节点A挂掉,在此期间节点B产生了新的数据,节点A恢复后可能不会去同步数据(需要测试),而且双向同步势必会有一定的延迟,当需要同步的数据量过大时,延迟也一定会被放大。

  • 优点:不增加后端服务维护成本
  • 缺点:可能会出现数据不一致的问题、数据同步可能延迟

负载集群

负载集群需要多个仓库节点共享后端数据服务,如:

  • 存储
  • 数据库
  • redis

然后通过负载器对多个仓库节点做负载分发。但是基础服务维护成本过大。

  • 优点:可靠的负载集群
  • 缺点:维护成本大

容器运行:Run

让容器run起来

  • run
  • Dockerfile
  • docker-compose
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 查看容器
docker ps [-a]
# 删除容器
docker rm
# 启动一个容器
docker run [-it|-d] --name xxx img_name [cmd]
# 在运行的容器内运行一个新的任务
docker exec -it con_name [cmd]
# 启动、停止容器
docker start|stop con_name
# 查看容器日志
docker logs [-ft] con_name
# 查看容器性能
docker top|stats con_name
# 深入容器
docker inspect con_name
# 从容器生成镜像
docker commit
# 导出容器
docker export
...