Docker

时间:2022-12-13 07:16:05

Docker

Docker

假如你写好了一个Web应用,并且本地调试都没有任何问题,这时候你想部署到远程的云服务器上,那么首先你需要配置相同的环境,这显然很麻烦,而且你配置好后也不一定能运行起来,因为使用的可能是完全不同的操作系统。

为了模拟完全相同的环境,我们自然会想到使用虚拟机,但是虚拟机需要模拟硬件,来运行整个操作系统,不但体积臃肿内存占用高,程序的性能也会受到影响。这是我们就可以使用到Docker。

Docker

Docker基本概念

什么是Docker?

Docker 翻译为“码头工人”,其是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化。

Docker的使用机制

Docker并不需要创建完整的虚拟操作系统,但还是会创建独立的环境,这个环境就是容器Container。

Docker不会去模拟底层的硬件,只为每一个应用提供完全隔离的运行环境,你可以在环境中配置不同的工具软件,并且不同环境之前相互不影响,这个环境在Docker也被称作 Container/容器

容器之间共用主机的内核,但不是直接访问内核,而是通过Docker引擎来访问。

虚拟机是硬件的虚拟化,而Docker容器是操作系统的虚拟化。

Docker

两者的差距:

Docker

什么是容器?镜像?

​Docker重要的三个基本概念:镜像、容器和仓库​

  1. 镜像(Image)和容器(Container)的关系,就像是面向对象编程中的类和实例。镜像是静态的定义,而容器是镜像运行的实体,容器是可以被创建、启动、停止、删除和暂停的
  2. 每一个镜像(Image)的形成都需要特定的步骤,如指明用什么操作系统、应用版本等,这些都写在镜像定义文件(Dockerfile)中。
  3. 镜像(Image)可以分享到远程仓库(DockerHub)中,也可以从远程仓库中获取。

Docker初体验

配置docker

在演示之前我们先进行环境的配置

# linux安装docker
curl -sSL https://get.daocloud.io/docker | sh

# linux安装 nodejs
yum install nodejs

创建项目

演示项目准备

  1. 创建一个文件夹,名为 dockerTest
  2. 在dockerTest中执行初始化命令 ​​npm init -y​​ (-y yes,这样后面就不用敲回车),执行完毕后生成package.json
  3. 导入所需的模块 ​​npm install express​​,这里使用express模块搭建一个网页进行演示
  4. 编写代码:
const express = require("express");
const res = require("express/lib/response");
const app = express();
const PORT = 3000;

// 在对根目录发起请求时,响应"<h1>Hello World</h1>"
app.get("/", (req, res) => {
res.send("<h1>Hello World</h1>");
});

// 设置端口号为3000
app.listen(PORT, () => console.log("port 3000 running!"));

这里可以使用 ​​node app.js​​进行测试下项目的可运行性。

编写Dockerfile

​DockerHUb​

直接命名 Dockerfile,无需后缀。

  • FROM 引用镜像
  • WOEKDIR 设置工作目录
  • COPY 复制文件
  • RUN 容器创建时执行的命令
  • CMD 容器运行时执行的命令
# 使用 From 来引用基础镜像,这里是使用18版本的nodejs,操作系统为alpine3.15
FROM node:18-alpine3.15

# 指定容器的工作目录,之后使用的Docker命令都会使用这个目录
WORKDIR /dockerTest/Workdir

# package.json是Node项目的核心,把package.json复制到镜像里
# 前面已经指明了工作路径,这里可以使用相对路径 .
COPY package.json . # COPY <本地路径> <目标路径>

# 有了COPY 和RUN后,我们的镜像在运行时就可以自己安装模块
RUN npm install

# 将构建服务器的文件复制到镜像中
COPY . .
# 这里我们不需要 node_modules,我们可以使用.dockignore对其进行忽略

# 为什么复制了一次 package.json 后,后面又复制一次呢?
# 这样做是为性能考虑,在构建镜像时,Docker会根据Dockerfile的信息一行一行的执行,每一行执行完了都会缓存起来
# package.json 主要是记录配置和依赖信息,我们一般很少改动
# 如果我们每次修改js文件后,再次构建镜像,因为前面几个步骤不变,就会变得超快(因为可以用缓存)

# 设置容器中端口号,容器中的端口号与本机的端口号是不一样的(各自处于不同的OS,不同的环境)
EXPOSE 3000

# CMD : Docker容器运行起来后要执行的命令
# 运行镜像中的 app.js
CMD [ "node","app.js" ]

编写.dockerignore 对文件进行忽略

node_modules
Dockerfile
.dockerignore
.git
.gitignore

启动项目

docker build -t dockertest . 
docker run -p 3000:3000 -d dockertest
  • -t tag,命名 .表示在当前目录寻找Dockerfile
  • -p 将容器上的某一端口映射到你的本地主机上,这样才能从主机*问容器中的web应用
  • -d detached的意思,让容器在后台运行

