Docker概述

Docker为什么出现


一款产品: 开发–上线 两套环境! 应用环境,应用配置!

开发 — 运维。 问题:我在我的电脑上可以运行!版本更新,导致服务部可用! 对于运维来说,考验十分大

Docker的思想就来自于集装箱,看他现在的logo就一目了然,一个小鲸鱼上面装了很都货物

Docker的出现解决了项目部署的的问题。比如MySQL集群或者是Redis集群等,如果单独去部署这些东西很麻烦,但是通过Docker一键就可以安装了,就是因为Docker本身就写好了这些东西的配置。

Docker官网: https://www.docker.com/

文档地址: https://docs.docker.com/

仓库地址: https://hub.docker.com/

容器化技术,不是虚拟出一套完整的操作系统。每一个容器都是隔离的,每一个容器都会跟自己的项目进行打包形成一个镜像,而且它是直接运行在系统之上的。不像虚拟机,有很多得lib拓展。占用很大一部分资源。像虚拟机上如果一些拓展崩了的话,有可能会直接影响系统运行。但是容器就不会。因为它的隔离性。即使是其中的一项崩掉。也不会影响其他的项目运行。从而将自己的服务器资源最大化利用。这也是Docker最大的优势。

比较Docker与虚拟机技术的不同

  1. 传统虚拟机,虚拟出一套完整的操作系统,然后再这个系统上安装和运行软件
  2. 容器内法人应用直接运行在宿主机的内容,容器是没有自己的内核的,也没有虚拟我们的硬件。
  3. 每个容器间是互相隔离的,每个容器内都有一个属于自己的文件系统,互不影响。

Docker的基本组成

镜像(image):docker镜像就好比一个模板,可以通过这个模板来创建容器服务。镜像==》run==》镜像容易(提供服务器),通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)

容器(container):Docker利用容器技术,独立运行一个或者一组应用。通过镜像来创建的。启动,停止,删除,基本命令,可以吧容器理解为一个建议的linux系统。

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

Docker Hub

阿里云,华为云都有容器服务(配置镜像加速)。

Docker安装

  1. 卸载旧版本:
sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine
  1. 安装基本环境:
# 需要的安装包
sudo yum install -y yum-utils# 设置镜像的仓库
sudo yum-config-manager \--add-repo \https://download.docker.com/linux/centos/docker-ce.repo # 默认是国外源,不用这个sudo yum-config-manager \--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 用阿里云的

更新yum软件包索引

yum makecac fask
  1. 安装docker相关的内容 docker-ce社区办(推荐) ee企业版
yum install docker-ce docker-ce-cli containerd.io
  1. 启动docker
systemctl start docker
# 利用docker version查看是否启动
  1. 测试 hellow world
docker run hello-world
  1. 查看下载的镜像
docker images
  1. 卸载 docker
# 卸载依赖
yum remove docker-ce docker-ce-cli containerd.io
# 删除docker资源目录
rm -rf /var/lib/dockerrm -rf /var/lib/containerd

阿里云镜像加速

  1. 登录阿里云 找到容器服务

  2. 找到镜像加速地址

  1. 按上述命令配置使用

回顾hellowworld是如何启动的

底层原理

docker是怎么工作的额

Docker是一个Client - Server结构的系统,Docker的守护进程运行在主机上,通过Socker从客户端访问!

DockerServer接收到DockerClient的指令,就会执行这个命令

Docker为什么比vm快?

  1. Docker有着比虚拟机更少的抽象层

  2. docker利用的是宿主机的内核,vm需要的是Guest OS。

所以说,新建一个容器的时候,docker不需要像虚拟机一样重新加载一个操作系统内核,避免引导。虚拟机是加载Guest OS,分钟级别的,而docker是利用宿主机的操作系统,省略了这个复杂的过程,秒级!

之后学习完毕所有的命令没再回过头来看这段理论,就会很清晰!

docker的常用命令

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

镜像命令

docker images [options] # 查看所有本地主机所有镜像
-a #列出所有镜像
-f #过滤
-q #只显示镜像ID(常用)
REPOSITORY(镜像仓库源) TAG(标签) CREATED(镜像创建时间) IMAGE ID(镜像ID) SEZI(大小)docker search #搜索镜像
docker pull [:tag] #下载镜像tag可选,不加默认拉取最新镜像
# pull complete部分为分层下载,docker image的核心,联合文件系统
# Digest 为签名
# docker.io/library/mysql:latest 为真实地址
# 直接用docker pull就等于docker.io/library/mysql:latest
# 指定版本下载 docker pull mysql:5.7
# 分层下载是很高明的。精髓在于不同的镜像版本也会有一些相同部分,这样分层下载,相同部分就相当于共用,不需要重复下载。具体如下图。

docker rmi 删除镜像

docker rmi-f [options] # + 镜像ID,按ID删除, + 镜像名称,按名称删除,rmi就是 remove image
docker rmi -f # 也可以加多个镜像id同时删除多个镜像
docker rmi -f $(docker images -aq) #删除所有容器,递归删除

容器命令

说明:有了镜像才可以创建容器,linux,下载一个centos镜像来测试学习

docker pull centos

新建容器并启动

docker run [可选参数] image
# 参数说明
--name="Name"   # 给容器命名,用来区分容器
-d              # 后台方式运行
-it             # 使用交互方式运行,进入容器查看内容(进入容器)
-p              # 指定容器的端口 -p 主机ip:主机端口:容器端口(主机和容器映射)-p 主机端口:容器端口(主机和容器映射)  常用!!!-p 容器端口直接写容器端口也是可以的
-P              # 随机端口(大写P)

测试,启动并进入容器

docker run -it centos /bin/bash

Ls查看容器内详情

你会发现其实容器内核容器外的内容几乎是一样的,因为他就是一个小型的服务器。 内部的centos与外部的centos没有任何关系

