从零开始的unity3d入门教程----基本功能讲解

Posted 风吹落叶花飘荡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始的unity3d入门教程----基本功能讲解相关的知识,希望对你有一定的参考价值。

从零开始的unity3d入门教程(二)----基本功能讲解

文章目录

一、教学目标

独立搭建出一个简单场景,并基于该场景开发一个简单的第一人称3D游戏


本次教学Demo已上传github:https://github.com/waitKey1/Unity-Game-Demo,有需要自取
模型与音效unity包百度链接:
链接:https://pan.baidu.com/s/1Sh77qhYaan_mZ6MpTVwPog?pwd=xvz8
提取码:xvz8

注:以上百度链接可以完全不用下载,下面有详细获取与场景模型搭建方法

二、Unity界面的摆放与介绍

1、Unity界面移动及复原

这是界面中各个窗口的默认摆放位置,也是项目刚打开时候的样子

不过由于这样子摆放,平常工作的时候不是很方便。
1、场景窗口有些太偏,工作不是很舒服
2、游戏播放场景被折叠起来,不能实时看到游戏情况
所以,我们需要对各个窗口进行一下移动,让其看起来更舒服

(1)窗口移动方式


鼠标左键点击窗口名,拖拽移动窗口,左键提起放下窗口


随便拖拽就可以形成以上这样的效果

(2)窗口初始化方法

有时候窗口玩坏了怎么办?一键复原!
Window–>Layouts–>Default

2、平常工作界面

(1)Layouts转换成2 by 3类型

  • Window–>Layouts–>2 by 3
  • 移动Project窗口到Hierarchy窗口下
  • 打开Console窗口并让其与Projecy窗口并列
    Window–>General–>Console

    注:这个指令当窗口消失时候,会新建一个新的,当窗口存在的时候则选定这个窗口

(2)最终工作窗口如下

移动并调整下各个窗口的宽高
令各个窗口摆放位置一般如下

3、各个窗口的中文名及其功能简述

  • Hierarchy 层级窗口:可以看到Scene中物体的层级关系
  • Scene 场景窗口:可以查看/修改物体在场景中的位置
  • Game 游戏场景窗口:游戏开始之后这个画面,就是游戏画面,
    可以通过调整层级窗口里的Main Camera物体来调整画面位置与角度
  • Inspector 检查器窗口/属性窗口:具备选定物体,查看/修改物体属性,添加组件等功能
  • Project 项目窗口:查看该项目有哪些资源/程序
  • Console 控制台窗口:可以看程序运行报的警告/报错

二、游戏物体GameObject的创建与修改

1、创建一个正方体

向Unity世界发出一声HelloWorld吧,Unity世界在此刻将为你缓缓展开!

  • 鼠标移动到Hierarchy窗口下的任意区域,右键3D Object -->Cube

    创建成功!
    注:物体创建时候,是在当前视图中心位置创建,不是在(0,0,0)点创建

2、3D Object介绍

  • 基础对象
    1. Cube:立方体
    2. Sphere:球体
    3. Capsule:胶囊体
    4. Cylinder:圆柱体
    5. Plane:平面
    6. Quad:四边形
  • 文字与动画系统
    1. Text :文字
    2. legacy:动画系统
  • 布娃娃系统
    1. RagDoll:布娃娃系统
  • 基础地形、环境
    1. Tree:树
    2. Wind Zone:风

3、3D Object创建练习

最终目标图如下:

(1)创建Cube,Sphere,Capsule,Cylinder,Quad依次排列
对象移动方式:

  1. 鼠标左键点击选定目标对象后,拖动坐标轴也就是那三个箭头,物体将沿着拖动的坐标轴移动
  2. 鼠标左键点击选定目标对象后,拖动坐标系也就是那三个平面,物体将沿着拖动的坐标系移动

(2)创建Plane放在中间
(3)创建一个Text

(4)创建一颗树
都摆放好即可完成创建与移动练习!

4、Cube Object Transform属性修改练习

在对Cube的Transform组件修改之前需求先练习一下如下技巧。

  • 选定物体练习
  • 删除物体练习
  • Scene窗口画面放大、移动、旋转角度和对象追踪的练习

