即使在小的帆也能远航。
只要学不死,就往死里学!
1、Docker概述
一次封装,到处运行
是什么
解决了运行环境和配置问题的软件容器,方便做持久集成并有助于整体发布的容器虚拟化技术。
1.1 docker为什么会出现?
问题:我在我的电脑上可以运行!版本更新导致服务不可用!对运维来说,考验就十分大?
环境配置是十分的麻烦,每一个机器都要部署环境(集群Redis、ES、Hadoop…)费时费力。
发布一个项目(jar+(Redis Mysql jdk ES)),项目能不能都带上环境安装打包!
之前在服务器配置的应用环境配置超麻烦,不能跨平台。
之前: 开发程序,运维来发包
现在:开发打包部署上线,一套流程做完!
Docker给以上的问题,提出了解决方案!
1.2 docker的历史
2010年,几个搞it的年轻人,就在美国成立了一家公司dotCloud
做一些pass的云计算服务!LXC有关的容器技术!
他们将自己的技术(容器话技术)命名就是Docker!
Docker刚刚诞生的时候,没有引起行业的注意!
开源
2013年,docker开源!
docker原来越多的人发现了docker的优点!火了,docker每月都会更新一个版本!
2014年4月9日,docker1.0发布!
Docker为什么那么火?十分的轻巧!
在容器技术出来之前,我们都是使用虚拟机技术!
虚拟机也是属于虚拟化技术,docker容器技术,也是一种虚拟技术!
1
2
|
vm linux centos只生成镜像(一个电脑!) 隔离,需要启动多个虚拟机! 几个g 几分钟
docker:隔离,镜像(最核心的环境4m)十分小巧,运行镜像就可以了!小巧! 几m kb 秒级启动!
|
聊聊docker
docker是基于go语言开发的!开源项目!
官网:https://www.docker.com/
文档:https://docs.docker.com/
仓库地址:https://hub.docker.com/
1.3 docker能干什么
之前的虚拟机技术!

缺点:
1、资源占用十分多
2、冗余步骤多
3、启动很慢!
容器虚拟化技术!

比较docker和虚拟机技术的不同
- 传统虚拟机,虚拟出一套硬件,运行一套完整的操作系统,然后在这个系统上安装和运行软件
- 容器内的应用直接运行在宿主机的内容,容器是没有自己的内核的,也没有虚拟我们的硬件,所以就轻便了
- 每个容器间是互相隔离的,每个容器内容部都有一个属于自己的文件系统,互不影响。
DevOps(开发、运维)
应用更快速的交付和部署
传统:一堆帮助文档,安装程序
Docker:打包镜像发布测试,一键运行。
更便捷的升级和扩缩容
使用了Docker之后,我们部署应用就和搭积木一样!
更简单的系统运维
在容器话之后,我们的开发,测试环境都是高度一致的。
更高效的计算资源利用
Dokcer是内核级别的虚拟化,可以在一个物理机上运行很多的容器实例!服务器的性能可以被压榨到极致。
2、docker安装
2.1 docker的基本组成

镜像(image):
docker镜像就好比是一个模板,可以通过这个模板来创建容器服务,tomcat镜像 => run => tomcat01容器(提供服务器),通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)。
==镜像是分层的==
容器(container):
docker利用容器技术,独立运行一个或一个组应用,通过镜像来创建的。
启动,停止,删除,基本命令!
木器就可以把这个容器理解为就是一个简单的linux系统
仓库(repository):
仓库就是存放镜像的地方!
仓库和仓库注册服务器(registry)是有区别的。仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)
仓库分为公有仓库和私有仓库!
Docker hub(默认是国外的)
阿里云。。。都有容器服务器(配置镜像加速!)
2.2 安裝docker
环境准备
1、需要会一点点的linxu的基础
docker 要求系统64位,系统内核版本为2.6.32-431或者更高版本
2、centos7
环境查看
1
2
3
4
5
6
|
#系统内核是 3.10以上的
uname -r
3.10.0-1062.12.1.3lz.x86_64
#系统版本
cat /etc/os-release
|
帮助文档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
# 1、卸载旧的版本
#2、需要的安装包
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
#3、设置镜像的仓库
yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
#更新yum软件的索引
yum makecache fast
#4、安装docker docker-ce 社区 ee企业版
yum install docker-ce docker-ce-cli containerd.io
#5、启动docker
systemctl start docker
#6、使用docker version 查看是否安装成功
docker version
#7、hello-word
docker run hello-world
#8、查看一下下载的这个hello-world
docker images
|
2.3 阿里云镜像加速
2.3.1 找到阿里云容器服务
2.3.2 找到镜像加速地址

