banner
NEWS LETTER

运维

Scroll down

[TOC]



网络协议

Web协议通信原理

在开始学web服务器之前,需要先理解web通信协议,才能够更好的吸收其中精华。

我们平时浏览网页的时候,会打开浏览器,输入网址后按下回车键,然后就会显示出你想要浏览的内容。在这个看似简单的用户行为背后,到底隐藏了些什么呢?

  • 浏览器本身是一个客户端,当你输入URL的时候,首先浏览器会去请求DNS服务器,通过DNS获取相应的域名对应的IP

  • 然后通过IP地址找到IP对应的服务器后,要求建立TCP连接

  • 等浏览器发送完HTTP Request(请求)包后,服务器接收到请求包之后才开始处理请求包

  • 服务器调用自身服务,返回HTTP Response(响应)包;

  • 客户端收到来自服务器的响应后开始渲染这个Response包里的主体(body),等收到全部的内容随后断开与该服务器之间的TCP连接。

1
2
3
4
5
	部署web服务器提供网站功能,最基本的内容展示html内容,指的是静态数据,动态数据。
静态数据:
png、css、js、gif、mp4。
动态数据:
从mysql等数据库中读取出来的数据。

image-20240805173032474

image-20240805173924900

一个Web服务器也被称为HTTP服务器,它通过HTTP协议与客户端通信。
这个客户端通常指的是Web浏览器。

web服务器工作原理

Web服务器的工作原理可以简单地归纳为:

  • 客户端通过TCP/IP协议建立到服务器的TCP连接
  • 客户端向服务器发送HTTP协议请求包,请求服务器里的资源文档
  • 服务器向客户端发送HTTP协议应答包,如果请求的资源包含有动态语言的内容,那么服务器会调用动态语言的解释引擎负责处理”动态内容”,并将处理得到的数据返回给客户端
  • 客户端与服务器断开。由客户端解释HTML文档,在客户端屏幕上渲染图形结果

客户端请求到达服务端流程

  • 当客户端拿到服务端域名对应的ip后,浏览器会以一个随机端口(1024<随机端口<65535)向服务器的web程序(nginx、apache)的80端口发起tcp连接请求。
  • 该请求经过复杂的网络环境后到达服务端,进入到服务器的对应的网卡,再进入到linux内核的tcplip协议栈,一层一层的解开数据包,甚至经过防火墙,最终到达nginx程序,确认tcp/ip连接。
  • 确认tcp连接之后,客户端继续发起http请求,如常见的get、post请求方法。

小结

因此你会发现,web通信原理中,主要分两块协议的建立:

  • tcp/ip
  • HTTP

image-20240805174800248

Tcp和HTTP协议

TCPIIP协议
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

简单说就是TCP协议就是控制数据包在传过过程中的规范格式。

IP是Internet Protocol(网际互连协议)的缩写,是TCP/IP体系中的网络层协议。

设计IP的目的是提高网络的可扩展性:
一是解决互联网问题,实现大规模、异构网络的互联互通;
二是分割顶层网络应用和底层网络技术之间的耦合关系,以利于两者的独立发展。

根据端到端的设计原则,IP只为主机提供一种无连接、不可靠的、尽力而为的数据包传输服务。

  • TCP/IP协议指的不仅仅是tcp、和ip这两个协议。
  • 而是由FTP、SMTP、TCP、UDP、IP等各种协议组成的协议簇,但是TCPIP最最具有代表性,因此俗称TCP/IP协议。

image-20240805175818345

image-20240805175905131

OSI七层模型

image-20240805175938880

image-20240805180502231


抓包分析三次握手

image-20240805180727622

讲解TCP三次握手和四次握手之前,我们先了解一下TCP和UDP这两个重量级的传输层协议。

用户数据报协议UDP (User Datagram Protocol) :

  • UDP在传送数据之前不需要先建立连接,远程主机在收到UDP报文后,不需要给出任何确认。
  • 虽然UDP不提供可靠交付,但在某些情况下UDP确是一种最有效的工作方式(一般用于即时通信),比如: QQ语音、QQ视频、直播等等

**传输控制协议TCP **(Transmission Control Protocol) :

  • TCP提供面向连接的服务。在传送数据之前必须先建立连接,数据传送结束后要释放连接。
  • TCP不提供广播或多播服务。由于TCP要提供可靠的,面向连接的传输服务(TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、流量控制、拥塞控制机制,在数据传完后,还会四次挥手断开连接用来节约系统资源),这不仅使协议数据单元的首部增大很多,还要占用许多处理机资源。
  • TCP一般用于文件传输、发送和接收邮件、远程登录等场景

抓包查看三次握手(真理)
鼎鼎大名的tcp/ip协议的三次握手、也就是关乎于syn、syn/ack、ack 三个标识位
tcp是一个及其复杂的知识点,这里咱们通过wireshare来尽量的通过实践,理解tcp的序列号和确认号的关系。
TCP协议正在报文中使用了大量的标志位来控制tcp的连接状态;

最主要的三个标志位如下

  • SYN、创建一个连接
  • FIN、终止一个连接
  • ACK、确认接收到了数据

第一次握手(SYN)
打开tcp层的数据包信息,展开TCP的标志位区域(flag),可以看到tcp用到的所有标志位。

第二次握手(SYN、ACK)

第三次握手(ACK)
第二个数据包。第二次握手,服务端给与的ack标志位,用于确认收到客户端的SYN数据包

为什么是【SYN.ACH】?因为表明服务端也是想要建立TCP连接

图解三次握手流程


[!CAUTION]

抓包工具,查看TCP/IP的三次握手

经典问题:你了解TCP/IP的三次握手,四次挥手嘛?

具体数据包的报文格式,暂时不用过多去琢磨;
什么时候需要琢磨数据包的序列号,只有当你在生产环境下,遇见的及其棘手的问题,比如一些数据不同步,交易数据丢失等极端情况,需要去抓取数据包,逐个分析,数据包的完整性,序列号。
目前只需要大致了解数据包的类型,以及作用即可;

image-20240805181502056

image-20240805182752406

image-20240805183003068

image-20240805183032502

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1、抓取ssh登录的数据包,查看 tcp/ip协议基础之上的 SSH应用层协议
登录web-7机器
windows(本机:192.168.xx.xx)

Vment8(nat虚拟网卡:10.0.0.1)

web-7(虚拟机nat:10.0.0.7)

两个ip:port的数据包交互
10.0.0.1:随机端口 > 10.0.0.7:22

wireshark大鲨鱼抓包工具查看ssh登录数据包收发情况

在window中使用 shell工具连接 web-7
ssh远程连接,也是建立在TCP/IP的三次握手之上


2、查看网站访问的数据包,基于 tcp/ip基础之上的HTTP应用层协议
由于捕捉的是vment8的网卡流量,检测http协议,只能给这个网段内的机器,发http请求。

这里需要注意,如果多次访问nginx,可能回抓不到默认的tcp三次握手。
TCP三次握手是用于首次连接的请求。
访问nginx页面的请求,第一次以及建立好连接,默认会有一个保持连接后续的请求。便不会再继续tcp三次握手,而是直接发送http请求,和nginx服务器已经建立好tcp连接了。

使用另一个浏览器如firefox、chrome,去重新建立tcp连接

image-20240805183630852

image-20240805183907434


抓包分析四次挥手

理论知识,确实重要。但得先记住理论的流程,再去动手实践,得出真知。

1
1、先建立ssh连接,然后退出ssh会话,查看是否发出tcp/ip的四次挥手,四个数据包。

image-20240807155726965

TCP连接状态报文

面试时候拿出来背诵即可

1
2
3
4
5
6
7
8
9
10
1 cLOSE 		没有任何连接状态
2 LISTEN 监听状态、等待来自TCP的连接请求
3 SYN-SENT 发送连接请求后,等待对方确认
4 SYN-RECEIVED 收到、发送一个连接请求后,等待对方确认
5 ESTABLISHED 代表传输连接已经建立,双方进入数据传输状态
6 FIN-WAIT-1 主动关闭1,主机已发送关闭连接请求,等待对方确认
7 FIN-wAIT-2 主动关闭2,主机己收到对方关闭传输连接的确认,等待对方发送关闭传输连接请求
8 TIME-WAIT 完成双向传输连接关闭,等待服务端最终确认
9 CLOSE-wAIT 被动关闭连接,收到对方发来的关闭连接请求,且已经确认。1l LAST-ACK被动关闭,等待最后一个关闭传输连接的确认。
10 CLOSE 双方同时确认了关闭传输连接

常用的熟知端口号

应用程序 FTP TFTP TELNET SMTP DNS HTTP SSH MYSQL
熟知端口 21,20 69 23 25 53 80 22 3306
传输层协议 TCP UDP TCP TCP UDP TCP TCP TCP

套接字socket学习

TCP与套接字(socket)

通过前面对OSI网络模型的学习,我们知道了在网络层中可以通过IP地址实现两个机器之间的通信。

但是这并不具体,因为真正进行通信的实体是在主机中的进程,是一个主机汇总的一个进程与另一个主机中的一个进程在交换数据。

什么是socket套接字

1
2
3
4
5
6
7
任何,两个机器的连接,指的是TCP/IP协议的连接,本质上是两个socket的通信

socket套接字就是 ip+port的具象化

比如部署nginx服务,运行在10.0.0.7:80 端口,这就是一个socket通过本地去访问这个socket,浏览器随机指定的端口,发出请求

10.0.0.1:61145 这也是一个socket

本地套接字

比如单机LNMP,应用程序在机器内部内存之间数据交互

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
套接字存在的意义,在于让两端进行数据交互,数据传输
php
10.0.0.7:9000
mysql(10.0.0.7:3306 这是一个ip:port形式的 socket)
mysql -uroot -p -h10.0.0.7

另一个形式是,也就是文件形式的本地socket
(因为本地内存文件socket形式,比网络socket传输效率更高)
在后面高级部分,python的高级部署

socket的都是具象化证明服务的确运行了,可以对外提供访问了,可以交互了
mysql运行在 /tmp/mysql.sock

这个文件存在,则表示mysql运行
你可通过这个 /tmp/mysql.sock去登录mysql 数据库

此时暂时部署形式都是 网络socket形式,将程序部署在 ip:port的形式。


网络套接字

部署LNMP,这是不同机器之间的远程访问,就是远程socket

1
2
3
4
5
6
7
比如http协议的交互

本质上是主机与web-7的交互以及nginx反向代理、远程部署
10.0.0.1:61145 > 10.0.0.7:80

后端机器
远程socket通信,部署后端机器(lb-5:10.0.0.5:80)服务,与远程的另一个服务通过网络通信(web-7:10.0.0.7:80) 就是socket通信

