软件开发最大的麻烦事之一,就是环境配置。用户计算机的环境都不相同,你怎么知道自家的软件,能在那些机器跑起来?
用户必须保证两件事:操作系统的设置,各种库和组件的安装。只有它们都正确,软件才能运行。举例来说,安装一个 Python 应用,计算机必须有 Python 引擎,还必须有各种依赖,可能还要配置环境变量。
如果某些老旧的模块与当前环境不兼容,那就麻烦了。开发者常常会说:“它在我的机器可以跑了”(It works on my machine),言下之意就是,其他机器很可能跑不了。
环境配置如此麻烦,换一台机器,就要重来一次,旷日费时。很多人想到,能不能从根本上解决问题,软件可以带环境安装?也就是说,安装的时候,把原始环境一模一样地复制过来。
虚拟机(virtual machine)就是带环境安装的一种解决方案。它可以在一种操作系统里面运行另一种操作系统,比如在 Windows 系统里面运行 Linux 系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。
虽然用户可以通过虚拟机还原软件的原始环境。但是,这个方案有几个缺点。
(1)资源占用多
虚拟机会独占一部分内存和硬盘空间。它运行的时候,其他程序就不能使用这些资源了。哪怕虚拟机里面的应用程序,真正使用的内存只有 1MB,虚拟机依然需要几百 MB 的内存才能运行。
(2)冗余步骤多
虚拟机是完整的操作系统,一些系统级别的操作步骤,往往无法跳过,比如用户登录。
(3)启动慢
启动操作系统需要多久,启动虚拟机就需要多久。可能要等几分钟,应用程序才能真正运行。
由于虚拟机存在这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩写为 LXC)。
Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。
由于容器是进程级别的,相比虚拟机有很多优势。
(1)启动快
容器里面的应用,直接就是底层系统的一个进程,而不是虚拟机内部的进程。所以,启动容器相当于启动本机的一个进程,而不是启动一个操作系统,速度就快很多。
(2)资源占用少
容器只占用需要的资源,不占用那些没有用到的资源;虚拟机由于是完整的操作系统,不可避免要占用所有资源。另外,多个容器可以共享资源,虚拟机都是独享资源。
(3)体积小
容器只要包含用到的组件即可,而虚拟机是整个操作系统的打包,所以容器文件比虚拟机文件要小很多。
总之,容器有点像轻量级的虚拟机,能够提供虚拟化的环境,但是成本开销小得多。
Docker 就像是一个轻量级的虚拟机,Docker是一个轻量级的容器,我们可以将环境交给 Docker 来管理,当我们需要移植我们的产品的时候,就可以将环境整个的迁移到另一台主机上,而不向虚拟机一样,迁移一台虚拟机极其资源;
Docker 是一个开源的应用容器引擎,基于 Go 语言开发。
Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。
总之:Docker相较于虚拟机来说具有更加轻量,启动更快,效率更高,可移植性更强等优势,实现了“一次封装,到处运行”,不用在关心环境的不一致问题
1、容器技术
Linux容器技术很早就有了,比较有名的是被集成到主流Linux内核中的LXC项目。容器通过对操作系统的资源访问进行限制,构建成独立的资源池,让应用运行在一个相对隔离的空间里,同时容器间也可以进行通信。
容器技术对比虚拟化技术,容器比虚拟化更轻量级,对资源的消耗小很多。容器操作也更快捷,启动和停止都要比虚拟机快。但Docker容器需要与主机共享操作系统内核,不能像虚拟机那样运行独立的内核。
Docker是一个基于LXC技术构建的容器引擎,基于GO语言开发,遵循Apache2.0协议开源。Docker的发展得益于为使用者提供了更好的容器操作接口。包括一系列的容器,镜像,网络等管理工具,可以让用户简单的创建和使用容器。
Docker支持将应用打包进一个可以移植的容器中,重新定义了应用开发,测试,部署上线的过程,核心理念就是 Build once, Run anywhere。
Docker容器技术的典型应用场景是开发运维上提供持续集成和持续部署的服务。
下面我们开始介绍Docker中的几个基本概念。
2、镜像
Docker的镜像概念类似于虚拟机里的镜像,是一个只读的模板,一个独立的文件系统,包括运行容器所需的数据,可以用来创建新的容器。
镜像可以基于Dockerfile构建,Dockerfile是一个描述文件,里面包含若干条命令,每条命令都会对基础文件系统创建新的层次结构。
用户可以通过编写Dockerfile创建新的镜像,也可以直接从类似github的Docker Hub上下载镜像使用。
3、容器
Docker容器是由Docker镜像创建的运行实例。Docker容器类似虚拟机,可以支持的操作包括启动,停止,删除等。每个容器间是相互隔离的,但隔离的效果比不上虚拟机。容器中会运行特定的应用,包含特定应用的代码及所需的依赖文件。
在Docker容器中,每个容器之间的隔离使用Linux的 CGroups 和 Namespaces技术实现的。其中 CGroups 对CPU,内存,磁盘等资源的访问限制,Namespaces 提供了环境的隔离。
4、仓库
如果你使用过 git 和 github 就很容易理解Docker的仓库概念。Docker仓库相当于一个 github 上的代码库。
Docker 仓库是用来包含镜像的位置,Docker提供一个注册服务器(Registry)来保存多个仓库,每个仓库又可以包含多个具备不同tag的镜像。Docker运行中使用的默认仓库是 Docker Hub 公共仓库。
仓库支持的操作类似 git,创建了新的镜像后,我们可以 push 提交到仓库,也可以从指定仓库 pull 拉取镜像到本地。
安装docker
使用国内 daocloud 一键安装命令:
curl -sSL https://get.daocloud.io/docker | sh
通过将用户添加到docker用户组可以将sudo去掉,命令如下
sudo groupadd docker #添加docker用户组
sudo gpasswd -a $USER docker #将登陆用户加入到docker用户组中
newgrp docker #更新用户组
然后我们将通过检查版本来验证安装是否成功:
docker --version
安装docker-compose
GitHub源,下载很慢
sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
使用 DaoCloud源下载
sudo curl -L https://get.daocloud.io/docker/compose/releases/download/1.22.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
接下来我们将设置权限:
sudo chmod +x /usr/local/bin/docker-compose
然后我们将通过检查版本来验证安装是否成功:
docker-compose --version
命令 | 解释 |
---|---|
systemctl start docker | 启动 Docker |
systemctl stop docker | 停止docker |
systemctl restart docker | 查看docker状态 |
systemctl status docker | 开机启动 |
systemctl enable docker | 开机启动 |
docker info | 查看docker概要信息 |
docker --help | 查看docker帮助文档 |
命令 | 解释 |
---|---|
docker images | 查看本地存在的镜像 这些镜像都存储在 Docker 的 /var/lib/docker目录下 |
docker search 镜像名称 | 从网络中查找需要的镜像 |
docker pull 镜像名称 | 将远程镜像拉取到本地 |
docker rmi [-f] 镜像ID | (强制)删除本地镜像 |
docker rmi -f 镜像名1:TAG 镜像名2:TAG | 删除多个镜像 |
docker rmi -f $(docker images -qa) | 删除所有镜像 |
使用:
docker images
docker search 镜像名字
查看容器
命令 | 解释 |
---|---|
docker ps | 查看正在运行的容器 |
docker ps -a | 查看所有容器 |
docker ps -l | 查看最后一次运行的容器 |
docker ps -f status=exited | 查看停止的容器 |
创建与启动容器
docker run
-i:表示运行容器
-t:表示容器启动后会进入其命令行。加入这两个参数后,容器创建就能登录进去。即分配一个伪终端。
-v:表示目录映射关系(前者是宿主机目录,后者是映射到宿主机上的目录),可以使用多个-v做多个目录或文件映射。注意:最好做目录映射,在宿主机上做修改,然后共享到容器上。
-d:在run后面加上-d参数,则会创建一个守护式容器在后台运行(这样创建容器后不会自动登录容器,如果只加-i -t两个参数,创建后就会自动进去容器)。
-p:表示端口映射,前者是宿主机端口,后者是容器内的映射端口。可以使用多个-p做多个端口映射
–name:为创建的容器命名。
以交互的方式创建容器
以交互式方式创建并启动容器,启动完成后,直接进入当前容器。使用exit命令退出容器。需要注意的是以此种方式启动容器,如果退出容器,则容器会进入停止状态。可以理解成交互式容器是前台容器。
docker run -it --name=容器名称 镜像名称:标签 /bin/bash
比如:docker run -it --name=mycentos centos:7 /bin/bash
docker run:表示创建容器
-it:表示运行容器并进入它的命令行
–name=mycentos:给当前的容器命名
centos:7:使用该镜像创建
/bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash
创建后台容器
创建一个守护式容器;如果对于一个需要长期运行的容器来说,我们可以创建一个守护式容器。
创建守护式容器:docker run -id --name=容器名字 centos:7
登录守护式容器:docker exec -it 容器名称 (或者容器ID) /bin/bash
docker stop 容器名称(或者容器ID)
docker start 容器名称(或者容器ID)
将宿主机的文件拷贝的容器的指定目录下
宿主机向容器拷贝:docker cp
需要拷贝的文件或目录 容器名称:容器目录
容器向宿主机拷贝:docker cp
容器名称:容器目录 需要拷贝的文件或目录
例如: docker cp /opt/tomcat8080/conf/web.xml mycentos7_2:/usr/local/
目录挂载
我们可以在创建容器的时候,将宿主机的目录与容器内的目录进行映射,这样我们就可以通过修改宿主机某个目录的文件从而去影响容器。
创建容器并挂载:docker run -id --name=mycentos1 -v
宿主机的目录:容器的目录 centos:7
创建容器并挂载 linux 中的 /usr/local/myhtml 到,容器的 /usr/local/myhtml 目录
docker run -id --name=mycentos1 -v /usr/local/myhtml:/usr/local/myhtml centos:7
如果你共享的是多级的目录,可能会出现权限不足的提示。
这是因为CentOS7中的安全模块selinux把权限禁掉了,我们需要添加参数
--privileged=true
来解决挂载的目录没有权限的问题
docker run -id --privileged=true --name=mycentos1 -v /usr/local/myhtml:/usr/local/myhtml centos:7
查看容器 IP 地址
我们可以通过 docker inspect
容器名称 来查看容器运行时的各种数据
当然我们也可以通过 docker inspect --format='{{属性的键}}'
容器名称 来查看单独某个属性
例如我们单独查看 IP 地址
docker inspect --format='{{.NetworkSettings.IPAddress}}' mycentos1
删除容器
docker rm 容器名称
当我们在删除一个正在运行的容器时,会报错;需要先关闭这个容器才可删除
当然我们也可以用 docker rm -f
容器名称 来强制删除一个容器
docker pull centos/mysql-57-centos7
docker run -di --name=mysql5.7 -p 33306:3306 -e MYSQL_ROOT_PASSWORD=123456 centos/mysql-57-centos7
选项 | 解释 |
---|---|
-p 宿主机端口:容器端口 | 指定端口映射 |
-e … | 传递环境变量,这里表示 MySQL 的密码为 123456,账户默认为 root |
docker pull tomcat:7-jre7
docker run -di --name=mytomcat -p 9000:8080 -v /usr/local/webapps:/usr/local/tomcat/webapps tomcat:7-jre7
docker pull nginx
docker run -di --name=mynginx -p 80:80 nginx
拉取镜像
docker pull redis
创建容器
docker run -di --name=myredis -p 6379:6379 redis
保存容器为镜像:docker commit
容器名称 镜像名称
例如:docker commit mynginx mynginx_i
保存镜像为 tar 文件:docker save –o tar
文件名 镜像名
docker save -o mynginx.tar mynginx_i
加载 tar 文件为镜像:docker load -i tar
文件名
docker load -i mynginx.tar
前面已经知道了,想要获得一个镜像可以从 Docker 中心仓库拉取,但是如果我们想要自己制作一个镜像,这时就需要 Dockerfile 了;
Docker 实际上就是一个文本文件,我们可以在这个文本文件里输入一些命令或参数,来定制我们需要的镜像,之后 Docker 可以通过读取 Dockerfile 文件的描述来构建镜像;
Dockerfile 常用的命令:
命令 | 作用 |
---|---|
FROM image_name:tag | 定义了使用哪个基础镜像启动构建流程 |
MAINTAINER user_name | 声明镜像的创建者 |
ENV key value | 设置环境变量 (可以写多条) |
RUN command | 是Dockerfile的核心部分(可以写多条) RUN 后面输入 linux 命令 |
ADD source_dir/file dest_dir/file | 将宿主机的文件复制到容器内,如果是一个压缩文件,将会在复制后自动解压 |
COPY source_dir/file dest_dir/file | 和ADD相似,但是如果有压缩文件并不能解压 |
WORKDIR path_dir | 设置工作目录(进入容器之后默认的工作路径) |
下面我们以创建一个具有 jdk1.8 的 centos7 系统为例制作一个镜像
mkdir –p /usr/local/dockerjdk8
#依赖的镜像的名称和版本
FROM centos:7
#指定镜像创建者信息
MAINTAINER ayi
#设置工作目录,进入容器之后默认的工作目录,即进入 centos7 应用容器之后默认的工作目录为 /usr
WORKDIR /usr
#RUN yum install -y glibc.i686 制作完镜像之后,用镜像安装的centos7可能会有问题,缺少 glibc.i686,所以这里我们可以安装一下,也可以稍后安装
#RUN 后面是我们在构建镜像时需要执行的命令,下面是创建一个文件夹
RUN mkdir /usr/local/java
#ADD 是相对路径jar,把java添加到容器中
ADD jdk-8u144-linux-i586.tar.gz /usr/local/java/
#配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_144
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH J A V A H O M E / l i b / d t . j a r : JAVA_HOME/lib/dt.jar: JAVAHOME/lib/dt.jar:JAVA_HOME/lib/tools.jar: J R E H O M E / l i b : JRE_HOME/lib: JREHOME/lib:CLASSPATH
ENV PATH J A V A H O M E / b i n : JAVA_HOME/bin: JAVAHOME/bin:PATH
docker build -t='jdk1.8' .
注意:这里的 . 不要拉下了,“.” 的意思是指定相对目录为当前目录
-t:指定构建的镜像的名称
5.查看镜像是否构建成功
docker images
最后,运行容器进行测试
执行 java -version 发现报错,那么我们需要安装一下 glibc
yum install -y glibc.i686
docker pull registry
docker run -di --name=registry -p 5000:5000 registry
http://192.168.211.144:5000/v2/_catalog,IP 改为你们自己的宿主机 IP 即可
修改 daemon.json,
Docker 默认我们的私有仓库是不安全的,所以我们要配置一下,就和之前配置镜像加速一样
vi /etc/docker/daemon.json
"insecure-registries":["192.168.211.144:5000"]
IP,改为你们自己的宿主机 IP
重启docker 服务
systemctl restart docker
docker tag 自己的镜像名称 宿主机IP:私有服务容器的端口/jdk1.8
docker tag jdk1.8 192.168.17.132:5000/jdk1.8
5000:是我们之前创建 registry 的时候映射的端口,是宿主机的端口
docker start registry
docker push 192.168.211.144:5000/jdk1.8
docker pull 192.168.211.144:5000/jdk1.8
我整理了16张图,彻底搞懂Docker容器!
Docker 入门教程
Docker 概念及基本用法
什么是 Docker?为什么会有 Docker?Docker 的优势?
Ubuntu下安装docker和docker-compose