赞
踩
1)从面向对象角度来看,Docker利用容器(Container)独立运行一个或一组应用,应用程序或服务运行在容器里面,容器就类似于一个虚拟化的运行环境,容器是用镜像创建出来的运行实例。就像是Java中的类和实例对象一样,容器是镜像运行的实体。容器为镜像提供了一个标准的和隔离的运行环境,它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。
2)从镜像角度来看,可以把容器看做是一个简易版的Linux环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
# 启动,停止,重启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的时候没有指定名称则会随机指定一个
# 退出容器,以上述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退出则会导致容器的停止
# 从容器中拷贝文件到主机上
docker cp [CONTAINER ID]:[容器内的文件路径] [目的端的拷贝路径]
# 导入和导出容器
# export 导出容器的内容流作为一个tar归档文件[对应import命令]
docker export [CONTAINER ID] > [文件名].tar
# import 从tar包中的内容创建一个新的文件系统再导入为镜像[对应export]
cat [文件名].tar | docker import - 镜像用户/镜像名:镜像版本号
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
# 容器卷与主机互联互通,其实可以理解为将宿主机的某个路径共享给了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中经常会安装一些常用的软件,这里记录一下常用工具的安装配置,持续补充
Tomcat、MySQL、Redis
首先在https://hub.docker.com/
dockerHub上寻找镜像,官网上有对应的镜像时,直接pull即可docker pull tomcat
:
# 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/
# 简单版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涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用程序需要和系统服务和内核进程交互时,这时就需要考虑如何设计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
首先通过maven clean package
打一个jar包出来:
# 进入一个目录,将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 network ls
命令可以查看:
docker0
虚拟网桥中,默认为该模式(常用);# 新建/删除一个自定义网络模式
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
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
命令来完成:
但这个原生的命令同时也存在着一些局限性,比如只能查看宿主机的上的docker容器、状态信息不能持久化保存、无法做到实时监控等等,因此我们需要借助容器监控三剑客CIG:CAdvisor监控收集+InfluxDB存储数据+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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。