Docker容器
1:什么是容器
容器就是在隔离的环境运行的一个进程,如果进程停止,容器就会销毁。隔离的环境拥有自己的系统文件,ip地址,主机名等。
程序:代码,命令
进程:正在运行的程序
容器的系统文件就是宿主机的某一个目录
2:容器和虚拟化的区别
Linux容器技术,容器虚拟化和kvm虚拟化的区别
kvm虚拟化:需要硬件的支持,需要模拟硬件,可以运行不同的操作系统,启动时间分钟级
Linux开机启动流程:
BIOS开机硬件自检
根据BIOS设置的优先启动项boot 网卡 硬盘 U盘 光驱
读取mbr引导 UEFI(gpt分区) mbr硬盘分区信息,内核加载路径
加载内核
启动第一个进程init systemd
系统初始化完成
运行服务
。。。
容器:共用宿主机内核,容器的第一个进程直接运行服务,损耗少,启动快,性能高
容器虚拟化:不需要硬件的支持,不需要模拟硬件,共用宿主机的内核,启动时间秒级(没有开机流程)
- 虚拟机(重量级)
- 基础设施(Infrastructure):它可以是你的个人电脑,数据中心的服务器,或者是云主机。
- 虚拟机管理系统(Hypervisor):利用Hypervisor,可以在主操作系统之上运行多个不同的从操作系统。类型1的Hypervisor有支持MacOS的HyperKit,支持Windows的Hyper-V、Xen以及KVM。类型2的Hypervisor有VirtualBox和VMWare workstation。
- 客户机操作系统(Guest Operating System):假设你需要运行3个相互隔离的应用,则需要使用Hypervisor启动3个客户机操作系统,也就是3个虚拟机。这些虚拟机都非常大,也许有700MB,这就意味着它们将占用2.1GB的磁盘空间。更糟糕的是,它们还会消耗很多CPU和内存。
- 各种依赖:每一个客户机操作系统都需要安装许多依赖。如果你的应用需要连接PostgreSQL的话,则需要安装libpq-dev;如果你使用Ruby的话,应该需要安装gems;如果使用其他编程语言,比如Python或者Node.js,都会需要安装对应的依赖库。
- 应用:安装依赖之后,就可以在各个客户机操作系统分别运行应用了,这样各个应用就是相互隔离的
- 容器(轻量级)
- 基础设施(Infrastructure):它可以是你的个人电脑,数据中心的服务器,或者是云主机。
- 主操作系统(Host Operating System):所有主流的Linux发行版都可以运行Docker。对于MacOS和Windows,也有一些办法”运行”Docker。
- Docker守护进程(Docker Daemon):Docker守护进程取代了Hypervisor,它是运行在操作系统之上的后台进程,负责管理Docker容器。
- 各种依赖:对于Docker,应用的所有依赖都打包在Docker镜像中,Docker容器是基于Docker镜像创建的。
- 应用:应用的源代码与它的依赖都打包在Docker镜像中,不同的应用需要不同的Docker镜像。不同的应用运行在不同的Docker容器中,它们是相互隔离的。
总结:
(1) 与宿主机使用同一个内核,性能损耗小;
(2) 不需要指令集模拟;
(3) 容器可以在cpu核心的本地运行指令,不需要任何专门的解释机制;
(4) 避免了准虚拟化和系统调用替换中的复杂性;
(5) 轻量级隔离,在隔离的同时还提供共享机制,以实现容器与宿主机的资源共享
3:什么是 Docker
Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在 GitHub 上进行维护。Docker 项目后来还加入了 Linux 基金会,并成立推动 开放容器联盟(OCI)。
Docker 自开源后受到广泛的关注和讨论,至今其 GitHub 项目已经超过 4 万 6 千个星标和一万多个 fork。甚至由于 Docker 项目的火爆,在 2013 年底,dotCloud 公司决定改名为 Docker。Docker 最初是在 Ubuntu 12.04 上开发实现的;Red Hat 则从 RHEL 6.5 开始对 Docker 进行支持;Google 也在其 PaaS 产品中广泛应用 Docker。
Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于 LXC,从 0.7 版本以后开始去除 LXC,转而使用自行开发的 libcontainer,从 1.11 开始,则进一步演进为使用 runC 和 containerd。
Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。
传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
chroot技术
新建一个子系统(拥有自己完整的系统文件)
参考资料:https://www.ibm.com/developerworks/cn/linux/l-cn-chroot/
4:为什么要使用 Docker
作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。
更高效的利用系统资源
由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。
更快速的启动时间
传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。
一致的运行环境
开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 「这段代码在我机器上没问题啊」 这类问题。
持续交付和部署
对开发和运维(DevOps)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。
使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合 持续集成(Continuous Integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 持续部署(Continuous Delivery/Deployment) 系统进行自动部署。
而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。
更轻松的迁移
由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。
更轻松的维护和扩展
Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。
对比传统虚拟机总结
特性 | 容器 | 虚拟机 |
---|---|---|
启动 | 秒级 | 分钟级 |
运行形态 | 直接运行于宿主机的内核上,不同容器共享同一个liunx内核 | 运行于Hypervisior上 |
硬盘使用 | 一般为 MB | 一般为 GB |
性能 | 接近原生 | 弱于 |
系统支持量 | 单机支持上千个容器 | 一般几十个 |
5:docker不是完整的虚拟化
不是一个系统而是一个进程组,docker是一个进程(父进程),docker里有很多进程(子进程)。
注意:
千万不要误以为Docker就是虚拟机。其实Docker不是虚拟机!
docker是前台运行的需要hold住,也就是command是在前台停留的
docker没有systemd概念
6:docker容器
docker是通过进程虚拟化技术(namespaces及cgroups cpu、内存、磁盘io等)来提供容器的资源隔离与安全保障等。由于Docker通过操作系统层的虚拟化实现隔离,所以Docker容器在运行时,不需要类似虚拟机(VM)额外的操作系统开销,提高资源利用率。
namespace:资源隔离
cgroups:进程的资源限制
kvm:虚拟磁盘文件,资源隔离;资源限制
chroot:提供文件系统
docker:初期把lxc二次开发,libcontainer
docker的主要目标是"Build,Ship and Run any App,Angwhere",构建,运输,处处运行
Build once,Run anywhere 一次构建,处处运行
部署服务,环境问题
docker是一种软件的打包技术
构建:做一个docker镜像
运输:docker pull
运行:启动一个容器
每一个容器,他都有自己的系统文件rootfs
kvm解决了硬件和操作系统之间的依赖
kvm独立的虚拟磁盘,xml配置文件
docker解决了软件和操作系统环境之间的依赖,能够让独立服务或应用程序在不同环境中,得到相同的运行结果。
docker镜像有自己的文件系统。
容器启动运行流程
7:Docker镜像
镜像原理(镜像文件系统原理)
Docker镜像是有文件系统叠加而成,最低端是一个引导文件系统,即bootfs。
Docker镜像的第二层是root文件系统rootfs,它位于引导文件系统bootfs之上。rootfs可以使一种或多种 操作系统,传统的Linux系统引导时root文件系统最先以只读的方式加载,引导结束并完成完整性检查后会切换到读写模式,Docker中root文件系统永远只能是只读状态,并且Docker利用联合加载(unionmount)技术又会再root文件系统上加载更多的只读文件系统。
当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
Docker将这种文件系统称为镜像,镜像可以叠加,在下方的镜像称为父镜像,最底层的镜像称为基础镜像,当使用一个镜像启动容器时,Docker会在该镜像的最顶层加载一个读写文件系统,在Docker中运行的程序就在这个读写层。
写时复制:Docker第一次启动一个容器时,初始的读写层是空的。当文件系统发生变化时,这些变化都会应用到这一层上,如果要修改一个文件,此文件会从下面的只读层复制到读写层,此文件的只读版本依然存在,但是会被读写层的副本隐藏。
联合加载:指一次同时加载多个文件系统(镜像),但是再外面只能看到一个文件系统,联合加载会将各层文件系统叠加到一起,这样最终文件系统会包含所有底层的文件和目录。
当一个容器启动后,容器会被移动到内存中,引导文件系统则会被卸载,以留出更多的内存公initrd磁盘镜像使用。
简单来说docker容器是由镜像的只读层(只读层以下的都归为只读层)和容器赋予的可读可写层组成的,如下图
8:镜像、容器、仓库
镜像
Docker的镜像是一个只读的模板,一个独立的文件系统,包括运行容器所需的数据,可以用来创建新的容器。
Docker的镜像相当于centos操作系统的iso安装包,ISO镜像的内容是固定的,不会发生改变,可以重复使用。
容器
Docker镜像生成出来的东西,我们就叫他它“容器”
仓库
仓库支持的操作类似git,当用户创建了自己的镜像之后就可以使用 push 命令将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上 pull 下来就可以了。
类似于,将某项目模块打成jar包,然后上传到私有或者公有的maven仓库,然后在其他的所有支持maven环境的机器上,都可以拉取这个jar进行使用。
Docker Hub是由Docker公司负责维护的公共注册中心,包含大量的容器镜像,Docker工具默认从这个公共镜像库下载镜像。地址:https://hub.docker.com/explore
简单来说
镜像:启动容器的模板
仓库:保存镜像的地方
容器:对外提供服务的实例
实验环境
主机名 | IP地址 | 备注 |
---|---|---|
docker | 192.168.188.70 |
Docker安装
环境准备
[root@docker ~]# yum install -y wget
[root@docker ~]# cd /etc/yum.repos.d/
[root@docker yum.repos.d]# mkdir bak
[root@docker yum.repos.d]# mv ./* bak/
# 准备Base源
[root@docker yum.repos.d]# wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
[root@docker ~]# yum makecache
开始安装
打开阿里源的docker-ce源里面也有教程
# step 1: 安装必要的一些系统工具
[root@docker ~]# yum install -y yum-utils device-mapper-persistent-data lvm2
# Step 2: 添加软件源信息
[root@docker ~]# yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Step 3
[root@docker ~]# sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
# Step 4: 更新并安装Docker-CE
[root@docker ~]# yum makecache fast
[root@docker ~]# yum -y install docker-ce-18.06.3.ce-3.el7
# Step 4: 开启Docker服务
[root@docker ~]# service docker start
[root@docker ~]# systemctl enable docker
# 注意:
# 官方软件源默认启用了最新的软件,您可以通过编辑软件源的方式获取各个版本的软件包。例如官方并没有将测试版本的软件源置为可用,您可以通过以下方式开启。同理可以开启各种测试版本等。
# vim /etc/yum.repos.d/docker-ce.repo
# 将[docker-ce-test]下方的enabled=0修改为enabled=1
#
# 安装指定版本的Docker-CE:
# Step 1: 查找Docker-CE的版本:
# yum list docker-ce.x86_64 --showduplicates | sort -r
# Loading mirror speeds from cached hostfile
# Loaded plugins: branch, fastestmirror, langpacks
# docker-ce.x86_64 17.03.1.ce-1.el7.centos docker-ce-stable
# docker-ce.x86_64 17.03.1.ce-1.el7.centos @docker-ce-stable
# docker-ce.x86_64 17.03.0.ce-1.el7.centos docker-ce-stable
# Available Packages
# Step2: 安装指定版本的Docker-CE: (VERSION例如上面的17.03.0.ce.1-1.el7.centos)
# sudo yum -y install docker-ce-[VERSION]
镜像加速(可以指向个人仓库或企业仓库)
这里采用阿里云的镜像加速,https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
mkdir -p /etc/docker
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://2l058lvr.mirror.aliyuncs.com"]
}
EOF
systemctl daemon-reload
systemctl restart docker
Docker基本操作
官网操作文档:https://docs.docker.com/engine/reference/commandline/docker/
查看docker版本号
[root@docker ~]# docker version # 查看docker版本号
查看镜像信息 images
# 查看镜像信息 images
[root@docker ~]# docker images ls
REPOSITORY TAG IMAGE ID CREATED SIZE
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest feb5d9fea6a5 7 months ago 13.3kB
# 该表格包含了5列,含义如下:
1. REPOSITORY:镜像所属仓库名称。
2.TAG:镜像标签。默认是latest,表示最新。
3.IMAGE ID:镜像ID,表示镜像唯一标识。
4.CREATED:镜像创建时间。
5.SIZE:镜像大小。
查看容器 ps
# 查看当前启动的容器,二选一
[root@docker ~]# docker container ls
[root@docker ~]# docker ps(只列出运行的容器)
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 列出所有容器,包括未运行的容器
[root@docker ~]# docker ps -a
# 该表格包含了七列,含义如下:
1.CONTAINER_ID:表示容器ID。
2.IMAGE:表示镜像名称。
3.COMMAND:表示启动容器时运行的命令。运行的时候必须使用,不然可能起不来
4.CREATED:表示容器的创建时间。
5.STATUS:表示容器运行的状态。Up表示运行中,Exited表示已停止。
6.PORTS:表示容器对外的端口号。
7.NAMES:表示容器名称。该名称默认由Docker自动生成,也可使用docker run命令的–name选项自行指定。
镜像拉取centos和ubuntu
# pull命令可以从远程仓库拉取镜像,如果本地仓库已经存在该镜像,则会更新,默认源是Docker Hub,不指定版本时默认为latest版本
[root@docker ~]# docker pull centos:7 # 拉取指定版本的centos
[root@docker ~]# docker pull ubuntu # 拉取ubuntu
[root@docker ~]# docker pull hello-world #最小的例子,一般用来监测是否正常安装成功docker
容器运行镜像
[root@docker ~]# docker run hello-world
# 运行centos:7
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest ba6acccedd29 7 months ago 72.8MB
hello-world latest feb5d9fea6a5 7 months ago 13.3kB
centos 7 eeb6ee3f44bd 8 months ago 204MB
[root@docker ~]# docker run eeb6ee3f44bd # docker run 指定镜像ID 容器运行镜像,前台运行。(备注生产中调试用)
[root@docker ~]# docker ps # 运行完上一条命令查看容器,发现没有
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@docker ~]# docker ps -a # 要指定command,可以看到/bin/bash了一下,容器刷新一下就挂掉了
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
45861729009d eeb6ee3f44bd "/bin/bash" 22 seconds ago Exited (0) 20 seconds ago nifty_hermann
354e1fb5eb93 hello-world "/hello" 36 hours ago Exited (0) 36 hours ago infallible_aryabhata
# 正确使用
[root@docker ~]# docker run -it eeb6ee3f44bd /bin/bash #分配伪终端启动容器。如果默认命令可以hold住退出容器,容器不消亡。如果hold 不住,则容器消亡。
[root@9954d6e3cb32 /]# # 可以看到主机名变成了容器ID,证明成功进入
# 在容器内使用df -h 、free -h、lscpu等命令查看得到的都是宿主机的资源
# 可以使用ip a查看ip进行判断
[root@9954d6e3cb32 /]# yum -y install net-tools
[root@9954d6e3cb32 /]# ifconfig # 会发现IP地址是172开头的,同时宿主机也有一张网卡容器的IP地址就是来源它;如果容器存活,宿主机还有一张对应的网卡,反之
[root@9954d6e3cb32 /]# exit # 退出容器,不存活
# 错误后台使用
[root@docker ~]# docker run -d eeb6ee3f44bd /bin/bash # 输出结果看起来启动了,实际并没有;有一些可以不接command,因为容器里面已经写好
68928d357d8bd801d4442df50d5c3f28c2a245c3e8864268c74055c8aafe1b74
[root@docker ~]# docker ps # 因为后台运行/bin/bash就是刷新一下重新分配一个终端,所以容器就挂掉了
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 应挂载文件与开放端口
[root@docker ~]# docker run -d eeb6ee3f44bd # 后台运行镜像容器,一般还需要开放端口和挂载文件等
删除容器
# 单一删除
[root@docker ~]# docker ps -a
[root@docker ~]# docker rm 354e1fb5eb93 # 通过容器id来删除容器
354e1fb5eb93
[root@docker ~]# docker rm awesome_dirac # 通过容器名字来删除容器
awesome_dirac
# 批量删除
[root@docker ~]# docker ps -a -q # 批量显示容器id
9954d6e3cb32
45861729009d
[root@docker ~]# docker rm `docker ps -q -a` # 通过id批量删除容器
9954d6e3cb32
45861729009d
查看容器详细信息
[root@docker ~]# docker run -d eeb6ee3f44bd # 返回容器ID
af0ba7c7c3a7086c0b3d9623e90acae36660f6ff2df467d14a59edbe3eefe96f
[root@docker ~]# docker inspect af0ba7 # 接容器ID前几位数即可,但并没有运行起来,所以没有网络信息,因为docker是前台运行的,前文有说到
镜像仓库
使用阿里云的镜像仓库,请自行创建好(容器镜像服务https://cr.console.aliyun.com/cn-hangzhou/instances)
# 举例
# 推送
docker login --username=注册的阿里云仓库账号或者默认dockerhub registry.cn-shenzhen.aliyuncs.com(跟仓库域名)
docker tag 镜像id registry.cn-shenzhen.aliyuncs.com/adif0028(账号空间)/centos(账号仓库):[自定义镜像版本号]
docker push registry.cn-shenzhen.aliyuncs.com/adif0028/centos:[自定义镜像版本号]
# 通过docker ps查看会发现多了一个相当于软链接的镜像
# 拉取取决于是公开还是私有,在阿里云选择不同的网络拉取
删除镜像
# 单一删除
# 删除hello-wold镜像
[root@docker ~]# docker images
[root@docker ~]# docker rmi feb5d9fea6a5 # docker rmi 镜像id/镜像名 (备注如果镜像间有父子镜像关系是不能被删除的,已经被容器运行起来的镜像也不允许删除)
# 批量删除
[root@docker ~]# docker images -q # 显示所有镜像ID
ba6acccedd29
eeb6ee3f44bd
[root@docker ~]# docker rmi $(docker images -q) # 通过镜像id批量删除镜像
搜索镜像
[root@docker ~]# docker search nginx
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
# 表格包含五列,含义如下:
1.NAME:镜像仓库名称。
2.DESCRIPTION:镜像仓库描述。
3.STARS:镜像仓库收藏数,表示该镜像仓库的受欢迎程度,类似于GitHub的Stars。
4.OFFICAL:表示是否为官方仓库,该列标记为[OK]的镜像均由各软件的官方项目组创建和维护。由结果可知,java这个镜像仓库是官方仓库,而其他的仓库都不是镜像仓库。
5.AUTOMATED:表示是否是自动构建的镜像仓库。
# 选用镜像建议
1.优先考虑官方的
2.Stars数多的
# 运行一个nginx容器:
# pull一个nginx镜像注意选择合适版本
[root@docker ~]# docker pull nginx
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 4 months ago 141MB
ubuntu latest ba6acccedd29 7 months ago 72.8MB
centos 7 eeb6ee3f44bd 8 months ago 204MB
# 拉取一个最小系统镜像alpine
[root@docker ~]# docker pull alpine
[root@docker ~]# docker pull nginx:stable-alpine # 拉取以alpine为底层镜像的nginx
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 4 months ago 141MB
alpine latest c059bfaa849c 5 months ago 5.59MB
nginx stable-alpine 373f8d4d4c60 6 months ago 23.2MB
ubuntu latest ba6acccedd29 7 months ago 72.8MB
centos 7 eeb6ee3f44bd 8 months ago 204MB
[root@docker ~]# docker run -d -p 80:80 373f8d4d4c60 # 启动以alpine为底层镜像的nginx,-p端口映射,宿主机的80映射到容器里面的80
f8c67ad2a29bb87f45a2c5f7339e390efc72fd72c91959a440431e0768025f9d
[root@docker ~]# docker ps # 检查
# 浏览器打开宿主机IP:80
进入已经运行的容器
[root@docker ~]# docker exec -it f8c67ad2a29b /bin/sh # alpine没有bash的只有sh
/ # exit
[root@docker ~]# # 退出后容器仍然存活
[root@docker ~]# docker ps # 检查发现command是容器里面写好的一个脚本
#进入容器:
# 进入容器(目的,调试,排错)
# docker exec (会分配一个新的终端tty)
# docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
# docker exec -it 容器id或容器名字 /bin/bash(/bin/sh)
docker attach(使用同一个终端)
# attach是同时操作同步的
# docker attach :连接到正在运行中的容器
# 终端1
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 4 months ago 141MB
alpine latest c059bfaa849c 5 months ago 5.59MB
nginx stable-alpine 373f8d4d4c60 6 months ago 23.2MB
ubuntu latest ba6acccedd29 7 months ago 72.8MB
centos 7 eeb6ee3f44bd 8 months ago 204MB
[root@docker ~]# docker run -it ba6acccedd29
root@5eb73951fddc:/#
# 终端2
[root@docker ~]# docker attach 5eb73951fddc # 接容器ID
# exec是重新分配一个终端,attach是使用同一个终端
# docker attach(使用同一个终端)
# docker attach [OPTIONS] CONTAINER
# nsenter(安装yum install -y util-linux 弃用)
宿主机挂载文件进容器
# 拉去一个mysql5.7的容器
[root@docker ~]# docker pull mysql:5.7
[root@docker ~]# docker images # 检查
[root@docker ~]# yum install -y lsof # 安装lsof
[root@docker ~]# lsof -i :3306 # 检查3306端口是否被占用
# -e 传入一个环境变量,可以是字符串也可以是文件
[root@docker ~]# docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD="123456" c20987f18b13
[root@docker ~]# docker ps # 验证
# 使用navicat连接验证
# 暴力结束所以运行中的容器(慎重)
[root@docker ~]# docker rm -f `docker ps -a -q` # 使用-f参数
# 在宿主机创建挂载数据库文件目录
[root@docker ~]# mkdir /opt/mysql_data
# 使用-v参数,宿主机的挂载源挂到容器里面挂载点上;对应容器mysql就是/var/lib/mysql,这里是容器内生成数据的目录
[root@docker ~]# docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD="123456" -v /opt/mysql_data/:/var/lib/mysql c20987f18b13
[root@docker ~]# docker ps # 验证
# 测试把容器删掉,宿主机上数据目录里面的数据是否还在
[root@docker ~]# cd /opt/mysql_data/
[root@docker mysql_data]# ls
[root@docker mysql_data]# docker stop e05cb1581c9d # 停止容器
e05cb1581c9d
[root@docker mysql_data]# docker rm e05cb1581c9d
e05cb1581c9d
[root@docker mysql_data]# docker ps -a
[root@docker mysql_data]# ls # 仍存在
# 所以当把数据目录放在宿主机上是可以做到防止重要数据丢失,可是生产上还是不建议使用容器数据库
# 数据库容器存货时,宿主机数据库目录会发现有一个临时文件ibtmp1,容器消亡后临时文件z不存在
# 进入容器新创库删掉看看是否会初始化覆盖掉造成数据丢失
[root@docker mysql_data]# docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD="123456" -v /opt/mysql_data/:/var/lib/mysql c20987f18b13
db2a557bb47c3dff4ef6f43d2beee7f784047c1ba1c37344f1eb9b6312276a5b
[root@docker mysql_data]# docker exec -it db2a557 /bin/bash
root@db2a557bb47c:/# mysql -uroot -p
Enter password:
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)
mysql> create database shy charset utf8mb4;
Query OK, 1 row affected (0.00 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| shy |
| sys |
+--------------------+
5 rows in set (0.00 sec)
mysql> exit
Bye
root@db2a557bb47c:/# exit
exit
[root@docker mysql_data]# ls # 发现刚新创的数据库文件
# 重新挂载验证是否有刚刚新创的库
[root@docker mysql_data]# docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD="123456" -v /opt/mysql_data/:/var/lib/mysql c20987f18b13
[root@docker mysql_data]# docker exec -it 1e13f80 /bin/bash
root@1e13f807e359:/# mysql -uroot -p123456 -e "show databases" # 没有影响,新创的库还在;证明没有初始化
# 杀死容器
docker kill container_name # ps查看的任务码非0,非0是不正常退出,程序内部错误
# 查看容器列表
docker ps
docker ps –a
导入导出镜像
# 导出
# 格式:docker save [OPTIONS] IMAGE [IMAGE...]
OPTIONS:可选参数
IMAGE:镜像
OPTIONS的常用值
-o string: 指定目标文件,和linux原生命令>有相同作用
# docker save 镜像id/镜像名 > /opt/alpine.tar 用镜像id导出来的是废镜像<none>
# docker save -o /opt/alpine.tar 镜像名
# 导出镜像
docker save -o 否则导入镜像没有镜像标签以及名字
例子:docker image save centos > docker-centos7.4.tar.gz
#导入镜像
docker load 例子:docker image load -i docker-centos7.4.tar.gz
# load命令可以从指定文件中加载镜像,该文件需要是save命令保存的文件
# 将alpine镜像打包成alpine.tar
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 4 months ago 141MB
mysql 5.7 c20987f18b13 5 months ago 448MB
alpine latest c059bfaa849c 5 months ago 5.59MB
nginx stable-alpine 373f8d4d4c60 6 months ago 23.2MB
ubuntu latest ba6acccedd29 7 months ago 72.8MB
centos 7 eeb6ee3f44bd 8 months ago 204MB
[root@docker ~]# docker save c059bfaa849c > /opt/alpine.tar # 没有使用-o指定名字导出来的是废镜像
# 导入
# docker load < 文件来源 或者 docker load -i 文件来源
[root@docker ~]# docker rmi c059bfaa849c
[root@docker ~]# docker load < /opt/alpine.tar
8d3ac3489996: Loading layer [==================================================>] 5.866MB/5.866MB
Loaded image ID: sha256:c059bfaa849c4d8e4aecaeb3a10c2d9b3d85f5165c66ad3a4d937758128c4d18
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 4 months ago 141MB
mysql 5.7 c20987f18b13 5 months ago 448MB
<none> <none> c059bfaa849c 5 months ago 5.59MB # 没有指定名字识别不出来,是废镜像
nginx stable-alpine 373f8d4d4c60 6 months ago 23.2MB
ubuntu latest ba6acccedd29 7 months ago 72.8MB
centos 7 eeb6ee3f44bd 8 months ago 204MB
[root@docker ~]# docker rmi c059bfaa849c
Deleted: sha256:c059bfaa849c4d8e4aecaeb3a10c2d9b3d85f5165c66ad3a4d937758128c4d18
Deleted: sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759
[root@docker ~]# docker load -i /opt/alpine.tar
# 正确导出与导入一
[root@docker ~]# docker save -o /opt/alpine.tar alpine:latest
[root@docker ~]# docker rmi c059bfaa849c
[root@docker ~]# docker load -i /opt/alpine.tar
导入导出容器
#export:将容器打包成一个文件
#export命令可以将容器打包到一个文件中,它和save命令比较容易混淆
#export和save的不同之处在于:export打包的是容器,save打包的是镜像
#export打包的是容器当时的快照,至于容器的历史记录和元数据信息都会丢失。还有,export的文件在被import成一个镜像时,可以重新指定镜像的名称和版本号
#格式:docker export [OPTIONS] CONTAINER
OPTIONS:可选参数
CONTAINER:容器
OPTIONS的常用值
-o string: 指定打包文件
export常用写法
将my-boot容器打包到my-boot.tar文件
# 由容器导出镜像:
docker export -o 名字.tar.gz 容器名字/id
docker import 名字.tar.gz 另外命名 (注意export模式会把默认命令取消掉。启动时要指定默认命令)
# 例如:
docker run -d -p80:80 4ce44d891b27 /docker-entrypoint.sh nginx -g 'daemon off;'
# import可以从本地文件或远程文件中导入镜像到本地仓库;如果是从文件中导入,这个文件需要是export命令导出的文件
[root@docker ~]# docker rm -f `docker ps -a -q`
4250861b2dd5
1e13f807e359
[root@docker ~]# docker run -d -p 80:80 373f8d4d4c60
c5061caffa08793ae6b3afcb69c6032b4ffdce47c319fa3ab73ebeb7a5f54ccb
[root@docker ~]# docker ps
[root@docker ~]# docker exec -it c5061caffa08 /bin/sh
/ # vi /usr/share/nginx/html/index.html
# 更改如下
This is 70
/ # exit
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c5061caffa08 373f8d4d4c60 "/docker-entrypoint.…" 4 minutes ago Up 4 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp relaxed_wescoff
[root@docker ~]# docker export -o /opt/seven.tar.gz c5061caffa08 # 导出
# 导入并启动
# 可以直接创建导入,也可以重新打标签和备注
# 从/opt/seven.tar.gz文件创建镜像,并指定镜像名称为seven、版本号为v1
[root@docker ~]# docker import /opt/seven.tar.gz seven:v1
sha256:b95540f0c1470c5969c64f4e842d477849d98141f5774aede97f943e97c51f83
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
seven v1 b95540f0c147 14 seconds ago 22.9MB
nginx latest 605c77e624dd 4 months ago 141MB
mysql 5.7 c20987f18b13 5 months ago 448MB
alpine latest c059bfaa849c 5 months ago 5.59MB
nginx stable-alpine 373f8d4d4c60 6 months ago 23.2MB
ubuntu latest ba6acccedd29 7 months ago 72.8MB
centos 7 eeb6ee3f44bd 8 months ago 204MB
# 启动,因为容器所有元信息都没有,所以启动需要/docker-entrypoint.sh nginx -g 'daemon off;'
[root@docker ~]# docker run -d -p 90:80 b95540f0c147 /docker-entrypoint.sh nginx -g 'daemon off;'
b1d6af7610591327697f23fc5771fa153ed9ce8508bf47921246c313e3841eb4
[root@docker ~]# docker ps
# 浏览器打开90
镜像与容器的导入导出总结
- save命令打包的是镜像,包含镜像的所有信息
- exprot命令打包的是容器,只是保存容器当时的快照,历史记录和元数据信息将会丢失
- export和save的不同之处在于:export打包的是容器,save打包的是镜像
- 保存镜像(保存镜像载入后获得跟原镜像id相同的镜像)
- 保存容器(保存容器载入后获得跟原镜像id不同的镜像)
Dockerfile
Dockerfile 是一个文本文件,其内包含了一条条的 指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
1、FROM
格式:FROM <image>或 FROM <image>:<tag>
基础镜像来源,必须在第一行指令中指定,不指定标记则默认为latest。
2、LABEL
格式:LABEL <name>=<value>
指定添加元数据到镜像,可指定多个标签信息。
3、RUN
格式:RUN <command>
每条指令将在当前镜像基础上执行,并提交为新的镜像。
4、CMD
格式:CMD <command>
指定启动容器时执行的命令,每个Dockerfile只能有一条CMD指令,如果指定了多条CMD指令,则只会执行最后一条CMD指令。如果在启动镜像时指定了启动指令,则镜像预设的CMD指令不执行。一般用于执行容器时提供默认值。
5、EXPOSE
格式:EXPOSE <port>
指定镜像启动后暴露的端口,在容器启动时需要通过 -p 做端口映射
6、ENV
格式:ENV <key> <value>
指定环境变量,使用在构建阶段中的所有后续指令的环境。
7、ADD
格式:ADD <src> <dest>
该指令会在<src>(本地文件系统或远程网络位置) 进行复制新文件,并将它们添加到容 路径上的文件系统中<dest>。
8、COPY
格式:COPY <src> <dest>
复制本地主机的 <src> (为 Dockerfile 所在目录的相对路径) 到容器中的 <dest>。
9、ENTRYPOINT
格式:ENTRYPOINT <command>
配置容器启动后执行的命令,并且不可被docker run提供的参数覆盖。如果指定了多条ENTRYPOINT指令,则只会执行最后一条ENTRYPOINT指令。
10、VOLUME
格式:VOLUME <FileSystemMountPoint>
创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等
11、USER
格式:USER <user>:<group> | USER <uid>:<gid>
指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。
12、WORKDIR
格式:WORKDIR <FileSystemPath>
为后续的 COPY、ADD、RUN、CMD、ENTRYPOINT 指令配置工作目录。(可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径, 则会基于之前命令指定的路径)
13、ARG
格式:ARG <varname>[=<value>]
该ARG指令定义了一个变量,用户可以docker build使用该--build-arg <varname>=<value> 标志在构建时将该变量传递给构建器。
14、ONBUILD
格式:ONBUILD [INSTRUCTION]
配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令,就好像它已经FROM在下游指令之后立即插入一样 Dockerfile。
15、STOPSIGNAL
格式:STOPSIGNAL <signal>
设置将发送到容器的系统调用信号以退出。此信号可以是与内核的系统调用表中的位置匹配的有效无符号数,例如9,或SIGNAME格式的信号名,例如SIGKILL。
16、HEALTHCHECK heathcheck
格式:HEALTHCHECK [OPTIONS] CMD command
测试容器以检查它是否仍在工作。即使服务器进程仍在运行,这也可以检测到陷入无限循环且无法处理新连接的Web服务器等情况。当容器指定了运行状况检查时,除了正常状态外,它还具有运行状况。这个状态最初是starting。每当健康检查通过时,它就会变成healthy(以前所处的状态)。经过一定数量的连续失败后,它就变成了unhealthy。
17、SHELL
格式:SHELL ["executable", "parameters"]
该SHELL指令允许覆盖用于shell命令形式的默认shell。Linux上的默认shell是["/bin/sh", "-c"],而在Windows上["cmd", "/S", "/C"]。该SHELL指令必须以JSON格式写入Dockerfile。
总结
FROM 开头必须是FROM注意一定是大写。后面跟镜像名字或者空(特殊写法)
MAINTAINER 声明作者是谁,可以省略。
ENV 声明环境变量,往镜像里注入需要的环境变量
COPY/ADD 都是把本地文件或者目录加入到镜像内,其中COPY可以覆盖文件,ADD与COPY区别在于有解压包的作用。
WORKDIR 声明工作目录在哪。构建镜像时会在容器内改目录一直操作,直到下一个WORKDIR出现或者RUN中出现cd命令。
RUN 执行主要命令和安装过程的关键字。注意RUN写的次数越少,镜像层就越少,建议所有操作只写一个RUN
dockerfile支持变量用,定义与引用与shell脚本相同
EXPOSE 暴露容器运行时默认的端口。
ENTRYPOINT/CMD 容器运行时默认运行的命令也就是pid 1进程命令。此命令在docker中要求必须hold住,容器才可以正常运行。这俩参数都可以生产pid 1运行命令,但是有区别,当两个关键字都存在时。ENTRYPOINT级别高于CMD,CMD运行内容会默认变成ENTRYPOINT的运行内容参数。从而两个参数形成一条运行命令。
centos-redis
[root@docker ~]# mkdir -p /docker/redis
[root@docker ~]# cd /docker/redis/
[root@docker redis]# wget http://download.redis.io/releases/redis-5.0.8.tar.gz
[root@docker redis]# ls
redis-5.0.8.tar.gz
[root@docker redis]# vim Dockerfile
FROM centos:7
WORKDIR /data
ADD redis-5.0.8.tar.gz /data
RUN yum install gcc gcc-c++ make -y && cd redis-5.0.8 && make && make install
EXPOSE 6379
CMD ["/data/redis-5.0.8/src/redis-server"]
[root@docker redis]# docker build -t redis:v1 .
# 验证
[root@docker redis]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis v1 9a8285dd6298 5 seconds ago 625MB
[root@docker redis]# docker run -d 9a8285dd6298
9eb3bcb1dacda4494aaa8d396bfc7a885ba6beda192cc6cf7a0b6937422a8633
[root@docker redis]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9eb3bcb1dacd 9a8285dd6298 "/data/redis-5.0.8/s…" 5 seconds ago Up 5 seconds 6379/tcp naughty_rubin
[root@docker redis]# docker exec -it 9eb3bcb1dacd bash
[root@9eb3bcb1dacd data]# ls
redis-5.0.8
[root@9eb3bcb1dacd data]# ps -ef | grep redis
root 1 0 0 14:05 ? 00:00:00 /data/redis-5.0.8/src/redis-server *:6379
root 28 10 0 14:06 pts/0 00:00:00 grep --color=auto redis
[root@9eb3bcb1dacd data]# redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> exit
不同启动方式,调用容器启动的第一个命令是不同的
[root@docker1 redis]# docker start 651cace10140
651cace10140
[root@docker1 redis]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
651cace10140 1fe9fbc1d6f5 "/bin/bash" 3 minutes ago Up 2 seconds 6379/tcp focused_allen
[root@docker1 redis]# docker run -d 1fe9fbc1d6f5
15210e98c9ddb6098b815882e98606bc3607376899183d2612a131149f1889a4
[root@docker1 redis]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
15210e98c9dd 1fe9fbc1d6f5 "/data/redis-5.0.8/s…" 6 seconds ago Up 5 seconds 6379/tcp dreamy_borg
# 优化一
[root@docker redis]# vim Dockerfile
FROM centos:7
WORKDIR /data
ADD redis-5.0.8.tar.gz /data
RUN yum install gcc gcc-c++ make -y && cd redis-5.0.8 && make && make install \
&& cp /data/redis-5.0.8/src/redis-server /usr/local/bin && cp /data/redis-5.0.8/src/redis-cli /usr/local/bin \
&& cp /data/redis-5.0.8/redis.conf /etc/ && rm -rf /data/* && yum remove gcc gcc-c++ make -y && yum clean all
EXPOSE 6379
CMD ["redis-server"]
[root@docker redis]# docker build -t redis:v2 .
[root@docker redis]# docker images # 可以看出v2经过优化比原来少了一半左右
REPOSITORY TAG IMAGE ID CREATED SIZE
redis v2 18680627f22d 12 seconds ago 326MB
redis v1 9a8285dd6298 20 minutes ago 625MB
# 验证
[root@docker redis]# docker run -d 18680627f22d
5a9d56c1caf2dee01c7070bd00904db47ec9281c45e5896db201e489798d71be
[root@docker redis]# docker ps
[root@docker redis]# docker exec -it 5a9d56c1caf2 bash
[root@5a9d56c1caf2 data]# redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> exit
ubuntu_redis
[root@docker redis]# cp Dockerfile Dockerfile.bak
[root@docker redis]# vim Dockerfile
FROM ubuntu
WORKDIR /data
ADD redis-5.0.8.tar.gz /data
RUN sed -i 's#archive.ubuntu.com#mirrors.aliyun.com#g' /etc/apt/sources.list && apt-get update \
&& apt-get install gcc make -y && cd redis-5.0.8 && make && make install \
&& cp /data/redis-5.0.8/src/redis-server /usr/local/bin && cp /data/redis-5.0.8/src/redis-cli /usr/local/bin \
&& cp /data/redis-5.0.8/redis.conf /etc/
EXPOSE 6379
CMD ["redis-server"]
[root@docker redis]# docker build -t redis-ubuntu:v1 .
# 验证
[root@docker redis]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis-ubuntu v1 65ba35d38b5e 4 seconds ago 466MB
[root@docker redis]# docker ps # 一般5s后不消亡都是成功的
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
de51550497e9 65ba35d38b5e "redis-server" 11 seconds ago Up 10 seconds 6379/tcp dreamy_lewin
# 优化一
[root@docker redis]# vim Dockerfile
FROM ubuntu
WORKDIR /data
ADD redis-5.0.8.tar.gz /data
RUN sed -i 's#archive.ubuntu.com#mirrors.aliyun.com#g' /etc/apt/sources.list && apt-get update \
&& apt-get install gcc make -y && cd redis-5.0.8 && make && make install \
&& cp /data/redis-5.0.8/src/redis-server /usr/local/bin && cp /data/redis-5.0.8/src/redis-cli /usr/local/bin \
&& cp /data/redis-5.0.8/redis.conf /etc/ && rm -rf /data/* && apt-get remove gcc make -y && apt clean
EXPOSE 6379
CMD ["redis-server"]
[root@docker redis]# docker build -t redis-ubuntu:v2 .
# 验证
[root@docker redis]# docker images # 可以看出v2经过优化比原来少了一半左右
REPOSITORY TAG IMAGE ID CREATED SIZE
redis-ubuntu v2 00880168900a 5 seconds ago 320MB
redis-ubuntu v1 65ba35d38b5e 18 minutes ago 466MB
[root@docker redis]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
99584cdb6957 00880168900a "redis-server" About a minute ago Up About a minute 6379/tcp cool_davinci
# 优化三 多阶段构建(可以不用别名用数字代替层数,也可以设置别名AS)
[root@docker redis]# vim Dockerfile
FROM ubuntu AS base
WORKDIR /data
ADD redis-5.0.8.tar.gz /data
RUN sed -i 's#archive.ubuntu.com#mirrors.aliyun.com#g' /etc/apt/sources.list && apt-get update \
&& apt-get install gcc make -y && cd redis-5.0.8 && make && make install
FROM ubuntu
COPY --from=base /data/redis-5.0.8/src/redis-server /usr/local/bin/
COPY --from=base /data/redis-5.0.8/src/redis-cli /usr/local/bin/
COPY --from=base /data/redis-5.0.8/redis.conf /etc/
EXPOSE 6379
CMD ["redis-server"]
[root@docker redis]# docker build -t redis-ubuntu:v3 .
# 验证
[root@docker redis]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis-ubuntu v3 0c6f5f472e5a 3 minutes ago 90.8MB
[root@docker redis]# docker run -d 0c6f5f472e5a
24c64bd380f2c10b777515f322ddc488f59a375586f9cf6d1c30d756d90055de
[root@docker redis]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
24c64bd380f2 0c6f5f472e5a "redis-server" 8 minutes ago Up 8 minutes 6379/tcp magical_spence
nginx-redis
[root@docker ~]# mkdir /docker/nginx-redis
[root@docker ~]# cd /docker/nginx-redis/
[root@docker nginx-redis]# ls
nginx-1.20.2.tar.gz redis-5.0.8.tar.gz
[root@docker nginx-redis]# tar xf redis-5.0.8.tar.gz && cd redis-5.0.8
[root@docker redis-5.0.8]# sed -i "s/daemonize no/daemonize yes/g" redis.conf && cp redis.conf ../
[root@docker nginx-redis]# vim run.sh
[root@docker nginx-redis]# pwd
/docker/nginx-redis
[root@docker nginx-redis]# vim run.sh
#!/bin/sh
echo "start nginx"
nginx
echo "start redis"
redis-server /etc/redis.conf
tail -f /dev/null
[root@docker nginx-redis]# vim Dockerfile
FROM centos:7
WORKDIR /data
ADD redis-5.0.8.tar.gz /data
ADD nginx-1.20.2.tar.gz /data
COPY redis.conf /etc/redis.conf
COPY run.sh /run.sh
RUN yum install gcc gcc-c++ make prce-devel openssl-devel -y && chmod a+x /run.sh \
&& cd redis-5.0.8 && make && make install \
&& cp /data/redis-5.0.8/src/redis-server /usr/local/bin && cp /data/redis-5.0.8/src/redis-cli /usr/local/bin \
&& cd /data/nginx-1.20.2 && useradd www -s /sbin/nologin \
&& CONFIG="\
--prefix=/usr/local/nginx \
--with-http_stub_status_module \
--with-http_v2_module \
--with-http_ssl_module \
--with-http_gzip_static_module \
--with-http_realip_module \
--with-http_flv_module \
--with-stream \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib/nginx/modules \
--conf-path=/usr/local/nginx/conf/nginx.conf \
--user=www \
--group=www \
" \
&& ./configure $CONFIG && make && make install \
&& rm -rf /data/* && yum remove gcc gcc-c++ make -y && yum clean all
EXPOSE 6379
EXPOSE 80
ENTRYPOINT ["/bin/sh","-c"]
CMD ["/run.sh"]
[root@docker nginx-redis]# docker build -t nginx-redis:nr-v1 .
[root@docker nginx-redis]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx-redis nr-v1 0a044c0707b0 About a minute ago 349MB
[root@docker nginx-redis]# docker run -d 0a044c0707b0
5e915a45e4c8b0dd115f8f4a8dd10a6ad1ae9a92e6bd0b06ca7abcd4da075d6c
[root@docker nginx-redis]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5e915a45e4c8 0a044c0707b0 "/bin/sh -c /run.sh" 5 seconds ago Up 5 seconds 80/tcp, 6379/tcp silly_germain
[root@docker nginx-redis]# docker exec -it 5e915a45e4c8 bash
[root@5e915a45e4c8 data]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 17:14 ? 00:00:00 /bin/sh /run.sh
root 7 1 0 17:14 ? 00:00:00 nginx: master process nginx
www 9 7 0 17:14 ? 00:00:00 nginx: worker process
root 10 1 0 17:14 ? 00:00:00 redis-server 127.0.0.1:6379
root 14 1 0 17:14 ? 00:00:00 tail -f /dev/null
root 15 0 0 17:14 pts/0 00:00:00 bash
root 33 15 0 17:15 pts/0 00:00:00 ps -ef
# 优化
[root@docker nginx-redis]# vim Dockerfile
FROM centos:7
WORKDIR /data
ADD redis-5.0.8.tar.gz /data
ADD nginx-1.20.2.tar.gz /data
RUN yum install gcc gcc-c++ make prce-devel openssl-devel -y \
&& cd redis-5.0.8 && make && make install \
&& cd /data/nginx-1.20.2 && useradd www -s /sbin/nologin \
&& CONFIG="\
--prefix=/usr/local/nginx \
--with-http_stub_status_module \
--with-http_v2_module \
--with-http_ssl_module \
--with-http_gzip_static_module \
--with-http_realip_module \
--with-http_flv_module \
--with-stream \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib/nginx/modules \
--conf-path=/usr/local/nginx/conf/nginx.conf \
--user=www \
--group=www \
" \
&& ./configure $CONFIG && make && make install
FROM centos:7
COPY redis.conf /etc/redis.conf
COPY run.sh /run.sh
COPY --from=0 /data/redis-5.0.8/src/redis-server /usr/local/bin/
COPY --from=0 /data/redis-5.0.8/src/redis-cli /usr/local/bin/
COPY --from=0 /data/redis-5.0.8/redis.conf /etc/
COPY --from=0 /usr/local/nginx /usr/local/nginx
COPY --from=0 /usr/sbin/nginx /usr/sbin/
COPY --from=0 /lib64 /lib64
RUN useradd www -s /sbin/nologin && chmod a+x /run.sh
EXPOSE 6379
EXPOSE 80
ENTRYPOINT ["/bin/sh","-c"]
CMD ["/run.sh"]
[root@docker nginx-redis]# docker build -t nginx-redis:nr-v2 .
[root@docker nginx-redis]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx-redis nr-v2 79991815f3f0 42 seconds ago 306MB
[root@docker nginx-redis]# docker run -d 79991815f3f0
529d0163efdabc56db95195da1e90f2e5667d57bf0c121c1b07fc914cb100913
[root@docker nginx-redis]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
529d0163efda 79991815f3f0 "/bin/sh -c /run.sh" 38 seconds ago Up 37 seconds 80/tcp, 6379/tcp laughing_mayer
[root@docker nginx-redis]# docker exec -it 529d0163efda bash
[root@529d0163efda /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 17:56 ? 00:00:00 /bin/sh /run.sh
root 8 1 0 17:56 ? 00:00:00 nginx: master process nginx
root 9 1 0 17:56 ? 00:00:00 redis-server 127.0.0.1:6379
www 10 8 0 17:56 ? 00:00:00 nginx: worker process
root 14 0 0 17:56 pts/0 00:00:00 bash
root 28 14 0 17:56 pts/0 00:00:00 ps -ef
# 写法二
[root@docker nginx-redis]# vim Dockerfile
FROM centos:7
WORKDIR /data
ADD redis-5.0.8.tar.gz /data
ADD nginx-1.20.2.tar.gz /data
RUN yum install gcc gcc-c++ make prce-devel openssl-devel -y \
&& cd redis-5.0.8 && make && make install \
&& cd /data/nginx-1.20.2 && useradd www -s /sbin/nologin \
&& CONFIG="\
--prefix=/usr/local/nginx \
--with-http_stub_status_module \
--with-http_v2_module \
--with-http_ssl_module \
--with-http_gzip_static_module \
--with-http_realip_module \
--with-http_flv_module \
--with-stream \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib/nginx/modules \
--conf-path=/usr/local/nginx/conf/nginx.conf \
--user=www \
--group=www \
" \
&& ./configure $CONFIG && make && make install
FROM centos:7
COPY run.sh /run.sh
COPY --from=0 /data/redis-5.0.8/src/redis-server /usr/local/bin/
COPY --from=0 /data/redis-5.0.8/src/redis-cli /usr/local/bin/
COPY --from=0 /data/redis-5.0.8/redis.conf /etc/
COPY --from=0 /usr/local/nginx /usr/local/nginx
COPY --from=0 /usr/sbin/nginx /usr/sbin/
COPY --from=0 /lib64 /lib64
RUN useradd www -s /sbin/nologin && chmod a+x /run.sh && sed -i "s/daemonize no/daemonize yes/g" /etc/redis.conf
EXPOSE 6379
EXPOSE 80
ENTRYPOINT ["/bin/sh","-c"]
CMD ["/run.sh"]
[root@docker nginx-redis]# docker build -t nginx-redis:nr-v3 .
[root@docker nginx-redis]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx-redis nr-v3 48b050033b9d 9 seconds ago 306MB
[root@docker nginx-redis]# docker run -d 48b050033b9d
dea9e5ef0b8ac70a52aa157f16eafaef35edd880546b69e8528c553bd8256c10
[root@docker nginx-redis]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
dea9e5ef0b8a 48b050033b9d "/bin/sh -c /run.sh" 53 seconds ago Up 53 seconds 80/tcp, 6379/tcp loving_lovelace
自写mysql镜像
[root@docker ~]# mkdir /docker/lnmp
[root@docker ~]# cd !$
[root@docker lnmp]# ls
mysql-5.7.30-linux-glibc2.12-x86_64.tar.gz
[root@docker lnmp]# vim Dockerfile
FROM centos:7
WORKDIR /usr/local/
COPY my.cnf /etc/my.cnf
COPY run.sh /run.sh
ADD mysql-5.7.30-linux-glibc2.12-x86_64.tar.gz /usr/local/
RUN yum install -y libaio-devel numactl-devel && yum clean all \
&& mv mysql-5.7.30-linux-glibc2.12-x86_64 mysql \
&& useradd mysql -s /sbin/nologin \
&& mkdir -p /data/mysql \
&& mkdir -p /var/log/mysql \
&& mkdir -p /data/binlog/ \
&& chmod o+w /data/ \
&& chown mysql:mysql -R /usr/local/mysql /data/mysql /var/log/mysql /data/binlog/ /etc/my.cnf \
&& echo 'export PATH=$PATH:/usr/local/mysql/bin' >> /etc/profile \
&& source /etc/profile \
&& echo 'source /etc/profile'>> /root/.bashrc \
&& chmod a+x /run.sh
EXPOSE 3306
ENTRYPOINT ["/bin/sh","-c"]
CMD ["/run.sh"]
[root@docker lnmp]# cat my.cnf
[mysqld]
user=mysql
basedir=/usr/local/mysql
datadir=/data/mysql
server_id=1
port=3306
socket=/data/mysql/mysql.sock
log_bin=/data/binlog/mysql-bin
sync_binlog=1
binlog_format=row
[mysql]
socket=/data/mysql/mysql.sock
[mysqld_safe]
log-error=/var/log/mysql/mysql.log
pid-file=/data/mysql/mysql.pid
[root@docker lnmp]# cat run.sh
#!/bin/sh
#/usr/local/mysql/bin/mysqld_safe --defaults-file=/etc/my.cnf --user=mysql
#/usr/local/mysql/bin/mysqld --initialize --user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql
/usr/local/mysql/bin/mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql
/usr/local/mysql/support-files/mysql.server start
tail -f /dev/null
[root@docker lnmp]# docker build -t mysql:v1 .
复习
1、创建容器
docker run [参数] [镜像名称|ID] [CMD]
-d : 以守护进程运行
-e : 定义一个容器内部的环境变量
-i :打开标准输出
-t : 创建一个终端
-v : 挂载一个本地目录/文件到容器内部
--name : 指定容器的名称,将名称解析到Docker DNS 上
-P: 随机映射一个端口
-p :指定一个端口
--rm : 当容器的生命周期结束的时候立即自动删除容器
--link : 连接一个容器,单向链接
--restart=always 需要开机自启容器
--network
2、启动容器
docker start 容器名称|ID
3、停止容器
docker stop 容器名称|ID
4、查看容器列表
docker ps
-a : 查看所有的容器
-q : 仅查看容器ID
5、删除容器
docker rm [容器ID|名称]
-f : 强制删除
docker rm -f $(docker ps -a -q)
6、复制
# 由宿主主机复制到容器内部
docker cp [宿主主机路径] [容器ID]:[容器内部的路径]
# 由容器内部复制到宿主主机
docker cp [容器ID]:[容器内部的路径] [宿主主机路径]
7、进入容器
# exec : exec的设计目的实际上是在宿主主机外执行一个容器内部的命令
docker exec -it [容器的ID|名称] [cmd]
# attach : 连接上容器内部PID=1的进程
# nsenter : 宿主主机的命令,
# ssh :
数据卷
据卷的本质其实依然是宿主操作系统上的一个目录,只不过这个目录存放在 Docker 内部,接受 Docker 的管理。
数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:
- 数据卷 可以在容器之间共享和重用
- 对 数据卷 的修改会立马生效
- 对 数据卷 的更新,不会影响镜像
- 数据卷 默认会一直存在,即使容器被删除
[root@docker ~]# ls / # 刚开始没有data目录
bin boot dev docker etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
[root@docker ~]# docker run -d -v /data/web:/usr/share/nginx/html 605c77e624dd # 选择挂载
7eb2ca32105517d58a5d012ce34366d9484ac974db42be2c7b24902e8acb1a44
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7eb2ca321055 605c77e624dd "/docker-entrypoint.…" 3 seconds ago Up 2 seconds 80/tcp suspicious_volhard
[root@docker ~]# ls / # 可以看出尽管没有data目录,加了-v会自动创建
bin boot data dev docker etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
查看数据卷
[root@docker ~]# docker volume ls
DRIVER VOLUME NAME
local c608e204441ddf9e2d8bb66b655abde7f3ec44331d57947d965f1b5f1865e3d5
[root@docker1 volumes]# ll /var/lib/docker/volumes
总用量 24
drwxr-xr-x 3 root root 19 11月 7 17:13 c608e204441ddf9e2d8bb66b655abde7f3ec44331d57947d965f1b5f1865e3d5
-rw------- 1 root root 32768 11月 9 09:20 metadata.db
测试
当没有写明宿主机文件路径,会在哪里创建目录
[root@docker ~]# docker run -d -v uplooking:/usr/share/nginx/html 605c77e624dd
7c3611d9b12a47f5bf240ec024e3f0703eb4eae66e5f9abbd18ef124af40c17b
[root@docker ~]# docker volume ls # 当不指定路径的时候就会生成一个卷
DRIVER VOLUME NAME
local uplooking
[root@docker ~]# docker volume inspect uplooking # 查看卷的信息
[
{
"CreatedAt": "2022-05-28T23:05:16+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/uplooking/_data", #可以看到创建在这里,去到里面会发现nginx的一些文件也在里面
"Name": "uplooking",
"Options": null,
"Scope": "local"
}
]
创建卷
[root@docker ~]# docker volume create shy
shy
[root@docker ~]# docker volume create ls
ls
[root@docker ~]# docker volume ls
DRIVER VOLUME NAME
local ls
local shy
local uplooking
# 使用卷
[root@docker ~]# docker run -d -v ls:/usr/share/nginx/html 605c77e624dd
831316f118989ecb38e3bdaf9cb4a3fa4607b617f70331b7335e240091f6039d
[root@docker ~]# ll /var/lib/docker/volumes/ls/_data/ # 换了卷照样会在里面生成文件
50x.html index.html
删除卷
# 正在使用的卷是不允许删除的
# docker volume rm 名字|ID
[root@docker ~]# docker volume rm shy
shy
docker修改存储位置
如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v
这个命令。
网络配置
bridge模式(默认)
- 当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。
- 从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备,Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中。可以通过brctl show命令查看。
- bridge模式是docker的默认网络模式,不写–net参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能。可以使用iptables -t nat -vnL查看。
host模式
- 众所周知,Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
- 例如,我们在10.10.101.105/24的机器上用host模式启动一个含有web应用的Docker容器,监听tcp80端口。当我们在容器中执行任何类似ifconfig命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用10.10.101.105:80即可,不用任何NAT转换,就如直接跑在宿主机中一样。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry.cn-shenzhen.aliyuncs.com/xiaosu_shy/oldliu nginx-php-74v3 38ec4b2456ca 2 weeks ago 564MB
nginx latest 605c77e624dd 5 months ago 141MB
mysql 5.7 c20987f18b13 5 months ago 448MB
ubuntu latest ba6acccedd29 7 months ago 72.8MB
centos 7 eeb6ee3f44bd 8 months ago 204MB
# 使用host模式
[root@docker ~]# docker run -d --net=host 38ec4b2456ca
12217c0c6fb0411a763369ca7906ddd674ce75d5513154dc265593c122949ba3
# docker ps发现ports没有
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
12217c0c6fb0 38ec4b2456ca "/run.sh /bin/sh -c" 9 seconds ago Up 8 seconds silly_ardinghelli
# 浏览器直接输入宿主机的IP地址
# 进入容器发现主机名没有变化而且目录变为根下
[root@docker ~]# docker exec -it 12217c0c6fb0 bash
[root@docker /]# yum install -y net-tools
[root@docker /]# ifconfig #可以看出容器内把宿主机的端口、网络等都映射进来;
坏处攻击这个容器的网络、端口其实就是攻击宿主机
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
12217c0c6fb0 38ec4b2456ca "/run.sh /bin/sh -c" 7 minutes ago Up 7 minutes silly_ardinghelli
# 生产上不建议使用host模式
[root@docker ~]# docker inspect 12217c0c6fb0 # 发现host中IP地址是空的;因为用的是宿主机的
[root@docker ~]# docker rm -f 12217c0c6fb0
12217c0c6fb0
container模式
- 在理解了host模式后,这个模式也就好理解了。这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry.cn-shenzhen.aliyuncs.com/xiaosu_shy/oldliu nginx-php-74v3 38ec4b2456ca 2 weeks ago 564MB
nginx latest 605c77e624dd 5 months ago 141MB
mysql 5.7 c20987f18b13 5 months ago 448MB
ubuntu latest ba6acccedd29 7 months ago 72.8MB
centos 7 eeb6ee3f44bd 8 months ago 204MB
[root@docker ~]# docker run -d --name web nginx
bb9b6b9238f356ad9888a0a4bf2ffdff113e83874fb39415e1d6ff356119f4b3
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bb9b6b9238f3 nginx "/docker-entrypoint.…" 7 seconds ago Up 6 seconds 80/tcp web
#格式:docker run -d --net=container:容器名或容器ID 镜像ID
[root@docker ~]# docker run -d --net=container:web 38ec4b2456ca # 命名一个容器为web,采用container网络模式
8d730393288ecc660d6113a6810549c6bd1b65eee6c232649b99174d72d352c7
[root@docker ~]# docker ps # 发现采用container网络模式的容器没有ports
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8d730393288e 38ec4b2456ca "/run.sh /bin/sh -c" 2 seconds ago Up 1 second epic_lichterman
bb9b6b9238f3 nginx "/docker-entrypoint.…" About a minute ago Up About a minute 80/tcp web
# 进入container网络模式的容器
[root@docker ~]# docker exec -it 8d730393288e bash # 进去发现主机名并不是这个容器的id,而是另外一个容器的id
[root@bb9b6b9238f3 /]# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 bb9b6b9238f3
# 具体看图
[root@docker ~]# docker inspect 8d730393288e # 详细信息里host也是无IP地址的
none模式(了解)
- 这个模式和前两个不同。在这种模式下,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
[root@docker ~]# docker run -d --net=none 38ec4b2456ca
d5e9ec254eafc5c47cbad75de5e9fd8013597e62ae1ea9473d541821e4fcc9a3
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d5e9ec254eaf 38ec4b2456ca "/run.sh /bin/sh -c" 4 seconds ago Up 3 seconds sad_mccarthy
8d730393288e 38ec4b2456ca "/run.sh /bin/sh -c" 10 minutes ago Up 10 minutes epic_lichterman
bb9b6b9238f3 nginx "/docker-entrypoint.…" 11 minutes ago Up 11 minutes 80/tcp web
[root@docker ~]# docker inspect d5e9ec254eaf
# 这种模式容器里只有一张网卡lo
网络模式 | 区别 | |
---|---|---|
bridge | 与宿主机共享网卡,会产生一张虚拟网卡 | |
host | 直接与宿主机共用网卡,不会产生虚拟网卡 | |
container | 与其它容器共用网卡,也不会产生虚拟网卡 | |
none | 需要手动配置网卡配置 |
总结
None:不为容器配置任何网络功能,–net=none
Container:与另一个运行中的容器共享Network Namespace,–net=container:containerID(K8S)
Host:与宿主机共享Network Namespace,–net=host
Bridge:Docker设计的NAT网络模型
Docker compose
**Compose
项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。从功能上看,跟 OpenStack
中的 Heat
十分类似。**
其代码目前在 https://github.com/docker/compose 上开源。
Compose
定位是 「定义和运行多个 Docker 容器的应用(Defining and running multi-container Docker applications)」,其前身是开源项目 Fig。
Docker-Compose
将所管理的容器分为三层,分别是工程(project),服务(service)以及容器(container)
。Docker-Compose运行目录下的所有文件(docker-compose.yml,extends文件或环境变量文件等)组成一个工程,若无特殊指定工程名即为当前目录名。一个工程当中可包含多个服务,每个服务中定义了容器运行的镜像,参数,依赖。一个服务当中可包括多个容器实例,Docker-Compose并没有解决负载均衡的问题,因此需要借助其它工具实现服务发现及负载均衡。
Docker-Compose的工程配置文件默认为docker-compose.yml,可通过环境变量COMPOSE_FILE或-f参数自定义配置文件,其定义了多个有依赖关系的服务及每个服务运行的容器。
使用一个Dockerfile模板文件,可以让用户很方便的定义一个单独的应用容器。在工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个Web项目,除了Web服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等。
Compose允许用户通过一个单独的docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。
Docker-Compose项目由Python
编写,调用Docker服务提供的API来对容器进行管理。因此,只要所操作的平台支持Docker API,就可以在其上利用Compose来进行编排管理。
Docker-compose模板文件简介
Compose允许用户通过一个docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。
Compose模板文件是一个定义服务、网络和卷的YAML文件。Compose模板文件默认路径是当前目录下的docker-compose.yml,可以使用.yml或.yaml作为文件扩展名。
Docker-Compose标准模板文件应该包含version、services、networks 三大部分,最关键的是services和networks两个部分。Compose目前有三个版本分别为Version 1,Version 2,Version 3,Compose区分Version 1和Version 2(Compose 1.6.0+,Docker Engine 1.10.0+)。Version 2支持更多的指令。Version 1将来会被弃用。更详细看版本与协议的关系
image
image是指定服务的镜像名称或镜像ID。如果镜像在本地不存在,Compose将会尝试拉取镜像。
services:
web:
image: hello-world
build
服务除了可以基于指定的镜像,还可以基于一份Dockerfile,在使用up启动时执行构建任务,构建标签是build,可以指定Dockerfile所在文件夹的路径。Compose将会利用Dockerfile自动构建镜像,然后使用镜像启动服务容器。
build: /path/ to /build/dir
也可以是相对路径,只要上下文确定就可以读取到Dockerfile
build: ./dir
设定上下文根目录,然后以该目录为准指定Dockerfile
build:
context: ../
dockerfile: path/ of /Dockerfile
build都是一个目录,如果要指定Dockerfile文件需要在build标签的子级标签中使用dockerfile标签指定。
如果同时指定image和build两个标签,那么Compose会构建镜像并且把镜像命名为image值指定的名字
context
context选项可以是Dockerfile的文件路径,也可以是到链接到git仓库的url,当提供的值是相对路径时,被解析为相对于撰写文件的路径,此目录也是发送到Docker守护进程的context
build:
context: ./dir
dockerfile
使用dockerfile文件来构建,必须指定构建路径
build:
context: .
dockerfile: Dockerfile-alternate
commond
使用command可以覆盖容器启动后默认执行的命令
command: bundle exec thin -p 3000
container_name
Compose的容器名称格式是:<项目名称><服务名称><序号>
可以自定义项目名称、服务名称,但如果想完全控制容器的命名,可以使用标签指定:
container_name: app
depends_on
在使用Compose时,最大的好处就是少打启动命令,但一般项目容器启动的顺序是有要求的,如果直接从上到下启动容器,必然会因为容器依赖问题而启动失败。例如在没启动数据库容器的时候启动应用容器,应用容器会因为找不到数据库而退出。depends_on标签用于解决容器的依赖、启动先后的问题
version: '2'
services:
web:
build: .
depends_on:
- db
- redis
redis:
image: redis
db:
image: postgres
上述YAML文件定义的容器会先启动redis和db两个服务,最后才启动web 服务
PID
pid: "host"
将PID模式设置为主机PID模式,跟主机系统共享进程命名空间。容器使用pid标签将能够访问和操纵其他容器和宿主机的名称空间
ports
ports用于映射端口的标签。
使用HOST:CONTAINER格式或者只是指定容器的端口,宿主机会随机映射端口
ports:
- "3000"
- "8000:8000"
- "49100:22"
- "127.0.0.1:8001:8001"
当使用HOST:CONTAINER格式来映射端口时,如果使用的容器端口小于60可能会得到错误得结果,因为YAML将会解析xx:yy这种数字格式为60进制。所以建议采用字符串格式。
extra_hosts
添加主机名的标签,会在/etc/hosts文件中添加一些记录
extra_hosts:
- "somehost:162.242.195.82"
- "otherhost:50.31.209.229"
volumes
挂载一个目录或者一个已存在的数据卷容器,可以直接使用 [HOST:CONTAINER]格式,或者使用[HOST:CONTAINER:ro]格式,后者对于容器来说,数据卷是只读的,可以有效保护宿主机的文件系统。
Compose的数据卷指定路径可以是相对路径,使用 . 或者 … 来指定相对目录。
数据卷的格式可以是下面多种形式
volumes:
// 只是指定一个路径,Docker 会自动在创建一个数据卷(这个路径是容器内部的)。
- /var/lib/mysql
// 使用绝对路径挂载数据卷
- /opt/data:/var/lib/mysql
// 以 Compose 配置文件为中心的相对路径作为数据卷挂载到容器。
- ./cache:/tmp/cache
// 使用用户的相对路径(~/ 表示的目录是 /home/<用户目录>/ 或者 /root/)。
- ~/configs:/etc/configs/:ro
// 已经存在的命名的数据卷。
- datavolume:/var/lib/mysql
如果不使用宿主机的路径,可以指定一个volume_driver。
volume_driver: mydriver
volumes_from
从另一个服务或容器挂载其数据卷:
volumes_from:
- service_name
- container_name
dns
自定义DNS服务器。可以是一个值,也可以是一个列表
dns: 8.8.8.8
dns:
- 8.8.8.8
- 9.9.9.9
expose
暴露端口,但不映射到宿主机,只允许能被连接的服务访问。仅可以指定内部端口为参数,如下所示:
expose:
- "3000"
- "8000"
links
链接到其它服务中的容器。使用服务名称(同时作为别名),或者“服务名称:服务别名”(如 SERVICE:ALIAS),例如:
links:
- db
- db:database
- redis
net
设置网络模式
net: "bridge"
net: "none"
net: "host"
net: "container"
Docker-compose模板文件示例
基于lnmp的禅道
作业:使用docker-compose搭建wordpress
1.安装
官网安装方式,这里我采用官网的安装方式,也就是方法一
下载地址:https://github.com/docker/compose/releases
方法一:
[root@docker ~]# DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
[root@docker ~]# mkdir -p $DOCKER_CONFIG/cli-plugins
[root@docker ~]# curl -SL https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
[root@docker ~]# chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
方法二:
[root@docker ~]# mv docker-compose /usr/local/bin/docker-compose
[root@docker ~]# chmod a+x /usr/local/bin/docker-compose
[root@docker ~]# docker- # 用tab键是可以提示补全的
docker-compose docker-init docker-proxy
[root@docker ~]# docker-compose version
Docker Compose version v2.5.0
2.编写模板文件
[root@docker ~]# mkdir -p /docker/script
[root@docker ~]# cd !$
cd /docker/script
[root@docker script]# mkdir compose
[root@docker script]# cd compose/
[root@docker compose]# vim docker-compose.yaml
version: '3'
services:
db:
image: mysql:5.7.36
container_name: mysql
restart: always
tty: true
environment:
MYSQL_ROOT_PASSWORD: "123456"
volumes:
- /opt/data:/var/lib/mysql
networks: #其它容器都要同样的网络,但名字可以自定义
- lnmp
web:
image: registry.cn-shenzhen.aliyuncs.com/adif0028/nginx_php:74v3
container_name: web01
restart: always
tty: true
depends_on:
- db
volumes:
- /docker/wordpress:/usr/local/nginx/html
ports:
- 80:80
networks:
- lnmp
volumes:
/opt/data:
/docker/wordpress:
networks:
lnmp:
# 上传禅道到/docker/script/compose下
[root@docker compose]# unzip ZenTaoPMS.12.3.2.zip
[root@docker compose]# mv zentaopms/ zentao
3.启动
[root@docker compose]# docker compose up # 前台启动,docker-compose up这条命令也可以
[root@docker compose]# docker compose up -d # 后台启动
[root@docker compose]# docker ps
4.修改配置文件
[root@docker compose]# docker exec -it 882cee75d749 bash
[root@882cee75d749 /]# cd /usr/local/nginx/conf/
[root@882cee75d749 conf]# vi nginx.conf
改: 71 root /usr/local/nginx/html;
为: 71 root /usr/local/nginx/html/www;
[root@882cee75d749 conf]# vi /usr/local/php/etc/php.ini
改: 1312 ;session.save_handler = files
为: 1312 session.save_handler = files
改: 1341 ;session.save_path = "/tmp"
为: 1341 session.save_path = "/tmp"
[root@882cee75d749 conf]# nginx -s reload
[root@882cee75d749 conf]# /etc/init.d/php-fpm restart
Gracefully shutting down php-fpm . done
Starting php-fpm done
[root@882cee75d749 conf]# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.21.0.3 882cee75d749
# 浏览器输入宿主机IP地址,到达数据库填写可以写db,因为compose里的yaml文件当时就已经定义好了,容器里面是有联系的
总结
注意compose文件需要特定的名字docker-compose.yaml
docker compose up -d (-d是后台启动,否则前台有输出启动)
关闭容器并且清理相关资源,清理容器,清理网络
docker compose down 也是在有yaml文件的目录下执行。
docker搭建registry仓库
镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。
一个 Docker Registry 中可以包含多个仓库(Repository
);每个仓库可以包含多个标签(Tag
);每个标签对应一个镜像。
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签>
的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest
作为默认标签。
以 Ubuntu 镜像 为例,ubuntu
是仓库的名字,其内包含有不同的版本标签,如,14.04
, 16.04
。我们可以通过 ubuntu:14.04
,或者 ubuntu:16.04
来具体指定所需哪个版本的镜像。如果忽略了标签,比如 ubuntu
,那将视为 ubuntu:latest
。
仓库名经常以 两段式路径 形式出现,比如 jwilder/nginx-proxy
,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。
Docker Registry 公开服务
Docker Registry 公开服务是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。
最常使用的 Registry 公开服务是官方的 Docker Hub,这也是默认的 Registry,并拥有大量的高质量的官方镜像。除此以外,还有 CoreOS 的 Quay.io,CoreOS 相关的镜像存储在这里;Google 的 Google Container Registry,Kubernetes 的镜像使用的就是这个服务。
由于某些原因,在国内访问这些服务可能会比较慢。国内的一些云服务商提供了针对 Docker Hub 的镜像服务(Registry Mirror
),这些镜像服务被称为加速器。常见的有 阿里云加速器 一节中有详细的配置方法。
国内也有一些云服务商提供类似于 Docker Hub 的公开服务。比如 时速云镜像仓库、网易云镜像服务、DaoCloud 镜像市场、阿里云镜像库 等。
私有 Docker Registry
除了使用公开服务外,用户还可以在本地搭建私有 Docker Registry。Docker 官方提供了 Docker Registry 一节中,会有进一步的搭建私有 Registry 服务的讲解。
开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 docker
命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。在官方的商业化版本 Docker Trusted Registry 中,提供了这些高级功能。
除了官方的 Docker Registry 外,还有第三方软件实现了 Docker Registry API,甚至提供了用户界面以及一些高级功能。比如,VMWare Harbor 和 Sonatype Nexus。
搭建私有的registry
1.拉取镜像
[root@docker ~]# docker pull registry
2.启动
[root@docker ~]# docker run -d -p 5000:5000 --restart=always --name registry -v /opt/myregistry:/var/lib/registry registry
e1eb131be448e653a40f5eb742ae38bdc3b503c1f1e0d1316b30646e905e98e3
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e1eb131be448 registry "/entrypoint.sh /etc…" 5 seconds ago Up 3 seconds 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp registry
# 浏览器输入宿主机IP地址:5000
# 在没有任何东西的时候打开是空白的
3.推送镜像
操作跟推送上阿里云一样的操作
# 打标签
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine latest c059bfaa849c 6 months ago 5.59MB
[root@docker ~]# docker tag c059bfaa849c 192.168.188.70:5000/alpine:v1
# 上传
[root@docker ~]# docker push 192.168.188.70:5000/alpine:v1
The push refers to repository [192.168.188.70:5000/alpine]
Get "https://192.168.188.70:5000/v2/": http: server gave HTTP response to HTTPS client
[root@docker ~]#
# 遇到报错The push refers to repository [192.168.188.70:5000/alpine] Get "https://192.168.188.70:5000/v2/": http: server gave HTTP response to HTTPS client
#为什么需要增加此配置呢?docker默认采用https的方式推送镜像,如果不进行此配置,则走https协议,此时我们安装的repositry并不支持https
# 解决方案
[root@docker ~]# vim /etc/docker/daemon.json
# 增加这一项
"insecure-registries": ["192.168.188.70:5000"]
# 完整是
{
"registry-mirrors": ["https://niphmo8u.mirror.aliyuncs.com"],
"insecure-registries": ["192.168.188.70:5000"]
}
[root@docker ~]# systemctl restart docker
# 重新上传
[root@docker ~]# docker push 192.168.188.70:5000/alpine:v1
# 浏览器访问http://192.168.188.70:5000/v2/ ;根据前面提示信息里有,但是这里没有采用https,所以换成http
4.查看镜像列表
# 浏览器输入 http://192.168.188.70:5000/v2/_catalog 可以查看推送上的镜像
5.查看镜像标签
# 浏览器输入 http://192.168.188.70:5000/v2/alpine/tags/list
# 宿主机查看
[root@docker ~]# ls /opt/myregistry/docker/registry/v2/repositories/alpine/
_layers _manifests _uploads
6.带basic认证的registry
就是相当于阿里云镜像仓库的登陆
yum install httpd-tools -y
mkdir /opt/registry-var/auth/ -p
htpasswd -Bbn shy 123456 >> /opt/registry-var/auth/htpasswd
docker rm -f registry
# 启动
docker run -d -p 5000:5000 -v /opt/registry-var/auth/:/auth/ -v /opt/myregistry:/var/lib/registry -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" registry
docker login 192.168.188.70:5000
docker push 192.168.188.70:5000/alpine:v1
7.删除镜像
1)进入docker registry的容器中
docker exec -it registry /bin/sh
2) 删除repo
rm -fr /var/lib/registry/docker/registry/v2/repositories/nginx
3) 清除掉blob
registry garbage-collect /etc/docker/registry/config.yml
Docker跨主机容器之间的通信macvlan
默认一个物理网卡,只有一个物理地址,虚拟多个mac地址;基于二层
实验环境
主机名 | IP | 备注 |
---|---|---|
docker | 192.168.188.70 | docker |
docker-71 | 192.168.188.71 | docker |
目的:实现容器基于macvlan的单网段跨主机通信
1.创建网络
# 70的机子
[root@docker compose]# ifconfig # 确认自己的网卡
# 创建macvlan网络,指定网段、网关等信息
[root@docker compose]# docker network create --driver macvlan --subnet 10.0.0.0/24 --gateway 10.0.0.254 -o parent=ens33 macvlan_1
# 查看docker有什么网络
[root@docker compose]# docker network list # container和macvlan、overlay是需要创建才会出现,其他都是默认有的
NETWORK ID NAME DRIVER SCOPE
ff04f1f2a772 bridge bridge local
77e4bd1d47af compose_lnmp bridge local
fe2a52aeaa07 host host local
d934bcc430b5 macvlan_1 macvlan local
10f9ba31708a none null local
# 开启ens33网卡的混杂模式,也就是开启网卡的多个虚拟interface(接口)
[root@docker compose]# ip link set ens33 promisc on
# 71的机子
[root@docker-71 compose]# ifconfig # 确认自己的网卡
# 创建macvlan网络,指定网段、网关等信息
[root@docker-71 ~]# docker network create --driver macvlan --subnet 10.0.0.0/24 --gateway 10.0.0.254 -o parent=ens33 macvlan_1
# 查看docker-71有什么网络
[root@docker-71 ~]# docker network list
NETWORK ID NAME DRIVER SCOPE
1753cd6d5fcc bridge bridge local
fe2a52aeaa07 host host local
20ea7618f39f macvlan_1 macvlan local
10f9ba31708a none null local
# 开启ens33网卡的混杂模式,也就是开启网卡的多个虚拟interface(接口)
[root@docker-71 ~]# ip link set ens33 promisc on
2.启动容器指定网络
# 70的机子
# 基于新创建的macvlan网络运行一个容器,并指定其IP
[root@docker compose]# docker run -it --network macvlan_1 --ip=10.0.0.200 centos:7
[root@7afc1f78d410 /]# # 注意这里无法走外网
# 71的机子
# 基于新创建的macvlan网络运行一个容器,并指定其IP
[root@docker-71 ~]# docker run -it --network macvlan_1 --ip=10.0.0.201 centos:7
[root@b547ebdbb223 /]#
3.验证
# 互ping
# 70
[root@7afc1f78d410 /]# ping 10.0.0.201
# 71
[root@b547ebdbb223 /]# ping 10.0.0.200
Docker跨主机容器通信之overlay
http://www.cnblogs.com/CloudMan6/p/7270551.html
暂未实操,基于三层;在k8s中体现,使用网络插件flannel
1)准备工作
docker01上:
docker run -d -p 8500:8500 -h consul --name consul progrium/consul -server -bootstrap
设置容器的主机名
consul:kv类型的存储数据库(key:value)
docker01、02上:
vim /etc/docker/daemon.json
{
"hosts":["tcp://0.0.0.0:2376","unix:///var/run/docker.sock"],
"cluster-store": "consul://10.0.0.13:8500",
"cluster-advertise": "10.0.0.11:2376"
}
vim /etc/docker/daemon.json
vim /usr/lib/systemd/system/docker.service
systemctl daemon-reload
systemctl restart docker
2)创建overlay网络
docker network create -d overlay --subnet 172.16.1.0/24 --gateway 172.16.1.254 ol1
3)启动容器测试
docker run -it --network ol1 --name oldboy01 busybox /bin/bash
每个容器有两块网卡,eth0实现容器间的通讯,eth1实现容器访问外网
总结
docker跨节点网络方案:端口映射、host模式、macvlan、overlay
Harbor
docker企业级镜像仓库harbor(vmware 中国团队)
Github地址https://github.com/goharbor/harbor
官方地址:https://github.com/goharbor/harbor/releases
我这里选择离线安装,把包下载下来
1.上传解压缩
[root@docker ~]# ls
anaconda-ks.cfg harbor-offline-installer-v2.5.0.tgz
[root@docker ~]# tar xf harbor-offline-installer-v2.5.0.tgz
[root@docker ~]# ls
anaconda-ks.cfg harbor harbor-offline-installer-v2.5.0.tgz
2.修改文件
[root@docker ~]# cd harbor
[root@docker harbor]# ls
common.sh harbor.v2.5.0.tar.gz harbor.yml.tmpl install.sh LICENSE prepare
[root@docker harbor]# cp harbor.yml.tmpl harbor.yml
[root@docker harbor]# vim harbor.yml
改: 5 hostname: reg.mydomain.com
为: 5 hostname: 192.168.188.70
改: 注释掉https(443)
改: 34 harbor_admin_password: Harbor12345
为: 34 harbor_admin_password: 123456
改: 数据库密码生产上需要改,这是dev就不改了
改: 47 data_volume: /data
为: 47 data_volume: /data/harbordata
[root@docker harbor]# mkdir -p /data/harbordata
3.安装
[root@docker harbor]# ./install.sh
4.验证
[root@docker harbor]# docker ps
# 浏览器输入宿主机的IP地址
操作与阿里云一样
注意:修改docker配置文件,增加如下内容
“insecure-registries”: [ “http://192.168.188.70”]
然后重启服务systemctl restart docker
为什么需要增加此配置呢?docker默认采用https的方式推送镜像,如果不进行此配置,则走https协议,此时我们安装的harbor并不支持https
`shell
互ping
70
[root@7afc1f78d410 /]# ping 10.0.0.201
71
[root@b547ebdbb223 /]# ping 10.0.0.200
## Docker跨主机容器通信之overlay
http://www.cnblogs.com/CloudMan6/p/7270551.html
暂未实操,基于三层;在k8s中体现,使用网络插件flannel
```shell
1)准备工作
docker01上:
docker run -d -p 8500:8500 -h consul --name consul progrium/consul -server -bootstrap
设置容器的主机名
consul:kv类型的存储数据库(key:value)
docker01、02上:
vim /etc/docker/daemon.json
{
"hosts":["tcp://0.0.0.0:2376","unix:///var/run/docker.sock"],
"cluster-store": "consul://10.0.0.13:8500",
"cluster-advertise": "10.0.0.11:2376"
}
vim /etc/docker/daemon.json
vim /usr/lib/systemd/system/docker.service
systemctl daemon-reload
systemctl restart docker
2)创建overlay网络
docker network create -d overlay --subnet 172.16.1.0/24 --gateway 172.16.1.254 ol1
3)启动容器测试
docker run -it --network ol1 --name oldboy01 busybox /bin/bash
每个容器有两块网卡,eth0实现容器间的通讯,eth1实现容器访问外网
总结
docker跨节点网络方案:端口映射、host模式、macvlan、overlay
Harbor
docker企业级镜像仓库harbor(vmware 中国团队)
Github地址https://github.com/goharbor/harbor
官方地址:https://github.com/goharbor/harbor/releases
我这里选择离线安装,把包下载下来
1.上传解压缩
[root@docker ~]# ls
anaconda-ks.cfg harbor-offline-installer-v2.5.0.tgz
[root@docker ~]# tar xf harbor-offline-installer-v2.5.0.tgz
[root@docker ~]# ls
anaconda-ks.cfg harbor harbor-offline-installer-v2.5.0.tgz
2.修改文件
[root@docker ~]# cd harbor
[root@docker harbor]# ls
common.sh harbor.v2.5.0.tar.gz harbor.yml.tmpl install.sh LICENSE prepare
[root@docker harbor]# cp harbor.yml.tmpl harbor.yml
[root@docker harbor]# vim harbor.yml
改: 5 hostname: reg.mydomain.com
为: 5 hostname: 192.168.188.70
改: 注释掉https(443)
改: 34 harbor_admin_password: Harbor12345
为: 34 harbor_admin_password: 123456
改: 数据库密码生产上需要改,这是dev就不改了
改: 47 data_volume: /data
为: 47 data_volume: /data/harbordata
[root@docker harbor]# mkdir -p /data/harbordata
3.安装
[root@docker harbor]# ./install.sh
[外链图片转存中…(img-dtxw5pbd-1674898358081)]
4.验证
[root@docker harbor]# docker ps
# 浏览器输入宿主机的IP地址
[外链图片转存中…(img-9ANhXLsy-1674898358082)]
操作与阿里云一样
注意:修改docker配置文件,增加如下内容
“insecure-registries”: [ “http://192.168.188.70”]
然后重启服务systemctl restart docker
为什么需要增加此配置呢?docker默认采用https的方式推送镜像,如果不进行此配置,则走https协议,此时我们安装的harbor并不支持https