docker 笔记一

docker 官网:https://www.docker.com/

docker概述

docker 出现的原因,解决了什么问题

在我们接触的传统项目开发中,项目或者说产品的一般拥有多套环境,开发,测试,运维。不同的阶段我们都需要在相应的服务器上重新搭建一套环境来进行使用。可能我们开发在用windows,测试和上线用到的环境是linux。我们不能很简便的通过一套统一流程来完成,需要根据具体的平台等等去相应解决。

docker的出现和应用,能有效解决上面的问题。比如我们开发一个java项目,通过docker,可以把我们的代码应用(jar包)+jre环境打包成一个docker镜像,然后在其他的环境,我们只需要通过部署这个docker镜像然后进行实例化就可以。

docker 是什么

docker 是一个开源的应用容器引擎,是一种轻量级的pass虚拟化技术。基于Go语言开发。通过docker可以让开发者打包他们的应用以及依赖环境成为一个镜像。然后上传到仓库中保存。在需要运行这个应用时,可以在任意平台环境(win/linux/mac)上获取这个镜像,并实例化一个容器使用。实例化的应用相当于一个个小沙箱,每个应用都相当于一个小型的操作系统+服务,是相互隔离的,网络,存储。因为轻量级,容器性能开销极低。

大家都用过vmware,上面的概念可能过于抽象,我们可以从相对于vmware的角度来理解docker。通过vmware我们可以在原有操作系统之上虚拟化出一个与操作系统的虚拟机,docker 其实也是这样,也是在原有系统之上,进行虚拟化,只不过相较于vmware,docker虚拟化更加轻量,docker是一种轻量的容器化虚拟技术。

vmware 与 docker 虚拟化技术对比

vmware: 传统虚拟机技术,属于type2虚拟化,虚拟出一套硬件,运行一个完整的操作系统包括系统内核,然后在这个系统之上安装运行软件,与宿主机之间完全隔离开来。资源占用十分多, 冗余步骤多, 启动很慢!

docker:容器应用直接运行在宿主机内核之上,不进行硬件的虚拟化,相比较与vmware没有硬件虚拟化,更加轻便。容器之间相互隔离,容器内拥有属于自己的文件系统,互相隔离,互不影响。

docker 应用场景

1.应用更快速交付部署

传统:一对帮助文档,安装程序。

Docker:打包镜像发布测试一键运行。

2.更便捷的升级和扩缩容

使用了 Docker之后,我们部署应用就和搭积木一样
项目打包为一个镜像,扩展服务器A!服务器B

3.更简单的系统运维

在容器化之后,我们的开发,测试环境都是高度一致的

4.更高效的计算资源的利用

Docker是内核级别的虚拟化,可以在一个物理机上可以运行很多的容器实例!服务器的性能可以被压榨到极致。

docker 基本概念

  1. 镜像(image)

    docker 的镜像是我们的程序打包出来的具体服务应用包括依赖环境,我们可以通过镜像实例化我们的服务,一个实例化的服务我们称之为容器,一个镜像可以实例化多个容器。镜像可以进行更新迭代的。

  2. 容器(container)

    容器是镜像的一个实例化。docker 利用容器化虚拟技术,独立运行一个或者一组应用在一个容器内。容器是由镜像来进行创建的。容器可以启动,删除,停止,执行基本linux命令。可以把容器理解为一个包含我们应用的简易linux系统。

  3. 仓库(repository)

    仓库是存放镜像的地方。仓库可以分为公有仓库和私有仓库。仓库起到了代管镜像的作用,这和我们的github是一样的。仓库在我们的docker中可以分为本地仓库和远程仓库。我们本地镜像在docker中的管理的位置就是本地仓。常用的远程docker仓库 docker hub,国内云服务厂商也有各自的容器仓库和容器镜像加速。

docker安装(ubutu 为例)

docker 安装与使用要参考官方文档:https://docs.docker.com/

