查看原文
其他

技术变化那么快,学 Docker 看这篇就够了

作者:Fysddsw_lc 搜云库技术团队 2019-04-07
搜云库技术团队关注送4000G架构师视频关注

来源:juejin.im/post/5bffdb645188251b8a270058

整编:搜云库技术团队(公众号ID:souyunku)

2013年发布至今, Docker 是近年来非常火的容器技术, 一直广受瞩目,被认为可能会改变软件行业。而且啊 Docker不仅仅是linux Redhat 和Canonical等Linux巨头眼里的宠儿,微软等专有软件公司也在热烈拥抱 Docker,所以就知道 Docker 为啥这么火了。

公众号回复 1024 可获 4000G 架构师视频

我相信有很多人对 Docker 感兴趣,都想学学 Docker,毕竟天天听、毕竟这么火、毕竟技多不压身。许多人并不清楚 Docker 到底是什么,要解决什么问题,好处又在哪里?接下来我就详细解释一下,帮助大家理解它,还带有简单易懂的实例,教你如何将它用于日常开发。

什么是容器

一句话概括容器:容器就是将软件打包成标准化单元,以用于开发、交付和部署。

1、容器镜像是轻量的,可执行的独立软件包,包含软件运行所需的所有内容:代码,运行时环境,系统工具,系统库和设置。

2、容器化软件适用于基于Linux适用于基于Linux和Windows的应用,在任何环境中都能够始终如一地运行。

3、容器赋予了软件独立性,使其免受外在环境差异(例如,开发和预演环境的差异)的影响,从而有助于减少团队间在相同基础设施上运行不同软件时的冲突。

我觉得容器就是一个存放东西的地方,就像房子可以装各种家具,暑假可以放各种书。我们现在所说的容器存放的东西可能更偏向于应用比如网站,程序甚至是系统环境。

虚拟机和容器

虚拟机

虚拟机就是带环境安装的一种解决方案,他可以再一种操作系统里面运行另一种操作系统,比如在Windows系统里面运行Linux系统,应用程序对此毫无感知,因为虚拟机看上去跟真是系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。

虽然用户可以通过虚拟机还原团建的原始环境。但是如下缺点。

(1)资源占用多

虚拟机会独占一部分内存和硬盘空间。他运行的时候,其他程序就不能使用这些资源了。哪怕虚拟机里面的应用程序,真正使用的内存只有1MB,虚拟机却需要几百MB的内存才能运行。一个系统一般只支持几十个虚拟机。

(2)冗余步骤多

虚拟机是完整的操作系统,一些系统级别的操作步骤,往往无法跳过,比如用户登陆。

(3)启动慢

启动系统需要多久,启动虚拟机就需要多久。可能要等几分钟,应用程序才能真正运行。

Linux容器

由于虚拟机存在这些缺点,Linux发展出了另一种虚拟化技术,Linux容器。

Linux容器不是模拟一个完整的操作系统,而是对程序进行隔离。或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。

由于容器是进程级别的,相比虚拟机有很多优势。

(1)启动快

容器里面的应用,直接就是底层系统的一个进程,而不是虚拟机内部的进程。所以,启动容器相当于启动本机的一个进程,而不是启动一个操作系统,速度就快很多。

(2)资源占用少

容器只占用需要的资源,不占用那些没有用到的资源;虚拟机由于是完整的操作系统,不可避免要占用所有资源。另外,多个容器可以共享资源,虚拟机都是独享资源。一个单机上支持上千个容器。

(3)体积小

容器只要包含用到的组件即可,而虚拟机是整个操作系统的打包,所以容器文件比虚拟机文件要小很多。

两者对比

传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程,容器虚拟化的是操作系统而不是硬件,容器之间是共享同一套操作系统资源的。虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统。因此容器的隔离级别会稍低一些。

简单来说,容器和虚拟机具有相似的资源隔离和分配优势,但功能有所不同,因为容器虚拟化的是操作系统,而不是硬件,因此容器更容易移植,效率也更高。而容器的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更轻便。

