Docker服务基本操作实践
查找Docker的所在的资源路径
不属于docker指令
whereis docker
查看Docker运行状态
查看docker是否启动了,是否是运行状态。
systemctl status docker
启动Docker服务
systemctl start docker
设置开机自启
systemctl enable docker
重新启动服务
systemctl restart docker
重启docker后,默认docker中启动的容器会关闭退出。所以需要先设置好容器开机自启。
查看Docker信息
docker info
查看docker info中具体key的信息,例如:
docker info | grep 'Docker Root Dir:'
停止Docker服务
systemlctl stop docker
Docker镜像加速
由于国内网络问题,需要配置加速器来加速。修改配置文件 /etc/docker/daemon.json
下面命令直接生成文件 daemon.json
cat <<EOF > /etc/docker/daemon.json
{"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn","http://hub-mirror.c.163.com"],"max-concurrent-downloads": 10,"log-driver": "json-file","log-level": "warn","log-opts": {"max-size": "10m","max-file": "3"},"data-root": "/var/lib/docker"
}
EOF
说明:在执行如上指令时,保证你的登录用户为root管理员用户。
Docker镜像操作实践
镜像文件就是容器中的APP,启动镜像文件=启动容器
下载镜像
语法:docker pull 镜像名案例:docker pull hello-world
浏览镜像文件
docker images
这里查看的镜像是当前系统中拥有的镜像文件,是静态的。
查看镜像详情
语法:docker inspect 镜像名或镜像id案例:docker inspect hello-world
导出镜像文件
镜像导出(linux系统中的镜像文件下载到本地-例如window),导出后给他人使用
docker save hello-world | gzip > hello-world.tar.gz
删除镜像文件
语法:docker image rm 镜像名或镜像id案例:docker image rm hello-world
导入镜像操作
镜像导入(要在hello-world.tar.gz 文件所在目录下执行)
docker load < hello-world.tar.gz
运行镜像文件
从一个镜像可以运行启动一个或多个容器(镜像文件)。
所谓容器,我们可以理解为是一个虚拟的计算机,其中运行着操作系统,操作系统中运行着我们部署的应用。
基于镜像,启动容器运行。
docker run hello-world
后台运行镜像文件
后台运行启动 tomcat 容器:
docker run -d tomcat
Docker 容器操作实践实践
本次以CentOS镜像为例,讲解容器的基本操作。
下载镜像
通过docker pull指令下载CentOS镜像,例如:
网络拉取: docker pull centos:7本地加载: docker load < centos.tar.gz
本地加载确保系统中有镜像压缩包
下载完以后,查看centos7镜像文件。
docker images
创建并启动容器
docker run -it xxxx bash
其中:
- xxxx - 镜像名, 或 image id 的前几位,
- -it 这是两个参数(-i表示交互式操作, -t 表示终端)
- bash 表示进入操作终端,基于交互式进行相关操作(例如执行linux相关指令,相当于windows中的cmd)。
案例:通过docker启动运行 centos7镜像
docker run -it centos:7 bash
注意:不要在容器中运行Docker命令,因为Docker安装在宿主机中不在容器当中。
退出容器
假如从宿主机进入了启动的容器,退出容器需要使用exit指令,例如:
exit
查看Docker中的容器
查看docker运行中的容器
docker ps
查看docker运行中的所有容器
docker ps -a
其中,-a表示全部(all)
仅列出容器的 id
docker ps -aq
查看容器日志信息
查看后台运行的容器输出结果,这个指令非常重要,假如容器没有启动,要通过此指令去看一下错误日志。
语法:docker container logs 容器ID(前三位)案例:docker container logs dee
显示出对日志所有的操作记录
停止或重启容器
停止运行的容器,代码如下:
docker container stop dee
停止所有运行的容器:
docker stop $(docker ps -a -q)
重新启动容器,代码如下:
docker container restart XXX(CONTAINER ID 前三位)
容器的改名与随系统自动启动
–name 和 --restart=always
–name:
每个启动的容器都可以指定一个名称,方便使用名称来操作容器。
–restart=always:
docker系统服务重启,或者操作系统重启,容器可以随系统自动启动。
# 启动一个临时容器
docker run -d \
--rm \
--name tmp \
tomcat# 把 tomcat 的 server.xml 复制到宿主机的 /root/ 目录下
docker cp tmp:/usr/local/tomcat/conf/server.xml /root/# 停止临时容器,会自动删除
docker stop tmpdocker ps -a# 修改 server.xml 中的8080端口,改成80
vim server.xml# -v 把宿主机路径挂载到容器的一个路径
# 挂载的路径可以是文件,也可以是文件夹
# 这里把修改过的 server.xml 挂载到新启动的容器中
docker run -d \
--name cat2 \
-v /root/server.xml:/usr/local/tomcat/conf/server.xml \
tomcat# 查看启动日志,确认使用 80 端口启动
docker logs cat2
进入指定容器
当需要进入容器进行操作时(容器运行着),可以使用 docker exec 命令,例如:
docker exec -it dee bash #dee为容器id
当容器没有运行会报错
运行容器后进入指定容器
绝大多数情况下一个容器中只运行一个应用。
容器中也允许运行启动多个应用,可以进入已经启动的容器, 在里面运行启动其他应用:
容器启动后运行的命令
当容器启动后,需要容器中运行指定的命令来启动一个应用时。
例如
查看在镜像中指定的默认运行命令:docker history
docker history 容器名
tomcat 镜像中设置的 CMD 指令指定了容器启动后默认运行的命令: catalina.sh run。
再来看看其他镜像中设置的默认命令:
docker history redis---------------------------------------------------------------------------------
[root@localhost ~]# docker history redis
IMAGE CREATED CREATED BY SIZE
bc8d70f9ef6c 3 weeks ago /bin/sh -c #(nop) CMD ["redis-server"] 0B
<missing> 3 weeks ago /bin/sh -c #(nop) EXPOSE 6379 0B
<missing> 3 weeks ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0Bdocker history mariadb---------------------------------------------------------------------------------
[root@localhost ~]# docker history mariadb
IMAGE CREATED CREATED BY SIZE
eff629089685 13 days ago /bin/sh -c #(nop) CMD ["mysqld"] 0B
<missing> 13 days ago /bin/sh -c #(nop) EXPOSE 3306 0B
<missing> 13 days ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0Bdocker history centos:8----------------------------------------------------------------------------------
[root@localhost ~]# docker history centos:8
IMAGE CREATED CREATED BY SIZE
300e315adb2f 6 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
设置容器中运行的命令
ENTRYPOINT 和 CMD
这两向设置都是用来设置容器中运行的命令。
只设置 CMD 来执行 ls -a -l:
CMD ["ls", "-a", "-l"]
只设置 CMD 是常见的用法。
用 ENTRYPOINT 和 CMD 两个一起设置来执行 ls -a -l:
ENTRYPOINT ["ls"]
CMD ["-a", "-l"]
两项一起设置时,会把两项设置的内容连接起来作为一个完整的命令。
覆盖 CMD:
以 tomcat 镜像为例,镜像中设置的默认命令是 catalina.sh run,可以任意指定命令覆盖这个默认命令,这里执行 ls -a -l 来测试:
docker run tomcat ls -a -l
覆盖 ENTRYPOINT:
--entrypoint
:设置运行的命令,不许写在镜像名称 tomcat 的前面。注意,这里不能添加命令的参数;
镜像名称 tomcat 后面的内容会覆盖 CMD
docker run --entrypoint ls tomcat -a -l
删除容器
假如容器不用了,可执行删除操作(原则来说先停止再删除),例如:
普通删除:docker container rm dee xx1 xx2... #dee为容器id,可以添加多个容器id,中间用空格隔开强制删除:docker container rm -f dee #dee为容器id
其中,如果删除运行中的容器,需要添加 -f 参数。
清理所有终止状态容器,例如:
docker container prune
删除所有容器:
docker rm $(docker ps -a -q)
或者
docker rm -f $(docker ps -aq)
在容器与宿主机之间复制
docker cp
在容器和宿主机之间复制文件
下面来看一个实际的例子,这个例子中我们从 tomcat 的一个临时容器复制配置文件 server.xml 到宿主机,然后在 server.xml 中修改端口号,把 8080 改成 80。
# 启动一个临时容器
docker run -d --rm --name tmp tomcat# 把 tomcat 的 server.xml 复制到宿主机的 /root/ 目录下
docker cp tmp:/usr/local/tomcat/conf/server.xml /root/# 停止临时容器,会自动删除
docker stop tmpdocker ps -a# 修改 server.xml 中的8080端口,改成80
vim server.xml# -v 把宿主机路径挂载到容器的一个路径
# 挂载的路径可以是文件,也可以是文件夹
# 这里把修改过的 server.xml 挂载到新启动的容器中
docker run -d --name cat2 -v /root/server.xml:/usr/local/tomcat/conf/server.xml \
tomcat# 查看启动日志,确认使用 80 端口启动
docker logs cat2
Docker数据管理实践
概述
在容器中管理数据主要有两种方式:
- 挂载主机目录 (Bind mounts)——最常用 (docker run –v 宿主机目录:容器目录)
- 数据卷(Volumes)
数据卷
数据卷是一个可供一个或多个容器使用的特殊目录,可以在容器之间共享和重用,默认会一直存在,即使容器被删除。
数据卷操作
第一步:创建数据卷,例如:
docker volume create container-vol
第二步:查看所有数据卷,例如:
docker volume ls
查看指定 数据卷 的信息
docker volume inspect container-vol
查询的结果:
[{"Driver": "local","Labels": {},"Mountpoint": "/var/lib/docker/volumes/container-vol/_data",(挂载点 资源地址)"Name": "container-vol","Options": {},"Scope": "local"}
]
第三步:启动挂载数据卷的容器,例如:
docker run -it --mount source=container-vol,target=/root centos:7 bash
或者采用如下简写方式
-v container-vol:/root (把数据卷 container-vol 挂载到容器的 /root 目录)
docker run -it -v container-vol:/root centos:7 bash
第四步:删除数据卷(如果数据卷被容器使用则无法删除),例如:
docker volume rm container-vol
清理无主数据卷
docker volume prune
挂载主机目录
我们还可以在启动容器时,以目录直接挂载的方式进行数据操作,例如:
docker run -it -v /usr/app:/opt/app centos:7 bash
其中:
1)/usr/app:为宿主机目录
2)/opt/app: 为启动容器的一个目录
3)-v 用于指定挂载目录,如果本地目录(宿主机目录)不存在, Docker 会自动为你按照挂载目录进行目录的创建。
挂载可以理解为桌面的快捷方式与资源的实际位置
docker run -it -v (主机目录,相当于实例位置)/usr/app: (容器目录,相当于桌面快捷方式)/opt/app centos:7 bash
又比如我把钱存银行了(宿主机),但我有一张银行卡(容器),我能通过银行卡查钱取钱。(容器查询获取宿主机的数据)
例如:
宿主机数据卷挂载到容器后,可以直接通过宿主机操作数据卷,也可以通过容器间接操作数据卷。
查看挂载目录信息
docker inspect 91a #91a 为容器id
显示结果:
"Mounts": [{"Type": "bind","Source": "/usr/app","Destination": "/opt/app","Mode": "","RW": true,"Propagation": "rprivate"}
],
...
文件的挂载
# 清理容器
docker rm -f $(docker ps -aq)# -v 宿主机路径:容器路径
# 挂载的可以是文件,也可以是文件夹
# -v 可以在宿主机自动新建目录
docker run -d \
--name cat1 \
-v /usr/app:/opt/app \
tomcat# 进入容器,在 /opt/app 下新建文件 f1.txt
docker exec -it cat1 bashtouch /opt/app/f1.txt# 退出容器的命令行
exit# 访问宿主机的文件夹
cd /usr/app
ls
数据卷挂载
# 新建数据卷
docker volume create my-vol# 查看 my-vol 数据卷的信息
docker volume ls# /var/lib/docker/volumes/my-vol/_data
docker inspect my-vol# 挂载 my-vol 数据卷到容器的 /opt/app 路径
docker run -d \
--name cat2 \
-v my-vol:/opt/app \
tomcat# 在容器的 /opt/app 下新建 f2.txt
docker exec -it cat2 bashtouch /opt/app/f2.txt# 退出容器的命令行
exit# 进入 my-vol 数据卷的真实目录,查看 f2.txt
cd /var/lib/docker/volumes/my-vol/_datals
Docker 网络
端口映射
客户端要访问宿主机内部运行的容器时,可以在宿主机打开一个端口,当客户单访问这个端口时,可以将访问转发到内部的容器。
-p 参数:通过 -p 参数设置,可以在宿主机上选择一个端口映射到容器的端口
# 清理容器
docker rm -f $(docker ps -aq)# 端口映射
# -p 宿主机端口:容器端口
docker run -d --name cat1 -p 80:8080 tomcat
浏览器访问宿主机映射的端口 80
http://192.168.64.150
看到 tomcat 返回的 404 页,说明已经正常访问到 tomcat 容器
虚拟网络
容器键互联可以使用 Docker 的虚拟网络来连接。
在 Docker 中可以创建任意多个虚拟网络,容器之间可以通过虚拟网络互联互通。创建虚拟网络时宿主机也会连接到虚拟网络。
# 新建虚拟网络 my-net
docker network create my-net# 查看虚拟网络
docker network ls# 查看网络描述信息
docker inspect my-net# 查看宿主机新建的虚拟网卡
ifconfig# 清理容器
docker rm -f $(docker ps -aq)# 新建两个容器 cat1 和 cat2
# 连接到虚拟网络 my-net
docker run -d --name cat1 \
--net my-net \
tomcatdocker run -d --name cat2 \
--net my-net \
tomcat# 查看两个容器的虚拟网络ip
docker inspect cat1
docker inspect cat2# 测试网络能否互联互通
# 从宿主机ping两个容器
ping 172.18.0.2
ping 172.18.0.3# 进入cat1,ping宿主机和cat2
docker exec -it cat1 ping 172.18.0.1
docker exec -it cat1 ping 172.18.0.3
# 从容器访问另一个容器,可以使用容器名称访问,容器内部实现了解析环境
docker exec -it cat1 ping cat2
Docker 构建镜像
文件下载
https://download.csdn.net/download/weixin_38305440/19683180
构建镜像
构建镜像类似于一台电脑的装机过程,添加文件、安装软件、配置环境…
例如构建一个 tomcat 10 镜像流程,就像在一台电脑上安装配置 tomcat 环境一样:
- 选择基础镜像 centos:8(相当于一台新电脑,只有操作系统)
- 添加 jdk 和 tomcat 文件
- 设置环境变量
- 设置开机启动 tomcat
下面来演示构建 tomcat 10 镜像的过程:
准备必要的文件
jdk 和 tomcat 10 的压缩文件放入一个文件夹中,这个文件夹不应该包含其他无关文件:
[/root/tomcat/]- jdk-8u291-linux-x64.tar.gz- apache-tomcat-10.0.6.tar.gz
Dockerfile
Dockerfile 类似于一个批处理文件,用来设置镜像的构建流程
在上一步的 tomcat 文件夹下创建 Dockerfile 文件:
[/root/tomcat/]- jdk-8u291-linux-x64.tar.gz- apache-tomcat-10.0.6.tar.gz- Dockerfile
编辑 Dockerfile 文件
cd /root/tomcatvim Dockerfile
在文件中添加以下内容:
# 选择基础镜像
FROM centos:8# jdk 和 tomcat 文件添加到镜像的 /usr/local/ 目录下
# ADD 指令会自动解压文件
ADD jdk-8u291-linux-x64.tar.gz apache-tomcat-10.0.6.tar.gz /usr/local/# 切换到镜像中指定的文件夹下
WORKDIR /usr/local/apache-tomcat-10.0.6/# 设置环境变量
ENV JAVA_HOME=/usr/local/jdk1.8.0_291 \CATALINA_HOME=/usr/local/apache-tomcat-10.0.6 \PATH=/usr/local/jdk1.8.0_291/bin:/usr/local/apache-tomcat-10.0.6/bin:$PATH# EXPOSE 8080 只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务
# 这个声明有两个好处:
# 1.帮助镜像使用者理解这个镜像服务的端口,以方便配置映射
# 2.在运行时使用随机端口映射时,也就是 docker run -P时,会自动随机映射 EXPOSE 的端口
EXPOSE 8080# 设置启动命令
CMD ["catalina.sh", "run"]
Dockerfile 指令参考手册:
https://docs.docker.com/engine/reference/builder/
执行构建
进入 tomcat 文件夹,并在当前文件夹下找到 Dockerfile 和其他需要的文件,来构建镜像:
cd /root/tomcat# 使用当前文件夹中的 Dockerfile 文件进行构建
# 新构建的镜像命名为 tomcat:10
docker build -t tomcat:10 ./
查看构建结果:
docker images
docker history tomcat:10
docker inspect tomcat:10
启动容器
docker run -d --name cat1 -p 8080:8080 tomcat:10
docker ps -a
docker logs cat1
浏览器访问测试:
http://192.168.64.150:8080
docker的镜像分层
docker里的镜像绝大部分都是在别的镜像的基础上去进行创建的,也就是使用镜像的分层结构。
实验
比如说使用dockerfile去创建一个最简单的hello镜像。创建好对应的dockerfile之后去进行创建:
FROM alpine:latest
MAINTAINER sbb
CMD echo "hello world"
执行了上面的命令我们可以看到存在着两个镜像,其中hello_world是我刚刚创建好的镜像。
$ docker imgaes
alpine
hello_world
docker分层
那么为什么会有两个镜像呢?这是由于docker的镜像分层结构所导致的,如下图所示。
一个docker镜像由多个可读的镜像层组成,然后运行的容器会在这个docker的镜像上面多加一层可写的容器层,任何的对文件的更改都只存在此容器层。因此任何对容器的操作均不会影响到镜像。
如何实现
至于容器如何获取镜像层文件而又不影响到是镜像层的呢?docker是这样实现的?
如果需要获取某个文件,那么容器曾会从上到下去下一层的镜像层去获取文件,如果该层文件不存在,那么就会去下一镜像层去寻找,直到最后一层。
对于用户而言,用户面向的是一个叠加后的文件系统。
而任何对于文件的操作都会记录在容器层,例如说修改文件,容器层会把在镜像层找到的文件拷贝到容器层然后进行修改,删除文件则会在容器层内记录删除文件的记录。
好处
- 基本上每个软件都是基于某个镜像去运行的,因此一旦某个底层环境出了问题,就不需要去修改全部基于该镜像的软件的镜像,只需要修改底层环境的镜像。
- 这个好处也是最大好处,就是可以共享资源(所有层都可以进行重用),其他相同环境的软件镜像都共同去享用同一个环境镜像,而不需要每个软件镜像要去创建一个底层环境。
案列一
关闭防火墙
# 关闭防火墙
systemctl stop firewalld.service
# 禁止防火墙开机启动
systemctl disable firewalld.service
启动或重启docker
# 启动docker
systemctl start docker
# 重启docker
systemctl restart docker
案列二
启动多个 redis 容器
启动三个redis容器,将端口分别映射到7000,7001和7002端口
# 如果7000已经启动,不必重复启动
docker run -d --name redis7000 -p 7000:6379 redisdocker run -d --name redis7001 -p 7001:6379 redis
docker run -d --name redis7002 -p 7002:6379 redis# 查看容器
docker ps -a
jedis 连接测试
新建测试项目
pom.xml
添加 redis 和 junit 依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>cn.tedu</groupId><artifactId>docker</artifactId><version>0.0.1-SNAPSHOT</version><dependencies><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.0</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>
</project>
jedis 分片测试
编写测试方法
@Testpublic void test2() {JedisPoolConfig cfg = new JedisPoolConfig();cfg.setMaxTotal(500);cfg.setMaxIdle(20);List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();shards.add(new JedisShardInfo("192.168.64.150", 7000));shards.add(new JedisShardInfo("192.168.64.150", 7001));shards.add(new JedisShardInfo("192.168.64.150", 7002));ShardedJedisPool pool = new ShardedJedisPool(cfg, shards);ShardedJedis j = pool.getResource();for (int i = 0; i < 100; i++) {j.set("key"+i, "value"+i);}pool.close();}
在容器中查看数据
分别进入三个redis容器,执行 keys * 查看结果
docker exec -it redis7000 redis-cli
docker exec -it redis7001 redis-cli
docker exec -it redis7002 redis-cli