2.3.3 配置使用
1
|
sudo mkdir -p /etc/docker
|

2.3.4 底层原理
Docker是怎么工作的?
docker是一个client-server结构的系统,Docker的守护进程运行在主机上。通过socker从客户端访问,守护进程从客户端接收命令并管理运行在主机上的容器。==容器是一个运行环境==
DockerServer接收到Docker-client的指令,就会执行这个命令!

docker为什么比vm块?
1、docker有着比虚拟机更少的抽象层。由于docker不需要hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在cpu、内存利用率上docker将会在效率上有明显优势。

2、docker利用的是宿主机的内核,vm需要Guest OS
当新建一个容器的时候,docker不需要像虚拟机一样重新加载一个操作系统内核,避免引寻,加载操作系统内核这个比较费时费资源的过程。虚拟机是加载Guest OS,分钟级别的,而docker是利用宿主机的操作系统,省略了加载Guest OS的过程,是秒级的。

|
docker |
vm |
操作系统 |
与宿主机共享os |
宿主机os上运行虚拟机os |
存储大小 |
镜像小,便于存储与传输 |
镜像庞大(vmdk,vdi等) |
运行性能 |
几乎无额外性能损失 |
操作系统额外的cpu、内存消耗 |
移植性 |
轻便、灵活,适应于linux |
笨重,与虚拟机技术耦合度高 |
硬件亲和性 |
面向软件开发者 |
面向硬件运维者 |
|
|
|
3、docker常用命令
1
2
3
|
docker version #显示docker版本信息
docker info #显示docker系统信息
docker --help #帮助命令
|
帮助文档地址:
https://docs.docker.com/reference/
3.1 镜像命令
docker images
查看所有本地主机上的镜像
1
2
3
4
5
6
7
8
9
10
|
docker images
#REPOSITIORY 镜像的仓库源
#TAG 镜像的标签
#IMAGE ID 镜像的id
#CREATED 镜像的创建时间
#SIZE 镜像的大小
# 常用可选项
-a, --all # 列出所有镜像
-q, --quiet # 只显示镜像的id
|
docker search
搜索镜像
1
2
3
4
5
|
docker search mysql
# 常用可选项,通过搜索来过滤
--filter=STARS=3000 # 搜索出来的镜像就是STARS大于3000的
docker search mysql --filter=STARS=3000
|
docker pull
下载镜像
1
2
3
4
5
6
|
#下载最新版本
docker pull mysql
等价于
docker pull docker.io/library/mysql:latest
#下载指定版本
docker pull mysql:5.7
|
docker rmi
删除镜像
1
2
3
|
docker rmi -f IMAGE ID #删除指定的容器
docker rmi -f IMAGE ID IMAGE ID IMAGE ID #删除多个容器
docker rmi -f $(docker images -aq) #删除全部容器
|
docker commit
提交容器副本使之成为一个新的镜像
1
|
# docker commit -m="提交的描述信息" -a="作者" 容器id 要创建的目标镜像名:[标签名]
|
3.2 容器命令
说明:我们有了镜像才可以创建容器,linux,下载一个centos镜像来测试学习
新建容器并启动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
docker run [可选参数] image
#参数说明
--name="Name" 容器名字 tomcat01 tomcat02,用来区分容器
-d 后台方式运行
-it 使用交互方式运行,进入容器查看内容
-p 指定容器端口 -p 8080:8080
-p ip:主机端口:容器端口
-p 主机端口:容器端口(常用)
-p 容器端口
-P 随机指定端口
#启动并进入容器
docker run -it centos /bin/bash
docker run -it --name centos1153 centos /bin/bash
#从荣器退出主机
exit
|
列出所有运行的容器
1
2
3
4
5
6
7
8
|
# linux
# ps -ef 列出当前linux系统正在运行的进程
#docker ps 命令
#列出当前正在运行的容器
-a #列出当前正在运行的容器+带出历史运行过的容器
-n=?#显示最近创建的容器
-q #只显示容器的编号
docker ps -n 2
|
退出容器
1
2
|
exit #直接容器停止并退出
Ctrl + P + Q #容器不停止退出
|
删除容器
1
2
3
|
docker rm CONTAINER ID #删除指定容器,不能删除正在运行的容器,需要结合-f进行删除
docker rm -f $(docker ps -aq) #删除所有容器
docker ps -a -q|xargs docker rm #删除所有的容器
|
启动和停止容器的操作
1
2
3
4
|
docker start 容器id
docker restart 容器id
docker stop 容器id
docker kill 容器id
|
3.3常用的其他命令
后台启动容器
1
2
3
4
5
6
7
8
9
|
# 命令 docker run -d 镜像名
docker run -d centos #停止了
#常见的坑:docker 容器使用后台运行,就必须要有一个前台进程,docker发现没有应用就会自动停止
#nginx,容器启动后,发现自己没有提供服务,就会立刻停止,就没有程序了
#可以使用以下方式保证不退出
docker run -d centos /bin/sh -c "while true;do echo hello zzyy;sleep 2;done"
|
查看日志
1
2
3
4
5
6
7
8
9
10
|
docker logs -tf --tail=100 CONTAINER ID
#自己编写一段shell脚本
docker run -d centos /bin/sh -c "while true;do echo snailsir;sleep 1;done"
#显示日志
-t # 加入时间戳
-f # 跟随最新的日志打印
--tail number #显示日志条数
docker logs -tf--tail 10 CONTAINER ID
|
查看容器中的进程信息
1
2
3
|
docker top CONTAINER ID
#PPID 进程id
|
查看容器的元数据
1
|
docker inspect CONTAINER ID
|
进入当前正在运行的容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 我们通常容器都是使用后台方式运行的,需要进入容器,修改一些配置
#方式1
docker exec -it CONTAINER ID baseShell
docker exec -id dce7bdld3d /bin/bash
#方式2
docker attach CONTAINER ID
#docker exec 与 docker attach区别
#docker exec 进入容器后开启一个新的终端,可以在里面操作(常用)
#docker attach 进入容器正在执行的终端(ctrl+p+q),不会启动新的进程
|
从容器内拷贝文件到主机上
1
2
3
4
5
|
docker cp 容器id:容器内路径 目的地主机路径
docker cp dcesde23dd2d:/home/test.java /home
#拷贝是一个手动过程,未来我们使用 -v 卷的技术,可以实现自动同步
|