HTTP协议基础篇

这些内容都是部署网站,后续的nginx高级知识点的必备内容。

HTTP协议概述

HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TCP)。

  • 通过使用网页浏览器、网络爬虫或者其它的工真,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)
  • 我们称这个客户端为用户代理程序(user agent) .
  • 应答的服务器上存储着一些资源,比如HTML文件和图像。我们称这个应答服务器为源服务器(origin server)。
  • 在用户代理和源服务器中间可能存在多个“中间层”,比如代理服务器.网关或者隧道(tunnel)。
  • 通常,由HTTP客户端发起一个请求,创建一个到服务器指定端口(默认是80端口)的TCP连接。
  • HTTP服务器则在那个端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态,比如“HTTP/1.1 200 OK”,以及返回的内容,如请求的文件、错误消息、或者其它信息。

HTTP工作原理

HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。

HTTP协议采用了 请求/响应 模型。

客户端向服务器发送一个请求报文。请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行 作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。

HTTP请求/响应的步骤

image-20240808143917243

1
2
1、客户端连接到web服务器
一个HTTP客户端,通常是浏览器,与web服务器的HTTP端口(默认为80)建立一个TCP套接字连接。例如:http://www.yuncha

例如:在浏览器地址栏键入URL.按下回车之后会经历以下流程:

  1. 浏览器向DNS 服务器请求解析该URL中的域名所对应的IP地址;
  2. 解析出IP地址后,根据该IP地址和默认端口80。和服务器建立TCP连接;
  3. 浏览器发出读取文件(URL 中域名后面部分对应的文件)的HTTP请求,该请求报文作为TCP三次握手的第三个报文的数据发送给服务器:
  4. 服务器对浏览器请求作出啊应,并把对应的html文本发送给浏览器;
  5. 浏览器将该 html文本并显示内容:
  6. 释放 TCP连接;
    总结
    HTTP协议是基手TCP/IP协议之上的应用层协议。

请求-响应的模式

HTTP协议规定,请求从客户端发出,最后服务器端响应该请求并返回。
换句话说,肯定是先从客户端开始建立通信的.服务器端在没有接收到请求之前不会发送响应。

image-20240808144617342

无状态

HTTP是一种不保存状态,即无状态(stateless)协议。

HTTP协议自身不对请求和响应之间的通信状态进行保存。

也就是说在HTTP这个级别.协议对于发送过的请求或响应都不做持久化处理。

image-20240808144653616

这种无状态设计是为了保证HTTP可以处理大量的请求响应;

http默认不会记住每一次连接的状态信息,下一次都会认为是一个新的客户端连接

1
2
3
4
5
6
7
8
9
比如你去找到禁用chrome浏览器的cookie功能。

在禁用了这个功能后;

每次登入一个需要验证身份的网站都需要重新登录;
比如淘宝网,每一次打开页面网页都提示你需要重新登录;

这是网站为了解决无状态实现的技术。

但是如果HTTP完全无状态,你登录到淘宝网后,点击电子产品的跳转链接,它又提示你需要登录了,这就是一个无状态的实际效果。
因此此类需要保持用户身份信息的业务,必须要保存用户的状态。
于是引入了Cookie技术,能够保持用户的身份信息,下一次客户端发出请求,服务端能记忆客户端的身份。

没有cookie:

image-20240808152646938

有Cookie:

image-20240808153120250

HTTP请求方法

请求方法

定义client发送给server的请求是什么类型:

  • get获取静态数据(查看一张图片)
  • post发送提交一些数据(登录表单,提交账户密码数据)
  • …等等方法

HTTP请求方法

HTTP请求方法是客户端(如浏览器)与服务器之间进行通信时,用于指定请求类型或所需操作的一组指令。根据HTTP标准,HTTP请求可以使用多种请求方法,这些方法定义了客户端与服务器之间如何交互以及服务器应该如何响应。以下是HTTP请求方法的一些主要类型及其简要说明:

HTTP/1.0定义的请求方法

  1. GET

- 用途:用于请求服务器发送资源(如网页、图片等)。GET请求通常用于获取数据,而不对数据进行修改。
- 特点:请求的参数附加在URL后面,多个参数之间用&分隔。由于URL长度有限制(通常在1024K左右),因此GET请求不适合传输大量数据。

  1. POST

- 用途:用于向服务器提交数据,以创建或更新资源。POST请求常用于提交表单数据或上传文件。
- 特点:请求的数据包含在请求体中,而不是附加在URL后面。POST请求对数据的长度没有限制(尽管实际限制取决于服务器和浏览器的配置),且相对GET请求更安全(因为数据不会显示在URL中)。

  1. HEAD

- 用途:类似于GET请求,但服务器仅返回响应头信息,而不返回实际的内容体。HEAD请求通常用于检查资源的元数据(如资源是否存在、响应头的信息等)。

HTTP/1.1新增的请求方法

  1. PUT
  • 用途:用于向服务器发送数据以更新现有资源。如果指定的资源不存在,则服务器可能会根据请求创建新资源。PUT请求通常被视为幂等的,即多次执行相同的PUT请求应该产生相同的结果。
  1. DELETE
  • 用途:请求服务器删除指定的资源。DELETE请求通常包含要删除的资源标识符(如URL)。
  1. OPTIONS

- 用途:用于获取服务器针对特定资源所支持的HTTP请求方法。OPTIONS请求也可以用于检查服务器的性能或进行跨域资源共享(CORS)的预检请求。

  1. PATCH
  • 用途:用于对资源进行部分修改。与PUT请求不同,PATCH请求只更改资源的部分内容,而不是替换整个资源。
  1. TRACE
  • 用途:用于回显服务器收到的请求,主要用于测试和诊断。TRACE请求允许客户端查看请求在服务器中的处理路径。
  1. CONNECT
  • 用途:HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。CONNECT方法通常用于HTTPS连接,客户端可以通过该隧道发送加密的数据。

其他注意事项

- 安全性:GET请求由于将参数附加在URL上,因此其安全性较低。POST、PUT、DELETE和PATCH等请求方法由于将数据传输在请求体中,因此相对更安全。然而,如果不使用HTTPS,这些请求的数据仍然可能被截获。
- 缓存和书签:GET请求通常可以被缓存和收藏为书签,而POST等请求则不行。
- 数据格式:POST和PUT请求可以传输的数据格式多样,包括表单数据(application/x-www-form-urlencoded)、文件上传(multipart/form-data)等。而GET请求通常只能传输ASCII字符,并且数据会附加在URL上。

请求、响应报文查看

请求和响应报文是HTTP协议中客户端与服务器通信的基本单位。它们包含了请求和响应的详细信息,包括请求方法、资源路径、协议版本、头信息(如Cookie、User-Agent等)以及可选的消息体(如表单数据、文件等)。

要查看请求和响应报文,可以通过以下几种方式:

1. 使用网络调试工具

大多数现代浏览器都内置了网络调试工具,如Chrome浏览器的开发者工具中的“Network”标签页。通过打开该页面,你可以看到所有网络请求的详细信息,包括请求方法、URL、状态码、响应时间、头信息以及预览和原始消息体内容。

2. 使用命令行工具(如curl)

如果你正在使用命令行界面或脚本进行HTTP请求,可以使用像curl这样的命令行工具来查看请求和响应的详细内容。例如:

1
curl -v -X GET "http://example.com"

上面的命令将执行一个GET请求到http://example.com,并显示详细的请求过程和响应内容。其中-v参数用于显示详细的输出信息,而-X参数用于指定HTTP请求方法。

3. 通过编程方式分析网络流量(如Wireshark)

对于网络专业人士或安全专家来说,他们可能会使用像Wireshark这样的网络协议分析器来查看和分析网络流量。这些工具能够捕获所有流经网络的流量并展示每个数据包的详细信息,包括TCP/IP头、HTTP头、SSL/TLS握手等。通过这些工具可以更深入地了解网络通信的细节。

4. 使用专门的抓包软件(如Fiddler、Charles Proxy)

Fiddler、Charles Proxy等代理软件可以捕获电脑或移动设备上的网络流量,并提供一个友好的用户界面来查看和编辑请求与响应的内容。这些工具通常被用来调试Web应用、测试API接口或者监控网络性能问题等场景中。

当使用这些工具时,通常需要设置浏览器或其他客户端应用程序以通过代理服务器进行通信。然后你就可以在抓包软件中看到所有的HTTP请求和响应了。

响应状态码

1
2
3
4
5
6
7
8
9
1、当你 client 发出 get 请求 获取一个图片信息 发出requests
2、server 响应结果,nginx找到这个图片,返回给用户 response
响应信息是有对应的状态码的。

20x系列,表示请求,响应正常解析
30x系列,表示本次请求,被转发到另一个服务器上了
40x系列,联示client客户端访问的url有问题,该资源不存在
50x系列,表示网站的服务端出错了(php-fpm)没有运行
……

HTTP状态码是服务器在响应客户端请求时返回的一组标准代码,用来表示服务器对请求的处理结果。HTTP状态码由三位数字组成,根据其类别,可以分为五大类:

  1. 信息性状态码(100-199)

- 100 Continue:服务器已收到请求头,并且客户端应继续发送请求消息体。
- 101 Switching Protocols:服务器将根据其Upgrade头部字段中指定的协议切换通信协议。

  1. 成功状态码(200-299)

- 200 OK:请求已成功处理。
- 201 Created:请求已成功,且服务器已创建新的资源。
- 204 No Content:服务器成功处理了请求,但没有返回任何内容。
- 206 Partial Content:服务器成功处理了部分GET请求。

  1. 重定向状态码(300-399)

- 301 Moved Permanently:资源已被永久移动到新位置。
- 302 Found:资源已被临时移动到新位置。
- 304 Not Modified:未修改文档,服务器返回此状态码时,不会返回文档内容。

  1. 客户端错误状态码(400-499)

- 400 Bad Request:请求有语法错误或者无法满足。
- 401 Unauthorized:请求需要用户验证。
- 403 Forbidden:服务器拒绝该请求。
- 404 Not Found:无法找到指定位置的资源。这可能是最为人熟知的HTTP状态码之一,通常用于页面或资源不存在的场景中。

  1. 服务器错误状态码(500-599)

- 500 Internal Server Error:服务器遇到意外情况,无法完成请求。
- 503 Service Unavailable:服务器当前无法处理请求(可能是过载或停机维护)。

其他状态码如502 Bad Gateway、504 Gateway Timeout等也用于表示不同类型的服务器端问题。

每个状态码都包含了特定的含义和用途,它们帮助客户端理解发生了什么以及应该如何响应这些信息。例如当接收到一个404 Not Found的状态码时,浏览器通常会显示一个错误页面告知用户该资源不存在;而接收到200 OK则表示一切正常并会显示所请求的资源内容等。

