镜像操作
本次操作以nginx为例,需要注意nginx容器默认配置文件目录所在位置/etc/nginx 下 欢迎页所在位置/usr/share/nginx/html 下 可以在docker hub官网中查询
搜索镜像
docker search
示例docker search nginx
当然这里搜索镜像大概率是无法搜索到的,因为docker search命令访问的是docker hub 而docker hub在国内是无法访问的。在这里我们需要区别一个概念,就是我们之前安装docker的时候配置的镜像仓库,我们已经在/etc/docker/daemon.json中配置了如下,为什么这里的docker search任然无法使用?
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com",
"https://docker.m.daocloud.io"
]
参考deepseek给出的答案
综上:除了使用梯子否则我们是无法访问到docker hub的。
拉取镜像
docker pull
示例:docker pull nginx:1.26.0
或docker pull nginx
拉取镜像实际是由镜像名称+版本号组成,如果没有版本号,则默认拉取最新的。当我们不知道要拉取哪个版本的时候,需要去[docker hub](https://registry.hub.docker.com/上去查询,或者使用上面的docker search 命令来查询,可惜的是docker hub在国内无法访问,需要梯子。
查看镜像
docker images
# 或者
docker images ls
删除镜像
docker rmi
例如下图中两个mysql,如果我们要删除则分别使用命令docker rmi mysql:latest
docker rmi mysql:5.7
或者使用每个镜像的唯一ID也就是下图中的IMAGE ID来删除 docker rmi 5568fddd4f66
docker rmi aa803eda0f25
当然这里删除镜像的前提是没有使用该镜像生成任何容器,如果一旦生成了容器,则需要先把容器删除才能删除镜像。
如果容器没有删除就删除镜像,则会报如下错误
容器操作!
查看容器
docker ps #查看运行中的容器
docker ps -a #查看所有容器,包括未运行的
运行容器
docker run
根据镜像生成容器并运行,我们可以使用help查看该命令的使用方法,如下图,分为 docker run [options] IMAGE [command] [arg…]
- options 参数项,启动一个容器需要用到的参数项都在下图中Options中所示
- IMAGE 镜像
- command 命令
- arg 参数
一般来说镜像后面的启动命令和参数我们不用写,因为每个镜像都有自己的启动命令和参数,是镜像自带的,除非我们要改变镜像的默认行为。最简单的启动方式就是 docker run 镜像名
例如 docker run ngixn
这里的docker run nginx 默认启动的是最新版nginx镜像,如果我们没有下载最新版,这个命令会自动下载最新版nginx然后运行。由于我们之前已经下载过1.26.0版本的nginx,因此我们需要使用 docker run nginx:1.26.0
或者 docker run 镜像ID
运行效果如下,可以看到此时控制台处于运行状态,我们一旦结束这个命令窗口则运行的容器就会停止,因此一般情况我们会让他后台运行
当我们把这个运行的容器退出后,使用命令docker ps -a
查看所有已经运行的容器命令,如下图:
我们来解释一下这个打印的内容
- container id 容器id
- image 镜像
- command 镜像启动的默认命令
- created 创建时间
- status 状态 目前是exited 已退出
- ports 端口映射状况
- names 名称,不指定名称的时候会自动生成一个随机的名字
run命令详细介绍
上面的run命令我们只是简单的使用,从镜像启动了一个容器,那么同时也存在很多问题,例如控制台关闭就容器停止,例如无法访问等。现在我们想让容器后台启动并且可以访问,可以使用如下命令dcoker run -d --name nginx -p 80:80 nginx:1.26.0
启动之后如下图,我们分别看一下这些内容,其他的在上面都已经解释了,主要看一下这个port,这个port的就是说主机的80端口映射到容器的80端口,表示任何IP访问80端口就映射到容器的80端口。
那么这时候就存在一个问题,假如我们又启动了一个nginx的容器,那么请问还能用同样的命令来启动吗?启动之后80端口是映射到哪里了呢?如下图:
从这里我们可以看出来,每个容器都有自己独立的运行环境,因此每个容器都有自己独立的80端口,然而主机自己只有一个80端口,已经映射给了容器1的80端口,因此无法再映射到容器2的80端口,因此只能让主机的其他端口去映射到容器2的80端口。
启动容器
docker start 容器名称或容器ID
示例:使用容器名称的方式启动docker start suspicious_black
或者使用容器id的方式启动 docker start b71b196ccef0
或者只写容器ID的前三位,能与其他容器ID区分开即可
停止容器
docker stop 容器名称或容器ID
示例:使用容器名称的方式启动docker stop suspicious_black
或者使用容器id的方式启动 docker stop b71b196ccef0
或者只写容器ID的前三位,能与其他容器ID区分开即可
重启容器
docker restart 容器名称或容器ID
示例:使用容器名称的方式启动docker restart suspicious_black
或者使用容器id的方式启动 docker restart b71b196ccef0
或者只写容器ID的前三位,能与其他容器ID区分开即可
查看容器状态
docker stats 容器名称或容器ID
示例:使用docker stats b71
(b71是上面我们nginx的容器id的前三位)命令查看后如下图,这里显示了该容器的cpu 内存 io 等占用情况,并且是实时的
查看容器日志
docker logs 容器名称或容器ID
示例:使用docker logs b71
b71是上面我们nginx的容器id的前三位)命令查看后如下图,这里显示该容器从启动至今的所有日志信息。
删除容器
docker rm 容器名称或容器ID
示例:使用docker rm b71
(只能删除未在运行中的容器) 强制删除使用 docker rm -f b71
-f 的意思就是 force 武力有力的意思
当我们想要批量删除的时候可以使用 docker rm $(docker ps -aq)
docker ps -aq
的意思就是返回当前所有容器的ID,$(docker ps -aq)
就是将该命令的结果作为参数给docker rm
进入容器
docker exec
保存镜像
提交
docker commit
示例:docker commit -a "作者" -m "修改欢迎页" nginx newnginx:v1.0.1
这个命令参考下图意思就是将nginx容器(这里的nginx是我们之前启动容器时给起的名字,换成容器ID也可以)提交为一个新的镜像叫newnginx 版本号是v1.0.1 同时指定了作者和提交信息
我们使用-- help命令查看一下这个命令是如何使用的,我们可以看到这个命令的描述信息: 创建一个新镜像从一个改变的容器。命令参数有-a
作者信息 -c
容器改变的信息列表 -m
提交信息 -p
暂停容器的运行。
下面我们把我们刚才安装的nginx容器的index页面修改一下,然后将该容器重新保存为一个新镜像并发布:
进入容器
docker exec -it nginx /bin/bash
进入容器的nginx的index页面
cd /usr/share/nginx/html/
(我们怎么知道nginx容器的欢迎页面在哪呢?还是要去docker hub的官网上找) 这是默认的nginx欢迎页面
修改index页面
echo "<h1>hello docker I'm Iron man</h1>" > index.html
(由于新容器中没有vi命令,因此我们直接修改这个文件内容) 给欢迎页面打印一句话 你好docker 我是钢铁侠
使用
docker commit
来提交这个容器为一个新的镜像docker commit -a "iron man" -m "202503提交" nginx newnginx:v1
使用
docker images
查看新生成的镜像,从下图我们可以看到已经有了一个新的镜像叫newnginx 版本tag是v1
保存
docker save
在上面的步骤中我们已经将这个nginx容器提交成了一个新的镜像,现在我们需要将这个镜像打包并分享给其他人来用,那么就首先把这个镜像打包,使用docker save --help
查看命令用法,只有一个-o
的参数,这个参数是指定输出为一个tar文件或一个路径下的tar文件
我们使用docker save -o newnginx.tar newnginx:v1
命令来将这个镜像打成一个tar包,执行完这个命令会在当前目录下生成一个tar包,我们就可以将这个tar包传给其他人来使用,也就是下面的加载
加载
docker load
在上面的示例中我们已经将容器提交为一个新的镜像,并且将该镜像保存为一个tar包,现在我们把tar包发送给一个新的docker环境中,在新的docker环境中去加载这个tar包并生成镜像,然后运行该镜像生成容器并访问,如果显示的是我们修改过的欢迎页,则说明成功。
我们在新的机器或docker环境中,或者将现有机器上的镜像和容器都删掉,使用docker load --help
查看该命令的用法
有两个参数 -i
指定从哪个路径加载哪个tar文件,我们使用docker load -i newnginx.tar
来加载刚才的tar包,然后使用docker images
查看从刚才的tar包里加载到的镜像,然后用docker run -d --name hellonginx -p 80:80 newnginx:v1
来指定后台运行端口映射命名为hellonginx运行镜像,用docker ps
来查看容器
分享社区
在上面的例子中我们通过分享文件的方式来将镜像分享给其他人使用,那么我们如果想将镜像推送至社区让大家都可以docker pull
的方式来下载使用该如何做呢?
登录
- 这是docker hub 的官网,我们可以在这里登录和注册,
- 然后在命令行窗口中登录,使用
docker login
命令登录,然后输入用户名和密码,显示login succeeded 表示登录成功,国内登录应该是链接超时。
命名
docker tag 原镜像 目标镜像
使用docker tag newnginx:v1 用户名/新名字:版本号
再使用docker images
这时就有两个镜像,但是它们的镜像ID是一样的,接下来就可以用docker push
将这个新镜像推送到docker hub的个人仓库中
推送
docker push
docker存储
目录挂载,数据卷等
目录挂载
docker run -v /usr/local/nginx/conf:/etc/nginx/conf nginx:1.26.0
示例:
- 首先删除所有容器
docker rm $(docker ps -aq)
- 然后删除nginx镜像
docker rmi 镜像ID
- 然后重新从pull一个新的nginx镜像
docker pull nginx:1.26.0
- 然后使用命令运行镜像生成容器
docker run -v /usr/local/nginx/conf:/etc/nginx/conf -p 80:80 -d --name nginx:1.26.0
重点解释这里的-v参数 -v /usr/local/nginx/conf:/etc/nginx/conf 的意思就是将本机的/usr/local/nginx/conf目录映射为容器的/etc/nginx/conf目录由于nginx容器启动时我们将nginx的配置文件目录映射为本机的目录,而此时我们并没有这个目录,因此run命令运行时会自动创建该目录,而新建出来的目录是空的,因此这个容器的启动必然是失败的,我们可以用docker ps -a
和docker logs 容器ID
来查看 - 启动第一个80端口映射
- 启动第二个81端口映射
- 启动第三个挂载目录的82端口映射
docker ps -a
查看三个容器状况
那么如何让这个82端口映射的nginx正常启动呢?第一种方法是可以将nginx的配置文件上传到本机的/usr/local/nginx/conf ,同时需要注意上传的nginx配置文件内容是否正确,否则会出现404 index页面,因为容器的默认index页面目录在/usr/share/nginx/html下,而非容器的nginx配置的欢迎页在conf文件夹同级的html文件夹内。第二种方法就是下一节的卷映射,何为卷映射?就是将容器内原本存在的文件映射到主机目录中- 多个目录映射
docker run -d -p 82:80 \ -v /usr/local/nginx/conf:/etc/nginx \ -v /your/local/html:/usr/share/nginx/html \ # 新增挂载 --name nginx82 nginx:1.26.0
卷映射
- 卷映射与目录挂载的区别是
- 卷映射命令
docker run -v 文件夹:/etc/nginx ....
以nginx为例完整的卷配置文件映射命令为docker run -v dc_ng_conf:/etc/nginx -d -p 83:80 --name nginx83 nginx:1.26.0
- 目录挂载命令
docker run -v /usr/local/nginx/conf:/etc/nginx ...
- 区别在于卷映射本机目录仅写一个目录名称(文件夹名称)即可,而目录挂载是需要一个完整的目录,也就是说核心就是目录挂载比卷挂载多了一个
/
。
- 卷映射命令
- 如何找到卷映射目录?
- 当我们使用卷映射时只写了一个文件夹的名字,该文件夹会自动帮我们创建,那么这个文件夹在哪呢?这个位置docker统一放在了
/var/lib/docker/volumes/<volume-name>
下
- 当我们使用卷映射时只写了一个文件夹的名字,该文件夹会自动帮我们创建,那么这个文件夹在哪呢?这个位置docker统一放在了
下面我们使用挂载目录和挂载容器卷的两种方式来启动一个映射83端口的nginxdocker run -v /usr/local/nginx/html:/usr/share/nginx/html -v dc_ng_conf:/etc/nginx -d -p 83:80 --name nginx83 nginx:1.26.0
我们将nginx的欢迎页用目录挂载的方式映射到本机的/usr/local/nginx/html文件夹下,由于该文件夹是空的,因此我们使用touch index.html
命令来创建一个欢迎页,用echo "你好欢迎来到83号nginx" >> index.html
命令来给这个文件写一句话,然后访问IP:83
来访问这个nginx,欢迎页显示如下:
然后我们去找nginx的配置文件,使用命令cd /var/lib/docker/volumes
看到这个文件夹内有个文件夹叫dc_ng_conf 然后pwd
命令查看当前目录及目录内的文件,如下图我们可以看到容易将配置文件nginx.conf映射到该文件夹内
目录挂载初始的时候是空的,容器卷挂载初始的时候是以容器内的文件为准,后面不论是在容器内修改还是容器外修改都是一样的
查看所有容器卷
docker volume ls
创建容器卷
docker volume create myvs # 自定义容器卷名称
查看容器卷详情
docker volume inspect myvs
下图是容器卷操作的相关命令,与network基本一致
此时无论我们是否删除这个容器,它的数据依然存在,下次我们还可以用同样的目录启动容器,那么就相当于是恢复了之前的容器。
docker网络
容器间通信-通过主机访问
- 我们现在清除所有容器,然后重新使用命令新建两个容器
docker run -d -p 80:80 --name nginx80 nginx1.26.0
运行nginx80容器docker run -d -p 81:80 --name nginx81 nginx1.26.0
运行nginx81容器
- 然后进入80容器访问81容器
docker exec -it nginx80 bash
进入80容器curl 主机ip:81
访问81容器,打印欢迎页面
此时的网络请求是80容器访问了主机的81端口,然后主机81端口映射到了81容器的80端口,然后81容器响应了欢迎页面,这么一来就相当于绕了一圈比较麻烦,那么怎么能直接让容器之间互相通信?
容器间通信-通过ip访问
- 每一个docker应用在启动的时候都会加入一个docker的默认网络,我们使用
ip a
命令查看一下
- 我们可以看到,这里有一个docker0的网卡,这个网络是在安装docker的时候就有了,并且它的ip是172.17.0.1,这个ip并不固定,docker每启动的任何一个应用都会加入到这个网络中,并且docker会为每个容器分配它自己的ip,我们可以使用命令
docker inspect nginx80
或者docker container inspect nginx80
来查看这个容器的详细信息,如下图中所示,172.17.0.2是该容器的ip,而172.17.0.1则是它的网关,也就是docker0网卡的ip。 - 由此我们可以推断出,容器之间既然已经都加入了这个docker0的网络,并且互相有了独立的ip,那么我们应该可以通过他们各自独立的ip能访问对方,此时我们再次测试
docker exec -it nginx80 bash
进入80容器curl 172.17.0.2:80
访问nginx81容器的ip
容器间通信-通过域名访问
docker0 默认不支持主机域名
创建自定义网络,容器名就是稳定域名
docker network --help
查看docker network用法
connect
连接一个容器到网络中create
创建一个网络disconnect
从网络中断开一个容器的链接inspect
显示详细信息在一个或多个网络ls
网络列表prune
删除所有未使用的网络rm
删除一个或多个网络
创建自定义网络
docker network create 自定义名
查看网络列表
docker network ls
示例
- 删除所有现有容器,因为他们加入的是默认docker0的网络,而docker0不支持主机域名
docker rm $(docker ps -aq)
- 创建自定义网络
docker network create mynet
- 查看创建的网络
docker network ls
如下图,我们可以看到有多个网络,第一个桥接网络就是之前默认使用的,而我们自己创建的mynet也是一个桥接网络
- 重新运行两个nginx容器并将其加入我们新建的网络中(通过
--network
参数)docker run -d -p 80:80 --name nginx80 --network mynet nginx:1.26.0
docker run -d -p 81:80 --name nginx81 --network mynet nginx:1.26.0
- 使用
docker inspect nginx80
查看该容器的细节,我们发现这个容器的网关和ip都变了
- 进入容器中用容器名称作为域名来访问
docker exec -it nginx80 bash
进入80容器curl http://nginx81
访问81容器。为啥容器名字就可以作为域名来访问呢?可能是因为在加入网络的时候做了什么处理吧
docker实现Redis主从复制集群
# 主节点启动命令
docker run -d -p 6379:6379 \
> -v /usr/local/redis/data:/bitnami/data \ # 目录挂载
> -e REDIS_REPLICATION_MODE=master \ # 设置节点为主节点
> -e REDIS_PASSWORD=123456 \ # 设置密码
> --network mynet --name redis01 \ # 加入网络
> bitnami/redis # 非官方镜像,bitnami镜像主从复制集群不需要修改配置文件,而是用设置环境变量的方式
# 从节点启动命令
docker run -d -p 6389:6379 \
> -v /usr/local/redis_slave/data:/bitnami/data \
> -e REDIS_REPLICATION_MODE=slave \ # 设置节点为从节点
> -e REDIS_MASTER_HOST=redis01 \ # 配置主节点域名、网络ip、主机地址
> -e REDIS_MASTER_PORT=6379 \ # 配置主节点端口
> -e REDIS_MASTER_PASSWORD=123456 \ # 配置主节点密码
> -e REDIS_PASSWORD=123456 \ # 设置从节点密码
> --network mynet --name redis02 \ # 加入网络
> bitnami/redis # 同上
解释:
\
表示调到下一行继续输入命令-e
表示环境变量 environment的意思-v
目录挂载,将镜像的data目录挂载到本机的/usr/local/redis/data
目录,那么我们怎么知道这个镜像的data目录在哪个位置?还是要上docker hub官网找到bitnami/redis这个镜像的官方文档里看- 主从复制集群主要是配置
REDIS_REPLICATION_MODE
参数,然后给从机设置主机地址即可
docker compose
docker compos 的作用就是用来批量管理容器,将我们要部署的容器全部编写到一个yaml文件中,然后使用命令批量执行
示例
下面我们用docker启动一个博客系统,WordPress,这是一个开源的博客系统,会将博客内容存储到数据库中,因此我们要安装两个容器,一个是WordPress,另一个是数据库
传统方式启动
#创建网络 docker network create blog #启动mysql docker run -d -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=123456 \ -e MYSQL_DATABASE=wordpress \ -v mysql-data:/var/lib/mysql \ -v /app/myconf:/etc/mysql/conf.d \ --restart always --name mysql \ --network blog \ mysql:8.0 #启动wordpress docker run -d -p 8080:80 \ -e WORDPRESS_DB_HOST=mysql \ -e WORDPRESS_DB_USER=root \ -e WORDPRESS_DB_PASSWORD=123456 \ -e WORDPRESS_DB_NAME=wordpress \ -v wordpress:/var/www/html \ --restart always --name wordpress-app \ --network blog \ wordpress:latest
- 首先创建网络blog,让WordPress和数据库容器加入同一个网络,用于互相通信
- 然后启动mysql,
-d
后台启动-p
端口映射3306-e MYSQL_DATABASE=wordpress
容器启动创建默认的数据库-e MYSQL_ROOT_PASSWORD=123456
指定root用户的密码-v mysql-data:/var/lib/mysql
挂载mysql数据存储容器卷v /app/myconf:/etc/mysql/conf.d
挂载mysql配置文件目录 这里注意区分与上面的容器卷的区别--restart always
容器随系统自动启动--name mysql
指定容器名称--network blog
加入指定网络,可以让其他容器用容器名称访问
- 然后启动WordPress,
-d
后台启动-p
端口映射到8080-e WORDPRESS_DB_HOST=mysql
指定环境变量数据库为mysql-e WORDPRESS_DB_USER=root
数据库用户root-e WORDPRESS_DB_PASSWORD
数据库密码123456-v wordpress:/var/www/html
容器卷挂载,- 然后依次是随机自启,指定容器名称,加入blog网络,使用的镜像
使用docker compose方式启动
编写compose.yml文件
name
compose文件的编写根据docker官方文档描述共有6个顶级元素分别是name services networks volumes configs secrets
对应到下面我们编写的compose.yml文件来说就是首先要用name来指定项目名,其次使用services来指定要启动的容器services
使用services指定启动的容器,实际上与上面命令启动类似,只不过是将命令中的各个部分持久化到这个文件中用来重复使用。参考 官方文档- 在services下面的mysql和wordpress可以任意命名,能区分出来即可,在mysql下面有个
container_name
指定容器的名称,我们这个任意命名的也可以当做容器名称 image
指定镜像prots
指定端口映射environment
指定环境变量volumes
指定容器卷或目录映射,如果是容器卷映射,则需要在与services同级的volumes顶级元素下声明容器卷networks
指定加入的网络,如果指定加入的网络,则需要在与services同级的networks的顶级元素下声明网络restart
指定随机器重启
- 在services下面的mysql和wordpress可以任意命名,能区分出来即可,在mysql下面有个
name: myblog services: mysql: container_name: mysql image: mysql:8.0 ports: - "3306:3306" environment: - MYSQL_ROOT_PASSWORD=123456 - MYSQL_DATABASE=wordpress volumes: - mysql-data:/var/lib/mysql - /app/myconf:/etc/mysql/conf.d restart: always networks: - blog wordpress: image: wordpress ports: - "8080:80" environment: WORDPRESS_DB_HOST: mysql WORDPRESS_DB_USER: root WORDPRESS_DB_PASSWORD: 123456 WORDPRESS_DB_NAME: wordpress volumes: - wordpress:/var/www/html restart: always networks: - blog depends_on: - mysql volumes: mysql-data: wordpress: networks: blog:
运行compose文件
docker compose -f compose.yml up -d
其中-f
表示指定compose文件位置,-d
表示后台启动,至此就可以根据compose来启动容器了。
docker compose 增量更新
当我们将上面的compose.yml文件修改了之后,如何让容器重新运行新修改后的内容呢?,还是同样的启动命令 docker compose -f compose.yml up -d
此时docker会重新加载该文件并且判断哪些是修改的内容需要重启的,哪些没有修改的不动。
docker compos 下线
docker compose -f compose.yml down
该命令会移除compose.yml文件中的所有容器,但是并不一会移除容器的映射卷
docker file
当我们用java开发了一个应用并打成jar包后,需要将其发布,那么我们需要将其制作为一个镜像,这个镜像不仅要包含操作系统的相关指令和内容还要包括JDK 应用jar包以及启动命令等,那么该如何制作呢?
docker file 常见指令
参考 官方文档
示例
FROM openjdk:17
LABEL author=leifengyang
COPY app.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]