热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

docker知识点全解

《docker安装》一.docker解决问题(为什么使用docker):1.运行环境不一致:比如开发人员在开发环境下使用的是jdk版本1.8,而运维部署项目使用的

《docker安装》


一.docker解决问题(为什么使用docker):

1.运行环境不一致:比如开发人员在开发环境下使用的是jdk版本1.8,而运维部署项目使用的是jdk版本1.6,就会到时程序出现问题,又比如开发版本tomcat过高,运维环境下tomcat版本过低,导致应用程序中配置的一些tomcat参数低版本tomcat不支持问题或开发程序在本地引用系统环境变量而运维环境却没配置引用环境变量等问题等等一系列的问题;

2.隔离运行的应用:比如多个应用部署在linux上(linux是多租户操作),如果其中一个应用出现bug死循环,疯狂的吃内存,这样会导致其它应用占用的内存越来越小,最终导致所有部署在这一台linux上的所有应用都会被挂掉。单docker就相当于一个小型的linux系统,当一个docker部署这个应用时,就算这个应用出现了这样的问题,也不会影响在此linux上其它docker部署的应用,因为docker本身就类似于一个独立的小型的linux,已经限定分配了大小。

 


二.容器,镜像与仓库之间的关系的理解

1.一个镜像可以创建多个容器

2.镜像与容器之间的关系对比java实例
如有一个PersonBean类,如果PersonBean是镜像。
PersonBean person1 = new PersonBean();
PersonBean person2 = new PersonBean();
PersonBean person3 = new PersonBean();
那么person1,person2,person3就是容器;就是说‘容器是用镜像创建的运行实例’。
容器可以停止,开始,删除。每个容器都是相互隔离的。可以把容器看做一个简易版的Linux环境。

3.仓库是集中存放镜像文件的场所。

 


三.docker常见的命令 

1.镜像命令

①docker images:列出本地镜像。
语法:docker images [OPTIONS] [REPOSITORY[:TAG]]
OPTIONS说明
-a :列出本地所有的镜像(含中间映像层,默认情况下,过滤掉中间映像层);
--digests :显示镜像的摘要信息;
--no-trunc :显示完整的镜像信息;
-q :只显示镜像ID。

②docker search 某个XXX镜像名字:查询指定的镜像。
OPTIONS说明
--no-trunc : 显示完整的镜像描述;
-s : 列出收藏数不小于指定值的镜像;
--automated : 只列出 automated build类型的镜像;
如:查询列出点赞数超过30的tomcat:docker search -s 30 tomcat;

③docker pull 镜像名字[:TAG]:下载镜像。

④docker rmi 某个XXX镜像名字或ID:删除镜像。
docker rmi  -f 镜像ID :删除单个;
docker rmi -f 镜像名1:TAG 镜像名2:TAG  : 删除多个; 
docker rmi -f $(docker images -qa) :删除全部(组合命令);

2.容器命令

①docker run :创建一个新的容器并运行此容器。
语法:docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
OPTIONS说明:
-i: 以交互模式运行容器,通常与 -t 同时使用;
-t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;
-d: 后台运行容器,并返回容器ID;
-p: 端口映射,格式为:主机(宿主)端口:容器端口
--name="nginx-lb": 为容器指定一个名称;
-h "hostname": 指定容器的hostname;
-m :设置容器使用内存最大值;
COMMAND说明:镜像在运行起来的时候要执行什么样的命令。
ARG:表示COMMAND命令所依赖的参数。
示例(例如运行tomcat):docker run --name tomcat -p 8080:8080 -v $PWD/test:/usr/local/tomcat/webapps/test -d tomcat
示例(例如运行centos):docker run -it --name mycentos centos 
命令说明:
-p 8080:8080:将容器的8080端口映射到主机的8080端口;
-v $PWD/test:/usr/local/tomcat/webapps/test:将主机中当前目录下的test挂载到容器的/test;
-it:运行容器后直接进入此运行容器的小型linux操作系统,直接与此容器交互并操作。
[注意:docker run -d 容器名:以后台模式启动一个容器;
 问题:然后docker ps -a 进行查看,?会发现容器已经退出;
 原因:很重要的要说明的一点,Docker容器后台运行,就必须有一个前台进程。这个是docker的机制问题;]

②docker ps : 列出当前机器上正在运行的容器。
语法:docker ps [OPTIONS]
OPTIONS说明:
-a :显示所有的容器,包括未运行的。
-l :显示最近创建的容器。
-n :列出最近创建的n个容器。
--no-trunc :不截断输出。
-q :静默模式,只显示容器编号。

③退出容器
exit :容器停止退出;
Ctrl+P+Q :容器不停止退出;