# 卸载原有docker相关组件
sudo apt-get remove docker docker-engine docker.io containerd runc
# 更新apt依赖
sudo apt-get update
# 安装docker相关依赖
sudo apt-get install \apt-transport-https \ca-certificates \curl \gnupg \lsb-release
# 增加Docker的官方GPG密钥:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 添加docker 源官方库
echo \
"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 更新apt
sudo apt-get update
# 安装最新版docker
sudo apt-get install docker-ce docker-ce-cli containerd.io
# 运行docker hello-world demo
sudo docker run hello-world

# 安装其他版本docker
sudo apt-cache madison docker-ce # 查看其他版本docker信息
sudo apt-get install docker-ce=<VERSION_STRING> docker-ce-cli=<VERSION_STRING> containerd.io # 安装docker,把VERSION_STRING替换为版本号
# 卸载docker和残留文件
sudo apt-get purge docker-ce docker-ce-cli containerd.io
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd

docker 在ubuntu 下还支持deb包方式的安装,其他操作系统和包的安装参照官方文档。

docker 配置镜像加速服务

docker 镜像仓库默认是docker hub,docker hub 在国外,为了加速下载镜像我们可以配置阿里云镜像加速服务。

  1. 登录阿里云镜像服务器

  2. 找到镜像配置,按照阿里说明配置镜像加速服务

    sudo mkdir -P /etc/docker # 级联创建docker配置目录
    # 添加镜像仓库地址到配置文件
    sudo tee /etc/docker/daemon.json <<-'EOF'
    {"registry-mirrors": ["https://t60v9rvb.mirror.aliyuncs.com"]
    }
    EOF
    # 重新加载配置,重启服务
    sudo systemctl daemon-reload
    sudo systemctl restart docker
    

hello-world案例探究

docker run 流程

  1. docker run 运行一个镜像,实例化一个容器,会先从本地仓查看是否有镜像
  2. 本地有镜像,直接运行,实例化一个容器
  3. 本地没有,会先去远程仓库拉取,默认拉取最新版,拉取成功,实例化容器
  4. 远程没有,返回错误找不到镜像

docker 工作原理

docker 是一个client-server架构,docker的守护进程运行在服务主机上,通过socket通信从客户端访问。docker-server接收到这条docker-client指令,解释执行

为什么docker 比 vm 速度快?

1、docker有着比虚拟机更少的抽象层。由于docker不需要Hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显优势。
2、docker利用的是宿主机的内核,而不需要Guest OS(虚拟机的操作系统)。

因此,当新建一个 容器时,docker不需要和虚拟机一样重新加载一个操作系统内核。仍而避免引导、加载操作系统内核返个比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载GuestOS,返个新建过程是分钟级别的。而docker由于直接利用宿主机的操作系统,则省略了这个复杂的过程,因此新建一个docker容器只需要几秒钟。

docker基本命令使用

官方客户端命令文档和API:https://docs.docker.com/reference/,docker 命令操作需要用户有root权限

1.帮助命令

docker version # 显示docker 版本信息
docker info # 显示docker的系统信息,包括docker镜像容器和数量
docker 命令 --help # 查看命令帮助

2.镜像命令

docker images # 查看本地主机docker仓库中的docker镜像
-a # 列出所有镜像
-q # 只显示镜像id
docker search 镜像名 # 搜索镜像
-f # 通过条件过滤镜像 -f=START=3000 过滤出START大于3000的镜像
docker pull 镜像名 # 下载镜像
docker pull 镜像名:版本号 # 下载指定tag版本的镜像
docker rmi 镜像名 # 删除所有同名镜像
docker rmi 镜像id # 删除指定镜像id镜像
docker rmi -f $(docker images -aq) #删除全部的镜像
docker inspect 容器id # 查看镜像元数据信息

3.容器命令

