Docker的技术依赖于Linux内核的虚拟化技术的发展,Docker使用到的网络技术有Network Namespace、Veth设备对、Iptables/Netfilter、网桥、路由等。接下来,我将以Docker容器网络实现的基础技术来分别阐述,在到真正的容器篇章节之前,能形成一个稳固的基础知识网。

Network Namespace

为了支持网络协议栈的多个实例,Linux在网络栈引入了Network Namespace,这些独立的协议栈被隔离到不同的Namespace中,处于不同Namespace中的网络栈是完全隔离的,彼此无法通信。具体有关Linux Namespace的介绍,可以另行浏览之前写的《Linux Namespace》。

Linux的网络协议栈十分复杂,为了支持独立的协议栈,相关的全局变量都必须修改为协议栈私有。Linux实现Network Namespace的核心就是让这些全局变量称为Network Namespace变量的成员,然后为协议栈的函数调用加入一个Namespace参数。与此同时,为了保证已开发程序及内核代码的兼容性,内核代码隐式地使用了Namespace空间内的变量。应用程序如果没有对Namespace有特殊需求,那么不需要额外的代码,Network Namespace对应用程序而言是透明的。

在建立了新的Network Namespace,并将某个进程关联到这个网络命名空间后,就出现了如下的命名空间下的内核数据结构,所有网络栈变量都放入了Network Namespace的数据结构中,这个Network Namespace是属于它进程组私有的,与其他进程组不冲突。

Docker正是利用了Network Namespace特性,实现了不同容器之间的网络隔离。如果一个容器声明使用宿主机的网络栈(-net = host),即不开启Network Namespace,例如:

docker run –d –net=host --name c_name i_name

这种情况下,这个容器启动之后监听的是宿主机的80端口。像这样直接使用宿主机网络栈的方式,虽然可以为容器提供良好的网络性能,但也不可避免的造成端口冲突等网络资源冲突的问题。

所以在一般情况下,我们都希望程序引入Network Namespace里的网络栈,即这个容器拥有自己的IP和端口。但是,这个时候也会带来一个新的问题,被隔离的容器进程,是怎么与其它被隔离的进程进行网络通信的?

Net Bridge

上文说到,Linux 可以支持不同的网络,他们之间是怎么支持够互相通信的呢?如果是两台主机,那需要的可能只是一根网线,把它们连接在一台交换机上。而在Linux当中,网桥(Bridge)就起到相应的作用。本质上来说,这是一个数据链路层(data link)的设备,根据Mac地址的信息转发到网桥的不同端口上。而Docker就是在宿主机上默认创建一个docker0的网桥,凡是连接docker0的网桥,都可以用它来通信。

细述Bridge

网桥是一个二层的虚拟网络设备,把若干个网络接口“连接”起来,使得网口之间的报文可以转发。网桥能够解析收发的报文,读取目标的Mac地址信息,和自己的Mac地址表结合,来决策报文转发的目标网口。为了实现这些功能,网桥会学习源Mac地址。在转发报文时,网桥只需要向特定的端口转发,从而避免不必要的网络交互。如果它遇到了一个自己从未学过的地址,就无法知道这个报文应该向哪个网口转发,就将报文广播给除了报文来源之外的所有网口。

在实际网络中,网络拓扑不可能永久不变。如果设备移动到另一个端口上,而它没有发送任何数据,那么网桥设备就无法感知到这个变化,结果网桥还是向原来的端口发数据包,在这种情况下数据就会丢失。所以网桥还要对学习到的Mac地址表加上超时时间,默认5min。如果网桥收到了对应端口MAC地址回发的包。则重置超时时间,否则过了超时时间后,就认为哪个设备不在那个端口上了,他就会广播重发。

Linux为了支持越来越多的网卡以及虚拟设备,所以使用网桥去提供这些设备之间转发数据的二层设备。Linux内核支持网口的桥接(以太网接口),这与单纯的交换机还是不太一样,交换机仅仅是一个二层设备,对于接受到的报文,要么转发,要么丢弃。运行着Linux内核的机器本身就是一台主机,有可能是网络报文的目的地,其收到的报文要么转发,要么丢弃,还可能被送到网络协议的网络层,从而被自己主机本身的协议栈消化,所以我们可以把网桥看作一个二层设备,也可以看做是一个三层设备。

Linux中Bridge实现

