企业运维实战--Docker学习笔记1 Docker简介、安装部署、镜像构建、Dockerfile详解、镜像构建、镜像优化、本地私有仓库搭建、harbor仓库

简介

Docker是管理容器的引擎。
Docker为应用打包、部署平台,而非单纯的虚拟化技术

1.配置docker

1.1搭建docker仓库

准备一台虚拟机server5:
配置软件仓库,从官网下载的仓库!
[root@server5 ~]# cd /etc/yum.repos.d/
[root@server5 yum.repos.d]# vim docker.repo

[docker]
name=docker-ce
baseurl=https://mirrors.aliyun.com/docker-ce/linux/centos/7/x86_64/stable/
gpgcheck=0

下载过程中会出现很多的依赖性,这些依赖性都可以从centos源找到,这里我们需从阿里云镜像站搭建一个centos源。
[root@server5 yum.repos.d]# vim CentOS-Base.repo

# CentOS-Base.repo
#
# The mirror system uses the connecting IP address of the client and the
# update status of each mirror to pick mirrors that are updated to and
# geographically close to the client.  You should use this for CentOS updates
# unless you are manually picking other mirrors.
#
# If the mirrorlist= does not work for you, as a fall back you can try the 
# remarked out baseurl= line instead.
#
#
 
[base]
name=CentOS-7 - Base - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/7/os/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
 
#released updates 
[updates]
name=CentOS-7 - Updates - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/7/updates/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
 
#additional packages that may be useful
[extras]
name=CentOS-7 - Extras - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/7/extras/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
 
#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-7 - Plus - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/7/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
 
#contrib - packages by Centos Users
[contrib]
name=CentOS-7 - Contrib - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/7/contrib/$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7

1.2下载docker-ce

yum install -y docker-ce
开启服务,并开机自启
systemctl enable --now docker
查看docker 信息
docker info
在这里插入图片描述若使用docker info 出现警告iptables的情况的话
解决方式:
vim /etc/sysctl.ddocker.conf

net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1

sysctl --system 刷新
ip addr
在这里插入图片描述

1.3测试

若虚拟机连不上网 则在真机:

[root@foundation76 Desktop]# iptables -t nat -I POSTROUTING -s
172.25.76.0/24 -j MASQUERADE
#伪装

查看
在这里插入图片描述

[root@server5 yum.repos.d]# docker search yakexi007
NAME                 DESCRIPTION   STARS     OFFICIAL   AUTOMATED
yakexi007/game2048                 0                    
yakexi007/mario                    0                    
yakexi007/nginx                    0            
[root@server5 ~]# docker load -i game2048    ##本地加载
[root@server5 yum.repos.d]# docker pull yakexi007/game2048    ## 本地没有 网上拉取拉取镜像   
Using default tag: latest
latest: Pulling from yakexi007/game2048
534e72e7cedc: Pull complete 
f62e2f6dfeef: Pull complete 
fe7db6293242: Pull complete 
3f120f6a2bf8: Pull complete 
4ba4e6930ea5: Pull complete 
Digest: sha256:8a34fb9cb168c420604b6e5d32ca6d412cb0d533a826b313b190535c03fe9390
Status: Downloaded newer image for yakexi007/game2048:latest
docker.io/yakexi007/game2048:latest
[root@server5 yum.repos.d]# docker images  查看
REPOSITORY           TAG       IMAGE ID       CREATED       SIZE
yakexi007/game2048   latest    19299002fdbe   4 years ago   55.5MB
[root@server5 yum.repos.d]# docker run -d --name game2048 -p 80:80 yakexi007/game2048   ###做端口映射 第一个80是宿主机的,第二个对应镜像的端口 -d是打入后台,–name 是给镜像取个名字

0c5d7855caa98cc0c8e3370f24d8d9e0f778217e924b64cdc213887e7683a735

##查询端口连接情况
[root@server5 yum.repos.d]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0c5d7855caa9 yakexi007/game2048 “/bin/sh -c 'sed -i …” 6 seconds ago Up 6 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp, 443/tcp game2048
docker ps -a ## 查看所有的,包括已经停掉的

测试:
访问docker主机的ip:172.25.76.5
在这里插入图片描述若要玩mario则 前面的步骤与2048 相同
docker run -d --name demo1 -p 8080:8080 mario
其中 端口在 history 查找

在这里插入图片描述

在这里插入图片描述成功!

2.镜像的构建(分层)

