旭日东升百花鸣,手持Docker万里行

白昔月 · · 1260 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

前言

区块链技术因为比特币的疯狂而被世人所熟知,最近我司也要准备上区块链项目了。不过在区块链的技术选型上,我与同事产生了分歧。我之前是一个nodejs主义的捍卫者,本来计划上一个基于lisk的区块链项目,不过,在与同事们的交谈中,我了解到了一个新的区块链架构,hyperledge,区块链项目名为fabric。hyperledge fabric用golang开发,基于docker部署。为了能快速深入学习整个框架,我决定开始做技术积累,并写下我积累过程中的技术博客。本文是整个技术博客的开篇文章,我准备从docker入手,一点一点的分析、研究hyperledge这个区块链项目。鉴于对市面上相关技术类中文博客的了解,我打包票,接下来我将写一篇非常棒的关于docker的入门文章。我相信,只要掌握了这篇文章所讲的知识,对于大多数想要学习docker的人就足够了,特别是初入职场的开发们,读完这篇文章,就会让你成为半个docker专家。That's right,大家准备好了吗?让我们开始吧。

痛点

上初中那会,第一次安装win或者linux的时候,还真的是费了一番功夫。各种配置、各种驱动程序,麻烦的要死。当时我就在想,要是能有个一键安装的工具就好了。win、linux安装麻烦,估计也是大家的痛点,于是,歪果仁们发明了虚拟化技术,你可以拷贝着你自己的镜像到处使用,只需要挂载好你的镜像即可。再后来,有一个工具更加方便,都不需要你自己带着镜像了,直接通过仓库下载镜像,然后本地导入就能使用了,这个工具就是docker。

docekr的理念与早期的java很像,Build,Ship,and Run Any App,Anywhere。这条标语道出了只做一次的思想,这种只做一次的思想也是IT行业的共识。在云技术大行其道的今天,只做一次已经不单单是一种思想那么简单了,它早已成为IT行业的最佳实践。大家可以设想这样一个场景,云技术运维人员面对上万台服务器的运维场景,他们该如何运维呢?一台服务器、一台服务器的去做运维吗?肯定不会。他们会利用虚拟化技术,做好一个节点的运维工作,然后打包,并为其他同样的节点安装相同的镜像。docker是在众多虚拟化技术中脱颖而出的一款工具。利用docker的容器+虚拟化特性,可以进一步提高运维效率,大大的降低运维成本和难度。


docker

在还没有云技术的年代,其实类似“云”的概念早已提出。人们利用vmware等工具,进行服务器的虚拟化工作,并取得了一定的成果。但是,这样的工具与操作系统内核的契合性不足,需要在操作系统上安装一个虚拟机系统,这样就造成了不必要的资源浪费。随着云技术的发展,硬件管理的问题逐步得到解决。但是,面对分布式、虚拟化、大数据的冲击,仅仅通过硬件升级和软件局部调优已经不能突破性能瓶颈了,这时,人们改变了DevOps思路,发明了docker这种与操作系统内核紧密联系的容器,最大限度的榨取了系统资源,从而突破了性能瓶颈,解决了一直以来困扰运维人员的技术难题。

从另外一个角度来说,在一台服务器上运行上百个虚拟机系统那简直是痴人说梦,但是,在一台服务器上运行上千个docker容器却已经成为现实。这也要归功于docker与操作系统的紧密联系。通过docker容器的这个特性,为运维工作带来无限可能。

docker

docker是由dotCloud开发的golang程序,是仅次于openstack的云计算开源项目,它的官网是docker.com,学习资料多半都来自于这个网站,github网站是github.com/docker,现在还有一个moby项目和dockerforge工具组件集。docker的主要目标是通过对应用组件的封装(packaging)、分发(distribution)、部署(deployment)、运行(runtime)等生命周期的管理,达到应用组件级别的一次封装到处运行。这里的应用组件,既可以是一个web应用,也可以是一整套数据库服务,甚至是一个操作系统或者编译器。docker引擎的基础是Linux容器(Linux Containers,LXC),这也是docker高效的原因所在。既然已经说了这么多容器,那么容器到底是什么呢?简单来说,容器是有效地将由单个操作系统管理的资源划分到孤立的组中,以便更好的在孤立的组之间平衡有冲突的资源使用需求的工具。与传统的虚拟化工具相比,docker容器(LXC)既不需要指令级模拟,也不需要即时编译,它可以在核心cpu本地运行指令,而不需要任何专门的解释机制。此外,也避免了准虚拟化(paravirtualization)和系统调用替换中的复杂性。

