Docker 学习

Docker

弱小和无知不是生存的障碍,傲慢才是。

Docker 学习

  • Docker概述

  • Docker安装

  • Docker命令

    • 镜像命令
    • 容器命令
    • 操作命令
    • .......
  • Docker镜像!

  • 容器数据卷

  • Docker File

  • Docker网络原理

  • IDEA整合Docker

  • Docker Compose

  • Docker Swarm

  • CI\CD Jenkins

即使再小的帆也能远航。

Docker概述?

Docker为什么会出现?

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

开发 --- 运维。问题:我在我的电脑上可以运行!版本更新,导致服务不可用!对于运维来说,十分痛苦。

导致现在开发即运维,运维即开发。

环境配置十分麻烦,每一个机器都要部署机器(集群Redis、ES、Hadoop....)!费事费力。

发布一个项目( jar +(Redis MySQL jdk ES 运行环境)),jar 包打包发布很简单,但是环境安装麻烦,考虑项目能不能带上环境安装!

之前在服务器配置一个应用的环境 Redis MySQL jdk ES ,配置很麻烦,不能够跨平台。

Windows开发,最后发布到Linux。

传统:开发 jar,运维来上线。

现在:开发打包部署上线,一套流程做完。

安卓应用:java 开发 --- apk 打包 --- 发布(应用商店) ---张三使用 apk --- 安装即可用!

Java 项目:java 开发 --- jar (环境) --- 打包项目带上环境(镜像) --- (Docker 仓库:相当于商店) --- 下载打包的镜像 --- 直接运行即可!

Docker 针对上述问题,提出了解决方案!

Docker 的思想来自于集装箱!

服务器 JRE 环境下 --- 多个应用(常出现端口冲突)--- 原来都是交叉的。

隔离:Docker 的核心思想!打包装箱!每个箱子相互隔离。

Docker 通过隔离机制,可以将服务器利用到极致!

本质:所有的技术都是因为出现了一些问题,我们需要去解决,才会去学习。

Docker的历史

2010年,几个搞 IT 的年轻人,就在美国成立了一家公司 dotCloud

做一些 pass 的云计算服务!LXC(虚拟机) 有关的容器技术!

他们将自己的技术(容器化技术)命名就是 Docker !

Docker 刚诞生的时候,没有引起行业的注意!dotCloud,就活不下去。

想着生存 --- 开源

开放源代码!

2013年,Docker 开源!

Docker 原来越多的人发现了 Docker 的优点。火了,开源之后Docker 每个月都会更新一个版本!

2014年4月9日,Docker 1.0 发布!

Docker 为什么这么火?十分的轻巧!

在容器技术出来之前,我们都是使用的虚拟机技术!

虚拟机:在 Windows 中装一个 Vmware 软件,通过这个软件可以虚拟出多台电脑!但是太占内存同时速度效率很慢,显得笨重!

虚拟机也是属于虚拟化技术,Docker 容器技术,也是一种虚拟化技术!

VM:linus centos 原生镜像(相当于一个电脑!) 隔离,需要开启多个虚拟机  几个 G 起步,启动几分钟
docker:隔离,镜像机制(最核心的环境 4m可能就够了 + jdk  + MySQL) 十分小巧,运行镜像就可以了!小巧! 很小,秒级启动

到现在所有开发人员,都必须会 Docker !

聊聊 Docker

Docker 是基于 Go 语言开发的!开源项目。

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

文档地址:https://docs.docker.com/ Docker 文档超级详细(但是是英文,需要耐心)

仓库地址:https://hub.docker.com/ docker 仓库操作和 git 操作很像 pull push 操作等类似

Docker能干吗

之前的虚拟机技术

image

虚拟机技术缺点:

1、资源占用十分多

2、冗余步骤多(开关机等等)

3、启动很慢

容器化技术

容器化技术不是模拟的一个完整的操作系统(只是把需要的东西进行打包隔离)

image

比较Docker 和虚拟机技术的不同:

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

DevOps(开发、运维)

应用更快速的交付和部署

传统:一堆帮助文档,安装程序

Docker:打包镜像,发布测试,一件运行

更便捷的升级和扩缩容

使用了 Docker 之后,部署应用就和搭积木一样!

项目打包为一个镜像,扩展服务器A!服务器B运行镜像就行了

更简单的运维系统

在容器化之后,我们的开发、测试环境都是高度一致的。

更高效的计算资源利用

Docker 是内核级别的虚拟化,可以在一个物理机上运行很多的容器实例!服务器的性能可以被压榨到极致。

Docker 安装

Docker 的基本组成

img

镜像(image):

docker 镜像就好比一个模板,可以通过这个模板来创建容器服务,tomcat镜像 ===> run ==> tomcat01容器(提供服务器),

通过镜像可以创建多个容器(最终服务运行或者项目运行是在容器中的)。

容器(container):

docker 利用容器技术,独立运行一个或者一组应用,通过镜像来创建的。

启动、删除、停止,基本命令!

目前就可以把容器理解为就是一个简易的 linux 系统

仓库(repository):

仓库就是存放镜像的地方!

仓库分为公有和私有仓库!

Docker Hub 、阿里云....都有容器服务器(默认都是国外地址,所以需要配置镜像加速!)

安装 Docker

环境准备(一台服务器并且会操作)

1、需要会有一点 linux 基础

2、CentOS 7

3、使用 终端模拟软件 xshell 之类连接远程服务器进行操作

环境查看

# 系统内核是 3.10.0
[root@iZwz98iah2s0nzrecqij5fZ ~]# uname -r
3.10.0-957.21.3.el7.x86_64
# 系统版本用的是 centos 7.6
[root@iZwz98iah2s0nzrecqij5fZ ~]# cat /etc/os-release 
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"

CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"

安装(帮助文档)

帮助文档:

# 1、卸载旧版本
yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

# 2、需要的安装包
yum -y install yum-utils

# 3、设置镜像的仓库
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		# 推荐使用阿里云

# 更新 yum 软件包索引
yum makecache (centos 8以上) yum makecache fast(centos 7)

# 4、安装 docker 相关内容  docker-ce 社区版 -ee 企业版
yum install docker-ce docker-ce-cli containerd.io

# 如果不想安装最新的 docker engine,可以自己找出 yum 源里 docker-ce 的可安装版本,然后指定版本安装
yum list docker-ce --showduplicates | sort -r
yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io

# 5、启动docker
systemctl start docker

# 查看成功 docker version

# 6、测试 hello-world
docker run hello-world

# 7、查看一下下载的这个 hello-world 镜像 docker images
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker images
REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
hello-world   latest    feb5d9fea6a5   7 months ago   13.3kB

了解:卸载docker uninstall docker engine

# 1、卸载当前软件,移除依赖
yum remove docker-ce docker-ce-cli containerd.io

# 2、删除当前软件目录残留,删除资源
rm -rf /var/lib/docker  (docker 的默认工作路径!)
rm -rf /var/lib/containerd

阿里云镜像加速

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

image

2、找到镜像加速地址

image

3、配置使用

# 创建 docker 目录
sudo mkdir -p /etc/docker

# 编写配置文件
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://g7j1ziwb.mirror.aliyuncs.com"]
}
EOF

# 重启 docker 服务
sudo systemctl daemon-reload
sudo systemctl restart docker

回顾 docker 镜像 image run 流程

image

底层原理

Docker 是怎么工作的?

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

Docker-Server 接收到 Docker-Client 的指令就会执行这个命令!

image

Docker 为什么比 VM 快?

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

2、docker 利用的是宿主机的内核,vm 需要是 guest os ,自己的内核。

image

image

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

Docker 的常用命令

帮助命令

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

帮助文档的地址:https://docs.docker.com/engine/reference/

镜像命令

docker images 查看所有本地的主机上的镜像