docker run 镜像id # 通过镜像新建容器并启动docker run [可选参数] image --name="指定容器名字"-d 后台方式启动 # 通常需要以后台交互这种方式启动,否则容器会运行一下就退出结束,无法对外提供服务-it 交互方式启动 # 一旦退出交互界面,容器停止。docker 容器底层都有系统内核,可以执行基本的linux/win指令-p ip:主机端口:容器端口 # 配置服务通信端口,常用-p 主机端口:容器端口(常用)-p 容器端口容器端口-P(大写)   随机指定端口-v # 挂载数据卷 # 三种挂载: 匿名挂载、具名挂载、指定路径挂载-v 容器内路径            #匿名挂载-v 卷名:容器内路径       #具名挂载-v /宿主机路径:容器内路径 #指定路径挂载 docker volume ls 是查看不到的--link 容器名 # 添加 /etc/hosts 映射关系,支持容器名ping
docker ps # 列出所有正在运行的镜像 docker container list 与docker ps 等价-a, --all         #列出当前正在运行的容器 + 带出历史运行过的容器-n=?, --last int   #列出最近创建的?个容器 ?为1则只列出最近创建的一个容器,为2则列出2个-q, --quiet        #只列出容器的编号
exit # 容器直接结束退出
Ctrl+P+Q # 容器不停止退出 -- 常用,与容器交互完成,需要容器在后台继续服务
docker rm 容器id # 删除指定容器,容器需要先停止
docker rm -f $(docker ps -aq)    #删除所有的容器
docker ps -a -q|xargs docker rm  #删除所有的容器
docker start 容器id # 启动指定容器
docker stop 容器id # 停止指定容器
docker restart 容器id # 重启容器
docker kill 容器id # 强制杀死容器

docker 容器的补充说明

# 命令 docker run -d 镜像名
[root@iz2zeak7sgj6i7hrb2g862z ~]# docker run -d centos
a8f922c255859622ac45ce3a535b7a0e8253329be4756ed6e32265d2dd2fac6c[root@iz2zeak7sgj6i7hrb2g862z ~]# docker ps
CONTAINER ID      IMAGE       COMMAND    CREATED     STATUS   PORTS    NAMES
# 问题docker ps. 发现centos 停止了
# 常见的坑,docker容器使用后台运行,就必须要有要一个前台进程,docker发现没有应用,就会自动停止
# nginx,容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了
docker run -d centos /bin/sh -c "while true;do echo 6666;sleep 1;done" #模拟前台进程

4.日志/进程相关命令

docker logs -- help # 查看docker日志
docker logs 容器id # 查看指定容器id 的日志,通常需要配合参数使用
-tf     #显示日志信息(一直更新)
--tail number #需要显示日志条数
docker logs -t --tail n 容器id #查看n行日志
docker logs -tf 容器id #跟着日志 类似于linux 操作系统中的tail -f
docker top 容器id # 查看容器中进程相关信息

5.进入正在后台运行的容器/容器与主机文件之间的相互拷贝(后面会有数据卷)

docker 容器通常是以后台服务的形式运行的,我们有时候需要进入后台服务的容器的内部进行操作修改配置