举个例子,例如使用LAMP来开发web app,需要配置大量的服务器资源,如果需要迁移,那么管理这些的成本都是空前庞大的。使用docker,可以通过容器对于已有的环境进行打包,迁移的话,只需要在新的服务器上启动需要的容器即可。这样开发和运维人员就可以只进行一次开发、配置,然后在任何服务器的docker中正常使用了。

docker特点归纳

特点 说明
快速部署 开发人员首先在开发环境docker中快速构建开发环境,运维人员只需要在服务器安装好docker,就可以轻松实现在相同的环境下部署应用
高效利用资源 因为是基于linux内核的虚拟化工具
高适应性 在各种环境,如云,个人电脑等都可以安装
增量更新 使用dockerfile增量更新,配置小幅度修改即可
容器隔离 一台服务器可以起到n个容器
安全镜像 利用linux防护实现安全和镜像签名

docke安装

因为,我的服务器是CentOS因此,本处只做CentOS的安装介绍,其他的安装方法,请大家参考docker官方网站


docke安装

在CentOS上安装docker

此处介绍Docker CE的安装方法,步骤如下:

步骤 行为 指令
1 查看linux版本 lsb_release -a
2 创建和设置仓库

指令
3 安装docker sudo yum -y install docker-ce
4 启动docker sudo systemctl start docker
5 测试docker是否安装成功 sudo docker run hello-world

注意:创建和设置仓库的指令如下,方便大家直接复制粘贴

sudo yum install -y yum-utils
sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
sudo yum makecache fast

docker核心概念

docker的核心概念是image、container和repository。下图展示了docker的核心概念以及彼此之间的关系。


docker核心概念与docker的命令关系

docker的命令和参数

在深入学习docker之前,先来看看docker的命令和参数,看看docker都能干什么

命令

docker命令一般使用docker-cmd或者docker cmd的形式

cmd 说明
attach 进入容器并读写容器内容,但是多个attach命令会同时操作一个容器
exec 独立运行容器内shell
build 利用dockerfile创建镜像
commit 利用容器构建镜像
create 利用镜像创建一个不运行的容器
cp 从容器中复制文件到宿主系统中
diff 检查一个容器文件系统的修改
events 从服务器端获取实时事件
export 导出容器快照
history 显示镜像历史
images 展示镜像
ps 展示容器
info 显示相关系统信息
inspect 显示容器详细的底层信息
login 注册或登录到docker仓库服务器
logout 从docker仓库服务器登出
logs 获取容器的log信息
save 备份本地容器,格式为tar
load 加载本地容器
pause 暂停容器进程
unpause 将进程从暂停状态恢复
port 查找一个nat到一个私有网口的公共口
pull 获取镜像或者仓库
push 上传镜像或者仓库
restart 重新启动容器
rm 删除容器
rmi 删除镜像
search 搜索镜像
run 创建并启动容器
start 启动容器运行
stop 停止容器运行
kill 关闭运行的容器,包括进程和相关资源
tag 修改镜像标签
top 查看一个容器正在运行的进程信息
version 展示docker版本信息
wait 阻塞直到一个容器终止,然后输出它的退出符

参数

