docker实践(1) 入门和springBoot实践部署

2022-04-14 17:11:11 浏览数 (1)

一. 什么是docker

Docker是一个开源项目,诞生于2013年初, 最初是 dotCloud 公司内部的一个业余项目。 它基于Google公司推出的Go语言实现。项目后来加入了Linux基金会, 遵从了 Apache 2.0 协议,项目代码在GitHub上进行维护。 Docker 自开源后受到广泛的关注和讨论, 以至于dotCloud公司后来都改名为Docker Inc。 Redhat 已经在其RHEL6.5中集中支持Docker;Google也在其PaaS产品中广泛应用。

Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案。Docker的基础是Linux容器(LXC) 等技术。在LXC的基础上Docker进行了进一步的封装,让用户不需要去关心容器的管理, 使得操作更为简便。 用户操作Docker的容器就像操作一个快速轻量级的虚拟机一样简单。

docker有效地将单个操作系统管理的资源划分到独立地组中,以便更好地在各个独立组直接平衡有冲突的资源使用需求。

下面的图片比较了Docker和传统虚拟化方式的不同之处, 可见容器是在操作系统层面上实现虚拟化, 直接复用本地主机的操作系统, 而传统方式则是在硬件层面实现。

容器和 VM(虚拟机)的主要区别是,容器提供了基于进程的隔离,而虚拟机提供了资源的完全隔离。虚拟机可能需要一分钟来启动,而容器只需要一秒钟或更短。容器使用宿主操作系统的内核,而虚拟机使用独立的内核。

Docker 的局限性之一是,它只能用在 64 位的操作系统上。

二. 为什么使用docker


作为一种新兴的虚拟化方式,Docker跟传统的虚拟化方式相比具有众多的优势。

首先,秒级实现:Docker容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多。

其次,高效使用资源: Docker 对系统资源的利用率很高, 一台主机上可以同时运行数千个Docker容器。容器除了运行其中应用外, 基本不消耗额外的系统资源, 使得应用的性能很高, 同时系统的开销尽量小。 传统虚拟机方式运行10个不同的应用就要起10个虚拟机, 而Docker只需要启动10 个隔离的应用即可。

具体说来, Docker 在如下几个方面具有较大的优势:

2.1 更快速的交付和部署 对开发和运维(devop) 人员来说, 最希望的就是一次创建或配置, 可以在任意地方正常运行。开发者可以使用一个标准的镜像来构建一套开发容器, 开发完成之后, 运维人员可以直接使用这个容器来部署代码。Docker可以快速创建容器, 快速迭代应用程序, 并让整个过程全程可见, 使团队中的其他成员更容易理解应用程序是如何创建和工作的。Docker容器很轻很快!容器的启动时间是秒级的, 大量地节约开发、测试、 部署的时间。

2.2 更高效的虚拟化

Docker 容器的运行不需要额外的hypervisor支持, 它是内核级的虚拟化, 因此可以实现更高的性能和效 率。

2.3 轻松的迁移和扩展 Docker 容器几乎可以在任意的平台上运行, 包括物理机、 虚拟机、 公有云、 私有云、 个人电脑、 服务器 等。 这种兼容性可以让用户把一个应用程序从一个平台直接迁移到另外一个。

2.4 更简单的管理 使用 Docker, 只需要小小的修改, 就可以替代以往大量的更新工作。 所有的修改都以增量的方式被分发和更新, 从而实现自动化并且高效的管理。

对比传统虚拟机总结:

三. docker体系结构

docker使用C/S 架构,docker daemon作为 server 端接受 client 的请求,并处理(创建、运行、分发容器),他们可以运行在一个机器上,也通过sockerts或者 RESTful API 通信。

Docker daemon 一般在宿主主机后台运行,用户使用 client 而直接跟 daemon 交互。Docker client以系统做 bin 命令的形式存在,用户用 docker 命令来跟 docker daemon 交互。

3.1 Docker的内部组件

docker有三个内部组件 • docker images 镜像 • docker registries 仓库 • docker containers 容器

3.1.1 镜像images

docker images就是一个只读的模板。比如:一个image 可以包含一个ubuntu的操作系统,里面安装了apache或者你需要的应用程序。images可以用来创建docker containers,docker提供了一个很简单的机制来创建images或者更新现有的images,你甚至可以直接从其他人那里下载一个已经做好的images。 镜像工作原理: 每个 docker 都有很多层次构成,docker使用union file systems 将这些不同的层结合到一个image中去。AUFS (AnotherUnionFS)是一种Union FS, 简单来说就是支持将不同目录挂载到同一个虚拟文件 系统下(unite several directories into a single virtual filesystem)的文件系统,更进一步的理解, AUFS支持为每一个成员目录(类似Git Branch)设定 readonly、readwrite和 whiteout-able 权限,同时 AUFS里有一个类似分层的概念,对readonly 权限的 branch 可以逻辑上进行修改(增量地,不影响readonly部分的)。通常Union FS有两个用途,一方面可以实现不借助LVM、RAID将多个disk 挂到同一个目录下,另一个更常用的就是将一个 readonly 的branch 和一个 writeable 的 branch 联合在一起,LiveCD 正是基于此方法可以允许在 OS image 不变的基础上允许用户在其上进行一些写操作。Docker在AUFS上构建的 container image 也正是如此。

