搞事情之初识 Docker 与尝试构建 Swift

搞事情系列文章主要是为了继续延续本身的 “T” 字形战略所作,同时也表明着毕设相关内容的学习总结。本文章是 Docker 部分的第一篇,主要是给本身解释与 Docker 有关的内容。python

原文地址:PJ 的 iOS 开发平常linux

虚拟化和容器化技术

虚拟化技术

虚拟化技术是一种将计算机物理资源进行抽象、转换为虚拟的计算机资源提供给程序使用的技术。这些资源包括了 CPU 提供的运算控制资源,硬盘提供的数据存储资源,网卡提供的网络传输资源等。nginx

跨平台

保证程序跨平台兼容,也就是要保证操做系统或物理硬件所提供的接口调用方式一致,程序便不须要兼容不一样硬件平台的接口。此时忽然想到,使用 Swift 编写 iOS app 时,构建出包后老是会带上 Swift 的整个运行时,以保证随着 iOS 系统版本的升级 app 的正常运行,因其 ABI 并未稳定,还不能内置在操做系统中。git

资源管理

可将虚拟化技术运用于计算机资源的管理,其中最实用的就是“虚拟内存”虚拟化技术可以提升计算机资源的使用率,是指利用虚拟化,能够将原来程序用不到的一些资源拿出来,分享给另一些程序,让计算机资源不被浪费。github

虚拟化技术的分类

主要分为两大类:硬件虚拟化软件虚拟化web

  • 硬件虚拟化:好比假设 iOS 基于的 arm 架构 CPU 可以运行基于 x86 架构的 macOS 应用程序,这是由于 CPU 可以将另一个平台的指令集转换为自身的指令集执行(但实际上并不可能)。sql

  • 软件虚拟化:在 2018 WWDC 中,宣布能够在 UIKit 层面提供一部分把 iOS app 转移到 macOS app 中的特性,能够理解为是 Apple 在 Xcode 层面协助开发者构建了迁移代码,帮开发者解决了不一样平台指令的转换。也就是说,软件虚拟化其实是经过一层夹杂在应用程序和硬件平台上的虚拟化实现软件来进行指令的转换。docker

其它虚拟化技术的分类:shell

  • 平台虚拟化:在操做系统和硬件平台间搭建虚拟化设施,使得整个操做系统都运行在虚拟后的环境中。相似 VMwarePD
  • 应用程序虚拟化:在操做系统和应用程序间实现虚拟化,只让应用程序运行在虚拟化环境中。相似 Python 的虚拟环境;
  • 内存虚拟化:将不相邻的内存区,甚至硬盘空间虚拟成统一连续的内存地址,即虚拟内存;
  • 桌面虚拟化:让本地桌面程序利用远程计算机资源运行,达到控制远程计算机的目的。相似华为云的云桌面以及各类远程桌面控制软件,如 Teamviewer。
  • ......

虚拟机

虚拟机一般说法是经过一个虚拟机监视器( Virtual Machine Monitor ) 的设施来隔离操做系统与硬件或应用程序和操做系统,以达到虚拟化的目的。这个虚拟机监视器,一般被称为:Hypervisordjango

虚拟机有一个永远都逃不掉的问题:性能低下。这种效率的低下有时候是没法容忍的,故真实的虚拟机程序经常不彻底遵照 Hypervisor 的设计结构,而是引入一些其它技术来解决效率低下问题,好比解释执行、即时编译(Just In Time)运行机制,但这些技术的引入已不属于虚拟化的范畴了。

容器技术

按分类或者实现方式来讲,容器技术应该属于操做系统虚拟化,也就是在由操做系统提供虚拟化的支持。总的来讲,容器技术指的是操做系统自身支持一些接口,可以让应用程序间能够互不干扰的独立运行,并可以对其在运行中所使用的资源进行干预。

那这也不该该被称为“容器”呀?是的,这里所谓的容器指的是因为应用程序的运行被隔离在了一个独立的运行环境之中,这个独立的运行环境就好似一个容器,包裹了应用程序。

容器这么火爆,火到一心扑在 iOS 上的我都要好好梳理一番,很重要的一个缘由是其在运行性能上远超虚拟机等其它虚拟化实现,甚至在运行效率上与真实运行在物理平台的应用程序不相上下。但注意,容器技术并无进行指令转换,运行爱容器中的应用程序自身必须支持在真实操做系统上运行,也就是必须遵照硬件平台的指令规则。