URL格式

静态资源与web服务器

什么是动态资源

总结梳理(面试背诵)

这些更多的理论,很重要,必须你要做好笔记,去背,记忆,否则面试一问三不知。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1.dns篇
用户访问域名www.yuchaoit.cn

浏览器跳转

浏览器缓存(disk cache)

Hosts文件

本地DNS服务器(递归查询〉迭代查询)

client向server发起查询(递归查询)
server向server发起查询(迭代查询)


2.TCP/IP协议篇(三次握手)
client > SYN报文,请求连接
server > SYN,ACK报文响应client
client > ACK,建立连接


3.客户端发起http请求
-请求方法是什么、get、post、 delete
请求主机是什么、www.yuchaoit.cn
-请求资源是什么 、 http: //yuchaoit.cn:8090/upload/2022/05/xnip2022-05-01_16-32-30- b82235c9b62c42af8ea25e0313ca42f7.jpg
-请求端口是什么、默认http是80、https是443
-请求参数是什么、请求头部信息(资源类型、是否压缩、cookie、浏览器客户端等)
-请求信息最后的换行

网络设备协议

网络设备的相关协议以及基础配置,以及有线网络和无线网络的基本配置,是网络通信中的重要组成部分。以下是对这些内容的详细阐述:

一、网络设备的相关协议

网络设备的相关协议种类繁多,每种协议都有其特定的应用场景和功能。以下是一些常见的网络设备相关协议:

  1. TCP/IP协议:这是互联网通信的基础协议,包括IP协议和TCP协议等。IP协议负责给每台联网设备分配地址,确保数据能够准确发送到目标设备;TCP协议则负责发现传输中的问题,并请求重新传输,直到数据安全正确地到达目的地。

  2. HTTP协议:超文本传输协议,用于在客户端和服务器之间传输超文本文档,如网页内容。它是一种无状态协议,即每次请求都是独立的。

  3. FTP协议:文件传输协议,用于在客户端和服务器之间传输文件。它支持匿名登录和认证登录,允许用户上传和下载文件。

  4. SMTP协议:简单邮件传输协议,用于在邮件服务器之间传输电子邮件。它定义了邮件的传输规则,包括邮件格式和命令。

  5. POP3协议:邮局协议版本3,用于从邮件服务器上接收邮件。它允许用户在本地计算机上下载邮件,并在服务器上删除邮件。

  6. IMAP协议:互联网消息访问协议,用于在客户端和邮件服务器之间管理邮件。它允许用户在客户端上对邮件进行操作,而不需要将邮件下载到本地计算机。

  7. DNS协议:域名系统协议,用于将域名解析为IP地址。它将域名与IP地址进行映射,并将域名解析请求发送到适当的DNS服务器上。

  8. DHCP协议:动态主机配置协议,用于在局域网中为计算机分配IP地址。它可以自动分配IP地址、子网掩码和默认网关等网络配置信息。

  9. SSH协议:安全外壳协议,用于在不安全的网络上安全地远程登录和执行命令。它通过加密和身份验证机制保证通信的安全性。

这些协议共同构成了网络通信的基础,确保了数据能够在各种设备和系统之间安全、可靠地传输。

二、网络设备的基础配置

网络设备的基础配置通常包括以下几个方面:

  1. 管理IP设置:为网络设备分配一个管理IP地址,以便通过该地址远程访问和管理设备。

  2. 登录认证信息配置:设置设备的登录用户名和密码,以确保只有授权用户才能访问设备。

  3. 系统时间配置:设置设备的系统时间,以确保日志记录、时间同步等功能的准确性。

  4. 网络接口配置:根据网络拓扑结构和需求,配置设备的网络接口参数,如IP地址、子网掩码、网关等。

  5. 安全策略配置:配置设备的安全策略,如防火墙规则、访问控制列表(ACL)等,以保护网络免受攻击和未经授权的访问。

三、有线网络和无线网络的基本配置

有线网络的基本配置

  1. 物理连接:使用网线将计算机、交换机、路由器等设备连接起来,形成物理网络拓扑结构。

  2. IP地址分配:为网络中的每台设备分配一个唯一的IP地址,以便进行通信和数据传输。

  3. 子网划分:根据网络规模和需求,将网络划分为不同的子网,以提高网络性能和安全性。

  4. 网关和DNS设置:设置网络的默认网关和DNS服务器地址,以确保设备能够正确访问互联网和其他网络资源。

无线网络的基本配置

  1. SSID设置:为无线网络设置一个易于识别的名称(SSID),以便用户能够连接到网络。

  2. 安全加密:配置无线网络的安全加密方式(如WPA2-PSK),以保护网络通信的安全性和隐私性。

  3. 信道选择:选择一个合适的无线信道,以避免与其他无线网络的干扰。

  4. MAC地址过滤:配置MAC地址过滤规则,以限制只有特定设备才能连接到无线网络。

  5. DHCP服务:启用DHCP服务,为连接到无线网络的设备自动分配IP地址和其他网络配置信息。

通过以上配置,可以确保有线网络和无线网络能够正常运行,并满足用户的通信需求。


NGINX中间件(nginx)

nginx基础篇

web服务器介绍

Web服务器常指的是(world wide web,www)服务器、也是HTTP服务器,主要用于提供网上信息浏览。

我们大部分人接触互联网,都基本上是通过浏览器访问互联网中各种资源。

Web网络服务是一种被动访问的服务程序,即只有接收到互联网中其他主机发出的请求后才会响应,最终用于提供服务程序的Web服务器会通过HTTP(超文本传输协议)或HTTPS(安全超文本传输协议)把请求的内容传送给用户。

Unix和Linux平台下的常用Web服务器常见有:

  • Apache

  • Nginx

  • Lighttpd

  • Tomcat

  • IBM WebSphere

其中最为广泛的是Nginx,在Windows平台上最常用的是微软的IIS(Internet Information Server,互联网信息服务)是Windows系统中默认的Web服务程序。

Apache

image-20240808160835306

Apache是世界主流的Web服务器,世界上大多著名网站都是Apache搭建,优势在于开放源代码,开发维护团队强大、支持跨平台应用(Unix、Linux、Windows),强大的移植性等优点。
Apache属于重量级产品,功能以模块化定制,消耗内存较高,性能稍弱于其他轻量级Web服务器。

Lighttpd

image-20240808161005627

Lighttpd是一款高安全性、快速、且灵活的Web服务器产品,专为高性能环境而设计,相比其他Web服务器,内存占用量小,能够有效管理CPU负载,支持(FastCGI、SCGI,Auth,输出压缩,url重写,别名)等重要功能,是Nginx的重要对手之一。

Tomcat

image-20240808161044973

Tomcat是一个开源、运行基于Java的Web应用软件的容器,Tomcat Server根据servlet和JSP规范执行,但是Tomcat对于平台文件、高并发处理较弱。
要使用Tomcat需要对Java的应用部署有足够的了解。

IBM WebSphere

image-20240808161127729

WebSphere Applicaiton Server是一种强大的Web应用服务器,基于Java的应用环境、建立、部署和管理网站应用。

Microsoft IIS

image-20240808161224491

微软的IIS是一种灵活,安全易管理的Web服务器,从流媒体到Web应用程序,IIS提供了图形化的管理界面,用于配置和管理网络服务。
llS是一整套Web组件,包含了Web服务器,FTP服务器,SMTP服务器等常用的网页浏览、文件传输,邮件新闻等功能。
缺点是只能运行在Windows平台,还得购买商业化的操作系统。

Nginx

image-20240808161241700

Nginx是俄罗斯人lgor Sysoev(伊戈尔·塞索耶夫)开发的一款高性能的HTTP和反向代理服务器。
Nginx以高效的epollkqueueeventport作为网络IO模型,在高并发场景下,Nginx能够轻松支持5w并发连接数的响应,并且消耗的服务器内存、CPU等系统资源消耗却很低,运行非常稳定。

当前想让nginx支持5万并发,甚至百万级并发, 都是可以的,但需要做很多的优化工作,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
想至少支持5万并发的基本调优
1.服务器内存、CPU硬件支持,如8核16线程、32G内存的服务器
2.磁盘使用SSD、或者购买至少15000转的SAS企业级硬盘,做成RAID e
3.安装光纤网口
4.使用linux系统,如centos7,优化内核参数。对TCP连接的没置
5.优化nginx.conf中并发相关的参数。如等
worker_processes 16;
worker_connections 50000;
6.未完待续,nginx百万级并发优化篇的知识,.面试造火箭必备

优化工作思路为
1、优化硬件
2、优化linux内核
3、优化nginx配置文件此参数

国内著名站点,新浪博客、网易、淘宝、豆瓣、迅雷等大型网站都在使用Nginx作为web服务器或是反向代理服务器

异步非阻塞的高性能IO模型,任你有N个请求来临,nginx都临危不惧,从容解析响应。

为何选择nginx

image-20240808162241660

在互联网的快速普及,全球化、物联网的迅速发展,世界排名前3的分别是Apache、llS、Nginx,而Nginx一直在呈现增长趋势。

在线自动生成nginx配置文件

https://www.digitalocean.com/community/tools/nginx?global.app.lang=zhCN

可以自由选择所需的应用,生成nginx配置作为参考。

1
根据业务需求,自动生成复杂的nginx配置文件,提供参考。

nginx介绍

1、nginx是什么

1
2
3
4
5
1.nginx是一个高性能的HTTP服务器、反向代理服务器。
2.主要特点
-开源源代码
-高性能,并发性能、处理tcp连接性能极高
-可靠,服务稳定,得到了全世界的验证。

2、为什么选择nginx

1
2
3
1、每一家公司都会用到nginx
2、技术成熟,是企业最常见且必要的工具
3、适用于任意架构,单机环境、集群环境、微服务架构、云原生架构等

3、nginx重要特性

1
2
3
4
5
6
7
8
9
10
11
1.官网直接获取源码,免费用,讲道理这种高性能的软件,是很责的。
2.高性能,官网提供测试数据,性能残暴,1秒内能支持5万个tcp连接
3.消耗资源很低,有数据证明在生产环境下3万左右的并发tcp连接,开启10个nginx进程消耗不到150M。
4.有能力可以自己对nginx进行二次开发,如淘宝的tengine.
5.模块化管理,nginx有大量的模块(插件),运维根据公司的业务需求,按需编译安装设置即可。模块插件是为了提供额外的功能,如
- url重写,根据域名、url、客户端的不同,转发http请求到不同的机器
- https证书的支持
-支持静态资源压缩
-支持热部署、无须重启,更新配置文件
-支持二次开发新插件

4.企业用nginx做什么

