Docker学习总结

在bilibili观看kuangshen讲解Docker做的笔记

安装

  1. 更新下载源
$ apt update
  1. 安装软件包以允许apt通过HTTPS使用存储库
$ apt install     apt-transport-https     ca-certificates     curl     gnupg-agent     software-properties-common
  1. 添加Docker的官方GPG密钥
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
  1. 通过搜索指纹的后8个字符,验证您现在是否拥有带有指纹的密钥
$ apt-key fingerprint 0EBFCD88
  1. 设置稳定的存储库
$ sudo add-apt-repository    "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

使用的是官方存储库,由于在国外,会下载很久还可能失败,建议使用国内存储库。由于我在阿里云的云服务器上安装,阿里云在背后已经配置了地址映射,我下载都是从阿里云下载很快,若是本地VM安装建议换掉

  1. 安装最新版本的Docker Engine和容器
$ apt install docker-ce docker-ce-cli containerd.io
  1. 列出仓库中可用的版本
$ apt-cache madison docker-ce

docker-ce | 5:20.10.33-0ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
docker-ce | 5:20.10.23-0ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
docker-ce | 5:20.10.13-0ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
docker-ce | 5:20.10.03-0ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
docker-ce | 5:19.03.153-0ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
docker-ce | 5:19.03.143-0ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
docker-ce | 5:19.03.133-0ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
docker-ce | 5:19.03.123-0ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages

……

  1. 使用第一行中的版本字符串,安装特定版本
$ apt install docker-ce=5:20.10.3~3-0~ubuntu-bionic docker-ce-cli=5:20.10.3~3-0~ubuntu-bionic containerd.io
  1. 测试是否成功
$ docker run hello-world

第一行会说没有安装成功,然后自己自动下载,下载完后就会说Hello from Docker!

镜像加速

登陆阿里云,然后在产品与服务 -> 容器镜像与服务 -> 开通该服务并设置密码 -> 镜像加速器 -> 操作文档

在操作文档里面选择自己安装的系统,然后复制下面给出的命令,到云主机中执行。

# 创建目录
sudo mkdir -p /etc/docker
# 写入镜像站点信息
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://pmr6fmnw.mirror.aliyuncs.com"]
}
EOF
# 重新载入镜像信息
sudo systemctl daemon-reload
# 重启docker服务
sudo systemctl restart docker

基础命令

命令行文档地址:https://docs.docker.com/engine/reference/run/

镜像搜索地址:https://hub.docker.com/search?q=&type=image

一、帮助命令

docker version # 显示docker的版本信息

docker info # 显示docker的系统信息,包括镜像和容器的数量

docker --help # 所有命令的粗略信息

docker 命令 --help(例:docker images --help) # 显示

二、镜像命令

1.列出所有安装的镜像
root@zxh:~$ docker images

REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest bf756fb1ae65 13 months ago 13.3kB

.# 解释各项的意思

pepository 镜像的仓库源

tag 镜像的标签,或者说版本,默认latest(最新)

image id 镜像的ID

created 镜像的创建时间

size 镜像的大小

.# 可选项

root@zxh:/etc/docker# docker images --help

Usage: docker images [OPTIONS] [REPOSITORY[:TAG]]

List images

Options:
-a, --all Show all images (default hides intermediate images)
–digests Show digests
-f, --filter filter Filter output based on conditions provided
–format string Pretty-print images using a Go template
–no-trunc Don’t truncate output
-q, --quiet Only show image IDs

2.搜索镜像

可以到官网站点图形化直接搜索某个镜像,也可以在命令行下敲命令搜索。以搜索mysql为例

root@zxh:~$ docker search mysql

root@zxh:~$ docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 10471 [OK]
mariadb MariaDB is a community-developed fork of MyS… 3894 [OK]
mysql/mysql-server Optimized MySQL Server Docker images. Create… 768 [OK]
percona Percona Server is a fork of the MySQL relati… 526 [OK]

……

过滤搜索

root@zxh:~$ docker search mysql -f stars=10000
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 10471 [OK]

3.下载镜像

下载MySQL为例,默认下载最新版。docker pull 镜像名[:tag] 。tag即版本,使用tag默认是latest,指定的版本一定要支持才行,可通过官网站点搜索该镜像,点进去后可查看支持的版本

root@zxh:~$ docker pull mysql
Using default tag: latest  # 如果不写tag,默认就是latest(最新的)
latest: Pulling from library/mysql
a076a628af6f: Pull complete  # 分层下载,一个个的下载
f6c208f3f991: Pull complete 
88a9455a9165: Pull complete 
406c9b8427c6: Pull complete 
7c88599c0b25: Pull complete 
25b5c6debdaf: Pull complete 
43a5816f1617: Pull complete 
1a8c919e89bf: Pull complete 
9f3cf4bd1a07: Pull complete 
80539cea118d: Pull complete 
201b3cad54ce: Pull complete 
944ba37e1c06: Pull complete 
Digest: sha256:feada149cb8ff54eade1336da7c1d080c4a1c7ed82b5e320efb5beebed85ae8c  # 签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest  # 真实地址

$ docker pull mysql 等价于 $ docker pull docker.io/library/mysql:latest

指定版本下载,例如下载MySQL的5.7版本。前面已经安装过mysql最新版,再安装另一个版本后会发现有点区别

