Docker
1. 是什么?
在计算机上的隔离环境中运行应用程序:node app,react app Mogodb等
2. 目的
在他人电脑上,提供与自己开发一致的环境
理解这三个东西就够了

三者关系

镜像是什么?
Images 镜像 是独立的,如果修改了内容,需要构建一个全新的镜像
镜像里包括了应用程序运行所需的一切。
镜像是由多层组成的,多层的叠加
容器是什么?
一个容器Container(隔离进程,执行程序)分享镜像
卷是什么?
把本地或者一些资源映射到容器中,实现热更新等
举个例子
例子说明
我需要用node写个接口,在浏览器上访问localhost:4000/时,可以看到相关内容

准备
javascript
一: 创建文件夹api
二: npm init
一直敲回车,创建出了pakejson.json文件
三: 安装包 npm install --save cors express
四: 创建app.js件,内容如下
const express = require('express')
const cors = require('cors')
const app = express()
app.use(cors())
app.get('/', (req, res) => {
res.json([
{
"id":"1",
"title":"Book Review: The Bear & The Nightingale.ABCD"
},
{
"id":"2",
"title":"Game Review: Pokemon Brillian Diamond"
},
{
"id":"3",
"title":"Show Review: Alice in Borderland"
}
])
})
app.listen(4000, () => {
console.log('listening for requests on port 4000')
})

先别急,我们先想一下,如果你现在是从git上面拉取的这个项目你会想要,实现这个例子,怎么做?
确保自己电脑有node
npm install 去安装包
node app.js 运行该文件
访问localhost:4000
那么接下来,我们用docker来做这些事情。
docker相关操作
前提准备
1. Docker Desktop
所有镜像操作,都得打开此软件进行

2. 项目里加入Dockerfile文件
里面表明了构建镜像要做哪些事情
需要在项目下创建Dockerfile文件,如果使用VSCode,可以安装docker插件
Dockerfile文件就是创建镜像的关键文件,值得注意的是,你还需要启动 Docker Desktop软件
javascript
#FROM 指令是 Dockerfile 中的第一条指令,
#它为后续命令(如 RUN、COPY)设定了基础环境。
#最好指定运行的版本,保证了稳定的输出
FROM node:20.19.6
#指定后面的工作目录
WORKDIR /app
#复制文件
COPY package.json .
#安装包
RUN npm install
#第一个.是复制源文件的相对目录,第二个.是复制到镜像中的路径,镜像是有自己的结构的
COPY . .
#容器暴露出的接口
EXPOSE 4000
#运行指令
CMD ["node", "app.js"]

3. 最好加入.dockerignore
COPY复制源文件时,忽略复制一下的文件、文件夹
javascript
node_modules
*.md
创建镜像
在Dockerfile文件的根目录下,执行docker相关指令

构建镜像 -t tag的意思 . 是Dockerfile的根路径
myapp2就是镜像名称,你要是想同一镜像名,不同的版本的话
myapp2:v1 如图Tag就会显示v1,如果你不加,Tag就会显示latest,表示这就是最新版本
javascript
docker build -t myapp2 .

如图所示,构建镜像成功了
创建容器

这是在软件上操作

用docker指令操作
docker run 是创建并运行容器的意思,每次执行都是创建一个新的容器
-p 4000:4000 端口port 把本地的4000端口映射到容器的4000,没有这一步,在浏览器上可访问不到
-d detached 分离模式 独立于后台运行长期驻守的服务,不受其他干扰
myapp2是上面创建的镜像的名称
javascript
docker run --name myapp_c1 -p 4000:4000 -d myapp2


ok,到这一步,你就可以去访问
localhost:4000
试一试效果,发现也可以访问

接下来,我们试着改变app.js内容,看能浏览器能不能同步更新