列出所有运行中的容器

docker ps [options] 参数可叠加
# 列出当前正在运行的容器
-a   # 列出当前正字啊运行的容器 + 历史运行过的容器
-n=? # 查询最近常见的容器
-q   # 只显示容器的编号

退出容器

exit    # 直接退出并停止容器
Ctrl + P + Q  #  容器不停止并退出

删除容器

docker rm  容器id   #删除指定容器,不能删除生在运行中的容器,如果要强制删除   rm -f
docker rm -f $(docker ps -ap) #删除所有容器
docker ps -a -q |xargs docker rm  # 利用管道符删除所有容器

启动和停止容器

docker start 容器id        # 启动容器
docker restart  容器id     # 重启容器
docker stop 容器id         # 停止当前正在运行的容器
docker kill 容器id             # 杀掉容器,强制停止

常用的其他命令(日志,元数据,进程的查看)

后台启动容器

docker run -d 镜像名   # 例如 docker run -d centos

问题:docker ps, 发现centos停止了

常见的坑: docker容器用后台运行,就必须要有一个前台进程,docker发现没有应用,就会自动停止。

例如docker启动一个nginx 但是发现nginx没有提供服务,就会立即停止,就是没有程序了

查看日志命令

# 启动容器并手动写一段shell脚本使容器不为空容器,保证容器处于运行中
docker run -d centos /bin/sh -c "while true;do echo Ken; sleep 1;done"(该脚本的含义为无限循环每隔一秒钟打印一个Ken)
# 显示日志
docker logs -ft --tail num 容器id(-ft:查看所有日志f代表时间戳、tail:以字符串的形式查看、num:查看最近的多少条)

查看容器中的进程信息

# top命令
docker top 容器id

以后要杀进程的话可以通过这种方式杀

查看镜像的元数据(很重要:彻底了解镜像中有哪些数据)

docker inspect 容器id(显示容器的全部信息)

我们可以看到我们常使用的id只是容器全路径的缩写

以下为全部信息

# 命令
docker inspect 容器id#测试
➜  ~ docker inspect 55321bcae33d
[{"Id": "55321bcae33d15da8280bcac1d2bc1141d213bcc8f8e792edfd832ff61ae5066","Created": "2020-05-15T05:22:05.515909071Z","Path": "/bin/sh","Args": ["-c","while true;do echo 6666;sleep 1;done"],"State": {"Status": "running","Running": true,"Paused": false,"Restarting": false,"OOMKilled": false,"Dead": false,"Pid": 22973,"ExitCode": 0,"Error": "","StartedAt": "2020-05-15T05:22:06.165904633Z","FinishedAt": "0001-01-01T00:00:00Z"},"Image": "sha256:470671670cac686c7cf0081e0b37da2e9f4f768ddc5f6a26102ccd1c6954c1ee","ResolvConfPath": "/var/lib/docker/containers/55321bcae33d15da8280bcac1d2bc1141d213bcc8f8e792edfd832ff61ae5066/resolv.conf","HostnamePath": "/var/lib/docker/containers/55321bcae33d15da8280bcac1d2bc1141d213bcc8f8e792edfd832ff61ae5066/hostname","HostsPath": "/var/lib/docker/containers/55321bcae33d15da8280bcac1d2bc1141d213bcc8f8e792edfd832ff61ae5066/hosts","LogPath": "/var/lib/docker/containers/55321bcae33d15da8280bcac1d2bc1141d213bcc8f8e792edfd832ff61ae5066/55321bcae33d15da8280bcac1d2bc1141d213bcc8f8e792edfd832ff61ae5066-json.log","Name": "/bold_bell","RestartCount": 0,"Driver": "overlay2","Platform": "linux","MountLabel": "","ProcessLabel": "","AppArmorProfile": "docker-default","ExecIDs": null,"HostConfig": {"Binds": null,"ContainerIDFile": "","LogConfig": {"Type": "json-file","Config": {}},"NetworkMode": "default","PortBindings": {},"RestartPolicy": {"Name": "no","MaximumRetryCount": 0},"AutoRemove": false,"VolumeDriver": "","VolumesFrom": null,"CapAdd": null,"CapDrop": null,"Capabilities": null,"Dns": [],"DnsOptions": [],"DnsSearch": [],"ExtraHosts": null,"GroupAdd": null,"IpcMode": "private","Cgroup": "","Links": null,"OomScoreAdj": 0,"PidMode": "","Privileged": false,"PublishAllPorts": false,"ReadonlyRootfs": false,"SecurityOpt": null,"UTSMode": "","UsernsMode": "","ShmSize": 67108864,"Runtime": "runc","ConsoleSize": [0,0],"Isolation": "","CpuShares": 0,"Memory": 0,"NanoCpus": 0,"CgroupParent": "","BlkioWeight": 0,"BlkioWeightDevice": [],"BlkioDeviceReadBps": null,"BlkioDeviceWriteBps": null,"BlkioDeviceReadIOps": null,"BlkioDeviceWriteIOps": null,"CpuPeriod": 0,"CpuQuota": 0,"CpuRealtimePeriod": 0,"CpuRealtimeRuntime": 0,"CpusetCpus": "","CpusetMems": "","Devices": [],"DeviceCgroupRules": null,"DeviceRequests": null,"KernelMemory": 0,"KernelMemoryTCP": 0,"MemoryReservation": 0,"MemorySwap": 0,"MemorySwappiness": null,"OomKillDisable": false,"PidsLimit": null,"Ulimits": null,"CpuCount": 0,"CpuPercent": 0,"IOMaximumIOps": 0,"IOMaximumBandwidth": 0,"MaskedPaths": ["/proc/asound","/proc/acpi","/proc/kcore","/proc/keys","/proc/latency_stats","/proc/timer_list","/proc/timer_stats","/proc/sched_debug","/proc/scsi","/sys/firmware"],"ReadonlyPaths": ["/proc/bus","/proc/fs","/proc/irq","/proc/sys","/proc/sysrq-trigger"]},"GraphDriver": {"Data": {"LowerDir": "/var/lib/docker/overlay2/1f347949ba49c4dbee70cea9ff3af39a14e602bc8fac8331c46347bf6708757a-init/diff:/var/lib/docker/overlay2/5afcd8220c51854a847a36f52775b4ed0acb16fe6cfaec3bd2e5df59863835ba/diff","MergedDir": "/var/lib/docker/overlay2/1f347949ba49c4dbee70cea9ff3af39a14e602bc8fac8331c46347bf6708757a/merged","UpperDir": "/var/lib/docker/overlay2/1f347949ba49c4dbee70cea9ff3af39a14e602bc8fac8331c46347bf6708757a/diff","WorkDir": "/var/lib/docker/overlay2/1f347949ba49c4dbee70cea9ff3af39a14e602bc8fac8331c46347bf6708757a/work"},"Name": "overlay2"},"Mounts": [],"Config": {"Hostname": "55321bcae33d","Domainname": "","User": "","AttachStdin": false,"AttachStdout": false,"AttachStderr": false,"Tty": false,"OpenStdin": false,"StdinOnce": false,"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd": ["/bin/sh","-c","while true;do echo 6666;sleep 1;done"],"Image": "centos","Volumes": null,"WorkingDir": "","Entrypoint": null,"OnBuild": null,"Labels": {"org.label-schema.build-date": "20200114","org.label-schema.license": "GPLv2","org.label-schema.name": "CentOS Base Image","org.label-schema.schema-version": "1.0","org.label-schema.vendor": "CentOS","org.opencontainers.image.created": "2020-01-14 00:00:00-08:00","org.opencontainers.image.licenses": "GPL-2.0-only","org.opencontainers.image.title": "CentOS Base Image","org.opencontainers.image.vendor": "CentOS"}},"NetworkSettings": {"Bridge": "","SandboxID": "63ed0c837f35c12453bae9661859f37a08541a0749afb86e881869bf6fd9031b","HairpinMode": false,"LinkLocalIPv6Address": "","LinkLocalIPv6PrefixLen": 0,"Ports": {},"SandboxKey": "/var/run/docker/netns/63ed0c837f35","SecondaryIPAddresses": null,"SecondaryIPv6Addresses": null,"EndpointID": "b129d9a5a2cbb92722a2101244bd81a9e3d8af034e83f338c13790a1a94552a1","Gateway": "172.17.0.1","GlobalIPv6Address": "","GlobalIPv6PrefixLen": 0,"IPAddress": "172.17.0.4","IPPrefixLen": 16,"IPv6Gateway": "","MacAddress": "02:42:ac:11:00:04","Networks": {"bridge": {"IPAMConfig": null,"Links": null,"Aliases": null,"NetworkID": "ad5ada6a106f5ba3dda9ce4bc1475a4bb593bf5f7fbead72196e66515e8ac36a","EndpointID": "b129d9a5a2cbb92722a2101244bd81a9e3d8af034e83f338c13790a1a94552a1","Gateway": "172.17.0.1","IPAddress": "172.17.0.4","IPPrefixLen": 16,"IPv6Gateway": "","GlobalIPv6Address": "","GlobalIPv6PrefixLen": 0,"MacAddress": "02:42:ac:11:00:04","DriverOpts": null}}}}
]