root@zxh:~$ docker pull mysql:5.7
5.7: Pulling from library/mysql
a076a628af6f: Already exists # 已存在
f6c208f3f991: Already exists
88a9455a9165: Already exists
406c9b8427c6: Already exists
7c88599c0b25: Already exists
25b5c6debdaf: Already exists
43a5816f1617: Already exists
1831ac1245f4: Pull complete # 前面几个不会下载,从这里开始
37677b8c1f79: Pull complete
27e4ac3b0f6e: Pull complete
7227baa8c445: Pull complete
Digest: sha256:b3d1eff023f698cd433695c9506171f0d08a8f92a0c8063c1a4d9db9a55808df
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7

4.删除镜像

docker rmi 镜像名/镜像ID

root@zxh:~$ docker rmi a70d36bc331a
Untagged: mysql:5.7
Untagged: mysql@sha256:b3d1eff023f698cd433695c9506171f0d08a8f92a0c8063c1a4d9db9a55808df
Deleted: sha256:a70d36bc331a13d297f882d3d63137d24b804f29fa67158c40ad91d5050c39c5
Deleted: sha256:50c77bf7bcddd1f1d97789d80ac2404eec22c860c104e858620d2a2e321f0ef7
Deleted: sha256:14244329b83dfc8982398ee4104a548385652d2bffb957798ff86a419013efd6
Deleted: sha256:6d990477f90af28473eb601a9bca22253f6381e053c5a8edda0a4f027e124a3c
Deleted: sha256:ee0449796df204071589162fc16f8d65586312a40c68d1ba156c93c56f5e5ce8

删除多个

root@zxh:~$ docker rmi a70d36bc331a 621ceef7494a f6d0b4767a6c

删除全部。$()符号用于传递要删除镜像ID的参数

root@zxh:~$ docker rmi $(docker images -aq)

三、容器(container)命令

一个镜像运行之后就是一个容器,或者比喻成实例(对象相当于镜像,实例代表容器),可以启动多个镜像,一个镜像可以启动多次,每个都是容器,它们互相隔离。容器创建之后不管停不停止都会一直存在,直到删除。

1.创建容器

以下载centos镜像,然后创建容器为例

root@zxh:~$ docker pull centos
root@zxh:~$ docker run --help

可以看到有很多参数,最常用的参数

–name=“centos系统” # 给运行的容器起个名字

-d # 后台的方式运行

-it # 使用交互方式运行,进入容器查看内容

-p # 指定端口-p 8080,也可以和主机的端口相互映射-p 8080:8080,注意容器的端口不能随意指定,比如nginx使用80,不能随意指定其它,但主机的端口是随意的

​ -p 主机端口:容器端口

​ -p 容器端口

​ -p IP:主机端口:容器端口

​ 直接容器端口

-P # 随机指定端口。大写的P

-v # 容器数据卷,后面讲到

创建容器。并交互式运行,同时命名为centosxitong

root@zxh:~$ docker run -it --name=centosxitong centos /bin/bash
[root@8629db002d20 /]$ ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
[root@8629db002d20 /]$ exit  # 退出
exit
root@zxh:~$ 
2.查看有哪些运行的容器

默认查看正在运行的容器

root@zxh:~$ docker ps

-a # 查看当前正在运行的和历史创建过的容器

-q # 只显示容器的ID,该ID和镜像的ID是不一样

-n=5 # 查看最近创建的容器(最近的5个)

3.交互式下退出容器

容器停止退出

[root@8629db002d20 /]$ exit  # 退出
exit

容器不停止退出

[root@8629db002d20 /]$   # 按ctrl+ P + Q
4.删除容器

当容器在后台运行没有停止,可以删除容器,由于正在运行不能删除,加参数 -f 强制删除。docker rm 容器ID -f

也可以全部删除。

root@zxh:~$ docker rm -f $(docker ps -aq)
5.启动和停止容器
root@zxh:~$ docker start 容器ID		# 开启已创建的容器(后台运行)
root@zxh:~$ docker restart 容器ID		# 重启正在运行的容器
root@zxh:~$ docker stop 容器ID		# 停止正在运行的容器
root@zxh:~$ docker kill 容器ID		# 强制退出正在运行的容器(没有走软件正常的退出流程)
6.进入正在运行的容器

docker start 启动的容器是后台运行的,使用命令进行交互式

docker exec -it 容器ID 容器使用的shell # 进入后相当于新建一个终端,退出后容器还在运行

或者

docker attach 容器ID # 进入后相当于使用该容器的终端,退出后容器停止

root@zxh:~$ docker ps 
CONTAINER ID   IMAGE     COMMAND       CREATED         STATUS          PORTS     NAMES
41376cc19388   centos    "/bin/bash"   6 minutes ago   Up 15 seconds             admiring_austin
root@zxh:~$ docker exec -it 41376cc19388 /bin/bash
[root@41376cc19388 /]# exit
exit
root@zxh:~$ docker ps 
CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS         PORTS     NAMES
41376cc19388   centos    "/bin/bash"   10 minutes ago   Up 4 minutes             admiring_austin
root@zxh:~$ docker attach 41376cc19388
[root@41376cc19388 /]# exit
exit
root@zxh:~$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

四、辅助命令

1.后台创建启动容器

