(2021-04-27)后端开发学习之Docker入门

Posted Mr. Dreamer Z

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(2021-04-27)后端开发学习之Docker入门相关的知识,希望对你有一定的参考价值。

这两周花了些时间学了docker,对docker也有了一个大概的了解。下面就简单谈谈自己的看法以及一些常见的命令。

docker简介以及安装

1.docker为什么会出现

在使用docker之前,我们最常见的操作就是将服务打包之后,部署到运维同学已经安装好所有的插件的服务器上。但是常常会有问题出现,最常见的就属,我在测试环境上可以运行,但是生产环境上报错。较为复杂的项目会进行大量的重复配置。
首先,我们先来了解一下传统模式下程序开发部署的流程:

问题很明显了,针对每个环境我门都需要做重复的操作。
1.资源利用效率低
2.运维部署不方便
3.各个环境版本管理复杂
4.迁移成本高
5.虚拟机空间占用大,启动慢等

我们知道,java的愿望是“write once,run anywhere”,而docker呢?它提出了“Build once,Run anywhere,Configure once,Run anything”。从这句话我们其实可以大致的对它有点了解了。不就是我只需要搞一次事情,那么在其他地方我就不需要再做重复的工作了的意思吗。

而对于docker来说,它提出了集装箱的概念

docker的初衷其实就是将各个应用和它们所依赖的运行环境,打包成标准的image,进而发布到不同的平台上运行。——划重点

简单来说,docker就是为了方便应用更快速的交付和部署。
传统:一对帮助运维文档,安装程序
docker:打包镜像发布测试,一键运行

下图是官方docker的架构

可以看出docker省去了操作系统,整个层级更加的简化了,直接借用宿主机的内核系统。
如果上图不是太直观,那么下图可能会让你更好的了解docker

虚拟机实现资源隔离的方法是利用一个独立的Guest OS,并利用Hypervisor虚拟化CPU、内存、IO设备等实现的。
Docker就显得简练很多,它不像虚拟机一样重新加载一个操作系统内核,引导、加载操作系统内核是一个比较耗时而又消耗资源的过程,Docker是利用Linux内核特性实现的隔离,运行容器的速度几乎等同于直接启动进程。

总结一下:

  • 传统虚拟机,虚拟出一条硬件,运行一个完整的操作系统,然后在这个系统上安装和运行软件。
  • 容器内的应用直接运行在宿主机的内容,容器自己是没有内核的,也没有虚拟我们的硬件,所以就轻便了。
  • 每个容器间是相互隔离,每个容器都有属于自己的文件系统,互不影响。

2.docker的构成

docker其实组成也很简单,由镜像(image),容器(container)。仓库(repository)组成。

  • image:docker的镜像好比是一个模板,可以通过这个模板来创建容器服务。例如:tomcat镜像->run->tomcat容器。

  • container:dokcer利用容器技术,独立运行一个个应用,通常镜像来创建。可以把容器理解为一个简单的linux系统。

  • repository:仓库就是存放镜像的地方,分为私有仓库和共有仓库。

3.安装docker

安装docker操作,这里提供docker的官网,里面写的很全面,应有尽有。

docker官网

进入之后选择自己需要安装的环境,我们这里选择linux。

然后选择自己对应的系统,我这里是centos。

1.需要的安装包

yum install -y yum-utils

2.设置镜像的仓库

yum-config-manager \\
    --add-repo \\
    https://download.docker.com/linux/centos/docker-ce.repo

这里推荐阿里云的

yum-config-manager \\
    --add-repo \\
    http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

3.更新yum软件包索引

yum makecache fast

4.安装dokcer docker-ce 社区版 ee 企业版

yum install docker-ce docker-ce-cli containerd.io

5.启动docker

systemctl start docker

华为云镜像加速
阿里云镜像加速

到此,docker算是安装完成了。

我们来测试一下

docker run hello-world


之后,我们查看是否安装成功

docker images


如果能找到名称叫hello-world的镜像,那么说明安装成功。

4.docker命令

4.1 帮助命令

docker version #显示docker的版本信息
docker info #显示docker的系统信息,包括镜像和容器的数量
docker 命令 --help #帮助命令

4.2 镜像命令

4.2.1 查询本地的主机上的所有镜像

首先启动docker

systemctl start docker

查看

docker images

说明
REPOSITORY 镜像的仓库源
TAG 镜像的标签
IMAGE ID 镜像的id
CREATED 镜像创建的时间
SIZE 镜像的大小