1
2
3
4
5
1.提供静态页面展示,网页服务
2.提供多个网站、多个域名的网页服务
3.提供反向代理服务(结合动态应用程序)
4.提供简单资源下载服务(密码认证)
5.用户行为分析(日志功能)

nginx架构

nginx是多进程架构,当启动nginx会使用root创建master进程,由master进程创建多个worker进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@yuchao-tx-server ~]#ps -ef|grep nginx |grep -v grep

root 8382 1 0 5月01 ? 00:00:00 nginx: master process /usr/sbin/nginx
wordpre+ 16566 8382 0 5月01 ? 00:00:02 nginx: worker process

========================================================================
nginx运行后,有多少个干活的工人,多进程,调用多个cpu去解析用户的请求

在linux中进行多进程开发,开辟多个进程,调用多个cpu,当然也会消耗更多的机器资源,内存,cpu资源,给服务器带来更大的压力
-不是说进程越多,干活越快,合理的分配,才能达到最高效的处理效率

========================================================================
有一个屋子要装修,请1个工人干活,还是请5个工人干活,请500个工人干活


哪一个效率是最高的呢?最合适的?
========================================================================
关于nginx的优化设置,nginx默认应该启动多少个进程去工作呢?默认就是根据cpu的核数去设置进程数即可。

master进程

包工头进程,管理nginx的数据,创建worker工作进程。

master主进程原理

1
2
3
4
5
1.启动时检查nginx.conf是否正确,语法错误		--nginx -t 命令给master进程发送信号,去检测配置文件
2.根据配置文件的参数创建、且监控worker进程的数量和状态
3.监听socket,接收client发起的请求,然后worker竞争抢夺链接,获胜的可以处理且响应请求。 --master监听ip:port 0.0.0.0:80 被动的等待用户发来连接请求 nginx的主进程工作状态
4.接收管理员发送的管理nginx进程的信号,并且将信号通知到worker进程。 --接受、响应、处理管理员请求的worker进程,worker进程由master进程创建并管理。 如果使用 kill -9 nginx_pid(master)则将结束master以及下辖的所有的worker进程。
5.如果管理员发送了reload命令,则读取新配置文件,创建新的worker进程,结束旧的worker进程。

image-20240808164554338

worker进程

1
2
3
4
5
1.实际处理client网络请求的是worker
2.master根据nginx.conf决定worker的数量
3.有client用户请求到达时,worker之间进程竞争,获胜者和client建立连接且处理用户请求;
4.接收用户请求后,若需要代理转发给后端,则后端处理完毕后接收处理结果,再响应给用户
5.接收并处理master发来的进程信号,如启动、重启、重载、停止。

nginx进程通信

image-20240808165530845

nginx处理http请求流程

image-20240808165725075

nginx动手环节

nginx模块介绍

模块解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Nginx模块名称模块作用
ngx_http_access_module 四层基于工P的访问控制,可以通过匹配客户端源IP地址进行限剑
ngx_http_auth_basic_module 状态页,使用basic机制进行用户认证,在编译安装nginx的时候需要添加编译参数--wit..没写完
ngx_http_stub_status_module 状态统计模块
ngx_http_gzip_module 文件的压缩功能
ngx_http_gzip_static_module 静态压缩模块
ngx_http_ssl_module nginx 的https功能
ngx_http_rewrite_module 重定向模块,解析和处理rewrite请求
ngx_http_referer_module 防盗链功能,基于访问安全考虑
ngx_http_proxy_module 将客户端的请求以http协议转发至指定服务器进行处理
ngx_stream_proxy_module tcp负载,将客户端的请求以tcp协议转发至指定服务器处理
ngx_http_fastcgi_module 将客户端对php的请求以fastcgi协议转发至指定服务器助理
ngx_http_uwsgi_module 将客户端对Python的请求以uwsgi协议转发至指定服务器处理ngx_http_headers_module 可以实现对头部报文添加指定的key与值
ngx_http_upstream_module 负载均衡模块,提供服务器分组转发、权重分配、状态监测、调度算法等高级功能
ngx_stream_upstream_module 后端服务器分组转发、权重分配、状态监测、调度算法等高级功能
ngx_http_fastcgi_module 实现通过fastcgi协议将指定的客户端请求转发至php-fpm处理
ngx_http_flv_module 为flv伪流媒体服务端提供支持

模块官网

官网文档

https://nginx.org/en/docs/ 所有模块

1
nginx核心功能都以插件的方式,是否安装插件,开启该功能决定是否有各种功能。

image-20240808170952753

image-20240808171013648

nginx部署实践

nginx安装形式

  • 源代码编译安装,优点
    • 版本,可以获取官网最新的软件包,甚至最新测试版,都可以直接编译安装
    • 还有稳定版本
    • 自由定义,安装路径自由定义,·自由定义第三方插件
    • 缺点,安装步骤繁琐,耗时太长,看你要装多少个模块,编译添加的模块多,安装的就更久
  • rpm安装
    • 得提前准备好nginx本身的rpm包,以及相关依赖的rpm包
    • 用于离线安装nginx的环境
  • yum安装,你会用哪些形式的仓库?
    • 阿里云第三方仓库(centos-base.repo,epel.repo)
      • 这个其实都不靠谱。
    • 自建yum仓库(得提前准备好nginx本身的rpm包,以及相关依赖的rpm包)
    • nginx官网仓库(获取官网最新稳定版的yum源仓库)
      • yum一键安装,省心省事,版本也是有一定的保障的,rpm的安全性也是有保障的
1
2
3
官网yum仓库
源代码编译
离线的rpm安装(yum自建仓库)

编译安装

官网

1
https ://nginx.org/en/docs/configure.html

超哥的文档

1
2
3
4
5
6
7
8
9
1.创建用户
groupadd wwaw-g 666
useradd wwnw -u 666 -g 666 -H -s /sbin/nologin4
2.安装依赖环境
[root@web-9 ~]#yum install pcre pcre-devel openssl openssl-devel zlib zlib-devel gzip

3.下载源码https://nginx.org/download/,且解压缩
mkdir /yuchao-linux-data/ -p ;wget -o /yuchao-linux-data/nginx-1.19.0.tar.gz https://nginx.org/en/docs/configure.html
cd /yuchao-linux-data/ && tar -zxf nginx-1.19.8.tar-gz
开始编译,编译三部曲
1
2
3
4
5
6
7
8
0.查看稿译参数帮助信息
[rootiweb-9 lyuchso-linux-data/nginx-1.19.0]#. /configure --help
1.编译黎数
[rootipweb-9/yuchao . linux.data/nginx-1.19.0]#./configure .-usormw ..group-ww . .prefixm/opt/nginx-1....

2.编译安装
[rootiweb-9 / yuchao-linux-dats/nginx-1.19.0]#make && make install

创建软连接
1
2
3
[ root@web-9 /yuchao-linux-data/nginx-1.19.0]#ln -s /opt/nginx-1-19-0/ /opt/nginx[root@web-9 /yuchao-linux-data/nginx-1.19.0]#
[root@web-9 /yuchao-linux-data/nginx-1.19.0]#1l -d /opt/nginx
lrwxrwxrwx 1 root root 18 May 7 17:12 /opt/nginx -> /opt/nginx-1-19-0/
默认的nginx安装目录
1
2
[root@web-9 /yuchao-linux-data/nginx-1.19.e]#ls /opt/nginx	conf html logssbin

检查语法
1
2
3
[root@seb-9 /opt]#/opt/nginx/sbin/nginx -t
nginx: the configuration file /opt/nginx-1-19-0/conf/nginx.conf syntax is oknginx: configuration file /opt/nginx-1-19-8/conf/nginx.conf test is successful

启动nginx
1
2
3
4
5
6
7
先不着急配置PATH,先绝对路径启动
-c参数指定配置文件启动
[root@web-9 /opt]#/opt/nginx/sbin/nginx -c /opt/nginx/conf/nginx.conf[root@web-9 /opt]#ps -eflgrep nginx
root 5e9761 0 17:26 ? 00:00:00 nginx: master process /opt/nginx/sbin/nginx -c /opt...
wovow 5097750976 8 17:26 ? 00:00:00 nginx: worker process
root 5097945214 0 17:26 pts/0 00:00:00 grep --color=auto nginx

停止nginx
1
[root@web-9 /opt]#/opt/nginx/sbin/nginx -s stop
测试nginx
1
[root@web-9 /opt]#/opt/nginx/sbin/nginx -c /opt/nginx/conf/nginx.conf

image-20240808172651222

yum安装

epel源安装就不操作了
1
2
3
1.配置阿里云yum epel源

2 .yum install nginx -y
官网nginx源
1
2
官网文档
https://nginx.org/en/linux_packages.html#RHEL-Centos
于超文档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1.安装yum工具包
sudo yum install yum-utils
2.设置yum仓库
最新稳定版,线上用这个
cat > /etc/yum.repos.d/nginx.repo << "EOF"
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/gpgcheck=1
enabled=1
gpgkey=https: //nginx.org/keys/nginx_signing.key
EOF


最新开发版,但是并不稳定
cat > /etc/yum.repos.d/yuchao-nginx.repo << 'EOF"[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/gpgcheck=1
enabled=0
gpgkey=https:/ /nginx.org/keys/nginx_signing.keyEOF




nginx配置文件详解


nginx虚拟主机


web基础协议

Docker容器的部署与使用

同学们,在前两天我们学习了Linux操作系统的常见命令以及如何在Linux上部署一个单体项目。大家想一想自己最大的感受是什么?
我相信,除了个别天赋异禀的同学以外,大多数同学都会有相同的感受,那就是麻烦。核心体现在三点:

  • 命令太多了,记不住
  • 软件安装包名字复杂,不知道去哪里找
  • 安装和部署步骤复杂,容易出错

其实上述问题不仅仅是新手,即便是运维在安装、部署的时候一样会觉得麻烦、容易出错。
特别是我们即将进入微服务阶段学习,微服务项目动辄就是几十台、上百台服务需要部署,有些大型项目甚至达到数万台服务。而由于每台服务器的运行环境不同,你写好的安装流程、部署脚本并不一定在每个服务器都能正常运行,经常会出错。这就给系统的部署运维带来了很多困难。

那么,有没有一种技术能够避免部署对服务器环境的依赖,减少复杂的部署流程呢?
答案是肯定的,这就是我们今天要学习的Docker技术。你会发现,有了Docker以后项目的部署如丝般顺滑,大大减少了运维工作量。
即便你对Linux不熟悉,你也能轻松部署各种常见软件、Java项目

通过今天的学习,希望大家能达成下面的学习目标:

  • 能利用Docker部署常见软件
  • 能利用Docker打包并部署Java应用
  • 理解Docker数据卷的基本作用
  • 能看懂DockerCompose文件