3.1.2 仓库registries

仓库是集中存放镜像文件的场所。 有时候会把仓库和仓库注册服务器(Registry) 混为一谈, 并不严格区分。实际上,仓库注册服务器上往往存放着多个仓库, 每个仓库中又包含了多个镜像, 每个镜像有不同的标签(tag) 。 仓库分为公开仓库(Public) 和私有仓库(Private)两种形式。 最大的公开仓库是Docker Hub, 存放了数量庞大的镜像供用户下载。 国内的公开仓库包括Docker Pool等, 可以提供大陆用户更稳定快速的访问。 当然, 用户也可以在本地网络内创建一个私有仓库。当用户创建了自己的镜像之后就可以使用push命令将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候, 只需要从仓库上pull下来就可以了。 *注:Docker仓库的概念跟Git类似, 注册服务器可以理解为GitHub这样的托管服务。

3.1.3 容器containers

Docker利用容器来运行应用。容器是从镜像创建的运行实例。 它可以被启动、 开始、 停止、删除。 每个容器都是相互隔离的、 保证安全 的平台。 可以把容器看做是一个简易版的Linux环境( 包括root用户权限、进程空间、 用户空间和网络空间等) 和运行在其中的应用程序。 *注:镜像是只读的, 容器在启动的时候创建一层可写层作为最上层。

3.1.4、容器工作原理

当我们运行 docker run -i -t ubuntu /bin/bash 命令时,docker 在后台运行的操作如下:

  1. 拉取镜像:如果本地有 ubuntu 这个 image 就从它创建容器,否则从公有仓库下载
  2. 创建容器:从image 创建容器
  3. 挂载文件:分配一个文件系统,并在只读的 image 层外面挂载一层可读写的层
  4. 网络桥接:从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  5. 分配 IP:从地址池配置一个ip 地址给容器
  6. 执行命令:执行你指定的程序,在这里启动一个/bin/bash进程
  7. 输入输出: -i -t 指定标准输入和输出

3.2 Docker底层技术

docker 底层的2个核心技术分别是Namespaces和 Control groups 以下内容摘自 InfoQ Docker,自 1.20 版本开始 docker 已经抛开 lxc,不过下面的内容对于理解 docker还是有很大帮助。

1)pid namespace 不同用户的进程就是通过 pid namespace 隔离开的,且不同 namespace 中可以有相同 pid。所有的LXC进程在docker 中的父进程为 docker 进程,每个 lxc 进程具有不同的 namespace。同时由于允许嵌套,因此可以很方便的实现Docker in Docker。

2) net namespace 有了 pid namespace, 每个 namespace中的pid 能够相互隔离,但是网络端口还是共享 host 的端口。 网络隔离是通过 net namespace实现的, 每个net namespace 有独立的 network devices, IPaddresses, IP routing tables, /proc/net目录。这样每个container 的网络就能隔离开来。docker默认采用veth的方式将 container 中的虚拟网卡同 host 上的一个 docker bridge: docker0 连接在一起。

3) ipc namespace

container 中进程交互还是采用linux常见的进程间交互方法(interprocess communication - IPC),包括常见的信号量、消息队列和共享内存。然而同VM不同的是,container的进程间交互实际上还是host上具有相同 pid namespace 中的进程间交互,因此需要在IPC 资源申请时加入namespace 信息 -每个IPC 资源有一个唯一的 32 位 ID。

4) mnt namespace 类似 chroot,将一个进程放到一个特定的目录执行。mnt namespace允许不同namespace 的进程看到的文件结构不同,这样每个namespace中的进程所看到的文件目录就被隔离开了。同chroot不同,每个namespace 中的 container 在/proc/mounts的信息只包含所在 namespace 的 mount point。

5) uts namespace

UTS("UNIX Time-sharing System") namespace 允许每个 container 拥有独立的 hostname和domainname,使其在网络上可以被视作一个独立的节点而非Host上的一个进程。

6) user namespace 每个 container 可以有不同的 user 和 group id, 也就是说可以在 container 内部用 container内部的 用户执行程序而非 Host 上的用户。Control groups 主要用来隔离各个容器和宿主主机的资源利用。

四. docker安装并运行spring boot

4.1 安装

