首页>软件资讯>常见问题

常见问题

Docker轻松编译部署Web应用

发布时间:2024-09-05 12:37:45人气:134


背景介绍

Docker 是一个开源的应用容器引擎,它允许开发者打包他们的应用以及应用的运行环境到一个可移植的容器中。容器技术的出现,极大地简化了应用的部署、扩展和迁移过程。本文将从 Docker 的基础知识入手,逐步深入到使用 Docker 部署一个基于 Nginx 的前端 React 项目。为什么需要学习Docker学习 Docker 对于前端开发者来说不仅是提高工作效率的一种手段,也是跟上现代软件开发趋势的必要条件。随着云计算和微服务架构的普及,Docker 已经成为必不可少的工具之一。下面列出几个使用容器的用处。环境一致性:在Docker容器中进行代码编译和部署、运行,可以保证运行结果一致。

资源隔离:Docker容器提供了良好的资源隔离,我们的应用及依赖在一个干净的隔离的环境中运行,不会受到其它应用的影响 。

可移植性:Docker镜像可以在任何支持Docker的平台上运行,我们的程序可以在任意操作系统之间轻松迁移。

快速搭建:Docker容器相比虚拟机更加轻量级,我们可以使用一条Docker命令快速创建一个运行环境,并且使用完之后,立即释放掉。Docker 容器基础知识虚拟化技术简介虚拟化是一种在单个物理计算机上运行多个逻辑实例的技术。通过虚拟化,可以在单一硬件平台上同时运行多个操作系统或应用程序实例。虚拟化技术通常分为两大类:

完全虚拟化:通过模拟完整的硬件环境来运行操作系统。

容器化:不模拟整个操作系统,而是隔离运行在相同操作系统上的多个独立的应用程序。Docker 代表了一种容器化技术,它与其他虚拟化技术的主要区别在于其轻量级、快速启动时间和高效资源利用。Docker 容器共享宿主机的操作系统内核,因此它们不需要额外的操作系统层,这使得容器非常轻巧且易于管理。

基础概念

基本概念.png

操作系统操作系统是管理计算机硬件与软件资源的程序集合。它为用户提供了一个平台来运行各种应用程序,并控制硬件设备的访问。Docker引擎负责管理容器和调度,实现容器的隔离和资源管理。客户端: 用户与 Docker 交互的界面。守护进程:运行在主机上,负责处理客户端请求并管理容器。

仓库:用于存储和分发 Docker 镜像的地方,如 Docker Hub。镜像:构建容器的模板,包含应用程序及其依赖项。

卷:用于数据持久化的机制,独立于容器的存在,即使容器被删除,卷中的数据也不会丢失,可以被多个容器共享重用,主要用于存储应用数据。

容器:基于镜像运行的实例。docker中容器相当于是一个操作系统,包含了镜像中所有的内容,并在运行时分配额外的资源,如内存、CPU。容器并不运行自己的操作系统内核,而是共享主机的操作系统内核,实际上容器是宿主机上运行的一个进程。提供了隔离的环境,使得应用程序可以在任何地方都以相同的方式运行。容器可以被复制、迁移和销毁。Docker 镜像管理Docker 把应用程序及其依赖,打包在镜像里,通过镜像文件,才能生成容器。同一个镜像文件,可以同时生成多个容器实例并运行。

镜像是一个二进制文件,实际开发中,镜像文件往往通过继承另一个镜像,加上一些需要的配置生成。比如可以在ubuntu的镜像基础上,加入Nginx服务器,变成一个属于你的Nginx镜像。列出本机目前所有的镜像文件docker image ls

REPOSITORY镜像名称

TAG镜像的版本号

IMAGE ID镜像的唯一ID

