docker 定义
Docker 是一个开源的平台,允许开发者自动化应用程序的部署、扩展和管理。它使用操作系统级别的虚拟化技术,将应用程序及其所有依赖项封装到一个名为容器的轻量级、可移植的运行环境中。
简单来说就是可以把自己的项目部署到服务器,由于我们平时开发的项目都是在本地开发,只能自己可以调试和使用,如果需要供大家使用可以借助docker等容器来部署到服务器,目前很多公司都是使用docker gitlab k8s
实现ci/cd(持续集成和持续部署),好处是方便更好的进行版本控制,一键发布各种环境等,实现持续集成和部署,这里的话我不具体说明了,大家有需要可以去了解其它博客和官方文档。接下来上正题......
本篇博客记录一下基于docker 和 nginx
实现项目部署,我会从几个实际案例由浅到深带大家去了解docker怎么使用,包括重点的docker网络和存储等,以及我个人在使用中的一些难点和坑点。
了解docker的常用指令和原理
docker由三部分组成,镜像
,容器
,仓库
。镜像可以当成一个模板,然后使用这个模板创建多个容器,每一个容器都是实例,而仓库的话就是管理和存储这写镜像的地方,类似于前端框架的脚手架,我可以使用脚手架创建多个不同的项目,然后存储到自己的网络仓库上。
镜像
镜像是一个只读的模板,包含运行某个应用程序所需的所有文件、依赖库和环境设置。镜像是构建容器的基础,举个例子,相当于前端框架里面的脚手架一样。
- 查找:
docker search
+ 镜像名 - 下载:
docker pull
+ 镜像名 - 列表:
docker images
+ 镜像名 - 删除:
docker rmi + id(docker rmi -f + id
可以强制删除) - 提交镜像:
docker commit -m '消息' + 容器名 + 消息
- 保存镜像:
docker save -o image.tar(自定义镜像名.格式) + 容器名:v版本
- 加载镜像:
docker load -i + 文件名,然后可以docker run + 镜像名
运行 - 发布镜像
docker login
先登录,输入账号密码,docker tag
+ 本地名称 + 希望发布的文件名称 用户名+ 希望发布的名称 + tagdocker push
推送,后面要接上名称 + tag,因为id会冲突, 推送完之后建议上传一个初始版本的,防止用户没有添加版本号而访问不到内容,只需把版本号改成latest推送即可,
容器
容器是镜像的一个实例,是在 Docker 环境中运行的独立、可执行的软件包。容器与容器间是隔离的,容器提供了一个隔离的运行环境,包含了应用程序的代码、运行时、系统工具、库等。
- 运行:
docker run
- docker run [OPTIONS] IMAGE [COMMAND] [ARG...] OPTIONS: 参数类
-d
:后台启动--name
:自定义容器名称-p
:端口映射,映射后可在浏览器访问,本地端口在后,映射在后,由于容器之间是相互隔离的,所以开启多个服务不会出现端口冲突,只是映射的端口不能重复就行--network
:要加入的自定义网络名称,通常用于网络之间建立桥接,实现反向代理
- docker run [OPTIONS] IMAGE [COMMAND] [ARG...] OPTIONS: 参数类
- 查看:
docker ps
查看已经运行的容器状态- docker ps -a 查看全部容器,包括未启用的
- 停止:
docker stop
- 启动:
docker start
- 重启:
docker restart
- 状态:
docker stats
- 日志:
docker logs
- 进入:
docker exec
- -it 表示需要交互 (docker exec -it + name + 目录)可进入容器编辑文件,修改完成输入exit退出容器
docker exec -it mydumi /bin/bash
- -it 表示需要交互 (docker exec -it + name + 目录)可进入容器编辑文件,修改完成输入exit退出容器
- 删除:
docker rm
存储
docker的存储功能通常实现数据的持久化等功能,常见的例子就是需要持久化配置文件,因为每次启动容器都是一个崭新的容器,容器被删除的话,对应的容器数据也会同样被删除,有时候希望把容器内部数据存储起来的话得借助docker数据卷挂载映射的功能,将宿主机上的目录挂载到容器里面。
- -v: 数据卷映射,
-v /user/container:/root/usr /宿主机目录 : /容器目录
,数据卷可以实现数据持久化,具体可以根据需求挂载不同的目录,可以通过-v 数据卷1 -v 数据卷2 挂载多个数据卷 volume
: 数据卷docker volume create + 名字
创建一个数据卷docker volume rm + 名字
删除一个数据卷docker volume ls
查看全部数据卷
网络
网络是容器化应用程序中的重要组成部分,它决定了容器之间以及容器与外部世界之间的通信方式,由于容器与容器都是相互隔离的,但是他们的网络是互通的,每个容器启动时内部都一个默认的自增ip和端口,容器与容器间可以通过这个ip来相互访问,另外一个就是自定义网络,这种方法也可以实现容器之间的相互建立连接,玩法很多。
docker network create + 名称
创建一个网络docker network rm + 网络id
删除一个网络docker network ls
查看全部网络docker network connect + 网络名称 + 容器id
连接容器到网络docker network disconnect + 网络名称 + 容器id
容器断开网络docker network inspect + 网络名称
查看网络详细
启动的时候提到了端口映射(容器run 命令 -p 是参数),就是把容器内部的端口映射到宿主机上供外部访问,如果不添加-p的话就是使用默认端口80
Dockerfile
Dockerfile文件的作用是定义了docker容器每一层的命令,用于构建docker镜像,在项目中根目录下新建一个Dockerfile文件,然后就可以通过docker build命令制作成镜像了。
FROM
: 继承的镜像 FROM nginxCOPY
: 拷贝 COPY ./index/appWORKDIR
: 指定工作路径 WORKDIR /indexRUN
: 编译打包阶段允许的命令 RUN npm installEXPOSE
: 暴露端口 EXPOSE 3000CMD
: 容器运行阶段运行命令 CMD npm run start
需求一:部署一个自己的前端项目
由于我是mac系统,可以通过官网直接下载docker客户端使用,window用户的话可以去查找博客和询问ai怎么安装docker。
作为初学者来说,可以不使用服务器,通过本地localhost一样实现效果,如果有服务器的话更好了,
- 首先第一步准备自己的前端项目,可以随便编写一个todolist,然后在根目录编写一个Dockerfile文件,上面已经提到了Dockerfile文件是干嘛用的了。这里的Dockerfile内容第一阶段使用node镜像安装依赖等,第2️二阶段使用nginx镜像来部署我们的项目,nginx是一款开源的高性能 web 服务器和反向代理服务器,当然还有Apache和Caddy等,都可以部署我们的前端项目。
# 第一阶段:构建React应用
FROM node:16 AS build
# 设置工作目录
WORKDIR /app
# 复制 package.json 和 package-lock.json
COPY package*.json ./
# 安装依赖
RUN npm install
# 复制项目文件
COPY . .
# 构建React应用
RUN npm run build
# 第二阶段:部署到Nginx
FROM nginx:latest
# 删除默认的Nginx页面
RUN rm -rf /usr/share/nginx/html/*
# 将构建好的React应用文件复制到Nginx的html目录中
COPY --from=build /app/dist /usr/share/nginx/html
# 暴露端口 80
EXPOSE 80
# 启动Nginx
CMD ["nginx", "-g", "daemon off;"]
- 接下来使用docker build构建成docker镜像,命令:
docker build -t myapp .
注意最后有个点,这个意思是当前整个目录下,这个myapp是你起的容器名称。 - 构建成功后可以测试一下是否构建成功,在终端运行
docker ps
,就可以看到构建出来的myapp镜像了。 - 然后使用docker基于这个myapp镜像启动容器,
docker run -d --name myapp -p 80:80 myapp
,如果返回一段id就说明启动成功 就可以使用在浏览器打开查看了,http://localhost:80 , 默认80端口可以省略,直接访问 http://localhost 就行,就会出现项目内容了,恭喜你成功使用docker运行了第一个项目。 - 如果你有云服务的话那么你可以把自己的镜像上传到云服务,然后通过docker部署。
- 前期准备:我这里推荐
centos7
来作为系统环境,这里系统根据你熟悉的来,我们进入自己的服务器,安装最新docker, 拉去nginx
镜像,具体操作可以根据不同云服务来,可以去百度某某云安装docker,很简单的就是几行命令,如果你已经完成了准备工作,那么可以开始下一步了。 - 可以通过
scp
把镜像压缩包上传到服务器,先把本地镜像制作成压缩文件。 - 使用docker save把本地项目制作成.tar镜像文件
docker save -o myapp(自定义容器名).tar react-app-todo
。 - 使用
scp myapp.tar root@:139.xxxx.xxx(服务器ip)/root/
把压缩文件上传到服务器,这个/root 是根目录下,你可以在服务器创建一个目录专门存储镜像,输入命令后然后输入你的云服务器密码,密码正常后就可以看到在上传中了。 - 接下来就是在服务器加载docker镜像了, 使用命令
docker load -i myapp.tar
加载镜像,可以在服务上通过docker images
查看是否加载成功。 - 接下来就是在服务器适应
myapp
运行容器了,docker run -d --name myapp -p 80:80 myapp
,出现一行id就是启动成功,可以通过docker ps查看是否在运行,如果在运行就可以在浏览器使用http://你的服务器ip:80 访问这个项目了。
注意事项:
- 编写Dockerfile文件的时候要注意实际构建的项目在哪个目录下,不同的脚手架可能打包后的静态目录名不一样,需要根据真实情况替换。
- 要注意服务器防火墙是否关闭,是否设置安全组开放80端口,如果第一个项目没有弄出来就不要进行下一步了,可以留言或者私信遇到了啥问题了。
需求二:不使用80端口部署多个项目
如果你已经操作到了这里,说明你已经入门docker了,接下来就是通过不同端口部署多个项目,这里说一下为什么不使用80端口,因为80端口的话直接映射就好了,没啥难度。
- 准备两个项目,如上操作制作成两个不同的镜像。
- 基于两个镜像启动两个容器,假如镜像a和镜像b。
docker run -d --name appa -p 3000:3000 a //项目a
docker run -d --name appb -p 3001:3001 b //项目b
直接访问的话是访问不了的
- 现在就是通过配置容器网络来实现开放3000和3001端口,
// 进入容器
docker exec -it appa /bin/bash
// 使用 vim 或者 nano 编辑 /etc/nginx/nginx.conf 配置文件
vim /etc/nginx/nginx.conf
// 对了 再此之前得安装一下vim或者nano 任一即可
// 在容器内部使用apt工具 先输入apt update 完成后 输入apt install vim 等待安装完成即可
//然后继续使用命令 vim /etc/nginx/nginx.conf 在http 模块下增加配置
// 项目a
server {
listen 3000;
server_name localhost; // 如果是服务器直接写域名或者ip
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
< ----- >
// 项目b
server {
listen 3001;
server_name localhost; // 如果是服务器直接写域名或者ip
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
// 修改好之后就保存退出文件 还需要的就是检验配置是否正确 在容器内部输入以下命令
nginx -t // 校验配置是否有错误
nginx -s reload // 更新配置
exit // 退出容器后就可以访问了
注意事项:
- 配置网络代理时需要注意符号等,尽量不要出现符号错误。
- 命令要敲正确,最好是可以脱离可视化程序在终端命令中直接全程通过命令操作。
- 如果是在服务器部署的话也是一样的,要注意的是开放网络安全组。
- 如果这一步没有成功那就不要进行下个项目,下个项目要涉及到反向代理。
需求三:由于通过端口访问项目实在是不雅,现在需要通过/app(路径)来访问项目
实现思路:通过一拖二(一个代理容器,两个项目)的形式代理两个项目,然后在location上面使用指定的路径来代理到两个项目。
- 制作镜像还是上述流程,可以在需求二上面实现当前这个需求,当然,多敲一遍可以增加记忆,取决于个人。
- 我们在docker里面拉取nginx镜像,然后通过nginx 启动一个默认容器设置反向代理,由于80端口可以默认隐藏起来,这个nginx容器我们就使用80端口,实现反向代理的难点是需要把这几个项目都放在同一个网络下,使容器之间可以相互访问。
docker pull nginx // 拉取ngixn官方镜像
docker network create myproxy // 自定义网路建立容器之间的桥接
dcoker run -d --name nginx -p 80:80 --network myporxy nginx // 创建代理容器
dcoker run -d --name appa -p 3000:3000 --network myporxy a // 创建容器a
dcoker run -d --name appb -p 3001:3001 --network myporxy a // 创建容器b
dokcer exec -it nginx /bin/bash // 进入代理容器
vim /etc/nginx/conf.d/default.conf // 这个配置文件专门用于设置网络代理等
// 配置文件内容
server {
listen 80;
listen 3000;
listen 3001;
listen 3000:3000;
listen 3001:3001;
server_name localhost; # 替换为你的域名或 IP
location /appa/ {
proxy_pass http://appa:3000/; # 这里使用容器名的原因是因为容器都在一个网络下面
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /appb/ {
proxy_pass http://appb:3001/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 错误处理
error_page 404 /404.html;
location = /40x.html { }
error_page 500 502 503 504 /50x.html;
location = /50x.html { }
}
// 以上就是配置内容
接下来还需要在appa appb内设置一下监听3000和3001端口,步骤同需求二
// 然后就可以通过http://localhost/appa 访问项目一
// http://localhost/appb 访问项目二
注意事项及需求难点
- 要熟悉docker网络,了解容器与容器之间的关系,需要知道什么情况下容器间可以相互连接。
- 配置文件比较多,其实很多都是语法糖,具体要了解每一行配置是什么意思可以查阅官方文档和咨询ai。
- 出现问题可以自己排查,通过
docker logs
查看日志,docker inspect
查看容器详情等方法查找问题。 - 对于单页面应用,切换了路由之后刷新页面就会出现 404,原因是新的url和代理的不一样,所以需要处理,方法就是在app中
location
里面加上try_files $uri $uri/ /index.html;
表示路径访问不到时定向到根目录
有问题及时私信和留言,我会逐一解决,我带大家用实现需求的方式学习docker,这样可以上手的更快,增加记忆,只有你自己真正的把学习知识当成需求来弄的话你会事半功倍。祝大家都能如愿所尝!