1.快速入门

要想让Docker帮我们安装和部署软件,肯定要保证你的机器上有Docker. 由于大家的操作系统各不相同,安装方式也不同。为了便于大家学习,我们统一在CentOS的虚拟机中安装Docker,统一学习环境。

注意:使用MacBook的同学也请利用 VMwareFusion来安装虚拟机,并在虚拟机中学习Docker使用。

安装方式参考文档:《安装Docker》

1.1.部署Docker容器

安装docker :smile: :heart: :+1:

第一步,打开官网

地址:https://docs.docker.com/engine/install/centos/

第二布 在Linux系统上卸载旧版本

image-20240813151955787

1
2
3
4
5
6
7
8
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

image-20240813152010262

第三步 安装依赖

1
sudo yum install -y yum-utils

第四步 下载docker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
sudo yum-config-manager --add-repo
https://download.docker.com/linux/centos/docker-ce/repo

#官方链接,下载速度较慢

--------------------
#使用官方镜像文件报错
[root@node yum.repos.d]# yum-config-manager \
> --add-repo \
> https://download.docker.com/linux/centos/docker-ce.repo

已加载插件:fastestmirror
adding repo from: https://download.docker.com/linux/centos/docker-ce.repo
grabbing file https://download.docker.com/linux/centos/docker-ce.repo to /etc/yum.repos.d/docker-ce.repo
Could not fetch/save url https://download.docker.com/linux/centos/docker-ce.repo to file /etc/yum.repos.d/docker-ce.repo: [Errno 14] curl#7 - "Failed to connect to 2a03:2880:f127:83:face:b00c:0:25de: 网络不可达"

#出现访问失败的情况,配置aliyun的docker源或其他镜像源文件
#打开或创建yum源配置文件:
sudo vi /etc/yum.repos.d/docker-ce.repo

#docker源
[docker-ce-stable]
name=Docker CE Stable - $basearch
baseurl=https://mirrors.aliyun.com/docker-ce/linux/centos/7.9/x86_64/stable
enabled=1
gpgcheck=1
gpgkey=https://mirrors.aliyun.com/docker-ce/linux/centos/gpg


#保存文件并退出。
#清除缓存并重新尝试安装Docker:

yum clean all
yum makecache
----------------------------
yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin#跟着第五步继续

#查看docker是否安装成功
docker version

#启动Docker服务并设置开机自启

sudo systemctl start docker
sudo systemctl enable docker
————————————————
原文链接:https://blog.csdn.net/youziguo/article/details/139737449

第五步 安装docker

1
sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

image-20240813152258330

启动docker服务
1
sudo systemctl start docker
运行第一个docker案例
1
sudo docker run hello-world

第一个docker案例运行成功

image-20240813152407666

运行第一个案例失败

问题:连接失败,访问超时

还是镜像的问题,在/etc/docker/中创建一个daemon.json文件

1
2
3
4
5
6
7
8
9
10
11
12
13
cd /etc/docker
vi daemon.json

#在文件中添加内容
{
"registry-mirrors":[
"https://i1el1i0w.mirror.aliyuncs.com",
"https://hub-mirror.c.163.com",
"https://registry.aliyuncs.com",
"https://registry.docker-cn.com",
"https://docker.mirrors.ustc.edu.cn"
]
}
重启docker服务

重启守护进程

1
sudo systemctl daemon-reload

重启服务

1
sudo systemctl restart docker

如果需要强制启动Docker服务,可以先重置失败状态,然后再次尝试启动:

1
2
3
systemctl status docker.service
systemctl reset-failed docker.service
systemctl start docker.service
清理缓存
1
2
3
4
5
清理Docker缓存:运行 docker system prune --all 清除无用的缓存和镜像。

清除缓存:运行 docker pull --no-cache <image> 清除本地缓存,然后重新下载。

清理缓存并重新拉取:删除本地的镜像缓存 (docker rmi -f <image_name>),然后重新拉取。

问题描述:

在刚安装完docker,执行sudo docker pull hello-world 来测试 docker 是否安装成功时,报错:

1
2
3
4
5
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Retrying in 1 second
docker: error pulling image configuration: download failed after attempts=6: dial tcp [2a03:2880:f130:83:face:b00c:0:25de]:443: i/o timeout.
See 'docker run --help'.

原因及解决方案:

Docker 守护进程作为一个后台服务运行,它并不会读取或继承用户的 shell 环境变量(除非在启动时特别指定)。为了确保 Docker 在拉取镜像或进行其他网络通讯时能够通过代理服务器,必须在 Docker 的服务配置中设置这些代理变量。

如果你的代理服务器运行在本地机器上,并且使用的端口是 7890,你需要在 Docker 的配置文件中设置这些代理参数以确保 Docker 通过这个本地代理端口进行网络通信。以下是具体的配置文件内容:

创建或编辑 /etc/systemd/system/docker.service.d/http-proxy.conf 文件

首先,你需要确保有一个目录来存放 Docker 的服务配置。如果这个目录还不存在,你可以使用以下命令创建它:

1
sudo mkdir -p /etc/systemd/system/docker.service.d

接着,使用文本编辑器创建或编辑 http-proxy.conf 文件:

1
sudo vim /etc/systemd/system/docker.service.d/http-proxy.conf

在打开的编辑器中,输入以下内容:

1
2
3
4
[Service]
Environment="HTTP_PROXY=http://127.0.0.1:7890"
Environment="HTTPS_PROXY=http://127.0.0.1:7890"
Environment="NO_PROXY=localhost,127.0.0.1,docker-registry.somecorporation.com"

这里配置了 HTTP 和 HTTPS 代理都指向 127.0.0.17890 端口。NO_PROXY 环境变量指定了不需要通过代理访问的地址列表,通常包括本地地址和内部网络地址等。

保存并关闭编辑器

如果你使用的是 nano 编辑器,可以通过按 Ctrl+O 保存文件,然后按 Ctrl+X 退出编辑器。

如果用的是vim,则按esc键,然后输入:wq保存退出。

重新加载系统守护进程并重启 Docker 服务

为了让这些更改生效,你需要重新加载系统守护进程并重启 Docker 服务:

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker

这样,Docker 服务在启动和运行时就会使用本地的 7890 端口的代理服务器进行网络通信。可以通过尝试拉取一个 Docker 镜像来测试代理配置是否生效:

1
sudo docker run hello-world

如果这个命令能够成功执行,并且能够从 Docker Hub 拉取镜像,那么就说明你的代理配置是正确的。如果遇到连接问题,需要检查本地代理服务器是否正常运行在指定端口上。

成功运行的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:266b191e926f65542fa8daaec01a192c4d292bff79426f47300a046e1bc576fd
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/get-started/

1.2.部署MySQL

首先,我们利用Docker来安装一个MySQL软件,大家可以对比一下之前传统的安装方式,看看哪个效率更高一些。
如果是利用传统方式部署MySQL,大概的步骤有:

  • 搜索并下载MySQL安装包
  • 上传至Linux环境
  • 编译和配置环境
  • 安装

而使用Docker安装,仅仅需要一步即可,在命令行输入下面的命令(建议采用CV大法):

1
2
3
4
5
6
docker run -d \
--name mysql2 \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123 \
mysql

运行效果如图:
image.png
MySQL安装完毕!通过任意客户端工具即可连接到MySQL.

大家可以发现,当我们执行命令后,Docker做的第一件事情,是去自动搜索并下载了MySQL,然后会自动运行MySQL,我们完全不用插手,是不是非常方便。

而且,这种安装方式你完全不用考虑运行的操作系统环境,它不仅仅在CentOS系统是这样,在Ubuntu系统、macOS系统、甚至是装了WSL的Windows下,都可以使用这条命令来安装MySQL。
要知道,不同操作系统下其安装包、运行环境是都不相同的!如果是手动安装,必须手动解决安装包不同、环境不同的、配置不同的问题

而使用Docker,这些完全不用考虑。就是因为Docker会自动搜索并下载MySQL。注意:这里下载的不是安装包,而是镜像。镜像中不仅包含了MySQL本身,还包含了其运行所需要的环境、配置、系统级函数库。因此它在运行时就有自己独立的环境,就可以跨系统运行,也不需要手动再次配置环境了。这套独立运行的隔离环境我们称为容器
说明:

  • 镜像:英文是image
  • 容器:英文是container

因此,Docker安装软件的过程,就是自动搜索下载镜像,然后创建并运行容器的过程。

Docker会根据命令中的镜像名称自动搜索并下载镜像,那么问题来了,它是去哪里搜索和下载镜像的呢?这些镜像又是谁制作的呢?

Docker官方提供了一个专门管理、存储镜像的网站,并对外开放了镜像上传、下载的权利。Docker官方提供了一些基础镜像,然后各大软件公司又在基础镜像基础上,制作了自家软件的镜像,全部都存放在这个网站。这个网站就成了Docker镜像交流的社区:
Docker
基本上我们常用的各种软件都能在这个网站上找到,我们甚至可以自己制作镜像上传上去。

像这种提供存储、管理Docker镜像的服务器,被称为DockerRegistry,可以翻译为镜像仓库。DockerHub网站是官方仓库,阿里云、华为云会提供一些第三方仓库,我们也可以自己搭建私有的镜像仓库。
官方仓库在国外,下载速度较慢,一般我们都会使用第三方仓库提供的镜像加速功能,提高下载速度。而企业内部的机密项目,往往会采用私有镜像仓库。

总之,镜像的来源有两种:

  • 基于官方基础镜像自己制作
  • 直接去DockerRegistry下载

::: warning
总结一下
Docker本身包含一个后台服务,我们可以利用Docker命令告诉Docker服务,帮助我们快速部署指定的应用。Docker服务部署应用时,首先要去搜索并下载应用对应的镜像,然后根据镜像创建并运行容器,应用就部署完成了。
:::

用一幅图标示如下:

1.2.命令解读

利用Docker快速的安装了MySQL,非常的方便,不过我们执行的命令到底是什么意思呢?

1
2
3
4
5
6
docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123 \
mysql

