容器介绍

容器其实是一种沙盒技术。沙盒就是能够像一个集装箱一样,把你的应用”装”起来的技术。这样,应用与应用之间,就因为有了边界而不至于相互干扰;而被装进集装箱的应用,也可以被方便地搬来搬去。

问题:容器的本质到底是什么?

容器的本质是进程。容器镜像就是这个系统里的”.exe”安装包

Docker介绍:

  1. DockerDocker.Inc公司开源的一个基于轻量级虚拟化技术的容器引擎项目,整个项目基于Go语言开发,并遵从Apache 2.0协议。通过分层镜像标准化和内核虚拟化技术,Docker使得应用开发者和运维工程师可以以统一的方式跨平台发布应用,并且以几乎没有额外开销的情况下提供资源隔离的应用运行环境。
  2. Docker是一个开源工具,它可以让创建和管理 docker容器变得简单。容器就像是轻量级的虚拟机,并且可以以秒级的速度来启动或停止。
  1. 官网:docker.com
  2. docker.io ---docker官方库也叫docker-hub

没有容器的时候:

开发 测试 运维

  1. 1.rd开发产品(需要配置开发环境)LAMP
  2. 2.测试(需要配置测试环境)
  3. 3.op上线(需要线上环境)

开发 测试 运维

有容器之后:

  1. 1. rd开发产品(需要在docker容器里配置开发环境)LAMP
  2. 2. 把容器打包成镜像交给运维,运维上线。

Docker跟原有的工具区别:

  1. 传统的部署模式是:安装(包管理工具或者源码包编译)->配置->运行;
  2. Docker的部署模式是:复制->运行。

Docker对服务器端开发/部署带来的变化:

  1. 方便快速部署
  2. 对于部署来说可以极大的减少部署的时间成本和人力成本
  3. Docker支持将应用打包进一个可以移植的容器中,重新定义了应用开发,测试,部署上线的过程,核心理念是 Build once, Run anywhere
  4. 1)标准化应用发布,docker容器包含了运行环境和可执行程序,可以跨平台和主机使用;
  5. 2)节约时间,快速部署和启动,VM启动一般是分钟级,docker容器启动是秒级;
  6. 3)方便构建基于微服务架构的系统,通过服务编排,更好的松耦合;
  7. 4)节约成本,以前一个虚拟机至少需要几个G的磁盘空间,docker容器可以减少到MB级;

Docker 优势:

  1. 1、交付物标准化
  2. Docker的标准化交付物称为"镜像",它包含了应用程序及其所依赖的运行环境,大大简化了应用交付的模式。
  3. 2、应用隔离
  4. Docker可以隔离不同应用程序之间的相互影响,但是比虚拟机开销更小。总之,容器技术部署速度快,开发、测试更敏捷;提高系统利用率,降低资源成本.
  5. 3、一次构建,多次交付
  6. 类似于集装箱的"一次装箱,多次运输"Docker镜像可以做到"一次构建,多次交付"
  7. Docker的度量:
  8. Docker是利用容器来实现的一种轻量级的虚拟技术,从而在保证隔离性的同时达到节省资源的目的。Docker的可移植性可以让它一次建立,到处运行。Docker的度量可以从以下四个方面进行:
  9. 1)隔离性
  10. 通过内核的命名空间来实现的,将容器的进程、网络、消息、文件系统和主机名进行隔离。
  11. 2)可度量性
  12. Docker主要通过cgroups控制组来控制资源的度量和分配。
  13. 3)可移植性
  14. Docker利用AUFS来实现对容器的快速更新。
  15. AUFS是一种支持将不同目录挂载到同一个虚拟文件系统下的文件系统,支持对每个目录的读写权限管理。AUFS具有层的概念,每一次修改都是在已有的只写层进行增量修改,修改的内容将形成新的文件层,不影响原有的层。
  16. 4)安全性
  17. 安全性可以分为容器内部之间的安全性;容器与托管主机之间的安全性。
  18. 容器内部之间的安全性主要是通过命名空间和cgroups来保证的。
  19. 容器与托管主机之间的安全性主要是通过内核能力机制的控制,可以防止Docker非法入侵托管主机。
  20. Docker容器使用AUFS作为文件系统,有如下优势:
  21. 1)节省存储空间
  22. 多个容器可以共享同一个基础镜像存储。
  23. 2)快速部署
  24. 3)升级方便
  25. 升级一个基础镜像即可影响到所有基于它的容器。需要注意已经在运行的docker容器不受影响

容器和 VM 的主要区别:

  1. 表面区别:
  2. 容器占用体积小,虚拟机占用体积大
  3. 隔离性:容器提供了基于进程的隔离,而虚拟机提供了资源的完全隔离。
  4. 启动速度:虚拟机可能需要一分钟来启动,而容器只需要一秒钟或更短。
  5. 容器使用宿主操作系统的内核,而虚拟机使用独立的内核。Docker 的局限性之一是,它只能用在64位的操作系统上。
  6. 本质区别:
  7. 容器是被隔离的进程

Docker安装

CentOS 7 中 Docker 的安装:

Docker 软件包已经包括在默认的 CentOS-Extras 软件源(联网使用centos7u2自带网络Yum源)里。因此想要安装 docker,只需要运行下面的 yum 命令:

  1. # yum install -y epel*
  2. # yum install docker
  3. 启动 Docker 服务:
  4. # service docker start
  5. # chkconfig docker on
  6. CentOS 7
  7. # systemctl start docker.service
  8. # systemctl enable docker.service

确定docker服务在运行:

结果会显示服务端和客户端的版本,如果只显示客户端版本说明服务没有启动

  1. # docker version
  2. Client:
  3. Version: 1.10.3
  4. API version: 1.22
  5. ...

Docker版本与官方安装方式

moby、docker-ce与docker-ee

最早时docker是一个开源项目,主要由docker公司维护。

2017年3月1日起,docker公司将原先的docker项目改名为moby,并创建了docker-ce和docker-ee。

三者关系:

  1. moby是继承了原先的docker的项目,是社区维护的的开源项目,谁都可以在moby的基础打造自己的容器产品
  2. docker-cedocker公司维护的开源项目,是一个基于moby项目的免费的容器产品
  3. docker-eedocker公司维护的闭源产品,是docker公司的商业产品
  1. moby project由社区维护,docker-ce projectdocker公司维护,docker-ee是闭源的docker公司维护。

CentOS—官方安装

  1. 如果是centos,上面的安装命令会在系统上添加yum源:/etc/yum.repos.d/docker-ce.repo
  2. # wget https://download.docker.com/linux/centos/docker-ce.repo
  3. # mv docker-ce.repo /etc/yum.repos.d
  4. # yum install -y docker-ce
  1. 或者直接下载rpm安装:
  1. # wget https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-17.09.0.ce-1.el7.centos.x86_64.rpm
  2. # yum localinstall docker-ce-17.09.0.ce-1.el7.centos.x86_64.rpm

国内源安装新版Docker

使用aliyun docker yum源安装新版docker

删除已安装的Docker

  1. [root@docker-server ~]# yum remove docker \
  2. docker-client \
  3. docker-client-latest \
  4. docker-common \
  5. docker-latest \
  6. docker-latest-logrotate \
  7. docker-logrotate \
  8. docker-selinux \
  9. docker-engine-selinux \
  10. docker-engine

配置阿里云Docker Yum源

  1. # yum install -y yum-utils device-mapper-persistent-data lvm2 git
  2. # yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

安装指定版本

  1. 查看Docker版本:
  2. # yum list docker-ce --showduplicates
  1. 安装较旧版本(比如Docker 17.03.2)
  2. 需要指定完整的rpm包的包名,并且加上--setopt=obsoletes=0 参数:
  1. # yum install -y --setopt=obsoletes=0 \
  2. docker-ce-17.03.2.ce-1.el7.centos.x86_64 \
  3. docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch
  4. 例如:
  5. # yum install -y --setopt=obsoletes=0 docker-ce-19.03.2 docker-ce-selinux-19.03.2

安装Docker新版本(比如Docker 18.03.0):加上rpm包名的版本号部分或不加都可以:

  1. # yum install docker-ce-18.03.0.ce -y
  2. 或者
  3. [root@docker-server ~]# yum install -y docker-ce #默认安装的是最新版本

启动Docker服务:

  1. #systemctl enable docker
  2. #systemctl start docker

查看docker版本状态:

  1. [root@docker-server ~]# docker -v
  2. Docker version 19.03.2, build 6a30dfc
  1. [root@docker-server ~]# docker version
  2. Client: Docker Engine - Community
  3. Version: 19.03.2
  4. API version: 1.40
  5. Go version: go1.12.8
  6. Git commit: 6a30dfc
  7. Built: Thu Aug 29 05:28:55 2019
  8. OS/Arch: linux/amd64
  9. Experimental: false
  10. Server: Docker Engine - Community
  11. Engine:
  12. Version: 19.03.2
  13. API version: 1.40 (minimum version 1.12)
  14. Go version: go1.12.8
  15. Git commit: 6a30dfc
  16. Built: Thu Aug 29 05:27:34 2019
  17. OS/Arch: linux/amd64
  18. Experimental: false
  19. containerd:
  20. Version: 1.2.6
  21. GitCommit: 894b81a4b802e4eb2a91d1ce216b8817763c29fb
  22. runc:
  23. Version: 1.0.0-rc8
  24. GitCommit: 425e105d5a03fabd737a126ad93d62a9eeede87f
  25. docker-init:
  26. Version: 0.18.0
  27. GitCommit: fec3683

查看docker运行状态:

  1. # docker info
  2. Containers: 0
  3. Running: 0
  4. Paused: 0
  5. Stopped: 0
  6. Images: 0
  7. ...

=======================================================

  1. 报错1
  2. docker info的时候报如下错误
  3. bridge-nf-call-iptables is disabled
  4. 解决1
  5. 追加如下配置,然后重启系统
  6. # vim /etc/sysctl.conf
  7. net.bridge.bridge-nf-call-ip6tables = 1
  8. net.bridge.bridge-nf-call-iptables = 1
  9. net.bridge.bridge-nf-call-arptables = 1
  10. 问题2
  11. 虚拟机ping百度也能ping通,但是需要等好几秒才出结果,关键是下载镜像一直报错如下
  12. # docker pull daocloud.io/library/nginx
  13. Using default tag: latest
  14. Error response from daemon: Get https://daocloud.io/v2/: dial tcp: lookup daocloud.io on 192.168.1.2:53: read udp 192.168.1.189:41335->192.168.1.2:53: i/o timeout
  15. 解决2
  16. 我的虚拟机用的网关和dns都是虚拟机自己的.1或者.2,把DNS改成8.8.8.8问题就解决了,ping百度也秒出结果
  17. # vim /etc/resolv.conf
  18. nameserver 8.8.8.8

简单测试

  1. 运行一个容器
  2. [root@192 ~]# docker run -it daocloud.io/library/ubuntu /bin/bash #运行容器
  3. Unable to find image 'daocloud.io/library/ubuntu:latest' locally
  4. latest: Pulling from library/ubuntu
  5. 5c939e3a4d10: Pull complete
  6. c63719cdbe7a: Pull complete
  7. 19a861ea6baf: Pull complete
  8. 651c9d2d6c4f: Pull complete
  9. Digest: sha256:bc025862c3e8ec4a8754ea4756e33da6c41cba38330d7e324abd25c8e0b93300
  10. Status: Downloaded newer image for daocloud.io/library/ubuntu:latest
  11. 如果自动进入下面的容器环境,说明ubuntu镜像运行成功,Docker的安装也没有问题:可以操作容器了
  12. root@db8e84e2ea96:/#