可选项
-a, --all #列出所有的镜像
-q,–quiet #只显示镜像的id

4.2.2 搜索镜像

docker search tomcat

4.2.3 下载镜像

docker pull 镜像名[:tag]


由于我已经安装了,所以不会再次进行安装。至于指定版本的话,通常建议是去docker官网去查询

4.2.4 删除镜像

docker rmi 镜像id

4.3 容器命令

注意:我们有了镜像之后才能创建容器。这里我们下载一个centos来玩玩

docker pull centos

4.3.1 新建容器并启动

docker run [可选参数] image

#参数说明
–name=“NAME” 容器名称 tomcat01,tomcat02 用来区分容器
-d 后台方式启动
-it 使用交互方式运行,进入容器查看内容
-p 指定容器的端口 -p 8080:8080
-p ip:主机端口:容器端口
-p 主机端口:容器端口(常用)
-p 容器端口
-P 随机指定端口

docker run -it centos /bin/bash


进入之后,我们可以发现 ll 命令找不到。这是因为docker要求的是最简化的组装。

4.3.2 退出容器

exit

4.3.3 列出所有容器

docker ps


这里说明一下,退出容器的命令

exit #直接退出并且停止容器
ctrl + p + q #容器不停止退出

4.3.4 删除容器

docker rm 容器id
docker rm -f $(docker ps -qa) #查询出来之后再删除

4.3.5 启动和停止容器

docker start 容器id
docker stop 容器id
docker restart 容器id
docker kill 容器id

4.3.6 后台启动容器

docker run -d 镜像名

4.3.7 查看容器中进程信息

docker top 容器id

4.3.8 查看容器的元数据

docker inspect 容器id

4.3.9 进入正在运行的容器命令行

docker attach 容器id

区别说明

docker exec #进入容器后开启一个新的终端(常用)
docker attach #进入容器正在执行的终端,不会启动新的进程

4.3.10 可视化

通常来说docker可视化管理都是使用portainer

docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer

5. docker镜像详解

5.1 镜像是什么

镜像是一种轻量级、可执行的独立软件包,用来打包软件运行和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码,运行时库、环境变量和配置文件。
所有的应用,直接打包docker镜像,就可以直接跑起来

如何得到镜像:
从远程仓库下载
自己制作镜像DockerFile

5.2 docker镜像加载原理

UFS(联合文件系统):该文件系统是一种分层,轻量级并且高性能的文件系统。
具体详细的描述大家可以自行百度,
大家只需要记住的是它支持对文件系统的修改作为一次提交来一层层的叠加,之前我们下载镜像的时候可以看出来,它本身就是分层下载的,并且可以共用。如下图:

5.2.1 docker镜像加载原理

docker的镜像实际上事由一层一层的文件系统组成的。

这里重新提一下我之前说到的下载centos镜像之后发现有些命令找不到的原因:
对于一个精简的OS,rootfs可以很小,只需要包含最基本的命令,工具和程序库就行了。因为底层直接使用Host的kernel,自己只需要提供rootfs就可以了。由此可见对于不同的linux发行版,bootfs基本是一致的

5.2.2 分层理解

刚刚我们提到过,我们下载镜像的时候可以发现,日志的输出是一层层的在下载。

大家可以想想,为什么docker镜像要采用这种分层的结构呢?
最大的好处,我觉得就是资源共享了。。。

比如有多个镜像都从相同的base镜像构建而来,那么宿主机只需要在磁盘上保留一份base镜像,同事内存中也需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。

如下图所示,可以看出当前镜像包含三个镜像层了。

在添加额外的镜像层的同时,镜像始保持当前所有的镜像的组合,理解这一点非常重要。入下图所示,每个镜像层包含了3个文件,而镜像包含了来自两个镜像层的6个文件。

如果说我们需要修改文件5怎么办呢?

上图可以明显的看出,最上层的文件7是文件5的一个更新版本。
之前我们也提过了,镜像是有一层层的文件系统组成的,为的就是资源共享。

————————————————————————————————

docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部。 这一层就是我们所说的容器层,容器层之下的层都被叫做镜像层。

比如下图tomcat容器启动大致流程图,

6.数据卷Data Volumes

6.1 什么是数据卷

