基础篇:容器化部署技术-—docker,从此摆脱多环境配置的苦恼!

前言

我们设想这样一个场景,现在 leader 给了你一个任务,让你把开发完毕的应用程序进行打包、分发、部署,要求多平台通用,程序的前后端是单独的工程、后端依赖了 mysql、redis、RabbitMQ 等。按常规的运维思路就是,先将配置好的前后端程序分别打包 --> 将打包文件上传到服务器 --> 后端安装 mysql、redis、RabbitMQ --> 配置 nginx 服务器实现反向代理,虽然看上去步骤很简单,顺利部署成功,也不算太糟心,但是保不齐部署的时候,就会出现各种环境上的不兼容问题。此外,当一个程序依赖了五个、六个或者更多的环境时,按照这种传统的部署方式显然很繁琐,一旦遇到环境上的冲突、不兼容等问题,不禁让人陷入苦恼之中。假如有这样一种工具,能够将程序依赖的环境根据层级关系进行一个叠加,比如一个 Java 应用程序最底层肯定依赖的是 JDK,接着是一些数据库、中间件等其他环境,最后就是我们运行这个程序的一些入口,将这些环境根据层级关系叠加在一起,我们暂且将其称为安装包吧!而且这些安装包已经有人已经制作完毕并发布到公共仓库里边,当我们在部署我们的应用时,只需要按照这种工具规定的方式拉取安装包,部署岂不是方便很多了, docker 就应运而生了!

docker

一、什么是 Docker?

docker 是一款可以为应用程序进行打包、分发、部署的工具,也可以理解为一个轻量的虚拟机,只需要虚拟机软件运行的环境,多余的一点都不需要。而 docker 可以帮助我们下载应用镜像,创建并运行镜像的容器,从而快速部署应用。
对于上边提到的打包、分发、部署、镜像、容器这些概念,屏幕前的你是否会有些疑惑?

  • 打包:就是将软件所需要的依赖,第三方库,软件包打包到一起,变成一个安装包。
  • 分发:你可以把你打包好的"安装包"上传到一个镜像仓库,其他人可以非常方便的获取和安装。
  • 部署:拿着"安装包",就可以一行命令运行起来你的应用,在不同的平台上自动模拟出一摸一样的运行环境。
  • 镜像(image):将应用所需的函数库、依赖、配置等与应用一起打包得到的就是镜像。更加官方的说法就是:镜像是一个包含程序运行必要依赖环境和代码的只读文件,它采用分层的文件系统,将每一层的改变以读写层的形式增加到原来的只读文件上。哈哈,官方说法看来很晦涩难懂呀,我们只需要把它认为是一个安装包就行,随着使用的增加和深入,慢慢会理解的
  • 容器(container):为每个镜像的应用创建的隔离运行环境就是容器,说白了就是一个进程。
  • 镜像仓库(repository):存储和管理镜像的服务就是镜像仓库,目前,DockerHub 是最大的镜像仓库,其中包含各种常见的应用镜像。

对于镜像和容器之间的关系我们需要有一定的了解,是非常重要的概念,是我们学习 docker 的基础,当然,我也不可能通过上边两个简单的概念就可以讲明白这两者之间的概念。打个比方吧,镜像(image)好比面向对象编程语言里边的类,而容器 (container)好比面向编程语言里类实例化后的对象。一个类可以有多个对象,同理,一个镜像可以有多一个容器。容器是由镜像实例话而来,简单来说,镜像就是文件,容器就是进程。说到这里,你应该对两者的概念和关系有初步的理解了吧,镜像就是容器运行的基础,两者是辩证统一的关系。谈到这里,不得不提一下 docker 的生命周期了,它主要由三部分组成:镜像+容器+仓库。

二、docker 的安装

桌面版

服务器版

