docker从入门到实战

275 阅读14分钟

docker 定义

Docker 是一个开源的平台,允许开发者自动化应用程序的部署、扩展和管理。它使用操作系统级别的虚拟化技术,将应用程序及其所有依赖项封装到一个名为容器的轻量级、可移植的运行环境中。

简单来说就是可以把自己的项目部署到服务器,由于我们平时开发的项目都是在本地开发,只能自己可以调试和使用,如果需要供大家使用可以借助docker等容器来部署到服务器,目前很多公司都是使用docker gitlab k8s实现ci/cd(持续集成和持续部署),好处是方便更好的进行版本控制,一键发布各种环境等,实现持续集成和部署,这里的话我不具体说明了,大家有需要可以去了解其它博客和官方文档。接下来上正题......

本篇博客记录一下基于docker 和 nginx实现项目部署,我会从几个实际案例由浅到深带大家去了解docker怎么使用,包括重点的docker网络和存储等,以及我个人在使用中的一些难点和坑点。

了解docker的常用指令和原理

docker由三部分组成,镜像容器仓库。镜像可以当成一个模板,然后使用这个模板创建多个容器,每一个容器都是实例,而仓库的话就是管理和存储这写镜像的地方,类似于前端框架的脚手架,我可以使用脚手架创建多个不同的项目,然后存储到自己的网络仓库上。

镜像

镜像是一个只读的模板,包含运行某个应用程序所需的所有文件、依赖库和环境设置。镜像是构建容器的基础,举个例子,相当于前端框架里面的脚手架一样。

  1. 查找:docker search + 镜像名
  2. 下载:docker pull + 镜像名
  3. 列表:docker images + 镜像名
  4. 删除:docker rmi + id(docker rmi -f + id 可以强制删除)
  5. 提交镜像:docker commit -m '消息' + 容器名 + 消息
  6. 保存镜像:docker save -o image.tar(自定义镜像名.格式) + 容器名:v版本
  7. 加载镜像:docker load -i + 文件名,然后可以docker run + 镜像名 运行
  8. 发布镜像
    1. docker login 先登录,输入账号密码,
    2. docker tag + 本地名称 + 希望发布的文件名称 用户名+ 希望发布的名称 + tag
    3. docker push 推送,后面要接上名称 + tag,因为id会冲突, 推送完之后建议上传一个初始版本的,防止用户没有添加版本号而访问不到内容,只需把版本号改成latest推送即可,

容器

容器是镜像的一个实例,是在 Docker 环境中运行的独立、可执行的软件包。容器与容器间是隔离的,容器提供了一个隔离的运行环境,包含了应用程序的代码、运行时、系统工具、库等。

  1. 运行: docker run
    1. docker run [OPTIONS] IMAGE [COMMAND] [ARG...] OPTIONS: 参数类
      1. -d:后台启动
      2. --name:自定义容器名称
      3. -p:端口映射,映射后可在浏览器访问,本地端口在后,映射在后,由于容器之间是相互隔离的,所以开启多个服务不会出现端口冲突,只是映射的端口不能重复就行
      4. --network:要加入的自定义网络名称,通常用于网络之间建立桥接,实现反向代理
  2. 查看: docker ps 查看已经运行的容器状态
    1. docker ps -a 查看全部容器,包括未启用的
  3. 停止:docker stop
  4. 启动:docker start
  5. 重启:docker restart
  6. 状态:docker stats
  7. 日志:docker logs
  8. 进入:docker exec
    1. -it 表示需要交互 (docker exec -it + name + 目录)可进入容器编辑文件,修改完成输入exit退出容器 docker exec -it mydumi /bin/bash
  9. 删除:docker rm

存储

docker的存储功能通常实现数据的持久化等功能,常见的例子就是需要持久化配置文件,因为每次启动容器都是一个崭新的容器,容器被删除的话,对应的容器数据也会同样被删除,有时候希望把容器内部数据存储起来的话得借助docker数据卷挂载映射的功能,将宿主机上的目录挂载到容器里面。

  1. -v: 数据卷映射,-v /user/container:/root/usr /宿主机目录 : /容器目录,数据卷可以实现数据持久化,具体可以根据需求挂载不同的目录,可以通过-v 数据卷1 -v 数据卷2 挂载多个数据卷
  2. volume: 数据卷
    1. docker volume create + 名字 创建一个数据卷
    2. docker volume rm + 名字 删除一个数据卷
    3. docker volume ls 查看全部数据卷

网络

网络是容器化应用程序中的重要组成部分,它决定了容器之间以及容器与外部世界之间的通信方式,由于容器与容器都是相互隔离的,但是他们的网络是互通的,每个容器启动时内部都一个默认的自增ip和端口,容器与容器间可以通过这个ip来相互访问,另外一个就是自定义网络,这种方法也可以实现容器之间的相互建立连接,玩法很多。

  1. docker network create + 名称 创建一个网络
  2. docker network rm + 网络id 删除一个网络
  3. docker network ls 查看全部网络
  4. docker network connect + 网络名称 + 容器id 连接容器到网络
  5. docker network disconnect + 网络名称 + 容器id 容器断开网络
  6. docker network inspect + 网络名称 查看网络详细

启动的时候提到了端口映射(容器run 命令 -p 是参数),就是把容器内部的端口映射到宿主机上供外部访问,如果不添加-p的话就是使用默认端口80

Dockerfile

Dockerfile文件的作用是定义了docker容器每一层的命令,用于构建docker镜像,在项目中根目录下新建一个Dockerfile文件,然后就可以通过docker build命令制作成镜像了。

  1. FROM: 继承的镜像 FROM nginx
  2. COPY: 拷贝 COPY ./index/app
  3. WORKDIR: 指定工作路径 WORKDIR /index
  4. RUN: 编译打包阶段允许的命令 RUN npm install
  5. EXPOSE: 暴露端口 EXPOSE 3000
  6. CMD: 容器运行阶段运行命令 CMD npm run start

