docker数据卷介绍 Docker将运用与运行的环境打包形成容器运行, Docker容器产生的数据,如果不通过docker commit生成新的镜像,使得数据做为镜像的一部分保存下来, 那么当容器删除后,数据自然也就没有了。 为了能保存数据在Docker中我们使用卷。|
卷就是目录或文件,存在于一个或多个容器中,由Docker挂载到容器,但卷不属于联合文件系统(Union FileSystem),因此能够绕过联合文件系统提供一些用于持续存储或共享数据的特性:。
卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。
数据卷的特点:
1.数据卷可在容器之间共享或重用数据 2.卷中的更改可以直接生效 3.数据卷中的更改不会包含在镜像的更新中 4.数据卷的生命周期一直持续到没有容器使用它为止
docker的理念回顾 将应用和环境打包成一个镜像 数据?如果数据都在容器中,那么我们容器删除,数据就会丢失!?需求:数据可持久化
MySql,容器删了,删库跑路了!需求:MySQL数据可以保存在本地
容器之间可以有一个数据共享的技术!Docker容器中产生的数据,同步到本地!
这就是卷技术!目录的挂载,将我们容器内的目录,挂载到Linux上面!

?总结一句话:容器的持久化和同步操作!容器之间也是可以数据共享的!
用数据使卷
方式1:直接使用命令挂载
#docker run -it -v 主机目录:容器内目录

?#启动起来的时候我们可以通过docker inspect 容器id

?测试文件的同步

?再来测试!
1.停止容器
2.宿主机上修改文件
3.启动容器
4.容器内的数据依旧是同步的

?实战:安装MySQL
思考:MySQL的数据持久化的问题
#获取镜像

?#运行容器,需要做数据挂载 #安装启动mysql,需要配置密码的,
#官方测试:
$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
-
#-d 后台运行
#-p 端口映射
#-v 卷(目录)挂载
#-e 环境配置
#-name 容器命名
docker run -d -p 3310:3306 -v /home/admin/mountDir/mysql/conf:/etc/mysql/conf.d -v /home/admin/mountDir/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root --name mysql01 mysql:5.7
#启动成功后,我们载本地使用navicat连接测试成功
#navicat-连接到服务器的3310 ----3310和容器内的3306映射,这个时候我们就是连接上了
#在本地测试创建一个数据库,查看一下我们映射的路径是否正确
假设我们将容器删除

具名挂载和匿名挂载
#匿名挂载
-v 容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx
#查看所有的volume的情况

#这里发现,这种就是匿名挂载,我们在 -v 只写了容器内路径,没有写容器外的路径
#具名挂载

?
?#通过 -v 卷名:容器内路径
#查看一下这个卷

?所有的docker容器内的卷,没有指定目录的情况下都是在??/var/lib/docker/volumes/xxxx/_data
我们通过具名挂载可以方便的找到我们的一个卷,大多数情况都使用? 具名挂载
?# 如何确定是具名挂载还是匿名挂载,还是指定路径挂载
-v 容器内路径? ? ? ? ? ? ? ?#匿名挂载
-v 卷名:/容器内路径? ? ? #具名挂载
-v /宿主机路径:/容器内路径? ? ? ?#指定路径挂载
?拓展:
# 通过-v 容器内路径:ro? ?rw 改变读写权限
ro? ?readonly? ? ?#只读
rw? ?readwrite? ?#可读可写
# 一旦设置了容器权限,容器对挂载出来的内容就有限制了
docker run -d -P --name nginx02 -v JM-nginx:/etc/nginx:ro nginx
docker run -d -P --name nginx02 -v JM-nginx:/etc/nginx:rw?nginx
#ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内部只有只读权限
初识Dockerfile?
Dockerfile就是用来构建docker镜像的构建文件!命令脚本??
通过脚本可以生成经i想,镜像是一层一层的,脚本的一个个的命令,每个命令都是一行!
# 创建一个dockerfile文件,名字可以随机,建议Dockerfile?
# 文件的内容?
FROM centos? ?--基础镜像
VOLUME ["volume1","volume2"]
CMD echo "-----------end-----------"
CMD /bin/bash
# 这里的每个命令,都是镜像的一层
?
?
?# 启动自己写的容器