Linux内核是通过一个虚拟的网桥设备(Net Device)来实现桥接的。这个虚拟设备可以绑定若干个以太网接口,从而将它们连接起来。Net Device网桥和普通的设备不同,最明显的是它还可以有一个ip地址。

如上图所示,网桥设备br0绑定的eth0和eth1。对于网络协议栈的上层来说,只看到br0。因为桥接是在数据链路层实现的,上层不需要关心桥接的细节,于是协议栈上层需要发送的报文被送到br0,网桥设备的处理代码判断报文被转发到eth0还是eth1,或者两者皆转发。反之,从eth0或者从eth1接收到的报文被提交给网桥的处理代码,在这里判断报文应该被转发、丢弃或者提交到协议栈上层。

而有时eth0、eth1也可能会作为报文的源地址或目的地址,直接参与报文的发送和接收,从而绕过网桥。

Bridge常用操作

Docker自动完成了对网桥的创建和维护。如果想要进一步理解网桥,可以看下如下举的一些常用操作命令。

新增一个网桥:

brctl addbr xxxxx

在新增网桥的基础上增加网口,在linux中,一个网口其实就是一个物理网卡。将物理网卡和网桥连接起来:

brctl addif xxxx ethx

网桥的物理网卡作为一个网口,由于在链路层工作,就不再需要IP地址了,这样上面的IP地址自然失效:

ipconfig ethx 0.0.0.0

给网桥配置一个IP地址:

ipconfig brxxx xxx.xxx.xxx.xxx

这样网桥就是一个有了IP地址,而连接在这之上的网卡就是一个纯链路层设备了。

Veth Pair

上文说到,docker在宿主机上创建docker0网桥后,凡是连接到docker0上的网桥,就可以用它来通信。那么这里又有个问题,就是这些容器是如何连接到docker0网桥上的?所以这就是Veth Pair虚拟设备的作用了,Veth Pair就是为了在不同的Network Namespace之间进行通信,利用它,可以将两个Network Namespace连接起来。

Veth Pair设备的特点是:它被创建出来后,总是以两张虚拟网卡(Veth Peer)的形式出现。并且,其中一个网卡发出的数据包,可以直接出现在另一张“网卡”上,哪怕这两张网卡在不同的Network Namespace中。

正是因为这样的特点,Veth Pair成对出现,很像是一对以太网卡,常常被看做是不同Network Namespace直连的“网线”。在Veth一端发送数据时,他会将数据发送到另一端并触发另一端的接收操作。我们可以把Veth Pair其中一端看做另一端的一个Peer。

Veth Pair操作命令

创建Veth Pair:

ip link add veth0 type veth peer name veth1

创建后查看Veth Pair的信息:

ip link show

将其中一个Veth Peer设置到另一个Namespace:

ip link set veth1 netns netns1

在netns1中查看veth1设备:

ip netns exec netns1 ip link show

当然,在docker里面,除了将Veth放入容器,还改名为eth0。想要通信必须先分配IP地址:

ip netns exec netns1 ip addr add 10.1.1.1/24 dev veth1ip addr add 10.1.1.2/24 dev veth0

启动它们:

ip netns exec netns1 ip link set dev veth1 upip link set dev veth0 up

测试通信:

ip netns exec netns1 ping 10.1.1.2

Veth Pair查看端对端

在实际操作Veth Pair时,可以使用ethtool便于操作。

在一个Namespace中查看Veth Pair接口在设备列表中的序列号:

ip netns exec netns1 ethtool -S veth1

如果得知另一端的接口设备序列号,假如序列号为6,则可以继续查看6代表了什么设备:

ip netns exec netns2 ip link | grep 6

Iptables/Netfilter

Linux协议栈非常高效且复杂。如果我们想要在数据处理过程中对关心的数据进行一些操作,则需要Linux提供一套相应的机制帮助用户实现自定义的数据包处理过程。

在Linux网络协议栈有一组网络回调函数挂接点,通过这些挂接点函数挂接的钩子函数可以在Linux网络栈处理数据包的过程中对数据包一些操作,例如过滤、修改、丢弃等。整个挂接点技术叫做Iptables和Netfilter。

Netfilter负责在内核中执行各种各样的挂接规则,运行在内核模式中。而Iptables是在用户模式下运行的进程,负责协助维护内核中Netfilter的各种规则表。通过二者的配合来实现整个Linux网络协议栈中灵活的数据包处理机制。

规则表Table