base镜像提供的是最小的Linux发行版 同一docker主机支持运行多种Linux发行版 采用分层结构的最大好处是:共享资源

上传busybox镜像到docker
docker load -i busybox.tar

开启容器busybox,-i表示交互式,-t表示打开伪终端
docker run -it --name demo busybox
在这里插入图片描述
创建file1在这里插入图片描述docker ps -a查看所有进程
在这里插入图片描述发现我们操作的痕迹

启动已有容器 并查看进程
在这里插入图片描述
上传新的镜像,源于demo容器 demo:v1

docker commit demo demo:v1
docker images

在这里插入图片描述
对比查看两层镜像的分层结构
docker history demo:v1
docker history busybox:latest
在这里插入图片描述
多了一步我们操作的层

然后删除v1容器
在这里插入图片描述
重启读取v1中的内容 并删掉demo,发现不影响v1容器

docker commit demo demo:v1
docker images
docker stop demo
docker rm demo
docker run -it --name demo demo:v1

在这里插入图片描述

docker container attach demo 为 进入以存在的demo容器


文件导入镜像

对比层级结构查看,手动添加的容器没有解释每一层的作用
在这里插入图片描述
解决方法:
Dockerfile文件方式导入镜像v2,history可以查看具体内容,容器用busybox
mkdir docker
cd docker/
vim Dockerfile

FROM busybox
RUN echo "lcf"
RUN touch file1

在当前目录文件Dockerfile下创建镜像

docker build -t demo:v1 .

在这里插入图片描述
查看导入的镜像,可以查到镜像内容
docker images
docker history demo:v1
在这里插入图片描述可以看到层级操作的具体操作解释

3.Dockerfile详解

FROM
指定base镜像,如果本地不存在会从远程仓库下载。

RUN
在容器中运行命令并创建新的镜像层,常用于安装软件包

MAINTAINER
设置镜像的作者,比如用户邮箱等

COPY
把文件从build context复制到镜像
支持两种形式:COPY src dest 和 COPY [“src”, “dest”]
src必须指定build context中的文件或目录

ADD
用法与COPY类似,不同的是src可以是归档压缩文件,文件会被自
动解压到dest,也可以自动下载URL并拷贝到镜像

ENV
设置环境变量,变量可以被后续的指令使用:

ENV HOSTNAME sevrer1.example.com

EXPOSE
如果容器中运行应用服务,可以把服务端口暴露出去:

EXPOSE 80

VOLUME
申明数据卷,通常指定的是应用的数据挂在点:

VOLUME ["/var/www/html"]

WORKDIR
为RUN、CMD、ENTRYPOINT、ADD和COPY指令设置镜像中的当前工作目录,如果目录不存在会自动创建。

RUN
在容器中运行命令并创建新的镜像层,常用于安装软件包:

RUN yum install -y vim

CMD 与 ENTRYPOINT
这两个指令都是用于设置容器启动后执行的命令,但CMD会被docker run后面的命令行覆盖,而ENTRYPOINT不会被忽略,一定会被执行。
docker run后面的参数可以传递给ENTRYPOINT指令当作参数。
Dockerfile中只能指定一个ENTRYPOINT,如果指定了很多,只有最后一个有效。

Shell和exec格式的区别:

FROM busybox
ENV name world
ENTRYPOINT echo "hello, $name"

Shell格式底层会调用/bin/sh -c来执行命令,可以解析变量,而下面的exec格式不会:

FROM busybox
ENV name world
ENTRYPOINT ["/bin/echo", "hello, $name"]

需要改写成以下形式:

FROM busybox
ENV name world
ENTRYPOINT ["/bin/sh", "-c", "echo hello, $name"]

Exec格式时,ENTRYPOINT可以通过CMD提供额外参数,CMD的额外参数可以在容器启动时动态替换。在shell格式时ENTRYPOINT会忽略任何CMD或docker run提供的参数。

FROM busybox
ENTRYPOINT ["/bin/echo", "hello"]
CMD ["world"]

看下在运行容器时的区别:

 docker run --rm busybox:v1
hello world
#docker run --rm busybox:v1 linux
hello linux

因此推荐使用exec格式书写

4.镜像构建- -nginx

导入rhel7镜像,相当于使用rhel7的内核
docker load -i /root/rhel7.tar
在这里插入图片描述编写Dokcerfile创建镜像

vim Dockerfile