(1)选定Scene中的对象

  • 对象选定方法
    1. 在Scene窗口中左键点击并拖拽,最后松开,即可批量选定矩形框定的对象
    2. 在Hierarchy窗口中左键选定Sphere然后再Shift+左键点击Tree,即可批量选定Sphere到Tree之间的全部对象
    3. 单次选定以及取消某个对象选定方法:批量选定后,Ctrl+左键即可选定某个对象,若这个对象已被选定则取消选定。

(2)删除Scene中的对象

界面有些嘈杂,我们将其他不是Cube和初始的Camera和Light的对象删掉吧

  • 对象删除方法
    1. 在选定对象后,按一个在你键盘右上角附近的键:Delete。即可删除所有选定的对象

(3)Scene窗口基本操作练习

  1. Scene窗口纵深前进后退的方法鼠标滚轮滚动 或 Alt+鼠标滚轮滚动,向前滚动窗口画面前进。向后滚动,窗口画面后退。

    注:直接滚动,窗口视图是向中心前进。Alt+鼠标滚动则可以选择窗口视图向哪个方向前进

  2. Scene窗口上下左右移动的方法鼠标滚轮摁住拖拽,鼠标前移,画面向上、鼠标左移,画面向左等等,其他方向同理

  3. Scene窗口以当前位置为旋转原点的角度旋转的方法鼠标右键摁住拖拽,鼠标前移,画面向上旋转、鼠标左移,画面向左旋转等等,其他方向同理

  4. Scene窗口以窗口中心位置为旋转原点的角度旋转的方法Alt+鼠标左键摁住拖拽,鼠标前移,画面向上旋转、鼠标左移,画面向左旋转等等,其他方向同理

  5. Scene窗口聚焦某个或某些对象在窗口中心的方法:选定目标对象/对象们,键盘按F

(4)Cube对象的Transform组件修改

Transform组件是整个Unity中重要的组件之一,之后也是接触最多,且属性改变带来的视觉效果也很明显,所以对Unity组件熟悉,可以从Transform组件开始。

  • 将其他组件全部折叠
    一开始的时候,其他组件也展开了,这样有些干扰我们的学习。一开始不用接触那么多组件,我们来将其折叠起来吧
  • 展开Transform组件

    注:折叠单个组件,则是点击目标组件左边的倒三角形。只有展开才能折叠,也只有折叠才能展开
  • Transform组件的修改练习
    目标属性修改如下

Poition:在坐标系中的位置
Rotation:对象旋转的角度,逆时针为正,顺时针为负
Scale:对象的大小

  • 结果图如下:
  • 修改的时候有些同学可能会发现,Scene窗口中立方体不见了,这是因为对游戏引擎来说,模型的内部由于一般看不到,所以没有渲染的必要。如果视角在里面,就会出现这种情况–穿模。只要鼠标滚轮往后滚,让Scene窗口纵深往后移,就可以看到修改后的立方体了。

(5)Cube对象的Transform组件属性快捷键修改

  • 快捷键在Scene窗口中的位置
  • 键盘快捷键:
    • W:移动
    • E:旋转
    • R:拉伸
    • T:矩形拉伸辅助工具
      注:这四个比较常用的Transform组件操作,也可以用在选定多个物体之后,批量进行操作

恭喜完成针对物体的操作课程!

三、常用功能与组件介绍与练习

在正式场景搭建前,还有一个功模块Gizmo 导航器,需要学习:

1、Gizmo 导航器

  • 导航器位置

  • 透视视图与正交视图

    1. Perspective 透视视图:近大远小
    2. Orthographic 正交视图 :物体的显示与距离无关。比如:一个立方体,离视野比较近的边与离视野比较远的边的长度相等。
  • Scene窗口中的视图是透视视图还是正交视图,可以在Gizmo 导航器中设置

    • 注:Persp是透视视图,再点击一下,就会转换成Iso正交视图
  • 三视图
    y轴:Top
    z轴:Front
    x轴:Right

    • 注1:在很多Unity开发中y轴默认是三维空间中的高度,z轴是默认是前进后退,是正面。x轴是侧面
    • 注2:点击Gizmo中的哪个轴就切换到哪个视图。