解读:

  • docker run -d :创建并运行一个容器,-d则是让容器以后台进程运行
  • --name mysql : 给容器起个名字叫mysql,你可以叫别的
  • -p 3306:3306 : 设置端口映射。
    • 容器是隔离环境,外界不可访问。但是可以将宿主机端口映射容器内到端口,当访问宿主机指定端口时,就是在访问容器内的端口了。
    • 容器内端口往往是由容器内的进程决定,例如MySQL进程默认端口是3306,因此容器内端口一定是3306;而宿主机端口则可以任意指定,一般与容器内保持一致。
    • 格式: -p 宿主机端口:容器内端口,示例中就是将宿主机的3306映射到容器内的3306端口
  • -e TZ=Asia/Shanghai : 配置容器内进程运行时的一些参数
    • 格式:-e KEY=VALUE,KEY和VALUE都由容器内进程决定
    • 案例中,TZ=Asia/Shanghai是设置时区;MYSQL_ROOT_PASSWORD=123是设置MySQL默认密码
  • mysql : 设置镜像名称,Docker会根据这个名字搜索并下载镜像
    • 格式:REPOSITORY:TAG,例如mysql:8.0,其中REPOSITORY可以理解为镜像名,TAG是版本号
    • 在未指定TAG的情况下,默认是最新版本,也就是mysql:latest

镜像的名称不是随意的,而是要到DockerRegistry中寻找,镜像运行时的配置也不是随意的,要参考镜像的帮助文档,这些在DockerHub网站或者软件的官方网站中都能找到。

如果我们要安装其它软件,也可以到DockerRegistry中寻找对应的镜像名称和版本,阅读相关配置即可。

2.Docker基础

接下来,我们一起来学习Docker使用的一些基础知识,为将来部署项目打下基础。具体用法可以参考Docker官方文档:
Docker Docs: How to build, share, and run applications

2.1.常见命令

首先我们来学习Docker中的常见命令,可以参考官方文档:
Use the Docker command line

2.1.1.命令介绍

其中,比较常见的命令有:

命令 说明 文档地址
docker pull 拉取镜像 docker pull
docker push 推送镜像到DockerRegistry docker push
docker images 查看本地镜像 docker images
docker rmi 删除本地镜像 docker rmi
docker run 创建并运行容器(不能重复创建) docker run
docker stop 停止指定容器 docker stop
docker start 启动指定容器 docker start
docker restart 重新启动容器 docker restart
docker rm 删除指定容器 docker rm
docker ps 查看容器 docker ps
docker logs 查看容器运行日志 docker logs
docker exec 进入容器 docker exec
docker save 保存镜像到本地压缩文件 docker save
docker load 加载本地压缩文件到镜像 docker load
docker inspect 查看容器详细信息 docker inspect

用一副图来表示这些命令的关系:

补充:
默认情况下,每次重启虚拟机我们都需要手动启动Docker和Docker中的容器。通过命令可以实现开机自启:

1
2
3
4
5
6
# Docker开机自启
systemctl enable docker

# Docker容器开机自启
docker update --restart=always [容器名/容器id]

2.1.2.演示

教学环节说明:我们以Nginx为例给大家演示上述命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 第1步,去DockerHub查看nginx镜像仓库及相关信息

# 第2步,拉取Nginx镜像
docker pull nginx

# 第3步,查看镜像
docker images
# 结果如下:
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 16 months ago 141MB
mysql latest 3218b38490ce 17 months ago 516MB

# 第4步,创建并运行Nginx容器
docker run -d --name nginx -p 80:80 nginx

# 第5步,查看运行中容器
docker ps
# 也可以加格式化方式访问,格式会更加清爽
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"

# 第6步,访问网页,地址:http://虚拟机地址

# 第7步,停止容器
docker stop nginx

# 第8步,查看所有容器
docker ps -a --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"

# 第9步,再次启动nginx容器
docker start nginx

# 第10步,再次查看容器
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"

# 第11步,查看容器详细信息
docker inspect nginx

# 第12步,进入容器,查看容器内目录
docker exec -it nginx bash
# 或者,可以进入MySQL
docker exec -it mysql mysql -uroot -p

# 第13步,删除容器
docker rm nginx
# 发现无法删除,因为容器运行中,强制删除容器
docker rm -f nginx

2.1.3.命令别名

给常用Docker命令起别名,方便我们访问:

1
2
# 修改/root/.bashrc文件
vi /root/.bashrc

内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# .bashrc

# User specific aliases and functions

alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias dps='docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"'
alias dis='docker images'

# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi

然后,执行命令使别名生效

1
source /root/.bashrc

接下来,试试看新的命令吧。

2.2.数据卷

容器是隔离环境,容器内程序的文件、配置、运行时产生的数据都在容器内部,与容器绑定。但在某些情况下这回带来一些问题,大家思考一下:

  • 如果要升级MySQL版本,需要销毁旧容器,那么数据岂不是跟着被销毁了?
  • MySQL、Nginx容器运行后,如果我要修改其中的某些配置该怎么办?
  • 我想要让Nginx代理容器外部的静态资源怎么办?

可见,配置与数据存放在容器内是非常不方便的,而且容易出现问题。

因此,容器提供程序的运行环境,但是程序运行产生的数据、程序运行依赖的配置都应该与容器解耦

2.2.1.什么是数据卷

数据卷(volume)是一个虚拟目录,是容器内目录宿主机目录之间映射的桥梁。
以Nginx为例,我们知道Nginx中有两个关键的目录:

  • html:放置一些静态资源
  • conf:放置配置文件

如果我们要让Nginx代理我们的静态资源,最好是放到html目录;如果我们要修改Nginx的配置,最好是找到conf下的nginx.conf文件。
但遗憾的是,容器运行的Nginx所有的文件都在容器内部,读写都非常不方便。所以我们通常会利用数据卷将两个目录与宿主机目录关联,方便我们操作。如图:

在上图中:

  • 我们创建了两个数据卷:confhtml
  • Nginx容器内部的conf目录和html目录分别与两个数据卷关联。
  • 而数据卷conf和html分别指向了宿主机的/var/lib/docker/volumes/conf/_data目录和/var/lib/docker/volumes/html/_data目录

这样以来,容器内的confhtml目录就 与宿主机的confhtml目录关联起来,我们称为挂载。此时,我们操作宿主机的/var/lib/docker/volumes/html/_data就是在操作容器内的/usr/share/nginx/html/_data目录。只要我们将静态资源放入宿主机对应目录,就可以被Nginx代理了。

:::warning
小提示
/var/lib/docker/volumes这个目录就是默认的存放所有容器数据卷的目录,其下再根据数据卷名称创建新目录,格式为/数据卷名/_data

为什么不让容器目录直接指向宿主机目录呢

  • 因为直接指向宿主机目录就与宿主机强耦合了,如果切换了环境,宿主机目录就可能发生改变了。由于容器一旦创建,目录挂载就无法修改,这样容器就无法正常工作了。
  • 但是容器指向数据卷,一个逻辑名称,而数据卷再指向宿主机目录,就不存在强耦合。如果宿主机目录发生改变,只要改变数据卷与宿主机目录之间的映射关系即可。

不过,我们通过由于数据卷目录比较深,不好寻找,通常我们也允许让容器直接与宿主机目录挂载而不使用数据卷,具体参考2.2.3小节。
:::

2.2.2.数据卷命令

数据卷的相关命令有:

命令 说明 文档地址
docker volume create 创建数据卷 docker volume create
docker volume ls 查看所有数据卷 docker volume ls
docker volume rm 删除指定数据卷 docker volume prune
docker volume inspect 查看某个数据卷的详情 docker volume inspect
docker volume prune 清除数据卷 docker volume prune

注意:容器与数据卷的挂载要在创建容器时配置,对于创建好的容器,是不能设置数据卷的。而且创建容器的过程中,数据卷会自动创建

教学演示环节:演示一下nginx的html目录挂载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 1.首先创建容器并指定数据卷,注意通过 -v 参数来指定数据卷
docker run -d --name nginx -p 80:80 -v html:/usr/share/nginx/html nginx

# 2.然后查看数据卷
docker volume ls
# 结果
DRIVER VOLUME NAME
local 29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f
local html

# 3.查看数据卷详情
docker volume inspect html
# 结果
[
{
"CreatedAt": "2024-05-17T19:57:08+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/html/_data",
"Name": "html",
"Options": null,
"Scope": "local"
}
]

# 4.查看/var/lib/docker/volumes/html/_data目录
ll /var/lib/docker/volumes/html/_data
# 可以看到与nginx的html目录内容一样,结果如下:
总用量 8
-rw-r--r--. 1 root root 497 12月 28 2021 50x.html
-rw-r--r--. 1 root root 615 12月 28 2021 index.html

# 5.进入该目录,并随意修改index.html内容
cd /var/lib/docker/volumes/html/_data
vi index.html

# 6.打开页面,查看效果

# 7.进入容器内部,查看/usr/share/nginx/html目录内的文件是否变化
docker exec -it nginx bash

教学演示环节:演示一下MySQL的匿名数据卷

1
2
3
# 1.查看MySQL容器详细信息
docker inspect mysql
# 关注其中.Config.Volumes部分和.Mounts部分

我们关注两部分内容,第一是.Config.Volumes部分:

1
2
3
4
5
6
7
8
9
{
"Config": {
// ... 略
"Volumes": {
"/var/lib/mysql": {}
}
// ... 略
}
}

可以发现这个容器声明了一个本地目录,需要挂载数据卷,但是数据卷未定义。这就是匿名卷。
然后,我们再看结果中的.Mounts部分:

1
2
3
4
5
6
7
8
9
10
11
{
"Mounts": [
{
"Type": "volume",
"Name": "29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f",
"Source": "/var/lib/docker/volumes/29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f/_data",
"Destination": "/var/lib/mysql",
"Driver": "local",
}
]
}

可以发现,其中有几个关键属性:

  • Name:数据卷名称。由于定义容器未设置容器名,这里的就是匿名卷自动生成的名字,一串hash值。
  • Source:宿主机目录
  • Destination : 容器内的目录

上述配置是将容器内的/var/lib/mysql这个目录,与数据卷29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f挂载。于是在宿主机中就有了/var/lib/docker/volumes/29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f/_data这个目录。这就是匿名数据卷对应的目录,其使用方式与普通数据卷没有差别。

接下来,可以查看该目录下的MySQL的data文件:

1
ls -l /var/lib/docker/volumes/29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f/_data

:::warning
注意:每一个不同的镜像,将来创建容器后内部有哪些目录可以挂载,可以参考DockerHub对应的页面
:::

2.2.3.挂载本地目录或文件

可以发现,数据卷的目录结构较深,如果我们去操作数据卷目录会不太方便。在很多情况下,我们会直接将容器目录与宿主机指定目录挂载。挂载语法与数据卷类似:

1
2
3
4
# 挂载本地目录
-v 本地目录:容器内目录
# 挂载本地文件
-v 本地文件:容器内文件

:::warning
注意:本地目录或文件必须以 /./开头,如果直接以名字开头,会被识别为数据卷名而非本地目录名。
:::

例如:

1
2
-v mysql:/var/lib/mysql # 会被识别为一个数据卷叫mysql,运行时会自动创建这个数据卷
-v ./mysql:/var/lib/mysql # 会被识别为当前目录下的mysql目录,运行时如果不存在会创建目录