等等,还有一些主机的配置和自身的配置。还有挂载等等

进入当前正在运行的容器(更常用)

方式1

# 我们通常容易都是使用后台方式运行的,需要进入容器,修改一些配置
# 命令
docker exec -it 容器id bashshellexec  # 执行-it   #已交互的方式进入

此时在输入命令,就是docker容器内部命令,也可执行部分linux命令,例如 ps -ef

方式2

docker attach 容器id

方式1 进入容器后开启一个新的终端(常用)

方式2 进入容器正在执行的终端,不会启动新的进程

从容器内拷贝文件到主机上

docker cp 容器id:容器内路径  目的主机路径

拷贝是一个手动过程,未来我们使用 -v卷的技术,可以实现自动同步,例如可以讲容日内的home目录和主机的home目录联通

小结

docker 命令大全

  attach      Attach local standard input, output, and error streams to a running container# 当前shell下 attach连接指定运行的镜像build       Build an image from a Dockerfile # 通过Dockerfile定制镜像commit      Create a new image from a container's changes # 提交当前容器为新的镜像cp          Copy files/folders between a container and the local filesystem #拷贝文件create      Create a new container #创建一个新的容器diff        Inspect changes to files or directories on a container's filesystem # 查看docker容器的变化events      Get real time events from the server # 从服务获取容器实时时间exec        Run a command in a running container # 在运行中的容器上运行命令export      Export a container's filesystem as a tar archive # 导出容器文件系统作为一个tar归档文件[对应import]history     Show the history of an image # 展示一个镜像形成历史images      List images # 列出系统当前的镜像import      Import the contents from a tarball to create a filesystem image # 从tar包中导入内容创建一个文件系统镜像info        Display system-wide information # 显示全系统信息inspect     Return low-level information on Docker objects #查看容器详细信息kill        Kill one or more running containers # kill指定docker容器load        Load an image from a tar archive or STDIN # 从一个tar包或标准输入中加载一个镜像[对应save]login       Log in to a Docker registry # 注册或者登录一个docker源服务器logout      Log out from a Docker registry # 从当前docker registry 退出logs        Fetch the logs of a container # 输出当前容器日志信息pause       Pause all processes within one or more containersport        List port mappings or a specific mapping for the container # 查看映射端口对应的容器内部源端口ps          List containers  # 暂停容器pull        Pull an image or a repository from a registry # 从docker镜像源服务器拉取指定镜像或者库镜像push        Push an image or a repository to a registry # 推送指定镜像或者库镜像到docker源服务器rename      Rename a container # 给容器重命名restart     Restart one or more containers # 重启运行的容器rm          Remove one or more containers # 移除一个或者多个容器rmi         Remove one or more images #移除一个或者多个镜像[无容器使用该镜像才可删除,否则需删除相关容器才可继续 或 -f 强制删除]run         Run a command in a new container # 创建一个信的容器并运行一个命令save        Save one or more images to a tar archive (streamed to STDOUT by default) # 保存一个镜像为一个 tar包 [对应load]search      Search the Docker Hub for images # 在docker hub中搜索镜像start       Start one or more stopped containers # 启动容器stats       Display a live stream of container(s) resource usage statistics # 查看容器状态stop        Stop one or more running containers # 停止容器tag         Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE # 给源中镜像打标签top         Display the running processes of a container # 查看容器中运行的unpause     Unpause all processes within one or more containers # 取消暂停容器update      Update configuration of one or more containers # 更新一个或多个容器配置version     Show the Docker version information # 查看docker 版本号wait        Block until one or more containers stop, then print their exit codes # 截取容器停止时的退出状态值