这些挂载点能挂接的规则也分不同的类型,目前主要支持的Table类型如下:

•RAW•MANGLE•NAT•FILTER

上述4个规则链的优先级是RAW最高,FILTER最低。

在实际应用中,不同挂接点需要的规则类型通常不同。例如,在Input的挂接点上明显不需要FILTER的过滤规则,因为根据目标地址,已经在本机的上层协议栈了,所以无需再挂载FILTER过滤规则。

Route

Linux系统包含了一个完整的路由功能。当IP层在处理数据发送或者转发时,会使用路由表来决定发往哪里。通常情况下,如果主机与目的主机直接相连,那么主机可以直接发送IP报文到目的主机。

路由功能是由IP层维护的一张路由表来实现。当主机收到数据报文时,它用此表来决策接下来应该做什么操作。当从网络侧接收到数据报文时,IP层首先会检查报文的IP地址是否与主机自身的地址相同。如果数据报文中的IP地址是自身主机的地址,那么报文将被发送到传输层相应的协议栈中去。如果报文中的IP地址不是主机自身的地址,并且配置了路由功能,那么报文将被转发,否则报文将被丢弃。

路由表的数据一般以条目形式存在,一个典型的路由表条目通常包含以下主要的条目项:

•目的IP地址•下一个路由器的IP地址•标志•网络接口规范

通过路由表转发时,如果任何条目的第一个字段完全匹配目标条目的IP地址(主机)或部分匹配(网络),那么它将指示下一个路由器的IP地址。这些信息将告诉主机数据包该转发到哪一个“下一个路由器”。而条目中所有其它字段将提供更多的辅助信息来为路由转发做决定。

如果没有一个完全匹配的IP,则继续搜索网络ID。找到则转发数据到指定路由器上。由此可知,网络上所有主机都是通过这个路由表中的单个条目进行管理。

如果上述两个条件都不匹配,则将数据报文转发到一个默认路由器上。

如果上述步骤失败,默认路由器也不存在,那么这个数据报文无法转发。任何无法投递的数据都会产生一个ICMP主机不可达或者ICMP网络不可达的错误,并将该错误返回给生成此数据的应用程序。

Route Table

Linux路由表至少2个,一个是LOCAL,一个是MAIN。

Local表用于供Linux协议栈识别本地地址,以及进行本地各个不同网络之间的数据转发。MAIN表用于各类网络IP的转发。它的建立既可以使用静态配置生成,也可以使用动态路由发现协议生成。动态路由发现协议一般使用组播功能来通过发送路由发现数据,动态获取和交换网络的路由信息,并更新到路由表中。

通过下列命令查看LOCAL表的内容:

ip route show table local type local

路由表的查看:

ip route list

管中窥豹

我们在执行docker run -d --name xxx之后,进入容器内部:

## docker ps 可查看所有docker## 进入容器docker exec -it 228ae947b20e /bin/bash

并执行 ifconfig:

$ ifconfigeth0      Link encap:Ethernet  HWaddr 22:A4:C8:79:DD:1A          inet addr:192.168.65.28  Bcast:0.0.0.0  Mask:255.255.255.255          UP BROADCAST RUNNING MULTICAST  MTU:1440  Metric:1          RX packets:2231528 errors:0 dropped:0 overruns:0 frame:0          TX packets:3340914 errors:0 dropped:0 overruns:0 carrier:0          collisions:0 txqueuelen:0          RX bytes:249385222 (237.8 MiB)  TX bytes:590701793 (563.3 MiB)lo        Link encap:Local Loopback          inet addr:127.0.0.1  Mask:255.0.0.0          UP LOOPBACK RUNNING  MTU:65536  Metric:1          RX packets:0 errors:0 dropped:0 overruns:0 frame:0          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0          collisions:0 txqueuelen:1000          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

我们看到一张叫eth0的网卡,它正是一个Veth Pair设备在容器的这一端。

我们再通过 route 查看该容器的路由表:

$ routeKernel IP routing tableDestination     Gateway         Genmask         Flags Metric Ref    Use Ifacedefault         169.254.1.1     0.0.0.0         UG    0      0        0 eth0169.254.1.1     *               255.255.255.255 UH    0      0        0 eth0

我们可以看到这个eth0是这个容器的默认路由设备。我们也可以通过第二条路由规则,看到所有对 169.254.1.1/16 网段的请求都会交由eth0来处理。