曾经看到一篇文章说 linux 内核命名空间的改进,直接推进了容器的最大化发展。

利用内核命名空间,从进程 ID 到网络名称,一切均可在 Linux 内核中实现虚拟化。新增的用户命名空间“使得用户和组 ID 能够按命名空间进行映射。对于容器而言,这意味着用户和组能够在容器内部拥有执行某些操做的特权,而在容器外部则没有这种特权。”Linux 容器项目 (LXC) 还添加了用户亟需的一些工具、模板、库和语言绑定,从而推进了进步,改善了使用容器的用户体验。LXC 使得用户可以经过简单的命令行界面轻松地启动容器。(来源 redhat 官网)

容器因为没有虚拟操做系统和虚拟机监视器这两个层次,大幅减小了应用程序带来的额外消耗。因此在容器中的应用程序其实彻底运行在了宿主操做系统中,与其它真实运行在其中的应用程序在指令运行层面是彻底没有任何区别的。

Docker 的核心组成

四大组成对象

镜像

能够理解为一个只读的文件包,其中包含了虚拟环境运行的最原始文件系统的内容。

由于 Docker 采用 AUFS 做为底层文件系统的实现,实现了一种增量式的镜像结构。每次对镜像内容修改,Docker 都会将这些修改铸形成一个镜像层,而一个镜像本质上是由其下层全部的镜像层所组成的,而每个镜像层单独拿出来,均可以与它之下的镜像层组成一个镜像。正是因为这种结构,Docker 的镜像本质上是没法被修改的,由于因此的镜像修改只会产生新的镜像,而不是更新原有的镜像。

容器

在容器技术中,容器是用来隔离虚拟环境的基础设施,但在 Docker 中,被引伸为隔离出来的虚拟环境。若是咱们把镜像理解为类,则容器为实例对象。镜像内存放的是不可变化的东西,当以他们为基础的容器启动后,容器内也就成为类一个“活”的空间。

Docker 的容器应该有三项内容组成:

  • 一个 Docker 镜像;
  • 一个程序运行环境;
  • 一个指令集合。

网络

Docker 中可对每一个容器进行单独的网络配置,也可对各个容器间创建虚拟网络,将数个容器包裹其中,同时与其它网络环境隔离,而且 Docker 还能在容器中构造独立的 DNS,咱们能够在不修改代码和配置的前提下直接迁移容器。

数据卷

在以往的虚拟机中,大部分状况下都直接使用虚拟机的文件系统做为应用数据等文件的存储位置,但并未是彻底安全的,当虚拟机或容器出现问题致使文件系统没法使用时,虽可直接经过快速的镜像进行重制文件系统以致于恢复,但数据也就丢失了。

为保证数据的独立性,一般会单独挂在一个文件系统来存放数据,得意与 Docker 底层的 Union File System 技术,咱们能够不用管相似于搞定挂载在不一样宿主机中实现的方法、考虑挂载文件系统兼容性、虚拟机操做系统配置等问题。

镜像与容器

Docker 镜像

全部的 Docker 镜像都是按照 Docker 所设定的逻辑打包的,也是收到 Docker Engine 所控制。常见的虚拟机镜像都是由其它用户经过各自熟悉的方式打包成镜像文件,公布到网上再被其它用户所下载后,恢复到虚拟机中的文件系统中,但 Docker 的镜像必须经过 Docker 来打包,也必须经过 Docker 下载或导入后使用,不能单独直接恢复成容器中的文件系统。这样,咱们就能够直接在服务器之间传递 Docker 镜像,并配合 Docker 自身对镜像的管理功能,使得在不一样的机器中传递和共享变得很是方便。

每个记录文件系统修改的镜像层 Docker 都会根据它们的信息生产一个64位的 hash 码,正是由于这个编码,能够可以区分不一样的镜像层并保证内容和编码是一致的,咱们能够在镜像之间共享镜像层。当 A 镜像依赖了 C 镜像,且 B 镜像也依赖了 C 镜像,在实际使用过程当中,AB 两个镜像是能够公用 C 镜像内部的镜像层的。

查看镜像

$ docker images
复制代码

镜像命名

能够分为三部分:

  • username:通常都是镜像创做者,但若是不写则是由官方进行维护。
  • repository:通常都是该镜像中所包含的软件名。但镜像名归镜像名,镜像归镜像,Docker 对容器的设计和定义是微型容器而不是庞大臃肿的完整环境,全部一般只会在一个容器中运行一个应用程序,可以大幅下降程序之间互相的影响,利用容器技术控制每一个程序所使用的资源。
  • tag