国内镜像源

  1. 去查看如何使用aliyundocker镜像库
  2. 去查看如何使用网易蜂巢的docker镜像库----作业,并且通过网易蜂巢源下载一个nginx的镜像---docker pull 镜像仓库与镜像名字
  3. daocloud.io

Docker 加速器

  1. 使用 Docker 的时候,需要经常从官方获取镜像,但是由于显而易见的网络原因,拉取镜像的过程非常耗时,严重影响使用 Docker 的体验。因此 DaoCloud 推出了加速器工具解决这个难题,通过智能路由和缓存机制,极大提升了国内网络访问 Docker Hub 的速度。
  2. Docker 加速器对 Docker 的版本有要求吗?
  3. 需要 Docker 1.8 或更高版本才能使用。
  4. Docker 加速器支持什么系统?
  5. Linux, MacOS 以及 Windows 平台。
  6. Docker 加速器是否收费?
  7. 提供永久免费的加速器服务,请放心使用。

国内比较好的镜像源:网易蜂巢、aliyun和daocloud

daocloud

使用国内镜像:

进入网站:https://daocloud.io/

注册帐号:

Docker - 图1

进入镜像市场

Docker - 图2

随便选择一个,选择mysql

Docker - 图3

Docker - 图4

上面有详细的使用命令。但是每个镜像的命令不一样,在选择一个:

Docker - 图5

Docker - 图6

  1. [root@docker-server ~]# docker pull daocloud.io/library/nginx #下载镜像
  2. Using default tag: latest
  3. latest: Pulling from library/nginx
  4. 0a4690c5d889: Pull complete
  5. 9719afee3eb7: Pull complete
  6. 44446b456159: Pull complete
  7. Digest: sha256:f83b2ffd963ac911f9e638184c8d580cc1f3139d5c8c33c87c3fb90aebdebf76
  8. Status: Downloaded newer image for daocloud.io/library/nginx:latest
  9. daocloud.io/library/nginx:latest

现在我们使用web界面管理docker容器:

Docker - 图7

Docker - 图8

Docker - 图9

等一会,不要手动终止:

  1. [root@docker-server ~]# curl -sSL https://get.daocloud.io/daomonit/install.sh | sh -s 8e6d8ce76e4ec4668f31859dae6297439ac49243

完成之后:

Docker - 图10

点击查看主机

Docker - 图11

启动我们的容器

  1. [root@docker-server ~]# docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. daocloud.io/library/nginx latest 0901fa9da894 2 weeks ago 132MB
  4. registry.cn-hangzhou.aliyuncs.com/youngfit/nginx 1.1 0901fa9da894 2 weeks ago 132MB
  5. daocloud.io/library/ubuntu latest 1d622ef86b13 3 months ago 73.9MB
  6. daocloud.io/daocloud/daocloud-toolset latest bbdc71e950ea 3 years ago 147MB
  7. [root@docker-server ~]# docker run -it daocloud.io/library/nginx /bin/bash #运行容器
  8. root@37b8b8cdd75f:/#

将容器启动之后刷新一下刚才的界面点击容器就会显示出来

Docker - 图12

Docker - 图13

Docker - 图14

Docker - 图15

点击查看详情…

阿里云容器镜像服务

配置阿里云的镜像仓库

Docker - 图16

Docker - 图17

Docker - 图18

创建阿里云的私有仓库

Docker - 图19

Docker - 图20

Docker - 图21

Docker - 图22
公开:在拉取的时候,不需要输入用户名和密码。推送仍然需要输入用户名和密码;
私有:拉取,推送都需要输入用户名和密码;

Docker - 图23

Docker - 图24

Docker - 图25

Docker - 图26

是因为未设置登录密码,这里设置一下

Docker - 图27

Docker - 图28

Docker - 图29

使用自己的私有仓库

  1. 登录:
  2. [root@docker-server ~]# docker pull nginx
  3. [root@docker-server ~]# docker login --username=youngfit registry.cn-shanghai.aliyuncs.com
  4. Password:
  5. ...
  6. Login Succeeded
  7. 重新打标记
  8. [root@docker-server ~]# docker tag daocloud.io/library/nginx:latest registry.cn-shanghai.aliyuncs.com/youngfit/nginx:1.1
  9. 上传到自己的仓库
  10. [root@docker-server ~]# docker push registry.cn-shanghai.aliyuncs.com/youngfit/nginx:1.1
  11. The push refers to repository [registry.cn-shanghai.aliyuncs.com/testpm/nginx]
  12. be91fceb796e: Layer already exists
  13. 919b6770519b: Layer already exists
  14. b60e5c3bcef2: Layer already exists
  15. 1.1: digest: sha256:6b3b6c113f98e901a8b1473dee4c268cf37e93d72bc0a01e57c65b4ab99e58ee size: 948

查看上传的镜像

Docker - 图30

Docker - 图31

Docker - 图32

自己的nginx:1.1版本已经上传成功!

使用自己的仓库

Docker - 图33

Docker - 图34

  1. 使用阿里的仓库下载镜像:
  2. 用本机就先删除镜像。换台机器也可以
  3. [root@docker-server ~]# docker rmi registry.cn-hangzhou.aliyuncs.com/youngfit/nginx:1.1
  4. Untagged: registry.cn-hangzhou.aliyuncs.com/youngfit/nginx:1.1
  5. Untagged: registry.cn-hangzhou.aliyuncs.com/youngfit/nginx@sha256:8ff4598873f588ca9d2bf1be51bdb117ec8f56cdfd5a81b5bb0224a61565aa49
  6. 从自己的仓库下载
  7. [root@docker-server ~]# docker pull registry.cn-hangzhou.aliyuncs.com/youngfit/nginx:1.1
  8. 1.1: Pulling from youngfit/nginx
  9. Digest: sha256:8ff4598873f588ca9d2bf1be51bdb117ec8f56cdfd5a81b5bb0224a61565aa49
  10. Status: Downloaded newer image for registry.cn-hangzhou.aliyuncs.com/youngfit/nginx:1.1
  11. registry.cn-hangzhou.aliyuncs.com/youngfit/nginx:1.1
  12. 查看
  13. [root@docker-server ~]# docker images
  14. REPOSITORY TAG IMAGE ID CREATED SIZE
  15. registry.cn-shanghai.aliyuncs.com/youngfit/nginx 1.1 e791337790a6 4 weeks ago 127MB

Docker - 图35

Docker - 图36

  1. 配置阿里加速器:
  2. 如果这个目录/etc/docker/不存在就创建
  3. [root@docker-server ~]# vim /etc/docker/daemon.json
  4. {
  5. "registry-mirrors": ["https://ukblsmil.mirror.aliyuncs.com"]
  6. }
  7. [root@docker-server ~]# systemctl daemon-reload
  8. [root@docker-server ~]# systemctl restart docker

腾讯云容器镜像服务

注册账号—>登录—>搜索“容器镜像服务”

Docker - 图37

创建命名空间

Docker - 图38

创建仓库

Docker - 图39

使用指南

Docker - 图40

Docker - 图41

  1. # 登录1
  2. [root@docker-server1 ~]# docker login ccr.ccs.tencentyun.com --username=100003726264
  3. Password: syf_****** #这里直接填写用户名的密码即可
  4. # 登录2
  5. [root@docker-server1 ~]# docker login ccr.ccs.tencentyun.com --username=100003726264 --password=syf_*** #这里直接填写用户名的密码即可

Docker - 图42

推送镜像

  1. [root@docker-server1 ~]# docker tag daocloud.io/library/mysql:5.7.5-m15 ccr.ccs.tencentyun.com/youngfit/mysql:5.7
  2. [root@docker-server1 ~]# docker push ccr.ccs.tencentyun.com/youngfit/mysql:5.7

Docker - 图43

拉取镜像

Docker - 图44

  1. # 先删除之前的镜像
  2. [root@docker-server1 ~]# docker rmi ccr.ccs.tencentyun.com/youngfit/mysql:5.7 daocloud.io/library/mysql:5.7.5-m15
  3. # 拉取
  4. [root@docker-server1 ~]# docker pull ccr.ccs.tencentyun.com/youngfit/mysql:5.7

Docker - 图45

  1. # 使用镜像
  2. [root@docker-server1 ~]# docker run -d --name some-mysql -e MYSQL_ROOT_PASSWORD=FeiGe@520 ccr.ccs.tencentyun.com/youngfit/mysql:5.7
  3. [root@docker-server1 ~]# mysql -uroot -p'FeiGe@520' -h 172.17.0.2

Docker - 图46

Docker基本概念

Docker系统:

  1. Docker系统有两个程序:docker服务端和docker客户端
  2. Docker服务端:是一个服务进程,管理着所有的容器。也叫docker engine
  3. Docker客户端:扮演着docker服务端的远程控制器,可以用来控制docker的服务端进程
  4. docker服务端和客户端运行在一台机器上

Docker三大核心组件:

  1. Docker 仓库 - Docker registery
  2. Docker 镜像 - Docker images
  3. Docker 容器 - Docker containers

容器的三大组成要素:

  1. 名称空间 namespace 容器隔离(pid,net,mnt,user,hostname...)
  2. 资源限制 cgroups 资源(内存,cpu)
  3. 文件系统 overlay2(UnionFS)

Docker 仓库:

用来保存镜像,可以理解为代码控制中的代码仓库。同样的,Docker 仓库也有公有和私有的概念。

公有的 Docker 仓库名字是 Docker Hub。Docker Hub 提供了庞大的镜像集合供用户使用。这些镜像可以是自己创建,或者在别人的镜像基础上创建。

  1. 仓库(registry) -->Repository-->镜像:v1.1(按版本区分)
  2. docker.io/centos:7
  3. registry/repository:tag
  4. repository:存储库
  5. docker 国内仓库
  6. aliyun
  7. 网易蜂巢
  8. daocloud
  9. ===============================
  10. docker公有仓库
  11. docker.io -------docker官方库也叫docker-hub
  12. 类似于github一样,面向全球的一个docker镜像的公共仓库。如果在国内使用速度太慢。
  13. ===============================
  14. docker私有仓库
  15. 个人或者公司部署的非公开库

Docker 镜像

  1. Docker 镜像是 Docker 容器运行时的只读模板,每一个镜像由一系列的层 (layers) 组成。Docker 使用 UnionFS 来将这些层联合到单独的镜像中。正因为有了这些层的存在,Docker 是如此的轻量。当你改变了一个 Docker 镜像,比如升级到某个程序到新的版本,一个新的层会被创建。因此,不用替换整个原先的镜像或者重新建立(在使用虚拟机的时候你可能会这么做),只是一个新的层被添加或升级了。

在 Docker 的术语里,一个只读层被称为镜像,一个镜像是永久不会变的。由于 Docker 使用一个统一文件系统,由于镜像不可写,所以镜像是无状态的。

  1. 镜像由三部分组成:
  2. 镜像名称:仓库名称+镜像分类+tag名称(镜像版本)
  3. 1.存储对象:images
  4. 2.格式:库名/分类:tag
  5. 3.tag:表示镜像版本