容器是一个应用层抽象,用于将代码和依赖资源打包在一起。多个容器可以在同一台机器上运行,共享操作系统内核,但各自作为独立的进程在用户空间中运行。与虚拟机相比,容器占用的空间交少,瞬间就能完成启动。

虚拟机是一个物理硬件层抽象,用于将一台服务器变成多台服务器。管理程序允许多个vm在一台机器上运行。每个vm都包含一整套操作系统,一个或多个应用,必要的二进制文件和库资源,因此占用大量空间。而vm启动也非常缓慢。

什么是Docker

Docker是属于Linux容器的一种封装,提供简单易用的容器使用接口,他是目前最流行的Linux容器解决方案。

Docker 将应用程序与该程序的依赖,打包在一个文件里。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了Docker,就不用担心环境问题。

总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。

Docker基本概念

Docker 中有三个核心概念:Image、Container、Repository。

  • 镜像(Image)

  • 容器(Container)

  • 仓库(Repository)

理解了这三个概念,就理解了 Docker 的整个生命周期,

镜像(Image)一个特殊的文件系统

操作系统分为内核和用户空间。对于Linux而言,内核启动后,会挂载root文件系统为其提供用户空间支持。而Docker镜像(Image),就相当于一个root文件系统。

Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。 镜像不包含任何动态数据,其内容在构建之后也不会被改变。

镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。

  分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。

  和windows的那种iso镜像相比,Docker中的镜像的概念不会陌生。但是windows的那种iso镜像相比,Docker中的镜像是分层的,可复用的,而非简单的一堆文件碟在一起(类似于一个压缩包的源码和一个git仓库的区别)

容器(Container)

—镜像运行时的实体


镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

      容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。前面讲过镜像使用的是分层存储,容器也是如此。

  容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。

  容器的存在离不开镜像的支持,他是镜像运行时的一个载体(类似于实例和类的关系)。依托Docker的虚拟化技术,给容器创建了独立的端口,进程,文件等空间,Container就是一个宿机隔离“容器”。容器可宿主机之间可以进行port,volumes,network等通信。

仓库(Repository)——集中存放镜像文件的地方

 镜像构建完成后,可以很容易的在当前宿主上运行,但是, 如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry就是这样的服务。

  一个 Docker Registry中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。所以说:镜像仓库是Docker用来集中存放镜像文件的地方类似于我们之前常用的代码仓库。

  通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本 。我们可以通过 <仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签.。

  Docker 的仓库和 git 的仓库比较相似,拥有仓库名、tag。在本地构建完镜像之后,即可通过仓库进行镜像的分发。常用的 Docker hub 有 https://hub.docker.com/ 、 https://cr.console.aliyun.com/ 等。

Docker 作用

1.多环境的部署切换

业务中王湾需要区分开发环境与线上环境,利用Docker能原封不动的将开发环境中的代码与环境原封不动无污染的迁移到线上环境,配合一定的自动胡流程即可自哦对那个发动。

2.复杂环境一键配置

某些场景下可能会配一些超级复杂的环境,这个时候可以对Docker对环境配置做封装,直接生成镜像,让大家低成本使用。

3.持续集成单元测试

类似于 travis-ci 这种

4.同应用多版本隔离,文件隔离

比如这个项目依赖java 7 ,那个项目依赖java 8,同一个服务器上跑了100个陈永,可以用Docker建立隔离开,防止互相传染。

5.云构建

同一个仓库下不同人开发往往会遇到不同的人使用不同的 包版本且自己根本不知道与别人不一样,最终导致发布之后产生线上问题。利用 Docker 可以在云端新建容器,远程 无污染、低成本 构建代码,实现 不同人用的一定是同一个版本

6.省钱

低成本安全超售

相关命令

安装

Docker 的安装是非常便捷的,在 macOS、ubuntu 等下面都有一键式安装工具或者脚本。更多可以参考 Docker 官方教程。

下面简单介绍一下ubuntu下的安装。

Docker 支持以下的 Ubuntu 版本:

  • Ubuntu Precise 12.04 (LTS)

  • Ubuntu Trusty 14.04 (LTS)

  • Ubuntu Wily 15.10