SIZE当前镜像的大小删除镜像docker rmi node:20.15.1删除镜像的格式为docker rmi 镜像名称[:版本号],如果不带版本号,默认删除latest版本。否则要指定删除的版本。拉取镜像docker pull node:20.15.1从服务器上拉取镜像到本地,与删除同理,如果不指定版本号,默认拉取latest版本的镜像。Docker 容器管理创建一个nginx容器以下命令的作用就是创建并启动一个nginx容器。docker run -d -p 81:80 -v /www/wwwroot/html:/usr/share/nginx/html --name testNg nginxdocker run : 这一个Docker命令,用于运行一个新的容器。

-d 容器将以“分离模式”(detached mode)运行,容器会在后台运行,不影响终端窗口继续接受其它命令;

-p 81:80: 用于做端口映射,它将宿主机的81端口映射到容器的80端口(宿主机端口:容器端口)。这样就可以通过访问宿主机的81端口来访问容器内部的80端口。

-v /www/wwwroot/html:/usr/share/nginx/html: 这个选项用于卷挂载,它将宿主机的/www/wwwroot/html目录挂载到容器内的目录/usr/share/nginx/html。这样Nginx服务可以通过读取容器内的nginx/html目录来访问宿主机上的对应目录的文件。

--name testNg:给容器指定一个方便记忆的名字,这样可以通过这个名字方便地管理和引用这个容器,不需要使用自动生成的容器id。

nginx:指定镜像名称,如果要使用指定版本的镜像,后面还应该带上版本号,如nginx:1.27.0。查看容器这个命令查看正在运行的容器的信息。docker ps

CONTAINER ID 容器的唯一ID,可以通过容器ID对容器进行管理。

IMAGE镜像名称

COMMAND 启动容器执行的命令

STATUS 容器运行状态,Up运行中,Exited已退出,Created已创建未运行等

PORTS容器的端口映射信息,显示了容器内部端口和宿主机的映射关系

NAMES 容器的名称,容器可以被赋予一个或多个名称,以便更容易地管理和识别。这个命令是查看所有容器docker ps -a查看单个容器的详细信息docker inspect testNg这会以json形式返回容器的详细配置,详细配置里可以看到当前容器的挂载目录、端口映射、执行的命令、网络信息、系统变量等。停止容器停止正在运行的容器。docker stop testNg删除容器删除一个容器,这个容器必须是停止状态才能删除。docker rm testNg以下命令删除一个正在运行的容器。docker rm -f testNg进入容器在运行中的容器中启动一个shell容器。docker exec -it testNg /bin/bashdocker exec:这是 Docker 的一个子命令,用于在一个运行中的容器内执行命令

-i 或 --interactive:这个选项使容器保持标准输入(STDIN)打开,即使没有附加

-t 或 --tty:这个选项分配一个伪终端(TTY),使你可以在容器内进行交互式操作

testNg指定 容器的名称,也可以使用容器ID

/bin/bash这是要在容器内执行的命令。这里是启动一个shell窗口,你也可以执行其它命令,如 ls -al数据持久化由于 Docker 容器是短暂的和可替换的,一旦容器被删除,容器内部的数据也会丢失。为了保持数据的持久性,你需要将数据存储在容器外部或者使用 Docker 提供的持久化机制。在 Docker 中,数据持久化和卷(Volumes)是非常重要的概念,特别是当你需要在容器之间共享数据或者在容器重启后保留数据的时候。Docker卷(Volumes)Docker 卷是一种用于存储数据的方法,它与容器的生命周期无关。这意味着即使容器被删除了,数据仍然会被保留下来。卷提供了以下优势:

卷是独立于容器的,它们可以被多个容器所共享和重用。

卷会一直存在,直到没有容器引用它们为止。

Docker 会处理卷的备份和恢复。创建卷docker volume create myVolume删除卷docker volume rm myVolume列表所有卷docker volume ls检查卷的详细信息docker volume inspect myVolume使用卷docker run -d -v myVolume:/usr/share/nginx/html --name testNg nginx卷有哪些使用场景?

