Docker学习笔记

文章目录

  • 1. 简介
    • 1.1 什么是Docker
    • 1.2 Docker起源
    • 1.3 优势
    • 1.4 Docker和虚拟机区别
    • 1.5 核心架构和概念
      • 1.镜像 (Image)
        • 是什么
        • 为什么这么大
        • UnionFS
        • 镜像底层原理(了解即可)
      • 2.容器 (Container)
      • 3.仓库 (Repository)
      • 4.DockerFile
      • 5.tar
      • 6.数据卷(Docker Volume)
    • 1.6 执行流程
  • 2. 使用
    • 2.1 Docker引擎安装
      • a. Mac
      • b. Centos7.x
        • 安装docker(centos7.x)
        • bash安装(通用所有平台)
    • 2.2 配置阿里云镜像加速
    • 2.3 常用命令
      • 辅助命令
      • Images 镜像命令
      • Container 容器命令
  • 3. Docker安装常用服务
    • a. MySQL
      • 基本命令
      • 数据备份
    • b. Redis
    • c. Nginx
  • 4. 高级网络配置
    • 4.1 说明
    • 4.2 自定义网桥
      • 网桥基本命令
      • 指定使用网桥
  • 5. 数据卷
    • 5.1 简介
    • 5.2 使用
    • 5.3 别名
  • 6. Dockerfile
    • 6.1 是什么
    • 6.2 使用
    • 6.3 解析过程
    • 6.4 命令
      • 0. 概览
      • 1. FROM
      • 2. MAINTAINER
      • 3. RUN
      • 4. EXPOSE
      • 5. CMD & ENTRYPOINT
  • 7. Dockerfile构建应用

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起源

  1. 历史

Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在 GitHub 上进行维护。Docker 项目后来还加入了 Linux 基金会,并成立推动 开放容器联盟(OCI)。

  1. 开源

Docker 自开源后受到广泛的关注和讨论,至今其 GitHub 项目 已经超过 5 万 7 千个星标和一万多个 fork。甚至由于 Docker 项目的火爆,在 2013 年底,dotCloud 公司决定改名为 Docker。Docker 最初是在 Ubuntu 12.04 上开发实现的;Red Hat 则从 RHEL 6.5 开始对 Docker 进行支持;Google 也在其 PaaS 产品中广泛应用 Docker。

  1. 本质

Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 OverlayFS 类的 Union FS 等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。

1.3 优势

  • 问题1:在开发的时候,在本机测试环境可以跑,生产环境跑不起来

    这里我们拿java Web应用程序举例,我们一个java Web应用程序涉及很多东西,比如jdk、tomcat、mysql等软件环境(配置环境非常繁琐)。当这些其中某一项版本不一致的时候,可能就会导致应用程序跑不起来这种情况。Docker则将程序以及使用软件环境直接打包在一起,无论在那个机器上保证了环境一致。

    优势1: 环境一致,高效迁移

  • 问题2:服务器自己的程序挂了,结果发现是别人程序出了问题把内存吃完了,自己程序因为内存不够就挂了

    这种也是一种比较常见的情况,如果你的程序重要性不是特别高的话,公司基本上不可能让你的程序独享一台服务器的,这时候你的服务器就会跟公司其他人的程序共享一台服务器,所以不可避免地就会受到其他程序的干扰,导致自己的程序出现问题。Docker就很好解决了环境隔离的问题,别人程序不会影响到自己的程序。

    优势2:(操作系统)进程级隔离,容器独立

    Docker学习笔记_第1张图片
  • 问题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学习笔记_第2张图片

由上图可知,Docker省略了Hypervisor(虚拟层)和Guest OS(来宾操作系统)。比较上面两张图,我们发现虚拟机是携带操作系统,本身很小的应用程序却因为携带了操作系统而变得非常大,很笨重。Docker是不携带操作系统的,所以Docker的应用就非常的轻巧。另外在调用宿主机的CPU、磁盘等等这些资源的时候,拿内存举例,虚拟机是利用Hypervisor去虚拟化内存,整个调用过程是虚拟内存->虚拟物理内存->真正物理内存,但是Docker是利用Docker Engine去调用宿主的的资源,这时候过程是虚拟内存->真正物理内存。

传统虚拟机 Docker容器
磁盘占用 几个GB到几十个GB左右 几十MB到几百MB左右
CPU内存占用 虚拟操作系统非常占用CPU和内存 Docker引擎占用极低
启动速度 (从开机到运行项目)几分钟 (从开启容器到运行项目)几秒
安装管理 需要专门的运维技术 安装、管理方便
应用部署 每次部署都费时费力 从第二次部署开始轻松简捷
耦合性 多个应用服务安装到一起,容易互相影响 每个应用服务一个容器,达成隔离
系统依赖 需求相同或相似的内核,目前推荐是Linux

1.5 核心架构和概念

Docker学习笔记_第3张图片

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 执行流程

  1. 在收到run命令后,先去本地仓库查看是否有对应的镜像,有则运行
  2. 如果没有在本地仓库找到,就去Docker Hub中查找该镜像并下载,然后运行

Docker学习笔记_第4张图片

2. 使用

2.1 Docker引擎安装

a. Mac

官网下载地址:https://docs.docker.com/get-docker/

选择Mac Intel版本,下载,安装,授权,启动 Docker Desktop。

Docker学习笔记_第5张图片

可在命令行查看Docker版本:

docker version

Docker学习笔记_第6张图片

注意Docker分为客户端和服务端,服务端引擎用来运行容器,客户端引擎用来接受、解析命令,并发送给服务端。

测试Docker安装是否成功

$ docker run hello-world

Docker学习笔记_第7张图片

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 容器命令

映射端口号:

oyLCnF

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文件形式

  1. 使用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
  1. 使用软件应用,如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 就创建了在主机和所有容器之间一个虚拟共享网络。

总结:

  1. 默认docker在创建容器时,将所有容器都连接到docker0网桥上,默认在docker0网桥的容器都可以使用容器内ip地址进行通信
  2. 默认docker在创建容器时,将所有容器都连接到docker0网桥上,默认在docker0网桥的容器都可以使用容器名(代替ip地址,因为容器重启ip地址可能不同)进行通信。注意:使用容器名必须自定义网桥,不能使用默认docker0

注意:

在实际生产实践中,不应当使用默认的docker0网桥进行通信。因为一台服务器上可能有多个项目(注意不是容器),不同项目有自己的mysql, tomcat, redis…,可能出现内网碰撞(视频里没听清这里说的是什么)。所以最佳实践是,一个项目对应一个网桥。

Docker学习笔记_第8张图片

Docker学习笔记_第9张图片

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 宿主机目录:容器目录 ...
Docker学习笔记_第10张图片

设置数据卷方式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/
  • 用别名方式设置数据卷,容器路径内容保留(使用绝对路径,不会保留容器中的原始数据)

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)架构:客户端写命令;服务端接收命令并执行

Docker学习笔记_第11张图片

作用:

  • 通过架构图可以看出通过DockerFile可以直接构建镜像
  • Dockerfile类似对container使用的commit,但是Dockerfile比这个方式更加灵活强大。

为什么需要:

  • 官方镜像确实足够好了,可以应付大部分应用程序。但如果想要有属于自己业务的镜像,此时需要自定义镜像

6.2 使用

粗略步骤:

  1. 在指定位置创建一个Dockerfile文件,并编写,注意该文件无需后缀,名字就是’Dockerfile’

  2. 通过Dockerfile构建镜像

    docker build -t aa:1.0 .(指定Dockerfile文件所在位置)
    

6.3 解析过程

Docker学习笔记_第12张图片

  • 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命令: “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>
    

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构建应用

  1. 创建springboot项目

    1. 依赖

    2. 配置文件

      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的容器别名

  2. 开发简单功能

    1. 连接数据库:使用学习MyBatis Plus时使用的数据库mybatis_plus,使用插件Easy Code,直接根据数据库的表生成实体类
    2. RESTFUL接口
  3. 测试项目代码(跳过)

  4. 打包项目->jar

    1. 需要跳过测试

      Docker学习笔记_第13张图片
    2. 打包

  5. 将jar包传至服务器/宿主机

  6. 编写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"]
    
  7. build镜像

    1. 在宿主机下载jre-8

      docker pull openjdk:8-jre
      
    2. cd到apps目录

      cd apps
      ls
      Dockerfile                        springbootdemo-0.0.1-SNAPSHOT.jar
      
    3. build

      docker build -t apps:1.0 .
      
    4. 查看宿主机镜像列表

      docker images
      

      Docker学习笔记_第14张图片

  8. 启动容器(别忘了映射端口)

docker run -p 8989:8989 apps:1.0
  1. 测试,访问:localhost:8989/ems/users,发现报错,因为配套的服务(mysql)没有运行

  2. 在宿主机运行mysql镜像的容器

    1. 在宿主机下载mysql (我这里使用的是8.x版本的mysql)
    2. 运行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容器,注意有几种方式:

    1. 在容器启动后手动导入,也就是我这里使用的方式:在启动mysql时没有设置数据卷,而是启动容器后,将构建数据库的sql文件通过docker cp命令传入容器中,再进入容器,再进入mysql交互模式,使用source命令执行sql文件完成初始化。

    2. Spring Boot客户端连接Mysql容器时初始化数据库,可以参考使用flyway进行数据库版本控制一文,但是这依赖客户端的能力。

    3. /docker-entrypoint-initdb.d:

      1. https://stackoverflow.com/questions/25920029/setting-up-mysql-and-importing-dump-within-dockerfile
      2. https://jiuto.github.io/jiuto_blog/guide/else/docker_initSql.html
      3. https://juejin.cn/post/6844904169623519240
    4. Docker Hub也为我们提供了一种方式,但个人觉得本质就是第一种,不过是把命令都写在run之后的一行中 Docker学习笔记_第15张图片

      $ 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

    1. 启动成功Docker学习笔记_第16张图片
    2. 至此,我们的Springboot应用和MySQL已经都正常启动 PN67XC
  3. 配置网桥,让mysql和springboot可以在同一个网桥上通信

    1. 创建网桥

      docker network create app
      
    2. 设置MySQL容器连接app网桥

    # mysql连接网桥,使用容器名或id
    docker network connect app 8e0
    
    1. 查看app网桥,发现网桥中mysql添加成功
    docker inspect app
    

    Docker学习笔记_第17张图片

  4. 此时我们的SpringBoot应用已经无法通过localhost访问mysql数据库,而是应当通过app网桥,访问mysql容器(url中写mysql容器的名字)

    1. 修改数据源的url

      Docker学习笔记_第18张图片

    2. 重新打包,并把jar包传至主机原位置

  5. 构建Dockerfile

    docker build -t apps:1.1 .
    
  6. 使用容器启动新构建的镜像apps:1.1,注意network可以在启动时设置,也可以启动后设置,这里直接启动

    docker run -d -p 8989:8989 --name apps apps:1.1
    

    使用命令查看apps:1.1的日志

    docker logs -f 容器id
    

    Docker学习笔记_第19张图片

    apps已经正常启动

  7. 设置app容器连接app网桥

    docker network connect app c8
    
  8. 在宿主机访问:http://localhost:8989/ems/users,测试成功

    Docker学习笔记_第20张图片

你可能感兴趣的:(分布式系统,操作系统,docker,学习,java)