A1: 聚集索引(主键索引)所有ROW都会按照主键索引进行排序
A2: 非聚集索引即普通索引加上字段
A3: 几个字段组成的索引
A4: 聚集索引在物理上连续,非聚集索引在物理上不连续,但在逻辑上连续
A5: 聚集索引影响物理存储顺序,而非聚集索引不影响
A6: 聚集索引插入慢,查询快,非聚集索引反之
A7: 索引是通过二叉树来描述的,聚集索引的子叶节点也是数据节点,而非聚集索引子叶节点仍是索引节点
%
开头的LIKE
语句,模糊匹配OR
前后字段未同时使用索引WHERE
和ORDER BY
所涉及的列上加上索引SELECT
避免使用*
,SQL语句全部大写WHERE
对索引列上进行IS NULL
判断,替换成IS NOT NULL
IN
和NOT IN
会导致全表扫描,替换为EXISTS
或NOT EXISTS
WHRER
使用OR
会放弃索引进而全表扫描CHAR
长度在创建时候指定(1~255),在存储时尾部全部填充空格mysqld
show processlist
查看是否是 SQL 的问题,A1:
GODEBUG=gctrace=1 go run *.go
其中 gctrace=1
表示只针对这个进程进行GC追踪
go采用三色标记法,主要是为了提高并发度,这样扫描过程可以拆分为多个阶段,而不用一次扫描全部
黑 根节点扫描完毕,子节点也扫描完毕
灰 根节点扫描完毕,子节点未扫描
白 未扫描
扫描是从 .bss .data goroutine栈开始扫描,最终遍历整个堆上的对象树
标记过程是一个广度优先的遍历过程,扫描节点,将节点的子节点推送到任务队列中,然后递归扫描子叶节点,直到所有工作队列被排空
mark阶段会将白色对象标记,并推入队列中变为灰色
保障了代码描述中对内存的操作顺序,
即不会在编译期被编译器进行调整,也不会在运行时被CPU的乱序执行所打乱
在应用进入 GC 标记阶段前的 stw 阶段,会将全局变量 runtime.writeBarrier.enabled 修改为 true,这时所有的堆上指针修改操作在修改之前便会额外调用 runtime.gcWriteBarrier
由于GC和Go主程序并发执行,所以必须要在扫描时监控内存可能出现的状态改变,所以需要写屏障,所以需要暂停GO主程序(STW)
改方式的基本思想是:对正在被覆盖的对象进行着色,且如果当时栈未扫描完成,则同样对指针进行着色
程序启动会为每个P分配一个 mark worker 来标记内存,负责为进入STW做前期工作
GC会将灰色object标记为黑色,将灰色object所包含的所有指针所指向的地址都标记为灰色,递归这两个步骤,最终对象非黑即白,其中白色object即未被引用且可以被回收,如果object标记为no scan,则递归结束,标记为黑色
todo https://blog.csdn.net/asd1126163471/article/details/124113816
tcp client发送连接请求报文,报文首部同步标记位 SYN=1 同时随机序列号 seq=x,此时 tcp client 进入 SYNC-SENT
状态的
tcp server 若同意连接则确认报文为 ACK=1,SYN=1,ASK=x+1,seq=y
返回给客户端,并进入 SYNC_RCVD
状态
tcp client 收到回复并确认 ACK 是否为1,seq 是否为 x+1,并返回报文 ACK=1,ASK=y+1
,此时双方进入 ESTABLISHED
状态
主动方发送报文FIN=1,seq=last+1
并进入FIN_WAIT_1
,此时报文不能携带任何数据
被动方收到连接释放报文,并发送确认报文 ACK=1,ack=u+1,seq=v
,并进入 CLOSE_WAIT
状态,但此时如果缓冲区存在未发送数据,那么需要继续发送(这也是 CLOSE_WAIT 持续的时长),主动方收到此条报文后进入 FIN_WAIT_2
,因为还需要处理未发送数据
上一步执行完毕,被动方发送 FIN=1,ack=w+1,seq=u+1
并进入 LAST-ACK
状态,而主动方收到此条报文后进入 TIME_WAIT
(2msl maximum segment life),之后才会进入 CLOSED
在主动方进入 CLOSED
之前,需要发送报文确认退出
1MSL保证主动方最后的 ACK 能到达对端,1MSL 确保 ACK 重传
三次握手,四次挥手确保连接和断开的可靠
记录了哪些数据被接受,哪些未接收,序列号保证了消息的顺序性
ACK应答,超时重传,失序重传,丢弃重复数据,流量控制,拥塞控制
Round-Trip Time 消息往返时间 Retransmission Timeout 超时重传
Golang 默认指针是类型安全的,但它有很多限制。Golang 还有非类型安全的指针,这就是 unsafe 包提供的 unsafe.Pointer。在某些情况下,它会使代码更高效,当然,也更危险。unsafe 包用于 Go 编译器,在编译阶段使用。从名字就可以看出来,它是不安全的,官方并不建议使用。Go 语言类型系统是为了安全和效率设计的,有时,安全会导致效率低下。unsafe 包绕过了 Go 的类型系统,达到直接操作内存的目的,使用它有一定的风险性。但是在某些场景下,使用 unsafe 包提供的函数会提升代码的效率,Go 源码中也是大量使用 unsafe 包。
unsafe
包//定义
type ArbitraryType int
type Pointer *ArbitraryType
//函数
func Sizeof(x AribitraryType) uintptr{}
func Offsetof(x AribitraryType) uintptr{}
func Alignof(x AribitraryType) uintptr{}
Pointer
: 指向任意类型,类似于 C 中的 void*
。
Sizeof
: 返回所传类型的大小,指针只返回指针的本身(x64 8byte x86 4byte
),而不会返回所指向的内存大小。
Offsetof
: 返回 struct
成员在内存中的位置,相对于此结构体的头位置,所传参数必须是结构体成员。传入指针,或者结构体本身,会 error
Alignof
: 返回 M,M 是内存对齐时的倍数。
任意指针都可以和 unsafe.Pointer
相互转换。
uintptr
可以和 unsafe.Pointer
相互转换。
综上,
unsafe.Pointer
是不能进行指针运算的,只能先转为uintptr
计算完再转回unsafe.Pointer
,还有一点要注意的是,uintptr
并没有指针的语义,意思就是uintptr
所指向的对象会被 gc。而unsafe.Pointer
有指针语义,可以保护它所指向的对象在“有用”的时候不会被垃圾回收。
mipmap用于减少渲染的带宽压力,但会有额外的内存开销,一般而言UI是建议关闭的,3D模型看情况开启
不同大小的纹理尺寸对内存的占用也是不同,依照项目的实际情况来决定Size
由于ETC、PVRTC等格式均为有损压缩,因此,当纹理色差范围跨度较大时,均不可避免地造成不同程度的“阶梯”状的色阶问题。因此,很多研发团队使用RGBA32/ARGB32格式来实现更好的效果。但是,这种做法将造成很大的内存占用
ETC1 不支持透明通道问题 可以通过 RGB24 + Alpha8 + Shader 的方式达到比较好的效果
ECT2,ASTC 但需要设备支持 OpenGL ES3.0
unity内置的一项技术,主要是根据目标离相机的距离来断定使用何种精度的模型,减少顶点数的绘制,但代价就是要牺牲部分内存
遮挡剔除是用来消除躲在其他物件后面看不到的物件,这代表资源不会浪费在计算那些看不到的顶点上,进而提升性能
将一些足够小的网格,在CPU上转换它们的顶点,将许多相似的顶点组合在一起,并一次性绘制它们。 无论静态还是动态合批都要求使用相同的材质,动态合批有以下限制:
+ 如果GameObjects在Transform上包含镜像,则不会对其进行动态合批处理
+ 使用多个pass的shader不会被动态合批处理
+ 使用不同的Material实例会导致GameObjects不能一起批处理,即使它们基本相同。
+ [官方25个不能动批的情况](https://links.jianshu.com/go?to=https%3A%2F%2Fgithub.com%2FUnity-Technologies%2FBatchBreakingCause)
静态合批是将静态(不移动)GameObjects组合成大网格,然后进行绘制。静态合批使用比较简单,PlayerSettings中开启static batching,然后对需要静态合批物体的Static打钩即可,unity会自动合并被标记为static的对象,前提它们共享相同的材质,并且不移动,被标记为static的物体不能在游戏中移动,旋转或缩放。但是静态批处理需要额外的内存来存储合并的几何体。注意如果多个GameObject在静态批处理之前共享相同的几何体,则会在编辑器或运行时为每个GameObject创建几何体的副本,这会增大内存的开销
使用GPU Instancing可以一次渲染(render)相同网格的多个副本,仅使用少量DrawCalls。在渲染诸如建筑、树木、草等在场景中重复出现的事物时,GPU Instancing很有用。
每次draw call,GPU Instancing只渲染相同(identical )的网格,但是每个实例(instance)可以有不同的参数(例如,color或scale),以增加变化(variation),减少重复的出现。
GPU Instancing可以减少每个场景draw calls次数。这显著提升了渲染性能。
Auto Simulation 根据项目实际需要是否开启物理模拟,默认是是开启的
Fixed Timestep 过小的值会操成计算量过大,过大的值可能造成部分机制异常(如卡墙,穿透等),根据项目实际来确定
Maximum Allowed Timestep 这里我们需要先知道物理系统本身的特性,即当游戏上一帧卡顿时,Unity会在当前帧非常靠前的阶段连续调用N次FixedUpdate.PhysicsFixedUpdate,Maximum Allowed Timestep的意义就在于单帧限制物理更新的次数,
传输层安全性协议(Transport Layer Security),及其前身 SSL3.0
之后安全套接层(Secure Sockets Layer,缩写作SSL)是一种安全协议,目的是为互联网通信提供安全及数据完整性保障。SSL包含记录层(Record Layer)和传输层,记录层协议确定传输层数据的封装格式。传输层安全协议使用X.509认证,之后利用RSA
加密演算来对通信方做身份认证,之后交换对称密钥作为会谈密钥(Session key)。这个会谈密钥是用来将通信两方交换的数据做加密,保证两个应用间通信的保密性和可靠性,使客户与服务器应用之间的通信不被攻击者窃听。
安全传输层协议(TLS)用于在两个通信应用程序之间提供保密性和数据完整性。
协议由两层组成: TLS 记录协议(TLS Record)和 TLS 握手协议(TLS Handshake)。
TLS协议的优势是与高层的应用层协议(如HTTP、FTP、Telnet等)无耦合。应用层协议能透明地运行在TLS协议之上,由TLS协议进行创建加密通道需要的协商和认证。应用层协议传送的数据在通过TLS协议时都会被加密,从而保证通信的私密性。
当客户端连接到支持TLS协议的服务器要求创建安全连接并列出了受支持的密码组合(加密密码算法和加密哈希函数),握手开始。
服务器从该列表中决定加密和散列函数,并通知客户端。
服务器发回其数字证书,此证书通常包含服务器的名称、受信任的证书颁发机构(CA)和服务器的公钥。 客户端确认其颁发的证书的有效性。
为了生成会话密钥用于安全连接,客户端使用服务器的公钥加密随机生成的密钥,并将其发送到服务器,只有服务器才能使用自己的私钥解密。
利用随机数,双方生成用于加密和解密的对称密钥。这就是TLS协议的握手,握手完毕后的连接是安全的,直到连接(被)关闭。如果上述任何一个步骤失败,TLS握手过程就会失败,并且断开所有的连接。
https
本身基于 http
传输,但是信息通过了 tls
协议加密。
tls
协议位于传输层之上,应用层之下。首次进行 tls 1.3
协议传输需要一个 RTT
tls
可以使用对称加密和非对称加密。
客户端发送一个随机值以及需要的协议和加密方式。
服务端收到客户端的随机值,自己也产生一个随机值,并根据客户端需求的协议和加密方式来使用对应的方式,并且发送自己的证书(如果需要验证客户端证书需要说明)。
客户端收到服务端的证书并验证是否有效,验证通过会再生成一个随机值,通过服务端证书的公钥去加密这个随机值并发送给服务端,如果服务端需要验证客户端证书的话会附带证书。
服务端收到加密过的随机值并使用私钥解密获得第三个随机值,这时候两端都拥有了三个随机值,可以通过这三个随机值按照之前约定的加密方式生成密钥,接下来的通信就可以通过该密钥来加密解密。
之后通过此非对称加密传输的对称加密的密钥来进行正式通讯。
Protocol Buffers
,是Google公司开发的一种数据描述语言,类似于XML能够将结构化数据序列化,可用于数据存储、通信协议等方面。本文只介绍 syntax = proto3
的协议语法。
.proto | 注释 | C++ | Python | Go | C# |
---|---|---|---|---|---|
double | 定长编码 | double | float | float64 | double |
float | 定长编码 | float | float | float32 | float |
int32 | 变长编码,负数编码效率低,可使用sint32 |
int32 | int | int32 | int |
int64 | 变长编码,负数编码效率低,可使用sint64 |
int64 | int/long | int64 | long |
uint32 | 变长编码 | uint32 | int/long | uint32 | uint |
uint64 | 变长编码 | uint64 | int/long | unit64 | ulong |
sint32 | 变长编码,对负数编码比int32 更有效率 |
int32 | int | int32 | int |
sint64 | 变长编码,对负数编码比int64 更有效率 |
int64 | int/long | int64 | long |
fixed32 | 总是4 字节,如果值大于2^28 比uint32 更有效率 |
uint32 | int/long | uint64 | ulong |
fixed64 | 总是8 字节,如果值大于2^56 比uint64 更有效率 |
uint64 | int/long | uint64 | ulong |
bool | 1或0的变长编码 | bool | boolean | bool | bool |
string | 必须是UTF-8 编码 |
string | str/unicode | string | string |
bytes | 可包含任意的字节顺序 | string | str | []byte | ByteString |
一个 os 线程会有一个给固定大小的内存块(一般是 2MB),用来存储当前线程中调用或挂起函数的内部变量,固定大小的栈对于复杂和深层次递归是不够的,而 Goroutine 会以一个很小的栈(2KB)开始其生命周期,这个栈会动态伸缩,最大能到达 1GB(32位系统是 250M)
os 线程由操作系统内核调用,每过一定时间(毫秒),硬件计时器会中断处理器,并调用一个名为 scheduler 的内建函数,这个函数会挂起当前执行的线程并保存内存中它的寄存器内存,然后检查线程列表并决定下一次执行哪个线程,并从内存中恢复该线程的寄存器信息,恢复该线程的线程并执行,这就是上下文切换,增加了 CPU 的运行周期。而 Go 的 runtime 包含了自身的调度器,和 os 线程不同是,
Goroutine
属于用户级线程由语言支持,调度由语言支持,所有开销会减少很多(相比于内核上下文切换)。
Docker 是一个开源的容器引擎,可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器。开发者和系统管理员在笔记本上编译测试通过的容器可以批量地在生产环境中部署,包括 VMs(虚拟机)、bare metal、OpenStack 集群、云端、数据中心和其他的基础应用平台。容器是完全使用沙箱机制,相互之间不会有任何接口。
轻量,在一台机器上运行的多个Docker容器可以共享这台机器的操作系统内核;它们能够迅速启动,只需占用很少的计算和内存资源。镜像是通过文件系统层进行构造的,并共享一些公共文件。这样就能尽量降低磁盘用量,并能更快地下载镜像。
标准,Docker 容器基于开放式标准,能够在所有主流Linux版本、Microsoft Windows以及包括VM、裸机服务器和云在内的任何基础设施上运行。
安全,Docker 赋予应用的隔离性不仅限于彼此隔离,还独立于底层的基础设施。Docker默认提供最强的隔离,因此应用出现问题,也只是单个容器的问题,而不会波及到整台机器。
一次发布,到处使用
容器和虚拟机具有相似的资源隔离和分配优势,但功能有所不同,因为容器虚拟化的是操作系统,而不是硬件,因此容器更容易移植,效率也更高。
传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
特性 | 容器 | 虚拟机 |
---|---|---|
启动 | 秒级 | 分钟级 |
硬盘 | MB | GB |
性能 | 接近原生 | 弱于原生 |
支持量 | 单机上千 | 单机几十左右 |
容器是一个应用层抽象,用于将代码和依赖资源打包在一起。 多个容器可以在同一台机器上运行,共享操作系统内核,但各自作为独立的进程在用户空间中运行 。与虚拟机相比, 容器占用的空间较少(容器镜像大小通常只有几十兆),瞬间就能完成启动。
虚拟机(VM)是一个物理硬件层抽象,用于将一台服务器变成多台服务器。 管理程序允许多个VM在一台机器上运行。每个VM都包含一整套操作系统、一个或多个应用、必要的二进制文件和库资源,因此占用大量空间。而且VM启动也十分缓慢 。
虚拟机更擅长于彻底隔离整个运行环境。例如,云服务提供商通常采用虚拟机技术隔离不同的用户。而 Docker 通常用于隔离不同的应用 ,例如前端,后端以及数据库。
镜像 (Image)
容器(Container)
仓库(Repository)
操作系统分为内核和用户空间。对于Linux而言,内核启动后,会挂载root文件系统为其提供用户空间支持。而Docker镜像(Image),就相当于是一个root文件系统。Docker镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。 镜像不包含任何动态数据,其内容在构建之后也不会被改变。Docker设计时,就充分利用Union FS的技术,将其设计为分层存储的架构。 镜像实际是由多层文件系统联合组成。镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等 。容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。前面讲过镜像使用的是分层存储,容器也是如此。容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。按照Docker最佳实践的要求,容器不应该向其存储层内写入任何数据 ,容器存储层要保持无状态化。所有的文件写入操作,都应该使用数据卷(Volume)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此, 使用数据卷后,容器可以随意删除、重新run,数据却不会丢失。
镜像构建完成后,可以很容易的在当前宿主上运行,但是, 如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry就是这样的服务。一个Docker Registry中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。所以说:镜像仓库是Docker用来集中存放镜像文件的地方类似于我们之前常用的代码仓库。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本 。我们可以通过<仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以latest作为默认标签。
Docker Registry公开服务是开放给用户使用、允许用户管理镜像的Registry服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。最常使用的Registry公开服务是官方的Docker Hub ,这也是默认的Registry,并拥有大量的高质量的官方镜像,网址为:hub.docker.com/ 。在国内访问Docker Hub可能会比较慢国内也有一些云服务商提供类似于Docker Hub的公开服务。除了使用公开服务外,用户还可以在本地搭建私有Docker Registry 。Docker官方提供了Docker Registry镜像,可以直接使用做为私有Registry服务。开源的Docker Registry镜像只提供了Docker Registry API的服务端实现,足以支持Docker命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。
搜索镜像
docker search name[:tag]
拉取镜像,若不指定tag则默认拉取latest
docker pull name[:tag]
查看本地所有镜像
docker images
删除镜像,可以多个删除
docker rmi [option] image ...
-f
强制删除在指定路径中找到 Dockerfile 并构建Image, 后面是路径,但路径中必须存在 Dockerfile
docker build -t [:namespace]/name:tag Path
给镜像赋予新的标签, namespace
必须为 dockerid
,除非另外购买。
docker tag oldname:oldtag namespace/newname:newtag
将镜像上传至 docker 仓库 DockerHub 上,namespace
必须是用户名,也可以上传至 Gitlab
docker push namespace/name:tag
docker
提交修改的镜像
docker commit [-a] [-m] CONTAINER [REPOSITORY[:TAG]]
-a
指明提交者
-m
提交信息
在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。
此方式更新的镜像有依赖通过 docker save -o dst [REPOSITORY[:TAG]]
存盘,删除所有镜像,再通过 docker load -i path
加载新镜像。
docker ps [-a|-s]
-a
查看所有容器。
-s
查看已启动的容器。
docker top containerID
可以多个同时删除
docker rm container ... [option]
-f
: 强制删除容器。
-v
: 若删除容器则数据卷也删除。
可以同时停止多个
docker stop container ...
docker run [:--name] [:-e] [:-v] [:-h] [:--net] [:-p prot0:prot1] [:-d|-i] [:-t] [:--rm] [:--restart] [:--privileged=false] [:--ip] [:--network=] name:tag [:shell]
run
命令将会启动 dockerfile
中定义的 CMD
或 ENTRYPOINT
指令。
--name=xxx
指定容器运行时的名称,可不选,默认为随机字符。
-p Host0:Host1
表示本地 Host0 映射容器 Host1 端口,若为 -P
则随机映射49000 ~ 49900 端口。
-d
:分离模式: 在后台运行。
-h
:指定主机域名。如 -h domyson.cn
。
-e
:为 dockerfile
中的 ENV
的参数变量,设置环境变量,或者覆盖已存在的环境变量 -e TZ="Asia/Shanghai"
设置时区为上海。
-u
:指定执行用户,一般为 root
。
--rm
:停止容器就移除。
-it
: 以交互模式运行容器 (不同于 -d
: 以分离模式运行容器),这意味着交互回话 session 结束时,容器就会停止运行,与 -d
互斥。
-v
: 容器内创建一个数据卷。多次重复使用 -v 标记可以创建多个数据卷,也可以挂载一个主机目录作为数据卷 path0:path1(其中path0是主机目录,path1是容器目录)。
--link container
: 连接到其他容器。 这个方法以后将被弃用,推荐使用 --network
--network NETWORK
:指定连接到的网络。
--ip
:指定容器的ip。
--restart
:no、on-failure:n、always
设置容器自动重启模式,若容器已经启动,可以通过 docker update --restart args
来设置参数。
--privileged
:真正给予 Container 中 root 用户
root权限,否则 root
只是一个普通用户。
shell
:指定交互的方式,一般为bash bash -c "cmd string"
,这条命令将由启动容器执行。
docker logs [opt] CONTAINER
-f
: 跟踪日志输出
--since
:显示某个开始时间的所有日志
-t
: 显示时间戳
--tail N
:仅列出最新N条容器日志
docker exec [opt] CONTAINER shell [:args]
-d
:分离模式: 在后台运行
-it
:以交互模式运行容器 (不同于 -d : 以分离模式运行容器),这意味着交互回话 session 结束时,容器就会停止运行。与 -d
互斥
-u
:指定运行用户,一般设置为 root
进入容器内部之后,通过 exit
退出
Docker
由多个Layers
组成(上限是127层)。而 Dockerfile 每一条指令都会创建一层Layers
。
使用 Alpine
基础镜像
Alpine是一个高度精简又包含了基本工具的轻量级Linux发行版,基础镜像仅
4.41MB
使用 scratch
基础镜像
scratch是一个空镜像,只能用于构建其他镜像
使用 busybox
基础镜像
如果希望镜像里可以包含一些常用的Linux工具,busybox镜像是个不错选择,镜像本身只有1.16M,非常便于构建小镜像。
Dockerfile
指令通过
&&
和\
将多个Run
命令合并成一个
待完善
在 run 命令中 -v /data 标记来创建一个数据卷并挂载到容器里。在一次 run 中多次使用可以挂载多个数据卷。(创建一个容器,并加载一个数据卷到容器的 /data 目录)
也可以在 Dockerfile 中使用 VOLUME 来添加一个或者多个新的卷到由该镜像创建的任意容器。
数据卷是被设计用来持久化数据的,它的生命周期独立于容器,Docker不会在容器被删除后自动删除数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的数据卷。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个命令。无主的数据卷可能会占据很多空间,要清理会很麻烦。
docker run -d -P --name web -v /src/webapp:/opt/webapp[:权限]
上面的命令加载主机的 /src/webapp 目录到容器的 /opt/webapp 目录,默认权限是读写,也可以指定为只读(ro)
--volumes-from 在run的时候指定数据卷容器
查看数据卷的信息
docker inspect contianerID
查看所有数据卷
docker volume ls
清除所有无主数据卷
docker volume prune
版本
docker version
登陆
docker login
登出
docker logout
Docker-Compose
(docker编排)是 docker 提供的一个命令行工具,用来定义和运行由多个容器组成的应用。可以通过 docker-compose.yml 文件声明式的定义应用程序的各个服务,并由单个命令完成应用的创建和启动。
Docker-Compose
将所管理的容器分为三层,分别是工程(project),服务(service)以及容器(container)。Docker-Compose运行目录下的所有文件(docker-compose.yml,extends文件或环境变量文件等)组成一个工程,若无特殊指定工程名即为当前目录名。一个工程当中可包含多个服务,每个服务中定义了容器运行的镜像,参数,依赖。一个服务当中可包括多个容器实例,Docker-Compose并没有解决负载均衡的问题,因此需要借助其它工具实现服务发现及负载均衡。