官方文档地址:https://www.docker.com/get-started
中文参考手册:https://docker_practice.gitee.io/zh-cn/
本笔记参考B站视频:【编程不良人】Docker&Docker-Compose 实战!
Docker中文文档:https://yeasy.gitbook.io/docker_practice/
官方定义:
帮助开发人员和开发团队构建和发布应用
为开发人员和团队提供一个完整容器(container)解决方案
docker是一个容器技术
运行你的应用不需要你的环境(早起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:在开发的时候,在本机测试环境可以跑,生产环境跑不起来
这里我们拿java Web应用程序举例,我们一个java Web应用程序涉及很多东西,比如jdk、tomcat、mysql等软件环境(配置环境非常繁琐)。当这些其中某一项版本不一致的时候,可能就会导致应用程序跑不起来这种情况。Docker则将程序以及使用软件环境直接打包在一起,无论在那个机器上保证了环境一致。
优势1: 环境一致,高效迁移
问题2:服务器自己的程序挂了,结果发现是别人程序出了问题把内存吃完了,自己程序因为内存不够就挂了
这种也是一种比较常见的情况,如果你的程序重要性不是特别高的话,公司基本上不可能让你的程序独享一台服务器的,这时候你的服务器就会跟公司其他人的程序共享一台服务器,所以不可避免地就会受到其他程序的干扰,导致自己的程序出现问题。Docker就很好解决了环境隔离的问题,别人程序不会影响到自己的程序。
优势2:(操作系统)进程级隔离,容器独立
问题3:公司要搞一个活动,可能会有大量的流量进来,公司需要再多部署几十台服务器
在没有Docker的情况下,要在几天内部署几十台服务器,这对运维来说是一件非常折磨人的事,而且每台服务器的环境还不一定一样,就会出现各种问题,最后部署地头皮发麻。用Docker的话,我只需要将程序打包到镜像,你要多少台服务,我就给力跑多少容器,极大地提高了部署效率。
优势3: 镜像机制,便于部署
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 |
简而言之就是,除了这个软件本身,他的运行环境占了非常多空间。拿Tomcat举例,除了Tomcat本身的代码,还有Tomcat的依赖、基础Linux操作系统和JDK。
StackOverflow:Why are Docker container images so large?
如此一来,不仅用户本地仓库的存储压力非常大,Docker Hub的存储压力也非常大。Docker使用了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为我们一层层下载,而不是一次下载一个独立的镜像。
镜像每次运行之后就是产生一个容器,就是正在运行的镜像,特点就是可读可写
用来存放镜像的位置,类似于maven仓库,也是镜像下载和上传的位置。和Maven一样,Docker官方提供了一个远程Web界面仓库:Docker Hub
;同时为了避免重复下载,本地还有一个本地仓库,存储使用过的镜像。
docker生成镜像配置文件,用来书写自定义镜像的一些配置
(第六章节有详细讲)
一个对镜像打包的文件,日后可以还原成镜像
用来实现容器中数据和宿主机中数据相互映射(同步),减少频繁cp
命令带来的麻烦。
(第五章节有详细讲)
官网下载地址:https://docs.docker.com/get-docker/
选择Mac Intel版本,下载,安装,授权,启动 Docker Desktop。
可在命令行查看Docker版本:
docker version
注意Docker分为客户端和服务端,服务端引擎用来运行容器,客户端引擎用来接受、解析命令,并发送给服务端。
测试Docker安装是否成功
$ docker run hello-world
卸载原始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
在测试或开发环境中 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
在国内下载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
[root@localhost ~]# docker info
..........
127.0.0.0/8
Registry Mirrors:
'https://lz2nib3q.mirror.aliyuncs.com/'
Live Restore Enabled: false
Product License: Community Engine
1.安装完成辅助命令
docker version -------------------------- 查看docker的信息
docker info -------------------------- 查看更详细的信息
docker --help -------------------------- 帮助命令
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镜像
映射端口号:
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
关于如何启动常用服务,Docker Hub官网通常都有教程。
注意所有需要持有/存储数据的服务,都要看Docker Hub的文档,查找数据卷的映射地址。
这里以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文件形式
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
# 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
# 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
中文文档: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 就创建了在主机和所有容器之间一个虚拟共享网络。
总结:
docker0
网桥上,默认在docker0
网桥的容器都可以使用容器内ip地址进行通信docker0
网桥上,默认在docker0
网桥的容器都可以使用容器名(代替ip地址,因为容器重启ip地址可能不同)进行通信。注意:使用容器名必须自定义网桥,不能使用默认docker0
注意:
在实际生产实践中,不应当使用默认的docker0
网桥进行通信。因为一台服务器上可能有多个项目(注意不是容器),不同项目有自己的mysql, tomcat, redis…,可能出现内网碰撞(视频里没听清这里说的是什么)。所以最佳实践是,一个项目对应一个网桥。
# 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
数据卷 (Data Volume)
作用:用来实现容器中数据和宿主机中数据相互映射(同步),减少频繁cp
命令带来的麻烦。
重要性:是Docker的核心,使用的服务但凡需要持有数据
,就需要使用数据卷,如mysql,redis,es…拿MySQL数据库举例,服务器使用Docker运行MySQL数据库,数据都存放在Docker容器中,容器停止数据不会丢失(还存储在容器中),容器删除数据也跟着删除。如果使用了数据卷将容器内数据同步到宿主机,日后还可以再新建一个容器运行MySQL,实现数据备份、迁移等。
注意:数据卷的使用必须在容器首次启动时设置
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
/var/lib/docker/volums/
别名:一个别名代表Docker维护的一个数据卷
# 查看所有docker数据卷
docker volume ls
# 查看数据卷详细内容
# 注意inspect命令可以查看:容器、网桥、数据卷(优先级从高到低)
docker inspect 数据卷别名
# 如果有重名的现象,可以加上volume
docker volume inspect 数据卷别名
# 删除一个数据卷
docker volume rm 数据卷别名
# 提前创建一个数据卷
docker volume create 数据卷别名
Dockerfile可以认为是Docker镜像的描述文件
,是由一系列命令和参数构成的脚本。主要作用是用来构建docker镜像
的构建文件。
Dockerfile是一种CS(Client-Server)架构:客户端写命令;服务端接收命令并执行
作用:
commit
,但是Dockerfile比这个方式更加灵活强大。为什么需要:
粗略步骤:
在指定位置创建一个Dockerfile文件,并编写,注意该文件无需后缀,名字就是’Dockerfile’
通过Dockerfile构建镜像
docker build -t aa:1.0 .(指定Dockerfile文件所在位置)
build
之后,Dockerfile文件所在目录(上下文/context)内的所有文件都会被打包发送给Docker Server进行构建,可以使用dockerIgnore来忽略某些文件。命令官方文档:https://docs.docker.com/engine/reference/builder/
保留字 | 作用 |
---|---|
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一样,都是在指定容器启动程序及其参数 |
基础知识:
基于哪个镜像进行构建新的镜像,在构建时会自动从docker hub拉取base镜像 必须作为Dockerfile的第一个指令出现
语法:
FROM <image>
FROM <image>[:<tag>] 使用版本不写为latest
FROM <image>[@<digest>] 使用摘要
镜像维护者的姓名和邮箱地址[废弃],建议使用LABEL
命令: “The LABEL
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>
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
用来指定
构建的镜像在运行为容器时对外暴露的端口,起到声明
作用,并不是真正起到暴露的作用!
此命令的作用就是为了增加配置文件的可读性,让查看该配置文件的人一目了然,使用/不使用改命令,在启动时都要使用-p 8080:8080
来映射端口
语法:
EXPOSE 80/tcp 如果没有显示指定则默认暴露都是tcp
EXPOSE 80/udp
ENTRYPOINT
指令,往往用于设置容器启动后的第一个命令
,这对一个容器来说往往是固定的。CMD
指令,往往用于设置容器启动的第一个命令的默认参数
,这对一个容器来说可以是变化的。创建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
,使用插件Easy Code
,直接根据数据库的表生成实体类测试项目代码(跳过)
打包项目->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镜像
启动容器(别忘了映射端口)
docker run -p 8989:8989 apps:1.0
测试,访问:localhost:8989/ems/users,发现报错,因为配套的服务(mysql)没有运行
在宿主机运行mysql镜像的容器
# 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
配置网桥,让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容器的名字)
构建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,测试成功