?这个卷和外部一定有一个同步的目录

?这种方式我们未来使用的会十分多,因为我们通常会构建自己的镜像!
假设构建镜像的时候没有挂载卷,要手动镜像挂载 -v 卷名:容器内路径
数据卷容器
多个mysql同步数据!

?# 启动3个容器,通过我们刚才自己构建的镜像myimage来启动


?
?
?# 测试:可以删除docker01(抽象意义上的父容器),查看docker02和docker03是否还可以访问这个文件
# 答案:依旧可以访问
?
?多个mysql实现数据共享

?结论:
容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止!
但是一旦持久化到了本地,这个时候,本地的数据是不会删除的!
?DockerFile
dockerfile介绍
dockerfile 是用来构建docker镜像的文件!命令参数脚本!
构建步骤:
1.编写一个dockerfile文件
2.docker build 构建成为一个镜像
3.docker run 运行镜像
4.docker push 发布镜像(DockerHub,阿里云镜像仓库!)


?很多官方镜像都是基础包,很多功能都没有,我们通常需要自己搭建需要的镜像
官方既然可以制作镜像,我们也可以!
DockerFile构建过程
基础知识:
1. 每个保留关键字(指令)都是必须是大写字母
2. 执行从上到下顺序执行
3. # 表示注释
4. 每一个命令都会创建提交一个新的镜像曾,并提交!
DockerFile是面向开发的,我们以后要发布项目,做镜像,需要编写dockerfile文件,这个文件十分简单
Docker镜像逐渐成为了企业交付的标准,必须要掌握!
DockerFile: 构建文件,定义了一切的步骤,源代码
DockerImages: 通过DockerFile构建生成的镜像,最终发布和运行的产品
Docker容器: 容器就是镜像运行起来提供服务的
DockerFile的指令
FROM? ? ? ? ? ? ? ? ? ? ? ?# 基础镜像,一切从这里开始
MAINTAINER? ? ? ? ? ? # 镜像是谁写的, 姓名+邮箱
RUN? ? ? ? ? ? ? ? ? ? ? ? ? # 镜像构建的时候需要运行的命令
ADD? ? ? ? ? ? ? ? ? ? ? ? ? # 步骤: 构建包含tomcat的镜像,添加tomcat压缩包即可
WORKDIR? ? ? ? ? ? ? ? # 镜像的工作目录
VOLUME? ? ? ? ? ? ? ? ? # 卷:挂载的路径
EXPOSE? ? ? ? ? ? ? ? ? ?# 配置端口暴露,否则需要 -p 指定
CMD? ? ? ? ? ? ? ? ? ? ? ? ? # 指定这个容器启动的时候要运行的命令,只有最后一个生效,可被替代
ENTRYPOINT? ? ? ? ? ?# 指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD? ? ? ? ? ? ? ? ? # 构建一个被继承的DockerFile时,就会运行 ONBUILD 的指令(触发指令)
COPY? ? ? ? ? ? ? ? ? ? ? ? # 类似ADD,将我们文件拷贝到镜像中
ENV? ? ? ? ? ? ? ? ? ? ? ? ? #构建的时候设置环境变量

?实战测试
?DockerHub 中99% 镜像都是从这个基础镜像scratch构建起来的,然后配置需要的软件和配置向上构建
?创建一个自己的centos
?第一步,编写dockerfile的文件
?
?第二步,通过这个文件构建镜像
docker build -f dockerfile文件路径 -t 镜像名:[版本号]? 防止目录



?第三步,测试运行:
启动,默认进入工作目录

?对比:之前的原生的centos区别

?我们增加之后的镜像

?我们可以列出我们本地镜像的变更历史:

?CMD和ENTRYPOINT区别
测试cmd

?测试ENTRYPOINT
 