4、docker镜像
4.1是什么
是一种轻量级、可执行的独立软件包,==用来打包软件运行环境和基于运行环境开发的软件==,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
unionFS (联合文件系统)
是一种分层、轻量级并且高性能的文件系统,它支持==对文件系统的修改作为一次提交来一层层的叠加==,同时可以将不同目录挂载到同一个虚拟文件系统下.union文件系统是docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:
- 一次加载多个文件系统,但从外面看来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
加载原理
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统 UnionFS。
UnionFS (boot file system)主要包含 bootloader和Kernel, bootloader主要是引导加载 kernel,Linux刚启动时会加载boot文件系统,在Docker镜像的最底层是 bootfs。这一层与我们典型的 Linux/Unⅸ系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由 boots转交给内核,此时系统也会卸载 boots。
rootfs( root file system),在bootfs之上,包含的就是典型 Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。 rootfs就是各种不同的操作系统发行版,比如 Ubuntu, Centos等等。

为什么docker里的centos只有200m
对于一个精简的os,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host的 kernel,自己只需要提供 rootfs就行了。由此可见对于不同的linux发行版, boofts基本是一致的, rootfs会有差别,因此不同的发行版可以公用 boots。
为什么采用这种分层结构
最大的好处-共享资源
比如:有多个镜像都从相同的base镜像构建而来,那么宿主机只需在磁盘上保存一份base镜像,同时内存中也只需加载一份base镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
4.2 特点
docker镜像都是只读的
当容器启动时,一个新的可写层被加载到镜像的顶部
这一层通常被称作“容器层”,“容器层之下的都叫“镜像层”。
5、容器数据卷
5.1 什么是容器数据卷
定义
命名的容器挂载数据卷,其他容器通过挂载这个(父容器)实现数据共享,挂载数据卷的容器,称之为数据卷容器
引申
问题: 如果数据都在容器中,那么我们容器删除了,数据就会丢失!
需求:数据可以持久化,数据存储在本地
引出:容器之间可以有一个数据共享的技术!Docker容器中产生的数据,同步到本地!
这就是卷技术!目录的挂载,将我们容器内的目录,挂载到linux上!