这里随便列出几个使用的场景,不是全部。做数据库持久化,如创建mysql容器并挂载卷存储数据库文件。在容器间共享文件,比如图片服务器,一个容器负责存入图片数据,另一个容器负责读取图片。应用的访问日志,错误日志其实也需要做持久化处理,用来做分析、问题定位等。绑定挂载(Bind Mounts)挂载宿主目录是将宿主机上的目录直接挂载到容器中的一种方式。宿主机目录可以直接映射到容器内的目录,使得容器可以访问宿主机上的文件。容器删除后,宿主机上的文件仍然存在。可以挂载宿主机上的任意目录到容器中。docker run -v /host/path:/path/in/container nginx卷和绑定挂载是Docker中持久化数据的两种主要方法。卷由Docker管理,适用于需要在容器之间共享数据的场景。绑定挂载则直接使用主机文件系统,适用于需要直接访问主机文件的场景。一般需要可能在开发场景需要使用主机目录场景会比较多一些,比如在容器中搭建开发环境。代码在我们宿主机上存放,这样便于快速迭代代码和调试。在生产环境可以使用卷,docker的卷提供了更好的隔离性和安全性。DockerfileDockerfile是一个文本文件,其中包含了一系列指令,用于构建Docker镜像。每条指令都会为镜像添加一个新层,Docker通过读取Dockerfile的内容,逐步执行每条指令,最终生成一个镜像。层越多,构建出的镜像体积会越大。常用关键字FROM:指定基础镜像。每个 Dockerfile 必须以 FROM 指令开头。

RUN:在镜像内执行命令,并创建一个新的镜像层。

COPY:将文件或目录从宿主系统复制到镜像内的指定路径。

ADD:类似于 COPY,但可以解压缩本地的 tar 文件并支持 URL 下载。

WORKDIR:设置工作目录,后续的 RUN、CMD、ENTRYPOINT 等指令都将在这个目录下执行。

CMD:指定容器启动时要运行的命令。可以有多个 CMD,但只有最后一个生效。

ENTRYPOINT:类似于 CMD,但更适合用于指定不可变的容器主进程。

EXPOSE:声明容器将监听的端口。

ENV:设置环境变量。

ARG:定义构建参数,可以在 docker build 时使用--build-arg ARG1="val"传入。构建镜像使用以下命令构建镜像:docker build -t myImage .docker build 是 Docker 的一个命令,用于构建镜像。Docker 会读取指定路径下的 Dockerfile 文件,并根据其中的指令创建一个新的镜像

-t 或 --tag 用于给新构建的镜像指定一个标签(tag)

myImage是给新镜像指定的tag名称,这个相当于创建一个latest版本的myImage镜像。如果指定版本,则按照版本号构建镜像,如myImage:v0.1,就会创建一个v0.1tag的myImage镜像。

. 这个命令后面还有一个点(.),指当前目录,Docker会在这个目录下寻找Dockerfile文件,并使用这个文件中的指令构建镜像,这里也可以使用其它目录指定Dockerfile所在位置使用Nginx容器部署前端应用为了部署前端应用,我们需要选择一个适合的 Nginx 镜像。官方 Docker Hub 提供了多种版本的 Nginx 镜像,其中 nginx:alpine 版本一般都比较小。

Alpine Linux 本身是一个轻量级的Linux发行版,特别适合容器使用。# 使用 Node.js 官方镜像作为基础镜像

FROM node:20 AS build

# 设置工作目录

WORKDIR /app

# 从宿主机复制 package.json 和 package-lock.json

COPY package*.json ./

# 安装 npm 依赖

RUN npm install

# 复制项目文件到工作目录

COPY . .

# 构建项目

RUN npm run build

# 使用 Nginx 官方镜像作为基础镜像

FROM nginx:alpine

# 从构建阶段复制构建结果到 Nginx 的默认静态文件目录

# 我们假设项目编译结果会存放到dist目录