?Dockerfile中很多命令都十分相似,我们需要了解他们的区别,我们最好的学习就是对比他们然后测试效果!
?实战:tomcat镜像
1. 准备镜像文件 tomcat压缩包,jdk压缩包?

?2. 编写dockerfile文件,官方命名 Dockerfile ,build会自动寻找这个文件,就不需要 -f 去指定了
3. 构建镜像
docker build -t diytomcat .
4.执行
root@VM-16-5-debian:~# docker run -d -p 80:8080 --name zhangsantomcat1 -v /home/zhangsan/build/tomcat/test:/usr/local/apache-tomcat-9.0.27/webapps/test -v /home/zhangsan/build/tomcat/tomcatlogs/:/usr/local/apache-tomcat-9.0.27/logs diytomcat
55f18468f6df5f74d38dd9f48660b79da156a8169a40dc250ee2378b0066f3b8
5.访问测试

?6. 发布项目(由于做了卷挂载,我们直接在本地编写项目就可以发布了)
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
</web-app>

??

?发布自己的镜像
?DockerHub
1. 地址? 注册自己的账号Docker Hub https://hub.docker.com/
?2. 在我们的服务器上提交

?3.登录成功后,就可提交镜像了! dockers push?
?

?阿里云镜像
1.登录阿里云
2. 找到容器镜像服务
3.创建命名空间

4.创建容器镜像

?5.浏览阿里云页面信息


?2.后续操作,参考官方文档
小结
?
?Docker网络
测试 ip addr

?三个网络代表三种不同的环境
# 问题: docker 是如何处理容器之间的网络访问的

docker run -d -P --name tomcat01 tomcat

?# 查看容器的内部网络地址 ip addr ,发现容器启动的时候会得到一个 eth0@if61? ip地址,docker分配的

?总结:
1. 我们每次启动一个docker容器,docker就会给docker容器分配一个ip,我们只要安装了docker,就会有一个 docker0??网卡,通过 桥接模式 连接外网,使用的linux技术是 evth-pair?
再次测试 ip addr

?2.再启动一个容器测试?,发现又多了一对网卡!
 
?3. 我们来测试下 tomcat01 和 tomcat02 能否ping#?
?
?# 结论: 容器和容器之间是可以互相 ping 通的
绘制一个网络模型图

?结论: tomcat01 和 tomcat02 是公用同一个路由器,docker0 网卡 .所有容器在不指定网络的情况下,都是 docker0 路由的,docker 会给我们的容器分配一个默认的可用 ip 地址
?

?Docker中所有的网络接口都是虚拟的,虚拟转发的效率高!(内网传递文件!)
只要容器一删除,对应的一对网桥就没了
?--link?
?思考一个场景,我们编写了一个微服务程序, database url=ip:,项目不重启,数据库ip换掉了,我们希望可以处理这个问题,可以通过 [应用名称]?来进行访问容器数据?
通过--link 可以解决, tomcat03 通过 name 去ping 通tomcat02
探究:docker network inspect [docker0网关ID]

其实,tomcat03 hosts 文件配置了 tomcat02名称的 地址映射路径

?现在玩 Docker 已经不建议使用 --link
自定义网络,不适用docker0
docker0 问题,他不支持同意名连接访问
自定义网络
查看所有的docker 网络
?
?网络模式
bridge: 桥接模式(默认,自定义也可使用)
none:? ?不配置网络
host:? ? ?和宿主机共享网络
container: 容器内网络连通(少,局限大)
# 我们直接启动的命令 --net bridge ,而这个就是我们的docker0
docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat01 --net bridge tomcat
# docker0 特点,默认 域名不能访问, --link可以打通连接
# 我们可以自定义网络
# --driver bridge 选择 默认桥接模式
# --subnet 192.168.0.0/16 子网掩码
# --gateway 192.168.0.1 网关地址
 


?自定义网络,通过 ip 或者 name 都可以 ping 通
?好处:
redis-不同的集群使用不同的网络,保证集权是安全和健康的
mysql-不同的集群使用不同的网络,保证集权是安全和健康的
?网络连通

