Docker入门:介绍Docker的常用命令、镜像的加载原理、Dockerfile打包、Docker网络、Docker Compose容器编排+监控

Docker入门

1)从面向对象角度来看,Docker利用容器(Container)独立运行一个或一组应用,应用程序或服务运行在容器里面,容器就类似于一个虚拟化的运行环境,容器是用镜像创建出来的运行实例。就像是Java中的类和实例对象一样,容器是镜像运行的实体。容器为镜像提供了一个标准的和隔离的运行环境,它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。

2)从镜像角度来看,可以把容器看做是一个简易版的Linux环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。

3)实操:将本地SpringBoot微服务制作成Docker Image镜像包然后运行

一、Docker常用命令:

  • 帮助启动类命令
# 启动,停止,重启Mac中直接针对软件进行操作即可
# 查看docker概要信息
docker info
# 查看docker总体帮助文档
docker --help
# 查看docker命令帮助文档
docker <具体命令> --help
  • 镜像命令
# 展示机器上所有的镜像
docker images
	# 参数
	-a # 列出本地所有镜像(包括历史镜像)
	-q # 只显示镜像ID
		# 查询结果
		REPOSITORY	# 镜像的仓库源
		TAG	# 镜像的版本标签号,同一个仓库源可以有多个TAG版本,代表这个仓库源的不同个版本,我们使用REPOSITORY:TAG可以定义不同的镜像,如果不指定默认使用最新的
		IMAGE ID	# 镜像ID
		CREATED	# 镜像创建时间
		SIZE	# 镜像大小
# 查询某个镜像
docker search [镜像名称]
	# 参数,限制查询结果数量
	<search> --limit [数字] 
		# 查询结果
		NAME	# 镜像名称
		DESCRIPTION	# 镜像说明
		STARS	# 镜像点赞数量
		OFFICIAL	# 该镜像是否是官方的
		AUTOMATED	# 该镜像是否是自动构建的
# 拉取某个镜像到本地
docker pull [镜像名称]
	# 参数,指定拉取镜像版本
	:[TAG]
# 查看docker磁盘空间情况
docker system df
# 删除某个镜像
docker rmi [镜像名]/[镜像ID]
	# 参数,强制删除
	<rmi> -f
# docker虚悬镜像是什么?在构建或删除镜像的时候出现问题,导致REPOSITORY与TAG都为none的镜像,直接删除即可,查看所有的虚悬镜像
docker image ls -f dangline=true
# 删除所有虚悬镜像
docker image prune
  • 容器命令
# 有镜像才能创建容器,这是根本的前提,首先拉取一个ubuntu镜像
docker pull ubuntu
# 启动镜像
docker run [options] IMAGE [COMMAND][ARG...]
	# 参数,options说明
	--name	# 为容器启动后指定一个名称
	-d	# 后台运行容器并返回容器ID,也即守护式容器(守护进程)
	-i	 # 以交互式模式运行容器,通常与-t同时使用,也即启动交互式容器(前台有伪终端,等待交互)
	-t	 # 为容器重新分配一个伪输入终端,通常与-i同时使用
	-P	# 随机端口映射
	-p	# 指定端口映射,eg:-p 8080:80
# 交互式启动ubuntu镜像
docker run -it ubuntu /bin/bash
	# 参数
	<-it> --name=mycontainer1	# 指定容器名
# 显示所有正在运行的容器实例
docker ps [options]
	# 参数options
	-a	# 列出当前所有容器,包括启动和未启动的
	-I	 # 显示最近创建的容器
	-n	# 显示最近n个创建的容器
	-q	# 静默模式,只显示容器编号
		# 查询结果
		CONTAINER ID	# 容器ID
		IMAGE	# 镜像名
		COMMAND	# 命令
		CREATED	# 创建时间
		STATUS	# 状态
		PORTS	# 对外暴露的端口(ubuntu不需要暴露端口所以此处没有)
		NAMES	# 容器名,如果前面run的时候没有指定名称则会随机指定一个

image-20221231181558624