cmd 说明
-D=true orfalse 使用debug模式,默认为false
-H,--host=[unix:///var/run/docker.sock],tcp://[host:port] 在daemon模式下绑定socket,通过host来指定地址
--api-enable-cors=true or false 在远端api中启用cors头
-b="" 将容器挂载到一个已经存在的网桥上
--bip="" 让动态创建的docker0采用给定的CIDR地址与-b互斥
-d=true or false 使用daemon模式
--dns="" 为docker分配给定的dns
-g="" 指定docker运行时的root路径,默认为/var/lib/docker
--icc=true or false 启动容器间通信
--ip="" 绑定端口时候的默认ip地址
--iptables=true or false 禁止docker添加iptables规则
--mtu=VALUE 指定容器网络的mtu,默认1500
-p="" 指定daemon的pid文件路径,默认为/var/run/docker.pid
--registry-mirror=:// 指定一个注册服务器的镜像地址
-s="" 强制docker运行时使用给定的存储驱动
-v=true or false 输出版本信息
--selinux-enabled=true or false 启用SELinux支持

下面我们就来看看这些命令和参数是如何应用在docker中的。

image

image是一个面向docker引擎的,包含了文件系统的只读模板,例如,包含完整ubuntu的ubuntu镜像,安装了apache的apache镜像等。下边,我们来看一下基于image的一系列操作:

docker+操作命令 说明
pull 获取镜像
run 运行镜像
images 查看镜像
tag 变更标签
inspect 查看镜像详细信息
search 查找镜像
rmi 删除镜像
ps -a 查看本机运行的全部容器
commit 创建镜像
save 备份镜像,将会备份全部的镜像信息、历史记录、日志等,与export导出快照有很大区别
laod 载入镜像
push 上传镜像

下面我们结合着例子具体看一下操作命令应该如何使用:

获取镜像

镜像是docker运行容器的前提,在docker运行容器前,需要存在本地镜像,如果不存在,docker会先从用户配置的默认镜像仓库下载(如果没有设置的话,一般情况下是Docker Hub)

使用docker pull从网络上下载镜像格式为docker pull NAME[:TAG],如果不显示的制定TAG,则默认选择latest标签。

sudo docker pull ubuntu
sudo docker pull ubuntu:14.04
sudo docker pull dl.dockerpoll.com:5000/ubuntu

下载镜像

注意:下载过程中可以看出,镜像文件一般由若干层组成,行首的字符串代表了各层的ID。这些层是一个个的Layer,是AUFS(Advanced Union File System)中的重要概念,通过AUFS来实现增量保存和更新。

运行镜像

使用run就可以运行镜像了,前提也是镜像一定要存在,否则报错

sudo docker run -t -i ubuntu /bin/bash
sudo docker run ubuntu echo 'hello world'

参数说明

参数 说明
-t 为docker分配伪shell(pseudo-tty)
-i 打开容器的标准输入
-d 后台守护进程,后边有详细说明

运行镜像

查看镜像

使用查看镜像命令可以列出本地主机上已经保存的镜像。

sudo docker images

查看镜像
表头 说明
REPOSITORY 镜像来源仓库
TAG 版本标签
IMAGE ID 唯一ID
CREATED 镜像在仓库中的创建时间
VIRTUAL SIZE 镜像大小

变更标签

为了更好的管理标签,还可以使用标签变更命令

//格式
sudo docker tag image[:tag] [registry-host/][username/]image-name[:tag]
sudo docker tag ubuntu ubuntu:mytag

Paste_Image.png

查看镜像详细信息

命令格式docker inspect [OPTIONS] NAME|ID [NAME|ID...]

sudo docker inspect ebcd9d4fca80

详细信息全部
//如果想查看详细信息,可以使用下面这条指令
sudo docker inspect -f {{".Architecture"}} ebcd9d4fca80

详细信息

查找镜像

通过search命令来查找存在于仓库中的镜像,该命令有几个参数,先来看一下参数

参数 说明
--automated=false 仅显示自动创建的镜像
--no-trunc=false 输出信息不截断显示
-s,--stars=0 制定星级,显示设定星级以上的镜像
sudo docker search mysql
sudo docker search mysql --automated=false
sudo docker search mysql --no-trunc=false
sudo docker search mysql --filter=stars=0

查找镜像

删除镜像

通过标签或者ID来删除镜像

sudo docker rmi ubuntu:mytag
sudo docker rmi ubuntu:ebcd9d4fca80

删除镜像

注意,当某个镜像创造的容器存在时,是无法删除该镜像的,可以通过sudo docker ps -a来查看本机存在的全部容器。


查看本机存在的全部容器


如果想要删除这个镜像的话,可以使用-f参数。sudo docker rmi -f ebcd9d4fca80


Paste_Image.png


这样做,就会先删除容器,然后再删除镜像

注意:书中有一个sudo docker rm e81,看不懂。p22/36

创建镜像

创建镜像有三种方法,基于已有镜像的容器创建、基于本地模板导入、基于Dockerfile创建

1.基于已有镜像的容器创建新的镜像

这个命令的格式为:sudo docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

参数包括:

参数 说明
-a,--author="作者" 添加作者信息
-m,--message="" 提交信息
-p,--pause=0 提交时暂停容器运行

那么如何创建呢?按照下方的步骤走

1.先要有个镜像
2.用这个镜像做个容器
3.对这个容器镜像改变
4.使用commit命令创建新的镜像

//1.先要有个镜像
sudo docker pull ubuntu
sudo docker images
//2.用这个镜像做个容器,换句话说就是运行这个镜像,程序会自己建个容器
sudo docker run -ti ubuntu /bin/bash
//3.对这个容器做出改变,比如增加个文件啥滴,也可以试试不改变,看看会不会报错
touch test 
ls
exit
sudo docker ps -a
sudo docker commit -m "a new image" -a "ryan" b156aa17c90c test
sudo docker images

基于已有镜像的容器创建

注意,此处如果不小心执行了两次相同的命令,那么问题就来了,会产生箭头1的那个情况,出现<none>这个标签,箭头2是通过不同的镜像来覆盖,箭头3是不做任何改变通过镜像直接复制出一个新的镜像来


注意事项

2.基于本地模板导入

直接通过操作系统的镜像文件来制作docker image,此处只做了解,不推荐使用,命令为

sudo cat ubuntu-14.04-x86.tar.gz |docker import - ubuntu:14

3.基于Dockerfile创建(重点)

前边两个是不太重要的镜像创建方法,因为,这样的镜像多半是别人创建好的,下边,我们来说一下用户自定义镜像创建方法,在这里,我们使用Dockerfile来快速创建。

Dockerfile是docker的脚本或者批处理脚本,需要你自己创建一个dockerfile文件,然后按照脚本语法要求写出镜像生成的流程语句。这个脚本包含了很多的预制指令,下面我们就来先说说这些指定

dockerfile的脚本指令
指令 说明
form 指定一个或者多个镜像来源
maintainer 添加维护者信息
workdir 为run、cmd、entrypoint等指令配置工作目录
run 在默认shell下运行镜像,生成容器。用\换行
cmd 指定启动时执行的命令格式
expose 为容器设置暴露的端口
env 设置环境变量
add 将复制指定的<src>到容器中的<dest>中
copy 将复制指定的<src>到容器中的<dest>中
entrypoint 指定启动时执行的命令格式1,不被run参数覆盖
volume 创建挂载点
user 指定linux的运行用户或者uid
onbuild 配置当前所创建的镜像作为其他新创建镜像的基础镜像
from

第一条指令必须是from,如果在同一个dockerfile中创建多个镜像,可以使用多个from指令,格式为:

form image [:tage]
maintainer

指定维护者信息,格式为:

maintainer name
env

设置静态变量。格式为:

env key value
env god_v 99.0
run echo $god_v
run

在from指定的镜像上执行指令命令,并提交为新的镜像,格式为:

run comand
//or
run ["executable","param1","param2"...]
run ["/bin.bash","-c","echo hello"...]
cmd

如果没有在run中指定运行命令,那么就会执行dockerfile中的cmd命令。该命令有三种格式,但是美国dockerfile文档中只能有一条cmd,格式为:

cmd  ["executable","param1","param2"...]
cmd  "executable","param1","param2"...
cmd  ["param1","param2"...]
entrypoint

除了不能被覆盖,其他与cmd功能一样。格式为:

entrypoint  ["executable","param1","param2"...]
entrypoint command,"param1","param2"...
expose

告诉docker服务端容器暴露的端口号,供互联系统使用。在启动容器时需要通过-p,docker主机会自动分配一个端口转发到指定端口,使用-p,则可以具体指定哪个本地端口映射过来。格式为:

expose port [port...]
expose 22 80 8441
add

复制src到dest。格式为:

add src dest
copy

复制src到dest,如果不存在则自动创建。格式为:

copy src dest
volume

创建一个数据管理,为数据库或者其他数据文件服务。格式为:

volume ["/data"]
user

指定linux的运行用户或者uid。格式为:

user daemon
workdir

为run、cmd、entrypoint等指令配置工作目录。格式为:

workdir /path/to/workdir
workdir /a
workdir b
workdir c
run pwd
//show
/a/b/c
onbuild

配置当前所创建的镜像作为其他新创建镜像的基础镜像,使用onbuild构建的镜像,需要在镜像文件的尾部表明。格式为:

onbuild instruction
//可以使用如下内容先创建一个基础镜像
onbuild  add ./app/src
onbuild run build --dir /app/src
//等价于
from 基础镜像
//自动执行
add ./app/src
run build --dir /app/src
构建dockerfile脚本

一般分为四部分:基础镜像信息、维护者信息、镜像操作指令、容器启动时指令

#nginx
#
#version 0.0.1
from ubuntu
maintainer ryan
run apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server
创建docker镜像

格式为:

sudo docker build [param...] path
//指定dockerfile路径为a,镜像标签为b
sudo docker build -t b a

执行dockerfile


执行dockerfile

执行完之后创建的cc镜像


执行完之后的cc镜像

备份镜像

将docker镜像备份到本地

sudo docker save -o ubuntu_14.04.tar ubuntu:14.04

备份镜像

载入镜像

从本地保存的镜像文件导入一个镜像

sudo docker load --input ubuntu_14.04.tar
//或者
sudo docker load < ubuntu_14.04.tar

载入镜像

上传镜像

将本地构建的镜像上传到默认的镜像仓库。(上传镜像这部分的演示,将会与后面的创建本地repository相结合)

sudo docker tag test:latest user/test:latest
sudo docker push user/test:latest

container

容器是镜像的一个运行实例,并提供可写文件层(时刻记住,镜像是只读模板)。docker的容器是直接提供应用服务的组件,也是docker实现快速的启动、停止以及提供高效服务性能的基础。

docker+操作命令 说明
create 新建容器
start 启动容器
restart 终止一个运行的容器,然后再启动它
run 新建并启动容器,在镜像那一节,这条指令叫做运行镜像
logs 查看日志
stop 默认等待10秒后,停止容器运行
kill 直接杀死容器进程,停止容器运行
attach 同步进入容器,如果多个窗口进入的话,将会同步显示
exec 独立进入容器,不同步容器信息
使用nsenter工具 类似于win桌面程序的一个shell,可以通过它连接到容器
rm 删除容器
export 导出容器快照,不保留历史信息,只记录导出时的容器状态
import 导入容器快照

新建并启动容器

新建并启动容器有两种方法,一个是使用create + start,一个是直接使用run,换句或说,run与create + start是等价的。这个过程docker执行了查找镜像、创建容器、分配文件系统、为只读镜像挂载可写层、配置网桥接口桥接一个虚拟接口分配给容器、从地址池分配一个IP地址给容器、执行用户指定或者默认的应用程序、执行完毕后等待用户终止或继续操作容器。在进入容器的shell中,可以执行ls、ps、exit等命令。

sudo docker create -it ubuntu:latest
sudo docker ps -a
sudo docker start ubuntu:latest
//或者
sudo docker run ubuntu

docker守护进程

生产环境下,docker需要在linux守护进程(Daemonized)的状态下运行,通过增加-d参数来实现。

//-c参数是增加要执行的命令
sudo docker run -d ubuntu /bin/sh -c "while true;do echo hello world;sleep 1;done"
sudo docker ps -a
//logs可以是CONTAINER ID也可以是NAMES 
sudo docker logs -f 3029eee9645e

docker守护进程

终止容器

使用stop来终止容器,格式为sudo docker stop [-t|--time[=10]],这个命令会先向容器发送sigterm信号,等待一段时间后(默认为10秒),再发送sigkill信号终止容器。

sudo docker stop loving_mclean

还可以使用kill直接杀死进程,不过一般情况下,不要这样做。


Paste_Image.png

进入容器

在使用-d参数启动后台进程时,可以通过attach(同步更新各个窗口的信息)、exec(不同步更新各个窗口的信息)、以及nsenter工具进入容器。

sudo docker run -idt ubuntu
sudo docker ps
//同步更新
sudo docker attach loving_mclean
//不同步更新
sudo docker exec loving_mclean

同步更新
sudo docker exec -it loving_mclean /bin/sh

此处需要注意,使用exec要有两个参数


不同步更新

删除容器

使用rm来删除终止状态的容器,格式为sudo docker rm [options] container [container...]

参数 说明
-f,--force=false 强行终止并删除一个运行中的容器
-l,--link=false 删除容器的连接,但保留容器
-v,--volumes=fasle 删除容器挂载的数据卷
sudo docker rm hardcore_aryabhata keen_bhaskara

Paste_Image.png

此处要注意,如果没有使用-f参数,不能删除没有停止的容器

导出容器快照

在容器运行或者停止的状态导出容器,格式为sudo docker export container > tag_tar_file

sudo docker export cocky_curie > cocky_curie.tar

导出容器

导入容器快照

sudo docker import cocky_curie.tar

导入容器快照

repository

registry是存放repository的具体服务器,每个registry上可以有多个repository,每个repository下面又会有多个image,例如前面我们看到的dl.dockerpool.com/ubuntu,dl.dockerpool.com就是registry的地址,ubuntu就是repository名。一般情况下,一个repository内存放的是不同版本的同种image。repository还分公有和私有,之前的例子使用的都是公有repository。


仓库和注册服务器

repository又分为公共和私有两种,公共的有docker hub、docker pool等,用户可以将自己的镜像push自己的容器到repository,在用pull拉到这个镜像。下面我们来重点讲解一下docker hub和docker pool。

Docker Hub

docker官方的repository,可以执行docker login进行注册和登录。注册成功后,本地用户目录的.dockercfg中将保持用户的认证信息。search、pull等操作不需要登录,push操作需要登录。

配置docker hub automated builds(自动创建)

automated build 使用户可以通过docker hub指定跟踪一个目标网站(如github、bitbucket等)上的项目,一旦项目发现新的提交,则自动执行创建。
配置自动创建的步骤如下:

1.创建并登陆docker hub,关联目标网站
2.在docker hub中配置一个automated builds
3.选取一个目标网站中的项目(需要包含dockerfile)和分支
4.指定dockerfile的位置,并提交创建
5.在docker hub的automated builds页面跟踪每次build的状态

Docker Pool

Docker Pool国内的docker社区,也提供镜像下载等服务,另外,该网站提供的镜像与官方镜像一致,可以作为墙后的备选方案。

创建私有repository并管理

执行命令sudo docker run -d -p 5000:5000 -v /opt/data/registry:/tmp/registry registry

参数 说明
-p 监听端口
-v 指定镜像存放的路径,不写该参数,镜像默认存放在/tmp/registry下,-v也是创建data volume的命令,可以创建多个data volumes

创建私有repository并管理

这样本地repository就建立好了,之后可以通过其他服务器来建立上传下载功能了

container数据管理

container数据管理有两种形式:Data Volumes(数据卷)和Data Volume Containers(数据卷容器)

Data Volumes

Data Volumes是一个可供容器使用的特殊目录,它绕过文件系统,可以提供很多有用的特性,如:

特性 说明
共享 Data Volumes可以在容器间共享和重用
迅速 对Data Volumes的修改会瞬间生效
独立 更新不影响镜像,Data Volumes独立存在,持续供应容器使用

在容器中创建data volume 需要使用-v。多次使用-v可以创建多个数据卷。格式如下:

sudo docker run -d -P 8441 --name web -v /webapp training/webapp python app.py

Data Volume Containers

容器间共享数据,需要使用data volume containers

尾记

本文系统的讲解了docker怎么用,对于新手来说,算是非常好的入门文章了。关于docker如何用好的文章我会继续研究,继续分享的。感谢大家的阅读。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:白昔月

查看原文:旭日东升百花鸣,手持Docker万里行

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1260 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传