而Veth Pair 设备的另一端,则在宿主机上,我们同样也可以通过查看宿主机的网络设备来查看它:

$ ifconfig......eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500        inet 172.16.241.192  netmask 255.255.240.0  broadcast 172.16.255.255        ether 00:16:3e:0a:f3:75  txqueuelen 1000  (Ethernet)        RX packets 3168620550  bytes 727592674740 (677.6 GiB)        RX errors 0  dropped 0  overruns 0  frame 0        TX packets 2937180637  bytes 8661914052727 (7.8 TiB)        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0......docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255        ether 02:42:16:58:92:43  txqueuelen 0  (Ethernet)        RX packets 0  bytes 0 (0.0 B)        RX errors 0  dropped 0  overruns 0  frame 0        TX packets 0  bytes 0 (0.0 B)        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0......vethd08be47: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500        ether 16:37:8d:fe:36:eb  txqueuelen 0  (Ethernet)        RX packets 193  bytes 22658 (22.1 KiB)        RX errors 0  dropped 0  overruns 0  frame 0        TX packets 134  bytes 23655 (23.1 KiB)        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0......

在宿主机上,容器对应的Veth Pair设备是一张虚拟网卡,我们再用brctl show命令查看网桥:

$ brctl showbridge name    bridge id        STP enabled    interfacesdocker0        8000.0242afb1a841    no        vethd08be47

可以清楚的看到Veth Pair的一端 vethd08be47 就插在 docker0 上。

我现在执行docker run 启动两个容器,就会发现docker0上插入两个容器的 Veth Pair的一端。如果我们在一个容器内部互相ping另外一个容器的IP地址,是不是也能ping通?

$ brctl showbridge name    bridge id        STP enabled    interfacesdocker0        8000.0242afb1a841    no        veth26cf2cc                                                                    veth8762ad2

容器1:

$ docker exec -it f8014a4d34d0 /bin/bash$ ifconfigeth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:03          inet addr:172.17.0.3  Bcast:172.17.255.255  Mask:255.255.0.0          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1          RX packets:76 errors:0 dropped:0 overruns:0 frame:0          TX packets:106 errors:0 dropped:0 overruns:0 carrier:0          collisions:0 txqueuelen:0          RX bytes:16481 (16.0 KiB)  TX bytes:14711 (14.3 KiB)lo        Link encap:Local Loopback          inet addr:127.0.0.1  Mask:255.0.0.0          UP LOOPBACK RUNNING  MTU:65536  Metric:1          RX packets:48 errors:0 dropped:0 overruns:0 frame:0          TX packets:48 errors:0 dropped:0 overruns:0 carrier:0          collisions:0 txqueuelen:1000          RX bytes:2400 (2.3 KiB)  TX bytes:2400 (2.3 KiB)$ routeKernel IP routing tableDestination     Gateway         Genmask         Flags Metric Ref    Use Ifacedefault         172.17.0.1      0.0.0.0         UG    0      0        0 eth0172.17.0.0      *               255.255.0.0     U     0      0        0 eth0

容器2:

$ docker exec -it 9a6f38076c04 /bin/bash$ ifconfigeth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1          RX packets:133 errors:0 dropped:0 overruns:0 frame:0          TX packets:193 errors:0 dropped:0 overruns:0 carrier:0          collisions:0 txqueuelen:0          RX bytes:23423 (22.8 KiB)  TX bytes:22624 (22.0 KiB)lo        Link encap:Local Loopback          inet addr:127.0.0.1  Mask:255.0.0.0          UP LOOPBACK RUNNING  MTU:65536  Metric:1          RX packets:198 errors:0 dropped:0 overruns:0 frame:0          TX packets:198 errors:0 dropped:0 overruns:0 carrier:0          collisions:0 txqueuelen:1000          RX bytes:9900 (9.6 KiB)  TX bytes:9900 (9.6 KiB)$ routeKernel IP routing tableDestination     Gateway         Genmask         Flags Metric Ref    Use Ifacedefault         172.17.0.1      0.0.0.0         UG    0      0        0 eth0172.17.0.0      *               255.255.0.0     U     0      0        0 eth0

从一个容器ping另外一个容器:

# -> 容器1内部 ping 容器2$ ping 172.17.0.3PING 172.17.0.3 (172.17.0.3): 56 data bytes64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.142 ms64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.096 ms64 bytes from 172.17.0.3: seq=2 ttl=64 time=0.089 ms