④Docker start/stop/restart 命令
docker start :启动一个或多个已经被停止的容器
语法:docker start [OPTIONS] CONTAINER [CONTAINER...]
docker stop :停止一个运行中的容器
语法:docker stop [OPTIONS] CONTAINER [CONTAINER...]
docker restart :重启容器
语法:docker restart [OPTIONS] CONTAINER [CONTAINER...]
示例:docker start 某个XXX镜像名字或ID;
扩展:强制关闭容器:docker kill 某个容器ID;
      删除已停止的容器:docker rm 某个容器ID;

⑤docker logs [OPTIONS] 容器ID:查看容器日志。
OPTIONS说明:
*-t 是加入时间戳;
*-f 跟随最新的日志打印;
*--tail 数字 显示最后多少条;

⑥docker top 容器ID:查看容器内运行的进程。
  docker inspect 容器ID:查看容器内部细节。

⑦docker attach 容器ID:重新进入正在运行的容器,去与容器交互。
  docker exec -it 容器ID 执行容器里的命令:不用进入容器,去与容器交互。
示例: docker exec -it 容器ID ls -l /tmp :查询容器中tmp目录下的内容。

⑧(docker cp  容器ID:容器内路径 目的主机路径) :将容器中的文件复制到主机中。
场景:如想删除某一个运行容器,但是需要这个容器中的一些重要数据的时候,可以将这些数据拿出来备份。

⑨.常用命令
attach   # 当前 shell 下 attach 连接指定运行镜像
build    # 通过 Dockerfile 定制镜像
commit   # 提交当前容器为新的镜像
cp       #从容器中拷贝指定文件或者目录到宿主机中
create   # 创建一个新的容器,同 run,但不启动容器
diff     # 查看 docker 容器变化
events   # 从 docker 服务获取容器实时事件
exec     # 在已存在的容器上运行命令
export   # 导出容器的内容流作为一个 tar 归档文件[对应 import ]
history  # 展示一个镜像形成历史
images   # 列出系统当前镜像
import   # 从tar包中的内容创建一个新的文件系统映像[对应export]
info     # 显示系统相关信息
inspect  # 查看容器详细信息
kill     # kill 指定 docker 容器
load     # 从一个 tar 包中加载一个镜像[对应 save]
login    # 注册或者登陆一个 docker 源服务器
logout   # 从当前 Docker registry 退出
logs     # 输出当前容器日志信息
port     # 查看映射端口对应的容器内部源端口
pause    # 暂停容器
ps       # 列出容器列表
pull     # 从docker镜像源服务器拉取指定镜像或者库镜像
push     # 推送指定镜像或者库镜像至docker源服务器
restart  # 重启运行的容器
rm       # 移除一个或者多个容器
rmi      # 移除一个或多个镜像[无容器使用该镜像才可删除,否则需删除相关容器才可继续或 -f 强制删除]
run      # 创建一个新的容器并运行一个命令
save     # 保存一个镜像为一个 tar 包[对应 load]
search   # 在 docker hub 中搜索镜像
start    # 启动容器
stop     # 停止容器
tag      # 给源中镜像打标签
top      # 查看容器中运行的进程信息
unpause  # 取消暂停容器
version  # 查看 docker 版本号
wait     # 截取容器停止时的退出状态值



四.docker镜像

1.什么是镜像:上图。
2.特点:Docker镜像都是只读的。当容器启动时,一个新的可写层被加载到镜像的顶部。
3.提交容器副本使之成为一个新的镜像:docker commit -m=“提交的描述信息” -a=“作者” 容器ID 要创建的目标镜像名:[标签名]。

4:将本机的镜像导入到另一台机上
1)列如打包tomcat镜像:docker save tomcat:latest -o tomcat313.tar
2)再使用scp命令将文件拷贝到另一台机上,如(10.134.236.111虚机)  :scp tomcat313.tar root@10.134.236.111:/root/
3)docker去加载这个镜像包:docker load -i tomcat313.tar


五.docker容器数据卷