基础实战1 docker部署nginx

端口暴露概念(宿主机与容器以及外网的关系)

思考问题:我们每次改动nginx配置文件,都需要进入容器内部,十分麻烦。我要是可以在容器外部提供一个映射路径,达到在容器外部修改文件,容器内部就可以自动修改。 利用 -v 数据卷!(后续讲)

这里提一个官方文档的坑,以tomcat举例

官方文档里,tomcat的启动命令为

docker run -it --rm tomcat:9.0
# 这个--rm的意思就是启动完毕后,如果我们停止了这个容器,那么这个容器就会直接删除,所以一般这个命令都用于测试上,用完即删

思考问题:我们以后要部署项目,如果每次都要进入容器是不是十分麻烦?我要是可以在容器外部提供一个映射路径,我们在外部防止项目,就自动同步到内部就好了。现在docker容器包含着你的项目跟数据库的,但是如果我们把docker删掉相当于把这些都删掉了,那就不好了。那就真是删库跑路了。(后续解决)

问题:docker如何限制内存

例如elasticsearch这样的容器是非常非常耗内存的。像我们这样的学生机一般都是1核2G。一共才一点几个G的内存被一个es占了70%以上。即使不是学生机,那么这样耗内存的操作也是不好的。所以我们该如何限制docker的内存使用呢?

利用 -e参数去限制,每个不同的容器可以到官方文档去查看,例如es

docker run -d --name elasticsearch -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="Xms64m -Xmx512m" elasticsearch:7.6.2    # 第二个-e参数就是限制es最大只占512M

经过限制之后,明显看到内存占用变小了。

如何将两个容器联通。

可视化

portainer(先用着这个)

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

Rancher(CI/CD再用(持续集成持续部署时))

什么是portainer?

Docker图形化界面管理工具! 提供一个后台面板供我们操作

大家自行安装测试着玩玩就行

Docker镜像讲解

镜像是什么

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

如何得到镜像:

  1. 从远程仓库下载
  2. 朋友拷贝给你
  3. 自己制作一个镜像 Dockerfile

Docker镜像加载原理

UnionFS(联合文件系统)

UnionFS(联合文件系统):Union文件系统是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同事可以将不同目录挂载到同一个虚拟文件系统下。Uion文件系统是Docker镜像的基础。镜像可以通过分层来进行集成,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

特性:一次同事加载多个文件系统。但从外面看起来,只能看到一个文件系统。联合加强在会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。

通俗的讲,大家都学过git,git可以控制版本。我们每操作一次都会被记录下来。比如我们在上面安装了centos 这是第一层,然后我又安装了docker,这就是第二层。每一层都会被记录下来。我们最开始下载镜像的时候看到的一层层的就是这个。

假设我们有一个mysql,还有一个tomcat,都用到了linux系统的内核(假设centos),那么现在我的tomcat下载了centos,那我的mysql就不需要下载了。他们之间是共用的。直接拿过去就可以了。这就是联合文件系统。节省了内存空间。

Docker镜像加载原理

docker的镜像实际上是由一层一层的文件系统组成,这种层级的文件系统就是联合文件系统(UnionFS)

bootfs系统启动的时候需要引导加载,这一块基本是不变的。就是一个内核在加一个加载器。这样我们的系统就运行起来了。运行起来之后这个bootfs就不要了。也就是说。我们从黑屏到开机进入系统之间,我们就称之为加载,我们的系统运行起来了。这个加载是不是就可以不要了。这一部分是共用的。

rootfs在bootfs之上,包含典型的Linux系统中的/dev, /proc,/etc等标准目录和文件,rootfs就是各种不同操作系统的发行版,比如Ubuntu,CentOS等。所以为什么说我们的容器就是一个小型的虚拟机环境,就因为此。

对于一个精简的OS,rootfs可以很小,主需要包含最基本的命令,工具和程序库就可以了,因为底层直接用Host和kernel,自己值需要提供rootfs就可以了。由此可见对于不同的linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以共用bootfs。

通俗的讲。我们的内核是共用的。也就是说每次安装容器的时候。我们不需要在安装内核。只需要安装rootfs这一部分就可以了。这个时候容器的内部就是命令这些。真正的内核用的还是主机的。这也是虚拟机和容器的最大区别,为什么虚拟机启动这么慢,是因为这个内核的启动慢。而容器不需要启动内核。用的是宿主机的内核。所以说虚拟机的分钟级别的,而容器的秒级别的。