之前我们学习了镜像,也知道了镜像是将应用和环境打包之后生成。
这里我们就需要考虑一个事情,如果我们的数据都放在容器中,那么我们的容器被删除了之后怎么办,数据就会丢失!所以,我们要求数据可以进行持久化操作。
而容器之间可以有一个数据共享的技术!docker容器中产生的数据,同步到本地!
这就是卷技术。目录的挂载,将我们容器内的目录,挂载到linux上面。

6.2 使用数据卷

-v 命令

docker run -it -v 主机目录:容器内目录

操作演示

使用mysql进行挂载

docker run -d -p 3310:3306 -v /home/mysql/conf:/ect/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql

参数说明
-d 后台启动
-p 端口映射
-v 卷挂载
-e 环境配置
–name 容器名称

之后进入终端

docker exec -it mysql01  /bin/bash

开放3344端口,让我们用Navicat去连接。之后创建名称叫test01的数据库
如果Navicat连接报错可以看看这个博客解决无法连接mysql问题

然后根据对应的目录查询

可以看到我们容器中已经创建成功了,保存了对应的数据库信息。

接着我们重新打开一个窗口,在主机对应的映射地址中查询是否有对应的数据库信息

发现也有,说明操作成功。
接着我们在主机地址创建一个文件,看看容器中是否能够同步

容器内

可以看到是能够同步成功的。

接着我们在容器中删除test02,发现主机中依然能够同步到


可以看出是可以双向同步的

6.3 具名和匿名挂载

-v 容器内路径 #匿名挂载
-v 卷名:容器内路径 #具名挂载
-v /宿主机路径:容器内路径 #指定路径挂载(我们刚刚使用的挂载就是指定路径·挂载)

6.3.1 匿名挂载

docker run -d -P --name nginx01 -v /etc/nginx nginx

6.3.2 具名挂载

docker run -d -P --name nginx02 -v jumingguazai-nginx:/etc/nginx nginx

通过命令可以看出具名挂载和匿名挂载的区别

docker volume ls

可知只有具名挂载才会显示对应的名称,而匿名挂载,则会将其隐藏起来,很麻烦

那有人就会问了,那我没指定路径,我怎么知道到底把同步的数据放到哪个目录下面了?

docker volume inspect jumingguazai-nginx


所有的的docker容器内的卷,没有指定目录的情况下都是在 : /var/lib/docker/volumes/xxxx/_data

而绝大多数情况,我们都回去使用具名挂载。因为这样会方便找到我们这个卷。通过我们创建的卷名即可找到。当然了,匿名挂载也可以找到对应的挂载目录。


我们在挂载的时候,执行成功会得到一个容器的id,如下图

我们可以通过下面命令,来进行查看对应的挂载路径

docker  inspect  容器id


也可以找到对应的路径。一样满足之前的规则,只是由卷名改成了容器id。


6.3.3 扩展

通过 -v 容器内路径, ro rw 改变读写权限

ro readonly #只读
rw readwrite #可读可写

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就说明这个路径只能通过宿主机来操作,容器内部是无法操作的!

7.DockerFile

dockerfile就是用来构建镜像的构建文件!
下面我们简单的来了解一下
创建一个dockerfile,内容如下,

#创建一个dockerfile文件,名字可以随机  建议DockerFile
#文件中的内容 指令(大写) 参数
FROM centos

VOLUME ["volume01","volume02"]

CMD echo "---end---"

CMD /bin/bash

#这里的每一个命令,都是镜像的一层

注意后面一个 .

docker build -f /home/docker-test-volume/dockerfile -t zcy01/centos:1.0 .


之前说的每个命令都是一层,大家可以通过上图简明的看出!!!

查看对应的镜像,可以看到我们刚刚构建的镜像

让我们来执行一下

docker run -it 2a0fb40b0fff /bin/bash

进入容器内可以看我们刚刚创建的两个数据卷目录
让我们再来看看它们对应的主机地址在哪,通过 docker inspect 容器id

8.数据卷容器

刚刚我们知道了数据卷Data Volumes,那数据卷容器Data Volume Containers又是什么呢?
我们先给出它们的定义:

数据卷(Data Volumes): 容器内数据直接映射到本地宿主机。
数据卷容器(Data Volume Containers):使用特定容器维护数据卷

数据卷简单来说就是容器内部映射关系。
数据卷容器,既然加了“容器两个字”,那就说明涉及到了容器间的数据卷了。

比如说,我们现在要同步两个容器之间的数据怎么办呢?先来看个图

我们首先把之前的都删除掉

docker rm -f $(docker ps -qa)