1.是什么:一句话:有点类似我们Redis里面的rdb和aof文件,是做数据持久化的。
2.能干嘛:容器的持久化;容器间继承+共享数据;
特点:容器和宿主机之间数据共享。
3.容器内添加数据卷
方式一:命令直接添加
执行命令:  docker run -it -v /宿主机绝对路径目录:/容器内目录 镜像名;
如:docker run -it -v /masterDataValumeCentos:/dockerDataValume centos;
查看数据卷是否挂载成功:docker inspect 容器ID;
命令(带权限),容器只可以读不可以写ro(read only): docker run -it -v /宿主机绝对路径目录:/容器内目录:ro 镜像名;
方式二:dockerFile添加
①dockerFile构建:
# volume test
FROM centos
VOLUME ["/dataVolumeContainer1","/dataVolumeContainer2"]
CMD echo "finished,--------success1"
CMD /bin/bash
②build后生成lqCentos(镜像名随便取)镜像,获得一个新镜像lqCentos,而此镜像默认打开就有两个数据卷:
命令:docker build 当前dockerfile文件 构建新镜像名称
4.容器间传递共享(--volumes-from)
如:docker run -it --name lqc1 lqCentos; 在此容器中的数据卷中建立新的文件,退出但不真正退出,
    docker run -it --name lqc2 --volumes-from lqc1 lqCentos;在此容器中查看有木有lqc1容器中建立的新数据卷,
    然后在lqc2容器中建立新的数据卷,退出但不真正退出,然后在lqc1容器查看有木有lqc2容器中建立的新数据卷.
5.Docker挂载主机目录Docker访问出现cannot open directory .: Permission denied
解决办法:在挂载目录后多加一个--privileged=true参数即可


六.dockerFile解析

1.是什么:Dockerfile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本。
2.DockerFile构建过程解析
①构建三步骤:编写(注意:每条保留字指令都必须为大写字母且后面要跟随至少一个参数),构建,执行。
②Docker执行Dockerfile的大致流程:
docker从基础镜像运行一个容器->执行一条指令并对容器作出修改->执行类似docker commit的操作提交一个新的镜像层
->docker再基于刚提交的镜像运行一个新容器->执行dockerfile中的下一条指令直到所有指令都执行完成。
3.保留字指令
FROM:基础镜像,当前新镜像是基于哪个镜像的;
MAINTAINER:镜像维护者的姓名和邮箱地址;
RUN:容器构建时需要运行的命令;
EXPOSE:当前容器对外暴露出的端口;
WORKDIR:指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点;
ENV:用来在构建镜像过程中设置环境变量;比如:ENV MY_PATH /usr/mytest,那么WORKDIR $MY_PATH。语法就是KV键值对的意思;
ADD:将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包;
COPY:类似ADD,拷贝文件和目录到镜像中。将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置;
VOLUME:容器数据卷,用于数据保存和持久化工作;
CMD:指定一个容器启动时要运行的命令;(注意:Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换)
ENTRYPOINT:指定一个容器启动时要运行的命令,ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数,但是多个ENTRYPOINT指令会追加组合后执行;
ONBUILD:当构建一个被继承的Dockerfile时运行命令,父镜像在被子继承后父镜像的onbuild被触发;



七.dockerFile案列:

----执行dockerFile文件:docker build -f 当前dockerfile文件所在位置的绝对路径  -t 指定镜像的名字:标签 . ;
1.自定义镜像mycentos的dockerFile文件编写案列:
#基于centos镜像构建
FROM centos
#镜像维护者的姓名和邮箱地址
MAINTAINER lq
#定义环境变量
ENV MYPATH /usr/local
#指定在创建容器后,终端默认登陆的进来工作目录,引用了定义的环境变量
WORKDIR $MYPATH
#因为centos镜像没有vim与ifconfig指令,所以使用RUN在自定义镜像构建时,给自定义镜像安装vim与ifconfig指令
RUN yum -y install vim
RUN yum -y install net-tools
#此自定义镜像对外暴露的端口
EXPOSE 80
#相当于java中的日志打印输入
CMD echo "success--------------ok"
#结尾‘CMD /bin/bash?’是默认就有,写不写一样
CMD /bin/bash?
2.自定义镜像Tomcat9的dockerFile文件编写案列
FROM centos
MAINTAINER lq
#把宿主机当前上下文的c.txt拷贝到容器/usr/local/路径下
COPY c.txt /usr/local/cincontainer.txt
#把java与tomcat添加到容器中(add命令自动解压缩)
ADD jdk-8u171-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-9.0.8.tar.gz /usr/local/
#安装vim编辑器
RUN yum -y install vim
#设置工作访问时候的WORKDIR路径,登录落脚点
ENV MYPATH /usr/local
WORKDIR $MYPATH
#配置java与tomcat环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_171
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.8
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.8
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
#容器运行时监听的端口
EXPOSE 8080
#启动时运行tomcat的三种方式
# ENTRYPOINT ["/usr/local/apache-tomcat-9.0.8/bin/startup.sh" ]
# CMD ["/usr/local/apache-tomcat-9.0.8/bin/catalina.sh","run"]
CMD /usr/local/apache-tomcat-9.0.8/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.8/bin/logs/catalina.out
 


八.实际案列