???????
?测试,打通 tomcat01 - mynet
root@VM-16-5-debian:~# docker network connect mynet tomcat01

将 tomcat01 网络分配新的ip,放到 mynet 网络内(一个容器,多个ip)
?
?实战:部署Redis集群
1. 首先创建redis 专用网络
root@VM-16-5-debian:~# docker network create --subnet 172.38.0.0/16 redis
225ab16bb7d6998e25a41842b8e62824e34469a41c203b70ff65f53c541f3a6b
root@VM-16-5-debian:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
7e03fe46b82d bridge bridge local
b591b08bafb9 host host local
a44b1b6b2f68 mynet bridge local
7487e650e2e8 none null local
225ab16bb7d6 redis bridge local
root@VM-16-5-debian:~# docker network inspect redis
[
{
"Name": "redis",
"Id": "225ab16bb7d6998e25a41842b8e62824e34469a41c203b70ff65f53c541f3a6b",
"Created": "2022-02-27T18:51:26.574357577+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.38.0.0/16"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
for port in $(seq 1 6);
do
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
docker run -p 6371:6379 -p 16371:16379 --name redis-1 \
-v /mydata/redis/node-1/data/:/data \
-v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6372:6379 -p 16372:16379 --name redis-2 \
-v /mydata/redis/node-2/data/:/data \
-v /mydata/redis/node-2/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.12 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6373:6379 -p 16373:16379 --name redis-3 \
-v /mydata/redis/node-3/data/:/data \
-v /mydata/redis/node-3/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.13 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6374:6379 -p 16374:16379 --name redis-4 \
-v /mydata/redis/node-4/data/:/data \
-v /mydata/redis/node-4/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.14 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6375:6379 -p 16375:16379 --name redis-5 \
-v /mydata/redis/node-5/data/:/data \
-v /mydata/redis/node-5/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.15 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6376:6379 -p 16376:16379 --name redis-6 \
-v /mydata/redis/node-6/data/:/data \
-v /mydata/redis/node-6/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.16 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
/data # redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-rep
licas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: ce491ee32f15fed12bb6e46f4416a8bc78a4e0a2 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
M: 8878c75cece10bc3ab61e08f53307261ec549f36 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
M: f447f5b7e23358a5c89e1d2ea4b2610afc2592f1 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
S: 88e98e82a17003af755f13dc8c6a461c1b194d67 172.38.0.14:6379
replicates f447f5b7e23358a5c89e1d2ea4b2610afc2592f1
S: cb1968ec2e75986748b469d94e3f79bb6fb186bb 172.38.0.15:6379
replicates ce491ee32f15fed12bb6e46f4416a8bc78a4e0a2
S: 64d6c279ab0e2386ff9c1b87f5c21d7d1c9d91bc 172.38.0.16:6379
replicates 8878c75cece10bc3ab61e08f53307261ec549f36
Can I set the above configuration? (type 'yes' to accept): y
/data # redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-rep
licas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: ce491ee32f15fed12bb6e46f4416a8bc78a4e0a2 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
M: 8878c75cece10bc3ab61e08f53307261ec549f36 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
M: f447f5b7e23358a5c89e1d2ea4b2610afc2592f1 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
S: 88e98e82a17003af755f13dc8c6a461c1b194d67 172.38.0.14:6379
replicates f447f5b7e23358a5c89e1d2ea4b2610afc2592f1
S: cb1968ec2e75986748b469d94e3f79bb6fb186bb 172.38.0.15:6379
replicates ce491ee32f15fed12bb6e46f4416a8bc78a4e0a2
S: 64d6c279ab0e2386ff9c1b87f5c21d7d1c9d91bc 172.38.0.16:6379
replicates 8878c75cece10bc3ab61e08f53307261ec549f36
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
..
>>> Performing Cluster Check (using node 172.38.0.11:6379)
M: ce491ee32f15fed12bb6e46f4416a8bc78a4e0a2 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 8878c75cece10bc3ab61e08f53307261ec549f36 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: f447f5b7e23358a5c89e1d2ea4b2610afc2592f1 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 64d6c279ab0e2386ff9c1b87f5c21d7d1c9d91bc 172.38.0.16:6379
slots: (0 slots) slave
replicates 8878c75cece10bc3ab61e08f53307261ec549f36
S: 88e98e82a17003af755f13dc8c6a461c1b194d67 172.38.0.14:6379
slots: (0 slots) slave
replicates f447f5b7e23358a5c89e1d2ea4b2610afc2592f1
S: cb1968ec2e75986748b469d94e3f79bb6fb186bb 172.38.0.15:6379
slots: (0 slots) slave
replicates ce491ee32f15fed12bb6e46f4416a8bc78a4e0a2
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
/data # redis-cli -c
127.0.0.1:6379> CLUSTER INFO
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:52
cluster_stats_messages_pong_sent:53
cluster_stats_messages_sent:105
cluster_stats_messages_ping_received:48
cluster_stats_messages_pong_received:52
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:105
127.0.0.1:6379> CLUSTER NODES
8878c75cece10bc3ab61e08f53307261ec549f36 172.38.0.12:6379@16379 master - 0 1645961571000 2 connected 5461-10922
f447f5b7e23358a5c89e1d2ea4b2610afc2592f1 172.38.0.13:6379@16379 master - 0 1645961572566 3 connected 10923-16383
64d6c279ab0e2386ff9c1b87f5c21d7d1c9d91bc 172.38.0.16:6379@16379 slave 8878c75cece10bc3ab61e08f53307261ec549f36 0 1645961571000 6 connected
88e98e82a17003af755f13dc8c6a461c1b194d67 172.38.0.14:6379@16379 slave f447f5b7e23358a5c89e1d2ea4b2610afc2592f1 0 1645961572967 4 connected
cb1968ec2e75986748b469d94e3f79bb6fb186bb 172.38.0.15:6379@16379 slave ce491ee32f15fed12bb6e46f4416a8bc78a4e0a2 0 1645961571966 5 connected
ce491ee32f15fed12bb6e46f4416a8bc78a4e0a2 172.38.0.11:6379@16379 myself,master - 0 1645961572000 1 connected 0-5460
127.0.0.1:6379> set a b
-> Redirected to slot [15495] located at 172.38.0.13:6379
OK
172.38.0.13:6379> get a
Error: Operation timed out
/data # get a
/bin/sh: get: not found
/data # redis-cli -c
127.0.0.1:6379> get a
-> Redirected to slot [15495] located at 172.38.0.14:6379
"b"
172.38.0.14:6379> cluter nodes
(error) ERR unknown command `cluter`, with args beginning with: `nodes`,
172.38.0.14:6379> CLUSTER NODES
cb1968ec2e75986748b469d94e3f79bb6fb186bb 172.38.0.15:6379@16379 slave ce491ee32f15fed12bb6e46f4416a8bc78a4e0a2 0 1645961809644 5 connected
f447f5b7e23358a5c89e1d2ea4b2610afc2592f1 172.38.0.13:6379@16379 master,fail - 1645961688791 1645961687388 3 connected
88e98e82a17003af755f13dc8c6a461c1b194d67 172.38.0.14:6379@16379 myself,master - 0 1645961809000 7 connected 10923-16383
ce491ee32f15fed12bb6e46f4416a8bc78a4e0a2 172.38.0.11:6379@16379 master - 0 1645961808641 1 connected 0-5460
8878c75cece10bc3ab61e08f53307261ec549f36 172.38.0.12:6379@16379 master - 0 1645961808142 2 connected 5461-10922
64d6c279ab0e2386ff9c1b87f5c21d7d1c9d91bc 172.38.0.16:6379@16379 slave 8878c75cece10bc3ab61e08f53307261ec549f36 0 1645961809543 6 connected
?我们使用了docker之后,所有的技术都会慢慢变得简单起来
SpringBoot微服务打包Docker镜像
|