后台启动,docker ps会发现没有正在运行的容器,因为docker使用后台运行就必须要有一个前台应用,否则自动停止

root@zxh:~$ docker run -d centos
2.查看日志

查看运行容器时的所有操作日志docker logs 容器ID ,查看所有日志

root@zxh:~$ docker logs 41376cc19388

-t # 显示时加上时间戳

–tail 5 # 限制输出最后n(5)个

root@zxh:~$ docker logs --tail 2 41376cc19388

注意:root@zxh:~$ docker exec -it 41376cc19388 /bin/bash命令进入容器后的操作不会被记录

3.查看容器的进程信息
root@zxh~$ docker top 41376cc19388
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                13688               13658               0                   22:42               pts/0               00:00:00            /bin/bash
4.查看容器的元数据
root@zxh:~$ docker inspect 41376cc19388
5.复制容器内部的文件到外面的宿主机(也可反方向)

在容器内新建一个zhong.txt文件,退出容器,使用以下命令复制文件到达宿主机docker cp 容器ID:容器内文件路径 宿主机路径

root@zxh:~$ docker cp 41376cc19388:/root/zhong.txt ~/  # 内到外
root@zxh:~$ docker cp ~/zhong.txt  3e92d468df2f:/etc/nginx/  # 外到内

小实战,部署Nginx

  1. 搜索镜像
root@zxh:~$ docker search nginx
  1. 下载镜像
root@zxh:~$ docker pull nginx
  1. 查看镜像
root@zxh:~$ docker images
  1. 创建容器
root@zxh:~$ docker run -d -p 80:80 nginx
  1. 查看正在运行的容器
root@zxh:~$ docker ps

CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                NAMES
3e92d468df2f   nginx     "/docker-entrypoint.…"   13 minutes ago   Up 13 minutes   0.0.0.0:80->80/tcp   adoring_archimedes
  1. 查看部署是否成功
root@zxh:~$ curl 127.0.0.1:80
  1. 进入容器
root@zxh:~$ docker exec -it 3e92d468df2f /bin/bash
  1. 查看与nginx有关文件的路径
(进入nginx后)root@3e92d468df2f:/# whereis nginx
nginx: /usr/sbin/nginx /usr/lib/nginx /etc/nginx /usr/share/nginx
  1. 查看文件
root@3e92d468df2f:/# cd /etc/nginx
root@3e92d468df2f:/etc/nginx# ls
conf.d  fastcgi_params  koi-utf  koi-win  mime.types  modules  nginx.conf  scgi_params  uwsgi_params  win-utf
  1. 复制文件
root@zxh:~$ docker cp 3e92d468df2f:/etc/nginx/nginx.conf ~/
root@zxh:~$ ls
code  dump.rdb  nginx.conf  zhong.txt

图形化管理Docker

root@zxh:~$ docker run -d -p 8088:9000  --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer

使用以上命令后会自动下载镜像并创建运行容器,之后就可以在浏览器访问。进入面板后,首相设置一个密码,之后选择本地,就可以查看和管理Docker

高级使用

一个软件的运行需要操作系统环境,需要库环境等,docker镜像使用分层的思想,假设操作系统环境作为第一层,然后将一些依赖的库环境作为一层放到第一层的上方,形成两层的结构,软件再次形成一层放到第二层的上方,形成三层结构…… 每次新增或者修改都不会改变原来已封装好的结构层(这些结构层打包起来形成镜像)。镜像创建容器后,在容器内进行修改操作,所有的修改就是一层,放在原来的容器上方,该容器可以打包提交形成新的镜像,该镜像比之前的镜像多加了一层。

一、commit镜像

提交容器,使用命令和git的commit命令类似,注意:该命令要在容器运行时使用

root@zxh:~$ docker commit  -m="提交的描述信息"  -a="作者"  容器ID  目标镜像名[:tag版本]

实战测试

docker的tomcat镜像中默认的站点不存在,但有一个模板文件夹,每次测试是否部署成功都需要拷贝模板文件到达webapps目录下,那么我们可以部署测试成功后,将该容器打包提交形成新的镜像,下次使用该镜像后,就不需要再拷贝模板文件到webapps目录了。

下载tomcat

root@zxh:~# docker pull tomcat

Using default tag: latest
latest: Pulling from library/tomcat
b9a857cbf04d: Pull complete
d557ee20540b: Pull complete
3b9ca4f00c2e: Pull complete
667fd949ed93: Pull complete
661d3b55f657: Pull complete
511ef4338a0b: Pull complete
a56db448fefe: Pull complete
00612a99c7dc: Pull complete
326f9601c512: Pull complete
c547db74f1e1: Pull complete
Digest: sha256:94cc18203335e400dbafcd0633f33c53663b1c1012a13bcad58cced9cd9d1305
Status: Downloaded newer image for tomcat:latest
docker.io/library/tomcat:latest

以交互式运行tomcat

root@zxh:~# docker run -it -p 8080:8080 tomcat

新建终端后,再新终端敲命令

查看正在运行的容器

root@zxh:~# docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ee777f9bc81e tomcat “catalina.sh run” 20 seconds ago Up 19 seconds 0.0.0.0:8080->8080/tcp epic_torvalds

进入容器

root@zxh:~# docker exec -it ee777f9bc81e /bin/bash