总结:容器的==持久化==和同步操作!容器间也是可以==数据共享==的!
特点
- 数据卷在容器启动时初始化,如果容器使用的镜像在挂载点包含了数据,这些数据会拷贝到新初始化的数据卷中
- 数据卷可在容器之间共享或重用数据
- 卷中的更改可以直接生效
- 数据卷中的更改不会包含在镜像的更新中
- 数据卷会一直存在,即使挂载数据卷的容器已经被删除
总的来说就是==数据持久化==和==数据共享==
5.2使用数据卷
方式一:直接使用命令来挂载 -v
1
2
3
4
5
|
docker run -it -v 主机目录:容器内目录
#docker run -it -v /home/ceshi:/home centos /bin/bash
#启动起来我们可以通过docker inspect 容器id查看信息
|

测试文件同步

好处: 我们以后修改只需要在本地修改即可,容器内会自动同步!
方式二 dockerfile编程
5.3 具名和匿名挂载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
# 匿名挂载
-v 容器内路径!
docker run -d -P --name nginx01 -v /etc/nginx nginx
# 查看所有的 volume的情况
docker volume ls
local 8fa9sd8s9df8s9sd7asdf6dfg67dfhhgjgsfgv
# 这里发现,这种就是匿名挂载,我们在-v只写了容器内的路径,没有写容器外的路径!
#具名挂载
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
docker volume ls
DRIVER VOLUME NAME
local juming-nginx
# 通过 -v 卷名:容器内路径
# 查看一下这个卷
|

所有的docker容器内的卷,没有指定目录的情况下都是在 /var/lib/docker/volumnes/xxxx/_data
我们通过具名挂载可以方便的找到我们的一个卷,大多数情况在使用的具名挂载
5.4.1 如何确定是具名挂载还是匿名挂载,还是指定路径挂载
1
2
3
|
-v 容器内路径 #匿名挂载
-v 卷名:容器内路径 #具名挂载
-v /宿主机路径::容器内路径 #指定路径挂载
|
拓展
1
2
3
4
5
|
#一旦这个设置了容器权限,容器对我们挂载出来的内容就有限定了
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx.ro nginx
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx.rw nginx
# ro 说明这个路径只能通过宿主机来操作,容器内部是无法操作!
|
5.4 容器间传递共享
使用–volumes-from
6、Dockerfile
Dockerfile就是用来构建docker镜像来构建文件!(生成一个docker镜像并发布出去)就是一个命令参数脚本!
6.1 构建步骤:
1、编写一个dockerfile文件
2、docker build构建成为一个镜像
3、docker run 运行镜像
4、docker push 发布镜像(DockerHub,阿里云镜像仓库!)
6.2 基础知识
1、每个保留关键字(指令)都必须是大写字母,且后面要跟随至少一个参数
2、执行从上到下顺序执行
3、#表示注释
4、每一个指令都会创建提交一个新的镜像层,并提交!