主进程

Docker 的设计中,容器的生命周期与容器中 PID 为 1 这个进程由密切的关系,容器的启动本质上能够理解为这个进程的启动,而容器的中止也就意味着这个进程的中止。

写时复制

经过镜像运行容器时并非当即把镜像里全部内容拷贝到容器所运行的沙盒文件系统中,而是利用 UnionFS 将镜像以只读方式挂载到沙盒文件系统中,只有在容器对文件的修改时,修改才会体现到沙盒环境上。

从镜像仓库得到镜像

获取镜像

docker pull ubuntu
复制代码

获取镜像更详细的信息

docker inspect ubuntu
复制代码

搜索镜像

docker search django
复制代码

删除镜像

docker rmi ubuntu
复制代码

运行和管理容器

容器的生命周期

  • Created
  • Running
  • Paused
  • Stopped:容器的中止状态下,占用的资源和沙盒环境都存在,只是容器中的应用程序均已中止
  • Deleted

建立容器

$ docker create ubuntu
复制代码

若是咱们以前选择的 docker pull 容器并非默认的 latest 版本,而是手动选择了一个版本,那镜像的名字将会好比 nginx:1.12,对于后续的操做都十分的不方便,对此,咱们能够采用 --name 进行重命名:

$ docker create --name nginx nginx:1.12
复制代码

启动容器

$ docker start ubuntu
复制代码

经过 docker run 可将上述两个命令进行合并:

$ docker run --name nginx nginx:1.12
复制代码

以上命令跑起来的容器运行都是运行在前台,若是咱们想要容器运行在后台,能够经过 -d,其是 -detach 的简称,告诉 Docker 在启动后将程序和控制进行分离。:

$ docker run -d ubuntu 
复制代码

管理容器

列出运行中的全部容器

$ docker ps 
复制代码

列出全部容器

$ docker ps -a/-all
复制代码

其中打印出的列表须要注意的是 STATUS 字段,常见的状态表示有三种:

  • Create:容器已建立,没有启动过;
  • Up[ Time ]:容器正在运行,[ Time ] 表明从开始运行到查看时的时间;
  • Exited([ Code ]) [ Time ]:容器已结束运行,[ Code ] 表示容器结束运行时,主程序返回的程序退出码,而 [ Time ] 则表示容器结束到查看时的时间。

中止和删除容器

$ docker stop ubuntu
复制代码

容器中止后,其维持的文件系统沙盒环境会一直保存,内部被修改的内容也会被保留。经过 docker start 将容器继续启动。

当须要把容器彻底删除容器,可使用:

$ docker rm ubuntu
复制代码

但在运行中的容器默认状况下是不能被删除的,但咱们能够经过如下命令进行删除:

$ docker rm -f ubuntu
复制代码

随时删除容器

Docker 与其它虚拟机不一样,其所定位的轻量级设计讲究随用随开,随关随删,当咱们短期内不须要使用容器时,最佳的作法是删除它而不是仅仅中止它。

若是咱们要对程序作一些环境配置,彻底能够直接将这些配置打包至一个新的镜像中,下次直接使用该镜像建立容器便可。对于一些重要的文件资料,不能随着容器的删除而删除,可使用 Docker 中的数据卷来单独存放。

进入容器

直接建立,进入

$ docker run -it --name ubuntu ubuntu 
复制代码

已经建立完成,进入

$ docker exec -it ubuntu /bin/bash
复制代码
  • -i 表示保持咱们的输入流;
  • -t 表示启用一个伪终端,造成咱们与 bash 的交互。

当容器运行在后台,想要在将当前的输入输出流链接到指定的容器上,能够这么作:

$ docker attach ubuntu
复制代码

经过 docker attach 启动的容器,能够理解为与 docker run -d 作了相反的事情,把当前容器从后台拉回了前台。

为容器配置网络

容器网络

Docker 网络中,有三个比较核心的概念,造成了 Docker 的网络核心模型,即容器网络模型(Container Network Model)

  • 沙盒:提供容器的虚拟网络栈。好比端口套接、IP 路由表、防火墙等;
  • 网络:Docker 内部的虚拟子网,网络内的参与者相互可见并可以进行通信。须要注意的是,这种虚拟网络与宿主机存在隔离关系。
  • 端点:主要目的是造成一个能够控制的突破封闭网络环境的出入口,当容器的端点与网络的端点造成配对后,就如同在这二者之间搭建了桥梁,可进行数据传输。

