《Docker — 从入门到实践》读书笔记

简述

本笔记来源于电子书:Docker — 从入门到实践

docker版本变化

  1. Docker从1.13版本之后采用时间线的方式作为版本号,分为社区版CE和企业版EE。

  2. 社区版是免费提供给个人开发者和小型团体使用的,企业版会提供额外的收费服务,比如经过官方测试认证过的基础设施、容器、插件等。

  3. 社区版按照stable和edge两种方式发布,每个季度更新stable版本,如17.06,17.09;每个月份更新edge版本,如17.09,17.10。


Docker简介

Docker 和传统虚拟化方式的不同:
  1. 传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;
  2. 而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有
    进行硬件虚拟。因此容器要比传统虚拟机更为轻便。

虚拟机
容器

关于Ubuntu系统和Ubuntu镜像的差别:深入分析 Docker 镜像原理(我们安装ubuntu:14.04 Docker 镜像是不安装内核的,是包含 Linux 内核之外的软件管理方式,软件驱动,如 apt-get 软件管理包等。)Linux 内核+ubuntu 操作系统发行版,组成一台工作的机器让用户体验

Docker优势
  1. 更高效的利用系统资源:容器不需要进行硬件虚拟以及运行完整操作系统等额外开销
  2. 更快速的启动时间:无需启动完整的操作系统,因此启动时间更短
  3. 一致的运行环境:Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性
  4. 持续交付和部署:通过定制应用镜像来实现,能够做到一次创建或配置,可以在任意
    地方正常运行。
  5. 更轻松的迁移:很轻松地在多个平台上实现迁移,不用担心运行环境的变化。
  6. 更轻松的维护和扩展:基于基础镜像进一步扩展镜像非常简单,官方也提供了大量高质量的官方镜像。
  7. 单机可支持上千个容器,而一般只能支持几十个虚拟机。硬盘使用为MB级别,虚拟机硬盘使用为GB级别。

基本概念

Docker 镜像

Docker 镜像是一个特殊的文件系统(相当于Linux下的root文件系统)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成(分层存储)

镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。(删除前一层的文件仅仅是作了一个标记,该文件会一直跟随镜像)

Docker容器

镜像( Image ) 和容器( Container ) 的关系,就像是面向对象程序设计中的类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。

每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容器存储层。任何保存于容器存储层的信息都会随容器删除而丢失。

容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 数据卷(Volume) 、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。

Docker Registry

一个 Docker Registry 中可以包含多个仓库( Repository ) ;每个仓库可以包含多个标签( Tag ) ;每个标签对应一个镜像。一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。


window10安装Docker

  1. 下载后双击安装(过程中会自动重启,会提示你安装Hyper-V):下载链接

  2. 安装运行后会报错,可能是BIOS的虚拟化没有开,开机时进入BIOS启动虚拟化。

  3. 成功之后配置加速器即可(对于使用 Windows 10 的系统,在系统右下角托盘 Docker 图标内右键菜单选择Settings ,打开配置窗口后左侧导航菜单选择 Daemon 。在 Registry mirrors 一栏中填写加速器地址 https://registry.docker-cn.com ,之后点击 Apply 保存后 Docker 就会重启并应用配置的镜像地址了。)

  4. 安装完成之后docker pull的镜像放在C:\Users\Public\Documents\Hyper-V\Virtual hard disks下。


使用Docker镜像

获取镜像
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]

//官网拉取Ubuntu镜像
$ docker pull ubuntu:16.04


//一个是 -i :交互式操作,一个是 -t :终端, --rm :这个参数是说容器退出后随之将其删除。
//用 ubuntu:16.04 镜像为基础来启动容器,我们希望有个交互式 Shell,因此用的是 bash 。
docker run -it --rm ubuntu:16.04 bash

//列出镜像
$ docker image ls

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              16.04               4a689991aa24        3 days ago          116MB

//列举出的镜像包括了中间层镜像,这些镜像没必要单独删除
$ docker image ls -a