镜像的大体分类方式:这不是规定

  1. 1.以操作系统名字
  2. centosdocker镜像:
  3. centos5
  4. centos6
  5. centos7
  6. -----------------
  1. 2.以应用的名字
  2. nginxdocker镜像
  3. tomcatdocker镜像
  4. mysqldocker镜像

镜像名字:

  1. 完整镜像名称示例:
  2. docker.io/library/nginx:v1
  3. docker.io/library/nginx:latest
  4. daocloud.io/library/nginx

镜像ID:

所有镜像都是通过一个 64 位十六进制字符串来标识的。 为简化使用,前 12 个字符可以组成一个短ID,可以在命令行中使用。短ID还是有一定的碰撞机率,所以服务器总是返回长ID。

镜像ID:64位的id号,一般我们看到的是12位的我们称之为短ID,只要我们每个ID号不冲突就可以了

Docker - 图47

  1. 镜像本身:是由一层一层的镜像合在一起的,最底层的镜像我们称为基础镜像,在这个基础镜像的基础上还可以在做镜像,在做的镜像称为子镜像,对于子镜像来讲在谁的基础之上做的就是父镜像。
  2. 基础镜像:一个没有任何父镜像的镜像,谓之基础镜像。
  3. centos7 镜像
  4. centos7+nginx 镜像

Docker 容器

  1. Docker 容器和文件夹很类似,一个Docker容器包含了所有的某个应用运行所需要的环境。每一个 Docker 容器都是从 Docker 镜像创建的。Docker 容器可以运行、开始、停止、移动和删除。每一个 Docker 容器都是独立和安全的应用平台,Docker 容器是 Docker 的运行部分。

Docker镜像命名解析

Docker镜像命名解析

镜像是Docker最核心的技术之一,也是应用发布的标准格式。无论你是用docker pull image,或者是在Dockerfile里面写FROM image,从Docker官方Registry下载镜像应该是Docker操作里面最频繁的动作之一了。那么docker镜像是如何命名的,这也是Docker里面比较容易令人混淆的一块概念:Registry,Repository, Tag and Image。

那么Registry又是什么呢?Registry存储镜像数据,并且提供拉取和上传镜像的功能。Registry中镜像是通过Repository来组织的,而每个Repository又包含了若干个Image。

下面是在本地机器运行docker images的输出结果:

Docker - 图48

常说的”ubuntu”镜像其实不是一个镜像名称,而是代表了一个名为ubuntu的Repository,同时在这个Repository下面有一系列打了tag的Image,Image的标记是一个GUID,为了方便也可以通过Repository:tag来引用。

Image[:tag]

当一个镜像的名称不足以分辨这个镜像所代表的含义时,你可以通过tag将版本信息添加到run命令中,以执行特定版本的镜像。

  1. 例如:docker run ubuntu:14.04

Docker镜像和容器的区别

一、Docker镜像

一个Docker镜像可以构建于另一个Docker镜像之上,这种层叠关系可以是多层的。第1层的镜像层我们称之为基础镜像(Base Image),其他层的镜像(除了最顶层)我们称之为父层镜像(Parent Image)。这些镜像继承了他们的父层镜像的所有属性和设置。

Docker镜像通过镜像ID进行识别。镜像ID是一个64字符的十六进制的字符串。但是当我们运行镜像时,通常我们不会使用镜像ID来引用镜像,而是使用镜像名来引用。

要列出本地所有有效的镜像,可以使用命令

  1. # docker images

镜像可以发布为不同的版本,这种机制我们称之为标签(Tag)。

可以使用pull命令加上指定的标签:

  1. # docker pull ubuntu:
  2. # docker pull ubuntu:12.04

二、Docker容器

Docker容器可以使用命令创建:

  1. # docker run -it imagename /bin/bash

它会在所有的镜像层之上增加一个可写层。这个可写层有运行在CPU上的进程,而且有两个不同的状态:运行态(Running)和退出态 (Exited)。这就是Docker容器。当我们使用docker run启动容器,Docker容器就进入运行态,当我们停止Docker容器时,它就进入退出态。

当我们有一个正在运行的Docker容器时,从运行态到停止态,我们对它所做的一切变更都会永久地写到容器的文件系统中。要切记,对容器的变更是写入到容器的文件系统的,而不是写入到Docker镜像中的。我们可以用同一个镜像启动多个Docker容器,这些容器启动后都是活动的,彼此还是相互隔离的。我们对其中一个容器所做的变更只会局限于那个容器本身。如果对容器的底层镜像进行修改,那么当前正在运行的容器是不受影响的,不会发生自动更新现象。

名字空间—namespace

  1. namespace 空间隔离
  2. cgroup 资源限制

名字空间是 Linux 内核一个强大的特性。每个容器都有自己单独的名字空间,运行在其中的应用都像是在独立的操作系统中运行一样。名字空间保证了容器之间彼此互不影响。

  1. pid 名字空间

不同用户的进程就是通过 pid 名字空间隔离开的,且不同名字空间中可以有相同的 pid。所有的 LXC(Linux container) 进程在 Docker中的父进程为Docker进程,每个 LXC 进程具有不同的名字空间。同时由于允许嵌套,因此可以很方便的实现嵌套的 Docker 容器。

  1. net 名字空间 ——做网络接口隔离的
    有 了 pid 名字空间, 每个名字空间中的 pid 能够相互隔离,但是网络端口还是共享 host 的端口。网络隔离是通过 net 名字空间实现的, 每个 net 名字空间有独立的网络设备, IP 地址, 路由表, /proc/net 目录。这样每个容器的网络就能隔离开来。
  2. ipc 名字空间

容器中进程交互还是采用了 Linux 常见的进程间交互方法(interprocess communication - IPC), 包括信号量、共享内存、socket、管道等。

面试题:linux系统里面ipc通信有几种方式

  1. socket:网络进程间的通信
  2. 管道:本地进程间的通信:echo hello | grep e
  3. 信号: kill -9 PID 这种我们叫信号量级,也是本地进程间的通信
  4. 共享内存:每个操作系统里面共享内存多大,是物理内存的一半
  1. mnt名字空间

mnt 名字空间允许不同名字空间的进程看到的文件结构不同,这样每个名字空间中的进程所看到的文件目录就被隔离开了。

  1. uts 名字空间

UTS(“UNIX Time-sharing System”) 名字空间允许每个容器拥有独立的 hostname 和 domain name, 使其在网络上可以被视作一个独立的节点而非主机上的一个进程。

  1. user 名字空间

每个容器可以有不同的用户和组 id, 也就是说可以在容器内用容器内部的用户执行程序而非主机上的用户。

镜像管理

搜索镜像:

  1. 这种方法只能用于官方镜像库
  2. 搜索基于 centos 操作系统的镜像
  3. [root@docker-server ~]# docker search centos

按星级搜索镜像:

  1. 查找 star 数至少为 100 的镜像,默认不加 s 选项找出所有相关 centos 镜像:
  2. [root@docker-server ~]# docker search centos -s 100
  3. 或者
  4. [root@docker-server ~]# docker search ubuntu -f stars=100

拉取镜像:

  1. # docker pull centos
  2. 注:没有加registry,默认是从docker.io下载的
  3. [root@docker-server ~]# docker pull daocloud.io/library/tomcat:7
  4. [root@docker-server ~]# docker pull daocloud.io/library/centos:6

查看本地镜像:

  1. [root@docker-server ~]# docker image list
  2. 或者
  3. [root@docker-server ~]# docker images

查看镜像详情:

  1. [root@docker-server ~]# docker image inspect 镜像id/镜像名称
  2. [root@docker-server ~]# docker inspect 镜像id/镜像名称

删除镜像:

  1. 删除一个或多个,多个之间用空格隔开,可以使用镜像名称或id
  2. [root@docker-server ~]# docker rmi daocloud.io/library/mysql
  3. 或者
  4. [root@docker-server ~]# docker rmi 81debc
  5. 参数解释:
  6. rm Remove one or more containers ---移除一个或多个容器
  7. rmi Remove one or more images ---删除一个或多个镜像

强制删除:—force

  1. 如果镜像正在被使用中可以使用--force强制删除
  2. # docker rmi docker.io/ubuntu:latest --force
  3. -f, --force Force removal of the image
  4. 注意: 容器运行中不能删除,将容器停止后,删除容器在删除镜像。

只查看所有镜像的id:

  1. [root@docker-server ~]# docker images -q
  2. 98ebf73aba75
  3. 81debc95563d
  4. d0957ffdf8a2
  5. -q, --quiet

删除所有镜像:

  1. [root@docker-server ~]# docker rmi $(docker images -q)

查看镜像制作的过程:
相当于dockfile

  1. [root@docker-server ~]# docker history daocloud.io/library/nginx 使用镜像名或者镜像ID都可以

容器管理

创建新容器但不启动:

  1. [root@docker-server ~]# docker create -it daocloud.io/library/centos:5 /bin/bash

创建并运行一个新Docker 容器:同一个镜像可以启动多个容器,每次执行run子命令都会运行一个全新的容器

  1. [root@docker-server ~]# docker run -it --restart=always daocloud.io/library/centos:7 /bin/bash #最常用
  2. -i:标准输入输出
  3. -t:分配一个终端或控制台
  4. --restart=always:容器随docker engine自启动,因为在重启docker的时候默认容器都会被关闭
  5. 也适用于create选项
  6. -d 后台运行容器,并返回容器ID

如果执行成功,说明CentOS 容器已经被启动,并且应该已经得到了 bash 提示符。

  1. --rm:默认情况下,每个容器在退出时,它的文件系统也会保存下来.另一方面,也可以保存容器所产生的数据。但是当你仅仅需要短暂的运行一个容器,并且这些数据不需要保存,你可能就希望Docker能在容器结束时自动清理其所产生的数据。这个时候就需要--rm参数了。
  2. 示例:
  3. [root@docker-server ~]# docker run -it --rm daocloud.io/library/nginx:latest /bin/bash
  4. root@be2d0a462ce1:/# exit
  5. exit
  6. [root@docker-server ~]# docker ps -a
  7. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

容器名称

  1. 为容器分配一个名字,如果没有指定,docker会自动分配一个随机名称是 docker run子命令的参数
  2. --name= Assign a name to the container
  3. # docker run -it --name 名字 daocloud.io/centos:6 /bin/bash #名字自定义

如果你在执行docker run时没有指定—name,那么自动生成一个随机字符串UUID。这个UUID标识是由Docker deamon生成的。但是对于一个容器来说有个name会非常方便,当你需要连接其它容器时或者类似需要区分其它容器时,使用容器名称可以简化操作。无论容器运行在前台或者后台,这个名字都是有效的。

若要断开与容器的连接,并且关闭容器:容器内部执行如下命令

  1. root@37b8b8cdd75f:/# exit

如果只想断开和容器的连接而不关闭容器:

  1. 快捷键:ctrl+p+q

查看容器:

  1. 1.只查看运行状态的容器:
  2. # docker ps
  3. 2.-a 查看所有容器
  4. # docker ps -a
  5. 3.只查看所有容器id:
  6. # docker ps -a -q

查看容器详细信息:

inspect :用于查看容器的配置信息,包含容器名、环境变量、运行命令、主机配置、网络配置和数据卷配置等。

