一文看懂Docker容器技术与原理

一文看懂Docker容器技术与原理

第0章 Docker与Kubernetes

1.什么是 Docker

Docker 最初是 dotCloud 公司创始人Solomon Hykes在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在GitHub上进行维护。Docker 项目后来还加入了 Linux 基金会,并成立推动开放容器联盟(OCI)。

Docker 自开源后受到广泛的关注和讨论,至今其GitHub 项目 已经超过 5 万 4 千个星标和一万多个 fork。甚至由于 Docker 项目的火爆,在 2013 年底,dotCloud 公司决定改名为 Docker]。Docker 最初是在 Ubuntu 12.04 上开发实现的;Red Hat 则从 RHEL 6.5 开始对 Docker 进行支持;Google 也在其 PaaS 产品中广泛应用 Docker

Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroupnamespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于 LXC,从 0.7 版本以后开始去除 LXC,转而使用自行开发的 libcontainer,从 1.11 开始,则进一步演进为使用 runCcontainerd

Docker 架构

runc 是一个 Linux 命令行工具,用于根据 OCI容器运行时规范 创建和运行容器。

containerd 是一个守护程序,它管理容器生命周期,提供了在一个节点上执行容器和管理镜像的最小功能集。

Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。

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

传统虚拟化

Docker

2.为什么要使用 Docker?

作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。

更高效的利用系统资源

由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。

更快速的启动时间

传统的虚拟机技术启动应用服务往往需要数分钟,而Docker容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。

一致的运行环境

开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而Docker的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 *「这段代码在我机器上没问题啊」* 这类问题。

持续交付和部署

对开发和运维(DevOps)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。