教学演示,删除并重新创建mysql容器,并完成本地目录挂载:

  • 挂载/root/mysql/data到容器内的/var/lib/mysql目录
  • 挂载/root/mysql/init到容器内的/docker-entrypoint-initdb.d目录(初始化的SQL脚本目录)
  • 挂载/root/mysql/conf到容器内的/etc/mysql/conf.d目录(这个是MySQL配置文件目录)

在课前资料中已经准备好了mysql的init目录和conf目录:
image.png
以及对应的初始化SQL脚本和配置文件:
image.png
image.png
其中,hm.cnf主要是配置了MySQL的默认编码,改为utf8mb4;而hmall.sql则是后面我们要用到的黑马商城项目的初始化SQL脚本。

我们直接将整个mysql目录上传至虚拟机的/root目录下:
image.png

接下来,我们演示本地目录挂载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 1.删除原来的MySQL容器
docker rm -f mysql

# 2.进入root目录
cd ~

# 3.创建并运行新mysql容器,挂载本地目录
docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123 \
-v ./mysql/data:/var/lib/mysql \
-v ./mysql/conf:/etc/mysql/conf.d \
-v ./mysql/init:/docker-entrypoint-initdb.d \
mysql

# 4.查看root目录,可以发现~/mysql/data目录已经自动创建好了
ls -l mysql
# 结果:
总用量 4
drwxr-xr-x. 2 root root 20 5月 19 15:11 conf
drwxr-xr-x. 7 polkitd root 4096 5月 19 15:11 data
drwxr-xr-x. 2 root root 23 5月 19 15:11 init

# 查看data目录,会发现里面有大量数据库数据,说明数据库完成了初始化
ls -l data

# 5.查看MySQL容器内数据
# 5.1.进入MySQL
docker exec -it mysql mysql -uroot -p123
# 5.2.查看编码表
show variables like "%char%";
# 5.3.结果,发现编码是utf8mb4没有问题
+--------------------------+--------------------------------+
| Variable_name | Value |
+--------------------------+--------------------------------+
| character_set_client | utf8mb4 |
| character_set_connection | utf8mb4 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8mb4 |
| character_set_server | utf8mb4 |
| character_set_system | utf8mb3 |
| character_sets_dir | /usr/share/mysql-8.0/charsets/ |
+--------------------------+--------------------------------+

# 6.查看数据
# 6.1.查看数据库
show databases;
# 结果,hmall是黑马商城数据库
+--------------------+
| Database |
+--------------------+
| hmall |
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
# 6.2.切换到hmall数据库
use hmall;
# 6.3.查看表
show tables;
# 结果:
+-----------------+
| Tables_in_hmall |
+-----------------+
| address |
| cart |
| item |
| order |
| order_detail |
| order_logistics |
| pay_order |
| user |
+-----------------+
# 6.4.查看address表数据
+----+---------+----------+--------+----------+-------------+---------------+-----------+------------+-------+
| id | user_id | province | city | town | mobile | street | contact | is_default | notes |
+----+---------+----------+--------+----------+-------------+---------------+-----------+------------+-------+
| 59 | 1 | 北京 | 北京 | 朝阳区 | 13900112222 | 金燕龙办公楼 | 李佳诚 | 0 | NULL |
| 60 | 1 | 北京 | 北京 | 朝阳区 | 13700221122 | 修正大厦 | 李佳红 | 0 | NULL |
| 61 | 1 | 上海 | 上海 | 浦东新区 | 13301212233 | 航头镇航头路 | 李佳星 | 1 | NULL |
| 63 | 1 | 广东 | 佛山 | 永春 | 13301212233 | 永春武馆 | 李晓龙 | 0 | NULL |
+----+---------+----------+--------+----------+-------------+---------------+-----------+------------+-------+
4 rows in set (0.00 sec)


2.3.自定义镜像

前面我们一直在使用别人准备好的镜像,那如果我要部署一个Java项目,把它打包为一个镜像该怎么做呢?

2.3.1.镜像结构

要想自己构建镜像,必须先了解镜像的结构。
之前我们说过,镜像之所以能让我们快速跨操作系统部署应用而忽略其运行环境、配置,就是因为镜像中包含了程序运行需要的系统函数库、环境、配置、依赖。
因此,自定义镜像本质就是依次准备好程序运行的基础环境、依赖、应用本身、运行配置等文件,并且打包而成。

举个例子,我们要从0部署一个Java应用,大概流程是这样:

  • 准备一个Linux环境(CentOS或者Ubuntu均可)
  • 安装并配置JDK
  • 上传Jar包
  • 运行jar包

那因此,我们打包镜像也是分成这么几步:

  • 准备Linux运行环境(java项目并不需要完整的操作系统,仅仅是基础运行环境即可)
  • 安装并配置JDK
  • 拷贝jar包
  • 配置启动脚本

上述步骤中的每一次操作其实都是在生产一些文件(系统运行环境、函数库、配置最终都是磁盘文件),所以镜像就是一堆文件的集合
但需要注意的是,镜像文件不是随意堆放的,而是按照操作的步骤分层叠加而成,每一层形成的文件都会单独打包并标记一个唯一id,称为Layer)。这样,如果我们构建时用到的某些层其他人已经制作过,就可以直接拷贝使用这些层,而不用重复制作。

例如,第一步中需要的Linux运行环境,通用性就很强,所以Docker官方就制作了这样的只包含Linux运行环境的镜像。我们在制作java镜像时,就无需重复制作,直接使用Docker官方提供的CentOS或Ubuntu镜像作为基础镜像。然后再搭建其它层即可,这样逐层搭建,最终整个Java项目的镜像结构如图所示:
image.png

2.3.2.Dockerfile

由于制作镜像的过程中,需要逐层处理和打包,比较复杂,所以Docker就提供了自动打包镜像的功能。我们只需要将打包的过程,每一层要做的事情用固定的语法写下来,交给Docker去执行即可。
而这种记录镜像结构的文件就称为Dockerfile,其对应的语法可以参考官方文档:
Dockerfile 官方文档

其中的语法比较多,比较常用的有:

指令 说明 示例
FROM 指定基础镜像 FROM centos:6
ENV 设置环境变量,可在后面指令使用 ENV key value
COPY 拷贝本地文件到镜像的指定目录 COPY ./xx.jar /tmp/app.jar
RUN 执行Linux的shell命令,一般是安装过程的命令 RUN yum install gcc
EXPOSE 指定容器运行时监听的端口,是给镜像使用者看的 EXPOSE 8080
ENTRYPOINT 镜像中应用的启动命令,容器运行时调用 ENTRYPOINT java -jar xx.jar

例如,要基于Ubuntu镜像来构建一个Java应用,其Dockerfile内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录、容器内时区
ENV JAVA_DIR=/usr/local
ENV TZ=Asia/Shanghai
# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar
# 设定时区
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 安装JDK
RUN cd $JAVA_DIR \
&& tar -xf ./jdk8.tar.gz \
&& mv ./jdk1.8.0_144 ./java8
# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin
# 指定项目监听的端口
EXPOSE 8080
# 入口,java项目的启动命令
ENTRYPOINT ["java", "-jar", "/app.jar"]

同学们思考一下:以后我们会有很多很多java项目需要打包为镜像,他们都需要Linux系统环境、JDK环境这两层,只有上面的3层不同(因为jar包不同)。如果每次制作java镜像都重复制作前两层镜像,是不是很麻烦。

所以,就有人提供了基础的系统加JDK环境,我们在此基础上制作java镜像,就可以省去JDK的配置了:

1
2
3
4
5
6
7
8
9
# 基础镜像
FROM openjdk:11.0-jre-buster
# 设定时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 拷贝jar包
COPY docker-demo.jar /app.jar
# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]

是不是简单多了。

2.3.3.构建镜像

当Dockerfile文件写好以后,就可以利用命令来构建镜像了。
在课前资料中,我们准备好了一个demo项目及对应的Dockerfile:
image.png
首先,我们将课前资料提供的docker-demo.jar包以及Dockerfile拷贝到虚拟机的/root/demo目录:
image.png

然后,执行命令,构建镜像:

1
2
3
4
# 进入镜像目录
cd /root/demo
# 开始构建
docker build -t docker-demo:1.0 .