需求一:部署一个自己的前端项目

由于我是mac系统,可以通过官网直接下载docker客户端使用,window用户的话可以去查找博客和询问ai怎么安装docker。

作为初学者来说,可以不使用服务器,通过本地localhost一样实现效果,如果有服务器的话更好了,

  1. 首先第一步准备自己的前端项目,可以随便编写一个todolist,然后在根目录编写一个Dockerfile文件,上面已经提到了Dockerfile文件是干嘛用的了。这里的Dockerfile内容第一阶段使用node镜像安装依赖等,第2️二阶段使用nginx镜像来部署我们的项目,nginx是一款开源的高性能 web 服务器和反向代理服务器,当然还有ApacheCaddy等,都可以部署我们的前端项目。
# 第一阶段:构建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;"]

  1. 接下来使用docker build构建成docker镜像,命令:docker build -t myapp . 注意最后有个点,这个意思是当前整个目录下,这个myapp是你起的容器名称。
  2. 构建成功后可以测试一下是否构建成功,在终端运行docker ps,就可以看到构建出来的myapp镜像了。
  3. 然后使用docker基于这个myapp镜像启动容器,docker run -d --name myapp -p 80:80 myapp,如果返回一段id就说明启动成功 就可以使用在浏览器打开查看了,http://localhost:80 , 默认80端口可以省略,直接访问 http://localhost 就行,就会出现项目内容了,恭喜你成功使用docker运行了第一个项目。
  4. 如果你有云服务的话那么你可以把自己的镜像上传到云服务,然后通过docker部署。
  5. 前期准备:我这里推荐centos7来作为系统环境,这里系统根据你熟悉的来,我们进入自己的服务器,安装最新docker, 拉去nginx镜像,具体操作可以根据不同云服务来,可以去百度某某云安装docker,很简单的就是几行命令,如果你已经完成了准备工作,那么可以开始下一步了。
  6. 可以通过scp把镜像压缩包上传到服务器,先把本地镜像制作成压缩文件。
  7. 使用docker save把本地项目制作成.tar镜像文件 docker save -o myapp(自定义容器名).tar react-app-todo
  8. 使用scp myapp.tar root@:139.xxxx.xxx(服务器ip)/root/ 把压缩文件上传到服务器,这个/root 是根目录下,你可以在服务器创建一个目录专门存储镜像,输入命令后然后输入你的云服务器密码,密码正常后就可以看到在上传中了。
  9. 接下来就是在服务器加载docker镜像了, 使用命令 docker load -i myapp.tar加载镜像,可以在服务上通过docker images 查看是否加载成功。
  10. 接下来就是在服务器适应myapp运行容器了, docker run -d --name myapp -p 80:80 myapp,出现一行id就是启动成功,可以通过docker ps查看是否在运行,如果在运行就可以在浏览器使用http://你的服务器ip:80 访问这个项目了。

注意事项:

  1. 编写Dockerfile文件的时候要注意实际构建的项目在哪个目录下,不同的脚手架可能打包后的静态目录名不一样,需要根据真实情况替换。
  2. 要注意服务器防火墙是否关闭,是否设置安全组开放80端口,如果第一个项目没有弄出来就不要进行下一步了,可以留言或者私信遇到了啥问题了。

需求二:不使用80端口部署多个项目

如果你已经操作到了这里,说明你已经入门docker了,接下来就是通过不同端口部署多个项目,这里说一下为什么不使用80端口,因为80端口的话直接映射就好了,没啥难度。

  1. 准备两个项目,如上操作制作成两个不同的镜像。
  2. 基于两个镜像启动两个容器,假如镜像a和镜像b。
 docker run -d --name appa -p 3000:3000 a //项目a
 docker run -d --name appb -p 3001:3001 b //项目b
 
 直接访问的话是访问不了的
  1. 现在就是通过配置容器网络来实现开放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 // 退出容器后就可以访问了

注意事项:

  1. 配置网络代理时需要注意符号等,尽量不要出现符号错误。
  2. 命令要敲正确,最好是可以脱离可视化程序在终端命令中直接全程通过命令操作。
  3. 如果是在服务器部署的话也是一样的,要注意的是开放网络安全组。
  4. 如果这一步没有成功那就不要进行下个项目,下个项目要涉及到反向代理。

需求三:由于通过端口访问项目实在是不雅,现在需要通过/app(路径)来访问项目

实现思路:通过一拖二(一个代理容器,两个项目)的形式代理两个项目,然后在location上面使用指定的路径来代理到两个项目。

  1. 制作镜像还是上述流程,可以在需求二上面实现当前这个需求,当然,多敲一遍可以增加记忆,取决于个人。
  2. 我们在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 访问项目二

注意事项及需求难点

  1. 要熟悉docker网络,了解容器与容器之间的关系,需要知道什么情况下容器间可以相互连接。
  2. 配置文件比较多,其实很多都是语法糖,具体要了解每一行配置是什么意思可以查阅官方文档和咨询ai。
  3. 出现问题可以自己排查,通过docker logs 查看日志,docker inspect 查看容器详情等方法查找问题。
  4. 对于单页面应用,切换了路由之后刷新页面就会出现 404,原因是新的url和代理的不一样,所以需要处理,方法就是在app中 location 里面加上 try_files $uri $uri/ /index.html; 表示路径访问不到时定向到根目录

有问题及时私信和留言,我会逐一解决,我带大家用实现需求的方式学习docker,这样可以上手的更快,增加记忆,只有你自己真正的把学习知识当成需求来弄的话你会事半功倍。祝大家都能如愿所尝!

完结