Docker命令

Docker

镜像管理

创建镜像:​​docker build <dockerfile路径>​

删除镜像:​​docker rmi <镜像的名称> ​

命名镜像:​​docker tag <镜像id> <用户名/镜像名:版本>​

查看所有镜像:​​docker images​

创建

# 示例1 构建镜像,由于没有取名,为none    "."是在当前位置寻找dockerfile进行构建
docker build .

# 示例2 在构建时取名字,-t是tag的意思
docker build -t dockertest:1.0 .
# 使用相同内容创建的两个镜像,其镜像id是一样的,因此我们在删除时都是用名字来删除的

删除

# remove image,有时要删除的镜像在运行,可以使用-f force进行删除
docker rmi -f dockertest

命名

若在创建镜像时没有给镜像取名字,可以使用下面的指令对镜像进行命名

docker tag 镜像id 用户名/镜像名:版本 #不加版本就是lastest

远程管理

我们创建好镜像后,可以推送到dockerhub上,但在推送前需要先登录;除了推送自己的镜像,我们还可以从仓库中拉取别人的镜像直接使用。

  • ​docker login​
  • ​docker push <用户名/镜像名:版本>​
  • ​docker pull <镜像名称>​

容器管理

镜像要变为容器才能进行使用,我们对镜像执行​​docker run​​指令,将其变为容器,这样获得的容器是开启状态的;

若是只想创建一个容器而不开启它,可以使用​​docker create​​指令。

创建并启动容器:​​docker run <镜像名称>​​ (-d 后台运行、--name 给容器命名)

创建容器(不启动):​​docker create <镜像名称>​

查看容器:​​docker ps​​ (-a 显示所有,包括暂停运行的容器)

关闭容器:​​docker stop <容器ID>​

重启容器:​​docker restart <容器ID>​

删除容器:​​docker rm <容器ID>​​ (-f 强制移除在运行的容器)

端口映射:本地的端口号 和 容器的端口号 是不一样的,Dockerfile 中的 EXPOSE 只是用于告知Docker中的其他容器可以使用这个端口号。此时我们要进行端口映射,让主机也能使用到 EXPOSE 中的端口号。

​docker run -p <主机端口>:<容器端口>​

访问容器:进入到容器里与容器中的文件进行交互,此时进行交互的目录就是Dockerfile设置的WorkDir工作目录

​docker exec -it <容器ID> /bin/sh​

execute -i interactive(交互) -t pseudo-TTY虚拟终端

/bin/sh 表示执行一个新的bash shell,用这个shell进行交互

若是对容器中的文件进行了修改,需要重启容器才能得到看到更新的效果

数据卷

一个容器被删除后,之前所做的修改将全部丢失。如果希望保留容器中的数据,我们可以使用Docker提供的数据卷Volume

数据卷可以认为是一个在本地主机和不同容器*享的文件夹

Docker

如果你在某个容器中修改了某一个volume的数据,它会同时反映在其他的容器上。

创建数据卷:​​docker volumn create ​

挂载数据卷:在容器启动时将数据卷挂载到容器的某一路径(指定)​​docker run -v <本地文件夹路径>:<容器文件夹路径>​

同步中的ignore

容器里的一些内容是不能动的,如node_modules,若是本地删除了,那么容器中的也会被删除,我们可以声明不动这些文件。

docker run  -v <本地文件夹路径>:<容器文件夹路径> -v /xxx/node_modules 
# -v /xxx/node_modules 声明node_modules 不参与同步

由于同步是双向的,若容器中有新增内容出现,本地也会出现新内容,此时我们可以把本地变为readonly,在 ​​-v 本地文件夹路径:容器文件夹路径​​ 后加上 ​​:ro​​ 即可。

删除volume

由于使用了 ​​-v​​,所以当我们不使用这个容器时,需要使用将对应的volume删除掉,不然volume会越来越多。

docker rm -fv 容器名称
# -f 强制删除
# -v 删除数据卷volume

容器组合

每次启动容器都要写一长串命令,太费劲啦,此时我们可以使用docker-compose

一、先创建一个docker-compose.yml文件,也就是需要用YAML语言来编写

version: '3.8'  # 1.声明yaml版本
services: # 2.在compose中每个容器都要在services下进行声明
dockerTest-container: # 3.整个容器先,名字就叫dockerTest-container
build: . #. 表示根据当前文件夹进行构建
ports: # 端口映射
- "3000:3000"
volumes:
- ./:/dockerTest:ro
- /dockerTest/node_modules

二、编写完毕后,在命令行端口执行 ​​docker-compose up​​,如果想在后台运行,可以加上 -d

除此之外,我们还可以加上 --build,如果镜像有修改,docker-compose就会重建,不加上 --build的话下次就会使用之前的缓存。

停止并删除所有容器 ​​docker-compose down​​ (-v 表示清除对应的volume)