首页 / 资讯中心 / 文章详情

【K8s系列 (一) 】Docker与容器(上篇)

【K8s系列 (一) 】Docker与容器(上篇) ★ FEATURED ARTICLE
⭐️名不显时心不朽再挑灯火看文章。个人主页 YYYing.⭐️Kubernetes系列专栏从零开始的k8s学习系列下期内容暂无目录前言一、初识容器1、docker的形态2、docker的安装3、docker的使用4、docker的架构二、被隔离的进程1、容器到底是什么2、为什么要隔离3、与虚拟机的区别是什么4、隔离是怎么实现的三、容器化的应用1、什么是容器化的应用2、常用的镜像操作有哪些3、常用容器操作有哪些结语前言你一定听过Kubernetes吧或许更熟悉一点的是当今许多人挂在嘴边的“K8s”。不管你是研发、测试、还是运维不管你是前台、后台、还是中台不管你用的是C、Java还是Python不管你是搞数据库、区块链、还是人工智能不管你是从事网站、电商、还是音视频在这个“云原生”时代Kubernetes都是一个绕不过去的产品是我们工作中迟早要面对的“坎儿”。毕竟现代应用是什么是微服务是服务网格这些统统要围绕着容器来开发、部署和运行而使用容器就必然要用到容器编排技术在现在只有唯一的选项那就是Kubernetes它的地位就如同Linux一样成为了事实上的云原生操作系统是构建现代应用的基石。一、初识容器容器这部分内容其实早在docker系列就应该讲了的但是当时时间平衡不过来再加上有点小懒那几个数据库和docker的系列我前面一直没做doge但是没关系此系列面向的群体只是要有一定linux基础的人群至少也得有个虚拟机至于docker和k8s等云原生相关技术我将会从0开始说起这个门槛我感觉还是很低的。1、docker的形态在用docker之前我们需要对docker的形态有所了解目前市面上使用docker有两个选择Docker Desktop和Docker Engine。Docker Desktop是针对个人使用而设计的支持Mac和Windows快速安装具有直观的图形界面还集成了许多周边工具方便易用。但不太推荐使用Docker Desktop原因有两个。第一个它是商业产品难免会带有Docker公司的“私人气息”有一些自己的、非通用的东西不利于我们后续的Kubernetes学习。第二个它只是对个人学习免费受条款限制不能商用我们在日常工作中难免会“踩到雷区”。Docker Engine则和Docker Desktop正好相反完全免费但只能在Linux上运行只能使用命令行操作缺乏辅助工具需要我们自己动手DIY运行环境。不过要是较起真来它才是Docker当初的真正形态“血脉”最纯正也是现在各个公司在生产环境中实际使用的Docker产品毕竟机房里99%的服务器跑的都是Linux。所以我们接下来学习过程中我将使用Docker Engine来给大家讲解。2、docker的安装安装这个东西不管怎么样只要能安装好并能正常运行测试就行所以如果你觉得我的安装教程可能有些不是很全或者跟着很难受那完全可以自行处理然后跳过这节看下面的内容。先让我们尝试输入命令docker会得到“命令未找到”的提示还有如何安装的建议Command docker not found, but can be installed with: sudo apt install docker.io所以你只需要按照系统的提示输入命令安装docker.io就可以了。为了方便你还可以使用-y参数来避免确认实现自动化操作sudo apt install -y docker.io #安装Docker Engine我们安装好后必须要做一些手工调整才能用起来所以你还要在安装完毕后执行下面的两条命令sudo service docker start #启动docker服务 sudo usermod -aG docker $USER #当前用户加入docker组第一个service docker start是启动Docker的后台服务第二个usermod -aG是把当前的用户加入Docker的用户组。这是因为操作Docker必须要有root权限而直接使用root用户不够安全加入Docker用户组是一个比较好的选择这也是Docker官方推荐的做法。当然如果只是为了图省事你也可以直接切换到root用户来操作Docker。上面的三条命令执行完之后我们还需要运行下面的命令让它刷新一下权限这样才能让修改用户组的命令usermod生效。newgrp docker现在我们就可以来验证Docker是否安装成功了使用的命令是docker version和docker info。3、docker的使用首先我们使用命令docker ps它会列出当前系统里运行的容器就像我们在Linux系统里使用ps命令列出运行的进程一样。接下来让我们尝试另一个非常重要的命令docker pull从外部的镜像仓库Registry拉取一个busybox镜像image你可以把它类比成是Ubuntu里的“apt install”下载软件包docker pull会有一些看起来比较奇怪的输出信息现在我们暂时不用管后续会有详细解释。我们再执行命令docker images它会列出当前Docker所存储的所有镜像其他镜像先不管可以看到命令会显示有一个叫busybox的镜像镜像的ID号是一串16进制数字大小是1.41MB。现在我们就要从这个镜像启动容器了命令是docker run执行echo输出字符串docker run busybox echo hello world这条命令会在我们的终端上输出计算机世界最著名的语句“hello world”然后我们再用docker ps命令加上一个参数-a就可以看到这个已经运行完毕的容器初次接触容器的你可能会感到很困惑这些命令都做了什么看起来并没有展示出什么特别神奇的本领啊可能还不如直接写一个Shell脚本来得省事。不用着急既然我都这么说了那我们后续肯定来讲解其中的奥秘。那运行都有了那停止和删除肯定也得要有# 停止容器如果容器正在运行 docker stop 容器ID或名称 # 删除容器 docker rm 容器ID或名称 # 如果容器未停止可以直接使用强制删除 docker rm -f 容器ID或名称不过这点我们在容器操作中也会强调。4、docker的架构这里我再稍微讲一下Docker Engine的架构让你有个初步的印象也为之后的学习做一个铺垫。下面的这张图来自Docker官网What is Docker? | Docker Docs精准地描述了Docker Engine的内部角色和工作流程对我们的学习研究非常有指导意义。刚才我们敲的命令行docker实际上是一个客户端client 它会与Docker Engine里的后台服务Docker daemon通信而镜像则存储在远端的仓库Registry里客户端并不能直接访问镜像仓库。Docker client可以通过build、pull、run等命令向Docker daemon发送请求而Docker daemon则是容器和镜像的“大管家”负责从远端拉取镜像、在本地存储镜像还有从镜像生成容器、管理容器等所有功能。所以在Docker Engine里真正干活的其实是默默运行在后台的Docker daemon而我们实际操作的命令行工具“docker”只是个“传声筒”的角色。Docker官方还提供一个“hello-world”示例可以为你展示Docker client到Docker daemon再到Registry的详细工作流程你只需要执行这样一个命令docker run hello-world它会先检查本地镜像如果没有就从远程仓库拉取再运行容器最后输出运行信息二、被隔离的进程广义上来说容器技术是动态的容器、静态的镜像和远端的仓库这三者一起组合而成的。不过“容器”这个术语作为容器技术里的核心概念不仅是大多数初次接触这个领域的人即使是有一些使用经验的人想要准确的把握其内涵和本质仍是比较困难的。那么现在我们就来一起看看所谓容器到底是什么。1、容器到底是什么从字面上来看容器就是Container一般把它形象地比喻成现实世界里的集装箱它也正好和Docker的现实含义相对应因为码头工人那只可爱的小鲸鱼就是不停地在搬运集装箱。集装箱的作用是标准化封装各种货物一旦打包完成之后就可以从一个地方迁移到任意的其他地方。相比散装形式而言集装箱隔离了箱内箱外两个世界保持了货物的原始形态避免了内外部相互干扰极大地简化了商品的存储、运输、管理等工作。所以我们容器也是一样的不过它封装的货物是运行中的应用程序也就是进程同样他会把进程跟外界隔离开互不影响。我们可以看看在容器里运行的进程是个什么样子docker pull alpine然后我们使用docker run命令运行它的Shell程序docker run -it alpine sh注意我们在这里多加了一个-it参数这样我们就会暂时离开当前的Ubuntu操作系统进入容器内部。现在让我们执行cat /etc/os-release还有ps这两个命令看看容器里与容器外有什么不同最后再使用exit退出就像这张截图里所显示的在容器里查看系统信息会发现已经不再是外面的Ubuntu系统了而是变成了Alpine Linux 3.15使用ps命令也只会看到一个完全“干净”的运行环境除了Shell即sh没有其他的进程存在。也就是说在容器内部是一个全新的Alpine操作系统在这里运行的应用程序完全看不到外面的Ubuntu系统两个系统被互相“隔离”了就像是一个“世外桃源”。我们还可以再拉取一个Ubuntu 18.04的镜像用同样的方式进入容器内部然后执行apt update、apt install等命令来看看docker pull ubuntu:18.04 docker run -it ubuntu:18.04 sh # 下面的命令都是在容器内执行 cat /etc/os-release apt update apt install -y wget redis redis-server 具体结果我就不展示了如果不想下太多占内存这块弄不弄也无所谓虽说一个镜像内存也就几十MB。不过我想说的就是我们可以在这个“世外桃源”做任意的事情比如安装应用、运行Redis服务等。但无论我们在容器里做什么都不会影响外面的Ubuntu系统当然不是绝对的。到这里我们就可以得到一个初步的结论容器就是一个特殊的隔离环境它能够让进程只看到这个环境里的有限信息不能对外界环境施加影响。那么好此时就会有一个问题我们为什么要这么做也就是为什么要创建这么一个隔离环境直接让进程就运行在操作系统不就行了2、为什么要隔离对于Linux操作系统来说一个不受任何限制的应用程序是十分危险的。如果这个进程能够看到系统里所有的文件、所有的进程、所有的网络流量访问内存里的任何数据那么恶意程序很容易就会把系统搞瘫痪正常程序也可能会因为无意的Bug导致信息泄漏或者其他安全事故。虽然Linux提供了用户权限控制能够限制进程只访问某些资源但这个机制还是比较薄弱的和真正的“隔离”需求相差得很远。而现在使用容器技术我们就可以让应用程序运行在一个有严密防护的“沙盒”Sandbox环境之内就好像是把进程请进了“隔离酒店”它可以在这个环境里自由活动但绝不允许“越界”从而保证了容器外系统的安全。而且我们计算机是有各种各样的资源的CPU内存硬盘网卡虽然很多大公司的高性能服务器其实按理来说这些资源还是很宽泛的但终归是有限的且要考虑成本那么就注定不可能让一个程序随意无限制地占用所以容器技术的另一个本领就是为应用程序加上资源隔离在系统里切分出一部分资源让它只能使用指定的配额虽然这使程序损失了很多自由但却保证了系统性安全。3、与虚拟机的区别是什么这么看来容器不过就是常见的“沙盒”技术中的一种和虚拟机差不了多少那么它与虚拟机的区别在哪里呢又有什么样的优势呢其实容器和虚拟机面对的都是相同的问题使用的也都是虚拟化技术只是所在的层次不同我们可以参考Docker官网上的两张图把这两者对比起来会更利于学习理解。Docker官网的图示其实并不太准确容器并不直接运行在Docker上Docker只是辅助建立隔离环境让容器基于Linux操作系统运行首先容器和虚拟机的目的都是隔离资源保证系统安全然后是尽量提高资源的利用率。想想当时你自己用VMware或VirtualBox创建虚拟机的时候它们能够在宿主机系统里完整虚拟化出一套计算机硬件在里面还能够安装任意的操作系统这内外两个系统也同样是完全隔离互不干扰。而在数据中心的服务器上虚拟机软件即图中的Hypervisor同样可以把一台物理服务器虚拟成多台逻辑服务器这些逻辑服务器彼此独立可以按需分隔物理服务器的资源为不同的用户所使用。从实现的角度来看虚拟机虚拟化出来的是硬件需要在上面再安装一个操作系统后才能够运行应用程序而硬件虚拟化和操作系统都比较“重”会消耗大量的CPU、内存、硬盘等系统资源但这些消耗其实并没有带来什么价值属于“重复劳动”和“无用功”不过好处就是隔离程度非常高每个虚拟机之间可以做到完全无干扰。我们再来看容器即图中的Docker它直接利用了下层的计算机硬件和操作系统因为比虚拟机少了一层所以自然就会节约CPU和内存显得非常轻量级能够更高效地利用硬件资源。不过因为多个容器共用操作系统内核应用程序的隔离程度就没有虚拟机那么高了。还有一点就是运行效率这可以说是容器相比于虚拟机最大的优势在这个对比图中就可以看到同样的系统资源虚拟机只能跑3个应用其他的资源都用来支持虚拟机运行了而容器则能够把这部分资源释放出来同时运行6个应用。当然这个对比图只是一个形象的展示并不是严谨的数值比较大体有个印象就行。一个普通的Ubuntu虚拟机安装完成之后体积都是GB级别的再安装一些应用很容易就会上到几十GB我们的电脑上同时运行十来个虚拟机可能就是极限了。而一个Ubuntu镜像大小则只有几十MB启动起来更是非常快基本上不超过一秒钟同时跑上百个容器也毫无问题。虚拟机和容器这两种技术并不是互相排斥的它们完全可以结合起来使用就像我们现在的环境一样用虚拟机实现与宿主机的强隔离然后在虚拟机里使用Docker容器来快速运行应用程序。4、隔离是怎么实现的我们知道虚拟机使用的是HypervisorKVM、Xen等那么容器是怎么实现和下层计算机硬件和操作系统交互的呢为什么它会具有高效轻便的隔离特性呢奥秘就在于Linux操作系统内核之中为资源隔离提供了三种技术namespace、cgroup、chroot虽然这三种技术的初衷并不是为了实现容器但它们三个结合在一起就会发生奇妙的“化学反应”。namespace是2002年从Linux 2.4.19开始出现的和编程语言里的namespace有点类似它可以创建出独立的文件系统、主机名、进程号、网络等资源空间相当于给进程盖了一间小板房这样就实现了系统全局资源和进程局部资源的隔离。chroot的历史则要比前面的namespace、cgroup要古老得多早在1979年的UNIX V7就已经出现了它可以更改进程的根目录也就是限制访问文件系统相当于给进程的小板房铺上了地砖。综合运用这三种技术一个具有完善的隔离特性的容器就此出现了进程就可以搬进这个小房间工作了。三、容器化的应用1、什么是容器化的应用之前我们运行容器的时候显然不是从零开始的而是要先拉取一个“镜像”image再从这个“镜像”来启动容器像这一篇的第一章那样docker pull busybox docker run busybox echo hello world那么这个镜像又是什么东西它与容器又有什么关系其实我们在其他场合中也曾经见到过“镜像”这个词比如最常见的光盘镜像重装电脑时使用的硬盘镜像还有虚拟机系统镜像。这些“镜像”都有一些相同点只读不允许修改以标准格式存储了一系列的文件然后在需要的时候再从中提取出数据运行起来。容器技术里的镜像也是同样的道理。因为容器是由操作系统动态创建的那么必然就可以用一种办法把它的初始环境给固化下来保存成一个静态的文件相当于是把容器给“拍扁”了这样就可以非常方便地存放、传输、版本化管理了。镜像可以说是一个“样板间”把运行进程所需要的文件系统、依赖库、环境变量、启动参数等所有信息打包整合到了一起。之后镜像文件无论放在哪里操作系统都能根据这个“样板间”快速重建容器应用程序看到的就会是一致的运行环境了。从功能上来看镜像和常见的tar、rpm、deb等安装包一样都打包了应用程序但最大的不同点在于它里面不仅有基本的可执行文件还有应用运行时的整个系统环境。这就让镜像具有了非常好的跨平台便携性和兼容性能够让开发者在一个系统上开发例如Ubuntu然后打包成镜像再去另一个系统上运行例如CentOS完全不需要考虑环境依赖的问题是一种更高级的应用打包方式。理解了这一点我们再回过头来看看前面运行的Docker命令。docker pull busybox就是获取了一个打包了busybox应用的镜像里面固化了busybox程序和它所需的完整运行环境。docker run busybox echo hello world就是提取镜像里的各种信息运用namespace、cgroup、chroot技术创建出隔离环境然后再运行busybox的echo命令输出hello world的字符串。这两个步骤由于是基于标准的Linux系统调用和只读的镜像文件所以无论是在哪种操作系统上或者是使用哪种容器实现技术都会得到完全一致的结果。推而广之任何应用都能够用这种形式打包再分发后运行这也是无数开发者梦寐以求的“一次编写到处运行Build once, Run anywhere”的至高境界。所以所谓的“容器化的应用”或者“应用的容器化”就是指应用程序不再直接和操作系统打交道而是封装成镜像再交给容器环境去运行。现在你就应该知道了镜像就是静态的应用容器容器就是动态的应用镜像两者互相依存互相转化密不可分。2、常用的镜像操作有哪些在前面的课程里你应该已经了解了两个基本命令docker pull从远端仓库拉取镜像docker images列出当前本地已有的镜像。docker pull的用法还是比较简单的和普通的下载非常像不过我们需要知道镜像的命名规则这样才能准确地获取到我们想要的容器镜像。镜像的完整名字由两个部分组成名字和标签中间用 : 连接起来。名字表明了应用的身份比如busybox、Alpine、Nginx、Redis等等。标签tag则可以理解成是为了区分不同版本的应用而做的额外标记任何字符串都可以比如3.15是纯数字的版本号、jammy是项目代号、1.21-alpine是版本号加操作系统名等等。其中有一个比较特殊的标签叫“latest”它是默认的标签如果只提供名字没有附带标签那么就会使用这个默认的“latest”标签。那么现在你就可以把名字和标签组合起来使用 docker pull 来拉取一些镜像了docker pull grafana/grafana:latest docker pull mysql:5.7 docker pull nginx:1.21-alpine docker pull nginx:alpine docker pull redis:6.0.8有了一些镜像之后我们再用docker images命令来看看它们的具体信息再来看另一个镜像操作命令docker rmi它用来删除不再使用的镜像可以节约磁盘空间注意命令rmi实际上是“remove image”的简写。下面我们就来试验一下使用名字和IMAGE ID来删除镜像docker rmi alpine docker rmi a46这里的第一个rmi删除了alpine镜像因为没有显式写出标签默认使用的就是“latest”。第二个rmi没有给出名字而是直接使用了IMAGE ID的前三位也就是“a46”Docker就会直接找到这个ID前缀的镜像然后删除。命令还有很多不过我们此处就讲这几个后续博客我们还会陆陆续续介绍其他命令。3、常用容器操作有哪些现在我们已经在本地存放了镜像就可以使用docker run命令把这些静态的应用运行起来变成动态的容器了。基本的格式是“docker run 设置参数”再跟上“镜像名或ID”后面可能还会有附加的“运行命令”。比如这个命令docker run -h srv alpine hostname这里的-h srv就是容器的运行参数alpine是镜像名它后面的hostname表示要在容器里运行的“hostname”这个程序输出主机名。docker run是最复杂的一个容器操作命令有非常多的额外参数用来调整容器的运行状态你可以加上--help来看它的帮助信息今天我只说几个最常用的参数。-it表示开启一个交互式操作的Shell这样可以直接进入容器内部就好像是登录虚拟机一样。它实际上是“-i”和“-t”两个参数的组合形式-d表示让容器在后台运行这在我们启动Nginx、Redis等服务器程序的时候非常有用。--name可以为容器起一个名字方便我们查看不过它不是必须的如果不用这个参数Docker会分配一个随机的名字。下面我们就来练习一下这三个参数分别运行Nginx、Redis和Ubuntudocker run -d nginx:alpine # 后台运行Nginx docker run -d --name red_srv redis:6.0.8 # 后台运行Redis docker run -it --name ubuntu 你ubuntu的名字 sh # 使用IMAGE ID登录Ubuntu18.04因为第三个命令使用的是-it而不是-d所以它会进入容器里的Ubuntu系统我们需要另外开一个终端窗口使用docker ps命令来查看容器的运行状态可以看到每一个容器也会有一个“CONTAINER ID”它的作用和镜像的“IMAGE ID”是一样的唯一标识了容器。对于正在运行中的容器我们可以使用docker exec命令在里面执行另一个程序效果和docker run很类似但因为容器已经存在所以不会创建新的容器。它最常见的用法是使用-it参数打开一个Shell从而进入容器内部例如docker exec -it red_srv sh这样我们就“登录”进了Redis容器可以很方便地查看服务的运行状态或者日志。运行中的容器还可以使用docker stop命令来强制停止这里我们仍然可以使用容器名字不过或许用“CONTAINER ID”的前三位数字会更加方便。容器被停止后使用docker ps命令就看不到了不过容器并没有被彻底销毁我们可以使用docker ps -a命令查看系统里所有的容器当然也包括已经停止运行的容器这些停止运行的容器可以用docker start再次启动运行如果你确定不再需要它们可以使用docker rm命令来彻底删除。你可能会感觉这样的容器管理方式很麻烦启动后要ps看ID再删除如果稍微不注意系统就会遗留非常多的“死”容器占用系统资源有没有什么办法能够让Docker自动删除不需要的容器呢办法当然有就是在执行docker run命令的时候加上一个--rm参数这就会告诉Docker不保存容器只要运行完毕就自动清除省去了我们手工管理容器的麻烦。我们还是用刚才的Nginx、Redis和Ubuntu这三个容器来试验一下加上--rm参数省略了name参数docker run -d --rm nginx:alpine docker run -d --rm redis:6.0.8 docker run -it --rm 你的ubuntu名字 sh然后我们用docker stop停止容器再用docker ps -a就会发现不需要我们再手动执行docker rmDocker已经自动删除了这三个容器。结语好了今天我们初步了解了容器技术以及容器技术中最关键的概念动态的容器然后使用Docker实际操作了镜像和容器运行了被容器化的Alpine、Nginx、Redis等应用。镜像是容器的静态形式它打包了应用程序的所有运行依赖项方便保存和传输。使用容器技术运行镜像就形成了动态的容器由于镜像只读不可修改所以应用程序的运行环境总是一致的。而容器化的应用就是指以镜像的形式打包应用程序然后在容器环境里从镜像启动容器。由于Docker的命令比较多而且每个命令还有许多参数一篇文章里很难把它们都详细说清楚希望你课下参考Docker自带的帮助或者官网文档Reference documentation | Docker Docs再多加实操练习相信你一定能够成为Docker高手。最后再简单小结一下主要的内容容器技术起源于Docker它目前有两个产品Docker Desktop和Docker Engine我们的课程里推荐使用免费的Docker Engine它可以在Ubuntu系统里直接用apt命令安装。Docker Engine需要使用命令行操作主命令是docker后面再接各种子命令。查看Docker的基本信息的命令是docker version和docker info其他常用的命令有docker ps、docker pull、docker images、docker run。Docker Engine是典型的客户端/服务器C/S架构命令行工具Docker直接面对用户后面的Docker daemon和Registry协作完成各种功能。容器就是操作系统里一个特殊的“沙盒”环境里面运行的进程只能看到受限的信息与外部系统实现了隔离。容器隔离的目的是为了系统安全限制了进程能够访问的各种资源。相比虚拟机技术容器更加轻巧、更加高效消耗的系统资源非常少在云计算时代极具优势。容器的基本实现技术是Linux系统里的namespace、cgroup、chroot。常用的镜像操作有docker pull、docker images、docker rmi分别是拉取镜像、查看镜像和删除镜像。用来启动容器的docker run是最常用的命令它有很多参数用来调整容器的运行状态对于后台服务来说应该使用-d。docker exec命令可以在容器内部执行任意程序对于调试排错特别有用。其他常用的容器操作还有docker ps、docker stop、docker rm用来查看容器、停止容器和删除容器。我是YYYing后面还有更精彩的内容希望各位能多多关注支持一下主包。无限进步我们下次再见。
阅读完成 · 觉得有帮助?
咨询建站