# 退出容器,以上述ubuntu容器为例,run进入的命令行
exit	# 容器停止
<ctrl> + <p> + <q>	# 容器不停止
# 启动已停止的容器
docker start [CONTAINER ID]/[NAMES]
# 重启容器
docker restart [CONTAINER ID]/[NAMES]
# 停止容器
docker stop [CONTAINER ID]/[NAMES]
# 强制停止容器
docker kill [CONTAINER ID]/[NAMES]
# 删除已停止的容器(一般是先停止容器,然后再删除)
docker rm [CONTAINER ID]/[NAMES]
	# 参数,强制删除
	<rm> -f

Docker守护式容器解决方案:

# 启动守护式容器(daemon后台服务器),通常情况下我们希望docker的服务是在后台运行的,就像在linux环境中许多软件设置后台启动那样,这里以redis为例
docker pull redis
# 前台交互式启动(复习),ctrl+c可以退出
docker run -it redis:latest
# 后台守护式启动
docker run -d redis:latest
# 查看容器日志
docker logs [CONTAINER ID]
# 查看容器内运行的进程
docker top [CONTAINER ID]
# 查看容器内部细节(配置信息)
docker inspect [CONTAINER ID]
# 进入正在运行的容器(守护式容器)并用命令行进行交互
docker exec -it [CONTAINER ID] bashShell	# exec是在容器中打开新的终端,并且新启动的进程可以用exit退出,不会导致源容器的停止,常用
docker attach [CONTAINER ID]	# 直接进入容器启动命令的终端,不会启动新的进程,用exit退出则会导致容器的停止

image-20221231210225137

# 从容器中拷贝文件到主机上
docker cp [CONTAINER ID]:[容器内的文件路径]  [目的端的拷贝路径]
# 导入和导出容器
	# export 导出容器的内容流作为一个tar归档文件[对应import命令]
	docker export [CONTAINER ID] > [文件名].tar
	# import 从tar包中的内容创建一个新的文件系统再导入为镜像[对应export]
	cat [文件名].tar | docker import - 镜像用户/镜像名:镜像版本号

image-20221231213239487

二、Docker镜像加载原理

​ docker镜像实际上是由一层一层的文件系统的文件系统组成,这种层级的文件系统称之为UnionFS联合文件系统。镜像分层的最大一个好处就是可以共享资源,方便复制迁移,就是为了复用。假如有多个镜像都从相同的base镜像构建而来,那么DockerHost只需要在磁盘上保存一份base镜像;同时内存中也只需要加载一份base镜像,就可以为所有容器服务了。而且镜像每一层都可以被共享。

​ 举个例子,假设现在拉取一个linux镜像,然后运行,Linux在刚启动时会加载bootfs文件系统,然后再加载rootfs。Linux镜像其实就是一层层文件系统组成,如果了解过该镜像,会镜像中是没有vim命令的,并没有加入非必须的文件系统,但你在本地添加了一些文件系统扩展了原有镜像,这很方便,就相当于原来的Linux镜像是base镜像,只需要在磁盘与内存中保存/加载一份即可。

UnionFS(联合文件系统):这是一种分层、轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟机文件系统下。Union文件系统是Docker镜像的基础,镜像可以通过分层来继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

​ 需要注意的是,**Docker镜像层都是只读的,容器层是可写的。**当容器启动时,一个新的可写层会被加载到镜像顶部,这一层通常被称为容器层,容器层之下的都叫镜像层(保证程序的复用)。

# 提交当前容器副本使之成为一个新的镜像(类似于Java中的继承,层层增强)
docker commit -m="[提交的描述信息]" -a="[作者]" [CONTAINER ID] [要创建的目标镜像名]:[标签名]

# ubuntu容器中安装vim举例,首先进入docker容器
docker run -it ubuntu:latest
apt-get update	# 更新包管理工具
apt-get install vim	# 安装vim
# 将当前添加了vim功能的ubuntu提交副本成为一个新的镜像
docker commit -m="vim cmd add ok" -a="shadowy" 39932b4d0bab heavyhead/myubuntu:1.1