[root@iZwz98iah2s0nzrecqij5fZ ~]# docker images
REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
hello-world   latest    feb5d9fea6a5   7 months ago   13.3kB

# 解释
REPOSITORY 	镜像的仓库地址
TAG 		镜像的标签
IMAGE ID 	镜像的Id
CREATED 	镜像的创建时间
SIZE 		镜像的大小

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

docker search 搜索镜像

[root@iZwz98iah2s0nzrecqij5fZ ~]# docker search mysql
NAME                             DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
mysql                            MySQL is a widely used, open-source relation…   12456     [OK]       
mariadb                          MariaDB Server is a high performing open sou…   4800      [OK]       
mysql/mysql-server               Optimized MySQL Server Docker images. Create…   922                  [OK]
[root@iZwz98iah2s0nzrecqij5fZ home]# docker search nginx -f stars=3000
NAME      DESCRIPTION                STARS     OFFICIAL   AUTOMATED
nginx     Official build of Nginx.   16688     [OK]       

# 可选项,通过搜藏来过滤
Options:
  -f, --filter=STARS=3000	# 搜索出来的镜像是收藏大于3000的
 

docker pull 下载镜像

# 下载镜像 docker pull 镜像名[:tags]
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker pull mysql
Using default tag: latest			# 如果不写 tag,默认就是 latest
latest: Pulling from library/mysql	
72a69066d2fe: Pull complete 		# 分层下载,docker image的核心,联合文件系统
93619dbc5b36: Pull complete 
99da31dd6142: Pull complete 
626033c43d70: Pull complete 
37d5d7efb64e: Pull complete 
ac563158d721: Pull complete 
d2ba16033dad: Pull complete 
688ba7d5c01a: Pull complete 
00e060b6d11d: Pull complete 
1c04857f594f: Pull complete 
4d7cfa90e6ea: Pull complete 
e0431212d27d: Pull complete 
Digest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709	# 签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest		# 真实地址

docker pull mysql等价于 docker pull docker.io/library/mysql:latest

# 指定版本下载
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker pull mysql:5.7
5.7: Pulling from library/mysql
72a69066d2fe: Already exists 
93619dbc5b36: Already exists 
99da31dd6142: Already exists 
626033c43d70: Already exists 
37d5d7efb64e: Already exists 
ac563158d721: Already exists 
d2ba16033dad: Already exists 
0ceb82207cd7: Pull complete 
37f2405cae96: Pull complete 
e2482e017e53: Pull complete 
70deed891d42: Pull complete 
Digest: sha256:f2ad209efe9c67104167fc609cca6973c8422939491c9345270175a300419f94
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7

# 可选项
Options:
  -a, --all-tags                Download all tagged images in the repository
      --disable-content-trust   Skip image verification (default true)
      --platform string         Set platform if server is multi-platform capable
  -q, --quiet                   Suppress verbose output

docker rmi 删除镜像!

[root@iZwz98iah2s0nzrecqij5fZ ~]# docker rmi -f 容器id					# 删除指定的容器
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker rmi -f 容器id 容器id 容器id		 # 删除多个容器
Untagged: mysql:5.7

[root@iZwz98iah2s0nzrecqij5fZ ~]# docker rmi -f $(docker images -aq)	# 删除全部容器($为linux系统取值)
Untagged: mysql:latest
Untagged: mysql@sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709

容器命令

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

docker pull centos

新建容器并启动

docker run [可选参数] image

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


# 测试,启动并进入容器
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker run -it centos /bin/bash
[root@f3dae0664a40 /]# ls	# 查看容器内的 centos,基础版本,很多命令都不完善
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

# 退出
[root@f3dae0664a40 /]# exit
exit

列出所有的运行中的容器

# docker ps 命令
		# 列出当前正在运行的容器
-a  	# 列出当前正在运行的容器 + 带出历史运行过的容器
-n=?	# 显示最近创建的容器数量
-q		# 只显示容器的id

[root@iZwz98iah2s0nzrecqij5fZ ~]# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker ps -a
CONTAINER ID   IMAGE          COMMAND       CREATED         STATUS                     PORTS     NAMES
f3dae0664a40   centos         "/bin/bash"   7 minutes ago   Exited (0) 2 minutes ago             compassionate_rosalind
9734f19444b6   feb5d9fea6a5   "/hello"      22 hours ago    Exited (0) 22 hours ago              quizzical_visvesvaraya
4d915c8388f7   feb5d9fea6a5   "/hello"      22 hours ago    Exited (0) 22 hours ago              charming_leakey

退出容器

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

删除容器

docker rm 容器id				   # 删除指定的容器,不能删除正在运行的容器,如果要强制删除 rm -f
docker rm -f $(docker ps -aq)	# 删除所有的容器
docker ps -a -q|xrags docker rm # 删除所有容器(linux管道命令)

启动和停止容器的操作

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

常用其他命令

后台启动容器

docker run -d 镜像名  # 后台启动容器
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker run -d centos

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

# 常见的坑:docker 容器使用后台运行,就必须要有一个前台进程,docker发现没有应用,就会自动停止
# nginx,容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了

查看日志命令

docker logs -f -t --tail 容器,没有日志

# 自己写一个shell脚本
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker run -d centos /bin/sh -c "while true;do echo zengzx;sleep 1;done"

# 找到容器id
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker ps 
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS        PORTS     NAMES
209a0a06f8d2   centos    "/bin/sh -c 'while t…"   2 seconds ago   Up 1 second             sad_cohen
 
# 显示日志
-tf				# 显示日志,-f 输出,-t 带上时间戳
--tail number	# 要显示的日志条数
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker logs -f -t --tail 3 209a0a06f8d2
2022-04-25T09:17:53.352195058Z zengzx
2022-04-25T09:17:54.354675748Z zengzx
2022-04-25T09:17:55.357348976Z zengzx

可选项
Options:
      --details        Show extra details provided to logs
  -f, --follow         Follow log output
      --since string   Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)
  -n, --tail string    Number of lines to show from the end of the logs (default "all")
  -t, --timestamps     Show timestamps
      --until string   Show logs before a timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)

查看容器中的进程信息ps

docker top 容器id		# 查看容器中进程
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker top 209a0a06f8d2
UID                 PID                 PPID                C                   STIME               TTY                 TIME     
root                14412               14393               0                   17:16               ?                   00:00:00 
root                21311               14412               0                   17:22               ?                   00:00:00 

查看镜像的元数据