桌面版和服务器版的区别就是桌面版有 docker-compose 这个工具,而服务器版本没有,建议安装桌面版。
对于 mac 用户,可以使用 homebrew 安装,但是前提你得有 homebrew 安装工具,关于这个工具的安装,自行搜索网上的教程安装。当你电脑有 homebrew 时,直接用下面的命令行安装:

brew install --cask docker

三、docker 常用命令

当我们的电脑上安装了 docker 之后,接着我们便可以使用 docker 来安装软件,在此处,我们以安装 mysql 为例来认识 docker 的基本命令。

在终端输入上述命令之后,就会安装好 mysql。关于这条命令的解读如下:

  • docker run :创建并运行一个容器,-d 是让容器在后台运行
  • –name mysql :给容器起个名字,必须唯一
  • -p 3306:3306 :设置端口映射
  • -e KEY=VALUE :是设置环境变量
  • mysql 指定映射镜像运行的名称。镜像命名一般格式为:镜像名称: 镜像版本 ==> [ repository ]: [ tag ],在没有指定版本的时候,默认是 latest 最新版本。
    通过上边的小例子我们算是对 docker 的命令有了一定的了解,下边这张图是对 docker 命令的一个概述:

命令具体作用
docker pull将远程镜像仓库的镜像拉取到本地镜像中,类似于 git 的推送,注意在拉取镜像到本地后,镜像已经开始运行了
docker push将本地仓库的镜像推送到远程镜像仓库中,类似于 git 的推送
docker images查看本地运行的镜像
docker rmi删除本地的镜像,具体命令为 docker rmi -f 镜像名称
docker build构建本地的镜像并运行
docker load加载本地的镜像并运行
docker run运行容器
docker logs产看容器运行的日志
docker exec进入容器内部
docker stop停止容器运行
docker start启动容器
docker ps查看正在运行的容器
docker rm删除容器
这里有一个技巧,命令的具体细节我们可以不记忆,可以通过 --help 指令来查看它的用法,比如 docker rm 删除容器,我们可以通过 docker rm --help 来查看他的具体用法:

从终端的命令我们可看到 docker rm -f 容器名称 就是删除掉一个运行中的容器。通过这种方法我们便可以不用在记忆这么多繁琐的命令,只知道具体的命令关键字即可。
通过上边的学习,我们便可以拉取、删除、运行、查看镜像了。

四、容器目录挂载的方式

现在我们可以通过 docker 拉取远程镜像来部署我们的应用,现在有这样一个需求,用 nginx 部署一个 vue 项目,我们首先做的是拉取 nginx 镜像,接着配置 nginx. conf 文件,然后将 vue 打包的应用上传到 nginx 上。
那么,问题来了,我们上边提到容器是镜像的实例化对象,而镜像本身就是一个虚拟机,如何去修改配置文件和上传 vue 打包的文件。对于修改配置文件 nginx. conf,我们不妨使用命令 docker exec nginx 进入容器内部, 但是当我们使用 vi 编辑器的时候,发现容器内部不支持,我们上边提到过:docker 是一款可以为应用程序进行打包、分发、部署的工具,也可以理解为一个轻量的虚拟机,只需要虚拟机软件运行的环境,多余的一点都不需要,所以便不会存在 vi 了。假如我们本地存在这样一个目录,可以将修改的配置文件和 vue 打包文件同步到容器内部,那么就方便多了。
因此,我们需要了解一个新的概念–数据卷,它是一个虚拟目录,是容器内目录与宿主机目录之间映射的桥梁

关于数据卷的详细命令,直接通过帮助指令在终端自行查看,这里只提供常用指令。

docker volume --help // 查看其他数据卷指令

下边我将介绍三种目录挂载的方式:

1、volume 方式

由容器创建和管理,创建在宿主机上,所以删除容器不会丢失,官方推荐,更高效,Linux 文件系统,适合存储数据库数据,可以挂载多个容器。
当我们在创建并运行镜像的时候,可通过语法 -v 数据卷名称(自定义):容器内目录 来挂载目录,容器创建的时候,如果发现挂载的数据卷不存在时,就会自动创建数据卷

    docker run -d --name nginx -p 80:80 -v html:/usr/share/nginx/html nginx

