zoukankan      html  css  js  c++  java
  • jenkins+git+docker构建持续化集成环境

    CI/CD介绍

    image_1cv26f7kq1cfl1uj7ed59uap581p.png-165.9kB

     

    发布流程设计

    image_1cv26ive51qic1vf844j15o5hll1b.png-171kB

    服务器IP地址主机名
    Git/Harbor 192.168.200.70 git-harbor
    Docker 192.168.200.111 docker
    Jenkins 192.168.200.112 jenkins

    工具版本
    CentOS 7.5_x64
    Maven 3.5
    Tomcat 8
    JDK 1.8
    Jenkins 2.6
    Docker CE 18.03.1

    cat /etc/redhat-release

    uname -r

    image_1d0s2ktihiib12km1973m0o15tc9.png-23.1kB

     

    Jenkins+Docker+Git所有包

    链接:https://pan.baidu.com/s/10GWHTqAx9E9d1hhJNuI1gw 
    提取码:py3b

     

    部署Harbor镜像仓库

    服务器IP地址
    Git/Harbor 192.168.200.70
     

    创建ca证书

    mkdir -p /data/ssl

    cd /data/ssl

    which openssl

    openssl req -newkey rsa:4096 -nodes -sha256 -keyout ca.key -x509 -days 365 -out ca.crt

     
    1. Generating a 4096 bit RSA private key
    2. .................................................++
    3. ......................................................................................................................++
    4. writing new private key to 'ca.key'
    5. -----
    6. You are about to be asked to enter information that will be incorporated
    7. into your certificate request.
    8. What you are about to enter is what is called a Distinguished Name or a DN.
    9. There are quite a few fields but you can leave some blank
    10. For some fields there will be a default value,
    11. If you enter '.', the field will be left blank.
    12. -----
    13. Country Name (2 letter code) [XX]:CN
    14. State or Province Name (full name) []:Beijing
    15. Locality Name (eg, city) [Default City]:Beijing
    16. Organization Name (eg, company) [Default Company Ltd]:yunjisuan
    17. Organizational Unit Name (eg, section) []:yunjisuan
    18. Common Name (eg, your name or your servers hostname) []:www.yunjisuan.com
    19. Email Address []:

    image_1d0s2o1ge193d1eb0mn9fsb1bpam.png-103.5kB

     

    生成证书请求

    openssl req -newkey rsa:4096 -nodes -sha256 -keyout www.yunjisuan.com.key -out www.yunjisuan.com.csr

     
    1. Generating a 4096 bit RSA private key
    2. ..........................................................++
    3. .......................................................................................................................++
    4. writing new private key to 'www.yunjisuan.com.key'
    5. -----
    6. You are about to be asked to enter information that will be incorporated
    7. into your certificate request.
    8. What you are about to enter is what is called a Distinguished Name or a DN.
    9. There are quite a few fields but you can leave some blank
    10. For some fields there will be a default value,
    11. If you enter '.', the field will be left blank.
    12. -----
    13. Country Name (2 letter code) [XX]:CN
    14. State or Province Name (full name) []:Beijing
    15. Locality Name (eg, city) [Default City]:Beijing
    16. Organization Name (eg, company) [Default Company Ltd]:yunjisuan
    17. Organizational Unit Name (eg, section) []:yunjisuan
    18. Common Name (eg, your name or your servers hostname) []:www.yunjisuan.com
    19. Email Address []:
    20. Please enter the following 'extra' attributes
    21. to be sent with your certificate request
    22. A challenge password []:
    23. An optional company name []:

    image_1d0s2pduj1bl8jve1olu1fgfmj13.png-104.2kB

     

    生成注册表主机的证书

    openssl x509 -req -days 365 -in www.yunjisuan.com.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out www.yunjisuan.com.crt

     
    1. Signature ok
    2. subject=/C=CN/ST=Beijing/L=Beijing/O=yunjisuan/OU=yunjisuan/CN=www.yunjisuan.com
    3. Getting CA Private Key

    ll

    image_1d0s2rsib132dv497rncb51o0d3g.png-64.1kB

     

    信任自签发的证书

    cp www.yunjisuan.com.crt /etc/pki/ca-trust/source/anchors/

    update-ca-trust enable

    update-ca-trust extract

    image_1d0s2so0gl47n0m1sp292v1ndl3t.png-31.1kB

     

    安装docker-ce社区版

    setenforce 0

    yum -y install yum-utils device-mapper-persistent-data lvm2

    curl https://download.docker.com/linux/centos/docker-ce.repo -o /etc/yum.repos.d/docker-ce.repo

    yum -y install docker-ce

    systemctl start docker

    systemctl enable docker

    docker version

    image_1d0s2v0ov11cm1qev1onv1iin1uek4a.png-192.1kB


    image_1d0s40jr81372vto17hv18l217si4n.png-154.2kB

     

    安装harbor仓库

    mkdir -p /etc/ssl/harbor

    cp /data/ssl/www.yunjisuan.com.key /etc/ssl/harbor/

    cp /data/ssl/www.yunjisuan.com.crt /etc/ssl/harbor/

    wget http://harbor.orientsoft.cn/harbor-v1.5.0/harbor-offline-installer-v1.5.0.tgz 
    上文有下载包,这里就没有wget下载。

    mkdir -p /data/install

    cd /data/install

    ls

    tar xf harbor-offline-installer-v1.5.0.tgz

    cd harbor

    cp harbor.cfg{,.bak}

    vim harbor.cfg

    cat -n harbor.cfg | sed -n '7p;11p;23p;24p;68p'

     
    1. 7 hostname = www.yunjisuan.com
    2. 11 ui_url_protocol = https
    3. 23 ssl_cert = /etc/ssl/harbor/www.yunjisuan.com.crt
    4. 24 ssl_cert_key = /etc/ssl/harbor/www.yunjisuan.com.key
    5. 68 harbor_admin_password = Harbor12345

    image_1d0s4d9ntoi618hkiufmchvcm54.png-118.3kB

     

    安装命令docker-compose(需要1.21版本)

     
    1. curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname
    2. -s)-$(uname -m) -o /usr/local/bin/docker-compose
    3. 上文有下载包,这里就没有下载

    cd /usr/local/bin/

    ls

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

    which docker-compose

    docker-compose -version

    image_1d0s4fhbpu01i1q1fgg7v1a9k5h.png-43.2kB

     

    启动harbor私有镜像仓库

    cd /data/install/harbor

    ./install.sh --with-clair

    image_1d0s4j39bcdp71fv01sg0gru5u.png-31.6kB

     

    为其他服务器下发证书,并映射域名

     

    为其他服务器下发证书

    scp /data/ssl/www.yunjisuan.com.crt 192.168.200.111:/etc/pki/ca-trust/source/anchors/

    scp /data/ssl/www.yunjisuan.com.crt 192.168.200.112:/etc/pki/ca-trust/source/anchors/

    image_1d0s4smljmd91hso1cj7jsoemj6b.png-124kB

     

    在Docker客户端上(192.168.200.111)

    update-ca-trust enable

    update-ca-trust extract

    vim /etc/hosts

    tail -1 /etc/hosts

     
    1. 192.168.200.70 www.yunjisuan.com

    image_1d0s4vebp84pfqkvbtscfjj585.png-28.7kB

     

    在jenkins服务器上(192.168.200.203)

    update-ca-trust enable

    update-ca-trust extract

    vim /etc/hosts

    tail -1 /etc/hosts

     
    1. 192.168.200.70 www.yunjisuan.com

    image_1d0s4v6ikbns170118e919a99v07o.png-27.8kB

     

    部署Git服务器

    服务器IP地址主机名
    Git/Harbor 192.168.200.70 git-harbor
    Jenkins服务器 192.168.200.112 jenkins
     

    以下操作在Harbor/Git上(192.168.200.70)

    yum -y install git

    which git

    image_1d0s51h091ti01lerk268qolf68i.png-162.4kB

     

    创建git用户密码

    useradd git

    passwd git

    su - git

    image_1d0s52mjkdv81p771mog1i7m678v.png-30.7kB

     

    创建git项目目录

    mkdir solo.git

    cd solo.git/

     

    初始化git目录

    git --bare init

    ls

    image_1d0s53l5pcrilcq1sii1q9leuu9c.png-58.2kB

     

    以下的操作在Jenkins上(192.168.200.112)

     

    在192.168.200.112上也安装git模拟项目代码提交

    yum -y install git

    which git

    image_1d0s564jl1mrk13m3b4n1o4m1lp69p.png-164.6kB

     

    创建用于提交的git目录

    mkdir -p /code

    cd /code

    git clone root@192.168.200.70:/home/git/solo.git

    ls

    image_1d0s57fbt19h11eav1jk6154igfra6.png-74.1kB

     

    将solo项目的源码拷贝到git的上传目录下(solo源代码在上文有下载链接)

    mv ~/solo/* solo/

    ls solo/

    image_1d0s5gi4r1a847p21mor1lkocnaj.png-31kB

     

    添加需要提交的文件目标

    cd solo

    git add .

    image_1d0s5h36j1ng71k05gt910epcuqb0.png-18.4kB

     

    进行代码提交

    git commit -m "all"

     
    1. *** Please tell me who you are. #出现这个提示是让你补充提交信息
    2. Run
    3. git config --global user.email "you@example.com" #你的邮箱
    4. git config --global user.name "Your Name" #你的名字
    5. to set your account's default identity.
    6. Omit --global to set the identity only in this repository.
    7. fatal: unable to auto-detect email address (got 'root@JenkinsServer.(none)')

    git config --global user.email "1123400300@qq.com"

    git config --global user.name "Mr.sun"

    git commit -m "all" #补充信息后,即可提交成功

    image_1d0s5jsnj1e8b1u2p11cj8fb1udpbd.png-226.8kB

     

    提交完代码之后,需要推送到git服务端

    git push origin master --->origin master版本信息

    image_1d0s5kh00r1n1iok7mb1t8obpmbq.png-44.9kB

     

    为了最后的solo项目测试,我们需要修改一下solo项目源代码的某个配置文件

    cd /code/solo/src/main/resources

    ls

    cat -n latke.properties | sed -n '29p;31p'

     
    1. 29 serverHost=localhost
    2. 31 serverPort=8080

    image_1d0s5ldhb1m6t3u416f21rrheahc7.png-42.6kB

     

    将文件的上边两行代码修改成如下所示

    vim latke.properties

    cat -n latke.properties | sed -n '29p;31p'

     
    1. 29 serverHost=192.168.200.111 #修改成docker的IP地址
    2. 31 serverPort=8888

    image_1d0s5mlhmpod5471m2r99d10mrck.png-24.4kB

     

    再次进行git版本提交

    cd /code/solo/

    git add .

    git commit -m "latke.properties"

    git push origin master

    image_1d0s5nlso678a49bh6grr1bcbd1.png-78.6kB

     

    构建业务基础镜像(tomcat:v1)

    在后边构建

    服务器IP地址主机名
    Docker 192.168.200.111 docker
     

    安装docker

    yum -y install yum-utils device-mapper-persistent-data lvm2

    curl https://download.docker.com/linux/centos/docker-ce.repo -o /etc/yum.repos.d/docker-ce.repo

    yum -y install docker-ce

    docker --version

    image_1d0s5qu931obllb24h1uub1ueede.png-169.9kB


    image_1d0s6aabbl0k5a312vb1ba1njcdr.png-209.1kB

     

    添加docker国内镜像源

    mkdir -p /etc/docker

    vim /etc/docker/daemon.json

    cat /etc/docker/daemon.json

     
    1. {
    2. "registry-mirrors":[ "https://registry.docker-cn.com" ]
    3. }

    systemctl daemon-reload

    systemctl restart docker

    image_1d0s6c6hvsk7nghc6g1sq0maqe8.png-32.6kB

     

    部署jdk环境(不需要添加环境变量)

    ls

    tar xf jdk-8u45-linux-x64.tar.gz -C /usr/local/

    image_1d0s6lcnvjs7fmsg38t381j6lel.png-24.2kB


    cd /usr/local

    ls

    ln -s jdk1.8.0_45 jdk

    image_1d0tpl67u17ms1pp81dah156k77q4t.png-28.5kB

     

    Jenkins安装

    服务器IP地址主机名
    Jenkins服务器 192.168.200.112 jenkins
     

    安装docker-ce环境

    yum -y install yum-utils device-mapper-persistent-data lvm2

    curl https://download.docker.com/linux/centos/docker-ce.repo -o /etc/yum.repos.d/docker-ce.repo

    yum -y install docker-ce

    mkdir -p /etc/docker

    vim /etc/docker/daemon.json

    cat /etc/docker/daemon.json

     
    1. {
    2. "registry-mirrors":[ "https://registry.docker-cn.com" ]
    3. }

    systemctl daemon-reload

    systemctl restart docker

    image_1d0s6ohjp1nmdddi1uu71rlkl89f2.png-171.5kB


    image_1d0s6uia71fr01ons1qqr11vq2hcff.png-190.8kB

     

    安装JDK环境(因为是要用在容器中,因此宿主机不配PATH)

    ls

    tar xf jdk-8u45-linux-x64.tar.gz -C /usr/local/

    image_1d0s717kj17ch1j7n3f91q42ttfgs.png-25.5kB


    cd /usr/local

    ls

    ln -s jdk1.8.0_45 jdk

    image_1d0tpoe1k20kb9upmt1bb73ns5q.png-28.9kB

     

    安装maven-3.5.0

    ls

    tar xf apache-maven-3.5.0-bin.tar.gz -C /usr/local/

    image_1d0s71o3o4aboa1sjhpfc15gqh9.png-28.2kB


    cd /usr/local

    ls

    ln -s apache-maven-3.5.0 maven

    image_1d0tppdbjpqu120pr9d18pk1f6e67.png-33.4kB

     

    创建jenkins镜像的Dockerfile

    没有wget命令需要提前yum安装

    mkdir -p dockerfile/jenkins

    cd dockerfile/jenkins

    vim Dockerfile

    cat Dockerfile

     
    1. FROM jenkins
    2. USER root
    3. RUN echo "" > /etc/apt/sources.list.d/jessie-backports.list &&
    4. wget http://mirrors.163.com/.help/sources.list.jessie -O /etc/apt/sources.list
    5. RUN apt-get update && apt-get install -y git libltdl-dev

    image_1d0s733pcv7gucf1fhu6k3ql0hm.png-45kB

     

    创建jenkins镜像

    docker build -t jenkins:v1 .

    docker images

    image_1d0s78m6e15mu1t841jt69pa129ci3.png-199.7kB

     

    由于我们是在镜像中去构建Jenkins的,所以

    • jenkins容器的数据目录我们需要从宿主机上挂载(避免容器数据丢失)

    • jenkins的运行需要jdk环境,所以我们直接挂载宿主机上的jdk

    • jenkins构建java代码需要maven支持,所以我们直接挂载宿主机上的maven

    • Jenkins需要docker支持

    • Jenkins需要免交互拉取git代码,因此挂载本地的ssh密钥

     

    创建jenkins数据目录

    mkdir -p /var/jenkins_home

    image_1d0s79kgl1ea62f01c9o1fpk1ls6ig.png-16.8kB

     

    进行ssh免密钥交互验证

    ssh-keygen --->一律回车即可

    ssh-copy-id git@192.168.200.70

    image_1d0s7avhp87q1qis1p4q15l45uait.png-120.3kB

     

    进行免交互测试

    ssh git@192.168.200.70

    image_1d0s7bdls1vqr1pbs3vmnmm13m6ja.png-24.9kB

     

    启动jenkins容器

    docker run -dit --name jenkins -p 8080:8080 -v /var/jenkins_home/:/var/jenkins_home/ -v /usr/local/apache-maven-3.5.0:/usr/local/maven -v /usr/local/jdk1.8.0_45:/usr/local/jdk -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v ~/.ssh:/root/.ssh jenkins:v1

    image_1d0s7cp56f1vkllnm6ei3t6tjn.png-30.3kB

     

    利用浏览器访问Jenkins容器

    http://192.168.200.112:8080

    image_1d0tlmb7g1qutcqp19p2als1sm29.png-127.5kB


    docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

     
    1. c7e4ae00fd5941d6b20f1e45ab6835b6 #这就是密码,输入到浏览器里

    image_1d0tlo08j1jij1fbjm4q1a5pbeum.png-29.9kB


    选择所有插件后,直接点install即可

    image_1d0tm8fn41tjs1rj1q9p57o4lq9.png-143.4kB


    image_1d0tmab0i1qqd189qot81rka1793m.png-171.6kB

     

    我们现在构建一个可以运行solo代码的tomcat镜像

    mkdir -p /root/dockerfile/solo

    cd /root/dockerfile/solo

    vim Dockerfile

    cat Dockerfile

     
    1. FROM centos:7
    2. MAINTAINER www.yunjisuan.com
    3. RUN yum install unzip iproute -y
    4. ENV JAVA_HOME /usr/local/jdk
    5. ADD apache-tomcat-8.0.46.tar.gz /usr/local
    6. RUN mv /usr/local/apache-tomcat-8.0.46 /usr/local/tomcat
    7. WORKDIR /usr/local/tomcat
    8. EXPOSE 8080
    9. ENTRYPOINT ["./bin/catalina.sh", "run"]

    image_1d0s7sph31bgmh001bii1b2vem6lo.png-63.7kB


    ls

    image_1d0s7v67b180o1pj6licojpvdsm5.png-24.9kB

     

    构建镜像

    docker build -t tomcat:v1 .

    docker images

    image_1d0s820ls8hm1apb17igmv5hfkmi.png-157.6kB

     

    登陆harbor私有仓库

    docker login -uadmin -pHarbor12345 www.yunjisuan.com

    image_1d0s82hmbiqcmeg1kk41quqt04mv.png-41.1kB

     

    推送镜像到harbor仓库(如果推送失败请查看证书验证或者docker是否登陆)

    docker images

    docker tag tomcat:v1 www.yunjisuan.com/library/tomcat:v1

    docker push www.yunjisuan.com/library/tomcat:v1

    image_1d0s866f314l37ch10l513rm355ns.png-115kB


    image_1d0s86nsa9eo142417t01h6ttqeo9.png-96.2kB

     

    Jenkins基本配置

    用户名:admin 密码:linyaonie

    image_1d0s8fqjge4j1ojt1ldjid712kdom.png-88.8kB

     

    设定全局配置

    image_1d0s8t7qh1rus16u9a2rovlgmep3.png-471.1kB


    image_1cv27ei1bt9916ck1n21151dv2rbt.png-92.2kB


    image_1d0s909i71q5d1l871ubi1n04b65pg.png-105.6kB


    image_1d0s912eor5p129140nmcrbblpt.png-106.9kB

     

    设定ssh连接凭据

     

    jenkins连接Docker测试服务器免交互验证

    ssh-copy-id root@192.168.200.111

    ssh root@192.168.200.111

    image_1d0s92uhk1h8511e51la7udtdv3qa.png-104.7kB

     

    在Jenkins的Web界面上添加凭据

    image_1d0s95cabrgt1cg9mknpus9v2r4.png-98kB


    image_1d0s94sd2ci21fv5pl71ukckmjqn.png-85.8kB


    image_1d0s99l31q508hl14vs1jgud08ru.png-130.2kB


    cat ~/.ssh/id_rsa #就是把这些内容复制

    image_1d0s96r5b7u5163viaegv697urh.png-240.4kB


    image_1d0s9as3n1mkc19oh1ard1qpp7glsb.png-381.7kB


    image_1d0s9lr3fl4lmp71r06rgkivkso.png-127.5kB

     

    Jenkins创建项目

     

    我们先开始一个新的任务

    image_1d0tr4ifl1l331li042sqjp87b7k.png-155.6kB


    image_1d0tr7m1sig5chp11a8i9es6s8h.png-107.5kB

    图片说明图片2.png-62kB


    image_1d0tr9715l9g1m7c6q51kaj1sco9e.png-109.5kB

    图片说明图片3.png-71.1kB

     

    到这里我们先来测试一下maven构建java代码的效果

    点击solo_blog项目的立刻构建,查看构建信息

    image_1d0trb5pp1gsu145g1huut011dtbb.png-103.5kB

     

    在Jenkins服务器上查看构建后的结果

    cd /var/jenkins_home/workspace/solo_blog/target

    ls

    ll solo.war --->这就是构建出来的war包

     
    1. 2.[root@JenkinsServer target]# pwd
    2. 3./var/jenkins_home/workspace/solo_blog/target
    3. 4.[root@JenkinsServer target]# ls
    4. 5.classes generated-test-sources maven-status solo_h2_test surefire-reports
    5. 6.generated-sources maven-archiver solo solo.war test-classes
    6. 7.[root@JenkinsServer target]# ll solo.war #这就是构建出来的war包
    7. 8.-rw-r--r-- 1 root root 43037193 7 25 22:12 solo.war
     

    通过脚本将war包封装进一个tomcat的镜像中,然后推送到harbor

    所以利用maven构建java的源代码实际上就是生成可以在tomcat等容器中运行的war包  
    现在我们重新修改一下项目的配置,增加POST Steps(构建之后的操作)  
    其实,构建之后,我们只需要通过脚本将war包封装进一个tomcat的镜像中,然后推送到harbor里即可。

    image_1cv27rn714lu3rko7v1chvchfjl.png-77.3kB

     

    这就是需要添加进去的脚本内容

    cd $WORKSPACE --->这是jenkins的可用变量,具体可以在上图下边查看

     
    1. cd $WORKSPACE
    2. cat > Dockerfile << FOF
    3. FROM www.yunjisuan.com/library/tomcat:v1
    4. MAINTAINER www.yunjisuan.com
    5. COPY target/solo.war /tmp/ROOT.war
    6. RUN rm -rf /usr/local/tomcat/webapps/* &&
    7. unzip /tmp/ROOT.war -d /usr/local/tomcat/webapps/ROOT &&
    8. rm -f /tmp/ROOT.war
    9. WORKDIR /usr/local/tomcat
    10. EXPOSE 8080
    11. ENTRYPOINT ["./bin/catalina.sh","run"]
    12. FOF
    13. docker build -t www.yunjisuan.com/library/solo:v1 .
    14. docker login -uadmin -pHarbor12345 www.yunjisuan.com
    15. docker push www.yunjisuan.com/library/solo:v1
     

    然后我们再次进行构建查看

    image_1cv27t6lv1tk4r475i11ehnmhfl2.png-76.8kB

     

    至此我们就完成了以下几步

    git拉取java的solo项目源代码 
    maven构建java的solo项目war包 
    将war包封装成tomcat的容器启动镜像 
    将镜像上传harbor私有镜像仓库

     

    我们还需要能够直接部署到远程测试主机

     

    (192.168.200.111)上,因此我们继续设置

    image_1cv27unoobo91p3i1a2pcc41krnlv.png-75.8kB

     

    在远程主机(Docker测试服务器)执行的脚本如下

     
    1. docker rm -f solo #清理旧的solo容器进程
    2. docker rmi -f www.yunjisuan.com/library/solo:v1 #清理旧的solo:v1镜像(不清理就不拉取镜像了)
    3. docker login -uadmin -pHarbor12345 www.yunjisuan.com
    4. docker run -d --name solo -p 8888:8080 -v /usr/local/jdk1.8.0_45/:/usr/local/jdk www.yunjisuan.com/library/solo:v1
     

    再次进行构建,并在docker主机上查看构建结果

    docker images --->docker测试服务器上有镜像了

     
    1. 2.REPOSITORY TAG IMAGE ID CREATED SIZE
    2. 3.www.yunjisuan.com/library/solo v1 e1b0d010c11b 11 minutes ago 408MB
    3. 4.redis latest f06a5773f01e 8 days ago 83.4MB
    4. 5.centos latest 49f7960eb7e4 7 weeks ago 200MB

    docker ps -a --->启动容器进程了

     
    1. 7.CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    2. 8.c4dba5567fd5 www.yunjisuan.com/library/solo:v1 "./bin/catalina.sh r…" 11 minutes ago Up 11 minutes 0.0.0.0:8888->8080/tcp solo
     

    我们通过浏览器访问

    http://192.168.200.112:8888

    image_1cv27vu101p92g9d13ivvpeo7emc.png-41.2kB

  • 相关阅读:
    300. Longest Increasing Subsequence_算法有误
    LIS (DP)_代码
    pthread_detach pthread_create实例
    pthread_detach
    DP(动态规划)
    括号匹配(二)
    gdb调试遇到的问题
    matplotlib 显示中文
    一个奇怪的编码 big5-hkscs
    python 重载 __hash__ __eq__
  • 原文地址:https://www.cnblogs.com/linyaonie/p/11238398.html
Copyright © 2011-2022 走看看