Docker
1、入门基础
1.1、虚拟化技术
1.1.1、什么是虚拟化技术
虚拟化(Virtualization)是一种资源管理技术。它是将计算机的各种实体资源,如:服务器、网络、内存及存储等,予以抽象、转换后呈现出来,打破实体结构间的不可切割的障碍,使用户可以用更好的方式来利用这些资源。 虚拟化的目的是为了在同一个主机上运行多个系统或应用,从而提高系统资源的利用率,并带来降低成本、方便管理和容错容灾等好处。
从实现形式来分,虚拟化技术可分为基于硬件的虚拟化和基于软件的虚拟化。
KVM - KVM是一个Linux kernel模块,可以使用modprobe来加载KVM,加载后还需要通过其他工具创建虚拟机。KVM是一个全虚拟化的解决方案,但需要CPU支持虚拟化功能。相比Xen来说,KVM可以更加方便的整合进Linux内核,但它还需要其它虚拟化软件(如:QEMU)才能实现虚拟化功能。
LXC - 即:Linux Container,Linux容器,是一种轻量级的虚拟化的手段。它可以提供轻量级的虚拟化,以隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。容器会有效地将由单个操作系统管理的资源划分到孤立的组中,以更好地在孤立的组之间平衡有冲突的资源使用需求。
1.1.2、什么是Docker
Docker是一个开源的应用容器引擎,它让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到安装了任何 Linux 发行版本的机器上。Docker基于LXC来实现类似VM的功能,可以在更有限的硬件资源上提供给用户更多的计算资源。与同VM等虚拟化的方式不同,LXC不属于全虚拟化、部分虚拟化或半虚拟化中的任何一个分类,而是一个操作系统级虚拟化。
Docker是直接运行在宿主操作系统之上的一个容器,使用沙箱机制完全虚拟出一个完整的操作,容器之间不会有任何接口,从而让容器与宿主机之间、容器与容器之间隔离的更加彻底。每个容器会有自己的权限管理,独立的网络与存储栈,及自己的资源管理能,使同一台宿主机上可以友好的共存多个容器。
Docker借助Linux的内核特性,如:控制组(Control Group)、命名空间(Namespace)等,并直接调用操作系统的系统调用接口。从而降低每个容器的系统开销,并实现降低容器复杂度、启动快、资源占用小等特征。
1.1.3、Docker能干什么
-
简化配置:Docker在降低额外开销的情况下提供了和虚拟机同样的功能。它能让你将运行环境和配置放在代码中然后部署,同一个Docker的配置可以在不同的环境中使用,这样就降低了硬件要求和应用环境之间耦合度。
-
代码流水线管理:代码从开发者的机器到最终在生产环境上的部署,需要经过很多的中间环境。而每一个中间环境都有自己微小的差别,Docker给应用提供了一个从开发到上线均一致的环境,让代码的流水线变得更简单。
-
提高开发效率:Docker能使开发环境尽量贴近生产环境,并且能够快速搭建开发环境。
-
隔离应用:在一个机器上运行不同的应用。
-
整合服务器:Docker没有多个操作系统的内存占用,以及能在多个实例之间共享没有使用的内存,可以比虚拟机提供更好的服务器整合解决方案。
-
调适能力:Docker提供了很多的工具,它们提供了很多的功能,包括可以为容器设置检查点、设置版本和查看两个容器之间的差别,这些特性可以帮助调试Bug。
-
多租户:在多租户的应用中,可以避免关键应用的重写。Docker可以为每一个租户的应用层的多个实例创建隔离的环境
-
1.1.4、虚拟机和Docker的对比
虚拟机
-
基础设施(Infrastructure)。它可以是你的个人电脑,数据中心的服务器,或者是云主机。
-
主操作系统(Host Operating System)。你的个人电脑之上,运行的可能是MacOS,Windows或者某个Linux发行版。
-
虚拟机管理系统(Hypervisor)。利用Hypervisor,可以在主操作系统之上运行多个不同的从操作系统。类型1的Hypervisor有支持MacOS的HyperKit,支持Windows的Hyper-V以及支持Linux的KVM。类型2的Hypervisor有VirtualBox和VMWare。
-
操作系统(Guest Operating System)。假设你需要运行3个相互隔离的应用,则需要使用Hypervisor启动3个从操作系统,也就是3个虚拟机。这些虚拟机都非常大,也许有700MB,这就意味着它们将占用2.1GB的磁盘空间。更糟糕的是,它们还会消耗很多CPU和内存。
-
各种依赖。每一个从操作系统都需要安装许多依赖。如果你的的应用需要连接PostgreSQL的话,则需要安装libpq-dev;如果你使用Ruby的话,应该需要安装gems;如果使用其他编程语言,比如Python或者Node.js,都会需要安装对应的依赖库。
Docker容器
-
主操作系统(Host Operating System)。所有主流的Linux发行版都可以运行Docker。对于MacOS和Windows,也有一些办法"运行"Docker。
-
Docker守护进程(Docker Daemon)。Docker守护进程取代了Hypervisor,它是运行在操作系统之上的后台进程,负责管理Docker容器。
-
各种依赖。对于Docker,应用的所有依赖都打包在Docker镜像中,Docker容器是基于Docker镜像创建的。
-
应用。应用的源代码与它的依赖都打包在Docker镜像中,不同的应用需要不同的Docker镜像。不同的应用运行在不同的Docker容器中,它们是相互隔离的。
虚拟机是在物理资源层面实现的隔离,而Docker是APP层面实现的隔离,并且省去了虚拟机操作系统(Guest OS),从而节省了一部分的系统资源; Docker守护进程可以直接与主操作系统进行通信,为各个Docker容器分配资源; 它还可以将容器与主操作系统隔离,并将各个容器互相隔离。虚拟机启动需要数分钟,而Docker容器可以在数毫秒内启动。 由于没有臃肿的从操作系统,Docker可以节省大量的磁盘空间以及其他系统资源。
虚拟机与容器docker的区别,在于vm多了一层guest OS,虚拟机的Hypervisor会对硬件资源也进行虚拟化,而容器Docker会直接使用宿主机的硬件资源。
采用形象的比喻区分两者的隔离级别:
-
服务器:比作一个大型的仓管基地,包含场地与零散的货物——相当于各种服务器资源。
-
虚拟机技术:比作仓库,拥有独立的空间堆放各种货物或集装箱,仓库之间完全独立——仓库相当于各种系统,独立的应用系统和操作系统。
-
Docker:比作集装箱,操作各种货物的打包——将各种应用程序和他们所依赖的运行环境打包成标准的容器,容器之间隔离。
虚拟机和Docker容器的进一步对比:
-
隔离性:由于vm对操作系统也进行了虚拟化,隔离的更加彻底。而Docker共享宿主机的操作系统,隔离性较差。
-
运行效率:因为Docker直接利用宿主主机的系统内核,所以生成虚拟机的速率远低于容器Docker生成的速率;虚拟机增加了一个虚拟硬件层,进行数值计算时是运行在Hypervisor虚拟的CPU上的,数值计算程序一般针对特定的cpu架构有一定的优化措施,虚拟化可能使这些措施作废,甚至起到反效果。
-
资源利用率:虚拟机由于隔离更彻底,因此利用率也会相对较低。
1.2、HelloWorld
1.2.1、Docker架构
Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。
-
Docker 客户端(Client) : Docker 客户端通过命令行或者其他工具使用
-
Docker 主机(Host) :一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。
Docker 包括三个基本概念:
-
镜像(Image):Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。
-
容器(Container):镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
-
仓库(Repository):仓库可看着一个代码控制中心,用来保存镜像。
1.2.2、Docker安装
https://docs.docker.com/desktop/mac/install/
1.2.3、配置镜像加速
阿里云的加速器:https://help.aliyun.com/document_detail/60750.html
网易加速器:http://hub-mirror.c.163.com
Docker官方中国加速器:https://registry.docker-cn.com
ustc 的镜像:https://docker.mirrors.ustc.edu.cn
daocloud:https://www.daocloud.io/mirror#accelerator-doc(注册后使用)
配置Docker官方中国加速器:
{
"builder": {
"gc": {
"defaultKeepStorage": "20GB",
"enabled": true
}
},
"experimental": false,
"features": {
"buildkit": true
},
"registry-mirrors": [
"https://registry.docker-cn.com"
]
}
1.2.4、拉取镜像
# 拉取镜像 hello-world 的最新版
docker pull hello-world:latest
1.2.5、运行容器
# 运行镜像的实例(即容器)
docker run hello-world
如果你在没有镜像的时候,直接docker run hello-world
也是可以的;它会先检查本地是否有这个镜像,没有的话会先从指定仓库中拉取。
1.2.6、在容器内运行应用程序
# 创建容器并运行应用程序
# Docker 以 ubuntu 最新的(默认是latest) 镜像创建一个新容器,然后在容器里执行 bin/echo "Hello world",然后输出结果
docker run ubuntu:latest /bin/echo "Hello world"
各个参数的含义:
-
docker
: Docker 的二进制执行文件。 -
run
: 与前面的 docker 组合来运行一个容器。 -
ubuntu:latest
指定要运行的镜像,Docker 首先从本地主机上查找镜像是否存在,如果不存在,Docker 就会从镜像仓库 Docker Hub 下载公共镜像。 -
/bin/echo "Hello world"
: 在启动的容器里执行的命令
1.2.7、交互式容器
通过 docker 的两个参数 -i -t,让 docker 运行的容器实现"对话"的能力
docker run -i -t ubuntu:latest
各个参数解析:
-
-t
: 在新容器内指定一个伪终端或终端。 -
-i
: 允许你对容器内的标准输入 (STDIN) 进行交互。
可以通过运行 exit 命令或者使用 CTRL+D 来退出容器
1.2.8、以后台模式运行容器
查看当前运行的容器实例
# 查看当前运行的容器实例
docker ps
输出详情介绍:
-
CONTAINER ID: 容器 ID。
-
IMAGE: 使用的镜像。
-
COMMAND: 启动容器时运行的命令。
-
CREATED: 容器的创建时间。
-
STATUS: 容器状态(状态有7种)。
-
created(已创建)
-
restarting(重启中)
-
running(运行中)
-
removing(迁移中)
-
paused(暂停)
-
exited(停止)
-
dead(死亡)
-
-
PORTS: 容器的端口信息和使用的连接类型(tcp\udp)。
-
NAMES: 自动分配的容器名称。
# -d 让容器实例在后台运行
docker run -d ubuntu:latest /bin/sh -c "while true; do echo hello world; sleep 1; done"
以上命令输出容器唯一的id
docker logs +id 查看指定id的容器内的标准输出
# 查看指定id的容器内的标准输出
docker logs 1a51d2f023c9
关闭后台实例
# 关闭后台实例
docker stop 1a51d2f023c9
2、镜像、容器、仓库
2.0、进程相关命令
# 启动Docker命令
systemctl start docker
# 停止Docker命令
systemctl stop docker
# 重启Docker命令
systemctl restart docker
# 查看启动结果
systemctl status docker
# 设置开机启动Docker
systemctl enable docker
2.1、镜像
当运行容器时,使用的镜像如果在本地中不存在,docker 就会自动从 docker 镜像仓库中下载,默认是从 Docker Hub 公共镜像源下载。
2.1.1、查看镜像列表
# 列出本地主机上的镜像
docker images
# 查看所用镜像的id
docker images -q
各个选项说明:
-
REPOSITORY:表示镜像的仓库源
-
TAG:镜像的标签, 同一仓库源可以有多个 TAG,代表这个仓库源的不同个版本
-
IMAGE ID:镜像ID
-
CREATED:镜像创建时间
-
SIZE:镜像大小
2.1.2、查找镜像
# 搜索镜像
docker search xxx
各标签:
-
NAME: 镜像仓库源的名称
-
DESCRIPTION: 镜像的描述
-
OFFICIAL: 是否 docker 官方发布
-
STARS: 类似 Github 里面的 star,表示点赞、喜欢的意思。
-
AUTOMATED: 自动构建。
2.1.3、拉取镜像
# 拉取搜索出的MySQL
docker pull mysql
2.1.4、删除镜像
# 删除指定本地镜像
# docker rmi 镜像id
docker rmi hello-world
# 删除所有本地镜像
docker rmi `docker images -q`
如果报错了,主要看两点:要么就是container实例存在,要么存在镜像依赖;
2.2、容器
2.2.1、创建容器
# docker run [参数] 镜像名称:Tag <交互方式>
# 创建容器c1并进入
docker run -it --name c1 centos:7 /bin/bash
# 创建容器并后台启动
docker run -id --name c2 centos:7
参数说明:
-
-i
:保持容器运行。通常与-t
同时使用。加入-it
这两个参数后,容器创建后自动进入容器中,退出容器后,容器自动关闭。 -
-t
:为容器重新分配一个伪输入终端,通常与-i
同时使用。 -
-d
:以守护(后台)模式运行容器。创建一个容器在后台运行,需要使用docker exec 进入容器。 退出后,容器不会关闭。 -
-it
:创建容器并进入容器中,退出时自动关闭容器,这种方法创建的容器一般称为交互式容器 -
-id
:创建容器并在后台运行,这种方法创建的容器一般称为守护式容器 -
--name
:为创建的容器命名 -
/bin/bash
:交互方式为shell终端
2.2.2、查看容器
# 查看正在运行的容器
docker ps
# 查看所有容器
docker ps -a
# 查看所有容器id
docker ps -aq
2.2.3、进入容器
# docker exec [参数] 容器名称 /bin/bash
docker exec -it redis /bin/bash
2.2.4、启动容器
# docker start 容器名称
docker start redis
# 重新启动停止的容器
docker restart redis
2.2.5、停止容器
# docker stop 容器名称
docker stop redis
2.2.6、删除容器
需要先停止容器才能删除
# 删除指定的容器
# docker rm 容器名称
docker rm redis
# 删除当前使用的容器
docker rm 'docker ps -aq'
2.2.7、查看容器信息
# docker inspect 容器名称
docker inspect redis
2.2.8、更新容器配置
# docker update [参数] 容器名称
# 例如:设置redis容器随docker一起重启
docker update --restart always redis
2.2.9、退出容器
exit
2.3、仓库
3、在Java中使用Docker
3.1、构建镜像
3.1.1、启用BuildKit
BuildKit 允许您高效地构建 Docker 镜像。Docker Desktop 上的所有用户默认启用 BuildKit。
3.1.2、创建Dockerfile
Dockerfile 是一个文本文档,其中包含组装 Docker 映像的说明。当我们告诉 Docker 通过执行 docker build
命令来构建我们的镜像时,Docker 会读取 Dockerfile 中的指令并执行它们,并作为结果创建一个 Docker 镜像。
创建Dockerfile
在项目的根目录中,创建一个名为Dockerfile
并在文本编辑器中打开此文件的文件。 用于 Dockerfile 的默认文件名是Dockerfile
(不带文件扩展名)。使用默认名称允许运行docker build
命令而无需指定其他命令标志。
添加解析器指令
Dockerfile 的第一行是 # syntax
解析器指令。该指令指示 Docker 构建器在解析 Dockerfile 时使用什么语法,并允许启用 BuildKit 的旧 Docker 版本在开始构建之前升级解析器。
解析器指令必须出现在 Dockerfile 中的任何其他注释、空格或 Dockerfile 指令之前,并且应该是 Dockerfiles 中的第一行。
# syntax=docker/dockerfile:1
建议使用docker/dockerfile:1
,它始终指向版本 1 语法的最新版本。BuildKit 在构建之前会自动检查语法的更新,确保您使用的是最新版本。
设置镜像
需要在 Dockerfile 中添加一行,告诉 Docker 我们想为我们的应用程序使用什么基础镜像。
# syntax=docker/dockerfile:1
FROM openjdk:16-alpine3.13
Docker 镜像可以从其他镜像继承。例如这里我们使用来自 Docker Hub 的官方openjdk镜像和 Java JDK,该镜像已经拥有运行 Java 应用程序所需的所有工具和包。
设置镜像的工作目录
为了在运行其余命令时更容易,让我们设置镜像的工作目录。这指示 Docker 将此路径用作所有后续命令的默认位置。通过这样做,我们不必输入完整的文件路径,而是可以使用基于工作目录的相对路径。
WORKDIR /app
添加依赖项
-
将Maven包装器和pom.xml文件添加到镜像中
COPY .mvn/ .mvn
COPY mvnw pom.xml ./
COPY
命令用于复制文件,第一个参数是原文件地址,第二个参数是要复制到的地址(默认复制到镜像的工作目录/app
中)
-
使用
RUN
命令将依赖添加到镜像中
RUN ./mvnw dependency:go-offline
以上命令和在本地运行 mvnw
或 mvn
的效果类似,将依赖打包到镜像中
添加源代码
COPY src ./src
设置镜像在容器中执行时运行的命令
CMD ["./mvnw", "spring-boot:run"]
一个完整的Dockerfile文件
# syntax=docker/dockerfile:1
FROM openjdk:16-alpine3.13
WORKDIR /app
COPY .mvn/ .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline
COPY src ./src
CMD ["./mvnw", "spring-boot:run"]
3.1.3、创建.dockerignore
建议创建.dockerignore文件
.dockerignore应该创建在和Dockerfile相同的目录下
.dockerignore文件可以只有一行,例如这里设置只忽略targer文件夹
target
3.1.4、构建镜像
使用 docker build
根据dockerfile和上下文(位于指定PATH或URL中的文件)构建镜像
可以添加 --tag
设置镜像的名称和标签 name:tag
,如果不设置tag则默认为latest
docker build --tag java-docker
3.1.5、查看镜像
查看镜像列表有两种方式,一是通过Docker Destop,二是使用CLI的docker images
命令
docker images
3.1.6、标记镜像
一个镜像可以有多个标签(但镜像ID是唯一的),使用 docker tag
设置标签
# 为上面创建的镜像设置一个新标签
docker tag java-docker:latest java-docker:v1.0.0
3.1.7、删除镜像
使用 docker rmi
删除镜像
docker rmi java-docker:v1.0.0
3.2、运行容器
3.2.1、在容器中运行镜像
使用 docker run
在容器中运行镜像,唯一的参数为镜像的名称
docker run java-docker
运行此命令后没有返回命令提示符。这是因为我们的应用程序是一个 REST 服务器,它在循环中运行,等待传入的请求,而不会将控制权返回给操作系统,直到我们使用 ctrl+c
停止容器。
此时如果通过localhost 的 curl向容器服务器发出请求会失败,因为容器是独立运行的,包括网络,我们无法使用localhost连接
3.2.2、设置容器内外端口映射
使用 --publish
+ [host port]:[container port]
设置容器外的端口和容器内的端口的映射关系(--publish
可以简写为-p
)
# 运行容器并设置暴露的端口
docker run --publish 8080:8080 java-docker
此时再使用curl命令请求可以请求成功
curl --request GET \
--url http://localhost:8080/actuator/health \
--header 'content-type: application/json'
3.2.3、以后台模式运行容器
使用 --detach
或 -d
启动容器,启动后会自动返回到终端中,并打印容器的ID
docker run -d -p 8080:8080 java-docker
3.2.4、查看容器
docker ps
可以查看正在运行的容器列表
docker ps
--all
或 -a
查看所有容器(包括停止的)
docker ps -a
3.2.5、停止容器
docker stop
+ 容器ID或名称可以停止容器(docker会随机生成容器名称)
当我们停止一个容器时,它并没有被移除,而是状态变为已停止,容器内部的进程也停止了。
docker stop xxx
停止后的容器可以通过 restart
命令重新启动
docker restart xxx
3.2.6、删除容器
docker rm
+ 容器名称可以删除容器,可以同时移除多个容器,以空格分隔。
移除容器时,容器内的进程将停止,容器的元数据将被移除。
docker rm container1 container2 contain3
3.2.7、命名容器
在启动容器时使用 --name
设置容器的名称
为容器命名可以更容易识别容器中运行的内容以及与之关联的应用程序或服务。
docker run --rm -d -p 8080:8080 --name springboot-server java-docker
--rm
设置容器在退出时自动删除
3.3、开发应用
3.3.1、在容器中运行数据库
创建卷
分别为数据和 MySQL 的配置创建卷,Docker 可以管理这些卷来存储我们的持久数据和配置。
docker volume create mysql_data
docker volume create mysql_config
创建用户自定义的桥接网络
桥接网络提供了一个很好的 DNS 查找服务,我们可以在创建连接字符串时使用它。
docker network create mysqlnet
在容器中运行 MySQL 并附加卷和网络
docker run -it --rm -d -v mysql_data:/var/lib/mysql \
-v mysql_config:/etc/mysql/conf.d \
--network mysqlnet \
--name mysqlserver \
-e MYSQL_USER=petclinic -e MYSQL_PASSWORD=petclinic \
-e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=petclinic \
-p 3306:3306 mysql:8.0.23
更新Dockerfile
更新Dockerfile来启动MySQL Spring配置文件,并切换到MySQL服务器
只需要在 CMD
中添加MySQL配置文件作为参数,(CMD用来设置镜像在容器中执行时运行的命令)
CMD ["./mvnw", "spring-boot:run", "-Dspring-boot.run.profiles=mysql"]
运行镜像和容器
构建镜像
docker build --tag java-docker .
运行容器
注意运行容器时需要设置MYSQL_URL
环境变量,以便应用程序知道使用什么连接字符串来访问数据库。
docker run --rm -d \
--name springboot-server \
--network mysqlnet \
-e MYSQL_URL=jdbc:mysql://mysqlserver/petclinic \
-p 8080:8080 java-docker
测试
curl --request GET \
--url http://localhost:8080/vets \
--header 'content-type: application/json'
3.3.2、使用Compose进行本地开发
我们可以将所有内容整合到一个 Compose 文件中,该文件允许我们使用一个命令设置和运行本地开发环境。
Compose 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。使用前面介绍的Dockerfile我们很容易定义一个单独的应用容器。然而在日常开发工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个 Web 项目,除了 Web 服务容器本身,往往还需要再加上后端的数据库服务容器;再比如在分布式应用一般包含若干个服务,每个服务一般都会部署多个实例。如果每个服务都要手动启停,那么效率之低、维护量之大可想而知。这时候就需要一个工具能够管理一组相关联的的应用容器,这就是Docker Compose。
创建Compose文件
创建一个名为 docker-compose.dev.yml
的文件,并将以下命令复制到文件中
version: '3.8'
services:
petclinic:
build:
context: .
ports:
- 8000:8000
- 8080:8080
environment:
- SERVER_PORT=8080
- MYSQL_URL=jdbc:mysql://mysqlserver/petclinic
volumes:
- ./:/app
command: ./mvnw spring-boot:run -Dspring-boot.run.profiles=mysql -Dspring-boot.run.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000"
mysqlserver:
image: mysql:8.0.23
ports:
- 3306:3306
environment:
- MYSQL_ROOT_PASSWORD=
- MYSQL_ALLOW_EMPTY_PASSWORD=true
- MYSQL_USER=petclinic
- MYSQL_PASSWORD=petclinic
- MYSQL_DATABASE=petclinic
volumes:
- mysql_data:/var/lib/mysql
- mysql_config:/etc/mysql/conf.d
volumes:
mysql_data:
mysql_config:
这里公开了 8000 端口并声明了 JVM 的调试配置,以便后续附加调试器。
并且设置了服务名称,对MySQL服务器进行了命名,此后可以直接在连接字符串中使用 mysqlserver
启动应用
docker-compose
使用Compose
--file
或 -f
指定使用的 Compose 模板文件
up
尝试自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器等
--build
构建镜像并启动容器
docker-compose -f docker-compose.dev.yml up --build
测试
curl --request GET \
--url http://localhost:8080/vets \
--header 'content-type: application/json'
3.3.3、连接调试器
在 IntelliJ IDEA 中打开您的项目,然后转到Run菜单 > Edit Configuration。添加类似以下的新远程 JVM 调试配置
设置一个断点
打开文件src/main/java/org/springframework/samples/petclinic/vet/VetController.java
并在函数showResourcesVetList
中设置一个断点
执行调试
3.4、测试
https://docs.docker.com/language/java/run-tests/
3.5、配置CI/CD
3.6、部署应用
注意:部署项目时如果项目中连接数据库的url使用localhost
可能会报错Connection refused (Connection refused)
,此时改成服务器的ip地址即可
4、数据卷
3.1、数据卷的概念
数据卷是宿主机中的一个目录或文件
当容器目录和数据卷目录绑定后,对方的修改会立即同步
一个数据卷可以被多个容器同时挂载
一个容器也可以被挂载多个数据卷
数据卷,默认会一直存在,即使容器被删除
3.2、数据卷的作用
容器数据持久化
外部机器和容器间接通信
容器之间数据交换
3.3、配置数据卷
创建启动容器时,使用 –v 参数 设置数据卷
docker run ... -v 宿主机目录(文件):容器内目录(文件) ... 镜像名:tag [交互方式]
#挂载单目录
docker run -it --name c1 -v /root/volume:/root/volum_container centos:7
#挂载多目录
docker run -it --name c2 -v /root/data:/root/data -v /root/data1:/root/data1 centos:7
注意:
目录必须是绝对路径
如果目录不存在,会自动创建
可以挂载多个数据卷
3.4、数据卷容器
多容器进行数据交换的两种方式:
-
多个容器挂载同一个数据卷(弊端:如果容器多了,创建容器的时候构建数据卷比较麻烦)
-
使用数据卷容器
数据卷容器实现步骤:
-
创建数据卷容器,使用-v指定数据卷
docker run -id --name 容器名称 -v 宿主机目录路径 镜像名:Tag
-
为容器设置数据卷容器,使用--volumes-from设置数据卷容器
docker run -id --name 容器名称 --volumes-from 数据卷容器名 镜像名:Tag
5、Docker中的网络功能
Docker 允许通过外部访问容器或容器互联的方式来提供网络服务。
5.1、外部访问容器
容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P
或 -p
参数来指定端口映射。
-
当使用
-P
标记时,Docker 会随机映射一个 49000~49900 的端口到内部容器开放的网络端口。 -
使用
docker ps -l
可以看到,各个容器的端口映射。 -
-p
则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort
。
5.1.1、映射所有接口地址
使用 hostPort:containerPort
格式本地的5000端口映射到容器的5000端口
docker run -d -p 5000:5000 --name 容器名 镜像名:Tag
5.1.2、映射到指定地址的特定端口
可以使用 ip:hostPort:containerPort
格式指定映射使用一个特定地址
docker run -d -p 127.0.0.1:5000:5000 --name 容器名 镜像名:Tag
5.1.3、映射到指定地址的任意端口
使用 ip::containerPort
绑定 localhost 的任意端口到容器的 5000 端口,本地主机会自动分配一个端口。
docker run -d -p 127.0.0.1::5000 --name 容器名 镜像名:Tag
注意:
-
容器有自己的内部网络和 ip 地址(使用
docker inspect
可以获取所有的变量,Docker还可以有一个可变的网络配置。) -
-p
标记可以多次使用来绑定多个端口#例如
docker run -d \
-p 5000:5000 \
-p 3000:80 \
training/webapp \
python app.py
5.2、容器互联
5.2.1、新建网络
创建一个新的Docker网络
docker network create -d bridge my-net
-d
参数指定 Docker 网络类型,有 bridge 和 overlay 。其中 overlay 网络类型用于Swarm mode, 资料中可查看到。
使用 docker network ls
可以查看当前创建的网络列表。
5.2.2、连接容器
运行一个容器并连接到新建的 my-net 网络
docker run -it --name busybox1 --network my-net busybox sh
打开新的终端,再运行一个容器并加入到 my-net 网络
docker run -it --name busybox2 --network my-net busybox sh
#下面通过 ping 来证明 busybox1 容器和 busybox2 容器建立了互联关系。
# 在busybox1中执行
ping busybox2
# 在busybox2中执行
ping busybox1
如果有多个容器之间需要互相连接,推荐使用 Docker Compose。
5.2.3、在容器中使用vim
# 首先进入到需要使用vim命令行的容器
# 1.更新系统
apt-get update
# 2.安装vim
apt-get install -y vim
# 3.如果安装失败可以尝试改为国内镜像再重复1 2两步
mv /etc/apt/sources.list /etc/apt/sources.list.bak
echo "deb http://mirrors.163.com/debian/ jessie main non-free contrib" >/etc/apt/sources.list
echo "deb http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list
echo "deb-src http://mirrors.163.com/debian/ jessie main non-free contrib" >>/etc/apt/sources.list
echo "deb-src http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list
6、Dockerfile
6.0、Linux系统组成
-
bootfs:包含bootloader(引导加载程序)和 kernel(内核)
-
rootfs: root文件系统,包含的就是典型 Linux 系统中的/dev,/proc,/bin,/etc等标准目录和文件
不同的linux发行版,bootfs基本一样,而rootfs不同,如ubuntu,centos等
6.1、Docker镜像组成
6.2、Docker镜像制作
7、Docker Compose
8、进阶
9、Docker应用部署
6.0、安装Docker Compose
# 下载docker compose 使用giehub安装较慢推荐使用daocloud安装
curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
# 添加可执行权限
chmod +x /usr/local/bin/docker-compose
# 将文件copy到 /usr/bin/目录下
# ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
# 查看版本
docker-compose --version
6.1、部署mysql
6.1.1、安装mysql
# 搜索镜像
docker search mysql
# 拉取镜像
docker pull mysql
# 创建容器,设置端口映射和目录映射
# 在用户名目录下创建mysql目录用于存储mysql数据信息
mkdir ~/mysql
cd ~/mysql
# 创建docker容器
docker run -id \
-p 3306:3306 \
--name mysql8 \
--restart always \
-v $PWD/conf:/etc/mysql/conf.d \
-v $PWD/logs:/logs \
-v $PWD/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql
如果创建容器时出现错误:
Error response from daemon: driver failed programming external connectivity on endpoint mysql8
错误原因:
当前系统中存在未卸载完成的mysql服务
解决:删除残留的mysql服务(彻底删除后再拉取镜像),结束占用3306端口的进程
# 彻底删除残留的mysql服务
# 查找mysql相关文件
find / -name mysql
# 删除相关文件
rm -r [PATH]
# 查看系统中mysql服务的状态
service mysql status
# 结束占用3306端口的进程
# 查看端口占用情况,state为listen的说明被占用
netstat -nultp
# 如果3306端口被非docker程序占用,则使用kill+PID终止占用程序
kill [PID]docker服务启动时定义的自定义链DOCKER被清除(网上查到的原因)
解决:重启docker
systemctl restart docker
在云服务器中安全组添加3306端口/防火墙开放3306端口
6.1.2、设置容器时间
使用docker安装mysql,时间要比主机晚8个小时,此时需要将本地时间文件复制到MySQL容器中
docker cp /usr/share/zoneinfo/Asia/Shanghai 容器名称:/etc/localtime
6.2、部署Nacos
https://nacos.io/zh-cn/docs/quick-start-docker.html
clone后修改standalone-derby.yaml,
云服务器上用standalone模式部署会卡住,因为核心内存不满足要求?
6.3、部署Nginx
创建容器,设置端口映射、目录映射
docker pull nginx
# 在/root目录下创建nginx目录用于存储nginx数据信息 mkdir ~/nginx
cd ~/nginx
mkdir conf
mkdir logs
cd conf
# 在~/nginx/conf/下创建nginx.conf文件,粘贴下面内容
vim nginx.conf
nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request"'
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
server{
location / {
root html;
index index.php index.html index.htm;
}
}
}
启动容器
docker run -id --name nginx -p 80:80 \
-v /root/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \
-v /root/nginx/logs:/var/log/nginx \
-v /root/nginx/html:/usr/share/nginx/html \
nginx
访问nginx:www.ip:80
6.4、部署tomcat
docker pull tomcat:8.5-jdk8
mkdir ~/tomcat
cd ~/tomcat
docker run -id --name tomcat8 \
-p 8080:8080 \
-v $PWD/webapps:/usr/local/tomcat/webapps \
-v $PWD/logs:/usr/local/tomcat/logs \
tomcat:8.5-jdk8
在docker中安装vim
建议不要替换镜像?
#修改镜像源为国内镜像
mv /etc/apt/sources.list /etc/apt/sources.list.bak
tee /etc/apt/sources.list << EOF
deb http://mirrors.aliyun.com/debian/ stretch main non-free contrib
deb-src http://mirrors.aliyun.com/debian/ stretch main non-free contrib
deb http://mirrors.aliyun.com/debian-security stretch/updates main
deb-src http://mirrors.aliyun.com/debian-security stretch/updates main
deb http://mirrors.aliyun.com/debian/ stretch-updates main non-free contrib
deb-src http://mirrors.aliyun.com/debian/ stretch-updates main non-free contrib
deb http://mirrors.aliyun.com/debian/ stretch-backports main non-free contrib
deb-src http://mirrors.aliyun.com/debian/ stretch-backports main non-free contrib
EOF
#更新系统
apt-get update
#安装vim
apt-get install -y vim
6.5、部署redis
6.3、部署Sentinel
6.3.1、安装Sentinel
# 查找镜像
docker search sentinel
# 拉取镜像
docker pull bladex/sentinel-dashboard
# 启动容器
docker run --name sentinel -d -p 8858:8858 bladex/sentinel-dashboard
在云服务器中安全组添加8858端口
6.7、部署RocketMQ
安装 Name Server
docker run -d \
-p 9876:9876 \
--name mqnamesrv \
--restart always \
-v $PWD/mqnamesrv/logs:/home/rocketmq/logs \
-v $PWD/mqnamesrv/store:/home/rocketmq/store \
apacherocketmq/rocketmq:4.6.0 sh mqnamesrv
安装 Broker Server
docker run -d \
--name mqbroker \
--net=host \
--restart always \
-v $PWD/broker/logs:/home/rocketmq/logs \
-v $PWD/broker/store:/home/rocketmq/store \
-v $PWD/broker/conf:/home/rocketmq/rocketmq-4.6.0/conf \
-e "JAVA_OPT_EXT=-server -Xms128m -Xmx128m" \
apacherocketmq/rocketmq:4.6.0 sh mqbroker -c /home/rocketmq/rocketmq-4.6.0/conf/broker.conf
安装控制台
docker run -d \
--name rocketmq-console-ng \
-p 8280:8080 \
--restart always \
-e "JAVA_OPTS=-Drocketmq.namesrv.addr=192.168.28.133:9876 -Dlogging.level.root=info -Dcom.rocketmq.sendMessageWithVIPChannel=false" \
styletang/rocketmq-console-ng:1.0.0
开放端口
firewall-cmd --add-port 9876/tcp --add-port 10911/tcp --add-port 8280/tcp --permanent
firewall-cmd --reload
6.8、部署Seata
sh nacos-config.sh -h 192.168.1.6 -p 8848 -g SEATA_GROUP -t [命名空间id] -u nacos -w nacos
sh nacos-config.sh -h 192.168.1.6 -p 8848 -g SEATA_GROUP -t 8ea2414c-9dcd-455d-8acd-eb9deff1957b -u nacos -w nacos
-
-h -p 指定nacos的端口地址;
-
-g 指定配置的分组,注意,是配置的分组;
-
-t 指定命名空间id;
-
-u -w指定nacos的用户名和密码
docker run \
-d -p 8091:8091 \
--name seata-server \
-e SEATA_IP=192.168.1.6 \
-e SEATA_PORT=8091 \
-v /home/nacos/seata/seata-config/file.conf:/seata-server/resources/file.conf \
-v /home/nacos/seata/seata-config/registry.conf:/seata-server/resources/registry.conf \
-v /home/nacos/seata/logs:/root/logs \
seataio/seata-server:1.3.0
curl -X DELETE 'http://192.168.1.6:8848/nacos/v1/ns/instance?serviceName=seata-server&groupName=SEATA_GROUP&namespaceId=public&ip=192.168.1.6&clusterName=DEFAULT&port=8091&ephemeral=true'
curl -X DELETE 'http://192.168.1.6:8848/nacos/v1/ns/instance?serviceName=seata-server&groupName=SEATA_GROUP&namespaceId=public&ip=192.168.1.6&port=8091'
6.9、部署FastDFS
cd /home/dfs/docker/dockerfile_network/
docker build -f ./Dockerfile -t awei/dfs .
docker run -d \
-e FASTDFS_IPADDR=192.168.1.6 \
--net=host \
--name fast-dfs \
-v /home/dfs/:/home/dfs/ \
awei/dfs
firewall-cmd --add-port 8888/tcp --add-port 22122/tcp --add-port 23000/tcp --permanent
firewall-cmd --reload