Docker 的网络实现

目前官方提供了五种网络驱动:

  • Bridge Driver(default):经过基于硬件或软件的网桥来实现通信
  • Host Driver
  • Overlay Driver:借助 Docker 的集群模块 Docker Swarm 来搭建的跨 Docker Daemon 网络,能够经过它搭建跨物理主机的虚拟网络,从而让不一样物理机中运行的容器感知不到多个物理机的存在。
  • MacLan Driver
  • None Driver

网络剩余内容将在下篇文章中继续进行......

搞点事情

学习到这里后,开始对 Docker 所谓“轻量级”的主打理念有了一个初步的认识,准备利用 Docker 的这一特性作一个 Swift 编译服务,主要想利用 Vapor/Perfect (这两个到底选哪个还需调研)来搭建 HTTP 服务,接收传入的代码文本,执行并返回结果。

思考了一下,须要:

  • 具有编译 Swift 能力的 Docker 镜像;
  • 具有 Vapor/Perfect 框架的 Docker 镜像;
  • 具有 Nginx web 服务器的 Docker 镜像;

这一套下来后,将从新发布一个“开箱即用”的提供 Swift 编译服务的 Docker 镜像~想一想就是个很是美好的事情呢!接下来开始第一步

构建具有编译 Swift 能力的 Docker 镜像

以前有看到的文章说直接能够在 Ubuntu 上构建本身的 Swift 版本,因此个人第一步先去找一个 Ubuntu 镜像,这点很是容易:

$ docker pull ubuntu
$ docker run -it --name ubuntu ubuntu /bin/bash 复制代码

成功进入到 bash 后,继续下一步。找到一个万能命令,根据这个命令能够先把编译 Swift 须要的相关依赖都下载完成:

sudo apt-get install git cmake ninja-build clang python uuid-dev libicu-dev icu-devtools libbsd-dev libedit-dev libxml2-dev libsqlite3-dev swig libpython-dev libncurses5-dev pkg-config
复制代码

接着,下载 Swift 源码:

git clone https://github.com/apple/swift.git
复制代码

再下载项目依赖的其它源码:

./utils/update-checkout --clone
复制代码

完成后,便可开始利用源码中的工具进行编译和测试!

utils/build-script -t
复制代码

此处将会经历漫长的等待。二十分钟后,我获得了两个报错:

clang: error: unable to execute command: Killed
clang: error: linker command failed due to signal (use -v to see invocation)
ninja: build stopped: subcommand failed.
utils/build-script: fatal error: command terminated with a non-zero exit status 1, aborting
复制代码
clang: error: unable to execute command: Killed
clang: error: linker command failed due to signal (use -v to see invocation)
[1747/3019] Linking CXX shared library lib/libLTO.so.7svn
FAILED: lib/libLTO.so.7svn 
复制代码

看提示是一些依赖库出了问题,刚开始觉得更新下对应的依赖库就完事了,没想到在网上竟然找到不对应的报错提示!这对于第一次手动编译 Swift 的玩家来讲十分的不友好,折腾了一下子后放弃!

此时,又看到一篇文章有说能够直接利用 Swift 官网已经构建完成的二进制文件进行使用,地址在此 swift.org/download/ ,在 Docker 中能够经过 wget 进行下载。但因未找到 Swift 4.2.1 的正确下载地址,而且也担忧直接修改以往版本下载地址进行猜想地址不对,在宿主机上下载完成后,经过 docker cp /path dockerContainer:/path 命令把文件夹传递到了容器中。

在添加 PATH 我又遇到了以下错误:

swift: error while loading shared libraries: libatomic.so.1: cannot open shared object file: No such file or directory
复制代码
swift: error while loading shared libraries: libedit.so.2: cannot open shared object file: No such file or directory
复制代码

几乎已经把 SO 上全部的解决方案进行了尝试,皆无果,有 issue 说估计是 Docker 自己的问题,折腾了好一下子,遂放弃。

当时觉得这已是最后一种方案,因此折腾了特别久,没想到其实 Apple 官方竟然维护了一套 swift-docker,开箱即用,特别香!!!

$ docker pull swift
$ docker run --privileged -i -t --name swiftfun swift:latest /bin/bash
复制代码

至此,第一步已经完成!这回都省去了本身构建镜像的工做了~

Swift-Docker.png
相关文章
相关标签/搜索