我们看到,在一个容器内部ping另外一个容器的ip,是可以ping通的。也就意味着,这两个容器是可以互相通信的。

容器通信

我们不妨结合前文时所说的,理解下为什么一个容器能访问另一个容器?先简单看如一幅图:

当在容器1里访问容器2的地址,这个时候目的IP地址会匹配到容器1的第二条路由规则,这条路由规则的Gateway是0.0.0.0,意味着这是一条直连规则,也就是说凡是匹配到这个路由规则的请求,会直接通过eth0网卡,通过二层网络发往目的主机。而要通过二层网络到达容器2,就需要127.17.0.3对应的MAC地址。所以,容器1的网络协议栈就需要通过eth0网卡来发送一个ARP广播,通过IP找到MAC地址。

所谓ARP(Address Resolution Protocol),就是通过三层IP地址找到二层的MAC地址的协议。这里说到的eth0,就是Veth Pair的一端,另一端则插在了宿主机的docker0网桥上。eth0这样的虚拟网卡插在docker0上,也就意味着eth0变成docker0网桥的“从设备”。从设备会降级成docker0设备的端口,而调用网络协议栈处理数据包的资格全部交给docker0网桥。

所以,在收到ARP请求之后,docker0就会扮演二层交换机的角色,把ARP广播发给其它插在docker0网桥的虚拟网卡上,这样,127.17.0.3就会收到这个广播,并把其MAC地址返回给容器1。有了这个MAC地址,容器1的eth0的网卡就可以把数据包发送出去。这个数据包会经过Veth Pair在宿主机的另一端veth26cf2cc,直接交给docker0。

docker0转发的过程,就是继续扮演二层交换机,docker0根据数据包的目标MAC地址,在CAM表查到对应的端口为veth8762ad2,然后把数据包发往这个端口。而这个端口,就是容器2的Veth Pair在宿主机的另一端,这样,数据包就进入了容器2的Network Namespace,最终容器2将响应(Ping)返回给容器1。在真实的数据传递中,Linux内核Netfilter/Iptables也会参与其中,这里不再赘述。

CAM就是交换机通过MAC地址学习维护端口和MAC地址的对应表

这里介绍的容器间的通信方式就是docker中最常见的bridge模式,当然此外还有host模式、container模式、none模式等,对其它模式有兴趣的可以去阅读相关资料。

跨主通信

好了,这里不禁问个问题,到目前为止只是单主机内部的容器间通信,那跨主机网络呢?在Docker默认配置下,一台宿主机的docker0网桥是无法和其它宿主机连通的,它们之间没有任何关联,所以这些网桥上的容器,自然就没办法多主机之间互相通信。但是无论怎么变化,道理都是一样的,如果我们创建一个公共的网桥,是不是集群中所有容器都可以通过这个公共网桥去连接?

当然在正常的情况下,节点与节点的通信往往可以通过NAT的方式,但是,这个在互联网发展的今天,在容器化环境下未必适用。例如在向注册中心注册实例的时候,肯定会携带IP,在正常物理机内的应用当然没有问题,但是容器化环境却未必,容器内的IP很可能就是上文所说的172.17.0.2,多个节点都会存在这个IP,大概率这个IP是冲突的。

如果我们想避免这个问题,就会携带宿主机的IP和映射的端口去注册。但是这又带来一个问题,即容器内的应用去意识到这是一个容器,而非物理机,当在容器内,应用需要去拿容器所在的物理机的IP,当在容器外,应用需要去拿当前物理机的IP。显然,这并不是一个很好的设计,这需要应用去配合配置。所以,基于此,我们肯定要寻找其他的容器网络解决方案。

在上图这种容器网络中,我们需要在我们已有的主机网络上,通过软件构建一个覆盖在多个主机之上,且能把所有容器连通的虚拟网络。这种就是Overlay Network(覆盖网络)。