2、场景布置常用操作与组件

在正式开始布置场景前,还需要注意:如何复制物体、光照设置、主摄像头、如何利用切换视图搭建场景

(1)复制物体


- 选定物体后 ctrl+d

注:复制物体后,Scene窗口下的视图可能没有任何变化,这是因为二者重叠了。选定复制出来的物体,再移动一下就看到有两个了。

(2)光照设置

  • 亮度与颜色修改

    在布置的时候,可能会感觉灯光太亮了,导致物体之间的分辨度很低。
    • Directional Light 属性简要介绍

      注:一般情况,Light组件的属性我们就只修改Intensity即可,也就是修改亮度。可以改到0.8,具体数值大家可以多尝试一下,找出最合适的。
      颜色则根据游戏当前的环境,进行调整,比如黄昏类的就可以设置偏黄一些。
  • 光照角度修改

注1:选定光源后,按E,然后视情况调整
注2:光源的位置,与大小,对光照效果没有任何影响,只需要调整角度即可

(3)主摄像头

  • 手动调整主摄像头位置以及角度
    • 注:选定后调整transform组件里面的Position坐标和Rotation角度即可
  • 将摄像头位置与角度,改为Scene窗口视图
    • 选定主摄像头后,Ctrl+Shift+F
    • Field of View 视场视情况调整成,Ctrl+Shitf+F之后Scene窗口视图与Game视图无大的差异为主

      注:视场可以类比为,摄像头的焦距,越小,拍摄的图像越近。

(4)利用切换视图与角点吸附搭建场景

如何在只能看到二维的画面情况下,搭建出完美符合自己审美要求的三维场景?三视图!

a、不断转换视角搭建场景


  • 以上视图就是,先将透视视图 转换 成正交视图,再点x轴转换成各种xyz视图,或在移动下视图,看一下其他角度等等,然后移动Cube到合适位置
b、角点吸附搭建场景

  • 按V的同时选定方块上的某个角点按住开始拖拽,可以感受到,Cube移动不再是连贯的而是跳动的,目标从一个角点到另一个角点。松手之后,一开始选定的Cube的角点,就会和松手前选定吸附角点重叠。物体也就移动好了。

四、搭建游戏场景,诸葛亮的八卦阵!

1、保存搭建地图参考图

  • 保存地图参考图
    网络上流传的八卦阵图纸

我们开始搭建场景吧,以第二个图为主,中间的亭子就用一个胶囊体代替。
将第二个图片,另存为在电脑里面

2、新建空对象并改名与地面

  • 在Hierarchy窗口下创建九个Empty并改名如下
    • 注1:鼠标移动到地面那一行,右键创建一个Cube,这样Cube的层级就归在地面下面了
    • 注2:也可以在外面创建,然后拖拽新建的Cube到地面那一行,并松手也可以让Cube成为地面的子对象
    • 注3:将已经是地面子对象的Cube拖拽到空的地方松手,就可以将其移出

3、将地图参考图 导入Unity3d项目

  • 新建一个imgs的文件夹

  • 将地图参考图 导入Unity3d项目

  • 将方块贴图修改为图片
  • 最终成果图如下

4、创建Cube按照地图参考图摆放

完成北面墙壁的效果图如下:

  • 搭建场景时Scene窗口的视图修改为正交视图。
  • 主要使用操作:Ctrl+D 复制方块 —》 W 移动方块 —》T 拉长/压缩方块 —》Ctrl+D 复制方块 —》 W 移动方块 —》 …
    当需要旋转的时候就 E 旋转方块,按住V再利用角点吸附拼接方块



    最终图如上

5、生成迷宫预制体

为了让整个墙与地面成为一个整体,可以自由缩放,旋转而不影响结构,我们需要将其制成一个prefab 预制体。

(1) 新建一个空物体命名为迷宫,并列入刚刚的建模模型

  • 新建一个文件夹prefab

(2)将迷宫拖拽到prefab文件夹内