对于CentOS6,可以使用 EPEL库安装Docker,命令如下

$ sudo yum install http://mirrors.yun-idc.com/epel/6/i386/epel-release-6-8.noarch.rpm

$ sudo yum install docker-io

CentOS7系统CentOS-Extras 库中已带Docker,可以直接安装:

  1. $ sudo yum install docker

4.2 启动 Docker 服务

安装之后启动Docker 服务,并让它随系统启动自动加载。

$ sudo service docker start

$ sudo chkconfig docker on

4.3 下载官方的 CentOS 镜像到本地

必须先启动docker才能下载:

docker pull centos

4.4 运行一个 Docker 容器:

# docker run -i -t centos /bin/bash

[root@dbf66395436d /]#

我们可以看到,CentOS 容器已经被启动,并且我们得到了 bash 提示符。在 docker 命令中我们使用了 “-i 捕获标准输入输出”和 “-t 分配一个终端或控制台”选项。若要断开与容器的连接,输入 exit。

[root@cd05639b3f5c /]# cat /etc/redhat-release

CentOSLinux release 7.3.1611(Core) [root@cd05639b3f5c /]#exit exit [root@localhost ~]#

4.5 利用Dockerfile来创建镜像

我们安装java8, 基础镜像是centos。新建一个目录和一个Dockerfile

mkdir java8 cd java8

Dockerfile 中每一条指令都创建镜像的一层:

代码语言:javascript复制
# Pull base image.  
FROM centos  
  
# 设置jdk 的环境变量。
ENV JAVA_HOME /usr/java8
  
# 复制/usr/java文件到镜像中(jdk1.8.0_66 目录和Dockerfile在同一目录)
#ADD jdk1.8.0_66 /usr/java8
COPY jdk1.8.0_66 /usr/java8
# Define default command.  
CMD ["bash"] 

Dockerfile 基本的语法是 使用 # 来注释 FROM 指令告诉Docker使用哪个镜像作为基础 接着是维护者的信息 RUN 开头的指令会在创建中运行。

Build构建:

docker build -t='java8' ./

语法:docker build [OPTIONS] PATH| URL| - 常见选项: -t 设置镜像的名称和TAG,格式为name:tag -f Dockerfile的名称,默认为PATH/Dockerfile 例子:docker build -f ~/php.Dockerfile . 注意:PATH是编译镜像使用的工作目录,Docker Daemon在编译开始时,会扫描PATH中的所有文件,可以在编译目录中加入.dockerignore过滤不需要的文件

Dockerfile 的文件名并不要求必须为 Dockerfile,而且并不要求必须位于上下文目录中,比如

$ docker build -f /path/to/a/Dockerfile .

然后查看镜像:

docker images

REPOSITORY TAG IMAGE ID CREATED SIZE java8 latest 2abc9f66aba0 4 minutes ago 484.9 MB docker.io/centos latest 36540f359ca3 9 days ago 192.5 MB

运行已经构件好的镜像:

docker run -it java8 /bin/bash

然后echo $JAVA_HOME 看看。

4.6 构建sping-boot

这个java jar 包就是turing-api-1.0-SNAPSHOT.jar 简单http服务:

直接下载jar包:https://github.com/huangguisu/k8s.git

https://github.com/huangguisu/k8s/blob/master/docker/springboot/turing-api-1.0-SNAPSHOT.jar

Dockerfile:

代码语言:javascript复制
#基础镜像:仓库是java,标签用java8 
FROM java8  
#当前镜像的维护者和联系方式  
MAINTAINER guisu guisu@example.com  
#将打包好的spring程序拷贝到容器中的指定位置  
ADD turing-api-1.0-SNAPSHOT.jar /opt/turing-api-1.0-SNAPSHOT.jar
#容器对外暴露8080端口  
EXPOSE 8080  
#容器启动后需要执行的命令  
CMD $JAVA_HOME/bin/java  -jar /opt/turing-api-1.0-SNAPSHOT.jar

构建:

docker build-t="spingboot"

启动:

本地80端口映射到容器的8081端口

sudo docker run-d -p 80:8080 spingboot

然后就可以访问主机的80端口了。

五. 问题

1、具体错误:Error response from daemon: conflict: unable to delete b227a9dfe196 (must be forced) - image is being used by stopped container 7b080f1e1f17

错误解析:这是由于要删除的目标镜像中有容器存在,故无法删除镜像

解决办法:先删除镜像中的容器,再删除该镜像。

2、docker无法启动:

通过systemctl status docker.service 查看错误信息:

docker.service failed to run 'start' task: No such file or directory

解决:

这个一般是/usr/lib/systemd/system/docker.service的

EnvironmentFile项目某个文件不存在:比如/run/docker_opts.env文件不存在,就启动报这个错误。

0 人点赞