大家好我是苏麟今天说一说Docker.
官方网站 : Docker: Accelerated Container Application Development

微服务虽然具备各种各样的优势,但服务的拆分通用给部署带来了很大的麻烦。
大型项目组件较多,运行环境也较为复杂,部署时会碰到一些问题:

例如一个项目中,部署时需要依赖于node.js、Redis、RabbitMQ、MySQL等,这些服务部署时所需要 的函数库、依赖项各不相同,甚至会有冲突。给部署带来了极大的困难。
而Docker确巧妙的解决了这些问题,Docker是如何实现的呢?
Docker为了解决依赖的兼容问题的,采用了两个手段:

这样打包好的应用包中,既包含应用本身,也保护应用所需要的Libs、Deps,无需再操作系统上安装这 些,自然就不存在不同应用之间的兼容问题了。
要解决不同操作系统环境差异问题,必须先了解操作系统结构。以一个Ubuntu操作系统为例,结构如 下:

结构包括:
应用于计算机交互的流程如下:
1)应用调用操作系统应用(函数库),实现各种功能
2)系统函数库是对内核指令集的封装,会调用内核指令
3)内核指令操作计算机硬件
Ubuntu和CentOSpringBoot都是基于Linux内核,无非是系统应用不同,提供的函数库有差异:

此时,如果将一个Ubuntu版本的MySQL应用安装到CentOS系统,MySQL在调用Ubuntu函数库时,会 发现找不到或者不匹配,就会报错了:

Docker如何解决不同系统环境的问题?
如图:

小结
Docker如何解决大型项目依赖关系复杂,不同组件依赖的兼容性问题?\
Docker如何解决开发、测试、生产环境有差异的问题?
Docker是一个快速交付应用、运行应用的技术,具备下列优势:
Docker可以让一个应用在任何操作系统中非常方便的运行。而以前我们接触的虚拟机,也能在一个操作 系统中,运行另外一个操作系统,保护系统中的任何应用。
虚拟机(virtual machine)是在操作系统中模拟硬件设备,然后运行另一个操作系统,比如在 Windows 系统里面运行 Ubuntu 系统,这样就可以运行任意的Ubuntu应用了。
Docker仅仅是封装函数库,并没有模拟完整的操作系统,如图:

对比来看:

小结:
Docker和虚拟机的差异:
Docker中有几个重要的概念:
镜像(Image):Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起,称为镜 像。
容器(Container):镜像中的应用程序运行后形成的进程就是容器,只是Docker会给容器进程做隔 离,对外不可见。
一切应用最终都是代码组成,都是硬盘中的一个个的字节形成的文件。只有运行时,才会加载到内存, 形成进程。
而镜像,就是把一个应用在硬盘上的文件、及其运行环境、部分系统函数库文件一起打包形成的文件 包。这个文件包是只读的。
容器呢,就是将这些文件中编写的程序、函数加载到内存中允许,形成进程,只不过要隔离起来。因此 一个镜像可以启动多次,形成多个容器进程。

例如你下载了一个QQ,如果我们将QQ在磁盘上的运行文件及其运行的操作系统依赖打包,形成QQ镜 像。然后你可以启动多次,双开、甚至三开QQ,跟多个妹子聊天。
开源应用程序非常多,打包这些应用往往是重复的劳动。为了避免这些重复劳动,人们就会将自己打包 的应用镜像,例如Redis、MySQL镜像放到网络上,共享使用,就像GitHub的代码共享一样。
我们一方面可以将自己的镜像共享到DockerHub,另一方面也可以从DockerHub拉取镜像:

我们要使用Docker来操作镜像、容器,就必须要安装Docker。
Docker是一个CS架构的程序,由两部分组成:
如图:

镜像:
容器:
Docker结构:
DockerHub:
企业部署一般都是采用Linux操作系统,而其中又数CentOS发行版占比最多,因此我们在CentOS下安装 Docker。参考文档:
首先来看下镜像的名称组成:
如图:

这里的mysql就是repository,5.7就是tag,合一起就是镜像名称,代表5.7版本的MySQL镜像。
常见的镜像操作命令如:

| docker load | 加载镜像 |
| docker build | 构建镜像 |
| docker pull | 拉取镜像 |
| docker push | 推送镜像 |
| docker save | 压缩镜像 |
| docker images | 查看镜像 |
| docker rmi | 删除镜像 |
| docker --help | 帮助文档 |
案例 : Docker简单案例-CSDN博客
容器操作的命令如图:

容器保护三个状态:
其中:
案例 : Docker简单案例-CSDN博客
在之前的nginx案例中,修改nginx的html页面时,需要进入nginx内部。并且因为没有编辑器,修改文 件也很麻烦。
这就是因为容器与数据(容器内文件)耦合带来的后果。

要解决这个问题,必须将数据与容器解耦,这就要用到数据卷了。
数据卷(volume)是一个虚拟目录,指向宿主机文件系统中的某个目录。

一旦完成数据卷挂载,对容器的一切操作都会作用在数据卷对应的宿主机目录了。
这样,我们操作宿主机的/var/lib/docker/volumes/html目录,就等于操作容器内 的/usr/share/nginx/html目录了
数据卷操作的基本语法如下:
docker volume [COMMAND]
docker volume命令是数据卷操作,根据命令后跟随的command来确定下一步的操作:
数据卷的作用:
数据卷操作:
我们在创建容器时,可以通过 -v 参数来挂载一个数据卷到某个容器内目录,命令格式如下:
docker run --name mn -v html:/root/html -p 8080:80 nginx
这里的-v就是挂载数据卷的命令:
常见的镜像在DockerHub就能找到,但是我们自己写的项目就必须自己构建镜像了。 而要自定义镜像,就必须先了解镜像的结构才行。
镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。 我们以MySQL为例,来看看镜像的组成结构:

简单来说,镜像就是在系统函数库、运行环境基础上,添加应用程序文件、配置文件、依赖文件等组 合,然后编写好启动脚本打包在一起形成的文件。
我们要构建镜像,其实就是实现上述打包的过程。
构建自定义的镜像时,并不需要一个个文件去拷贝,打包。
我们只需要告诉Docker,我们的镜像的组成,需要哪些BaseImage、需要拷贝什么文件、需要安装什么 依赖、启动脚本是什么,将来Docker会帮助我们构建镜像。
而描述上述信息的文件就是Dockerfile文件。
Dockerfile就是一个文本文件,其中包含一个个的指令(Instruction),用指令来说明要执行什么操作来 构建镜像。每一个指令都会形成一层Layer。

基于Ubuntu构建Java项目
需求:基于Ubuntu镜像构建一个新镜像,运行一个java项目




其中的内容如下:
- # 指定基础镜像
- 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
docker build -t javaweb:1.0 .
上传资料
查看镜像

构建镜像


访问

虽然我们可以基于Ubuntu基础镜像,添加任意自己需要的安装包,构建镜像,但是却比较麻烦。所以大 多数情况下,我们都可以在一些安装了部分软件的基础镜像上做改造。
例如,构建java项目的镜像,可以在已经准备了JDK的基础镜像基础上构建。
需求:基于java:8-alpine镜像,将一个Java项目构建为镜像
实现思路如下:
内容如下:
- FROM java:8-alpine
- COPY ./app.jar /tmp/app.jar
- EXPOSE 8090
- ENTRYPOINT java -jar /tmp/app.jar