docker inspect 容器id		# 查看镜像元数据
# 测试
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker inspect 209a0a06f8d2
[
    {
        "Id": "209a0a06f8d2af96ea8a6d891a331649531aca673dc1ea72c4fa1b5df9a9af2c",
        "Created": "2022-04-25T09:16:24.789196328Z",
        "Path": "/bin/sh",
        "Args": [
            "-c",
            "while true;do echo zengzx;sleep 1;done"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 14412,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2022-04-25T09:16:25.127779156Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6",
        "ResolvConfPath": "/var/lib/docker/containers/209a0a06f8d2af96ea8a6d891a331649531aca673dc1ea72c4fa1b5df9a9af2c/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/209a0a06f8d2af96ea8a6d891a331649531aca673dc1ea72c4fa1b5df9a9af2c/hostname",
        "HostsPath": "/var/lib/docker/containers/209a0a06f8d2af96ea8a6d891a331649531aca673dc1ea72c4fa1b5df9a9af2c/hosts",
        "LogPath": "/var/lib/docker/containers/209a0a06f8d2af96ea8a6d891a331649531aca673dc1ea72c4fa1b5df9a9af2c/209a0a06f8d2af96ea8a6d891a331649531aca673dc1ea72c4fa1b5df9a9af2c-json.log",
        "Name": "/sad_cohen",
        "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/72e90fca4c75fba1c64ba950e7a1dbd5f33cd89c37ef9052bbced533f6cbde93-init/diff:/var/lib/docker/overlay2/3ca622ae0213126a8411d0bc2d80ab9b78aba019813971d19b6fbd0b6356a04f/diff",
                "MergedDir": "/var/lib/docker/overlay2/72e90fca4c75fba1c64ba950e7a1dbd5f33cd89c37ef9052bbced533f6cbde93/merged",
                "UpperDir": "/var/lib/docker/overlay2/72e90fca4c75fba1c64ba950e7a1dbd5f33cd89c37ef9052bbced533f6cbde93/diff",
                "WorkDir": "/var/lib/docker/overlay2/72e90fca4c75fba1c64ba950e7a1dbd5f33cd89c37ef9052bbced533f6cbde93/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [],
        "Config": {
            "Hostname": "209a0a06f8d2",
            "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 zengzx;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": "34b1bd0a6817640ad65d92488fc3352b7b93f20973d7b13af38e3fe8be76b06c",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {},
            "SandboxKey": "/var/run/docker/netns/34b1bd0a6817",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "505e8001b8f35e8bb0e530c1d29caecaca849063727ba0dd1020ad87a9573048",
            "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": "218fa1c753161cdc59928f36ac47dd7f134b0ae6e22273d092f5f7e945a9e187",
                    "EndpointID": "505e8001b8f35e8bb0e530c1d29caecaca849063727ba0dd1020ad87a9573048",
                    "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 bashShell

# 测试
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker exec -it 209a0a06f8d2 /bin/bash
[root@209a0a06f8d2 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var


# 方式二
docker attach 容器id
正在执行的代码。。。。


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

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

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

# 进入 docker 容器
[root@iZwz98iah2s0nzrecqij5fZ data]# docker attach c1e9429bdbe4
[root@c1e9429bdbe4 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
[root@c1e9429bdbe4 /]# cd home
# 创建 zzx.java 文件
[root@c1e9429bdbe4 home]# touch zzx.java
[root@c1e9429bdbe4 home]# ls
zzx.java
# 退出容器
[root@c1e9429bdbe4 home]# exit
exit
[root@iZwz98iah2s0nzrecqij5fZ data]# docker ps -a
CONTAINER ID   IMAGE     COMMAND       CREATED         STATUS                     PORTS     NAMES
c1e9429bdbe4   centos    "/bin/bash"   4 minutes ago   Exited (0) 8 seconds ago             brave_germain
# 复制文件,将容器中文件复制到宿主机
[root@iZwz98iah2s0nzrecqij5fZ data]# docker cp c1e9429bdbe4:/home/zzx.java /data	# 复制命令
[root@iZwz98iah2s0nzrecqij5fZ data]# ls
zengzx.java  zzx.java
# 复制文件,将宿主机文件复制到容器中
docker cp 宿主机文件 容器:容器路径

# 拷贝是一个手动过程,未来我们使用 -v 卷的技术,可以实现自动联通

小结

image

docker 命令
attach		Attach 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						# 创建一个新容器,同 run,但不启动容器
diff		Inspect changes to files or directories on a container’s filesystem	# 查看 docker 容器变化
events		Get real time events from the server		# 从 docker 服务获取容器实时事件
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 包中的内容创建一个新的文件系统映像[对应 export]
info		Display system-wide information				# 显示系统范围的信息(系统相关信息)
inspect		Return low-level information on Docker objects	# 展示docker对象的元数据信息(详细信息)
kill		Kill one or more running containers			# 杀死正在运行的一个或多个容器
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 服务器退出
logs		Fetch the logs of a container				# 获取容器的日志信息
pause		Pause all processes within one or more containers	# 暂停容器中的所有进程(暂停容器)
port		List port mappings or a specific mapping for the container # 查看映射端口对应的容器内部源端口
ps			List containers								# 列出容器列表
pull		Pull an image or a repository from a registry		# 从仓库拉取镜像
push		Push an image or a repository to a registry		# 向仓库推送镜像
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			# 恢复容器中的所有进程(取消暂停容器[对应 pause])
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	# 截取容器停止时的退出状态

docker 的命令十分多,上面的并不全,需要完整的去 https://docs.docker.com/engine/reference 网址查看。

作业练习

Docker 安装 Nginx

# 1、搜索镜像 search 建议大家去 docker hub 上搜索,可以看到帮助文档
docker search nginx -f stars=3000
# 2、下载镜像 pull
docker pull nginx
# 3、后台运行 nginx
# -d   后台运行
# -name 命名
# -p 指定端口 宿主机端口:容器内端口
[root@iZwz98iah2s0nzrecqij5fZ home]# docker run -d --name=nginx01 -p 3304:80 nginx
e399fa2812906339c526f7de61d438d1dc6580314e76f22f221dacb6d80ed651
[root@iZwz98iah2s0nzrecqij5fZ home]# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                  NAMES
e399fa281290   nginx     "/docker-entrypoint.…"   6 seconds ago   Up 5 seconds   0.0.0.0:3304->80/tcp   nginx01

# 4、测试成功
[root@iZwz98iah2s0nzrecqij5fZ home]# curl localhost:3304		# 如果出现 nginx 的成功页面,成功

# 5、进入容器
[root@iZwz98iah2s0nzrecqij5fZ home]# docker exec -it nginx01 /bin/bash
root@e5db21bc9bbd:/# whereis nginx
nginx: /usr/sbin/nginx /usr/lib/nginx /etc/nginx /usr/share/nginx
root@e5db21bc9bbd:/# cd /etc/nginx
root@e5db21bc9bbd:/etc/nginx# ls
conf.d  fastcgi_params  mime.types  modules  nginx.conf  scgi_params  uwsgi_params

端口暴露的概念

从外网访问 docker 容器服务 nginx,需要经过宿主机的安全策略,然后 docker 容器有自己安全策略,需要将 docker 容器服务的安全策略映射到宿主机的安全策略,才能访问到 docker 容器服务,这里安全策略指的是防火墙对 ip + 端口的拦截。
image

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

作业:docker 来装一个 tomcat

# 官方的使用
$ docker run -it --rm tomcat:9.0
$ docker run -it --rm -p 8888:8080 tomcat:9.0

# 我们之前容器动的都是后台启动,停止了容器之后,容器还是可以查到。docker run -it --rm,一般用来测试,用完即删除容器,不建议使用
# 正常步骤,先下载镜像,再启动容器
docker pull tomcat
docker run -d --name=tomcat01 -p 8088:8080

# 测试访问没有问题

# 进入容器
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker exec -it tomcat /bin/bash
# 发现问题:1、linux 命令少了,2、没有webapps
# 阿里云镜像的原因:默认是最小的镜像,所有不必要的都剔除掉,保证最小可运行的环境 
# tomcat 的 webapps 目录下并没有发布的应用,所以访问没有资源,将 tomcat 自带的 webapps.dist 目录下的例子复制到 webapps 目录下,可以查看案例
root@421f3ce01c81:/usr/local/tomcat# cp -r webapps.dist/* webapps

思考问题:我们以后要部署项目,如果每次要进入容器是不是十分麻烦,要是可以在容器外部提供一个映射路径,webapps,在外部放置项目,就自动同步到内部就方便了。

docker 容器 tomcat + 网站 docker mysql;之前是删除mysql才会删库,现在删除容器就会,所以是不是可以映射路径,删除容器之后数据还保存在这。

作业:部署 es + kibana

# es 暴露的端口很多!
# es 十分的耗内存
# es 的数据一般需要放置到安全目录!挂载
# --net somenetwork ? 网络配置(先可以去掉)
# $ docker run -d --name elasticsearch --net somenetwork -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:tag
# 启动 elasticsearch
$ docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.6.2

# 启动了 elasticsearch,linux系统内存和 cpu 爆满,很卡,docker stats 查看cpu的状态
# es 十分耗内存,如果云主机卡停,可以重启主机或关闭 docker

# 测试一下 es 是否成功
[root@iZwz98iah2s0nzrecqij5fZ ~]# curl localhost:9200
{
  "name" : "c6f605c6cec1",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "984-302nTl2YVrHRORV-OQ",
  "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"
}

# 查看内存占用,docker stats
CONTAINER ID   NAME            CPU %     MEM USAGE / LIMIT     MEM %     NET I/O   BLOCK I/O       PIDS
c6f605c6cec1   elasticsearch   0.71%     1.249GiB / 1.694GiB   73.73%    0B / 0B   478MB / 696kB   43

# 关闭 es,增加内存的限制,修改配置文件。 -e 环境配置修改
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker stop elasticsearch
$ 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,相比小了很多,修改了环境配置,最小内存为64m,最大为512m(-e ES_JAVA_OPTS="-Xms64m -Xmx512m"
CONTAINER ID   NAME              CPU %     MEM USAGE / LIMIT     MEM %     NET I/O     BLOCK I/O       PIDS
6e7b803a75d0   elasticsearch02   0.39%     397.7MiB / 1.694GiB   22.93%    96B / 96B   128MB / 729kB   43

作业:使用 kibana 连接 elasticsearch?思考网络如何才能连接过去?

因为容器内部相互隔离,所以容器安全策略不相同,并不能通过 localhost + 端口直连方式,会被容器自己安全策略隔离。解决方式为:容器与 linux 内网打通,通过 linux 内网与下一个容器交互,以达到容器通信的效果。

image

可视化

  • portainer(先用这个)
docker run -d -p 8888:9000	\
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
  • Rancher(CI/CD 再使用)

什么是portainer?

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

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

访问测试:外网:8888

image

连接

image

进入之后面板

image

可视化面板平时不会使用,测试玩玩就行。

Docker 镜像讲解

镜像是什么

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

所有的应用,直接打包 docker 镜像,就可以直接跑起来。

如何得到镜像:

  • 从远程仓库下载
  • 朋友拷贝
  • 自己制作一个镜像 DockerFile

Docker 镜像加载原理

UnionFS(联合文件系统)

我们下载的时候看到的一层一层的就是这个!

UnionFs(联合文件系统):是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。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 等等。

image

平时安装虚拟机的 centos 都是几个G,为什么 Docker 才200M ?

[root@iZwz98iah2s0nzrecqij5fZ ~]# docker images centos
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
centos       latest    5d0da3dc9764   7 months ago   231MB

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

虚拟机是分钟级别,而容器是秒级启动。

分层理解

分层的镜像

在下载镜像的时候,观察下载日志的输出,可以看到是一层一层在下载的。

[root@iZwz98iah2s0nzrecqij5fZ ~]# docker pull redis
Using default tag: latest
latest: Pulling from library/redis
a2abf6c4d29d: Already exists 
c7a4e4382001: Pull complete 
4044b9ba67c9: Pull complete 
c8388a79482f: Pull complete 
413c8bb60be2: Pull complete 
1abfd3011519: Pull complete 
Digest: sha256:db485f2e245b5b3329fdc7eff4eb00f913e09d8feb9ca720788059fdc2ed8339
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest

思考:为什么 Docker 镜像要采用这种分层思想呢?

最大的好处莫过于资源共享了!比如说有多个镜像需要从相同的 Base 镜像来进行构造,那么宿主机只需要留有一份 Base 镜像即可,而不需要每次都下载一份新的 Base 镜像,同时内存中也只需要加载一份 Base 镜像,这样就可以为所有容器进行服务了,而且镜像的每一层都可以被共享。极大的减少了资源冗余。

查看镜像分层的方式通过 docker image inspect 命令进行查看。

[root@iZwz98iah2s0nzrecqij5fZ ~]# docker inspect redis
[
		// .......
        "RootFS": {
            "Type": "layers",
            // 代表了安装的每一步
            "Layers": [
                "sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f",
                "sha256:9b24afeb7c2f21e50a686ead025823cd2c6e9730c013ca77ad5f115c079b57cb",
                "sha256:4b8e2801e0f956a4220c32e2c8b0a590e6f9bd2420ec65453685246b82766ea1",
                "sha256:529cdb636f61e95ab91a62a51526a84fd7314d6aab0d414040796150b4522372",
                "sha256:9975392591f2777d6bf4d9919ad1b2c9afa12f9a9b4d260f45025ec3cc9b18ed",
                "sha256:8e5669d8329116b8444b9bbb1663dda568ede12d3dbcce950199b582f6e94952"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]

理解:所有的 Docker 镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。

举一个简单例子,假如基于 Ubuntu Linux 16.04 创建一个新的镜像,这就是镜像的第一层;如果在该镜像中添加 Python 包,就会在该镜像基础之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。

该镜像当前已经包含3个镜像层,如下图所示(简单例子)。

如果将上面两个镜像删掉,就是纯 Ubuntu 镜像,如果要下载 Jdk,就会添加 Jdk 为镜像第四层。

image

在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。

简单例子:每个镜像包含3个文件,而镜像包含了来自两个镜像层的 6 个文件。

两个大层都有 3 个文件,如果镜像打包成一个整体镜像需要 6 个文件,会再进行压缩成为 6 层。

image

上图镜像层与之前略有区别,主要目的用于展示文件。

下图中展示了稍复杂的三层镜像,在外部看来只有 6 个文件,这是因为最上层的文件 7 是文件 5 的一个更新版本。相当于在原有的镜像基础上再进行打包新镜像,但是发现文件 7 是文件 5 的升级版,于是会将文件 5 自动覆盖掉,还是 6 个文件展示。

image

上面这种替换的情况下,上层镜像文件中的文件覆盖了镜像底层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。

Docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。

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

Docker 在 Windows 上仅支持 windowsfilter 一种存储引擎,该引擎基于 NTFS 文件系统之上实现了分层和 CoW[1]。

下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。

image

特点

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

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

比如说我们现在下载了一个 tomcat,docker pull tomcat,这时候是最初始的 tomcat 镜像。

我们启动镜像的时候会在原始的 tomcat 镜像层之上加上容器层,为我们自己的配置之类,然后重新打包发布。docker run ---- tomcat,原来的 tomcat 镜像并不会改变。就相当于我们在删除容器的同时,但是 tomcat 镜像还是保留在那里。

如何提交一个自己的镜像

commit 镜像

docker commit # 提交容器成为一个新的副本
# 命令和 git 原理相似
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG]

实战测试

# 1、启动一个默认的 tomcat(如果启动过,可以直接重启)
docker run -it -p 8088:8080 --name=tomcat01 tomcat /bin/bash
docker start tomcat01

# 2、发现默认的 tomcat 是没有 webapps 应用的,镜像的原因,官方镜像默认 webapps 下面是没有文件的

# 3、自己可以拷贝进去基本文件,将 webapps.dist 目录下的文件拷贝到 webapps 下,就相当于自己进行了操作,然后如果想有一个不需要操作的镜像。

# 4、提交修改之后的镜像 docker commit

image

# 5、验证 docker images,会发现多了一个镜像为 tomcat02 版本为 1.0,然后 SIZE 变大了,证明已经 commit 修改后的镜像成功。
如果你想保存当前容器的状态,就可以通过 commit 来提交。获得一个镜像,
就好比 VM 当中的快照。

容器数据卷

什么是容器数据卷

docker的理念回顾

将应用和环境打包成一个镜像!

数据?如果数据读在容器中,那么容器一删除数据就会丢失,这是不允许的。出现了需求:数据持久化

MySQL,容器删了,就是删库跑路了! == 需求MySQL数据可以存储在本地!

容器之间可以有一个数据共享的技术!Docker 容器中产生的数据可以存储到本地!

这就是卷技术!其实本质就是目录的挂载,将容器中的目录挂载到本机上。

将 mysql 文件系统下的 /usr/mysql 目录挂载到 linux 系统下的 /home/mysql 目录。

image

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

使用数据卷

方式一:直接使用命令来挂载 -v
docker run -it -v 主机目录:容器内目录 -p 主机端口:容器内端口

# 测试
[root@iZwz98iah2s0nzrecqij5fZ /]# docker run -it -v /home/ceshi:/home centos /bin/bash
# 退出容器
ctrl + p + q
# 查看本机目录是否有挂载
[root@iZwz98iah2s0nzrecqij5fZ /]# cd /home
[root@iZwz98iah2s0nzrecqij5fZ home]# ls
admin  ceshi
# docker inspect 1a7ea3b836ec
去查看容器详细信息中的 mount 信息。是否有挂载信息。
# 创建文件或文件夹验证即可

测试容器停止之后的文件同步机制

# 停止容器
[root@iZwz98iah2s0nzrecqij5fZ home]# docker stop 1a7ea3b836ec
1a7ea3b836ec
# 挂载目录下创建测试文件
[root@iZwz98iah2s0nzrecqij5fZ home]# cd ceshi
[root@iZwz98iah2s0nzrecqij5fZ ceshi]# touch test01.java
[root@iZwz98iah2s0nzrecqij5fZ ceshi]# 
# 启动容器,查看目录是否自动同步
[root@iZwz98iah2s0nzrecqij5fZ ceshi]# docker start 1a7ea3b836ec
1a7ea3b836ec
[root@iZwz98iah2s0nzrecqij5fZ ceshi]# docker exec -it 1a7ea3b836ec /bin/bash
[root@1a7ea3b836ec /]# cd /home
[root@1a7ea3b836ec home]# ls
test.java  test01.java  zengzx

好处:修改容器内容和配置只需要在本机进行修改即可,无需进入容器,容器会自动同步。

实战:安装MySQL

思考:数据的持久化问题

# 下载镜像:
[root@iZwz98iah2s0nzrecqij5fZ /]# docker pull mysql
Using default tag: latest
latest: Pulling from library/mysql
72a69066d2fe: Pull complete 
......
docker.io/library/mysql:latest
# 运行容器,需要做书记挂载
# 安装启动 mysql 需要配置密码
# 官方测试:docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
-e:配置容器的配置项
# 启动 mysql
-d 		后台运行
-p 		端口映射
-v 		卷挂载
-e 		环境配置
--name 	容器名字
[root@iZwz98iah2s0nzrecqij5fZ /]# docker run -it -d -p 3306:3306 -v /data/mysql/config:/etc/mysql/config.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=115932@qq --name mysql01 mysql:latest
c29af4b5a4941eda6f58940dc7f764c613298b5919177ce6307bb6b29148961c
[root@iZwz98iah2s0nzrecqij5fZ /]# 
# 启动成功后,使用本地 navicat 连接测试一下
# 在本地测试创建一个数据库,查看是否数据同步
# 删除容器重新创建挂载卷查看是否同步

重新创建启动容器后,发现挂载到本地的数据卷并没有丢,这就实现了容器数据持久化功能。

具名和匿名挂载

# 匿名挂载
-v	容器内路径!
[root@iZwz98iah2s0nzrecqij5fZ data]# docker run -it -d --name nginx01 -p 3304:80 -v /etc/nginx nginx:latest
9f4979930bad53f0924b6a3029dfa26c4386c55042288e5241caf7acfb47a4e2

# 查看所有卷 volume 的情况
[root@iZwz98iah2s0nzrecqij5fZ data]# docker volume ls
DRIVER    VOLUME NAME
local     3e586633e4497566f7e6ccb07398206663980999e324cc98cc7d48541e1d957d
local     6133ed2802e41e3c5f005418100dd23b9fcc1e82ae4b0fb8deaeb808140c2617
# 如果没有 name 就是匿名卷,我们在 -v 的时候只写了容器内的路径,并没有写容器外的路径

# 具名挂载
[root@iZwz98iah2s0nzrecqij5fZ data]# docker run -it -d --name nginx02 -P -v juming-nginx:/etc/nginx nginx:latest
4516f8cae959b783843efcec903c3123c61a802c3bf1fb9ffffc85143d38b15f
[root@iZwz98iah2s0nzrecqij5fZ data]# docker volume ls
DRIVER    VOLUME NAME
local     6133ed2802e41e3c5f005418100dd23b9fcc1e82ae4b0fb8deaeb808140c2617
local     juming-nginx
nginx挂载的名字并不是乱码了。
# 通过 -v 卷名:容器内路径
# 查看一下这个卷

image

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

我们通过具名挂载可以方便的的找到我们的卷,推荐使用具名挂载,不使用匿名下载。

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

拓展:

# 通过 -v 容器内路径:ro rw 改变读写权限
ro		read only	# 只读
rw		read write	# 可读可写

# 一旦设置了容器权限,容器对我们挂载出来的内容就有限定了!
docker run -it -d --name nginx02 -P -v juming-nginx:/etc/nginx:ro nginx:latest
docker run -it -d --name nginx02 -P -v juming-nginx:/etc/nginx:rw nginx:latest

# ro 这个路径只能通过宿主机进行操作,容器内部无法进行操作

初识DockerFile

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

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

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

FROM centos
VOLUME ["volume01","volume02"]
CMD echo "end-----"
CMD /bin/bash

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

image

# 启动自己生成的容器

image

有挂载的数据卷,那么一定和外面有一个同步的数据卷

image 匿名挂载

可以查看一下是否匿名挂载,是否文件同步成功。同 mysql 操作一样。

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

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

数据卷容器

多个 MySQL 同步!相当于 centos02 容器一的卷来自于父容器 centos01 的卷,然后就容器之间数据进行同步。

image

# 启动 3 个容器,通过我们刚才自己写的镜像进行启动
[root@iZwz98iah2s0nzrecqij5fZ docker-test-volume]# docker run -it --name docker01 zengzx/centos:latest
[root@52c54a53cc60 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var  volume01  volume02
# 启动 docker01 里面有两个数据卷

# 继续启动 docker02 挂载 docker01 的卷
--volumes-from 挂载卷来自哪个容器,相当于继承父类,docker01 就相当于容器数据卷
[root@iZwz98iah2s0nzrecqij5fZ docker-test-volume]# docker run -it  --name docker02 --volumes-from docker01 zengzx/centos:latest 
[root@587e76b2d21c /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var  volume01  volume02
# 进入 docker01 的 volume01 目录里创建文件,去 docker02 的目录下也同步了,成功
# 创建 docker03 也能同样继承
# 测试,可以删除 docker01,查看 docker02,03 是否还留有文件
# 测试依旧可以访问
# 所以容器数据卷同步的机制使用的是相互拷贝的方式。

多个 MySQL 或者 Redis 可以实现数据共享

[root@iZwz98iah2s0nzrecqij5fZ /]# docker run -it -d -p 3306:3306 -v /etc/mysql/config.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=115932@qq --name mysql01 mysql:latest

[root@iZwz98iah2s0nzrecqij5fZ /]# docker run -it -d -p 3306:3306 --volumes-from mysql01 -e MYSQL_ROOT_PASSWORD=115932@qq --name mysql02 mysql:latest

# 这个时候,可以实现两个容器数据同步!

结论:

容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。同步机制使用的是相互拷贝的方式。

但是一旦你持久化到了本地,这个时候,本地的数据是不会删除的。

DockerFile

DockerFile介绍

dockerfile 是用来构建 docker 镜像的文件!命令参数脚本!

构建步骤:

1、编写一个 dockerfile 文件

2、docker build 构建成为一个镜像

3、docker run 运行镜像

4、docker push 发布镜像(DockerHub、阿里云镜像仓库!)

# dockerhub 上找到的镜像版本点进去,就是进入到 github 上的一个 dockerfile 镜像文件,就是打包发布的一个公有镜像
FROM scratch							# 最基本的镜像,90% 镜像都源于此镜像
ADD centos-7-x86_64-docker.tar.xz /		# 增加了 centos7 镜像

LABEL \									# 添加了一些 centos 的基本标签
    org.label-schema.schema-version="1.0" \
    org.label-schema.name="CentOS Base Image" \
    org.label-schema.vendor="CentOS" \
    org.label-schema.license="GPLv2" \
    org.label-schema.build-date="20201113" \
    org.opencontainers.image.title="CentOS Base Image" \
    org.opencontainers.image.vendor="CentOS" \
    org.opencontainers.image.licenses="GPL-2.0-only" \
    org.opencontainers.image.created="2020-11-13 00:00:00+00:00"

CMD ["/bin/bash"]						# 运行命令

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

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

DockerFile构建过程

基础知识:

1、每个保留关键字(指令)必须是大写字母

2、执行从上到下顺序执行

3、# 表示注释

4、每一个指令都会创建一个新的镜像层,并提交

image

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

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

步骤:开发、部署、运维

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

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

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

DockerFile 指令:

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

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

image

实战测试

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

image

创建一个自己的 centos

# 1、编写 dockerfile 文件
FROM centos
MAINTAINER zengzx<zengzx19990317@163.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、通过这个文件构建镜像 docker build -f centosfile -t mycentos:1 .
Successfully built 554d3dd38892
Successfully tagged mycentos:1

# 3、测试运行
发现工作目录到了 /usr/local,增加了 vim 和 ifconfig 命令
[root@b56fcf67ebe0 local]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

我们可以列出本地镜像变更的历史。每一行都是一个命令构建镜像。

image

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

CMD 和 ENTRYPOINT 的区别

CMD			# 指定容器启动时要运行的命令,只有最后一个会生效,可被替代 CMD echo "":输出字符串
ENTRYPOINT	# 指定容器启动时要运行的命令,可以追加命令

测试 CMD 命令

# 编写 dockerfile 文件
[root@iZwz98iah2s0nzrecqij5fZ dockerfile]# vim ceshifile
FROM centos:7
CMD ["ls","-a"]

# 构建镜像
[root@iZwz98iah2s0nzrecqij5fZ dockerfile]# docker build -f ceshifile -t test01:1.0 .

# run 运行,发现我们的 CMD 命令生效
[root@iZwz98iah2s0nzrecqij5fZ dockerfile]# docker run e602c78e509d

# 想追加一个命令 -l ls -al
[root@iZwz98iah2s0nzrecqij5fZ dockerfile]# docker run dd172632d27e -l
docker: Error response from daemon: failed to create shim: 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 不是命令所以报错
使用 CMD 命令加命令需要写完整 ls -al 则替换成功
[root@iZwz98iah2s0nzrecqij5fZ dockerfile]# docker run dd172632d27e ls -al

测试 ENTRYPOINT

# 编写 dockerfile 文件
[root@iZwz98iah2s0nzrecqij5fZ dockerfile]# vim ceshifile02
FROM centos:7
ENTRYPOINT ["ls","-a"]

......

# 追加命令 ,测试成功
[root@iZwz98iah2s0nzrecqij5fZ dockerfile]# docker run ceshi02:1 -l
total 64

DockerFile 中很多命令都十分相似,我们需要了解他们的区别,最好的理解是去实际操作。

实战:Tomcat 镜像

1、准备镜像文件 tomcat 压缩包,jdk 的压缩包!

2、编写 dockerfile 文件,官方命名DockerFile,build 会自动寻找这个文件,不需要 + -f 指定了。

FROM centos
MAINTAINER zengzx<zengzx19990317@163.com>
COPY readme.txt /usr/local/readme.txt

ADD apache-tomcat-10.0.20.tar.gz /usr/local/
ADD jdk-11.0.14_linux-x64_bin.tar.gz /usr/local/

RUN yum -y install vim

ENV MYPATH /usr/local
WORKDIR $MYPATH

ENV JAVA_HOME /usr/local/jdk-11.0.14
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-10.0.20
ENV CATALINA_BASH /usr/local/apache-tomcat-10.0.20
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin

EXPOSE 8080

CMD /usr/local/apache-tomcat-10.0.20/bin/startup.sh  && tail -F /usr/local/apache-tomcat-10.0.20/bin/logs/catalina.out

3、构建镜像

# docker build -f DockerFile -t diytomcat .

4、启动镜像

-p	暴露端口
-v	挂载数据卷,可直接在宿主机发布 test 项目,可以查看日志
[root@iZwz98iah2s0nzrecqij5fZ tomcat]# docker run -d -p 9090:8080 --name diytomcat -v /home/tomcat/test:/usr/local/apache-tomcat-10.0.20/webapps/test -v /home/tomcat/tomcatlogs/:/usr/local/apache-tomcat-10.0.20/logs diytomcat

5、访问测试

6、发布项目(由于做了卷挂载,我们直接在本地编写项目就可以发布了!)

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                      https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
  version="5.0"
  metadata-complete="true">
<!DOCTYPE HTML>
<html lang="en"><head>
<meta charset="UTF-8">
<title>Apache Tomcat Examples</title>
</head>
<body>
	hello,world
</body></html>

发现:项目部署成功,可以直接访问。

开发步骤:需要掌握 DockerFile 的编写!我们之后的一切都是使用 docker 镜像发布运行。

发布自己的镜像

DockerHub

1、地址 https://hub.docker.com/,注册账号

2、确定账号可以登录

3、在我们服务器上提交自己的镜像

Usage:  docker login [OPTIONS] [SERVER]

Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.

Options:
  -p, --password string   Password
      --password-stdin    Take the password from stdin
  -u, --username string   Username
  
[root@iZwz98iah2s0nzrecqij5fZ data]# docker login -u xxx -p xxx
Login Succeeded

4、登录完毕后就可以提交自己的镜像了,就是一步 docker push

[root@iZwz98iah2s0nzrecqij5fZ data]# docker push diytomcat
Using default tag: latest
The push refers to repository [docker.io/library/diytomcat]
9a5baf03c5c4: Preparing 
a9c5b0e6104a: Preparing 
afb8ecbafdb9: Preparing 
f4cb6e9bbd7f: Preparing 
174f56854903: Preparing 
denied: requested access to the resource is denied	# 拒绝

# 解决,因为如果要推送镜像到远程仓库,必须命名为 作者名称/容器名,才能进行推送
docker tag 增加一个 tag
[root@iZwz98iah2s0nzrecqij5fZ data]# docker tag diytomcat zengzx19990317/diytomcat
[root@iZwz98iah2s0nzrecqij5fZ data]# docker push zengzx19990317/diytomcat

image

提交的时候也是按照镜像的层级来进行提交的!

阿里云镜像服务

1、登录阿里云

2、找到容器镜像服务

3、创建命名空间

image

4、创建容器镜像仓库

创建容器镜像仓库记得选本地仓库

image

5、点击容器镜像仓库里面有详细操作指南

$ docker login --username=云上的星星小马 registry.cn-hangzhou.aliyuncs.com
$ docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/zengzx-docker-test/diytomcat:[镜像版本号]
$ docker push registry.cn-hangzhou.aliyuncs.com/zengzx-docker-test/diytomcat:[镜像版本号]
$ docker pull registry.cn-hangzhou.aliyuncs.com/zengzx-docker-test/diytomcat:[镜像版本号]

# 推送镜像
1、修改命名,生成镜像版本号
[root@iZwz98iah2s0nzrecqij5fZ data]# docker tag diytomcat registry.cn-hangzhou.aliyuncs.com/zengzx-docker-test/diytomcat:1.0
2、推送镜像
docker push registry.cn-hangzhou.aliyuncs.com/zengzx-docker-test/diytomcat:1.0

# 拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/zengzx-docker-test/diytomcat:1.0

小结(Docker组成关系):

DockerFile:dockerfile 文件

# build 将 dockerfile 文件构建成为镜像	

Images:镜像

# tag 	镜像可以更改镜像名称和版本号		
# run 	将镜像启动成为容器
# push 	将镜像发布到远程仓库(DockerHub 或者阿里云仓库),如果镜像无法 push,可以选择先 tag 一下
# save	将镜像打包成 tar 文件

Docker registry :远程仓库(DockerHub 或者阿里云镜像服务)

# pull	从远程仓库拉取镜像

Containers:容器

# commit	将容器提交成镜像
# stop		停止容器服务
# start		启动容器服务
# restart	重启容器服务

backup.tar:使用端

# load		加载镜像 tar 文件

image

Docker 网络(铺垫、容器编排、容器部署)

理解 Docker 网络(Docker0)

清空所有环境(容器、镜像),保持环境干净

测试

[root@iZwz98iah2s0nzrecqij5fZ data]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo													# 本地地址
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:16:3e:0a:b0:3a brd ff:ff:ff:ff:ff:ff
    inet 172.22.30.201/18 brd 172.22.63.255 scope global dynamic eth0				# 阿里云内网地址
       valid_lft 313974709sec preferred_lft 313974709sec
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:b6:aa:86:75 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0						# docker0 网络地址
       valid_lft forever preferred_lft forever

三个网络

# 问题:docker 是如何处理容器网络访问的?比如说 tomcat 访问 mysql

作业:使用 kibana 连接 elasticsearch?思考网络如何才能连接过去?

因为容器内部相互隔离,所以容器安全策略不相同,并不能通过 localhost + 端口直连方式,会被容器自己安全策略隔离。解决方式为:容器与 linux 内网打通,通过 linux 内网与下一个容器交互,以达到容器通信的效果。

image

# 启动一个 tomcat 我已经启动过一个
docker run -d -p 8088:8080 --name tomcat01 tomcat
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker start tomcat01 

# 查看容器内部网络地址 ip addr

可能出现 ip addr 无法使用的情况,因为 tomcat 并没有带 ip 工具命令。所以需要我们自己使用软件管理软件 apt 安装 iproute2,但是会出现 apt 下载镜像是国外网址,网速过慢导致无法进行更新下载安装。我们就需要替代 apt 镜像地址配置,然后再进行更新下载。

解决地址 CSDN:https://blog.csdn.net/weixin_45630258/article/details/124438067

# docker exec -it tomcat01 ip addr 发现容器启动的时候会得到一个 eth0@if107 ip 地址,docker 分配的!
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker exec -it tomcat01 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
106: eth0@if107: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

# 思考:linux 能不能 ping 通这个网络(可以 ping 通)
[root@iZwz98iah2s0nzrecqij5fZ ~]# 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.087 ms

# linux 可以 ping 通 docker 容器内部

原理:

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

再次测试 ip addr,发现本机网络多了个 vethc...@if106,对应 docker 容器网络
image
2、再启动一个容器,发现又多了一对网卡
image

# 我们发现这个容器带来的网卡,都是一对对的
# evth-pair 就是一对的虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连
# 正因为有这个特性,evth-pair 充当一个桥梁,连接各种虚拟网络设备的
# OpenStac,Docker 容器之间的连接,OVS 的连接,都是使用 evth-pair 技术。
相当于本机有个网卡接口对应相应设备自己的网卡接口(虚拟的),例子中本机中是 113 对应了 tomcat02 中 112,11:veth....@if112,
这就是一种相当于桥接,中间桥梁连接,docker 容器先连接本机接口网络,然后本机接口网络再去对接另一个 docker 容器网络接口,实现网络 ping 通。

3、测试下 tomcat01 和 tomcat02 是否能 ping 通。

# 如果出现 docker 容器没有 ping 指令,可以 apt install iputils-ping 进行安装
# 查询 tomcat02 ip 地址
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker exec -it tomcat02 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
112: eth0@if113: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
# 使用 tomcat01 ping tomcat02
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker exec -it tomcat01 ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.100 ms
# ping 通,证明 docker 容器之间通信正常,虽然 docker 容器内部隔离,但是使用 evth-pairs 实现网络桥接

image
结论:tomcat01 和 tomcat02 是公用一个路由器,docker0

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

小结

Docker使用的是linux桥接,宿主机种是一个Docker容器的网桥 docker0.
image
Docker 中的所有网络接口都是虚拟的。虚拟的转发效率高!(就像内网传递文件速率非常快)

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

思考一个场景,编写了一个微服务,database url = ip:,项目不重启,数据库IP换掉了,我们希望可以处理这个问题,可以通过名字来进行访问容器?

就像springcloud 里面feign 调用服务使用服务名一样,实现高可用。

但是容器直接使用容器名字进行通信是无法ping通的。
# docker exec -it tomcat01 ping tomcat02 无法ping通

# 如何解决
# 在容器启动时可以使用 --link 命令进行容器之间名称进行网络绑定
# docker run -d -P --name tomcat01 --link tomcat02 tomcat 将tomcat02网络绑定至tomcat01 ,但是tomcat01网络并没有绑定至tomcat02
# docker exec -it tomcat01 ping tomcat02 可以ping通
# docker exec -it tomcat02 ping tomcat01 无法ping通 

tomcat01可以ping通tomcat02时因为在本地(tomcat01容器内部)配置了tomcat02的配置?

可以查看tomcat01的注册表 cat /etc/hosts (是用来进行绑定地址的),里面注册了tomcat02,实际上我们使用tomcat02名称进行网络通信是使用的 hosts 里面注册的 tomcat02 网络地址。(下面图表用的tomcat03 --link tomcat02)

image

--link 就是在我们hosts配置中增加容器的网络映射 172.18.0.3 tomcat02 312857784cd4

我们现在玩Docker 已经不建议使用 --link了!

一般使用自定义网络!不适用docker0

docker0问题:不支持容器名称访问。

自定义网络

查看所有docker网络 docker network ls

image

网络模式

bridge : 桥接 docker (默认,自己创建也使用bridge模式)

none :不配置网络

host :和宿主机共享网络

container :容器网络连通!(用的少!局限太大)

测试

# 我们直接启动的命令  --net bridge,而这个就是我们的docker0
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 表示网段的CIDR格式的子网,子网掩码
# --gateway 192.168.0.1 用于主子网的IPv4或IPv6网关,代表网络从哪出去,一般是.1
[root@iZwz98iah2s0nzrecqij5fZ ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
ac2c6a98014b5dcc5b17702f4a44d38149f97585cac3f5f6275231ceac956726

[root@iZwz98iah2s0nzrecqij5fZ ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
053bdd4768af   bridge    bridge    local
d497571243be   host      host      local
ac2c6a98014b   mynet     bridge    local
aff9080be468   none      null      local
[root@iZwz98iah2s0nzrecqij5fZ ~]# 

image

# 自己的网络创建好之后就能将容器启动时添加自己的网络
# --net 给容器绑定网络
docker run -d -P --name tomcat-net-01 --net mynet tomcat
docker run -d -P --name tomcat-net-02 --net mynet tomcat

# 使用自己创建的网络就比较完善,可以通过容器名或者ip地址都能进行通信
# 现在容器启动时不使用 --link 也能进行ping通了
docker exec -it tomcat-net-01 ping tomcat-net-02 可以ping通

我们自定义的网络docker都已经帮我们维护好了对应的关系,而docker0是不具备的,推荐使用自定义网络。

好处:

redis - 不同的集群使用不同的网络,保证集群是安全和健康的

mysql - 不同的集群使用不同的网络,保证集群是安全和健康的

网络连通

问题:不同网段的容器网络之间无法进行通信。比如docker0和自创建的mynet不属于同一网段。

解决:网段不同,容器时不可能进行通信的,可以将容器连接到需要通信的网段之上,docker network connect

image

image

# 打通不同网段容器通信,tomcat01为docker0网络段
docker network connect mynet tomcat01

# 连通之后就是将tomcat01放到了mynet网络下,tomcat01就相当于有了两个网络

# 一个容器两个ip地址! 类似阿里云服务:公网ip 私网 ip
docker exec -it tomcat01 ping tomcat-net-01 可以ping通

实战:部署redis集群

redis集群部署,采用分片式

image

shell脚本

# 创建网卡
# --driver 不写会默认指定网络模式为桥接模式 --dricer bridge
docker network create redis --subnet 175.12.0.0/16

# 通过脚本创建6个redis配置
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${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 node.conf
cluster-node-timeout 5000
cluster-announce-ip 175.12.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done

for port in $(seq 1 6); \
do \
docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \
-v /mydata/redis/node-${port}/data:/data \
-v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 175.12.0.1${port} redis:latest redis-server /etc/redis/redis.conf; \
done

# 如果不使用脚本就需要一个个启动
docker run -p 6371:6379 -p 16371:16379 --name redis-1 \
-v /mydata/redis/node-1/data:/data \
-v /mydata/redis/node-1/conf/redis/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 175.12.0.11 redis:latest redis-server /etc/redis/redis.conf

# 创建集群 6台redis服务器,然后主从节点1对1
# --cluster create 创建集群,后面携带redis服务器端口
# --cluster-replicas 1 指定主节点辅助副节点数,这里设置1
redis-cli --cluster create 175.12.0.11:6379 175.12.0.12:6379 175.12.0.13:6379 175.12.0.14:6379 175.12.0.15:6379  \
175.12.0.16:6379 --cluster-replicas 1

root@2bfb7034f1e8:/data# redis-cli --cluster create 175.12.0.11:6379 175.12.0.12:6379 175.12.0.13:6379 175.12.0.14:6379 175.12.0.15:6379  \
> 175.12.0.16:6379 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 175.12.0.15:6379 to 175.12.0.11:6379
Adding replica 175.12.0.16:6379 to 175.12.0.12:6379
Adding replica 175.12.0.14:6379 to 175.12.0.13:6379
M: 396baf656c27b78a99e2840618e44754329edd0e 175.12.0.11:6379
   slots:[0-5460] (5461 slots) master
M: a84c7a846457b8f25ce943bb9399e46bd72d3a3c 175.12.0.12:6379
   slots:[5461-10922] (5462 slots) master
M: 69dbcf68b3a79dbb57b52eaf2ff2184621a59a35 175.12.0.13:6379
   slots:[10923-16383] (5461 slots) master
S: 36b25ce0f697e1fdd9aa4ea0b6466f685764a7b2 175.12.0.14:6379
   replicates 69dbcf68b3a79dbb57b52eaf2ff2184621a59a35
S: 68f0139fff5f59f91d0e3a7ad10957bfb3c1b456 175.12.0.15:6379
   replicates 396baf656c27b78a99e2840618e44754329edd0e
S: ef16deccaff958e04ad1f5e026263a0d78a035b8 175.12.0.16:6379
   replicates a84c7a846457b8f25ce943bb9399e46bd72d3a3c
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.
>>> Performing Cluster Check (using node 175.12.0.11:6379)
M: 396baf656c27b78a99e2840618e44754329edd0e 175.12.0.11:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: a84c7a846457b8f25ce943bb9399e46bd72d3a3c 175.12.0.12:6379
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 36b25ce0f697e1fdd9aa4ea0b6466f685764a7b2 175.12.0.14:6379
   slots: (0 slots) slave
   replicates 69dbcf68b3a79dbb57b52eaf2ff2184621a59a35
S: 68f0139fff5f59f91d0e3a7ad10957bfb3c1b456 175.12.0.15:6379
   slots: (0 slots) slave
   replicates 396baf656c27b78a99e2840618e44754329edd0e
M: 69dbcf68b3a79dbb57b52eaf2ff2184621a59a35 175.12.0.13:6379
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: ef16deccaff958e04ad1f5e026263a0d78a035b8 175.12.0.16:6379
   slots: (0 slots) slave
   replicates a84c7a846457b8f25ce943bb9399e46bd72d3a3c
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.


docker搭建redis集群完成,我们往里面存入值,会随机分配一台主服务器进行存储,如果将这台服务器停掉,他的副节点会自动监测转化为主节点,不影响用户使用。实现高可用。

image

我们使用docker之后,所有的技术都会慢慢的变得简单起来!

SpringBoot微服务打包Docker镜像

1、构建springboot项目

(最简单得springweb项目测试就行了)

image

2、打包应用

package应用,build success 之后会生成项目 target 文件,target里面会有项目打包得 jar 包,这是构建镜像需要得,将他拿出来,可以java -jar运行一下试一试

image

3、编写dockerfile

使用 IDE 进行docker操作,得先安装docker插件,然后可以再/dettings/build 里面连接docker仓库

image

安装了docker插件,然后创建dockerfile文件以Dockerfile命名会高亮显示

image

# docker 文件编写
# java版本,运行环境:注意这里java11要写成openjdk,java8就是java:8
FROM openjdk:11
# 复制所有 jar 为 / app.jar文件
COPY *.jar /app.jar
# 执行命令 服务端口为 8001
CMD ["--server.port=8001"]
# 暴露8001端口
EXPOSE 8001
# java运行jar包命令
ENTRYPOINT ["java","-jar","/app.jar"]

4、构建镜像

将应用 jar 包和 Dockerfile 文件上传到服务器(jar包只有18.3K肯定是有问题的,项目的pom文件中maven打包配置储配,需要重新配置打包)

image

image

image

这时候就可以 docker build 生成应用镜像了

# 以Dockerfile命名会自动查找,-t 给容器取名,记得结尾以 . 结束
docker build -t zengzx666 .

image

5、发布运行

打包镜像完成之后,就可以使用docker镜像运行容器了

# 验证是否打包镜像成功
[root@iZwz98iah2s0nzrecqij5fZ ideal]# docker images
REPOSITORY                                                        TAG             IMAGE ID       CREATED         SIZE
zengzx666                                                         latest          7eb4158e3cab   5 minutes ago   659MB
......

# 运行镜像
docker run -d -P --name zengzx666-test zengzx666

# 查看容器,如果容器启动失败,记得查看容器启动日志
[root@iZwz98iah2s0nzrecqij5fZ ideal]# docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED         STATUS         PORTS                                                                                                                        NAMES
af917438e82a   zengzx666             "java -jar /app.jar …"   2 minutes ago   Up 2 minutes   0.0.0.0:49157->8001/tcp                                                                                                      zengzx-test
# 访问地址,如果再浏览器访问记得开通防火墙端口,端口映射在ports里面
curl localhost:49157/docker/hello
如果返回 hello;zengzx 就是成功

image

以后使用docker,给别人交付的就是一个镜像即可!

到了这里已经完全使用一遍docker了

问题:如果有很多个镜像,??100个镜像,怎么去进行维护,不可能一个个去写dockerfile啥的

企业实战

Docker Compose

Docker Swarm

CI/CD Jenkins流水线

posted @   星星小马  阅读(272)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示