迷宫预制体完成
注:预制体相当于一个自定义的一个结构,可以进行Unity自带的立方体,椭圆,球一样的操作。以后发射子弹,自动生成敌人等都需要用到这种技巧。

五、玩家创建

游戏地图制备好了,第二步就是创建一个玩家Player。
玩家角色 是游戏的灵魂之一,单独的场景无法与游玩的人进行交互。需要通过玩家这一身份与场景进行交互,这样更有代入感一些。

1、玩家 Player!

我们就定义一个正方体为Player。当然也可以是其他的形状,或者人什么的。

2、创建一个实现玩家移动控制功能的C#文件

  • 创建一个叫Scripts文件夹

  • 在Scripts文件夹内创建C# Script

    文件命名为:MoveControl(移动控制)

    注:文件名字与类名字必须一致,如果创建文件后,只修改文件名就会报错,需要两个都改

  • 编写程序
    从之前的学习可以看出,只要修改物体的Transform组件中的position属性即可实现,修改物体位置的效果。
    综上,编程目标:让按键与修改目标物体的position属性绑定,形成一个交互效果。

    1. Start():该函数程序运行开始会运行一次
    2. Update():每次周期都会运行一次
      所以初始化一般会放在Start中,每次都需要修改比如这次的移动控制则需要放在Update中

具体实现代码以及注解如下:

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class MoveControl : MonoBehaviour

    // Start is called before the first frame update
    //注:所有的有小数的数字后面都要加f,表示是float数据类型
    public float speed = 8.0f;
    public float Rotatespeed = 60.0f;
    void Start()
    
        Application.targetFrameRate = 60;//设置帧率为60帧率
        //注:设置帧率为60帧,表示游戏会尽量把帧率往60帧靠近,不是固定60帧
    

    // Update is called once per frame
    void Update()
    
        KeyControl();
    
   private void KeyControl()
    
        //Translate是transform组件中自带的修改自身position属性的函数,在原有的基础上进行加处理
        //Time.deltaTime :记录每帧的用时
        //distance(一帧 移动距离)=speed(速度) * Time.deltaTime(一帧时间)

        //Translate(x,y,z,type):加的x,y,z值,由于一般设定z轴为前方,所以加z即可,Space.Self表示使用自身坐标系,这个下一章教材会讲解
        

        if (Input.GetKey(KeyCode.W))
        
            this.transform.Translate(0, 0, speed * Time.deltaTime, Space.Self); //W 前进
            Debug.Log(speed * Time.deltaTime);
        

        if (Input.GetKey(KeyCode.S))
        
            this.transform.Translate(0, 0, -speed * Time.deltaTime, Space.Self);//S 后退
        

        //Rotate是transform组件中自带的修改自身属性的函数,在原有的基础上进行加处理
        //逆时针为减法,顺时针加法
        //y轴是top,旋转一般是人头往左或往右,所以一般我们默认以y轴为旋转轴
        if (Input.GetKey(KeyCode.A))
        
            this.transform.Rotate(0,  -Rotatespeed * Time.deltaTime, 0,Space.Self);//A逆时针旋转
        
        if (Input.GetKey(KeyCode.D))
        
            this.transform.Rotate(0, Rotatespeed * Time.deltaTime, 0, Space.Self);//A逆时针旋转
        
    

3、将MoveControl脚本添加进Player

4、给Player添加刚体组件


5、第一人称摄像头设置

(1)让摄像头的position与Player的position一致


点击Player的Position,然后Ctrl+C复制
然后再点击Main Camera的Position Ctrl+V粘贴
完成!

(2)调整下摄像头的角度与Field of View 视场

(3)令Main Camera成为Player的子对象

六、游戏音效添加

1、免费音乐资源网站推荐

https://www.aigei.com/music/class/romantic_3/

2、下载音乐,并将其加载进Unity中

  • 新建一个musics的文件夹
  • 将mp3文件拖入musics文件夹

    加载成功!

3、添加游戏音效

  • 新建一个Empty对象,并改名为游戏音效
  • 点击Add Component 添加 Audio Source组件

七、开始运行游戏与游戏导出

1、运行测试

  • 玩家速度修改

修改Speed:影响玩家的速度
修改Rotatespeed:影响玩家的旋转速度