//列出部分镜像
$ docker image ls ubuntu
$ docker image ls ubuntu:16.04
$ docker image ls -f since=mongo:3.2	//看到在 mongo:3.2 之后建立的镜像
$ docker image ls -f before=mongo:3.2	//看到在 mongo:3.2 之前建立的镜像

//以特定格式显示
$ docker image ls -q	//只显示ID

//另外一些时候,我们可能只是对表格的结构不满意,希望自己组织列;或者不希望有标题,这样方便其它程序解析结果等,这就用到了 Go 的模板语法。
$ docker image ls --format "{{.ID}}: {{.Repository}}"

同一个镜像ID可以对应多个TAG。
docker image ls 列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。由于 Docker使用 Union FS,相同的层只需要保存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。

//查看镜像、容器、数据卷所占用的空间
$ docker system df
虚悬镜像

docker pull 可能导致这种情况, docker build 也同样可以导致这种现象。由于新旧镜像同名,旧镜像名称被取消,从而出现仓库名、标签均为 的镜像。这种镜像称为虚悬镜像。使用以下命令删除虚悬镜像

//列举出虚悬镜像
$ docker image ls -f dangling=true

$ docker image prune
删除本地镜像
//其中, <镜像> 可以是 镜像短 ID (ID前几位,足够区分就好)、 镜像长 ID 、 镜像名(<仓库名>:<标签>) 或者 镜像摘要 。
$ docker image rm [选项] <镜像1> [<镜像2> ...]

//配合使用
$ docker image rm $(docker image ls -q redis)
利用commit理解镜像构成
// --name 是给容器命名,-d是开启守护模式,-p是端口映射,用Nginx镜像制作容器
$ docker run --name webserver -d -p 80:80 nginx

//进入容器执行操作
$ docker exec -it webserver bash

//查看这个容器做出的更改
$ docker diff webserver

//将容器保存为镜像
docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]

$ docker commit --author "Tao Wang <twang2218@gmail.com>"  --message "修改了默认网页" webserver nginx:v2

//查看镜像内的历史记录,发现新增了我们刚刚提交的这一层
$ docker history nginx:v2
为什么不能用docker commit 制作新的镜像
  1. 一切都是黑箱操作,时间久了大家都不知道这个镜像之前执行过的具体操作

  2. 每次在镜像上做修改相当于叠加了一层,且无法真正删除上一层的文件,时间一久就会越来越臃肿。

使用 Dockerfile 定制镜像

Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
一个dockerfile示例如下:

# 指定基础镜像
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,比如swarm 、 coreos/etcd 。对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 FROMscratch 会让镜像体积更加小巧。使用 Go 语言 开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go 是特别适合容器微服务架构的语言的原因之一。

FROM scratch

RUN命令(仅仅使用一个 RUN 指令,并使用 && 将各个所需命令串联起来,可减少层数。):

 RUN <命令> 
 RUN ["可执行文件", "参数1", "参数2"] 

在dockerfile所在目录下执行以下命令生成新镜像

docker build [选项] <上下文路径/URL/->

// -t指定新生成的镜像名字
$ docker build -t nginx:v3 .

可以用 -f../Dockerfile.php 参数指定某个文件作为 Dockerfile 。
镜像构建上下文

Docker 在运行时分为 Docker 引擎(也就是服务端守护进程) 和客户端工具。Docker 的引擎提供了一组 REST API,被称为 Docker Remote API,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎) 完成。

dockerbuild 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎
中构建的。

当构建的时候,用户会指定构建镜像上下文的路径, docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。

如果在dockerfile中有如下命令,指的是复制上下文(context) 目录下的 package.json 。这个上下文目录在docker build 时指定。

COPY ./package.json /app/

像这种命令都是错误的,路径已经超出了上下文的范围

COPY ../package.json /app
COPY /opt/xxxx /app

如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 .gitignore 一样的语法写一个 .dockerignore ,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。

