视频地址:【疯神说Java】Docker最新的超详细版教程通俗易懂_哔哩哔哩_bilibili
这个视频讲棒。。
docker概述
docker为什么会出现?
docker的历史
docker能干嘛
docker安装
docker的基本组成
安装docker
阿里云镜像加速
回顾hello-world流程
底层原理
docker的常用命令
Docker镜像讲解
镜像是什么
Docker镜像加载原理
分层理解
commit镜像
容器数据卷
容器数据卷是什么?
使用数据卷
实战:安装mysql
具名和匿名挂载
初识DockerFile
数据卷容器
Docker File
Docker File介绍
DockerFile的构建过程
DockerFile的指令
实战:Tomcat镜像
发布自己的镜像
Docker 网络
理解docker0
--link
自定义网络-toc" style="margin-left:80px;">自定义网络
网络连通
实战:部署redis集群
SpringBoot微服务打包docker镜像
docker概述
docker为什么会出现?
产品,开发-上线,两套环境!应用环境,应用配置!
开发:可以在我的电脑上运行... 运维:服务器没问题...
发布一个项目(jar (redis,mysql ...), 项目能否带运行环境包装!
java --- jar(环境) --- 带环境的包装项目 --- docker仓库:商店 --- 下载发布的镜像-操作!
docker提出上述问题的解决方案!
docker想法来自集装箱!
隔离:docker核心思想!包装箱!每个盒子都是隔离的
docker服务器可以通过隔离机制使用!
docker的历史
Docker的历史 2010年,几个搞IT的年轻人,就在美国成立了一家公司dotcloud
做一些pass云计算服务! LXC相关容器技术! 他们把自己的技术(容器化技术)命名为Docker ! Docker刚出生的时候,并没有引起业界的关注! dotCloud,活不下去! 开源
开发源代码!
2013年,Docker开源!
Docker越来越多的人发现了docker优点!Docker每月更新一个版本!
2014年4月9日,Docker1.0发布!
Docker为什么这么火?
在容器技术出来之前,我们都使用虚拟机技术!
虚拟机∶在window中装一个Vmware,我们可以通过这个软件虚拟一台或多台电脑!
虚拟机也属于虚拟化技术,Docker容器技术,也是虚拟化技术!
vm : linux centos原生镜像(一台电脑!)隔高,需要打开多个虚拟机!―几分钟G docker,隔离、镜像(核心环境) 4m jdk mysq1)十的小巧,运行镜像就可以了!小巧!几个M KB秒级启动!
到现在,所有开发人员都必须要会Docker !
docker能干嘛
之前的虚拟化技术!
虚拟机技术缺点:
1.资源占用十分多
2.冗余步骤非常多
3.启动很慢
容器化技术
==容器化技术不是模拟完整的一个操作系统==
比较docker和虚拟机技术的不同:
-
传统虚拟机,虚拟出一套硬件,运行一个完整的操作系统,然后在这个系统上安装和运行 软件
-
容器内的应用直接运行在宿主机的内核,容器是没有内核的,也没有虚拟硬件,所有就轻便了
-
每个容器间是互相隔离,每个容器内都有一个属于自己的文件系统,互不影响
DevOps ( 开发、运维 )
传统:一堆帮助文档,安装程序
docker:打包镜像发布测试,一键运行
使用 docker之后,我们部署应用就和搭积木一样
项目打包为一个镜像,扩展服务器A!到服务器B
在容器化之后,我们的开发,测试环境都是高度一致的
docker是内核级别的虚拟化,可以在一个物理机上运行很多个容器实例,服务器的性能可以压榨到机制
docker安装
docker的基本组成
docker镜像就好比一个模板,可以通过这个模板来创建容器服务,通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)
docker利用容器技术,独立运行一个或者一组应用,通过镜像来创建的
启动,停止,删除,基本命令!
目前就可以把这个容器理解为就是一个简易的linux系统
仓库就是存放镜像的地方!
仓库分为公有仓库和私有仓库!
docker hub (默认是国外的)
阿里云。。。都要容器服务器(配置镜像加速)
安装docker
环境准备
安装
# 1、卸载旧的版本 sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine # 2、需要的安装包 yum install -y yum-utils # 3、设置镜像的仓库 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 makecache fast # 4. 安装docker docker-ce社区版 ee企业版 yum install docker-ce docker-ce-cli containerd.io # 5.启动docker systemctl start docker # 6.使用docker version 检测是否安装成功 # 7.运行hallo world docker run hello-world # 查看下载的hello-world镜像 docker images
了解:卸载docker
# 1.卸载依赖 yum remove docker-ce docker-ce-cli containerd.io # 2.删除资源 rm -rf /var/lib/docker # /var/lib/docker docker的默认工作路径
阿里云镜像加速
配置使用:
sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://1wvif11x.mirror.aliyuncs.com"] } EOF sudo systemctl daemon-reload sudo systemctl restart docker
回顾hello-world流程
底层原理
docker是一个Client-Server结构的系统,docker的守护进程运行在主机上。通过socket从客户端进行访问!
Docker-Server接收到Docker-Client的指令,就会执行这个命令!
-
Docker有着比虚拟机更少的抽象层。
-
docker利用的是宿主机的内核,vm需要是Guest OS。
所以说,新建一个容器的时候,docker不需要像虚拟机一样重新加载一个操作系统,避免引导。虚拟机是加载Guest OS,分钟级别的;而docker是利用宿主机的操作系统,省略了这个复杂的过程,秒级!
docker的常用命令
帮助命令
docker version # 显示docker的版本的信息 docker info # 显示docker的系统信息,包括镜像和容器的数量 docker 命令 --help # 帮助命令
帮助文档的地址:Reference documentation | Docker Documentation
镜像命令
,查看所有的镜像
[root@heyinchang /]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest feb5d9fea6a5 4 weeks ago 13.3kB # 解释 REPOSITORY # 镜像的仓库源 TAG # 镜像的标签 IMAGE ID # 镜像的id CREATED # 镜像的创建时间 SIZE # 镜像的大小 # 可选项 -a, --all # 列出所有的镜像 -q, --quiet # 只显示镜像的id
[root@heyinchang /]# docker search mysql NAME DESCRIPTION STARS OFFICIAL mysql MySQL is a widely used, open-source relation… 11598 [OK] mariadb MariaDB Server is a high performing open sou… 4414 [OK] # 可选项 通过搜索来过滤 --filter=STARS=3000 # 搜索出来的镜像就是stars大于3000的 [root@heyinchang /]# docker search mysql --filter=stars=5000 NAME DESCRIPTION STARS OFFICIAL mysql MySQL is a widely used, open-source relation… 11598 [OK]
# 下载镜像 docker pull 镜像名 [:tag] [root@heyinchang /]# docker pull mysql Using default tag: latest # 如果不写tag,默认就是latest(下载最新版) latest: Pulling from library/mysql b380bbd43752: Pull complete # 分层下载,docker image的核心,联合文件系统 f23cbf2ecc5d: Pull complete 30cfc6c29c0a: Pull complete b38609286cbe: Pull complete 8211d9e66cd6: Pull complete 2313f9eeca4a: Pull complete 7eb487d00da0: Pull complete 4d7421c8152e: Pull complete 77f3d8811a28: Pull complete cce755338cba: Pull complete 69b753046b9f: Pull complete b2e64b0ab53c: Pull complete Digest: sha256:6d7d4524463fe6e2b893ffc2b89543c81dec7ef82fb2020a1b27606666464d87 Status: Downloaded newer image for mysql:latest docker.io/library/mysql:latest # 真实地址 docker pull mysql 等价于 docker pull docker.io/library/mysql:latest # 下载指定版本 docker pull mysql:5.7
[root@heyinchang /]# docker rmi -f 938b57d64674 # 删除指定的镜像 [root@heyinchang /]# docker rmi -f 镜像id 镜像id 镜像id # 删除多个镜像 [root@heyinchang /]# docker rmi -f $(docker images -aq) # 删除全部镜像
容器命令
说明: 我们有了镜像才可以创建容器,linux,下载一个centos镜像来测试学习
docker pull centos
新建容器并启动
docker run [可选参数] image # 参数说明 --name="name" 容器名字, tomcat01 Tomcat02 来区分容器 -d 后台方式运行 -it 使用交互式运行,进入容器查看内容 -p 指定容器的端口 -p 8080:8080 -p 主机端口:容器端口(常用) -p 容器端口 -P 随机指定端口 # 测试 启动并进入容器 [root@heyinchang /]# docker run -it centos /bin/bash [root@4a0b0d394adc /]# ls bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var # 从容器中退回主机 [root@4a0b0d394adc /]# exit exit [root@heyinchang /]#
# docker ps命令 # 显示正在运行的容器 -a # 历史运行过的容器 -n=? # 显示最近创建的容器 -q # 只显示容器的id
exit # 直接容器停止退出 ctrl + P + Q # 容器不停止退出
docker rm 容器id # 指定删除容器,不能删除正在运行的容器,如果要强制删除,要强制rm -f docker rm -f $(docker ps -aq) # 删除所有的容器 docker ps -aq | 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发现没有应用,就会自动停止 # nginx,容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了
docker logs -tf --tail 10 容器id # 没有日志 # 自己编写一段shell脚本 docker run -d centos /bin/sh -c "while true; do echo heyinchang; sleep 1; done" # 显示日志 [root@heyinchang /]# docker logs -tf --tail 10 c1f2d2694905 # 查看10条日志 [root@heyinchang /]# docker logs -tf c1f2d2694905 # 查看所有日志
[root@heyinchang /]# docker top c1f2d2694905 UID PID PPID C STIME TTY TIME CMD root 27040 26986 0 19:54 ? 00:00:00 /bin/sh -c while true;do echo heyinchang;sleep 1;done root 27646 27040 0 19:58 ? 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1
# 命令 docker inspect 容器id # 测试 [root@heyinchang /]# docker inspect c1f2d2694905 [ { "Id": "c1f2d26949058fa9e3206bac45890b71e783617a98a93a5fd177345cceca17be", "Created": "2021-10-28T11:54:38.506714556Z", "Path": "/bin/sh", "Args": [ "-c", "while true;do echo heyinchang;sleep 1;done" ], "State": { "Status": "running", "Running": true, "Paused": false, "Restarting": false, "OOMKilled": false, "Dead": false, "Pid": 27040, "ExitCode": 0, "Error": "", "StartedAt": "2021-10-28T11:54:38.864645403Z", "FinishedAt": "0001-01-01T00:00:00Z" }, "Image": "sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6", "ResolvConfPath": "/var/lib/docker/containers/c1f2d26949058fa9e3206bac45890b71e783617a98a93a5fd177345cceca17be/resolv.conf", "HostnamePath": "/var/lib/docker/containers/c1f2d26949058fa9e3206bac45890b71e783617a98a93a5fd177345cceca17be/hostname", "HostsPath": "/var/lib/docker/containers/c1f2d26949058fa9e3206bac45890b71e783617a98a93a5fd177345cceca17be/hosts", "LogPath": "/var/lib/docker/containers/c1f2d26949058fa9e3206bac45890b71e783617a98a93a5fd177345cceca17be/c1f2d26949058fa9e3206bac45890b71e783617a98a93a5fd177345cceca17be-json.log", "Name": "/cool_payne", "RestartCount": 0, "Driver": "overlay2", "Platform": "linux", "MountLabel": "", "ProcessLabel": "", "AppArmorProfile": "", "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, "CgroupnsMode": "host", "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/dd787179c8fc858d0f812950bc6b7bb3efb646b1fb540495b31cdfda21f31219-init/diff:/var/lib/docker/overlay2/fe5c5fd0c8780790a981564a209f82be30a23f84b031b48d115a271de68ec86b/diff", "MergedDir": "/var/lib/docker/overlay2/dd787179c8fc858d0f812950bc6b7bb3efb646b1fb540495b31cdfda21f31219/merged", "UpperDir": "/var/lib/docker/overlay2/dd787179c8fc858d0f812950bc6b7bb3efb646b1fb540495b31cdfda21f31219/diff", "WorkDir": "/var/lib/docker/overlay2/dd787179c8fc858d0f812950bc6b7bb3efb646b1fb540495b31cdfda21f31219/work" }, "Name": "overlay2" }, "Mounts": [], "Config": { "Hostname": "c1f2d2694905", "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 heyinchang;sleep 1;done" ], "Image": "centos", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": { "org.label-schema.build-date": "20210915", "org.label-schema.license": "GPLv2", "org.label-schema.name": "CentOS Base Image", "org.label-schema.schema-version": "1.0", "org.label-schema.vendor": "CentOS" } }, "NetworkSettings": { "Bridge": "", "SandboxID": "1d5e9105da9d3643cdb8d06ea842ae472f143b008a7873e95b83a669b0ab27b4", "HairpinMode": false, "LinkLocalIPv6Address": "", "LinkLocalIPv6PrefixLen": 0, "Ports": {}, "SandboxKey": "/var/run/docker/netns/1d5e9105da9d", "SecondaryIPAddresses": null, "SecondaryIPv6Addresses": null, "EndpointID": "8cef86ba61ba067950e022fba9d44409b05fc1fa293bf4d09eaacd1a81330a76", "Gateway": "172.17.0.1", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "IPAddress": "172.17.0.2", "IPPrefixLen": 16, "IPv6Gateway": "", "MacAddress": "02:42:ac:11:00:02", "Networks": { "bridge": { "IPAMConfig": null, "Links": null, "Aliases": null, "NetworkID": "000be53bcfef22eeedc1350c1bfedc9743fc907ef835ae8a77b3c165d52f7b89", "EndpointID": "8cef86ba61ba067950e022fba9d44409b05fc1fa293bf4d09eaacd1a81330a76", "Gateway": "172.17.0.1", "IPAddress": "172.17.0.2", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:11:00:02", "DriverOpts": null } } } } ]
# 我们通常容器都是后台方式运行的,需要进入容器,修改一些配置 # 命令 docker exec -it 容器id /bin/bash [root@heyinchang /]# docker exec -it c1f2d2694905 /bin/bash [root@c1f2d2694905 /]# ls bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var [root@c1f2d2694905 /]# ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 11:54 ? 00:00:00 /bin/sh -c while true;do echo heyinchang;sleep 1;done root 783 0 0 12:07 pts/0 00:00:00 /bin/bash root 855 1 0 12:08 ? 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1 root 856 783 0 12:08 pts/0 00:00:00 ps -ef # 方式二 docker attach 容器id # 测试 [root@heyinchang /]# docker attach c1f2d2694905 # 正在执行当前的代码。。。 # docker exec # 进入容器后开启一个新的终端,可以在里面操作(常用) # docker attach # 进入容器正在执行的终端,不会启动新的进程
docker cp 容器id:容器内路径 目的的主机路径 # 测试 # 进入docker容器 [root@heyinchang /]# docker attach 11c32f5e165b [root@11c32f5e165b /]# ls bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var [root@11c32f5e165b /]# cd home/ # 在容器内新建一个docker.txt文件 [root@11c32f5e165b home]# touch docker.txt [root@11c32f5e165b home]# ls docker.txt # 退出容器 [root@11c32f5e165b home]# exit exit # 将容器内的文件拷贝到本地/home目录下 [root@heyinchang /]# docker cp 11c32f5e165b:/home/docker.txt /home [root@heyinchang /]# cd /home [root@heyinchang home]# ls admin docker.txt hyc_doc redis www # 拷贝是一个手动过程,以后我们使用 -v卷的技术,可以实现同步
小结
作业练习
作业1:
docker 安装nginx
1.搜索镜像
2.下载镜像
3.运行容器
# 启动镜像创建容器,本地的3344端口映射到容器的80端口 # -d 后台运行 # --name 给容器起名字 # -p 宿主机端口:容器内部端口 [root@heyinchang home]# docker run -d --name nginx01 -p 3344:80 nginx b6512eb6a673cb1641354030c9078a42793f77426007b5e45e860e82a85fbbb3 [root@heyinchang home]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b6512eb6a673 nginx "/docker-entrypoint.…" 2 seconds ago Up 2 seconds 0.0.0.0:3344->80/tcp nginx01 [root@heyinchang home]# curl localhost:3344
作业2:
docker安装Tomcat
# 官方的使用 docker run -it --rm tomcat:9.0 # 我们之前的启动都是后台,停止了容器之后,容器还是可以查到,docker run -it --rm,一般用来测试,用完即删 # 拉取tomcat镜像 docker pull tomcat # 启动镜像,创建tomcat容器 docker run -d --name tomcat01 -p 3355:8080 tomcat # 进入tomcat容器 docker exec -it tomcat01 /bin/bash
思考问题:我们以后部署项目,如果每次都要进去容器配置十分麻烦?是不是在容器外部提供一个映射路径,达到在容器修改、配置文件。
作业3:部署es + kibana
# docker stats 查看cpu的状态 # es暴露的端口很多 # es十分的耗内存 1.xG! # es的数据一半需要放置到安全目录!挂载 # --net somenetwork ? 网络配置 # 启动elasticsearch docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.6.2 # 测试elasticsearch是否启动成功 [root@heyinchang home]# curl localhost:9200 { "name" : "149137daac89", "cluster_name" : "docker-cluster", "cluster_uuid" : "wF7Hi5fgRUuGOzUgLL73LQ", "version" : { "number" : "7.6.2", "build_flavor" : "default", "build_type" : "docker", "build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f", "build_date" : "2020-03-26T06:34:37.794943Z", "build_snapshot" : false, "lucene_version" : "8.4.0", "minimum_wire_compatibility_version" : "6.8.0", "minimum_index_compatibility_version" : "6.0.0-beta1" }, "tagline" : "You Know, for Search" } # 赶紧关闭,增加内存的限制,修改配置文件 -e 环境配置修改 docker run -d --name elasticsearch02 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.6.2 # docker stats 查看状态
可视化
-
portainer(先用这个)
docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
-
rancher(CI/CD再用)
docker图形化界面管理工具!提供一个后台面板供我们操作!
docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
测试访问:http://120.24.96.233:8088/
Docker镜像讲解
镜像是什么
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
所有的应用,直接打包docker镜像,就可以直接跑起来!
如何得到镜像:
-
从远程仓库下载
-
朋友拷贝给你
-
自己制作一个镜像 DockerFile
Docker镜像加载原理
UnionFS(联合文件系统)
我们下载的时候看到一层层的就是这个!
UnionFS ( 联合文件系统): Union文件系统(UnionFS )是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtualfilesystem)。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性︰一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
Docker镜像加载原理
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要引导加载kernel,linux刚启动时会加载bootfs文件系统,在docker镜像的最底层是bootfs。这一层与我们典型的linux/unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
rootfs(root file system),在bootfs之上。包含的就是典型的linux系统中的/dev, /proc, /bin, /etc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如ubuntu,centos等等。
分层理解
分层的镜像
我们去下载一个镜像,注意观察下载的日志输出,可以看到是一层一层的往下下载!
为什么docker镜像要采用这种分层的结构呢?
最大的好处,我觉得莫过于是资源共享了!比如有多个镜像都是从相同的base镜像构建而来,那么宿主机只需要在磁盘上保存一份base镜像,同时内存中也只需加载一份base镜像,这样就可以为所有的容器服务器了,而且镜像的每一层都可以被共享。
查看镜像分层的方式可以通过
所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单的例子,假如基于Ubuntu Linux 16.04创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。
该镜像当前已经包含3个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。
在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。下图中举了一个简单的例子,每个镜像层包含3个文件,而镜像包含了来自两个镜像层的6个文件。
上图中的镜像层跟之前的图片略有区别,主要目的是便于展示文件。
下图中展示了一个稍微复制的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层中的文件7是文件5的一个更新版本。
这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。
Linux上可用的存储引擎有AUFS、Overlay2、Device Mapper、Btrfs以及ZFS。顾名思义,每种存储引擎都基于Linux中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。
Docker在Windows 上仅支持windowsfilter一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和CoW[1].下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。
特点
docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!
这一层就是我们通常说的容器层,容器之下的都叫镜像层!
如何提交一个自己的镜像
commit镜像
docker commit 提交容器成为一个新的副本 # 命令个git原理类似 docker commit -m='提交的描述信息' -a='作者' 容器id 目标镜像名:[tag]
实战测试
# 1.启动一个默认的tomcat # 2.进入tomcat终端,发现默认的tomcat没有webapps # 3.自己拷贝进去的了基本的文件到webapps # 4.将操作过的容器通过commit提交为一个新的镜像!我们以后就使用修改过后的镜像即可,这就是我们自己修改过后的镜像
学习方式说明:理解概念,但是一定要实践,最后实践和理论相结合一次搞定。
如果你想要保存当前容器的状态,就可以通过commit来提交,获得一个镜像
学习完了以上内容,才算是docker入门!
容器数据卷
什么是容器数据卷
:将应用和环境打包成一个镜像!
数据?如果数据都在容器中,那么我们容器删除,数据也会丢失!==需求:数据可持久化==
mysql,容器删了,数据也丢失了!==需求:mysql数据可以存储在本地==
容器之间可以有一个数据共享的技术!docker容器中产生的数据,同步到本地!
这就是卷技术!目录的挂载,将我们容器内的目录,挂载到linux本地!
使用数据卷
方式一:直接使用命令来挂载 -v
docker run -it -v 主机目录:容器内目录
测试:
# 启动容器 [root@heyinchang home]# docker run -it -v /home/docker/centos:/home centos # 启动容器后,可以通过docker inspect 容器id # 来查看目录是否映射成功!
测试文件的同步:
再测试:
1.停止容器
2.在宿主机上修改文件
3.启动容器
4.观察容器内的文件是否同步被修改
实战:安装mysql
思考:mysql的数据持久化的问题!
# 获取镜像 docker pull mysql:5.7 # 运行容器,需要做数据挂载! # 安装启动mysql,需要配置密码,这是要注意的点! # 官方测试: docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag # 启动我们的 -d 后台启动 -p 端口映射 -v 卷挂载 -e 环境配置 [root@heyinchang /]# docker run -d -p 3310:3306 -v /home/docker/mysql/conf:/etc/mysql/conf.d -v /home/docker/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7 # 启动成功之后,启动本地的sql可视化工具来测试一下
具名和匿名挂载
# 匿名挂载 -v 容器内路径 docker run -d -p --name nginx01 -v /etc/nginx nginx [root@heyinchang /]# docker run -d -P --name nginx01 -v /etc/nginx nginx 60f33e1e44e66bc8a7da57185e0dc92b859798ffc0f06f0f1e3781735d32fab8 # 查看所有的 volume的情况 docker volume ls [root@heyinchang /]# docker volume ls DRIVER VOLUME NAME local 0d83f86b0ead271e855fab6285b7cce1d73af39942e25345ea790a8af7de9f5e ... # 我们发现,这种就是匿名挂载,我们再-v只写了容器内的路径,没有写容器外的路径 # 具名挂载 # 通过-v 卷名:容器内路径 docker run -d -P --name nginx01 -v juming-nginx:/etc/nginx nginx
# 查看一下这个卷 [root@heyinchang /]# docker volume inspect juming-nginx [ { "CreatedAt": "2021-10-29T19:54:19+08:00", "Driver": "local", "Labels": null, "Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data", # 改卷存放的路径 "Name": "juming-nginx", "Options": null, "Scope": "local" } ]
所有的docker容器内的卷,没有指定目录的情况下的都是在
/var/lib/docker/volumes/xxx/_data
我们通过具名挂载可以方便的找到我们的一个卷,大多数情况使用
具名挂载
# 如何确定是具名挂载还是匿名挂载,还是指定路径挂载? -v 容器内路径 # 匿名挂载 -v 卷名:容器内路径 # 具名挂载 -v /本地路径:容器内路径 # 指定路径挂载
# 通过 -v 容器内路径:ro 或者 :rw 改变读写权限 # ro read only # 只读 # rw read write # 可读写 docker run -d -P --name nginx01 -v juming-nginx:/etc/nginx:ro nginx docker run -d -P --name nginx01 -v juming-nginx:/etc/nginx:rw nginx
初识DockerFile
方式二:通过dockerfile 创建数据卷
DockerFile就是用来构建docker镜像的构建文件!命令脚本!
通过这个脚本可以生成镜像,镜像是一层一层的的,脚本是一个个的命令,每个命令都是一层!
# 1.在本机home目录下创建docker-test-volume文件夹 # 2.cd 文件夹,创建dockerfile1文件 # 3.vim 编写一个脚本 指令(大写) 参数 FROM centos VOLUME ['volume01','volume02'] CMD echo '---end---' CMD /bin/bash # 这里的每个命令,就是镜像的一层 # 4.运行脚本 docker build -f /home/docker-test-volume/dockerfile1 -t hyc/centos:1.0 .
# 启动我们自己创建的镜像
查看卷挂载的路径:docker inspect 容器id
# 在容器卷目录下新建一个文件,看能否同步到宿主机上 [root@6bf514fb82ed /]# cd volume01 [root@6bf514fb82ed volume01]# ls [root@6bf514fb82ed volume01]# touch container.txt [root@6bf514fb82ed volume01]# ls container.txt # 进入宿主机对应的挂载目录 [root@heyinchang ~]# cd /var/lib/docker/volumes/02080b1232c43f954c5fd2ea985578bf3281dba299dc043a68985f2d925f5dc5/_data [root@heyinchang _data]# ls container.txt # 发现在容器内卷中创建的文件,同步到了宿主机挂载的目录下 # 这种方式我们未来用的十分多,因为我们通常会构建自己的镜像! # 假设构建镜像时没有挂载卷,要手动镜像挂载 -v卷名:容器内路径!
数据卷容器
多个mysql同步数据
# 实现容器数据卷 --volumes-from 父容器 # 启动一个我们刚刚创建的容器 [root@heyinchang docker-test-volume]# docker run -it --name docker01 hyc/centos:1.0 # 再启动一个容器,--volumes-from docker01 [root@heyinchang docker-test-volume]# docker run -it --name docker02 --volumes-from docker01 hyc/centos:1.0 # 在docker01 数据卷内新建文件,会自动同步到docker02中
多个mysql实现数据共享
docker run -d -p 3310:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7 docker run -d -p 3310:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 --volumes-from mysql01 mysql:5.7 # 这个时候,可以实现两个容器数据同步!
容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。
但是一旦你持久化到了本地,这个时候,本地的数据是不会删除的!
Docker File
Docker File介绍
dockerfile是用来构建docker镜像的文件! 命令参数脚本!
构建步骤:
编写一个dockerfile文件
docker build构建成为一个镜像
docker run 运行镜像
docker push发布镜像(docker Hub,阿里云镜像仓库)
DockerFile的构建过程
-
每个保留关键字(指令)都必须是大写字母
-
执行顺序从上到下
-
‘#’ 表示注释
-
每一个指令都会创建提交一个新的镜像层,并提交!
dockerfile是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile文件,这个文件十分简单!
docker镜像逐渐成为了企业交付的标准,必须要掌握!
DockerFile:构建文件,定义了一切的步骤,相当于源代码
DockerImages:通过DockerFile构建生成的镜像,最终发布和运行的产品。
Docker容器:容器就是镜像运行起来提供服务的。
DockerFile的指令
以前我们都是用别人的镜像,现在我们知道这些指令后,我们来练习自己构建一个镜像!
FROM # 基础镜像,一切从这里开始 MAINTAINER # 镜像是谁写的,姓名+邮箱 RUN # 镜像构建的时候需要运行的命令 ADD # 添加内容 WORKDIR # 镜像的工作目录 VOLUME # 挂载的目录 EXPOSE # 暴露端口配置 CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代 ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令 ONBUILD # 当构建一个被继承的DockerFile,这个时候就会运行ONBUILD的指令。触发指令 COPY # 类似ADD,将我们的文件拷贝到镜像中 ENV # 构建的时候设置环境变量
:
docker Hub上 99%的镜像都是从这个基础镜像 FROM scratch
,然后添加需要的软件和配置来构建的!
创建一个自己的centos
# 1. 编写DockerFile文件 [root@heyinchang dockerfile]# cat mydockerfile-centos FROM centos MAINTAINER heyinchang<503337227@qq.com> ENV MYPATH /usr/local WORKDIR $MYPATH RUN yum -y install vim RUN yum -y install net-tools CMD echo $MYPATH CMD echo '---end---' CMD /bin/bash # 2. 通过这个文件构建镜像 # 命令 docker build -f dockerfile文件路径 -t 镜像名 . [root@heyinchang dockerfile]# docker build -f mydockerfile-centos -t mycentos:0.1 . Successfully built ca097bc02916 Successfully tagged mycentos:0.1 # 表示镜像创建成功 # 3. 测试运行
对比原生的centos:
我们增加之后的centos镜像
我们可以查看镜像的构建过程
docker history 镜像id
CMD 和 ENTRYPOINT 区别
CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代 ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令
测试 CMD
# 1.编写dockerfile文件 [root@heyinchang dockerfile]# vim mydockerfile-cmd-test FROM centos CMD ["ls", "-a"] # 2.运行dockerfile文件,生成镜像 [root@heyinchang dockerfile]# docker build -f mydockerfile-cmd-test -t mycmd:0.1 . # 3.运行这个镜像,会自动执行 ls -a命令 # 4.想追加一个命令 -l [root@heyinchang dockerfile]# docker run 1547a942deba -l docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "-l": executable file not found in $PATH: unknown. ERRO[0000] error waiting for container: context canceled # 报错了,CMD的情况下, -l 替换了CMD ["ls", "-a"]命令, -l 不是命令所有报错!
测试 ENTRYPOINT
# 1.编写dockerfile文件 [root@heyinchang dockerfile]# vim mydockerfile-cmd-entrypoint FROM centos ENTRYPOINT ["ls", "-a"] # 2.运行dockerfile文件,生成镜像 [root@heyinchang dockerfile]# docker build -f mydockerfile-cmd-test -t entrypoint01 . # 3.运行这个镜像,会自动执行 ls -a命令 # 4.想追加一个命令 -l ,是直接追加在 ENTRYPOINT ["ls", "-a"] 命令的后面 [root@heyinchang dockerfile]# docker run 1547a942deba -l
dockerfile中很多命令都十分相似,我们需要了解他们的区别,我们最好的学习就是对比它们然后测试效果!
实战:Tomcat镜像
-
准备镜像文件 tomcat压缩包,jdk的压缩包
-
编写dockerfile文件, 官方命令
Dockerfile
FROM centos MAINTAINER heyinchang<503337227@qq.com> COPY readme.txt /usr/local/readme.txt ADD jdk-17_linux-x64_bin.tar.gz /usr/local/ ADD apache-tomcat-9.0.54.tar.gz /usr/local/ RUN yum -y install vim ENV MYPATH /usr/local WORKDIR $MYPATH ENV JAVA_HOME /usr/local/jdk-17.0.1 ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.54 ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.54 ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_BASH/bin EXPOSE 8080 CMD /usr/local/apache-tomcat-9.0.54/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.54/bin/logs/catalina.out
-
运行dockerfile,构建镜像
docker build -t diytomcat .
-
启动镜像
[root@heyinchang tomcat]# docker run --name hyc-tomcat -d -p 3344:8080 -v /home/heyinchang/build/tomcat/test:/usr/local/apache-tomcat-9.0.54/webapps/test -v /home/heyinchang/build/tomcat/tomcatlog/:/usr/local/apache-tomcat-9.0.54/logs diytomcat
-
访问测试 curl localhost:3344
-
发布项目(由于做了卷挂载,我们直接在本地编写项目就可以发布了!)
# cd test目录,创建文件夹mkdir WEB-INF # cd WEB-INF, vim web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> </web-app> # cd .. 创建index.html # 访问 localhost:3344/test
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>hell heyinchang</title> </head> <body> Hello World!<br/> <% out.println("你的 IP 地址 " + request.getRemoteAddr()); System.out.println('---if he really loves you---') %> </body> </html>
发现:项目部署成功,可以直接访问ok!
我们以后开发的步骤:需要掌握DockerFile的编写!我们之后的一切都是使用docker镜像来发布运行!
发布自己的镜像
docker Hub
-
地址:Docker Hub 注册自己的账号
-
确定这个账号可以登录
-
在我们服务器上提交自己的镜像
[root@heyinchang ~]# docker login --help Options: -p, --password string Password --password-stdin Take the password from stdin -u, --username string Username
-
登录完毕后就可以提交镜像了
# push自己的镜像到服务器上! root@heyinchang ~]# docker push diytomcat Using default tag: latest The push refers to repository [docker.io/library/diytomcat] 6c7e8e8b5dd9: Preparing 2d5e282a13b6: Preparing e1db46511555: Preparing 0005af736d02: Preparing 74ddd0ec08fa: Preparing denied: requested access to the resource is denied # 被拒绝 # push镜像的问题? [root@heyinchang ~]# docker push heyinchang/diytomcat:1.1 The push refers to repository [docker.io/heyinchang/diytomcat] An image does not exist locally with the tag: heyinchang/diytomcat # 解决,增加一个tag [root@heyinchang ~]# docker tag 镜像id heyinchang/diytomcat:1.0 # docker push 上去即可,自己发布的镜像尽量带上版本号! root@heyinchang ~]# docker push heyinchang/diytomcat:1.0
push的时候也是按镜像的层级来进行提交的!
阿里云镜像服务器上
-
登录阿里云
-
进入容器镜像服务
-
创建命名空间
-
创建容器镜像
-
查看基本信息,push镜像
Docker 网络
理解docker0
测试
发现有三个网络
# 问题 docker 是如何处理容器网络访问的?
# 安装运行tomcat镜像 docker run -d -P --name tomcat01 tomcat # 查看容器内部的网络 # docker exec -it tomcat01 ip addr(狂神用的这个命令,但是我试了报错了,于是我用下面这条命令查看到了容器内部的网络) # docker inspect 容器id,查看容器内部的网络 # 思考,linux能不能ping通容器内部! # linux可以ping通docker 容器内容 [root@heyinchang ~]# ping 172.17.0.2 PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data. 64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.124 ms 64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.048 ms 64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.099 ms ^