复制模板文件到webapps目录

root@ee777f9bc81e:/usr/local/tomcat# cp -r webapps.dist/* webapps

此时浏览器可以访问了(如果使用阿里云主机,需要到控制台的安全,防火墙中允许8080端口通过)

打包提交容器

root@zxh:~# docker commit -a=“zhongxiaohang” -m=“tomcat is OKdocker ps -a!” ee777f9bc81e tomcatsoga:1.0

查看镜像是否创建成功

root@zxh:~# docker images

REPOSITORY TAG IMAGE ID CREATED SIZE
tomcatsoga 1.0 3201a04bd7af 2 minutes ago 654MB
tomcat latest 040bdb29ab37 4 weeks ago 649MB

停止之前的tomcat

root@zxh:~# docker stop ee777f9bc81e

运行tomcatsoga镜像

root@zxh:~# docker run -it -p 8080:8080 3201a04bd7af

不用修改里面的内容,直接在浏览器访问,就可以访问的到

二、容器数据卷

容器产生的数据在容器内部,删除容器后数据也会跟着被删除,使用容器数据卷(相当于目录挂载)可以共享文件,将容器内部的目录同步到宿主机的目录,或者容器与容器之间目录的共享。

挂载命令和实战
root@zxh:~$ docker run -it -v 主机目录:容器目录 -p 主机端口:容器端口 镜像ID /bin/bash

实战测试一,Centos

挂载,宿主机的/home/centostest目录,和centos容器的/home目录同步。双向的

root@zxh:~# docker run -it -v /home/centostest:/home centos /bin/bash

在/home目录下自动创建centostest目录

查看宿主机该目录内部是否有东西。空的

root@zxh:~# ls /home/centostest/

查看centos容器的/home目录下是否有东西。空的

[root@30c260de846a /]# ls /home/

在centos容器中的/home目录下创建文件

[root@30c260de846a /]# touch /home/zhong.txt

查看宿主机的/home/centostest目录是否有东西。有文件

root@zxh:~# ls /home/centostest/
zhong.txt

注意:新增文件是双向的,删除文件也是双向的。但是删除容器后,宿主机的目录不会被删除

实战测试二,MySQL

安装MySQL5.7版本

root@iZbp18qtvejt7jqx8ghcanZ:~# docker pull mysql:5.7

查看镜像

root@iZbp18qtvejt7jqx8ghcanZ:~# docker images

创建容器,后台运行。挂载目录可以同时挂载多个,使用多个-v参数就行。安装MySQL要设置密码,怎么添加参数docker官网的MySQL镜像说明已给出。将MySQL的配置文件目录和数据文件目录挂载同步到宿主机的目录

root@iZbp18qtvejt7jqx8ghcanZ:~# docker run -d -p 3306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

navicat连接MySQL,成功。注意阿里云主机安全,防火墙开放3306端口

查看挂载后的目录中的数据

root@zxh:/home/mysql# ls data/
auto.cnf ca.pem client-key.pem ibdata1 ib_logfile1 mysql private_key.pem server-cert.pem sys
ca-key.pem client-cert.pem ib_buffer_pool ib_logfile0 ibtmp1 performance_schema public_key.pem server-key.pem

载navicat中新建一个叫zhongxiaohang的数据库,然后再次查看目录中的数据

root@zxh:/home/mysql# ls data/
auto.cnf client-cert.pem ibdata1 ibtmp1 private_key.pem server-key.pem
ca-key.pem client-key.pem ib_logfile0 mysql public_key.pem sys
ca.pem ib_buffer_pool ib_logfile1 performance_schema server-cert.pem zhongxiaohang

具名挂载和匿名挂载

匿名挂载

root@zxh:~# docker run -d -P -v /home/nginx:/etc/nginx nginx

具名挂载(常用)

宿主目录不指定,使用一个名字(该名字前没有/符号,否则为目录),或者没有名字而随机生成名字

root@zxh:~# docker run -d -P -v juming_nginx:/etc/nginx nginx

查看容器数据卷

root@zxh:~# docker volume ls
DRIVER VOLUME NAME
local c17fcbcf4f9c0a975c9728024456c0866574b700a77f8febbd0575896e2d7b12
local juming_nginx

查看容器数据卷的详细信息

root@zxh:~# docker volume inspect juming_nginx

[
{
“CreatedAt”: “2021-02-13T15:35:29+08:00”,
“Driver”: “local”,
“Labels”: null,
“Mountpoint”: “/var/lib/docker/volumes/juming_nginx/_data”, # 默认宿主机挂载位置
“Name”: “juming_nginx”,
“Options”: null,
“Scope”: “local”
}
]

具名挂载扩展知识

:ro 代表容器的该文件夹是只读权限,不能在容器内部修改该文件夹,只能在外部修改

root@zxh:~# docker run -d -P -v juming_nginx:/etc/nginx:ro nginx

:rw 代表可读可写权限(默认)

root@zxh:~# docker run -d -P -v juming_nginx:/etc/nginx:rw nginx

注意:删除容器时,不会删除挂载后宿主机的目录,但删除容器数据卷时,即docker volume rm 卷名,会删除宿主机的目录

初识Dockerfile

实战一

dockerfile就是用来构建docker镜像的构建文件。通过这个脚本可以生成镜像,镜像是一层一层的,脚本也是一行一行命令,每个命令都是一层。