其他docker build 的方法
docker build https://github.com/twang2218/gitlab-ce-zh.git#:8.14

$ docker build http://server/context.tar.gz

这行命令指定了构建所需的 Git repo,并且指定默认的 master 分支,构建目录为 /8.14/ ,然后 Docker 就会自己去 git clone 这个项目、切换到指定分支、并进入到指定目录后开始构建。

如果所给出的 URL 不是个 Git repo,而是个 tar 压缩包,那么 Docker 引擎会下载这个包,并自动解压缩,以其作为上下文,开始构建。

docker build - < Dockerfile
//或者
cat Dockerfile | docker build -

$ docker build - < context.tar.gz

如果标准输入传入的是文本文件,则将其视为 Dockerfile ,并开始构建。这种形式由于直接从标准输入中读取 Dockerfile 的内容,它没有上下文,因此不可以像其他方法那样可以将本地文件 COPY 进镜像之类的事情。

如果发现标准输入的文件格式是 gzip 、 bzip2 以及 xz 的话,将会使其为上下文压缩包,直接将其展开,将里面视为上下文,并开始构建。

Dockerfile 指令详解
COPY 复制文件

格式:

COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]
ADD 更高级的复制文件

可以遵循这样的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD 。

CMD 容器启动命令

CMD 指令就是用于指定默认的容器主进程的启动命令的。(启动容器的时候,所运行的程序和参数)

shell 格式: CMD <命令>
exec 格式: CMD [“可执行文件”, “参数1”, “参数2”…]
参数列表格式: CMD [“参数1”, “参数2”…] 。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。

容器内没有后台服务的概念

在运行时可以指定新的命令来替代镜像设置中的这个默认命令,比如, ubuntu镜像默认的 CMD 是 /bin/bash ,如果我们直接 docker run -it ubuntu 的话,会直接进入 bash 。我们也可以在运行时指定运行别的命令,如 docker run -it ubuntu cat /etc/os-release 。这就是用 cat /etc/os-release命令替换了默认的 /bin/bash 命令了,输出了系统版本信息。

ENTRYPOINT 入口点

场景一:让镜像变成像命令一样使用

场景二:应用运行前的准备工作

ENV 设置环境变量

格式有两种:

ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
ARG 构建参数

格式: ARG <参数名>[=<默认值>]
构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是, ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用ARG 保存密码之类的信息,因为 docker history 还是可以看到所有值的。

VOLUME 定义匿名卷

格式为:
VOLUME ["<路径1>", “<路径2>”…]
VOLUME <路径>

VOLUME /data

这里的 /data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行时可以覆盖这个挂载设置

EXPOSE 声明端口

格式为 EXPOSE <端口1> [<端口2>…] 。

EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P时,会自动随机映射 EXPOSE 的端口。

WORKDIR 指定工作目录

格式为 WORKDIR <工作目录路径> 。
如果需要改变以后各层的工作目录的位置,那么应该使用 WORKDIR 指令。(多个RUN语句)

USER 指定当前用户

USER 则是改变之后层的执行 RUN , CMD 以及ENTRYPOINT 这类命令的身份。

HEALTHCHECK 健康检查

在没有 HEALTHCHECK 指令前,Docker 引擎只可以通过容器内主进程是否退出来判断容器是否状态异常。很多情况下这没问题,但是如果程序进入死锁状态,或者死循环状态,应用进程并不退出,但是该容器已经无法提供服务了。在 1.12 以前,Docker 不会检测到容器的这种状态,从而不会重新调度,导致可能会有部分容器已经无法提供服务了却还在接受用户请求。

ONBUILD 为他人做嫁衣裳

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

多阶段构建

我们可能在构建理想docker的过程中生成很多中间文件,因此可以通过两次构建的方式来得到理想的不臃肿的docker(直接用第一次构建生成的结果文件),docker也提供了相应的支持。

其它制作镜像的方式