2、游戏导出

File —> Build Settings
打开导出设置


八、结语

如果你能跟着做到这里,那么恭喜!Unity成功入门了,下一节将开始更深入的各项功能与组件的使用,以及实现各类基础功能的脚本书写练习,模型导入与导出,最终实现一个使用Unity开发的星战游戏Demo!
下期预告,星战前夜!

从零开始入门 K8s | 深入剖析 Linux 容器

作者 | 唐华敏(华敏)? 阿里云容器平台技术专家

本文整理自《CNCF x Alibaba 云原生技术公开课》第 15 讲。

关注“阿里巴巴云原生”公众号,回复关键词“入门”,即可下载从零入门 K8s 系列文章 PPT。

导读:Linux 容器是一种轻量级的虚拟化技术,在共享内核的基础上,基于 namespace 和 cgroup 技术做到进程的资源隔离和限制。本文将会以 docker 为例,介绍容器镜像和容器引擎的基本知识。

容器

容器是一种轻量级的虚拟化技术,因为它跟虚拟机比起来,它少了一层 hypervisor 层。先看一下下面这张图,这张图简单描述了一个容器的启动过程。

技术图片

最下面是一个磁盘,容器的镜像是存储在磁盘上面的。上层是一个容器引擎,容器引擎可以是 docker,也可以是其它的容器引擎。引擎向下发一个请求,比如说创建容器,这时候它就把磁盘上面的容器镜像运行成在宿主机上的一个进程。

对于容器来说,最重要的是怎么保证这个进程所用到的资源是被隔离和被限制住的,在 Linux 内核上面是由 cgroup 和 namespace 这两个技术来保证的。接下来以 docker 为例,详细介绍一下资源隔离和容器镜像两部分的内容。

一、资源隔离和限制

namespace

namespace 是用来做资源隔离的,在 Linux 内核上有七种 namespace,docker 中用到了前六种。第七种 cgroup namespace 在 docker 本身并没有用到,但是在 runC 实现中实现了 cgroup namespace。

技术图片

我们先从头看一下:

  • 第一个是 mout namespace。mout namespace 就是保证容器看到的文件系统的视图,是容器镜像提供的一个文件系统,也就是说它看不见宿主机上的其他文件,除了通过 -v 参数 bound 的那种模式,是可以把宿主机上面的一些目录和文件,让它在容器里面可见的;

?

  • 第二个是 uts namespace,这个 namespace 主要是隔离了 hostname 和 domain;

?

  • 第三个是 pid namespace,这个 namespace 是保证了容器的 init 进程是以 1 号进程来启动的;

?

  • 第四个是网络 namespace,除了容器用 host 网络这种模式之外,其他所有的网络模式都有一个自己的 network namespace 的文件;

?

  • 第五个是 user namespace,这个 namespace 是控制用户 UID 和 GID 在容器内部和宿主机上的一个映射,不过这个 namespace 用的比较少;

?

  • 第六个是 IPC namespace,这个 namespace 是控制了进程兼通信的一些东西,比方说信号量;

?

  • 第七个是 cgroup namespace,上图右边有两张示意图,分别是表示开启和关闭 cgroup namespace。用 cgroup namespace 带来的一个好处是容器中看到的 cgroup 视图是以根的形式来呈现的,这样的话就和宿主机上面进程看到的 cgroup namespace 的一个视图方式是相同的;另外一个好处是让容器内部使用 cgroup 会变得更安全。

这里我们简单用 unshare 示例一下 namespace 创立的过程。容器中 namespace 的创建其实都是用 unshare 这个系统调用来创建的。

技术图片

上图上半部分是 unshare 使用的一个例子,下半部分是我实际用 unshare 这个命令去创建的一个 pid namespace。可以看到这个 bash 进程已经是在一个新的 pid namespace 里面,然后 ps 看到这个 bash 的 pid 现在是 1,说明它是一个新的 pid namespace。

cgroup

两种 cgroup 驱动

cgroup 主要是做资源限制的,docker 容器有两种 cgroup 驱动:一种是 systemd 的,另外一种是 cgroupfs 的。