我们在终端查看发现目录 html 挂载成功了。

其实,这个 html 目录位于你本地 docker 安装目录下的 volumes 目录内,比如 mac 上是这样的,orbstack 是 mac 电脑上一种内含 dockerde 轻量化软件,不必深究这个目录,只需要找到你的 docker 安装目录即可。

2、bind Mount 方式

直接把宿主机的物理目录映射到容器内,适合挂载代码目录和配置文件。而且是一种双向绑定,宿主机和容器内的内容都是同步的,也可以挂载到多个容器上。
现在有这样一个需求:

查看 mysql 容器,判断是否有数据卷挂载,基于宿主机目录实现 MySQL 数据目录、配置文件、初始化脚本的挂载(查阅官方镜像文档)
①挂载/root/mysql/data到容器内的/var/lib/mysql目录
②挂载/root/mysql/init 到容器内的/docker-entrypoint-initdb.d 目录
③挂载/root/mysql/conf 到容器内的/etc/mysql/conf. d 目录

对于以上需求,我们可以使用 volume 方式挂载,但是由于涉及的配置文件过多,我们难免在各个过程都需要修改,需要保持本地配置文件和容器内的配置一致,尤为重要的一点是当我们用 volume 的挂载删除容器时,虽然挂载的目录还在,但是当再次运行容器时,挂载的目录名称变了,上次的数据就会丢失,数据便没有迁移过来 ,因此,我们便选用 bind mount 进行绑定。具体语法如下:

 -v 本地目录 : 容器内目录

在运行 docker run 命令的时候,便可以将这条命令加入到后边。
具体执行如下:

docker run -d \                                              --name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123 \
-v /Users/username/code/dockerApp/mysql/data:/var/lib/mysql \
-v /Users/username/code/dockerApp/mysql/init:/docker-entrypoint-initdb.d \
-v /Users/username/code/dockerApp/mysql/conf:/etc/mysql/conf.d \
mysql

由于命令太长,这里使用了 \ 将命令进行分割。在终端运行以上命令,如下图:

我们使用 docker inspect mysql 命令来查看容器的详细信息,这里只截取了挂载目录的信息,如下图:

从上图发现已经主机目录已经和容器目录映射成功了!

3、tmpfs Mount

适合存储零食文件,存储在宿主机内存中,不可多容器共享。

以上就是我们常见的三种目录挂载方式,核心掌握前两种方法。

五、自定义镜像

通过以上的学习,我们可以通过别人制作好的镜像去部署一个应用了,但是我们如果要制作属于自己的镜像该怎么做?下边我们将通过一个例子来讲述如何制作一个镜像。
镜像就是包含了应用程序、程序运行的系统函数库、运行配置等文件的文件包。构建镜像的过程其实就是把上述文件打包的过程。

每一个镜像结构都是根据其依赖关系的层级关系或者说是依赖范围大小分层的,分别有基础镜像,中间的每一层,入口层。

关于这些层之间的结构 docker 提供了一个描述文件 Dockfile ,它会说明每一层具体要执行哪些操作,帮我们自动构建镜像,常见指令如下:


具体指令的详细描述如下 (可以略过):

// FROM 指令
FROM <IMAGE>
FROM <IMAGE>:<TAG>

通过 FROM 指定的镜像名称必须是一个已经存在的镜像,这个镜像称之为基础镜像,必须位于第一条非注释指令

// MAINTAINER指令
MAINTAINER <NAME>

指定镜像的作者信息,包含镜像的所有者和联系人信息

// RUN 指令
用于指定构建镜像时运行的命令,两种模式:
RUN <command> (shell 模式)
RUN ["executable", "param1", "param2"] (exec模式)

