• Docker


    1、docker介绍

    1.1 docker是什么

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows操作系统的机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。
    docker原理:一次镜像,处处运行,从搬家到搬楼。把能运行的程序以及环境打包成一个镜像文件,在docker上运行。
    docker是什么:docker是一个解决了运行环境和配置问题的软件容器,方便做持续集成并有助于整体发布的容器虚拟化技术。

    1.2 容器与虚拟机的比较

    • 传统虚拟机技术 是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;
    • 容器 内的应用进程直接运行于宿主的内核,容器内没有自己的内核且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
      每个容器之间互相隔离,每个容器有自己的文件系统,容器之间进程不会相互影响,能区分计算资源。
      在这里插入图片描述

    2、docker安装

    2.1 docker 基本组成

    • 镜像(image)(类比java中的类)(将应用程序及其依赖、环境、配置打包在一起)。
    • 容器(container)(类比java中的实例对象)(镜像运行起来就是容器,一个镜像可以运行多个容器)
    • 仓库(repository)(集中存放镜像文件的地方)(国内一般都用阿里云)

    Docker镜像(lmage)就是一个只读的模板。镜像可以用来创建Docker容器,一个镜像可以创建很多容器。

    docker平台架构图解:

    在这里插入图片描述

    2.2 安装步骤

    安装地址: https://docs.docker.com/engine/install/centos/.
    我安装的是CentOS7版本

    • 卸载旧版本
     sudo yum remove docker \
                      docker-client \
                      docker-client-latest \
                      docker-common \
                      docker-latest \
                      docker-latest-logrotate \
                      docker-logrotate \
                      docker-engine
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • yum安装gcc相关
    yum -y install gcc
    yum -y install gcc-c++
    
    • 1
    • 2
    • 安装需要的软件包
    sudo yum install -y yum-utils
    
    • 1
    • 设置stable镜像仓库,用阿里云的
    yum-config-manager --add-repo \
        http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
    • 1
    • 2
    • 更新yum软件包索引
    yum makecache fast
    
    • 1
    • 安装DOCKER ce 社区版
    yum -y install docker-ce docker-ce-cli containerd.io
    
    • 1
    • 启动docker
    # 启动(执行命令后没有输出,是正常)
    systemctl start docker
    
    # 查看启动是否成功
    ps -ef | grep docker
    # 查看版本号
    docker version
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 测试
    # 启动hello-world镜像
    docker run hello-world
    
    • 1
    • 2
    • 卸载
    # 停止docker服务
    systemctl stop docker
    # 删除docker服务
    yum remove docker-ce docker-ce-cli containerd.io
    # 删除docker的一些配置文件、库、包等
    rm -rf /var/lib/docker
    # 删除docker容器等相关东西
    rm -rf /var/lib/containerd
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.3 镜像加速

    之前配置的阿里云的还是很慢,采用网易docker镜像源很快。

    sudo mkdir -p /etc/docker
    sudo tee /etc/docker/daemon.json <<-'EOF'
    {
      "registry-mirrors": [
    						"https://a3e6u0iu.mirror.aliyuncs.com",
    						"https://hub-mirror.c.163.com",
    						"https://mirror.baidubce.com"
    		      		  ]
    }
    EOF
    sudo systemctl daemon-reload
    sudo systemctl restart docker
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.4 run干了什么

    在这里插入图片描述
    doker底层原理:
    docker是一个 client-server 结构的系统,docker的守护进程运行在主机上,通过socket从客户端访问。当docker-server接收到docker-client的指令,就会执行这个命令。

    docker为什么比VM快?

    • docker有着比虚拟机更少的抽象层
    • docker利用的是宿主机的内核,而虚拟机每次都需要重新搭建一个操作系统内核

    所以说,新建一个容器的时候,docker不需要向虚拟机一样重新加载一个操作系统内核,直接用的宿主机的操作系统,省略了这个复杂的过程。

    3、docker常用命令

    3.1 帮助命令及启动命令

    # 1.启动docker
    systemctl start docker
    # 2.停止docker
    systemctl stop docker
    # 3.重启docker
    systemctl restart docker
    # 4.查看docker状态
    systemctl status docker
    # 5.开机启动
    systemctl enable docker
    # 6.查看docker概要信息
    docker info
    # 7.查看docker总体帮助文档
    docker --help
    # 8.查看docker命令帮助文档(会列出命令的可选参数)
    docker 具体命令 --help
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.2 镜像命令

    1.列出本地的镜像

    # 1.列出本地的镜像
    docker images [OPTIONS]
    -a 列出本地所有的镜像,含历史映像
    -q 只显示镜像ID
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述
    在这里插入图片描述
    2、查远方仓库某个镜像

    docker search [OPTIONS] 镜像名字
    --limit:只列出N个镜像,默认25个
    # 举例
    docker search --limit 5 redis
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述
    3、下载镜像到本地

    在这里插入图片描述

    docker pull 镜像名字[:TAG]
    TAG:版本号,默认是下载最新的latest
    
    • 1
    • 2

    在这里插入图片描述
    指定版本:
    在这里插入图片描述
    4、查看镜像、容器、数据卷所占空间

    docker system df
    
    • 1

    目前3个镜像(mysql-latest、mysql-5.7、hello-world)1个容器(hello-world)
    在这里插入图片描述
    5、删除某个镜像

    docker rmi [-f] 镜像名字(或imageID)
    rmi:就是 remove image 的意思
    注:如果删不掉,可能镜像被某个容器正在用,可以加 -f 强制删除
    
    # 删除单个镜像
    docker rmi -f 镜像[:TAG]  # 也可以按照镜像id删除
    # 删除多个镜像
    docker rmi -f 镜像[:TAG] 镜像[:TAG]  # 也可以按照镜像id删除
    # 删除所有的镜像,($符号是引用的意思)
    docker rmi -f $(docker images -qa)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    6、其他常用命令

    docker push 推送镜像到服务

    docker save 保存镜像为一个压缩包

    在这里插入图片描述

    docker load 加载压缩包为镜像

    在这里插入图片描述

    注意:多通过 --help 指令来查看命令的使用方法!

    3.3 容器命令

    有镜像才能创建容器 (java中由类才能创建对象实例)
    下载一个centos镜像来测试学习

    docker pull centos
    
    • 1

    1、新建+启动容器

    docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
    
    # 参数说明
    --name="name"   容器名字,用来区分容器
    -d   后台方式运行
    -it  使用交互方式运行,进入容器查看内容
    -p   指定容器的端口 -p 8080:8080 跟主机映射
    	四种方式: -p 宿主机端口:容器端口  (常用)
    			 -p ip:宿主机端口:容器端口
    			 -p 容器端口
    			 容器端口
    -P   大写,随机指定端口
    
    # COMMAND 说明
    例如 使用 -it 进行交互运行,交互运行肯定需要一个控制台
    可使用 /bin/bash
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    可通过如下指令启动并进入容器

    docker run -it centos /bin/bash
    
    • 1

    在这里插入图片描述
    进入之后,ls可发现,容器内就是一个自己的服务器环境!它就是我们下载的centos,就是一个小型的服务器!

    exit # 退出容器
    
    • 1

    2、查看正在运行的容器

    # 查看正在运行的容器
    docker ps
    # 查看正在运行的容器以及历史中启动过的容器
    docker ps -a
    # 查看最近创建的容器
    docker ps -l
    # 显示最近n个创建的容器
    docker ps -n 2
    # 显示当前正在运行的容器编号
    docker ps -q
    # 显示所有启动过的容器编号
    docker ps -aq
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    3、退出容器

    # run进去容器,exit退出,容器会停止
    exit
    # run进去容器,ctrl+p+q退出,容器不会停止
    ctrl+p+q
    
    • 1
    • 2
    • 3
    • 4

    通过 ctrl+p+q 退出
    在这里插入图片描述

    4、停止容器

    docker stop 容器ID(容器名)
    
    • 1

    在这里插入图片描述

    5、启动已经停止过的容器

    docker start 容器ID(容器名)
    
    • 1

    在这里插入图片描述
    6、重启容器

    docker restart 容器ID(容器名)
    
    • 1

    7、强制停止容器

    docker kill 容器ID(容器名)
    
    • 1

    8、删除已停止容器

    # 不能删除正在运行的容器(可以先stop下,再去删),也可以指定 -f 强制删除
    docker rm [-f] 容器ID(容器名)
    # 两种方式删除多个(危险)
    docker rm [-f] $(docker ps -a -q)
    docker ps -a -q | xargs docker rm
    
    • 1
    • 2
    • 3
    • 4
    • 5

    9、后台启动容器

    docker run -d centos 
    
    • 1

    在创建容器时可通过 -it 指令直接创建一个标准输入、输出终端,允许与容器交互

    docker run -d -it centos /bin/bash
    
    • 1

    10、进入正在运行的容器(后台运行的容器)(重要)

    docker exec -it 容器id /bin/bash
    
    • 1

    在这里插入图片描述
    注意:通过后台启动的容器,在exec进入容器后,当exit退出后,容器不会停止运行。

    docker exec -it 容器ID(容器名) [COMMAND]
    docker attach 容器ID(容器名)
    
    # 举例
    docker exec -it 4216d5daf8e5 /bin/bash
    docker attach 4216d5daf8e5
    两者区别:
    exec 是在容器中打开新的终端,并且启动新的进程。用exit退出,不会 导致容器的停止。(实际用的多)
    attach 直接进入容器启动命令的终端,不会启动新的进程。用exit退出,会导致容器的停止。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    11、查看容器日志

    docker logs 容器ID(容器名)
    
    • 1

    12、查看容器内运行的进程

    docker top 容器ID(容器名)
    
    • 1

    13、查看容器内部细节(已json格式展示)

    docker inspect 容器ID(容器名)
    
    • 1

    14、从容器内拷贝文件到主机上 (重要)

    docker cp 容器ID:容器内路径 目的主机路径
    
    • 1

    在这里插入图片描述
    容器内所做的修改不会因为容器的停止而消失。即容器在关闭状态下也可以拷贝

    15、部署nginx(本机通过一个端口号可访问到容器中的nginx)

    docker pull nginx # 拉取
    docker run -d --name nginx01 -p 3344:80 nginx # 启动容器,并将宿主机3344端口与容器内部端口80绑定,宿主机通过3344端口可访问到此容器。
    -d 后台运行
    --name 指定名称
    -p 宿主机端口:容器端口 (宿主机端口一般可以任意指定,容器端口一般是采用默认的,例如nginx 80 redis 6379)
    最后的 nginx 是镜像
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述
    本机自测:
    在这里插入图片描述
    端口暴漏的概念:
    首先服务器安全组开放3344端口,然后就算过了第一层防护。其次服务器防火墙要开放3344端口,过第二层防护,然后根据服务器的3344端口绑定容器中的80端口,访问到nginx服务!
    在这里插入图片描述
    需要在阿里云安全组配置3344端口:
    在这里插入图片描述
    然后就可以通过服务器公网:3344 进行访问容器内的80端口
    在这里插入图片描述

    进入容器:
    在这里插入图片描述

    16、导入和导出容器(整个容器做备份) (重要)

    docker export 容器ID > 文件名.tar
    cat 文件名.tar | docker import - 镜像用户/镜像名:镜像版本号
    
    说明:
    export 导出容器的内容流作为一个tar归档文件[对应import命令]
    import 从tar包中的内容创建一个新的文件系统再导入为镜像[对应export]
    举例:
    docker export 4a364837bb4f > myos.tar
    cat myos.tar | docker import - huige/myos:3.7
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4、Docker镜像

    4.1 镜像是什么?

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

    • 从远程仓库下载
    • 别人拷贝给你
    • 自己制作一个镜像

    4.2 分层镜像 UnionFS 联合文件系统

    Docker中的镜像是分层的,就是为了保证复用。

    Union文件系统(UnionFS) 是一种 分层、轻量级 并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下,Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像,可以制作各种具体的应用镜像。即当两个不同镜像有重叠部分时,不需要下载两次,一次即可,复用。

    特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含底层的文件和目录。

    4.3 镜像加载原理

    docker的镜像实际上由一层一层的文件系统组成
    bootfs(boot file system) 主要包含bootloader和kernel, bootloader 主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是引导文件系统bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权己由bootfs转交给内核,此时系统也会卸载bootfs,底层直接用宿主机的内核,因此会省去大量的空间。
    rootfs (root file system) 在bootfs之上。包含的就是典型Linux系统中的/dev, /proc, /bin, /etc等标准目录和文件。可以很小的只包含最基本的命令,工具和程序库,这就是一个小的 linux。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。

    4.4 重点理解

    (1)镜像下载时是分层下载的方式,如果某个层已经存在,则不会重复下载!
    例如 docker pull redis

    在这里插入图片描述
    (2)Docker镜像层都是只读的,容器层是可写的
    即 pull 下来的镜像是不会改变的,当我们通过 run 新建一个容器后,一个新的可写层被加载到镜像的顶部,这一层通常被称作容器层,容器下面的层叫做镜像层。
    所有对容器的改动,无论添加、删除、还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的。

    4.5 修改原始镜像并保存为新的镜像(生成镜像的方法)

    docker commit 根据镜像创建一个容器后,对容器做修改后,将镜像层和容器层打包并提交使之成为一个新的镜像。

    docker commit [OPTIONS] 容器id [REPOSITORY[:TAG]]
    
    例如:
    docker commit -m="这里是提交的信息" -a "作者" 容器id 目标镜像名:TAG
    
    • 1
    • 2
    • 3
    • 4

    举例:
    原始 ubuntu 没有vim命令,先去下载 ubuntu 镜像到本地,启动镜像,在容器中安装vim,然后在命令行窗口执行 commit ,生成我们自己的新镜像,启动新镜像与老的进行对比。
    过程:
    拉取镜像并创建容器运行:
    在这里插入图片描述
    发现vim不存在,安装
    在这里插入图片描述
    在这里插入图片描述
    提交新镜像,并查看内存变化
    在这里插入图片描述

    5、数据卷

    5.1 基本概述

    容器与数据耦合的问题:

    在这里插入图片描述
    针对此问题,引出数据卷 volume
    数据卷(volume)是一个虚拟目录,指向宿主机文件系统中的某个目录。其作用是将容器与数据分离,解耦合,方便操作容器内数据,保证数据安全。

    在这里插入图片描述

    操作数据卷常见命令:

    在这里插入图片描述

    5.2 挂载数据卷

    5.2.1 挂载到虚拟目录

    在这里插入图片描述

    例如做如下一个需求:在一个 nginx 容器中修改容器内的 html 目录内的 index.html 内容。通过数据卷来实现。
    (1)创建容器并挂载数据卷(当前宿主机 /var/lib/docker/volumes/html 对应 容器中 /usr/share/nginx/html)
    在这里插入图片描述
    将数据卷 html 挂载到容器内的 /usr/share/nginx/html

    (2)在宿主机查看挂载到容器内目录的内容
    在这里插入图片描述
    可发现,已经拿到 nginx1 容器中的 html目录下的内容,此时可直接在容器外部修改 index.html

    (3)修改 index.html 后,在虚拟机外部访问 master:80 访问
    在这里插入图片描述

    在这里插入图片描述

    注:当创建容器时挂载数据卷,数据卷不存在的话,会自动创建!因此我们不需要刻意去创建数据卷。

    5.2.2 挂载到主机目录

    除了挂载数据卷,还可以直接将宿主机目录挂载到容器目录!这种方式需要我们自己创建目录,即自己指定目录位置。(需要注意的是!被挂载的宿主机目录必须存在!如果不存在会报错!而容器中的目录若不存在,会自动创建!)
    比如 在宿主机目录 /home/docker/nginx/html:/usr/share/nginx/html 就完成了挂载,在宿主机此目录修改文件是,容器内部也会立即改变!

    在这里插入图片描述
    总结:

    在这里插入图片描述

    6、自定义镜像 Dockerfile

    6.1 镜像结构

    在这里插入图片描述

    在这里插入图片描述

    6.2 dockerfile

    什么是Dockerfile?
    Dockerfile 就是一个文本文件,其中包含一个个的指令,用指令来说明要执行什么操作来构建镜像,每一个指令都会形成一层Layer。
    在这里插入图片描述

    例如下面案例:
    在这里插入图片描述

    在 /tmp 目录下创建文件夹docker-demo
    在这里插入图片描述
    此案例中 Dockerfile 中内容如下:可以看到一系列指令

    在这里插入图片描述

    将如下三个文件传上去,jdk8用于配置java环境变量,docker-demo.jar 是打包的java程序。
    在这里插入图片描述

    然后执行此数据卷
    在这里插入图片描述

    可查看到镜像已经被build
    在这里插入图片描述

    创建容器(后台启动):
    在这里插入图片描述

    在浏览器访问:master:8090/hello/count:
    在这里插入图片描述
    到这里这个项目就成功的构建镜像并且部署到docker容器了!

    但是还有一个问题,因为是基于 ubuntu 镜像构建的,还是比较麻烦,配java环境。对于微服务项目,有很多个,难道都配一遍,太麻烦了。

    在这里插入图片描述
    这个镜像 docker 也已经提供了! java:8-alpine 。将Dockerfile修改成如下:

    在这里插入图片描述

    总结:
    在这里插入图片描述

    7、Docker-Compose

    7.1 基本概述

    在这里插入图片描述
    可以认为,Compose文件就是多个 docker run 命令的集合。只不过它不是直接用run,而是用另一种语法来代替了。

    例如:下面一个compose文件。与右边docker run 是等效的。

    可以不指定镜像,而是直接通过build 创建镜像,如下面的web。

    在这里插入图片描述

    7.2 安装docker-compose

    地址:https://github.com/docker/compose/releases/tag/1.25.0-rc4

    安装过后将 docker-compose 上传到 /usr/local/bin/ 目录下

    修改权限:

    chmod +x /usr/local/bin/docker-compose
    
    • 1

    Base自动补全命令:

    echo "199.232.68.133 raw.githubusercontent.com" >> /etc/hosts
    
    curl -L https://raw.githubusercontent.com/docker/compose/1.29.1/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose
    
    • 1
    • 2
    • 3

    7.3 微服务集群利用DockerCompose部署

    将之前学习 springcloud 的 cloud-demo 微服务集群利用DockerCompose部署。

    (1)查看cloud-demo文件,编写docker-compose文件

    在这里插入图片描述
    docker-compose内容:

    在这里插入图片描述
    build 是 分别执行 当前目录 user-service、order-service、gateway下面的 Dockerfile 文件,即构建了镜像!
    构建完镜像后就 run 创建容器了。

    (2)修改自己的cloud-demo项目,将数据库、nacos地址都命名为docker-compose中的服务名。

    需要注意的是,用docker-compose部署时,将来这些个服务不一定在同一台机器,因此项目中的地址不能写死。服务之间通过服务名来相互访问。

    在这里插入图片描述

    在这里插入图片描述

    (3)打包各个服务
    在这里插入图片描述
    打包:

    在这里插入图片描述
    三个服务得打包都命名为app.jar ,分别放在上面三个文件夹下。然后三个文件夹下都创建一个Dockerfile,其中内容如下。即当docker-compose文件执行时,会执行Dockerfile,并build,将各个服务创建镜像。

    在这里插入图片描述

    (4)将 cloud-demo 上传到虚拟机,利用 docker-compose -d 来部署。
    部署过程中遇到如下错误:

    • java:8-alpine 错误,需将三个服务的 Dockerfile中 的FROM java:8-alpine 改为 FROM openjdk:8-alpine
    • nacos 连接拒绝错误,报错原因是之前我部署nacos集群,端口是80,我没改,然后部署上去后就报错连接不到nacos:80,后来我删除每个服务的容器,端口改成8848重新打包上传,但还是一直报连接不到nacos:80,最后发现是镜像一直没删!只删了容器!所以每次docker-compose执行时看镜像存在就没更新,一直相当于没更新,还是80,就一直错。。。。。。。后来镜像全删除、重新执行docker-compose up -d 解决问题
    • mysql 连接密码错误。由于我在java代码设置的密码是123456,拉下来的mysql镜像设置的默认密码是123(后来我发现 docker-compose。yml 中配置的数据库密码不生效,永远默认是123),所以我进入容器,将其密码改为123456,解决问题。
      docker exec -it b87d bash 进入mysql容器
      mysql -u root -p 登录数据库,默认密码123
      use mysql 使用mysql数据库
      update user set authentication_string=password(‘123456’) where user=“root”; 修改密码
      exit 退出mysql
      service mysql restart 重启mysql
      docker restart b87d 重启docker容器

    后来看到一位博主针对 docker-compose.yml 中 设置数据库密码无效的解释:

    在这里插入图片描述

    (5)查看日志

    docker-compose logs -f 
    
    • 1

    可能会查看到错误,因为可能 nacos 还没启动时,服务先去连接了。这样的话重新启动一下服务就行了。后面遇到一直连接拒绝 nacos:8848 ,删除镜像重新拉取后好了。

    docker-compose restart gateway userservice orderservice 
    
    • 1

    (6)测试访问
    成功:

    在这里插入图片描述

    在这里插入图片描述

    7.4 使用docker-compose 搭建 nginx 和 keepalived 高可用集群

    使用docker-compose 搭建 nginx 和 keepalived 高可用集群

    docker-compose 小记

    hostname: 为每一个服务起主机名,其他服务在访问此服务时可以通过 http://hostname

  • 相关阅读:
    IIC通信
    『Java安全』Struts 2.3.14.2 action占位符OGNL注入漏洞S2-015复现与浅析
    非连续台阶状物体的仿真-含Matlab源码
    Tomcat部署与优化
    劲松中西医医院谭巍主任在线分析:HPV复阳的三大祸首
    服务器又被挖矿记录
    沃尔玛平台入驻条件,沃尔玛平台可以做哪些产品——站斧浏览器
    安全性证明
    [Power Query] 数据行列的增删、填充与替换
    P3177 [HAOI2015] 树上染色
  • 原文地址:https://blog.csdn.net/henulmh/article/details/127475459