技术图片

  • cgroupfs?比较好理解。比如说要限制内存是多少、要用 CPU share 为多少?其实直接把 pid 写入对应的一个 cgroup 文件,然后把对应需要限制的资源也写入相应的 memory cgroup 文件和 CPU 的 cgroup 文件就可以了;

  • 另外一个是?systemd?的一个 cgroup 驱动。这个驱动是因为 systemd 本身可以提供一个 cgroup 管理方式。所以如果用 systemd 做 cgroup 驱动的话,所有的写 cgroup 操作都必须通过 systemd 的接口来完成,不能手动更改 cgroup 的文件。

容器中常用的 cgroup

接下来看一下容器中常用的 cgroup。Linux 内核本身是提供了很多种 cgroup,但是 docker 容器用到的大概只有下面六种:

技术图片

  • 第一个是 CPU,CPU 一般会去设置 cpu share 和 cupset,控制 CPU 的使用率;
  • 第二个是 memory,是控制进程内存的使用量;
  • 第三个 device ,device 控制了你可以在容器中看到的 device 设备;
  • 第四个 freezer。它和第三个 cgroup(device)都是为了安全的。当你停止容器的时候,freezer 会把当前的进程全部都写入 cgroup,然后把所有的进程都冻结掉,这样做的目的是:防止你在停止的时候,有进程会去做 fork。这样的话就相当于防止进程逃逸到宿主机上面去,是为安全考虑;
  • 第五个是 blkio,blkio 主要是限制容器用到的磁盘的一些 IOPS 还有 bps 的速率限制。因为 cgroup 不唯一的话,blkio 只能限制同步 io,docker io 是没办法限制的;
  • 第六个是 pid cgroup,pid cgroup 限制的是容器里面可以用到的最大进程数量。

不常用的 cgroup

也有一部分是 docker 容器没有用到的 cgroup。容器中常用的和不常用的,这个区别是对 docker 来说的,因为对于 runC 来说,除了最下面的 rdma,所有的 cgroup 其实都是在 runC 里面支持的,但是 docker 并没有开启这部分支持,所以说 docker 容器是不支持下图这些 cgroup 的。

技术图片

二、容器镜像

docker images

接下来我们讲一下容器镜像,以 docker 镜像为例去讲一下容器镜像的构成。

docker 镜像是基于联合文件系统的。简单描述一下联合文件系统,大概的意思就是说:它允许文件是存放在不同的层级上面的,但是最终是可以通过一个统一的视图,看到这些层级上面的所有文件。

技术图片

如上图所示,右边是从 docker 官网拿过来的容器存储的一个结构图。

这张图非常形象地表明了 docker 的存储,docker 存储也就是基于联合文件系统,是分层的。每一层是一个 Layer,这些 Layer 由不同的文件组成,它是可以被其他镜像所复用的。可以看一下,当镜像被运行成一个容器的时候,最上层就会是一个容器的读写层。这个容器的读写层也可以通过 commit 把它变成一个镜像顶层最新的一层。

docker 镜像的存储,它的底层是基于不同的文件系统的,所以它的存储驱动也是针对不同的文件系统作为定制的,比如 AUFS、btrfs、devicemapper?还有 overlay。docker 对这些文件系统做了一些相对应的?graph driver?的驱动,通过这些驱动把镜像存在磁盘上面。

以 overlay 为例

存储流程

接下来我们以 overlay 这个文件系统为例,看一下 docker 镜像是怎么在磁盘上进行存储的。

先看一下下面这张图,简单地描述了 overlay 文件系统的工作原理。

技术图片

  • 最下层是一个 lower 层,也就是镜像层,它是一个只读层;

  • 右上层是一个 upper 层,upper 是容器的读写层,upper 层采用了写实复制的机制,也就是说只有对某些文件需要进行修改的时候才会从 lower 层把这个文件拷贝上来,之后所有的修改操作都会对 upper 层的副本进行修改;

  • upper 并列的有一个 workdir,它的作用是充当一个中间层的作用。也就是说,当对 upper 层里面的副本进行修改时,会先放到 workdir,然后再从 workdir 移到 upper 里面去,这个是 overlay 的工作机制;

  • 最上面的是 mergedir,是一个统一视图层。从 mergedir 里面可以看到 upper 和 lower 中所有数据的整合,然后我们 docker exec 到容器里面,看到一个文件系统其实就是 mergedir 统一视图层。