docker exec -it 容器id/容器名  /bin/bash #进入当前容器后开启一个新的终端,可以在里面操作。(常用) 配合CTRL+Q+P/exit退出
docker exec 容器id/容器名  cat /etc/hosts #容器执行单一命令
docker attach 容器id/容器名  #进入容器正在执行的终端
docker cp 容器id:容器内路径  主机目的路径 # 拷贝容器内文件到宿主机
docker cp 主机路径 容器id:容器内路径 # 拷贝宿主机文件到容器
  attach      Attach local standard input, output, and error streams to a running container#当前shell下 attach连接指定运行的镜像build       Build an image from a Dockerfile # 通过Dockerfile定制镜像commit      Create a new image from a container's changes #提交当前容器为新的镜像cp          Copy files/folders between a container and the local filesystem #拷贝文件create      Create a new container #创建一个新的容器diff        Inspect changes to files or directories on a container's filesystem #查看docker容器的变化events      Get real time events from the server # 从服务获取容器实时时间exec        Run a command in a running container # 在运行中的容器上运行命令export      Export a container's filesystem as a tar archive #导出容器文件系统作为一个tar归档文件[对应import]history     Show the history of an image # 展示一个镜像形成历史images      List images #列出系统当前的镜像import      Import the contents from a tarball to create a filesystem image #从tar包中导入内容创建一个文件系统镜像info        Display system-wide information # 显示全系统信息inspect     Return low-level information on Docker objects #查看容器详细信息kill        Kill one or more running containers # kill指定docker容器load        Load an image from a tar archive or STDIN #从一个tar包或标准输入中加载一个镜像[对应save]login       Log in to a Docker registry #logout      Log out from a Docker registrylogs        Fetch the logs of a containerpause       Pause all processes within one or more containersport        List port mappings or a specific mapping for the containerps          List containerspull        Pull an image or a repository from a registrypush        Push an image or a repository to a registryrename      Rename a containerrestart     Restart one or more containersrm          Remove one or more containersrmi         Remove one or more imagesrun         Run a command in a new containersave        Save one or more images to a tar archive (streamed to STDOUT by default)search      Search the Docker Hub for imagesstart       Start one or more stopped containersstats       Display a live stream of container(s) resource usage statisticsstop        Stop one or more running containerstag         Create a tag TARGET_IMAGE that refers to SOURCE_IMAGEtop         Display the running processes of a containerunpause     Unpause all processes within one or more containersupdate      Update configuration of one or more containersversion     Show the Docker version informationwait        Block until one or more containers stop, then print their exit codes

docker 搭建环境使用案例

docker 安装nginx

docker search nginx # 搜索nginx镜像,

docker pull nginx # 可以后面跟:加版本号
docker images # 查看下载镜像

docker run -d --name="nginx01" -p 3344:80 nginx # 根据镜像构建容器运行,80 nginx 默认端口
docker ps # 查看正在运行中的容器

docker exec -it nginx01 /bin/bash # 后台进入容器,基本linux系统操作
cd /etc/nginx
ls

docker stop 容器id # 停止nginx
docker ps # 查看运行中的容器

docker 安装portainer: portainer 是docker图型化工具,可以通过portainer可视化面板对docker进行管理

docker search portainer
docker images
docker run -d -p 8082:9000 \
--restart=always --name="portainer" -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer # 9000 portainer 默认端口
docker ps

可以通过web面板对我们的docker 进行可视化操作

docker镜像原理之联合文件系统

镜像是一种轻量级,可执行的独立软件包,用于打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有的内容,包括代码,运行时的库,环境变量和配置文件。所有的应用,可以直接打包成docker镜像跑起来。

获取docker镜像的方式:1.远程仓库下载 2.拷贝别人镜像 3.自己制作一个DockerFile

unionfs 联合文件系统:union 文件系统是一种分层,轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下,联合文件系统是制作docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像,可以制作各种具体的镜像。特性:一次同时加载多个文件系统,但从外面看起来是一个整体文件系统,联合加载会把各层的文件系统叠加起来形成最终包含底层文件和目录的文件系统。

docker镜像加载的原理:docker 的镜像是由一层层的文件系统组成的,这种层级文件系统,就是我们说的联合文件系统unionfs:

boots(boot file system)主要包含 bootloader和 Kernel, bootloader主要是引导加 kernel, Linux刚启动时会加bootfs文件系统,在 Docker镜像的最底层是 boots。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs转交给内核,此时系统也会卸载bootfs。
rootfs(root file system),在 bootfs之上。包含的就是典型 Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。 rootfs就是各种不同的操作系统发行版,比如 Ubuntu, Centos等等。

问题:平时我们安装进虚拟机的CentOS都是好几个G,为什么Docker这里才200M?