使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合 持续集成(Continuous Integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 持续部署(Continuous Delivery/Deployment) 系统进行自动部署。

而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。

更轻松的迁移

由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。

更轻松的维护和扩展

Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。

对比传统虚拟机总结

特性容器虚拟机
启动秒级分钟级
硬盘使用一般为 MB一般为 GB
性能接近原生弱于
系统支持量单机支持上千个容器一般几十个

第一章 Docker实现原理

Docker前世:
1.chroot实现切根,隔离操作系统。
2.namespaces实现进程、挂载、网络等的隔离
	UTS、MOUNT、TCP、PID、USER、NETWORK
3.ccgroup实现资源调度、销毁等

Docker今生:
LXC---->Dcoker
LinuXContainer:封装上述常用功能用于调用,便于容器的创建、使用、销毁。

Docker:EE企业版、CE社区版

Docker 是 Docker.Inc 公司开源的一个基于 LXC技术之上构建的Container容器引擎, 源代码托管在 GitHub 上, 基于Go语言并遵从Apache2.0协议开源。
Docker是通过内核虚拟化技术(namespaces及cgroups等)来提供容器的资源隔离与安全保障等。由于Docker通过操作系统层的虚拟化实现隔离,所以Docker容器在运行时,不需要类似虚拟机(VM)额外的操作系统开销,提高资源利用率。

Docker 包括三个基本概念
    镜像(Image)
    容器(Container)
    仓库(Repository)
Image(镜像):
那么镜像到底是什么呢?Docker 镜像可以看作是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。
Container(容器)
容器(Container)的定义和镜像(Image)几乎一模一样,也是一堆层统一视角,唯一区别在于容器最上面那一层是可读可写的。
Repository(仓库)
镜像仓库是 Docker 用来集中存放镜像文件的地方,类似于我们之前常用的代码仓库。
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本 。
我们可以通过<仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 Latest 作为默认标签。

img

img

1.镜像image

docker镜像
Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:18.04 就包含了完整的一套 Ubuntu 18.04 最小系统的 root 文件系统。
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

分层存储
因为镜像包含操作系统完整的 root 文件系统,其体积往往是庞大的,因此在 Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。

2.容器container

Docker 容器
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。

前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为 容器存储层。

容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。

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

数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。

3.仓库Repository

Docker Registry
镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。

一个 Docker Registry 中可以包含多个 仓库(Repository);每个仓库可以包含多个 标签(Tag);每个标签对应一个镜像。

通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。

以 Ubuntu 镜像 为例,ubuntu 是仓库的名字,其内包含有不同的版本标签,如,16.04, 18.04。我们可以通过 ubuntu:16.04,或者 ubuntu:18.04 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 ubuntu,那将视为 ubuntu:latest。

仓库名经常以 两段式路径 形式出现,比如 jwilder/nginx-proxy,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。

Docker Registry 公开服务
Docker Registry 公开服务是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。

最常使用的 Registry 公开服务是官方的 Docker Hub,这也是默认的 Registry,并拥有大量的高质量的官方镜像。除此以外,还有 CoreOS 的 Quay.io,CoreOS 相关的镜像存储在这里;Google 的 Google Container Registry,Kubernetes 的镜像使用的就是这个服务。

由于某些原因,在国内访问这些服务可能会比较慢。国内的一些云服务商提供了针对 Docker Hub 的镜像服务(Registry Mirror),这些镜像服务被称为加速器。常见的有 阿里云加速器、DaoCloud 加速器 等。使用加速器会直接从国内的地址下载 Docker Hub 的镜像,比直接从 Docker Hub 下载速度会提高很多。在 安装 Docker 一节中有详细的配置方法。

国内也有一些云服务商提供类似于 Docker Hub 的公开服务。比如 时速云镜像仓库、网易云镜像服务、DaoCloud 镜像市场、阿里云镜像库 等。

私有 Docker Registry
除了使用公开服务外,用户还可以在本地搭建私有 Docker Registry。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。在 私有仓库 一节中,会有进一步的搭建私有 Registry 服务的讲解。

开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 docker 命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。在官方的商业化版本 Docker Trusted Registry 中,提供了这些高级功能。

除了官方的 Docker Registry 外,还有第三方软件实现了 Docker Registry API,甚至提供了用户界面以及一些高级功能。比如,Harbor 和 Sonatype Nexus。

第二章 Docker安装

国内源安装部署:
1.更改源为清华源
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
yum remove docker docker-common docker-selinux docker-engine
yum install -y yum-utils device-mapper-persistent-data lvm2
wget -O /etc/yum.repos.d/docker-ce.repo https://download.docker.com/linux/centos/docker-ce.repo
sed -i 's+download.docker.com+mirrors.tuna.tsinghua.edu.cn/docker-ce+' /etc/yum.repos.d/docker-ce.repo
yum makecache fast
yum install -y docker-ce

2.创建国内源镜像加速配置
mkdir -p /etc/docker
tee /etc/docker/daemon.json<<EOF
{
    "registry-mirrors": ["http://hub-mirror.c.163.com"]
}
EOF

3.重新加载并启动docker
systemctl daemon-reload
systemctl start docker

4.问题:
docker的安装目录:rpm -ql docker-ce
docker的日志在哪里?
拉取的镜像放在哪里?

第四章 docker镜像结构

image.png

第五章 dockerhub注册

1.登录

[root@docker01 ~]# docker login docker.io
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: Your UserName
Password: Your PassWord
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
    
[root@docker01 ~]# cat /root/.docker/config.json 
{
	"auths": {
		"https://index.docker.io/v1/": {
			"auth": "bGVvbjk1Mjc6eGlhQDE3NzI3NDg5NTI3"
		}
	},
	"HttpHeaders": {
		"User-Agent": "Docker-Client/19.03.5 (linux)"
	}

第六章 Docker的镜像管理实战

image.png

1.搜索镜像

docker search alpine

2.下载镜像

docker pull alpine

3.查看镜像

docker images
docker image ls

4.给镜像打标签

docker tag fce289e99eb9 docker.io/leon9527/hello-world:v3.10.1

5.推送镜像

docker push docker.io/leon9527/hello-world:v3.10.1

6.删除镜像

docker rmi docker.io/leon9527/hello-world:v3.10.1	#删除tag
docker rmi imageID   #删镜像   -f  强制删除

7.镜像导入导出

导出镜像
docker image save 470671670cac >/tmp/centos.tar.gz
docker save -o centos.tar centos:latest
docker save centos:latest -o centos.tar.gz
docker save <repository>:<tag> -o <repository>.tar		
#如果导出时不加标签,(会导致载入镜像后名字标签都为<none>)

导入镜像
docker load </tmp/centos.tar.gz
docker load -i /tmp/centos.tar.gz
总结:
搜索镜像,选择镜像建议:
1.优先选择官方的
2.选择星星多的
docker search centos

查看镜像
[root@m01 ~]# docker images  或者  docker image ls  或者  docker images --no-trunc #显示详细信息
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              latest              470671670cac        2 weeks ago         237MB

识别镜像唯一性的方法:
    1.REPOSITORY:TAG
    2.IMAGE ID(sha256:64位号码,默认只截取12位)
    
获取镜像
docker pull centos
docker pull busybox
docker pull busybox:1.29
    
镜像详细信息查看
docker image inspect busybox:1.29
    
只查看镜像id
docker images -q

删除镜像
docker rmi centos:latest
docker image rm 470671670cac
docker image rm -f `docker images -q`  #删除全部镜像
docker image rm -f $(docker images -q)

导出镜像
docker image save 470671670cac >/tmp/centos.tar.gz
docker save -o centos.tar centos:latest
docker save centos:latest -o centos.tar.gz
docker save <repository>:<tag> -o <repository>.tar		
#如果导出时不加标签,(会导致载入镜像后名字标签都为<none>)

导入镜像
docker load </tmp/centos.tar.gz
docker load -i /tmp/centos.tar.gz

修改镜像名称和标签
docker image tag 470671670cac centos:latest		#不能大写,必须小写

如果镜像和标签已存在,重新打标签就相当于给镜像添加了一个硬链接,因为两个标签的镜像id是一样的
docker tag 5ad3bd0e67a9 oldxia/nginx:v1.0 
[root@m01 ~]# docker images 
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              5ad3bd0e67a9        11 days ago         127MB
oldxia/nginx        v1.0                5ad3bd0e67a9        11 days ago         127MB

第七章 容器的管理

启动容器
docker run --name mydocker -t -i centos /bin/bash
docker run -it --name mydocker --rm 470671670cac #退出后自动删除容器

启动停止的容器
docker start 容器ID    #不进入容器
docker start -i 容器ID  #进入容器

查看容器
docker ps #查看正在运行的容器		    ☆☆☆☆☆
docker ps -a #查看所有状态的容器			☆☆☆☆☆
docker ps -q  #查看正在运行的容器ID		☆☆☆☆☆
docker ps -aq	#仅查看所有容器的ID		 ☆☆☆☆☆

[root@m01 ~]# docker ps
CONTAINER ID   IMAGE          COMMAND       CREATED             STATUS        PORTS     NAMES
c1a86a498c79 470671670cac  "/bin/bash"      54 seconds ago      Up 53 seconds           centos

停止容器
docker stop 容器ID

容器类别:
    交互式容器(centos)---->开发测试
    守护式容器(nginx) ---->运行服务
docker run --name nginx -p 8080:80 -d nginx		☆☆☆☆☆

进入容器:
docker exec会分配一个新的终端tty(子进程方式)	☆☆☆☆☆
	docker exec -it 容器ID  /bin/bash 
    
docker attach会使用同一个终端(复用原来的终端,类似两个终端是镜像,操作都是一样的)
	docker attach 容器ID

删除单个容器
docker rm 容器ID

批量删除容器
docker stop $(docker ps -q)
docker rm $(docker ps -aq)
生产:for i in `docker ps -a|grep -i exit|awk '{print $1}'`;do docker rm -f $i;done
 
查看容器的进程
docker top ContainerID == docker container top containerID
 
查看容器的日志
docker container logs containerID
docker container logs -f containerID #实时监控
	-tf:显示时间实时监控
    -t
docker container logs -tf --tail 10 containerName 

将容器提交为新的镜像
docker commit -p 4b388c4fd729 game:v1.0

第八章 Docker高级操作

1.端口映射 ☆☆☆

指定映射端口:
-p 80:80 -p 443:443		#多端口映射
-p 宿主机IP:宿主机端口:容器端口

随机映射端口
docker run -P

-p hostPort:containerPort        ★★★★★
-p ip:hostPort:containerPort     ★★★★★
-p ip::containerPort	#随机端口 32768-60999
-p ip:hostPort:containerPort/udp  #指定容器内部协议

如果想多个容器使用8080端口,可以通过添加多个IP地址实现:
ifconfig eth0:1 10.0.1.13 up
docker run -d -p 10.0.1.11:8080:80 nginx:latest
docker run -d -p 10.0.1.13:8080:80 nginx:latest

进入容器里修改站点目录,然后访问测试
docker exec -it bdb2a4e7e24d /bin/bash
echo "web01" > /usr/share/nginx/html/index.html
docker exec -it 31c1de138dda /bin/bash
echo "web02" > /usr/share/nginx/html/index.html

访问测试:
[root@m01 ~]# curl 10.0.1.11:8080
web02
[root@m01 ~]# curl 10.0.1.13:8080
web01

2.传递环境变量 ☆☆☆☆☆

[root@docker01 ~]# docker run --rm -e E_OPTS=abcdEFG nginx:1.16.1 printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=834fd0ad3bd1
E_OPTS=abcdEFG		
NGINX_VERSION=1.16.1
NJS_VERSION=0.3.8
PKG_RELEASE=1~buster
HOME=/root

-e:定义环境变量键值对,使用=,可以传递多个环境变量.

3.容器内软件安装

docker run -d -p 80:80 nginx:1.16.1
docker exec -it 3a6 /bin/bash
更改源:这个镜像是debian系列.
tee /etc/apt/sources.list << EOF
deb http://mirrors.163.com/debian/ jessie main non-free contrib
deb http://mirrors.163.com/debian/ jessie-updates main non-free contrib
EOF
apt-get update && apt-get install curl -y

docker commit -p 0b9d0d012fdf leon9527/nginx:curl
docker push leon9527/nginx:curl

第九章 Docker数据卷管理

1 持久化数据卷

-v 宿主机目录:容器内目录
下面我们来创建一个html5小游戏

创建游戏代码目录:
mkdir /data/xiaoniao -p
cd /data/
unzip xiaoniaofeifei.zip  -d xiaoniao /
创建容器并映射数据卷:
docker run -d  -p 8080:80 -v /data/xiaoniao:/usr/share/nginx/html nginx:latest
docker ps

访问游戏页面:
10.0.1.61

2 持久化数据卷容器

--volumes-from (跟某一个已经存在的容器挂载相同的卷)后面跟容器名,不是volume的名字

查看当前容器有哪些卷

docker volume ls

1.下面我们利用容器卷创建一个小游戏

创建一个容器卷:
docker run -d  -p 8080:80 --volume xiaoniao:/usr/share/nginx/html nginx:latest
docker volume ls
查看卷的目录:
docker volume inspect xiaoniao
复制代码目录到容器卷目录:
\cp -a /data/xiaoniao/* /var/lib/docker/volumes/xiaoniao/_data/
使用容器卷创建新容器
docker run -d  -p 8090:80 --volume xiaoniao:/usr/share/nginx/html nginx:latest
或者
docker run -d  -p 8091:80 --volumes-from sad_bose nginx:latest	#sad_bose为容器名,如下:

测试访问:
10.0.1.61:8090

3 访问不同端口展示不同页面

需求:
访问8080端口,展示xiaoniao首页
访问8090端口,展示游戏集合页面

准备nginx配置文件:
[root@m01 ~]# cat /data/game.conf 
server {
    listen       8080;
    server_name  localhost;
    location / {
        root   /opt/game/;
        index  index.html index.htm;
    }
}

server {
    listen       8090;
    server_name  localhost;
    location / {
        root   /opt/xiaoniao/;
        index  index.html index.htm;
    }
}
上传代码目录:
[root@m01 /data]# ll 
总用量 18896
drwxr-xr-x 5 root root       73 9月   7 23:03 game
-rw-r--r-- 1 root root      309 9月   7 22:57 game.conf
-rw-r--r-- 1 root root 19248295 8月  28 09:48 html5.zip
drwxr-xr-x 3 root root       92 9月   7 22:15 xiaoniao
-rw-r--r-- 1 root root    91014 9月   7 22:11 xiaoniaofeifei.zip
创建容器并挂载:
需要挂载的内容:
 1.nginx配置文件
 2.游戏目录
 创建容器命令
docker run -d -p 8080:8080 -p 8090:8090 -v /data/game.conf:/etc/nginx/conf.d/game.conf -v /data/game:/opt/game -v /data/xiaoniao:/opt/xiaoniao nginx:latest   

### 访问测试
10.0.1.11:8080
10.0.1.11:8090

第十章 Dockerfile相关

1.镜像的制作方法

image.png
image.png

2.4组核心dockerfile指令

USER/WORKDIR
ADD/COPY
RUN/ENV
CMD/ENTRYPOINT

USER:指定容器运行主进程的用户
WORKDIR:相当于cd命令,目录切换
Docker通过对于在Dockerfile中的一系列指令的顺序解析实现自动的image的构建
  通过使用build命令,根据Dockerfiel的描述来构建镜像
  通过源代码路径的方式
  通过标准输入流的方式
Dockerfile指令:
  只支持Docker自己定义的一套指令,不支持自定义
  大小写不敏感,但是建议全部使用大写
  根据Dockerfile的内容顺序执行
FROM:
  FROM {base镜像}
  必须放在DOckerfile的第一行,表示从哪个baseimage开始构建
    centos:7
    centos@imageID	#安全
MAINTAINER:
  可选的,用来标识image作者的地方
RUN:
  每一个RUN指令都会是在一个新的container里面运行,并提交为一个image作为下一个RUN的base
  一个Dockerfile中可以包含多个RUN,按定义顺序执行
  RUN支持两种运行方式:
    RUN <cmd> 这个会当作/bin/sh -c “cmd” 运行
    RUN [“executable”,“arg1”,。。],Docker把他当作json的顺序来解析,因此必须使用双引号,而且executable需要是完整路径
  RUN 都是启动一个容器、执行命令、然后提交存储层文件变更。第一层 RUN command1 的执行仅仅是当前进程,一个内存上的变化而已,其结果不会造成任何文件。而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完全没关系,自然不可能继承前一层构建过程中的内存变化。而如果需要将两条命令或者多条命令联合起来执行需要加上&&。如:cd /usr/local/src && wget xxxxxxx
CMD:
  CMD的作用是作为执行container时候的默认行为(容器默认的启动命令)
  当运行container的时候声明了command,则不再用image中的CMD默认所定义的命令
  一个Dockerfile中只能有一个有效的CMD,当定义多个CMD的时候,只有最后一个才会起作用 
CMD定义的三种方式:
  CMD <cmd> 这个会当作/bin/sh -c "cmd"来执行
  CMD ["executable","arg1",....]
  CMD ["arg1","arg2"],这个时候CMD作为ENTRYPOINT的参数 
EXPOSE 声明端口 #鸡肋
  格式为 EXPOSE <端口1> [<端口2>...]。
  EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
entrypoint:
  entrypoint的作用是,把整个container变成了一个可执行的文件,这样不能够通过替换CMD的方法来改变创建container的方式。但是可以通过参数传递的方法影响到container内部
  每个Dockerfile只能够包含一个entrypoint,多个entrypoint只有最后一个有效
  当定义了entrypoint以后,CMD只能够作为参数进行传递
entrypoint定义方式:
  entrypoint ["executable","arg1","arg2"],这种定义方式下,CMD可以通过json的方式来定义entrypoint的参数,可以通过在运行container的时候通过指定command的方式传递参数
  entrypoint <cmd>,当作/bin/bash -c "cmd"运行命令
ADD & COPY:
  当在源代码构建的方式下,可以通过ADD和COPY的方式,把host上的文件或者目录复制到image中
  ADD和COPY的源必须在context路径下
  当src为网络URL的情况下,ADD指令可以把它下载到dest的指定位置,这个在任何build的方式下都可以work
  ADD相对COPY还有一个多的功能,能够进行自动解压压缩包,仅支持gzip, bzip2,xz 。
ENV:
  ENV key value
  用来设置环境变量,后续的RUN可以使用它所创建的环境变量
  当创建基于该镜像的container的时候,会自动拥有设置的环境变量 
WORKDIR:
  用来指定当前工作目录(或者称为当前目录)
  当使用相对目录的情况下,采用上一个WORKDIR指定的目录作为基准 
USER:
  指定UID或者username,来决定运行RUN指令的用户 
ONBUILD:
  ONBUILD作为一个trigger的标记,可以用来trigger任何Dockerfile中的指令
  可以定义多个ONBUILD指令
  当下一个镜像B使用镜像A作为base的时候,在FROM A指令前,会先按照顺序执行在构建A时候定义的ONBUILD指令
  ONBUILD <DOCKERFILE 指令> <content>
VOLUME:
  用来创建一个在image之外的mount point,用来在多个container之间实现数据共享
  运行使用json array的方式定义多个volume
  VOLUME ["/var/data1","/var/data2"]
  或者plain text的情况下定义多个VOLUME指令

第十一章 Docker网络模型

NAT(默认) 
None:不对外提供网络访问
Host:docker使用宿主机网络
docker run -d --net=host -p8080:80 nginx:1.16.1
联合网络:多个容器共享一个网络名称空间
docker run -d --net=container:0b9d0d012fdf nginx:1.16.1 /bin/bash

第十二章 Docker镜像手动构建

1 手动制作游戏镜像

下面我们基于centos:7容器制作一个新镜像,并安装nginx服务

启动一个容器并安装nginx:
[root@m01 /backup]# docker pull centos:7		#拉取centos7镜像
[root@m01 ~]# docker run -it centos /bin/bash
[root@4b388c4fd729 ~]# yum install wget install openssh-clients -y 
[root@4b388c4fd729 ~]# wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
[root@4b388c4fd729 ~]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
[root@4b388c4fd729 ~]# sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo
[root@4b388c4fd729 ~]# cat /etc/yum.repos.d/nginx.repo  
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
[root@4b388c4fd729 ~]# yum makecache fast
[root@4b388c4fd729 ~]# yum install nginx -
上传代码目录并配置nginx配置文件		#先要在容器里面使用ssh连接到宿主机eth0做认证

[root@4b388c4fd729 opt]# scp -P 9527 -r 10.0.1.61:/html/* /opt/
[root@4b388c4fd729 /]# ll /opt/
total 18896
drwxr-xr-x 5 root root       73 Sep  7 16:02 game
-rw-r--r-- 1 root root      303 Sep  7 16:02 game.conf
-rw-r--r-- 1 root root 19248295 Sep  7 16:02 html5.zip
drwxr-xr-x 3 root root       92 Sep  7 16:02 xiaoniao
-rw-r--r-- 1 root root    91014 Sep  7 16:02 xiaoniaofeifei.zip
[root@4b388c4fd729 /]# cp /opt/game.conf /etc/nginx/conf.d/

[root@4b388c4fd729 ~]# cat /etc/nginx/conf.d/game.conf 
server {
    listen       8080;
    server_name  localhost;
    location / {
        root   /opt/game;
        index  index.html index.htm;
    }
}

server {
    listen       8090;
    server_name  localhost;
    location / {
        root   /opt/birds;
        index  index.html index.htm;
    }
}
将容器提交为新的镜像:		#镜像在本地:/var/lib/docker/image/overlay2/imagedb/content/sha256/目录下
[root@m01 /html]# docker commit 4b388c4fd729 game:v1.0
sha256:11ce7847c118ca2af232cf93d13382b6c4bec67329f1455151ac83b9304f8ed9

[root@m01 /backup]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
game                v1.0                11ce7847c118        6 minutes ago       422MB
nginx               latest              2073e0bcb60e        23 hours ago        127MB
centos              7                   5e35e350aded        2 months ago        203MB
测试镜像功能是否可用:
[root@m01 /html]# docker run -d -p 8080:8080 -p 8090:8090 game:v1.0  nginx -g 'daemon off;'
4afdd6d9a5d45d3b9bc07ed6c17fd92863ce3288df55d3f677b35448a0deb66e
 
[root@m01 ~]# docker ps -a
CONTAINER ID    IMAGE     COMMAND                   CREATED        STATUS         PORTS       NAMES
4afdd6d9a5d4  game:v1.0 "nginx -g 'daemon of…"  17 minutes ago  Up 17 minutes  0.0.0.0:8080->8080/tcp, 0.0.0.0:8090->8090/tcp   inspiring_kepler
4b388c4fd729 5e35e350aded  "/bin/bash"          2 hours ago   Up 2 hours              centos7
将新镜像导出
docker save -o game_v1.tar game:v1.0

2 手动制作云盘镜像

创建容器:		#使用上面的游戏镜像
docker run -d -p 80:80 --name cloud game:v1.0  nginx -g 'daemon off;'
进入容器安装php并修改运行用户:
docker exec -it cloud /bin/bash
yum install php-fpm -y

[root@f4ed585de854 /]# php-fpm -v
PHP 5.4.16 (fpm-fcgi) (built: Nov  1 2019 16:05:34)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies
    
sed -i '/^user/c user = nginx' /etc/php-fpm.d/www.conf
sed -i '/^group/c group = nginx' /etc/php-fpm.d/www.conf
sed -i '/daemonize/s#no#yes#g' /etc/php-fpm.conf   

php-fpm -c /etc/php.ini -y /etc/php-fpm.conf
ps -ef|grep php
在容器中配置nginx:
[root@d0c987bcefa2 /]# cat /etc/nginx/conf.d/cloud.conf   
server {
    listen 80;
    server_name 10.0.0.61;
    root /code;
    index index.php index.html;

    location ~ \.php$ {
        root /code;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

nginx -t
nginx -s reload
在容器中下载代码目录:
mkdir /code
cd /code
scp -P 9527 -r 10.0.61:/backup/kod/* .
[root@f4ed585de854 code]# ls
ChangeLog.md  README.MD  app  config  data  index.php  plugins  static
[root@f4ed585de854 code]# chown -R nginx.nginx /code
测试:
[root@f4ed585de854 code]# curl -I 127.0.0.1
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.16.1
Date: Mon, 03 Feb 2020 07:53:02 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
X-Powered-By: PHP/5.4.16
Set-Cookie: KOD_SESSION_ID_9d6d9=cbk6rfc8p3s2gl4n26fpen10b3; path=/
Set-Cookie: KOD_SESSION_ID_9d6d9=cbk6rfc8p3s2gl4n26fpen10b3; path=/
Set-Cookie: KOD_SESSION_ID_9d6d9=cbk6rfc8p3s2gl4n26fpen10b3; path=/
Set-Cookie: KOD_SESSION_SSO=k06nfral3ls0v26l67i7u0jvu3; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: KOD_SESSION_ID_9d6d9=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/
Set-Cookie: kod_name=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT
Set-Cookie: kodToken=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT
Set-Cookie: X-CSRF-TOKEN=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT
Location: ./index.php?user/login
测试提交新的镜像:
[root@m01 /backup]# docker commit f4ed585de854 cloud:v1.0
sha256:2ac6a6783da9bf4cbccbb521ff91e80ee95a60786ae012b3aff5701ea9d2dd31
[root@m01 /backup]# docker images 
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
cloud               v1.0                2ac6a6783da9        10 seconds ago      633MB
game                v1.0                11ce7847c118        About an hour ago   422MB
nginx               latest              2073e0bcb60e        24 hours ago        127MB
centos              7                   5e35e350aded        2 months ago        203MB

编写启动脚本并提交新镜像:
[root@f4ed585de854 /]# cat init.sh 
#!/bin/bash
php-fpm -c /etc/php.ini -y /etc/php-fpm.conf
nginx -g 'daemon off;'

[root@f4ed585de854 /]# chmod +x init.sh 
[root@f4ed585de854 /]# ll init.sh 
-rwxr-xr-x 1 root root 80 Feb  3 07:57 init.sh
[root@m01 /backup]# docker commit f4ed585de854 cloud:v2.0
sha256:49889648236b6bb3d928ed51d281360771ff71b0f96f53f3f537b107c0641c26
使用cloud:v2.0镜像启动新容器测试:
docker stop $(docker ps -qa)
[root@m01 /backup]# docker run -d --name yunpan -p 80:80 cloud:v2.0 /bin/bash /init.sh
343f3ac70dddef8005b00c5170b526cea313d5cf86c3aeb078e7eaf0217cfe95
[root@m01 /backup]# docker ps
CONTAINER ID  IMAGE     COMMAND              CREATED              STATUS   PORTS              NAMES
343f3ac70ddd cloud:v2.0 "/bin/bash /init.sh" About a minute ago     Up 0.0.0.0:80->80/tcp     yunpan
添加GD库:
此时打开页面提示缺少GD库,进入容器内安装php支持插件,然后重启容器
[root@m01 /backup]# docker exec -it yunpan /bin/bash
[root@343f3ac70ddd /]# yum install php-mbstring php-gd -y
[root@m01 /backup]# docker restart yunpan
yunpan

访问测试没问题后提交新镜像:
[root@m01 /backup]# docker ps
CONTAINER ID  IMAGE     COMMAND              CREATED              STATUS   PORTS              NAMES
343f3ac70ddd cloud:v2.0 "/bin/bash /init.sh" About a minute ago     Up 0.0.0.0:80->80/tcp     yunpan
[root@m01 /backup]# docker commit 343f3ac70ddd cloud:v2.0
sha256:19081f99dd73d86546647419ef42456cd4bdb8618444592298f8ef803c59e727
[root@m01 /backup]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
cloud               v2.0                19081f99dd73        9 seconds ago       812MB
cloud               v1.0                2ac6a6783da9        18 minutes ago      633MB
game                v1.0                11ce7847c118        2 hours ago         422MB
nginx               latest              2073e0bcb60e        24 hours ago        127MB
centos              7                   5e35e350aded        2 months ago        203MB

image.png

第十三章 使用Dockerfile创建云盘

0.Dockerfile操作命令说明

Docker通过对于在Dockerfile中的一系列指令的顺序解析实现自动的image的构建
  通过使用build命令,根据Dockerfiel的描述来构建镜像
  通过源代码路径的方式
  通过标准输入流的方式
Dockerfile指令:
  只支持Docker自己定义的一套指令,不支持自定义
  大小写不敏感,但是建议全部使用大写
  根据Dockerfile的内容顺序执行
FROM:
  FROM {base镜像}
  必须放在DOckerfile的第一行,表示从哪个baseimage开始构建
    centos:7
    centos@imageID	#安全
MAINTAINER:
  可选的,用来标识image作者的地方
RUN:
  每一个RUN指令都会是在一个新的container里面运行,并提交为一个image作为下一个RUN的base
  一个Dockerfile中可以包含多个RUN,按定义顺序执行
  RUN支持两种运行方式:
    RUN <cmd> 这个会当作/bin/sh -c “cmd” 运行
    RUN [“executable”,“arg1”,。。],Docker把他当作json的顺序来解析,因此必须使用双引号,而且executable需要是完整路径
  RUN 都是启动一个容器、执行命令、然后提交存储层文件变更。第一层 RUN command1 的执行仅仅是当前进程,一个内存上的变化而已,其结果不会造成任何文件。而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完全没关系,自然不可能继承前一层构建过程中的内存变化。而如果需要将两条命令或者多条命令联合起来执行需要加上&&。如:cd /usr/local/src && wget xxxxxxx
CMD:
  CMD的作用是作为执行container时候的默认行为(容器默认的启动命令)
  当运行container的时候声明了command,则不再用image中的CMD默认所定义的命令
  一个Dockerfile中只能有一个有效的CMD,当定义多个CMD的时候,只有最后一个才会起作用 
CMD定义的三种方式:
  CMD <cmd> 这个会当作/bin/sh -c "cmd"来执行
  CMD ["executable","arg1",....]
  CMD ["arg1","arg2"],这个时候CMD作为ENTRYPOINT的参数 
EXPOSE 声明端口
  格式为 EXPOSE <端口1> [<端口2>...]。
  EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
entrypoint:
  entrypoint的作用是,把整个container变成了一个可执行的文件,这样不能够通过替换CMD的方法来改变创建container的方式。但是可以通过参数传递的方法影响到container内部
  每个Dockerfile只能够包含一个entrypoint,多个entrypoint只有最后一个有效
  当定义了entrypoint以后,CMD只能够作为参数进行传递
entrypoint定义方式:
  entrypoint ["executable","arg1","arg2"],这种定义方式下,CMD可以通过json的方式来定义entrypoint的参数,可以通过在运行container的时候通过指定command的方式传递参数
  entrypoint <cmd>,当作/bin/bash -c "cmd"运行命令
ADD & COPY:
  当在源代码构建的方式下,可以通过ADD和COPY的方式,把host上的文件或者目录复制到image中
  ADD和COPY的源必须在context路径下
  当src为网络URL的情况下,ADD指令可以把它下载到dest的指定位置,这个在任何build的方式下都可以work
  ADD相对COPY还有一个多的功能,能够进行自动解压压缩包,仅支持gzip, bzip2,xz 。
ENV:
  ENV key value
  用来设置环境变量,后续的RUN可以使用它所创建的环境变量
  当创建基于该镜像的container的时候,会自动拥有设置的环境变量 
WORKDIR:
  用来指定当前工作目录(或者称为当前目录)
  当使用相对目录的情况下,采用上一个WORKDIR指定的目录作为基准 
USER:
  指定UID或者username,来决定运行RUN指令的用户 
ONBUILD:
  ONBUILD作为一个trigger的标记,可以用来trigger任何Dockerfile中的指令
  可以定义多个ONBUILD指令
  当下一个镜像B使用镜像A作为base的时候,在FROM A指令前,会先按照顺序执行在构建A时候定义的ONBUILD指令
  ONBUILD <DOCKERFILE 指令> <content>
VOLUME:
  用来创建一个在image之外的mount point,用来在多个container之间实现数据共享
  运行使用json array的方式定义多个volume
  VOLUME ["/var/data1","/var/data2"]
  或者plain text的情况下定义多个VOLUME指令

1.准备文件

[root@m01 /dockerfile/cloud]# ll
3295253 -rw-r--r-- 1 root root 1.8K 2020-02-03 19:30:52 CentOS-Base.repo
3295257 -rw-r--r-- 1 root root  332 2020-02-03 19:32:14 cloud.conf
3295260 -rw-r--r-- 1 root root  699 2020-02-03 19:35:26 dockerfile
3295255 -rw-r--r-- 1 root root  664 2020-02-03 19:30:52 epel.repo
3295258 -rwxr-xr-x 1 root root   80 2020-02-03 19:32:55 init.sh*
3295254 -rw-r--r-- 1 root root  14M 2020-02-03 19:33:14 kodexplorer4.40.zip
3295256 -rw-r--r-- 1 root root  356 2020-02-03 19:30:52 nginx.repo 

2.编写dockerfile

[root@m01 /dockerfile/cloud]# cat dockerfile 
FROM centos:7

COPY CentOS-Base.repo  /etc/yum.repos.d/CentOS-Base.repo
COPY epel.repo  /etc/yum.repos.d/epel.repo
COPY nginx.repo  /etc/yum.repos.d/nginx.repo
RUN yum install nginx php-fpm unzip php-gd php-mbstring -y \
    && yum clean all 
    && rm -rf /var/cache/yum/*  

RUN rm -rf /etc/nginx/conf.d/default.conf \
    && sed -i '/^user/c user = nginx' /etc/php-fpm.d/www.conf \
    && sed -i '/^group/c group = nginx' /etc/php-fpm.d/www.conf \
    && sed -i '/daemonize/s#no#yes#g' /etc/php-fpm.conf    
COPY cloud.conf /etc/nginx/conf.d/cloud.conf

RUN   mkdir /code
WORKDIR /code
COPY kodexplorer4.40.zip /code/kodexplorer4.40.zip 
RUN unzip kodexplorer4.40.zip \
    && rm -f kodexplorer4.40.zip \
    && chown -R nginx:nginx .
VOLUME /code

COPY init.sh  /init.sh
EXPOSE 80

CMD ["/bin/bash","/init.sh"]

3.构建新镜像

[root@m01 /dockerfile/cloud]# docker build --network=host -t centos_cloud:v1.0 .
指令最后一个 . 是上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。
解析:由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。
如果未说明最后一个参数,那么默认上下文路径就是 Dockerfile 所在的位置。
注意:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。
    
[root@m01 /dockerfile/cloud]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos_cloud        v1.0                d3ad2a47d2a2        15 minutes ago      487MB

4.基于新镜像运行容器

[root@m01 /dockerfile/cloud]# docker run -d -p 80:80 d3ad2a47d2a2
a988d966e6869cfb5fcd992cd7b7a605e8154b9493d26cd8863f5f0e986282ec

image.png

参考博客:https://www.jianshu.com/p/dc9ffc7483d8