文件操作

接下来我们讲一下基于 overlay 这种存储,怎么对容器里面的文件进行操作?

技术图片

先看一下读操作,容器刚创建出来的时候,upper 其实是空的。这个时候如果去读的话,所有数据都是从 lower 层读来的。

写操作如刚才所提到的,overlay 的 upper 层有一个写实数据的机制,对一些文件需要进行操作的时候,overlay 会去做一个 copy up 的动作,然后会把文件从 lower 层拷贝上来,之后的一些写修改都会对这个部分进行操作。

然后看一下删除操作,overlay 里面其实是没有真正的删除操作的。它所谓的删除其实是通过对文件进行标记,然后从最上层的统一视图层去看,看到这个文件如果做标记,就会让这个文件显示出来,然后就认为这个文件是被删掉的。这个标记有两种方式:

  • 一种是 whiteout 的方式;
  • 第二个就是通过设置目录的一个扩展权限,通过设置扩展参数来做到目录的删除。

操作步骤

接下来看一下实际用 docker run 去启动 busybox 的容器,它的 overlay 的挂载点是什么样子的?

技术图片

第二张图是 mount,可以看到这个容器 rootfs 的一个挂载,它是一个 overlay 的 type 作为挂载的。里面包括了 upper、lower 还有 workdir 这三个层级。

然后看一下容器里面新文件的写入。docker exec 去创建一个新文件,diff 这个从上面可以看到,是它的一个 upperdir。再看 upperdir 里面有这个文件,文件里面的内容也是 docker exec 写入的。

最后看一下最下面的是 mergedir,mergedir 里面整合的 upperdir 和 lowerdir 的内容,也可以看到我们写入的数据。

三、容器引擎

containerd 容器架构详解

接下来我们基于 CNCF 的一个容器引擎上的 containerd,来讲一下容器引擎大致的构成。下图是从 containerd 官网拿过来的一张架构图,基于这张架构图先简单介绍一下 containerd 的架构。

技术图片

上图如果把它分成左右两边的话,可以认为 containerd 提供了两大功能。

第一个是对于 runtime,也就是对于容器生命周期的管理,左边 storage 的部分其实是对一个镜像存储的管理。containerd 会负责进行的拉取、镜像的存储。

按照水平层次来看的话:

  • 第一层是 GRPC,containerd 对于上层来说是通过 GRPC serve 的形式来对上层提供服务的。Metrics 这个部分主要是提供 cgroup Metrics 的一些内容;

  • 下面这层的左边是容器镜像的一个存储,中线 images、containers 下面是 Metadata,这部分 Matadata 是通过?bootfs?存储在磁盘上面的。右边的 Tasks 是管理容器的容器结构,Events 是对容器的一些操作都会有一个 Event 向上层发出,然后上层可以去订阅这个 Event,由此知道容器状态发生什么变化;

  • 最下层是 Runtimes 层,这个 Runtimes 可以从类型区分,比如说 runC 或者是安全容器之类的。

shim v1/v2 是什么

接下来讲一下 containerd 在 runtime 这边的大致架构。下面这张图是从 kata 官网拿过来的,上半部分是原图,下半部分加了一些扩展示例,基于这张图我们来看一下 containerd 在 runtime 这层的架构。

技术图片

如图所示:按照从左往右的一个顺序,从上层到最终 runtime 运行起来的一个流程。

我们先看一下最左边,最左边是一个 CRI Client。一般就是 kubelet 通过 CRI 请求,向 containerd 发送请求。containerd 接收到容器的请求之后,会经过一个 containerd shim。containerd shim 是管理容器生命周期的,它主要负责两方面:

  • 第一个是它会对 io 进行转发;
  • 第二是它会对信号进行传递。

图的上半部分画的是安全容器,也就是 kata 的一个流程,这个就不具体展开了。下半部分,可以看到有各种各样不同的 shim。下面介绍一下 containerd shim 的架构。