我们会发现没有及时更新,这是因为我们创建了一个镜像,这个镜像就是一个独立的只读的内容,容器根据镜像创建出独立的运行环境,当然不会发生改变。
做法: 删除镜像容器,重新创建新的镜像容器
弊端:每次npm install会很耗时
如果不使用镜像,在本地实现,实时更新怎么做?
node要是实时更新,我们在本地安装全局的nodemon
执行nodemon app,js 实现接口实时更新
使用docker怎么做?
安装nodemon
卷可以解决这个问题
接下来我们要修改一下Doderfile文件和package.json文件
javascript
Dockerfile文件如下
FROM node:20.19.6
RUN npm config set registry https://registry.npmmirror.com
RUN npm install -g nodemon
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
EXPOSE 4000
CMD ["npm", "run","dev"]
----------------------------------------------------------------------------------
package.json的scripts的dev的命令,发生改变
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "nodemon -L app.js",
}
层缓存
还有一个点,Dockerfile文件的命令是一层一层往下读的,有执行的顺序
如果执行时没有发生改变,就会拿缓存内容,这样可以提高效率
如果有行命令读取到,你做出了改变,那么接下来的执行工作,都会重新执行
所以你会发现为什么会单独先COPY package.json,再安装包,再复制其他的源文件
如果你只是改变了app.js内容,包没有发生改变,如果Copy . .在前,它发现app.js发生改变后,就会把后面每层的指令重新执行,那么每次都会重新安装包,效率就会下降。
好了理解这个后,我们再来说卷,如果说把本地下的文件都放在卷里,卷再映射给容器,卷更新,容器 就会更新

需要映射到容器文件的绝对路径 -v Volumes 代表着卷
--rm 移除,当停止容器时,就删除容器等
-v C:\Users\luback\Desktop\docker\api:/app
// Dockerfile的绝对路径的上级:容器工作路径
-v /app/node_modules
//创建并映射容器的匿名卷

那么了解了我们要做什么,我们开始从小在VScode的终端上执行下面操作
创建新镜像
javascript
docker build -t myapp2:nodemon .
创建容器,卷
记得把4000:4000映射的容器都给停掉,不然会报错
查看docker容器正在运行的进程
docker ps
根据容器ID/名称停止容器进程
docker stop ContainerID/Names
javascript
docker run --name myapp_c_nodemon -p 4000:4000 --rm -v C:\Users\luback\Desktop\docker\api:/app -v /app/node_modules myapp2:nodemon

欧克,我们再试一下,修改app.js文件内容,发现及时更新了

对镜像,容器,卷有了认知后,你会发现命令好长
docker run --name myapp_c_nodemon -p 4000:4000 --rm -v C:\Users\luback\Desktop\docker\api:/app -v /app/node_modules myapp2:nodemon
又是创建容器,又是映射端口,又是创建卷,又是关联相关镜像
如果是有前后端的项目,多个项目想集中使用,有没有什么办法,使这些指令过程变得更加便捷的方法?
Docker Compose
解决:数据库,前端,后端都启动,并且同时运行所有的容器,使他们相互通讯。
主要是使用.ymal文件,里面记录了所有项目的执行命令,在通过指令,快速实现互通
javascript
// 执行
docker-compose up
//停止容器,但是镜像和卷会保留下来
docker-compose down
//停止容器且删除镜像
docker-compose down --rmi
// 删除由docker comppose创建的镜像,卷
docker-compose down --rmi all -v
多项目运行,举个例子
我的后端项目接口是localhost:4000
我的前端React项目启动访问地址是localhost:3000
那么如何使用docker,使双方进行通信。
如图的效果


javascript
一:在api文件放入命名为docker的文件夹下(随便取的名字)
二:在docker文件的终端上创建一个react前端项目
npx create-react-app my_book
三:修改my_book src/App.js内容,主要是访问后端接口的数据并展示在页面
import { useEffect, useState } from 'react'
import './App.css';
function App() {
const [blogs, setBlogs] = useState([])
useEffect(() => {
fetch('http://localhost:4000/')
.then(res => res.json())
.then(data => setBlogs(data))
}, [])
return (
<div className="App">
<header className="App-header">
<h1>all blogs</h1>
{blogs && blogs.map(blog => (
<div key={blog.id}>{blog.title}</div>
))}
</header>
</div>
);
}
export default App;
四:在my_app下创建Dockerfile文件,这样my_app才能构建镜像,.dockerignore也加上
Dockerfile内容
FROM node:20.19.6
WORKDIR /app
COPY package.json .
RUN npm config set registry https://registry.npmmirror.com
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
.dockerignore内容
node_modules
欧克,api后端项目有了,my_book前端项目有了,在docker文件下,创建一个
docker-compose.yaml 文件
下面是举例的目录