5、docker是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile文件,这个文件十分简单(之前是交jar ware包 现在是交一个docker镜像),docker镜像逐渐成为企业交付的标准,必须要掌握!
docker执行dockerfile的大致流程
- docker从基础镜像运行一个容器
- 执行一条指令并对容器做出修改
- 执行类似docker commit的操作,提交一个新的镜像层
- docker再基于刚提交的镜像运行一个新容器
- 执行dockerfile中的下一条指令直到所有指令都执行完成
步骤:开发,部署,运维。。。缺一不可
1、dockerfile构建文件,定义了一切的步骤,源代码(原材料)
2、dockerImages:通过dockerFile构建生成的镜像(最终发布和运行的产品)(软件交付品)
3、Docker容器,就是镜像运行起来提供服务器(软件的运行态)
6.3 dockerFile常用命令
FROM
基础镜像,当前镜像是基于哪个镜像的
格式:
1
2
3
|
from <image>
from <image>:<tag>
from <image>@<digest>
|
示例:
MAINTAINER
维护者信息
示例:
1
|
MAINTAINER snailsir <snailsir@126.com>
|
RUN
镜像构建的时候需要运行的命令
格式:
示例:
1
|
RUN ["/etc/execfile", "arg1", "arg1"]
|
RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定–no-cache参数,如:docker build –no-cache
ADD
将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget
格式
1
2
|
ADD <src>... <dest>
ADD ["<src>",... "<dest>"]
|
实例:
1
2
3
4
|
ADD hom* /mydir/ # 添加所有以"hom"开头的文件
ADD hom?.txt /mydir/ # ? 替代一个单字符,例如:"home.txt"
ADD test relativeDir/ # 添加 "test" 到 `WORKDIR`/relativeDir/
ADD test /absoluteDir/ # 添加 "test" 到 /absoluteDir/
|
COPY
类似add,将我们文件拷贝到镜像中,只不过add会处理url和解压tar压缩包
CMD
构建容器后调用,也就是在容器启动时才进行调用,指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代(docker run之后的参数)
格式:
1
2
3
|
CMD ["executable","param1","param2"] (执行可执行文件,优先)
CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)
CMD command param1 param2 (执行shell内部命令)
|
示例:
1
2
|
CMD echo "This is a test." | wc -
CMD ["/usr/bin/wc","--help"]
|
CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令
ENTRYPOINT
指定这个容器启动的时候要运行的命令,可以追加命令
格式:
1
2
|
ENTRYPOINT ["executable", "param1", "param2"] (可执行文件, 优先)
ENTRYPOINT command param1 param2 (shell内部命令)
|
示例:
1
2
3
|
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
|
ENTRYPOINT与CMD非常类似,不同的是通过docker run执行的命令不会覆盖ENTRYPOINT,而docker run命令中指定的任何参数,都会被当做参数再次传递给ENTRYPOINT。Dockerfile中只允许有一个ENTRYPOINT命令,多指定时会覆盖前面的设置,而只执行最后的ENTRYPOINT指令。
LABEL
用于为镜像添加元数据
格式:
1
|
LABEL <key>=<value> <key>=<value> <key>=<value> ...
|
示例:
1
|
LABEL version="1.0" description="这是一个Web服务器" by="IT笔录"
|
ENV
构建的时候设置环境变量!
格式:
1
2
|
ENV <key> <value> #<key>之后的所有内容均会被视为其<value>的组成部分,因此,一次只能设置一个变量
ENV <key>=<value> ... #可以设置多个变量,每个变量为一个"<key>=<value>"的键值对,如果<key>中包含空格,可以使用\来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行
|
示例:
1
2
3
|
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat=fluffy
|
EXPOSE
当前容器对外暴露的端口
格式:
1
|
EXPOSE <port> [<port>...]
|
示例:
1
2
|
EXPOSE 80 443
EXPOSE 8080
|
EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在docker run运行容器时通过-p来发布这些端口,或通过-P参数来发布EXPOSE导出的所有端口
VOLUME
容器数据卷,用于数据保存和持久化工作,挂载到目录
格式:
1
|
VOLUME ["/path/to/dir"]
|
示例:
1
2
|
VOLUME ["/data"]
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"
|
一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:
- 卷可以容器间共享和重用
- 容器并不一定要和其它容器共享卷
- 修改卷后会立即生效
- 对卷的修改不会对镜像产生影响
- 卷会一直存在,直到没有任何容器在使用它
WOEKDIR
指定在创建容器后,终端默认登陆进来工作目录,镜像的工作目录
格式:
1
|
WORKDIR /path/to/workdir
|
示例:
1
2
3
|
WORKDIR /a (这时工作目录为/a)
WORKDIR b (这时工作目录为/a/b)
WORKDIR c (这时工作目录为/a/b/c)
|
通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行。在使用docker run运行容器时,可以通过-w参数覆盖构建时所设置的工作目录
USER
指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。使用USER指定用户时,可以使用用户名、UID或GID,或是两者的组合。当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户
格式:
1
2
3
4
5
6
|
USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group
|
示例:
使用USER指定用户后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT都将使用该用户。镜像构建完成后,通过docker run
运行容器时,可以通过-u参数来覆盖所指定的用户。
ARG
用于指定传递给构建运行时的变量
格式:
1
|
ARG <name>[=<default value>]
|
示例:
1
2
|
ARG site
ARG build_user=www
|
ONBUILD
用于设置镜像触发器,当构建一个被继承 DockerFile时候就会运行 ONBUILD 的指令。(父镜像在被子镜像继承后父镜像的onbuild被触发)触发指令
格式:
示例:
1
2
|
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
|
当所构建的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被钥触发

6.4 实战测试-centos
Docker Hub 中99%镜像都是从这个基础镜像过来的 FROM scratch,然后配置需要的软件和配置来进行的构建

