赞
踩
Docker 在前端开发中的应用实例
1、使用 Docker 搭建 WordPress
2、Docker + Nginx 部署 React 应用
3、Docker 部署 node 服务
了解 Docker 之前,我们先来了解一下虚拟机(Virtual Machine,简称 VM)。
「为什么要用虚拟机?」
整合资源是使用虚拟机的首要原因和目的。大多数的操作系统和应用,在安装(部署)时,都只会占用很少的硬件资源。
我们可以将虚拟机视为一种「由软件组成的计算机」,可以使用它来「运行在真实物理计算机上运行的任何软件」。通过将一台物理服务器划分为多个虚拟机并共享资源,可以更有效地利用硬件资源,「提高资源利用率」。
与物理机一样,虚拟机拥有自己的操作系统(Windows、Linux 等)、存储、网络、设置和软件等,并且与在该主机上运行的其他虚拟机完全隔离。
「虚拟机可以做什么,有什么好处?」
「容器化」:容器化是软件开发的一种方法,可以将应用程序和它所依赖的组件、相关的环境变量配置文件等打包成容器,然后在不同的环境中运行。容器技术最流行的实现就是 Docker。Docker 是一种虚拟化容器技术,其设计理念是 "一次构建,到处运行"。
举个例子:这就像码头上的集装箱运载货物一样,把货物(应用程序)打包后放在一个集装箱里,假设这个货物是一箱罐头和食用所需的勺子与叉子(依赖环境),通过货轮,货物可以很方便地从一个码头(假设是 Ubuntu 环境)运送到另一个码头(假设是 Centos环境)。在运输期间,货物不会受到任何的损坏(程序文件没有丢失和损坏),所以在另一个码头卸货后,依然可以很好地食用(正常启动)。
与虚拟机相比,Docker 以一种轻量级的方式实现了运行空间的隔离。虚拟机拥有的好处,Docker 也都有。打个比方,「如果物理机是一栋写字楼,那么虚拟机就是写字楼当中不同的租赁单位(公司),而 Docker 就是办公室内的隔断或分区」。
从这个比喻中,可以发现 Docker 和虚拟机之间的一些区别:
1.1、镜像
Docker 会把应用程序及依赖打包进镜像(Images)里,提供了容器运行时所需的程序、库、配置文件等,同时还包含了一些为容器运行时准备的一些配置参数(如 volume 卷)。
2.1、容器
容器(Container)是镜像的可运行实例。一个 Docker 镜像可以创建多个容器,镜像和容器的关系,「就像是 JavaScript 中的类和实例的关系一样」。
我们可以使用 Docker 提供的 API 创建、启动、停止、删除容器。在默认情况下,容器与容器、容器与主机在默认情况下是隔离的,拥有自己的独立进程空间、网络配置等。
容器由其镜像以及在创建或者启动容器时提供的配置选项来定义,当容器被删除时,未对容器状态做持久化存储的更改都会消失。
3.1、仓库
镜像构建完成后,可以很容易地在当前宿主机上运行。但是,如果需要在其它服务器上使用这个镜像,就需要一个集中的存储、分发镜像的服务,Docker Hub 就是这样的服务,叫作仓库( Docker Registry )。仓库的概念与 Git 类似,可以理解为 GitHub 这样的托管服务。
一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。
参考官网:https://docs.docker.com/get-docker/
验证安装:
$ docker --version
输出所安装的 Docker 版本。
Docker Hub 上有大量的镜像可以用。从 Docker 镜像仓库获取镜像的命令是 docker pull:
$ docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
以拉取 centos 镜像为例:
$ docker pull centos
因为省略作者名,则作者名为 library,表示使用 Docker 官方的镜像,所以上面的命令等同于:
$ docker pull library/centos:latest
使用 docker run 命令,可以通过镜像创建一个容器:
$ docker run -it centos /bin/bash
要想列出已经下载下来的镜像,可以使用 docker image ls 命令或者快捷命令 docker images 。
$ docker images
「列出部分镜像」
假设我们拉取了很多镜像,现在想列出 java 仓库中的镜像,可以使用命令:
$ docker images java
也可以添加标签做进一步的过滤:
$ docker images java:8
当本地有些镜像我们不再需要时,那我们也可以删除该镜像,以节省存储空间。不过要注意,如果有使用该镜像创建的容器未删除,则不允许删除镜像。
$ docker image rm image_name/image_id
image_name 表示镜像名,image_id 表示镜像 id 。
快捷命令为:
$ docker rmi image_name/image_id
如果想要删除全部镜像:
$ docker rmi $(docker images -q)
强制删除全部镜像:
$ docker rmi -f $(docker images -q)
启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态的容器重新启动。
「新建并启动:」
$ docker run hello-world
如果本地环境中没有 hello-world 镜像,会从远程仓库中拉取镜像;如果有,则使用本地的镜像。
注意:命令执行之后, hello-world 这个容器会自动停止,因为该镜像的目的就是输出一段内容就结束了,但并不是所有的容器运行之后都会停止的。如果需要容器在后台持续运行,可以加一个 -d 参数,-d 参数的作用是在后台运行容器并打印容器 ID。
重新启动停止的容器:
$ docker start CONTAINER_ID
如果要查看本地的容器,可以使用 docker container ls 命令:
$ docker container ls
查看所有容器也有简洁的写法,如下:
$ docker ps
注意,上面两条命令都是列出正在运行的容器。如果想要列出所有容器(包含已停止的容器),可以使用命令:
$ docker ps --all
或者快捷命令:
$ docker ps -a
可以使用 docker container stop 来终止一个运行中的容器。也可以使用快捷命令:
$ docker stop container_id
此外,docker container restart 命令会将一个运行态的容器终止,然后再重新启动它。
我们可以使用 docker container rm 命令,或者简洁的写法 docker rm 命令来删除容器。不过不允许删除正在运行的容器,因此如果要删除的话,就必须先停止容器。
$ docker rm container_id
container_id 表示容器 id,可以通过 docker ps 查看容器 id 。
当我们需要批量删除所有容器,可以用下面的命令:
$ docker rm $(docker ps -aq)
删除所有退出的容器:
$ docker container prune
$ docker exec -it container_id command
container_id 表示容器的 id,command 表示 linux 命令,如 /bin/bash。
下面的命令则启动一个 bash 终端,允许用户进行交互:
$ docker run -it ubuntu:18.04 /bin/bash
root@75053a970732:/#
其中,-t 选项让 Docker 分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。可以合起来写成 -it 。
在交互模式下,可以通过所创建的终端来输入命令,例如:
root@75053a970732:/# ls
bin dev home lib64 mnt proc run srv tmp var
boot etc lib media opt root sbin sys usr
Dockerfile 是一个 Docker 镜像的描述文件,由一行行构建镜像时所需的指令和说明构成。
下面以一个定制 nginx 镜像为例。
在一个空白目录中,建立一个文本文件,并命名为 Dockerfile:
$ mkdir mynginx
$ cd mynginx
$ touch Dockerfile
Dockerfile 的内容为:
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
FROM:定制的镜像都是基于 FROM 的镜像,这里的 nginx 就是定制需要的基础镜像,后续的操作都是基于 nginx; RUN:用于执行后面跟着的命令行命令。
docker build 命令用于构建镜像,其格式为:
$ docker build -t [镜像的名字及标签,通常 name:tag] -f [指定要使用的 Dockerfile 路径] [ContextPath]
下面使用在 Dockerfile 文件所在目录执行:
# 镜像名称为 nginx:test, 上下文为根目录
$ docker build -t nginx:test .
启动容器:
$ docker run -p 8088:80 -d nginx:test
浏览器访问 http://localhost:8088/ 查看输入内容。
前面介绍的 Dockerfile,它可以用于「构建」一个独立的镜像。而如果涉及多个容器的组合运行(如一个 web 项目,除了 web 服务容器本身,还需要加上数据库服务容器等等),就可以通过 docker-compose 来实现「编排」了。
有三个步骤:
常用参数:
WordPress 是一个基于 Mysql 和 PHP 的博客应用程序。使用 Docker 来搭建 WordPress,需要两个镜像:Mysql 和 WordPress。
下面使用 docker-compose 来编排这两个镜像:
version: '3.8'
services:
db:
image: mysql:5.7
# 持久化数据 HOST:CONTAINER 格式
volumes:
- ./db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: your_mysql_root_password
MYSQL_DATABASE: your_mysql_database
MYSQL_USER: your_mysql_user
MYSQL_PASSWORD: your_mysql_password
wordpress:
image: wordpress:latest
volumes:
- ./wp_data:/var/www/html
ports:
- 8000:80
restart: always
depends_on:
- db
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: your_mysql_user
WORDPRESS_DB_PASSWORD: your_mysql_password
WORDPRESS_DB_NAME: your_mysql_database
# 定义 Docker 卷的集合
volumes:
db_data:
wp_data:
在 Docker 中,volumes(卷)是一种用于持久化存储数据的机制。它允许你在容器和宿主机之间共享和保留数据。当你在 Docker 中创建或使用一个卷时,你可以将它挂载到容器的特定路径上,使得容器内的数据可以存储在该卷中。这样做的好处是,即使容器被删除或重新创建,卷中的数据仍然保持不变,不会丢失。这对于持久化存储数据、在容器之间共享数据或与宿主机进行数据交互非常有用。
使用 docker-compose 命令启动容器:
$ docker-compose up -d
编写 Dockerfile 文件:
# node 镜像
# apline 版本的 node 镜像会小很多
FROM node:16-alpine3.16 as build-stage
# 在容器中创建目录
RUN mkdir -p /app
# 指定工作空间,后面的指令都会在当前目录下执行
WORKDIR /app
# 解释说明:
# 1、Docker 镜像是分层的。Dockerfile 中的每个指令都会创建一个新的镜像层,每个 RUN 都是一个指令
# 2、当 Dockerfile 的指令修改了,或者复制的文件变化了,或者构建镜像时指定的变量变化了,对应的镜像层缓存就会失效
# 3、而某一层的镜像缓存失效之后,它之后的镜像层缓存都会失效
# 4、镜像层是不可变的,如果我们在某一层中添加一个文件,然后在下一层中删除它,则镜像中依然会包含该文件(只是这个文件在 Docker 容器中不可见了)。
# 5、所以我们先拷贝 package.json,然后 RUN npm i 安装依赖,形成一个镜像层,再拷贝其他所有文件,形成一个镜像层;
# 之后如果代码有所变动,但是 package.json 没有变动,再次执行时,就不会再安装依赖了,可以节省很多时间。
# package.json 有变动,才会重新执行 RUN npm i 安装依赖。
# 拷贝 package.json、package-lock.json 或者 yarn.lock
COPY package.json *.lock ./
# 设置 npm 源
RUN npm config set registry https://registry.npmmirror.com
# 安装依赖
RUN npm install
# 拷贝其他所有文件到容器(除了 .dockerignore 中的目录和文件)
COPY . .
# build 项目
RUN npm run build
# nginx 镜像
FROM nginx:alpine
# 复制 build 后的文件到 nginx 的指定目录(/usr/share/nginx/html)
COPY --from=build-stage /app/build/ /usr/share/nginx/html
# 用本地的 default.conf 配置来替换 nginx 镜像里的默认配置
COPY nginx/nginx.conf /etc/nginx/nginx.conf
# 暴露 80 端口
EXPOSE 80
# 运行容器时执行命令
# 容器启动时执行的那条入口命令一旦结束了,容器也会结束。所以需要将守护关闭,让 nginx 后台运行。
CMD ["nginx", "-g", "daemon off;"]
使用 Dockerfile 构建
$ docker build -t react_nginx:1.0 .
$ docker run -p 3000:80 react_nginx:1.0
也可以在 docker-compose 中使用这个 Dockerfile 文件进行构建:
version: '3'
services:
web:
# 指定 build 配置,使其自动构建镜像并启动容器
build:
context: ./
dockerfile: ./Dockerfile
image: react_nginx:1.0
ports:
- 3000:80
container_name: react_nginx_test
运行:
$ docker-compose up -d --build
部署一个 node + nest.js + mysql 的应用服务,需要使用 node 和 mysql 两个基础镜像。
编写 Dockerfile:
FROM node:16-alpine3.16 as build-stage
# 指定工作空间,后面的指令都会在当前目录下执行
WORKDIR /usr/src/app
COPY package.json *.lock ./
RUN npm config set registry https://registry.npmmirror.com
RUN yarn install
COPY . /usr/src/app
RUN yarn build
EXPOSE 7001
CMD ["node", "dist/main.js"]
使用 docker-compose 编排镜像:
version: '3.9'
services:
database:
platform: linux/x86_64
image: mysql:5.7
restart: always
container_name: db
ports:
- '3306:3306'
volumes:
- ./data/mysql:/var/lib/mysql
- ./doc/mysql-init/:/docker-entrypoint-initdb.d
networks:
- nesjs-network
env_file:
- .env
server:
image: node:current-alpine3.11
container_name: nestjs
build:
context: .
dockerfile: Dockerfile
ports:
- '7001:7001'
depends_on:
- database
networks:
- nesjs-network
restart: always
links:
- database
env_file:
- .env
volumes:
- .:/app
- /app/node_modules
command: npm run start:prod
networks:
nesjs-network:
driver: bridge
name: nesjs-network
运行:
$ docker-compose up -d --build
在 postman 中调试接口:
本文由 mdnice 多平台发布
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。