FROM rhel7
COPY dvd.repo /etc/yum.repos.d/
ADD nginx-1.20.1.tar.gz /mnt
WORKDIR /mnt/nginx-1.20.1
RUN rpmdb --rebuilddb
RUN yum install -y gcc pcre-devel zlib-devel make
RUN ./configure --prefix=/usr/local/nginx
RUN make 
RUN make install 
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]

通过Dockerfile建立镜像v1,执行命令创建镜像rhel7:v1
docker build -t webserver:v1 .

在这里插入图片描述
docker images
在这里插入图片描述
拉起容器,进程名为demo,查看进程
docker run -d --name demo2 rhel7:v1
docker ps
在这里插入图片描述
查demo进程/容器信息
docker inspect demo
在这里插入图片描述
测试访问可访问到nginx主页

curl 172.17.0.2
在这里插入图片描述
但是我们的镜像v1的大小346MB 但官方的镜像包只有150M左右 的大小
我们实际生产中,需要docker容器中的镜像越小越好 为了解决此问题

我们如果需要每次加载docker之后不希望保留进程则需要加上参数 --rm
eg: docker run -it --rm demo:v1
若想批量删除掉建立的镜像,使用快捷的指令直接删除

powershell docker rmi `docker images |grep ^demo | awk '{print $3}'`

5.镜像优化

方法:
选择最精简的基础镜像
减少镜像的层数
清理镜像构建的中间产物
注意优化网络请求
尽量去用构建缓存
使用多阶段构建镜像