Docker 要求 Ubuntu 系统的内核版本高于 3.10 ,查看本页面的前提条件来验证你的 Ubuntu 版本是否支持 Docker。

通过 uname -r 命令查看你当前的内核版本

runoob@runoob:~$ uname -r

1.选择国内的云服务商,这里选择阿里云为例

  1. curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/internet | sh -复制代码

2.安装所需要的包

  1. sudo apt-get install linux-image-extra-$(uname -r) linux-image-extra-virtual复制代码

3.添加使用 HTTPS 传输的软件包以及 CA 证书

  1. sudo apt-get update

  2. sudo apt-get install apt-transport-https ca-certificates复制代码

4.添加GPG密钥

  1. sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D复制代码

5.添加软件源

  1. echo "deb https://apt.dockerproject.org/repo ubuntu-xenial main" | sudo tee /etc/apt/sources.list.d/docker.list复制代码

6.添加成功后更新软件包缓存

  1. sudo apt-get update复制代码

7.安装 docker

  1. sudo apt-get install docker-engine复制代码

8.启动 docker

  1. sudo systemctl enable docker

  2. sudo systemctl start docker复制代码

寻找基础镜像

DockerHub 等网站都提供了众多镜像,一般情况下我们都会从它那找个镜像作为基础镜像,然后再进行我们的后续操作。

拉取基础镜像

当我们在本地主机上使用一个不存在的镜像时 Docker 就会自动下载这个镜像。如果我们想预先下载这个镜像,我们可以使用 docker pull 命令来下载它。

利用docker pull 命令即可从相关 hub 网站上拉取镜像到本地。同时在拉的过程中就能看到是按照多个 “层” 去拉镜像的

  1. Crunoob@runoob:~$ docker pull ubuntu:13.10

  2. 13.10: Pulling from library/ubuntu

  3. 6599cadaf950: Pull complete

  4. 23eda618d451: Pull complete

  5. f0be3084efe9: Pull complete

  6. 52de432f084b: Pull complete

  7. a3ed95caeb02: Pull complete

  8. Digest: sha256:15b79a6654811c8d992ebacdfbd5152fcf3d165e374e264076aa435214a947a3

  9. Status: Downloaded newer image for ubuntu:13.10复制代码

我们可以使用 docker images 来列出本地主机上的镜像。

  1. runoob@runoob:~$ docker images          

  2. REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

  3. ubuntu              14.04               90d5884b1ee0        5 days ago          188 MB

  4. php                 5.6                 f40e9e0f10c8        9 days ago          444.8 MB

  5. nginx               latest              6f8d099c3adc        12 days ago         182.7 MB

  6. mysql               5.6                 f2e8d6c772c0        3 weeks ago         324.6 MB

  7. httpd               latest              02ef73cf1bc0        3 weeks ago         194.4 MB

  8. ubuntu              15.10               4e3b13c8a266        4 weeks ago         136.3 MB

  9. hello-world         latest              690ed74de00f        6 months ago        960 B

  10. training/webapp     latest              6fae60ef3446        11 months ago       348.8 MB复制代码

各个选项说明:

REPOSITORY:表示镜像的仓库源

TAG:镜像的标签

IMAGE ID:镜像ID

CREATED:镜像创建时间

SIZE:镜像大小

创建Docker容器

docker create 命令通过镜像去创建一个容器,同时吐出容器 id。

  1. > docker create --name ubuntuContainer ubuntu:18.04

  2. 0da83bc6515ea1df100c32cccaddc070199b72263663437b8fe424aadccf4778

  3. 复制代码复制代码

用 docker start 即可运行改容器。

  1. > docker start ubuntuContainer

  2. 复制代码复制代码

用 docker ps 即可查看运行中的 container

  1. > docker ps

  2. CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

  3. 9298a27262da        ubuntu:18.04        "/bin/bash"         4 minutes ago       Up About a minute                       ubuntuContainer

  4. 复制代码复制代码

用 docker exec 即可进入该 container。

  1. > docker exec -it 9298

  2. root@9298a27262da:/# ls

  3. bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

  4. root@9298a27262da:/# exit

  5. 复制代码复制代码

