文章目录
-
- [1. Docker简介](#1. Docker简介)
- [2. Docker安装](#2. Docker安装)
- [3. Docker常用命令](#3. Docker常用命令)
-
- [3.1 帮助命令](#3.1 帮助命令)
- [3.2 镜像的基本命令](#3.2 镜像的基本命令)
- [3.3 容器的基本命令](#3.3 容器的基本命令)
- [3.5 常用其他命令](#3.5 常用其他命令)
- [3.6 练习](#3.6 练习)
- [4. Protainer可视化面板](#4. Protainer可视化面板)
- [5. Docker镜像](#5. Docker镜像)
- [6. 容器数据卷](#6. 容器数据卷)
-
- [6.1 使用命令来挂载 -v](#6.1 使用命令来挂载 -v)
- [6.2 Dockerfile挂载](#6.2 Dockerfile挂载)
- [6.3 数据卷容器](#6.3 数据卷容器)
- [7. DockerFile](#7. DockerFile)
-
- [7.1 介绍](#7.1 介绍)
- [7.2 构建过程](#7.2 构建过程)
- [7.3 DockerFile指令](#7.3 DockerFile指令)
- [8. 发布镜像](#8. 发布镜像)
-
- [8.1 发布镜像到Docker Hub](#8.1 发布镜像到Docker Hub)
- [8.2 发布镜像到阿里云容器服务](#8.2 发布镜像到阿里云容器服务)
- [9. Docker网络原理](#9. Docker网络原理)
-
- [9.1 理解Docker网络](#9.1 理解Docker网络)
- [9.2 -link](#9.2 -link)
- [9.3 自定义网络](#9.3 自定义网络)
- [9.4 网络连通](#9.4 网络连通)
- [10. 部署Redis集群实战](#10. 部署Redis集群实战)
- [11. SpringBoot微服务打包Docker镜像](#11. SpringBoot微服务打包Docker镜像)
1. Docker简介
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可抑制的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。容器完全使用沙盒机制,相互之间不会存在任何接口。几乎没有性能开销,可以很容易的在机器和数据中心运行。最重要的是,他们不依赖于任何语言、框架或者包装系统。
DevOps:开发+运维
传统:一堆帮助文档,安装程序
Docker:打包镜像,发布测试,一键运行
使用了Docker之后,我们部署应用就像搭积木一样。
Docker之所以发展如此迅速,因为它提供了系统平滑移植,容器虚拟化技术。
开发人员利用Docker可以消除协作编码时,"在我的机器上可正常工作"的问题。
Docker是dotCloud公司开源的一个基于LXC的高级容器引擎,源码托管在Github上,基于go语言并且遵从Apache2.0协议开源。
GitHub地址:https://github.com/moby/moby
docker官网:https://www.docker.com
docker中文库:https://www.docker.org.cn/
docker hub:https://hub.docker.com/
LXC为Linux Container的简写。Linux Container 容器是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。
LXC主要通过Kernel的namespace实现每个用户实例之间的项目隔离,通过cgroup实现对资源的配额和调度。
docker和容器技术和虚拟机技术,都是虚拟化技术。
不同点:
DOcker容器是在操作系统层面上实现虚拟化,直接复用本地主机的操作系统,而传统虚拟机则是在硬件层面实现虚拟化。
与传统的虚拟机相比,Docker优势体现为启动速度快,占用体积小。
容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核且也没有进行硬件虚拟。因此容器比传统虚拟机更为轻便。
每个容器之间互相隔离,每个容器有自己的文件系统,容器之间进程不会互相影响,能区分计算资源。
Docker的出现,就是为了解决运行环境和配置问题的软件容器,方便做持续集成并有助于整体发布的容器虚拟化技术。
Docker整体架构
Docker的基本组成
-
镜像(image)
docker镜像好比是一个模版,可以通过这个模版来创建容器服务,tomcat镜像->run->tomcat01容器(提供服务),通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)。
-
容器(container)
Docker利用容器技术,独立运行一个或者一组应用,通过镜像来创建。拥有启动、停止、删除等基本命令。可以把容器理解为一个简易的Linux系统。
-
仓库(repository)
仓库就是存放镜像的地方。仓库分为共有仓库和私有仓库。如Docker Hub(官方),阿里云,华为云都有容器服务。仓库默认使用的是Docker Hub,是国外的,国内访问速度非常慢,所以要配置镜像加速(像Maven配置镜像一样)。
Docker利用容器(Container)独立运行的一个或一组应用,应用程序或服务运行在容器里面,容器就类似于一个虚拟化的运行环境,容器是用镜像创建的运行实例。就像是Java中的类和实例对象一样,镜像是静态的定义,容器是镜像运行时的实体。容器为镜像提供了一个标准的和隔离的运行环境,它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。
可以把容器看作是一个简易版的Linux环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
仓库是集中存放镜像文件的场所,docker提供的官方registry被称为Docker Hub。
仓库分为公开仓库和私有仓库两种形式。最大的公开仓库就是Docker Hub,国内的公开仓库包括阿里云、网易云等。
2. Docker安装
Docker并非是一个通用的容器工具,它依赖于已经存在并运行的Linux内核环境。
DOcker实质上是在已经运行的Linux下制造了一个隔离的文件环境,因此它执行的效率几乎等同于所部署的Linux主机。
因此,Docker必须部署在Linux内核的系统上。如果其他系统想部署Docker就必须安装一个Linux环境。
查看环境信息
shell
# 系统内核版本在3.10以上
[root@iZbp13w34ju4eg8v1cixguZ ~]# uname -r
3.10.0-1160.105.1.el7.x86_64
# 查看系统版本
[root@iZbp13w34ju4eg8v1cixguZ ~]# 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
安装(使用rpm存储库安装)
官方教程:https://docs.docker.com/engine/install/centos/
1.卸载旧的版本
旧版本的 Docker 采用docker
或docker-engine
。在尝试安装新版本之前卸载任何此类旧版本以及相关的依赖项。
shell
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
yum
可能会报告没有安装这些软件包。
/var/lib/docker/
卸载 Docker 时,不会自动删除存储的映像、容器、卷和网络。
2.需要的软件包
安装yum-utils
软件包(提供yum-config-manager
实用程序)
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# sudo yum install -y yum-utils
3.设置镜像的仓库
shell
# 使用国内的阿里云
[root@iZbp13w34ju4eg8v1cixguZ ~]# sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
3.更新yum软件包索引
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# yum makecache fast
4.安装Docker引擎
shell
# docker-ce 社区版 docker-ee 企业版
[root@iZbp13w34ju4eg8v1cixguZ ~]# sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
5.启动docker
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# sudo systemctl start docker # 启动docker
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker version # 查看版本成功,如果有信息则启动成功
Client: Docker Engine - Community
Version: 26.0.0
API version: 1.45
Go version: go1.21.8
Git commit: 2ae903e
Built: Wed Mar 20 15:21:09 2024
OS/Arch: linux/amd64
Context: default
Server: Docker Engine - Community
Engine:
Version: 26.0.0
API version: 1.45 (minimum version 1.24)
Go version: go1.21.8
Git commit: 8b79278
Built: Wed Mar 20 15:20:06 2024
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.28
GitCommit: ae07eda36dd25f8a1b98dfbf587313b99c0190bb
runc:
Version: 1.1.12
GitCommit: v1.1.12-0-g51d5e94
docker-init:
Version: 0.19.0
GitCommit: de40ad0
6.通过运行镜像来验证Docker是否安装成功hello-world
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# sudo docker run hello-world
7.查看下载的hello-world
镜像
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest d2c94e258dcb 10 months ago 13.3kB
卸载Docker
1.卸载 Docker Engine、CLI、containerd 和 Docker Compose 软件包:
shell
sudo yum remove docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras
2.主机上的映像、容器、卷或自定义配置文件不会自动删除。要删除所有映像、容器和卷:
shell
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
Docker默认工作路径:/var/lib/docker
配置阿里云镜像加速
安装完成后,其实我们的镜像仓库速度还不够快,上面我们设置的阿里云镜像仓库是阿里云内部的,这里我们可以再配置一个阿里云的镜像加速。
进入容器加速服务的控制台,点击左侧边栏的镜像工具下的镜像加速器,查看操作文档。
针对Docker客户端版本大雨1.10.0的用户,可以通过修改daemon配置文件/etc/docker/daemon.json
来使用加速器
shell
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://xxxx.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
这样,阿里云容器加速就配置完成了。
Run的流程
Docker原理
Docker是怎么工作的?
DOcker是一个Client-Server结构的系统,Docker的守护进程运行在主机上,通过Socket从客户端进行访问。
DockerServer接收到Docker-Client的指令就会执行这个命令。
3. Docker常用命令
3.1 帮助命令
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker version # 查看docker版本信息
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker info # 查看docker系统信息,包括镜像和容器的数量
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker 命令 --help # 查看命令的帮助信息
官方文档:https://docs.docker.com/reference/#command-line-interfaces-clis
3.2 镜像的基本命令
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker images # 查看本地主机上所有的镜像
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker image list # 查看本地主机上所有的镜像
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker images -a # 列出所有镜像,也可以写成--all
[root@iZbp13w34ju4eg8v1cixguZ ~]# REPOSITORY TAG IMAGE ID CREATED SIZE
[root@iZbp13w34ju4eg8v1cixguZ ~]# hello-world latest d2c94e258dcb 10 months ago 13.3kB
解释:
- REPOSITORY:仓库源
- TAG:镜像的标签
- IMAGE ID:镜像的ID
- CREATED:镜像的创建时间
- SIZE:镜像的大小
只显示镜像的ID信息
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker images -q # 只显示镜像的ID,也可以写成--quiet
显示完整的镜像ID
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker images --no-trunc # 列出完整长度的镜像ID
显示出镜像的摘要信息
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker images --digests # 列出摘要
根据名称或者标签进行镜像显示
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker images java # 按名次列出镜像
[root@iZbp13w34ju4eg8v1cixguZ ~]# dokcer images java:8 # 按名次和标签列出镜像
对镜像进行过滤
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker images --filter "dangling=true" --filter "bif=baz" # 过滤,可以同时存在多个过滤器 可以简写为 -f
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker images -f "before=image1" # 按时间过滤,显示指定镜像之前创建的镜像,可以使用before,since
格式化输出
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker images --format "table {{.ID}}\t{{.Repository}}"
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker images --format json # 格式化json输出
占位符 | 描述 |
---|---|
.ID |
图像ID |
.Repository |
图片库 |
.Tag |
图片标签 |
.Digest |
图像摘要 |
.CreatedSince |
自创建图像以来经过的时间 |
.CreatedAt |
图像创建时间 |
.Size |
镜像盘大小 |
搜索镜像
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker search [OPTIONS] TERM
选项 | 默认 | 描述 |
---|---|---|
-f, --filter |
根据提供的条件过滤输出 | |
--format |
使用 Go 模板进行漂亮的打印搜索 | |
--limit |
最大搜索结果数 | |
--no-trunc |
不要截断输出 |
如:
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker search mysql # 搜索mysql
NAME DESCRIPTION STARS OFFICIAL
mysql MySQL is a widely used, open-source relation... 14971 [OK]
mariadb MariaDB Server is a high performing open sou... 5710 [OK]
percona Percona Server is a fork of the MySQL relati... 627 [OK]
phpmyadmin phpMyAdmin - A web interface for MySQL and M... 962 [OK]
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker search -f stars=5000 mysql
NAME DESCRIPTION STARS OFFICIAL
mysql MySQL is a widely used, open-source relation... 14971 [OK]
mariadb MariaDB Server is a high performing open sou... 5710 [OK]
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker search mysql --limit=1
NAME DESCRIPTION STARS OFFICIAL
mysql MySQL is a widely used, open-source relation... 14971 [OK]
下载镜像
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker pull [OPTIONS] NAME[:TAG|@DIGEST]
或者
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker image pull [OPTIONS] NAME[:TAG|@DIGEST]
选项 | 默认 | 描述 |
---|---|---|
-a, --all-tags |
下载存储库中所有标记的图像 | |
--disable-content-trust |
true |
跳过图像验证 |
--platform |
API 1.32+ 如果服务器支持多平台,则设置平台 | |
-q, --quiet |
抑制详细输出 |
如:
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker pull mysql
Using default tag: latest # 如果不指定TAG,则默认下载最新版
latest: Pulling from library/mysql
72a69066d2fe: Pull complete # 分层下载,docker iamge的核心 联合文件系统
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 # 真实地址
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker pull docker.io/library/mysql:latest # 与上面命令等价
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker pull mysql:8.0.20 # 通过指定TAG,指定版本下载
删除镜像
删除可以通过镜像的ID进行删除,可以通过镜像的名称进行删除
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker image rm [OPTIONS] IMAGE [IMAGE...]
或者
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker image remove
或者
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker rmi
选项 | 默认 | 描述 |
---|---|---|
-f, --force |
强制删除图像 | |
--no-prune |
不要删除未标记的父项 |
如:
shell
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker rmi be0dbf01a0f3 # 指定ID进行删除
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker rmi ID1 ID2 ID3 # 一次删除多个容器
[root@iZbp13w34ju4eg8v1cixguZ ~]# docker rmi -f $(docker images -aq) # 删除所有镜像
# $():是指以参数形式传递进去
3.3 容器的基本命令
有了镜像,才可以创建容器!
下载centos镜像
shell
[root@iZbp13w34ju4eg8v1cixguZ /]# docker pull centos
新建容器并启动
shell
dokcer run [可选参数] image # 启动镜像
常用参数:
--name="Name"
:容器名字,如tomcat01,tomcat02,用来区分容器-d
::后台方式运行-it
:使用交互方式运行,进入容器查看内容-p
:指定容器端口- 方式一:
-p 主机端口 : 容器端口(常用)
- 方式二:
-p 容器端口
- 方式三:
容器端口
- 方式四:
-p ip : 主机端口 : 容器端口
- 方式一:
-P
:大写则是随机指定端口
如:
shell
# 启动centos 并使用交互模式进入容器 交互工具使用bash
[root@iZbp13w34ju4eg8v1cixguZ /]# docker run -it centos /bin/bash
[root@a6fef307536f /]#
# 此时已经进入了容器,进入了docker镜像创建的centos中了,该centos为基础版本,很多命令甚至都是不完善的
# 退出容器 从容器中退回主机 容器并且停止
[root@a6fef307536f /]# exit
exit
[root@iZbp13w34ju4eg8v1cixguZ /]#
列出所有运行中的容器
shell
[root@iZbp13w34ju4eg8v1cixguZ /]# docker ps # 查看正在运行的容器
[root@iZbp13w34ju4eg8v1cixguZ /]# docker ps -a # 查看历史运行的容器
常用参数:
-a
:列出当前正在运行的容器,并且列出历史运行过的程序-n=个数
:显示最近创建的容器,指定格式-q
:只显示容器编号
退出容器
shell
exit # 容器停止并退出
# 通过快捷键 Ctrl+P+Q:容器不停止退出
进入容器
shell
# 通过使用 exec命令进入容器
[root@iZbp13w34ju4eg8v1cixguZ /]# docker exec -it centos1 /bin/bash
或者
[root@iZbp13w34ju4eg8v1cixguZ /]# docker exec -it centos1 bash
# 通过使用 attach命令进入容器
[root@iZbp13w34ju4eg8v1cixguZ /]# docker attach centos1
注意:这两种方式存在不同,使用
exec
进入容器时,退出容器时使用exit
不会使容器停止,但是使用attach
命令进入容器后,使用exit
命令会使容器停止。exec:进入容器后,开启一个新的终端
attach:进入容器正在执行的终端,不会启动新的进程
删除容器
shell
docker rm 容器id/容器名称
docker rm -f $(docker ps -aq) # 删除全部容器
docker ps -a -q|xargs docker rm # 运用linux的管道符删除全部容器
注意:运行中的容器不能直接删除,如果要删除运行中的容器,需要使用命令
rm -f
进行强制删除
启动/停止容器
bash
docker start 容器ID/容器名称 # 启动容器
docker restart 容器ID/容器名称 # 重启容器
docker stop 容器ID/容器名称 # 停止当前正在运行的容器
docker kill 容器ID/容器名称 # 强制停止当前容器
3.5 常用其他命令
后台启动容器
shell
[root@iZbp13w34ju4eg8v1cixguZ /]# docker run -dit --name centos1 centos bash # 通过指定参数 -d 来指定后台运行
但是当我们执行下面命令时
bash
docker run -d centos
然后后执行docker ps
,会发现centos已经停止了。容器使用后台运行时,就必须要有一个前台进程,如果没有前台进程,docker就会发现没有对外提供服务的应用,就会自动停止。
查看日志
bash
docker logs
常用参数:
选项 | 默认 | 描述 |
---|---|---|
--details |
显示提供给日志的额外详细信息 | |
-f, --follow |
跟随日志输出 | |
--since |
显示自时间戳(例如2013-01-02T13:23:37Z )或相对时间(例如42m 42 分钟)以来的日志 |
|
-n, --tail |
all |
从日志末尾开始显示的行数 |
-t, --timestamps |
显示时间戳 | |
--until |
API 1.35+ 在时间戳(例如2013-01-02T13:23:37Z )或相对时间(例如42m 42 分钟)之前显示日志 |
查看容器中的进程信息
bash
docker top 容器ID/名称
查看镜像元数据
shell
docker inspect 容器ID/名称
参数:
选项 | 默认 | 描述 |
---|---|---|
-f, --format |
使用自定义模板设置输出格式: 'json':以 JSON 格式打印 'TEMPLATE':使用给定的 Go 模板打印输出。 | |
-s, --size |
显示总文件大小 |
从容器内拷贝文件到主机上
shell
docker cp 容器:容器内路径 目的主机路径
如:
shell
docker cp centos1:/home/test.py /home # 从容器内拷贝到主机
docker cp /home/my.py centos1:/home # 从主机拷贝到容器内
一般从主机拷贝到容器内,不实用cp
命令进行拷贝,我们一般使用挂载!
拷贝是一个手动过程,未来我们使用-v
卷的技术,可以实现主机目录与容器内目录同步。
查看cpu状态
shell
docker stats
3.6 练习
shell
docker search -f starts=100 nginx # 搜索nginx镜像
dokcer pull nginx # 下载镜像
dokcer run -d --name nginx1 -p 3344:80 nginx # 通过镜像创建容器并启动 主机端口3344映射到容器端口80
按照上面的操作,我们就可以通过公网ip:3344
访问nginx
配置tomcat
shell
docker pull tomcat:90. # 下载镜像 指定版本
docker run -it --rm -p 8888:8080 tomcat # 启动容器
启动后访问公网ip:8888
即可访问,但是出现404
,因为docker镜像的tomcat是阉割版本
--rm
:停止了容器后,自动删除容器,一般用来测试,用完即删
部署es
shell
# es 暴露的端口很多
# es 十分的耗内存
# es 的数据一般需要放置到安全目录! 挂载
# --net somenetwork ? 网络配置
# -e:环境配置修改
# 启动 elasticsearch
docker run -d --name es -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.6.2
docker stop es # 关闭es
docker run -d --name es02 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.6.2 # 启动 并限制内存
4. Protainer可视化面板
Protainer是docker图像化界面管理工具,提供一个后台面板供我们操作!
shell
docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
访问测试:
访问公网IP:8088
,即可访问到管理页面。
首先,设置密码
设置数据,一般是选择本地数据Local
成功进入!
5. Docker镜像
Docker镜像是什么?
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基本运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
UnionFS(联合文件系统)
UnionFS:Union文件系统是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同的目录挂载到同一个虚拟文件系统下。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等等。
平时我们安装虚拟机的CentOs都是好几个G,为什么docker才几百M
对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供 rootfs 就行了。由此可见对于不同的linux发行版, bootfs基本是一致的, rootfs会有差别, 因此不同的发行版可以公用bootfs。
分层理解
下载docker镜像时一层一层的其实就是分层最直观的体现(已经下载过得不会重复下载)
我们还可以通过inspect
命令看到分层的具体信息,如
shell
docker image inspect nginx:latest
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f",
"sha256:e379e8aedd4d72bb4c529a4ca07a4e4d230b5a1d3f7a61bc80179e8f02421ad8",
"sha256:b8d6e692a25e11b0d32c5c3dd544b71b1085ddc1fddad08e68cbd7fda7f70221",
"sha256:f1db227348d0a5e0b99b15a096d930d1a69db7474a1847acbc31f05e4ef8df8c",
"sha256:32ce5f6a5106cc637d09a98289782edf47c32cb082dc475dd47cbf19a4f866da",
"sha256:d874fd2bc83bb3322b566df739681fbd2248c58d3369cb25908d68e7ed6040a6"
]
}
分层的好处:
最大的一个好处就是 - 共享资源
比如:有多个镜像都从相同的 base 镜像构建而来,那么 Docker Host 只需在磁盘上保存一份 base 镜像;同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
这时可能就有人会问了:如果多个容器共享一份基础镜像,当某个容器修改了基础镜像的内容,比如 /etc 下的文件,这时其他容器的 /etc 是否也会被修改? 答案:不会!因为修改会被限制在单个容器内。
Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部。
这一层就是我们通常说的容器层,容器之下的都叫做镜像层。所有操作都是针对容器层的,只有容器层是可写的,容器层下面的所有镜像层都是只读的。
commit镜像
容器提交为镜像
shell
# docker commit 提交容器称为一个新的副本
docker commit -m="提交的描述信息" -a="作者" 容器ID 目标镜像名:[TAG]
[root@iZbp13w34ju4eg8v1cixguZ /]# docer commit -m "my tomcat" -a "codewei" tomcat01 mytomcat01:1.0
6. 容器数据卷
需求:希望数据可以存储在本地,不存储在容器内
容器与主机之间可以有一个数据共享的技术!那么就是容器数据卷。
Docker容器中产生的数据,可以同步到本地。
也就是目录挂载,将我们容器内到目录,挂载到宿主机上面。
数据卷就是为了容器的持久化和同步操作。
容器间也是可以数据共享的。
6.1 使用命令来挂载 -v
shell
docker run -it -v 主机目录地址:容器内目录
docker run -it --name centos2 -v /home/test:/home centos /bin/bash
这样,在容器内的/home目录下创建文件,在本地宿主机上也可以看到这个创建的文件。
安装MySQL
shell
# 获取镜像
docker pull mysql:5.7
# 运行容器,做数据挂载
# 注意:安装启动mysql的时候,是需要配置密码的!
# -d 后台运行 -p 端口映射 --name 名字 -v 数据集挂载 -e 环境配置
docker run -d -p 8888:3306 --name mysql01 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=shw123zxc mysql:5.7
这样就实现了mysql数据挂载到了本地!
具名和匿名挂载
匿名挂载:匿名挂载就是在指定数据卷的时候,不指定容器路径对应的主机路径,这样对应映射的主机路径就是默认的路径/var/lib/docker/volumes/中自动生成一个随机命名的文件夹。
只指定容器内目录,不指定容器外目录
shell
docker run -d -P --name nginx01 -v /etc/nginx nginx
具名挂载:具名挂载,就是指定文件夹名称,区别于指定路径挂载,这里的指定文件夹名称是在Docker指定的默认数据卷路径下的。
shell
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
注意!上面这个命令
juming-nginx
前面没有斜杠/
,一旦加了斜杠就成为了目录地址,这样不加,就是名字
通过docker volume
查看所有卷的情况
shell
docker volume ls
通过命令,查看具名挂载卷到本地的目录信息
shell
docker volume inspect 卷名
具名挂载和匿名挂载会挂载到Docker指定的默认数据卷路径下:
/var/lib/docker/volumes/xxxx/_data
Mac系统是在:/usr/local/lib/docker
我们通过具名挂载可以方便的找到我们的卷,大多数情况都会使用具名挂载
总结:
-v 容器内路径 # 匿名挂载
-v 卷名:容器内路径 # 具名挂载
-v /主机目录:容器内目录 # 指定路径挂载
拓展:
一旦设定了容器权限,容器对我们挂载出来的内容就有限定了!
shell
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx
ro
:read-only,只读
rw
:readwrite,可读可写只要看到
ro
,就说明,这个指定的目录只能通过宿主机来操作,容器内部无法操作!!
6.2 Dockerfile挂载
Dockerfile就是用来构建docker镜像文件的构建文件!
通过这个脚本文件可以生成镜像,镜像是一层层的,脚本是一个一个的命令,每个命令都是一层。
编写Dockerfile
创建dockerfile文件,名字可以随意,但是建议是dockerfile
注意:dockerfile中的名字全部都是大写的
这里的每个命令,就是镜像的一层
shell
# 指令 参数
FROM centos
VOLUME ["volume01","volume02"] # 挂载卷 匿名挂载两个卷
CMD echo "----success----" # 调用cmd输出
CMD /bin/bash # 调用cmd 运行bash
通过Dockerfile构建镜像
shell
docker build -f dockerfile1 -t codewei/centos:1.0 .
# -f 指定dockerfile路径
# -t 镜像名称
# .
通过镜像创建容器,并启动容器
shell
docker run -it --name mycentos codewei/centos:1.0 /bin/bash
# 通过下面命令,可以查看容器中目录挂载到的位置
dokcer inspect 容器id/容器名称
这种方式,我们使用的非常多!
不可以通过Dockerfile进行具名挂载
6.3 数据卷容器
两个容器之间共享同步数据。例如,两个mysql同步数据。
通过我们刚才自己写的镜像,启动三个容器
shell
docker run -it --name centos01 codewei/centos:1.0 /bin/bash
docker run -it --name centos02 --volumes-from centos01 codewei/centos:1.0 /bin/bash
docker run -it --name centos03 --volumes-from centos01 codewei/centos:1.0 /bin/bash
这样,在centos02
中的volume01
中创建文件,在centos01
的volume01
中也能看得到,这样就是将centos02
挂载了centos01
的目录。这里的centos01
就称为数据卷容器。
centos03
也挂载centos01
,这样在centos02
中创建文件,centos3
中也会同步。
如果将
centos01
删除,也不会影响到cenetos2
和centos3
个人理解:
centos1
是挂载了本地目录,假设这个是通过指针指向了本地目录
centos2
和centos3
在启动时挂载centos1
所挂载的目录,也就是centos2
和centos3
会继承centos1
指向本地的指针通过
--volumes-from
看似指向了centos1
,其实是centos1
将其所挂载的指针给了centos2
和centos3
疑问解答
**疑问:**如果我在dockerfile1中定义了 VOLUME ["v1","v2"]
,在dockerfile2中定义了VOLUME["v3","v4"]
,然后通过dockerfile1创建镜像并启动了centos1,通过dockerfile2创建了镜像,并且在启动时通过 --volumes-from
指定了centos1,命名这台为centos2,那么centos2如何挂载?
解答:
在这种情况下,centos2
将会挂载 centos1
中定义的卷(v1
和 v2
),而不是 centos2
中定义的卷(v3
和 v4
)。
当你使用 --volumes-from
参数启动 centos2
时,它将会继承自 centos1
的卷挂载点。换句话说,centos2
将会拥有 centos1
中定义的卷,并将其挂载到相应的路径上,而不会创建 centos2
自己的卷。
因此,无论你在 dockerfile2
中定义了什么卷,centos2
在启动时都会挂载 centos1
中定义的卷。
v3
和v4
会在centos2
中存在,但是它不会从本地挂载,只能在容器内部使用,也就相当于是容器内部的目录里。
多个mysql实现数据共享
shell
dokcer run -d -p 3340:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7 # mysql01 通过匿名挂载本地
dokcer run -d -p 3310:3306 --volumes-from mysql01 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 mysql:5.7 #mysql02挂载mysql01
7. DockerFile
7.1 介绍
dockerfile是用来构建docker镜像的文件。它就是一个命令参数脚本。
构建步骤:
- 编写一个dockerfile文件
docker build
构建成为一个镜像docker run
运行镜像docker push
发布镜像(发布到DockerHub、阿里云镜像仓库)
很多官方镜像都是基础包,很多功能没有,我们通常会自己搭建自己的镜像。
7.2 构建过程
基础知识:
- 每个保留关键字(指令)都必须是大写字母
- 执行顺序为从上至下
#
表示注释- 每一个指令都会创建提交一个新的镜像层,并提交
dockerfile是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile文件,这个文件十分简单。
Docker镜像逐渐成为企业交付的标准。
DockerFile:构建文件,定义了一切的步骤。
DockerImages:通过DockerFile构建生成的镜像,最终发布和运行的产品。
Docker容器:容器就是镜像运行起来提供服务的。
7.3 DockerFile指令
From
:基础镜像,一切从这里开始构建,如centos等MAINTAINER
:镜像是谁写的,一般留姓名+邮箱RUN
:镜像构建的时候需要运行的命令ADD
:添加内容,如果我们要建一个Tomcat镜像,就通过ADD添加一个tomcat的压缩包WORKDIR
:镜像的工作目录VOLUME
:设置挂载的目录(匿名挂载)EXPOSE
:指定暴露端口CMD
:指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代ENTRYPOINT
:指定这个容器启动的时候要运行的命令,可以追加命令ONBUILD
:当构建一个被继承的镜像,这个时候就会运行这个指令,是一个触发指令COPY
:类似ADD
命令,将文件拷贝到镜像中ENV
:构建的时候设置环境变量
实战:构建自己的Centos
Docker Hub中99%的镜像都是从这个基础镜像过来的,From scratch
然后配置需要的软件和配置进行构建
创建自己的Centos:
创建dockerfile文件,mydockerfile-centos
dockerfile
FROM centos:7
MAINTAINER codewei<mango_1698@163.com>
ENV MYPATH /usr/local # 环境变量 key-value
WORKDIR $MYPATH # $ 取环境看了定义的值
RUN yum -y install vim # 安装vim
RUN yum -y install net-tools # 安装net-tools,安装后就可以使用ifconfig命令了
EXPOSE 80 # 暴露端口
CMD echo $MYPATH
CMD echo ------ Success ------
CMD /bin/bash
通过dockerfile,构建镜像
bash
docker build -f mydockerfile-centos -t mycentos:1.0 .
通过该镜像创建容器并启动
bash
docker run -it --name mycentos mycentos:1.0
我们可以列出本地镜像的变更历史,通过docker history
命令
bash
docker history mycentos:1.0
CMD和ENTRYPOINT的区别
CMD
:指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代ENTRYPOINT
:指定这个容器启动的时候要运行的命令,可以追加命令
测试CMD
创建dockerifle-cmd-test
文件
dockerfile
FROM centos
CMD ["ls","-a"]
通过该文件构建镜像
bash
docker build -f dockerfile-cmd-test -t docker-cmd-test:1.0 .
通过该镜像创建容器并运行
bash
docker run docker-cmd-test:1.0
此时,我们看到我们的ls -a
命令生效了。如果我们想追加一个参数-l
,也就是我们期望执行ls -al
bash
docker run docker-cmd-test:1.0 -l
此时,我们发现报错了!
这就是在使用CMD
指令的情况下,-l
会替换掉CMD
所指定的['ls','-a']
命令,-l
不是一个正确的命令,所以会报错
如果,此时我们想要用ls -al
命令的话,就要写完整
bash
docker run docker-cmd-test:1.0 ls -al
这样就意味着,ls -al
命令替换掉了CMD ['ls','-a']
,此时就可以正确执行了。
测试ENTRYPOINT
创建dockerifle-entrypoint-test
文件
dockerfile
FROM centos
ENTRYPOINT ["ls","-a"]
通过该文件构建镜像
bash
docker build -f dockerfile-entrypoint-test -t docker-entrypoint-test:1.0 .
通过该镜像创建容器并运行
bash
docker run docker-entrypoint-test:1.0
此时我们想要追加命令-l
,可以直接追加,其不会覆盖dockerfile中定义的命令
bash
docker run docker-entrypoint-test:1.0 -l
这样,便会执行ls -al
实战:Tomcat镜像
-
准备镜像文件,tomcat压缩包,jdk压缩包
-
编写dockerfile文件,官方命名
Dockerfile
,如果使用了官方命名,我们在执行build
命名构建镜像时,就不再需要使用-f
参数,它会自动寻找这个文件。dockerfileFROM centos:7 MAINTAINER codewei<mango_1698@163.com> COPY readme.txt /usr/local/readme.txt ADD jdk-8u401-linux-x64.tar.gz /usr/local ADD apache-tomcat-9.0.46.tar.gz /usr/local RUN yum -y install vim ENV MYPATH /usr/local WORKDIR $MYPATH ENV JAVA_HOME /usr/local/jdk1.8.0_401 ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.46 ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.46 ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin EXPOSE 8080 CMD /usr/local/apache-tomcat-9.0.46/bin/startup.sh
使用
ADD
指令,它会帮我们自动解压
通过该文件,构建镜像diytomcat
bash
docker build -t diytomcat .
启动容器
bash
docker run -it -p 9999:8080 --name diytomcat01 -v /home/environment/build/tomcat/test:/usr/local/apache-tomcat-9.0.46/webapps/test -v /home/environment/build/tomcatlogs:/usr/local/apache-tomcat-9.0.46/logs diytomcat
访问测试
发布项目(由于做了挂载,所以我们可以直接在本地编写项目就可以发布了)
8. 发布镜像
8.1 发布镜像到Docker Hub
Docker Hub:https://hub.docker.com/
- 注册自己的账号
- 在服务器上提交自己的镜像
bash
[root@localhost ~]# docker login --help
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
登录完毕后提交镜像
bash
# 登录
[root@localhost ~]# docker login -u candy821
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
# 提交镜像,带上版本号,否则会被拒绝
[root@localhost ~]# docker push diytomcat
Using default tag: latest
The push refers to repository [docker.io/library/diytomcat]
3514cac4541c: Preparing
b9a3a8ee3650: Preparing
aac332cceb25: Preparing
74ddd0ec08fa: Preparing
denied: requested access to the resource is denied # 被拒绝
# 解决,重新命名
[root@localhost ~]# docker push candy821/diytomcat:1.0
The push refers to repository [docker.io/candy821/diytomcat]
An image does not exist locally with the tag: candy821/diytomcat
# 重新命名再提交
[root@localhost ~]# docker tag 89f2a31cc01a candy821/tomcat:1.0
[root@localhost ~]# docker push candy821/tomcat:1.0
The push refers to repository [docker.io/candy821/tomcat]
3514cac4541c: Pushed
b9a3a8ee3650: Pushed
aac332cceb25: Pushed
74ddd0ec08fa: Pushed
1.0: digest: sha256:73540a37afd6060c8989966e0bdc4f9b2906319ce3ed0fe3ba50d11c1ee71bc4 size: 1161
退出登录
bash
[root@localhost ~]# docker logout
8.2 发布镜像到阿里云容器服务
1.登录阿里云
2.找到镜像服务
3.创建命名空间
4.创建容器镜像
5.浏览页面信息
9. Docker网络原理
9.1 理解Docker网络
清空所有环境
bash
# 删除所有容器
[root@localhost ~]# docker rm -f $(docker ps -aq)
# 删除所有镜像
[root@localhost ~]# docker rmi -f $(docker images -aq)
Docker是如何处理容器网络访问的
bash
# 运行容器
[root@localhost ~]# docker run -d -P --name tomcat01 tomcat
# 查看容器内部网络地址
# 若没有被阉割命令
[root@localhost ~]# docker exec -it tomcat01 ip addr
# 若被阉割命令,先进入容器
[root@localhost ~]# docker exec -it tomcat01 /bin/bash
# 再查看
root@3e7a784d1d49:/usr/local/tomcat# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 3e7a784d1d49
# Linux能不能ping通容器内部
[root@localhost ~]# 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.065 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.071 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.127 ms
64 bytes from 172.17.0.2: icmp_seq=4 ttl=64 time=0.050 ms
64 bytes from 172.17.0.2: icmp_seq=5 ttl=64 time=0.060 ms
... ...
# Linux可以ping通docker容器内部
原理:
1.每启动一个Docker容器,Docker就会给Docker容器分配一个IP,只要安装了Docker,就会有一个网卡Docker桥接模式,使用的技术是 evth-pair技术再次测试ip addr
2.在启动一个容器测试,发现又多了一对网卡
markdown
# 发现这个容器带来的网卡,都是一对一对的
# evth-pair 就是一对虚拟设备接口,他们都是成对出现的,一端连着协议,一端彼此相连
# 正因为有这个特性,evth-pair 充当一个桥梁,连接各种虚拟网络设备的
# OpenStac,Docker容器之间的连接,OVS的连接,都是使用 evth-pair 技术
3.测试 tomcat01 和 tomcat02 是否可以ping通
bash
[root@localhost ~]# docker exec -it tomcat02 ping 172.17.0.2
# 结论:容器和容器之间是可以互相ping通的
绘制一个网络模型图:
结论:Tomcat01和Tomcat02是公用的一个路由器,Docker0。
所有的容器不指定网络的情况下,都是Docker0路由的,Docker会给我们的容器分配一个默认的可用IP。
Docker使用的是Linux的桥接,宿主机是一个Docker容器的网桥
Docker中的所有的网络接口都是虚拟的,虚拟的转发效率高!(内网传递文件!)
只要容器删除,对应网桥一对就没了。
9.2 -link
思考一个场景:我们编写了一个微服务,database url=ip: ,项目不重启,数据库ip换了,我们希望可以处理这个问题,可以通过名字来进行访问容器?
bash
# 如何可以解决呢?
[root@localhost ~]# docker exec -it tomcat02 ping tomcat01
ping: tomcat01: Name or service not known
# 通过 --link 就可以解决网络连通问题
[root@localhost ~]# docker run -d -P --name tomcat03 --link tomcat02 tomcat
8d727b26b070698d3a84db2b6269f1a86edbb1c110a23c2733addef16f6e3bef
[root@localhost ~]# docker exec -it tomcat03 ping tomcat02
# 反向可以ping通吗
[root@localhost ~]# docker exec -it tomcat02 ping tomcat03
ping: tomcat03: Name or service not known
- 探究:
docker network inspect bridge
其实这个tomcat03就是在本地配置了tomcat02的配置
bash
# 查看 hosts 配置,原理发现
[root@localhost ~]# docker exec -it tomcat03 cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3 tomcat02 6846b34d8575
172.17.0.4 8d727b26b070
本质探究
---link 就是我们在hosts配置中增加了一个172.17.0.3 tomcat02 6846b34d8575
的映射,但是现在学习Docker已经不建议使用 ---link 了,自定义网络不适用于Docker0!!!
Docker0问题:不支持容器名连接访问
9.3 自定义网络
查看所有的Docker网络
bash
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
7ce5bdb21f44 bridge bridge local
cd5de991401c host host local
fbd8caf8d260 none null
local
网络模式
- Bridge:桥接模式,Docker上搭桥
- None:不配置网络
- Host:和宿主机共享网络
- Container:容器内网络连通
测试
bash
# 直接启动的命令 --net bridge,这个就是docker0
[root@localhost ~]# docker run -d -P --name tomcat01 --net bridge tomcat
# docker0特点:默认,域名不能访问,--link可以打通连接
# 自定义一个网络!
# --driver bridge
# --subnet 192.168.0.0/16 192.168.0.2-->192.168.255.255
# --gateway 192.168.0.1
[root@localhost ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
eee16692df425281c8ce3ce0d2d9cd3e98b442ee483e536f28051b75c4e7fea6
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
7ce5bdb21f44 bridge bridge local
cd5de991401c host host local
eee16692df42 mynet bridge local
fbd8caf8d260 none null local
自己的网络就创建好了
bash
[root@localhost ~]# docker run -d -P --name tomcat-net-01 --net mynet tomcat
eebad75b51ea69848416542d16898efc49fb60a8cfcb10cb013563ca351d6493
[root@localhost ~]# docker run -d -P --name tomcat-net-02 --net mynet tomcat
aa84a57faf837c325c9694e27b3a4290ec6628ee5a92b38123f9525030088773
[root@localhost ~]# docker network inspect mynet
[
{
"Name": "mynet",
"Id": "eee16692df425281c8ce3ce0d2d9cd3e98b442ee483e536f28051b75c4e7fea6",
"Created": "2021-12-17T07:36:18.272108685+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.0.0/16",
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"aa84a57faf837c325c9694e27b3a4290ec6628ee5a92b38123f9525030088773": {
"Name": "tomcat-net-02",
"EndpointID": "8c3923f7409afc7c759f34ef18c0219dda7f09f361b654afcad13382ac2f96f5",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
},
"eebad75b51ea69848416542d16898efc49fb60a8cfcb10cb013563ca351d6493": {
"Name": "tomcat-net-01",
"EndpointID": "2750d27a0fb5a2529fd6261b6c645db98c1597db6f43d84da09de755e1d6f95b",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
# ping不通可能是因为tomcat里没有ping命令,可以换个方式
[root@localhost ~]# docker exec -it tomcat-net-01 ping 192.168.0.3
OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: "ping": executable file not found in $PATH: unknown
# 换个方式可以
[root@localhost ~]# docker exec -it tomcat-net-01 curl tomcat-net-02:8080
<!doctype html><html lang="en"><head><title>HTTP Status 404 -- Not Found</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 404 -- Not Found</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.</p><hr class="line" /><h3>Apache Tomcat/10.0.14</h3></body></html>
自定义的网络docker都已经帮我们维护好了对应的关系,推荐平时这样使用网络!
好处:
Redis - 不同的集群使用不同的网络,保证集群是安全和健康的
MySQL - 不同的集群使用不同的网络,保证集群是安全和健康的
9.4 网络连通
bash
# 测试打通 tomcat01 - mynet
[root@localhost ~]# docker network connect mynet tomcat01
# 连通之后,就是将tomcat01放到了mynet网络下
# 一个容器,两个ip地址 阿里云服务:公网ip,私网ip
bash
# tomcat01可以打通tomcat-net-01
[root@localhost ~]# docker exec -it tomcat01 curl tomcat-net-01:8080
<!doctype html><html lang="en"><head><title>HTTP Status 404 -- Not Found</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 404 -- Not Found</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.</p><hr class="line" /><h3>Apache Tomcat/10.0.14</h3></body></html>
结论:假设要跨网络操作别人,就需要使用docker network connect
连通
10. 部署Redis集群实战
启动六个容器,三主三从(分片+高可用+负载均衡)
bash
# 创建网卡
docker network create redis --subnet 172.38.0.0/16
# 通过脚本创建六个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 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
# 启动
docker run -p 6371:6379 -p 16371:16379 --name redis-1 \
-v /mydata/reids/node-1/data:/data \
-v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6372:6379 -p 16372:16379 --name redis-2 \
-v /mydata/reids/node-2/data:/data \
-v /mydata/redis/node-2/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.12 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6373:6379 -p 16373:16379 --name redis-3 \
-v /mydata/reids/node-3/data:/data \
-v /mydata/redis/node-3/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.13 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6374:6379 -p 16374:16379 --name redis-4 \
-v /mydata/reids/node-4/data:/data \
-v /mydata/redis/node-4/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.14 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6375:6379 -p 16375:16379 --name redis-5 \
-v /mydata/reids/node-5/data:/data \
-v /mydata/redis/node-5/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.15 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6376:6379 -p 16376:16379 --name redis-6 \
-v /mydata/reids/node-6/data:/data \
-v /mydata/redis/node-6/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.16 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
[root@localhost conf]# docker exec -it redis-1 /bin/sh
/data # ls
appendonly.aof nodes.conf
# 创建集群
/data # 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
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: 0c97073d209ffccedb055c68f2dbe8c481a77504 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
M: 84d8d25a96ba851ba8b4fe54a925255b415ad4bc 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
M: 3dfbd972115d98be889adca20199f9d7c5c5577a 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
S: 6101466ee56f010b223e9495963e724b59c6a435 172.38.0.14:6379
replicates 3dfbd972115d98be889adca20199f9d7c5c5577a
S: 47895310a5642cb566b03450ffe8e23b12b5d3bf 172.38.0.15:6379
replicates 0c97073d209ffccedb055c68f2dbe8c481a77504
S: 968211de1981e640d8a194e0700ac856dc88a6d0 172.38.0.16:6379
replicates 84d8d25a96ba851ba8b4fe54a925255b415ad4bc
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 172.38.0.11:6379)
M: 0c97073d209ffccedb055c68f2dbe8c481a77504 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 968211de1981e640d8a194e0700ac856dc88a6d0 172.38.0.16:6379
slots: (0 slots) slave
replicates 84d8d25a96ba851ba8b4fe54a925255b415ad4bc
M: 3dfbd972115d98be889adca20199f9d7c5c5577a 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: 84d8d25a96ba851ba8b4fe54a925255b415ad4bc 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 6101466ee56f010b223e9495963e724b59c6a435 172.38.0.14:6379
slots: (0 slots) slave
replicates 3dfbd972115d98be889adca20199f9d7c5c5577a
S: 47895310a5642cb566b03450ffe8e23b12b5d3bf 172.38.0.15:6379
slots: (0 slots) slave
replicates 0c97073d209ffccedb055c68f2dbe8c481a77504
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
Docker搭建Redis集群完成!
bash
# 进入集群
/data # redis-cli -c
# 查看集群信息
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:1643
cluster_stats_messages_pong_sent:1604
cluster_stats_messages_sent:3247
cluster_stats_messages_ping_received:1599
cluster_stats_messages_pong_received:1643
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:3247
# 查看集群节点-->redis-3是主节点,redis-4是从节点
127.0.0.1:6379> cluster nodes
968211de1981e640d8a194e0700ac856dc88a6d0 172.38.0.16:6379@16379 slave 84d8d25a96ba851ba8b4fe54a925255b415ad4bc 0 1639727967684 6 connected
0c97073d209ffccedb055c68f2dbe8c481a77504 172.38.0.11:6379@16379 myself,master - 0 1639727967000 1 connected 0-5460
3dfbd972115d98be889adca20199f9d7c5c5577a 172.38.0.13:6379@16379 master - 0 1639727968501 3 connected 10923-16383
84d8d25a96ba851ba8b4fe54a925255b415ad4bc 172.38.0.12:6379@16379 master - 0 1639727968707 2 connected 5461-10922
6101466ee56f010b223e9495963e724b59c6a435 172.38.0.14:6379@16379 slave 3dfbd972115d98be889adca20199f9d7c5c5577a 0 1639727968707 4 connected
47895310a5642cb566b03450ffe8e23b12b5d3bf 172.38.0.15:6379@16379 slave 0c97073d209ffccedb055c68f2dbe8c481a77504 0 1639727967000 5 connected
# 存储值-->存在redis-3里
127.0.0.1:6379> set a b
-> Redirected to slot [15495] located at 172.38.0.13:6379
OK
# 停掉172.38.0.13
[root@localhost ~]# docker stop redis-3
redis-3
# 关闭集群
Ctrl+C
# 重新启动集群
/data # redis-cli -c
# 获取值-->在redis-4中
127.0.0.1:6379> get a
-> Redirected to slot [15495] located at 172.38.0.14:6379
"b"
# 查看节点-->redis-3停掉了,redis-4变成了主节点
172.38.0.14:6379> cluster nodes
3dfbd972115d98be889adca20199f9d7c5c5577a 172.38.0.13:6379@16379 master,fail - 1639735747538 1639735745000 3 connected
47895310a5642cb566b03450ffe8e23b12b5d3bf 172.38.0.15:6379@16379 slave 0c97073d209ffccedb055c68f2dbe8c481a77504 0 1639735859000 5 connected
968211de1981e640d8a194e0700ac856dc88a6d0 172.38.0.16:6379@16379 slave 84d8d25a96ba851ba8b4fe54a925255b415ad4bc 0 1639735860000 6 connected
84d8d25a96ba851ba8b4fe54a925255b415ad4bc 172.38.0.12:6379@16379 master - 0 1639735859000 2 connected 5461-10922
6101466ee56f010b223e9495963e724b59c6a435 172.38.0.14:6379@16379 myself,master - 0 1639735858000 7 connected 10923-16383
0c97073d209ffccedb055c68f2dbe8c481a77504 172.38.0.11:6379@16379 master - 0 1639735859579 1 connected 0-5460
# 退出
172.38.0.14:6379> exit
/data # exit
[root@localhost conf]# clear
[root@localhost conf]# docker rm -f $(docker ps -aq)
3ca0ed7155f6
9aaa81b6a46c
80cf12d69a3c
464f19f93ac4
3d7e55508b14
8a9e49712cc9
使用了Docker之后,所有的技术都会慢慢变得简单起来。
11. SpringBoot微服务打包Docker镜像
1、构建SpringBoot项目
2、打包应用
3、编写dockerfile
bash
FROM java:8
COPY *.jar /app.jar
CMD ["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
4、构建镜像
bash
[root@localhost conf]# cd /home
[root@localhost home]# ls
ceshi dockerfile docker-test-volume mysql Packages tomcat
[root@localhost home]# mkdir idea
[root@localhost home]# cd idea
[root@localhost idea]# ls
[root@localhost idea]# ls
Docker-0.0.1-SNAPSHOT.jar Dockerfile
[root@localhost idea]# docker build -t idea666 .
Sending build context to Docker daemon 17.56MB
Step 1/5 : FROM java:8
8: Pulling from library/java
5040bd298390: Pull complete
fce5728aad85: Pull complete
76610ec20bf5: Pull complete
60170fec2151: Pull complete
e98f73de8f0d: Pull complete
11f7af24ed9c: Pull complete
49e2d6393f32: Pull complete
bb9cdec9c7f3: Pull complete
Digest: sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c0e9d
Status: Downloaded newer image for java:8
---> d23bdf5b1b1b
Step 2/5 : COPY *.jar /app.jar
---> 6d10e7a0a488
Step 3/5 : CMD ["--server.port=8080"]
---> Running in d25c16614512
Removing intermediate container d25c16614512
---> 56d94e2cba98
Step 4/5 : EXPOSE 8080
---> Running in a5682ef63fd1
Removing intermediate container a5682ef63fd1
---> 635c54c90069
Step 5/5 : ENTRYPOINT ["java","-jar","/app.jar"]
---> Running in 43739bc53e3e
Removing intermediate container 43739bc53e3e
---> 73188d20cb6c
Successfully built 73188d20cb6c
Successfully tagged idea666:latest
5、发布运行
bash
[root@localhost idea]# docker run -d -P --name idea-springboot-web idea666
972a723ab17766b87cd8f0199db61c0494f5a5a3cfffd4a6b4a50c94dd064eab
[root@localhost idea]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
972a723ab177 idea666 "java -jar /app.jar ..." 12 seconds ago Up 10 seconds 0.0.0.0:49165->8080/tcp, :::49165->8080/tcp idea-springboot-web
# 访问49165->8080
[root@localhost idea]# curl localhost:49165/hello
Hello,Controller!
以后我们使用了Docker之后,给别人交付的就是一个镜像即可!