注意首行缩进
services: // 就是执行的命令
api://镜像名称
build: ./api //Dockerfile所在路径,创建镜像
container_name: api_c //创建容器的名称
ports:
- '4000:4000' //映射端口
volumes: //创建卷
./api:/app
/app/node_modules
mybook:
build: ./my_book
container_name: mybook_c
ports:
- '3000:3000'
stdin_open: true //这两个是开启交互模式
tty: true //意味着访问后端地址可以是 localhost:4000
你会不会思考,前端更新,会不会也需要想后端项目一样去做卷映射?但是为什么这里的执行命令没有做这步操作
你可以尝试一下,前端页面不会更新的,那是因为,前端项目会打包,浏览器只会读取成静态资源,所有你就算用卷做映射也没用,因为并浏览器读取的静态资源没有变
然后只能每次重新打镜像容器做更新了
如果测试想前端页面做到后端热更新这种,额,我目前还不了解这一块,如果你知道,欢迎留言,相互学习。
好了,回归正题,以下就是docker-compose.yaml的内容
javascript
version: '3.8'
services:
api:
build: ./api
container_name: api_c
ports:
- '4000:4000'
volumes:
- ./api:/app
- /app/node_modules
myblog:
build: ./myblog
container_name: myblog_c
ports:
- '3000:3000'
stdin_open: true
tty: true
在docke文件下执行VScode的终端命令
docker-compose up
这一行代码,就可以把构建镜像,容器,卷,端口等系列操作做完
javascript
docker-compose up // 执行
docker-compose down //停止容器,但是镜像和卷会保留下来
docker-compose down --rmi //停止容器且删除镜像
docker-compose down --rmi all -v // 删除由docker comppose创建的镜像,卷
你可以去浏览器访问一下
localhost:4000
localhost:3000值得注意的是,你可以通过 docker ps 确保运行的容器没有占用端口
最后
如何在Docker Hub上分享镜像
这地址有着很多免费的镜像源,想了解一下,就看看
https://hub.docker.com/
https://hub.docker.com/


javascript
#创建镜像,注意Vscode执行命令的路径在项目的Docderfile路径上
C:\Users\luback\Desktop\docker\api
docker build -t thnetninjauk/myapi .
#登录 dorcker 账号
docker login
#推送到线上
docker push thnetninjauk/myapi


javascript
#可以尝试拉去这个项目的镜像
docker pull thnetninjauk/myapi
常用指令
javascript
构建镜像 -t tag的意思 . 是Dockerfile的根路径
docker build -t myapp2 .
查看所有镜像
docker images
运行某镜像,使用image的name,并创建使用容器命名为myapp_c1 run有重新创建的意思
docker run --name myapp_c1 myapp (到这一步,是无法在本地查看的,因为没有指明映射端口)
查看docker容器正在运行的进程
docker ps
根据容器ID/名称停止容器进程
docker stop ContainerID/Names
本地映射端口到容器端口,-p port端口的意思
-d detached 分离模式 独立于后台运行长期驻守的服务,不受其他干扰
docker run --name myapp_c1 -p 4000:4000 -d myapp
查看所有的容器 -a all的意思
docker ps -a
重启某容器,这次不需要映射端口,在创建的时候已经是做好了的
docker start ContainerID/Names
删除镜像 rm remove的意思 -f force强制清除的意思,尽管有容器存在,也会被删除
docker image rm ImageName/ID
docker image rm ImageName/ID -f
删除容器,还可以连着删
docker container rm ContainerID/Name ContainerID2/Name2
删除Docker所有内容,删除所有镜像,删除所有容器,删除所有的卷
docker system prune -a
为镜像添加版本
docker build -t myapp:v1
运行指定版本的镜像
docker run --name myapp_c -p 4000:4000 myapp:v1
--rm 的标志意味着停止容器的时候就删除它
docker run --name myapp_c_nodemon -p 4000:4000 --rm myapp:nodemon
需要映射到容器文件的绝对路径 -v 代表着卷
docker run --name myapp_c_nodemon -p 4000:4000 --rm -v C:\Users\luback\Desktop\docker\api:/app myapp:nodemon
本地不需要的卷,容器却需要的卷,用匿名卷映射给容器
docker run --name myapp_c_nodemon -p 4000:4000 --rm -v C:\Users\luback\Desktop\docker\api:/app -v /app/node_modules myapp:nodemon
