文章目录
Docker概述
docker就是容器虚拟化技术
一套产品从开发到上线,需要有两套环境 , dev和 test 。
环境的配置十分麻烦,每一个机器都要部署环境(集群redis,es,hadoop),费时费力
发布一个项目(jar + (redis mysql jdk es 的环境) ),也就是使用户的APP机器运行环境能够做到 一次封装,到处运行
docker的思想: 开发打包部署上线,一套流程做完。也就是通过对应用组件的封装、分发
Java-----> jar(带环境)------>打包项目带上环境(镜像)--------> docker仓库(商店 )------>下载我们发布的镜像------>直接运行
docker给以上的问题,提出了解决方案。安装的时候把原始环境一模一样的复制过来,之前是搬家,现在是搬楼
docker使用容器技术,docker是虚拟化技术。
docker是基于 GO 语言开发的,是一个开源项目
docker可以理解成是一个缩小版的Linux系统
容器化技术:
docker和虚拟机技术的不同:
- 传统虚拟机: 虚拟出一条硬件,运行一个完整的操作系统,然后在这个系统上安装和运行软件
- 容器内应用之间运行在宿主机的内容,容器是没有自己的内核的,也没有虚拟我们的硬件,所以就轻便了
每个容器内是相互隔离的,每个容器内都有一个属于自己的文件系统,互不影响
作用:
-
应用更快速的交付和部署:
传统: 一堆帮助文档,安装程序
Docker : 打包镜像发布测试,一键运行
-
更便捷的升级和扩缩容: 使用了docker之后,我们部署应用就和搭积木一样
-
更简单的系统运维 : 在容器化之后,我们开发、测试环境都是高度一致的(不会出现在我的电脑不能运行,在其他电脑就行)
-
更高效的计算机资源利用 : docker是内核级别的虚拟化,在一个物理机上可以运行很多的容器实例,服务器的性能可以被压榨到极致
docker 的安装
# step 1: 安装必要的一些系统工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# Step 2: 添加软件源信息
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Step 3: 更新并安装 Docker-CE
sudo yum makecache fast
sudo yum -y install docker-ce
# Step 4: 开启Docker服务
sudo service docker start
然后使用镜像加速器:
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://9ao1x8r9.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
然后我们就可以查看docker版本:
镜像(images):
docker镜像就好比是一个模板,可以通过这个模板来创建容器服务:
tomcat 镜像–> run --> tomcat01 容器(提供服务器),通过这个镜像可以创建多个容器,最终服务运行或者项目运行就是在容器中的
镜像即应用,docker通过镜像将系统核心除外,把运行应用程序所需要的系统环境从下而上打包,达到应用程序跨平台的运行
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。所有应用直接打包成docker镜像可以直接跑起来。
得到镜像的方法:
- dockerhub上下载
- 其他人拷贝直接给
- 自己制作dockerfile文件生成镜像
镜像加载原理
UnionFS : 联合文件系统,这是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像,可以制作各种具体的应用镜像。
Docker的镜像实际上就是由一层层的文件系统组成
Bootfs(boot file system 引导文件系统,系统启动都必须要引导加载)主要包含boot loader和kernel,boot loader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统,再docker镜像的最底层是bootfs,这一层和典型的Linux/UNIX系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存里了,此时内存的使用权已由bootfs转交给内核,此时系统也会写在bootsfs。
Rootfs(root file system) 再bootfs之上,包含的就是典型的Linux系统里的 /dev,/proc,/bin,/etc等标准目录和文件,rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos 等等
下载centos7的时候,几乎是4G这样,但是在docker里面只要200+MB,。这时因为我们的4g大小里面 ,bootfs占了差不多3.5G,这样。但是docker只需要提供rootfs即可,bootfs和kernel则直接使用虚拟机的。对于不同的Linux发行版,rootfs会有差别,bootfs则一般相同,所以可以这样用
docker镜像特点:
镜像默认都是只读的,当我们容器启动时,一个新的可写层被加载到镜像的顶部
这一层就是我们通常说的容器层,容器之下都叫镜像层。
然后新的镜像打包的时候,又是上层合并下层形成一个镜像层,比如上面的tomcat镜像里面的某个依赖版本为1.0,我们在容器层添加了2.0版本的,那么这个2.0版本的则会覆盖了原有的1.0版本
容器(container):
docker利用容器技术,独立运行一个或者一个组应用,容器是通过镜像来创建的
docker底层原理 :
docker是一个client-server结构的系统,docker的守护进程运行在主机,通过 socket从客户端访问
DockerServer 接收到 docker-client 的指令就会去执行这个命令
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MbXa2aQR-1620651873328)(E:\Docker\images\3.png)]
docker为何比 VM 快?
- docker有着比虚拟机更少的抽象层
- docker利用的是宿主机的内核,VM 需要的是 Guest os
所以,新建一个容器时,docker不需要像虚拟机一样重新加载一个操作系统的内核,避免引导,虚拟机是加载Guest os ,时间很长,而docker是利用宿主机的操作系统,省略了这个过程
docker常用命令:
其他命令:
- docker version
- docker info //显示
- docker 命令 --help
镜像命令:
-
docker images 查看本地主机上的所有镜像
REPOSITORY : 是镜像的仓库源
TAG :是镜像的标签
IMAGE ID : 是镜像的ID
CREATED : 是镜像的创建时间
SIZE : 镜像大小
-
docker images -q 镜像名 : 仅显示ID
-
docker search 镜像名 : 搜索镜像
-
docker pull 镜像名[: 版本] :拉取镜像
docker pull mysql 默认下载最新版本的镜像
docker pull mysql:5.7
-
docker rmi -f 镜像ID 删除镜像,使用ID来删除
-
docker rmi -f $(docker images -aq ) 删除所有镜像 ,原理是先查询出所有的镜像,然后删除所有的镜像
容器命令:
有了镜像才能创建容器
-
docker run [-options] 镜像名 /bin/bash 通过镜像创建容器
--name=“容器名” 一个镜像可以产生多个容器,用于区分容器
-d 后台运行 (这里改成 -dit 才行,不然会立刻停止)
-it 使用交互方式运行 ,可以进入容器查看内容。退出并停止容器是
exit
,退出但不停止是CTRL+P+Q
-p 指定容器的端口 -p 8080:8080
-
docker ps [options] 列出容器
无参数 列出正在运行的容器
-a 列出所有曾经运行过和正在运行的容器
-n= 0,1,2,3,4,5,… 列出最近创建的n个容器
-q 是只显示容器的编号
-
docker rm 容器ID 删除容器(无法删除正在运行的容器) 也可以
docker rm $(docker ps -aq)
-
docker rm -f 容器ID 删除容器(可以强制删除运行的容器)
-
docker start 容器ID 启动容器
-
docker restart 容器ID 重启容器
-
docker stop 容器ID 停止容器
-
docker kill 容器ID 杀死容器
这里docker kill和docker stop的区别: kill是不管容器同不同意,立刻执行kill -9 ,强行终止,而stop则是给容器发送一个 TERM 信号,让容器做一些退出前必须的保护性、安全性操作,然后让容器自动停止运行,如果在一段时间内容器还没停止,就执行kill -9 强行终止。所以docker stop也是一种优雅下线
-
docker logs 容器ID 查看日志
-
docker top 容器ID 查看容器内的进程信息
-
docker inspect 容器ID 查看容器的元数据,可以查看数据卷的挂载(就是里面的 mounts属性)等等
-
docker exec -it 容器ID /bin/bash 进入容器后开启一个新的终端,可以在里面操作
-
docker attach 容器ID /bin/bash 进入容器正在执行的终端,不会启动新的终端
-
docker cp 容器ID:filepath 宿主机的filepath 从容器内拷贝文件到主机内
提交镜像:
提交镜像需要进入容器内部
- docker commit
- docker commit -m=“提交的信息” -a=“作者” 容器ID 目标镜像名:[tag]
docker commit -a="kuangshen" -m="add webapps app" c1d9077334d5 tomcat:1.0
//提交镜像 ,作者 author是 kuangshen ,信息message 是 add webapps app ,容器id是后面那个,紧跟 镜像名:镜像版本
拷贝文件:
这里的命令都是在宿主机上执行
- 从容器内拷贝到宿主机:
docker cp 容器ID:要拷贝的文件在容器里面的路径 要拷贝到宿主机的相应路径
- 从宿主机拷贝到容器:
docker cp 要拷贝的文件路径 容器ID:要拷贝到容器里面对应的路径
容器是否启动不影响拷贝
端口暴露
我们在部署的时候需要使用到端口,但是现在端口是在docker容器内的,我们需要把容器内的端口暴露到宿主机上我们才能通过外网访问
使用nginx镜像作为示例 :
首先,下载nginx的镜像: docker pull nginx
然后我们运行容器的时候就需要进行端口暴露了 : docker run -dit --name nginx01 -p 3344:80 nginx
这里的 3344:80就是端口暴露,意思是 容器内的80端口暴露给宿主机的3344端口。因为nginx默认是80端口,所以我们暴露的是容器内的80
容器运行之后的结果:
容器数据卷:
默认情况下,当用户退出容器而容器中又没有非守护进程在运行时,容器会进入关闭状态,同时,数据的修改会保留在层级的可写文件系统内。当用户需要重新开启一个容器时,是无法访问原来所做的修改的,而是恢复到镜像的初始化状态。为了解决数据持久化的问题,Docker提供了卷和卷容器机制。
数据卷就是持久化技术
卷:目录的挂载,将我们的容器内的目录挂载到 Linux上去
这两个文件夹挂载之后,我们对宿主机的 /home/mysql做修改时,容器内的 /usr/mysql也会修改
使用方法:
- 启动容器时直接挂载 Docker run -it -v 主机目录:容器内目录 镜像名 /bin/bash
- 主机目录省略的话,就会是
/var/lib/docker/volumes/xxxx/
- 容器不管是否启动,数据卷的修改都有效
- 挂载之后,即使我们把容器删除,存到宿主机上的文件也不会被删除,这就实现了持久化
nginx示例:
以nginx为例,docker内的nginx配置文件 nginx.conf
路径是 /etc/nginx/nginx.conf
,我们想要修改配置
所以我们先把里面的配置文件挂载出来在外面进行修改。
挂载的时候是容器内文件夹(或文件)变为和宿主机一致,所以我们宿主机的文件夹(或文件)不能为空,所以先把容器内的 nginx.conf
复制到宿主机上
- 启动容器 :
docker run -dit nginx
- 把nginx.conf 复制到宿主机上 :
docker cp 3a4224be4b37:/etc/nginx/nginx.conf /envir/docker/nginx/nginx.conf
- 查看nginx.conf :
不能使用vim命令的话 , 先执行 apt-get update ,再执行 apt-get install vim
可以发现docker的nginx.conf
并没有明写 server块 ,而是使用include来配置,将server块都写到了 /etc/nginx/conf.d/*.conf
这些文件里去,然后我们去容器内查看这些文件:
不出我们所料,这些 *.conf
果然是写着 server块。既然docker的nginx采用include的方式,那么我们就把这个文件夹挂载,而不去挂载nginx.conf。
- 复制 conf.d 文件夹 :
docker cp 3a4224be4b37:/etc/nginx/conf.d /envir/docker/nginx/conf.d
- 把
default.conf
的 listen端口改成 81
,我们在这样修改之后,新的nginx容器应该运行在81端口,那么我们暴露端口时需要指定的是81 - 启动新容器并挂载我们的 default.conf :
docker run -it -p 3344:81 -v /envir/docker/nginx/conf.d/:/etc/nginx/conf.d/ --name mynginx nginx /bin/bash
- 去访问3344这个端口:
,发现访问成功,说明我们挂载成功了
8. 挂载成功,我们还要测试修改时,是否成功,我们再次到宿主机上修改端口号为80
9. 然后在容器内 nginx -s reload
,这次再去访问 3344时,发现无法访问了,说明容器内的配置文件修改成功
mysql示例:
我们启动mysql容器要指定默认密码 : -e MYSQL_ROOT_PASSWORD=123456
我们想要的就是将数据库的数据文件都挂载到宿主机上,然后docker的mysql的数据文件是存在 /var/lib/mysql/
下的,所以我们就把这个文件夹挂载到宿主机上
- 启动容器,并挂载:
docker run -e MYSQL_ROOT_PASSWORD=123456 -v /envir/docker/mysql/data/:/var/lib/mysql/ -it mysql
- 挂载之后,我们就会发现我们的宿主机上就存在了数据文件了:
查看挂载信息
docker inspect containerID
这是刚刚挂载的MySQL的挂载信息
- sources :宿主机的文件路径
- destination: 容器内的文件路径
第二种是 docker volume ls
查看所有卷
然后使用 docker volume inspect VOLUMENAME
来查看具体的挂载信息:
挂载分类:
挂载分为: 具名挂载、匿名挂载、指定路径挂载
- 具名挂载 :
-v 数据卷名:容器内路径
,指定容器内的某个目录和宿主机的默认目录+具名组成的目录进行绑定 - 匿名挂载 :
-v 容器内路径
,指定容器内的某个目录和宿主机的默认目录+随机生成串组成的目录进行绑定 - 指定路径挂载 :
-v 宿主机路径:容器内路径
,指定容器内的某个目录和宿主机的某个目录绑定
默认目录一般是 /www/server/docker/volumes
前面查看挂载信息的第二种命令 docker volume ls
只能查看具名和匿名挂载,指定路径挂载只能使用第一种命令查看
文件权限管理
这个文件权限是对容器内部来说的,宿主机有最大的权力
设置权限 docker run -it -v /宿主机绝对路径:/容器内目录:ro 镜像名
ro
readonly 容器内只读rw
readwrite 容器内可读可写
Docker run -d -p –name nginx -v juming-niginx : /etc/nginx:ro nginx #这样容器的数据卷文件只能读,修改只能从宿主机修改
Docker run -d -p –name nginx -v juming-niginx : /etc/nginx:rw nginx #这样容器内的数据卷文件可读可写,数据可以在容器内修改
如图,我们可以在挂载信息里查看 RW
是否开启
Dockerfile
dockerfile就是用于构建 docker镜像的 构建文件,命令脚本。
构建步骤: 编写dockerfile
---->docker build
----> docker run
dockerfile和commit不一样,这是不需要容器而创建新镜像的方法
通过这个脚本可以生成镜像,镜像是一层层的,脚本是一个个命令,每个命令都是一层
我们可以查看dockerhub的centos的dockerfile docker-centos:
其他的dockerfile与上图大致相同
Dockerfile内容基础知识
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行
- 用
#
表示 注释 - 每条指令都会创建一个新的镜像层,并对镜像进行提交commit
docker执行 dockerfile的流程 :
- docker从基础镜像运行一个容器,像上面的centos的基础镜像就是scratch
- 执行一条指令并对容器作出修改
- 执行类似 docker commit的操作提交一个新的镜像层
- docker再基于刚提交的镜像运行一个新容器
- 执行dockerfile中的下一条指令直到所有指令都执行完成
dockerfile是软件的原材料,docker镜像是软件的交付品,docker容器则可以认为是软件的运行态。dockerfile面向开发,docker镜像成为交付标准,docker容器则设计部署与运维
保留字指令:
FROM
基础镜像,这个镜像是基于哪个镜像,centos就是基于scratch镜像的MAINTAINER
作者姓名和邮箱RUN
容器构建时需要运行的命令EXPOSE
当前容器对外暴露的端口,并不是容器-主机映射端口,仅仅是容器开放哪些接口WORKDIR
创建容器时 ,-it
进入的工作目录,一个落脚点,省略则是/
ENV
设置环境变量
ENV MY_PATH /usr/mytest #这个是配置环境变量,那么我们在RUN指令时使用mytest文件夹下的命令就可以直接使用
WORKDIR $MY_PATH #也可以这样使用
ADD
把宿主机下的压缩包拷贝进镜像内,并解压COPY
1.把压缩包拷贝进镜像 。2.从构建上下文目录中的文件复制到新一层的目标路径位置 , COPY src dest 或 COPY[“src”,“dest”]VOLUME
容器数据卷,用于保存数据和持久化CMD
指定容器运行时要运行的命令,可以有多个CMD指令,但只有最后一个生效,我们在运行时指定的命令行参数会覆盖ENTRYPOINT
与CMD 一样 ,但是 这个我们的命令行参数不是覆盖,而是追加ONBUILD
当构建一个被继承 的 dockerfile时运行命令,父镜像在被子继承后父镜像的 onbuild 被触发,子镜像就是使用了FROM的centos,父镜像就是scratch
在centos的dockerfile里的scratch 就是BASE镜像,相当于Java里的 Object类,docker中99%的镜像都是通过在BASE镜像中按照和配置需要的软件构建出来的,像什么nginx镜像是基于centos来创建的,所以BASE镜像也是它的祖先镜像
dockerfile创建nginx镜像:
之前我们发现了nginx内不能使用vim,然后创建容器时进入的工作目录是 /
:
那么我们现在想新的nginx镜像能够直接使用vim,工作目录是 /usr
写dockerfile:
FROM nginx
MAINTAINER kkk<www@123.com>
ENV MYPATH /usr
WORKDIR $MYPATH
RUN apt-get update && \
apt-get install -y --no-install-recommends \
vim
EXPOSE 80
CMD echo $MYPATH
CMD echo "success--------ok"
CMD /bin/bash
注意:
-
一个是运行
apt-get upgrade
会更新所有包到最新版本 —— 不能这样做的理由是它会妨碍Dockerfile构建的持久与一致性。 -
另一个是在不同的行之间运行
apt-get update
与apt-get install
命令。不能这样做的原因是,只有apt-get update
的代码会在构建过程中被缓存,而且你需要运行apt-get install
命令的时候不会每次都被执行。因此,你需要将apt-get update
跟所要安装的包都在同一行执行,来确保它们正确的更新。# From https://github.com/docker-library/golang RUN apt-get update && \ apt-get install -y --no-install-recommends \ g++
构建镜像命令: docker build -f 文件路径 -t 镜像名:tag .
如果当前目录下有构建文件 Dockerfile,那么-f 就可以省略
记住最后有个 点 .
进行构建: docker build -f /envir/docker/mydockerfile/dockerfile -t dockerfile_nginx .
可以看到,构建成功。
我们运行我们创建的镜像:
可以发现,我们创建容器时的工作目录变成了我们所写的 /usr
,vim
命令也变得可用了
对CMD的理解:
cmd一般用于容器运行时对内部服务器的启动,如tomcat:
如图,这就是tomcat的dockerfile,最后的CMD命令是 ["catalina.sh", "run"]
,这个命令就是用于启动tomcat,也就是当我们运行容器时直接运行tomcat服务器,如图:
但是当我们在最后添加shell命令时:
此时,我们的 ls -l
命令将原来的启动Tomcat的shell命令覆盖了,所以就不能自动启动tomcat服务器了
对ENTRYPOINT的理解
ENTRYPOINT本身是将我们命令行的参数追加到dockerfile里的ENTRYPOIN后面
所以我们可以用于扩展命令。但是ENTRYPOINT也是存在多个,最后一个生效
写一个dockerfile:
这是ENTRYPOINT 的 exec
模式写法(注意空格)
然后构建镜像: docker build -f /envir/docker/mydockerfile/dockerfile01 -t testentrypoint .
这里的 ENTRYPOINT
是 ls -l ,我想测试 ls -l -i 是否能被追加上
如图,我们添加的-i
确实被追加到了ls
后面
前面说了,这里是使用exec
模式来创建dockerfile的,另一种是shell模式
ENTRYPOINT ["executable", "param1", "param2"] // 这是 exec 模式的写法,注意需要使用双引号。
ENTRYPOINT command param1 param2 // 这是 shell 模式的写法。
指定 ENTRYPOINT 指令为 exec 模式时,命令行上指定的参数会作为参数添加到 ENTRYPOINT 指定命令的参数列表中。
指定 ENTRYPOINT 指令为 shell 模式时,会完全忽略命令行参数
ONBUILD
当我们构建 docker build
的时候,这个镜像的FROM 镜像(即父镜像)如果有ONBUILD ,那么就会执行父镜像的ONBUILD
自定义tomcat镜像
我们使用自己的jdk和自己的tomcat压缩包制作tomcat镜像
tomcat9下载地址: tomcat
然后复制到Linux里去,并创建dockerfile:
然后修改dockerfile:
FROM centos
MAINTAINER kkk<www@123.com>
#把宿主机当前上下文的c.txt 拷贝到容器内的/usr/local/路径下
COPY c.txt /usr/local/cincontainer.txt
#把java与tomcat添加到容器中,这里ADD 是拷贝并且复制
ADD jdk-8u281-linux-x64.tar.gz /usr/local
ADD apache-tomcat-9.0.45.tar.gz /usr/local
#安装vim编辑器
RUN yum -y install vim
#设置工作访问时候的WORKERDIR路径,登录落脚点
ENV MYPATH /usr/local
WORKDIR $MYPATH
#配置java与tomcat环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_281
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.45
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.45
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
#容器运行时监听的端口
EXPOSE 8080
#启动时运行tomcat
CMD ["/usr/local/apache-tomcat-9.0.45/bin/catalina.sh", "run"]
然后构建 docker build -f /envir/docker/tomcat9/dockerfile -t mytomcat9 .
到此,tomcat镜像创建成功
运行容器:
运行我们自定义的tomcat容器成功
docker安装redis
-
查询redis镜像 ,
docker search redis
-
拉取镜像 :
docker pull redis
-
本地没有
redis.conf
,但是我们不能启动redis容器去 容器内取出这个配置文件,直接启动redis容器里面是不会有redis.conf
这个配置文件的,所以我们需要自己在主机上准备好。 -
准备好redis.conf文件之后,启动容器 :
docker run -p 6379:6379 -v /envir/docker/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf -v /envir/docker/redis/data:/data -d redis redis-server /usr/local/etc/redis/redis.conf --appendonly yes
-p 6379:6379:把容器内的6379端口映射到宿主机6379端口
-v /data/redis/redis.conf:/etc/redis/redis.conf:把宿主机配置好的redis.conf放到容器内的这个位置中,redis容器默认是没有的
-v /data/redis/data:/data:把redis持久化的数据在宿主机内显示,做数据备份
redis-server /etc/redis/redis.conf:这个是关键配置,让redis不是无配置启动,而是按照这个redis.conf的配置启动
–appendonly yes:redis启动后数据持久化