如上图所示。底下那一层就是共用的,安装什么就往上面叠加什么。

理解:

所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加信的内容时,就会在当前镜像层之上,常见信的镜像层。举一个简单的例子,例如基于Ubuntu Linux 16.04创建一个信的镜像,这就是新镜像的第一层,如果再该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层,如果继续添加一个安全补丁,就会创建第三个镜像层。该镜像当前已经包含3个镜像层,如下图所示

在议案家额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要,下图中举了一个简单的例子。每个镜像层包含3个文件,而镜像包含了来自两个镜像层的6个文件

上图中的镜像层跟之前图中的略有区别,主要目的是便于展示文件。下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件。这是因为最上层中7是文件5的一个更新版本。

如果再有一个镜像。这个镜像还是三层,如果第一层还是centos,mysql,tomcat。那么这层也可以直接拿过来应用。(老千层饼了。。。)

在这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件,这样就使得文件的更新版本欧威一个新镜像层添加到镜像当着。Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。

Linux上可以用的存储引擎有AUFS、Overlay2、Device Mapper、Btrfs以及ZFS。顾名思义,每种存储引擎都基于Linux中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。

Docker在Windows上仅支持windowsfilter一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和CoW[1]。下图展示了与系统现实相同的三层镜像,所有镜像层堆叠并合并,对外提供统一的视图。

特点:

Docker镜像都是只读的,当容器启动时,一个信的可写层被加载到镜像的顶部!

这一层就是我们通常说的容器层,容器之下的都叫镜像层,镜像层是无法改变的。

镜像从pull到run的过程

commit镜像

docker commit -a="提交人" -m="提交信息描述" 容器ID 容器命名:tag
  1. 启动一个tomcat镜像
  2. 发现这个默认的tomcat 是没有webapps应用的,镜像的原因,官方镜像默认webapps下没有文件!
  3. 我自己拷贝进去了基本的文件
  4. 讲我们操作过的容器通过commit提交为一个镜像!我们以后就使用我们修改过的镜像即可,这就是我们自己的一个修改的镜像!

原理:通过分层原理进行打包

提交总结:

如果你 要保存当前容器的状态,就可以通过commit提交,获得一个镜像。

到了这里我们才算是入门了Docker

容器数据卷*********

什么是容器数据卷

docker的理念回顾:将应用和环境打包成一个镜像!运行之后就变成一个容器。如果数据都在容器中,那么我们删除容器,数据就会丢失 需求:数据持久化

举例:我们用docker跑了一个mysql,跑了一段时间之后我们把它给停了然后把mysql这个容器给删了,那就相当于这段时间的数据也被我们删了,这可就是真的删库跑路了。需求:mysql数据可以存储在本地

容器之间有一个数据共享技术!,Docker容器中产生的数据,同步到本地。这就是卷技术,说白了就是目录的挂载,价格我们容器内的目录,挂载到Linux上!

总结:容器的持久化和同步操作!容器间也是可以数据共享的!

使用数据卷

方式一:直接使用命令挂载 -v

docker run -it -v 主机目录:容器那内目录 # 测试
docker run -it -v /home/ceshi:/home centos /bin/bash
# 启动之后可以通过docker inspect 容器id查看

此时两个容器内的目录home和容器外的home/ceshi目录就是打通状态。可以理解为双向绑定

反过来也是一样的。

  1. 停止容器
  2. 宿主机上修改文件
  3. 启动容器
  4. 容器内的数据依旧是同步的

好处:我以后修改只需要在本地修改即可,容器内会自动同步。

实战测试:安装MySQL

思考:MySQL的数据持久化的问题!

# 获取镜像
docker pull mysql:5.7
# 运行容器,需要做数据挂载!可以同时挂载多个目录
# 安装启动mysql的时候要注意,一定需要配置密码。
docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
# 参数解释
-d 后台运行
-p 端口映射
-v 卷挂载
-e 环境配置(此次为配置mysql密码)
--name 容器命名

现在可以自己测试一下。在远程的数据库上创建一个库,然后到自己本地目录下看看是不是也有,再反之。同样的也可以讲远程mysql容器直接干掉。然后看看自己本地存储的数据是否会丢失(一定不会)。这就实现了我们容器的持久化技术,也就是我们的卷技术。

具名挂载和匿名挂载

# 匿名挂载
-v 直接写容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx
# 查看所有的 volume 的情况
docker volume ls
# 这里发现,这种就是匿名挂载,我们在 -v只写了容器内的路径,没有写容器外的路径。 
# 具名挂载
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
# 这里我们在-v后只指定了一个容器外的文件而不是目录

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

我们通过具名挂载可以方便的找到我们的一个卷,大多数情况在使用具名挂载

# 如何确定是具名挂载还是匿名挂载,还是指定路径挂载!
-v 容器内路径  # 匿名挂载
-v 卷名:容器内路径   # 具名挂载
-v /宿主机路径:容器内路径  # 指定路径挂载

拓展:

# 通过-v 容器内路径:ro  rw 改写读写权限
ro  readonly # 只读
rw  readwrite # 可读可写# 一旦设置了容器权限  容器对我们挂载出来的内容就有限定了
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:/etc/nginx:ro nginxdocker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:/etc/nginx:ro nginx
# ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作的

Dockerfile就是用来构建docker镜像的构建文件!就是一段命令脚本!先体验一下

通过这个脚本可以生成一个镜像,镜像是一层一层的,脚本就是一个一个的命令,每个命令都是一层。

# 创建一个dockerfile文件, 名字可以随意,看一Dockerfile
mkdir dockerfile
vim dockerfile
# 文件中的内容
FROM centos
VOLUME ["volume01", "volume02"]  # 可以挂载多个
CMD echo "----end----"           # 打印----end----
CMD /bin/bash                    #
# 这里的每个命令就是镜像的一层

