1. 简介
官方文档地址:https://www.docker.com/get-started
中文参考手册:https://docker_practice.gitee.io/zh-cn/
本笔记参考B站视频:【编程不良人】Docker&Docker-Compose 实战!
Docker中文文档:https://yeasy.gitbook.io/docker_practice/
1.1 什么是Docker
官方定义:
-
帮助开发人员和开发团队构建和发布应用
-
为开发人员和团队提供一个完整容器(container)解决方案
-
docker是一个容器技术
-
运行你的应用不需要你的环境(早起Docker官方定义)
1.2 Docker起源
- 历史
Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在 GitHub 上进行维护。Docker 项目后来还加入了 Linux 基金会,并成立推动 开放容器联盟(OCI)。
- 开源
Docker 自开源后受到广泛的关注和讨论,至今其 GitHub 项目 已经超过 5 万 7 千个星标和一万多个 fork。甚至由于 Docker 项目的火爆,在 2013 年底,dotCloud 公司决定改名为 Docker。Docker 最初是在 Ubuntu 12.04 上开发实现的;Red Hat 则从 RHEL 6.5 开始对 Docker 进行支持;Google 也在其 PaaS 产品中广泛应用 Docker。
- 本质
Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 OverlayFS 类的 Union FS 等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。
1.3 优势
-
问题1:
在开发的时候,在本机测试环境可以跑,生产环境跑不起来
这里我们拿java Web应用程序举例,我们一个java Web应用程序涉及很多东西,比如jdk、tomcat、mysql等软件环境(配置环境非常繁琐)。当这些其中某一项版本不一致的时候,可能就会导致应用程序跑不起来这种情况。Docker则将程序以及使用软件环境直接打包在一起,无论在那个机器上保证了环境一致。
优势1: 环境一致,高效迁移
-
问题2:
服务器自己的程序挂了,结果发现是别人程序出了问题把内存吃完了,自己程序因为内存不够就挂了
这种也是一种比较常见的情况,如果你的程序重要性不是特别高的话,公司基本上不可能让你的程序独享一台服务器的,这时候你的服务器就会跟公司其他人的程序共享一台服务器,所以不可避免地就会受到其他程序的干扰,导致自己的程序出现问题。Docker就很好解决了环境隔离的问题,别人程序不会影响到自己的程序。
优势2:(操作系统)进程级隔离,容器独立
-
问题3:
公司要搞一个活动,可能会有大量的流量进来,公司需要再多部署几十台服务器
在没有Docker的情况下,要在几天内部署几十台服务器,这对运维来说是一件非常折磨人的事,而且每台服务器的环境还不一定一样,就会出现各种问题,最后部署地头皮发麻。用Docker的话,我只需要将程序打包到镜像,你要多少台服务,我就给力跑多少容器,极大地提高了部署效率。
优势3: 镜像机制,便于部署
1.4 Docker和虚拟机区别
Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker
技术比虚拟机技术更为轻便、快捷。
下面的图片比较了 Docker 和传统虚拟化方式的不同之处。传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fa3uKNxa-1651432281995)(https://vuepress.mirror.docker-practice.com/assets/img/virtualization.bfc621ce.png)]
由上图可知,Docker省略了Hypervisor
(虚拟层)和Guest OS
(来宾操作系统)。比较上面两张图,我们发现虚拟机是携带操作系统,本身很小的应用程序却因为携带了操作系统而变得非常大,很笨重
。Docker是不携带操作系统的,所以Docker的应用就非常的轻巧。另外在调用宿主机的CPU、磁盘等等这些资源的时候,拿内存举例,虚拟机是利用Hypervisor去虚拟化内存,整个调用过程是虚拟内存->虚拟物理内存->真正物理内存,但是Docker是利用Docker Engine去调用宿主的的资源,这时候过程是虚拟内存->真正物理内存。
传统虚拟机 | Docker容器 | |
---|---|---|
磁盘占用 | 几个GB到几十个GB左右 | 几十MB到几百MB左右 |
CPU内存占用 | 虚拟操作系统非常占用CPU和内存 | Docker引擎占用极低 |
启动速度 | (从开机到运行项目)几分钟 | (从开启容器到运行项目)几秒 |
安装管理 | 需要专门的运维技术 | 安装、管理方便 |
应用部署 | 每次部署都费时费力 | 从第二次部署开始轻松简捷 |
耦合性 | 多个应用服务安装到一起,容易互相影响 | 每个应用服务一个容器,达成隔离 |
系统依赖 | 无 | 需求相同或相似的内核,目前推荐是Linux |
1.5 核心架构和概念
1.镜像 (Image)
是什么
- 一个镜像代表一个应用环境/软件(最精简版linux系统加一个软件),他是一个只读的文件,如 mysql镜像,tomcat镜像,nginx镜像等。
- 镜像是一种轻量级的,可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、依赖(运行时所需的库、环境变量和配置文件)。
为什么这么大
简而言之就是,除了这个软件本身,他的运行环境占了非常多空间。拿Tomcat举例,除了Tomcat本身的代码,还有Tomcat的依赖、基础Linux操作系统和JDK。
StackOverflow🔗:Why are Docker container images so large?
如此一来,不仅用户本地仓库的存储压力非常大,Docker Hub的存储压力也非常大。Docker使用了UnionFS(联合/叠加文件系统)来解决这个问题。
UnionFS
Union文件系统是一种分层,轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。Union文件系统是Docker镜像的基础。这种文件系统特性:就是一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
- 简而言之就是把公共的部分拆分出来,使用时再联合加载(把各层文件系统叠加起来)
- 拆得越细,复用率就越高
镜像底层原理(了解即可)
docker的镜像实际是由一层一层的文件系统组成。
-
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统。在docker镜像的最底层就是bootfs。这一层与Linux/Unix 系统是一样的,包含boot加载器(bootloader)和内核(kernel)。当boot加载完,后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时会卸载bootfs。
-
rootfs(root file system),在bootfs之上,包含的就是典型的linux系统中的/dev,/proc,/bin,/etc等标准的目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu/CentOS等等。
-
我们平时安装进虚拟机的centos都有1到几个GB,为什么docker这里才200MB?对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令,工具,和程序库就可以了,因为底层直接使用Host的Kernal,自己只需要提供rootfs就行了。由此可见不同的linux发行版,他们的bootfs是一致的,rootfs会有差别。因此不同的发行版可以共用bootfs。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pNZiuXFP-1651432281998)(https://cdn.jsdelivr.net/gh/zewei94yomi/ImageLoader@master/uPic/1567585172(1)].jpg)
这种分层结构的优点:资源共享
比如:有多个镜像都是从相同的base镜像构建而来的,那么宿主机只需在磁盘中保存一份base镜像。同时内存中也只需要加载一份base镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。Docker镜像都是只读的。当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称为容器层,容器层之下都叫镜像层。这就是为什么我们在pull镜像的时候,Docker为我们一层层下载,而不是一次下载一个独立的镜像。
2.容器 (Container)
镜像每次运行之后就是产生一个容器,就是正在运行的镜像,特点就是可读可写
3.仓库 (Repository)
用来存放镜像的位置,类似于maven仓库,也是镜像下载和上传的位置。和Maven一样,Docker官方提供了一个远程Web界面仓库:Docker Hub
;同时为了避免重复下载,本地还有一个本地仓库,存储使用过的镜像。
4.DockerFile
docker生成镜像配置文件,用来书写自定义镜像的一些配置
(第六章节有详细讲)
5.tar
一个对镜像打包的文件,日后可以还原成镜像
6.数据卷(Docker Volume)
用来实现容器中数据和宿主机中数据相互映射(同步),减少频繁cp
命令带来的麻烦。
(第五章节有详细讲)
1.6 执行流程
- 在收到run命令后,先去本地仓库查看是否有对应的镜像,有则运行
- 如果没有在本地仓库找到,就去Docker Hub中查找该镜像并下载,然后运行
2. 使用
2.1 Docker引擎安装
a. Mac
官网下载地址:https://docs.docker.com/get-docker/
选择Mac Intel版本,下载,安装,授权,启动 Docker Desktop。
可在命令行查看Docker版本:
docker version
注意Docker分为客户端和服务端,服务端引擎用来运行容器,客户端引擎用来接受、解析命令,并发送给服务端。
测试Docker安装是否成功
$ docker run hello-world
b. Centos7.x
安装docker(centos7.x)
-
卸载原始docker
$ sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine
-
安装docker依赖
$ sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm2
-
设置docker的yum源
$ sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo
-
安装最新版的docker
$ sudo yum install docker-ce docker-ce-cli containerd.io
-
指定版本安装docker
$ yum list docker-ce --showduplicates | sort -r $ sudo yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io $ sudo yum install docker-ce-18.09.5-3.el7 docker-ce-cli-18.09.5-3.el7 containerd.io
-
启动docker
$ sudo systemctl enable docker $ sudo systemctl start docker
-
关闭docker
$ sudo systemctl stop docker
-
测试docker安装
$ sudo docker run hello-world
bash安装(通用所有平台)
-
在测试或开发环境中 Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,CentOS 系统上可以使用这套脚本安装,另外可以通过
--mirror
选项使用国内源进行安装:执行这个命令后,脚本就会自动的将一切准备工作做好,并且把 Docker 的稳定(stable)版本安装在系统中。$ curl -fsSL get.docker.com -o get-docker.sh $ sudo sh get-docker.sh --mirror Aliyun
-
启动docker
$ sudo systemctl enable docker $ sudo systemctl start docker
-
创建docker用户组
$ sudo groupadd docker
-
将当前用户加入docker组
$ sudo usermod -aG docker $USER
-
测试docker安装是否正确
$ docker run hello-world
2.2 配置阿里云镜像加速
在国内下载Docker官方镜像仓库速度慢,可以配置阿里云的镜像加速
- 访问阿里云登录自己账号查看docker镜像加速服务(每个人的加速服务都不一样)
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://lz2nib3q.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
- 验证docker的镜像加速是否生效
[root@localhost ~]# docker info
..........
127.0.0.0/8
Registry Mirrors:
'https://lz2nib3q.mirror.aliyuncs.com/'
Live Restore Enabled: false
Product License: Community Engine
2.3 常用命令
辅助命令
1.安装完成辅助命令
docker version -------------------------- 查看docker的信息
docker info -------------------------- 查看更详细的信息
docker --help -------------------------- 帮助命令
Images 镜像命令
1.查看本机中所有镜像
docker images -------------------------- 列出本地所有镜像
-a 列出所有镜像(包含中间映像层)
-q 只显示镜像id
docker images 镜像名 -------------------------- 列出本地某镜像名的所有镜像
2.搜索镜像
docker search [options] 镜像名 ------------------- 去dockerhub上查询当前镜像
-s 指定值 列出收藏数不少于指定值的镜像
--no-trunc 显示完整的镜像信息
3.从仓库下载镜像
docker pull 镜像名[:TAG|@DIGEST] ----------------- 下载镜像
docker pull 镜像名 ----------------- 下载最新版本镜像
4.删除镜像
docker rmi 镜像名/id -------------------------- 删除镜像
-f 强制删除
docker image rm 镜像名/id -------------------------- 删除未运行过的镜像
docker image rm -f 镜像名/id -------------------------- 强制删除镜像
docker image rm -f $(docker image tomcat -q) ------- 批量删除:强制删除所有tomcat镜像
Container 容器命令
映射端口号:
1.运行容器
docker run 镜像名/id -------------------------- 镜像名或id新建并启动容器
--name 别名为容器起一个名字
-d 启动守护式容器(在后台启动容器)
-p 映射端口号:宿主机端口号:容器内端口号(-p可以写多个)
-e Set environment variables, e.g. mysql: username, passwod
例:
注意:这些选项没有先后之分
docker run -p 8080:8080 tomcat 映射端口号
docker run -p 8080:8080 -d tomcat 后开启的容器
docker run -p 8080:8080 -d --name myTomcat tomcat 为容器起别名
docker run -d --name myTomcat -P tomcat 随机端口映射,容器内部端口随机映射到主机的端口
2.查看运行的容器
docker ps -------------------------- 列出所有"正在运行"的容器
-a 正在运行的和历史运行过的容器
-q 静默模式,只显示容器编号
3.停止|关闭|重启|暂停容器
docker start 容器名字/容器id --------------- 开启容器
docker restart 容器名/容器id --------------- 重启容器
docker stop 容器名/容器id ------------------ 正常停止容器运行
docker kill 容器名/容器id ------------------ 立即停止容器运行
docker pause 容器名/容器id ------------------ 暂停容器运行
docker unpause 容器名/容器id ------------------ 恢复容器运行
docker stop $(docker ps -a -q) ------------------ stop停止所有容器
4.删除容器
docker rm 容器名/容器id -------------------------- 删除已停止的容器
docker rm -f 容器名/容器id -------------------------- 强制删除容器
docker rm -f $(docker ps -aq) -------------------------- 删除所有容器
5.查看容器内进程
docker top 容器id或者容器名 ------------------ 查看容器内的进程
6.查看查看容器内部细节
docker inspect 容器id ------------------ 查看容器内部细节
7.查看容器的运行日志
docker logs [OPTIONS] 容器id或容器名 ------------------ 查看容器日志
-t 加入时间戳(容器外部/宿主机时间)
-f 实时日志
--tail 数字 显示最后多少条
8.进入容器内部
docker exec [options] 容器id 容器内命令 ------------------ 进入容器执行命令
-i 以交互模式运行容器,通常与-t一起使用
-t 分配一个伪终端 shell窗口 bash
例:
docker exec -it myTomcat bash 进入容器内部,并使用bash进行交互
exit 退出交互
9.容器和宿主机之间复制文件
docker cp 文件|目录 容器id:容器路径 ----------------- 将宿主机复制到容器内部
docker cp 容器id:容器内资源路径 宿主机目录路径 ----------------- 将容器内资源拷贝到主机上
10.数据卷(volum)实现与宿主机共享目录
docker run -v 宿主机的路径|任意别名:/容器内的路径 镜像名
注意:
1.如果是宿主机路径必须是绝对路径,宿主机目录会覆盖容器内目录内容
2.如果是别名则会在docker运行容器时自动在宿主机中创建一个目录,并将容器目录文件复制到宿主机中
11.打包镜像(备份)
docker save 镜像名:tag -o 名称.tar
12.载入镜像(恢复)
docker load -i 名称.tar
13.容器打包成新的镜像
docker commit -m "描述信息"(optional) -a "作者信息"(optional) 容器id或者名称 打包的镜像名称:标签
例:
docker commit -m "deploy demo project in tomcat" -a "me" 9f2 tomcat-test:8.0
3. Docker安装常用服务
关于如何启动常用服务,Docker Hub官网通常都有教程。
注意所有需要持有/存储数据的服务,都要看Docker Hub的文档,查找数据卷的映射地址。
a. MySQL
这里以MySQL 5.X版本为例
Docker Hub:https://hub.docker.com/_/mysql
前面在提到Data Volume(数据卷)时提到,任何需要持有数据的服务都应当使用数据卷,将数据同步到宿主机,防止容器误删和数据丢失,所以在启动MySQL时也应当配置数据卷。
基本命令
# 1.拉取mysql镜像到本地
docker pull mysql:tag (tag不加默认最新版本)
#2.运行mysql服务
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 -d --restart=always mysql:tag
# - 设置环境变量:mysql用户名和密码
# - 开放端口映射,注意查看宿主机的3306端口是否已经运行mysql,如果有则需要关闭
# - 总是运行:docker重启后,mysql会自动启动
# - 这里还没有使用数据卷,数据持久化不安全
# 3.进入mysql容器
docker exec -it 容器名称|容器id bash
# 4.外部查看mysql日志
docker logs 容器名称|容器id
# 使用数据卷保证数据安全
docker run --name mysql -v /root/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 -d mysql:tag
# 注意这种方式使用的是绝对路径,会清空容器内文件,但是/var/lib/mysql下面会在初始化时再创建几张默认表(文件),不会被清空
# 5.使用自定义配置参数
docker run --name mysql -v /root/mysql/conf.d:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=root -d mysql:tag
# 6.将容器数据位置与宿主机位置挂载保证数据安全
docker run --name mysql -v /root/mysql/data:/var/lib/mysql -v /root/mysql/conf.d:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 -d mysql:tag
# 7.通过其他客户端访问 如在window系统|macos系统使用客户端工具访问
数据备份
尽管使用数据卷已经可以很好地备份数据库底层文件,但对去MySQL数据库其还是不利于数据迁移,并且备份不够方便。MySQL数据备份和迁移应当使用sql文件形式
- 使用MySQL官方命令
mysqldump
# 将mysql数据库备份为sql文件
# 备份/导出全部数据:
docker exec mysql|容器id sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > /root/all-databases.sql
# 备份/导出指定库数据
docker exec mysql sh -c 'exec mysqldump --databases 库表 -uroot -p"$MYSQL_ROOT_PASSWORD"' > /root/all-databases.sql
# 备份/导出指定库数据不要数据
docker exec mysql sh -c 'exec mysqldump --no-data --databases 库表 -uroot -p"$MYSQL_ROOT_PASSWORD"' > /root/all-databases.sql
# 执行sql文件到mysql中
docker exec -i mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD"' < /root/xxx.sql
- 使用软件应用,如MySQL Workbench, Navicat…
b. Redis
# 1.在docker hub搜索redis镜像
docker search redis
# 2.拉取redis镜像到本地
docker pull redis
# 3.启动redis服务运行容器
docker run --name redis -d redis:tag (没有暴露外部端口)
docker run --name redis -p 6379:6379 -d redis:tag (暴露外部宿主机端口为6379进行连接)
# 4.查看启动日志
docker logs -t -f 容器id|容器名称
# 5.进入容器内部查看
docker exec -it 容器id|名称 bash
# 6.加载外部自定义配置启动redis容器
# 默认情况下redis官方镜像中没有redis.conf配置文件 需要去官网下载指定版本的配置文件
# 1. wget http://download.redis.io/releases/redis-5.0.8.tar.gz 下载官方安装包
# 2. 将官方安装包中配置文件进行复制到宿主机指定目录中如 /root/redis/redis.conf文件
# 3. 修改需要自定义的配置
# bind 0.0.0.0 开启远程权限
# appenonly yes 开启aof持久化
# 4. 加载配置启动
docker run --name redis -v /root/redis:/usr/local/etc/redis -p 6379:6379 -d redis redis-server /usr/local/etc/redis/redis.conf
# 7.将数据目录挂在到本地保证数据安全
docker run --name redis -v /root/redis/data:/data -v /root/redis/redis.conf:/usr/local/etc/redis/redis.conf -p 6379:6379 -d redis redis-server /usr/local/etc/redis/redis.conf
c. Nginx
# 1.在docker hub搜索nginx
docker search nginx
# 2.拉取nginx镜像到本地
[root@localhost ~]# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
afb6ec6fdc1c: Pull complete
b90c53a0b692: Pull complete
11fa52a0fdc0: Pull complete
Digest: sha256:30dfa439718a17baafefadf16c5e7c9d0a1cde97b4fd84f63b69e13513be7097
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
# 3.启动nginx容器
docker run -p 80:80 --name nginx01 -d nginx
# 4.进入容器
docker exec -it nginx01 /bin/bash
# 查找目录: whereis nginx
# 配置文件: /etc/nginx/nginx.conf
# 5.复制配置文件到宿主机
docker cp nginx01(容器id|容器名称):/etc/nginx/nginx.conf 宿主机名录
# 6.挂在nginx配置以及html到宿主机外部
docker run --name nginx02 -v /root/nginx/nginx.conf:/etc/nginx/nginx.conf -v /root/nginx/html:/usr/share/nginx/html -p 80:80 -d nginx
4. 高级网络配置
4.1 说明
中文文档:https://vuepress.mirror.docker-practice.com/advanced_network/
注意:本章属于
Docker
高级配置,如果您是初学者,您可以暂时跳过本章节,直接学习 Docker Compose 一节。
背景:
在使用Docker容器前,服务(软件)间通信主要通过ip地址和端口号(比如此时一个Java项目放在了Tomcat中,需要访问MySQL数据库。),而使用了Docker容器之后,因为做到了宿主机进程间隔离,容器间不是简单地使用之前的方式进行通信。
原理:
(复制自中文文档)
当 Docker 启动时,会自动在主机上创建一个
docker0
虚拟网桥,实际上是 Linux 的一个 bridge,可以理解为一个软件交换机。它会在挂载到它的网口之间进行转发。同时,Docker 随机分配一个本地未占用的私有网段(在 RFC1918 (opens new window)中定义)中的一个地址给
docker0
接口。比如典型的172.17.42.1
,掩码为255.255.0.0
。此后启动的容器内的网口也会自动分配一个同一网段(172.17.0.0/16
)的地址。当创建一个 Docker 容器的时候,同时会创建了一对
veth pair
接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。这对接口一端在容器内,即eth0
;另一端在本地并被挂载到docker0
网桥,名称以veth
开头(例如vethAQI2QT
)。通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。Docker 就创建了在主机和所有容器之间一个虚拟共享网络。
总结:
- 默认docker在创建容器时,将所有容器都连接到
docker0
网桥上,默认在docker0
网桥的容器都可以使用容器内ip地址进行通信 - 默认docker在创建容器时,将所有容器都连接到
docker0
网桥上,默认在docker0
网桥的容器都可以使用容器名(代替ip地址,因为容器重启ip地址可能不同)进行通信。注意:使用容器名必须自定义网桥,不能使用默认docker0
注意:
在实际生产实践中,不应当使用默认的docker0
网桥进行通信。因为一台服务器上可能有多个项目(注意不是容器),不同项目有自己的mysql, tomcat, redis…,可能出现内网碰撞(视频里没听清这里说的是什么)。所以最佳实践是,一个项目对应一个网桥。
4.2 自定义网桥
网桥基本命令
# 1.1 查看网络信息
docker network ls
# 1.2 查看某一个网桥细节
docker network inspect 网桥名称
# 2. 创建一个网桥
docker network create -d bridge 网桥名称
docker network create 网桥名称
# 3.1 删除一个网桥
docker network rm 网桥名称
# 3.2 删除所有未被用到的网桥
docker network prune
指定使用网桥
a. 启动容器时,使用某个网桥
# 指定使用`info`网桥
docker run -d -p 8890:80 --name nginx001 --network info nginx
注意:一旦指定网桥后,–name指定名字就是主机名,多个容器指定在同一个网桥时,可以在任意一个容器中使用主机名与容器进行互通
[root@centos ~]# docker run -d -p 8890:80 --name nginx001 --network info nginx
c315bcc94e9ddaa36eb6c6f16ca51592b1ac8bf1ecfe9d8f01d892f3f10825fe
[root@centos ~]# docker run -d -p 8891:80 --name nginx002 --network info nginx
f8682db35dd7fb4395f90edb38df7cad71bbfaba71b6a4c6e2a3a525cb73c2a5
[root@centos ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f8682db35dd7 nginx "/docker-entrypoint.…" 3 seconds ago Up 2 seconds 0.0.0.0:8891->80/tcp nginx002
c315bcc94e9d nginx "/docker-entrypoint.…" 7 minutes ago Up 7 minutes 0.0.0.0:8890->80/tcp nginx001
b63169d43792 mysql:5.7.19 "docker-entrypoint.s…" 7 minutes ago Up 7 minutes 3306/tcp mysql_mysql.1.s75qe5kkpwwttyf0wrjvd2cda
[root@centos ~]# docker exec -it f8682db35dd7 /bin/bash
root@f8682db35dd7:/# curl http://nginx001
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
.....
b. 启动容器后,加入某个网桥
docker network connect 网络名 容器id/name
5. 数据卷
5.1 简介
数据卷 (Data Volume)
作用:用来实现容器中数据和宿主机中数据相互映射(同步),减少频繁cp
命令带来的麻烦。
重要性:是Docker的核心,使用的服务但凡需要持有数据
,就需要使用数据卷,如mysql,redis,es…拿MySQL数据库举例,服务器使用Docker运行MySQL数据库,数据都存放在Docker容器中,容器停止数据不会丢失(还存储在容器中),容器删除数据也跟着删除。如果使用了数据卷将容器内数据同步到宿主机,日后还可以再新建一个容器运行MySQL,实现数据备份、迁移等。
5.2 使用
注意:数据卷的使用必须在容器首次启动时设置
docker run ... -v 宿主机目录:容器目录 -v 宿主机目录:容器目录 ...
设置数据卷方式1:绝对路径
docker run ... -v 宿主机绝对路径:容器路径
注意:这种方式设置数据卷,在启动容器时将会把容器路径内的全部原始内容清空(需要区分哪些文件是原本就在容器内路径,哪些文件是初始化时才生成的,例如Tomcat的/webapps
和MySQL/var/lib/mysql
会有不一样的效果:Tomcat的webapps会被清空,MySQL不会,会初始化时生成的),始终以宿主机路径为主,之后两者文件内容同步
docker run ... -v 宿主机绝对路径:容器路径:ro
ro (read only): 如果在设置数据卷时设置ro,代表日后容器内路径是只读的,容器内无法创建新文件(无法进行写操作等)
设置数据卷方式2:别名
例如:
docker run ... -v aa:/usr/local/tomcat/webapps
- aa: 代表docker数据卷的别名
- 别名可以存在,也可以不存在,如果不存在,docker自动创建
- 别名默认的创建目录:linux:
/var/lib/docker/volums/
- 别名默认的创建目录:linux:
- 用别名方式设置数据卷,容器路径内容保留(使用绝对路径,不会保留容器中的原始数据)
5.3 别名
别名:一个别名代表Docker维护的一个数据卷
# 查看所有docker数据卷
docker volume ls
# 查看数据卷详细内容
# 注意inspect命令可以查看:容器、网桥、数据卷(优先级从高到低)
docker inspect 数据卷别名
# 如果有重名的现象,可以加上volume
docker volume inspect 数据卷别名
# 删除一个数据卷
docker volume rm 数据卷别名
# 提前创建一个数据卷
docker volume create 数据卷别名
6. Dockerfile
6.1 是什么
Dockerfile可以认为是Docker镜像的描述文件
,是由一系列命令和参数构成的脚本。主要作用是用来构建docker镜像
的构建文件。
Dockerfile是一种CS(Client-Server)架构:客户端写命令;服务端接收命令并执行
作用:
- 通过架构图可以看出通过DockerFile可以直接构建镜像
- Dockerfile类似对container使用的
commit
,但是Dockerfile比这个方式更加灵活强大。
为什么需要:
- 官方镜像确实足够好了,可以应付大部分应用程序。但如果想要有属于自己业务的镜像,此时需要自定义镜像
6.2 使用
粗略步骤:
-
在指定位置创建一个Dockerfile文件,并编写,注意该文件无需后缀,名字就是’Dockerfile’
-
通过Dockerfile构建镜像
docker build -t aa:1.0 .(指定Dockerfile文件所在位置)
6.3 解析过程
- 在
build
之后,Dockerfile文件所在目录(上下文/context)内的所有文件都会被打包发送给Docker Server进行构建,可以使用dockerIgnore来忽略某些文件。 - 会根据Dockerfile中的指令构建一个个临时镜像,当遇到最后一条指令时,形成最终镜像。
- 临时镜像(可关闭):会被缓存起来,用于加快构建速度(未来修改Dockerfile后构建时复用)
6.4 命令
命令官方文档🔗:https://docs.docker.com/engine/reference/builder/
0. 概览
保留字 | 作用 |
---|---|
FROM | 当前镜像是基于哪个镜像的 第一个指令必须是FROM |
MAINTAINER | 镜像维护者的姓名和邮箱地址(deprecated),建议使用LABEL 命令替代本命令 |
RUN | 构建镜像时需要运行的指令 |
EXPOSE | 当前容器对外暴露出的端口号,只做声明,不做局限 |
WORKDIR | 指定在创建容器后,终端默认登录进来的工作目录,一个落脚点,类似linux的cd |
ENV | 用来在构建镜像过程中设置环境变量,ENV key=value |
ADD | 将宿主机目录下的文件拷贝进镜像且ADD命令,且会自动处理URL和解压tar包(与COPY的区别),例如可以把一个zip文件的url地址写在ADD命令后,无需在宿主机上下载再传输 |
COPY | 类似于ADD,拷贝文件和目录到镜像中 将从构建上下文目录中<原路径>的文件/目录复制到新的一层的镜像内的<目标路径>位置 |
VOLUME | 容器数据卷,用于数据保存和持久化工作,和EXPOSE 一样,只做声明,不做局限 |
CMD | 指定一个容器启动时要运行的命令 Dockerfile中可以有多个CMD指令,但只有最后一个生效,CMD会被docker run之后的参数替换 |
ENTRYPOINT | 指定一个容器启动时要运行的命令 ENTRYPOINT的目的和CMD一样,都是在指定容器启动程序及其参数 |
基础知识:
- 每条保留字指令必须为大写,且后面都要跟至少一个参数
- 指令按照从上到下,顺序执行
- #表示注释
- 每条指令都会创建一个新的镜像层,并对镜像进行提交
1. FROM
-
基于哪个镜像进行构建新的镜像,在构建时会自动从docker hub拉取base镜像 必须作为Dockerfile的第一个指令出现
-
语法:
FROM <image> FROM <image>[:<tag>] 使用版本不写为latest FROM <image>[@<digest>] 使用摘要
2. MAINTAINER
-
镜像维护者的姓名和邮箱地址[废弃],建议使用
LABEL
命令: “TheLABEL
instruction is a much more flexible version of this and you should use it instead, as it enables setting any metadata you require, and can be viewed easily” -
语法:
MAINTAINER <name>
3. RUN
-
RUN指令将在当前映像之上的新层中执行任何命令并提交结果。生成的提交映像将用于Dockerfile中的下一步
-
语法:
RUN <command> (shell form, the command is run in a shell, which by default is /bin/sh -c on Linux or cmd /S /C on Windows) RUN echo hello RUN ["executable", "param1", "param2"] (exec form) RUN ["/bin/bash", "-c", "echo hello"]
-
比如我们有一个centos操作系统的镜像,但是里面什么软件都没有安装,此时我们可以为其预安装
vim
,然后再build成新的镜像FROM centos:latest RUN yum install -y vim
然后在上面这个Dockerfile文件所在目录执行:
docker build -t mycentos .
之后再使用交互模式运行该centos的镜像,已经安装了vim
dockert run -it mycentos
4. EXPOSE
-
用来
指定
构建的镜像在运行为容器时对外暴露的端口,起到声明
作用,并不是真正起到暴露的作用! -
此命令的作用就是为了增加配置文件的可读性,让查看该配置文件的人一目了然,使用/不使用改命令,在启动时都要使用
-p 8080:8080
来映射端口 -
语法:
EXPOSE 80/tcp 如果没有显示指定则默认暴露都是tcp EXPOSE 80/udp
5. CMD & ENTRYPOINT
ENTRYPOINT
指令,往往用于设置容器启动后的第一个命令
,这对一个容器来说往往是固定的。CMD
指令,往往用于设置容器启动的第一个命令的默认参数
,这对一个容器来说可以是变化的。- 使用JSON数组形式
7. Dockerfile构建应用
-
创建springboot项目
-
依赖
-
配置文件
server: port: 8989 servlet: context-path: /ems spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:locahost://my-mysql:3306/mybatis_plus?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false username: root password: root mybatis: mapper-locations: classpath:com/zzw/mapper/*.xml type-aliases-package: com.zzw.entity
注意:后续需要将
localhost
改成docker同一网桥下的mysql的容器别名
-
-
开发简单功能
- 连接数据库:使用学习MyBatis Plus时使用的数据库
mybatis_plus
,使用插件Easy Code
,直接根据数据库的表生成实体类 - RESTFUL接口
- 连接数据库:使用学习MyBatis Plus时使用的数据库
-
测试项目代码(跳过)
-
打包项目->jar
-
需要跳过测试
-
打包
-
-
将jar包传至服务器/宿主机
-
编写Dockerfile
FROM openjdk:8-jre ENV APP_PATH=/apps WORKDIR $APP_PATH ADD springbootdemo-0.0.1-SNAPSHOT.jar $APP_PATH/apps.jar EXPOSE 8989 ENTRYPOINT ["java", "-jar"] CMD ["apps.jar"]
-
build镜像
-
在宿主机下载jre-8
docker pull openjdk:8-jre
-
cd到apps目录
cd apps ls Dockerfile springbootdemo-0.0.1-SNAPSHOT.jar
-
build
docker build -t apps:1.0 .
-
查看宿主机镜像列表
docker images
-
-
启动容器(别忘了映射端口)
docker run -p 8989:8989 apps:1.0
-
测试,访问:localhost:8989/ems/users,发现报错,因为配套的服务(mysql)没有运行
-
在宿主机运行mysql镜像的容器
- 在宿主机下载mysql (我这里使用的是8.x版本的mysql)
- 运行mysql(注意确保宿主机mysql关闭,3306端口没有被占用)
# 1. linux (数据卷别名的方式没有测试是否可行) docker run -p 3306:3306 -d --name my-mysql -e MYSQL_ROOT_PASSWORD=root -v aa:/var/lib/mysql mysql # 2. mac docker run -p 3306:3306 -d --name my-mysql -e MYSQL_ROOT_PASSWORD=root mysql # 向docker容器传入sql文件 docker cp db.sql my-mysql:/root # 进入docker 容器 docker exec -it my-mysql bash # 进入数据库交互模式 mysql -uroot -p # 执行数据库初始化文件 source /root/db.sql
关于使用数据卷初始化mysql容器,注意有几种方式:
-
在容器启动后手动导入,也就是我这里使用的方式:在启动mysql时没有设置数据卷,而是启动容器后,将构建数据库的sql文件通过docker cp命令传入容器中,再进入容器,再进入mysql交互模式,使用
source
命令执行sql文件完成初始化。 -
在Spring Boot客户端连接Mysql容器时初始化数据库,可以参考使用flyway进行数据库版本控制一文,但是这依赖客户端的能力。
-
/docker-entrypoint-initdb.d:
- https://stackoverflow.com/questions/25920029/setting-up-mysql-and-importing-dump-within-dockerfile
- https://jiuto.github.io/jiuto_blog/guide/else/docker_initSql.html
- https://juejin.cn/post/6844904169623519240
-
Docker Hub也为我们提供了一种方式,但个人觉得本质就是第一种,不过是把命令都写在
run
之后的一行中$ docker exec some-mysql sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > /some/path/on/your/host/all-databases.sql
https://hub.docker.com/_/mysql
- 启动成功
- 至此,我们的Springboot应用和MySQL已经都正常启动
-
配置网桥,让mysql和springboot可以在同一个网桥上通信
-
创建网桥
docker network create app
-
设置MySQL容器连接
app
网桥
# mysql连接网桥,使用容器名或id docker network connect app 8e0
- 查看
app
网桥,发现网桥中mysql添加成功
docker inspect app
-
-
此时我们的SpringBoot应用已经无法通过localhost访问mysql数据库,而是应当通过
app
网桥,访问mysql容器(url中写mysql容器的名字)-
修改数据源的url
-
重新打包,并把jar包传至主机原位置
-
-
构建Dockerfile
docker build -t apps:1.1 .
-
使用容器启动新构建的镜像
apps:1.1
,注意network可以在启动时设置,也可以启动后设置,这里直接启动docker run -d -p 8989:8989 --name apps apps:1.1
使用命令查看
apps:1.1
的日志docker logs -f 容器id
apps
已经正常启动 -
设置
app
容器连接app
网桥docker network connect app c8
-
在宿主机访问:http://localhost:8989/ems/users,测试成功