root@zxh:~# mkdir /home/dockerfiles
root@zxh:~# vim /home/dockerfiles/centos01
root@zxh:~# cat /home/dockerfiles/centos01 
FROM centos
VOLUME ["volume01", "volume02"]
CMD echo "-------------end--------------------"
CMD /bin/bash

命令都是大写,FROM导入原始镜像,VOLUME挂载,CMD是shell命令

生成镜像。-f指定脚本(绝对)路径,-t指定镜像名和版本,.代表放置到当前位置

root@zxh:~# docker build -f /home/dockerfiles/centos01 -t zhong_centos:1.0 .
Sending build context to Docker daemon  1.445GB
Step 1/4 : FROM centos
 ---> 300e315adb2f
Step 2/4 : VOLUME ["volume01", "volume02"]
 ---> Running in d765e433cb15
Removing intermediate container d765e433cb15
 ---> 4871f0e5d93a
Step 3/4 : CMD echo "-------------end--------------------"
 ---> Running in 878a616f0e85
Removing intermediate container 878a616f0e85
 ---> fc6fb1a9bd29
Step 4/4 : CMD /bin/bash
 ---> Running in 6dc40a91ef69
Removing intermediate container 6dc40a91ef69
 ---> 8ace5319ed40
Successfully built 8ace5319ed40
Successfully tagged zhong_centos:1.0

查看容器卷。空的

root@iZbp18qtvejt7jqx8ghcanZ:~# docker volume ls
DRIVER    VOLUME NAME

查看镜像

root@zxh:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
zhong_centos 1.0 8ace5319ed40 About a minute ago 209MB

创建容器,发现挂载的volume01和volume02目录已存在,在任意一个目录中创建文件,退出容器

root@zxh:~# docker run -it 8ace5319ed40 /bin/bash
[root@6ba1b3fec0f0 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var  volume01  volume02
[root@6ba1b3fec0f0 /]# touch volume01/zhong.txt
[root@6ba1b3fec0f0 /]# exit

再次查看容器卷,发现新增了两个匿名的容器卷

root@zxh:~# docker volume ls
DRIVER    VOLUME NAME
local     529dcaa0ee8401c2070c38dc2f3fa6691d5cf9ed71f893befa13ebf08dba1956
local     c101bee56174287f76cf1637e1e7bb519297123ea2b70551cc8883b8fb1d21f6

查看任意一个容器卷的详细信息,该卷挂载到宿主机的/var/lib/docker/volumes/c101bee56174287f76cf1637e1e7bb519297123ea2b70551cc8883b8fb1d21f6/_data目录,查看该目录有什么,之前建立的zhong.txt文件存在,说明脚本成功

root@zxh:~# docker volume inspect c101bee56174287f76cf1637e1e7bb519297123ea2b70551cc8883b8fb1d21f6
[
    {
        "CreatedAt": "2021-02-13T16:55:09+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/c101bee56174287f76cf1637e1e7bb519297123ea2b70551cc8883b8fb1d21f6/_data",
        "Name": "c101bee56174287f76cf1637e1e7bb519297123ea2b70551cc8883b8fb1d21f6",
        "Options": null,
        "Scope": "local"
    }
]
root@zxh:~# ls /var/lib/docker/volumes/c101bee56174287f76cf1637e1e7bb519297123ea2b70551cc8883b8fb1d21f6/_data
zhong.txt

实战二,多个容器之间的数据卷共享(使用了实战一的镜像)

之前都是在容器和宿主机之间的数据卷共享,现在要进行多个容器(相同镜像产生的多个容器)之间的数据卷共享,原理是第一个容器和宿主机之间数据卷共享,第二个容器共用(继承)第一个容器的数据卷,就相当于宿主机作为中间人,多个容器共同连接宿主机,它们之间互相同步,修改任意一方其它方都会改变。

使用实战一的镜像创建一个容器,名字叫centos0001。容器内的挂载目录新建一个文件

docker run -it --name centos0001 86a040f8ca4a
[root@acc5eacb55bb /]# touch volume01/zhong.txt

新建终端,在新终端中查看容器数据卷,产生了一个卷

root@iZbp18qtvejt7jqx8ghcanZ:~# docker volume ls
DRIVER VOLUME NAME
local 10854663bffa790fd935e547396db284f51b171474bda69cc1e399b1a3686dbb

在新终端中再次创建容器,名字叫centos0003,--volumes-from参数指定要共用数据卷的容器。查看挂载目录,有一个zhong.txt文件说明多个容器之间数据卷共享成功

docker run -it --name centos0003 --volumes-from centos0001 86a040f8ca4a
[root@bf457e887479 /]# ls volume01/
zhong.txt

再次创建一个新终端,查看容器数据卷的详细信息,查看宿主机的挂载目录

root@zxh:~# docker volume ls
DRIVER    VOLUME NAME
local     10854663bffa790fd935e547396db284f51b171474bda69cc1e399b1a3686dbb

root@zxh:~# docker volume inspect 10854663bffa790fd935e547396db284f51b171474bda69cc1e399b1a3686dbb          [
    {
        "CreatedAt": "2021-02-13T17:33:13+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/10854663bffa790fd935e547396db284f51b171474bda69cc1e399b1a3686dbb/_data",
        "Name": "10854663bffa790fd935e547396db284f51b171474bda69cc1e399b1a3686dbb",
        "Options": null,
        "Scope": "local"
    }
]
root@zxh:~# ls /var/lib/docker/volumes/10854663bffa790fd935e547396db284f51b171474bda69cc1e399b1a3686dbb/_data
zhong.txt

