Docker Registry介绍
网上有很多的Registry服务器都支持第三方用户注册,而后基于用户名去做自己的仓库,但是使用互联网上的Registry有一个缺陷,那就是我们去推送和下载镜像时都不会很快,而在生产环境中很可能并行启动的容器将达到几十、上百个,而且很有可能每个服务器本地是没有镜像的,此时如果通过互联网去下载镜像会有很多问题,比如下载速度会很慢、带宽会用很多等等,如果带宽不够的话,下载至启动这个过程可能要持续个几十分钟,这已然违背了使用容器会更加轻量、快速的初衷和目的。因此,很多时候我们很有可能需要去做自己的私有Registry。
Registry用于保存docker镜像,包括镜像的层次结构和元数据。用户可以自建Registry,也可以使用官方的Docker Hub。
Docker Registry分类:
Sponsor Registry:第三方的Registry,供客户和Docker社区使用(收费)
Mirror Registry:第三方的Registry,只让客户使用 (收费)
Vendor Registry:由发布docker镜像的供应商(官方docker hub镜像仓库)提供的registry
Private Registry(私有仓库):通过设有防火墙和额外的安全层的私有实体(某一个公司内部)提供的registry
事实上,如果运维的系统环境托管在云计算服务上,比如阿里云,那么用阿里云的Registry则是最好的选择。很多时候我们的生产环境不会在本地,而是托管在数据中心机房里,如果我们在数据中心机房里的某台主机上部署Registry,因为都在同一机房,所以属于同一局域网,此时数据传输走内网,效率会极大的提升。
所有的Registry默认情况下都是基于https工作的,这是Docker的基本要求,而我自建Registry时很可能是基于http工作的,但是Docker默认是拒绝使用http提供Registry服务的,除非明确的告诉它,我们就是要用http协议的Registry。
Docker Private Registry
为了帮助我们快速创建私有Registry,Docker专门提供了一个名为Docker Distribution的软件包(centos7上面有),我们可以通过安装这个软件包快速构建私有仓库。
问:既然Docker是为了运行程序的,Docker Distribution能否运行在容器中?
容器时代,任何程序都应该运行在容器中,除了Kernel和init。而为了能够做Docker Private Registry,Docker Hub官方直接把Registry做成了镜像,我们可以直接将其pull到本地并启动为容器即可快速实现私有Registry。
Registry的主要作用是托管镜像,Registry运行在容器中,而容器自己的文件系统是随着容器的生命周期终止和删除而被删除的,所以当我们把Registry运行在容器中时,客户端上传了很多镜像,随着Registry容器的终止并删除,所有镜像都将化为乌有,因此这些镜像应该放在存储卷上,而且这个存储卷最好不要放在Docker主机本地,而应该放在一个网络共享存储上,比如NFS。不过,镜像文件自己定义的存储卷,还是一个放在Docker本地、Docker管理的卷,我们可以手动的将其改成使用其它文件系统的存储卷。
这就是使用容器来运行Registry的一种简单方式。自建Registry的另一种方式,就是直接安装docker-distribution软件。
使用docker-distribution自建Registry
实例:
在node02上先自建Registry,便于node01上进行拉取
[root@node02 ~]# yum -y install docker-distribution
[root@node02 ~]# vim /etc/docker-distribution/registry/config.yml
version: 0.1
log:
fields:
service: registry
storage:
cache:
layerinfo: inmemory
filesystem:
rootdirectory: /var/lib/registry # 修改此处为一个容量大的磁盘分区目录
http:
addr: :5000
[root@node02 ~]# systemctl start docker-distribution
[root@node02 ~]# ss -antl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 100 127.0.0.1:25 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 100 [::1]:25 [::]:*
LISTEN 0 128 [::]:5000 [::]:*
LISTEN 0 128 [::]:22 [::]:*
在node01上使用自建的Registry去上传镜像
# 使用insecure-registries参数添加http支持
[root@node01 ~]# vim /etc/docker/daemon.json
{
"registry-mirrors": ["https://j3m2itm3.mirror.aliyuncs.com","https://registry.docker-cn.com"],
"insecure-registries": ["node02-linux.example.com:5000"]
}
[root@node01 ~]# systemctl restart docker
[root@node01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
seancheng1002/b1 v0.2 42a777e26541 2 weeks ago 1.22MB
seancheng1002/b1 v0.1 bb54705dfd51 2 weeks ago 1.22MB
nginx latest 2073e0bcb60e 2 weeks ago 127MB
centos latest 470671670cac 5 weeks ago 237MB
busybox latest 6d5fcfe5ff17 8 weeks ago 1.22MB
[root@node01 ~]# docker tag nginx:latest node02-linux.example.com:5000/nginx:latest
[root@node01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
seancheng1002/b1 v0.2 42a777e26541 2 weeks ago 1.22MB
seancheng1002/b1 v0.1 bb54705dfd51 2 weeks ago 1.22MB
nginx latest 2073e0bcb60e 2 weeks ago 127MB
node02-linux.example.com:5000/nginx latest 2073e0bcb60e 2 weeks ago 127MB
centos latest 470671670cac 5 weeks ago 237MB
busybox latest 6d5fcfe5ff17 8 weeks ago 1.22MB
[root@node01 ~]# docker push node02-linux.example.com:5000/nginx
The push refers to repository [node02-linux.example.com:5000/nginx]
22439467ad99: Pushed
b4a29beac87c: Pushed
488dfecc21b1: Pushed
latest: digest: sha256:62f787b94e5faddb79f96c84ac0877aaf28fb325bfc3601b9c0934d4c107ba94 size: 948
在node1上传镜像后,就可以在node2创建
[root@node02 ~]# docker run -d -p 5000:5000 -v /opt/data/registry:/tmp/registry registry
[root@node02 ~]# ss -antl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 100 127.0.0.1:25 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 100 [::1]:25 [::]:*
LISTEN 0 128 [::]:5000 [::]:*
LISTEN 0 128 [::]:22 [::]:*
Harbor
无论是使用Docker-distribution去自建仓库,还是通过官方镜像跑容器的方式去自建仓库,通过前面的演示我们可以发现其是非常的简陋的,还不如直接使用官方的Docker Hub去管理镜像来得方便,至少官方的Docker Hub能够通过web界面来管理镜像,还能在web界面执行搜索,还能基于Dockerfile利用Webhooks和Automated Builds实现自动构建镜像的功能,用户不需要在本地执行docker build,而是把所有build上下文的文件作为一个仓库推送到github上,让Docker Hub可以从github上去pull这些文件来完成自动构建。
但无论官方的Docker Hub有多强大,它毕竟是在国外,所以速度是最大的瓶颈,我们很多时候是不可能去考虑使用官方的仓库的,但是上面说的两种自建仓库方式又十分简陋,不便管理,所以后来就出现了一个被 CNCF 组织青睐的项目,其名为Harbor。
Harbor简介
Harbor是由VMWare在Docker Registry的基础之上进行了二次封装,加进去了很多额外程序,而且提供了一个非常漂亮的web界面。
Project Harbor是一个开源可信的云本地注册项目,用于存储、标识和扫描上下文。
Harbor通过添加用户通常需要的功能,如安全、身份和管理,扩展了开源Docker分发版。
Harbor支持高级特性,如用户管理、访问控制、活动监视和实例之间的复制。
Harbor的功能
Feathers:
—多租户内容签名和验证
-安全漏洞分析
——审计日志记录
—身份集成和基于角色的访问控制
—实例间的镜像复制
—可扩展API和图形化界面
-国际化(目前为中英文)
Harbor支持高级特性,如用户管理、访问控制、活动监视和实例之间的复制。
Docker compose
Harbor在物理机上部署是非常难的,而为了简化Harbor的应用,Harbor官方直接把Harbor做成了在容器中运行的应用,而且这个容器在Harbor中依赖类似redis、mysql、pgsql等很多存储系统,所以它需要编排很多容器协同起来工作,因此VMWare Harbor在部署和使用时,需要借助于Docker的单机编排工具(Docker compose)来实现。
Compose是一个用于定义和运行多容器Docker应用程序的工具。使用Compose,您可以使用YAML文件来配置应用程序的服务。然后,通过一个命令,您可以创建并启动配置中的所有服务。
Docker Compose官方文档
Harbor部署
Harbor官方文档
使用Harbor的注意事项:
1.在客户端上传镜像时一定要记得执行docker login进行用户认证,否则无法直接push
2.在客户端使用的时候如果不是用的https则必须要在客户端的/etc/docker/daemon.json配 置文件中配置insecure-registries参数----后接主机的ip
3.数据存放路径应在配置文件中配置到一个容量比较充足的共享存储中
4.Harbor是使用docker-compose命令来管理的,如果需要停止Harbor也应用docker-compose stop来停止,其他参数请–help
实例:
CentOS8
先下载compose容器编排包,利于管理hardor网页
[root@localhost ~]# curl -SL https://github.com/docker/compose/releases/download/v2.7.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 24.5M 100 24.5M 0 0 2530k 0 0:00:09 0:00:09 --:--:-- 3093k
[root@localhost ~]# ls
anaconda-ks.cfg docker-compose
[root@localhost ~]# mv docker-compose /usr/local/bin/ //移动到环境保护中
[root@localhost ~]# chmod +x /usr/local/bin/docker-compose //赋予x执行权限
[root@localhost ~]# ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose //做一个软连接,便于系统识别
[root@localhost ~]# which docker-compose //查看环境变量
/usr/local/bin/docker-compose
[root@localhost ~]# docker-compose version //查看版本
Docker Compose version v2.7.0
下载harbor包
[root@docker ~]# wget https://github.com/goharbor/harbor/releases/download/v2.4.3/harbor-offline-installer-v2.4.3.tgz
[root@localhost src]# ls
harbor-offline-installer-v2.4.3.tgz
//解压
[root@localhost src]# tar xf harbor-offline-installer-v2.4.3.tgz -C /usr/local/
[root@localhost local]# ls /usr/local/harbor/
common.sh harbor.yml.tmpl LICENSE(手册)
harbor.v2.4.3.tar.gz install.sh (安装脚本) prepare(支持)
将hardor这个模板剪切为hardor.yml
[root@localhost harbor]# mv harbor.yml.tmpl harbor.yml
//配置模板信息
[root@localhost harbor]# vim harbor.yml
hostname: 192.168.47.137 //只修改为当前的主机ip
//注释掉这几行
https:
# https port for harbor, default is 443
port: 443
# The path of cert and key files for nginx
certificate: /your/certificate/path
private_key: /your/private/key/path
harbor_admin_password: Harbor12345 //默认密码
配置信息解释
log: //日志容器
# options are debug, info, warning, error, fatal
level: info //为最低级别的日志
rotate_count: 50 //最多滚动50个日志
rotate_size: 200M //每次滚动超过200M后将重新生成
location: /var/log/harbor //日志的存放目录
这里需要在加速器指定ip
[root@localhost docker]# vim daemon.josn
{
"registry-mirrors": ["https://3bufl9dc.mirror.aliyuncs.com"]
"insecure-registries": ["192.168.47.137"]
}
执行脚本
[root@localhost harbor]# ./install.sh
//十个容器
[+] Running 10/10
⠿ Network harbor_harbor Created 0.2s
⠿ Container harbor-log Started 1.1s
⠿ Container redis Started 3.1s
⠿ Container registry Started 3.7s
⠿ Container registryctl Started 3.7s
⠿ Container harbor-portal Started 2.4s
⠿ Container harbor-db Started 3.3s
⠿ Container harbor-core Started 4.6s
⠿ Container harbor-jobservice Started 6.2s
⠿ Container nginx Started 6.4s
✔ ----Harbor has been installed and started successfully.----
//1514已启动
[root@localhost harbor]# ss -antl
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 128 127.0.0.1:1514 0.0.0.0:*
LISTEN 0 128 0.0.0.0:80 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
LISTEN 0 128 *:2375 *:*
LISTEN 0 80 *:3306 *:*
LISTEN 0 128 [::]:80 [::]:*
关闭防火墙并自启动
[root@localhost harbor]# systemctl stop firewalld
[root@localhost harbor]# systemctl disable firewalld
[root@localhost harbor]# vim /etc/selinux/config
[root@localhost harbor]# setenforce 0
[root@localhost harbor]# systemctl daemon-reload
[root@localhost harbor]# systemctl restart docker
[root@localhost harbor]# ss -antl
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 128 127.0.0.1:1514 0.0.0.0:*
LISTEN 0 128 0.0.0.0:80 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
LISTEN 0 128 *:2375 *:*
LISTEN 0 80 *:3306 *:*
LISTEN 0 128 [::]:80 [::]:*
访问网页
将harbor的程序运行成了容器
[root@localhost harbor]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
12c23f95a7e6 goharbor/harbor-jobservice:v2.4.3 "/harbor/entrypoint.…" 18 minutes ago Up 7 minutes (healthy) harbor-jobservice
a28d2b74f4aa goharbor/nginx-photon:v2.4.3 "nginx -g 'daemon of…" 18 minutes ago Up 7 minutes (healthy) 0.0.0.0:80->8080/tcp, :::80->8080/tcp nginx
5c309da5a8d7 goharbor/harbor-core:v2.4.3 "/harbor/entrypoint.…" 18 minutes ago Up 7 minutes (healthy) harbor-core
1544dc612b54 goharbor/harbor-portal:v2.4.3 "nginx -g 'daemon of…" 18 minutes ago Up 7 minutes (healthy) harbor-portal
df1c015ae5e2 goharbor/harbor-registryctl:v2.4.3 "/home/harbor/start.…" 18 minutes ago Up 7 minutes (healthy) registryctl
ccafaa54114f goharbor/redis-photon:v2.4.3 "redis-server /etc/r…" 18 minutes ago Up 7 minutes (healthy) redis
6bacbd9e272b goharbor/registry-photon:v2.4.3 "/home/harbor/entryp…" 18 minutes ago Up 7 minutes (healthy) registry
10af66cdcf2a goharbor/harbor-db:v2.4.3 "/docker-entrypoint.…" 18 minutes ago Up 7 minutes (healthy) harbor-db
95e4b8703d8c goharbor/harbor-log:v2.4.3 "/bin/sh -c /usr/loc…" 18 minutes ago Up 7 minutes (healthy) 127.0.0.1:1514->10514/tcp harbor-log
hardor已经启动了,但想一个问题当遇见真实生产环境时遇上关机和掉线,容器很可能会停止,以至于每次用手工去启动很繁琐。
这里是之前我们在启动容器脚本时生成的容器编排
[root@localhost harbor]# ls
common docker-compose.yml
[root@localhost harbor]# docker-compose restart //进行重启
[+] Running 9/9
⠿ Container harbor-log Started 11.1s
⠿ Container registry Started 2.2s
⠿ Container registryctl Started 11.9s
⠿ Container harbor-db Started 12.0s
⠿ Container harbor-portal Started 2.6s
⠿ Container redis Started 2.5s
⠿ Container harbor-core Started 1.4s
⠿ Container nginx Started 2.5s
⠿ Container harbor-jobservice Started 2.3s
//查看查看命令
[root@localhost harbor]# docker-compose --help
Usage: docker compose [OPTIONS] COMMAND
Docker Compose
Options:
--ansi string Control when to print ANSI control
characters ("never"|"always"|"auto")
(default "auto")
--compatibility Run compose in backward
compatibility mode
--env-file string Specify an alternate environment file.
-f, --file stringArray Compose configuration files
--profile stringArray Specify a profile to enable
--project-directory string Specify an alternate working
directory
(default: the path of the, first
specified, Compose file)
-p, --project-name string Project name
Commands:
build Build or rebuild services
convert Converts the compose file to platform's canonical format
cp Copy files/folders between a service container and the local filesystem
create Creates containers for a service.
down Stop and remove containers, networks
events Receive real time events from containers.
exec Execute a command in a running container.
images List images used by the created containers
上面说到了关机后容器会停止,所以我们运行一个脚本来进行开机自启
[root@localhost harbor]# vim startall.sh
#
!/bin/bash
cd /usr/local/hardor //这里首先执行时候需要进入绝对路径运行
/usr/local/bin/docker-compose stop && /usr/local/bin/docker-compose start //先停止后启动
[root@localhost harbor]# chmod +x startall.sh
[root@localhost harbor]# ls
common docker-compose.yml harbor.yml LICENSE startall.sh
common.sh harbor.v2.4.3.tar.gz install.sh prepare
//这里将脚本进入到容器,自动生成
[root@localhost harbor]# vim /etc/rc.local //这个配置文件是每次虚拟机启动后最后执行的命令
/bin/bash /usr/local/harbor/startall.sh //添加
[root@localhost harbor]# ll -d /etc/rc.local
lrwxrwxrwx. 1 root root 13 Jan 28 2022 /etc/rc.local -> rc.d/rc.local
[root@localhost harbor]# reboot
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
12c23f95a7e6 goharbor/harbor-jobservice:v2.4.3 "/harbor/entrypoint.…" About an hour ago Up 4 seconds (health: starting) harbor-jobservice
a28d2b74f4aa goharbor/nginx-photon:v2.4.3 "nginx -g 'daemon of…" About an hour ago Up 4 seconds (health: starting) 0.0.0.0:80->8080/tcp, :::80->8080/tcp nginx
5c309da5a8d7 goharbor/harbor-core:v2.4.3 "/harbor/entrypoint.…" About an hour ago Up 3 seconds (health: starting) harbor-core
1544dc612b54 goharbor/harbor-portal:v2.4.3 "nginx -g 'daemon of…" About an hour ago Up 3 seconds (health: starting) harbor-portal
df1c015ae5e2 goharbor/harbor-registryctl:v2.4.3 "/home/harbor/start.…" About an hour ago Up 3 seconds (health: starting) registryctl
ccafaa54114f goharbor/redis-photon:v2.4.3 "redis-server /etc/r…" About an hour ago Up 3 seconds (health: starting) redis
6bacbd9e272b goharbor/registry-photon:v2.4.3 "/home/harbor/entryp…" About an hour ago Up 3 seconds (health: starting) registry
10af66cdcf2a goharbor/harbor-db:v2.4.3 "/docker-entrypoint.…" About an hour ago Up 4 seconds (health: starting) harbor-db
95e4b8703d8c goharbor/harbor-log:v2.4.3 "/bin/sh -c /usr/loc…" About an hour ago Up 4 seconds (health: starting) 127.0.0.1:1514->10514/tcp harbor-log
上面运行了脚本后,在次重启虚拟机容器自动执行时容器开机自启
这时我们可以重新生成一个容器进行镜像挂载
//这个容器代表的是整体的数据存放位置
[root@localhost data]# docker ps | grep harbor-db
10af66cdcf2a goharbor/harbor-db:v2.4.3 "/docker-entrypoint.…" 2 hours ago Up About an hour (healthy) harbor-db
[root@localhost ~]# cd /
//这里可以看见根下有一个/data目录
[root@localhost /]# ls
bin data etc lib lty mnt proc run srv tmp var
boot dev home lib64 media opt root sbin sys usr
[root@localhost /]# cd data/
[root@localhost data]# ls
ca_download database job_logs redis registry secret
//这里做了6个存储卷挂载在宿主机上
他们都以之有相对应的容器
[root@localhost data]# ll
total 0
drwxr-xr-x. 2 10000 10000 6 Aug 11 18:20 ca_download
drwx------. 3 systemd-coredump input 18 Aug 11 18:20 database
drwxr-xr-x. 2 10000 10000 6 Aug 11 18:20 job_logs
drwxr-xr-x. 2 systemd-coredump input 22 Aug 11 20:37 redis
drwxr-xr-x. 2 10000 10000 6 Aug 11 18:20 registry
drwxr-xr-x. 5 root root 46 Aug 11 18:20 secret
创建用户
拉取一个镜像
[root@localhost ~]# docker pull busybox
Using default tag: latest
latest: Pulling from library/busybox
50783e0dfb64: Pull complete
Digest: sha256:ef320ff10026a50cf5f0213d35537ce0041ac1d96e9b7800bafd8bc9eff6c693
Status: Downloaded newer image for busybox:latest
docker.io/library/busybox:latest
这时我们的hardor已经创建好了,可以在里面进行镜像拉取和上传了
//busybox:latest是拉取已经存在的镜像,我们为它在创建一个镜像并打上标签
前面接的是ip、在接我们在网页里面的用户名 :v1接的是版本
[root@localhost ~]# docker tag busybox:latest 192.168.47.137/lty/busybox:v1
[root@localhost ~]# docker images | grep busybox
192.168.47.137/lty/busybox v1 7a80323521cc 12 days ago 1.24MB
ps:如果在启动中遇见
[root@localhost ~]# docker login -u admin -p Harbor12345 http://192.168.47.137
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
Error response from daemon: Get "http://192.168.47.137/v2/": dial tcp 192.168.47.137:80: connect: connection refused
//解决方法:
[root@localhost harbor]# cat /etc/docker/daemon.josn
{
"registry-mirrors":["https://3bufl9dc.mirror.aliyuncs.com"],
"insecure-registries":["192.168.47.137","192.168.47.139"]
}
[root@localhost harbor]# vim /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --insecure-registry "192.168.47.137" --containerd=/run/containerd/containerd.sock
//既可
//身份认证
[root@localhost harbor]# docker login -u admin -p Harbor12345 http://192.168.
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
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
推送镜像
[root@localhost harbor]# docker push 192.168.47.137/lty/busybox:v1
The push refers to repository [192.168.47.137/lty/busybox]
084326605ab6: Pushed
v1: digest: sha256:98de1ad411c6d08e50f26f392f3bc6cd65f686469b7c22a85c7b5fb1b820c154 size: 527
拉取镜像
//删除主机镜像
[root@localhost harbor]# docker rmi 192.168.47.137/lty/busybox:v1
Untagged: 192.168.47.137/lty/busybox:v1
Untagged: 192.168.47.137/lty/busybox@sha256:98de1ad411c6d08e50f26f392f3bc6cd65f686469b7c22a85c7b5fb1b820c154
//既然成功推送到个人仓库,测试从仓库在次拉取镜像
[root@localhost harbor]# docker pull 192.168.47.137/lty/busybox:v1
v1: Pulling from lty/busybox
Digest: sha256:98de1ad411c6d08e50f26f392f3bc6cd65f686469b7c22a85c7b5fb1b820c154
Status: Downloaded newer image for 192.168.47.137/lty/busybox:v1
192.168.47.137/lty/busybox:v1
[root@localhost harbor]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
192.168.47.137/lty/busybox v1 7a80323521cc 2 weeks ago 1.24MB //成功
使用Harbor的注意事项:
1.在客户端上传镜像时一定要记得执行docker login进行用户认证,否则无法直接push
2.在客户端使用的时候如果不是用的https则必须要在客户端的/etc/docker/daemon.json配 置文件中配置insecure-registries参数----后接主机的ip
3.数据存放路径应在配置文件中配置到一个容量比较充足的共享存储中
4.Harbor是使用docker-compose命令来管理的,如果需要停止Harbor也应用docker-compose stop来停止,其他参数请–help