小结:
1. Dockerfile的本质是一个文件,通过指令描述镜像的构建过程
2. Dockerfile的第一行必须是FROM,从一个基础镜像来构建
3. 基础镜像可以是基本操作系统,如Ubuntu。也可以是其他人制作好的镜像,例如:java:8-alpine
Docker Compose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行 容器!

Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。格式如下:
- version: "3.8"
- services:
- mysql:
- image: mysql:5.7.25
- environment:
- MYSQL_ROOT_PASSWORD: 123
- volumes:
- - "/tmp/mysql/data:/var/lib/mysql"
- - "/tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf"
- web:
- build: .
- ports:
- - "8090:8090"
上面的Compose文件就描述一个项目,其中包含两个容器:
DockerCompose的详细语法参考官网:https://docs.docker.com/compose/compose-file/
其实DockerCompose文件可以看做是将多个docker run命令写到一个文件,只是语法稍有差异。
安装DockerCompose : 安装Docker(Linux:CentOS)-CSDN博客
需求:将之前学习的cloud-demo微服务集群利用DockerCompose部署
实现思路:
① 查看课前资料提供的cloud-demo文件夹,里面已经编写好了docker-compose文件
② 修改自己的cloud-demo项目,将数据库、nacos地址都命名为docker-compose中的服务名
③ 使用maven打包工具,将项目中的每个微服务都打包为app.jar
④ 将打包好的app.jar拷贝到cloud-demo中的每一个对应的子目录中
⑤ 将cloud-demo上传至虚拟机,利用 docker-compose up -d 来部署
查看提供的cloud-demo文件夹,里面已经编写好了docker-compose文件,而且每个微服务都 准备了一个独立的目录:

内容如下:
- version: "3.2"
-
- services:
- nacos:
- image: nacos/nacos-server
- environment:
- MODE: standalone
- ports:
- - "8848:8848"
- mysql:
- image: mysql:latest
- environment:
- MYSQL_ROOT_PASSWORD: root
- volumes:
- - "$PWD/mysql/data:/var/lib/mysql"
- - "$PWD/mysql/conf:/etc/mysql/conf.d/"
- userservice:
- build: ./user-service
- orderservice:
- build: ./order-service
- gateway:
- build: ./gateway
- ports:
- - "10010:10010"
可以看到,其中包含5个service服务:
MODE: standalone :单点模式启动
ports :端口映射,这里暴露了8848端口
MYSQL_ROOT_PASSWORD: 123 :设置数据库root账户的密码为123
volumes :数据卷挂载,这里挂载了mysql的data、conf目录,其中有我提前准备好的数据
查看mysql目录,可以看到其中已经准备好了cloud_order、cloud_user表:

查看微服务目录,可以看到都包含Dockerfile文件:

内容如下:
- FROM java:8-alpine
- COPY ./app.jar /tmp/app.jar
- ENTRYPOINT java -jar /tmp/app.jar
因为微服务将来要部署为docker容器,而容器之间互联不是通过IP地址,而是通过容器名。这里我们将 order-service、user-service、gateway服务的mysql、nacos地址都修改为基于容器名的访问。
如下所示:

接下来需要将我们的每个微服务都打包。因为之前查看到Dockerfile中的jar包名称都是app.jar,因此我 们的每个微服务都需要用这个名称。
可以通过修改pom.xml中的打包名称来实现,每个微服务都需要修改:
-
-
-
-
app -
-
-
org.springframework.boot -
spring-boot-maven-plugin -
-
-
打包后:

编译打包好的app.jar文件,需要放到Dockerfile的同级目录中。注意:每个微服务的app.jar放到与服务 名称对应的目录,别搞错了。
user-service:

order-service:

gateway:

最后,我们需要将文件整个cloud-demo文件夹上传到虚拟机中,理由DockerCompose部署。 上传到任意目录:

部署:
进入cloud-demo目录,然后运行下面的命令:
docker-compose up -d
推送镜像到私有镜像服务必须先tag,步骤如下:
① 重新tag本地镜像,名称前缀为私有仓库的地址:192.168.150.101:8080/
docker tag nginx:latest ip地址:8080/nginx:1.0
② 推送镜像
docker push 192.168.150.101:8080/nginx:1.0
③ 拉取镜像
docker pull 192.168.150.101:8080/nginx:1.0
打包镜像
![]()
推送镜像

加载不出镜像

在docker-compose.yml中加上 - CATALOG_ELEMENTS_LIMIT="1000"
- version: '3.0'
- services:
- registry:
- image: registry
- volumes:
- - ./registry-data:/var/lib/registry
- ui:
- image: joxit/docker-registry-ui:static
- ports:
- - 8080:80
- environment:
- - REGISTRY_TITLE=苏麟私有仓库
- - REGISTRY_URL=http://registry:5000
- - CATALOG_ELEMENTS_LIMIT="1000"
- depends_on:
- - registry
访问

想了解更多知识请到官网阅读.
这期就到这里下期见!