# --from=build就是指定从这个阶段的文件中复制

COPY --from=build /app/dist /usr/share/nginx/html

# 暴露 Nginx 端口

EXPOSE 80

# 启动 Nginx

CMD ["nginx", "-g", "daemon off;"]上面这个示例其实做了两件事,使用了两次FROM关键字,其实就是这个构建分为两个阶段。

第一个阶段FROM node:20 AS build开始,给这个阶段的构建起了个别名,方便在后续阶段中引用这个阶段的构建结果。

第二个阶段从FROM nginx:alpine开始,docker构建的镜像是按照最后一个阶段的内容生成的,这个FROM就是它的基础镜像。# 构建镜像

docker built -t app:v1 .


# 启动镜像 

docker run -d -p 80:80 --name myApp app:v1这样我们就可以在宿主机通过http://localhost访问网站了。docker镜像瘦身随着应用功能的增加,镜像文件也会越来越大,一个过大的镜像不仅会占用更多的存储空间,还会在构建、分发、部署时消耗更多的时间和带宽。因此,在编写构建文件时就要考虑提高构建速度、减少镜像大小。

镜像文件过大,最直接的影响就是分发成本增加,需要更多的带宽和时间来传输,这在服务上云时会很明显。其次启动速度减慢,容器启动时需要加载整个镜像,镜像越大,启动时间越长。那么都有哪些瘦身的方式呢?选择基础镜像最直接的方式就是使用更小的基础镜像,我们可以使用node镜像做对比。完整node镜像基于Debian或Ubuntu等传统Linux发行版。包含大量的预装软件和工具,如curl、bash、git等。镜像体积较大,因为它包含了完整的操作系统和额外的工具。包含完整的glibc库,兼容性较好,大多数Linux软件都能在其中运行。适合需要运行多种工具或进行复杂操作的场景。Alpine版Node镜像基于Alpine Linux,这是一个轻量级的Linux发行版,以其小巧和安全著称。镜像体积小,通常只有几十兆字节,适合构建微服务和容器化应用。默认使用musl libc而不是glibc,这可能会导致一些依赖特定libc的软件不兼容。需要手动安装额外的工具和依赖,因为Alpine默认只包含最基本的软件包。适合对镜像大小有严格要求的场景,以及对安全性有较高要求的环境。除了很少数需要依赖C/C++编写的扩展模块,alpine版本的镜像对于运行node服务是完全没有问题的,所以在生产环境,我们完全可以选择占用空间更小的基础镜像,下图是node版本20.15.1的3个不同类型的镜像占用大小。

使用多阶段构建在上面使用Nginx容器部署前端应用节点的示例中,就是使用多阶段构建。可以看到我们将复制源文件、安装依赖都放到构建阶段。在最终的镜像中,只有最终阶段的构建结果会被保留,这样可以有效的减少镜像大小。项目源文件不放到最终构建的结果中,也可以避免源码泄漏的风险。减少构建层级在构建镜像时,RUN,ADD,COPY指令对应的层会增加镜像大小,可以通过合并RUN指令减少层级。可以使用自带的docker image history查看镜像的构建历史、各个指令分层占用的大小等。# 查看镜像构建历史

docker image history node:20.15.1可以看到每个层级执行的命令和占用的大小


通过这种方式判断哪些指令可以合并、删除或者放到前置阶段中。结论在这篇文章中,我们了解了 Docker 容器的基础知识,包括容器的概念、Docker 的常用命令和使用方法。我们还通过一个具体的示例,展示了如何使用 Docker 部署一个基于 Nginx 的前端项目。Docker 的强大之处在于它能够简化应用程序的开发、测试和部署过程,使得应用程序能够在任何环境中一致地运行。希望通过本文可以帮助你更好地理解和使用 Docker。



上一条:docker如何删除镜像

下一条:Docker Swarm轻松驾驭容器编排的王者之路