image-20230101164659500

三、Docker容器卷

# 容器卷与主机互联互通,其实可以理解为将宿主机的某个路径共享给了Docker,VM里面的共享文件夹操作,如果宿主机路径下之前有文件,那Docker也可以查看
docker run -it --privileged=true -v [宿主机绝对路径目录]:/[容器内目录] [镜像名]
	# 参数,容器卷内文件只读,只有读权限
	<[容器内目录]>:ro
docker run -it --privileged=true -v /Users/zhangyong/Downloads:/tmp/docker_data --name=u1 ubuntu
	# 新建文件,会发现Docker目录与宿主机目录同时会有这个文件(文件互通)
	touch dockerin.txt
# 查看容器卷是否挂载成功
docker inspect [CONTAINER ID]
	# 查询结果
	"Mounts": [{
    	"Type": "bind",
    	"Source": "/Users/zhangyong/Downloads",
    	"Destination": "/tmp/docker_data",
    	"Mode": "",
    	"RW": true,
    	"Propagation": "rprivate"
	}]
# 容器卷2继承容器卷1的卷规则,--volumes-from就类似于Java中的继承
docker run -it --privileged=true -v --volumes-from [CONTAINER ID] [镜像名]

四、Docker安装常用软件说明

Docker中经常会安装一些常用的软件,这里记录一下常用工具的安装配置,持续补充

Tomcat、MySQL、Redis

  • Tomcat

​ 首先在https://hub.docker.com/dockerHub上寻找镜像,官网上有对应的镜像时,直接pull即可docker pull tomcat

image-20230107142226959

# Docker中拉取Tomcat镜像
docker pull tomcat
# Docker中启动Tomcat镜像
docker run -d -p 8080:8080 --name t1 tomcat
	# 复习docker各个指令的作用
	-d	# 后台运行
	-i	# 交互式运行
	-t	# 返回一个伪终端
	-p	# 指定端口,格式:[主机端口]:[docker容器端口]
# 查看Tomcat容器
docker ps
# 此时访问tomcat主页会发现404,因为在高版本的tomcat中,对目录结构做了更改,需要手动处理,首先进入容器
docker exec -it [CONTAINER ID] /bin/bash	# exec是在容器中打开新的终端,并且新启动的进程可以用exit退出,不会导致源容器的停止,常用
cd /usr/local/tomcat
rm -r webapps
mv webapps.dist webapps
# 访问 http://localhost:8080/

image-20221231210225137

  • MySQL
# 简单版Mysql容器启动
docker search mysql
# 拉取MySQL镜像,以5.7版本为例
docker pull mysql:5.7
# 启动MySQL镜像,注意启动前先判断3306端口是否已经被占用了
docker run -p [本机端口]:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
	# 举例,我本地有一个mysql了,所以用我本地的3307端口去映射了docker的3306端口,在datagrip中使用3307端口就可以链接到这个docker中的mysql了
	docker run -p 3307:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
# 进入Mysql命令行
docker exec -it [CONTAINER ID] /bin/bash
mysql -uroot -p
[MYSQL_ROOT_PASSWORD]
# 实战版Mysql容器启动,生产环境下需要使用容器卷,防止数据核心资产丢失
docker run -p [主机端口]:3306 --privileged=true 
-v /Users/zhangyong/Shadowy/log:/var/log/mysql 
-v /Users/zhangyong/Shadowy/data:/var/lib/mysql 
-v /Users/zhangyong/Shadowy/conf:/etc/mysql/conf.d
-e MYSQL_ROOT_PASSWORD=123456 -d --name mysql mysql
	# 复习docker容器卷各个指令的作用
	privileged	# 开启容器卷
	-v	# 可以映射多个容器卷目录
# 修改my.cnf配置文件
cd /Users/zhangyong/Shadowy/conf
vim my.cnf
	# 将下面内容粘贴进去,保证客户端和服务端的编码都是utf8
	[client]
	default_character_set=utf8
	[mysqld]
	collation_server=utf8_general_ci
	character_set_server=utf8