1.SSM项目
①假设在opt目录下构建镜像文件myWpDockerFile(此镜像文件与myWebProject.war都在同一目录下)
from &#39;tomcat镜像的REPOSITORY&#39;
MAINTAINER liuqiang xxx.163.com
COPY myWebProject.war /usr/local/tomcat/webapps
②构建镜像:docker build -f  /opt/myWpDockerFile -t mm-wp:1.1 .
③运行容器:docker run -p 8080:8080 -d mmWp:1.1
2.SpringBoot项目
①假设在/opt/dockerFileImages/testSpringBootProject目录下构建镜像文件lqDockerDemoFile且springBoot项目的jar也在此目录下
# 基础镜像使用java
FROM java:8
#指定作者信息
MAINTAINER lq <12345678@qq.com>
#指定端口
EXPOSE 8080
# VOLUME 指定了临时文件目录为/tmp。
VOLUME /tmp 
# 将jar包添加到容器中并更名为app.jar
ADD lq-docker-demo-1.0.jar app.jar 
# 运行jar包
RUN bash -c &#39;touch /app.jar&#39;
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
②构建镜像
docker build -f /opt/dockerFileImages/testSpringBootProject/lqDockerDemoFile -t test-spring-boot:1.0 .
③运行容器:
docker run --name lqTestSpringboot -p 8080:8080 -d test-spring-boot:1.0



九.本地镜像发布到阿里云

省略........

 


十.常用中间件docker安装

下载镜像:docker pull mysql:5.6
mysql 运行
docker run -p 3306:3306 --name mysql -v $PWD/mysql/conf:/etc/mysql/conf.d -v $PWD/mysql/logs:/logs -v $PWD/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.6
docker exec -it MySQL运行成功后的容器ID  /bin/bash

tomcat运行
下载镜像:docker pull tomcat
docker run -p 8080:8080 --name tomcat -v $PWD/tomcat/webapps:/usr/local/tomcat/webapps -d tomcat

redis运行
下载镜像:docker pull redis:3.2
docker run -p 6379:6379 --name redis -v $PWD/redis/data:/data -v $PWD/redis/conf:/usr/local/etc/redis/redis.conf  -d redis:3.2 redis-server /usr/local/etc/redis/redis.conf --appendonly yes
将真正redis.conf文件放在$PWD/redis/conf目录下
docker exec -it [redis容器id] redis-cli
设置密码:docker run -d --name redis -p 6379:6379 redis:3.2 --requirepass "123456"

rabbitMQ的下载与运行:
下载镜像(下载management版本的有界面):docker pull rabbitmq:3.7-management
运行:docker run -p 5672:5672 -p 15672:15672 --name rabbitmq -d rabbitmq:3.7-management
web访问地址:http://localhost:15672/  用户名密码:guest guest

elasticsearch运行
下载镜像:docker pull elasticsearch
注:由于elasticsearch是java写的,运行时默认占用的堆内存2GB,若装的Linux虚拟机无法满足2GB,可以通过设置JVM参数去指定调节,下面指定最小与最大堆内存为256MB。
docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -p 9200:9200 -p 9300:9300 --name es -d elasticsearch
//docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -p 9200:9200 -p 9300:9300 --name es -v $PWD/elasticsearch/config:/usr/share/elasticsearch/config -d elasticsearch
 
 
zookeeper运行
下载镜像:docker pull zookeeper
不做集群情况下的启动只开2181端口:docker run --name zookeeper01 -p 2181:2181 --restart always -d zookeeper
这个镜像包括了2181 2888 3888端口,分别是client(客户端) port, follower(追随) port, election(选举) port

研究:Node-RED:简易版的kettle
docker安装,启动,访问
docker pull nodered/node-red-docker
docker run -it -p 1880:1880 --name docker-nodered nodered/node-red-docker
http://192.168.224.130:1880

docker run -itd --name mongo -p 27017:27017 mongo



十:docker其它操作

1.docker logs -f -t 容器名称 :查询容器运行日志

2.注:
如果getenforce 得到的结果是Enforcing
就setenforce 0,是getenforce得到的结果是Permissive


推荐阅读
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • Ubuntu 9.04中安装谷歌Chromium浏览器及使用体验[图文]
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 本文介绍了在CentOS 6.4系统中更新源地址的方法,包括备份现有源文件、下载163源、修改文件名、更新列表和系统,并提供了相应的命令。 ... [详细]
  • 如何利用 Myflash 解析 binlog ?
    本文主要介绍了对Myflash的测试,从准备测试环境到利用Myflash解析binl ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 开发笔记:spring boot项目打成war包部署到服务器的步骤与注意事项
    本文介绍了将spring boot项目打成war包并部署到服务器的步骤与注意事项。通过本文的学习,读者可以了解到如何将spring boot项目打包成war包,并成功地部署到服务器上。 ... [详细]
author-avatar
答你_妹
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有