创建一个自己的centos
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
FROM centos
MAINTAINER snailsir<snailsir@126.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "----end----"
CMD /bin/bash
|
2、通过dockerFile构建镜像
1
|
docker build -f ./nginxfile -t self_nginx:v1
|
3、测试运行
1
|
docker run -it mycentos:0.1
|
补充:
1
2
|
# 查看镜像的构建历史,我们平时拿到一个镜像,可以研究一下它是怎么做的了?
docker history
|
CMD 和 ENTRYPOINT 区别
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 编写 dockerfile文件
FROM centos
CMD ["ls","-a"]
# 构建镜像
docker build -f dockerfile-cmd-test -t cmdtest
#run 运行,发现我们的ls -a 命令生效
docker run dd8e4401d72f
#想追加一个命令-l ls -al
docker run dd8e4401d72f -l
报错: "exec:\"-l\":executable file not found in $PATH"
#cmd的情况下, -l替换了 cmd ["ls","-a"]命令,-l不是命令所以报错!
|
1
2
3
4
5
6
7
8
9
10
11
12
|
# 编写 dockerfile文件
FROM centos
ENTRYPOINT ["ls","-l"]
# 构建镜像
docker build -f dockerfile-cmd-test -t cmdtest
#run 运行,发现我们的ls -a 命令生效
docker run dd8e4401d72f
#想追加一个命令-l ls -al 不报错
docker run dd8e4401d72f -l
|
6.5 实战测试-tomcat
1、准备镜像文件 tomcat压缩包,jdk的压缩包!

2、编写dockerfile文件, 官方命名Dockerfile
,build会自动寻找这个文件,就不需要-f指定了!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
FROM centos
MAINTAINER snailsir<snailsir@126.com>
COPY readme.txt /usr/local/readme.txt
ADD jdk-Bull-linux-x64.tar.gz /usr/local/ #ADD 会自动解压
ADD apache-tomcat-9.0.22.tar.gz /usr/local/
RUM yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/java1.8.0_11
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.22
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.22
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.22/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.22/bin/logs/catalina.out
|
3、构建镜像
1
|
docker build -t diytomcat
|
4、启动镜像
1
|
docker run -d -p 9090:8080 --name snailsir -v /home/kuangsheng/build/tomcat/test:/usr/local/apache-tomcat-9.0.22/webapps/test -v /home/kuangsheng/build/tomcatlogs:/usr/local/apache-tomcat-9.0.22/logs diytomcat
|
5、访问测试
6、发布项目(由于做了卷挂载,我们直接在本地编写项目就可以发布了!)
6.6 发布自己的镜像
6.6.1 发布到dockerhub上
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#登录dockerhub
docker login -u snailsir
#提交镜像
docker push snailsir/diytomcat:1.0
#push镜像问题
docker push kuagnshen/diytomcat:1.0
An image does not exist locally with the tag:kuangsheng/diytomcat2
#解决,增加一个tag
docker tag f855234sdfeg kuangsheng/tomcat:1.0
docker push kuangshen/tomcat:1.0
|
6.6.2 发布到阿里云镜像仓库
6.7 小结

7、Docker网络
7.1 docker0

三个网络
问题: docker是如何处理容器网络访问的?

1
2
3
4
|
docker run -d -P --name tomcat01 tomcat
#查看容器的内部网络地址 ip addr 发现容器启动的时候回得到一个 eth0@if262 ip地址,docker分配的
docker exec -it tomcat01 ip addr
|

1
2
3
4
|
#linux 能不能pingt通容器内部!
ping 127.18.0.2
#linux 可以ping 通docker 容器内部
|
原理
1、我们每启动一个docker容器,docker就会给docker容器分配一个ip,我们只要安装了docker,就会有一个网卡docker0
桥接模式,使用的技术是evth-pair技术!
再次测试ip addr

2、在启动一个容器测试
1
|
docker run -d -P --name tomcat02 tomcat
|
测试ip addr, 发现又多了一对网卡!

1
2
3
4
|
#我们发现这个容器带来网卡,都是一对对的
# evth-pair 就是一对的虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连
# 正因为有这个特性,evth-pair充当一个桥梁,连接各种虚拟网络设备的
# OpenStac,Docker容器之间的连接,ovs的连接,都是使用evth-pair技术
|
3、我们来测试下tomcat01 和tomcat02 是否可以ping通!
1
2
3
|
docker exec -it tomcat02 ping 172.18.0.2
# 结论:容器和容器之间是可以互相ping通的!
|
网络模型图:

结论: tomcat01和tomcat02 是公用的一个路由器(docker0)
所有的容器不指定网络的情况下,都是docker0路由的,docker会给我们容器分配一个默认的可用ip
小结
Docker使用的是linux的桥接,宿主机中的一个Docker容器的网桥 docker0.

Docker中的所有的网络接口都是虚拟的。虚拟的转发效率高!(内网传递文件!)
只要容器删除,对应的网桥一对就没了!

7.2 –link
我们编写了一个微服务,database url=ip: 项目不重启,数据库ip换掉了,我们希望可以处理这个问题,可以名字来进行访问容器?
1
2
3
4
5
6
7
8
9
10
|
docker exec -it tomcat02 ping tomcat01
ping: tomcat01:Name or service not knowm
# 解决
docker run -d -P --name tomcat03 --link tomcat02 tomcat
#校验
docker exec -it tomcat03 ping tomcat02
#反向可以ping通吗?不通
docker exec -it tomcat02 ping tomcat03
|
探究:inspect

其实这个tomcat03 就是在本地配置了tomcat02的配置?
1
2
3
4
5
|
# 查看 hosts配置,在这里原理发现!
docker exec -it tomcat03 cat /etc/hosts
127.0.0.1 localhost
127.18.0.3 tomcat02 312857784cd4
127.18.0.4 5ca72d80ebb0
|
–link 就是我们在hosts配置中增加了一个172.18.0.3 tomcat02 312857784cd4
我们现在玩docker已经不建议使用 –link了!
自定义网络!不使用docker0!
docker0问题:不支持容器名连接访问!
7.3 自定义网络
查看所有的docker网络

7.3.1 网络模式
bridge:桥接(默认) docker上搭桥 1 访问3 直接访问不了 可以在1和3之间搭建2 访问
none: 不配置网络
host: 和宿主机共享网络
container: 容器内网络连通(用的少!局限很大)
7.3.2 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# 我们直接启动的命令 --net bridge,而这个就是我们的coker0
docker run -d -P --name tomcat01 tomcat #最初启动方式
docker run -d -P --name tomcat01 --net bridge tomcat
#docker0 特点,默认域名不能访问,--link可以打通连接!
#我们可以自定义一个网络!
# --driver bridge 桥接
# --subnet 192.168.0.0/16
# --gateway 192.168.0.1
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
# 查看网络
docker network ls
#测试
docker run -d -P --name tomcat-net-01 --net mynet tomcat
docker run -d -P --name tomcat-net-02 --net mynet tomcat
#查看inspect
docker network inspect mynet
|

1
2
3
4
|
#测试 01 与02 是否能ping通
docker exec -it tomcat-net-01 192.168.0.3
# 不使用--link也能ping名字了!
docker exec -it tomcat-net-01 tomcat-net-02
|
我们自定义的网络docker都已经帮我们维护好了对应关系,推荐我们平时这样使用网络!
7.3.3好处
redis 不同的集群使用不同的网络,保证集群是安全和健康的
mysql 不同的集群使用不同的网络,保证集群是安全和健康的
7.4 网络连通

1
2
3
4
5
6
|
#测试打通 tomcat01 -> mynet
docker network connet mynet tomcat01
#连通之后就是将 tomcat01 放到了 mynet网络下
# 一个容器两个ip地址! 阿里云的公网ip与私网ip
docker network inspect mynet
|

1
2
3
4
|
docker exec -it tomcat01 ping tomcat-net-01
#cg
docker exec -it tomcat02 ping tomcat-net-01
# 不通
|
通过个脚本可以生成镜像,镜像是一层层的,脚本是一个个命令,每个命令都是一层!
1
2
3
4
5
6
7
8
9
|
# 创建一个dockerfile文件,名字可以随机,建议Dockerfile
FROM centos
VOLUME ["volume01", "volume02"]
CMD echo "----end----"
CMD /bin/bash
#这里的每个命令,就是镜像的一层
|

1
2
|
# 启动自己的容器
docker run -it 5d04f189a434 /bin/bash
|

这个卷和外部一定是有一个同步的目录!

查看一下卷挂载的路径