用 docker run 可以一步到位创建并运行一个容器,然后进入该容器。

  1. > docker run -it --name runUbuntuContainer ubuntu:18.04 /bin/bash

  2. root@57cdd61d4383:/# ls

  3. bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

  4. root@57cdd61d4383:/#

  5. # docker ps 可以查到已经成功运行了 runUbuntuContainer

  6. > docker ps

  7. CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

  8. 57cdd61d4383        ubuntu:18.04        "/bin/bash"         9 seconds ago       Up 8 seconds                            runUbuntuContainer

  9. 9298a27262da        ubuntu:18.04        "/bin/bash"         9 minutes ago       Up 6 minutes 复制代码

commit 容器,创建新镜像

和 Ghost 装 windows 一样,很多时候,我们期望能定制自己的镜像,在里面安装一些基础环境(比如上文中的 node),然后制作出自己要的基础镜像。这个时候 docker commit 就派上用场了。

  1. > docker commit --author "rccoder" --message "curl+node" 9298 rccoder/myworkspace:v1

  2. sha256:68e83119eefa0bfdc8e523ab4d16c8cf76770dbb08bad1e32af1c872735e6f71

  3. # 通过 docker images 就能看到新制作的 rccoder/myworkspace 就躺在这里了

  4. >docker images

  5. REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE

  6. rccoder/myworkspace      v1              e0d73563fae8        20 seconds ago      196MB

  7. 复制代码复制代码

接着,试一下我们新创建的镜像?

  1. > docker run -it --name newWorkSpace rccoder/myworkspace:v1 /bin/bash

  2. root@9109f6985735:/# node -v

  3. 8.0.0

  4. 复制代码复制代码

看起来没问题。

push 镜像到 docker hub

镜像制作好了,怎么共享出去让别人使用呢?这里以 push 到 docker hub 为例。

第一步是先去 docker hub 注册一个账号,然后在终端上登录账号,进行 push。

  1. > docker login

  2. > docker push rccoder/myworkspace:v1

  3. The push refers to repository [docker.io/rccoder/myworkspace]

  4. c0913fec0e19: Pushing [=>                                                 ]  2.783MB/116.7MB

  5. bb1eed35aacf: Mounted from library/ubuntu

  6. 5fc1dce434ba: Mounted from library/ubuntu

  7. c4f90a44515b: Mounted from library/ubuntu

  8. a792400561d8: Mounted from library/ubuntu

  9. 6a4e481d02df: Waiting复制代码

Dockerfile

用 Docker 进行持续集成?相比在了解 Docker 之前肯定听过这个事情,那就意外着需要从某个地方拷贝代码,然后执行(对,听上去有点 travis-ci 的那种感觉)。

是时候该 Dockerfile 出场了!

Dockerfile 是一个由一堆命令+参数构成的脚本,使用 docker build 即可执行脚本构建镜像,自动的去做一些事(同类似于travis-ci 中的 .travis.yml)。

Dockerfile 的格式统统为:

  1. # Comment

  2. INSTRUCTION arguments

  3. 复制代码复制代码

必须以 FROM BASE_IMAGE 开头指定基础镜像。

更详细的规范与说明请参考 Dockerfile reference。这里我们以基于上面的 rccoder/myworkspace:v1 作为基础镜像,然后在根目录创建 a 目录为例

Dockerfile 如下:

  1. FROM rccoder/myworkspace:v1

  2. RUN mkdir a

  3. 复制代码复制代码

然后执行:

  1. > docker build -t newfiledocker:v1 .

  2. Sending build context to Docker daemon  3.584kB

  3. Step 1/2 : FROM rccoder/myworkspace:v1

  4. ---> 68e83119eefa

  5. Step 2/2 : RUN mkdir a

  6. ---> Running in 1127aff5fbd3

  7. Removing intermediate container 1127aff5fbd3

  8. ---> 25a8a5418af0

  9. Successfully built 25a8a5418af0

  10. Successfully tagged newfiledocker:v1

  11. # 新建基于 newfiledocker 的容器并在终端中打开,发现里面已经有 a 文件夹了。

  12. > docker docker run -it newfiledocker:v1 /bin/bash

  13. root@e3bd8ca19ffc:/# ls

  14. a  bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

  15. 复制代码复制代码