一开始在 containerd 中只有一个 shim,也就是蓝色框框起来的 containerd-shim。这个进程的意思是,不管是 kata 容器也好、runc 容器也好、gvisor 容器也好,上面用的 shim 都是 containerd。

后面针对不同类型的 runtime,containerd 去做了一个扩展。这个扩展是通过 shim-v2 这个 interface 去做的,也就是说只要去实现了这个 shim-v2 的 interface,不同的 runtime 就可以定制不同的 shim。比如:runC 可以自己做一个 shim,叫 shim-runc;gvisor 可以自己做一个 shim 叫 shim-gvisor;像上面 kata 也可以自己去做一个 shim-kata 的 shim。这些 shim 可以替换掉上面蓝色框的 containerd-shim。

这样做的好处有很多,举一个比较形象的例子。可以看一下 kata 这张图,它上面原先如果用 shim-v1 的话其实有三个组件,之所以有三个组件的原因是因为 kata 自身的一个限制,但是用了 shim-v2 这个架构后,三个组件可以做成一个二进制,也就是原先三个组件,现在可以变成一个 shim-kata 组件,这个可以体现出 shim-v2 的一个好处。

containerd 容器架构详解 - 容器流程示例

接下来我们以两个示例来详细解释一下容器的流程是怎么工作的,下面的两张图是基于 containerd 的架构画的一个容器的工作流程。

start 流程

先看一下容器 start 的流程:

技术图片

这张图由三个部分组成:

  • 第一个部分是容器引擎部分,容器引擎可以是 docker,也可以是其它的;
  • 两个虚线框框起来的 containerd 和 containerd-shim,它们两个是属于 containerd 架构的部分;
  • 最下面就是 container 的部分,这个部分是通过一个 runtime 去拉起的,可以认为是 shim 去操作 runC 命令创建的一个容器。

先看一下这个流程是怎么工作的,图里面也标明了 1、2、3、4。这个 1、2、3、4 就是 containerd 怎么去创建一个容器的流程。

首先它会去创建一个 matadata,然后会去发请求给 task service 说要去创建容器。通过中间一系列的组件,最终把请求下发到一个 shim。containerd 和 shim 的交互其实也是通过 GRPC 来做交互的,containerd 把创建请求发给 shim 之后,shim 会去调用 runtime 创建一个容器出来,以上就是容器 start 的一个示例。

exec 流程

接下来看下面这张图是怎么去 exec 一个容器的。
和 start 流程非常相似,结构也大概相同,不同的部分其实就是 containerd 怎么去处理这部分流程。和上面的图一样,我也在图中标明了 1、2、3、4,这些步骤就代表了 containerd 去做 exec 的一个先后顺序。

技术图片

由上图可以看到:exec 的操作还是发给 containerd-shim 的。对容器来说,去 start 一个容器和去 exec 一个容器,其实并没有本质的区别。

最终的一个区别无非就是:是否对容器中跑的进程做一个 namespace 的创建。

  • exec 的时候,需要把这个进程加入到一个已有的 namespace 里面;
  • start 的时候,容器进程的 namespace 是需要去专门创建。

本文总结

最后希望各位同学看完本文后,能够对 Linux 容器有更深刻的了解。这里为大家简单总结一下本文的内容:

  1. 容器如何用 namespace 做资源隔离以及 cgroup 做资源限制;
  2. 简单介绍了基于 overlay 文件系统的容器镜像存储;
  3. 以 docker+containerd 为例介绍了容器引擎如何工作的。

“ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

更多相关内容,请关注“阿里巴巴云原生”。

以上是关于从零开始的unity3d入门教程----基本功能讲解的主要内容,如果未能解决你的问题,请参考以下文章

从零开始入门 K8s | 深入剖析 Linux 容器

从零开始入门 K8s | 理解 CNI 和 CNI 插件

Unity3d新手入门之-----TexturePackers的使用

iOS Unity3D游戏引擎入门②

从零开始入门 K8s | 有状态应用编排 - StatefulSet

从零开始入门 K8s | 有状态应用编排 - StatefulSet