初识Docker
什么是Docker
每个应用都需要有自己的依赖和函数库,不同环境可能会有问题
隔离容器=沙箱,防止不同依赖形成干扰从而隔离起来
打包应用时 是基于某个操作系统打包的,比如基于ubuntu打包的,那能在mac上运行吗?
所有的linux内核操作系统都可以分成两级
一层是共享的内核-linux内核,不管是ubuntu还是centOS,区别是上层的系统应用不同
内核:是负责与计算机硬件交互的 比如说调用内存、cpu、读取文件等。内核把这些事情变成一个一个的指令,调用这些指令就可以操作计算机硬件了,但是这些指令比较简陋,如果基于指令去开发应用就太麻烦了。所以就有了系统应用
系统应用:比如说ubuntu,它会将内核的指令进行了组装再封装,形成了函数,许多的函数形成了函数库,程序员可以基于函数库进行开发。程序调用函数库,函数库调用内核指令,指令操作硬件,从而实现开发
问题来了:ubuntu的系统应用为什么不能在centOs系统上运行呢?
虽然内核一样,但是系统应用不同,也就是函数库不同,ubuntu上有的函数库centOS不一定有,或者名字不一样,比如一个ubuntu系统上的mysql应用迁移到了centOS系统上尝试去执行,可能会发现当mysql去调用一个函数库的时候,centOS上根本没有,那肯定就会报错了。这就是我们的应用 为什么不能跨系统的原因
针对这样的问题,我们的Docker干了什么事呢?
既然每个应用都依赖于系统函数库,那就把系统函数库和应用一起打包就好了,随便放到一个linux操作系统上,只要内核是linux内核,比如mysql迁移到了centOS系统上,它在执行的时候,调用的函数库是已经打包好了的函数库。而这个函数库直接调用操作系统的内核,而内核直接访问计算机硬件,这个调用就完成了,它不需要管系统应用是什么,基于这种模式,Docker就解决了跨系统问题
我们可以认为Docker打包好的程序包,可以运行在任何的linux内核的操作系统上
总结:
Docker和虚拟机的区别
我们在windows系统上装centOS系统,怎么实现在一个系统装了另一个系统的呢?
其实是用到了一种Hypervisor技术,这种技术可以模拟出一个计算机的各种硬件,比如cpu内存等,然后在这个模拟出的计算机上 就可以安装任何我们想要的操作系统,比如centOS ,操作系统都可以安装,那么在它上边就可以安装适合的依赖、函数库、以及应用,这样就实现了跨系统的应用部署了。当应用在执行时,它会以为它在一台真实的电脑上
而docker它的应用在执行时,是直接调用操作系统内核的。所以执行性能要好很多
docker应用只是简单的封装了一些函数库、依赖(只是应用执行所必须的),占用体积小,最小的docker容器可能只有几兆,大的也就几十几百M
启动时:
- 虚拟机得先启动虚拟的操作系统,而后再去启动虚拟操作系统上的应用。速度慢
- docker是直接启动应用,就是说在一个操作系统上直接启动了一个进程
总结
Docker架构
镜像:函数库、依赖等打包,这些东西落到硬盘上就是一个一个的文件 。也可以说镜像就是硬盘中的文件(有日志文件,写数据的data文件,依赖文件等)
容器:可以理解为我们把mysql跑起来,其中的进程就是容器 ,只不过在docker里边容器还需要做隔离,里边会有独立的cpu资源,内存资源 ,甚至还有独立的文件系统。因此在该容器内运行的进程 它就会以为自己是这台计算机里的唯一进程了,从而起到了一种隔离的效果。
将来mysql镜像不管是启动一个容器还是多个容器,他们之间都是相互隔离的,当容器运行过程中必然会做数据读写操作 ,比如mysql将来会做数据存储工作,那么他们能不能把数据写到mysql镜像的的data目录里呢?显然是不能的,如果这么做了就相当于对镜像产生污染了
所以说镜像都是只读的,容器在运行的过程中,不能往镜像里写数据。可以基于镜像去创建容器,容器可以从镜像里读取数据
将来mysql容器写到哪里呢?很简单,从镜像中拷贝一份文件到自己的独立文件系统中,这就是隔离的一个特性了,在写数据的时候写到自己的空间的文件里 ,不会对别的容器产生影响,也不会对镜像产生影响,包括将来记录日志也是个写个的。将来我们基于镜像创建容器也是全新的,里边不会有任何污染
这样我们的镜像才会放心大胆的交给别人去使用 ,那么问题来了,我们怎么把镜像共享给别人去使用呢?
DockerHub:镜像托管,这样的平台称为镜像服务器
类似于git Hub,这个是做代码的托管
程序员可以用docker命令去完成镜像的构建,构建出mysql等各种各样的镜像,而后把这镜像上传到DockerHub服务器上去
那么我们该怎么样利用Docker去完成镜像的构建,或者是从远端拉取镜像呢?又该怎么样去运行容器呢
我们可以在本地自己构建镜像build,也可以远程去服务器上拉取镜像pull
然后就可以运行镜像创建容器了,守护进程也会帮我们创建容器
只有一些我们自己的微服务等才需要自己去构建,否则没必要
一般情况下,我们就是向DockerServer发送命令,拉取镜像,启动镜像从而创建容器完成部署
安装Docker,我这里是mac安装了,就不安装如下的了
注意:\ 斜杠是命令的拼接,命令太长,一行写不下,告诉系统我这个命令没结束,要往下接着读
-y 就是安装的过程中不要问,问就是一路往下走
查看docker版本
Docker的基本操作
镜像操作
获取镜像的两种方式docker build本地,和dockerpull 远程
images在这里翻译成镜像
获取镜像一般有两种方法
1、本地获取,需要一个Dockerfile的文件,执行build命令,把Dockerfile构建成一个镜像
2、从服务器拉取镜像
查看本地有哪些镜像:docker images
删除本地镜像:docker rmi。 rmi全拼是remove images
镜像分享给别人
1、docker push 推送到服务器
2、将镜像拷贝给别人,执行docker save命令将镜像打成压缩包,别人执行docker load命令加载就可以了
查看docker所有命令:
docker --help :查看所有的命令
docker images --help :查看某个详细的命令
案例:
Docker dockerHub的网站,直接可以去搜索镜像名字
这个意思是官方镜像
点开后发现有很多版本
往上发现已经给了一个默认的拉取命令,没有指定版本,默认是最新的
mac 先启动小鲸鱼,然后终端就可以执行docker pull nginx了,得先起来
latest 就是最新版本
案例二:IMAGE是镜像的名称和版本号
发现导出成功了
docker rmi 加镜像的名字或者id删除镜像
然后给别人进行安装 -i输入一个文件
查看打包的文件
删除镜像,可以用镜像名称,也可以用镜像id,版本号最新的就用latest
容器操作
容器相关命令
docker run 创建容器,还可以让容器处于运行状态
容器除了有运行,还会有暂停、停止状态
从运行到暂停命令:docker pause、
从暂停到运行命令:docker unpause
从运行到停止命令:docker stop
从停止到运行命令:docker start
暂停和停止的差别主要在于操作系统的处理方式,如果进入暂停状态,操作系统会将容器的进程挂起,容器关联的内存暂存起来了,cpu不再执行该进程,如果恢复了,那么内存空间恢复,程序接着运行
停止意味着不玩了,直接把进程杀死,内存被回收,保留的紧紧是文件,start会创建一个全新的进程
如何查看容器状态?ps 、logs日志
docker exec 可以操作容器内部
docker rm 删除容器,进程、内存回收,文件都会被删除,删除了个干干静静
案例:
进入官网,找到nginx往下拉,找一个最简单的运行命令
$ docker run --name some-nginx -d -p 8080:80 some-content-nginx
注意:容器是对外隔离的,任何一台服务器想要访问我的容器,那么请走开,我不让你访问,而我们创建容器的目的就是为了让别人访问呀 所以做一个端口映射
比方说我们的宿主机有自己的很多端口,其中一个端口是80,那么我让宿主机的80和容器的80产生一个关联映射。这样一来,任何进入宿主机80的这个请求,就都会被转发到该容器的80端口去执行,这样就等于是nginx处理了这个请求
所以客户想要访问nginx容器,只需要去访问这台服务器,请求进来以后,宿主机一看端口,哦,往nginx容器里走呀,统一都扔过去,于是容器就处理了
所以端口映射的作用是:把本来完全隔离的容器 ,暴露一个小窗口,让你来透过它来访问!
宿主机端口可变,容器端口不可变,比如我想让宿主机的8080和容器的80端口映射,这样的话用户在访问的时候,就是访问8080,而不是80了
-p:端口映射
-d:后台运行,不加就是前台运行,比如360杀毒软件,我们点了小红x发现并没有关掉,它切换成后台了,我们打开任务管理器发现进程还在,这就是后台运行,点x直接关闭就是前台运行
最后一个参数是镜像名称,我们创建容器是基于那个镜像创建的呢,这里是nginx镜像,没有写标签tag,其实也就是最新版本的意思啦
启动容器保持后台运行 :基于nginx最新版本的镜像创建一个容器,起个名字,端口映射是 ,保持后台持续运行;返回的字符串是容器的唯一id
created 创建时间
status 运行状态,up正在运行
端口:宿主机端口与nginx端口的映射
names: 容器的名称
创建后需要访问呀,访问宿主机看下,因为在我自己的本机上,这就说明容器部署成了
如果关闭容器,docker pause id,那么在访问localhost就发现打不开了
查看日志,docker logs 容器名字,日志持续输出的话加个-f
总结:
注意:-p是非常重要的,暴露一个端口,让外界可以访问
Docker容器的基本操作二
bash:像cd、vim、rm等都是bash命令
docker exec -it mr bash 进入容器
pwd命令:显示当前的目录的路径
docker容器有自己的文件系统
我们要想知道比如nginx在docker 的安装位置,只能去找作者,就是docker,登陆docker网站,这里就有一个简单的静态内容地址
进入 cd /usr/share/nginx/html
查看文件内容,用cat命令,cat index.html,
想要编写,vim 发现找不到命令,那是因为容器封装的函数nginx不需要vim命令,没有封装 ,再次证明文件系统和函数库都是阉割版的
可以用代替的方式
从容器中退出来用exit命令
docker ps -a 展示所有容器,包括停掉的容器
删除容器,强制删除,不管容器是否在运行
总结:
不推荐修改:
1、不方便,命令没有
2、进入容器内修改是没有记录的 ,改完后可能忘了就,在启动一个容器修改成一模一样的,可能吗?自己都忘了呢
持久化启动,也去官网上找命令
如何访问呢?打开一个客户端RDM
然后进入exec容器内部,执行redis-cli启动redis客户端
打开客户端发现已经存储了
我们可以先进入bash 然后再进入cli,也可以直接进入cli
docker exec -it mr redis-cli ;因为docker exec -it mr是进入容器mr,然后执行一个命令,执行什么命令呢?执行redis-cli命令;而且退出只需要一次推出就可以了
数据卷(容器数据管理)
容器与数据耦合的问题:直接操作内容修改难
比如说有一个docker主机,在这个主机上会去管理很多很多的 数据卷,所有的数据卷一定会指向宿主机文件系统上的一个目录
数据卷目录只是一个虚拟的,在docker主机上创建了一个html文件,那么宿主机上一定就会创建一个html文件,他们俩会有一个映射关系
将来有一个容器,创建后比如说nginx,他的配置文件,页面等都在容器里,这就会造成容器与数据耦合了
我们让nginx的内部目录与数据卷进行关联,本质就是在和宿主机文件系统上的目录进行关联,此时docker就会管理该容器了
比如说我们在nginx容器的文件里写点东西,这些东西会立即写到宿主机文件系统里,反之亦然,在宿主机文件系统里写数据,也会反馈到容器文件里
也就是说我们对宿主机文件的任意修改,会立刻反应到与之数据卷关联的容器内部,不用进入容器内去修改了
1、解决了修改难,2、修改后配置共享 让该容器也去挂载同一个数据卷,3、管理版本,将来容器删了,但是数据卷不会删
操作 :第一个是现实数据卷的所有信息,包括地址等
挂载数据卷
-v 挂在数据卷,数据卷的名称:/容器内的数据卷地址 ,可以去dockrHub上看
案例:
如下操作我们发现docker的数据卷挂载目录不存在,可以路径是有的
mac下直接查看dockr地址是查不到的,因为还有一层vm,执行下边的命令,然后ls就可以看见了
docker run -it --privileged --pid=host justincormack/nsenter1
可以发现容器的目录
/usr/share/nginx/html
已经挂在到宿主机的真实目录下了
/var/lib/docker/volumes/html/_data
容器内的文件跑到容器外边来了
直接利用vi命令就可以修改了
修改后,可以去容器内部查看是否修改
docker exec -it mn bash, 然后执行cat命令,发现已经更改了,访问localhost发现已经变了
那么问题来了,如果数据卷不存在那么启动容器直接挂载,数据卷还会创建成功嘛?
我们吧html数据卷删掉,mn容器删掉
docker rm mn ,然后是数据卷docker volume prune 就会把没有用的数据卷删掉,因为mn容器已经被删了,html就没人用了
docker run --name mn -v html:/usr/share/nginx/html -d -p 80:80 nginx
我们发现数据卷被自动创建了
打开浏览器刷新,发现已经恢复了
这说明了我们启动容器在做数据卷挂在时,数据卷不存在,我们的docker会帮我们自动去创建
总结:所以大多数情况下我们不用自己去创建数据卷,可以由docker自动完成
以上演示是将数据卷挂在到容器目录!
案例:将宿主机目录直接挂在到容器
将tar文件拷贝到我的根目录下
创建目录 -p 是多级创建
mkdir -p mysql/data
然后找到命令-e 是指环境变量 MYSQL_ROOT_PASSWORD=my-secret-pw这环境变量密码是这个,还缺少端口和数据卷的挂载;conf.d d是指一个目录,my.cnf我们不建议直接覆盖这个目录,有很多自己的默认配置的,我们可以在另一个目录conf.d这个目录下操作,都会被加载
配置目录:
数据存储目录:
docker run \
--name mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-p 3339:3306 \
-v /Users/yuhaiyang/dockerMysqlConfigData/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf \
-v /Users/yuhaiyang/dockerMysqlConfigData/mysql/data:/var/lib/mysql \
-d \
mysql
docker ps 发现容器已经存在了
但是mac启动是失败的,没有起来,我也不知道为啥??? 版本问题,换个版本就可以了,但是还有一个问题就是我这里宿主机文件挂载后没有数据,不知道为啥
Dockerfile自定义镜像
镜像结构
从基础开始,逐层构建,依赖,打包,运行等,先从最底层构建
dockerfile语法
dockerfile就相当于是镜像构建的说明书
# 指定基础镜像 可以是一个从0开始的操作系统,也可以是别人共享的镜像
FROM ubuntu:16.04
案例:
# dockerfile文件内容
# 指定基础镜像 可以是一个从0开始的操作系统,也可以是别人共享的镜像
FROM ubuntu:16.04
# 配置env环境变量,JDK的安装目录
ENV JAVA_DIR=/usr/local
# 拷贝jdk和java项目的包 到/usr/local目录下
COPY ./jdk8.tar.gz $JAVA_DIR/
# 安装JDK tar -xf是解压缩,mv是重命名
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
# 以上的动作都是在安装JDK
COPY ./docker-demo.jar /tmp/app.jar
# 暴露端口
EXPOSE 8090
# 入口,java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar
-t全拼是-tag就是版本的意思(项目名称版本)
.代表的是dockerfile所在的目录
回车后就会逐步执行dockerfile的指令了,然后查看镜像就会发现已经构建好了,运行就和之前的一样了
访问发现已经成功了
重复构建的话就太麻烦了
我们可以把jdk的构建先构建一个镜像 事实上已经有人帮我们做了
java:8-alpine
# dockerfile文件内容
# 指定基础镜像
FROM java:8-alpine
COPY ./docker-demo.jar /tmp/app.jar
# 暴露端口
EXPOSE 8090
# 入口,java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar
覆盖原来的文件,再次构建,发现又构建成功了
# 空格.
docker build -t javaweb:2.0 .
构建Java项目
Docker-Compos
实际项目中可能有成百上千个微服务,不可能都要我们手动去打包形成镜像
初识:是一个分布式应用部署的帮手 ,但是它要做部署,还需要compos文件才可以
以前使用docker run来运行一个容器,而现在是compos是用来运行多个容器,可以认为compos是多个docker run的集合
mysql一般不需要暴露接口,因为是给集群内用的,不用暴露
默认就是-d
mysql 、web 就是名字 --name的名字,指定镜像构建,和自己build 临时构建一个服务并运行
安装docker compos,mac的docker已经有docker compos功能了
修改文件权限加x是代表执行权,绿了就代表可以执行了
配置docker-compos的自动补全功能,方便
curl -L https://raw.githubusercontent.com/docker/compose/1.29.1/contrib/completion/bash/docker-compose > /usr/local/bin/docker-compose
报错的话修改host文件
199.232.68.133 raw.githubusercontent.com
总结:就是基于compos文件帮助我们快速部署集群服务
部署微服务集群
将来我们把项目的jar包打好,然后放到相应的目录里 ,将来我们的docker-compose就会帮助我们自动的去构建微这几个微服务的镜像了,这些微服务依赖于mysql,mysql里面有数据库表等文件
docker-compose文件
version: "3.2" #是版本号 这里用3.x的版本
services:
nacos: #是服务名,所哟肚饿服务都需要注册到nacos里
image: nacos/nacos-server
environment: #就是单机启动的意思 -m参数 standalone
MODE: standalone
ports:
- "8848:8848"
mysql:
image: mysql:5.7.25
environment:
MYSQL_ROOT_PASSWORD: 123456
volumes:
- "$PWD/mysql/data:/var/lib/mysql" #$pwd 就是当前目录,相对于dockercompos的当前目录
- "$PWD/mysql/conf:/etc/mysql/conf.d/"
userservice: #.就是当前目录 执行dockercompose当前目录下的Dockerfile
build: ./user-service
orderservice:
build: ./order-service #执行当前目录下的Dockerfile
gateway:
build: ./gateway #执行当前目录下的Dockerfile
ports:
- "10010:10010" #我们网关才是整个微服务的入口,别的都不需要暴露
以前的地址都是localhost本地,现在是集群部署了,像mysql,order-service不一定在同一台机器了,那么如何知道他们的地址呢?
利用docker-compose部署时,所有的服务之间,都可以用服务名来访问,服务名mysql、orderservic、gateway等,这事compose底层做的一些配置
将原来的本机地址都改成服务名称
下一步开始打包app.jar
我们可以看userservice的pom文件 有个build,这个就是做项目打包,finalName就是打包的名称
然后我们利用生命周期,先clean,然后打包,打包成功后,服务会出现target目录
这里边就会出现app.jar的包
将我们项目的文件app.jar拷贝到dockerfile的同级目录下
以上所有准备就都做好了,只要我们去执行docker-compose镜像就回构建出,容器就会部署了
将其上传到服务器,然后就可以启动部署了
up:创建并执行容器
stop:停止 容器,down不仅回停掉,还会删掉
restart:重启
logs:查看日志
加-d就是后台运行
启动过程中发现orderservice、userservice报错了,是因为nacos还没起来,阿里巴巴的一个小bug,nacos起来后再重启一夏userservice、gateway等
Docker镜像服务
docker私有镜像仓库
dockerHub是公共的镜像仓库
docker Registry :docker注册中心,就是把我们的镜像注册到Registy的服务上去
搭建镜像仓库可以基于Docker官方提供的DockerRegistry来实现。
官网地址:Docker
3.1.简化版镜像仓库
Docker官方的Docker Registry是一个基础版本的Docker镜像仓库,具备仓库管理的完整功能,但是没有图形化界面。
搭建方式比较简单,命令如下:
docker run -d \
--restart=always \
--name registry \
-p 5000:5000 \
-v registry-data:/var/lib/registry \
registry
命令中挂载了一个数据卷registry-data到容器内的/var/lib/registry 目录,这是私有镜像库存放数据的目录。
访问http://YourIp:5000/v2/_catalog 可以查看当前私有镜像服务中包含的镜像
3.2.带有图形化界面版本
因为不是官方的,使用DockerCompose部署带有图象界面的DockerRegistry,命令如下:
dockerCompse里多个服务访问用服务名 这样ui控制台就知道了regisry的地址了,将来我们访问8080就可以了
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 #服务名称,就上边的同名:端口
depends_on:#依赖于上边配置registry
- registry
3.3.配置Docker信任地址
我们的私服采用的是http协议,默认不被Docker信任,所以需要做一个配置:
# 打开要修改的文件
vi /etc/docker/daemon.json
# 添加内容:
"insecure-registries":["http://192.168.150.101:8080"]
# 重加载
systemctl daemon-reload
# 重启docker
systemctl restart docker
新建一个文件夹,在创建一个yml文件,将内容拷贝里边去
访问就可以了
向镜像仓库推送镜像
推送要重新起名字tag,要想推送到私有仓库,一定要重命名
执行推送后就成功了
从镜像仓库拉取镜像
执行docker pull 名字 ,就拉取成功了