然后用我们刚刚构建的镜像来创建一个容器,叫docker01

docker run -d -it --name docker01 2a0fb40b0fff

当然我这里用的是镜像id,你也可以用名称。将镜像id替换成名称
执行成功之后,我们来将两个容器进行数据同步

docker run -it --name docker02 --volumes-from docker01 zcy01/centos:1.0

进入终端之后,我们可以看到docker02容器中也有对应的两个目录

这是因为我们在构建镜像的时候就创建了这两个目录,而此时docker02容器也是通过该镜像来run之后形成了容器。

我们在docker02中创建一个目录,来看看docker01是否同步
我们来创建一下文件,看看会不会同步。

我们在docker02的容器内部创建了一个目录,我们去docker01中看看。但是发现并没有出现,在docker01中!!!!奇了怪了,我们都已经进行容器间的同步了,为啥没有同步到呢?

其实答案我们刚才已经提到了,因为我们创建的数据卷是volume01和volume02两个。只有在数据卷中进行的操作,才能够同步。我们在docker01的volume01中创建一个文件,我们能够在docker02的volume01中看到。

下面我们来个骚操作,将dokcer01容器删除。看看会发生什么。

然后进入docker02中查看,我们发现,依然存在对应的目录和文件。
说明,删除了对应容器,其他容器并不会有什么反应。该吃吃该喝喝

9.docker网络

在学习这个之前,希望大家想一个问题。docker是如何处理网络访问的?
我们安装一个tomcat镜像来实践一下。

docker run -d  -P --name tomcat01 tomcat


我们来试试容器内部能不能ping通

明显可以!!!

为什么可以呢?

我们每启动一个容器,docker就会给docker容器分配一个ip,我们只需要安装docker,就会有一个网卡docker0进行桥接模式,使用的是veth-pair技术!

我们再启动一个tomcat02,看看tomcat02能不能ping通tomcat01

当然是可以的,因为我们刚刚已经提过了

ip addr 之后我们发现又又新增了一对网卡

下图是访问的流程

结论:tomcat01和tomcat02是共用的一个路由器,docker0。
所有容器不指定网络的情况下,都是docker0路由的。Docker会给我们的容器分配一个默认的可用IP。

Docker中所有的网络接口都是虚拟的,虚拟的转发效率高(内网传递速率高)。
只要删除容器,那么对应网桥就没有了。
—— 这一步大家去试试

9.1 自定义网络

查看网络

docker network ls

删除网络

docker network rm network ID

创建自定义网络

docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet

创建之后再次查看网络,查看是否创建成功!

docker network inspect mynet


可以看到我们刚刚设置的子网IP和网关IP。

现在我们来使用一下我们刚刚创建的网络。

docker run -d -P --name tomcat-net-01 --net mynet tomcat
docker run -d -P --name tomcat-net-02 --net mynet tomcat

由于这两个容器都使用了这个网络配置,那么肯定是可以联通的

下面,我们让之前的tomcat01容器也来用我们自定义的网络

docker network connect 网络名称 容器名称


再来试试tomcat02能否联通

因为使用的网络不同,所以是不通的。之所以推荐使用自定义网络支持:容器名连接访问。

10.基于springboot打包docker

最后一步,我们创建一个简单的springboot项目打包到docker然后执行
首先我们创建一个简单的springboot项目

1.下载docke插件

2.创建一个控制类

3.创建DockerFile

内容如下:

#基础镜像通过java8来构建
FROM java:8

#将所有的.jar文件打包到镜像内部
COPY *.jar /aap.jar

#输出一些东西
CMD ["--server.port=8080"]

#暴露端口
EXPOSE 8080

# 执行命令  java -jar /app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

4.将打包好的jar和DockerFile上传到服务器

5.构建镜像

6.执行镜像

docker run -d -P --name java-zcy zcy-java

7.查看容器

docker ps


由于我们使用的-P,所以说会自动映射地址。可以看到到我们java-zcy容器的8080端口被映射到本机的49153端口上。

0.0.0.0:49153->8080/tcp
说明:本机端口—>容器端口/协议

8.访问对于的地址

成功显示我们需要打印的信息。

以上是关于(2021-04-27)后端开发学习之Docker入门的主要内容,如果未能解决你的问题,请参考以下文章

1.docker学习之简介

Docker学习之Docker容器基本使用

docker学习之-什么是docker

Docker学习之docker-compose

Docker学习之仓库

docker学习之初识container