多条 RUN 指令可以合并为一条:

RUN yun install httpd && yun install ftp

这样在构建的时候会减少产生中间层镜像

// EXPOSE 指令
指定运行该镜像的容器使用的端口,可以是多个
EXPOSE <PORT>

使用这个指令的目的是告诉应用程序容器内应用程序会使用的端口,在运行时还需要使用 -p 参数指定映射端口。这是 docker 处于安全的目的,不会自动打开端口。

docker run -p 80 -d dockertest/dockerfile_build nginx -g "daemon off"
// CMD 指令
// 用于提供容器运行的默认命令 如果在 docker run 时指定了运行的命令,则 CMD 命令不会执行。CMD有如下三种模式:
CMD <command> (shell 模式)
CMD ["executable", "param1", "param2"] (exec模式)
CMD ["param1", "param2"] (通常与ENTRYPOINT 搭配指定ENTRYPOINT 的默认参数)
// ADD 和 COPY
作用都是将文件或目录复制到 Dockerfile 构建的镜像中
ADD <src> <dest>
ADD ["<src>" "<dest>"] (适用与文件路径包含空格的情况)

COPY <src> <dest>
ADD ["<src>" "<dest>"] (适用于文件路径包含空格的情况)
  • build 为镜像(安装包)和运行
docker build -t repository(package name):tag .

我们可以基于 Ubuntu 基础镜像,利用 Dockerfile 描述镜像结构,

但是这样有点麻烦,我们可以基于 Ubuntu 基础镜像,利用 Dockerfile 描述镜像结构,直接基于 JDK 为基础镜像,省略前面的步骤。

当我们便写好了 Dockefile 文件后,可以直接使用一下命令来构建镜像:

docker build -t 镜像名称 .

-t :是给镜像起名,格式依然是 repository: tag 的格式,不指定 tag 时,默认为 latest
. :是指定 Dockerfile 所在目录,如果就在当前目录, 则指定为 .

六、网络(多容器通信)

对于前后端分离的应用,我们单独部署的时候需要配置跨域,而且每个应用都是一个单独的容器,默认情况下,所有容器都是以 bridge 方式连接到 Docker 的一个虚拟网桥上:

但是如果重新启动容器后,IP 地址就会发生变化,我们又得在配置文件中修改配置,如果能把变化的 IP 地址用一个变量代替了,这样即使重启应用后,我们在也不需要修改 IP 地址了,而这个变量就是容器名称,但是默认情况下是不能通过容器名互相访问的,这时候我们需要通过自定义网络,让它们加入同一个网络,便可以使用容器名互相访问了!
Docker 网络操作命令如下:

也可以在容器运行的时候创建网络,具体示例操作如下:

docker run -d --name redis --netowrk test-net --network-alias redis redis:latest

七、docker-compose

通过上边的讲述,我们可以将一个项目中涉及的环境进行部署,但是这样的方式只能一个容器一个容器的单独部署运行,如果项目中涉及的容器比较多,效率就会很低,因此 docker 提供了一个工具 docker- compose。Docker Compose 通过一个单独的 docker-compose. yml 模板文件(YAML 格式)来定义一组相关联的应用容器,帮助我们实现多个相互关联的 Docker 容器的快速部署

事实上,Dockerfile 描述文件中的指令和 docker-compose. yml 配置文件的内容是对应的。

当我们配置好了 docker-compose. yml 文件后,直接运行 docker compose [OPTIONS] [COMMAND] 命令部署并运行所有容器。

总结

上述内容可以帮助我们处理日常开发过程中碰到的部署问题,对于不是专业的运维工作人员而言,已经足够了。有了 docker 后,我们在部署应用,分享自己的软件的时候,再也不会因为计算机系统、环境冲突而引起的问题苦恼了,这么好的工具我们有什么理由不学习使用呢?

  • 24
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值