命令说明:

  • docker build : 就是构建一个docker镜像
  • -t docker-demo:1.0-t参数是指定镜像的名称(repositorytag
  • . : 最后的点是指构建时Dockerfile所在路径,由于我们进入了demo目录,所以指定的是.代表当前目录,也可以直接指定Dockerfile目录:
1
2
# 直接指定Dockerfile目录
docker build -t docker-demo:1.0 /root/demo

结果:
image.png

查看镜像列表:

1
2
3
4
5
6
7
# 查看镜像列表:
docker images
# 结果
REPOSITORY TAG IMAGE ID CREATED SIZE
docker-demo 1.0 d6ab0b9e64b9 27 minutes ago 327MB
nginx latest 605c77e624dd 16 months ago 141MB
mysql latest 3218b38490ce 17 months ago 516MB

然后尝试运行该镜像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1.创建并运行容器
docker run -d --name dd -p 8080:8080 docker-demo:1.0
# 2.查看容器
dps
# 结果
CONTAINER ID IMAGE PORTS STATUS NAMES
78a000447b49 docker-demo:1.0 0.0.0.0:8080->8080/tcp, :::8090->8090/tcp Up 2 seconds dd
f63cfead8502 mysql 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp Up 2 hours mysql

# 3.访问
curl localhost:8080/hello/count
# 结果:
<h5>欢迎访问黑马商城, 这是您第1次访问<h5>

2.4.网络

上节课我们创建了一个Java项目的容器,而Java项目往往需要访问其它各种中间件,例如MySQL、Redis等。现在,我们的容器之间能否互相访问呢?我们来测试一下

首先,我们查看下MySQL容器的详细信息,重点关注其中的网络IP地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1.用基本命令,寻找Networks.bridge.IPAddress属性
docker inspect mysql
# 也可以使用format过滤结果
docker inspect --format='{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' mysql
# 得到IP地址如下:
172.17.0.2

# 2.然后通过命令进入dd容器
docker exec -it dd bash

# 3.在容器内,通过ping命令测试网络
ping 172.17.0.2
# 结果
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.053 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.058 ms

发现可以互联,没有问题。

但是,容器的网络IP其实是一个虚拟的IP,其值并不固定与某一个容器绑定,如果我们在开发时写死某个IP,而在部署时很可能MySQL容器的IP会发生变化,连接会失败。

所以,我们必须借助于docker的网络功能来解决这个问题,官方文档:
docker network
常见命令有:

命令 说明 文档地址
docker network create 创建一个网络 docker network create
docker network ls 查看所有网络 docker network ls
docker network rm 删除指定网络 docker network rm
docker network prune 清除未使用的网络 docker network prune
docker network connect 使指定容器连接加入某网络 docker network connect
docker network disconnect 使指定容器连接离开某网络 docker network disconnect
docker network inspect 查看网络详细信息 docker network inspect

教学演示:自定义网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 1.首先通过命令创建一个网络
docker network create hmall

# 2.然后查看网络
docker network ls
# 结果:
NETWORK ID NAME DRIVER SCOPE
639bc44d0a87 bridge bridge local
403f16ec62a2 hmall bridge local
0dc0f72a0fbb host host local
cd8d3e8df47b none null local
# 其中,除了hmall以外,其它都是默认的网络

# 3.让dd和mysql都加入该网络,注意,在加入网络时可以通过--alias给容器起别名
# 这样该网络内的其它容器可以用别名互相访问!
# 3.1.mysql容器,指定别名为db,另外每一个容器都有一个别名是容器名
docker network connect hmall mysql --alias db
# 3.2.db容器,也就是我们的java项目
docker network connect hmall dd

# 4.进入dd容器,尝试利用别名访问db
# 4.1.进入容器
docker exec -it dd bash
# 4.2.用db别名访问
ping db
# 结果
PING db (172.18.0.2) 56(84) bytes of data.
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=1 ttl=64 time=0.070 ms
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=2 ttl=64 time=0.056 ms
# 4.3.用容器名访问
ping mysql
# 结果:
PING mysql (172.18.0.2) 56(84) bytes of data.
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=1 ttl=64 time=0.044 ms
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=2 ttl=64 time=0.054 ms

OK,现在无需记住IP地址也可以实现容器互联了。

:::warning
总结

  • 在自定义网络中,可以给容器起多个别名,默认的别名是容器名本身
  • 在同一个自定义网络中的容器,可以通过别名互相访问
    :::

3.项目部署

好了,我们已经熟悉了Docker的基本用法,接下来可以尝试部署项目了。
在课前资料中已经提供了一个黑马商城项目给大家,如图:
image.png
image.png

项目说明:

  • hmall:商城的后端代码
  • hmall-portal:商城用户端的前端代码
  • hmall-admin:商城管理端的前端代码

部署的容器及端口说明:

项目 容器名 端口 备注
hmall hmall 8080 黑马商城后端API入口
hmall-portal nginx 18080 黑马商城用户端入口
hmall-admin 18081 黑马商城管理端入口
mysql mysql 3306 数据库

在正式部署前,我们先删除之前的nginx、dd两个容器:

1
docker rm -f nginx dd

mysql容器中已经准备好了商城的数据,所以就不再删除了。

3.1.部署Java项目

hmall项目是一个maven聚合项目,使用IDEA打开hmall项目,查看项目结构如图:
image.png
我们要部署的就是其中的hm-service,其中的配置文件采用了多环境的方式:
image.png
其中的application-dev.yaml是部署到开发环境的配置,application-local.yaml是本地运行时的配置。

查看application.yaml,你会发现其中的JDBC地址并未写死,而是读取变量:
image.png
这两个变量在application-dev.yamlapplication-local.yaml中并不相同:
image.png
在dev开发环境(也就是Docker部署时)采用了mysql作为地址,刚好是我们的mysql容器名,只要两者在一个网络,就一定能互相访问。

我们将项目打包:
image.png
结果:
image.png
hm-service目录下的Dockerfilehm-service/target目录下的hm-service.jar一起上传到虚拟机的root目录:
image.png
部署项目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1.构建项目镜像,不指定tag,则默认为latest
docker build -t hmall .

# 2.查看镜像
docker images
# 结果
REPOSITORY TAG IMAGE ID CREATED SIZE
hmall latest 0bb07b2c34b9 43 seconds ago 362MB
docker-demo 1.0 49743484da68 24 hours ago 327MB
nginx latest 605c77e624dd 16 months ago 141MB
mysql latest 3218b38490ce 17 months ago 516MB

# 3.创建并运行容器,并通过--network将其加入hmall网络,这样才能通过容器名访问mysql
docker run -d --name hmall --network hmall -p 8080:8080 hmall

测试,通过浏览器访问:http://你的虚拟机地址:8080/search/list

3.2.部署前端

hmall-portalhmall-admin是前端代码,需要基于nginx部署。在课前资料中已经给大家提供了nginx的部署目录:
image.png
其中:

  • html是静态资源目录,其中已经包含了hmall-portal以及hmall-admin
  • nginx.conf是nginx的配置文件,主要是完成对html下的两个静态资源目录做代理

我们现在要做的就是把整个nginx目录上传到虚拟机的/root目录下:
image.png
然后创建nginx容器并完成两个挂载:

  • /root/nginx/nginx.conf挂载到/etc/nginx/nginx.conf
  • /root/nginx/html挂载到/etc/nginx/html

由于需要让nginx同时代理hmall-portal和hmall-admin两套前端资源,因此我们需要暴露两个端口:

  • 18080:对应hmall-portal
  • 18081:对应hmall-admin

命令如下:

1
2
3
4
5
6
7
8
docker run -d \
--name nginx \
-p 18080:18080 \
-p 18081:18081 \
-v /root/nginx/html:/etc/nginx/html \
-v /root/nginx/nginx.conf:/etc/nginx/nginx.conf \
--network hmall \
nginx

测试,通过浏览器访问:http://你的虚拟机ip:18080
image.png

3.3.DockerCompose

大家可以看到,我们部署一个简单的java项目,其中包含3个容器:

  • MySQL
  • Nginx
  • Java项目

而稍微复杂的项目,其中还会有各种各样的其它中间件,需要部署的东西远不止3个。如果还像之前那样手动的逐一部署,就太麻烦了。

而Docker Compose就可以帮助我们实现多个相互关联的Docker容器的快速部署。它允许用户通过一个单独的 docker-compose.yml 模板文件(YAML 格式)来定义一组相关联的应用容器。

3.3.1.基本语法

docker-compose.yml文件的基本语法可以参考官方文档:
Compose file version 3 reference

docker-compose文件中可以定义多个相互关联的应用容器,每一个应用容器被称为一个服务(service)。由于service就是在定义某个应用的运行时参数,因此与docker run参数非常相似。
举例来说,用docker run部署MySQL的命令如下:

1
2
3
4
5
6
7
8
9
10
docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123 \
-v ./mysql/data:/var/lib/mysql \
-v ./mysql/conf:/etc/mysql/conf.d \
-v ./mysql/init:/docker-entrypoint-initdb.d \
--network hmall
mysql

如果用docker-compose.yml文件来定义,就是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
version: "3.8"

services:
mysql:
image: mysql
container_name: mysql
ports:
- "3306:3306"
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: 123
volumes:
- "./mysql/conf:/etc/mysql/conf.d"
- "./mysql/data:/var/lib/mysql"
- "./mysql/init:/docker-entrypoint-initdb.d"
networks:
- new
networks:
new:
name: hmall

对比如下:

docker run 参数 docker compose 指令 说明
–name container_name 容器名称
-p ports 端口映射
-e environment 环境变量
-v volumes 数据卷配置
–network networks 网络

明白了其中的对应关系,相信编写docker-compose文件应该难不倒大家。

黑马商城部署文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
version: "3.8"

services:
mysql:
image: mysql
container_name: mysql
ports:
- "3306:3306"
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: 123
volumes:
- "./mysql/conf:/etc/mysql/conf.d"
- "./mysql/data:/var/lib/mysql"
- "./mysql/init:/docker-entrypoint-initdb.d"
networks:
- new
hmall:
build:
context: .
dockerfile: Dockerfile
container_name: hmall
ports:
- "8080:8080"
networks:
- new
depends_on:
- mysql
nginx:
image: nginx
container_name: nginx
ports:
- "18080:18080"
- "18081:18081"
volumes:
- "./nginx/nginx.conf:/etc/nginx/nginx.conf"
- "./nginx/html:/etc/nginx/html"
depends_on:
- hmall
networks:
- new
networks:
new:
name: hmall

3.3.2.基础命令

编写好docker-compose.yml文件,就可以部署项目了。常见的命令:
Overview of docker compose CLI

基本语法如下:

1
docker compose [OPTIONS] [COMMAND]

其中,OPTIONS和COMMAND都是可选参数,比较常见的有:

类型 参数或指令 说明
Options -f 指定compose文件的路径和名称
-p 指定project名称。project就是当前compose文件中设置的多个service的集合,是逻辑概念
Commands up 创建并启动所有service容器
down 停止并移除所有容器、网络
ps 列出所有启动的容器
logs 查看指定容器的日志
stop 停止容器
start 启动容器
restart 重启容器
top 查看运行的进程
exec 在指定的运行中容器中执行命令

教学演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 1.进入root目录
cd /root

# 2.删除旧容器
docker rm -f $(docker ps -qa)

# 3.删除hmall镜像
docker rmi hmall

# 4.清空MySQL数据
rm -rf mysql/data

# 5.启动所有, -d 参数是后台启动
docker compose up -d
# 结果:
[+] Building 15.5s (8/8) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 358B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/openjdk:11.0-jre-buster 15.4s
=> [1/3] FROM docker.io/library/openjdk:11.0-jre-buster@sha256:3546a17e6fb4ff4fa681c3 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 98B 0.0s
=> CACHED [2/3] RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 0.0s
=> CACHED [3/3] COPY hm-service.jar /app.jar 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:32eebee16acde22550232f2eb80c69d2ce813ed099640e4cfed2193f71 0.0s
=> => naming to docker.io/library/root-hmall 0.0s
[+] Running 4/4
✔ Network hmall Created 0.2s
✔ Container mysql Started 0.5s
✔ Container hmall Started 0.9s
✔ Container nginx Started 1.5s

# 6.查看镜像
docker compose images
# 结果
CONTAINER REPOSITORY TAG IMAGE ID SIZE
hmall root-hmall latest 32eebee16acd 362MB
mysql mysql latest 3218b38490ce 516MB
nginx nginx latest 605c77e624dd 141MB

# 7.查看容器
docker compose ps
# 结果
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
hmall root-hmall "java -jar /app.jar" hmall 54 seconds ago Up 52 seconds 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp
mysql mysql "docker-entrypoint.s…" mysql 54 seconds ago Up 53 seconds 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp
nginx nginx "/docker-entrypoint.…" nginx 54 seconds ago Up 52 seconds 80/tcp, 0.0.0.0:18080-18081->18080-18081/tcp, :::18080-18081->18080-18081/tcp

打开浏览器,访问:http://yourIp:8080

4.作业

尝试用Docker的方式部署项目1

其他文章