目标:查找某一个运行中容器的id,然后使用docker inspect命令查看容器的信息。

提示:可以使用容器id的前面部分,不需要完整的id。

  1. [root@docker-server ~]# docker inspect bbdc71e #机器上运行的一个容器ID或者名称
  2. [
  3. {
  4. "Id": "d95a220a498e352cbfbc098c949fc528dbf5a5c911710b108ea3a9b4aa3a4761",
  5. "Created": "2017-07-08T03:59:16.18225183Z",
  6. "Path": "bash",
  7. "Args": [],
  8. "State": {
  9. "Status": "exited",
  10. "Running": false,
  11. "Paused": false,
  12. "Restarting": false,
  13. "OOMKilled": false,
  14. "Dead": false,
  15. "Pid": 0,
  16. 容器信息很多,这里只粘贴了一部分

启动容器:

  1. # docker start name #容器ID也可以
  2. 这里的名字是状态里面NAMES列列出的名字,这种方式同样会让容器运行在后台

关闭容器:

  1. # docker stop name
  2. # docker kill name --强制终止容器

杀死所有running状态的容器

  1. # docker kill $(docker ps -q)
  2. # docker stop `docker ps -q`

stop和kill的区别:

  1. docker stop命令给容器中的进程发送SIGTERM信号,默认行为是会导致容器退出,当然,容器内程序可以捕获该信号并自行处理,例如可以选择忽略。而docker kill则是给容器的进程发送SIGKILL信号,该信号将会使容器必然退出。

删除容器:

  1. # docker rm 容器id或名称
  2. 要删除一个运行中的容器,添加 -f 参数 --慎用。先stop在删除

根据格式删除所有容器:

  1. # docker rm $(docker ps -qf status=exited)
  2. -f:过滤
  3. pause :暂停容器中所有的进程
  4. unpause:恢复容器内暂停的进程,与pause对应
  5. [root@docker-server ~]# docker pause c7
  6. [root@docker-server ~]# docker ps
  7. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  8. 3c0e0f43807d 98ebf73aba "/bin/bash" 7 minutes ago Up 7 minutes (Paused) 80/tcp c7
  9. [root@docker-server ~]# docker unpause c7 #恢复

重启容器:

  1. [root@docker-server ~]# docker restart name

让容器运行在后台:

  1. # docker run -dit 镜像ID /bin/bash
  2. -d后台运行必须要加-it

如果在docker run后面追加-d=true或者-d,那么容器将会运行在后台模式。此时所有I/O数据只能通过网络资源或者共享卷组来进行交互。因为容器不再监听你执行docker run的这个终端命令行窗口。但你可以通过执行docker attach来重新附着到该容器的回话中。

  1. [root@docker-server ~]# docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. 455a92f0b90c daocloud.io/library/nginx "/docker-entrypoint.…" 14 minutes ago Up 33 seconds 80/tcp silly_mahavira
  4. [root@docker-server ~]# docker attach 容器ID/容器名字
  5. [root@docker-server ~]# docker attach silly_mahavira
  6. root@455a92f0b90c:/# ls
  7. bin dev docker-entrypoint.sh home lib64 mnt proc run srv tmp var
  8. boot docker-entrypoint.d etc lib media opt root sbin sys usr

注:

容器运行在后台模式下,是不能使用—rm选项的(老版本是这样,新版本已经可以同时生效)

rename —-修改容器名称

  1. [root@docker-server ~]# docker rename mytest testmy
  2. [root@docker-server ~]# docker ps -a
  3. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  4. 774c02898fb1 daocloud.io/library/nginx "/bin/bash -c 'while…" 5 minutes ago Up About a minute 80/tcp testmy

stats

  1. 显示容器资源使用统计信息的实时流
  1. [root@docker-server ~]# docker stats
  2. --当有容器在运行的时候动态显示容器的资源消耗情况,包括:CPU、内存、网络I/O

连接容器:前提是容器在运行状态中

方法1.attach

  1. # docker attach 容器id #前提是容器创建时必须指定了交互shell

方法2.exec

通过exec命令可以创建两种任务:后台型任务和交互型任务

  1. 1.交互型任务:
  2. [root@docker-server ~]# docker exec -it 容器id /bin/bash
  3. root@68656158eb8e:/# ls
  4. 2.后台型任务:不进入容器里面执行命令
  5. [root@docker-server ~]# docker exec 容器id touch /testfile

监控容器的运行:

可以使用logs、top、wait这些子命令

  1. logs:使用logs命令查看守护式容器
  2. 可以通过使用docker logs命令来查看容器的运行日志,其中--tail选项可以指定查看最后几条日志,使用-f选项可以跟踪日志的输出,直到手动停止。
  1. [root@docker-server ~]# docker pull daocloud.io/library/nginx
  2. [root@docker-server ~]# docker images
  3. [root@docker-server ~]# docker run -it --name nginx1 98ebf73 /bin/bash
  4. root@8459191dbe7c:/# /usr/sbin/nginx #启动nginx
  5. ctrl+p+q --- 退出
  6. [root@docker-server ~]# docker inspect nginx1 #找到ip地址
  7. [root@docker-server ~]# curl -I http://172.17.0.3 #宿主机访问容器可以访问成功
  8. HTTP/1.1 200 OK
  9. Server: nginx/1.17.1
  10. Date: Mon, 09 Mar 2020 14:49:40 GMT
  11. Content-Type: text/html
  12. Content-Length: 612
  13. Last-Modified: Tue, 25 Jun 2019 12:19:45 GMT
  14. Connection: keep-alive
  15. ETag: "5d121161-264"
  16. Accept-Ranges: bytes
  17. [root@docker-server ~]# curl -I http://172.17.0.3 #继续测试访问
  18. 在开启一个终端:
  19. [root@docker-server ~]# docker logs -f nginx1
  20. root@8459191dbe7c:/# /usr/sbin/nginx
  21. root@8459191dbe7c:/# 172.17.0.1 - - [09/Mar/2020:14:49:33 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.29.0" "-"
  22. 172.17.0.1 - - [09/Mar/2020:14:49:40 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.29.0" "-"
  1. top:显示一个运行的容器里面的进程信息
  1. [root@docker-server ~]# docker top nginx #容器ID也可以
  1. wait :--捕捉容器停止时的退出码

执行此命令后,该命令会”hang”在当前终端,直到容器停止,此时,会打印出容器的退出码

  1. 在第一个终端执行停止容器命令
  2. [root@docker-server ~]# docker stop nginx1
  3. =============================================================================
  4. [root@docker-server ~]# docker wait 01d8aa #第二个终端操作
  5. 0
  6. docker run 之后容器退出的状态码:
  7. 0,表示正常退出
  8. 0,表示异常退出(退出状态码采用chroot标准)
  9. 125Docker守护进程本身的错误
  10. 126,容器启动后,要执行的默认命令无法调用
  11. 127,容器启动后,要执行的默认命令不存在

宿主机和容器之间相互COPY文件

  1. cp的用法如下:
  1. Usage:
  2. docker cp [OPTIONS] CONTAINER:PATH LOCALPATH --从容器拷贝到本机
  3. docker cp [OPTIONS] LOCALPATH CONTAINER:PATH --从本机拷贝到容器
  1. 如:容器nginx中/usr/local/bin/存在test.sh文件,可如下方式copy到宿主机
  1. [root@docker-server ~]# docker run -it --name mytest nginx /bin/bash
  2. root@2a9a18b4a485:/# cd /usr/local/bin/
  3. root@2a9a18b4a485:/usr/local/bin# touch test.sh
  4. ctrl+p+q 退出
  5. [root@docker-server ~]# docker cp mytest:/usr/local/bin/test.sh /root/

修改完毕后,将该文件重新copy回容器

  1. [root@docker-server ~]# ls
  2. anaconda-ks.cfg test.sh
  3. [root@docker-server ~]# echo "123" >> test.sh
  4. [root@docker-server ~]# docker cp /root/test.sh mytest:/usr/local/bin/

Docker容器镜像制作

一、容器文件系统打包

将容器的文件系统打包成tar文件,也就是把正在运行的容器直接导出为tar包的镜像文件
export
Export a container’s filesystem as a tar archive

有两种方式:
第一种:

  1. [root@docker-server ~]# docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. centos latest 831691599b88 5 weeks ago 215MB
  4. [root@docker-server ~]# docker run -itd --name centos centos /bin/bash
  5. [root@docker-server ~]# docker ps
  6. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  7. b028c756d20f centos "/bin/bash" About a minute ago Up About a minute centos
  8. [root@docker-server ~]# docker exec -it centos /bin/bash
  9. [root@96e2b7265d93 /]# vi a.txt #编辑一个文件
  10. 123
  11. [root@96e2b7265d93 /]# yum install -y vim wget #安装一个软件
  12. [root@docker-server ~]# docker export -o centos6-1.tar 96e2b726
  13. -o, --output
  14. [root@docker-server ~]# ls #保存到当前目录下
  15. anaconda-ks.cfg centos6-1.tar

第二种:

  1. [root@docker-server ~]# docker export 容器名称 > 镜像.tar

导入镜像归档文件到其他宿主机:

import

  1. [root@docker-server ~]# docker import centos6-1.tar centos6-1:v1
  2. sha256:b8414448c913259a28d623643a7b67a6c13da9b3259e70ee5febb16c2b0b1095
  3. [root@docker-server ~]# docker images
  4. [root@docker-server ~]# docker run -it --name c6.1 centos6-1:v1 /bin/bash
  5. [root@4a29d58d3bd2 /]# ls
  6. a.txt bin dev etc home lib lib64 lost+found media mnt opt proc root sbin selinux srv sys tmp usr var
  7. [root@4a29d58d3bd2 /]# cat a.txt
  8. 123123

二、通过容器创建本地镜像

背景:容器运行起来后,又在里面做了一些操作,并且要把操作结果保存到镜像里
方案:使用 docker commit 指令,把一个正在运行的容器,直接提交为一个镜像。
commit 是提交的意思,类似告诉svn服务器我要生成一个新的版本。

例子:
在容器内部新建了一个文件

  1. [root@docker-server ~]# docker run -it --name c7 daocloud.io/library/centos:7 /bin/bash
  2. [root@2e8f79cb5922 /]# touch test.txt

将这个新建的文件提交到镜像中保存

  1. [root@docker-server ~]# docker commit 2e8f79cb5922 soso/test:v2
  2. sha256:2214bad66e9b1c2079dc89a2e14e997604237cd49a6dc6c29d84e915fbbeb5bd
  3. [root@docker-server ~]# docker images

也可以这样例子:

  1. # docker commit -m "my images version1" -a "soso" 108a85b1ed99 daocloud.io/ubuntu:v2
  2. sha256:ffa8a185ee526a9b0d8772740231448a25855031f25c61c1b63077220469b057
  1. -m 添加注释
  2. -a 作者
  3. 108a85b1ed99 容器环境id
  4. daocloud.io/ubuntu:v2 镜像名称:hub的名称/镜像名称:tag
  5. -p,–pause=true 提交时暂停容器运行

镜像迁移

保存一台宿主机上的镜像为tar文件,然后可以导入到其他的宿主机上:
save
将镜像打包,与下面的load命令相对应

  1. [root@docker-server ~]# docker save -o nginx.tar daocloud.io/library/nginx

load
与上面的save命令相对应,将上面sava命令打包的镜像通过load命令导入,(实验环境中原来机器上面有镜像可以先删除掉。)

  1. [root@docker-server ~]# docker load < nginx.tar
  2. [root@docker-server ~]# docker images
  1. 把容器导出成tar export import
  2. 把容器做成镜像 commit -a "" -m ""
  3. 把镜像保存为tar save load

通过Dockerfile创建镜像

Docker 提供了一种更便捷的方式,叫作 Dockerfile

  1. docker build命令用于根据给定的Dockerfile构建Docker镜像。

docker build语法:

  1. # docker build [OPTIONS] <PATH | URL | ->
  1. 1. 常用选项说明
  2. --build-arg,设置构建时的变量
  3. --no-cache,默认false。设置该选项,将不使用Build Cache构建镜像
  4. --pull,默认false。设置该选项,总是尝试pull镜像的最新版本
  5. --compress,默认false。设置该选项,将使用gzip压缩构建的上下文
  6. --disable-content-trust,默认true。设置该选项,将对镜像进行验证
  7. --file, -fDockerfile的完整路径,默认值为‘PATH/Dockerfile
  8. --isolation,默认--isolation="default",即Linux命名空间;其他还有processhyperv
  9. --label,为生成的镜像设置metadata
  10. --squash,默认false。设置该选项,将新构建出的多个层压缩为一个新层,但是将无法在多个镜像之间共享新层;设置该选项,实际上是创建了新image,同时保留原有image
  11. --tag, -t,镜像的名字及tag,通常name:tag或者name格式;可以在一次构建中为一个镜像设置多个tag
  12. --network,默认default。设置该选项,Set the networking mode for the RUN instructions during build
  13. --quiet, -q ,默认false。设置该选项,Suppress the build output and print image ID on success
  14. --force-rm,默认false。设置该选项,总是删除掉中间环节的容器
  15. --rm,默认--rm=true,即整个构建过程成功后删除中间环节的容器
  1. 示例:
  2. docker build -t soso/bbauto:v2.1 .
  3. docker build docker创建镜像的命令
  4. -t 是标识新建的镜像属于 soso bbauto镜像
  5. v2.1 tag
  6. "."是用来指明 我们的使用的Dockerfile文件当前目录的

2.1、 创建镜像所在的文件夹和Dockerfile文件

  1. [root@docker-server ~]# mkdir sinatra
  2. [root@docker-server ~]# cd sinatra/
  3. [root@docker-server sinatra]# touch Dockerfile

2.2、 在Dockerfile文件中写入指令,每一条指令都会更新镜像的信息例如:

  1. [root@docker-server sinatra]# vim Dockerfile
  2. #This is a comment
  3. FROM daocloud.io/library/centos:7 #基于哪个基础镜像
  4. MAINTAINER soso soso@docker-server #指定作者
  5. RUN touch a.txt
  6. RUN mkdir /test
  7. RUN yum -y install vim

格式说明:

  1. 命令要大写,"#"是注解。
  2. 每一个指令后面需要跟空格,语法。
  3. FROM 命令是告诉docker 我们的镜像什么从哪里下载。
  4. MAINTAINER 是描述 镜像的创建人。
  5. RUN 命令是在镜像内部执行。就是说他后面的命令应该是针对镜像可以运行的命令。

2.3、创建镜像

  1. 命令:
  2. # docker build -t soso/centos:7 .
  3. docker build docker创建镜像的命令

详细执行过程:

  1. [root@docker-server sinatra]# docker build -t soso/centos:7 .
  2. Sending build context to Docker daemon 2.048kB
  3. Step 1/4 : FROM daocloud.io/library/centos
  4. latest: Pulling from library/centos
  5. d8d02d457314: Pull complete
  6. Digest: sha256:a36b9e68613d07eec4ef553da84d0012a5ca5ae4a830cf825bb68b929475c869
  7. Status: Downloaded newer image for daocloud.io/library/centos:latest
  8. ---> 67fa590cfc1c
  9. Step 2/4 : MAINTAINER soso soso@docker-server
  10. ---> Running in aab3d80939d8
  11. Removing intermediate container aab3d80939d8
  12. ---> 12bae7d75a23
  13. ....

2.4、创建完成后,从镜像创建容器
Docker - 图49

Docker - 图50

Dockerfile实例:容器化python的flask应用

目标: 用 Docker 部署一个用 Python 编写的 Web 应用。

首先部署整个流程:

  1. 基础镜像(python)-->flask-->部署python应用
  2. web框架 flask django

代码功能:

  1. 如果当前环境中有"NAME"这个环境变量,就把它打印在"Hello"后,否则就打印"Hello world",最后再打印出当前环境的 hostname
  1. [root@docker-server ~]# mkdir python_app
  2. [root@docker-server ~]# cd python_app/
  3. [root@docker-server python_app]# vim app.py
  4. from flask import Flask
  5. import socket
  6. import os
  7. app = Flask(__name__)
  8. @app.route('/')
  9. def hello():
  10. html = "<h3>Hello {name}!</h3>" \
  11. "<b>Hostname:</b> {hostname}<br/>"
  12. return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname())
  13. if __name__ == "__main__":
  14. app.run(host='0.0.0.0', port=80)

应用依赖:

定义在同目录下的 requirements.txt 文件里,内容如下:

  1. [root@docker-server python_app]# vim requirements.txt
  2. Flask

Dockerfile制作容器镜像:

  1. # vim Dockerfile
  2. FROM python:2.7-slim
  3. WORKDIR /app
  4. ADD . /app
  5. RUN pip install --trusted-host pypi.python.org -r requirements.txt
  6. EXPOSE 80
  7. ENV NAME World
  8. CMD ["python", "app.py"]

Dockerfile文件说明:

  1. FROM python:2.7-slim
  2. # 使用官方提供的 Python 开发镜像作为基础镜像
  3. # 指定"python:2.7-slim"这个官方维护的基础镜像,从而免去安装 Python 等语言环境的操作。:
  4. WORKDIR /app ---cd /app
  5. # 将工作目录切换为 /app,意思是在这一句之后,Dockerfile 后面的操作都以这一句指定的 /app 目录作为当前目录。
  6. ADD . /app
  7. # 将当前目录下的所有内容复制到 /app 下 Dockerfile 里的原语并不都是指对容器内部的操作。比如 ADD,指的是把当前目录(即 Dockerfile 所在的目录)里的文件,复制到指定容器内的目录当中。
  8. RUN pip install --trusted-host pypi.python.org -r requirements.txt
  9. # 使用 pip 命令安装这个应用所需要的依赖
  10. EXPOSE 80
  11. # 允许外界访问容器的 80 端口
  12. ENV NAME World
  13. # 设置环境变量
  14. CMD ["python", "app.py"]
  15. # 设置容器进程为:python app.py,即:这个 Python 应用的启动命令,这里app.py 的实际路径是 /app/app.py。CMD ["python", "app.py"] 等价于 "docker run python app.py"。

现在目录结构:

  1. [root@docker-server python_app]# ls
  2. Dockerfile app.py requirements.txt

构建镜像:

  1. [root@docker-server python_app]# docker build -t testpython .
  2. -t 给这个镜像加一个 Tag

Dockerfile 中的每个原语执行后,都会生成一个对应的镜像层。即使原语本身并没有明显地修改文件的操作(比如,ENV 原语),它对应的层也会存在。只不过在外界看来,这个层是空的。

Docker - 图51

查看结果:

  1. [root@docker-server python_app]# docker images
  2. REPOSITORY TAG IMAGE ID ...
  3. testpython latest 16bc21f3eea3

启动容器:

  1. [root@docker-server python_app]# docker run -it -p 4000:80 testpython /bin/bash

查看容器:

  1. [root@docker-server python_app]# docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED
  3. ce02568e64ce testpython "/bin/bash" About a minute ago

进入容器:

  1. [root@docker-server python_app]# docker exec -it ce02568 /bin/bash
  2. root@ce02568e64ce:/app# python app.py & #将python运行起来

访问容器内应用:

  1. [root@docker-server ~]# curl http://localhost:4000
  2. <h3>Hello World!</h3><b>Hostname:</b> f201f6855136<br/>

Docker - 图52

实战练习

  1. 1.创建一个jenkinsDockerfile
  2. [root@docker-server ~]# mkdir tomcat
  3. [root@docker-server ~]# cd tomcat/
  4. 将以下安装包拷贝至tomcat目录中
  5. [root@docker-server1 tomcat]# ls
  6. apache-tomcat-8.5.47.tar.gz Dockerfile jdk-8u211-linux-x64.tar.gz jenkins.war
  7. [root@docker-server tomcat]# vim Dockerfile
  8. # This my first jenkins Dockerfile
  9. # Version 1.0
  10. FROM centos:7
  11. MAINTAINER docker-server
  12. ENV JAVA_HOME /usr/local/jdk1.8.0_211
  13. ENV TOMCAT_HOME /usr/local/apache-tomcat-8.5.47
  14. ENV PATH=$JAVA_HOME/bin:$PATH
  15. ADD apache-tomcat-8.5.47.tar.gz /usr/local/
  16. ADD jdk-8u211-linux-x64.tar.gz /usr/local/
  17. RUN rm -rf /usr/local/apache-tomcat-8.5.47/webapps/*
  18. ADD jenkins.war /usr/local/apache-tomcat-8.5.47/webapps
  19. RUN rm -rf apache-tomcat-8.5.47.tar.gz jdk-8u211-linux-x64.tar.gz
  20. EXPOSE 8080
  21. ENTRYPOINT ["/usr/local/apache-tomcat-8.5.47/bin/catalina.sh","run"] #运行命令
  22. [root@docker-server tomcat]# pwd
  23. /root/tomcat
  24. [root@docker-server tomcat]# ls #将jdk与tomcat还有jenkins的包上传到tomcat目录中
  25. apache-tomcat-8.5.47.tar.gz Dockerfile jdk-8u211-linux-x64.tar.gz jenkins.war
  26. [root@docker-server tomcat]# docker build -t jenkins:v1 .
  27. [root@docker-server tomcat]# docker run -itd --name jenkins1 -p 8081:8080 jenkins:v1

Docker - 图53

扩展——CMD与ENTRYPOINT区别

  1. 一、Dockerfile中的CMD
  2. 1、每个Dockerfile中只能有一个CMD如果有多个那么只执行最后一个。
  3. 2CMD 相当于启动docker时候后面添加的参数看,举个简单例子:
  4. # docker run -itd --name test image(镜像) /bin/bash -c
  5. a、镜像名称后面跟了一个/bin/bash -c ,其实等价于在dockerfile中的CMD ["/bin/bash","-c"]。
  6. b、如果dockerfile中的CMD中有了CMD["/bin/bash","-c"],那么就不用在执行的时候再添加了,如果添加了参数的话那么就相当于要执行你添加的参数,默认的CMD中的参数就无效了。
  7. 二、Dockerfile中的ENTRYPOINT
  8. 1、一个DockerfileENTRYPOINT也只能存在一个,若存在多个那么只执行最后一个,你可以理解为开机启动的意思,和CMD有点像,不过还是有区别。
  9. 2、举个简单例子:
  10. aDockerfile中有ENTRYPOINT ["tail","-f","/var/log/nginx/access.log"],那么启动的时候镜像就执行了这个里面的内容,如果你像上面带参数的话就相当于在这个执行的内容后面再加入参数。
  11. 案例:
  12. 如果我们的dockerfile中有a中的这句话然后我们启动我们的docker:
  13. # docker run -itd --name test image(镜像名) /bin/bash -c
  14. 此时就相当于我们启动docker的时候执行了:tail -f /var/log/nginx/access.log /bin/bash -c
  15. 这个命令明显就不对.

参考链接:https://blog.csdn.net/k_520_w/article/details/111731198

Dockerfile优化

编译一个简单的nginx成功以后发现好几百M。

  1. 1RUN 命令要尽量写在一条里,每次 RUN 命令都是在之前的镜像上封装,只会增大不会减小
  2. 2、每次进行依赖安装后,记得yum clean allcentos
  3. #yum clean all 清除缓存中的rpm头文件和包文件
  4. 3、选择比较小的基础镜像。alpine

部署私有仓库应用

私有仓库镜像:
registry —官方出品, 没有图形界面。Docker hub官方已提供容器镜像registry,用于搭建私有仓库
拉取镜像:

  1. [root@docker-server ~]# docker pull daocloud.io/library/registry:latest

运行容器:

  1. [root@docker-server ~]# docker run -itd -v /home/dockerdata/registry:/var/lib/registry --name "pri_registry" --restart=always -p 5000:5000 daocloud.io/library/registry:latest
  2. 参数解释:
  3. /home/dockerdata/registry表示为宿主机的目录,如果不存在自动创建
  4. -v映射目录: 宿主机的目录:容器目录
  5. 把宿主机的目录挂载到容器中,将数据目录挂载出来就是为了防止docker私有仓库这个容器被删除的时候,仓库里面的镜像也被删除。
  6. -p 端口映射:本地端口:容器端口
  1. 注:如果创建容器不成功,报错防火墙,解决方案如下
  1. # systemctl stop firewalld
  2. # yum install iptables*
  3. # systemctl start iptables
  4. # iptables -F
  5. # systemctl restart docker
  1. [root@docker-server ~]# docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. 0823df72b160 daocloud.io/library/registry "/entrypoint.sh /etc…" About a minute ago Up About a minute 0.0.0.0:5000->5000/tcp pri_registry

连接容器查看端口状态:

  1. [root@docker-server ~]# docker exec -it 0823df7 /bin/sh
  2. / # netstat -lntp #查看5000端口是否开启
  3. Active Internet connections (only servers)
  4. Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
  5. tcp 0 0 :::5000 :::* LISTEN 1/registry
  6. / #

在本机查看能否访问该私有仓库, 看看状态码是不是200

  1. [root@docker-server ~]# curl -I http://127.0.0.1:5000
  2. HTTP/1.1 200 OK

为了测试,下载1个比较小的镜像,buysbox

  1. [root@docker-server ~]# docker pull daocloud.io/library/busybox

上传前必须给镜像打tag 注明ip和端口:

  1. [root@docker-server ~]# docker tag daocloud.io/library/busybox 192.168.246.141:5000/busybox

下面这个Mysql是我测试的第二个镜像,从daocloud拉取的:

  1. [root@docker-server ~]# docker pull daocloud.io/library/mysql
  2. [root@docker-server ~]# docker tag daocloud.io/library/mysql 192.168.246.141:5000/daocloud.io/library/mysql
  3. [root@docker-server ~]# docker images

注:tag后面可以使用镜像名称也可以使用id,我这里使用的镜像名称,如果使用官方的镜像,不需要加前缀,但是daocloud.io的得加前缀.

修改请求方式为http:

  1. 默认为https,不改会报以下错误:
  2. Get https://master.up.com:5000/v1/_ping: http: server gave HTTP response to HTTPS client
  3. [root@docker-server ~]# vim /etc/docker/daemon.json #不存在则创建
  4. { "insecure-registries":["192.168.246.141:5000"] }

Docker - 图54

  1. 重启docker
  2. [root@docker-server ~]# systemctl restart docker

上传镜像到私有仓库:

  1. [root@docker-server ~]# docker push 192.168.246.141:5000/busybox
  2. [root@docker-server ~]# docker push 192.168.246.141:5000/daocloud.io/library/mysql
  3. 宿主机查看存放镜像目录:
  4. [root@docker-server ~]# ls /home/dockerdata/registry/docker/registry/v2/repositories/

Docker - 图55

查看私有仓库里的所有镜像:

  1. 语法: # curl http://ip:port/v2/repo名字/tags/list
  2. [root@docker-server ~]# curl http://192.168.246.141:5000/v2/busybox/tags/list
  3. {"name":"busybox","tags":["latest"]}
  4. [root@docker-server ~]# curl http://192.168.246.141:5000/v2/daocloud.io/library/mysql/tags/list
  5. {"name":"daocloud.io/library/mysql","tags":["latest"]}
  6. 这条命令会查看仓库下面所有的镜像:
  7. [root@docker-server ~]# curl http://192.168.246.141:5000/v2/_catalog

Docker - 图56

拉取镜像测试:

  1. 1.先将刚才打了tags的镜像删掉
  2. [root@docker-server ~]# docker rmi 192.168.246.141:5000/busybox
  3. 2.拉取镜像:
  4. [root@docker-server ~]# docker pull 192.168.246.141:5000/busybox
  5. [root@docker-server ~]# docker images

部署docker web ui应用

下载并运行容器:

  1. [root@docker-server ~]# docker pull uifd/ui-for-docker
  2. [root@docker-server ~]# docker run -it -d --name docker-web -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock docker.io/uifd/ui-for-docker

浏览器访问测试:
ip:9000
Docker - 图57

Docker资源限制

在使用 docker 运行容器时,一台主机上可能会运行几百个容器,这些容器虽然互相隔离,但是底层却使用着相同的 CPU、内存和磁盘资源。如果不对容器使用的资源进行限制,那么容器之间会互相影响,小的来说会导致容器资源使用不公平;大的来说,可能会导致主机和集群资源耗尽,服务完全不可用。

CPU 和内存的资源限制已经是比较成熟和易用,能够满足大部分用户的需求。磁盘限制也是不错的,虽然现在无法动态地限制容量,但是限制磁盘读写速度也能应对很多场景。

至于网络,docker 现在并没有给出网络限制的方案,也不会在可见的未来做这件事情,因为目前网络是通过插件来实现的,和容器本身的功能相对独立,不是很容易实现,扩展性也很差。

资源限制一方面可以让我们为容器(应用)设置合理的 CPU、内存等资源,方便管理;另外一方面也能有效地预防恶意的攻击和异常,对容器来说是非常重要的功能。

系统压力测试工具stress

  1. stress是一个linux下的压力测试工具,专门为那些想要测试自己的系统,完全高负荷和监督这些设备运行的用户。

cpu资源限制

限制CPU Share

什么是cpu share:

  1. docker允许用户为每个容器设置一个数字,代表容器的 CPU share,默认情况下每个容器的 share 1024。这个 share 是相对的,本身并不能代表任何确定的意义。当主机上有多个容器运行时,每个容器占用的 CPU 时间比例为它的 share 在总额中的比例。docker 会根据主机上运行的容器和进程动态调整每个容器使用 CPU 的时间比例。

例子:

  1. 如果主机上有两个一直使用 CPU 的容器(为了简化理解,不考虑主机上其他进程),其 CPU share 都是 1024,那么两个容器 CPU 使用率都是 50%;如果把其中一个容器的 share 设置为 512,那么两者 CPU 的使用率分别为 70% 30%;如果删除 share 1024 的容器,剩下来容器的 CPU 使用率将会是 100%。

好处:

  1. 能保证 CPU 尽可能处于运行状态,充分利用 CPU 资源,而且保证所有容器的相对公平;

缺点:

  1. 无法指定容器使用 CPU 的确定值。

设置 CPU share 的参数:

  1. -c --cpu-shares,它的值是一个整数

我的机器是 4 核 CPU,因此运行一个stress容器,使用 stress 启动 4 个进程来产生计算压力:(无CPU限制)

  1. [root@docker-server ~]# docker pull progrium/stress
  2. [root@docker-server ~]# yum -y install epel-release
  3. [root@docker-server ~]# yum install -y htop
  4. [root@docker-server ~]# docker run --rm -it progrium/stress --cpu 4
  5. stress: info: [1] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
  6. stress: dbug: [1] using backoff sleep of 12000us
  7. stress: dbug: [1] --> hogcpu worker 4 [6] forked
  8. stress: dbug: [1] using backoff sleep of 9000us
  9. stress: dbug: [1] --> hogcpu worker 3 [7] forked
  10. stress: dbug: [1] using backoff sleep of 6000us
  11. stress: dbug: [1] --> hogcpu worker 2 [8] forked
  12. stress: dbug: [1] using backoff sleep of 3000us
  13. stress: dbug: [1] --> hogcpu worker 1 [9] forked

在另外一个 terminal 使用 htop 查看资源的使用情况:

Docker - 图58

上图中看到,CPU 四个核资源都达到了 100%。

为了比较,另外启动一个 share 为 512 的容器:

  1. 1.先将没有做限制的命令运行起来
  2. [root@docker-server ~]# docker run --rm -it progrium/stress --cpu 4
  3. 2.在开启一个终端,运行做了CPU限制的命令
  4. [root@docker-server ~]# docker run --rm -it -c 512 progrium/stress --cpu 4
  5. stress: info: [1] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
  6. stress: dbug: [1] using backoff sleep of 12000us
  7. stress: dbug: [1] --> hogcpu worker 4 [6] forked
  8. stress: dbug: [1] using backoff sleep of 9000us
  9. stress: dbug: [1] --> hogcpu worker 3 [7] forked
  10. stress: dbug: [1] using backoff sleep of 6000us
  11. stress: dbug: [1] --> hogcpu worker 2 [8] forked
  12. stress: dbug: [1] using backoff sleep of 3000us
  13. stress: dbug: [1] --> hogcpu worker 1 [9] forked
  14. 3.在开启一个终端执行htop命令
  15. [root@docker-server ~]# htop

因为默认情况下,容器的 CPU share 为 1024,所以这两个容器的 CPU 使用率应该大致为 2:1,下面是启动第二个容器之后的监控截图:

Docker - 图59

两个容器分别启动了四个 stress 进程,第一个容器 stress 进程 CPU 使用率都在 66% 左右,第二个容器 stress 进程 CPU 使用率在 33% 左右,比例关系大致为 2:1,符合之前的预期。

限制CPU 核数

限制容器能使用的 CPU 核数

  1. -c --cpu-shares 参数只能限制容器使用 CPU 的比例,或者说优先级,无法确定地限制容器使用 CPU 的具体核数;从 1.13 版本之后,docker 提供了 --cpus 参数可以限定容器能使用的 CPU 核数。这个功能可以让我们更精确地设置容器 CPU 使用量,是一种更容易理解也因此更常用的手段.
  1. --cpus 后面跟着一个浮点数,代表容器最多使用的核数,可以精确到小数点二位,也就是说容器最小可以使用 0.01 CPU

限制容器只能使用 1.5 核数 CPU:

  1. [root@docker-server ~]# docker run --rm -it --cpus 1.5 progrium/stress --cpu 3
  2. stress: info: [1] dispatching hogs: 3 cpu, 0 io, 0 vm, 0 hdd
  3. stress: dbug: [1] using backoff sleep of 9000us
  4. stress: dbug: [1] --> hogcpu worker 3 [6] forked
  5. stress: dbug: [1] using backoff sleep of 6000us
  6. stress: dbug: [1] --> hogcpu worker 2 [7] forked
  7. stress: dbug: [1] using backoff sleep of 3000us
  8. stress: dbug: [1] --> hogcpu worker 1 [8] forked

在容器里启动三个 stress 来跑 CPU 压力,如果不加限制,这个容器会导致 CPU 的使用率为 300% 左右(也就是说会占用三个核的计算能力)。实际的监控如下图:

Docker - 图60

可以看到,每个 stress 进程 CPU 使用率大约在 50%,总共的使用率为 150%,符合 1.5 核的设置。

如果设置的 —cpus 值大于主机的 CPU 核数,docker 会直接报错:

  1. [root@docker-server ~]# docker run --rm -it --cpus 8 progrium/stress --cpu 3 #启用三个进程做测试
  2. docker: Error response from daemon: Range of CPUs is from 0.01 to 4.00, as there are only 4 CPUs available.
  3. See 'docker run --help'.

如果多个容器都设置了 —cpus ,并且它们之和超过主机的 CPU 核数,并不会导致容器失败或者退出,这些容器之间会竞争使用 CPU,具体分配的 CPU 数量取决于主机运行情况和容器的 CPU share 值。也就是说 —cpus 只能保证在 CPU 资源充足的情况下容器最多能使用的 CPU 数,docker 并不能保证在任何情况下容器都能使用这么多的 CPU(因为这根本是不可能的)。

CPU 绑定

限制容器运行在某些 CPU 核

一般并不推荐在生产中这样使用

docker 允许调度的时候限定容器运行在哪个 CPU 上。

案例:

假如主机上有 4 个核,可以通过 —cpuset 参数让容器只运行在前两个核上:

  1. [root@docker-server ~]# docker run --rm -it --cpuset-cpus=0,1 progrium/stress --cpu 2
  2. stress: info: [1] dispatching hogs: 2 cpu, 0 io, 0 vm, 0 hdd
  3. stress: dbug: [1] using backoff sleep of 6000us
  4. stress: dbug: [1] --> hogcpu worker 2 [6] forked
  5. stress: dbug: [1] using backoff sleep of 3000us
  6. stress: dbug: [1] --> hogcpu worker 1 [7] forked

这样,监控中可以看到只有前面两个核 CPU 达到了 100% 使用率。

Docker - 图61

mem资源限制

docker 默认没有对容器内存进行限制,容器可以使用主机提供的所有内存。

不限制内存带来的问题:

  1. 这是非常危险的事情,如果某个容器运行了恶意的内存消耗软件,或者代码有内存泄露,很可能会导致主机内存耗尽,因此导致服务不可用。
  2. 可以为每个容器设置内存使用的上限,一旦超过这个上限,容器会被杀死,而不是耗尽主机的内存。

限制内存带来的问题:

  1. 限制内存上限虽然能保护主机,但是也可能会伤害到容器里的服务。如果为服务设置的内存上限太小,会导致服务还在正常工作的时候就被 OOM 杀死;如果设置的过大,会因为调度器算法浪费内存。

合理做法:

  1. 1. 为应用做内存压力测试,理解正常业务需求下使用的内存情况,然后才能进入生产环境使用
  2. 2. 一定要限制容器的内存使用上限,尽量保证主机的资源充足,一旦通过监控发现资源不足,就进行扩容或者对容器进行迁移
  3. 3. 尽量不要使用 swapswap 的使用会导致内存计算复杂,对调度器非常不友好

docker 限制容器内存使用量:

  1. docker 启动参数中,和内存限制有关的包括(参数的值一般是内存大小,也就是一个正数,后面跟着内存单位 bkmg,分别对应 bytesKBMB、和 GB):
  2. -m --memory:容器能使用的最大内存大小,最小值为 4m

如果限制容器的内存使用为 64M,在申请 64M 资源的情况下,容器运行正常(如果主机上内存非常紧张,并不一定能保证这一点):

  1. [root@docker-server ~]# docker run --rm -it -m 64m progrium/stress --vm 1 --vm-bytes 64M --vm-hang 0
  2. stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
  3. stress: dbug: [1] using backoff sleep of 3000us
  4. stress: dbug: [1] --> hogvm worker 1 [6] forked
  5. stress: dbug: [6] allocating 67108864 bytes ...
  6. stress: dbug: [6] touching bytes in strides of 4096 bytes ...
  7. stress: dbug: [6] sleeping forever with allocated memory
  8. 容器可以正常运行。
  9. -m 64m:限制你这个容器只能使用64M
  10. --vm-bytes 64M:将内存撑到64兆是不会报错,因为我有64兆内存可用。
  11. hang:就是卡在这里。
  12. --vm:生成几个占用内存的进程

而如果申请 150M 内存,会发现容器里的进程被 kill 掉了(worker 6 got signal 9,signal 9 就是 kill 信号)

猜测:如果内存消耗占用限制数值的2倍,容器会被kill掉

  1. [root@docker-server ~]# docker run --rm -it -m 64m progrium/stress --vm 1 --vm-bytes 150M --vm-hang 0
  2. stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
  3. stress: dbug: [1] using backoff sleep of 3000us
  4. stress: dbug: [1] --> hogvm worker 1 [6] forked
  5. stress: dbug: [6] allocating 157286400 bytes ...
  6. stress: dbug: [6] touching bytes in strides of 4096 bytes ...
  7. stress: FAIL: [1] (416) <-- worker 6 got signal 9
  8. stress: WARN: [1] (418) now reaping child worker processes
  9. stress: FAIL: [1] (422) kill error: No such process
  10. stress: FAIL: [1] (452) failed run completed in 1s

io 资源限制(了解)

对于磁盘来说,考量的参数是容量和读写速度,因此对容器的磁盘限制也应该从这两个维度出发。目前 docker 支持对磁盘的读写速度进行限制,但是并没有方法能限制容器能使用的磁盘容量(一旦磁盘 mount 到容器里,容器就能够使用磁盘的所有容量)。

  1. 第一种是:磁盘的读写速率的限制
  2. 第二种是:磁盘的读写频率的限制

端口转发

Docker - 图62

使用端口转发解决容器端口访问问题

  1. -p:创建应用容器的时候,一般会做端口映射,这样是为了让外部能够访问这些容器里的应用。可以用多个-p指定多个端口映射关系。

mysql应用端口转发:

查看本地地址:

  1. [root@docker-server ~]# ip a
  2. ...
  3. 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
  4. link/ether 00:0c:29:9c:bf:66 brd ff:ff:ff:ff:ff:ff
  5. inet 192.168.246.141/24 brd 192.168.246.255 scope global dynamic ens33
  6. valid_lft 5217593sec preferred_lft 5217593sec
  7. inet6 fe80::a541:d470:4d9a:bc29/64 scope link
  8. valid_lft forever preferred_lft forever

运行容器:使用-p作端口转发,把本地3307转发到容器的3306,其他参数需要查看发布容器的页面提示

  1. [root@docker-server ~]# docker pull daocloud.io/library/mysql:5.7
  2. [root@docker-server ~]# docker run -d --name mysql1 -p 3307:3306 -e MYSQL_ROOT_PASSWORD=Qf@123! daocloud.io/library/mysql:5.7
  3. a4327dbddf665b4302c549320bff869b8a027c2e1eead363d84ce5d06acf2698
  4. -e MYSQL_ROOT_PASSWORD= 设置环境变量,这里是设置mysqlroot用户的密码

通过本地IP:192.168.246.141的3307端口访问容器mysql1内的数据库,出现如下提示恭喜你

  1. 1.安装一个mysql客户端
  2. [root@docker-server ~]# yum install -y mysql
  3. 2.登录
  4. [root@docker-server ~]# mysql -uroot -p'Qf@123!' -h 192.168.246.141 -P 3307
  5. Welcome to the MariaDB monitor. Commands end with ; or \g.
  6. Your MySQL connection id is 3
  7. Server version: 5.7.26 MySQL Community Server (GPL)
  8. Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
  9. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
  10. MySQL [(none)]>
  1. -P(大P):当使用-P标记时,Docker 会随机映射一个 32768~49900 的端口到内部容器开放的网络端口。如下:
  2. [root@docker-server ~]# docker pull daocloud.io/library/redis
  3. [root@docker-server ~]# docker images
  4. REPOSITORY TAG IMAGE ID CREATED SIZE
  5. daocloud.io/library/redis latest 598a6f110d01 2months ago 118MB
  6. [root@docker-server ~]# docker run -d --name myredis -P daocloud.io/library/redis
  7. ca06a026d84a0605d9a9ce6975389a79f4ab9a9a043a03f088cd909c1fe52e29
  8. [root@docker-server ~]# docker ps
  9. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  10. ca06a026d84a daocloud.io/library/redis "docker-entrypoint.s…" 22 seconds ago Up 21 seconds 0.0.0.0:32768->6379/tcp myredis

从上面的结果中可以看出,本地主机的32768端口被映射到了redis容器的6379端口上,也就是说访问本机的32768端口即可访问容器内redis端口。

在别的机器上通过上面映射的端口32768连接这个容器的redis

  1. [root@docker-server2 ~]# yum install -y redis
  2. [root@docker-server2 ~]# redis-cli -h 192.168.246.141 -p 32768
  3. 192.168.246.141:32768> ping
  4. PONG
  5. 192.168.246.141:32768>

容器卷

  1. 把本地宿主机上面的某一个目录挂载到容器里面的目录去。这两个目录都不用提前存在,会自动创建

新卷只能在容器创建过程当中挂载

  1. [root@docker-server ~]# docker run -it --name testnginx -v /test:/test2 daocloud.io/library/nginx /bin/bash
  2. root@86320e734cd1:/# ls
  3. root@86320e734cd1:/# ctrl+p+q #退出 ·
  4. 测试:
  5. [root@docker-server ~]# cd /test/
  6. [root@docker-server test]# ls
  7. [root@docker-server test]# touch a.txt
  8. [root@docker-server test]# cd
  9. [root@docker-server ~]# docker exec -it testnginx /bin/bash
  10. root@86320e734cd1:/# cd test2/
  11. root@86320e734cd1:/test2# ls
  12. a.txt
  13. 共享文件:
  14. [root@docker-server ~]# mkdir /dir
  15. [root@docker-server ~]# vim /dir/a.txt
  16. 123
  17. [root@docker-server ~]# docker run -it --name testnginx2 -v /dir/a.txt:/dir1/a.txt daocloud.io/library/nginx /bin/bash
  18. root@f899be627552:/# cat dir1/a.txt
  19. 123

共享其他容器的卷(其他容器用同一个卷):

  1. [root@docker-server ~]# docker run -it --name testnginx1 --volumes-from testnginx daocloud.io/library/nginx /bin/bash
  2. root@50e6f726335c:/# ls
  3. bin dev home lib64 mnt proc run srv test2 usr
  4. boot etc lib media opt root sbin sys tmp var
  5. root@50e6f726335c:/# cd test2/
  6. root@50e6f726335c:/test2# ls
  7. a.txt

实际应用中可以利用多个-v选项把宿主机上的多个目录同时共享给新建容器:

比如:

  1. # docker run -it -v /abc:/abc -v /def:/def 1ae9

部署Centos7容器应用

镜像下载:

  1. [root@docker-server ~]# docker pull daocloud.io/library/centos:7

systemd 整合:

  1. 因为 systemd 要求 CAPSYSADMIN 权限,从而得到了读取到宿主机 cgroup 的能力,CentOS7 中已经用 fakesystemd 代替了 systemd 但是我们使用systemd,可用参考下面的 Dockerfile
  1. [root@docker-server ~]# mkdir test
  2. [root@docker-server ~]# cd test/
  3. [root@docker-server test]# vim Dockerfile
  4. FROM daocloud.io/library/centos:7
  5. MAINTAINER "soso" soso@qq.com
  6. ENV container docker
  7. RUN yum -y swap -- remove fakesystemd -- install systemd systemd-libs
  8. RUN yum -y update; yum clean all; \
  9. (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
  10. rm -f /lib/systemd/system/multi-user.target.wants/*;\
  11. rm -f /etc/systemd/system/*.wants/*;\
  12. rm -f /lib/systemd/system/local-fs.target.wants/*; \
  13. rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
  14. rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
  15. rm -f /lib/systemd/system/basic.target.wants/*;\
  16. rm -f /lib/systemd/system/anaconda.target.wants/*;
  17. VOLUME [ "/sys/fs/cgroup" ]
  18. CMD ["/usr/sbin/init"]

这个Dockerfile删除fakesystemd 并安装了 systemd。然后再构建基础镜像:

  1. [root@docker-server test]# docker build -t local/c7-systemd .

执行没有问题这就生成一个包含 systemd 的应用容器示例

  1. [root@docker-server test]# docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. local/c7-systemd latest a153dcaa642e 6 minutes ago 391MB

为了使用像上面那样包含 systemd 的容器,需要创建一个类似下面的Dockerfile:

  1. [root@docker-server test]# mkdir http
  2. [root@docker-server test]# cd http/
  3. [root@docker-server http]# vim Dockerfile
  4. FROM local/c7-systemd
  5. RUN yum -y install httpd; yum clean all; systemctl enable httpd.service
  6. EXPOSE 80
  7. CMD ["/usr/sbin/init"]
  8. [root@docker-server http]# cat Dockerfile
  9. FROM local/c7-systemd
  10. RUN rm -rf /etc/yum.repos.d/*
  11. ADD Centos-7.repo /etc/yum.repos.d/
  12. RUN yum -y install httpd; yum clean all; systemctl enable httpd
  13. EXPOSE 80
  14. CMD ["/usr/sbin/init"]

Docker - 图63

构建镜像:

  1. [root@docker-server http]# docker build -t local/c7-systemd-httpd .

运行包含 systemd 的应用容器:

为了运行一个包含 systemd 的容器,需要使用—privileged选项, 并且挂载主机的 cgroups 文件夹。 下面是运行包含 systemd 的 httpd 容器的示例命令:

  1. [root@docker-server http]# docker run --privileged -tid -v /sys/fs/cgroup:/sys/fs/cgroup:ro -p 80:80 local/c7-systemd-httpd
  2. --privileged:授权提权。让容器内的root用户拥有真正root权限(有些权限是没有的)

注意:如果不加会运行在前台(没有用-d),可以用ctrl+p+q放到后台去

测试可用:

  1. [root@docker-server http]# yum install -y elinks
  2. [root@docker-server http]# elinks --dump http://192.168.246.141 #apache的默认页面
  3. Testing 123..
  4. This page is used to test the proper operation of the [1]Apache HTTP
  5. server after it has been installed. If you can read this page it means
  6. that this site is working properly. This server is powered by [2]CentOS.

再来个安装openssh-server的例子:

  1. [root@docker-server http]# cd ..
  2. [root@docker-server test]# mkdir ssh
  3. [root@docker-server test]# cd ssh/
  4. [root@docker-server ssh]# vim Dockerfile
  5. FROM local/c7-systemd
  6. RUN yum -y install openssh-server; yum clean all; systemctl enable sshd.service
  7. RUN echo 1 | passwd --stdin root
  8. EXPOSE 22
  9. CMD ["/usr/sbin/init"]
  10. [root@docker-server ssh]# docker build --rm -t local/c7-systemd-sshd .
  11. [root@docker-server ssh]# docker run --privileged -tid -v /sys/fs/cgroup:/sys/fs/cgroup:ro -p 2222:22 local/c7-systemd-sshd
  12. [root@docker-server ssh]# ssh 192.168.246.141 -p 2222
  13. [root@ce1af52a6f6c ~]#

Docker数据存储位置

  1. 查看存储路径
  2. [root@docker-server ~]# docker info | grep Root
  3. Docker Root Dir: /var/lib/docker
  4. 修改默认存储位置:
  5. dockerd的启动命令后面追加--data-root参数指定新的位置
  6. [root@docker-server ~]# vim /usr/lib/systemd/system/docker.service
  7. ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --data-root=/data
  8. [root@docker-server ~]# systemctl daemon-reload
  9. [root@docker-server ~]# systemctl restart docker
  10. 查看是否生效:
  11. [root@docker-server ~]# docker info | grep Root
  12. Docker Root Dir: /data
  13. [root@docker-server ~]# cd /data/
  14. [root@docker-server data]# ls
  15. builder buildkit containers image network overlay2 plugins runtimes swarm tmp trust volumes

Docker网络

容器网络分类

注:
面试用,用了编排之后就没有用了
查看当前网络:

  1. [root@docker-server ~]# docker network list
  2. NETWORK ID NAME DRIVER SCOPE
  3. 9b902ee3eafb bridge bridge local
  4. 140a9ff4bb94 host host local
  5. d1210426b3b0 none null local

docker安装后,默认会创建3种网络类型,bridge、host和none
1、bridge:网络桥接

  1. 默认情况下启动、创建容器都是用该模式,所以每次docker容器重启时会按照顺序获取对应ip地址。

2、none:无指定网络

  1. 启动容器时,可以通过--network=none,docker容器不会分配局域网ip

3、host:主机网络

  1. docker容器和主机共用一个ip地址。
  2. 使用host网络创建容器:
  3. [root@docker-server ~]# docker run -it --name testnginx2 --net host 98ebf73ab
  4. [root@docker-server ~]# netstat -lntp | grep 80
  5. tcp6 0 0 :::80 :::* LISTEN 3237/docker-proxy
  6. 浏览器访问宿主ip地址

Docker - 图64

4、固定ip:
创建固定Ip的容器:

  1. 4.1、创建自定义网络类型,并且指定网段
  2. [root@docker-server ~]# docker network create --subnet=192.168.0.0/16 staticnet
  3. 4efd309244c6ad70eda2d047a818a3aec5b162f5ca29fb6024c09a5efbf15854
  4. 通过docker network ls可以查看到网络类型中多了一个staticnet:
  5. [root@docker-server ~]# docker network ls
  6. NETWORK ID NAME DRIVER SCOPE
  7. 9b902ee3eafb bridge bridge local
  8. 140a9ff4bb94 host host local
  9. d1210426b3b0 none null local
  10. 4efd309244c6 staticnet bridge local
  1. 4.2、使用新的网络类型创建并启动容器
  2. [root@docker-server ~]# docker run -itd --name server --net staticnet --ip 192.168.0.2 daocloud.io/library/centos:7
  3. 通过docker inspect可以查看容器ip192.168.0.2:
  4. [root@docker-server ~]# docker inspect server | grep -i ipaddress
  5. "SecondaryIPAddresses": null,
  6. "IPAddress": "",
  7. "IPAddress": "192.168.0.2",
  8. 关闭容器并重启,发现容器ip并未发生改变
  1. 4.3、删除已创建网络
  2. 需要删除使用当前网络的容器。才能删除网络
  3. [root@docker-server]# docker network rm staticnet

异主容器互联

方式1、路由方式

小规模docker环境大部分运行在单台主机上,如果公司大规模采用docker,那么多个宿主机上的docker如何互联
Docker - 图65

Docker默认的内部ip为172.17.42.0网段,所以必须要修改其中一台的默认网段以免ip冲突。

  1. 注:docker版本为1.13
  2. 1.docker-server1上面操作----192.168.246.141
  3. [root@docker-server1 ~]# docker pull daocloud.io/library/centos
  4. [root@docker-server1 ~]# vim /etc/sysconfig/docker-network
  5. DOCKER_NETWORK_OPTIONS=--bip=172.17.0.1/16
  6. [root@docker-server1 ~]# vim /etc/sysctl.conf
  7. net.ipv4.ip_forward=1
  8. [root@docker-server1 ~]# sysctl -p
  9. [root@docker-server1 ~]# reboot
  10. [root@docker-server1 ~]# docker images
  11. REPOSITORY TAG IMAGE ID CREATED SIZE
  12. daocloud.io/library/centos latest 0f3e07c0138f 3 weeks ago 220MB
  13. [root@docker-server1 ~]# docker run -it --name centos daocloud.io/library/centos:latest /bin/bash
  14. [root@ef1a4d6be97f /]#
  15. [root@docker-server1 ~]# docker inspect centos | grep IPAddress
  16. "SecondaryIPAddresses": null,
  17. "IPAddress": "172.17.0.2",
  18. "IPAddress": "172.17.0.2",
  19. ===============================================
  20. 2.docker-server2(192.168.246.143)上:
  21. [root@docker-server2 ~]# vim /etc/sysconfig/docker-network
  22. DOCKER_NETWORK_OPTIONS=--bip=172.18.0.1/16
  23. [root@docker-server2 ~]# vim /etc/sysctl.conf
  24. net.ipv4.ip_forward = 1
  25. [root@docker-server2 ~]# sysctl -p
  26. [root@docker-server2 ~]# reboot
  27. [root@docker-server2 ~]# systemctl daemon-reload
  28. [root@docker-server2 ~]# systemctl restart docker
  29. [root@docker-server2 ~]# docker images
  30. REPOSITORY TAG IMAGE ID CREATED SIZE
  31. daocloud.io/library/centos latest 0f3e07c0138f 3 weeks ago 220MB
  32. [root@docker-server2 ~]# docker run -it --name centos daocloud.io/library/centos:latest /bin/bash
  33. [root@c84a8c704d03 /]#
  34. [root@docker-server2 ~]# docker inspect centos| grep IPAddress
  35. "SecondaryIPAddresses": null,
  36. "IPAddress": "172.18.0.2",
  37. "IPAddress": "172.18.0.2",

添加路由:

  1. [root@docker-server1 ~]# route add -net 172.18.0.0/16 gw 192.168.246.143
  2. [root@docker-server2 ~]# route add -net 172.17.0.0/16 gw 192.168.246.141

验证:

  1. 进入到docker-server1主机的centos容器中,ping docker-server2主机的centos容器,进行测试

Docker - 图66

  1. 进入到docker-server2主机的centos容器中,ping docker-server1主机的centos容器,进行测试

Docker - 图67
现在两台宿主机里的容器就可以通信了。

方式二、open vswitch

如果要在生产和测试环境大规模采用docker技术,首先就需要解决不同物理机建的docker容器互联问题。

centos7环境下可以采用open vswitch实现不同物理服务器上的docker容器互联