测试一下刚才的文件是否同步出去了!
这种方式我们未来使用的十分多,因为我们通常会构建自己的镜像!
假设构建镜像时没有挂载卷,要手动镜像关注 -v 卷名:容器内路径!
8、资源隔离与限制
8、实战
8.1mysql安装
安装5.7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#获取镜像
docker pull mysql:5.7
#运行容器,需要做数据挂载! #安装mysql,需要配置密码的
#官方测试:
#启动我们的
-d 后台启动
-p 端口映射
-v 卷挂载
-e 环境配置
-name 容器名字
docker run --name mysql5 -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
#启动成功,本地连接下
#连接的ip可可以使用查看
docker inspect 5d04f189a434
|
安装mysql8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
docker run --name mysql5 -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest
docker run --name mysql80 -d -p 3306:3306 -v /docker/mysql/mysql8/conf.d:/etc/mysql/conf.d -v /docker/mysql/mysql8/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest
#发现启动不成功
# 查看日志
[snailsir@localhost docker]$ sudo docker logs -tf --tail=100 a8e66b9175eb
#错误
[ERROR] [MY-013236] [Server] The designated data directory /var/lib/mysql/ is unusable. You can remove all files that the server added to it.
# 更换数据保存目录
docker run --name mysql5 -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysqld -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest
# 修改数据保存目录 /home/mysql/conf/my.cnf
[mysqld]
datadir = /var/lib/mysqld
#修改加密方式
docker exec -it mysql5 /bin/bash
mysql -uroot -p
use mysql;
select host, user, plugin from user;
alter user 'root'@'%' identified with mysql_native_password by 'your-password';
alter user 'root'@'localhost' identified with mysql_native_password by 'your-password';
flush privileges;
|
8.2 nginx安装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
#1、获取镜像
docker pull nginx
#镜像配置文件目录
# /etc/nginx/nginx.conf /etc/nginx/conf.d
#默认html路径 /usr/share/nginx/html
#镜像日志文件路径/var/log/nginx
#2、启动一个默认实例
docker run --name nginx -d nginx
#3、把容器内的文件拷贝出来
docker cp nginx:/etc/nginx/nginx.conf /docker/nginx/conf.d
docker cp nginx:/etc/nginx/conf.d/default.conf /docker/nginx/conf.d
docker cp nginx:/usr/share/nginx/html/50x.html /docker/www
docker cp nginx:/usr/share/nginx/html/index.html /docker/www
docker cp nginx:/var/log/nginx /docker/nginx/logs
#4、删除默认实例
docker stop nginx && docker rm nginx
#5、创建nginx新实例
docker run --name nginx-web -p 80:80 -v /docker/nginx/www:/usr/share/nginx/html -v /docker/nginx/conf.d:/etc/nginx/conf.d -v /docker/nginx/logs:/var/log/nginx -d nginx
#6、创建各个网站日志与代码地址
mkdir -p /docker/www/snailsir.cn
mkdir -p /docker/nginx/logs/snailsir.cn
|
nginx.conf默认文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
|
conf.d/default.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
server {
listen 80;
listen [::]:80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
|
snailsir.cn.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
|
server {
listen 80;
server_name www.snailsir.cn;
#charset koi8-r;
access_log /var/log/nginx/snailsir.cn/access.log main;
error_log /var/log/nginx/snailsir.cn/error.log warn;
location / {
root /usr/share/nginx/html/snailsir.cn;
index index.html index.htm;
}
}
|
8.3 redis安装
单机版redis安装
配置文件 redis.conf
1
2
3
4
5
6
7
8
9
|
# 修改配置文件
bind 127.0.0.1 #注释掉这部分,这是限制redis只能本地访问
protected-mode no #默认yes,开启保护模式,限制为本地访问
daemonize no#默认no,改为yes意为以守护进程方式启动,可后台运行,除非kill进程,改为yes会使配置文件方式启动redis失败
dir ./ #输入本地redis数据库存放文件夹(可选)
appendonly yes #redis持久化(可选)
#运行docker
docker run -p 6379:6379 --name redis -v /docker/redis/conf/redis.conf:/etc/redis/redis.conf -v /docker/redis/datas:/data -d redis redis-server /etc/redis/redis.conf --appendonly yes
|
9、bug
9.1 问题1
描述
1
|
cannot connect to the docker daemon at unix:///var/run/docker.sock
|
原因
可能是上一次没有正常退出docker,所以docker没有正常启动,在响应的/var/run/路径下没有找到docker进程。
解决方法
或者
docker安装之后,已经安装了开机启动service文件,但还需要在设置下开机启动,才能在服务器重启是自动启动
1
|
systemctl enable docker
|