# 重新启动mysql容器
docker restart mysql

五、Dockerfile

Dockerfile:定义一个Dockerfile,它定义了进程中需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用程序需要和系统服务和内核进程交互时,这时就需要考虑如何设计namespace的权限控制)等等;

Docker IMAGE:在Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行docker镜像时会真正开始提供服务;

Docker CONTAINER:容器是直接提供服务的。

# Dockerfile关键字
FROM	# 基础镜像,当前镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,第一条必须是from命令
MAINTAINER	# 镜像维护者的姓名+邮箱
RUN	# 容器构建时需要运行的命令,RUN会在docker build时运行
	# 两种格式
	shell	# eg: yum install vim
	exec  # eg:	["./test.php", "dev", "offline"]
EXPOSE	# 当前容器对外暴露的端口
WORKDIR	# 指定在容器创建之后,终端默认登录进来的工作目录,落脚点
USER	# 指定该镜像以什么用户去执行,如果不指定默认root
ENV	# 用来在构建镜像中设置环境
ADD	# 将宿主机目录下的文件拷贝至镜像且会自动处理URL和解压tar压缩包
COPY	# 类似ADD,拷贝文件和目录到镜像中,将从构建上下文目录中<原路径>的文件/目录复制到新的一层的镜像内的<目标路径>位置
	COPY src dest
	COPY ["src", "dest"]
	<src源路径>: 源文件或者源目录
	<dest目标路径>: 容器内的指定路径,该路径不用事先建好,不存在会自动创建
VOLUME	# 容器数据卷,用于数据持久化和保存
CMD	# 指定容器启动之后要干的事情,类似与run,需要注意的是只有最后一个cmd生效且cmd命令会被docker run后面的命令覆盖
ENTRYPOINT	# 指定容器启动之后要干的事情,类似与run,但不会被docker run后面的命令覆盖
	# ENTRYPOINT可以和CMD一起使用,一般是变参才会使用CMD,此时这里的CMD等于就是在给ENTRYPOINT传参。当指定了ENTRYPOINT之后,CMD的含义就发生了变化,不再是直接运行其命令而是将CMD的内容作为参数传递给ENTRYPOINT指令
	<ENTRYPOINT> <CMD>
	ENTRYPOINT ["nginx", "-c"] CMD ["/etc/nginx/nging.conf"] -> nginx -c /etc/nginx/nginx.conf

image-20221231210225137

六、Dockerfile打包SpringBoot项目

  • shell命令打包

​ 首先通过maven clean package打一个jar包出来:

image-20221231210225137

# 进入一个目录,将jar包上传至该目录中,然后新建Dockerfile文件
vim Dockerfile
# Dockerfile内容
	# 基础镜像使用Java
    FROM openjdk:17
    # 作者
    MAINTAINER shadowy
    # VOLUME指定临时文件目录为/tmp,在主机目录下创建了一个临时文件并连接到容器的/tmp
    VOLUME /tmp
    # 将jar包添加到容器并更名为shadowy_docker_v1.0.jar
    ADD MySpringBoot-0.0.1-SNAPSHOT.jar /shadowy_docker_v1_0.jar
    # 运行jar包
    RUN bash -c 'touch /shadowy_docker_v1_0.jar' 
    ENTRYPOINT ["java", "-jar", "/shadowy_docker_v1_0.jar"]
    # 暴露2023端口出来作为微服务的端口
    EXPOSE 2023
# 通过Dockerfile文件生成一个镜像包
docker build -t shadowy_docker:1.1 .
# 运行该镜像包
docker run -d -p 2023:2023 [IMAGE ID]
	# 如果是MAC,带上如下参数
	<-d> --platform linux/amd64

七、Docker网络

​ 安装Docker后,默认会自动创建三个网络模式,输入docker network ls命令可以查看:

image-20230114142217696

  1. bridge:为每个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥中,默认为该模式(常用);
  2. host:容器不会虚拟出自己的网卡、配置自己的ip等,而是直接使用宿主机的IP和端口;
  3. none:容器有独立的Network namespace,但并没有对其进行任何的网络设置,如分配veth pair和网桥连接、IP等;
  4. container:新创建的容器不会创建自己的网卡和配置自己的IP,而是和一个指定的容器共享IP、端口范围等。
# 新建/删除一个自定义网络模式
docker network create [自定义网络模式名]
docker network rm [自定义网络模式名]	
# 指定镜像采用哪种网络模式启动 --network
docker run -d -p 8080:8080 --network [网络模式] --name mytomcat tomcat
	# 参数 [网络模式]
	bridge # 统一使用docker0来作为通信路由器,如下图所示
	host # 直接使用主机的ip和端口,此处的-p命令就无意义了,端口号会以主机端口号为主如果重复则递增
	none # 需要自己为Docker容器添加网卡与IP配置
	container:<CONTAINER ID> # 新创建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样两个容器除了网络方面,其他如文件系统、进程列表还是隔离的
# 查看容器的(网络)配置
	<自定义网络模式名> # 使用自定义网络模式,解决了在集群环境下用容器名互相通信的问题,避免了ip浮动的问题
docker inspect [CONTAINER ID]
	# 参数,因为网络配置在容器详情信息的最后面,所以我们可以在该命令后面添加指定行参数
	| tail -n 20

image-20221231210225138

八、Docker Compose容器编排

​ Compose是Docker公式推出的一个工具软件,可以管理多个Docker容器组成一个应用。你需要定义一个YAML格式的配置文件docker-compose.yml,写好多个容器之间的调用关系。然后只要一个命令就能同时启动/关闭这些容器。

为什么会有Compose?

​ docker官方建议我们每个容器中都只运行一个服务,因为docker容器本身所占用的资源极少,最好是将每个服务单独的分割开来,但这样我们又面临了一个新的问题:

​ 如果我需要同事部署好多个服务,难道需要再每个微服务中都单独写Dockerfile,然后再构建镜像、构建容器?例如要实现一个web微服务项目,除了web本身的服务容器本身之外,往往还需要再加上后端的数据库mysql服务容器、redis服务器、注册中心eureka,甚至还包括负载均衡中间件等等。。。

​ Compose允许用户通过一个单独的docker-compose.yml模板文件来定义一组相关联的应用容器为一个总的项目,就类似与Spring中的SpringContext.xml文件去管理各个bean的生命周期一样。

​ 可以很容易地用一个配置文件就定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,然后完成构建。Docker-Compose解决了容器与容器之间如何管理编排的问题。

下载地址:https://docs.docker.com/compose/install/

# Compose常用命令
docker-compose [arg]
	# 查看帮助
	-h
	# 启动所有的docker-compose服务
	up
	# 启动所有的docker-compose服务并后台运行
	up -d
	# 停止并删除容器、网络、卷、镜像
	down
	# 进入容器实例内部
	exec [yml文件中写的服务id]
	# 展示当前docker-compose编排过的运行的所有容器
	ps
	# 展示当前docker-compose编排过的容器进程
	top
	# 查看容器输出日志
	logs [yml文件中写的服务id]
	# 检查配置
	config
	# 检查配置,有问题才输出
	config -q
	# 重启/启动/停止 服务
	restart
	start
	stop

案例:myboot镜像启动需要依赖mysql与redis服务,编写docker-compose.yml文件:

# 设置版本号
version: "3"