对于个精简的OS,rootfs可以很小,只需要包合最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。由此可见对于不同的Linux发行版, boots基本是一致的, rootfs会有差別,因此不同的发行版可以公用bootfs.

虚拟机是分钟级别,容器是秒级!

docker 镜像原理之分层加载

我们在在下载一个镜像,观察日志输出,可以看到是一层层的下载

docker 镜像采用这种分层结构,带来的最大的好处是资源共享,宿主机上只需要保留一份base基础镜像,同时内存也只需要加载一份,这样所有的镜像容器在启动应用时就可以共享这一份base镜像。并且镜像的每一层都是可以共享的,只要有容器运行需要。查看镜像分层的命令可以通过docker image inspact命令查看。

理解:

所有的docker镜像都起始于一个基础镜像,当进行修改或添加新的内容时,就会在当前镜像层之上创建新的镜像层。

举一个简单的例子,假如基于 Ubuntu Linux16.04创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加 Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创健第三个镜像层该像当前已经包含3个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。

在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点。

在添加额外的镜像层时,镜像始终是保持当前所有镜像的组合。下图中举了一个简单的例子,每个镜像层包含3个文件,而镜像包含了来自两个镜像层的6个文件。

上图中的镜像层跟之前图中的略有区別,主要目的是便于展示文件下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层中的文件7是文件5的一个更新版

文种情況下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中,Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。

Linux上可用的存储引撃有AUFS、 Overlay2、 Device Mapper、Btrfs以及ZFS。顾名思义,每种存储引擎都基于 Linux中对应的
件系统或者块设备技术,井且每种存储引擎都有其独有的性能特点。

Docker在 Windows上仅支持 windowsfilter 一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和CoW [1]。

下图展示了与系统显示相同的三层镜像。所有镜像层堆并合井,对外提供统一的视图。

特点:

docker 镜像都是只读的,当容器启动时,一个新的可写层加载到镜像的顶部,这一层就是我们所说的容器层,容器之下都叫镜像层。我们在对当前的容器层进行修改,进行打包发布时,现在的容器层会变成新容器的只读镜像层。

docker commit 镜像

docker 可以理解为我们的版本控制系统,我们对一个容器进行了修改,可以通过commit命令打包成一个新的命令推送到我们的本地image仓库中,也可以push推送到远程仓库中。如果你想要保存当前容器的状态,就可以通过commit来提交,获得一个镜像,就好比我们我们使用虚拟机的快照。

docker commit 提交容器成为一个新的副本# 命令和git原理类似
docker commit -m="描述信息" -a="作者" 容器id 目标镜像名:[版本TAG]

实例:

docker 安装tomcat并打包镜像

docker search tomcat
docker pull tomcat:8.5
docker run -d --name="tomcat01" -P 8080:8080 tomcat:8.5 # 后台启动tomcat,完成宿主机与容器网络的端口映射
docker ps -a

此时我们发现访问宿主机的8080端口没有出现tomcat主页而是报了tomcat的404界面

解决方式