Docker容器网络相关推荐

  1. 手工模拟实现 Docker 容器网络!

    大家好,我是飞哥! 如今服务器虚拟化技术已经发展到了深水区.现在业界已经有很多公司都迁移到容器上了.我们的开发写出来的代码大概率是要运行在容器上的.因此深刻理解容器网络的工作原理非常的重要.只有这样将 ...

  2. Docker容器网络解析

    Docker 容器网络的发展历史 在 Dokcer 发布之初,Docker 是将网络.管理.安全等集成在一起的,其中网络模块可以为容器提供桥接网络.主机网络等简单的网络功能. 从 1.7 版本开始,D ...

  3. docker容器网络及其配置

    docker容器网络及其配置 文章目录 docker容器网络及其配置 虚拟化网络 单主机与多主机的Docker网络 单节点容器间通信 不同节点容器间通信 Docker网络驱动 选择Docker网络驱动 ...

  4. Docker容器网络实例管理

    Docker容器网络实例管理 Linux内核实现名称空间的创建 ip netns命令 可以借助ip netns命令来完成对 Network Namespace 的各种操作.ip netns命令来自于i ...

  5. 解锁TrueNAS SCALE 三方docker容器网络访问限制,默认是关闭的;并添加Portainer-CE容器webGUI管理器

    解锁TrueNAS SCALE 三方docker容器网络访问限制,默认是关闭的:并添加Portainer-CE容器webGUI管理器 本教程环境版本:TrueNAS-SCALE-21.06-BETA. ...

  6. docker容器网络 - 同一个host下的容器间通信

    2019独角兽企业重金招聘Python工程师标准>>> 对于复杂的应用,不可避免需要多个服务部署在多个容器中,并且服务间存在相互间通信的情况.比如服务A需要连接mysql的容器.本文 ...

  7. Kubernetes Docker 容器网络终极之战(十四)

    与 Docker 默认的网络模型不同,Kubernetes 形成了一套自己的网络模型,该网络模型更加适应传统的网络模式,应用能够平滑的从非容器环境迁移到 Kubernetes 环境中. 自从 Dock ...

  8. 初学Docker容器网络不得不看的学习笔记

    [技术沙龙002期]数据中台:宜信敏捷数据中台建设实践|宜信技术沙龙 将于5月23日晚8点线上直播,点击报名 一.关于Docker Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Ap ...

  9. Docker容器-------网络模式,数据卷和数据卷容器

    目录 Docker网络实现原理 Docker的网络模式 Host模式 Container模式 none模式 bridge模式 自定义网络 查看网络模式列表 查看容器信息(包含配置.环境.网关.挂载.c ...

最新文章

  1. Redis事务中几种常见的场景-exec执行前后的语法错误时的处理
  2. 【Python】相当全面的Pandas文本数据处理,干货多多
  3. Python中的类(2)
  4. oracle 超市管理系统,SuperManager 超市账单管理系统 JSP + Servlet + Oracle Jsp/ 240万源代码下载- www.pudn.com...
  5. 安装了虚拟机后mysql用不了_在虚拟机上安装mysql,安装好了并且初始化之后,一直无法启动mysql如何解决?...
  6. 15、system V 与 posix 信号量的简单比较
  7. 无人车企哪家强?通用第一Waymo第二,苹果特斯拉进黑榜
  8. 计算机信息系统安全保卫工作,对计算机信息系统安全保护工作的检查
  9. 员工主动辞职公司也要支付经济补偿金的17种情况
  10. html5 跳转参数不显示_TeeChart for PHP教程(十二):Javascript / HTML5图表
  11. 手机CAD如何快速看图下载?
  12. 传奇私服服务器怎么增加npc,传奇添加NPC的方法以及形象代码计算
  13. 在未来税制下个人所得税信息管理系统建设设想
  14. matlab画双曲线的四种方法
  15. 华为云解锁云原生数据库发展新动能
  16. 主流视频编码压缩技术基本概念(一)
  17. Python 学习文档
  18. java 美团 订单推送,记一次美团外卖推送报文丢失
  19. BIM 那些事广联达BIM 5D
  20. 什么是FTP?FTP端口号是多少?FTP的端口号能改吗?ftp的端口号20、21有何区别?ftp命令?...

热门文章

  1. python pow和**_「Python学习笔记」Python函数高级应用
  2. qt获取场景的缩略图
  3. oracle 删除表 索引也会删除吗,Oracle 删除当前用户下所有的表、索引、序列
  4. daoi php_使用php生成RSA公钥私钥及进行加密解密和签名验证
  5. android 跨应用,Android跨应用获取资源
  6. 支付宝 android 2.3,app被拒记录-2.3-包含支付宝
  7. java创建型_Java创建型模式
  8. oracle olap报告,10g部署oracle olap组件
  9. linux设备分层优点,Linux设备驱动的分层设计思想
  10. mysql 字符转换函数是_MySQL日期和字符串转换函数