# 启动一下自己写的容器
docker run -it 镜像id /bin/bash

这个卷和外部一定有一个同步的目录!通过docker inspect命令查看

通过刚刚的查看我们知道,我生成的镜像中的挂载是匿名挂载

测试一下在容器内建一个文件,容器外是否有改变(自测)

这种方式我们未来使用非常多,因为我们通常会构建自己的镜像!

假设构建镜像时候没有挂在卷,要手动镜像挂载 -v 卷名:容器内路径!

数据卷容器


多个mysql同步数据!

# 启动3个容器,通过我们刚才自己写的镜像启动
docker run -it --name docker01 容器名:版本
docker run -it --name docker02 --volumes-from docker01 容器名:版本

然后我们在docker01容器中被挂载的目录里创建一个文件,然后到docker02容器中查看是否存在(自测)

在来个docker03,在docker03中创建一个文件docker03(自测去吧)

然后我们在停止并删除docker01,查看docker02和docker03是否还可以访问这个文件,测试依旧可以访问

多个mysql实现数据共享

docker run -it -p 3310:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7docker run -it -p 3310:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volume-from mysql01 mysql:5.7
# 这个时候,可以实现两个容器数据同步

结论:

容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止(所有使用它的容器都停止为止),但是一但你持久化到了本地,这个时候,本地的数据是不会删除的!

DockerDile**************

Dockerfile就是用来构建docker镜像的构建文件!就是一段命参数令脚本!先体验一下

通过这个脚本可以生成一个镜像,镜像是一层一层的,脚本就是一个一个的命令,每个命令都是一层。

构建步骤:

  1. 编写一个dockerfile文件
  2. docker build 构建成为一个镜像
  3. docker run 运行镜像
  4. docker push 发布镜像(docker hub、阿里云镜像仓库!)

查看一下官方是怎么做的

很多官方镜像都是基础包,很多功能都没有,我们通常会自己搭建自己的镜像!

官方既然可以制作镜像,那我们也可以!

DockerFile构建过程


基础知识:

  1. 每个保留关键字(指令)都必须是大写字母
  2. 执行从上到下顺序执行
  3. #表示注释
  4. 每一个指令都会创建提交一个新的镜像层!

dockerfile是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile文件,这个文件十分简单!

Docker镜像逐渐成为企业交付的标准,必须要掌握

DockerFile:构建文件,定义了一切的步骤,源代码

DockerImages:通过DockerFile构建生成的镜像,最终发布和运行的产品

Docker容器:容器就是镜像运行起来提供服务的

DockerFile指令:


以前我们都是使用别人的,现在我们知道了这些指令后,我们来练习自己写一个镜像!

FROM            # 基础镜像,一切从这里开始后构建
MAINTAINER      # 镜像是谁写的,姓名+邮箱
RUN             # 镜像构建的时候海需要运行的命令
ADD             # 步骤:tomcat镜像,这个tomcat的压缩包!添加内容
WORKDIR         # 镜像的工作目录
VOLUME          # 挂载的目录
EXPOSE          # 暴露端口配置
CMD             # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT      # 指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD         # 当构建一个被继承 DockerFile的容器时,就会运行ONBUILD指令,出发指令。
COPY            # 类似ADD命令,将我们的文件拷贝到镜像中
ENV             # 构建的时候设置光环境变量!

实战测试


Docker Hub中99%的镜像都是从这个基础镜像过来的 FEOM scratch,然后配置需要的软件和配置来进行构建的!!!

官方的centos镜像很多基础的命令都没有,比如ll,vim等。镜像都是被压缩过的!我们要自己做一个centos镜像来添加这个命令

# 首先创建dockerfile的文件,名字随意
自己创建(略)
# vim打开这个文件
自己打(略)
# 进入文件之后
FROM centos                            # 基础镜像
MAINTAINER minato<taizi911203@126.com> # 作者
ENV MYPATH /usr/local                  # 我自己进去的目录是哪
WORKDIR $MYPATH                        # 工作目录,$是取地址,这里我们将工作目录指定到了我们刚刚指定的MYPATH上
RUN yum -y insgall vim                 # 构建镜像时安装cim命令
run yum -y install net-tools           # 这个是ifconfig的命令
EXPOSE 80                              # 默认暴露端口80
CMD echo $MYPATH                       # 输出这个地址
CMD echo "----end----"                 # 输出这句话
CMD /bin/bash                          # 启动后进入哪个命令行
# 构建镜像
docker build -f dockerfile文件路径 -t 镜像名:版本号 . # 一定注意后面这个点 .
# 运行我们自己写的centos
# 尝试运行我们添加的命令,如vim等

对比:原来的centos

我们自己写的

我们可以列出镜像变更的历史

docker history 容器ID

我们平时拿到一个镜像,可以研究一下它是怎么做的了!

CMD 和ENTRYPOINT的区别

CMD添加的命令 比如我添加一个ls -a,添加完了之后。我docker run这个容器他就会执行这个ls -a打印我们当前目录下的所有内容。但是如果我执行docker run的时候 我突然想在加一个-l。也就是说我想最终执行的命令是ls -al。你会发现它会报错,这就是我在运行docker run -l的时候这个-l替换掉了原本的ls -a。而ENTRYPOINT就不会。如果我们构建镜像的时候是用ENTRYPOINT添加的命令。那么在运行容器是添加了一个-l参数,它最终执行的就会是 ls -al

发布自己的镜像


Docker Hub

  1. 在https://hub.docker.com/ 注册自己的账号
  2. 确定这个账号可以登录
  3. 在我们服务器上登录自己的账号
docker login -u 账号
# 回车后输入密码
  1. 登录完毕后就可以提交镜像了,就是一步, docker push