无论修改任意一个容器,还是修改宿主机的数据卷目录,它们都同步

三、Dockerfile

Dockerfile指令

FROM				# 导入基础镜像,一切从这里开始构建
MAINTAINER			# 指定镜像的作者,名字<邮箱>。zhongxiaohang<2915713554@qq.com>
RUN					# 镜像构建的时候需要运行的命令。RUN yum -y install vim
ADD					# 添加本地文件到镜像,添加压缩包,自动解压
WORKDIR				# 镜像的工作目录比如 / 或 ~/ 等。WORKDIR $MYPATH
VOLUME				# 挂载的目录
EXPOSE				# 暴露的端口配置。EXPOSE 80
CMD					# 指定镜像创建的容器启动的时候运行什么命令,命令行传参时会覆盖脚本中的命令。CMD echo "====end===="或者CMD ["ls", "-a"]
ENTRYPOINT			# 指定镜像创建的容器启动的时候运行什么命令,每个命令添加到前面一个命令的尾部
COPY				# 类似ADD,将文件拷贝到镜像
ONBUILD				# 但构建一个被继承时,就会触发该命令
ENV					# 构建的时候设置环境变量,键值对的方式设置。ENV MYPATH /usr/local
实战测试一

官方的centos默认没有vim和ifconfig工具,构建有该工具的centos,开放80端口,设置工作目录和设置环境变量

编写脚本

root@zxh:~# vim /home/dockerfiles/centos02
root@zxh:~# cat /home/dockerfiles/centos02
FROM centos
MAINTAINER zhongxiaohang<2915713554@qq.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "======end========"
CMD /bin/bash

构建镜像

root@zxh:~# docker build -f /home/dockerfiles/centos02 -t mycentos:2.0 .

查看镜像

root@zxh:~# docker images
REPOSITORY            TAG       IMAGE ID       CREATED         SIZE
mycentos              2.0       6cc6e9d58309   9 seconds ago   291MB

创建容器

root@zxh:~# docker run -it 6cc6e9d58309

测试,工作目录、环境变量、ifconfig命令执行成功

[root@e8218d23654a local]# pwd
/usr/local
[root@e8218d23654a local]# echo $MYPATH
/usr/local
[root@e8218d23654a local]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
……

也可以查看镜像怎么构建

root@zxh:~#docker history 镜像ID

root@zxh:~# docker history 6cc6e9d58309
IMAGE CREATED CREATED BY SIZE COMMENT
6cc6e9d58309 8 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" “-c” “/bin… 0B
c7f96158a38d 8 minutes ago /bin/sh -c #(nop) CMD [”/bin/sh" “-c” “echo… 0B
51354c004f60 8 minutes ago /bin/sh -c #(nop) CMD [”/bin/sh" “-c” “echo… 0B
e1f348e14e05 8 minutes ago /bin/sh -c #(nop) EXPOSE 80 0B
b00ff8d38269 8 minutes ago /bin/sh -c yum -y install net-tools 23.3MB
90ebea082790 8 minutes ago /bin/sh -c yum -y install vim 58.1MB
25cadbca8e53 8 minutes ago /bin/sh -c #(nop) WORKDIR /usr/local 0B
8cd0c90b1c6d 8 minutes ago /bin/sh -c #(nop) ENV MYPATH=/usr/local 0B
77509a3f14c4 8 minutes ago /bin/sh -c #(nop) MAINTAINER zhongxiaohang<… 0B
300e315adb2f 2 months ago /bin/sh -c #(nop) CMD [”/bin/bash"] 0B
2 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
2 months ago /bin/sh -c #(nop) ADD file:bd7a2aed6ede423b7… 209MB

四、发布镜像

发布到DockerHub

1.到Docker主页注册账号

2.类似Git在命令行登陆

root@zxh:~$ docker login -u DockerID
Password: 

3.修改镜像命名和版本号,命名格式:容器ID/名字[:版本号]

root@zxh:~$ docker images
REPOSITORY                 TAG       IMAGE ID       CREATED         SIZE
tomcatsoga                 1.0       a2bc188c1069   6 days ago      654MB
root@zxh:~$ docker tag a2bc188c1069 zhongxiaohang/tomcatsoga:1.0

4.提交镜像,很慢。提交完后在Docker官网自己的账号下就可以看到

root@zxh:~$ docker push zhongxiaohang/tomcatsoga:1.0
发布到阿里云

1.登陆阿里云,到容器镜像服务

2.创建命名空间并启动,默认私有,命名空间相当于一个大工程的名称。比如创建了一个zhongxiaohang的命名空间

3.第一次不知道怎么提交,所以先创建镜像仓库,命名空间默认使用上一步创建的。比如仓库名称为dockertest,私有,摘要随便填。下一步后选择本地仓库。创建完后点击刚创建的镜像仓库,里面写明了所有操作。按照操作指示进行以下操作。

4.在命令行下登录阿里云Docker Registry