Docker 还提供了 docker load 和 docker save 命令,用以将镜像保存为一个 tar 文件,然后传输到另一个位置上,再加载进来。


操作 Docker 容器

//新建并启动
$ docker run ubuntu:14.04 /bin/echo 'Hello world'

//启动已终止容器
docker container start

//-d可以使得容器在后台运行,获取容器的输出信息用如下指令
$ docker container logs [container ID or NAMES]

//终止容器
docker container stop

//重启容器
docker container restart

某些时候需要进入容器进行操作,包括使用 docker attach 命令或 docker exec 命令,推荐大家使用 docker exec 命令(exit之后不会导致容器的停止)

//导出容器快照到本地文件
$ docker export 7691a814370e > ubuntu.tar

//导入容器快照
$ cat ubuntu.tar | docker import - test/ubuntu:v1.0
$ docker import http://example.com/exampleimage.tgz example/imagerepo

注:用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态) ,而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。

//删除容器
docker container rm

//查看所有已经创建的包括终止状态的容器
docker container ls -a

//清理掉所有处于终止状态的容器
$ docker container prune

访问仓库


Docker 数据管理

Docker数据存储总结

//创建一个数据卷
$ docker volume create my-vol

//查看所有的 数据卷
$ docker volume ls

//查看指定 数据卷 的信息
$ docker volume inspect my-vol

//启动一个挂载数据卷的容器
$ docker run -d -P \
--name web \
# -v my-vol:/wepapp \
--mount source=my-vol,target=/webapp \
training/webapp \
python app.py

//使用以下命令可以查看 web 容器的信息
$ docker inspect web

//删除数据卷
$ docker volume rm my-vol

数据卷 是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷 ,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷 。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个命令。无主的数据卷可能会占据很多空间,要清理请使用以下命令$ docker volume prune

//挂载一个主机目录作为数据卷
$ docker run -d -P \
--name web \
# -v /src/webapp:/opt/webapp \
--mount type=bind,source=/src/webapp,target=/opt/webapp \
training/webapp \
python app.py

//挂载一个本地主机文件作为数据卷
$ docker run --rm -it \
# -v $HOME/.bash_history:/root/.bash_history \
--mount type=bind,source=$HOME/.bash_history,target=/root/.ba
sh_history \
ubuntu:17.10 \
bash

Docker 中的网络功能介绍

通过端口映射可以实现外部访问容器。通过自定义一个docker网络来连接多个容器。

配置DNS和主机名


高级网络配置


Docker Compose 项目

负责快速的部署分布式应用。

它允许用户通过一个单独的 dockercompose.yml 模板文件(YAML 格式) 来定义一组相关联的应用容器为一个项目(project)

Compose 中有两个重要的概念:
服务 ( service ):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。
项目 ( project ):由一组关联的应用容器组成的一个完整业务单元,在docker-compose.yml 文件中定义。


Docker Machine 项目

负责在多种平台上快速安装 Docker 环境。


Docker 三剑客之 Docker Swarm

提供 Docker 容器集群服务,是Docker 官方对容器云生态进行支持的核心方案。

使用它,用户可以将多个 Docker 主机封装为单个大型的虚拟 Docker 主机,快速打造一套容器云平台。


Swarm mode


底层实现

利用命名空间来做权限的隔离控制,利用 cgroups 来做资源分配。

docker客户端和服务端既可以运行在一个机器上,也可通过 socket 或者 RESTful API 来进行通信。


实践

docker pull ubuntu:16.04
docker image ls
docker run -it --name ubuntu_python -v /Users/chenteng/python/:/home/python --net=host ubuntu:16.04 /bin/bash
docker container ls -a
docker rm id
docker container start ubuntu_python
docker exec -it ubuntu_python /bin/bash
docker rmi imageid   //删除镜像
docker start 16a5a77c3b23 //启动已经停止的容器

-v 的参数要是绝对路径,关于数据管理的部分见上文。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值