# 编排服务
services:
 microService:
  image: myboot:1.9
  container_name: ms01
  ports:
   - "2023:2023"
  volumes:
   - /app/microService:/data
  networks:
   - mine_net
  depends_on:
   - redis
   - mysql

 redis:
  image: redis:6.0.8
  ports:
   - "6379:6379"
  volumes:
   - /app/redis/redis.conf:/etc/redis/redis.conf
   - /app/redis/data:/data
  networks:
   - mine_net
  command: redis-server /etc/redis/redis.conf
	
 mysql:
  image: mysql:8.0.31
  environment:
   MYSQL_ROOT_PASSWORD: '123456'
   MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
   MYSQL_DATABASE: 'reggie'
   MYSQL_USER: 'shadowy'
   MYSQL_PASSWORD: 'f88b33a6e159f0ac'
  ports:
   - "3306:3306"
  volumes:
   - /app/mysql/db:/var/lib/mysql
   - /app/mysql/conf/my.cnf:/etc/my.cnf
   - /app/mysql/init:/docker-entrypoint-initdb.d
  networks:
   - mine_net
  command: --default-authentication-plugin=mysql_native_password	# 解决外部无法访问的问题

# 新建一个自定义网络
networks:
 mine_net:

九、容器监控

​ 对于docker中的容器,想要获取实时的状态信息,可以通过docker stats命令来完成:image-20230115144813801

但这个原生的命令同时也存在着一些局限性,比如只能查看宿主机的上的docker容器、状态信息不能持久化保存、无法做到实时监控等等,因此我们需要借助容器监控三剑客CIG:CAdvisor监控收集+InfluxDB存储数据+Granfana可视化展示

  • CAdvisor:是一个容器资源监控工具,包括容器的内存、CPU、网络IO、磁盘IO等监控,同时提供了一个WEB页面用于查看容器的实时运行状态。它默认只存储两分钟的数据,而且只针对单物理机,不过它同时也提供了很多数据集成接口,支持InfluxDB、Redis、Kafka、Elasticsearch等集成,可以加上对应的配置将监控数据发往这些数据库存储起来;
  • InfluxDB:是一个用GO语言编写的开源分布式时序、事件和指标的数据库,无需外部的依赖;
  • Granfana:是一个开源的数据监控分析可视化平台,支持多种数据源配置和丰富的插件及模板功能,支持图标权限控制和报警。

使用Docker Compose使用三剑客:

# 首先新建一个cig目录,进入其中
pwd -> /Users/zhangyong/Shadowy/mydocker/cig
# 新建docker-compose.yml文件,将以下内容写入其中
version: '3.1'

volumes:
  grafana_data: {}

services:
 influxdb:
  image: tutum/influxdb:0.9
  restart: always
  environment:
    - PRE_CREATE_DB=cadvisor
  ports:
    - "8083:8083"
    - "8086:8086"
  volumes:
    - ./data/influxdb:/data

 cadvisor:
  image: google/cadvisor # 目前该镜像有问题,运行不了,github上讨论用 gcr.io/cadvisor/cadvisor 可以解决,但是这个镜像拉取不下来 https://github.com/google/cadvisor/issues/1943
  links:
    - influxdb:influxsrv
  command: -storage_driver=influxdb -storage_driver_db=cadvisor -storage_driver_host=influxsrv:8086
  restart: always
  ports:
    - "8080:8080"
  volumes:
    - /:/rootfs:ro
    - /var/run:/var/run:rw
    - /sys:/sys:ro
    - /var/lib/docker/:/var/lib/docker:ro

 grafana:
  user: "104"
  image: grafana/grafana
  user: "104"
  restart: always
  links:
    - influxdb:influxsrv
  ports:
    - "3000:3000"
  volumes:
    - grafana_data:/var/lib/grafana
  environment:
    - HTTP_USER=admin
    - HTTP_PASS=admin
    - INFLUXDB_HOST=influxsrv
    - INFLUXDB_PORT=8086
    - INFLUXDB_NAME=cadvisor
    - INFLUXDB_USER=root
    - INFLUXDB_PASS=root
# 检查yml文件格式、语法是否有问题
docker-compose config -q
# 启动docker-compose文件
docker-compose up
# 查看一下容器启动状态
docker ps
# 各个监控指标访问地址
浏览CAdvsior收集服务:http://ip:8080
浏览influxdb存储服务:http://ip:8083
浏览grafana展现服务:http://ip:3000

image-20230115154654461


版权声明:本文为zy_zhangruichen原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>