一.Docker概述
Docker是一个快速交付应用、运行应用的技术
1.1问题引入
大型项目组件较多,运行环境也较为复杂,部署时会碰到一些问题:
-
依赖关系复杂,容易出现兼容性问题
-
开发、测试、生产环境有差异
1.2操作系统结构
1.3Docker引入
Ubuntu和CentOS都是基于Linux内核,只是系统应用不同,提供的函数库有差异
Docker如何解决依赖的兼容问题
-
Docker允许开发中将应用、依赖、函数库、配置一起打包,形成可移植镜像
-
Docker应用运行在容器中,使用沙箱机制,相互隔离
Docker如何解决开发、测试、生产环境有差异的问题
-
Docker将用户程序与所需要调用的系统(比如Ubuntu)函数库一起打包
-
Docker运行到不同操作系统时,由于Docker镜像中包含完整运行环境,包括系统函数库,仅依赖系统的Linux内核,因此可以在任意Linux操作系统上运行
1.4Docker与虚拟机的差别
Docker与 虚拟机 似乎都能让一个应用在不同的Linux 系统上运行和部署,那么它们有什么区别呢
-
虚拟机(virtual machine)是在操作系统中模拟硬件设备,然后运行另一个操作系统,比如在 Windows 系统里面运行 Ubuntu 系统,这样就可以运行任意的Ubuntu应用了。虚拟机体积大、启动速度慢、性能一般
-
docker是一个系统进程,直接调用操作系统内核;docker体积小、启动速度快、性能好;
1.5Docker架构
-
镜像(Image):Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起,称为镜像。
-
容器(Container):镜像中的应用程序运行后形成的进程就是容器,一个镜像可以运行多个容器,Docker会给容器做隔离,具有独立的CPU资源、内存资源和文件系统,对外不可见。
镜像共享
-
DockerHub:DockerHub是一个Docker镜像的托管平台。这样的平台称为Docker Registry。国内也有类似于DockerHub 的公开服务,比如 网易云镜像服务、阿里云镜像库等。
docker架构
Docker是一个CS架构的程序,由两部分组成:
-
服务端(server):Docker守护进程,负责处理Docker指令,管理镜像、容器等
-
客户端(client):通过命令或RestAPI向Docker服务端发送指令。可以在本地或远程向服务端发送指令
二.CentOS安装Docker
1.1安装Docker
首先需要大家虚拟机联网,安装yum工具
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2 --skip-broken
然后更新本地镜像源:
# 设置docker镜像源
yum-config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo
yum makecache fast
然后输入命令:
yum install -y docker-ce
1.2启动docker
通过命令启动docker:
systemctl start docker # 启动docker服务
systemctl stop docker # 停止docker服务
systemctl restart docker # 重启docker服务
1.3配置镜像加速
docker官方镜像仓库网速较差,我们需要设置国内镜像服务:
参考阿里云的镜像加速文档:阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台
三.Docker基本操作
1.1镜像名称
-
镜像名称一般分两部分组成:[repository]:[tag]。
-
在没有指定tag时,默认是latest,代表最新版本的镜像
1.2镜像相关命令
1.3容器相关命令
1.4案例一:运行Nginx容器
像Nginx这种公开镜像的运行命令具体可以到dockerhub上查找
查看容器日志的命令:
-
docker logs
-
添加 -f 参数可以持续查看日志
1.5案例二:进入Nginx容器,修改HTML文件内容
查看容器状态:
-
docker ps 查看运行状态的容器
-
添加-a参数查看所有状态的容器
删除容器:
-
docker rm
-
不能删除运行中的容器,除非添加 -f 参数
进入容器:
-
命令是docker exec -it [容器名] [要执行的命令]
-
exec命令可以进入容器修改文件,但是在容器内修改文件是不推荐的,因为很不方便并且不会被记录
四.数据卷
1.1问题引入
-
Docker容器删除后,在容器中产生的数据也会随之销毁
-
Docker容器和外部机器不可以直接交换文件
-
容器之间不能进行数据交互
数据卷
-
数据卷是宿主机中的一个目录或文件,存在一个或者多个容器之中,由docker挂载到容器,但是不属于联合文件系统,因此能够绕过Union File System提供一些用于持续存储或者共享数据的特性。
-
当容器目录和数据卷目录绑定后,对方的修改会立即同步
-
卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此docker不会在容器删除时删除其挂载的数据卷。数据卷的生命周期一直持续到没有容器使用它为止。
数据卷作用
-
解决数据持久化问题
-
解决外部机器和容器的间接通讯问题
-
解决容器之间的数据交换
1.2操作数据卷
1.3挂载数据卷
方式一:启动容器时,直接使用命令来挂载
Linux 系统中“一切皆文件”,所有文件都放置在以根目录为树根的树形目录结构中。在 Linux 看来,任何硬件设备也都是文件,它们各有自己的一套文件系统(文件目录结构)。当在 Linux 系统中使用这些硬件设备时,只有将Linux本身的文件目录与硬件设备的文件目录合二为一,硬件设备才能为我们所用。合二为一的过程称为“挂载”。挂载,指的就是将设备文件中的顶级目录连接到 Linux 根目录下的某一目录(最好是空目录),访问此目录就等同于访问设备文件。
-
路径必须是绝对路径
-
如果目录不存在,会自动创建
-
若为文件,则宿主机文件会直接覆盖容器内文件
纠正一个误区,并不是根目录下任何一个目录都可以作为挂载点,由于挂载操作会使得原有目录中文件被隐藏,因此根目录以及系统原有目录都不要作为挂载点,会造成系统异常甚至崩溃,挂载点最好是新建的空目录。
方式二:具名挂载
通过 -v 卷名:容器内的路径
方式三:匿名挂载
我们在-v挂载目录时,只写了容器内的路径,没有写容器外的路径。
docker run -d -p --name nginx01 -v /etc/nginx nginx
1.4案例三:进入Nginx容器,通过数据卷修改HTML文件内容
如果容器运行时volume不存在,会自动被创建出来
五.Dockerfile自定义镜像
1.1镜像结构
镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。
镜像是分层结构,每一层称为一个Layer
1.2Dockerfile
Dockerfile就是一个文本文件,其中包含一个个的指令(Instruction),用指令来说明要执行什么操作来构建镜像,相当于是镜像构建的说明书。每一个指令都会形成一层Layer。
更新详细语法说明,请参考官网文档: Dockerfile reference | Docker Docs
1.3案例四:基于Ubuntu镜像构建一个新镜像,运行一个java项目
Dockerfile:
# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录
ENV JAVA_DIR=/usr/local
# 拷贝jdk
COPY ./jdk8.tar.gz $JAVA_DIR/
# 安装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
#java:8-alpine镜像都包含了以上步骤,可以直接基于java:8-alpine镜像
# 拷贝java项目的包
COPY ./docker-demo.jar /tmp/app.jar
# 暴露端口
EXPOSE 8090
# 入口,java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar
1.4案例五:基于java:8-alpine镜像,将一个Java项目构建为镜像
-
Dockerfile的第一行必须是FROM,从一个基础镜像来构建,
-
基础镜像可以是基本操作系统,如Ubuntu。也可以是其他人制作好的镜像,例如:java:8-alpine
六.DockerCompose
Docker Compose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器!而Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。
它集合了docker run命令的参数,并转化成了指令
DockerCompose的详细语法参考官网:Overview | Docker Docs
案例六:将cloud-demo微服务集群利用DockerCompose部署
用DockerCompose部署时,所有的服务之间都可以用服务名互相访问