root@zxh:~$ docker login --username=玩家_名狱 registry.cn-hangzhou.aliyuncs.com

用于登录的用户名为阿里云账号全名,密码为开通服务时设置的密码。

您可以在访问凭证页面修改凭证密码。

5.修改镜像标签,提交镜像

root@zxh:~$ docker images
REPOSITORY                 TAG       IMAGE ID       CREATED        SIZE
zhongxiaohang/tomcatsoga   1.0       a2bc188c1069   6 days ago     654MB

root@zxh:~$ docker tag a2bc188c1069 registry.cn-hangzhou.aliyuncs.com/zhongxiaohang/tomcatsoga:2.0
root@zxh:~$ docker push registry.cn-hangzhou.aliyuncs.com/zhongxiaohang/tomcatsoga:2.0

6.到镜像仓库查看,有tomcatsoga的镜像

五、备份和恢复

之前都是基于网络传输镜像,现在可以通过打包多个镜像为压缩包,然后拷贝到其它电脑,然后恢复镜像。

root@zxh:~$ docker images   # 查看镜像
REPOSITORY                   TAG       IMAGE ID       CREATED        SIZE
zhongxiaohang/tomcatsoga     1.0       a2bc188c1069   6 days ago     654MB
redis                        latest    621ceef7494a   5 weeks ago    104MB
root@zxh:~$ docker save zhongxiaohang/tomcatsoga redis -o ~/dockerimages.zip  # 打包两个镜像
root@zxh:~$ ls  # 查看是否生成
dockerimages.zip
root@zxh:~$ docker rmi --force zhongxiaohang/tomcatsoga:1.0 redis  # 强制删除镜像
root@zxh:~$ docker load --input ~/dockerimages.zip  # 加载本地打包的镜像

Docker网络

安装Docker后有个虚拟网卡docker0,之后每创建运行一个容器(运行时才有),就有一个veth虚拟网卡,该虚拟网卡桥接到docker0上,这样的话每个容器和宿主机它们在同一个内网下

root@zxh:~$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:16:3e:14:ef:bc brd ff:ff:ff:ff:ff:ff
    inet 172.17.42.167/18 brd 172.17.63.255 scope global dynamic eth0
       valid_lft 314759826sec preferred_lft 314759826sec  # 本地网卡
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:0c:8e:1a:d7 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0
       valid_lft forever preferred_lft forever  # Docker虚拟网卡
9: veth9b21263@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether ce:0e:98:af:20:c9 brd ff:ff:ff:ff:ff:ff link-netnsid 1  # 某一个容器网卡
11: veth28408ee@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 82:f3:0b:7b:39:e6 brd ff:ff:ff:ff:ff:ff link-netnsid 2  # 某一个容器网卡

各容器通过容器名互相访问

网络IP可能会发生变化,我们希望各容器之间能通过容器名,互相访问

1.新建两个容器,然后退出

root@zxh:~$ docker run -it --name centos01 centos /bin/bash
[root@69a8a12a8eee /]# exit
root@zxh:~$ docker run -it --name centos02 centos /bin/bash
[root@56516a03524a /]# exit

2.查看容器ID

root@zxh:~$ docker ps -a
CONTAINER ID   IMAGE    COMMAND        CREATED              STATUS                      PORTS    NAMES
56516a03524a   centos   "/bin/bash"    50 seconds ago       Exited (0) 43 seconds ago            centos02
69a8a12a8eee   centos   "/bin/bash"    About a minute ago   Exited (0) 59 seconds ago            centos01

3.运行容器(后台运行)

root@zxh:~$ docker start 69a8a12a8eee
root@zxh:~$ docker start 56516a03524a

4.进入centos01容器,查看IP,然后退出

root@zxh:~$ docker exec -it 69a8a12a8eee /bin/bash
[root@69a8a12a8eee /]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
20: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:12:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.18.0.4/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever
[root@69a8a12a8eee /]# exit

5.进入centos02容器,查看IP,ping容器centos01的IP可以通,但ping容器名centos01失败,退出

root@zxh:~$ docker exec -it 56516a03524a /bin/bash
[root@56516a03524a /]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
22: eth0@if23: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:12:00:05 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.18.0.5/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever
[root@56516a03524a /]# exit
[root@56516a03524a /]# ping 172.18.0.4
PING 172.18.0.4 (172.18.0.4) 56(84) bytes of data.
64 bytes from 172.18.0.4: icmp_seq=1 ttl=64 time=0.105 ms
64 bytes from 172.18.0.4: icmp_seq=2 ttl=64 time=0.089 ms
[root@56516a03524a /]# ping centos01
ping: centos01: Name or service not known
[root@56516a03524a /]# exit

6.重新创建一个容器centos03,加上参数--link 容器名,这样就把两个容器互相联系起来,这样centos03就可以通过容器名访问centos01

root@zxh:~$  docker run -it --name centos03 --link centos01 centos /bin/bash
[root@dbc056a3e1ef /]# ping centos01
PING centos01 (172.18.0.4) 56(84) bytes of data.
64 bytes from centos01 (172.18.0.4): icmp_seq=1 ttl=64 time=0.088 ms
64 bytes from centos01 (172.18.0.4): icmp_seq=2 ttl=64 time=0.083 ms