docker exec -it tomact /bin/bash # 后台方式进入容器
# 容器内执行如下命令,原因是docker的Tomcat镜像webapp下没有内容,内容在webapp.dist目录下
cd /usr/local/tomcat
cp -r webapp.dist/* webapp
exit
docker ps # 退出查看容器运行状态

再次通过浏览器访问tomcat成功出现主页,成功

docker stop 容器id
docker commit -a="ldy" -m="添加webapp" 容器id 镜像名:版本号
docker images

docker 笔记一相关推荐

  1. Docker笔记:常用服务安装——Nginx、MySql、Redis(转载)

    转载地址:https://www.cnblogs.com/spec-dog/p/11320513.html 开发中经常需要安装一些常用的服务软件,如Nginx.MySql.Redis等,如果按照普通的 ...

  2. Docker笔记——jdk镜像制作

    Docker笔记--jdk镜像制作 openjdk镜像依赖如下: openjdk:8-jdk -> buildpack-deps:jessie-scm -> buildpack-deps: ...

  3. Docker笔记四 发布自制DockerImage 到 Dockerhub

    Docker笔记 四 发布自制DockerImage 到 Dockerhub 1.注册Dockerhub账号 https://hub.docker.com/ 2.制作准备上传的dockerp_w_pi ...

  4. docker 笔记(2) -- 镜像

    docker 笔记(2) -- 镜像 Ubuntu 14.04 无论如何,飞蛾扑火都是一种高贵的姿态. 参考 菜鸟教程 -- docker 额 当使用的镜像文件本地不存在时,则从远程镜像库中下载,默认 ...

  5. Docker笔记三 Docker镜像制作

    Docker笔记三 Docker镜像制作 1.Docker镜像制作方法: docker commit 保存当前container的状态到镜像,生成p_w_picpath. docker build 利 ...

  6. Docker笔记01-发布一个dotnetcore应用

    Docker笔记01-发布一个dotnetcore应用 原文:Docker笔记01-发布一个dotnetcore应用 OS:Widows 10 IDE: VS2017 Docker:Docker De ...

  7. 尚硅谷周阳老师Docker笔记

    链接:https://pan.baidu.com/s/1UZD4vDzGzTK8YM9p37TKew 提取码:gjgj 我的Docker笔记 Docker学习笔记(一)–基础篇 Docker学习笔记( ...

  8. Docker笔记2 容器及其基本操作

    基础概念 容器是镜像的一个运行时的状态.镜像是静态的只读文件,容器是带有运行时需要的可写文件层,容器中的应用进程处于运行状态. 容器和虚拟机的差别: 容器:独立运行的一组应用以及这组应用所必须的运行环 ...

  9. Docker笔记(四)网络

    本笔记是记录一些学习微信公众号CloudMan的Docker的文章.本篇介绍容器的网络. 网络 Docker 安装时会自动在 host 上创建三个网络,可用 docker network ls 命令查 ...

最新文章

  1. R语言构建logistic回归模型并评估模型:模型预测结果抽样、可视化模型分类预测的概率分布情况、使用WVPlots包绘制ROC曲线并计算AUC值
  2. 合肥高校计算机协会联盟部门
  3. base--AuditResult
  4. pyecharts第六节、水球图
  5. 苹果加入AOM联盟 AV1获全主流生态平台支持
  6. 【鲲鹏来了】华为云鲲鹏弹性云服务器 KC1一文全掌握(2)
  7. windows c++ 原子操作_高分辨质谱数据处理操作篇
  8. ajax跨域获取数据后处理,简单实现ajax获取跨域数据
  9. 报表自动化就是连接数据库?错,它打开了数据仓库的大门
  10. zabbix安装与配置
  11. Robot Framework自动化测试用具 Wait Until Keyword Succeeds关键字使用案例
  12. sqlserver 附加数据库方法
  13. 新仙女木事件和农耕文明_后农业文明的排列
  14. Spring系列之静态代理、动态代理、cglib代理与Spring AOP的处理
  15. Java学习之代码扫描工具的使用方法
  16. Maven 标签scop值配置介绍
  17. VBA提取字符串纯数字的方法
  18. 边角地“变废为宝” 重庆首批社区体育文化公园交付使用
  19. 在线付费问诊互联网医院智慧医疗系统包含哪些功能
  20. 【happyz】MATLAB-FM仿真01

热门文章

  1. 数据检索的玄铁剑——索引
  2. 皮质层特异性标志物——RELN抗体参数说明
  3. Webmin与CSF的安装与配置
  4. ThinkPHP5聚合数据短信验证码接口实现注册/忘记密码功能
  5. linux 编译mqtt静态库_Windows 编译 MQTT C++
  6. Git拉取pull request到本地命令
  7. 大数据仓库-kudu
  8. 互联网视频通话的社交应用
  9. 简历照片底色要求是什么?怎么修改证件照底色?
  10. c++如何解决段错误 (核心已转储)