优化思路:
v2镜像:减少镜像层数,合并所有RUN指令,清理镜像构建的中间产物包括编译好的安装包和缓存/mnt/nginx-1.20.1 /var/cache/*

我们继续编辑文件Dockerfile,编辑结果如下:

FROM rhel7
COPY dvd.repo /etc/yum.repos.d/
ADD nginx-1.20.1.tar.gz /mnt
WORKDIR /mnt/nginx-1.20.1
RUN rpmdb --rebuilddb  && yum install -y gcc pcre-devel zlib-devel make && ./configure --prefix=/usr/local/nginx && make && make install && rm -rf /mnt/nginx-1.20.1 && yum clean all
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
webserver                         v3        6af940ceb6bc   20 seconds ago      258MB

单阶段镜像最佳优化已经到达极限了 则尽量去构建缓存,使用多阶段构建镜像
即创建 两个from
选择最精简的基础镜像

FROM rhel7
COPY dvd.repo /etc/yum.repos.d/
ADD nginx-1.20.1.tar.gz /mnt
WORKDIR /mnt/nginx-1.20.1
RUN rpmdb --rebuilddb  && yum install -y gcc pcre-devel zlib-devel make && ./configure --prefix=/usr/local/nginx && make && make install && rm -rf /mnt/nginx-1.20.1 && yum clean all
FROM rhel7
EXPOSE 80
COPY --from=build /usr/local/nginx /usr/local/nginx
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]

docker build -t rhel7:v4 .
查看压缩后的镜像v4大小
docker images webserver

webserver                         v4       85fdb89a1acc   25 seconds ago      144MB

继续优化镜像的话选择最精简的基础镜像替换rhel7
我们创建一个新的目录new用来存放新的base的文件
cd docker/new/
docker load -i base-debian10.tar
编写文件Dockerfile

FROM nginx:latest as base

# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
ARG Asia/Shanghai

RUN mkdir -p /opt/var/cache/nginx && \
    cp -a --parents /usr/lib/nginx /opt && \
    cp -a --parents /usr/share/nginx /opt && \
    cp -a --parents /var/log/nginx /opt && \
    cp -aL --parents /var/run /opt && \
    cp -a --parents /etc/nginx /opt && \
    cp -a --parents /etc/passwd /opt && \
    cp -a --parents /etc/group /opt && \
    cp -a --parents /usr/sbin/nginx /opt && \
    cp -a --parents /usr/sbin/nginx-debug /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/ld-* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libpcre.so.* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libz.so.* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libc* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libdl* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libpthread* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libcrypt* /opt && \
    cp -a --parents /usr/lib/x86_64-linux-gnu/libssl.so.* /opt && \
    cp -a --parents /usr/lib/x86_64-linux-gnu/libcrypto.so.* /opt && \
    cp /usr/share/zoneinfo/${TIME_ZONE:-ROC} /opt/etc/localtime

FROM gcr.io/distroless/base-debian10

COPY --from=base /opt /

EXPOSE 80 443

ENTRYPOINT ["nginx", "-g", "daemon off;"]

docker build -t webserver:v2 .
docker images webserver
在这里插入图片描述
测试: curl 172.17.0.2
在这里插入图片描述
成功

6.本地仓库搭建

先清理之前实验

[root@server5 ~]# docker image prune 
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Total reclaimed space: 0B
[root@server5 ~]# docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
58fdae465aed585b58ad533d348b3364faae2aeb25ef998ddcb9736cf72802e5

Total reclaimed space: 0B

启动本地仓库,端口映射为5000

docker run -d --name registry -p 5000:5000 registry

查看进程是否开启 并查看端口5000是否打开
docker ps
netstat -antlp
在这里插入图片描述
在这里插入图片描述将容器中的webserver镜像改名并标记到本地仓库中
docker tag webserver:v1 localhost:5000/webserver:latest
docker push localhost:5000/webserver push上传

上传成功后本地路径中也产生了文件
删除之后,重新加载,之前的内容依然存在

6.1本地容器仓库加密认证

首先创建加密认证的密钥,创建一个目录certs用来保存密钥
创建目录,之后生成密钥!

mkdir -p certs
openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/westos.org.key -x509 -days 365 -out certs/westos.org.crt

在这里插入图片描述注:server hostname这块需要写为reg.westos.org待会需要用!

需要在server5之后加上本地解析。
vim /etc/hosts

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
172.25.76.250 foundation76.ilt.example.com
172.25.76.1 server1
172.25.76.2 server2
172.25.76.3 server3
172.25.76.4 server4
172.25.76.5 server5 reg.westos.org
172.25.76.6 server6
172.25.76.7 server7
172.25.76.8 server8
172.25.76.9 server9
172.25.76.10 server10

运行docker容器 (-v 挂载进去)

docker run -d --restart=always --name registry -v "$(pwd)"/certs:/certs  -e   REGISTRY_HTTP_ADDR=0.0.0.0:443 -e 
REGISTRY_HTTP_TLS_CERTIFICATE=/certs/westos.org.crt -e
REGISTRY_HTTP_TLS_KEY=/certs/westos.org.key -p 443:443 registry

在这里插入图片描述
标记重命名nginx并归类到本地reg.westos.org仓库中
然后上传容器:
docker tag nginx:latest reg.westos.org/nginx:latest
docker push reg.westos.org/nginx:latest
在这里插入图片描述
发现上传容器的时候,报错了
原因:取不到认证,需要将认证移动到指定目录

解决::

mkdir -p /etc/docker/certs.d/reg.westos.org
cd /etc/docker/certs.d/reg.westos.org/
cp ~/certs/westos.org.crt ca.crt

重新标记上传
docker push reg.westos.org/nginx:latest
在这里插入图片描述成功
但若出现这种错误
在这里插入图片描述则为openssl版本低所致
只需升级openssl版本并重新生成证书时修改参数即可

openssl11 req -newkey rsa:4096 -nodes -sha256 -keyout certs/westosorg.key -addext "subjectAltName = DNS:reg.westos.org" -x509 -days 365 -out certs/westos.org.crt

6.2认证

创建认证目录并进入
mkdir auth/htpasswd -p
需要安装认证的软件

yum install httpd-tools -y

为admin用户和lcf用户添加认证信息和密码

htpasswd -Bc auth/htpasswd admin (-c覆盖)
htpasswd -B auth/htpasswd lcf

在这里插入图片描述在这里插入图片描述
删除之前的仓库信息registry,运行容器并查看进程!
docker rm -f registry

docker run -d --restart=always --name registry -v "$(pwd)"/certs:/certs  -e REGISTRY_HTTP_ADDR=0.0.0.0:443  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/westos.org.crt -e REGISTRY_HTTP_TLS_KEY=/certs/westos.org.key -v "$(pwd)"/auth:/auth -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd -p 443:443 registry

docker ps
在这里插入图片描述docker push reg.westos.org/nginx:latest
在这里插入图片描述上传文件时有报错
解决:
登陆

[root@server5 ~]# docker login reg.westos.org
Username: admin
Password: 
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

cat /root/.docker/config.json 登陆信息都在这 下次不需要在登陆
在这里插入图片描述
docker push reg.westos.org/nginx:latest 再次上传
在这里插入图片描述
成功

7.docker-harbor仓库

先删除registry

docker rm -f registry

7.1软件下载

harbor-offline-installer-v1.10.1.tgz
docker-compose-Linux-x86_64-1.27.0

tar zxf harbor-offline-installer-v1.10.1.tgz 
cd harbor/
mv docker-compose-Linux-x86_64-1.27.0 /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
mkdir /data
cp ~/certs/ /data/ -r
[root@server5 harbor]# ls /data/certs/
westos.org.crt  westos.org.key

7.2.修改配置信息

vim harbor.yml

5 hostname: reg.westos.org
13 https:
14 #   # https port for harbor, default is 443
15   port: 443
17    certificate: /data/certs/westos.org.crt
18    private_key: /data/certs/westos.org.key
27 harbor_admin_password: westos

安装
./install.sh

当出现 ERROR: yaml.parser.ParserError: while parsing a block mapping in
“./docker-peer.yaml”, line 10, column 5 expected , but found ‘’ in
“./docker-peer.yaml”, line 17, column 3 时是空格导致的未对齐(严格意义上的对齐)

在这里插入图片描述安装成功
docker-compose ps (必须在harbor目录下输入命令)
docker ps
在这里插入图片描述

7.3.登陆网页

访问172.25.76.5
在这里插入图片描述

7.4.添加默认仓库路径

vim /etc/docker/daemon.json

{
  "registry-mirrors": ["https://reg.westos.org"]
}

查看到新的添加的镜像路径
systemctl reload docker.service
docker info
在这里插入图片描述 docker pull nginx
docker rmi nginx:latest 删除nginx后images里没有
在这里插入图片描述docker run -d --name demo nginx
运行容器的时候,pull拉取镜像会自动下载没有的镜像

7.5.获取认证

docker logout reg.westos.org
docker login reg.westos.org
Username: admin
Password:
是登陆web页面的账号密码。
在这里插入图片描述docker tag nginx:latest reg.westos.org/library/nginx:latest
docker push reg.westos.org/library/nginx:latest
网页查看是否上传
在这里插入图片描述

7.6重新下载配置

docker-compose down
在这里插入图片描述
./prepare
在这里插入图片描述内容信任 ,镜像扫描
相当于:配置中加入了漏洞扫描,内容信任等选项。

./install.sh --with-notary --with-clair --with-chartmuseum

在这里插入图片描述
选中漏洞扫描
在这里插入图片描述开始扫描

docker tag nginx:latest reg.westos.org/library/nginx:latest
docker push reg.westos.org/library/nginx:latest
在这里插入图片描述扫描完成。

启用docker内容信任
在这里插入图片描述

export DOCKER_CONTENT_TRUST=1
export DOCKER_CONTENT_TRUST_SERVER=https://reg.westos.org:4443

部署根证书

mkdir ~/.docker/tls/reg.westos.org:4443 -p
cd ~/.docker/tls/reg.westos.org:4443
cp /etc/docker/certs.d/reg.westos.org/ca.crt .

上传之前上传的nginx/library

docker push reg.westos.org/library/nginx:latest

显示已经存在,需要我们输入root key 当我们只修改标签的时候,我们只用输入repository key
在这里插入图片描述

Enter passphrase for new root key with ID 72e5409:    #根密码
Repeat passphrase for new root key with ID 72e5409: 
Enter passphrase for new repository key with ID 3eb6164:   ##仓库的key
Repeat passphrase for new repository key with ID 3eb6164: 
Finished initializing "reg.westos.org/library/nginx"
Successfully signed reg.westos.org/library/nginx:latest

网页访问 签名成功
在这里插入图片描述
我们把镜像的名字更改之后重新拉取镜像v1 只需要更改仓库的key

docker tag nginx:latest reg.westos.org/library/nginx:v1
docker push reg.westos.org/library/nginx:v1

在这里插入图片描述
在这里插入图片描述还有一个好处为 本来拉取报错的镜像,现在也可以拉取镜像了

当输入

export DOCKER_CONTENT_TRUST=0

再次拉取的时候就不需要再输入认证了

7.8.优化容器

为了不让扫描使占用的空间越来越大,所以重新修改一下选项
export DOCKER_CONTENT_TRUST=0
cd
docker-compose down
./prepare
./install.sh --with-chartmuseum


阿里云镜像加速器查找方法:
登录阿里云帐号
找到 容器镜像服务 搜索框输入 镜像 找到 容器镜像服务
在这里插入图片描述找到 加速器地址
点镜像加速器 加速器地址 就可以找到操作步骤每个人都有一串不同的代码

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lll_cf

喜欢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值