Docker简介
markdown
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后
发布到任何流行的 Linux或Windows操作系统的机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互
之间不会有任何接口。
随着微服务架构的兴起,一个项目会被拆分成很多的微服务(大项目可能会有几百个微服务),部署时变得
越来越复杂,运维工程师也需要有更好用,简单的工具帮助他们快速部署微服务架构的项目。
请求路由负载均衡 拉取或注册服务信息 拉取或注册服务信息 客户端 服务网关 服务集群 注册中心 配置中心 消息队列 分布式缓存 分布式搜索 数据库
一、初始Docker
markdown
微服务架构虽然具备各种各样的优势,但服务的拆分通常给部署带来了很大的麻烦。
- 分布式系统中,依赖的组件(mysql,redis,es,...)非常多,不同组件之间部署时往往会产生一些冲突。
- 在数百上千台服务器中重复部署,环境不一定一致,会遇到各种问题
1、什么是Docker
markdown
1.应用部署的环境问题:
大型项目组件(node.js、Redis、RabbitMQ、MySQL等)较多,运行环境也较为复杂,部署时会碰到一些问题:
- 依赖关系复杂,容易出现兼容性问题
- 开发、测试、生产环境有差异
node.js Redis RabbitMQ MySQL Libraries Dependencies Linux操作系统 计算机硬件
markdown
看上图一个项目中,部署时需要依赖于node.js、Redis、RabbitMQ、MySQL等,这些服务部署时所需要的函数库、
依赖项各不相同,甚至会有冲突。给部署带来了极大的困难。
markdown
2.解决依赖兼容问题
Docker为采用了两个手段:
- 将应用的Libs(函数库)、Deps(依赖)、配置与应用一起打包
- 将每个应用放到一个隔离容器去运行,避免互相干扰
node.js Redis RabbitMQ MySQL Libraries Dependencies Libraries Dependencies Libraries Dependencies Libraries Dependencies 操作系统 计算机硬件
markdown
虽然解决了不同应用的兼容问题,但是开发、测试等环境会存在差异,操作系统版本也会有差异,
怎么解决这些问题呢?
markdown
3.解决操作系统环境差异
- Linux发行版:RedHat(收费)、CentOS(RedHat社区版)、Ubuntu(可视化环境)
markdown
结构包括:
- 最底层:计算机硬件:例如CPU、内存、磁盘等
- 系统内核:所有Linux发行版的内核都是Linux,例如CentOS、Ubuntu、Fedora等。内核可以与计算机硬件交互,
对外提供内核指令,用于操作计算机硬件。
- 系统应用:操作系统本身提供的应用、函数库。这些函数库是对内核指令的封装,使用更加方便。
`如果将一个Ubuntu版本的MySQL应用安装到CentOS系统,MySQL在调用Ubuntu函数库时,会发现找不到或者
不匹配,就会报错了:
markdown
Docker如何解决不同系统环境的问题?
- Docker将用户程序与所需要调用的系统(比如Ubuntu)函数库一起打包
- Docker运行到不同操作系统时,直接基于打包的函数库,借助于操作系统的Linux内核来运行
markdown
4.小结
Docker解决大型项目复杂依赖关系,不同组件依赖的兼容性问题的方式
- Docker允许开发中将应用、依赖、函数库、配置一起打包,形成可移植镜像
- Docker应用运行在容器中,使用沙箱机制,相互隔离
Docker解决开发、测试、生产环境有差异的方式
- Docker镜像中包含完整运行环境,包括系统函数库,仅依赖系统的Linux内核,因此可以在任意Linux操作系统上
运行
Docker的优势
- 可以将程序及其依赖、运行环境一起打包为一个镜像,可以迁移到任意Linux操作系统
- 运行时利用沙箱机制形成隔离容器,各个应用互不干扰
- 启动、移除都可以通过一行命令完成,方便快捷
2、Docker和虚拟机的区别
markdown
Docker可以让一个应用在任何操作系统中非常方便的运行。而以前我们接触的虚拟机,也能在一个操作系统中,
运行另外一个操作系统,保护系统中的任何应用。
虚拟机(virtual machine)是在操作系统中模拟硬件设备,然后运行另一个操作系统,比如在 Windows 系统里面
运行 Ubuntu 系统,这样就可以运行任意的Ubuntu应用了。
Docker仅仅是封装函数库,并没有模拟完整的操作系统
node.js MySQL Libraries Dependencies Libraries Dependencies 操作系统 计算机硬件 Docker node.js MySQL Libraries Dependencies Libraries Dependencies 操作系统 计算机硬件 操作系统 操作系统 Hypervisor 虚拟机
markdown
对比来看:
特性 | Docker | 虚拟机 |
---|---|---|
性能 | 接近原生 | 性能较差 |
硬盘占用 | 一般为MB | 一般为GB |
启动 | 秒级 | 分钟级 |
markdown
Docker和虚拟机的差异:
- docker是一个系统进程;虚拟机是在操作系统中的操作系统
- docker体积小、启动速度快、性能好;虚拟机体积大、启动速度慢、性能一般
3、Docker架构
markdown
1.Docker中有几个重要的概念:
镜像(Image):Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起,称为镜像。
容器(Container):镜像中的应用程序运行后形成的进程就是容器,只是Docker会给容器进程做隔离,对外不可见。
一切应用最终都是代码组成,都是硬盘中的一个个的字节形成的文件。只有运行时,才会加载到内存,形成进程。
而镜像,就是把一个应用在硬盘上的文件、及其运行环境、部分系统函数库文件一起打包形成的文件包。
这个文件包是只读的。
容器呢,就是将这些文件中编写的程序、函数加载到内存中允许,形成进程,只不过要隔离起来。因此一个镜像
可以启动多次,形成多个容器进程。
markdown
例如你下载了一个QQ,如果我们将QQ在磁盘上的运行文件及其运行的操作系统依赖打包,形成QQ镜像。
然后你可以启动多次,双开、甚至三开QQ,跟多个妹子聊天。
镜像: 操作系统安装包
容器: 基于 "操作系统安装包" 启动的一个 "操作系统"
远程仓库: 是一个网站,上面有各种镜像,可以下载
markdown
2.DockerHub
开源应用程序非常多,打包这些应用往往是重复的劳动。为了避免这些重复劳动,人们就会将自己打包的
应用镜像,例如Redis、MySQL镜像放到网络上,共享使用,就像GitHub的代码共享一样。
- DockerHub:DockerHub是一个官方的Docker镜像的托管平台。这样的平台称为Docker Registry。
https://hub.docker.com/
- 国内也有类似于DockerHub 的公开服务,比如 网易云镜像服务 https://c.163yun.com/hub、
阿里云镜像库 https://cr.console.aliyun.com/等。
我们一方面可以将自己的镜像共享到DockerHub,另一方面也可以从DockerHub拉取镜像:
markdown
3.Docker架构
Docker是一个CS架构的程序,由两部分组成:
- 服务端(server):Docker守护进程,负责处理Docker指令,管理镜像、容器等
- 客户端(client):通过命令或RestAPI向Docker服务端发送指令。可以在本地或远程向服务端发送指令。
markdown
4、小结
镜像:image
- 将应用程序及其依赖、环境、配置打包在一起
容器:container
- 镜像运行起来就是容器,一个镜像可以运行多个容器
DockerHub:
- 一个镜像托管的服务器,类似的还有阿里云镜像服务,统称为DockerRegistry
Docker结构:
- 服务端:接收命令或远程请求,操作镜像或容器
- 客户端:发送命令或者请求到Docker服务端
4.安装Docker
markdown
企业部署一般都是采用Linux操作系统,而其中又数CentOS发行版占比最多,因此在CentOS下安装Docker。
二、Docker的基本操作
1、镜像操作
markdown
1.镜像名称
- 镜名称一般分两部分组成:[repository]:[tag]。
wechat:8.0.35
wechat:7.0.10
- 在没有指定tag时,默认是latest,代表最新版本的镜像【不建议用】
wechat = wechat:latest
wechat就是repository,8.0.35就是tag,合一起就是镜像名称,代表8.0.35版本的wechat镜像。
markdown
2.镜像命令
常见的镜像操作命令如图:
markdown
3.拉取、查看镜像
从DockerHub中拉取一个nginx镜像并查看
1)首先去镜像仓库搜索nginx镜像,比如DockerHub: https://hub.docker.com/
2)根据查看到的镜像名称,拉取自己需要的镜像,通过命令:
- docker pull nginx:1.18.0
3)通过命令: 查看拉取到的镜像
- docker images
4.保存、导入镜像
利用docker save将nginx镜像导出磁盘,然后再通过load加载回来
1)利用docker xx --help命令查看docker save和docker load的语法
例如,查看save命令用法,可以输入命令:
docker save --help
markdown
命令格式:
docker save -o [保存的目标文件名称] [镜像名称:tag]
#output
2)使用docker save导出镜像到磁盘
运行命令:
- docker save -o nginx.tar nginx:latest
结果如图:
markdown
3)使用docker load加载镜像
先删除本地的nginx镜像:
- #rmi: remove images
- docker rmi -f nginx:latest
- #也可以使用镜像ID删除
- docker rmi -f 234234
#检查下否删除
- docker images
然后运行命令,加载本地文件:
- docker load -i nginx.tar
#input
properties
#小结
# 1_镜像操作
#前提:先启动docker服务
systemctl start docker
# 帮助
docker --help
# 查看镜像
docker images
# 删除nginx镜像
docker rmi nginx:version
# 拉取nginx 指定版本
docker pull nginx:1.21
#其他同学无法下载这一个镜像
# 将镜像打包成tar 文件: o -> output
docker save -o nginx.tar nginx:latest
#自动生成一个压缩包:nginx.tar
#删除镜像
docker rmi nginx
# 加载镜像: i->input
docker load -i nginx.tar
2、容器操作【掌握】
markdown
1.容器相关命令
容器操作的命令如图:
markdown
容器保护三个状态:
- 运行:进程正常运行
- 暂停:进程暂停,CPU不再运行,并不释放内存
- 停止:进程终止,回收进程占用的内存、CPU等资源
其中:
- docker run:创建并运行一个容器,处于运行状态
- docker pause:让一个运行的容器暂停
- docker unpause:让一个容器从暂停状态恢复运行
- docker stop:停止一个运行的容器
- docker start:让一个停止的容器再次运行
- docker rm:删除一个容器
- docker ps : 查看正在运行的容器
properties
#2.创建并运行容器
# 创建并运行nginx容器的命令:
docker run --name mynginx -p 80:80 -d nginx
#上边命令再次执行会报错,因为每次run都会创建一个新容器mynginx,重名了因此会报错
命令解读:
- docker run :创建并运行一个容器
- --name : 给容器起一个名字,比如叫做mynginx
- -p :将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口
- -d:后台运行容器:daemon
- nginx:镜像名称,例如nginx
# 这里的-p参数,是将容器端口映射到宿主机端口。
# 默认情况下,容器是隔离环境,我们直接访问宿主机的80端口,肯定访问不到容器中的nginx。
# 现在,将容器的80与宿主机的80关联起来,当我们访问宿主机的80端口时,就会被映射到容器的
#80,这样就能访问到nginx了:http://192.168.200.130
properties
# 关闭防火墙
systemctl stop firewalld
# 禁止开机启动防火墙
systemctl disable firewalld
#查看日志
docker logs --tail 5 -f 容器名称
#如何删除容器,-f强制删除正在运行的容器
docker rm -f mynginx
docker rm -f 容器id
#查询正在运行的容器
docker ps
#关闭容器
docker stop mynginx
#启动容器
docker start mynginx
#显示所有的容器
docker ps -a
markdown
3.修改容器内文件
1)进入容器。进入我们刚刚创建的nginx容器的命令为:
- docker exec -it mynginx bash
#execute: 执行
命令解读:
- docker exec :进入容器内部,执行一个命令
- -it : 给当前进入的容器创建一个标准输入、输出终端,允许我们与容器交互
- mynginx :要进入的容器的名称
- bash:进入容器后执行的命令,bash是一个linux终端交互命令
2)进入nginx的HTML所在目录 /usr/share/nginx/html
容器内部会模拟一个独立的Linux文件系统,看起来如同一个linux服务器一样:nginx的环境、配置、
运行文件全部都在这个文件系统中,包括我们要修改的html文件。
markdown
查看DockerHub网站中的nginx页面,可以知道nginx的html目录位置在`/usr/share/nginx/html`
properties
#确保已经进入到容器
#docker exec -it mynginx bash
cd /usr/share/nginx/html
markdown
查看目录下文件:
markdown
3)修改index.html的内容
容器内没有安装vi,无法直接修改,我们用下面的命令来修改:
#覆盖原来的内容: >
echo "<h1>Hello, Java</h1>" > index.html
#追加内容: >>
echo "<h1>Hello, test</h1>" >> index.html
#sed -i -e 's#Welcome to nginx#WenJd真酷#g' -e 's#<head>#<head><meta charset="utf-8">#g' index.html
#退出容器,回到虚拟机终端
exit
在浏览器访问自己的虚拟机地址:http://192.168.200.130
markdown
4.小结
docker run命令的常见参数:
#run命令会做两件事:
#1.根据镜像创建容器
#2.运行容器
docker run --name mynginx -p 80:80 -d nginx
- --name:指定容器名称
- -p:指定端口映射
- -d:让容器后台运行
查看容器日志的命令:
- docker logs 容器名称
- 添加 --tail -f 参数可以持续查看日志
进入容器:
- docker exec -it 容器名称 bash
- exit: 退出容器
查看容器状态:
- docker ps:只显示正在运行的容器
- docker ps -a 查看所有容器,包括已经停止的
3、数据卷
markdown
1、容器存在的问题
- 在上个步骤中,修改html页面时,需要进入nginx内部;并且因为没有编辑器vi,修改文件也很麻烦。
这就是因为容器与数据(容器内文件)耦合带来的后果。
缺点 { 不变修改 : { 当我们要修改 N g i n x 的 h t m l 内容时,需要 进入容器内部进行修改,很不方便。 数据不可复用 : { 在容器内的修改对外是不可见的,所有的修改 对创建的容器是不可复用的。 升级维护困难 : { 数据在容器内,如果要升级容器必然删除旧 容器,所有的数据都跟着删除了。 缺点\begin{cases}不变修改:\begin{cases} &当我们要修改Nginx的html内容时,需要\\&进入容器内部进行修改,很不方便。\end{cases}\\\\ \\数据不可复用:\begin{cases}&在容器内的修改对外是不可见的,所有的修改\\&对创建的容器是不可复用的。\end{cases}\\\\ \\升级维护困难:\begin{cases}& 数据在容器内,如果要升级容器必然删除旧\\&容器,所有的数据都跟着删除了。 \end{cases} \end{cases} 缺点⎩ ⎨ ⎧不变修改:{当我们要修改Nginx的html内容时,需要进入容器内部进行修改,很不方便。数据不可复用:{在容器内的修改对外是不可见的,所有的修改对创建的容器是不可复用的。升级维护困难:{数据在容器内,如果要升级容器必然删除旧容器,所有的数据都跟着删除了。
markdown
要解决这个问题,必须将数据与容器解耦,这就要用到数据卷了。
markdown
2、什么是数据卷
- 数据卷(volume)是一个虚拟目录,指向宿主机文件系统中的某个目录(真实存在的物理目录)。
markdown
一旦完成数据卷挂载,对容器的一切操作都会作用在数据卷对应的宿主机目录了。我们操作宿主机的
/var/lib/docker/volumes/html目录,就等于操作容器内的/usr/share/nginx/html目录了
markdown
2.数据卷操作命令
数据卷操作的基本语法:docker volume [COMMAND]
docker volume命令是数据卷操作,根据命令后跟随的command来确定下一步的操作:
- create 创建一个volume
- inspect 显示一个或多个volume的详情
- ls 列出所有的volume
- rm 删除一个或多个指定的volume
- prune 删除未使用的volume
markdown
3.创建和查看数据卷
需求:创建一个数据卷,并查看数据卷在宿主机的目录位置
- 创建数据卷
docker volume create html
- 查看所有数据
docker volume ls
结果:
markdown
- 查看数据卷详细信息卷
docker volume inspect html
#docker inspect html
结果:
markdown
可以看到,我们创建的html这个数据卷关联的宿主机目录为`/var/lib/docker/volumes/html/_data`目录。
properties
# 小结:
数据卷的作用:
- 将容器与数据分离,解耦合,方便操作容器内数据,保证数据安全
数据卷操作:
#创建数据卷
docker volume create html
#查看所有数据卷
docker volume ls
#查看数据卷详细信息,包括关联的宿主机目录位置
docker volume inspect
#删除指定数据卷
docker volume rm xxx
#删除所有未使用的数据卷(如果使用数据卷的容器关闭)
docker volume prune
markdown
4.接下来可以做一个小案例来进行挂载数据卷:
- 创建一个nginx容器,修改容器内的html目录内的index.html内容
首先找到nginx的html目录所在位置/usr/share/nginx/html
运行容器的时候 使用 -v 参数挂载数据卷
注意事项:
1) 只能在创建容器时挂载, 如果创建时忘了, 只能删除后 再创建容器
2) 一个容器可以挂载多个文件夹
3) 一个文件夹,可以被多个容器共享挂载
sh
①.创建容器并挂载数据卷到容器内的HTML目录
#如果先前创建过nginx容器,先删除之前的。
docker rm -f mynginx
docker run --name mynginx -v html:/usr/share/nginx/html -p 80:80 -d nginx
如果挂载数据卷的时候,数据卷html不存在,会自动创建
sh
② 进入html数据卷所在位置,并修改HTML内容
# 查看html数据卷的位置
docker inspect html
# 进入该目录
cd /var/lib/docker/volumes/html/_data
# 修改文件
vi index.html
http://192.168.200.130
(根据自己的端口进行访问)
markdown
5.挂载本地目录【常用】
容器不仅可以挂载数据卷,也可以直接挂载到宿主机目录上。
- 带数据卷模式:宿主机目录 --> 数据卷 ---> 容器内目录
- 直接挂载模式:宿主机目录 ---> 容器内目录
markdown
语法:
目录挂载与数据卷挂载的语法是类似的:
- -v [宿主机目录]:[容器内目录]
- -v [宿主机文件]:[容器内文件]
shell
现需求:
创建并运行一个MySQL容器,将宿主机目录直接挂载到容器。
1) 拉取镜像:
#镜像比较大,自己拉比较耗时间可以先在官网下载好直接上传
#docker pull mysql:5.7.25
#上传mysql.tar
#docker load -i mysql.tar
2)创建目录/tmp/mysql/data:数据库文件目录
#-p:parent,如果父目录不存在,会自动创建
mkdir -p /tmp/mysql/data
3)创建目录/tmp/mysql/conf,将hmy.cnf文件上传到/tmp/mysql/conf
mkdir -p /tmp/mysql/conf
cd /tmp/mysql/conf
#上传hmy.cnf
sh
#以下是hmy.cnf的内容创建一个txt文件将下面内容复制上改一下名称即可。
[mysqld]
skip-name-resolve
character_set_server=utf8
datadir=/var/lib/mysql
server-id=1000
properties
#4)去DockerHub查阅资料,创建并运行MySQL容器,要求:
#① 挂载/tmp/mysql/data到mysql容器内数据存储目录
#② 挂载/tmp/mysql/conf/hmy.cnf到mysql容器的配置文件
#③ 设置MySQL密码
#记得关闭之前安装的mysql:3306
#systemctl stop mysqld
#创建mysql容器, -e: environment(环境变量)
docker run \
--name mysql \
-e MYSQL_ROOT_PASSWORD=root \
-p 3306:3306 \
-v /tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf \
-v /tmp/mysql/data:/var/lib/mysql \
-d mysql:5.7.25
markdown
使用MySQL客户端连接数据库进行测试:
192.168.200.130
3306
root/root
# 小结
docker run的命令中通过 -v 参数挂载文件或目录到容器中:
- -v volume名称:容器内目录
- -v /宿主机文件:容器内文件
- -v /宿主机目录:容器内目录
数据卷挂载与目录直接挂载的区别
- 数据卷挂载耦合度低,由docker来管理目录,但是目录较深(/var/lib/docker/volumes),不好找
- 目录挂载耦合度高,需要我们自己管理目录,不过目录容易寻找查看
三、自定义镜像
markdown
常见的镜像在DockerHub就能找到,但是我们自己写的项目就必须自己构建镜像了。
而要自定义镜像,就必须先了解镜像的结构才行。
1、镜像结构
markdown
镜像是将应用程序MySQL及其需要的系统函数库、环境、配置、依赖打包而成。
其中组成结构为:
1. 入口(Entrypoint):镜像运行入口,一般是程序启动的脚本和参数。
2. 层(Layer):在BaseImage基础上添加安装包、依赖、配置等,每次操作都形成新的一层。
3. 基础镜像(BaseImage):应用依赖的系统函数库、环境、配置、文件等。
我们以MySQL为例,来看看镜像的组成结构:
markdown
简单来说,镜像就是在系统函数库、运行环境基础上,添加应用程序文件、配置文件、依赖文件等组合,然后
编写好启动脚本打包在一起形成的文件。
我们要构建镜像,其实就是实现上述打包的过程。
2、Dockerfile语法
markdown
构建自定义的镜像时,并不需要一个个文件去拷贝,打包。
我们只需要告诉Docker,我们的镜像的组成,需要哪些BaseImage、需要拷贝什么文件、需要安装什么依赖、启动
脚本是什么,将来Docker会帮助我们构建镜像。
而描述上述信息的文件就是Dockerfile文件。
Dockerfile就是一个文本文件,其中包含一个个的指令(Instruction),用指令来说明要执行什么操作来构建镜像。
每一个指令都会形成一层Layer。
指令 | 说明 | 示例 |
---|---|---|
FROM |
指定基础镜像 | FROM centos:6 |
ENV |
设置环境变量,可在后面指令使用 | ENV key value |
COPY |
拷贝本地文件到镜像的指定目录 | COPY ./mysql-5.7.rpm/tmp |
RUN |
执行Linux 的shell 命令,一般是安装过程的命令 |
RUN yum install gcc |
EXPOSE |
指定容器运行时监听的端口,是给镜像使用者看的 | EXPOST 8080 |
ENTRYPOINT |
镜像中应用的启动命令,容器运行时调用 | ENTRYPOINT java -jar xx.jar |
markdown
更新详细语法说明,请参考官网文档: https://docs.docker.com/engine/reference/builder
3、构建Java项目
markdown
需求:基于Ubuntu镜像构建一个新镜像,运行一个java项目
前提:我将下面要用到的文件放到了百度网盘,自行来取
- 链接:https://pan.baidu.com/s/1CwEoYItQLdwY8Y5_cF84AA
- 提取码:34cc
- 步骤1:新建一个空文件夹docker-demo
markdown
- 步骤2:拷贝资料中的docker-demo.jar文件到docker-demo这个目录
markdown
- 步骤3:拷贝资料中的jdk8.tar.gz文件到docker-demo这个目录
markdown
- 步骤4:拷贝资料提供的Dockerfile到docker-demo这个目录
dockerfile
其中的内容如下:
# 指定基础镜像(多镜像可以共享)
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录
ENV JAVA_DIR=/usr/local
# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar
# 安装JDK
RUN cd $JAVA_DIR \
&& tar -xf ./jdk8.tar.gz \
&& mv ./jdk1.8.0_144 ./java8
# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin
# 暴露端口
EXPOSE 8090
# 入口,java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar
markdown
- 步骤5:进入docker-demo
将准备好的docker-demo上传到虚拟机任意目录,然后进入docker-demo目录下
- 步骤6:开始制作镜像,运行命令:
shell
#构建镜像,最后的.代表是Dockerfile所在的目录
docker build -t javaweb:1.0 .
#查看镜像,确认下
docker images
markdown
- 步骤7:启动容器
properties
#创建,并启动容器
docker run --name myjavaweb -p 8090:8090 -d javaweb:1.0
# 最后访问 http://192.168.200.130:8090/hello/count,其中的ip改成你的虚拟机ip
4、基于java8构建Java项目
markdown
虽然我们可以基于Ubuntu基础镜像,添加任意自己需要的安装包,构建镜像,但是却比较麻烦。所以大多数情况
下,我们都可以在一些安装了部分软件的基础镜像上做改造。
例如,构建java项目的镜像,可以在已经准备了JDK的基础镜像基础上构建。
需求:基于java:8-alpine镜像,将一个Java项目构建为镜像
实现思路如下:
- ① 新建一个空的目录,然后在目录中新建一个文件,命名为Dockerfile
- ② 拷贝课前资料提供的docker-demo.jar到这个目录中
- ③ 编写Dockerfile文件:vi Dockerfile
dockerfile
#基础镜像包含:alpine(最小的Linux发行版)和JDK1.8
FROM java:8-alpine
#拷贝自己的Java项目到镜像
COPY ./docker-demo.jar /tmp/app.jar
#指定开放端口号
EXPOSE 8090
#入口
ENTRYPOINT java -jar /tmp/app.jar
markdown
- ④ 使用docker build命令构建镜像
properties
docker build -t javaweb:2.0 .
#最后的".":Dockerfile文件 所在的目录
markdown
- ⑤ 使用docker run创建容器并运行
properties
#必须先关闭之前启动的容器(占用了tomcat的8090端口)
docker stop myjavaweb
#创建,并启动容器
docker run --name myjavaweb2 -p 8090:8090 -d javaweb:2.0
# 测试:http://192.168.200.130:8090/hello/count
markdown
# 小结
1. Dockerfile的本质是一个文件,通过指令描述镜像的构建过程
2. Dockerfile的第一行必须是FROM,从一个基础镜像来构建
3. 基础镜像可以是基本操作系统,如Ubuntu。也可以是其他人制作好的镜像,例如:java:8-alpine
四、Docker-Compose
markdown
Docker Compose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器!
作用:能够批量操作镜像和容器,提高部署效率
1、初识DockerCompose
markdown
Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。格式如下:
docker-compose.yml
yaml
version: "3.8"
services: #每一个容器都是一个服务,多个容器
mysql: #容器名称
image: mysql:5.7.25 #根据镜像创建容器
environment:
MYSQL_ROOT_PASSWORD: root
volumes: #数据卷
- "/tmp/mysql/data:/var/lib/mysql"
- "/tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf"
web:
#image: javaweb:1.01
build: . #根据Dockerfile构建镜像,然后在根据镜像创建运行容器
ports: #端口映射: -p 8090:8090
- "8090:8090"
markdown
上面的Compose文件就描述一个项目,其中包含两个容器:
- mysql:一个基于`mysql:5.7.25`镜像构建的容器,并且挂载了两个目录
- web:一个基于`docker build`临时构建的镜像容器,映射端口时8090
DockerCompose的详细语法参考官网:https://docs.docker.com/compose/compose-file
其实DockerCompose文件可以看做是将多个docker run命令写到一个文件,只是语法稍有差异。
2、部署微服务集群
shell
首先安装DockerCompose
docker -compose -v
//查看DockerCompose的版本号
安装教程:Linux(Centos7)中安装Docker和DockerCompose
markdown
现将cloud-demo微服务集群利用DockerCompose部署
1、订单服务:order-service
2、用户服务:user-service
3、网关服务:gateway
依赖软件:MySQL,注册中心Nacos
自行取出cloud-demo
链接:https://pan.baidu.com/s/10wffziwxzwjiBgSio2zsUw
提取码:npfh
markdown
实现思路:
- ① 查看课前资料提供的cloud-demo文件夹,里面已经编写好了docker-compose文件
- ② 修改自己的cloud-demo项目,将数据库、nacos地址都命名为docker-compose中的服务名
- ③ 使用maven打包工具,将项目中的每个微服务都打包为app.jar
- ④ 将打包好的app.jar拷贝到cloud-demo中的每一个对应的子目录中~~
> 资料中已经将所有的jar包都打包好了直接使用即可:cloud-demo.tar.gz
> MySQL密码设置的是123
- ⑤ 将cloud-demo上传至虚拟机,利用 docker-compose up -d 来部署
markdown
# 1.准备compose文件
查看课前资料提供的cloud-demo文件夹,里面已经编写好了docker-compose文件,而且每个微服务都准备了一个独
立的目录:
yaml
内容如下:
version: "3.2"
services: #容器列表
nacos:
image: nacos/nacos-server #没指定版本,就用latest
environment:
MODE: standalone #单机版注册中心
ports: #端口映射
- "8848:8848"
mysql:
image: mysql:5.7.25
environment:
MYSQL_ROOT_PASSWORD: 123
volumes: #指定数据卷:目录文件共享
- "$PWD/mysql/init:/docker-entrypoint-initdb.d/"
- "$PWD/mysql/conf:/etc/mysql/conf.d/"
ports:
- "3306:3306"
userservice:
build: ./user-service #通过Dockerfile制作镜像
orderservice:
build: ./order-service
gateway:
build: ./gateway
ports:
- "10010:10010"
markdown
可以看到,其中包含5个service服务:
- `nacos`:作为注册中心和配置中心
- `image: nacos/nacos-server`: 基于nacos/nacos-server镜像构建
- `environment`:环境变量
- `MODE: standalone`:单点模式启动
- `ports`:端口映射,这里暴露了8848端口
- `mysql`:数据库
- `image: mysql:5.7.25`:镜像版本是mysql:5.7.25
- `environment`:环境变量
- `MYSQL_ROOT_PASSWORD: 123`:设置数据库root账户的密码为123
- `volumes`:数据卷挂载,这里挂载了mysql的data、conf目录,其中有我提前准备好的数据
- `userservice`、`orderservice`、`gateway`:都是基于Dockerfile临时构建的
查看mysql目录,可以看到其中已经准备好了cloud_order、cloud_user表:
markdown
查看微服务目录,可以看到都包含Dockerfile文件:
dockerfile
内容如下:
FROM java:8-alpine
COPY ./app.jar /tmp/app.jar
ENTRYPOINT java -jar /tmp/app.jar
markdown
# 2.修改微服务配置
因为微服务将来要部署为docker容器,而容器之间互联不是通过IP地址,而是通过容器名。这里我们将order-
service、user-service、gateway服务的mysql、nacos地址都修改为基于容器名的访问。
如下所示:
yaml
spring:
datasource:
url: jdbc:mysql://mysql:3306/cloud_order?useSSL=false
username: root
password: 1234
driver-class-name: com.mysql.jdbc.Driver
application:
name: orderservice
cloud:
nacos:
server-addr: nacos:8848 # nacos服务地址
markdown
# 3.打包
接下来需要将我们的每个微服务都打包。因为之前查看到Dockerfile中的jar包名称都是app.jar,因此我们的每个微服
务都需要用这个名称。
可以通过修改pom.xml中的打包名称来实现,每个微服务都需要修改:
xml
<build>
<!-- 服务打包的最终名称 -->
<finalName>app</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
markdown
# 4.拷贝jar包
编译打包好的app.jar文件,需要放到Dockerfile的同级目录中。注意:每个微服务的app.jar放到与服务名称对应的目
录,别搞错了。
user-service:
markdown
order-service:
markdown
gateway:
markdown
# 5.部署
最后,我们需要将文件整个cloud-demo文件夹上传到虚拟机中,理由DockerCompose部署。
上传到任意目录:
shell
#1.从资料中上传cloud-demo.tar.gz
#2.解压
- tar zxvf cloud-demo.tar.gz
进入cloud-demo目录,然后运行下面的命令:
cd cloud-demo
#1、停掉mysqld服务,防止3306端口冲突
#systemctl stop mysqld
#2、注意:关掉或者删除mysql的容器,占用后续要使用的3306端口
#docker rm -f mysql 或者停止 docker stop mysql
#执行docker-compose.yml文件,在前台运行5个容器
docker-compose up
#docker-compose up -d #-d:在后台运行
markdown
验证Nacos是否启动成功:http://192.168.200.130:8848/nacos
确认成功后,重启如下几个微服务:
shell
#nacos启动比较慢,因此需要重启下微服务,保证能注册到naocs
docker-compose restart userservice orderservice gateway
#查看日志的,也可以通过nacos管理端,看看服务有没有注册成功
docker-compose logs -f --tail 10 orderservice
#ctrl + c 关闭所有的容器
#可以手动启动
docker-compose start 容器名称
#删除当前创建的容器
docker-compose down
markdown
# 6.测试
- 通过网关,访问userservice:http://192.168.200.130:10010/user/1?authorization=admin
- 通过网关,访问orderservice:http://192.168.200.130:10010/order/101?authorization=admin
五、Docker镜像仓库
1、搭建私有镜像仓库
参考Linux(Centos7)中安装Docker和DockerCompose
2、推送&拉取镜像
shell
验证私有镜像仓库是否搭建成功:http://192.168.200.130:8080/
#因为重启了docker服务,所以容器都被关掉,重启一下私有镜像仓库的容器
docker -compose up
shell
推送镜像到私有镜像服务必须先tag,步骤如下:
① 重新tag本地镜像,名称前缀为私有仓库的地址:192.168.200.200:8080/
docker tag nginx:latest 192.168.200.130:8080/nginx:1.0
② 推送镜像
docker push 192.168.200.130:8080/nginx:1.0
③ 拉取镜像
#删除之前已经有的镜像
docker rmi 192.168.200.130:8080/nginx:1.0
#模拟从私有仓库拉取镜像
docker pull 192.168.200.130:8080/nginx:1.0
六、Docker常用命令
Linux 命令
shell
# 关闭
systemctl stop firewalld
# 禁止开机启动防火墙
systemctl disable firewalld
docker命令
查看某个命令的帮助
shell
#例如: 查看save 命令帮助
docker save --help
服务命令
shell
# 启动docker服务
systemctl start docker
# 重启docker服务
systemctl restart docker
# 停止docker服务
systemctl stop docker
# 查看状态
systemctl status docker
# 开机启动 docker
systemctl enable docker
镜像命令
shell
# 查看
docker images
# 删除镜像
docker rmi id
# 搜索
docker search xxxx
# 下载
docker pull xxx:tag
# 例如拉取nginx 指定版本
docker pull nginx:tag
# 镜像导出
# 命令格式:
docker save -o [保存的目标文件名称] [镜像名称]
# 例如
docker save -o nginx.tar nginx:latest
# 导入
docker load -i nginx.tar
容器命令
shell
# 查看容器(已经启动的)
docker ps
# 查看容器(所有)
docker ps -a
# 创建 并后台运行
docker run -id --name=c1 centos:7
# 创建后立即进入(注意这种方式进入后)
docker run -it --name=c1 centos:7
# 进入容器
docker exec -it c1 bash
# 退出容器
exit
# 启动
docker start c1
# 停止
docker stop c1
# 重启
docker restart c1
# 删除
docker rm -f c1
# 查看详情
docker inspect 容器名称或者容器id
# 让一个运行的容器暂停
docker pause c1
# 让一个容器从暂停状态恢复运行
docker unpause
# 查看日志
docker logs c1
# 查看日志 - 添加 -f 参数可以持续查看日志
docker logs --tail 10 -f c1
端口映射
-p 宿主机端口: 容器内部端口
说明: 一个容器可以映射多个端口
sh
# 创建并启动容器
docker run --name containerName -p 80:80 -d nginx
# docker run :创建并运行一个容器
# --name : 给容器起一个名字,比如叫做mn
# -p :将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口
# -d:后台运行容器
# nginx:镜像名称,例如nginx
数据卷
创建
shell
# 创建数据卷
docker volume create html
#查看所有数据卷
docker volume ls
#查看数据卷详细信息,包括关联的宿主机目录位置
docker volume inspect
#删除指定数据卷
docker volume rm xxx
#删除所有未使用的数据卷
docker volume prune
挂载数据卷
1.数据卷(目录)不存在会自动创建
2. 一个容器可以挂载多个数据卷
3. 一个数据卷也可以被多个容器挂载(两个容器可以挂载同一个文件夹)
4. 通过命令的方式不支持数据卷更新(只能修改配置,但是繁琐)
sh
# 格式
docker run ... -v 宿主机目录(文件):容器内目录(文件) ...
方式1: 使用上面创建好的数据卷
shell
# -v html:/root/htm :把html数据卷挂载到容器内的/root/html这个目录中
docker run \
--name mn \
-v html:/root/html \
-p 8080:80
nginx \
方式2: 不使用创建好的数据卷(linux绝对 路径)
sh
docker run \
--name mysql \
-e MYSQL_ROOT_PASSWORD=123 \
-p 3306:3306 \
-v /tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf \
-v /tmp/mysql/data:/var/lib/mysql \
-d \
mysql:5.7.25
# 这里的 -v /tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf 是将两个文件 挂载(不是目录)
# 这里的-v /tmp/mysql/data:/var/lib/mysql 是目录挂载
Dockerfile
sh
# 基于Dockerfile文件 构建 镜像(必须叫Dockerfile)
docker build -t javaweb:1.0 .
# 基于指定dockerfile文件名 构建 镜像(文件名任意)
docker build -f dockerfile2 -t javaweb:1.0 .
常用指令
指令 | 说明 | 示例 |
---|---|---|
FROM |
指定基础镜像 | FROM centos:6 |
ENV |
设置环境变量,可在后面指令使用 | ENV key value |
COPY |
拷贝本地文件到镜像的指定目录 | COPY ./mysql-5.7.rpm/tmp |
RUN |
执行Linux 的shell 命令,一般是安装过程的命令 |
RUN yum install gcc |
EXPOSE |
指定容器运行时监听的端口,是给镜像使用者看的 | EXPOST 8080 |
ENTRYPOINT |
镜像中应用的启动命令,容器运行时调用 | ENTRYPOINT java -jar xx.jar |
dockerfile
FROM java:8-alpine
COPY ./docker-demo.jar /tmp/app.jar
EXPOSE 8090
ENTRYPOINT java -jar /tmp/app.jar
Docker-Compose
shell
# 文件名必须是 docker-compose.yml,可以使用 -f 指定文件名
# 启动
docker-compose up -d
#查看 日志
docker-compose logs -f xxxx
# 重启服务
docker-compose logs -f xxxx
私有仓库
sh
#推送镜像到私有镜像服务必须先tag,步骤如下:
#重新tag本地镜像,名称前缀为私有仓库的地址:192.168.150.101:8080/
docker tag nginx:latest 192.168.150.101:8080/nginx:1.0
#推送镜像
docker push 192.168.150.101:8080/nginx:1.0
#拉取镜像
docker pull 192.168.150.101:8080/nginx:1.0