7.上面只实现了单方向的联通,也就是说centos01不能通过容器名访问centos03了

8.在centos03中查看hosts文件,发现--link参数他是通过修改hosts文件实现的

[root@dbc056a3e1ef /]# cat /etc/hosts
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.18.0.4      centos01 69a8a12a8eee
172.18.0.5      dbc056a3e1ef

所以我们以后搭建网络环境可以通过修改hosts文件,而不使用–link参数

自定义网络(重点)

查看网卡,发现docker支持三种网络模式:bridge(桥接);host(仅主机);none(无配置)

root@zxh:~$ docker network --help
root@zxh:~$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
d998cf35e936   bridge    bridge    local
c7d6dcaf8ae1   host      host      local
ffac7de97846   none      null      local

以前创建容器时默认使用桥接连接到docker0,下面两个命令相等,--net参数指定网络名称,并不是网络模式

root@zxh:~$ docker run -it --name centos01 centos /bin/bash
root@zxh:~$ docker run -it --name centos01 --net bridge centos /bin/bash

这里创建一个网络环境--driver指定网络模式,--subnet指定子网,--gateway指定网关,然后指定网络名称

root@zxh:~$ docker network create --help
root@zxh:~$ docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet192.168
3630c87ca00dcaa3254a1576cf0bcd5a8fe48524b351fb7fc6644101a959394f
root@zxh:~$ docker network ls
NETWORK ID     NAME           DRIVER    SCOPE
d998cf35e936   bridge         bridge    local
c7d6dcaf8ae1   host           host      local
3630c87ca00d   mynet192.168   bridge    local
ffac7de97846   none           null      local
root@zxh:~$ docker network inspect mynet192.168

应用该网络配置,然后ping百度可以ping通

root@zxh:~$ docker run -it --name centos01 --net mynet192.168 centos /bin/bash

[root@e5c89b68722a /]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
29: eth0@if30: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:c0:a8:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.0.2/16 brd 192.168.255.255 scope global eth0
       valid_lft forever preferred_lft forever
       
[root@e5c89b68722a /]# ping www.baidu.com
PING www.a.shifen.com (180.101.49.11) 56(84) bytes of data.
64 bytes from 180.101.49.11 (180.101.49.11): icmp_seq=1 ttl=49 time=11.4 ms
64 bytes from 180.101.49.11 (180.101.49.11): icmp_seq=2 ttl=49 time=11.4 ms

再新建一个终端,再创建一个容器,直接ping容器centos01的名字可以ping通,说明自定义网络修复了docker0的缺点

root@zxh:~$ docker run -it --name centos02 --net mynet192.168 centos /bin/bash
[root@44d323339135 /]# exit
exit
root@zxh:~$ docker start 44d323339135
root@zxh:~$ docker exec -it 44d323339135 /bin/bash
[root@44d323339135 /]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
35: eth0@if36: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:c0:a8:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.0.3/16 brd 192.168.255.255 scope global eth0
       valid_lft forever preferred_lft forever

[root@44d323339135 /]# ping centos01
PING centos01 (192.168.0.2) 56(84) bytes of data.
64 bytes from centos01.mynet192.168 (192.168.0.2): icmp_seq=1 ttl=64 time=0.059 ms
64 bytes from centos01.mynet192.168 (192.168.0.2): icmp_seq=2 ttl=64 time=0.107 ms

网络连通

自定义网络使得各容器划分到多个网络环境中,它们互相隔离,但有时候我们希望某些容器可以进入到某个环境中

1.保留使用上一个的centos01,网段处于192.168.0.0/16

2.新建一个centos03,使用默认的网络,即172.18.0.0/16网段,然后退出

root@zxh:~$ docker run -it --name centos03 centos
[root@6cae87f4b22a /]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
37: eth0@if38: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:12:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.18.0.4/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever
[root@6cae87f4b22a /]# exit

3.使用命令使得容器进入某个网络环境,docker network connect 网络环境 容器名

root@zxh:~$ docker network connect mynet192.168 centos03

4.再次启动centos03 ,发现增加了一个虚拟网卡,处于192.168.0.0/16网段,并且可以使用容器名的方式ping通192.168.0.0/16网段的容器,同时它们可以逆向ping通

root@zxh:~$ docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS                      PORTS                    NAMES
6cae87f4b22a   centos         "/bin/bash"              57 seconds ago   Exited (0) 44 seconds ago                            centos03
e5c89b68722a   centos         "/bin/bash"              25 minutes ago   Up 22 minutes                                        centos01
root@zxh:~$ docker start 6cae87f4b22a
6cae87f4b22a
root@zxh:~$ docker exec -it 6cae87f4b22a /bin/bash
[root@6cae87f4b22a /]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
39: eth0@if40: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:12:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.18.0.4/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever
41: eth1@if42: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:c0:a8:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.0.4/16 brd 192.168.255.255 scope global eth1
       valid_lft forever preferred_lft forever
[root@6cae87f4b22a /]# ping centos01
PING centos01 (192.168.0.2) 56(84) bytes of data.
64 bytes from centos01.mynet192.168 (192.168.0.2): icmp_seq=1 ttl=64 time=0.103 ms
64 bytes from centos01.mynet192.168 (192.168.0.2): icmp_seq=2 ttl=64 time=0.101 ms
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值