借助 Dockerfile 的能力,Docker 留下了无限的可能。

在容器里安装 Mysql 环境

方法一、docker pull php

查找Docker Hub上的php镜像

  1. runoob@runoob:~/php-fpm$ docker search php

  2. NAME                      DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED

  3. php                       While designed for web development, the PH...   1232      [OK]      

  4. richarvey/nginx-php-fpm   Container running Nginx + PHP-FPM capable ...   207                  [OK]

  5. phpmyadmin/phpmyadmin     A web interface for MySQL and MariaDB.          123                  [OK]

  6. eboraas/apache-php        PHP5 on Apache (with SSL support), built o...   69                   [OK]

  7. php-zendserver            Zend Server - the integrated PHP applicati...   69        [OK]      

  8. million12/nginx-php       Nginx + PHP-FPM 5.5, 5.6, 7.0 (NG), CentOS...   67                   [OK]

  9. webdevops/php-nginx       Nginx with PHP-FPM                              39                   [OK]

  10. webdevops/php-apache      Apache with PHP-FPM (based on webdevops/php)    14                   [OK]

  11. phpunit/phpunit           PHPUnit is a programmer-oriented testing f...   14                   [OK]

  12. tetraweb/php              PHP 5.3, 5.4, 5.5, 5.6, 7.0 for CI and run...   12                   [OK]

  13. webdevops/php             PHP (FPM and CLI) service container             10                   [OK]

  14. ...复制代码

这里我们拉取官方的镜像,标签为5.6-fpm

  1. runoob@runoob:~/php-fpm$ docker pull php:5.6-fpm复制代码

等待下载完成后,我们就可以在本地镜像列表里查到REPOSITORY为php,标签为5.6-fpm的镜像。

  1. runoob@runoob:~/php-fpm$ docker images

  2. REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

  3. php                 5.6-fpm             025041cd3aa5        6 days ago          456.3 MB复制代码

方法二、通过 Dockerfile 构建

创建Dockerfile

首先,创建目录php-fpm,用于存放后面的相关东西。

  1. runoob@runoob:~$ mkdir -p ~/php-fpm/logs ~/php-fpm/conf复制代码

logs目录将映射为php-fpm容器的日志目录

conf目录里的配置文件将映射为php-fpm容器的配置文件

进入创建的php-fpm目录,创建Dockerfile

通过Dockerfile创建一个镜像,替换成你自己的名字

  1. runoob@runoob:~/php-fpm$ docker build -t php:5.6-fpm .复制代码

创建完成后,我们可以在本地的镜像列表里查找到刚刚创建的镜像

  1. runoob@runoob:~/php-fpm$ docker images

  2. REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

  3. php                 5.6-fpm             025041cd3aa5        6 days ago          456.3 MB复制代码

送书 抽奖

往期 Docker 系列


Docker Compose 1.18.0 之服务编排详解

Docker 部署 SpringBoot 项目整合 Redis 镜像做访问计数Demo

Docker Registry 企业级私有镜像仓库Harbor管理WEB UI

Docker Registry Server TLS 的私有仓库

Docker Hub 仓库使用,及搭建 Docker Registry

Docker 容器常用操作命令讲解

Docker 安装在之初窥 Dockerfile 部署 Nginx

Spring Boot 中使用 Docker镜像push到DockerHub上

更多技术干货

100篇:搜云库技术团队,整理了一年的技术干货

百度、腾讯、阿里、谷歌 面试题视频详解合集

一文看懂 MySQL 高性能优化技巧实践

分布式事务不理解?一次给你讲清楚

微服务架构:如何用十步解耦你的系统

学习MySQL高性能优化原理,这一篇就够了

推荐:堪称最详细的支付系统设计

动画+原理+代码+优化,解读十大经典排序算法

面试必备:缓存穿透,缓存雪崩的四种解决方案

掌握分布式场景下的秒杀架构与秒杀实践

阅读原文可获得4000G架构师视频

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存