docker push minato/tomcat:1.0  # 带上作者名和版本号
# ***自己平时构建的镜像一定也要带上版本号,不然无法提交到仓库

发布到阿里云服务上

  1. 登录阿里云
  2. 找到容器机构箱服务
  3. 创建命名空间
  4. 创建容器镜像

自己看吧,人家写得很清楚了

Docker所有流程小结

Docker 网络*********

理解Docker0

清空所有环境

docker rmi -f(docker images -aq)

测试

id addr # 或者当前的ip地址

三个网络

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

查看容器内部网络 ip addr,发现容器启动的时候会得到一个eth0@if262这样的地址,docker分配的

思考:主机能不能ping这个地址

外部主机是可以ping通容器内部的

思考:主机可以ping通任何容器内的地址。那么容器之间是否能ping通其他容器呢?

原理

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

启动一个容器后再次测试ip addr,发现又多了一对网卡

我们发现它的地址和容器内部的地址惊人的相似。261,262

我们每启动一个容器,都会多一对网卡(容器内和容器外),这种一对一对的就是用了veth-pair技术

veth-pair技术

就是一对的虚拟设备借口,他们就是成对出现的,一端连着协议,一端彼此相连,正因为有这个特性,veth-pair充当一个桥梁,连接各种虚拟网络设备的。

openstac,Docker容器之间的连接,ovs的连接,都是使用veth-pair技术

所以同一宿主机下的容器与容器之间是可以ping通的。

总结:tomcat01和tomcat02是公用一个路由的,docker0.

所有的容器不指定网络的情况下,都是docker0路由的,docker会给我们的容器分配一个默认的可用ip

Docker使用的是linux的桥接,宿主机是Docker容器的网桥docker0。如果不指定网络,docker就会自动往这里分配,大约65535个

Docker中的所有网络借口都是虚拟的,虚拟的转发效率高。

只要容器删除,对应的网桥一对就没了。

思考一个问题,我们每一次启动一个容器,它的网段都会改变,因为是随机的,比如我这次启动了一个mysql,停止了之后我再次启动,它的ip就会改变。那我们随之的配置是不是也要改变。那我们在跑起来一个容器的时候不是给容器命了名吗,我们能不能用这个名字ping通这个容器,如果可以,那就代表这个mysql即使崩了重启导致ip改变了也没事。我们想做到的就是项目不重启,数据库ip换了,我们希望恶意处理这个问题,可以通过名字来进行访问。

容器互联–link

我们现在启动连个容器mysql01和mysql02,尝试着用mysql01 ping mysql02,发现是ping不通的。那如何可以解决呢?

那我们在启动一个mysql03

docker run -d -P --name mysql03 --link mysql02 mysql
# 然后我们再尝试用mysql03 ping mysql02
docker exec -it mysql03 ping mysql02

这次发现可以ping通了

但是如果我们用mysql02 去ping mysql03是不能ping通的,那怎么办?

# 我们可以通过docker network inspect 容器id 来查看网络详情

我们可以直接通过查看配置文件,/etc/host,这个文件就是配置本地绑定的,比如我们在这个下面绑定了百度,那么以后我们以后输入百度就会直接访问我们绑定的地址。

这就是根本原因,我们访问mysql03的时候,相当于直接访问了mysql02,所以他们是可以ping通的

本质本就:–link就是我们在hosts配置中增加了另一个容器的绑定,但是另一个容器却没有绑定这个容器,所以才是单项连接。

我们现在玩docker已经不建议使用–link了!我们需要的是自定义网络。不想使用这个docker0.因为官方的这个docker0是局限的。它不支持容器名连接访问!

自定义网络

网络模式:

  1. bridge:桥接(默认,自己创建也使用桥接模式) docker搭桥 0.1 0.2 0.3
  2. none: 不配置网络
  3. host: 和宿主机共享网络
  4. container : 容器内网络连通(用的少)

测试

# 我们直接启动的命令 默认都有一个 --net bridge操作,而这个就是我们的docker0
docker run -d -P --name tomcat01
docker run -d -P --name tomcat01 --net bridge tomcat
# docekr特点:默认,域名不能访问, --link可以打通连接
# 我们可以自定义一个网络
docker network create --driver bridge -subnet 192.168.0.0/16 -gateway192.168.0.1 mynet
# 参数说明
--dirver # 指明我们要创建一个网络
bridge   # 连接模式:桥接
--subnet # 子网(/16代表我可以创建65535个,如果是/24就说明只能创建255个)
--gatway # 网关
myney    # 我们创建的这个网络名

前面自己的一个网络就创建好了

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

此时我们在去尝试两个容器互ping,发现是可以互通的。也就是说我们自定义的这个网络修复了docker0的不可互通的缺点。

我们自定义的网络,docker都已经帮我们维护好了关系,推荐我们平时使用自定义网络。

好处:不同的集群使用不同的网络,宝恒集群是安全和健康的。

比如我有一个redis集群和一个mysql集群。每个集群都有各自的网络,各自是互通的。但是对外确实隔离的。

网络连通

我们现在在我们自定义的mynet网络上连了两个容器tomcat-net-01、tomcat-net-02,那此时,我们在把docker0上也加两个容器tomcat01和tomcat02,那么,tomcat01是一定无法ping通tomcat-net-01的。而且我们还不能直接将这两个容器直接连通,因为网络性质就变了,那怎么办?

上图命令就可以将一个容器连接到一个网络上

# 测试 打通tomcat01 =》mynet
docker network connect mynet tomcat01
# 查看mynet的网络详情
docker network inspect mynet
# 连通之后就是讲tomcat01 放到了mynet网络下
# 一个容器两个ip地址,好比阿里云,公网ip,私网ip。一个道理。就是这么暴力

结论:结社要跨网络曹旭哦别人买酒需要使用docker network connect连通

实战:部署Redis集群。

# 先建一个redis集群的网卡
docker network create redis --subnet 172.38.0.0/16

用脚本一次性创建6个redis配置文件

for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/mode-${port}/conf/redis.conf
cat <<EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done

分别启动这几个redis,记得改变名称跟ip

docker run -p 6371:6379 -p 16371:16379 --name redis-1\
-v /mydaya/redis/node-1/data:/data \
-v /mydaya/redis/node-1/conf/redis.conf:/ect/redis/redis.conf \
-d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /ect.redis/redis.conf

随便进入一个redis

docker exec -it redis-1 /bin/sh # 切记redis下是没有bash的,是sh,其实都一样
redis -cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
# 接下来会问你是否要这么配置,选yes就可以了

连接集群

redis-cli -c   # 一定要加-c  不然是单机不是集群
cluster info   # 查看集群
cluster nodes  # 查看节点

然后我们就可以完集群了。首先要试一下redis集群的高可用

我们随便set 一个值看看集群给我们分配在了那个redis上。因为我们没有设置分片,所以都会是主机。然后我们在停掉这台主机,看看还能否get到这个值

Docker小结

docker最细节的笔记相关推荐

  1. Docker基础、进阶笔记,为k8s的学习预预热

    标题 Docker基础.进阶笔记,为k8s的学习预预热 笔记来源于视频: 狂神docker基础篇 狂神docker进阶篇 笔记中图片有些取自于:这位博主的两篇docker笔记中的一些图片 百度云笔记工 ...

  2. Docker快速入门总结笔记

    文章目录 1. Docker概述 2. 虚拟化技术和容器化技术 3. Docker的基本组成 4. Docker的安装 5. Docker的卸载 6. 配置阿里云镜像加速 8. Docker容器运行流 ...

  3. 云原生之使用Docker部署Dailynotes个人笔记管理工具

    云原生之使用Docker部署Dailynotes个人笔记管理工具 一.Dailynotes介绍 二.检查本地docker环境 1.检查docker版本 2.检查docker状态 三.下载Dailyno ...

  4. Docker学习及实践笔记

    Docker笔记基础 " 不可只停留于run 开端口 " 1. Docker的组成 镜像(images): 通过镜像这个模板来创建容器服务,镜像 ==> run ==> ...

  5. docker 安装git_docker随手笔记第十二节 jenkins+docker+nginx+纯静态页面配置

    docker随手笔记第一节 docker概念及安装 docker随手笔记第二节 docker常用命令解析 docker随手笔记第三节 docker构建java镜像 docker随手笔记第四节 dock ...

  6. 《 第一本Docker书 》读书笔记 --- Docker 各项操作命令及参数说明(docker run 命令各个参数说明)

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. PS :个人所有读书笔记只记录个人想要的内容,很可能原书大量内容没有纳入笔记中... ... 1. ...

  7. docker搭建私有仓库笔记

    为什么80%的码农都做不了架构师?>>>    安装之前需要docker环境,安装docker环境参考:http://www.runoob.com/docker/centos-doc ...

  8. 【Docker】谷粒商城笔记记录:P104运行ElasticSearch容器

    在Docker中运行ES 感觉老师这一p讲解非常详细,因此做了笔记以供参考 下载镜像文件 注意ES和Kibana两个版本要统一 docker pull elasticsearch:7.4.2 #存储和 ...

  9. Docker基础讲解狂神笔记:容器数据卷,docker compose,docker swarm(2/2)未修订版欢迎留言补漏

    L06 容器数据卷 相对于01-05难度加大 什么是容器数据卷 Docker理念:将应用和环境打包成一个镜像! 程序要保存数据,数据并不能放在容器里面,因为一旦数据放在容器,误操作删除掉容器,数据也随 ...

最新文章

  1. python右对齐 数字_如何在Python中右对齐数值数据?
  2. 【CV秋季划】人脸关键点检测,人脸识别视频更新
  3. [原创 - 尚学堂科技 - 马士兵老师]
  4. colmak键盘_Colemak键盘布局学习
  5. 完美解决java.lang.OutOfMemoryError: bitmap size exceeds VM budget
  6. centos php 显示错误提示,Centos下编译php的典型错误及解决
  7. ubuntu复制文件到另一个文件夹_Excel VBA之FSO-2.2文件夹的复制
  8. NLP学习—20.WMD paper及代码
  9. 2021-08-20 JSP JSTL标签
  10. 关于烧写ESP8285核心板的相关事项
  11. 公达 TP-POS58G 打印机驱动
  12. 今天发一个制作课工场论坛发帖
  13. PowerDesigner显示Comment注释
  14. 阿里巴巴的东南亚战事:另一次拼多多式的奇袭
  15. 打印文字一边清晰而另一边不清晰的解决方法
  16. Latex表格换行垂直居中对齐
  17. 【软件工程】--设计阶段
  18. 仿天猫 购物车(Android studio 仿天猫 详情页面 添加购物车选择 颜色 尺寸demo)
  19. 越南籍学生如何进行计算机教学,浅谈计算机教学中越南文输入法的注意事项
  20. RK3399 / AR9201 / Tegra K1 /hi3559A / RV1126对比

热门文章

  1. [一篇读懂]C语言五讲:指针
  2. 大量企业取消微信办公,只因数据泄露屡禁不止
  3. 失败产品手册二:一款游戏租号产品的败局
  4. 设计经验分享:如何打造一款优质的品牌标识
  5. 运动防水耳机哪个牌子好、防水运动耳机品牌排行榜
  6. 群集共享卷建文件服务器,在故障转移群集中使用群集共享卷
  7. VGG Convolutional Neural Network Practical
  8. # 时间 减去 多少小时 python
  9. 如何解决绘图过程中的闪烁
  10. C++ 虚析构函数与析构函数的区别